On Fri, Sep 05, 2025 at 05:19:06PM +0200, Jason Merrill wrote: > > Regarding temporaries, I wonder if we want to .DEFERRED_INIT them when > > expanding TARGET_EXPRs in the gimplifier (but whether to do that always > > when flag_auto_var_init != AUTO_INIT_UNINITIALIZED or for C++26 only), > > or if it should be in the C++ gimplification hook (but then it is more > > costly because it would need to create a GENERIC IFN CALL only to > > immediately gimplify it). > > I guess doing it whenever flag_auto_var_init is set makes sense.
The following WIP patch does that (except for TARGET_EXPR_SLOTS created for [[indeterminate]] arguments. > > Also, I haven't added yet CLOBBER (bob) for -flifetime-dse=2 addition for > > new expressions before calling constructors (which is now desirable when > > the ctors no longer clobber it themselves). > > I'll look at this. Ok. I wonder whether to keep adding CLOBBER (bob) as before to the start of some of the ctors for -std=c++23 and earlier (unless -ftrivial-auto-var-init= is specified). The current patch does that. If we stop doing that, we'd need to emit also CLOBBER (bob) before the calls to full object constructors for -flifetime-dse=2. > > PARM_DECLs from function declarations (rather than definitions) are thrown > > away, so guess we need to remember those say in some custom attribute on > > the FUNCTION_DECLs (indexes of parameters with the attribute) and perhaps > > when the FE creates TARGET_EXPRs for those params, mark their > > TARGET_EXPR_SLOT with indeterminate attribute. > > Doesn't the "Merge parameter attributes" code in duplicate_decls address > this? You're right. I thought DECL_ARGUMENTS is only set on FUNCTION_DECLs with definitions rather than declarations (I think that is the case for C FE), but clearly C++ FE has DECL_ARGUMENTS even for declarations. Added there diagnostics of [[indeterminate]] not specified on the first declaration. > I guess we could change > > { > T t; > do_stuff (); > label: > do_more (); > } > > to > > { > T t = ERR; > do_stuff (); > goto skip; > label: > t = ERR; > skip: > do_more (); > } I'll try to work on this next, but it won't be trivial. The forward gotos and switch cases are one thing and backward gotos another one, both need to be handled differently, plus make it work properly with [[fallthrough]] etc. (I'm contemplating on marking the if (0) { } around the extra labels and perhaps moved case labels with some flag so that gimplification can see it has been added artificially). --- gcc/gimplify.cc.jj 2025-09-04 18:51:30.173760698 +0200 +++ gcc/gimplify.cc 2025-09-09 10:42:34.436859935 +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,14 +2315,14 @@ 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 ( + 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 @@ -6753,7 +6753,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; @@ -8458,6 +8459,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. */ @@ -8468,6 +8470,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 { @@ -8479,6 +8482,15 @@ gimplify_target_expr (tree *expr_p, gimp gimple_add_tmp_var (temp); } + if (var_needs_auto_init_p (temp)) + { + 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/cp/tree.cc.jj 2025-09-04 18:51:30.057762190 +0200 +++ gcc/cp/tree.cc 2025-09-09 10:31:28.080908314 +0200 @@ -5578,6 +5578,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[] = { @@ -5617,6 +5634,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/cp/call.cc.jj 2025-08-13 22:10:18.873791918 +0200 +++ gcc/cp/call.cc 2025-09-09 15:36:13.419845002 +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/decl.cc.jj 2025-09-08 11:45:23.324029341 +0200 +++ gcc/cp/decl.cc 2025-09-09 14:00:46.506872041 +0200 @@ -2930,6 +2930,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); } @@ -19316,7 +19329,8 @@ start_preparsed_function (tree decl1, tr start_function_contracts (decl1); if (!processing_template_decl - && (flag_lifetime_dse > 1) + && flag_lifetime_dse > 1 + && flag_auto_var_init == AUTO_INIT_UNINITIALIZED && DECL_CONSTRUCTOR_P (decl1) && !DECL_CLONED_FUNCTION_P (decl1) /* Clobbering an empty base is harmful if it overlays real data. */ --- gcc/flag-types.h.jj 2025-09-04 18:51:30.122761354 +0200 +++ gcc/flag-types.h 2025-09-09 10:31:28.086908233 +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/testsuite/g++.dg/cpp26/attr-indeterminate3.C.jj 2025-09-09 15:48:02.540320387 +0200 +++ gcc/testsuite/g++.dg/cpp26/attr-indeterminate3.C 2025-09-09 15:55:44.010150054 +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-09 15:51:54.689216319 +0200 +++ gcc/testsuite/g++.dg/cpp26/attr-indeterminate4.C 2025-09-09 15:52:27.164782082 +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/attr-indeterminate1.C.jj 2025-09-09 10:31:28.096908097 +0200 +++ gcc/testsuite/g++.dg/cpp26/attr-indeterminate1.C 2025-09-09 10:31:28.096908097 +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-09 15:42:05.219115549 +0200 +++ gcc/testsuite/g++.dg/cpp26/attr-indeterminate2.C 2025-09-09 15:51:33.316502091 +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/c-family/c-opts.cc.jj 2025-09-04 18:51:30.032762512 +0200 +++ gcc/c-family/c-opts.cc 2025-09-09 10:31:28.097908083 +0200 @@ -913,6 +913,10 @@ 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); + /* C23 Annex F does not permit certain built-in functions to raise "inexact". */ if (flag_isoc23) --- gcc/doc/invoke.texi.jj 2025-09-09 10:22:39.813089294 +0200 +++ gcc/doc/invoke.texi 2025-09-09 13:27:24.432827931 +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} Jakub