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

Reply via email to