[RFC] c++, gimplify: Implement C++26 P2795R5 - Erroneous behavior for uninitialized reads [PR114457]
Commit Message
Hi!
Here is my current version of the C++26 P2795R5 support.
This 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.
-ftrivial-auto-var-init= also calls .DEFERRED_INIT for temporaries
if they have VOID_TYPE_P TARGET_EXPR_INITIAL (otherwise they are expanded
as INIT_EXPR of the TARGET_EXPR_SLOT and TARGET_EXPR_INITIAL and so
everything is initialized; trying to emit .DEFERRED_INIT in such a case
breaks a lot of stuff because e.g. const vars turned into static but
not static when .DEFERRED_INIT is done will try to write into .rodata).
-std=c++26 implies -ftrivial-auto-var-init= mode that doesn't have an
explicit user option and is like -ftrivial-auto-var-init=zero except
__builtin_clear_padding calls aren't added (C++26 doesn't make reads from
padding erroneous). User can still override it in both directions,
so -ftrivial-auto-var-init={zero,pattern} will behave like before (except
for the gotos/switches working for C++ right and temporaries being also
initialized), and -ftrivial-auto-var-init=uninitialized allows to get
C++23-ish behavior with other C++26 features enabled (if performance
is more important than some sanity of erroneous code).
The -flifetime-dse=2 {CLOBBER(bob)} clobbers aren't emitted anymore
at the start of (some) constructors but instead when gimplifying DECL_EXPRs
and TARGET_EXPRs. Haven't done it in gimplify.cc so that it roughly matches
previous behavior (C++ only, not all languages, and adding bob CLOBBERs
only on types which used them inside ctors for now (doing more breaks a lot
of stuff in the testsuite).
I understood you have a patch for {CLOBBER(bob)} for new expressions, there
are some regressions without that, see below.
There are some regressions caused by the removal of {CLOBBER(bob)}
clobbers from the start of certain constructors, e.g. one testcase has
struct A
{
int f,g;
A() { f = g; // { dg-warning "g. is used uninitialized" } }
} a;
and this doesn't warn anymore for any C++ mode. Wonder what we could
do for the warnings, for code generation we have to assume the code
can read members in *this (for C++26 read there .DEFERRED_INIT, for
older C++ versions with -flifetime-dse=2 {CLOBBER(bob)}).
Perhaps we should add some artificial attribute to the functions we'd
add the -flifetime-dse=2 clobber to and treat *this reaching the start
of the function for those specially in the uninit pass?
Also there is one ICEs and various FAILs which reproduce even without
this patch with -ftrivial-auto-var-init=zero, I think I could temporarily
make those tests -ftrivial-auto-var-init=uninitialized until it is resolved.
Some tests got that already in the patch (e.g. ubsan/vla-1.c is testing UB
and will crash if one tries to write something into the -1 sized VLAs,
which -ftrivial-auto-var-init=zero or -std=c++26 do).
So far only tested with
GXX_TESTSUITE_STDS=98,11,14,17,20,23,26 make check-c++
and make check in libstdc++-v3.
The regressions (categorized) are below.
Your thoughts on this?
I hope the following would be fixed by your clobber new patch:
FAIL: g++.dg/opt/flifetime-dse2.C -std=gnu++11 execution test
FAIL: g++.dg/opt/flifetime-dse2.C -std=gnu++14 execution test
FAIL: g++.dg/opt/flifetime-dse2.C -std=gnu++17 execution test
FAIL: g++.dg/opt/flifetime-dse2.C -std=gnu++20 execution test
FAIL: g++.dg/opt/flifetime-dse2.C -std=gnu++23 execution test
FAIL: g++.dg/opt/flifetime-dse2.C -std=gnu++26 execution test
FAIL: g++.dg/opt/flifetime-dse2.C -std=gnu++98 execution test
FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++11 brace (test for warnings, line 29)
FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++11 LP64 note (test for warnings, line 38)
FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++11 note (test for warnings, line 48)
FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++14 brace (test for warnings, line 29)
FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++14 LP64 note (test for warnings, line 38)
FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++14 note (test for warnings, line 48)
FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++17 brace (test for warnings, line 29)
FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++17 LP64 note (test for warnings, line 38)
FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++17 note (test for warnings, line 48)
FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++20 brace (test for warnings, line 29)
FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++20 LP64 note (test for warnings, line 38)
FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++20 note (test for warnings, line 48)
FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++23 brace (test for warnings, line 29)
FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++23 LP64 note (test for warnings, line 38)
FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++23 note (test for warnings, line 48)
FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++26 brace (test for warnings, line 29)
FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++26 LP64 note (test for warnings, line 38)
FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++26 note (test for warnings, line 48)
FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++98 brace (test for warnings, line 29)
FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++98 LP64 note (test for warnings, line 38)
FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++98 note (test for warnings, line 48)
Dunno what to do with these, see above.
FAIL: g++.dg/warn/Wuninitialized-10.C -std=gnu++11 (test for warnings, line 10)
FAIL: g++.dg/warn/Wuninitialized-10.C -std=gnu++14 (test for warnings, line 10)
FAIL: g++.dg/warn/Wuninitialized-10.C -std=gnu++17 (test for warnings, line 10)
FAIL: g++.dg/warn/Wuninitialized-10.C -std=gnu++20 (test for warnings, line 10)
FAIL: g++.dg/warn/Wuninitialized-10.C -std=gnu++23 (test for warnings, line 10)
FAIL: g++.dg/warn/Wuninitialized-10.C -std=gnu++26 (test for warnings, line 10)
FAIL: g++.dg/warn/Wuninitialized-10.C -std=gnu++98 (test for warnings, line 10)
FAIL: g++.dg/warn/Wuninitialized-pr111123-1.C -std=gnu++11 (test for warnings, line 14)
FAIL: g++.dg/warn/Wuninitialized-pr111123-1.C -std=gnu++14 (test for warnings, line 14)
FAIL: g++.dg/warn/Wuninitialized-pr111123-1.C -std=gnu++17 (test for warnings, line 14)
FAIL: g++.dg/warn/Wuninitialized-pr111123-1.C -std=gnu++20 (test for warnings, line 14)
FAIL: g++.dg/warn/Wuninitialized-pr111123-1.C -std=gnu++23 (test for warnings, line 14)
FAIL: g++.dg/warn/Wuninitialized-pr111123-1.C -std=gnu++26 (test for warnings, line 14)
PR121975 - perhaps I can just make those effective target c++23_down temporarily with a comment
FAIL: c-c++-common/goacc-gomp/nesting-1.c -std=c++26 at line 30 (test for warnings, line 31)
FAIL: c-c++-common/goacc-gomp/nesting-1.c -std=c++26 (test for excess errors)
FAIL: c-c++-common/goacc/kernels-decompose-2.c -std=c++26 at line 76 (test for warnings, line 75)
FAIL: c-c++-common/goacc/kernels-decompose-2.c -std=c++26 (test for excess errors)
FAIL: c-c++-common/goacc/kernels-decompose-pr100400-1-1.c -std=c++26 at line 23 (test for warnings, line 24)
FAIL: c-c++-common/goacc/kernels-decompose-pr100400-1-1.c -std=c++26 at line 28 (test for warnings, line 27)
FAIL: c-c++-common/goacc/kernels-decompose-pr100400-1-1.c -std=c++26 (test for excess errors)
FAIL: c-c++-common/goacc/kernels-decompose-pr100400-1-3.c -std=c++26 at line 32 (test for warnings, line 33)
FAIL: c-c++-common/goacc/kernels-decompose-pr100400-1-3.c -std=c++26 at line 37 (test for warnings, line 36)
FAIL: c-c++-common/goacc/kernels-decompose-pr100400-1-3.c -std=c++26 (test for excess errors)
FAIL: c-c++-common/goacc/kernels-decompose-pr100400-1-3.c -std=c++26 w/ debug at line 29 (test for bogus messages, line 30)
FAIL: c-c++-common/goacc/kernels-decompose-pr104061-1-1.c -std=c++26 at line 26 (test for warnings, line 25)
FAIL: c-c++-common/goacc/kernels-decompose-pr104061-1-1.c -std=c++26 (test for excess errors)
FAIL: c-c++-common/goacc/kernels-decompose-pr104061-1-3.c -std=c++26 at line 33 (test for warnings, line 32)
FAIL: c-c++-common/goacc/kernels-decompose-pr104061-1-3.c -std=c++26 w/ debug at line 28 (test for bogus messages, line 29)
FAIL: c-c++-common/goacc/kernels-decompose-pr104061-1-4.c -std=c++26 at line 33 (test for warnings, line 32)
FAIL: c-c++-common/goacc/kernels-decompose-pr104061-1-4.c -std=c++26 w/ debug at line 28 (test for bogus messages, line 29)
FAIL: c-c++-common/goacc/kernels-decompose-pr104132-1.c -std=c++26 (test for excess errors)
FAIL: c-c++-common/goacc/kernels-decompose-pr104133-1.c -std=c++26 (test for excess errors)
FAIL: c-c++-common/goacc/kernels-decompose-pr104774-1.c -std=c++26 (test for excess errors)
FAIL: c-c++-common/goacc/mdc-1.c -std=c++26 scan-tree-dump-times omplower "pragma omp target oacc_data map.tofrom:.z .len: 40.. map.struct:s .len: 1.. map.alloc:s.a .len: 8.. map.tofrom:._1 .len: 40.. map.attach:s.a .bias: 0.." 1
PR121977 - I'd like to fix this, but ideally separately from this patch, so perhaps also c++23_down
FAIL: g++.dg/gomp/member-1.C -std=c++26 (internal compiler error: Segmentation fault)
FAIL: g++.dg/gomp/member-1.C -std=c++26 (test for excess errors)
Dunno about these analyzer issues, will probably need David for that
FAIL: c-c++-common/analyzer/invalid-shift-1.c -std=c++26 (test for warnings, line 15)
FAIL: c-c++-common/analyzer/invalid-shift-1.c -std=c++26 (test for warnings, line 18)
FAIL: g++.dg/analyzer/exception-value-2.C -std=c++26 (test for excess errors)
FAIL: g++.dg/analyzer/exception-value-2.C -std=c++26 (test for warnings, line 34)
FAIL: g++.dg/analyzer/exception-value-2.C -std=c++26 (test for warnings, line 35)
2025-09-17 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.
* 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.
* 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.
Jakub
Comments
On 9/17/25 6:08 PM, Jakub Jelinek wrote:
> Hi!
>
> Here is my current version of the C++26 P2795R5 support.
>
> This 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.
>
> -ftrivial-auto-var-init= also calls .DEFERRED_INIT for temporaries
> if they have VOID_TYPE_P TARGET_EXPR_INITIAL (otherwise they are expanded
> as INIT_EXPR of the TARGET_EXPR_SLOT and TARGET_EXPR_INITIAL and so
> everything is initialized; trying to emit .DEFERRED_INIT in such a case
> breaks a lot of stuff because e.g. const vars turned into static but
> not static when .DEFERRED_INIT is done will try to write into .rodata).
>
> -std=c++26 implies -ftrivial-auto-var-init= mode that doesn't have an
> explicit user option and is like -ftrivial-auto-var-init=zero except
> __builtin_clear_padding calls aren't added (C++26 doesn't make reads from
> padding erroneous). User can still override it in both directions,
> so -ftrivial-auto-var-init={zero,pattern} will behave like before (except
> for the gotos/switches working for C++ right and temporaries being also
> initialized), and -ftrivial-auto-var-init=uninitialized allows to get
> C++23-ish behavior with other C++26 features enabled (if performance
> is more important than some sanity of erroneous code).
>
> The -flifetime-dse=2 {CLOBBER(bob)} clobbers aren't emitted anymore
> at the start of (some) constructors but instead when gimplifying DECL_EXPRs
> and TARGET_EXPRs. Haven't done it in gimplify.cc so that it roughly matches
> previous behavior (C++ only, not all languages, and adding bob CLOBBERs
> only on types which used them inside ctors for now (doing more breaks a lot
> of stuff in the testsuite).
>
> I understood you have a patch for {CLOBBER(bob)} for new expressions, there
> are some regressions without that, see below.
Yes, my patches address those. It also runs into analyzer problems.
> There are some regressions caused by the removal of {CLOBBER(bob)}
> clobbers from the start of certain constructors, e.g. one testcase has
> struct A
> {
> int f,g;
> A() { f = g; // { dg-warning "g. is used uninitialized" } }
> } a;
> and this doesn't warn anymore for any C++ mode. Wonder what we could
> do for the warnings, for code generation we have to assume the code
> can read members in *this (for C++26 read there .DEFERRED_INIT, for
> older C++ versions with -flifetime-dse=2 {CLOBBER(bob)}).
> Perhaps we should add some artificial attribute to the functions we'd
> add the -flifetime-dse=2 clobber to and treat *this reaching the start
> of the function for those specially in the uninit pass?
Or some way to indicate in GIMPLE that whatever value is there, it's
poisoned?
> Also there is one ICEs and various FAILs which reproduce even without
> this patch with -ftrivial-auto-var-init=zero, I think I could temporarily
> make those tests -ftrivial-auto-var-init=uninitialized until it is resolved.
> Some tests got that already in the patch (e.g. ubsan/vla-1.c is testing UB
> and will crash if one tries to write something into the -1 sized VLAs,
> which -ftrivial-auto-var-init=zero or -std=c++26 do).
Makes sense.
> So far only tested with
> GXX_TESTSUITE_STDS=98,11,14,17,20,23,26 make check-c++
> and make check in libstdc++-v3.
>
> The regressions (categorized) are below.
> Your thoughts on this?
>
> I hope the following would be fixed by your clobber new patch:
> FAIL: g++.dg/opt/flifetime-dse2.C -std=gnu++11 execution test
> FAIL: g++.dg/opt/flifetime-dse2.C -std=gnu++14 execution test
> FAIL: g++.dg/opt/flifetime-dse2.C -std=gnu++17 execution test
> FAIL: g++.dg/opt/flifetime-dse2.C -std=gnu++20 execution test
> FAIL: g++.dg/opt/flifetime-dse2.C -std=gnu++23 execution test
> FAIL: g++.dg/opt/flifetime-dse2.C -std=gnu++26 execution test
> FAIL: g++.dg/opt/flifetime-dse2.C -std=gnu++98 execution test
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++11 brace (test for warnings, line 29)
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++11 LP64 note (test for warnings, line 38)
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++11 note (test for warnings, line 48)
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++14 brace (test for warnings, line 29)
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++14 LP64 note (test for warnings, line 38)
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++14 note (test for warnings, line 48)
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++17 brace (test for warnings, line 29)
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++17 LP64 note (test for warnings, line 38)
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++17 note (test for warnings, line 48)
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++20 brace (test for warnings, line 29)
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++20 LP64 note (test for warnings, line 38)
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++20 note (test for warnings, line 48)
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++23 brace (test for warnings, line 29)
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++23 LP64 note (test for warnings, line 38)
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++23 note (test for warnings, line 48)
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++26 brace (test for warnings, line 29)
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++26 LP64 note (test for warnings, line 38)
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++26 note (test for warnings, line 48)
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++98 brace (test for warnings, line 29)
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++98 LP64 note (test for warnings, line 38)
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++98 note (test for warnings, line 48)
>
> Dunno what to do with these, see above.
> FAIL: g++.dg/warn/Wuninitialized-10.C -std=gnu++11 (test for warnings, line 10)
> FAIL: g++.dg/warn/Wuninitialized-10.C -std=gnu++14 (test for warnings, line 10)
> FAIL: g++.dg/warn/Wuninitialized-10.C -std=gnu++17 (test for warnings, line 10)
> FAIL: g++.dg/warn/Wuninitialized-10.C -std=gnu++20 (test for warnings, line 10)
> FAIL: g++.dg/warn/Wuninitialized-10.C -std=gnu++23 (test for warnings, line 10)
> FAIL: g++.dg/warn/Wuninitialized-10.C -std=gnu++26 (test for warnings, line 10)
> FAIL: g++.dg/warn/Wuninitialized-10.C -std=gnu++98 (test for warnings, line 10)
> FAIL: g++.dg/warn/Wuninitialized-pr111123-1.C -std=gnu++11 (test for warnings, line 14)
> FAIL: g++.dg/warn/Wuninitialized-pr111123-1.C -std=gnu++14 (test for warnings, line 14)
> FAIL: g++.dg/warn/Wuninitialized-pr111123-1.C -std=gnu++17 (test for warnings, line 14)
> FAIL: g++.dg/warn/Wuninitialized-pr111123-1.C -std=gnu++20 (test for warnings, line 14)
> FAIL: g++.dg/warn/Wuninitialized-pr111123-1.C -std=gnu++23 (test for warnings, line 14)
> FAIL: g++.dg/warn/Wuninitialized-pr111123-1.C -std=gnu++26 (test for warnings, line 14)
>
> PR121975 - perhaps I can just make those effective target c++23_down temporarily with a comment
> FAIL: c-c++-common/goacc-gomp/nesting-1.c -std=c++26 at line 30 (test for warnings, line 31)
> FAIL: c-c++-common/goacc-gomp/nesting-1.c -std=c++26 (test for excess errors)
> FAIL: c-c++-common/goacc/kernels-decompose-2.c -std=c++26 at line 76 (test for warnings, line 75)
> FAIL: c-c++-common/goacc/kernels-decompose-2.c -std=c++26 (test for excess errors)
> FAIL: c-c++-common/goacc/kernels-decompose-pr100400-1-1.c -std=c++26 at line 23 (test for warnings, line 24)
> FAIL: c-c++-common/goacc/kernels-decompose-pr100400-1-1.c -std=c++26 at line 28 (test for warnings, line 27)
> FAIL: c-c++-common/goacc/kernels-decompose-pr100400-1-1.c -std=c++26 (test for excess errors)
> FAIL: c-c++-common/goacc/kernels-decompose-pr100400-1-3.c -std=c++26 at line 32 (test for warnings, line 33)
> FAIL: c-c++-common/goacc/kernels-decompose-pr100400-1-3.c -std=c++26 at line 37 (test for warnings, line 36)
> FAIL: c-c++-common/goacc/kernels-decompose-pr100400-1-3.c -std=c++26 (test for excess errors)
> FAIL: c-c++-common/goacc/kernels-decompose-pr100400-1-3.c -std=c++26 w/ debug at line 29 (test for bogus messages, line 30)
> FAIL: c-c++-common/goacc/kernels-decompose-pr104061-1-1.c -std=c++26 at line 26 (test for warnings, line 25)
> FAIL: c-c++-common/goacc/kernels-decompose-pr104061-1-1.c -std=c++26 (test for excess errors)
> FAIL: c-c++-common/goacc/kernels-decompose-pr104061-1-3.c -std=c++26 at line 33 (test for warnings, line 32)
> FAIL: c-c++-common/goacc/kernels-decompose-pr104061-1-3.c -std=c++26 w/ debug at line 28 (test for bogus messages, line 29)
> FAIL: c-c++-common/goacc/kernels-decompose-pr104061-1-4.c -std=c++26 at line 33 (test for warnings, line 32)
> FAIL: c-c++-common/goacc/kernels-decompose-pr104061-1-4.c -std=c++26 w/ debug at line 28 (test for bogus messages, line 29)
> FAIL: c-c++-common/goacc/kernels-decompose-pr104132-1.c -std=c++26 (test for excess errors)
> FAIL: c-c++-common/goacc/kernels-decompose-pr104133-1.c -std=c++26 (test for excess errors)
> FAIL: c-c++-common/goacc/kernels-decompose-pr104774-1.c -std=c++26 (test for excess errors)
> FAIL: c-c++-common/goacc/mdc-1.c -std=c++26 scan-tree-dump-times omplower "pragma omp target oacc_data map.tofrom:.z .len: 40.. map.struct:s .len: 1.. map.alloc:s.a .len: 8.. map.tofrom:._1 .len: 40.. map.attach:s.a .bias: 0.." 1
>
> PR121977 - I'd like to fix this, but ideally separately from this patch, so perhaps also c++23_down
> FAIL: g++.dg/gomp/member-1.C -std=c++26 (internal compiler error: Segmentation fault)
> FAIL: g++.dg/gomp/member-1.C -std=c++26 (test for excess errors)
>
> Dunno about these analyzer issues, will probably need David for that
> FAIL: c-c++-common/analyzer/invalid-shift-1.c -std=c++26 (test for warnings, line 15)
> FAIL: c-c++-common/analyzer/invalid-shift-1.c -std=c++26 (test for warnings, line 18)
> FAIL: g++.dg/analyzer/exception-value-2.C -std=c++26 (test for excess errors)
> FAIL: g++.dg/analyzer/exception-value-2.C -std=c++26 (test for warnings, line 34)
> FAIL: g++.dg/analyzer/exception-value-2.C -std=c++26 (test for warnings, line 35)
With my patch I also see
> FAIL: g++.dg/analyzer/new-2.C -std=c++26 (test for excess errors)
> FAIL: g++.dg/analyzer/new-2.C -std=c++26 (test for warnings, line 62)
> FAIL: g++.dg/analyzer/new-2.C -std=c++26 (test for warnings, line 64)
> FAIL: g++.dg/analyzer/noexcept-new.C -std=c++26 at line 43 (test for warnings, line 42)
> FAIL: g++.dg/analyzer/noexcept-new.C -std=c++26 (test for excess errors)
> FAIL: g++.dg/analyzer/noexcept-new.C -std=c++26 (test for warnings, line 20)
> FAIL: g++.dg/analyzer/noexcept-new.C -std=c++26 (test for warnings, line 22)
> FAIL: g++.dg/analyzer/noexcept-new.C -std=c++26 (test for warnings, line 40)
> FAIL: g++.dg/analyzer/noexcept-new.C -std=c++26 (test for warnings, line 42)
...because the analyzer gets confused by the array clobbers, and warns
about that, and then says "NULL" instead of "possibly-NULL".
I'll check in most of my work today, and just leave out the change to
actually enable the clobber on non-placement new until we have a plan
for the analyzer issues.
Jason
On Thu, Sep 18, 2025 at 06:54:06PM +0200, Jason Merrill wrote:
> > There are some regressions caused by the removal of {CLOBBER(bob)}
> > clobbers from the start of certain constructors, e.g. one testcase has
> > struct A
> > {
> > int f,g;
> > A() { f = g; // { dg-warning "g. is used uninitialized" } }
> > } a;
> > and this doesn't warn anymore for any C++ mode. Wonder what we could
> > do for the warnings, for code generation we have to assume the code
> > can read members in *this (for C++26 read there .DEFERRED_INIT, for
> > older C++ versions with -flifetime-dse=2 {CLOBBER(bob)}).
> > Perhaps we should add some artificial attribute to the functions we'd
> > add the -flifetime-dse=2 clobber to and treat *this reaching the start
> > of the function for those specially in the uninit pass?
>
> Or some way to indicate in GIMPLE that whatever value is there, it's
> poisoned?
Some new ifn which is say:
*this = .POISON (this);
or
*this = .POISON (*this);
would be an option (which would expand to nothing if the pointers are the
same), dunno what would be easier to implement though.
I'll try artificial attribute on the this PARM_DECL.
Richi, what is your preference from the middle-end POV?
Just a way to force -Wuninitialized/-Wmaybe-uninitialized warnings on
reads but similarly to .DEFERRED_INIT which sets to specific bytes
use whatever was in memory before.
Jakub
> On Sep 17, 2025, at 12:08, Jakub Jelinek <jakub@redhat.com> wrote:
>
> Hi!
>
> -ftrivial-auto-var-init= also calls .DEFERRED_INIT for temporaries
> if they have VOID_TYPE_P TARGET_EXPR_INITIAL (otherwise they are expanded
> as INIT_EXPR of the TARGET_EXPR_SLOT and TARGET_EXPR_INITIAL and so
> everything is initialized; trying to emit .DEFERRED_INIT in such a case
> breaks a lot of stuff because e.g. const vars turned into static but
> not static when .DEFERRED_INIT is done will try to write into .rodata).
A little confused with the above. Do you mean that not all temporaries are initialized to zero, only
the temporaries that have VOID_TYPE_P TARGET_EXPR_INITIAL are initialized to zero?
Should we initialize all temporaries?
If not, should we explain this in the documentation?
>
> -std=c++26 implies -ftrivial-auto-var-init= mode that doesn't have an
> explicit user option and is like -ftrivial-auto-var-init=zero except
> __builtin_clear_padding calls aren't added (C++26 doesn't make reads from
> padding erroneous). User can still override it in both directions,
> so -ftrivial-auto-var-init={zero,pattern} will behave like before (except
> for the gotos/switches working for C++ right and temporaries being also
> initialized), and -ftrivial-auto-var-init=uninitialized allows to get
> C++23-ish behavior with other C++26 features enabled (if performance
> is more important than some sanity of erroneous code).
In the above, “temporaries being also initialized”, so I assume that all temporaries should be initialized?
>
> The -flifetime-dse=2 {CLOBBER(bob)} clobbers aren't emitted anymore
> at the start of (some) constructors but instead when gimplifying DECL_EXPRs
> and TARGET_EXPRs. Haven't done it in gimplify.cc so that it roughly matches
> previous behavior (C++ only, not all languages, and adding bob CLOBBERs
> only on types which used them inside ctors for now (doing more breaks a lot
> of stuff in the testsuite).
>
> I understood you have a patch for {CLOBBER(bob)} for new expressions, there
> are some regressions without that, see below.
>
> There are some regressions caused by the removal of {CLOBBER(bob)}
> clobbers from the start of certain constructors, e.g. one testcase has
> struct A
> {
> int f,g;
> A() { f = g; // { dg-warning "g. is used uninitialized" } }
> } a;
> and this doesn't warn anymore for any C++ mode. Wonder what we could
> do for the warnings, for code generation we have to assume the code
> can read members in *this (for C++26 read there .DEFERRED_INIT, for
> older C++ versions with -flifetime-dse=2 {CLOBBER(bob)}).
> Perhaps we should add some artificial attribute to the functions we'd
> add the -flifetime-dse=2 clobber to and treat *this reaching the start
> of the function for those specially in the uninit pass?
>
> Also there is one ICEs and various FAILs which reproduce even without
> this patch with -ftrivial-auto-var-init=zero, I think I could temporarily
> make those tests -ftrivial-auto-var-init=uninitialized until it is resolved.
> Some tests got that already in the patch (e.g. ubsan/vla-1.c is testing UB
> and will crash if one tries to write something into the -1 sized VLAs,
> which -ftrivial-auto-var-init=zero or -std=c++26 do).
>
> So far only tested with
> GXX_TESTSUITE_STDS=98,11,14,17,20,23,26 make check-c++
> and make check in libstdc++-v3.
>
> The regressions (categorized) are below.
> Your thoughts on this?
>
> I hope the following would be fixed by your clobber new patch:
> FAIL: g++.dg/opt/flifetime-dse2.C -std=gnu++11 execution test
> FAIL: g++.dg/opt/flifetime-dse2.C -std=gnu++14 execution test
> FAIL: g++.dg/opt/flifetime-dse2.C -std=gnu++17 execution test
> FAIL: g++.dg/opt/flifetime-dse2.C -std=gnu++20 execution test
> FAIL: g++.dg/opt/flifetime-dse2.C -std=gnu++23 execution test
> FAIL: g++.dg/opt/flifetime-dse2.C -std=gnu++26 execution test
> FAIL: g++.dg/opt/flifetime-dse2.C -std=gnu++98 execution test
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++11 brace (test for warnings, line 29)
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++11 LP64 note (test for warnings, line 38)
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++11 note (test for warnings, line 48)
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++14 brace (test for warnings, line 29)
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++14 LP64 note (test for warnings, line 38)
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++14 note (test for warnings, line 48)
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++17 brace (test for warnings, line 29)
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++17 LP64 note (test for warnings, line 38)
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++17 note (test for warnings, line 48)
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++20 brace (test for warnings, line 29)
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++20 LP64 note (test for warnings, line 38)
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++20 note (test for warnings, line 48)
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++23 brace (test for warnings, line 29)
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++23 LP64 note (test for warnings, line 38)
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++23 note (test for warnings, line 48)
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++26 brace (test for warnings, line 29)
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++26 LP64 note (test for warnings, line 38)
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++26 note (test for warnings, line 48)
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++98 brace (test for warnings, line 29)
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++98 LP64 note (test for warnings, line 38)
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++98 note (test for warnings, line 48)
>
> Dunno what to do with these, see above.
> FAIL: g++.dg/warn/Wuninitialized-10.C -std=gnu++11 (test for warnings, line 10)
> FAIL: g++.dg/warn/Wuninitialized-10.C -std=gnu++14 (test for warnings, line 10)
> FAIL: g++.dg/warn/Wuninitialized-10.C -std=gnu++17 (test for warnings, line 10)
> FAIL: g++.dg/warn/Wuninitialized-10.C -std=gnu++20 (test for warnings, line 10)
> FAIL: g++.dg/warn/Wuninitialized-10.C -std=gnu++23 (test for warnings, line 10)
> FAIL: g++.dg/warn/Wuninitialized-10.C -std=gnu++26 (test for warnings, line 10)
> FAIL: g++.dg/warn/Wuninitialized-10.C -std=gnu++98 (test for warnings, line 10)
> FAIL: g++.dg/warn/Wuninitialized-pr111123-1.C -std=gnu++11 (test for warnings, line 14)
> FAIL: g++.dg/warn/Wuninitialized-pr111123-1.C -std=gnu++14 (test for warnings, line 14)
> FAIL: g++.dg/warn/Wuninitialized-pr111123-1.C -std=gnu++17 (test for warnings, line 14)
> FAIL: g++.dg/warn/Wuninitialized-pr111123-1.C -std=gnu++20 (test for warnings, line 14)
> FAIL: g++.dg/warn/Wuninitialized-pr111123-1.C -std=gnu++23 (test for warnings, line 14)
> FAIL: g++.dg/warn/Wuninitialized-pr111123-1.C -std=gnu++26 (test for warnings, line 14)
>
> PR121975 - perhaps I can just make those effective target c++23_down temporarily with a comment
> FAIL: c-c++-common/goacc-gomp/nesting-1.c -std=c++26 at line 30 (test for warnings, line 31)
> FAIL: c-c++-common/goacc-gomp/nesting-1.c -std=c++26 (test for excess errors)
> FAIL: c-c++-common/goacc/kernels-decompose-2.c -std=c++26 at line 76 (test for warnings, line 75)
> FAIL: c-c++-common/goacc/kernels-decompose-2.c -std=c++26 (test for excess errors)
> FAIL: c-c++-common/goacc/kernels-decompose-pr100400-1-1.c -std=c++26 at line 23 (test for warnings, line 24)
> FAIL: c-c++-common/goacc/kernels-decompose-pr100400-1-1.c -std=c++26 at line 28 (test for warnings, line 27)
> FAIL: c-c++-common/goacc/kernels-decompose-pr100400-1-1.c -std=c++26 (test for excess errors)
> FAIL: c-c++-common/goacc/kernels-decompose-pr100400-1-3.c -std=c++26 at line 32 (test for warnings, line 33)
> FAIL: c-c++-common/goacc/kernels-decompose-pr100400-1-3.c -std=c++26 at line 37 (test for warnings, line 36)
> FAIL: c-c++-common/goacc/kernels-decompose-pr100400-1-3.c -std=c++26 (test for excess errors)
> FAIL: c-c++-common/goacc/kernels-decompose-pr100400-1-3.c -std=c++26 w/ debug at line 29 (test for bogus messages, line 30)
> FAIL: c-c++-common/goacc/kernels-decompose-pr104061-1-1.c -std=c++26 at line 26 (test for warnings, line 25)
> FAIL: c-c++-common/goacc/kernels-decompose-pr104061-1-1.c -std=c++26 (test for excess errors)
> FAIL: c-c++-common/goacc/kernels-decompose-pr104061-1-3.c -std=c++26 at line 33 (test for warnings, line 32)
> FAIL: c-c++-common/goacc/kernels-decompose-pr104061-1-3.c -std=c++26 w/ debug at line 28 (test for bogus messages, line 29)
> FAIL: c-c++-common/goacc/kernels-decompose-pr104061-1-4.c -std=c++26 at line 33 (test for warnings, line 32)
> FAIL: c-c++-common/goacc/kernels-decompose-pr104061-1-4.c -std=c++26 w/ debug at line 28 (test for bogus messages, line 29)
> FAIL: c-c++-common/goacc/kernels-decompose-pr104132-1.c -std=c++26 (test for excess errors)
> FAIL: c-c++-common/goacc/kernels-decompose-pr104133-1.c -std=c++26 (test for excess errors)
> FAIL: c-c++-common/goacc/kernels-decompose-pr104774-1.c -std=c++26 (test for excess errors)
> FAIL: c-c++-common/goacc/mdc-1.c -std=c++26 scan-tree-dump-times omplower "pragma omp target oacc_data map.tofrom:.z .len: 40.. map.struct:s .len: 1.. map.alloc:s.a .len: 8.. map.tofrom:._1 .len: 40.. map.attach:s.a .bias: 0.." 1
>
> PR121977 - I'd like to fix this, but ideally separately from this patch, so perhaps also c++23_down
> FAIL: g++.dg/gomp/member-1.C -std=c++26 (internal compiler error: Segmentation fault)
> FAIL: g++.dg/gomp/member-1.C -std=c++26 (test for excess errors)
>
> Dunno about these analyzer issues, will probably need David for that
> FAIL: c-c++-common/analyzer/invalid-shift-1.c -std=c++26 (test for warnings, line 15)
> FAIL: c-c++-common/analyzer/invalid-shift-1.c -std=c++26 (test for warnings, line 18)
> FAIL: g++.dg/analyzer/exception-value-2.C -std=c++26 (test for excess errors)
> FAIL: g++.dg/analyzer/exception-value-2.C -std=c++26 (test for warnings, line 34)
> FAIL: g++.dg/analyzer/exception-value-2.C -std=c++26 (test for warnings, line 35)
>
> 2025-09-17 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.
> * 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.
> * 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.
>
> --- gcc/flag-types.h.jj 2025-08-23 18:15:36.338457849 +0200
> +++ gcc/flag-types.h 2025-09-17 10:20:47.847835866 +0200
> @@ -288,7 +288,8 @@ enum vect_cost_model {
> enum auto_init_type {
> AUTO_INIT_UNINITIALIZED = 0,
> AUTO_INIT_PATTERN = 1,
> - AUTO_INIT_ZERO = 2
> + AUTO_INIT_ZERO = 2,
> + AUTO_INIT_CXX26 = 3
> };
>
> /* Initialization of padding bits with zeros. */
> --- gcc/tree.h.jj 2025-08-23 18:15:36.386457215 +0200
> +++ gcc/tree.h 2025-09-17 10:20:47.849835839 +0200
> @@ -900,6 +900,19 @@ extern void omp_clause_range_check_faile
> #define UNUSED_LABEL_P(NODE) \
> (LABEL_DECL_CHECK (NODE)->base.default_def_flag)
>
> +/* Label used to goto around artificial .DEFERRED_INIT code for
> + C++ -ftrivial-auto-var-init= purposes with a goto around it.
> + VACUOUS_INIT_LABEL_P flag is used on the lab LABEL_DECL in:
> + goto lab;
> + lab1:
> + v1 = .DEFERRED_INIT (...);
> + v2 = .DEFERRED_INIT (...);
> + lab2:
> + v3 = .DEFERRED_INIT (...);
> + lab: */
> +#define VACUOUS_INIT_LABEL_P(NODE) \
> + (LABEL_DECL_CHECK (NODE)->base.nothrow_flag)
> +
> /* Nonzero means this expression is volatile in the C sense:
> its address should be of type `volatile WHATEVER *'.
> In other words, the declared item is volatile qualified.
> --- gcc/gimplify.cc.jj 2025-09-15 19:51:56.193398974 +0200
> +++ gcc/gimplify.cc 2025-09-17 16:25:30.279823667 +0200
> @@ -2102,13 +2102,13 @@ gimple_add_padding_init_for_auto_var (tr
> /* Return true if the DECL need to be automaticly initialized by the
> compiler. */
> static bool
> -is_var_need_auto_init (tree decl)
> +var_needs_auto_init_p (tree decl)
> {
> if (auto_var_p (decl)
> - && (TREE_CODE (decl) != VAR_DECL
> - || !DECL_HARD_REGISTER (decl))
> - && (flag_auto_var_init > AUTO_INIT_UNINITIALIZED)
> - && (!lookup_attribute ("uninitialized", DECL_ATTRIBUTES (decl)))
> + && (TREE_CODE (decl) != VAR_DECL || !DECL_HARD_REGISTER (decl))
> + && flag_auto_var_init > AUTO_INIT_UNINITIALIZED
> + && !lookup_attribute ("uninitialized", DECL_ATTRIBUTES (decl))
> + && !lookup_attribute ("indeterminate", DECL_ATTRIBUTES (decl))
> && !OPAQUE_TYPE_P (TREE_TYPE (decl))
> && !is_empty_type (TREE_TYPE (decl)))
> return true;
> @@ -2221,7 +2221,7 @@ gimplify_decl_expr (tree *stmt_p, gimple
> /* When there is no explicit initializer, if the user requested,
> We should insert an artifical initializer for this automatic
> variable. */
> - else if (is_var_need_auto_init (decl)
> + else if (var_needs_auto_init_p (decl)
> && !decl_had_value_expr_p)
> {
> gimple_add_init_for_auto_var (decl,
> @@ -2315,27 +2315,6 @@ emit_warn_switch_unreachable (gimple *st
> /* Don't warn for compiler-generated gotos. These occur
> in Duff's devices, for example. */
> return NULL;
> - else if ((flag_auto_var_init > AUTO_INIT_UNINITIALIZED)
> - && ((gimple_call_internal_p (stmt, IFN_DEFERRED_INIT))
> - || (gimple_call_builtin_p (stmt, BUILT_IN_CLEAR_PADDING)
> - && (bool) TREE_INT_CST_LOW (gimple_call_arg (stmt, 1)))
> - || (is_gimple_assign (stmt)
> - && gimple_assign_single_p (stmt)
> - && (TREE_CODE (gimple_assign_rhs1 (stmt)) == SSA_NAME)
> - && gimple_call_internal_p (
> - SSA_NAME_DEF_STMT (gimple_assign_rhs1 (stmt)),
> - IFN_DEFERRED_INIT))))
> - /* Don't warn for compiler-generated initializations for
> - -ftrivial-auto-var-init.
> - There are 3 cases:
> - case 1: a call to .DEFERRED_INIT;
> - case 2: a call to __builtin_clear_padding with the 2nd argument is
> - present and non-zero;
> - case 3: a gimple assign store right after the call to .DEFERRED_INIT
> - that has the LHS of .DEFERRED_INIT as the RHS as following:
> - _1 = .DEFERRED_INIT (4, 2, &"i1"[0]);
> - i1 = _1. */
> - return NULL;
> else
> warning_at (gimple_location (stmt), OPT_Wswitch_unreachable,
> "statement will never be executed");
> @@ -2383,6 +2362,18 @@ warn_switch_unreachable_and_auto_init_r
> there will be non-debug stmts too, and we'll catch those. */
> break;
>
> + case GIMPLE_ASSIGN:
> + /* See comment below in the GIMPLE_CALL case. */
> + if (flag_auto_var_init > AUTO_INIT_UNINITIALIZED
> + && gimple_assign_single_p (stmt)
> + && TREE_CODE (gimple_assign_rhs1 (stmt)) == SSA_NAME)
> + {
> + gimple *g = SSA_NAME_DEF_STMT (gimple_assign_rhs1 (stmt));
> + if (gimple_call_internal_p (g, IFN_DEFERRED_INIT))
> + break;
> + }
> + goto do_default;
> +
> case GIMPLE_LABEL:
> /* Stop till the first Label. */
> return integer_zero_node;
> @@ -2392,24 +2383,41 @@ warn_switch_unreachable_and_auto_init_r
> *handled_ops_p = false;
> break;
> }
> - if (warn_trivial_auto_var_init
> - && flag_auto_var_init > AUTO_INIT_UNINITIALIZED
> + /* Don't warn for compiler-generated initializations for
> + -ftrivial-auto-var-init for -Wswitch-unreachable. Though
> + do warn for -Wtrivial-auto-var-init.
> + There are 3 cases:
> + case 1: a call to .DEFERRED_INIT;
> + case 2: a call to __builtin_clear_padding with the 2nd argument is
> + present and non-zero;
> + case 3: a gimple assign store right after the call to .DEFERRED_INIT
> + that has the LHS of .DEFERRED_INIT as the RHS as following:
> + _1 = .DEFERRED_INIT (4, 2, &"i1"[0]);
> + i1 = _1.
> + case 3 is handled above in the GIMPLE_ASSIGN case. */
> + if (flag_auto_var_init > AUTO_INIT_UNINITIALIZED
> && gimple_call_internal_p (stmt, IFN_DEFERRED_INIT))
> {
> - /* Get the variable name from the 3rd argument of call. */
> - tree var_name = gimple_call_arg (stmt, 2);
> - var_name = TREE_OPERAND (TREE_OPERAND (var_name, 0), 0);
> - const char *var_name_str = TREE_STRING_POINTER (var_name);
> -
> - warning_at (gimple_location (stmt), OPT_Wtrivial_auto_var_init,
> - "%qs cannot be initialized with "
> - "%<-ftrivial-auto-var_init%>",
> - var_name_str);
> + if (warn_trivial_auto_var_init)
> + {
> + /* Get the variable name from the 3rd argument of call. */
> + tree var_name = gimple_call_arg (stmt, 2);
> + var_name = TREE_OPERAND (TREE_OPERAND (var_name, 0), 0);
> + const char *var_name_str = TREE_STRING_POINTER (var_name);
> +
> + warning_at (gimple_location (stmt), OPT_Wtrivial_auto_var_init,
> + "%qs cannot be initialized with "
> + "%<-ftrivial-auto-var_init%>", var_name_str);
> + }
> break;
> }
> -
> + if (flag_auto_var_init > AUTO_INIT_UNINITIALIZED
> + && gimple_call_builtin_p (stmt, BUILT_IN_CLEAR_PADDING)
> + && (bool) TREE_INT_CST_LOW (gimple_call_arg (stmt, 1)))
> + break;
> /* Fall through. */
> default:
> + do_default:
> /* check the first "real" statement (not a decl/lexical scope/...), issue
> warning if needed. */
> if (warn_switch_unreachable && !unreachable_issued)
> @@ -2489,26 +2497,38 @@ last_stmt_in_scope (gimple *stmt)
> if (!stmt)
> return NULL;
>
> + auto last_stmt_in_seq = [] (gimple_seq s) {
> + gimple_seq_node n;
> + for (n = gimple_seq_last (s);
> + n && (is_gimple_debug (n)
> + || (flag_auto_var_init > AUTO_INIT_UNINITIALIZED
> + && gimple_call_internal_p (n, IFN_DEFERRED_INIT)));
> + n = n->prev)
> + if (n == s)
> + return (gimple *) NULL;
> + return (gimple *) n;
> + };
> +
> switch (gimple_code (stmt))
> {
> case GIMPLE_BIND:
> {
> gbind *bind = as_a <gbind *> (stmt);
> - stmt = gimple_seq_last_nondebug_stmt (gimple_bind_body (bind));
> + stmt = last_stmt_in_seq (gimple_bind_body (bind));
> return last_stmt_in_scope (stmt);
> }
>
> case GIMPLE_TRY:
> {
> gtry *try_stmt = as_a <gtry *> (stmt);
> - stmt = gimple_seq_last_nondebug_stmt (gimple_try_eval (try_stmt));
> + stmt = last_stmt_in_seq (gimple_try_eval (try_stmt));
> gimple *last_eval = last_stmt_in_scope (stmt);
> if (gimple_stmt_may_fallthru (last_eval)
> && (last_eval == NULL
> || !gimple_call_internal_p (last_eval, IFN_FALLTHROUGH))
> && gimple_try_kind (try_stmt) == GIMPLE_TRY_FINALLY)
> {
> - stmt = gimple_seq_last_nondebug_stmt (gimple_try_cleanup (try_stmt));
> + stmt = last_stmt_in_seq (gimple_try_cleanup (try_stmt));
> return last_stmt_in_scope (stmt);
> }
> else
> @@ -2661,8 +2681,16 @@ collect_fallthrough_labels (gimple_stmt_
> }
> else if (gimple_call_internal_p (gsi_stmt (*gsi_p), IFN_ASAN_MARK))
> ;
> + else if (flag_auto_var_init > AUTO_INIT_UNINITIALIZED
> + && gimple_call_internal_p (gsi_stmt (*gsi_p),
> + IFN_DEFERRED_INIT))
> + ;
> else if (gimple_code (gsi_stmt (*gsi_p)) == GIMPLE_PREDICT)
> ;
> + else if (flag_auto_var_init > AUTO_INIT_UNINITIALIZED
> + && gimple_code (gsi_stmt (*gsi_p)) == GIMPLE_GOTO
> + && VACUOUS_INIT_LABEL_P (gimple_goto_dest (gsi_stmt (*gsi_p))))
> + ;
> else if (!is_gimple_debug (gsi_stmt (*gsi_p)))
> prev = gsi_stmt (*gsi_p);
> gsi_next (gsi_p);
> @@ -2699,9 +2727,13 @@ should_warn_for_implicit_fallthrough (gi
> {
> tree l;
> while (!gsi_end_p (gsi)
> - && gimple_code (gsi_stmt (gsi)) == GIMPLE_LABEL
> - && (l = gimple_label_label (as_a <glabel *> (gsi_stmt (gsi))))
> - && !case_label_p (&gimplify_ctxp->case_labels, l))
> + && ((gimple_code (gsi_stmt (gsi)) == GIMPLE_LABEL
> + && (l
> + = gimple_label_label (as_a <glabel *> (gsi_stmt (gsi))))
> + && !case_label_p (&gimplify_ctxp->case_labels, l))
> + || (flag_auto_var_init > AUTO_INIT_UNINITIALIZED
> + && gimple_call_internal_p (gsi_stmt (gsi),
> + IFN_DEFERRED_INIT))))
> gsi_next_nondebug (&gsi);
> if (gsi_end_p (gsi) || gimple_code (gsi_stmt (gsi)) != GIMPLE_LABEL)
> return false;
> @@ -2714,7 +2746,10 @@ should_warn_for_implicit_fallthrough (gi
> /* Skip all immediately following labels. */
> while (!gsi_end_p (gsi)
> && (gimple_code (gsi_stmt (gsi)) == GIMPLE_LABEL
> - || gimple_code (gsi_stmt (gsi)) == GIMPLE_PREDICT))
> + || gimple_code (gsi_stmt (gsi)) == GIMPLE_PREDICT
> + || (flag_auto_var_init > AUTO_INIT_UNINITIALIZED
> + && gimple_call_internal_p (gsi_stmt (gsi),
> + IFN_DEFERRED_INIT))))
> gsi_next_nondebug (&gsi);
>
> /* { ... something; default:; } */
> @@ -2891,7 +2926,33 @@ expand_FALLTHROUGH_r (gimple_stmt_iterat
>
> gimple_stmt_iterator gsi2 = *gsi_p;
> stmt = gsi_stmt (gsi2);
> - if (gimple_code (stmt) == GIMPLE_GOTO && !gimple_has_location (stmt))
> + if (flag_auto_var_init > AUTO_INIT_UNINITIALIZED
> + && gimple_code (stmt) == GIMPLE_GOTO
> + && VACUOUS_INIT_LABEL_P (gimple_goto_dest (stmt)))
> + {
> + /* Handle for C++ artificial -ftrivial-auto-var-init=
> + sequences. Those look like:
> + goto lab1;
> + lab2:;
> + v1 = .DEFERRED_INIT (...);
> + v2 = .DEFERRED_INIT (...);
> + lab3:;
> + v3 = .DEFERRED_INIT (...);
> + lab1:;
> + In this case, a case/default label can be either in between
> + the GIMPLE_GOTO and the corresponding GIMPLE_LABEL, if jumps
> + from the switch condition to the case/default label cross
> + vacuous initialization of some variables, or after the
> + corresponding GIMPLE_LABEL, if those jumps don't cross
> + any such initialization but there is an adjacent named label
> + which crosses such initialization. So, for the purpose of
> + this function, just ignore the goto but until reaching the
> + corresponding GIMPLE_LABEL allow also .DEFERRED_INIT
> + calls. */
> + gsi_next (&gsi2);
> + }
> + else if (gimple_code (stmt) == GIMPLE_GOTO
> + && !gimple_has_location (stmt))
> {
> /* Go on until the artificial label. */
> tree goto_dest = gimple_goto_dest (stmt);
> @@ -2926,6 +2987,9 @@ expand_FALLTHROUGH_r (gimple_stmt_iterat
> }
> else if (gimple_call_internal_p (stmt, IFN_ASAN_MARK))
> ;
> + else if (flag_auto_var_init > AUTO_INIT_UNINITIALIZED
> + && gimple_call_internal_p (stmt, IFN_DEFERRED_INIT))
> + ;
> else if (!is_gimple_debug (stmt))
> /* Anything else is not expected. */
> break;
> @@ -6753,7 +6817,8 @@ gimplify_init_constructor (tree *expr_p,
> && clear_padding_type_may_have_padding_p (type)
> && ((AGGREGATE_TYPE_P (type) && !cleared && !is_empty_ctor)
> || !AGGREGATE_TYPE_P (type))
> - && is_var_need_auto_init (object))
> + && var_needs_auto_init_p (object)
> + && flag_auto_var_init != AUTO_INIT_CXX26)
> gimple_add_padding_init_for_auto_var (object, false, pre_p);
>
> return ret;
> @@ -8460,6 +8525,7 @@ gimplify_target_expr (tree *expr_p, gimp
> if (init)
> {
> gimple_seq init_pre_p = NULL;
> + bool is_vla = false;
>
> /* TARGET_EXPR temps aren't part of the enclosing block, so add it
> to the temps list. Handle also variable length TARGET_EXPRs. */
> @@ -8470,6 +8536,7 @@ gimplify_target_expr (tree *expr_p, gimp
> /* FIXME: this is correct only when the size of the type does
> not depend on expressions evaluated in init. */
> gimplify_vla_decl (temp, &init_pre_p);
> + is_vla = true;
> }
> else
> {
> @@ -8481,6 +8548,15 @@ gimplify_target_expr (tree *expr_p, gimp
> gimple_add_tmp_var (temp);
> }
>
> + if (var_needs_auto_init_p (temp) && VOID_TYPE_P (TREE_TYPE (init)))
> + {
> + gimple_add_init_for_auto_var (temp, flag_auto_var_init, &init_pre_p);
> + if (flag_auto_var_init == AUTO_INIT_PATTERN
> + && !is_gimple_reg (temp)
> + && clear_padding_type_may_have_padding_p (TREE_TYPE (temp)))
> + gimple_add_padding_init_for_auto_var (temp, is_vla, &init_pre_p);
> + }
> +
> /* If TARGET_EXPR_INITIAL is void, then the mere evaluation of the
> expression is supposed to initialize the slot. */
> if (VOID_TYPE_P (TREE_TYPE (init)))
> --- gcc/doc/invoke.texi.jj 2025-09-15 19:52:51.461668311 +0200
> +++ gcc/doc/invoke.texi 2025-09-17 10:20:47.858835715 +0200
> @@ -14696,27 +14696,25 @@ and @option{-fauto-profile}.
>
> @opindex ftrivial-auto-var-init
> @item -ftrivial-auto-var-init=@var{choice}
> -Initialize automatic variables with either a pattern or with zeroes to increase
> -the security and predictability of a program by preventing uninitialized memory
> -disclosure and use.
> +Initialize automatic variables or temporary objects with either a pattern or with
> +zeroes to increase the security and predictability of a program by preventing
> +uninitialized memory disclosure and use.
> GCC still considers an automatic variable that doesn't have an explicit
> initializer as uninitialized, @option{-Wuninitialized} and
> @option{-Wanalyzer-use-of-uninitialized-value} will still report
> -warning messages on such automatic variables and the compiler will
> -perform optimization as if the variable were uninitialized.
> +warning messages on such automatic variables or temporary objects and the
> +compiler will perform optimization as if the variable were uninitialized.
> With this option, GCC will also initialize any padding of automatic variables
> -that have structure or union types to zeroes.
> -However, the current implementation cannot initialize automatic variables that
> -are declared between the controlling expression and the first case of a
> -@code{switch} statement. Using @option{-Wtrivial-auto-var-init} to report all
> -such cases.
> +or temporary objects that have structure or union types to zeroes.
> +However, the current implementation cannot initialize automatic variables
> +whose initialization is bypassed through @code{switch} or @code{goto}
> +statement. Using @option{-Wtrivial-auto-var-init} to report all such cases.
>
> The three values of @var{choice} are:
>
> @itemize @bullet
> @item
> @samp{uninitialized} doesn't initialize any automatic variables.
> -This is C and C++'s default.
>
> @item
> @samp{pattern} Initialize automatic variables with values which will likely
> @@ -14730,7 +14728,10 @@ The values used for pattern initializati
> @samp{zero} Initialize automatic variables with zeroes.
> @end itemize
>
> -The default is @samp{uninitialized}.
> +The default is @samp{uninitialized} except for C++26, in which case
> +if @option{-ftrivial-auto-var-init=} is not specified at all automatic
> +variables or temporary objects are zero initialized, but zero initialization
> +of padding bits does not happen.
>
> Note that the initializer values, whether @samp{zero} or @samp{pattern},
> refer to data representation (in memory or machine registers), rather
> @@ -14743,7 +14744,7 @@ with the bit patterns @code{0x00} or @co
> @var{choice}, whether or not these representations stand for values in
> that range, and even if they do, the interpretation of the value held by
> the variable will depend on the bias. A @samp{hardbool} variable that
> -uses say @code{0X5A} and @code{0xA5} for @code{false} and @code{true},
> +uses say @code{0x5A} and @code{0xA5} for @code{false} and @code{true},
> respectively, will trap with either @samp{choice} of trivial
> initializer, i.e., @samp{zero} initialization will not convert to the
> representation for @code{false}, even if it would for a @code{static}
> @@ -14754,7 +14755,8 @@ are initialized with @code{false} (zero)
> requested.
>
> You can control this behavior for a specific variable by using the variable
> -attribute @code{uninitialized} (@pxref{Variable Attributes}).
> +attribute @code{uninitialized} standard attribute (@pxref{Variable Attributes})
> +or the C++26 @code{[[indeterminate]]}.
>
> @opindex fvect-cost-model
> @item -fvect-cost-model=@var{model}
> --- gcc/c-family/c-opts.cc.jj 2025-08-10 19:43:47.082657317 +0200
> +++ gcc/c-family/c-opts.cc 2025-09-17 10:20:47.849835839 +0200
> @@ -913,6 +913,16 @@ c_common_post_options (const char **pfil
> else
> flag_permitted_flt_eval_methods = PERMITTED_FLT_EVAL_METHODS_C11;
>
> + if (cxx_dialect >= cxx26)
> + SET_OPTION_IF_UNSET (&global_options, &global_options_set,
> + flag_auto_var_init, AUTO_INIT_CXX26);
> +
> + /* The -Wtrivial-auto-var-init warning is useless for C++, where we always
> + add .DEFERRED_INIT calls when some (vacuous) initializers are bypassed
> + through jumps from switch condition to case/default label. */
> + if (c_dialect_cxx ())
> + warn_trivial_auto_var_init = 0;
If this is the case, should we mentioned this in the documentation of -Wtrivial-auto-var-init?
Or for cxx, we don’t have the issue?
thanks.
Qing
On 9/18/25 10:52 PM, Qing Zhao wrote:
>
>
>> On Sep 17, 2025, at 12:08, Jakub Jelinek <jakub@redhat.com> wrote:
>>
>> Hi!
>>
>> -ftrivial-auto-var-init= also calls .DEFERRED_INIT for temporaries
>> if they have VOID_TYPE_P TARGET_EXPR_INITIAL (otherwise they are expanded
>> as INIT_EXPR of the TARGET_EXPR_SLOT and TARGET_EXPR_INITIAL and so
>> everything is initialized; trying to emit .DEFERRED_INIT in such a case
>> breaks a lot of stuff because e.g. const vars turned into static but
>> not static when .DEFERRED_INIT is done will try to write into .rodata).
>
> A little confused with the above. Do you mean that not all temporaries are initialized to zero, only
> the temporaries that have VOID_TYPE_P TARGET_EXPR_INITIAL are initialized to zero?
Temporaries with non-void INITIAL are initialized to that value, so
there's no need to also initialize them to zero.
Jason
> On Sep 18, 2025, at 17:42, Jason Merrill <jason@redhat.com> wrote:
>
> On 9/18/25 10:52 PM, Qing Zhao wrote:
>>> On Sep 17, 2025, at 12:08, Jakub Jelinek <jakub@redhat.com> wrote:
>>>
>>> Hi!
>>>
>>> -ftrivial-auto-var-init= also calls .DEFERRED_INIT for temporaries
>>> if they have VOID_TYPE_P TARGET_EXPR_INITIAL (otherwise they are expanded
>>> as INIT_EXPR of the TARGET_EXPR_SLOT and TARGET_EXPR_INITIAL and so
>>> everything is initialized; trying to emit .DEFERRED_INIT in such a case
>>> breaks a lot of stuff because e.g. const vars turned into static but
>>> not static when .DEFERRED_INIT is done will try to write into .rodata).
>> A little confused with the above. Do you mean that not all temporaries are initialized to zero, only
>> the temporaries that have VOID_TYPE_P TARGET_EXPR_INITIAL are initialized to zero?
>
> Temporaries with non-void INITIAL are initialized to that value, so there's no need to also initialize them to zero.
Okay, I see.
thanks.
Qing
>
> Jason
On Thu, 18 Sep 2025, Jakub Jelinek wrote:
> On Thu, Sep 18, 2025 at 06:54:06PM +0200, Jason Merrill wrote:
> > > There are some regressions caused by the removal of {CLOBBER(bob)}
> > > clobbers from the start of certain constructors, e.g. one testcase has
> > > struct A
> > > {
> > > int f,g;
> > > A() { f = g; // { dg-warning "g. is used uninitialized" } }
> > > } a;
> > > and this doesn't warn anymore for any C++ mode. Wonder what we could
> > > do for the warnings, for code generation we have to assume the code
> > > can read members in *this (for C++26 read there .DEFERRED_INIT, for
> > > older C++ versions with -flifetime-dse=2 {CLOBBER(bob)}).
> > > Perhaps we should add some artificial attribute to the functions we'd
> > > add the -flifetime-dse=2 clobber to and treat *this reaching the start
> > > of the function for those specially in the uninit pass?
> >
> > Or some way to indicate in GIMPLE that whatever value is there, it's
> > poisoned?
>
> Some new ifn which is say:
> *this = .POISON (this);
> or
> *this = .POISON (*this);
> would be an option (which would expand to nothing if the pointers are the
> same), dunno what would be easier to implement though.
> I'll try artificial attribute on the this PARM_DECL.
>
> Richi, what is your preference from the middle-end POV?
> Just a way to force -Wuninitialized/-Wmaybe-uninitialized warnings on
> reads but similarly to .DEFERRED_INIT which sets to specific bytes
> use whatever was in memory before.
So a CLOBBER doesn't do because it might cause earlier writes to be
DSEd (even if it itself doesn't alter storage)?
I wonder if we can re-use .DEFERRED_INIT with a special mode here?
Again I'm noticing we do not document internal-functions anywhere,
in particular their expected signature or semantics ... :/
From internal-fn.cc it seems we have an INIT_TYPE parameter, can
we add AUTO_INIT_KEEP_OLD or is AUTO_INIT_UNINITIALIZED already
doing what we want here?
Richard.
> On Sep 17, 2025, at 12:08, Jakub Jelinek <jakub@redhat.com> wrote:
>
>
> Also there is one ICEs and various FAILs which reproduce even without
> this patch with -ftrivial-auto-var-init=zero, I think I could temporarily
> make those tests -ftrivial-auto-var-init=uninitialized until it is resolved.
Sounds like -ftrivial-auto-var-init=zero bugs, could you please file a PR for this issue?
I can take a look at it.
Thanks.
Qing
On Fri, Sep 19, 2025 at 02:30:34PM +0000, Qing Zhao wrote:
>
>
> > On Sep 17, 2025, at 12:08, Jakub Jelinek <jakub@redhat.com> wrote:
> >
> >
> > Also there is one ICEs and various FAILs which reproduce even without
> > this patch with -ftrivial-auto-var-init=zero, I think I could temporarily
> > make those tests -ftrivial-auto-var-init=uninitialized until it is resolved.
>
> Sounds like -ftrivial-auto-var-init=zero bugs, could you please file a PR for this issue?
> I can take a look at it.
Already done, PR121975 and PR121977 (the latter fixed already).
Plus some sanitizer issues.
Jakub
On Fri, Sep 19, 2025 at 08:52:47AM +0200, Richard Biener wrote:
> > Some new ifn which is say:
> > *this = .POISON (this);
> > or
> > *this = .POISON (*this);
> > would be an option (which would expand to nothing if the pointers are the
> > same), dunno what would be easier to implement though.
> > I'll try artificial attribute on the this PARM_DECL.
> >
> > Richi, what is your preference from the middle-end POV?
> > Just a way to force -Wuninitialized/-Wmaybe-uninitialized warnings on
> > reads but similarly to .DEFERRED_INIT which sets to specific bytes
> > use whatever was in memory before.
I think the first question needs to be, do we throw those away during
inlining or not?
My preference would be that we do throw those away.
Consider say
struct A { A () {} int a; };
struct B : A { B () {} int b; };
struct C : B { C () {} int c; };
struct D : C { D () {} A a; B b; C c; };
void bar (D &);
void
foo ()
{
D d;
bar (d);
}
right now we get after einline
d ={v} {CLOBBER(bob)};
MEM[(struct C *)&d] ={v} {CLOBBER(bob)};
MEM[(struct B *)&d] ={v} {CLOBBER(bob)};
MEM[(struct A *)&d] ={v} {CLOBBER(bob)};
MEM[(struct A *)&d + 12B] ={v} {CLOBBER(bob)};
MEM[(struct B *)&d + 16B] ={v} {CLOBBER(bob)};
MEM[(struct A *)&d + 16B] ={v} {CLOBBER(bob)};
MEM[(struct C *)&d + 24B] ={v} {CLOBBER(bob)};
MEM[(struct B *)&d + 24B] ={v} {CLOBBER(bob)};
MEM[(struct A *)&d + 24B] ={v} {CLOBBER(bob)};
clobbers at the start, but all but the first one are useless. And
even if we don't inline everything, the clobber at the start of
a ctor not being inlined covers everything that needs to be clobbered
and later clobbers on it are just IL waste. When ctor is inlined
into the ultimate caller of the ctor, either there will be a normal
CLOBBER(bob) added by the patch for the whole variable or temporary
or with Jason's changes for new expression too.
> So a CLOBBER doesn't do because it might cause earlier writes to be
> DSEd (even if it itself doesn't alter storage)?
If it is dropped during inlining, that wouldn't be a problem.
But a problem would be say SRA or other optimizations optimizing
reads from the
[MEM[this] ={v} {CLOBBER(newkind)};
destination, we don't want optimize those to just _3(D) or some
smaller temporary initialized with a clobber. Except for -W*uninitialized
we need to treat it as what the memory contains is unknown.
Unless we tweak SRA (does e.g. SCCVN do that too?) for that new kind.
> I wonder if we can re-use .DEFERRED_INIT with a special mode here?
> Again I'm noticing we do not document internal-functions anywhere,
> in particular their expected signature or semantics ... :/
> >From internal-fn.cc it seems we have an INIT_TYPE parameter, can
> we add AUTO_INIT_KEEP_OLD or is AUTO_INIT_UNINITIALIZED already
> doing what we want here?
If we drop it during inlining, maybe, but same problem with SRA and maybe
SCCVN. We don't want for this case to see what PR121894 mentions,
that
MEM[(struct C *)this] = .DEFERRED_INIT (16, 6, &""[0]);
would be SRA optimized into
this$a_1 = .DEFERRED_INIT (4, 6, &""[0]);
this$b_2 = .DEFERRED_INIT (4, 6, &""[0]);
this$c_3 = .DEFERRED_INIT (4, 6, &""[0]);
this$d_4 = .DEFERRED_INIT (4, 6, &""[0]);
because for this kind we'd want to expand it into nothing at all, not having
to track which byte or whatever to read from which offset (which
.DEFERRED_INIT doesn't express, it is meant to set all bytes to the same
value).
Jakub
On 9/17/25 5:08 PM, Jakub Jelinek wrote:
> Hi!
>
> Here is my current version of the C++26 P2795R5 support.
>
> This 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.
>
> -ftrivial-auto-var-init= also calls .DEFERRED_INIT for temporaries
> if they have VOID_TYPE_P TARGET_EXPR_INITIAL (otherwise they are expanded
> as INIT_EXPR of the TARGET_EXPR_SLOT and TARGET_EXPR_INITIAL and so
> everything is initialized; trying to emit .DEFERRED_INIT in such a case
> breaks a lot of stuff because e.g. const vars turned into static but
> not static when .DEFERRED_INIT is done will try to write into .rodata).
>
> -std=c++26 implies -ftrivial-auto-var-init= mode that doesn't have an
> explicit user option and is like -ftrivial-auto-var-init=zero except
> __builtin_clear_padding calls aren't added (C++26 doesn't make reads from
> padding erroneous). User can still override it in both directions,
> so -ftrivial-auto-var-init={zero,pattern} will behave like before (except
> for the gotos/switches working for C++ right and temporaries being also
> initialized), and -ftrivial-auto-var-init=uninitialized allows to get
> C++23-ish behavior with other C++26 features enabled (if performance
> is more important than some sanity of erroneous code).
>
> The -flifetime-dse=2 {CLOBBER(bob)} clobbers aren't emitted anymore
> at the start of (some) constructors but instead when gimplifying DECL_EXPRs
> and TARGET_EXPRs. Haven't done it in gimplify.cc so that it roughly matches
> previous behavior (C++ only, not all languages, and adding bob CLOBBERs
> only on types which used them inside ctors for now (doing more breaks a lot
> of stuff in the testsuite).
>
> I understood you have a patch for {CLOBBER(bob)} for new expressions, there
> are some regressions without that, see below.
>
> There are some regressions caused by the removal of {CLOBBER(bob)}
> clobbers from the start of certain constructors, e.g. one testcase has
> struct A
> {
> int f,g;
> A() { f = g; // { dg-warning "g. is used uninitialized" } }
> } a;
> and this doesn't warn anymore for any C++ mode. Wonder what we could
> do for the warnings, for code generation we have to assume the code
> can read members in *this (for C++26 read there .DEFERRED_INIT, for
> older C++ versions with -flifetime-dse=2 {CLOBBER(bob)}).
> Perhaps we should add some artificial attribute to the functions we'd
> add the -flifetime-dse=2 clobber to and treat *this reaching the start
> of the function for those specially in the uninit pass?
>
> Also there is one ICEs and various FAILs which reproduce even without
> this patch with -ftrivial-auto-var-init=zero, I think I could temporarily
> make those tests -ftrivial-auto-var-init=uninitialized until it is resolved.
> Some tests got that already in the patch (e.g. ubsan/vla-1.c is testing UB
> and will crash if one tries to write something into the -1 sized VLAs,
> which -ftrivial-auto-var-init=zero or -std=c++26 do).
>
> So far only tested with
> GXX_TESTSUITE_STDS=98,11,14,17,20,23,26 make check-c++
> and make check in libstdc++-v3.
>
> The regressions (categorized) are below.
> Your thoughts on this?
>
> I hope the following would be fixed by your clobber new patch:
> FAIL: g++.dg/opt/flifetime-dse2.C -std=gnu++11 execution test
> FAIL: g++.dg/opt/flifetime-dse2.C -std=gnu++14 execution test
> FAIL: g++.dg/opt/flifetime-dse2.C -std=gnu++17 execution test
> FAIL: g++.dg/opt/flifetime-dse2.C -std=gnu++20 execution test
> FAIL: g++.dg/opt/flifetime-dse2.C -std=gnu++23 execution test
> FAIL: g++.dg/opt/flifetime-dse2.C -std=gnu++26 execution test
> FAIL: g++.dg/opt/flifetime-dse2.C -std=gnu++98 execution test
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++11 brace (test for warnings, line 29)
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++11 LP64 note (test for warnings, line 38)
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++11 note (test for warnings, line 48)
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++14 brace (test for warnings, line 29)
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++14 LP64 note (test for warnings, line 38)
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++14 note (test for warnings, line 48)
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++17 brace (test for warnings, line 29)
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++17 LP64 note (test for warnings, line 38)
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++17 note (test for warnings, line 48)
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++20 brace (test for warnings, line 29)
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++20 LP64 note (test for warnings, line 38)
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++20 note (test for warnings, line 48)
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++23 brace (test for warnings, line 29)
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++23 LP64 note (test for warnings, line 38)
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++23 note (test for warnings, line 48)
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++26 brace (test for warnings, line 29)
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++26 LP64 note (test for warnings, line 38)
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++26 note (test for warnings, line 48)
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++98 brace (test for warnings, line 29)
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++98 LP64 note (test for warnings, line 38)
> FAIL: g++.dg/warn/Warray-bounds-20.C -std=gnu++98 note (test for warnings, line 48)
>
> Dunno what to do with these, see above.
> FAIL: g++.dg/warn/Wuninitialized-10.C -std=gnu++11 (test for warnings, line 10)
> FAIL: g++.dg/warn/Wuninitialized-10.C -std=gnu++14 (test for warnings, line 10)
> FAIL: g++.dg/warn/Wuninitialized-10.C -std=gnu++17 (test for warnings, line 10)
> FAIL: g++.dg/warn/Wuninitialized-10.C -std=gnu++20 (test for warnings, line 10)
> FAIL: g++.dg/warn/Wuninitialized-10.C -std=gnu++23 (test for warnings, line 10)
> FAIL: g++.dg/warn/Wuninitialized-10.C -std=gnu++26 (test for warnings, line 10)
> FAIL: g++.dg/warn/Wuninitialized-10.C -std=gnu++98 (test for warnings, line 10)
> FAIL: g++.dg/warn/Wuninitialized-pr111123-1.C -std=gnu++11 (test for warnings, line 14)
> FAIL: g++.dg/warn/Wuninitialized-pr111123-1.C -std=gnu++14 (test for warnings, line 14)
> FAIL: g++.dg/warn/Wuninitialized-pr111123-1.C -std=gnu++17 (test for warnings, line 14)
> FAIL: g++.dg/warn/Wuninitialized-pr111123-1.C -std=gnu++20 (test for warnings, line 14)
> FAIL: g++.dg/warn/Wuninitialized-pr111123-1.C -std=gnu++23 (test for warnings, line 14)
> FAIL: g++.dg/warn/Wuninitialized-pr111123-1.C -std=gnu++26 (test for warnings, line 14)
>
> PR121975 - perhaps I can just make those effective target c++23_down temporarily with a comment
> FAIL: c-c++-common/goacc-gomp/nesting-1.c -std=c++26 at line 30 (test for warnings, line 31)
> FAIL: c-c++-common/goacc-gomp/nesting-1.c -std=c++26 (test for excess errors)
> FAIL: c-c++-common/goacc/kernels-decompose-2.c -std=c++26 at line 76 (test for warnings, line 75)
> FAIL: c-c++-common/goacc/kernels-decompose-2.c -std=c++26 (test for excess errors)
> FAIL: c-c++-common/goacc/kernels-decompose-pr100400-1-1.c -std=c++26 at line 23 (test for warnings, line 24)
> FAIL: c-c++-common/goacc/kernels-decompose-pr100400-1-1.c -std=c++26 at line 28 (test for warnings, line 27)
> FAIL: c-c++-common/goacc/kernels-decompose-pr100400-1-1.c -std=c++26 (test for excess errors)
> FAIL: c-c++-common/goacc/kernels-decompose-pr100400-1-3.c -std=c++26 at line 32 (test for warnings, line 33)
> FAIL: c-c++-common/goacc/kernels-decompose-pr100400-1-3.c -std=c++26 at line 37 (test for warnings, line 36)
> FAIL: c-c++-common/goacc/kernels-decompose-pr100400-1-3.c -std=c++26 (test for excess errors)
> FAIL: c-c++-common/goacc/kernels-decompose-pr100400-1-3.c -std=c++26 w/ debug at line 29 (test for bogus messages, line 30)
> FAIL: c-c++-common/goacc/kernels-decompose-pr104061-1-1.c -std=c++26 at line 26 (test for warnings, line 25)
> FAIL: c-c++-common/goacc/kernels-decompose-pr104061-1-1.c -std=c++26 (test for excess errors)
> FAIL: c-c++-common/goacc/kernels-decompose-pr104061-1-3.c -std=c++26 at line 33 (test for warnings, line 32)
> FAIL: c-c++-common/goacc/kernels-decompose-pr104061-1-3.c -std=c++26 w/ debug at line 28 (test for bogus messages, line 29)
> FAIL: c-c++-common/goacc/kernels-decompose-pr104061-1-4.c -std=c++26 at line 33 (test for warnings, line 32)
> FAIL: c-c++-common/goacc/kernels-decompose-pr104061-1-4.c -std=c++26 w/ debug at line 28 (test for bogus messages, line 29)
> FAIL: c-c++-common/goacc/kernels-decompose-pr104132-1.c -std=c++26 (test for excess errors)
> FAIL: c-c++-common/goacc/kernels-decompose-pr104133-1.c -std=c++26 (test for excess errors)
> FAIL: c-c++-common/goacc/kernels-decompose-pr104774-1.c -std=c++26 (test for excess errors)
> FAIL: c-c++-common/goacc/mdc-1.c -std=c++26 scan-tree-dump-times omplower "pragma omp target oacc_data map.tofrom:.z .len: 40.. map.struct:s .len: 1.. map.alloc:s.a .len: 8.. map.tofrom:._1 .len: 40.. map.attach:s.a .bias: 0.." 1
>
> PR121977 - I'd like to fix this, but ideally separately from this patch, so perhaps also c++23_down
> FAIL: g++.dg/gomp/member-1.C -std=c++26 (internal compiler error: Segmentation fault)
> FAIL: g++.dg/gomp/member-1.C -std=c++26 (test for excess errors)
>
> Dunno about these analyzer issues, will probably need David for that
> FAIL: c-c++-common/analyzer/invalid-shift-1.c -std=c++26 (test for warnings, line 15)
> FAIL: c-c++-common/analyzer/invalid-shift-1.c -std=c++26 (test for warnings, line 18)
> FAIL: g++.dg/analyzer/exception-value-2.C -std=c++26 (test for excess errors)
> FAIL: g++.dg/analyzer/exception-value-2.C -std=c++26 (test for warnings, line 34)
> FAIL: g++.dg/analyzer/exception-value-2.C -std=c++26 (test for warnings, line 35)
>
> 2025-09-17 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.
> * 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.
> * 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.
>
> --- gcc/flag-types.h.jj 2025-08-23 18:15:36.338457849 +0200
> +++ gcc/flag-types.h 2025-09-17 10:20:47.847835866 +0200
> @@ -288,7 +288,8 @@ enum vect_cost_model {
> enum auto_init_type {
> AUTO_INIT_UNINITIALIZED = 0,
> AUTO_INIT_PATTERN = 1,
> - AUTO_INIT_ZERO = 2
> + AUTO_INIT_ZERO = 2,
> + AUTO_INIT_CXX26 = 3
> };
>
> /* Initialization of padding bits with zeros. */
> --- gcc/tree.h.jj 2025-08-23 18:15:36.386457215 +0200
> +++ gcc/tree.h 2025-09-17 10:20:47.849835839 +0200
> @@ -900,6 +900,19 @@ extern void omp_clause_range_check_faile
> #define UNUSED_LABEL_P(NODE) \
> (LABEL_DECL_CHECK (NODE)->base.default_def_flag)
>
> +/* Label used to goto around artificial .DEFERRED_INIT code for
> + C++ -ftrivial-auto-var-init= purposes with a goto around it.
> + VACUOUS_INIT_LABEL_P flag is used on the lab LABEL_DECL in:
> + goto lab;
> + lab1:
> + v1 = .DEFERRED_INIT (...);
> + v2 = .DEFERRED_INIT (...);
> + lab2:
> + v3 = .DEFERRED_INIT (...);
> + lab: */
> +#define VACUOUS_INIT_LABEL_P(NODE) \
> + (LABEL_DECL_CHECK (NODE)->base.nothrow_flag)
> +
> /* Nonzero means this expression is volatile in the C sense:
> its address should be of type `volatile WHATEVER *'.
> In other words, the declared item is volatile qualified.
> --- gcc/gimplify.cc.jj 2025-09-15 19:51:56.193398974 +0200
> +++ gcc/gimplify.cc 2025-09-17 16:25:30.279823667 +0200
> @@ -2102,13 +2102,13 @@ gimple_add_padding_init_for_auto_var (tr
> /* Return true if the DECL need to be automaticly initialized by the
> compiler. */
> static bool
> -is_var_need_auto_init (tree decl)
> +var_needs_auto_init_p (tree decl)
> {
> if (auto_var_p (decl)
> - && (TREE_CODE (decl) != VAR_DECL
> - || !DECL_HARD_REGISTER (decl))
> - && (flag_auto_var_init > AUTO_INIT_UNINITIALIZED)
> - && (!lookup_attribute ("uninitialized", DECL_ATTRIBUTES (decl)))
> + && (TREE_CODE (decl) != VAR_DECL || !DECL_HARD_REGISTER (decl))
> + && flag_auto_var_init > AUTO_INIT_UNINITIALIZED
> + && !lookup_attribute ("uninitialized", DECL_ATTRIBUTES (decl))
> + && !lookup_attribute ("indeterminate", DECL_ATTRIBUTES (decl))
> && !OPAQUE_TYPE_P (TREE_TYPE (decl))
> && !is_empty_type (TREE_TYPE (decl)))
> return true;
> @@ -2221,7 +2221,7 @@ gimplify_decl_expr (tree *stmt_p, gimple
> /* When there is no explicit initializer, if the user requested,
> We should insert an artifical initializer for this automatic
> variable. */
> - else if (is_var_need_auto_init (decl)
> + else if (var_needs_auto_init_p (decl)
> && !decl_had_value_expr_p)
> {
> gimple_add_init_for_auto_var (decl,
> @@ -2315,27 +2315,6 @@ emit_warn_switch_unreachable (gimple *st
> /* Don't warn for compiler-generated gotos. These occur
> in Duff's devices, for example. */
> return NULL;
> - else if ((flag_auto_var_init > AUTO_INIT_UNINITIALIZED)
> - && ((gimple_call_internal_p (stmt, IFN_DEFERRED_INIT))
> - || (gimple_call_builtin_p (stmt, BUILT_IN_CLEAR_PADDING)
> - && (bool) TREE_INT_CST_LOW (gimple_call_arg (stmt, 1)))
> - || (is_gimple_assign (stmt)
> - && gimple_assign_single_p (stmt)
> - && (TREE_CODE (gimple_assign_rhs1 (stmt)) == SSA_NAME)
> - && gimple_call_internal_p (
> - SSA_NAME_DEF_STMT (gimple_assign_rhs1 (stmt)),
> - IFN_DEFERRED_INIT))))
> - /* Don't warn for compiler-generated initializations for
> - -ftrivial-auto-var-init.
> - There are 3 cases:
> - case 1: a call to .DEFERRED_INIT;
> - case 2: a call to __builtin_clear_padding with the 2nd argument is
> - present and non-zero;
> - case 3: a gimple assign store right after the call to .DEFERRED_INIT
> - that has the LHS of .DEFERRED_INIT as the RHS as following:
> - _1 = .DEFERRED_INIT (4, 2, &"i1"[0]);
> - i1 = _1. */
> - return NULL;
> else
> warning_at (gimple_location (stmt), OPT_Wswitch_unreachable,
> "statement will never be executed");
> @@ -2383,6 +2362,18 @@ warn_switch_unreachable_and_auto_init_r
> there will be non-debug stmts too, and we'll catch those. */
> break;
>
> + case GIMPLE_ASSIGN:
> + /* See comment below in the GIMPLE_CALL case. */
> + if (flag_auto_var_init > AUTO_INIT_UNINITIALIZED
> + && gimple_assign_single_p (stmt)
> + && TREE_CODE (gimple_assign_rhs1 (stmt)) == SSA_NAME)
> + {
> + gimple *g = SSA_NAME_DEF_STMT (gimple_assign_rhs1 (stmt));
> + if (gimple_call_internal_p (g, IFN_DEFERRED_INIT))
> + break;
> + }
> + goto do_default;
> +
> case GIMPLE_LABEL:
> /* Stop till the first Label. */
> return integer_zero_node;
> @@ -2392,24 +2383,41 @@ warn_switch_unreachable_and_auto_init_r
> *handled_ops_p = false;
> break;
> }
> - if (warn_trivial_auto_var_init
> - && flag_auto_var_init > AUTO_INIT_UNINITIALIZED
> + /* Don't warn for compiler-generated initializations for
> + -ftrivial-auto-var-init for -Wswitch-unreachable. Though
> + do warn for -Wtrivial-auto-var-init.
> + There are 3 cases:
> + case 1: a call to .DEFERRED_INIT;
> + case 2: a call to __builtin_clear_padding with the 2nd argument is
> + present and non-zero;
> + case 3: a gimple assign store right after the call to .DEFERRED_INIT
> + that has the LHS of .DEFERRED_INIT as the RHS as following:
> + _1 = .DEFERRED_INIT (4, 2, &"i1"[0]);
> + i1 = _1.
> + case 3 is handled above in the GIMPLE_ASSIGN case. */
> + if (flag_auto_var_init > AUTO_INIT_UNINITIALIZED
> && gimple_call_internal_p (stmt, IFN_DEFERRED_INIT))
> {
> - /* Get the variable name from the 3rd argument of call. */
> - tree var_name = gimple_call_arg (stmt, 2);
> - var_name = TREE_OPERAND (TREE_OPERAND (var_name, 0), 0);
> - const char *var_name_str = TREE_STRING_POINTER (var_name);
> -
> - warning_at (gimple_location (stmt), OPT_Wtrivial_auto_var_init,
> - "%qs cannot be initialized with "
> - "%<-ftrivial-auto-var_init%>",
> - var_name_str);
> + if (warn_trivial_auto_var_init)
> + {
> + /* Get the variable name from the 3rd argument of call. */
> + tree var_name = gimple_call_arg (stmt, 2);
> + var_name = TREE_OPERAND (TREE_OPERAND (var_name, 0), 0);
> + const char *var_name_str = TREE_STRING_POINTER (var_name);
> +
> + warning_at (gimple_location (stmt), OPT_Wtrivial_auto_var_init,
> + "%qs cannot be initialized with "
> + "%<-ftrivial-auto-var_init%>", var_name_str);
> + }
> break;
> }
> -
> + if (flag_auto_var_init > AUTO_INIT_UNINITIALIZED
> + && gimple_call_builtin_p (stmt, BUILT_IN_CLEAR_PADDING)
> + && (bool) TREE_INT_CST_LOW (gimple_call_arg (stmt, 1)))
> + break;
> /* Fall through. */
> default:
> + do_default:
> /* check the first "real" statement (not a decl/lexical scope/...), issue
> warning if needed. */
> if (warn_switch_unreachable && !unreachable_issued)
> @@ -2489,26 +2497,38 @@ last_stmt_in_scope (gimple *stmt)
> if (!stmt)
> return NULL;
>
> + auto last_stmt_in_seq = [] (gimple_seq s) {
> + gimple_seq_node n;
> + for (n = gimple_seq_last (s);
> + n && (is_gimple_debug (n)
> + || (flag_auto_var_init > AUTO_INIT_UNINITIALIZED
> + && gimple_call_internal_p (n, IFN_DEFERRED_INIT)));
> + n = n->prev)
> + if (n == s)
> + return (gimple *) NULL;
> + return (gimple *) n;
> + };
> +
> switch (gimple_code (stmt))
> {
> case GIMPLE_BIND:
> {
> gbind *bind = as_a <gbind *> (stmt);
> - stmt = gimple_seq_last_nondebug_stmt (gimple_bind_body (bind));
> + stmt = last_stmt_in_seq (gimple_bind_body (bind));
> return last_stmt_in_scope (stmt);
> }
>
> case GIMPLE_TRY:
> {
> gtry *try_stmt = as_a <gtry *> (stmt);
> - stmt = gimple_seq_last_nondebug_stmt (gimple_try_eval (try_stmt));
> + stmt = last_stmt_in_seq (gimple_try_eval (try_stmt));
> gimple *last_eval = last_stmt_in_scope (stmt);
> if (gimple_stmt_may_fallthru (last_eval)
> && (last_eval == NULL
> || !gimple_call_internal_p (last_eval, IFN_FALLTHROUGH))
> && gimple_try_kind (try_stmt) == GIMPLE_TRY_FINALLY)
> {
> - stmt = gimple_seq_last_nondebug_stmt (gimple_try_cleanup (try_stmt));
> + stmt = last_stmt_in_seq (gimple_try_cleanup (try_stmt));
> return last_stmt_in_scope (stmt);
> }
> else
> @@ -2661,8 +2681,16 @@ collect_fallthrough_labels (gimple_stmt_
> }
> else if (gimple_call_internal_p (gsi_stmt (*gsi_p), IFN_ASAN_MARK))
> ;
> + else if (flag_auto_var_init > AUTO_INIT_UNINITIALIZED
> + && gimple_call_internal_p (gsi_stmt (*gsi_p),
> + IFN_DEFERRED_INIT))
> + ;
> else if (gimple_code (gsi_stmt (*gsi_p)) == GIMPLE_PREDICT)
> ;
> + else if (flag_auto_var_init > AUTO_INIT_UNINITIALIZED
> + && gimple_code (gsi_stmt (*gsi_p)) == GIMPLE_GOTO
> + && VACUOUS_INIT_LABEL_P (gimple_goto_dest (gsi_stmt (*gsi_p))))
> + ;
> else if (!is_gimple_debug (gsi_stmt (*gsi_p)))
> prev = gsi_stmt (*gsi_p);
> gsi_next (gsi_p);
> @@ -2699,9 +2727,13 @@ should_warn_for_implicit_fallthrough (gi
> {
> tree l;
> while (!gsi_end_p (gsi)
> - && gimple_code (gsi_stmt (gsi)) == GIMPLE_LABEL
> - && (l = gimple_label_label (as_a <glabel *> (gsi_stmt (gsi))))
> - && !case_label_p (&gimplify_ctxp->case_labels, l))
> + && ((gimple_code (gsi_stmt (gsi)) == GIMPLE_LABEL
> + && (l
> + = gimple_label_label (as_a <glabel *> (gsi_stmt (gsi))))
> + && !case_label_p (&gimplify_ctxp->case_labels, l))
> + || (flag_auto_var_init > AUTO_INIT_UNINITIALIZED
> + && gimple_call_internal_p (gsi_stmt (gsi),
> + IFN_DEFERRED_INIT))))
> gsi_next_nondebug (&gsi);
> if (gsi_end_p (gsi) || gimple_code (gsi_stmt (gsi)) != GIMPLE_LABEL)
> return false;
> @@ -2714,7 +2746,10 @@ should_warn_for_implicit_fallthrough (gi
> /* Skip all immediately following labels. */
> while (!gsi_end_p (gsi)
> && (gimple_code (gsi_stmt (gsi)) == GIMPLE_LABEL
> - || gimple_code (gsi_stmt (gsi)) == GIMPLE_PREDICT))
> + || gimple_code (gsi_stmt (gsi)) == GIMPLE_PREDICT
> + || (flag_auto_var_init > AUTO_INIT_UNINITIALIZED
> + && gimple_call_internal_p (gsi_stmt (gsi),
> + IFN_DEFERRED_INIT))))
> gsi_next_nondebug (&gsi);
>
> /* { ... something; default:; } */
> @@ -2891,7 +2926,33 @@ expand_FALLTHROUGH_r (gimple_stmt_iterat
>
> gimple_stmt_iterator gsi2 = *gsi_p;
> stmt = gsi_stmt (gsi2);
> - if (gimple_code (stmt) == GIMPLE_GOTO && !gimple_has_location (stmt))
> + if (flag_auto_var_init > AUTO_INIT_UNINITIALIZED
> + && gimple_code (stmt) == GIMPLE_GOTO
> + && VACUOUS_INIT_LABEL_P (gimple_goto_dest (stmt)))
> + {
> + /* Handle for C++ artificial -ftrivial-auto-var-init=
> + sequences. Those look like:
> + goto lab1;
> + lab2:;
> + v1 = .DEFERRED_INIT (...);
> + v2 = .DEFERRED_INIT (...);
> + lab3:;
> + v3 = .DEFERRED_INIT (...);
> + lab1:;
> + In this case, a case/default label can be either in between
> + the GIMPLE_GOTO and the corresponding GIMPLE_LABEL, if jumps
> + from the switch condition to the case/default label cross
> + vacuous initialization of some variables, or after the
> + corresponding GIMPLE_LABEL, if those jumps don't cross
> + any such initialization but there is an adjacent named label
> + which crosses such initialization. So, for the purpose of
> + this function, just ignore the goto but until reaching the
> + corresponding GIMPLE_LABEL allow also .DEFERRED_INIT
> + calls. */
> + gsi_next (&gsi2);
> + }
> + else if (gimple_code (stmt) == GIMPLE_GOTO
> + && !gimple_has_location (stmt))
> {
> /* Go on until the artificial label. */
> tree goto_dest = gimple_goto_dest (stmt);
> @@ -2926,6 +2987,9 @@ expand_FALLTHROUGH_r (gimple_stmt_iterat
> }
> else if (gimple_call_internal_p (stmt, IFN_ASAN_MARK))
> ;
> + else if (flag_auto_var_init > AUTO_INIT_UNINITIALIZED
> + && gimple_call_internal_p (stmt, IFN_DEFERRED_INIT))
> + ;
> else if (!is_gimple_debug (stmt))
> /* Anything else is not expected. */
> break;
> @@ -6753,7 +6817,8 @@ gimplify_init_constructor (tree *expr_p,
> && clear_padding_type_may_have_padding_p (type)
> && ((AGGREGATE_TYPE_P (type) && !cleared && !is_empty_ctor)
> || !AGGREGATE_TYPE_P (type))
> - && is_var_need_auto_init (object))
> + && var_needs_auto_init_p (object)
> + && flag_auto_var_init != AUTO_INIT_CXX26)
> gimple_add_padding_init_for_auto_var (object, false, pre_p);
>
> return ret;
> @@ -8460,6 +8525,7 @@ gimplify_target_expr (tree *expr_p, gimp
> if (init)
> {
> gimple_seq init_pre_p = NULL;
> + bool is_vla = false;
>
> /* TARGET_EXPR temps aren't part of the enclosing block, so add it
> to the temps list. Handle also variable length TARGET_EXPRs. */
> @@ -8470,6 +8536,7 @@ gimplify_target_expr (tree *expr_p, gimp
> /* FIXME: this is correct only when the size of the type does
> not depend on expressions evaluated in init. */
> gimplify_vla_decl (temp, &init_pre_p);
> + is_vla = true;
> }
> else
> {
> @@ -8481,6 +8548,15 @@ gimplify_target_expr (tree *expr_p, gimp
> gimple_add_tmp_var (temp);
> }
>
> + if (var_needs_auto_init_p (temp) && VOID_TYPE_P (TREE_TYPE (init)))
> + {
> + gimple_add_init_for_auto_var (temp, flag_auto_var_init, &init_pre_p);
> + if (flag_auto_var_init == AUTO_INIT_PATTERN
> + && !is_gimple_reg (temp)
> + && clear_padding_type_may_have_padding_p (TREE_TYPE (temp)))
> + gimple_add_padding_init_for_auto_var (temp, is_vla, &init_pre_p);
> + }
> +
> /* If TARGET_EXPR_INITIAL is void, then the mere evaluation of the
> expression is supposed to initialize the slot. */
> if (VOID_TYPE_P (TREE_TYPE (init)))
> --- gcc/doc/invoke.texi.jj 2025-09-15 19:52:51.461668311 +0200
> +++ gcc/doc/invoke.texi 2025-09-17 10:20:47.858835715 +0200
> @@ -14696,27 +14696,25 @@ and @option{-fauto-profile}.
>
> @opindex ftrivial-auto-var-init
> @item -ftrivial-auto-var-init=@var{choice}
> -Initialize automatic variables with either a pattern or with zeroes to increase
> -the security and predictability of a program by preventing uninitialized memory
> -disclosure and use.
> +Initialize automatic variables or temporary objects with either a pattern or with
> +zeroes to increase the security and predictability of a program by preventing
> +uninitialized memory disclosure and use.
> GCC still considers an automatic variable that doesn't have an explicit
> initializer as uninitialized, @option{-Wuninitialized} and
> @option{-Wanalyzer-use-of-uninitialized-value} will still report
> -warning messages on such automatic variables and the compiler will
> -perform optimization as if the variable were uninitialized.
> +warning messages on such automatic variables or temporary objects and the
> +compiler will perform optimization as if the variable were uninitialized.
> With this option, GCC will also initialize any padding of automatic variables
> -that have structure or union types to zeroes.
> -However, the current implementation cannot initialize automatic variables that
> -are declared between the controlling expression and the first case of a
> -@code{switch} statement. Using @option{-Wtrivial-auto-var-init} to report all
> -such cases.
> +or temporary objects that have structure or union types to zeroes.
> +However, the current implementation cannot initialize automatic variables
> +whose initialization is bypassed through @code{switch} or @code{goto}
> +statement. Using @option{-Wtrivial-auto-var-init} to report all such cases.
>
> The three values of @var{choice} are:
>
> @itemize @bullet
> @item
> @samp{uninitialized} doesn't initialize any automatic variables.
> -This is C and C++'s default.
>
> @item
> @samp{pattern} Initialize automatic variables with values which will likely
> @@ -14730,7 +14728,10 @@ The values used for pattern initializati
> @samp{zero} Initialize automatic variables with zeroes.
> @end itemize
>
> -The default is @samp{uninitialized}.
> +The default is @samp{uninitialized} except for C++26, in which case
> +if @option{-ftrivial-auto-var-init=} is not specified at all automatic
> +variables or temporary objects are zero initialized, but zero initialization
> +of padding bits does not happen.
>
> Note that the initializer values, whether @samp{zero} or @samp{pattern},
> refer to data representation (in memory or machine registers), rather
> @@ -14743,7 +14744,7 @@ with the bit patterns @code{0x00} or @co
> @var{choice}, whether or not these representations stand for values in
> that range, and even if they do, the interpretation of the value held by
> the variable will depend on the bias. A @samp{hardbool} variable that
> -uses say @code{0X5A} and @code{0xA5} for @code{false} and @code{true},
> +uses say @code{0x5A} and @code{0xA5} for @code{false} and @code{true},
> respectively, will trap with either @samp{choice} of trivial
> initializer, i.e., @samp{zero} initialization will not convert to the
> representation for @code{false}, even if it would for a @code{static}
> @@ -14754,7 +14755,8 @@ are initialized with @code{false} (zero)
> requested.
>
> You can control this behavior for a specific variable by using the variable
> -attribute @code{uninitialized} (@pxref{Variable Attributes}).
> +attribute @code{uninitialized} standard attribute (@pxref{Variable Attributes})
> +or the C++26 @code{[[indeterminate]]}.
>
> @opindex fvect-cost-model
> @item -fvect-cost-model=@var{model}
> --- gcc/c-family/c-opts.cc.jj 2025-08-10 19:43:47.082657317 +0200
> +++ gcc/c-family/c-opts.cc 2025-09-17 10:20:47.849835839 +0200
> @@ -913,6 +913,16 @@ c_common_post_options (const char **pfil
> else
> flag_permitted_flt_eval_methods = PERMITTED_FLT_EVAL_METHODS_C11;
>
> + if (cxx_dialect >= cxx26)
> + SET_OPTION_IF_UNSET (&global_options, &global_options_set,
> + flag_auto_var_init, AUTO_INIT_CXX26);
> +
> + /* The -Wtrivial-auto-var-init warning is useless for C++, where we always
> + add .DEFERRED_INIT calls when some (vacuous) initializers are bypassed
> + through jumps from switch condition to case/default label. */
> + if (c_dialect_cxx ())
> + warn_trivial_auto_var_init = 0;
> +
> /* C23 Annex F does not permit certain built-in functions to raise
> "inexact". */
> if (flag_isoc23)
> --- gcc/cp/cp-tree.h.jj 2025-09-15 19:51:49.388488936 +0200
> +++ gcc/cp/cp-tree.h 2025-09-17 10:20:47.825836169 +0200
> @@ -510,6 +510,7 @@ extern GTY(()) tree cp_global_trees[CPTI
> PACK_EXPANSION_FORCE_EXTRA_ARGS_P (in *_PACK_EXPANSION)
> LAMBDA_EXPR_STATIC_P (in LAMBDA_EXPR)
> TARGET_EXPR_ELIDING_P (in TARGET_EXPR)
> + IF_STMT_VACUOUS_INIT_P (IF_STMT)
> contract_semantic (in ASSERTION_, PRECONDITION_, POSTCONDITION_STMT)
> TYPENAME_IS_UNION_P (in TYPENAME_TYPE)
> 4: IDENTIFIER_MARKED (IDENTIFIER_NODEs)
> @@ -5686,6 +5687,9 @@ decl_template_parm_check (const_tree t,
> #define IF_SCOPE(NODE) TREE_OPERAND (IF_STMT_CHECK (NODE), 3)
> #define IF_STMT_CONSTEXPR_P(NODE) TREE_LANG_FLAG_0 (IF_STMT_CHECK (NODE))
> #define IF_STMT_CONSTEVAL_P(NODE) TREE_LANG_FLAG_2 (IF_STMT_CHECK (NODE))
> +/* True on artificial if (0) around .DEFERRED_INIT calls added for
> + !!flag_auto_var_init. */
> +#define IF_STMT_VACUOUS_INIT_P(NODE) TREE_LANG_FLAG_3 (IF_STMT_CHECK (NODE))
>
> /* Like PACK_EXPANSION_EXTRA_ARGS, for constexpr if. IF_SCOPE is used while
> building an IF_STMT; IF_STMT_EXTRA_ARGS is used after it is complete. */
> @@ -7279,7 +7283,7 @@ extern tree duplicate_decls (tree, tre
> extern void mark_label_addressed (tree);
> extern tree declare_local_label (tree);
> extern tree define_label (location_t, tree);
> -extern void check_goto (tree);
> +extern void check_goto (tree *);
> extern bool check_omp_return (void);
> extern tree make_typename_type (tree, tree, enum tag_types, tsubst_flags_t);
> extern tree build_typename_type (tree, tree, tree, tag_types);
> --- gcc/cp/call.cc.jj 2025-08-14 19:09:06.341140776 +0200
> +++ gcc/cp/call.cc 2025-09-17 10:20:47.820836237 +0200
> @@ -10474,6 +10474,7 @@ build_over_call (struct z_candidate *can
> unsigned int arg_index = 0;
> int conv_index = 0;
> int param_index = 0;
> + tree parmd = DECL_ARGUMENTS (fn);
>
> auto consume_object_arg = [&arg_index, &first_arg, args]()
> {
> @@ -10491,6 +10492,8 @@ build_over_call (struct z_candidate *can
> tree object_arg = consume_object_arg ();
> argarray[argarray_size++] = build_this (object_arg);
> parm = TREE_CHAIN (parm);
> + if (parmd)
> + parmd = DECL_CHAIN (parmd);
> /* We should never try to call the abstract constructor. */
> gcc_assert (!DECL_HAS_IN_CHARGE_PARM_P (fn));
>
> @@ -10499,6 +10502,8 @@ build_over_call (struct z_candidate *can
> argarray[argarray_size++] = (*args)[arg_index];
> ++arg_index;
> parm = TREE_CHAIN (parm);
> + if (parmd)
> + parmd = DECL_CHAIN (parmd);
> }
> }
> /* Bypass access control for 'this' parameter. */
> @@ -10586,6 +10591,8 @@ build_over_call (struct z_candidate *can
>
> argarray[argarray_size++] = converted_arg;
> parm = TREE_CHAIN (parm);
> + if (parmd)
> + parmd = DECL_CHAIN (parmd);
> }
>
> auto handle_arg = [fn, flags](tree type,
> @@ -10609,6 +10616,27 @@ build_over_call (struct z_candidate *can
> return val;
> };
>
> + auto handle_indeterminate_arg = [](tree parmd, tree val)
> + {
> + if (parmd
> + && lookup_attribute (NULL, "indeterminate", DECL_ATTRIBUTES (parmd)))
> + {
> + STRIP_NOPS (val);
> + if (TREE_CODE (val) == ADDR_EXPR
> + && TREE_CODE (TREE_OPERAND (val, 0)) == TARGET_EXPR)
> + {
> + val = TARGET_EXPR_SLOT (TREE_OPERAND (val, 0));
> + if (auto_var_p (val) && DECL_ARTIFICIAL (val))
> + {
> + tree id = get_identifier ("indeterminate");
> + DECL_ATTRIBUTES (val)
> + = tree_cons (build_tree_list (NULL_TREE, id), NULL_TREE,
> + DECL_ATTRIBUTES (val));
> + }
> + }
> + }
> + };
> +
> if (DECL_XOBJ_MEMBER_FUNCTION_P (fn))
> {
> gcc_assert (cand->num_convs > 0);
> @@ -10622,8 +10650,13 @@ build_over_call (struct z_candidate *can
> if (val == error_mark_node)
> return error_mark_node;
> else
> - argarray[argarray_size++] = val;
> + {
> + argarray[argarray_size++] = val;
> + handle_indeterminate_arg (parmd, val);
> + }
> parm = TREE_CHAIN (parm);
> + if (parmd)
> + parmd = DECL_CHAIN (parmd);
> }
>
> gcc_assert (first_arg == NULL_TREE);
> @@ -10669,7 +10702,12 @@ build_over_call (struct z_candidate *can
> if (val == error_mark_node)
> return error_mark_node;
> else
> - argarray[argarray_size++] = val;
> + {
> + argarray[argarray_size++] = val;
> + handle_indeterminate_arg (parmd, val);
> + }
> + if (parmd)
> + parmd = DECL_CHAIN (parmd);
> }
>
> /* Default arguments */
> @@ -10685,6 +10723,9 @@ build_over_call (struct z_candidate *can
> if (val == error_mark_node)
> return error_mark_node;
> argarray[argarray_size++] = val;
> + handle_indeterminate_arg (parmd, val);
> + if (parmd)
> + parmd = DECL_CHAIN (parmd);
> }
>
> /* Ellipsis */
> --- gcc/cp/constexpr.cc.jj 2025-09-05 23:10:45.820823159 +0200
> +++ gcc/cp/constexpr.cc 2025-09-17 10:20:47.823836196 +0200
> @@ -3009,6 +3009,9 @@ cxx_eval_internal_function (const conste
> vc_prvalue, non_constant_p,
> overflow_p, jump_target);
>
> + case IFN_DEFERRED_INIT:
> + return build_clobber (TREE_TYPE (t), CLOBBER_OBJECT_BEGIN);
> +
> case IFN_VEC_CONVERT:
> {
> tree arg = cxx_eval_constant_expression (ctx, CALL_EXPR_ARG (t, 0),
> @@ -7475,6 +7478,13 @@ cxx_eval_store_expression (const constex
> bool preeval = SCALAR_TYPE_P (type) || TREE_CODE (t) == MODIFY_EXPR;
> if (preeval && !TREE_CLOBBER_P (init))
> {
> + /* Ignore var = .DEFERRED_INIT (); for now, until PR121965 is fixed. */
> + if (flag_auto_var_init > AUTO_INIT_UNINITIALIZED
> + && TREE_CODE (init) == CALL_EXPR
> + && CALL_EXPR_FN (init) == NULL_TREE
> + && CALL_EXPR_IFN (init) == IFN_DEFERRED_INIT)
> + return void_node;
> +
> /* Evaluate the value to be stored without knowing what object it will be
> stored in, so that any side-effects happen first. */
> if (!SCALAR_TYPE_P (type))
> --- gcc/cp/cp-gimplify.cc.jj 2025-09-03 22:23:48.474976880 +0200
> +++ gcc/cp/cp-gimplify.cc 2025-09-17 16:25:42.436659374 +0200
> @@ -200,6 +200,33 @@ genericize_if_stmt (tree *stmt_p)
> }
> }
>
> + if (IF_STMT_VACUOUS_INIT_P (stmt))
> + {
> + gcc_checking_assert (integer_zerop (cond));
> + gcc_checking_assert (!else_ || !TREE_SIDE_EFFECTS (else_));
> + tree lab = create_artificial_label (UNKNOWN_LOCATION);
> + VACUOUS_INIT_LABEL_P (lab) = 1;
> + tree goto_expr = build_stmt (UNKNOWN_LOCATION, GOTO_EXPR, lab);
> + tree label_expr = build_stmt (UNKNOWN_LOCATION, LABEL_EXPR, lab);
> + if (TREE_CODE (then_) == STATEMENT_LIST)
> + {
> + tree_stmt_iterator i = tsi_start (then_);
> + tsi_link_before (&i, goto_expr, TSI_CONTINUE_LINKING);
> + i = tsi_last (then_);
> + tsi_link_after (&i, label_expr, TSI_CONTINUE_LINKING);
> + stmt = then_;
> + }
> + else
> + {
> + stmt = NULL_TREE;
> + append_to_statement_list (goto_expr, &stmt);
> + append_to_statement_list (then_, &stmt);
> + append_to_statement_list (label_expr, &stmt);
> + }
> + *stmt_p = stmt;
> + return;
> + }
> +
> if (!then_)
> then_ = build_empty_stmt (locus);
> if (!else_)
> @@ -603,6 +630,36 @@ cp_gimplify_arg (tree *arg_p, gimple_seq
>
> }
>
> +/* Emit a decl = {CLOBBER(bob)}; stmt before DECL_EXPR or first
> + TARGET_EXPR gimplification for -flifetime-dse=2. */
> +
> +static void
> +maybe_emit_clobber_object_begin (tree decl, gimple_seq *pre_p)
> +{
> + if (auto_var_p (decl)
> + && TREE_TYPE (decl) != error_mark_node
> + && DECL_NONTRIVIALLY_INITIALIZED_P (decl)
> + /* Don't do it if it is fully initialized. */
> + && DECL_INITIAL (decl) == NULL_TREE
> + && !DECL_HAS_VALUE_EXPR_P (decl)
> + && !OPAQUE_TYPE_P (TREE_TYPE (decl))
I notice this doesn't skip empty types, which could use a comment. I
guess if we're only doing this for the outermost object it isn't a
problem to clobber empty types, but is it useful to clobber them when
there's no data anyway?
> + /* Nor going to have decl = .DEFERRED_INIT (...); added. */
> + && (flag_auto_var_init == AUTO_INIT_UNINITIALIZED
> + || lookup_attribute ("uninitialized", DECL_ATTRIBUTES (decl))
> + || lookup_attribute ("indeterminate", DECL_ATTRIBUTES (decl))))
> + {
> + tree eltype = strip_array_types (TREE_TYPE (decl));
> + if (RECORD_OR_UNION_TYPE_P (eltype))
> + {
> + tree clobber
> + = build_clobber (TREE_TYPE (decl), CLOBBER_OBJECT_BEGIN);
> + gimple *g = gimple_build_assign (decl, clobber);
> + gimple_set_location (g, DECL_SOURCE_LOCATION (decl));
> + gimple_seq_add_stmt_without_update (pre_p, g);
> + }
> + }
> +}
> +
> /* Do C++-specific gimplification. Args are as for gimplify_expr. */
>
> int
> @@ -918,6 +975,10 @@ cp_gimplify_expr (tree *expr_p, gimple_s
> on the rhs of an assignment, as in constexpr-aggr1.C. */
> gcc_checking_assert (!TARGET_EXPR_ELIDING_P (*expr_p)
> || !TREE_ADDRESSABLE (TREE_TYPE (*expr_p)));
> + if (flag_lifetime_dse > 1
> + && TARGET_EXPR_INITIAL (*expr_p)
> + && VOID_TYPE_P (TREE_TYPE (TARGET_EXPR_INITIAL (*expr_p))))
> + maybe_emit_clobber_object_begin (TARGET_EXPR_SLOT (*expr_p), pre_p);
> ret = GS_UNHANDLED;
> break;
>
> @@ -929,6 +990,12 @@ cp_gimplify_expr (tree *expr_p, gimple_s
> ret = GS_OK;
> break;
>
> + case DECL_EXPR:
> + if (flag_lifetime_dse > 1)
> + maybe_emit_clobber_object_begin (DECL_EXPR_DECL (*expr_p), pre_p);
> + ret = GS_UNHANDLED;
> + break;
> +
> case RETURN_EXPR:
> if (TREE_OPERAND (*expr_p, 0)
> && (TREE_CODE (TREE_OPERAND (*expr_p, 0)) == INIT_EXPR
> --- gcc/cp/decl.cc.jj 2025-09-07 22:22:36.171534172 +0200
> +++ gcc/cp/decl.cc 2025-09-17 15:26:23.270005038 +0200
> @@ -76,6 +76,7 @@ enum bad_spec_place {
> static const char *redeclaration_error_message (tree, tree);
>
> static bool decl_jump_unsafe (tree);
> +static bool decl_instrument_init_bypass_p (tree);
> static void require_complete_types_for_parms (tree);
> static tree grok_reference_init (tree, tree, tree, int);
> static tree grokvardecl (tree, tree, tree, const cp_decl_specifier_seq *,
> @@ -173,6 +174,25 @@ vec<tree, va_gc> *static_decls;
> /* vector of keyed classes. */
> vec<tree, va_gc> *keyed_classes;
>
> +/* Used in the direct_goto vector of named_label_use_entry for
> + addresses of the LABEL_DECLs within GOTO_EXPR or asm goto
> + for forward jumps. */
> +
> +struct GTY(()) named_label_fwd_direct_goto {
> + tree *GTY((skip)) direct_goto;
> +};
> +
> +/* Used in the direct_goto vector of named_label_use_entry for
> + addresses of the LABEL_DECLs within GOTO_EXPR or asm goto
> + for backward jumps. */
> +
> +struct GTY(()) named_label_bck_direct_goto {
> + tree *GTY((skip)) direct_goto;
> + /* Number of the decl_instrument_init_bypass_p decls in bad_decls vector
> + at the time this backward goto has been seen. */
> + unsigned n_bad_decls;
> +};
> +
> /* Used only for jumps to as-yet undefined labels, since jumps to
> defined labels can have their validity checked immediately. */
>
> @@ -188,7 +208,11 @@ struct GTY((chain_next ("%h.next"))) nam
> tree names_in_scope;
> /* If the use is a possible destination of a computed goto, a vec of decls
> that aren't destroyed, filled in by poplevel_named_label_1. */
> - vec<tree,va_gc> *computed_goto;
> + vec<tree, va_gc> *computed_goto;
> + /* If the use is a destination of normal goto, a vec of addresses of
> + LABEL_DECLs that might need changing for !!flag_auto_var_init
> + forward jumps across vacuous initializers. */
> + vec<named_label_fwd_direct_goto, va_gc> *direct_goto;
> /* The location of the goto, for error reporting. */
> location_t o_goto_locus;
> /* True if an OpenMP structured block scope has been closed since
> @@ -226,20 +250,29 @@ struct GTY((for_user)) named_label_entry
> /* A list of uses of the label, before the label is defined. */
> named_label_use_entry *uses;
>
> - /* True if we've seen &&label. Appalently we can't use TREE_ADDRESSABLE for
> + /* If the use is a destination of normal goto, a vec of addresses of
> + LABEL_DECLs that might need changing for !!flag_auto_var_init
> + backward jumps across vacuous initializers. */
> + vec<named_label_bck_direct_goto, va_gc> *direct_goto;
> +
> + /* True if we've seen &&label. Apparently we can't use TREE_ADDRESSABLE for
> this, it has a more specific meaning for LABEL_DECL. */
> - bool addressed;
> + bool addressed : 1;
>
> /* The following bits are set after the label is defined, and are
> updated as scopes are popped. They indicate that a jump to the
> label will illegally enter a scope of the given flavor. */
> - bool in_try_scope;
> - bool in_catch_scope;
> - bool in_omp_scope;
> - bool in_transaction_scope;
> - bool in_constexpr_if;
> - bool in_consteval_if;
> - bool in_stmt_expr;
> + bool in_try_scope : 1;
> + bool in_catch_scope : 1;
> + bool in_omp_scope : 1;
> + bool in_transaction_scope : 1;
> + bool in_constexpr_if : 1;
> + bool in_consteval_if : 1;
> + bool in_stmt_expr : 1;
> +
> + /* True if bad_decls chain contains any decl_jump_unsafe decls
> + (rather than just decl_instrument_init_bypass_p). */
> + bool has_bad_decls : 1;
> };
>
> #define named_labels cp_function_chain->x_named_labels
> @@ -403,6 +436,59 @@ sort_labels (const void *a, const void *
> return DECL_UID (label1) > DECL_UID (label2) ? -1 : +1;
> }
>
> +static void adjust_backward_goto (named_label_entry *, tree_stmt_iterator);
> +static named_label_entry *lookup_label_1 (tree, bool);
> +
> +/* Helper of pop_labels, called through cp_walk_tree. Adjust
> + LABEL_EXPRs of named labels, if they are targets of backwards
> + gotos jumping across vacuous initialization for
> + !!flag_auto_var_init. */
> +
> +static tree
> +adjust_backward_gotos (tree *tp, int *walk_subtrees, void *data)
> +{
> + tree t = *tp;
> + switch (TREE_CODE (t))
> + {
> + case LABEL_EXPR:
> + if (DECL_NAME (LABEL_EXPR_LABEL (t)))
> + {
> + named_label_entry *ent
> + = lookup_label_1 (DECL_NAME (LABEL_EXPR_LABEL (t)), false);
> + if (ent->direct_goto)
> + {
> + *tp = alloc_stmt_list ();
> + append_to_statement_list_force (t, tp);
> + adjust_backward_goto (ent, tsi_last (*tp));
> + }
> + }
> + *walk_subtrees = 0;
> + break;
> + case STATEMENT_LIST:
Handling STATEMENT_LIST specially is an optimization to avoid the
alloc_stmt_list above, right? Seems worth a comment.
> + {
> + tree_stmt_iterator i;
> + *walk_subtrees = 0;
> + for (i = tsi_start (t); !tsi_end_p (i); tsi_next (&i))
> + if (TREE_CODE (tsi_stmt (i)) != LABEL_EXPR)
> + cp_walk_tree (tsi_stmt_ptr (i), adjust_backward_gotos,
> + data, (hash_set<tree> *) data);
> + else if (DECL_NAME (LABEL_EXPR_LABEL (tsi_stmt (i))))
> + {
> + named_label_entry *ent
> + = lookup_label_1 (DECL_NAME (LABEL_EXPR_LABEL (tsi_stmt (i))),
> + false);
> + if (ent->direct_goto)
> + adjust_backward_goto (ent, i);
> + }
> + break;
> + }
> + default:
> + if (TYPE_P (t))
> + *walk_subtrees = 0;
> + }
> + return NULL_TREE;
> +}
> +
> /* At the end of a function, all labels declared within the function
> go out of scope. BLOCK is the top-level block for the
> function. */
> @@ -420,8 +506,23 @@ pop_labels (tree block)
> table implementation changes. */
> auto_vec<tree, 32> labels (named_labels->elements ());
> hash_table<named_label_hash>::iterator end (named_labels->end ());
> - for (hash_table<named_label_hash>::iterator iter
> - (named_labels->begin ()); iter != end; ++iter)
> +
> + if (flag_auto_var_init > AUTO_INIT_UNINITIALIZED)
> + {
> + for (decltype (end) iter (named_labels->begin ()); iter != end; ++iter)
> + {
> + named_label_entry *ent = *iter;
> + if (ent->direct_goto)
> + {
> + hash_set<tree> pset;
> + cp_walk_tree (&DECL_SAVED_TREE (current_function_decl),
> + adjust_backward_gotos, &pset, &pset);
> + break;
> + }
> + }
> + }
> +
> + for (decltype (end) iter (named_labels->begin ()); iter != end; ++iter)
> {
> named_label_entry *ent = *iter;
>
> @@ -551,6 +652,11 @@ poplevel_named_label_1 (named_label_entr
> ? DECL_CHAIN (decl)
> : TREE_CHAIN (decl)))
> if (decl_jump_unsafe (decl))
> + {
> + vec_safe_push (ent->bad_decls, decl);
> + ent->has_bad_decls = true;
> + }
> + else if (decl_instrument_init_bypass_p (decl))
> vec_safe_push (ent->bad_decls, decl);
>
> ent->binding_level = obl;
> @@ -2930,6 +3036,19 @@ duplicate_decls (tree newdecl, tree oldd
> {
> DECL_ATTRIBUTES (newarg)
> = (*targetm.merge_decl_attributes) (oldarg, newarg);
> + if (lookup_attribute (NULL, "indeterminate",
> + DECL_ATTRIBUTES (newarg))
> + && !lookup_attribute (NULL, "indeterminate",
> + DECL_ATTRIBUTES (oldarg)))
> + {
> + auto_diagnostic_group d;
> + error_at (DECL_SOURCE_LOCATION (newarg),
> + "%<indeterminate%> attribute not specified "
> + "for parameter %qD on the first declaration of "
> + "its function", newarg);
> + inform (DECL_SOURCE_LOCATION (oldarg),
> + "earlier declaration");
> + }
> DECL_ATTRIBUTES (oldarg) = DECL_ATTRIBUTES (newarg);
> }
>
> @@ -3733,6 +3852,233 @@ decl_jump_unsafe (tree decl)
> || variably_modified_type_p (type, NULL_TREE)));
> }
>
> +/* Returns true if decl is an automatic variable with vacuous initialization
> + except when it is [[indeterminate]] or [[gnu::uninitialized]].
> + Jumps across such initialization need to be instrumented for
> + !!flag_auto_var_init. */
> +
> +static bool
> +decl_instrument_init_bypass_p (tree decl)
> +{
> + tree type = TREE_TYPE (decl);
> +
> + return (flag_auto_var_init > AUTO_INIT_UNINITIALIZED
> + && type != error_mark_node
> + && VAR_P (decl)
> + && !TREE_STATIC (decl)
> + && !DECL_EXTERNAL (decl)
> + && !(DECL_NONTRIVIALLY_INITIALIZED_P (decl)
> + || variably_modified_type_p (type, NULL_TREE))
> + && !lookup_attribute (NULL, "indeterminate", DECL_ATTRIBUTES (decl))
> + && !lookup_attribute ("uninitialized", DECL_ATTRIBUTES (decl))
> + && !DECL_HAS_VALUE_EXPR_P (decl));
> +}
> +
> +/* Build .DEFERRED_INIT call for DECL. */
> +
> +static tree
> +build_deferred_init_call (tree decl)
> +{
> + tree decl_size_arg = TYPE_SIZE_UNIT (TREE_TYPE (decl));
> + tree init_type_arg = build_int_cst (integer_type_node,
> + (int) flag_auto_var_init);
> + location_t loc = DECL_SOURCE_LOCATION (decl);
> + tree decl_name;
> +
> + if (DECL_NAME (decl))
> + decl_name = build_string_literal (DECL_NAME (decl));
> + else
> + {
> + char decl_name_anonymous[3 + (HOST_BITS_PER_INT + 2) / 3];
> + sprintf (decl_name_anonymous, "D.%u", DECL_UID (decl));
> + decl_name = build_string_literal (decl_name_anonymous);
> + }
> +
> + tree call = build_call_expr_internal_loc (loc, IFN_DEFERRED_INIT,
> + TREE_TYPE (decl), 3,
> + decl_size_arg, init_type_arg,
> + decl_name);
> + tree ret = build2_loc (loc, MODIFY_EXPR, void_type_node, decl, call);
> + return build_stmt (loc, EXPR_STMT, ret);
> +}
> +
> +/* Emit before ITER (and any labels/case labels before it) code like
> + if (0)
> + {
> + l1:
> + v4 = .DEFERRED_INIT (sizeof (v4), ?, "v4");
> + v3 = .DEFERRED_INIT (sizeof (v3), ?, "v3");
> + v2 = .DEFERRED_INIT (sizeof (v2), ?, "v2");
> + v1 = .DEFERRED_INIT (sizeof (v1), ?, "v1");
> + }
> + and return l1 label, or if it already exists, assert it has the
> + .DEFERRED_INIT calls for the right decls in the right order and
> + amend it, either by adding extra labels in between or further
> + ,DEFERRED_INIT calls before the first label and extra label before
> + that. If CASE_LABEL is non-NULL, emit that CASE_LABEL_EXPR instead
> + of adding a label. DECLS points to an array of NDECLS VAR_DECLs
> + which should be initialized. */
> +
> +static tree
> +maybe_add_deferred_init_calls (tree_stmt_iterator iter, tree case_label,
> + tree *decls, unsigned ndecls)
The logic in this function could use more comments.
> +{
> + tree lab = NULL_TREE;
> + for (; !tsi_end_p (iter); tsi_prev (&iter))
> + {
> + switch (TREE_CODE (tsi_stmt (iter)))
> + {
> + case LABEL_EXPR:
> + case CASE_LABEL_EXPR:
> + case DEBUG_BEGIN_STMT:
> + continue;
> + default:
> + break;
> + }
> + break;
> + }
> + if (!tsi_end_p (iter)
> + && TREE_CODE (tsi_stmt (iter)) == IF_STMT
> + && IF_STMT_VACUOUS_INIT_P (tsi_stmt (iter)))
> + {
> + tree then_clause = THEN_CLAUSE (tsi_stmt (iter));
> + iter = tsi_last (then_clause);
> + bool add = false;
> + for (unsigned int i = 0; i < ndecls; ++i)
> + {
> + tree decl = decls[i];
> + if (!add)
> + {
> + while (!tsi_end_p (iter)
> + && (TREE_CODE (tsi_stmt (iter)) == LABEL_EXPR
> + || (TREE_CODE (tsi_stmt (iter)) == CASE_LABEL_EXPR
> + && !case_label)))
> + tsi_prev (&iter);
Why is this loop inside the loop over the decls? Ah, in case we have
labels between groups of inits?
> + if (tsi_end_p (iter))
> + {
> + add = true;
> + iter = tsi_start (then_clause);
So we want to insert if there's nothing left at the beginning after we
skip labels.
> + }
> + else
> + {
> + tree t = tsi_stmt (iter);
> + gcc_checking_assert (TREE_CODE (t) == EXPR_STMT);
> + t = EXPR_STMT_EXPR (t);
> + gcc_checking_assert (TREE_CODE (t) == MODIFY_EXPR
> + && TREE_OPERAND (t, 0) == decl
> + && (TREE_CODE (TREE_OPERAND (t, 1))
> + == CALL_EXPR));
> + t = TREE_OPERAND (t, 1);
> + gcc_checking_assert (CALL_EXPR_FN (t) == NULL_TREE
> + && (CALL_EXPR_IFN (t)
> + == IFN_DEFERRED_INIT));
> + tsi_prev (&iter);
> + }
> + }
> + if (add)
> + {
> + tree t = build_deferred_init_call (decl);
> + STMT_IS_FULL_EXPR_P (t) = 1;
> + tsi_link_before (&iter, t, TSI_CONTINUE_LINKING);
> + }
> + }
> + if (!add)
> + {
> + while (!tsi_end_p (iter)
> + && !case_label
> + && TREE_CODE (tsi_stmt (iter)) == CASE_LABEL_EXPR)
> + tsi_prev (&iter);
So here we're deciding if we need another label, and we look through
case labels to see if we already have the label we want?
OK with the added comments.
Jason
> + if (tsi_end_p (iter))
> + {
> + add = true;
> + iter = tsi_start (then_clause);
> + }
> + else if (!case_label
> + && TREE_CODE (tsi_stmt (iter)) == LABEL_EXPR)
> + lab = LABEL_EXPR_LABEL (tsi_stmt (iter));
> + else
> + {
> + gcc_checking_assert (case_label
> + || (TREE_CODE (tsi_stmt (iter))
> + == EXPR_STMT));
> + add = true;
> + tsi_next (&iter);
> + gcc_checking_assert (!tsi_end_p (iter));
> + }
> + }
> + if (add)
> + {
> + tree t;
> + if (case_label)
> + t = case_label;
> + else
> + {
> + lab = create_artificial_label (UNKNOWN_LOCATION);
> + t = build_stmt (UNKNOWN_LOCATION, LABEL_EXPR, lab);
> + }
> + tsi_link_before (&iter, t, TSI_CONTINUE_LINKING);
> + }
> + }
> + else
> + {
> + tree new_then = push_stmt_list ();
> + if (!case_label)
> + {
> + lab = create_artificial_label (UNKNOWN_LOCATION);
> + add_stmt (build_stmt (UNKNOWN_LOCATION, LABEL_EXPR, lab));
> + }
> + else
> + add_stmt (case_label);
> + for (unsigned int i = ndecls; i; --i)
> + add_stmt (build_deferred_init_call (decls[i - 1]));
> + new_then = pop_stmt_list (new_then);
> + tree stmt = build4 (IF_STMT, void_type_node, boolean_false_node,
> + new_then, void_node, NULL_TREE);
> + IF_STMT_VACUOUS_INIT_P (stmt) = 1;
> + if (tsi_end_p (iter))
> + {
> + iter = tsi_start (iter.container);
> + tsi_link_before (&iter, stmt, TSI_SAME_STMT);
> + }
> + else
> + tsi_link_after (&iter, stmt, TSI_CONTINUE_LINKING);
> + }
> + return lab;
> +}
> +
> +/* Adjust backward gotos to named label ENT if they jump over vacuous
> + initializers if !!flag_auto_var_init. ITER is the location of
> + LABEL_EXPR for that named label. */
> +
> +static void
> +adjust_backward_goto (named_label_entry *ent, tree_stmt_iterator iter)
> +{
> + auto_vec<tree, 4> decls;
> + unsigned int i, max_cnt = ent->direct_goto->last ().n_bad_decls;
> + tree decl;
> + FOR_EACH_VEC_SAFE_ELT (ent->bad_decls, i, decl)
> + if (!decl_jump_unsafe (decl))
> + {
> + gcc_checking_assert (decl_instrument_init_bypass_p (decl));
> + decls.safe_push (decl);
> + if (decls.length () == max_cnt)
> + break;
> + }
> + named_label_bck_direct_goto *dgoto;
> + unsigned last = 0;
> + tree lab = NULL_TREE;
> + FOR_EACH_VEC_SAFE_ELT_REVERSE (ent->direct_goto, i, dgoto)
> + {
> + if (dgoto->n_bad_decls != last)
> + {
> + last = dgoto->n_bad_decls;
> + lab = maybe_add_deferred_init_calls (iter, NULL_TREE,
> + decls.address (), last);
> + }
> + *dgoto->direct_goto = lab;
> + }
> +}
> +
> /* A subroutine of check_previous_goto_1 and check_goto to identify a branch
> to the user. */
>
> @@ -3760,13 +4106,19 @@ identify_goto (tree decl, location_t loc
> is OK. DECL is the LABEL_DECL or 0; LEVEL is the binding_level for
> the jump context; NAMES are the names in scope in LEVEL at the jump
> context; LOCUS is the source position of the jump or 0. COMPUTED
> - is a vec of decls if the jump is a computed goto. Returns
> - true if all is well. */
> + is a vec of decls if the jump is a computed goto. DIRECT_GOTO is a
> + vec of pointers to LABEL_DECLs that might need adjusting if vacuous
> + initializations are crossed for !!flag_auto_var_init. CASE_LABEL is
> + CASE_LABEL_EXPR to be moved if needed for the check_switch_goto case.
> + Returns non-zero if all is well, 2 if any vacuous initializers were
> + crossed. */
>
> -static bool
> -check_previous_goto_1 (tree decl, cp_binding_level* level, tree names,
> +static int
> +check_previous_goto_1 (tree decl, cp_binding_level *level, tree names,
> bool exited_omp, const location_t *locus,
> - vec<tree,va_gc> *computed)
> + vec<tree, va_gc> *computed,
> + vec<named_label_fwd_direct_goto, va_gc> *direct_goto,
> + tree case_label)
> {
> auto_diagnostic_group d;
> cp_binding_level *b;
> @@ -3774,6 +4126,8 @@ check_previous_goto_1 (tree decl, cp_bin
> int identified = 0;
> bool saw_eh = false, saw_omp = false, saw_tm = false, saw_cxif = false;
> bool saw_ceif = false, saw_se = false;
> + auto_vec<tree> vacuous_decls;
> + bool vacuous_inits = false;
>
> if (exited_omp)
> {
> @@ -3796,7 +4150,15 @@ check_previous_goto_1 (tree decl, cp_bin
> {
> bool problem = decl_jump_unsafe (new_decls);
> if (! problem)
> - continue;
> + {
> + if (decl_instrument_init_bypass_p (new_decls))
> + {
> + if (direct_goto || case_label)
> + vacuous_decls.safe_push (new_decls);
> + vacuous_inits = true;
> + }
> + continue;
> + }
>
> if (!identified)
> {
> @@ -3895,7 +4257,30 @@ check_previous_goto_1 (tree decl, cp_bin
> }
> }
>
> - return !identified;
> + if (!vacuous_decls.is_empty () && !seen_error ())
> + {
> + tree_stmt_iterator iter = tsi_last (cur_stmt_list);
> + if (case_label)
> + {
> + gcc_checking_assert (tsi_stmt (iter) == case_label);
> + tsi_delink (&iter);
> + iter = tsi_last (cur_stmt_list);
> + }
> + tree lab = maybe_add_deferred_init_calls (iter, case_label,
> + vacuous_decls.address (),
> + vacuous_decls.length ());
> + if (lab)
> + {
> + unsigned int i;
> + named_label_fwd_direct_goto *dgoto;
> + FOR_EACH_VEC_SAFE_ELT (direct_goto, i, dgoto)
> + *dgoto->direct_goto = lab;
> + }
> + }
> +
> + if (identified)
> + return 0;
> + return vacuous_inits ? 2 : 1;
> }
>
> static void
> @@ -3903,24 +4288,27 @@ check_previous_goto (tree decl, struct n
> {
> check_previous_goto_1 (decl, use->binding_level,
> use->names_in_scope, use->in_omp_scope,
> - &use->o_goto_locus, use->computed_goto);
> + &use->o_goto_locus, use->computed_goto,
> + use->direct_goto, NULL_TREE);
> + vec_free (use->direct_goto);
> }
>
> -static bool
> -check_switch_goto (cp_binding_level* level)
> +static int
> +check_switch_goto (cp_binding_level *level, tree case_label)
> {
> return check_previous_goto_1 (NULL_TREE, level, level->names,
> - false, NULL, nullptr);
> + false, NULL, nullptr, nullptr, case_label);
> }
>
> -/* Check that a new jump to a label ENT is OK. COMPUTED is true
> - if this is a possible target of a computed goto. */
> +/* Check that a new jump to a label ENT is OK. DECLP is a pointer
> + to a LABEL_DECL for direct gotos and NULL for computed gotos. */
>
> void
> -check_goto_1 (named_label_entry *ent, bool computed)
> +check_goto_1 (named_label_entry *ent, tree *declp)
> {
> auto_diagnostic_group d;
> tree decl = ent->label_decl;
> + bool computed = declp == NULL;
>
> /* If the label hasn't been defined yet, defer checking. */
> if (! DECL_INITIAL (decl))
> @@ -3928,8 +4316,14 @@ check_goto_1 (named_label_entry *ent, bo
> /* Don't bother creating another use if the last goto had the
> same data, and will therefore create the same set of errors. */
> if (ent->uses
> + && ent->uses->binding_level == current_binding_level
> && ent->uses->names_in_scope == current_binding_level->names)
> - return;
> + {
> + if (declp && flag_auto_var_init > AUTO_INIT_UNINITIALIZED)
> + vec_safe_push (ent->uses->direct_goto,
> + named_label_fwd_direct_goto { declp });
> + return;
> + }
>
> named_label_use_entry *new_use
> = ggc_alloc<named_label_use_entry> ();
> @@ -3938,6 +4332,10 @@ check_goto_1 (named_label_entry *ent, bo
> new_use->o_goto_locus = input_location;
> new_use->in_omp_scope = false;
> new_use->computed_goto = computed ? make_tree_vector () : nullptr;
> + new_use->direct_goto = nullptr;
> + if (declp && flag_auto_var_init > AUTO_INIT_UNINITIALIZED)
> + vec_safe_push (new_use->direct_goto,
> + named_label_fwd_direct_goto { declp });
>
> new_use->next = ent->uses;
> ent->uses = new_use;
> @@ -3948,11 +4346,12 @@ check_goto_1 (named_label_entry *ent, bo
> int identified = 0;
> tree bad;
> unsigned ix;
> + unsigned n_bad_decls = 0;
>
> if (ent->in_try_scope || ent->in_catch_scope || ent->in_transaction_scope
> || ent->in_constexpr_if || ent->in_consteval_if
> || ent->in_omp_scope || ent->in_stmt_expr
> - || !vec_safe_is_empty (ent->bad_decls))
> + || ent->has_bad_decls)
> {
> enum diagnostics::kind diag_kind = diagnostics::kind::permerror;
> if (ent->in_try_scope || ent->in_catch_scope || ent->in_constexpr_if
> @@ -3967,8 +4366,14 @@ check_goto_1 (named_label_entry *ent, bo
> FOR_EACH_VEC_SAFE_ELT (ent->bad_decls, ix, bad)
> {
> bool problem = decl_jump_unsafe (bad);
> + if (!problem)
> + {
> + gcc_checking_assert (decl_instrument_init_bypass_p (bad));
> + n_bad_decls++;
> + continue;
> + }
>
> - if (problem && DECL_ARTIFICIAL (bad))
> + if (DECL_ARTIFICIAL (bad))
> {
> /* Can't skip init of __exception_info. */
> if (identified == 1)
> @@ -4075,16 +4480,21 @@ check_goto_1 (named_label_entry *ent, bo
> break;
> }
> }
> +
> + if (n_bad_decls && declp)
> + vec_safe_push (ent->direct_goto,
> + named_label_bck_direct_goto { declp, n_bad_decls });
> }
>
> -/* Check that a new jump to a label DECL is OK. Called by
> +/* Check that a new jump to a label *DECLP is OK. Called by
> finish_goto_stmt. */
>
> void
> -check_goto (tree decl)
> +check_goto (tree *declp)
> {
> if (!named_labels)
> return;
> + tree decl = *declp;
> if (TREE_CODE (decl) != LABEL_DECL)
> {
> /* We don't know where a computed goto is jumping,
> @@ -4095,7 +4505,7 @@ check_goto (tree decl)
> {
> auto ent = *iter;
> if (ent->addressed)
> - check_goto_1 (ent, true);
> + check_goto_1 (ent, NULL);
> }
> }
> else
> @@ -4104,7 +4514,7 @@ check_goto (tree decl)
> named_label_entry **slot
> = named_labels->find_slot_with_hash (DECL_NAME (decl), hash, NO_INSERT);
> named_label_entry *ent = *slot;
> - check_goto_1 (ent, false);
> + check_goto_1 (ent, declp);
> }
> }
>
> @@ -4359,7 +4769,8 @@ finish_case_label (location_t loc, tree
> if (cond && TREE_CODE (cond) == TREE_LIST)
> cond = TREE_VALUE (cond);
>
> - if (!check_switch_goto (switch_stack->level))
> + int chk_switch_goto = check_switch_goto (switch_stack->level, NULL_TREE);
> + if (!chk_switch_goto)
> return error_mark_node;
>
> type = SWITCH_STMT_TYPE (switch_stack->switch_stmt);
> @@ -4371,6 +4782,9 @@ finish_case_label (location_t loc, tree
>
> r = c_add_case_label (loc, switch_stack->cases, cond, low_value, high_value);
>
> + if (r != error_mark_node && chk_switch_goto == 2)
> + check_switch_goto (switch_stack->level, r);
> +
> /* After labels, make any new cleanups in the function go into their
> own new (temporary) binding contour. */
> for (p = current_binding_level;
> @@ -19316,18 +19730,6 @@ start_preparsed_function (tree decl1, tr
> start_function_contracts (decl1);
>
> if (!processing_template_decl
> - && (flag_lifetime_dse > 1)
> - && DECL_CONSTRUCTOR_P (decl1)
> - && !DECL_CLONED_FUNCTION_P (decl1)
> - /* Clobbering an empty base is harmful if it overlays real data. */
> - && !is_empty_class (current_class_type)
> - /* We can't clobber safely for an implicitly-defined default constructor
> - because part of the initialization might happen before we enter the
> - constructor, via AGGR_INIT_ZERO_FIRST (c++/68006). */
> - && !implicit_default_ctor_p (decl1))
> - finish_expr_stmt (build_clobber_this (CLOBBER_OBJECT_BEGIN));
> -
> - if (!processing_template_decl
> && DECL_CONSTRUCTOR_P (decl1)
> && sanitize_flags_p (SANITIZE_VPTR)
> && !DECL_CLONED_FUNCTION_P (decl1)
> --- gcc/cp/parser.cc.jj 2025-09-07 22:22:36.183533988 +0200
> +++ gcc/cp/parser.cc 2025-09-17 10:20:47.846835880 +0200
> @@ -31179,10 +31179,10 @@ cp_parser_asm_label_list (cp_parser* par
> if (TREE_CODE (label) == LABEL_DECL)
> {
> TREE_USED (label) = 1;
> - check_goto (label);
> name = build_string (IDENTIFIER_LENGTH (identifier),
> IDENTIFIER_POINTER (identifier));
> labels = tree_cons (name, label, labels);
> + check_goto (&TREE_VALUE (labels));
> }
> }
> /* If the next token is not a `,', then the list is
> --- gcc/cp/semantics.cc.jj 2025-09-15 19:51:49.395488843 +0200
> +++ gcc/cp/semantics.cc 2025-09-17 10:20:47.828836127 +0200
> @@ -919,10 +919,12 @@ finish_goto_stmt (tree destination)
> }
> }
>
> - check_goto (destination);
> -
> add_stmt (build_predict_expr (PRED_GOTO, NOT_TAKEN));
> - return add_stmt (build_stmt (input_location, GOTO_EXPR, destination));
> +
> + tree stmt = build_stmt (input_location, GOTO_EXPR, destination);
> + check_goto (&TREE_OPERAND (stmt, 0));
> +
> + return add_stmt (stmt);
> }
>
> /* Returns true if T corresponds to an assignment operator expression. */
> --- gcc/cp/tree.cc.jj 2025-09-15 19:51:49.397488817 +0200
> +++ gcc/cp/tree.cc 2025-09-17 10:20:47.816836292 +0200
> @@ -5591,6 +5591,23 @@ handle_maybe_unused_attribute (tree *nod
> return ret;
> }
>
> +/* The C++26 [[indeterminate]] attribute. */
> +
> +static tree
> +handle_indeterminate_attribute (tree *node, tree name, tree, int,
> + bool *no_add_attrs)
> +{
> + if (TREE_CODE (*node) != PARM_DECL
> + && (!VAR_P (*node) || is_global_var (*node)))
> + {
> + pedwarn (input_location, OPT_Wattributes,
> + "%qE on declaration other than parameter or automatic variable",
> + name);
> + *no_add_attrs = true;
> + }
> + return NULL_TREE;
> +}
> +
> /* Table of valid C++ attributes. */
> static const attribute_spec cxx_gnu_attributes[] =
> {
> @@ -5630,6 +5647,8 @@ static const attribute_spec std_attribut
> handle_noreturn_attribute, attr_noreturn_exclusions },
> { "carries_dependency", 0, 0, true, false, false, false,
> handle_carries_dependency_attribute, NULL },
> + { "indeterminate", 0, 0, true, false, false, false,
> + handle_indeterminate_attribute, NULL },
> { "pre", 0, -1, false, false, false, false,
> handle_contract_attribute, NULL },
> { "post", 0, -1, false, false, false, false,
> --- gcc/testsuite/g++.dg/cpp1y/vla-initlist1.C.jj 2020-01-14 20:02:46.782609849 +0100
> +++ gcc/testsuite/g++.dg/cpp1y/vla-initlist1.C 2025-09-17 11:23:55.875695269 +0200
> @@ -1,5 +1,4 @@
> // { dg-do run { target c++11 } }
> -// { dg-skip-if "power overwrites two slots of array i" { "power*-*-*" } }
> // { dg-options "-Wno-vla" }
>
> #include <initializer_list>
> @@ -7,7 +6,7 @@
> struct A
> {
> int i;
> - A(std::initializer_list<int>) { }
> + A(std::initializer_list<int>) : i{43} { }
> A(int i): i{i} { }
> ~A() {}
> };
> @@ -18,7 +17,7 @@ int main(int argc, char **argv)
> { int i[x] = { 42, 42, 42, 42 }; }
> {
> A a[x] = { argc };
> - if (a[1].i != 42)
> + if (a[1].i != 43)
> __builtin_abort ();
> }
> }
> --- gcc/testsuite/g++.dg/cpp26/attr-indeterminate1.C.jj 2025-09-17 10:20:47.847835866 +0200
> +++ gcc/testsuite/g++.dg/cpp26/attr-indeterminate1.C 2025-09-17 10:20:47.847835866 +0200
> @@ -0,0 +1,154 @@
> +// C++ 26 P2795R5 - Erroneous behaviour for uninitialized reads
> +// { dg-do compile { target c++11 } }
> +
> +int arr[2];
> +struct S { int a, b; };
> +S arr2[2];
> +
> +void
> +foo ([[indeterminate]] int n, int n2 [[indeterminate]], int n3 [[indeterminate]] [2])
> +{
> + [[indeterminate]] int x1, x11, x12, x13;
> + int x14, x15 [[indeterminate]];
> + [[indeterminate ("foobar")]] int x2; // { dg-error "'indeterminate' attribute does not take any arguments" }
> + // { dg-error "expected primary-expression before 'int'" "" { target *-*-* } .-1 }
> + [[indeterminate (0)]] int x3; // { dg-error "'indeterminate' attribute does not take any arguments" }
> + // { dg-error "expected primary-expression before 'int'" "" { target *-*-* } .-1 }
> + [[indeterminate ("foo", "bar", "baz")]] int x4;// { dg-error "'indeterminate' attribute does not take any arguments" }
> + // { dg-error "expected primary-expression before 'int'" "" { target *-*-* } .-1 }
> + [[indeterminate (0, 1, 2)]] int x5; // { dg-error "'indeterminate' attribute does not take any arguments" }
> + // { dg-error "expected primary-expression before 'int'" "" { target *-*-* } .-1 }
> +
> + auto a = [] [[indeterminate]] () {}; // { dg-error "'indeterminate' on declaration other than parameter or automatic variable" }
> + auto b = [] constexpr [[indeterminate]] {}; // { dg-warning "'indeterminate' attribute does not apply to types" }
> + // { dg-error "parameter declaration before lambda declaration specifiers only optional with" "" { target c++20_down } .-1 }
> + // { dg-error "'constexpr' lambda only available with" "" { target c++14_down } .-2 }
> + auto c = [] noexcept [[indeterminate]] {}; // { dg-warning "'indeterminate' attribute does not apply to types" }
> + // { dg-error "parameter declaration before lambda exception specification only optional with" "" { target c++20_down } .-1 }
> + auto d = [] () [[indeterminate]] {}; // { dg-warning "'indeterminate' attribute does not apply to types" }
> + auto e = new int [n] [[indeterminate]]; // { dg-warning "attributes ignored on outermost array type in new expression" }
> + auto e2 = new int [n] [[indeterminate]] [42]; // { dg-warning "attributes ignored on outermost array type in new expression" }
> + auto f = new int [n][42] [[indeterminate]]; // { dg-warning "'indeterminate' attribute does not apply to types" }
> + [[indeterminate]]; // { dg-warning "attributes at the beginning of statement are ignored" }
> + [[indeterminate]] {} // { dg-warning "attributes at the beginning of statement are ignored" }
> + [[indeterminate]] if (true) {} // { dg-warning "attributes at the beginning of statement are ignored" }
> + [[indeterminate]] while (false) {} // { dg-warning "attributes at the beginning of statement are ignored" }
> + [[indeterminate]] goto lab; // { dg-warning "attributes at the beginning of statement are ignored" }
> + [[indeterminate]] lab:; // { dg-error "'indeterminate' on declaration other than parameter or automatic variable" }
> + [[indeterminate]] try {} catch (int) {} // { dg-warning "attributes at the beginning of statement are ignored" }
> + if ([[indeterminate]] int x = 0) {}
> + switch (n)
> + {
> + [[indeterminate]] case 1: // { dg-error "'indeterminate' on declaration other than parameter or automatic variable" }
> + [[indeterminate]] break; // { dg-warning "attributes at the beginning of statement are ignored" }
> + [[indeterminate]] default: // { dg-error "'indeterminate' on declaration other than parameter or automatic variable" }
> + break;
> + }
> + for ([[indeterminate]] auto a : arr) {}
> + for ([[indeterminate]] auto [a, b] : arr2) {} // { dg-error "structured bindings only available with" "" { target c++14_down } }
> + [[indeterminate]] asm (""); // { dg-warning "attributes ignored on 'asm' declaration" }
> + try {} catch ([[indeterminate]] int x) {}
> + try {} catch ([[indeterminate]] int) {}
> + try {} catch (int [[indeterminate]] x) {} // { dg-warning "attribute ignored" }
> + try {} catch (int [[indeterminate]]) {} // { dg-warning "attribute ignored" }
> + try {} catch (int x [[indeterminate]]) {}
> +}
> +
> +[[indeterminate]] int bar (); // { dg-error "'indeterminate' on declaration other than parameter or automatic variable" }
> +using foobar [[indeterminate]] = int; // { dg-error "'indeterminate' on declaration other than parameter or automatic variable" }
> +[[indeterminate]] int a; // { dg-error "'indeterminate' on declaration other than parameter or automatic variable" }
> +[[indeterminate]] auto [b, c] = arr; // { dg-error "'indeterminate' on declaration other than parameter or automatic variable" }
> + // { dg-error "structured bindings only available with" "" { target c++14_down } .-1 }
> +[[indeterminate]]; // { dg-warning "attribute ignored" }
> +inline [[indeterminate]] void baz () {} // { dg-warning "attribute ignored" }
> + // { dg-error "standard attributes in middle of decl-specifiers" "" { target *-*-* } .-1 }
> +constexpr [[indeterminate]] int qux () { return 0; } // { dg-warning "attribute ignored" }
> + // { dg-error "standard attributes in middle of decl-specifiers" "" { target *-*-* } .-1 }
> +int [[indeterminate]] d; // { dg-warning "attribute ignored" }
> +int const [[indeterminate]] e = 1; // { dg-warning "attribute ignored" }
> +struct A {} [[indeterminate]]; // { dg-warning "attribute ignored in declaration of 'struct A'" }
> +struct A [[indeterminate]]; // { dg-warning "attribute ignored" }
> +struct A [[indeterminate]] a1; // { dg-warning "attribute ignored" }
> +A [[indeterminate]] a2; // { dg-warning "attribute ignored" }
> +enum B { B0 } [[indeterminate]]; // { dg-warning "attribute ignored in declaration of 'enum B'" }
> +enum B [[indeterminate]]; // { dg-warning "attribute ignored" }
> +enum B [[indeterminate]] b1; // { dg-warning "attribute ignored" }
> +B [[indeterminate]] b2; // { dg-warning "attribute ignored" }
> +struct [[indeterminate]] C {}; // { dg-warning "'indeterminate' attribute does not apply to types" }
> +int f [[indeterminate]]; // { dg-error "'indeterminate' on declaration other than parameter or automatic variable" }
> +int g[2] [[indeterminate]]; // { dg-warning "'indeterminate' attribute does not apply to types" }
> +int g2 [[indeterminate]] [2]; // { dg-error "'indeterminate' on declaration other than parameter or automatic variable" }
> +int corge () [[indeterminate]]; // { dg-warning "'indeterminate' attribute does not apply to types" }
> +int *[[indeterminate]] h; // { dg-warning "'indeterminate' attribute does not apply to types" }
> +int & [[indeterminate]] i = f; // { dg-warning "'indeterminate' attribute does not apply to types" }
> +int && [[indeterminate]] j = 0; // { dg-warning "'indeterminate' attribute does not apply to types" }
> +int S::* [[indeterminate]] k; // { dg-warning "'indeterminate' attribute does not apply to types" }
> +auto l = sizeof (int [2] [[indeterminate]]); // { dg-warning "'indeterminate' attribute does not apply to types" }
> +int freddy ([[indeterminate]] int a,
> + [[indeterminate]] int,
> + [[indeterminate]] int c = 0,
> + [[indeterminate]] int = 0);
> +void
> +corge ([[indeterminate]] int a,
> + [[indeterminate]] int,
> + [[indeterminate]] int c = 0,
> + [[indeterminate]] int = 0)
> +{
> +}
> +[[indeterminate]] void
> +garply () // { dg-error "'indeterminate' on declaration other than parameter or automatic variable" }
> +{
> +}
> +int grault (int [[indeterminate]] a, // { dg-warning "attribute ignored" }
> + int [[indeterminate]], // { dg-warning "attribute ignored" }
> + int [[indeterminate]] c = 0, // { dg-warning "attribute ignored" }
> + int [[indeterminate]] = 0); // { dg-warning "attribute ignored" }
> +void
> +waldo (int [[indeterminate]] a, // { dg-warning "attribute ignored" }
> + int [[indeterminate]], // { dg-warning "attribute ignored" }
> + int [[indeterminate]] c = 0, // { dg-warning "attribute ignored" }
> + int [[indeterminate]] = 0) // { dg-warning "attribute ignored" }
> +{
> +}
> +int plugh (int a [[indeterminate]],
> + int b [[indeterminate]] = 0);
> +void
> +thud (int a [[indeterminate]],
> + int b [[indeterminate]] = 0)
> +{
> +}
> +enum [[indeterminate]] D { D0 }; // { dg-warning "'indeterminate' attribute does not apply to types" }
> +enum class [[indeterminate]] E { E0 }; // { dg-warning "'indeterminate' attribute does not apply to types" }
> +enum F {};
> +enum [[indeterminate]] F; // { dg-warning "'indeterminate' attribute does not apply to types" }
> +enum G {
> + G0 [[indeterminate]], // { dg-error "'indeterminate' on declaration other than parameter or automatic variable" }
> + G1 [[indeterminate]] = 2 // { dg-error "'indeterminate' on declaration other than parameter or automatic variable" }
> +};
> +namespace [[indeterminate]] H { using H0 = int; }// { dg-warning "'indeterminate' attribute directive ignored" }
> +namespace [[indeterminate]] {} // { dg-warning "'indeterminate' attribute directive ignored" }
> +[[indeterminate]] using namespace H; // { dg-warning "'indeterminate' attribute directive ignored" }
> +struct [[indeterminate]] I // { dg-warning "'indeterminate' attribute does not apply to types" }
> +{
> + [[indeterminate]]; // { dg-error "declaration does not declare anything" }
> + [[indeterminate]] int i; // { dg-error "'indeterminate' on declaration other than parameter or automatic variable" }
> + [[indeterminate]] int foo (); // { dg-error "'indeterminate' on declaration other than parameter or automatic variable" }
> + [[indeterminate]] int bar () { return 1; } // { dg-error "'indeterminate' on declaration other than parameter or automatic variable" }
> + [[indeterminate]] int : 0; // { dg-error "'indeterminate' on declaration other than parameter or automatic variable" }
> + [[indeterminate]] int i2 : 5; // { dg-error "'indeterminate' on declaration other than parameter or automatic variable" }
> + [[indeterminate]] static int i3; // { dg-error "'indeterminate' on declaration other than parameter or automatic variable" }
> + static int i4;
> +};
> +[[indeterminate]] int I::i4 = 0; // { dg-error "'indeterminate' on declaration other than parameter or automatic variable" }
> +struct J : [[indeterminate]] C {}; // { dg-warning "attributes on base specifiers are ignored" }
> +#if __cpp_concepts >= 201907L
> +template <typename T>
> +concept K [[indeterminate]] = requires { true; };// { dg-error "'indeterminate' on declaration other than parameter or automatic variable" "" { target c++20 } }
> +#endif
> +typedef int L [[indeterminate]]; // { dg-error "'indeterminate' on declaration other than parameter or automatic variable" }
> +template <typename T>
> +struct M {};
> +template <>
> +struct [[indeterminate]] M<int> { int m; }; // { dg-warning "'indeterminate' attribute does not apply to types" }
> +typedef int N[2] [[indeterminate]]; // { dg-warning "'indeterminate' attribute does not apply to types" }
> +typedef int O [[indeterminate]] [2]; // { dg-error "'indeterminate' on declaration other than parameter or automatic variable" }
> --- gcc/testsuite/g++.dg/cpp26/attr-indeterminate2.C.jj 2025-09-17 10:20:47.848835852 +0200
> +++ gcc/testsuite/g++.dg/cpp26/attr-indeterminate2.C 2025-09-17 10:20:47.848835852 +0200
> @@ -0,0 +1,39 @@
> +// C++ 26 P2795R5 - Erroneous behaviour for uninitialized reads
> +// { dg-do compile { target c++11 } }
> +// { dg-additional-options "-fdump-tree-gimple" }
> +// { dg-skip-if "" { c++26 } { "-ftrivial-auto-var-init=*" } { "" } }
> +// Expect .DEFERRED_INIT calls for the h, r and s variables (3) and
> +// temporaries for the second arguments to foo and baz calls (4).
> +// { dg-final { scan-tree-dump-times " = \\.DEFERRED_INIT \\\(" 7 "gimple" { target c++26 } } }
> +
> +struct S { S (); S (const S &); ~S (); int s; };
> +void foo (S a [[indeterminate]], S b, S c [[indeterminate]] = S ());
> +void foo (S d, S e, S f [[indeterminate]]);
> +
> +void
> +bar ()
> +{
> + S g [[indeterminate]], h;
> + foo (g, h, S ());
> + foo (g, h);
> +}
> +
> +void
> +foo (S i [[indeterminate]], S j, S k)
> +{
> +}
> +
> +void
> +baz ([[indeterminate]] S l, S m, [[indeterminate]] S n = S ())
> +{
> +}
> +
> +void baz (S o, S p, S q);
> +
> +void
> +qux ()
> +{
> + S r, s;
> + baz (r, s, s);
> + baz (r, s);
> +}
> --- gcc/testsuite/g++.dg/cpp26/attr-indeterminate3.C.jj 2025-09-17 10:20:47.847835866 +0200
> +++ gcc/testsuite/g++.dg/cpp26/attr-indeterminate3.C 2025-09-17 10:20:47.847835866 +0200
> @@ -0,0 +1,21 @@
> +// C++ 26 P2795R5 - Erroneous behaviour for uninitialized reads
> +// { dg-do compile { target c++11 } }
> +// { dg-skip-if "" { c++26 } { "-ftrivial-auto-var-init=*" } { "" } }
> +
> +struct S { S (); S (const S &); ~S (); int s; };
> +void foo (S u, S v [[indeterminate]], int);
> +void foo (S a, S b, S c = S ()); // { dg-message "earlier declaration" }
> +void foo (S d, S e, S f [[indeterminate]]); // { dg-error "'indeterminate' attribute not specified for parameter 'f' on the first declaration of its function" }
> +
> +void
> +foo (S i [[indeterminate]], S j, S k) // { dg-error "'indeterminate' attribute not specified for parameter 'i' on the first declaration of its function" }
> +{
> +}
> +
> +void
> +bar (S l, S m, S n = S ()) // { dg-message "earlier declaration" }
> +{
> +}
> +
> +void bar (S o [[indeterminate]], S p, [[indeterminate]]S q); // { dg-error "'indeterminate' attribute not specified for parameter 'o' on the first declaration of its function" }
> + // { dg-error "'indeterminate' attribute not specified for parameter 'q' on the first declaration of its function" "" { target *-*-* } .-1 }
> --- gcc/testsuite/g++.dg/cpp26/attr-indeterminate4.C.jj 2025-09-17 10:20:47.847835866 +0200
> +++ gcc/testsuite/g++.dg/cpp26/attr-indeterminate4.C 2025-09-17 10:20:47.847835866 +0200
> @@ -0,0 +1,36 @@
> +// C++ 26 P2795R5 - Erroneous behaviour for uninitialized reads
> +// { dg-do compile { target c++11 } }
> +// { dg-additional-options "-ftrivial-auto-var-init=uninitialized -fdump-tree-gimple" }
> +// { dg-final { scan-tree-dump-not " = \\.DEFERRED_INIT \\\(" "gimple" } }
> +
> +struct S { S (); S (const S &); ~S (); int s; };
> +void foo (S a [[indeterminate]], S b, S c [[indeterminate]] = S ());
> +void foo (S d, S e, S f [[indeterminate]]);
> +
> +void
> +bar ()
> +{
> + S g [[indeterminate]], h;
> + foo (g, h, S ());
> + foo (g, h);
> +}
> +
> +void
> +foo (S i [[indeterminate]], S j, S k)
> +{
> +}
> +
> +void
> +baz ([[indeterminate]] S l, S m, [[indeterminate]] S n = S ())
> +{
> +}
> +
> +void baz (S o, S p, S q);
> +
> +void
> +qux ()
> +{
> + S r, s;
> + baz (r, s, s);
> + baz (r, s);
> +}
> --- gcc/testsuite/g++.dg/cpp26/erroneous1.C.jj 2025-09-17 10:20:47.848835852 +0200
> +++ gcc/testsuite/g++.dg/cpp26/erroneous1.C 2025-09-17 10:20:47.848835852 +0200
> @@ -0,0 +1,61 @@
> +// C++ 26 P2795R5 - Erroneous behaviour for uninitialized reads
> +// { dg-do run { target c++26 } }
> +// { dg-skip-if "" { *-*-* } { "-ftrivial-auto-var-init=*" } { "" } }
> +// { dg-options "-O2 -Wuninitialized" }
> +
> +#define assert(x) if (!(x)) __builtin_abort ()
> +
> +template <typename T>
> +[[gnu::noipa]] T
> +baz (T &x)
> +{
> + return x;
> +}
> +
> +[[gnu::noipa]] int
> +foo (bool b)
> +{
> + unsigned char c;
> + unsigned char d = c; // no erroneous behavior, but d has an erroneous value
> + // { dg-warning "'c' is used uninitialized" "" { target *-*-* } .-1 }
> +
> + assert (c == d); // holds, both integral promotions have erroneous behavior
> +
> + unsigned char f = c;
> + unsigned char g = baz (f);
> +
> + assert (g == c);
> +
> + int e = d; // erroneous behavior
> + baz (e);
> + return b ? d : 0; // erroneous behavior if b is true
> +}
> +
> +[[gnu::noipa]] void
> +bar ()
> +{
> + int d1, d2;
> +
> + int e1 = d1; // erroneous behavior
> + int e2 = d1; // erroneous behavior
> +
> + assert (e1 == e2); // holds
> + assert (e1 == d1); // holds, erroneous behavior
> + assert (e2 == d1); // holds, erroneous behavior
> +
> + int f = d1; // { dg-warning "'d1' is used uninitialized" }
> + int g = baz (f);
> + assert (g == d1);
> +
> + __builtin_memcpy (&d2, &d1, sizeof (int)); // no erroneous behavior, but d2 has an erroneous value
> + assert (e1 == d2); // holds, erroneous behavior
> + assert (e2 == d2); // holds, erroneous behavior
> +}
> +
> +int
> +main ()
> +{
> + foo (false);
> + foo (true);
> + bar ();
> +}
> --- gcc/testsuite/g++.dg/cpp26/erroneous2.C.jj 2025-09-17 10:20:47.848835852 +0200
> +++ gcc/testsuite/g++.dg/cpp26/erroneous2.C 2025-09-17 10:20:47.848835852 +0200
> @@ -0,0 +1,234 @@
> +// C++ 26 P2795R5 - Erroneous behaviour for uninitialized reads
> +// { dg-do compile }
> +// { dg-skip-if "" { *-*-* } { "-ftrivial-auto-var-init=*" } { "" } }
> +// { dg-options "-O2 -fdump-tree-gimple" }
> +// All the s1..s24 variables and i1 need .DEFERRED_INIT call on their
> +// declarations.
> +// Plus, forward gotos to l1 & l2 labels need up to s1-s4 and s6-s9 vars to
> +// be .DEFERRED_INITed (and backward gotos up to that minus the first two).
> +// switch to case 15 skips over s12, switch to case 16/17 skip
> +// over s12 and s13 but the adjacent l3 label needs to also skip over s3-s4
> +// and s6-s9 and s11. switch to case 18 skips over s12-s14 and switch to
> +// default in the same switch skips over s12-s15.
> +// goto l4; skips over s19 initialization.
> +// goto l5; skips over s20-s22 initialization.
> +// switch to case 32/33 skips over s23 but goto to adjacent l6 skips also
> +// over s20-s22. switch to default in that switch skips over s23-s24.
> +// { dg-final { scan-tree-dump-times " s1 = \.DEFERRED_INIT \\\(" 2 "gimple" { target c++26 } } }
> +// { dg-final { scan-tree-dump-times " s2 = \.DEFERRED_INIT \\\(" 2 "gimple" { target c++26 } } }
> +// { dg-final { scan-tree-dump-times " s3 = \.DEFERRED_INIT \\\(" 3 "gimple" { target c++26 } } }
> +// { dg-final { scan-tree-dump-times " s4 = \.DEFERRED_INIT \\\(" 3 "gimple" { target c++26 } } }
> +// { dg-final { scan-tree-dump-times " s5 = \.DEFERRED_INIT \\\(" 1 "gimple" { target c++26 } } }
> +// { dg-final { scan-tree-dump-times " s6 = \.DEFERRED_INIT \\\(" 3 "gimple" { target c++26 } } }
> +// { dg-final { scan-tree-dump-times " s7 = \.DEFERRED_INIT \\\(" 3 "gimple" { target c++26 } } }
> +// { dg-final { scan-tree-dump-times " s8 = \.DEFERRED_INIT \\\(" 3 "gimple" { target c++26 } } }
> +// { dg-final { scan-tree-dump-times " s9 = \.DEFERRED_INIT \\\(" 3 "gimple" { target c++26 } } }
> +// { dg-final { scan-tree-dump-times " s10 = \.DEFERRED_INIT \\\(" 1 "gimple" { target c++26 } } }
> +// { dg-final { scan-tree-dump-times " s11 = \.DEFERRED_INIT \\\(" 2 "gimple" { target c++26 } } }
> +// { dg-final { scan-tree-dump-times " s12 = \.DEFERRED_INIT \\\(" 5 "gimple" { target c++26 } } }
> +// { dg-final { scan-tree-dump-times " s13 = \.DEFERRED_INIT \\\(" 4 "gimple" { target c++26 } } }
> +// { dg-final { scan-tree-dump-times " s14 = \.DEFERRED_INIT \\\(" 3 "gimple" { target c++26 } } }
> +// { dg-final { scan-tree-dump-times " s15 = \.DEFERRED_INIT \\\(" 2 "gimple" { target c++26 } } }
> +// { dg-final { scan-tree-dump-times " s16 = \.DEFERRED_INIT \\\(" 1 "gimple" { target c++26 } } }
> +// { dg-final { scan-tree-dump-times " s17 = \.DEFERRED_INIT \\\(" 1 "gimple" { target c++26 } } }
> +// { dg-final { scan-tree-dump-times " s18 = \.DEFERRED_INIT \\\(" 1 "gimple" { target c++26 } } }
> +// { dg-final { scan-tree-dump-times " s19 = \.DEFERRED_INIT \\\(" 2 "gimple" { target c++26 } } }
> +// { dg-final { scan-tree-dump-times " s20 = \.DEFERRED_INIT \\\(" 3 "gimple" { target c++26 } } }
> +// { dg-final { scan-tree-dump-times " s21 = \.DEFERRED_INIT \\\(" 3 "gimple" { target c++26 } } }
> +// { dg-final { scan-tree-dump-times " s22 = \.DEFERRED_INIT \\\(" 3 "gimple" { target c++26 } } }
> +// { dg-final { scan-tree-dump-times " s23 = \.DEFERRED_INIT \\\(" 3 "gimple" { target c++26 } } }
> +// { dg-final { scan-tree-dump-times " s24 = \.DEFERRED_INIT \\\(" 2 "gimple" { target c++26 } } }
> +// { dg-final { scan-tree-dump-times " i1 = \.DEFERRED_INIT \\\(" 1 "gimple" { target c++26 } } }
> +
> +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;
> +}
> --- gcc/testsuite/g++.dg/cpp26/erroneous3.C.jj 2025-09-17 10:20:47.847835866 +0200
> +++ gcc/testsuite/g++.dg/cpp26/erroneous3.C 2025-09-17 10:20:47.847835866 +0200
> @@ -0,0 +1,158 @@
> +// C++ 26 P2795R5 - Erroneous behaviour for uninitialized reads
> +// { dg-do compile { target c++11 } }
> +// { dg-options "-Wimplicit-fallthrough -Wswitch-unreachable" }
> +// Make sure -Wimplicit-fallthrough and -Wswitch-unreachable
> +// are consistent between -std=c++23 and -std=c++26 even when
> +// the latter instruments jumps across vacuous initializations.
> +
> +int i;
> +
> +void
> +foo (int x)
> +{
> + switch (x)
> + {
> + case 1:
> + int j;
> + ++i; // { dg-warning "this statement may fall through" }
> + case 2: // { dg-message "here" }
> + int k;
> + ++i;
> + // FALLTHRU
> + case 3:
> + int l;
> + ++i;
> + [[fallthrough]];
> + default:
> + int m;
> + ++i;
> + j = 42;
> + k = 42;
> + l = 42;
> + m = 42;
> + i += (j - k) + (l - m);
> + break;
> + }
> +}
> +
> +void
> +bar (int x)
> +{
> + if (x == 6)
> + goto l1;
> + if (x == 7)
> + goto l2;
> + if (x == 8)
> + goto l3;
> + if (x == 9)
> + goto l4;
> + if (x == 10)
> + goto l5;
> + if (x == 11)
> + goto l6;
> + int j;
> + j = 5;
> + i += j;
> + switch (x)
> + {
> + case 1:
> + l1:
> + ++i; // { dg-warning "this statement may fall through" }
> + case 2: // { dg-message "here" }
> + l2:
> + ++i;
> + // FALLTHRU
> + case 3:
> + l3:
> + ++i;
> + [[fallthrough]];
> + default:
> + l4:
> + ++i;
> + break;
> + case 4:
> + ++i; // { dg-warning "this statement may fall through" }
> + case 5: // { dg-message "here" }
> + l5:;
> + ++i; // { dg-warning "this statement may fall through" }
> + case 6: // { dg-message "here" }
> + ++i;
> + case 7:
> + l6:;
> + }
> +}
> +
> +void
> +baz (int x)
> +{
> + switch (x)
> + {
> + case 1:
> + int j [[indeterminate]];
> + ++i; // { dg-warning "this statement may fall through" }
> + case 2: // { dg-message "here" }
> + int k [[indeterminate]];
> + ++i;
> + // FALLTHRU
> + case 3:
> + int l [[indeterminate]];
> + ++i;
> + [[fallthrough]];
> + default:
> + int m [[indeterminate]];
> + ++i;
> + j = 42;
> + k = 42;
> + l = 42;
> + m = 42;
> + i += (j - k) + (l - m);
> + break;
> + }
> +}
> +
> +void
> +qux (int x)
> +{
> + if (x == 6)
> + goto l1;
> + if (x == 7)
> + goto l2;
> + if (x == 8)
> + goto l3;
> + if (x == 9)
> + goto l4;
> + if (x == 10)
> + goto l5;
> + if (x == 11)
> + goto l6;
> + int j [[indeterminate]];
> + j = 5;
> + i += j;
> + switch (x)
> + {
> + case 1:
> + l1:
> + ++i; // { dg-warning "this statement may fall through" }
> + case 2: // { dg-message "here" }
> + l2:
> + ++i;
> + // FALLTHRU
> + case 3:
> + l3:
> + ++i;
> + [[fallthrough]];
> + default:
> + l4:
> + ++i;
> + break;
> + case 4:
> + ++i; // { dg-warning "this statement may fall through" }
> + case 5: // { dg-message "here" }
> + l5:;
> + ++i; // { dg-warning "this statement may fall through" }
> + case 6: // { dg-message "here" }
> + ++i;
> + case 7:
> + l6:;
> + }
> +}
> --- gcc/testsuite/g++.dg/cpp26/erroneous4.C.jj 2025-09-17 10:20:47.847835866 +0200
> +++ gcc/testsuite/g++.dg/cpp26/erroneous4.C 2025-09-17 10:20:47.847835866 +0200
> @@ -0,0 +1,37 @@
> +// C++ 26 P2795R5 - Erroneous behaviour for uninitialized reads
> +// { dg-do compile { target c++23 } }
> +// Make sure we don't reject this in C++26 because of
> +// .DEFERRED_INIT calls.
> +
> +constexpr int
> +foo (int x)
> +{
> + if (x == 6)
> + goto l1;
> + if (x == 7)
> + goto l2;
> + int i;
> + switch (x)
> + {
> + int j;
> + case 1:
> + i = 6;
> + return i;
> + case 2:
> + i = 4;
> + l1:
> + i = 5;
> + return i;
> + case 3:
> + l2:
> + i = 7;
> + return i;
> + default:
> + return 42;
> + }
> +}
> +
> +static_assert (foo (1) == 6);
> +static_assert (foo (2) == 5);
> +static_assert (foo (3) == 7);
> +static_assert (foo (4) == 42);
> --- gcc/testsuite/g++.dg/opt/store-merging-1.C.jj 2020-01-14 20:02:46.896608141 +0100
> +++ gcc/testsuite/g++.dg/opt/store-merging-1.C 2025-09-17 12:09:51.551342468 +0200
> @@ -1,7 +1,7 @@
> // PR target/92038
> // { dg-do compile { target int32 } }
> // { dg-require-effective-target store_merge }
> -// { dg-options "-O2 -flifetime-dse=2 -fdump-tree-store-merging-details" }
> +// { dg-options "-O2 -flifetime-dse=2 -fdump-tree-store-merging-details -ftrivial-auto-var-init=uninitialized" }
> // { dg-final { scan-tree-dump "New sequence of \[12] stores to replace old one of 2 stores" "store-merging" } }
>
> struct S { S () : a (0), b (0) {} int a; char b; char c[3]; };
> --- gcc/testsuite/g++.dg/uninit-pred-loop-1_b.C.jj 2022-01-17 21:13:37.221117995 +0100
> +++ gcc/testsuite/g++.dg/uninit-pred-loop-1_b.C 2025-09-17 11:45:42.323963152 +0200
> @@ -13,7 +13,7 @@ int foo(int n)
> _err;
> });
>
> - if (err == 0) return 17;
> + if (err == 0) return 17; /* { dg-warning "'_err' may be used uninitialized" "" { target c++26 } } */
> }
>
> return 18;
> --- gcc/testsuite/g++.dg/warn/Wuninitialized-13.C.jj 2021-10-14 22:03:51.999890041 +0200
> +++ gcc/testsuite/g++.dg/warn/Wuninitialized-13.C 2025-09-17 12:56:48.146328414 +0200
> @@ -8,12 +8,12 @@
> struct shared_count {
> shared_count () { }
> shared_count (shared_count &r)
> - : pi (r.pi) { } // { dg-warning "\\\[-Wuninitialized" }
> + : pi (r.pi) { }
> int pi;
> };
>
> // There's another (redundant) -Wuninitialized on the line below.
> -struct shared_ptr {
> +struct shared_ptr { // { dg-warning "\\\[-Wuninitialized" }
> int ptr;
> shared_count refcount;
> };
> --- gcc/testsuite/c-c++-common/ubsan/vla-1.c.jj 2025-01-07 16:37:13.685576699 +0100
> +++ gcc/testsuite/c-c++-common/ubsan/vla-1.c 2025-09-17 17:29:21.167318906 +0200
> @@ -1,5 +1,5 @@
> /* { dg-do run } */
> -/* { dg-options "-fsanitize=vla-bound -Wall -Wno-unused-variable -fno-stack-clash-protection" } */
> +/* { dg-options "-fsanitize=vla-bound -Wall -Wno-unused-variable -fno-stack-clash-protection -ftrivial-auto-var-init=uninitialized" } */
>
> typedef long int V;
> int x = -1;
> --- gcc/gcc/testsuite/c-c++-common/uninit-17.c.jj 2021-07-27 22:10:57.384098658 +0200
> +++ gcc/gcc/testsuite/c-c++-common/uninit-17.c 2025-09-17 17:33:53.713814847 +0200
> @@ -10,10 +10,10 @@ static void bar(int a, int *ptr)
> do
> {
> int b;
> - if (b < 40) {
> + if (b < 40) { /* { dg-warning "is used uninitialized" "" { target c++26 } } */
> ptr[0] = b;
> }
> - b += 1; /* { dg-warning "is used uninitialized" } */
> + b += 1; /* { dg-warning "is used uninitialized" "" { target { c || c++23_down } } } */
> ptr++;
> }
> while (--a != 0);
>
> Jakub
>
On Tue, Sep 23, 2025 at 10:13:07AM +0200, Jakub Jelinek wrote:
> I think the first question needs to be, do we throw those away during
> inlining or not?
> My preference would be that we do throw those away.
> Consider say
> struct A { A () {} int a; };
> struct B : A { B () {} int b; };
> struct C : B { C () {} int c; };
> struct D : C { D () {} A a; B b; C c; };
> void bar (D &);
> void
> foo ()
> {
> D d;
> bar (d);
> }
> right now we get after einline
> d ={v} {CLOBBER(bob)};
> MEM[(struct C *)&d] ={v} {CLOBBER(bob)};
> MEM[(struct B *)&d] ={v} {CLOBBER(bob)};
> MEM[(struct A *)&d] ={v} {CLOBBER(bob)};
> MEM[(struct A *)&d + 12B] ={v} {CLOBBER(bob)};
> MEM[(struct B *)&d + 16B] ={v} {CLOBBER(bob)};
> MEM[(struct A *)&d + 16B] ={v} {CLOBBER(bob)};
> MEM[(struct C *)&d + 24B] ={v} {CLOBBER(bob)};
> MEM[(struct B *)&d + 24B] ={v} {CLOBBER(bob)};
> MEM[(struct A *)&d + 24B] ={v} {CLOBBER(bob)};
> clobbers at the start, but all but the first one are useless. And
> even if we don't inline everything, the clobber at the start of
> a ctor not being inlined covers everything that needs to be clobbered
> and later clobbers on it are just IL waste. When ctor is inlined
> into the ultimate caller of the ctor, either there will be a normal
> CLOBBER(bob) added by the patch for the whole variable or temporary
> or with Jason's changes for new expression too.
>
> > So a CLOBBER doesn't do because it might cause earlier writes to be
> > DSEd (even if it itself doesn't alter storage)?
>
> If it is dropped during inlining, that wouldn't be a problem.
> But a problem would be say SRA or other optimizations optimizing
> reads from the
> [MEM[this] ={v} {CLOBBER(newkind)};
> destination, we don't want optimize those to just _3(D) or some
> smaller temporary initialized with a clobber. Except for -W*uninitialized
> we need to treat it as what the memory contains is unknown.
> Unless we tweak SRA (does e.g. SCCVN do that too?) for that new kind.
>
> > I wonder if we can re-use .DEFERRED_INIT with a special mode here?
> > Again I'm noticing we do not document internal-functions anywhere,
> > in particular their expected signature or semantics ... :/
> > >From internal-fn.cc it seems we have an INIT_TYPE parameter, can
> > we add AUTO_INIT_KEEP_OLD or is AUTO_INIT_UNINITIALIZED already
> > doing what we want here?
>
> If we drop it during inlining, maybe, but same problem with SRA and maybe
> SCCVN. We don't want for this case to see what PR121894 mentions,
> that
> MEM[(struct C *)this] = .DEFERRED_INIT (16, 6, &""[0]);
> would be SRA optimized into
> this$a_1 = .DEFERRED_INIT (4, 6, &""[0]);
> this$b_2 = .DEFERRED_INIT (4, 6, &""[0]);
> this$c_3 = .DEFERRED_INIT (4, 6, &""[0]);
> this$d_4 = .DEFERRED_INIT (4, 6, &""[0]);
> because for this kind we'd want to expand it into nothing at all, not having
> to track which byte or whatever to read from which offset (which
> .DEFERRED_INIT doesn't express, it is meant to set all bytes to the same
> value).
So, I've played with the attribute on the parameter idea rather than
new ifn or new CLOBBER kind or new .DEFERRED_INIT kind, and I think
it has an existing precedent.
After all, we could implement it purely in the C++ FE by adding
__attribute__((access (write_only, 1))) attribute on all the this PARM_DECLs
of the ctors, just I don't like the wording in that case, talking
about attribute access on the ctor when the user has not added anything
like that and furthermore for write_only access it emits only
-Wmaybe-uninitialized warning.
So, this incremental patch (where most of the C++ FE condition is just
readded earlier condition for build_clobber_this at that spot, just with
DECL_CLONED_FUNCTION_P check removed and lookup_attribute added) restores
the Wuninitialized-10.C Wuninitialized-pr111123-1.C tests to previous
behavior.
--- gcc/cp/decl.cc.jj 2025-09-23 10:55:43.332823523 +0200
+++ gcc/cp/decl.cc 2025-09-23 13:20:34.023736838 +0200
@@ -19730,6 +19730,21 @@ start_preparsed_function (tree decl1, tr
start_function_contracts (decl1);
if (!processing_template_decl
+ && flag_lifetime_dse > 1
+ && DECL_CONSTRUCTOR_P (decl1)
+ /* Clobbering an empty base is harmful if it overlays real data. */
+ && !is_empty_class (current_class_type)
+ /* We can't clobber safely for an implicitly-defined default constructor
+ because part of the initialization might happen before we enter the
+ constructor, via AGGR_INIT_ZERO_FIRST (c++/68006). */
+ && !implicit_default_ctor_p (decl1)
+ && !lookup_attribute ("clobber *this",
+ DECL_ATTRIBUTES (current_class_ptr)))
+ DECL_ATTRIBUTES (current_class_ptr)
+ = tree_cons (get_identifier ("clobber *this"), NULL_TREE,
+ DECL_ATTRIBUTES (current_class_ptr));
+
+ if (!processing_template_decl
&& DECL_CONSTRUCTOR_P (decl1)
&& sanitize_flags_p (SANITIZE_VPTR)
&& !DECL_CLONED_FUNCTION_P (decl1)
--- gcc/tree-ssa-uninit.cc.jj 2025-04-08 14:09:30.101741017 +0200
+++ gcc/tree-ssa-uninit.cc 2025-09-23 12:57:05.901318089 +0200
@@ -641,6 +641,7 @@ maybe_warn_operand (ao_ref &ref, gimple
return NULL_TREE;
bool found_alloc = false;
+ bool found_clobber_deref_this = false;
if (fentry_reached)
{
@@ -662,12 +663,23 @@ maybe_warn_operand (ao_ref &ref, gimple
tree fndecl = gimple_call_fndecl (def_stmt);
const built_in_function fncode = DECL_FUNCTION_CODE (fndecl);
if (fncode == BUILT_IN_ALLOCA
- || fncode == BUILT_IN_ALLOCA_WITH_ALIGN
- || fncode == BUILT_IN_MALLOC)
+ || fncode == BUILT_IN_ALLOCA_WITH_ALIGN
+ || fncode == BUILT_IN_MALLOC)
found_alloc = true;
break;
}
+ if (SSA_NAME_IS_DEFAULT_DEF (base)
+ && POINTER_TYPE_P (TREE_TYPE (base))
+ && SSA_NAME_VAR (base)
+ && TREE_CODE (SSA_NAME_VAR (base)) == PARM_DECL
+ && lookup_attribute ("clobber *this",
+ DECL_ATTRIBUTES (SSA_NAME_VAR (base))))
+ {
+ found_clobber_deref_this = true;
+ break;
+ }
+
if (!is_gimple_assign (def_stmt))
break;
@@ -702,7 +714,7 @@ maybe_warn_operand (ao_ref &ref, gimple
/* Do not warn if it can be initialized outside this function.
If we did not reach function entry then we found killing
clobbers on all paths to entry. */
- if (!found_alloc && fentry_reached)
+ if ((!found_alloc && !found_clobber_deref_this) && fentry_reached)
{
if (TREE_CODE (base) == SSA_NAME)
{
Jakub
@@ -288,7 +288,8 @@ enum vect_cost_model {
enum auto_init_type {
AUTO_INIT_UNINITIALIZED = 0,
AUTO_INIT_PATTERN = 1,
- AUTO_INIT_ZERO = 2
+ AUTO_INIT_ZERO = 2,
+ AUTO_INIT_CXX26 = 3
};
/* Initialization of padding bits with zeros. */
@@ -900,6 +900,19 @@ extern void omp_clause_range_check_faile
#define UNUSED_LABEL_P(NODE) \
(LABEL_DECL_CHECK (NODE)->base.default_def_flag)
+/* Label used to goto around artificial .DEFERRED_INIT code for
+ C++ -ftrivial-auto-var-init= purposes with a goto around it.
+ VACUOUS_INIT_LABEL_P flag is used on the lab LABEL_DECL in:
+ goto lab;
+ lab1:
+ v1 = .DEFERRED_INIT (...);
+ v2 = .DEFERRED_INIT (...);
+ lab2:
+ v3 = .DEFERRED_INIT (...);
+ lab: */
+#define VACUOUS_INIT_LABEL_P(NODE) \
+ (LABEL_DECL_CHECK (NODE)->base.nothrow_flag)
+
/* Nonzero means this expression is volatile in the C sense:
its address should be of type `volatile WHATEVER *'.
In other words, the declared item is volatile qualified.
@@ -2102,13 +2102,13 @@ gimple_add_padding_init_for_auto_var (tr
/* Return true if the DECL need to be automaticly initialized by the
compiler. */
static bool
-is_var_need_auto_init (tree decl)
+var_needs_auto_init_p (tree decl)
{
if (auto_var_p (decl)
- && (TREE_CODE (decl) != VAR_DECL
- || !DECL_HARD_REGISTER (decl))
- && (flag_auto_var_init > AUTO_INIT_UNINITIALIZED)
- && (!lookup_attribute ("uninitialized", DECL_ATTRIBUTES (decl)))
+ && (TREE_CODE (decl) != VAR_DECL || !DECL_HARD_REGISTER (decl))
+ && flag_auto_var_init > AUTO_INIT_UNINITIALIZED
+ && !lookup_attribute ("uninitialized", DECL_ATTRIBUTES (decl))
+ && !lookup_attribute ("indeterminate", DECL_ATTRIBUTES (decl))
&& !OPAQUE_TYPE_P (TREE_TYPE (decl))
&& !is_empty_type (TREE_TYPE (decl)))
return true;
@@ -2221,7 +2221,7 @@ gimplify_decl_expr (tree *stmt_p, gimple
/* When there is no explicit initializer, if the user requested,
We should insert an artifical initializer for this automatic
variable. */
- else if (is_var_need_auto_init (decl)
+ else if (var_needs_auto_init_p (decl)
&& !decl_had_value_expr_p)
{
gimple_add_init_for_auto_var (decl,
@@ -2315,27 +2315,6 @@ emit_warn_switch_unreachable (gimple *st
/* Don't warn for compiler-generated gotos. These occur
in Duff's devices, for example. */
return NULL;
- else if ((flag_auto_var_init > AUTO_INIT_UNINITIALIZED)
- && ((gimple_call_internal_p (stmt, IFN_DEFERRED_INIT))
- || (gimple_call_builtin_p (stmt, BUILT_IN_CLEAR_PADDING)
- && (bool) TREE_INT_CST_LOW (gimple_call_arg (stmt, 1)))
- || (is_gimple_assign (stmt)
- && gimple_assign_single_p (stmt)
- && (TREE_CODE (gimple_assign_rhs1 (stmt)) == SSA_NAME)
- && gimple_call_internal_p (
- SSA_NAME_DEF_STMT (gimple_assign_rhs1 (stmt)),
- IFN_DEFERRED_INIT))))
- /* Don't warn for compiler-generated initializations for
- -ftrivial-auto-var-init.
- There are 3 cases:
- case 1: a call to .DEFERRED_INIT;
- case 2: a call to __builtin_clear_padding with the 2nd argument is
- present and non-zero;
- case 3: a gimple assign store right after the call to .DEFERRED_INIT
- that has the LHS of .DEFERRED_INIT as the RHS as following:
- _1 = .DEFERRED_INIT (4, 2, &"i1"[0]);
- i1 = _1. */
- return NULL;
else
warning_at (gimple_location (stmt), OPT_Wswitch_unreachable,
"statement will never be executed");
@@ -2383,6 +2362,18 @@ warn_switch_unreachable_and_auto_init_r
there will be non-debug stmts too, and we'll catch those. */
break;
+ case GIMPLE_ASSIGN:
+ /* See comment below in the GIMPLE_CALL case. */
+ if (flag_auto_var_init > AUTO_INIT_UNINITIALIZED
+ && gimple_assign_single_p (stmt)
+ && TREE_CODE (gimple_assign_rhs1 (stmt)) == SSA_NAME)
+ {
+ gimple *g = SSA_NAME_DEF_STMT (gimple_assign_rhs1 (stmt));
+ if (gimple_call_internal_p (g, IFN_DEFERRED_INIT))
+ break;
+ }
+ goto do_default;
+
case GIMPLE_LABEL:
/* Stop till the first Label. */
return integer_zero_node;
@@ -2392,24 +2383,41 @@ warn_switch_unreachable_and_auto_init_r
*handled_ops_p = false;
break;
}
- if (warn_trivial_auto_var_init
- && flag_auto_var_init > AUTO_INIT_UNINITIALIZED
+ /* Don't warn for compiler-generated initializations for
+ -ftrivial-auto-var-init for -Wswitch-unreachable. Though
+ do warn for -Wtrivial-auto-var-init.
+ There are 3 cases:
+ case 1: a call to .DEFERRED_INIT;
+ case 2: a call to __builtin_clear_padding with the 2nd argument is
+ present and non-zero;
+ case 3: a gimple assign store right after the call to .DEFERRED_INIT
+ that has the LHS of .DEFERRED_INIT as the RHS as following:
+ _1 = .DEFERRED_INIT (4, 2, &"i1"[0]);
+ i1 = _1.
+ case 3 is handled above in the GIMPLE_ASSIGN case. */
+ if (flag_auto_var_init > AUTO_INIT_UNINITIALIZED
&& gimple_call_internal_p (stmt, IFN_DEFERRED_INIT))
{
- /* Get the variable name from the 3rd argument of call. */
- tree var_name = gimple_call_arg (stmt, 2);
- var_name = TREE_OPERAND (TREE_OPERAND (var_name, 0), 0);
- const char *var_name_str = TREE_STRING_POINTER (var_name);
-
- warning_at (gimple_location (stmt), OPT_Wtrivial_auto_var_init,
- "%qs cannot be initialized with "
- "%<-ftrivial-auto-var_init%>",
- var_name_str);
+ if (warn_trivial_auto_var_init)
+ {
+ /* Get the variable name from the 3rd argument of call. */
+ tree var_name = gimple_call_arg (stmt, 2);
+ var_name = TREE_OPERAND (TREE_OPERAND (var_name, 0), 0);
+ const char *var_name_str = TREE_STRING_POINTER (var_name);
+
+ warning_at (gimple_location (stmt), OPT_Wtrivial_auto_var_init,
+ "%qs cannot be initialized with "
+ "%<-ftrivial-auto-var_init%>", var_name_str);
+ }
break;
}
-
+ if (flag_auto_var_init > AUTO_INIT_UNINITIALIZED
+ && gimple_call_builtin_p (stmt, BUILT_IN_CLEAR_PADDING)
+ && (bool) TREE_INT_CST_LOW (gimple_call_arg (stmt, 1)))
+ break;
/* Fall through. */
default:
+ do_default:
/* check the first "real" statement (not a decl/lexical scope/...), issue
warning if needed. */
if (warn_switch_unreachable && !unreachable_issued)
@@ -2489,26 +2497,38 @@ last_stmt_in_scope (gimple *stmt)
if (!stmt)
return NULL;
+ auto last_stmt_in_seq = [] (gimple_seq s) {
+ gimple_seq_node n;
+ for (n = gimple_seq_last (s);
+ n && (is_gimple_debug (n)
+ || (flag_auto_var_init > AUTO_INIT_UNINITIALIZED
+ && gimple_call_internal_p (n, IFN_DEFERRED_INIT)));
+ n = n->prev)
+ if (n == s)
+ return (gimple *) NULL;
+ return (gimple *) n;
+ };
+
switch (gimple_code (stmt))
{
case GIMPLE_BIND:
{
gbind *bind = as_a <gbind *> (stmt);
- stmt = gimple_seq_last_nondebug_stmt (gimple_bind_body (bind));
+ stmt = last_stmt_in_seq (gimple_bind_body (bind));
return last_stmt_in_scope (stmt);
}
case GIMPLE_TRY:
{
gtry *try_stmt = as_a <gtry *> (stmt);
- stmt = gimple_seq_last_nondebug_stmt (gimple_try_eval (try_stmt));
+ stmt = last_stmt_in_seq (gimple_try_eval (try_stmt));
gimple *last_eval = last_stmt_in_scope (stmt);
if (gimple_stmt_may_fallthru (last_eval)
&& (last_eval == NULL
|| !gimple_call_internal_p (last_eval, IFN_FALLTHROUGH))
&& gimple_try_kind (try_stmt) == GIMPLE_TRY_FINALLY)
{
- stmt = gimple_seq_last_nondebug_stmt (gimple_try_cleanup (try_stmt));
+ stmt = last_stmt_in_seq (gimple_try_cleanup (try_stmt));
return last_stmt_in_scope (stmt);
}
else
@@ -2661,8 +2681,16 @@ collect_fallthrough_labels (gimple_stmt_
}
else if (gimple_call_internal_p (gsi_stmt (*gsi_p), IFN_ASAN_MARK))
;
+ else if (flag_auto_var_init > AUTO_INIT_UNINITIALIZED
+ && gimple_call_internal_p (gsi_stmt (*gsi_p),
+ IFN_DEFERRED_INIT))
+ ;
else if (gimple_code (gsi_stmt (*gsi_p)) == GIMPLE_PREDICT)
;
+ else if (flag_auto_var_init > AUTO_INIT_UNINITIALIZED
+ && gimple_code (gsi_stmt (*gsi_p)) == GIMPLE_GOTO
+ && VACUOUS_INIT_LABEL_P (gimple_goto_dest (gsi_stmt (*gsi_p))))
+ ;
else if (!is_gimple_debug (gsi_stmt (*gsi_p)))
prev = gsi_stmt (*gsi_p);
gsi_next (gsi_p);
@@ -2699,9 +2727,13 @@ should_warn_for_implicit_fallthrough (gi
{
tree l;
while (!gsi_end_p (gsi)
- && gimple_code (gsi_stmt (gsi)) == GIMPLE_LABEL
- && (l = gimple_label_label (as_a <glabel *> (gsi_stmt (gsi))))
- && !case_label_p (&gimplify_ctxp->case_labels, l))
+ && ((gimple_code (gsi_stmt (gsi)) == GIMPLE_LABEL
+ && (l
+ = gimple_label_label (as_a <glabel *> (gsi_stmt (gsi))))
+ && !case_label_p (&gimplify_ctxp->case_labels, l))
+ || (flag_auto_var_init > AUTO_INIT_UNINITIALIZED
+ && gimple_call_internal_p (gsi_stmt (gsi),
+ IFN_DEFERRED_INIT))))
gsi_next_nondebug (&gsi);
if (gsi_end_p (gsi) || gimple_code (gsi_stmt (gsi)) != GIMPLE_LABEL)
return false;
@@ -2714,7 +2746,10 @@ should_warn_for_implicit_fallthrough (gi
/* Skip all immediately following labels. */
while (!gsi_end_p (gsi)
&& (gimple_code (gsi_stmt (gsi)) == GIMPLE_LABEL
- || gimple_code (gsi_stmt (gsi)) == GIMPLE_PREDICT))
+ || gimple_code (gsi_stmt (gsi)) == GIMPLE_PREDICT
+ || (flag_auto_var_init > AUTO_INIT_UNINITIALIZED
+ && gimple_call_internal_p (gsi_stmt (gsi),
+ IFN_DEFERRED_INIT))))
gsi_next_nondebug (&gsi);
/* { ... something; default:; } */
@@ -2891,7 +2926,33 @@ expand_FALLTHROUGH_r (gimple_stmt_iterat
gimple_stmt_iterator gsi2 = *gsi_p;
stmt = gsi_stmt (gsi2);
- if (gimple_code (stmt) == GIMPLE_GOTO && !gimple_has_location (stmt))
+ if (flag_auto_var_init > AUTO_INIT_UNINITIALIZED
+ && gimple_code (stmt) == GIMPLE_GOTO
+ && VACUOUS_INIT_LABEL_P (gimple_goto_dest (stmt)))
+ {
+ /* Handle for C++ artificial -ftrivial-auto-var-init=
+ sequences. Those look like:
+ goto lab1;
+ lab2:;
+ v1 = .DEFERRED_INIT (...);
+ v2 = .DEFERRED_INIT (...);
+ lab3:;
+ v3 = .DEFERRED_INIT (...);
+ lab1:;
+ In this case, a case/default label can be either in between
+ the GIMPLE_GOTO and the corresponding GIMPLE_LABEL, if jumps
+ from the switch condition to the case/default label cross
+ vacuous initialization of some variables, or after the
+ corresponding GIMPLE_LABEL, if those jumps don't cross
+ any such initialization but there is an adjacent named label
+ which crosses such initialization. So, for the purpose of
+ this function, just ignore the goto but until reaching the
+ corresponding GIMPLE_LABEL allow also .DEFERRED_INIT
+ calls. */
+ gsi_next (&gsi2);
+ }
+ else if (gimple_code (stmt) == GIMPLE_GOTO
+ && !gimple_has_location (stmt))
{
/* Go on until the artificial label. */
tree goto_dest = gimple_goto_dest (stmt);
@@ -2926,6 +2987,9 @@ expand_FALLTHROUGH_r (gimple_stmt_iterat
}
else if (gimple_call_internal_p (stmt, IFN_ASAN_MARK))
;
+ else if (flag_auto_var_init > AUTO_INIT_UNINITIALIZED
+ && gimple_call_internal_p (stmt, IFN_DEFERRED_INIT))
+ ;
else if (!is_gimple_debug (stmt))
/* Anything else is not expected. */
break;
@@ -6753,7 +6817,8 @@ gimplify_init_constructor (tree *expr_p,
&& clear_padding_type_may_have_padding_p (type)
&& ((AGGREGATE_TYPE_P (type) && !cleared && !is_empty_ctor)
|| !AGGREGATE_TYPE_P (type))
- && is_var_need_auto_init (object))
+ && var_needs_auto_init_p (object)
+ && flag_auto_var_init != AUTO_INIT_CXX26)
gimple_add_padding_init_for_auto_var (object, false, pre_p);
return ret;
@@ -8460,6 +8525,7 @@ gimplify_target_expr (tree *expr_p, gimp
if (init)
{
gimple_seq init_pre_p = NULL;
+ bool is_vla = false;
/* TARGET_EXPR temps aren't part of the enclosing block, so add it
to the temps list. Handle also variable length TARGET_EXPRs. */
@@ -8470,6 +8536,7 @@ gimplify_target_expr (tree *expr_p, gimp
/* FIXME: this is correct only when the size of the type does
not depend on expressions evaluated in init. */
gimplify_vla_decl (temp, &init_pre_p);
+ is_vla = true;
}
else
{
@@ -8481,6 +8548,15 @@ gimplify_target_expr (tree *expr_p, gimp
gimple_add_tmp_var (temp);
}
+ if (var_needs_auto_init_p (temp) && VOID_TYPE_P (TREE_TYPE (init)))
+ {
+ gimple_add_init_for_auto_var (temp, flag_auto_var_init, &init_pre_p);
+ if (flag_auto_var_init == AUTO_INIT_PATTERN
+ && !is_gimple_reg (temp)
+ && clear_padding_type_may_have_padding_p (TREE_TYPE (temp)))
+ gimple_add_padding_init_for_auto_var (temp, is_vla, &init_pre_p);
+ }
+
/* If TARGET_EXPR_INITIAL is void, then the mere evaluation of the
expression is supposed to initialize the slot. */
if (VOID_TYPE_P (TREE_TYPE (init)))
@@ -14696,27 +14696,25 @@ and @option{-fauto-profile}.
@opindex ftrivial-auto-var-init
@item -ftrivial-auto-var-init=@var{choice}
-Initialize automatic variables with either a pattern or with zeroes to increase
-the security and predictability of a program by preventing uninitialized memory
-disclosure and use.
+Initialize automatic variables or temporary objects with either a pattern or with
+zeroes to increase the security and predictability of a program by preventing
+uninitialized memory disclosure and use.
GCC still considers an automatic variable that doesn't have an explicit
initializer as uninitialized, @option{-Wuninitialized} and
@option{-Wanalyzer-use-of-uninitialized-value} will still report
-warning messages on such automatic variables and the compiler will
-perform optimization as if the variable were uninitialized.
+warning messages on such automatic variables or temporary objects and the
+compiler will perform optimization as if the variable were uninitialized.
With this option, GCC will also initialize any padding of automatic variables
-that have structure or union types to zeroes.
-However, the current implementation cannot initialize automatic variables that
-are declared between the controlling expression and the first case of a
-@code{switch} statement. Using @option{-Wtrivial-auto-var-init} to report all
-such cases.
+or temporary objects that have structure or union types to zeroes.
+However, the current implementation cannot initialize automatic variables
+whose initialization is bypassed through @code{switch} or @code{goto}
+statement. Using @option{-Wtrivial-auto-var-init} to report all such cases.
The three values of @var{choice} are:
@itemize @bullet
@item
@samp{uninitialized} doesn't initialize any automatic variables.
-This is C and C++'s default.
@item
@samp{pattern} Initialize automatic variables with values which will likely
@@ -14730,7 +14728,10 @@ The values used for pattern initializati
@samp{zero} Initialize automatic variables with zeroes.
@end itemize
-The default is @samp{uninitialized}.
+The default is @samp{uninitialized} except for C++26, in which case
+if @option{-ftrivial-auto-var-init=} is not specified at all automatic
+variables or temporary objects are zero initialized, but zero initialization
+of padding bits does not happen.
Note that the initializer values, whether @samp{zero} or @samp{pattern},
refer to data representation (in memory or machine registers), rather
@@ -14743,7 +14744,7 @@ with the bit patterns @code{0x00} or @co
@var{choice}, whether or not these representations stand for values in
that range, and even if they do, the interpretation of the value held by
the variable will depend on the bias. A @samp{hardbool} variable that
-uses say @code{0X5A} and @code{0xA5} for @code{false} and @code{true},
+uses say @code{0x5A} and @code{0xA5} for @code{false} and @code{true},
respectively, will trap with either @samp{choice} of trivial
initializer, i.e., @samp{zero} initialization will not convert to the
representation for @code{false}, even if it would for a @code{static}
@@ -14754,7 +14755,8 @@ are initialized with @code{false} (zero)
requested.
You can control this behavior for a specific variable by using the variable
-attribute @code{uninitialized} (@pxref{Variable Attributes}).
+attribute @code{uninitialized} standard attribute (@pxref{Variable Attributes})
+or the C++26 @code{[[indeterminate]]}.
@opindex fvect-cost-model
@item -fvect-cost-model=@var{model}
@@ -913,6 +913,16 @@ c_common_post_options (const char **pfil
else
flag_permitted_flt_eval_methods = PERMITTED_FLT_EVAL_METHODS_C11;
+ if (cxx_dialect >= cxx26)
+ SET_OPTION_IF_UNSET (&global_options, &global_options_set,
+ flag_auto_var_init, AUTO_INIT_CXX26);
+
+ /* The -Wtrivial-auto-var-init warning is useless for C++, where we always
+ add .DEFERRED_INIT calls when some (vacuous) initializers are bypassed
+ through jumps from switch condition to case/default label. */
+ if (c_dialect_cxx ())
+ warn_trivial_auto_var_init = 0;
+
/* C23 Annex F does not permit certain built-in functions to raise
"inexact". */
if (flag_isoc23)
@@ -510,6 +510,7 @@ extern GTY(()) tree cp_global_trees[CPTI
PACK_EXPANSION_FORCE_EXTRA_ARGS_P (in *_PACK_EXPANSION)
LAMBDA_EXPR_STATIC_P (in LAMBDA_EXPR)
TARGET_EXPR_ELIDING_P (in TARGET_EXPR)
+ IF_STMT_VACUOUS_INIT_P (IF_STMT)
contract_semantic (in ASSERTION_, PRECONDITION_, POSTCONDITION_STMT)
TYPENAME_IS_UNION_P (in TYPENAME_TYPE)
4: IDENTIFIER_MARKED (IDENTIFIER_NODEs)
@@ -5686,6 +5687,9 @@ decl_template_parm_check (const_tree t,
#define IF_SCOPE(NODE) TREE_OPERAND (IF_STMT_CHECK (NODE), 3)
#define IF_STMT_CONSTEXPR_P(NODE) TREE_LANG_FLAG_0 (IF_STMT_CHECK (NODE))
#define IF_STMT_CONSTEVAL_P(NODE) TREE_LANG_FLAG_2 (IF_STMT_CHECK (NODE))
+/* True on artificial if (0) around .DEFERRED_INIT calls added for
+ !!flag_auto_var_init. */
+#define IF_STMT_VACUOUS_INIT_P(NODE) TREE_LANG_FLAG_3 (IF_STMT_CHECK (NODE))
/* Like PACK_EXPANSION_EXTRA_ARGS, for constexpr if. IF_SCOPE is used while
building an IF_STMT; IF_STMT_EXTRA_ARGS is used after it is complete. */
@@ -7279,7 +7283,7 @@ extern tree duplicate_decls (tree, tre
extern void mark_label_addressed (tree);
extern tree declare_local_label (tree);
extern tree define_label (location_t, tree);
-extern void check_goto (tree);
+extern void check_goto (tree *);
extern bool check_omp_return (void);
extern tree make_typename_type (tree, tree, enum tag_types, tsubst_flags_t);
extern tree build_typename_type (tree, tree, tree, tag_types);
@@ -10474,6 +10474,7 @@ build_over_call (struct z_candidate *can
unsigned int arg_index = 0;
int conv_index = 0;
int param_index = 0;
+ tree parmd = DECL_ARGUMENTS (fn);
auto consume_object_arg = [&arg_index, &first_arg, args]()
{
@@ -10491,6 +10492,8 @@ build_over_call (struct z_candidate *can
tree object_arg = consume_object_arg ();
argarray[argarray_size++] = build_this (object_arg);
parm = TREE_CHAIN (parm);
+ if (parmd)
+ parmd = DECL_CHAIN (parmd);
/* We should never try to call the abstract constructor. */
gcc_assert (!DECL_HAS_IN_CHARGE_PARM_P (fn));
@@ -10499,6 +10502,8 @@ build_over_call (struct z_candidate *can
argarray[argarray_size++] = (*args)[arg_index];
++arg_index;
parm = TREE_CHAIN (parm);
+ if (parmd)
+ parmd = DECL_CHAIN (parmd);
}
}
/* Bypass access control for 'this' parameter. */
@@ -10586,6 +10591,8 @@ build_over_call (struct z_candidate *can
argarray[argarray_size++] = converted_arg;
parm = TREE_CHAIN (parm);
+ if (parmd)
+ parmd = DECL_CHAIN (parmd);
}
auto handle_arg = [fn, flags](tree type,
@@ -10609,6 +10616,27 @@ build_over_call (struct z_candidate *can
return val;
};
+ auto handle_indeterminate_arg = [](tree parmd, tree val)
+ {
+ if (parmd
+ && lookup_attribute (NULL, "indeterminate", DECL_ATTRIBUTES (parmd)))
+ {
+ STRIP_NOPS (val);
+ if (TREE_CODE (val) == ADDR_EXPR
+ && TREE_CODE (TREE_OPERAND (val, 0)) == TARGET_EXPR)
+ {
+ val = TARGET_EXPR_SLOT (TREE_OPERAND (val, 0));
+ if (auto_var_p (val) && DECL_ARTIFICIAL (val))
+ {
+ tree id = get_identifier ("indeterminate");
+ DECL_ATTRIBUTES (val)
+ = tree_cons (build_tree_list (NULL_TREE, id), NULL_TREE,
+ DECL_ATTRIBUTES (val));
+ }
+ }
+ }
+ };
+
if (DECL_XOBJ_MEMBER_FUNCTION_P (fn))
{
gcc_assert (cand->num_convs > 0);
@@ -10622,8 +10650,13 @@ build_over_call (struct z_candidate *can
if (val == error_mark_node)
return error_mark_node;
else
- argarray[argarray_size++] = val;
+ {
+ argarray[argarray_size++] = val;
+ handle_indeterminate_arg (parmd, val);
+ }
parm = TREE_CHAIN (parm);
+ if (parmd)
+ parmd = DECL_CHAIN (parmd);
}
gcc_assert (first_arg == NULL_TREE);
@@ -10669,7 +10702,12 @@ build_over_call (struct z_candidate *can
if (val == error_mark_node)
return error_mark_node;
else
- argarray[argarray_size++] = val;
+ {
+ argarray[argarray_size++] = val;
+ handle_indeterminate_arg (parmd, val);
+ }
+ if (parmd)
+ parmd = DECL_CHAIN (parmd);
}
/* Default arguments */
@@ -10685,6 +10723,9 @@ build_over_call (struct z_candidate *can
if (val == error_mark_node)
return error_mark_node;
argarray[argarray_size++] = val;
+ handle_indeterminate_arg (parmd, val);
+ if (parmd)
+ parmd = DECL_CHAIN (parmd);
}
/* Ellipsis */
@@ -3009,6 +3009,9 @@ cxx_eval_internal_function (const conste
vc_prvalue, non_constant_p,
overflow_p, jump_target);
+ case IFN_DEFERRED_INIT:
+ return build_clobber (TREE_TYPE (t), CLOBBER_OBJECT_BEGIN);
+
case IFN_VEC_CONVERT:
{
tree arg = cxx_eval_constant_expression (ctx, CALL_EXPR_ARG (t, 0),
@@ -7475,6 +7478,13 @@ cxx_eval_store_expression (const constex
bool preeval = SCALAR_TYPE_P (type) || TREE_CODE (t) == MODIFY_EXPR;
if (preeval && !TREE_CLOBBER_P (init))
{
+ /* Ignore var = .DEFERRED_INIT (); for now, until PR121965 is fixed. */
+ if (flag_auto_var_init > AUTO_INIT_UNINITIALIZED
+ && TREE_CODE (init) == CALL_EXPR
+ && CALL_EXPR_FN (init) == NULL_TREE
+ && CALL_EXPR_IFN (init) == IFN_DEFERRED_INIT)
+ return void_node;
+
/* Evaluate the value to be stored without knowing what object it will be
stored in, so that any side-effects happen first. */
if (!SCALAR_TYPE_P (type))
@@ -200,6 +200,33 @@ genericize_if_stmt (tree *stmt_p)
}
}
+ if (IF_STMT_VACUOUS_INIT_P (stmt))
+ {
+ gcc_checking_assert (integer_zerop (cond));
+ gcc_checking_assert (!else_ || !TREE_SIDE_EFFECTS (else_));
+ tree lab = create_artificial_label (UNKNOWN_LOCATION);
+ VACUOUS_INIT_LABEL_P (lab) = 1;
+ tree goto_expr = build_stmt (UNKNOWN_LOCATION, GOTO_EXPR, lab);
+ tree label_expr = build_stmt (UNKNOWN_LOCATION, LABEL_EXPR, lab);
+ if (TREE_CODE (then_) == STATEMENT_LIST)
+ {
+ tree_stmt_iterator i = tsi_start (then_);
+ tsi_link_before (&i, goto_expr, TSI_CONTINUE_LINKING);
+ i = tsi_last (then_);
+ tsi_link_after (&i, label_expr, TSI_CONTINUE_LINKING);
+ stmt = then_;
+ }
+ else
+ {
+ stmt = NULL_TREE;
+ append_to_statement_list (goto_expr, &stmt);
+ append_to_statement_list (then_, &stmt);
+ append_to_statement_list (label_expr, &stmt);
+ }
+ *stmt_p = stmt;
+ return;
+ }
+
if (!then_)
then_ = build_empty_stmt (locus);
if (!else_)
@@ -603,6 +630,36 @@ cp_gimplify_arg (tree *arg_p, gimple_seq
}
+/* Emit a decl = {CLOBBER(bob)}; stmt before DECL_EXPR or first
+ TARGET_EXPR gimplification for -flifetime-dse=2. */
+
+static void
+maybe_emit_clobber_object_begin (tree decl, gimple_seq *pre_p)
+{
+ if (auto_var_p (decl)
+ && TREE_TYPE (decl) != error_mark_node
+ && DECL_NONTRIVIALLY_INITIALIZED_P (decl)
+ /* Don't do it if it is fully initialized. */
+ && DECL_INITIAL (decl) == NULL_TREE
+ && !DECL_HAS_VALUE_EXPR_P (decl)
+ && !OPAQUE_TYPE_P (TREE_TYPE (decl))
+ /* Nor going to have decl = .DEFERRED_INIT (...); added. */
+ && (flag_auto_var_init == AUTO_INIT_UNINITIALIZED
+ || lookup_attribute ("uninitialized", DECL_ATTRIBUTES (decl))
+ || lookup_attribute ("indeterminate", DECL_ATTRIBUTES (decl))))
+ {
+ tree eltype = strip_array_types (TREE_TYPE (decl));
+ if (RECORD_OR_UNION_TYPE_P (eltype))
+ {
+ tree clobber
+ = build_clobber (TREE_TYPE (decl), CLOBBER_OBJECT_BEGIN);
+ gimple *g = gimple_build_assign (decl, clobber);
+ gimple_set_location (g, DECL_SOURCE_LOCATION (decl));
+ gimple_seq_add_stmt_without_update (pre_p, g);
+ }
+ }
+}
+
/* Do C++-specific gimplification. Args are as for gimplify_expr. */
int
@@ -918,6 +975,10 @@ cp_gimplify_expr (tree *expr_p, gimple_s
on the rhs of an assignment, as in constexpr-aggr1.C. */
gcc_checking_assert (!TARGET_EXPR_ELIDING_P (*expr_p)
|| !TREE_ADDRESSABLE (TREE_TYPE (*expr_p)));
+ if (flag_lifetime_dse > 1
+ && TARGET_EXPR_INITIAL (*expr_p)
+ && VOID_TYPE_P (TREE_TYPE (TARGET_EXPR_INITIAL (*expr_p))))
+ maybe_emit_clobber_object_begin (TARGET_EXPR_SLOT (*expr_p), pre_p);
ret = GS_UNHANDLED;
break;
@@ -929,6 +990,12 @@ cp_gimplify_expr (tree *expr_p, gimple_s
ret = GS_OK;
break;
+ case DECL_EXPR:
+ if (flag_lifetime_dse > 1)
+ maybe_emit_clobber_object_begin (DECL_EXPR_DECL (*expr_p), pre_p);
+ ret = GS_UNHANDLED;
+ break;
+
case RETURN_EXPR:
if (TREE_OPERAND (*expr_p, 0)
&& (TREE_CODE (TREE_OPERAND (*expr_p, 0)) == INIT_EXPR
@@ -76,6 +76,7 @@ enum bad_spec_place {
static const char *redeclaration_error_message (tree, tree);
static bool decl_jump_unsafe (tree);
+static bool decl_instrument_init_bypass_p (tree);
static void require_complete_types_for_parms (tree);
static tree grok_reference_init (tree, tree, tree, int);
static tree grokvardecl (tree, tree, tree, const cp_decl_specifier_seq *,
@@ -173,6 +174,25 @@ vec<tree, va_gc> *static_decls;
/* vector of keyed classes. */
vec<tree, va_gc> *keyed_classes;
+/* Used in the direct_goto vector of named_label_use_entry for
+ addresses of the LABEL_DECLs within GOTO_EXPR or asm goto
+ for forward jumps. */
+
+struct GTY(()) named_label_fwd_direct_goto {
+ tree *GTY((skip)) direct_goto;
+};
+
+/* Used in the direct_goto vector of named_label_use_entry for
+ addresses of the LABEL_DECLs within GOTO_EXPR or asm goto
+ for backward jumps. */
+
+struct GTY(()) named_label_bck_direct_goto {
+ tree *GTY((skip)) direct_goto;
+ /* Number of the decl_instrument_init_bypass_p decls in bad_decls vector
+ at the time this backward goto has been seen. */
+ unsigned n_bad_decls;
+};
+
/* Used only for jumps to as-yet undefined labels, since jumps to
defined labels can have their validity checked immediately. */
@@ -188,7 +208,11 @@ struct GTY((chain_next ("%h.next"))) nam
tree names_in_scope;
/* If the use is a possible destination of a computed goto, a vec of decls
that aren't destroyed, filled in by poplevel_named_label_1. */
- vec<tree,va_gc> *computed_goto;
+ vec<tree, va_gc> *computed_goto;
+ /* If the use is a destination of normal goto, a vec of addresses of
+ LABEL_DECLs that might need changing for !!flag_auto_var_init
+ forward jumps across vacuous initializers. */
+ vec<named_label_fwd_direct_goto, va_gc> *direct_goto;
/* The location of the goto, for error reporting. */
location_t o_goto_locus;
/* True if an OpenMP structured block scope has been closed since
@@ -226,20 +250,29 @@ struct GTY((for_user)) named_label_entry
/* A list of uses of the label, before the label is defined. */
named_label_use_entry *uses;
- /* True if we've seen &&label. Appalently we can't use TREE_ADDRESSABLE for
+ /* If the use is a destination of normal goto, a vec of addresses of
+ LABEL_DECLs that might need changing for !!flag_auto_var_init
+ backward jumps across vacuous initializers. */
+ vec<named_label_bck_direct_goto, va_gc> *direct_goto;
+
+ /* True if we've seen &&label. Apparently we can't use TREE_ADDRESSABLE for
this, it has a more specific meaning for LABEL_DECL. */
- bool addressed;
+ bool addressed : 1;
/* The following bits are set after the label is defined, and are
updated as scopes are popped. They indicate that a jump to the
label will illegally enter a scope of the given flavor. */
- bool in_try_scope;
- bool in_catch_scope;
- bool in_omp_scope;
- bool in_transaction_scope;
- bool in_constexpr_if;
- bool in_consteval_if;
- bool in_stmt_expr;
+ bool in_try_scope : 1;
+ bool in_catch_scope : 1;
+ bool in_omp_scope : 1;
+ bool in_transaction_scope : 1;
+ bool in_constexpr_if : 1;
+ bool in_consteval_if : 1;
+ bool in_stmt_expr : 1;
+
+ /* True if bad_decls chain contains any decl_jump_unsafe decls
+ (rather than just decl_instrument_init_bypass_p). */
+ bool has_bad_decls : 1;
};
#define named_labels cp_function_chain->x_named_labels
@@ -403,6 +436,59 @@ sort_labels (const void *a, const void *
return DECL_UID (label1) > DECL_UID (label2) ? -1 : +1;
}
+static void adjust_backward_goto (named_label_entry *, tree_stmt_iterator);
+static named_label_entry *lookup_label_1 (tree, bool);
+
+/* Helper of pop_labels, called through cp_walk_tree. Adjust
+ LABEL_EXPRs of named labels, if they are targets of backwards
+ gotos jumping across vacuous initialization for
+ !!flag_auto_var_init. */
+
+static tree
+adjust_backward_gotos (tree *tp, int *walk_subtrees, void *data)
+{
+ tree t = *tp;
+ switch (TREE_CODE (t))
+ {
+ case LABEL_EXPR:
+ if (DECL_NAME (LABEL_EXPR_LABEL (t)))
+ {
+ named_label_entry *ent
+ = lookup_label_1 (DECL_NAME (LABEL_EXPR_LABEL (t)), false);
+ if (ent->direct_goto)
+ {
+ *tp = alloc_stmt_list ();
+ append_to_statement_list_force (t, tp);
+ adjust_backward_goto (ent, tsi_last (*tp));
+ }
+ }
+ *walk_subtrees = 0;
+ break;
+ case STATEMENT_LIST:
+ {
+ tree_stmt_iterator i;
+ *walk_subtrees = 0;
+ for (i = tsi_start (t); !tsi_end_p (i); tsi_next (&i))
+ if (TREE_CODE (tsi_stmt (i)) != LABEL_EXPR)
+ cp_walk_tree (tsi_stmt_ptr (i), adjust_backward_gotos,
+ data, (hash_set<tree> *) data);
+ else if (DECL_NAME (LABEL_EXPR_LABEL (tsi_stmt (i))))
+ {
+ named_label_entry *ent
+ = lookup_label_1 (DECL_NAME (LABEL_EXPR_LABEL (tsi_stmt (i))),
+ false);
+ if (ent->direct_goto)
+ adjust_backward_goto (ent, i);
+ }
+ break;
+ }
+ default:
+ if (TYPE_P (t))
+ *walk_subtrees = 0;
+ }
+ return NULL_TREE;
+}
+
/* At the end of a function, all labels declared within the function
go out of scope. BLOCK is the top-level block for the
function. */
@@ -420,8 +506,23 @@ pop_labels (tree block)
table implementation changes. */
auto_vec<tree, 32> labels (named_labels->elements ());
hash_table<named_label_hash>::iterator end (named_labels->end ());
- for (hash_table<named_label_hash>::iterator iter
- (named_labels->begin ()); iter != end; ++iter)
+
+ if (flag_auto_var_init > AUTO_INIT_UNINITIALIZED)
+ {
+ for (decltype (end) iter (named_labels->begin ()); iter != end; ++iter)
+ {
+ named_label_entry *ent = *iter;
+ if (ent->direct_goto)
+ {
+ hash_set<tree> pset;
+ cp_walk_tree (&DECL_SAVED_TREE (current_function_decl),
+ adjust_backward_gotos, &pset, &pset);
+ break;
+ }
+ }
+ }
+
+ for (decltype (end) iter (named_labels->begin ()); iter != end; ++iter)
{
named_label_entry *ent = *iter;
@@ -551,6 +652,11 @@ poplevel_named_label_1 (named_label_entr
? DECL_CHAIN (decl)
: TREE_CHAIN (decl)))
if (decl_jump_unsafe (decl))
+ {
+ vec_safe_push (ent->bad_decls, decl);
+ ent->has_bad_decls = true;
+ }
+ else if (decl_instrument_init_bypass_p (decl))
vec_safe_push (ent->bad_decls, decl);
ent->binding_level = obl;
@@ -2930,6 +3036,19 @@ duplicate_decls (tree newdecl, tree oldd
{
DECL_ATTRIBUTES (newarg)
= (*targetm.merge_decl_attributes) (oldarg, newarg);
+ if (lookup_attribute (NULL, "indeterminate",
+ DECL_ATTRIBUTES (newarg))
+ && !lookup_attribute (NULL, "indeterminate",
+ DECL_ATTRIBUTES (oldarg)))
+ {
+ auto_diagnostic_group d;
+ error_at (DECL_SOURCE_LOCATION (newarg),
+ "%<indeterminate%> attribute not specified "
+ "for parameter %qD on the first declaration of "
+ "its function", newarg);
+ inform (DECL_SOURCE_LOCATION (oldarg),
+ "earlier declaration");
+ }
DECL_ATTRIBUTES (oldarg) = DECL_ATTRIBUTES (newarg);
}
@@ -3733,6 +3852,233 @@ decl_jump_unsafe (tree decl)
|| variably_modified_type_p (type, NULL_TREE)));
}
+/* Returns true if decl is an automatic variable with vacuous initialization
+ except when it is [[indeterminate]] or [[gnu::uninitialized]].
+ Jumps across such initialization need to be instrumented for
+ !!flag_auto_var_init. */
+
+static bool
+decl_instrument_init_bypass_p (tree decl)
+{
+ tree type = TREE_TYPE (decl);
+
+ return (flag_auto_var_init > AUTO_INIT_UNINITIALIZED
+ && type != error_mark_node
+ && VAR_P (decl)
+ && !TREE_STATIC (decl)
+ && !DECL_EXTERNAL (decl)
+ && !(DECL_NONTRIVIALLY_INITIALIZED_P (decl)
+ || variably_modified_type_p (type, NULL_TREE))
+ && !lookup_attribute (NULL, "indeterminate", DECL_ATTRIBUTES (decl))
+ && !lookup_attribute ("uninitialized", DECL_ATTRIBUTES (decl))
+ && !DECL_HAS_VALUE_EXPR_P (decl));
+}
+
+/* Build .DEFERRED_INIT call for DECL. */
+
+static tree
+build_deferred_init_call (tree decl)
+{
+ tree decl_size_arg = TYPE_SIZE_UNIT (TREE_TYPE (decl));
+ tree init_type_arg = build_int_cst (integer_type_node,
+ (int) flag_auto_var_init);
+ location_t loc = DECL_SOURCE_LOCATION (decl);
+ tree decl_name;
+
+ if (DECL_NAME (decl))
+ decl_name = build_string_literal (DECL_NAME (decl));
+ else
+ {
+ char decl_name_anonymous[3 + (HOST_BITS_PER_INT + 2) / 3];
+ sprintf (decl_name_anonymous, "D.%u", DECL_UID (decl));
+ decl_name = build_string_literal (decl_name_anonymous);
+ }
+
+ tree call = build_call_expr_internal_loc (loc, IFN_DEFERRED_INIT,
+ TREE_TYPE (decl), 3,
+ decl_size_arg, init_type_arg,
+ decl_name);
+ tree ret = build2_loc (loc, MODIFY_EXPR, void_type_node, decl, call);
+ return build_stmt (loc, EXPR_STMT, ret);
+}
+
+/* Emit before ITER (and any labels/case labels before it) code like
+ if (0)
+ {
+ l1:
+ v4 = .DEFERRED_INIT (sizeof (v4), ?, "v4");
+ v3 = .DEFERRED_INIT (sizeof (v3), ?, "v3");
+ v2 = .DEFERRED_INIT (sizeof (v2), ?, "v2");
+ v1 = .DEFERRED_INIT (sizeof (v1), ?, "v1");
+ }
+ and return l1 label, or if it already exists, assert it has the
+ .DEFERRED_INIT calls for the right decls in the right order and
+ amend it, either by adding extra labels in between or further
+ ,DEFERRED_INIT calls before the first label and extra label before
+ that. If CASE_LABEL is non-NULL, emit that CASE_LABEL_EXPR instead
+ of adding a label. DECLS points to an array of NDECLS VAR_DECLs
+ which should be initialized. */
+
+static tree
+maybe_add_deferred_init_calls (tree_stmt_iterator iter, tree case_label,
+ tree *decls, unsigned ndecls)
+{
+ tree lab = NULL_TREE;
+ for (; !tsi_end_p (iter); tsi_prev (&iter))
+ {
+ switch (TREE_CODE (tsi_stmt (iter)))
+ {
+ case LABEL_EXPR:
+ case CASE_LABEL_EXPR:
+ case DEBUG_BEGIN_STMT:
+ continue;
+ default:
+ break;
+ }
+ break;
+ }
+ if (!tsi_end_p (iter)
+ && TREE_CODE (tsi_stmt (iter)) == IF_STMT
+ && IF_STMT_VACUOUS_INIT_P (tsi_stmt (iter)))
+ {
+ tree then_clause = THEN_CLAUSE (tsi_stmt (iter));
+ iter = tsi_last (then_clause);
+ bool add = false;
+ for (unsigned int i = 0; i < ndecls; ++i)
+ {
+ tree decl = decls[i];
+ if (!add)
+ {
+ while (!tsi_end_p (iter)
+ && (TREE_CODE (tsi_stmt (iter)) == LABEL_EXPR
+ || (TREE_CODE (tsi_stmt (iter)) == CASE_LABEL_EXPR
+ && !case_label)))
+ tsi_prev (&iter);
+ if (tsi_end_p (iter))
+ {
+ add = true;
+ iter = tsi_start (then_clause);
+ }
+ else
+ {
+ tree t = tsi_stmt (iter);
+ gcc_checking_assert (TREE_CODE (t) == EXPR_STMT);
+ t = EXPR_STMT_EXPR (t);
+ gcc_checking_assert (TREE_CODE (t) == MODIFY_EXPR
+ && TREE_OPERAND (t, 0) == decl
+ && (TREE_CODE (TREE_OPERAND (t, 1))
+ == CALL_EXPR));
+ t = TREE_OPERAND (t, 1);
+ gcc_checking_assert (CALL_EXPR_FN (t) == NULL_TREE
+ && (CALL_EXPR_IFN (t)
+ == IFN_DEFERRED_INIT));
+ tsi_prev (&iter);
+ }
+ }
+ if (add)
+ {
+ tree t = build_deferred_init_call (decl);
+ STMT_IS_FULL_EXPR_P (t) = 1;
+ tsi_link_before (&iter, t, TSI_CONTINUE_LINKING);
+ }
+ }
+ if (!add)
+ {
+ while (!tsi_end_p (iter)
+ && !case_label
+ && TREE_CODE (tsi_stmt (iter)) == CASE_LABEL_EXPR)
+ tsi_prev (&iter);
+ if (tsi_end_p (iter))
+ {
+ add = true;
+ iter = tsi_start (then_clause);
+ }
+ else if (!case_label
+ && TREE_CODE (tsi_stmt (iter)) == LABEL_EXPR)
+ lab = LABEL_EXPR_LABEL (tsi_stmt (iter));
+ else
+ {
+ gcc_checking_assert (case_label
+ || (TREE_CODE (tsi_stmt (iter))
+ == EXPR_STMT));
+ add = true;
+ tsi_next (&iter);
+ gcc_checking_assert (!tsi_end_p (iter));
+ }
+ }
+ if (add)
+ {
+ tree t;
+ if (case_label)
+ t = case_label;
+ else
+ {
+ lab = create_artificial_label (UNKNOWN_LOCATION);
+ t = build_stmt (UNKNOWN_LOCATION, LABEL_EXPR, lab);
+ }
+ tsi_link_before (&iter, t, TSI_CONTINUE_LINKING);
+ }
+ }
+ else
+ {
+ tree new_then = push_stmt_list ();
+ if (!case_label)
+ {
+ lab = create_artificial_label (UNKNOWN_LOCATION);
+ add_stmt (build_stmt (UNKNOWN_LOCATION, LABEL_EXPR, lab));
+ }
+ else
+ add_stmt (case_label);
+ for (unsigned int i = ndecls; i; --i)
+ add_stmt (build_deferred_init_call (decls[i - 1]));
+ new_then = pop_stmt_list (new_then);
+ tree stmt = build4 (IF_STMT, void_type_node, boolean_false_node,
+ new_then, void_node, NULL_TREE);
+ IF_STMT_VACUOUS_INIT_P (stmt) = 1;
+ if (tsi_end_p (iter))
+ {
+ iter = tsi_start (iter.container);
+ tsi_link_before (&iter, stmt, TSI_SAME_STMT);
+ }
+ else
+ tsi_link_after (&iter, stmt, TSI_CONTINUE_LINKING);
+ }
+ return lab;
+}
+
+/* Adjust backward gotos to named label ENT if they jump over vacuous
+ initializers if !!flag_auto_var_init. ITER is the location of
+ LABEL_EXPR for that named label. */
+
+static void
+adjust_backward_goto (named_label_entry *ent, tree_stmt_iterator iter)
+{
+ auto_vec<tree, 4> decls;
+ unsigned int i, max_cnt = ent->direct_goto->last ().n_bad_decls;
+ tree decl;
+ FOR_EACH_VEC_SAFE_ELT (ent->bad_decls, i, decl)
+ if (!decl_jump_unsafe (decl))
+ {
+ gcc_checking_assert (decl_instrument_init_bypass_p (decl));
+ decls.safe_push (decl);
+ if (decls.length () == max_cnt)
+ break;
+ }
+ named_label_bck_direct_goto *dgoto;
+ unsigned last = 0;
+ tree lab = NULL_TREE;
+ FOR_EACH_VEC_SAFE_ELT_REVERSE (ent->direct_goto, i, dgoto)
+ {
+ if (dgoto->n_bad_decls != last)
+ {
+ last = dgoto->n_bad_decls;
+ lab = maybe_add_deferred_init_calls (iter, NULL_TREE,
+ decls.address (), last);
+ }
+ *dgoto->direct_goto = lab;
+ }
+}
+
/* A subroutine of check_previous_goto_1 and check_goto to identify a branch
to the user. */
@@ -3760,13 +4106,19 @@ identify_goto (tree decl, location_t loc
is OK. DECL is the LABEL_DECL or 0; LEVEL is the binding_level for
the jump context; NAMES are the names in scope in LEVEL at the jump
context; LOCUS is the source position of the jump or 0. COMPUTED
- is a vec of decls if the jump is a computed goto. Returns
- true if all is well. */
+ is a vec of decls if the jump is a computed goto. DIRECT_GOTO is a
+ vec of pointers to LABEL_DECLs that might need adjusting if vacuous
+ initializations are crossed for !!flag_auto_var_init. CASE_LABEL is
+ CASE_LABEL_EXPR to be moved if needed for the check_switch_goto case.
+ Returns non-zero if all is well, 2 if any vacuous initializers were
+ crossed. */
-static bool
-check_previous_goto_1 (tree decl, cp_binding_level* level, tree names,
+static int
+check_previous_goto_1 (tree decl, cp_binding_level *level, tree names,
bool exited_omp, const location_t *locus,
- vec<tree,va_gc> *computed)
+ vec<tree, va_gc> *computed,
+ vec<named_label_fwd_direct_goto, va_gc> *direct_goto,
+ tree case_label)
{
auto_diagnostic_group d;
cp_binding_level *b;
@@ -3774,6 +4126,8 @@ check_previous_goto_1 (tree decl, cp_bin
int identified = 0;
bool saw_eh = false, saw_omp = false, saw_tm = false, saw_cxif = false;
bool saw_ceif = false, saw_se = false;
+ auto_vec<tree> vacuous_decls;
+ bool vacuous_inits = false;
if (exited_omp)
{
@@ -3796,7 +4150,15 @@ check_previous_goto_1 (tree decl, cp_bin
{
bool problem = decl_jump_unsafe (new_decls);
if (! problem)
- continue;
+ {
+ if (decl_instrument_init_bypass_p (new_decls))
+ {
+ if (direct_goto || case_label)
+ vacuous_decls.safe_push (new_decls);
+ vacuous_inits = true;
+ }
+ continue;
+ }
if (!identified)
{
@@ -3895,7 +4257,30 @@ check_previous_goto_1 (tree decl, cp_bin
}
}
- return !identified;
+ if (!vacuous_decls.is_empty () && !seen_error ())
+ {
+ tree_stmt_iterator iter = tsi_last (cur_stmt_list);
+ if (case_label)
+ {
+ gcc_checking_assert (tsi_stmt (iter) == case_label);
+ tsi_delink (&iter);
+ iter = tsi_last (cur_stmt_list);
+ }
+ tree lab = maybe_add_deferred_init_calls (iter, case_label,
+ vacuous_decls.address (),
+ vacuous_decls.length ());
+ if (lab)
+ {
+ unsigned int i;
+ named_label_fwd_direct_goto *dgoto;
+ FOR_EACH_VEC_SAFE_ELT (direct_goto, i, dgoto)
+ *dgoto->direct_goto = lab;
+ }
+ }
+
+ if (identified)
+ return 0;
+ return vacuous_inits ? 2 : 1;
}
static void
@@ -3903,24 +4288,27 @@ check_previous_goto (tree decl, struct n
{
check_previous_goto_1 (decl, use->binding_level,
use->names_in_scope, use->in_omp_scope,
- &use->o_goto_locus, use->computed_goto);
+ &use->o_goto_locus, use->computed_goto,
+ use->direct_goto, NULL_TREE);
+ vec_free (use->direct_goto);
}
-static bool
-check_switch_goto (cp_binding_level* level)
+static int
+check_switch_goto (cp_binding_level *level, tree case_label)
{
return check_previous_goto_1 (NULL_TREE, level, level->names,
- false, NULL, nullptr);
+ false, NULL, nullptr, nullptr, case_label);
}
-/* Check that a new jump to a label ENT is OK. COMPUTED is true
- if this is a possible target of a computed goto. */
+/* Check that a new jump to a label ENT is OK. DECLP is a pointer
+ to a LABEL_DECL for direct gotos and NULL for computed gotos. */
void
-check_goto_1 (named_label_entry *ent, bool computed)
+check_goto_1 (named_label_entry *ent, tree *declp)
{
auto_diagnostic_group d;
tree decl = ent->label_decl;
+ bool computed = declp == NULL;
/* If the label hasn't been defined yet, defer checking. */
if (! DECL_INITIAL (decl))
@@ -3928,8 +4316,14 @@ check_goto_1 (named_label_entry *ent, bo
/* Don't bother creating another use if the last goto had the
same data, and will therefore create the same set of errors. */
if (ent->uses
+ && ent->uses->binding_level == current_binding_level
&& ent->uses->names_in_scope == current_binding_level->names)
- return;
+ {
+ if (declp && flag_auto_var_init > AUTO_INIT_UNINITIALIZED)
+ vec_safe_push (ent->uses->direct_goto,
+ named_label_fwd_direct_goto { declp });
+ return;
+ }
named_label_use_entry *new_use
= ggc_alloc<named_label_use_entry> ();
@@ -3938,6 +4332,10 @@ check_goto_1 (named_label_entry *ent, bo
new_use->o_goto_locus = input_location;
new_use->in_omp_scope = false;
new_use->computed_goto = computed ? make_tree_vector () : nullptr;
+ new_use->direct_goto = nullptr;
+ if (declp && flag_auto_var_init > AUTO_INIT_UNINITIALIZED)
+ vec_safe_push (new_use->direct_goto,
+ named_label_fwd_direct_goto { declp });
new_use->next = ent->uses;
ent->uses = new_use;
@@ -3948,11 +4346,12 @@ check_goto_1 (named_label_entry *ent, bo
int identified = 0;
tree bad;
unsigned ix;
+ unsigned n_bad_decls = 0;
if (ent->in_try_scope || ent->in_catch_scope || ent->in_transaction_scope
|| ent->in_constexpr_if || ent->in_consteval_if
|| ent->in_omp_scope || ent->in_stmt_expr
- || !vec_safe_is_empty (ent->bad_decls))
+ || ent->has_bad_decls)
{
enum diagnostics::kind diag_kind = diagnostics::kind::permerror;
if (ent->in_try_scope || ent->in_catch_scope || ent->in_constexpr_if
@@ -3967,8 +4366,14 @@ check_goto_1 (named_label_entry *ent, bo
FOR_EACH_VEC_SAFE_ELT (ent->bad_decls, ix, bad)
{
bool problem = decl_jump_unsafe (bad);
+ if (!problem)
+ {
+ gcc_checking_assert (decl_instrument_init_bypass_p (bad));
+ n_bad_decls++;
+ continue;
+ }
- if (problem && DECL_ARTIFICIAL (bad))
+ if (DECL_ARTIFICIAL (bad))
{
/* Can't skip init of __exception_info. */
if (identified == 1)
@@ -4075,16 +4480,21 @@ check_goto_1 (named_label_entry *ent, bo
break;
}
}
+
+ if (n_bad_decls && declp)
+ vec_safe_push (ent->direct_goto,
+ named_label_bck_direct_goto { declp, n_bad_decls });
}
-/* Check that a new jump to a label DECL is OK. Called by
+/* Check that a new jump to a label *DECLP is OK. Called by
finish_goto_stmt. */
void
-check_goto (tree decl)
+check_goto (tree *declp)
{
if (!named_labels)
return;
+ tree decl = *declp;
if (TREE_CODE (decl) != LABEL_DECL)
{
/* We don't know where a computed goto is jumping,
@@ -4095,7 +4505,7 @@ check_goto (tree decl)
{
auto ent = *iter;
if (ent->addressed)
- check_goto_1 (ent, true);
+ check_goto_1 (ent, NULL);
}
}
else
@@ -4104,7 +4514,7 @@ check_goto (tree decl)
named_label_entry **slot
= named_labels->find_slot_with_hash (DECL_NAME (decl), hash, NO_INSERT);
named_label_entry *ent = *slot;
- check_goto_1 (ent, false);
+ check_goto_1 (ent, declp);
}
}
@@ -4359,7 +4769,8 @@ finish_case_label (location_t loc, tree
if (cond && TREE_CODE (cond) == TREE_LIST)
cond = TREE_VALUE (cond);
- if (!check_switch_goto (switch_stack->level))
+ int chk_switch_goto = check_switch_goto (switch_stack->level, NULL_TREE);
+ if (!chk_switch_goto)
return error_mark_node;
type = SWITCH_STMT_TYPE (switch_stack->switch_stmt);
@@ -4371,6 +4782,9 @@ finish_case_label (location_t loc, tree
r = c_add_case_label (loc, switch_stack->cases, cond, low_value, high_value);
+ if (r != error_mark_node && chk_switch_goto == 2)
+ check_switch_goto (switch_stack->level, r);
+
/* After labels, make any new cleanups in the function go into their
own new (temporary) binding contour. */
for (p = current_binding_level;
@@ -19316,18 +19730,6 @@ start_preparsed_function (tree decl1, tr
start_function_contracts (decl1);
if (!processing_template_decl
- && (flag_lifetime_dse > 1)
- && DECL_CONSTRUCTOR_P (decl1)
- && !DECL_CLONED_FUNCTION_P (decl1)
- /* Clobbering an empty base is harmful if it overlays real data. */
- && !is_empty_class (current_class_type)
- /* We can't clobber safely for an implicitly-defined default constructor
- because part of the initialization might happen before we enter the
- constructor, via AGGR_INIT_ZERO_FIRST (c++/68006). */
- && !implicit_default_ctor_p (decl1))
- finish_expr_stmt (build_clobber_this (CLOBBER_OBJECT_BEGIN));
-
- if (!processing_template_decl
&& DECL_CONSTRUCTOR_P (decl1)
&& sanitize_flags_p (SANITIZE_VPTR)
&& !DECL_CLONED_FUNCTION_P (decl1)
@@ -31179,10 +31179,10 @@ cp_parser_asm_label_list (cp_parser* par
if (TREE_CODE (label) == LABEL_DECL)
{
TREE_USED (label) = 1;
- check_goto (label);
name = build_string (IDENTIFIER_LENGTH (identifier),
IDENTIFIER_POINTER (identifier));
labels = tree_cons (name, label, labels);
+ check_goto (&TREE_VALUE (labels));
}
}
/* If the next token is not a `,', then the list is
@@ -919,10 +919,12 @@ finish_goto_stmt (tree destination)
}
}
- check_goto (destination);
-
add_stmt (build_predict_expr (PRED_GOTO, NOT_TAKEN));
- return add_stmt (build_stmt (input_location, GOTO_EXPR, destination));
+
+ tree stmt = build_stmt (input_location, GOTO_EXPR, destination);
+ check_goto (&TREE_OPERAND (stmt, 0));
+
+ return add_stmt (stmt);
}
/* Returns true if T corresponds to an assignment operator expression. */
@@ -5591,6 +5591,23 @@ handle_maybe_unused_attribute (tree *nod
return ret;
}
+/* The C++26 [[indeterminate]] attribute. */
+
+static tree
+handle_indeterminate_attribute (tree *node, tree name, tree, int,
+ bool *no_add_attrs)
+{
+ if (TREE_CODE (*node) != PARM_DECL
+ && (!VAR_P (*node) || is_global_var (*node)))
+ {
+ pedwarn (input_location, OPT_Wattributes,
+ "%qE on declaration other than parameter or automatic variable",
+ name);
+ *no_add_attrs = true;
+ }
+ return NULL_TREE;
+}
+
/* Table of valid C++ attributes. */
static const attribute_spec cxx_gnu_attributes[] =
{
@@ -5630,6 +5647,8 @@ static const attribute_spec std_attribut
handle_noreturn_attribute, attr_noreturn_exclusions },
{ "carries_dependency", 0, 0, true, false, false, false,
handle_carries_dependency_attribute, NULL },
+ { "indeterminate", 0, 0, true, false, false, false,
+ handle_indeterminate_attribute, NULL },
{ "pre", 0, -1, false, false, false, false,
handle_contract_attribute, NULL },
{ "post", 0, -1, false, false, false, false,
@@ -1,5 +1,4 @@
// { dg-do run { target c++11 } }
-// { dg-skip-if "power overwrites two slots of array i" { "power*-*-*" } }
// { dg-options "-Wno-vla" }
#include <initializer_list>
@@ -7,7 +6,7 @@
struct A
{
int i;
- A(std::initializer_list<int>) { }
+ A(std::initializer_list<int>) : i{43} { }
A(int i): i{i} { }
~A() {}
};
@@ -18,7 +17,7 @@ int main(int argc, char **argv)
{ int i[x] = { 42, 42, 42, 42 }; }
{
A a[x] = { argc };
- if (a[1].i != 42)
+ if (a[1].i != 43)
__builtin_abort ();
}
}
@@ -0,0 +1,154 @@
+// C++ 26 P2795R5 - Erroneous behaviour for uninitialized reads
+// { dg-do compile { target c++11 } }
+
+int arr[2];
+struct S { int a, b; };
+S arr2[2];
+
+void
+foo ([[indeterminate]] int n, int n2 [[indeterminate]], int n3 [[indeterminate]] [2])
+{
+ [[indeterminate]] int x1, x11, x12, x13;
+ int x14, x15 [[indeterminate]];
+ [[indeterminate ("foobar")]] int x2; // { dg-error "'indeterminate' attribute does not take any arguments" }
+ // { dg-error "expected primary-expression before 'int'" "" { target *-*-* } .-1 }
+ [[indeterminate (0)]] int x3; // { dg-error "'indeterminate' attribute does not take any arguments" }
+ // { dg-error "expected primary-expression before 'int'" "" { target *-*-* } .-1 }
+ [[indeterminate ("foo", "bar", "baz")]] int x4;// { dg-error "'indeterminate' attribute does not take any arguments" }
+ // { dg-error "expected primary-expression before 'int'" "" { target *-*-* } .-1 }
+ [[indeterminate (0, 1, 2)]] int x5; // { dg-error "'indeterminate' attribute does not take any arguments" }
+ // { dg-error "expected primary-expression before 'int'" "" { target *-*-* } .-1 }
+
+ auto a = [] [[indeterminate]] () {}; // { dg-error "'indeterminate' on declaration other than parameter or automatic variable" }
+ auto b = [] constexpr [[indeterminate]] {}; // { dg-warning "'indeterminate' attribute does not apply to types" }
+ // { dg-error "parameter declaration before lambda declaration specifiers only optional with" "" { target c++20_down } .-1 }
+ // { dg-error "'constexpr' lambda only available with" "" { target c++14_down } .-2 }
+ auto c = [] noexcept [[indeterminate]] {}; // { dg-warning "'indeterminate' attribute does not apply to types" }
+ // { dg-error "parameter declaration before lambda exception specification only optional with" "" { target c++20_down } .-1 }
+ auto d = [] () [[indeterminate]] {}; // { dg-warning "'indeterminate' attribute does not apply to types" }
+ auto e = new int [n] [[indeterminate]]; // { dg-warning "attributes ignored on outermost array type in new expression" }
+ auto e2 = new int [n] [[indeterminate]] [42]; // { dg-warning "attributes ignored on outermost array type in new expression" }
+ auto f = new int [n][42] [[indeterminate]]; // { dg-warning "'indeterminate' attribute does not apply to types" }
+ [[indeterminate]]; // { dg-warning "attributes at the beginning of statement are ignored" }
+ [[indeterminate]] {} // { dg-warning "attributes at the beginning of statement are ignored" }
+ [[indeterminate]] if (true) {} // { dg-warning "attributes at the beginning of statement are ignored" }
+ [[indeterminate]] while (false) {} // { dg-warning "attributes at the beginning of statement are ignored" }
+ [[indeterminate]] goto lab; // { dg-warning "attributes at the beginning of statement are ignored" }
+ [[indeterminate]] lab:; // { dg-error "'indeterminate' on declaration other than parameter or automatic variable" }
+ [[indeterminate]] try {} catch (int) {} // { dg-warning "attributes at the beginning of statement are ignored" }
+ if ([[indeterminate]] int x = 0) {}
+ switch (n)
+ {
+ [[indeterminate]] case 1: // { dg-error "'indeterminate' on declaration other than parameter or automatic variable" }
+ [[indeterminate]] break; // { dg-warning "attributes at the beginning of statement are ignored" }
+ [[indeterminate]] default: // { dg-error "'indeterminate' on declaration other than parameter or automatic variable" }
+ break;
+ }
+ for ([[indeterminate]] auto a : arr) {}
+ for ([[indeterminate]] auto [a, b] : arr2) {} // { dg-error "structured bindings only available with" "" { target c++14_down } }
+ [[indeterminate]] asm (""); // { dg-warning "attributes ignored on 'asm' declaration" }
+ try {} catch ([[indeterminate]] int x) {}
+ try {} catch ([[indeterminate]] int) {}
+ try {} catch (int [[indeterminate]] x) {} // { dg-warning "attribute ignored" }
+ try {} catch (int [[indeterminate]]) {} // { dg-warning "attribute ignored" }
+ try {} catch (int x [[indeterminate]]) {}
+}
+
+[[indeterminate]] int bar (); // { dg-error "'indeterminate' on declaration other than parameter or automatic variable" }
+using foobar [[indeterminate]] = int; // { dg-error "'indeterminate' on declaration other than parameter or automatic variable" }
+[[indeterminate]] int a; // { dg-error "'indeterminate' on declaration other than parameter or automatic variable" }
+[[indeterminate]] auto [b, c] = arr; // { dg-error "'indeterminate' on declaration other than parameter or automatic variable" }
+ // { dg-error "structured bindings only available with" "" { target c++14_down } .-1 }
+[[indeterminate]]; // { dg-warning "attribute ignored" }
+inline [[indeterminate]] void baz () {} // { dg-warning "attribute ignored" }
+ // { dg-error "standard attributes in middle of decl-specifiers" "" { target *-*-* } .-1 }
+constexpr [[indeterminate]] int qux () { return 0; } // { dg-warning "attribute ignored" }
+ // { dg-error "standard attributes in middle of decl-specifiers" "" { target *-*-* } .-1 }
+int [[indeterminate]] d; // { dg-warning "attribute ignored" }
+int const [[indeterminate]] e = 1; // { dg-warning "attribute ignored" }
+struct A {} [[indeterminate]]; // { dg-warning "attribute ignored in declaration of 'struct A'" }
+struct A [[indeterminate]]; // { dg-warning "attribute ignored" }
+struct A [[indeterminate]] a1; // { dg-warning "attribute ignored" }
+A [[indeterminate]] a2; // { dg-warning "attribute ignored" }
+enum B { B0 } [[indeterminate]]; // { dg-warning "attribute ignored in declaration of 'enum B'" }
+enum B [[indeterminate]]; // { dg-warning "attribute ignored" }
+enum B [[indeterminate]] b1; // { dg-warning "attribute ignored" }
+B [[indeterminate]] b2; // { dg-warning "attribute ignored" }
+struct [[indeterminate]] C {}; // { dg-warning "'indeterminate' attribute does not apply to types" }
+int f [[indeterminate]]; // { dg-error "'indeterminate' on declaration other than parameter or automatic variable" }
+int g[2] [[indeterminate]]; // { dg-warning "'indeterminate' attribute does not apply to types" }
+int g2 [[indeterminate]] [2]; // { dg-error "'indeterminate' on declaration other than parameter or automatic variable" }
+int corge () [[indeterminate]]; // { dg-warning "'indeterminate' attribute does not apply to types" }
+int *[[indeterminate]] h; // { dg-warning "'indeterminate' attribute does not apply to types" }
+int & [[indeterminate]] i = f; // { dg-warning "'indeterminate' attribute does not apply to types" }
+int && [[indeterminate]] j = 0; // { dg-warning "'indeterminate' attribute does not apply to types" }
+int S::* [[indeterminate]] k; // { dg-warning "'indeterminate' attribute does not apply to types" }
+auto l = sizeof (int [2] [[indeterminate]]); // { dg-warning "'indeterminate' attribute does not apply to types" }
+int freddy ([[indeterminate]] int a,
+ [[indeterminate]] int,
+ [[indeterminate]] int c = 0,
+ [[indeterminate]] int = 0);
+void
+corge ([[indeterminate]] int a,
+ [[indeterminate]] int,
+ [[indeterminate]] int c = 0,
+ [[indeterminate]] int = 0)
+{
+}
+[[indeterminate]] void
+garply () // { dg-error "'indeterminate' on declaration other than parameter or automatic variable" }
+{
+}
+int grault (int [[indeterminate]] a, // { dg-warning "attribute ignored" }
+ int [[indeterminate]], // { dg-warning "attribute ignored" }
+ int [[indeterminate]] c = 0, // { dg-warning "attribute ignored" }
+ int [[indeterminate]] = 0); // { dg-warning "attribute ignored" }
+void
+waldo (int [[indeterminate]] a, // { dg-warning "attribute ignored" }
+ int [[indeterminate]], // { dg-warning "attribute ignored" }
+ int [[indeterminate]] c = 0, // { dg-warning "attribute ignored" }
+ int [[indeterminate]] = 0) // { dg-warning "attribute ignored" }
+{
+}
+int plugh (int a [[indeterminate]],
+ int b [[indeterminate]] = 0);
+void
+thud (int a [[indeterminate]],
+ int b [[indeterminate]] = 0)
+{
+}
+enum [[indeterminate]] D { D0 }; // { dg-warning "'indeterminate' attribute does not apply to types" }
+enum class [[indeterminate]] E { E0 }; // { dg-warning "'indeterminate' attribute does not apply to types" }
+enum F {};
+enum [[indeterminate]] F; // { dg-warning "'indeterminate' attribute does not apply to types" }
+enum G {
+ G0 [[indeterminate]], // { dg-error "'indeterminate' on declaration other than parameter or automatic variable" }
+ G1 [[indeterminate]] = 2 // { dg-error "'indeterminate' on declaration other than parameter or automatic variable" }
+};
+namespace [[indeterminate]] H { using H0 = int; }// { dg-warning "'indeterminate' attribute directive ignored" }
+namespace [[indeterminate]] {} // { dg-warning "'indeterminate' attribute directive ignored" }
+[[indeterminate]] using namespace H; // { dg-warning "'indeterminate' attribute directive ignored" }
+struct [[indeterminate]] I // { dg-warning "'indeterminate' attribute does not apply to types" }
+{
+ [[indeterminate]]; // { dg-error "declaration does not declare anything" }
+ [[indeterminate]] int i; // { dg-error "'indeterminate' on declaration other than parameter or automatic variable" }
+ [[indeterminate]] int foo (); // { dg-error "'indeterminate' on declaration other than parameter or automatic variable" }
+ [[indeterminate]] int bar () { return 1; } // { dg-error "'indeterminate' on declaration other than parameter or automatic variable" }
+ [[indeterminate]] int : 0; // { dg-error "'indeterminate' on declaration other than parameter or automatic variable" }
+ [[indeterminate]] int i2 : 5; // { dg-error "'indeterminate' on declaration other than parameter or automatic variable" }
+ [[indeterminate]] static int i3; // { dg-error "'indeterminate' on declaration other than parameter or automatic variable" }
+ static int i4;
+};
+[[indeterminate]] int I::i4 = 0; // { dg-error "'indeterminate' on declaration other than parameter or automatic variable" }
+struct J : [[indeterminate]] C {}; // { dg-warning "attributes on base specifiers are ignored" }
+#if __cpp_concepts >= 201907L
+template <typename T>
+concept K [[indeterminate]] = requires { true; };// { dg-error "'indeterminate' on declaration other than parameter or automatic variable" "" { target c++20 } }
+#endif
+typedef int L [[indeterminate]]; // { dg-error "'indeterminate' on declaration other than parameter or automatic variable" }
+template <typename T>
+struct M {};
+template <>
+struct [[indeterminate]] M<int> { int m; }; // { dg-warning "'indeterminate' attribute does not apply to types" }
+typedef int N[2] [[indeterminate]]; // { dg-warning "'indeterminate' attribute does not apply to types" }
+typedef int O [[indeterminate]] [2]; // { dg-error "'indeterminate' on declaration other than parameter or automatic variable" }
@@ -0,0 +1,39 @@
+// C++ 26 P2795R5 - Erroneous behaviour for uninitialized reads
+// { dg-do compile { target c++11 } }
+// { dg-additional-options "-fdump-tree-gimple" }
+// { dg-skip-if "" { c++26 } { "-ftrivial-auto-var-init=*" } { "" } }
+// Expect .DEFERRED_INIT calls for the h, r and s variables (3) and
+// temporaries for the second arguments to foo and baz calls (4).
+// { dg-final { scan-tree-dump-times " = \\.DEFERRED_INIT \\\(" 7 "gimple" { target c++26 } } }
+
+struct S { S (); S (const S &); ~S (); int s; };
+void foo (S a [[indeterminate]], S b, S c [[indeterminate]] = S ());
+void foo (S d, S e, S f [[indeterminate]]);
+
+void
+bar ()
+{
+ S g [[indeterminate]], h;
+ foo (g, h, S ());
+ foo (g, h);
+}
+
+void
+foo (S i [[indeterminate]], S j, S k)
+{
+}
+
+void
+baz ([[indeterminate]] S l, S m, [[indeterminate]] S n = S ())
+{
+}
+
+void baz (S o, S p, S q);
+
+void
+qux ()
+{
+ S r, s;
+ baz (r, s, s);
+ baz (r, s);
+}
@@ -0,0 +1,21 @@
+// C++ 26 P2795R5 - Erroneous behaviour for uninitialized reads
+// { dg-do compile { target c++11 } }
+// { dg-skip-if "" { c++26 } { "-ftrivial-auto-var-init=*" } { "" } }
+
+struct S { S (); S (const S &); ~S (); int s; };
+void foo (S u, S v [[indeterminate]], int);
+void foo (S a, S b, S c = S ()); // { dg-message "earlier declaration" }
+void foo (S d, S e, S f [[indeterminate]]); // { dg-error "'indeterminate' attribute not specified for parameter 'f' on the first declaration of its function" }
+
+void
+foo (S i [[indeterminate]], S j, S k) // { dg-error "'indeterminate' attribute not specified for parameter 'i' on the first declaration of its function" }
+{
+}
+
+void
+bar (S l, S m, S n = S ()) // { dg-message "earlier declaration" }
+{
+}
+
+void bar (S o [[indeterminate]], S p, [[indeterminate]]S q); // { dg-error "'indeterminate' attribute not specified for parameter 'o' on the first declaration of its function" }
+ // { dg-error "'indeterminate' attribute not specified for parameter 'q' on the first declaration of its function" "" { target *-*-* } .-1 }
@@ -0,0 +1,36 @@
+// C++ 26 P2795R5 - Erroneous behaviour for uninitialized reads
+// { dg-do compile { target c++11 } }
+// { dg-additional-options "-ftrivial-auto-var-init=uninitialized -fdump-tree-gimple" }
+// { dg-final { scan-tree-dump-not " = \\.DEFERRED_INIT \\\(" "gimple" } }
+
+struct S { S (); S (const S &); ~S (); int s; };
+void foo (S a [[indeterminate]], S b, S c [[indeterminate]] = S ());
+void foo (S d, S e, S f [[indeterminate]]);
+
+void
+bar ()
+{
+ S g [[indeterminate]], h;
+ foo (g, h, S ());
+ foo (g, h);
+}
+
+void
+foo (S i [[indeterminate]], S j, S k)
+{
+}
+
+void
+baz ([[indeterminate]] S l, S m, [[indeterminate]] S n = S ())
+{
+}
+
+void baz (S o, S p, S q);
+
+void
+qux ()
+{
+ S r, s;
+ baz (r, s, s);
+ baz (r, s);
+}
@@ -0,0 +1,61 @@
+// C++ 26 P2795R5 - Erroneous behaviour for uninitialized reads
+// { dg-do run { target c++26 } }
+// { dg-skip-if "" { *-*-* } { "-ftrivial-auto-var-init=*" } { "" } }
+// { dg-options "-O2 -Wuninitialized" }
+
+#define assert(x) if (!(x)) __builtin_abort ()
+
+template <typename T>
+[[gnu::noipa]] T
+baz (T &x)
+{
+ return x;
+}
+
+[[gnu::noipa]] int
+foo (bool b)
+{
+ unsigned char c;
+ unsigned char d = c; // no erroneous behavior, but d has an erroneous value
+ // { dg-warning "'c' is used uninitialized" "" { target *-*-* } .-1 }
+
+ assert (c == d); // holds, both integral promotions have erroneous behavior
+
+ unsigned char f = c;
+ unsigned char g = baz (f);
+
+ assert (g == c);
+
+ int e = d; // erroneous behavior
+ baz (e);
+ return b ? d : 0; // erroneous behavior if b is true
+}
+
+[[gnu::noipa]] void
+bar ()
+{
+ int d1, d2;
+
+ int e1 = d1; // erroneous behavior
+ int e2 = d1; // erroneous behavior
+
+ assert (e1 == e2); // holds
+ assert (e1 == d1); // holds, erroneous behavior
+ assert (e2 == d1); // holds, erroneous behavior
+
+ int f = d1; // { dg-warning "'d1' is used uninitialized" }
+ int g = baz (f);
+ assert (g == d1);
+
+ __builtin_memcpy (&d2, &d1, sizeof (int)); // no erroneous behavior, but d2 has an erroneous value
+ assert (e1 == d2); // holds, erroneous behavior
+ assert (e2 == d2); // holds, erroneous behavior
+}
+
+int
+main ()
+{
+ foo (false);
+ foo (true);
+ bar ();
+}
@@ -0,0 +1,234 @@
+// C++ 26 P2795R5 - Erroneous behaviour for uninitialized reads
+// { dg-do compile }
+// { dg-skip-if "" { *-*-* } { "-ftrivial-auto-var-init=*" } { "" } }
+// { dg-options "-O2 -fdump-tree-gimple" }
+// All the s1..s24 variables and i1 need .DEFERRED_INIT call on their
+// declarations.
+// Plus, forward gotos to l1 & l2 labels need up to s1-s4 and s6-s9 vars to
+// be .DEFERRED_INITed (and backward gotos up to that minus the first two).
+// switch to case 15 skips over s12, switch to case 16/17 skip
+// over s12 and s13 but the adjacent l3 label needs to also skip over s3-s4
+// and s6-s9 and s11. switch to case 18 skips over s12-s14 and switch to
+// default in the same switch skips over s12-s15.
+// goto l4; skips over s19 initialization.
+// goto l5; skips over s20-s22 initialization.
+// switch to case 32/33 skips over s23 but goto to adjacent l6 skips also
+// over s20-s22. switch to default in that switch skips over s23-s24.
+// { dg-final { scan-tree-dump-times " s1 = \.DEFERRED_INIT \\\(" 2 "gimple" { target c++26 } } }
+// { dg-final { scan-tree-dump-times " s2 = \.DEFERRED_INIT \\\(" 2 "gimple" { target c++26 } } }
+// { dg-final { scan-tree-dump-times " s3 = \.DEFERRED_INIT \\\(" 3 "gimple" { target c++26 } } }
+// { dg-final { scan-tree-dump-times " s4 = \.DEFERRED_INIT \\\(" 3 "gimple" { target c++26 } } }
+// { dg-final { scan-tree-dump-times " s5 = \.DEFERRED_INIT \\\(" 1 "gimple" { target c++26 } } }
+// { dg-final { scan-tree-dump-times " s6 = \.DEFERRED_INIT \\\(" 3 "gimple" { target c++26 } } }
+// { dg-final { scan-tree-dump-times " s7 = \.DEFERRED_INIT \\\(" 3 "gimple" { target c++26 } } }
+// { dg-final { scan-tree-dump-times " s8 = \.DEFERRED_INIT \\\(" 3 "gimple" { target c++26 } } }
+// { dg-final { scan-tree-dump-times " s9 = \.DEFERRED_INIT \\\(" 3 "gimple" { target c++26 } } }
+// { dg-final { scan-tree-dump-times " s10 = \.DEFERRED_INIT \\\(" 1 "gimple" { target c++26 } } }
+// { dg-final { scan-tree-dump-times " s11 = \.DEFERRED_INIT \\\(" 2 "gimple" { target c++26 } } }
+// { dg-final { scan-tree-dump-times " s12 = \.DEFERRED_INIT \\\(" 5 "gimple" { target c++26 } } }
+// { dg-final { scan-tree-dump-times " s13 = \.DEFERRED_INIT \\\(" 4 "gimple" { target c++26 } } }
+// { dg-final { scan-tree-dump-times " s14 = \.DEFERRED_INIT \\\(" 3 "gimple" { target c++26 } } }
+// { dg-final { scan-tree-dump-times " s15 = \.DEFERRED_INIT \\\(" 2 "gimple" { target c++26 } } }
+// { dg-final { scan-tree-dump-times " s16 = \.DEFERRED_INIT \\\(" 1 "gimple" { target c++26 } } }
+// { dg-final { scan-tree-dump-times " s17 = \.DEFERRED_INIT \\\(" 1 "gimple" { target c++26 } } }
+// { dg-final { scan-tree-dump-times " s18 = \.DEFERRED_INIT \\\(" 1 "gimple" { target c++26 } } }
+// { dg-final { scan-tree-dump-times " s19 = \.DEFERRED_INIT \\\(" 2 "gimple" { target c++26 } } }
+// { dg-final { scan-tree-dump-times " s20 = \.DEFERRED_INIT \\\(" 3 "gimple" { target c++26 } } }
+// { dg-final { scan-tree-dump-times " s21 = \.DEFERRED_INIT \\\(" 3 "gimple" { target c++26 } } }
+// { dg-final { scan-tree-dump-times " s22 = \.DEFERRED_INIT \\\(" 3 "gimple" { target c++26 } } }
+// { dg-final { scan-tree-dump-times " s23 = \.DEFERRED_INIT \\\(" 3 "gimple" { target c++26 } } }
+// { dg-final { scan-tree-dump-times " s24 = \.DEFERRED_INIT \\\(" 2 "gimple" { target c++26 } } }
+// { dg-final { scan-tree-dump-times " i1 = \.DEFERRED_INIT \\\(" 1 "gimple" { target c++26 } } }
+
+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;
+}
@@ -0,0 +1,158 @@
+// C++ 26 P2795R5 - Erroneous behaviour for uninitialized reads
+// { dg-do compile { target c++11 } }
+// { dg-options "-Wimplicit-fallthrough -Wswitch-unreachable" }
+// Make sure -Wimplicit-fallthrough and -Wswitch-unreachable
+// are consistent between -std=c++23 and -std=c++26 even when
+// the latter instruments jumps across vacuous initializations.
+
+int i;
+
+void
+foo (int x)
+{
+ switch (x)
+ {
+ case 1:
+ int j;
+ ++i; // { dg-warning "this statement may fall through" }
+ case 2: // { dg-message "here" }
+ int k;
+ ++i;
+ // FALLTHRU
+ case 3:
+ int l;
+ ++i;
+ [[fallthrough]];
+ default:
+ int m;
+ ++i;
+ j = 42;
+ k = 42;
+ l = 42;
+ m = 42;
+ i += (j - k) + (l - m);
+ break;
+ }
+}
+
+void
+bar (int x)
+{
+ if (x == 6)
+ goto l1;
+ if (x == 7)
+ goto l2;
+ if (x == 8)
+ goto l3;
+ if (x == 9)
+ goto l4;
+ if (x == 10)
+ goto l5;
+ if (x == 11)
+ goto l6;
+ int j;
+ j = 5;
+ i += j;
+ switch (x)
+ {
+ case 1:
+ l1:
+ ++i; // { dg-warning "this statement may fall through" }
+ case 2: // { dg-message "here" }
+ l2:
+ ++i;
+ // FALLTHRU
+ case 3:
+ l3:
+ ++i;
+ [[fallthrough]];
+ default:
+ l4:
+ ++i;
+ break;
+ case 4:
+ ++i; // { dg-warning "this statement may fall through" }
+ case 5: // { dg-message "here" }
+ l5:;
+ ++i; // { dg-warning "this statement may fall through" }
+ case 6: // { dg-message "here" }
+ ++i;
+ case 7:
+ l6:;
+ }
+}
+
+void
+baz (int x)
+{
+ switch (x)
+ {
+ case 1:
+ int j [[indeterminate]];
+ ++i; // { dg-warning "this statement may fall through" }
+ case 2: // { dg-message "here" }
+ int k [[indeterminate]];
+ ++i;
+ // FALLTHRU
+ case 3:
+ int l [[indeterminate]];
+ ++i;
+ [[fallthrough]];
+ default:
+ int m [[indeterminate]];
+ ++i;
+ j = 42;
+ k = 42;
+ l = 42;
+ m = 42;
+ i += (j - k) + (l - m);
+ break;
+ }
+}
+
+void
+qux (int x)
+{
+ if (x == 6)
+ goto l1;
+ if (x == 7)
+ goto l2;
+ if (x == 8)
+ goto l3;
+ if (x == 9)
+ goto l4;
+ if (x == 10)
+ goto l5;
+ if (x == 11)
+ goto l6;
+ int j [[indeterminate]];
+ j = 5;
+ i += j;
+ switch (x)
+ {
+ case 1:
+ l1:
+ ++i; // { dg-warning "this statement may fall through" }
+ case 2: // { dg-message "here" }
+ l2:
+ ++i;
+ // FALLTHRU
+ case 3:
+ l3:
+ ++i;
+ [[fallthrough]];
+ default:
+ l4:
+ ++i;
+ break;
+ case 4:
+ ++i; // { dg-warning "this statement may fall through" }
+ case 5: // { dg-message "here" }
+ l5:;
+ ++i; // { dg-warning "this statement may fall through" }
+ case 6: // { dg-message "here" }
+ ++i;
+ case 7:
+ l6:;
+ }
+}
@@ -0,0 +1,37 @@
+// C++ 26 P2795R5 - Erroneous behaviour for uninitialized reads
+// { dg-do compile { target c++23 } }
+// Make sure we don't reject this in C++26 because of
+// .DEFERRED_INIT calls.
+
+constexpr int
+foo (int x)
+{
+ if (x == 6)
+ goto l1;
+ if (x == 7)
+ goto l2;
+ int i;
+ switch (x)
+ {
+ int j;
+ case 1:
+ i = 6;
+ return i;
+ case 2:
+ i = 4;
+ l1:
+ i = 5;
+ return i;
+ case 3:
+ l2:
+ i = 7;
+ return i;
+ default:
+ return 42;
+ }
+}
+
+static_assert (foo (1) == 6);
+static_assert (foo (2) == 5);
+static_assert (foo (3) == 7);
+static_assert (foo (4) == 42);
@@ -1,7 +1,7 @@
// PR target/92038
// { dg-do compile { target int32 } }
// { dg-require-effective-target store_merge }
-// { dg-options "-O2 -flifetime-dse=2 -fdump-tree-store-merging-details" }
+// { dg-options "-O2 -flifetime-dse=2 -fdump-tree-store-merging-details -ftrivial-auto-var-init=uninitialized" }
// { dg-final { scan-tree-dump "New sequence of \[12] stores to replace old one of 2 stores" "store-merging" } }
struct S { S () : a (0), b (0) {} int a; char b; char c[3]; };
@@ -13,7 +13,7 @@ int foo(int n)
_err;
});
- if (err == 0) return 17;
+ if (err == 0) return 17; /* { dg-warning "'_err' may be used uninitialized" "" { target c++26 } } */
}
return 18;
@@ -8,12 +8,12 @@
struct shared_count {
shared_count () { }
shared_count (shared_count &r)
- : pi (r.pi) { } // { dg-warning "\\\[-Wuninitialized" }
+ : pi (r.pi) { }
int pi;
};
// There's another (redundant) -Wuninitialized on the line below.
-struct shared_ptr {
+struct shared_ptr { // { dg-warning "\\\[-Wuninitialized" }
int ptr;
shared_count refcount;
};
@@ -1,5 +1,5 @@
/* { dg-do run } */
-/* { dg-options "-fsanitize=vla-bound -Wall -Wno-unused-variable -fno-stack-clash-protection" } */
+/* { dg-options "-fsanitize=vla-bound -Wall -Wno-unused-variable -fno-stack-clash-protection -ftrivial-auto-var-init=uninitialized" } */
typedef long int V;
int x = -1;
@@ -10,10 +10,10 @@ static void bar(int a, int *ptr)
do
{
int b;
- if (b < 40) {
+ if (b < 40) { /* { dg-warning "is used uninitialized" "" { target c++26 } } */
ptr[0] = b;
}
- b += 1; /* { dg-warning "is used uninitialized" } */
+ b += 1; /* { dg-warning "is used uninitialized" "" { target { c || c++23_down } } } */
ptr++;
}
while (--a != 0);