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.