See <https://wg21.link/P2795R5>.
.
This might require both gimple and rtl changes. Well
(In reply to Andrew Pinski from comment #2) Well RTL might be already done via init-regs (but there was some movement on removing that), see PR 61810 .
ML discussion: https://inbox.sourceware.org/gcc/aLVlJKBqfrLjwsIO@tucnak/
Created attachment 62368 [details] gcc16-pr114457-wip.patch Untested WIP progress, which compared to the last gcc-patches posted one can now handle also forward gotos across vacuous initializations for C++26 (but it can still mess up -Wimplicit-fallthrough* handling). Next steps are handling of backward gotos across vacuous initializations, then switch jumps to case labels across vacuous initializations and finally trying to deal with it for -Wimplicit-fallthrough*.
Created attachment 62375 [details] gcc16-pr114457-wip.patch Updated patch. This handles both forward and backward gotos as well as switch to case/default label jumps, but still want to debug some details in there and turn: struct S { int a, b, c; }; int foo (int x) { int r = 0; if (x == 1) goto l1; S s1; if (x == 2) goto l1; S s2; { S s10; if (x == 12) goto l1; s10.a = 1; r += s10.a; int i1; if (x == 13) goto l1; i1 = 2; r += i1; } if (x == 3) goto l2; if (x == 4) goto l1; { S s3; if (x == 5) goto l2; S s4; if (x == 6) goto l1; { S s5; if (x == 7) goto l1; s5.a = 5; r += s5.a; } S s6; { S s7; S s8; if (x == 8) goto l1; S s9; if (x == 9) goto l2; if (x == 10) goto l2; if (x == 11) goto l2; l1: l2: s1.a = 1; s2.b = 2; s3.c = 3; s4.a = 4; s6.b = 6; s7.c = 7; s8.a = 8; s9.b = 9; r += s1.a + s2.b + s3.c; r += s4.a + s6.b + s7.c; r += s8.a + s9.b; if (x == 14) goto l3; S s11; switch (x) { S s12; case 15: S s13; // FALLTHRU l3: case 16: case 17: S s14; s11.a = 1; s12.b = 2; s13.c = 3; s14.a = 4; r += s11.a + s12.b + s13.c; r += s14.a; return r; case 18: S s15; s11.a = 1; s12.b = 2; s13.c = 3; s14.a = 4; s15.b = 5; r += s11.a + s12.b + s13.c; r += s14.a + s15.b; return r; default: if (x != 19 && x != 20) break; S s16; s11.a = 1; s12.b = 2; s13.c = 3; s14.a = 4; s15.b = 5; s16.c = 6; r += s11.a + s12.b + s13.c; r += s14.a + s15.b + s16.c; return r; } if (x == 21) goto l3; } S s17; if (x == 22) goto l3; if (x == 23) goto l1; if (x == 24) goto l2; s17.a = 1; r += s17.a; } S s18; if (x == 25) { S s19; s19.c = 2; r += s19.c; if (x == 29) l4:; goto l3; } if (x == 26) goto l1; if (x == 27) goto l2; s18.b = 1; r += s18.b; if (x == 28) goto l4; { S s20; { S s21; if (x == 29) goto l1; S s22; if (x == 30) goto l2; l5: s20.a = 1; s21.b = 2; s22.c = 3; r += s20.a + s21.b + s22.c; switch (x) { case 31: S s23; // FALLTHRU l6: case 32: case 33: S s24; s23.a = 1; s24.b = 2; r += s23.a + s24.b; return r; default: if (x >= 34 && x <= 35) return r; break; } if (x == 34) goto l5; if (x == 35) goto l6; return r; } if (x == 36) goto l5; if (x == 37) goto l6; } if (x == 38) goto l5; if (x == 39) goto l6; return r; } into a testsuite testcase with .DEFERRED_INIT call count checks for each of the vars.
Created attachment 62390 [details] gcc16-pr114457-wip.patch Updated patch which handles the forward/backward gotos as well as switch jumps to case/default labels if they cross vacuous initializers for -std=c++26 or -ftrivial-auto-var-init= and handles it for -Wimplicit-fallthrough as well.
The master branch has been updated by Jakub Jelinek <jakub@gcc.gnu.org>: https://gcc.gnu.org/g:f256a13f8aed833fe964a2ba541b7b30ad9b4a76 commit r16-4212-gf256a13f8aed833fe964a2ba541b7b30ad9b4a76 Author: Jakub Jelinek <jakub@redhat.com> Date: Sat Oct 4 09:50:39 2025 +0200 c++, gimplify: Implement C++26 P2795R5 - Erroneous behavior for uninitialized reads [PR114457] The following patch implements the C++26 P2795R5 paper by enabling something like -ftrivial-auto-var-init=zero by default for -std=c++26/-std=gnu++26. There is an important difference between explicit -ftrivial-auto-var-init=zero and the implicitly enabled one, in particular the explicit one will try to clear padding bits on vars with explicit initializers, while the default C++26 mode does not - C++26 says that using the padding bits for anything but copying structures around is still undefined behavior rather than erroneous behavior. Users can still override the default C++26 behavior in both directions, with -ftrivial-auto-var-init=uninitialized even C++26 will act as C++23 with treating all uninitialized var reads (except for copying) as UB rather than EB, with -ftrivial-auto-var-init=zero it will also clear padding bits on explicit initialization and with -ftrivial-auto-var-init=pattern it will initialize to pattern with clearing padding bits in between. There are other changes that had to be implemented. First of all, we need to magicly preinitialize also temporary objects; this is implemented for both the C++26 implicit mode and explicit -ftrivial-auto-var-init={zero,pattern} by emitting .DEFERRED_INIT before construction of TARGET_EXPR temporaries (if they have void type initializers, i.e. are initialized by code rather than some value). Second needed change is dropping *this ={v} {CLOBBER(bob)}; statements at the start of the constructors for -flifetime-dse=2, that says the old content of *this is irrelevant, which is not true anymore for C++26, where we want to treat it like that for -W*uninitialized purposes, but at runtime actually initialize the values. Instead for -flifetime-dse=2 we emit such {CLOBBER(bob)} before calling whole object constructor (on TARGET_EXPR with void type initializer or on DECL_EXPR). And a separate patch added and another one will be adding more {CLOBBER(bob)} to new expressions. The third needed change is about gotos and switches across vacuous initialization. C++26 says those are still valid, but don't make an exception for those in the EB rules. The patch now includes redirecting of forward/backward gotos which cross vacuous initializations for -std=c++26 and -ftrivial-auto-var-init={zero,pattern} by adding an artificial if (0) { lab1: v1 = .DEFERRED_INIT (...); lab2: v2 = .DEFERRED_INIT (...); } etc. hunk before the user label (or for case labels moving the case label into it). Only one per adjacent set of labels, with perhaps multiple artificial labels in it. I believe (and testing seems to confirm that) that one only needs one set of such initialized vars per the adjacent label group, if some forward or backward jump crosses more vacuous inits, it will always cross a subset or superset of the others and when the vars are ordered right, it can jump into different positions in the same if (0). Furthermore, -Wimplicit-fallthrough and -Wswitch-unreachable warnings have been adjusted to deal with that. These changes mean that -Wtrivial-auto-var-init warning now doesn't make sense for C++, as there is nothing to warn about, all the switches and all the gotos are handled right for -ftrivial-auto-var-init= and are handled that way both for the implicit C++26 mode and for explicit -ftrivial-auto-var-init= options. The fourth change is to avoid regressions for code like struct A { int f, g; A () { f = g; // { dg-warning "g. is used uninitialized" } } } a; where with -flifetime-dse=2 -Wuninitialized we were able to warn about bugs like this because of the *this ={v} {CLOBBER(bob)}; statements at the start of the constructors, but with their removal wouldn't warn about it. Instead we now add a magic "clobber *this" attribute to the this PARM_DECL and use it in -W*uninitialized handling only as an implicit *this ={v} {CLOBBER(bob)}; at the start of the function. If a function is inlined, this disappears, but that shouldn't be a problem, either it is inlined into another constructor and that should have "clobber *this" for its this argument or it is inlined into whole object construction spot and there should be an explicit {CLOBBER(bob)} for the variable or temporary object. The fifth change is adding [[indeterminate]] attribute support and using it to avoid .DEFERRED_INIT calls (like [[gnu::uninitialized]] is handled). Some regressions caused by this patch had bugs filed (but for cases where those already didn't work before with explicit -ftrivial-auto-var-init=zero), those have been xfailed for now. See PR121975 and PR122044. 2025-10-04 Jakub Jelinek <jakub@redhat.com> PR c++/114457 gcc/ * flag-types.h (enum auto_init_type): Add AUTO_INIT_CXX26. * tree.h (VACUOUS_INIT_LABEL_P): Define. * gimplify.cc (is_var_need_auto_init): Renamed to ... (var_needs_auto_init_p): ... this. Don't return true for vars with "indeterminate" attribute. Formatting fixes. (gimplify_decl_expr): Use var_needs_auto_init_p instead of is_var_need_auto_init. (emit_warn_switch_unreachable): Remove the flag_auto_var_init special cases. (warn_switch_unreachable_and_auto_init_r): Handle them here by doing just returning NULL. (last_stmt_in_scope): Don't skip just debug stmts to find the last stmt in seq, skip for flag_auto_var_init > AUTO_INIT_UNINITIALIZED also IFN_DEFERRED_INIT calls. (collect_fallthrough_labels): For flag_auto_var_init > AUTO_INIT_UNINITIALIZED ignore IFN_DEFERRED_INIT calls and GIMPLE_GOTOs to VACUOUS_INIT_LABEL_P. (should_warn_for_implicit_fallthrough): For flag_auto_var_init > AUTO_INIT_UNINITIALIZED also skip over IFN_DEFERRED_INIT calls. (expand_FALLTHROUGH_r): Likewise, and handle GIMPLE_GOTOs to VACUOUS_INIT_LABEL_P. (gimplify_init_constructor): Use var_needs_auto_init_p instead of is_var_need_auto_init and for flag_auto_var_init AUTO_INIT_CXX26 don't call gimple_add_padding_init_for_auto_var. (gimplify_target_expr): If var_needs_auto_init_p and init has void type, call gimple_add_init_for_auto_var and for AUTO_INIT_PATTERN also gimple_add_padding_init_for_auto_var. * tree-ssa-uninit.cc (maybe_warn_operand): Handle loads from *this at the start of the function with "clobber *this" attribute on the PARM_DECL. * ipa-split.cc (split_function): Remove "clobber *this" attribute from the first PARM_DECL (if any). * doc/invoke.texi (ftrivial-auto-var-init=): Adjust documentation. gcc/c-family/ * c-opts.cc (c_common_post_options): For C++26 set flag_auto_var_init to AUTO_INIT_CXX26 if not specified explicitly. For C++ disable warn_trivial_auto_var_init. gcc/cp/ * cp-tree.h: Implement C++26 P2795R5 - Erroneous behavior for uninitialized reads. (IF_STMT_VACUOUS_INIT_P): Define. (check_goto): Change argument type from tree to tree *. * call.cc (build_over_call): Add indeterminate attribute to TARGET_EXPR slots for indeterminate parameters. * constexpr.cc (cxx_eval_internal_function): Handle IFN_DEFERRED_INIT. (cxx_eval_store_expression): Temporarily work around PR121965 bug. * cp-gimplify.cc (genericize_if_stmt): Handle IF_STMT_VACUOUS_INIT_P. (maybe_emit_clobber_object_begin): New function. (cp_gimplify_expr): Call it for DECL_EXPRs and TARGET_EXPRs with void type non-NULL TARGET_EXPR_INITIAL. * decl.cc (struct named_label_fwd_direct_goto, struct named_label_bck_direct_goto): New types. (struct named_label_use_entry): Add direct_goto member. Formatting fix. (struct named_label_entry): Add direct_goto member. Turn bool members into bool : 1. Add has_bad_decls bitfield. (adjust_backward_gotos): New function. (pop_labels): For flag_auto_var_init > AUTO_INIT_UNINITIALIZED call adjust_backward_gotos if needed. (poplevel_named_label_1): For decl_jump_unsafe also set ent->has_bad_decls, and for decl_instrument_init_bypass_p decls push them into ent->bad_decls vector too. (duplicate_decls): Complain if indeterminate attribute on function parameter isn't present on the first function declaration. (decl_instrument_init_bypass_p): New function. (build_deferred_init_call): Likewise. (maybe_add_deferred_init_calls): Likewise. (adjust_backward_goto): Likewise. (check_previous_goto_1): Add direct_goto and case_label arguments. For decl_instrument_init_bypass_p decls seen if direct_goto || case_label move case label if needed, call maybe_add_deferred_init_calls and adjust GOTO_EXPR operands remembered in direct_goto. Change return type from bool to int, return 0 on error, 1 for success with no need to adjust vacuous inits and 2 for success with need to adjust those. (check_previous_goto): Adjust check_previous_goto_1 call, vec_free direct_goto vector. (check_switch_goto): Add case_label argument, adjust check_previous_goto_1 call. Change return type from bool to int. (check_goto_1): Remove computed argument, add declp argument. Don't reuse previous ent->uses if ent->binding_level != current_binding_level. Push declp into direct_goto vectors if needed. (check_goto): Remove decl argument, add declp argument. Adjust check_goto_1 calls. (finish_case_label): Call check_switch_goto up to twice, first time to detect errors and find out if second call will be needed, and after c_add_case_label second time if needed. In the first case pass NULL_TREE as new argument to it, in the second case r. (start_preparsed_function): Don't emit CLOBBER_OBJECT_BEGIN here for -flifetime-dse=2, instead add "clobber *this" attribute to current_class_ptr. * parser.cc (cp_parser_asm_label_list): Call check_goto only after the TREE_LIST is created and pass address of its TREE_VALUE to it instead of the label. * semantics.cc (finish_goto_stmt): Call check_goto only after build_stmt has been called and pass it address of its first operand rather than destination. * tree.cc (handle_indeterminate_attribute): New function. (cxx_gnu_attributes): Add entry for indeterminate attribute. gcc/testsuite/ * g++.dg/cpp1y/vla-initlist1.C: Remove dg-skip-if for powerpc. Initialize i to 43 for ctor from initializer_list and expect value 43 instead of 42. * g++.dg/cpp26/attr-indeterminate1.C: New test. * g++.dg/cpp26/attr-indeterminate2.C: New test. * g++.dg/cpp26/attr-indeterminate3.C: New test. * g++.dg/cpp26/attr-indeterminate4.C: New test. * g++.dg/cpp26/erroneous1.C: New test. * g++.dg/cpp26/erroneous2.C: New test. * g++.dg/cpp26/erroneous3.C: New test. * g++.dg/cpp26/erroneous4.C: New test. * g++.dg/opt/store-merging-1.C: Add -ftrivial-auto-var-init=uninitialized to dg-options. * g++.dg/uninit-pred-loop-1_b.C: Expect a warning for C++26. * g++.dg/warn/Wuninitialized-13.C: Expect warning on a different line. * c-c++-common/ubsan/vla-1.c: Add -ftrivial-auto-var-init=uninitialized to dg-options. * c-c++-common/uninit-17.c: For c++26 expect warning on a different line. * g++.dg/warn/Warray-bounds-20.C: Expect warning on a different line. * c-c++-common/analyzer/invalid-shift-1.c: Xfail for c++26 until PR122044 is fixed. * g++.dg/analyzer/exception-value-2.C: Skip for c++26 until PR122044 is fixed. * c-c++-common/goacc-gomp/nesting-1.c: Skip for c++26 until PR121975 is fixed. * c-c++-common/goacc/kernels-decompose-2.c: Likewise. * c-c++-common/goacc/kernels-decompose-pr100400-1-1.c: Likewise. * c-c++-common/goacc/kernels-decompose-pr100400-1-3.c: Likewise. * c-c++-common/goacc/kernels-decompose-pr104061-1-1.c: Likewise. * c-c++-common/goacc/kernels-decompose-pr104061-1-3.c: Likewise. * c-c++-common/goacc/kernels-decompose-pr104061-1-4.c: Likewise. * c-c++-common/goacc/kernels-decompose-pr104132-1.c: Likewise. * c-c++-common/goacc/kernels-decompose-pr104133-1.c: Likewise. * c-c++-common/goacc/kernels-decompose-pr104774-1.c: Likewise. * c-c++-common/goacc/mdc-1.c: Likewise.
Implemented for GCC 16.
After this patch, we now are two XPASS: XPASS: c-c++-common/goacc/kernels-decompose-pr100280-1.c -std=c++26 TODO location at line 17 (test for bogus messages, line 10) XPASS: c-c++-common/goacc/kernels-decompose-pr100280-1.c -std=c++26 TODO at line 18 (test for warnings, line 19) they are XFAIL with -std=c++17 and -std=c++98 do we bother?
The trunk branch has been updated by Thomas Schwinge <tschwinge@gcc.gnu.org>: https://gcc.gnu.org/g:6173169792d7f59c14960792a504be6d00b943eb commit r16-4519-g6173169792d7f59c14960792a504be6d00b943eb Author: Thomas Schwinge <tschwinge@baylibre.com> Date: Mon Oct 20 17:36:49 2025 +0200 c++, gimplify: Implement C++26 P2795R5 - Erroneous behavior for uninitialized reads: Adjust 'c-c++-common/goacc/kernels-decompose-pr100280-1.c' [PR114457] With commit r16-4212-gf256a13f8aed833fe964a2ba541b7b30ad9b4a76 "c++, gimplify: Implement C++26 P2795R5 - Erroneous behavior for uninitialized reads [PR114457]", we acquired: @@ -181180,8 +184423,8 @@ PASS: c-c++-common/goacc/kernels-decompose-pr100280-1.c -std=c++26 at line 14 PASS: c-c++-common/goacc/kernels-decompose-pr100280-1.c -std=c++26 at line 15 (test for warnings, line 12) PASS: c-c++-common/goacc/kernels-decompose-pr100280-1.c -std=c++26 at line 16 (test for warnings, line 12) PASS: c-c++-common/goacc/kernels-decompose-pr100280-1.c -std=c++26 (test for excess errors) [-XFAIL:-]{+XPASS:+} c-c++-common/goacc/kernels-decompose-pr100280-1.c -std=c++26 TODO at line 18 (test for warnings, line 19) [-XFAIL:-]{+XPASS:+} c-c++-common/goacc/kernels-decompose-pr100280-1.c -std=c++26 TODO location at line 17 (test for bogus messages, line 10) As in other OpenACC 'kernels' test cases, the underlying issue again is PR121975 "Various goacc failures with -ftrivial-auto-var-init=zero" (to be resolved later on). PR c++/114457 gcc/testsuite/ * c-c++-common/goacc/kernels-decompose-pr100280-1.c: Skip for c++26 until PR121975 is fixed.
The trunk branch has been updated by Thomas Schwinge <tschwinge@gcc.gnu.org>: https://gcc.gnu.org/g:651df6b43e817a8e4b87eed9f338ea227da7bccb commit r16-4520-g651df6b43e817a8e4b87eed9f338ea227da7bccb Author: Thomas Schwinge <tschwinge@baylibre.com> Date: Mon Oct 20 17:36:49 2025 +0200 c++, gimplify: Implement C++26 P2795R5 - Erroneous behavior for uninitialized reads: Adjust 'libgomp.c++/{target-flex-101.C,target-std__flat_map-concurrent.C,target-std__flat_multimap-concurrent.C}' [PR114457, PR122268, PR120450] With commit r16-4212-gf256a13f8aed833fe964a2ba541b7b30ad9b4a76 "c++, gimplify: Implement C++26 P2795R5 - Erroneous behavior for uninitialized reads [PR114457]", we acquired: {+FAIL: libgomp.c++/target-flex-101.C (internal compiler error: in assign_temp, at function.cc:990)+} [-PASS:-]{+FAIL:+} libgomp.c++/target-flex-101.C (test for excess errors) [-PASS:-]{+UNRESOLVED:+} libgomp.c++/target-flex-101.C [-execution test-]{+compilation failed to produce executable+} ... for GCN, nvptx offloading compilation, and on the other hand: [-XFAIL:-]{+XPASS:+} libgomp.c++/target-std__flat_map-concurrent.C (internal compiler error[-: in assign_temp, at function.cc:990)-] [-XFAIL:-]{+XPASS:+} libgomp.c++/target-std__flat_map-concurrent.C (test for excess errors) [-UNRESOLVED:-]{+PASS:+} libgomp.c++/target-std__flat_map-concurrent.C [-compilation failed to produce executable-]{+execution test+} [-XFAIL:-]{+XPASS:+} libgomp.c++/target-std__flat_multimap-concurrent.C (internal compiler error[-: in assign_temp, at function.cc:990)-] [-XFAIL:-]{+XPASS:+} libgomp.c++/target-std__flat_multimap-concurrent.C (test for excess errors) [-UNRESOLVED:-]{+PASS:+} libgomp.c++/target-std__flat_multimap-concurrent.C [-compilation failed to produce executable-]{+execution test+} ... for GCN offloading compilation (already PASSed for nvptx). Note that these test cases explicitly use '-std=c++23', so don't undergo the new C++26 P2795R5 functionality. Yet, comparing before vs. after that commit, in the 'gimple' dumps (that is, early host compilation), there are a lot of changes where 'gimple_assign <constructor, [...], {CLOBBER(bob)}, NULL, NULL>'s and relatedly 'gimple_bind's newly appear/no longer appear elsewhere. This leads to correspondingly different code at the beginning of offloading compilation. Why/how that now ('libgomp.c++/target-flex-101.C') vs. before ('libgomp.c++/{target-std__flat_map-concurrent.C,target-std__flat_multimap-concurrent.C}') translates into 'expand' ICEs, I can't tell. PR c++/114457 PR c++/122268 PR c++/120450 libgomp/ * testsuite/libgomp.c++/target-flex-101.C: XFAIL GCN, nvptx offloading compilation. * testsuite/libgomp.c++/target-std__flat_map-concurrent.C: Un-XFAIL GCN offloading compilation. * testsuite/libgomp.c++/target-std__flat_multimap-concurrent.C: Likewise.