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.