https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114457
--- Comment #8 from GCC Commits <cvs-commit at gcc dot gnu.org> --- The master branch has been updated by Jakub Jelinek <[email protected]>: https://gcc.gnu.org/g:f256a13f8aed833fe964a2ba541b7b30ad9b4a76 commit r16-4212-gf256a13f8aed833fe964a2ba541b7b30ad9b4a76 Author: Jakub Jelinek <[email protected]> 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 <[email protected]> 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.
