https://gcc.gnu.org/bugzilla/show_bug.cgi?id=103328

--- Comment #11 from Richard Biener <rguenth at gcc dot gnu.org> ---
The first stmt we complain on remains in the same function.  The functions
scope tree at the point of complaint is

{ Scope block #0 

  { Scope block #0 (unused) 
    struct coroutine_handle _Coro_actor_continue;

    { Scope block #0 (unused) 
      void (*<Te0e>) (struct
_ZZZNSt11server_impl12send_messageISt7variantIJN12_GLOBAL__N_114append_requestEEEEEvNS2_8internal9tagged_idINS2_13server_id_tagEEET_ENKUlS9_E_clIS3_EEDaS9_ENKUlvE_clEv.Frame
*) _Coro_resume_fn [value-expr: frame_ptr->_Coro_resume_fn];
      void (*<Te0e>) (struct
_ZZZNSt11server_impl12send_messageISt7variantIJN12_GLOBAL__N_114append_requestEEEEEvNS2_8internal9tagged_idINS2_13server_id_tagEEET_ENKUlS9_E_clIS3_EEDaS9_ENKUlvE_clEv.Frame
*) _Coro_destroy_fn [value-expr: frame_ptr->_Coro_destroy_fn];
      struct promise_type _Coro_promise [value-expr: frame_ptr->_Coro_promise];
      struct coroutine_handle _Coro_self_handle [value-expr:
frame_ptr->_Coro_self_handle];
      const struct ._anon_5 * const __closure [value-expr:
frame_ptr->__closure];
      short unsigned int _Coro_resume_index [value-expr:
frame_ptr->_Coro_resume_index];
      bool _Coro_frame_needs_free [value-expr:
frame_ptr->_Coro_frame_needs_free];
      bool _Coro_initial_await_resume_called [value-expr:
frame_ptr->_Coro_initial_await_resume_called];

      { Scope block #0 (unused) 
        struct suspend_never Is [value-expr: frame_ptr->Is_1_1];

      }

      { Scope block #0 
        void resume.6 = <<< error >>>;
        void destroy.6 = <<< error >>>;
        void resume.4 = <<< error >>>;
        void destroy.4 = <<< error >>>;
        void resume.2 = <<< error >>>;
        void destroy.2 = <<< error >>>;
        void coro.delete.frame = <<< error >>>;
        void coro.delete.promise = <<< error >>>;
        void actor.continue.ret = <<< error >>>;
        void actor.suspend.ret = <<< error >>>;
        void actor.begin = <<< error >>>;
        void final.suspend = <<< error >>>;
        const struct tagged_id cid [value-expr: __closure->__cid];
        const struct append_request cm [value-expr: __closure->__cm];
        struct server_impl * const this [value-expr: __closure->__this];

        { Scope block #0 (unused) 
          struct
_ZZZNSt11server_impl12send_messageISt7variantIJN12_GLOBAL__N_114append_requestEEEEEvNS2_8internal9tagged_idINS2_13server_id_tagEEET_ENKUlS9_E_clIS3_EEDaS9_ENKUlvE_clEv.Frame
* _Coro_frameptr;
          bool _Coro_promise_live;
          bool _Coro_gro_live;

          { Scope block #0 (unused) 

          }

        }

      }

    }

  }

}

while at the point we lower control flow and set gimple_block it is

{ Scope block #0 

  { Scope block #0 (unused) 
    struct coroutine_handle _Coro_actor_continue;

    { Scope block #0 (unused) 
      void (*<Te0e>) (struct
_ZZZNSt11server_impl12send_messageISt7variantIJN12_GLOBAL__N_114append_requestEEEEEvNS2_8internal9tagged_idINS2_13server_id_tagEEET_ENKUlS9_E_clIS3_EEDaS9_ENKUlvE_clEv.Frame
*) _Coro_resume_fn [value-expr: frame_ptr->_Coro_resume_fn];
      void (*<Te0e>) (struct
_ZZZNSt11server_impl12send_messageISt7variantIJN12_GLOBAL__N_114append_requestEEEEEvNS2_8internal9tagged_idINS2_13server_id_tagEEET_ENKUlS9_E_clIS3_EEDaS9_ENKUlvE_clEv.Frame
*) _Coro_destroy_fn [value-expr: frame_ptr->_Coro_destroy_fn];
      struct promise_type _Coro_promise [value-expr: frame_ptr->_Coro_promise];
      struct coroutine_handle _Coro_self_handle [value-expr:
frame_ptr->_Coro_self_handle];
      const struct ._anon_5 * const __closure [value-expr:
frame_ptr->__closure];
      short unsigned int _Coro_resume_index [value-expr:
frame_ptr->_Coro_resume_index];
      bool _Coro_frame_needs_free [value-expr:
frame_ptr->_Coro_frame_needs_free];
      bool _Coro_initial_await_resume_called [value-expr:
frame_ptr->_Coro_initial_await_resume_called];

      { Scope block #0 
        void resume.6 = <<< error >>>;
        void destroy.6 = <<< error >>>;
        void resume.4 = <<< error >>>;
        void destroy.4 = <<< error >>>;
        void resume.2 = <<< error >>>;
        void destroy.2 = <<< error >>>;
        void coro.delete.frame = <<< error >>>;
        void coro.delete.promise = <<< error >>>;
        void actor.continue.ret = <<< error >>>;
        void actor.suspend.ret = <<< error >>>;
        void actor.begin = <<< error >>>;
        void final.suspend = <<< error >>>;
        const struct tagged_id cid [value-expr: __closure->__cid];
        const struct append_request cm [value-expr: __closure->__cm];
        struct server_impl * const this [value-expr: __closure->__this];

        { Scope block #0 
          struct append_request m [value-expr: frame_ptr->m_2_3];
          struct tagged_id id [value-expr: frame_ptr->id_2_3];

          { Scope block #0 (unused) 
            struct awaiter Aw0 [value-expr: frame_ptr->Aw0_3_4];

          }

        }

      }

      { Scope block #0 (unused) 
        struct suspend_never Is [value-expr: frame_ptr->Is_1_1];

      }

    }

  }

}

The block we associate with the stmt is

{ Scope block #0 (unused) 
  struct awaiter Aw0 [value-expr: frame_ptr->Aw0_3_4];

}

it looks like there's mangling of the block tree happening somehow.
We run into

/* Lower a bind_expr TSI.  DATA is passed through the recursion.  */

static void
lower_gimple_bind (gimple_stmt_iterator *gsi, struct lower_data *data)
{
  tree old_block = data->block;
  gbind *stmt = as_a <gbind *> (gsi_stmt (*gsi));
  tree new_block = gimple_bind_block (stmt);

  if (new_block)
    {
      if (new_block == old_block)
        {
          /* The outermost block of the original function may not be the
             outermost statement chain of the gimplified function.  So we
             may see the outermost block just inside the function.  */
          gcc_assert (new_block == DECL_INITIAL (current_function_decl));
          new_block = NULL;
        }
      else
        { 
          /* We do not expect to handle duplicate blocks.  */
          gcc_assert (!TREE_ASM_WRITTEN (new_block));
          TREE_ASM_WRITTEN (new_block) = 1;

          /* Block tree may get clobbered by inlining.  Normally this would
             be fixed in rest_of_decl_compilation using block notes, but
             since we are not going to emit them, it is up to us.  */
          BLOCK_CHAIN (new_block) = BLOCK_SUBBLOCKS (old_block);
          BLOCK_SUBBLOCKS (old_block) = new_block;
          BLOCK_SUBBLOCKS (new_block) = NULL_TREE;
          BLOCK_SUPERCONTEXT (new_block) = old_block;

with BLOCK_CHAIN (new_block) != NULL and lose those blocks.  old_block
is the outermost block here.

It looks like the BIND_EXPR nesting does not match the BLOCK tree nesting
and things go wrong from there.  The IL looks like

{
  [t.i:533:13] try
    {
      [t.i:533:13] {
        struct
_ZZZNSt11server_impl12send_messageISt7variantIJN12_GLOBAL__N_114append_requestEEEEEvNS2_8internal9tagged_idINS2_13server_id_tagEEET_ENKUlS9_E_clIS3_EEDaS9_ENKUlvE_clEv.Frame
* _Coro_frameptr;
        bool _Coro_promise_live;
        bool _Coro_gro_live;

        [t.i:533:13] _Coro_frameptr = 0B;
        [t.i:533:13] _Coro_promise_live = 0;
        [t.i:533:13] _Coro_gro_live = 0;
        [t.i:533:13] _1 = .CO_FRAME (48, _Coro_frameptr);
        [t.i:533:13] _Coro_frameptr = operator new (_1);
        [t.i:533:13] try
          {
            [t.i:533:13] [t.i:533:13] _Coro_frameptr->_Coro_frame_needs_free =
1;
            [t.i:533:13] [t.i:533:13] _Coro_frameptr->_Coro_resume_fn =
operator();
            [t.i:533:13] [t.i:533:13] _Coro_frameptr->_Coro_destroy_fn =
operator();
            [t.i:533:13] [t.i:533:13] _Coro_frameptr->__closure = __closure;
            [t.i:533:13] {
              [t.i:533:13] _2 = [t.i:533:13] &[t.i:533:13]
_Coro_frameptr->_Coro_promise;
              [t.i:533:13]
seastar::internal::coroutine_traits_base<>::promise_type::get_return_object
(_2);
              [t.i:533:13] [t.i:533:13] _Coro_frameptr->_Coro_resume_index = 0;
              [t.i:533:13]
std::server_impl::send_message<std::variant<{anonymous}::append_request>
>({anonymous}::server_id,
std::variant<{anonymous}::append_request>)::<lambda(auto:1)>::<lambda()>::operator()
(_Coro_frameptr);
              [t.i:533:13] return <retval>;
            }
          }
        catch
          {
            [t.i:533:13] catch (NULL)
              {
                [t.i:533:13] try
                  {
                    [t.i:533:13] _3 = __builtin_eh_pointer (0);
                    [t.i:533:13] __cxa_begin_catch (_3);
                    [t.i:533:13] operator delete (_Coro_frameptr);
                    [t.i:533:13] __cxa_rethrow ();
                  }
                finally
                  {
                    [t.i:533:13] __cxa_end_catch ();
                  }
              }
          }
      }
    }
  catch
    {
      <<<eh_must_not_throw (terminate)>>>
    }
}

IIRC there's no "verification" of IL BIND_EXPR/gimple_block nesting vs.
DECL_INITIAL block tree nesting.

Reply via email to