https://gcc.gnu.org/bugzilla/show_bug.cgi?id=116449
Jakub Jelinek <jakub at gcc dot gnu.org> changed: What |Removed |Added ---------------------------------------------------------------------------- CC| |ppalka at gcc dot gnu.org --- Comment #6 from Jakub Jelinek <jakub at gcc dot gnu.org> --- get_member_function_from_ptrfunc already properly calls save_expr if function has side-effects, but in this case it doesn't. The thing is that the function tree (which contains nested ARRAY_REFs here) is used several times, in particular ((long int) FUNCTION.__pfn & 1) != 0 ? (void C::<T40d> (struct C *) *) *(*(int (*) () * *) ((struct C *) this + (sizetype) FUNCTION.__delta) + (sizetype) ((long int) FUNCTION.__pfn - 1)) : FUNCTION.__pfn; where FUNCTION is that VIEW_CONVERT_EXPR<struct D[1]>(e)[((struct C *) this)->c[VIEW_CONVERT_EXPR<int>(x)]].d If all the 4 locations would use the same tree, it would work just fine, the first FUNCTION occurrence is evaluated unconditionally in the expression, so when cp_genericize then instruments it with .UBSAN_BOUNDS, the SAVE_EXPR which is created for that is evaluated there and then can be used in all the remaining 3 copies. But unfortunately build_conditional_expr -> build_conditional_expr -> convert_like -> ... -> scalar_constant_value -> constant_value_1 uses unshare_expr on the expression, so there are actually 2 separate unshared copies of FUNCTION, one evaluated in the (FUNCTION.__pfn & 1) != 0 check and one in the remaining 3 spots, and that is the problem, because the .UBSAN_BOUNDS instrumentation creates then 2 SAVE_EXPRs and the second one will have the temporary initialized when gimplifying the first occurrence, which is in one of the COND_EXPR branches; that means in the other COND_EXPR branch it will be uninitialized. The #c5 patch (aside from a formatting issue) isn't IMHO the right fix, save_expr doesn't really guarantee it will create a SAVE_EXPR. Given that for the cast to bool unshare_expr will be done, one possible fix is to e3 = unshare_expr (e3); before the build_conditional_expr, another is to ensure unshare_expr doesn't happen for the e1 case (say by using just build2 for the BIT_AND_EXPR and NE_EXPR), and last one would be to make sure function is a SAVE_EXPR or perhaps TARGET_EXPR whenever it isn't a non-side-effect decl or constant. E.g. through force_target_expr.