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

            Bug ID: 80075
           Summary: [7 regression] ICE: "statement marked for throw, but
                    doesn’t" with -fnon-call-exceptions
           Product: gcc
           Version: 7.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: c++
          Assignee: unassigned at gcc dot gnu.org
          Reporter: ian at airs dot com
  Target Milestone: ---

Compiling this C++ code with -O2 -fnon-call-exceptions crashes trunk:

struct s {
  int i;
};

extern int use_memcpy;
extern void my_memcpy(void*, void*, int);

int
f (struct s* p)
{
  struct s a;

  try
    {
      a = (struct s){};
      if (!use_memcpy)
        *p = a;
      else
        my_memcpy (p, &a, sizeof (struct s));
    } catch (...)
    {
      return 0;
    }
  return 1;
}

I get

foo.cc: In function ‘int f(s*)’:
foo.cc:9:1: error: statement marked for throw, but doesn’t
 f (struct s* p)
 ^
# .MEM_10 = VDEF <.MEM_7>
*p_8(D) = {};
foo.cc:9:1: internal compiler error: verify_gimple failed
0xe040ee verify_gimple_in_cfg(function*, bool)
        ../../gccgo3/gcc/tree-cfg.c:5266
0xce04ba execute_function_todo
        ../../gccgo3/gcc/passes.c:1966
0xce0ef5 execute_todo
        ../../gccgo3/gcc/passes.c:2016
Please submit a full bug report,
with preprocessed source if appropriate.
Please include the complete backtrace with any bug report.
See <https://gcc.gnu.org/bugs/> for instructions.


The problem arises when optimize_memcpy in tree-ssa-ccp changes

  struct s a;
  int use_memcpy.0_1;

  <bb 2> [100.00%]:
  a = {};
  use_memcpy.0_1 = use_memcpy;
  if (use_memcpy.0_1 == 0)
    goto <bb 3>; [63.36%]
  else
    goto <bb 4>; [36.64%]

  <bb 3> [63.36%]:
  *p_5(D) = a;
  goto <bb 5>; [100.00%]

  <bb 4> [36.64%]:
  my_memcpy (p_5(D), &a, 4);

  <bb 5> [100.00%]:
  a ={v} {CLOBBER};
  return;

into

  struct s a;
  int use_memcpy.0_1;

  <bb 2> [100.00%]:
  a = {};
  use_memcpy.0_1 = use_memcpy;
  if (use_memcpy.0_1 == 0)
    goto <bb 3>; [63.36%]
  else
    goto <bb 4>; [36.64%]

  <bb 3> [63.36%]:
  *p_5(D) = {};
  goto <bb 5>; [100.00%]

  <bb 4> [36.64%]:
  my_memcpy (p_5(D), &a, 4);

  <bb 5> [100.00%]:
  a ={v} {CLOBBER};
  return;

The RHS of the assignment in bb 3 changed from "a" to "{}".  That
transformation seems OK.  However, the verification pass believes that the new
statement, "*p_5(D) = {};", does not throw.  That is of course incorrect. 
Because I am using -fnon-call-exceptions, the statement will throw if p is
NULL.

In looking at this, I see stmt_could_throw_1_p in tree-eh.c calls
operation_could_trap_helper_p passing the assignment code, which is now
CONSTRUCTOR.  The latter function always returns false for CONSTRUCTOR with
*handled = true.  It's correct that CONSTRUCTOR can not trap, but it doesn't
follow that the statement as a whole can not trap.  But I don't really
understand why stmt_could_throw_1_p doesn't always check the LHS of the
assignment statement, so basically I'm not sure why this works in general.

I actually ran into this with some Go code, and reverse engineered a C++
example.

This C++ example compiles with GCC 6, so this appears to be a regression.

Reply via email to