]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
c++, gimplify: Implement C++26 P2795R5 - Erroneous behavior for uninitialized reads...
authorJakub Jelinek <jakub@redhat.com>
Sat, 4 Oct 2025 07:50:39 +0000 (09:50 +0200)
committerJakub Jelinek <jakub@gcc.gnu.org>
Sat, 4 Oct 2025 08:23:15 +0000 (10:23 +0200)
The following patch implements the C++26 P2795R5 paper by enabling something
like -ftrivial-auto-var-init=zero by default for -std=c++26/-std=gnu++26.
There is an important difference between explicit -ftrivial-auto-var-init=zero
and the implicitly enabled one, in particular the explicit one will try to
clear padding bits on vars with explicit initializers, while the default
C++26 mode does not - C++26 says that using the padding bits for anything
but copying structures around is still undefined behavior rather than
erroneous behavior.

Users can still override the default C++26 behavior in both directions,
with -ftrivial-auto-var-init=uninitialized even C++26 will act as C++23
with treating all uninitialized var reads (except for copying) as UB rather
than EB, with -ftrivial-auto-var-init=zero it will also clear padding bits
on explicit initialization and with -ftrivial-auto-var-init=pattern it will
initialize to pattern with clearing padding bits in between.

There are other changes that had to be implemented.  First of all, we need
to magicly preinitialize also temporary objects; this is implemented for
both the C++26 implicit mode and explicit
-ftrivial-auto-var-init={zero,pattern} by emitting .DEFERRED_INIT before
construction of TARGET_EXPR temporaries (if they have void type initializers,
i.e. are initialized by code rather than some value).

Second needed change is dropping *this ={v} {CLOBBER(bob)}; statements
at the start of the constructors for -flifetime-dse=2, that says the old
content of *this is irrelevant, which is not true anymore for C++26,
where we want to treat it like that for -W*uninitialized purposes, but
at runtime actually initialize the values.  Instead for -flifetime-dse=2
we emit such {CLOBBER(bob)} before calling whole object constructor
(on TARGET_EXPR with void type initializer or on DECL_EXPR).
And a separate patch added and another one will be adding more {CLOBBER(bob)}
to new expressions.

The third needed change is about gotos and switches across vacuous
initialization.  C++26 says those are still valid, but don't make an
exception for those in the EB rules.
The patch now includes redirecting of forward/backward gotos
which cross vacuous initializations for -std=c++26 and
-ftrivial-auto-var-init={zero,pattern} by adding an artificial
if (0) { lab1: v1 = .DEFERRED_INIT (...); lab2: v2 = .DEFERRED_INIT (...); }
etc. hunk before the user label (or for case labels moving the case
label into it).  Only one per adjacent set of labels, with perhaps
multiple artificial labels in it.  I believe (and testing seems to
confirm that) that one only needs one set of such initialized vars
per the adjacent label group, if some forward or backward jump
crosses more vacuous inits, it will always cross a subset or superset
of the others and when the vars are ordered right, it can jump into
different positions in the same if (0).
Furthermore, -Wimplicit-fallthrough and -Wswitch-unreachable warnings
have been adjusted to deal with that.
These changes mean that -Wtrivial-auto-var-init warning now doesn't
make sense for C++, as there is nothing to warn about, all the switches
and all the gotos are handled right for -ftrivial-auto-var-init= and
are handled that way both for the implicit C++26 mode and for explicit
-ftrivial-auto-var-init= options.

The fourth change is to avoid regressions for code like
struct A
{
  int f, g;
  A () { f = g; // { dg-warning "g. is used uninitialized" } }
} a;
where with -flifetime-dse=2 -Wuninitialized we were able to warn
about bugs like this because of the *this ={v} {CLOBBER(bob)};
statements at the start of the constructors, but with their removal
wouldn't warn about it.  Instead we now add a magic "clobber *this"
attribute to the this PARM_DECL and use it in -W*uninitialized handling
only as an implicit *this ={v} {CLOBBER(bob)}; at the start of the
function.  If a function is inlined, this disappears, but that shouldn't
be a problem, either it is inlined into another constructor and that
should have "clobber *this" for its this argument or it is inlined into
whole object construction spot and there should be an explicit
{CLOBBER(bob)} for the variable or temporary object.

The fifth change is adding [[indeterminate]] attribute support and
using it to avoid .DEFERRED_INIT calls (like [[gnu::uninitialized]]
is handled).

Some regressions caused by this patch had bugs filed (but for cases
where those already didn't work before with explicit
-ftrivial-auto-var-init=zero), those have been xfailed for now.
See PR121975 and PR122044.

2025-10-04  Jakub Jelinek  <jakub@redhat.com>

PR c++/114457
gcc/
* flag-types.h (enum auto_init_type): Add AUTO_INIT_CXX26.
* tree.h (VACUOUS_INIT_LABEL_P): Define.
* gimplify.cc (is_var_need_auto_init): Renamed to ...
(var_needs_auto_init_p): ... this.  Don't return true for
vars with "indeterminate" attribute.  Formatting fixes.
(gimplify_decl_expr): Use var_needs_auto_init_p instead of
is_var_need_auto_init.
(emit_warn_switch_unreachable): Remove the flag_auto_var_init
special cases.
(warn_switch_unreachable_and_auto_init_r): Handle them here
by doing just returning NULL.
(last_stmt_in_scope): Don't skip just debug stmts to find
the last stmt in seq, skip for
flag_auto_var_init > AUTO_INIT_UNINITIALIZED also IFN_DEFERRED_INIT
calls.
(collect_fallthrough_labels): For
flag_auto_var_init > AUTO_INIT_UNINITIALIZED ignore
IFN_DEFERRED_INIT calls and GIMPLE_GOTOs to
VACUOUS_INIT_LABEL_P.
(should_warn_for_implicit_fallthrough): For
flag_auto_var_init > AUTO_INIT_UNINITIALIZED also skip over
IFN_DEFERRED_INIT calls.
(expand_FALLTHROUGH_r): Likewise, and handle GIMPLE_GOTOs
to VACUOUS_INIT_LABEL_P.
(gimplify_init_constructor): Use var_needs_auto_init_p instead
of is_var_need_auto_init and for flag_auto_var_init
AUTO_INIT_CXX26 don't call gimple_add_padding_init_for_auto_var.
(gimplify_target_expr): If var_needs_auto_init_p and init has
void type, call gimple_add_init_for_auto_var and for
AUTO_INIT_PATTERN also gimple_add_padding_init_for_auto_var.
* tree-ssa-uninit.cc (maybe_warn_operand): Handle loads from *this
at the start of the function with "clobber *this" attribute on the
PARM_DECL.
* ipa-split.cc (split_function): Remove "clobber *this" attribute
from the first PARM_DECL (if any).
* doc/invoke.texi (ftrivial-auto-var-init=): Adjust documentation.
gcc/c-family/
* c-opts.cc (c_common_post_options): For C++26 set
flag_auto_var_init to AUTO_INIT_CXX26 if not specified explicitly.
For C++ disable warn_trivial_auto_var_init.
gcc/cp/
* cp-tree.h: Implement C++26 P2795R5 - Erroneous behavior for
uninitialized reads.
(IF_STMT_VACUOUS_INIT_P): Define.
(check_goto): Change argument type from tree to tree *.
* call.cc (build_over_call): Add indeterminate attribute to
TARGET_EXPR slots for indeterminate parameters.
* constexpr.cc (cxx_eval_internal_function): Handle IFN_DEFERRED_INIT.
(cxx_eval_store_expression): Temporarily work around PR121965 bug.
* cp-gimplify.cc (genericize_if_stmt): Handle IF_STMT_VACUOUS_INIT_P.
(maybe_emit_clobber_object_begin): New function.
(cp_gimplify_expr): Call it for DECL_EXPRs and TARGET_EXPRs with
void type non-NULL TARGET_EXPR_INITIAL.
* decl.cc (struct named_label_fwd_direct_goto,
struct named_label_bck_direct_goto): New types.
(struct named_label_use_entry): Add direct_goto member.  Formatting
fix.
(struct named_label_entry): Add direct_goto member.  Turn bool members
into bool : 1.  Add has_bad_decls bitfield.
(adjust_backward_gotos): New function.
(pop_labels): For flag_auto_var_init > AUTO_INIT_UNINITIALIZED
call adjust_backward_gotos if needed.
(poplevel_named_label_1): For decl_jump_unsafe also set
ent->has_bad_decls, and for decl_instrument_init_bypass_p decls
push them into ent->bad_decls vector too.
(duplicate_decls): Complain if indeterminate attribute on function
parameter isn't present on the first function declaration.
(decl_instrument_init_bypass_p): New function.
(build_deferred_init_call): Likewise.
(maybe_add_deferred_init_calls): Likewise.
(adjust_backward_goto): Likewise.
(check_previous_goto_1): Add direct_goto and case_label arguments.
For decl_instrument_init_bypass_p decls seen if
direct_goto || case_label move case label if needed, call
maybe_add_deferred_init_calls and adjust GOTO_EXPR operands remembered
in direct_goto.  Change return type from bool to int, return 0 on
error, 1 for success with no need to adjust vacuous inits and 2 for
success with need to adjust those.
(check_previous_goto): Adjust check_previous_goto_1 call, vec_free
direct_goto vector.
(check_switch_goto): Add case_label argument, adjust
check_previous_goto_1 call.  Change return type from bool to int.
(check_goto_1): Remove computed argument, add declp argument.  Don't
reuse previous ent->uses if
ent->binding_level != current_binding_level.  Push declp into
direct_goto vectors if needed.
(check_goto): Remove decl argument, add declp argument.  Adjust
check_goto_1 calls.
(finish_case_label): Call check_switch_goto up to twice, first time
to detect errors and find out if second call will be needed, and
after c_add_case_label second time if needed.  In the first case
pass NULL_TREE as new argument to it, in the second case r.
(start_preparsed_function): Don't emit CLOBBER_OBJECT_BEGIN here
for -flifetime-dse=2, instead add "clobber *this" attribute to
current_class_ptr.
* parser.cc (cp_parser_asm_label_list): Call check_goto only
after the TREE_LIST is created and pass address of its TREE_VALUE to
it instead of the label.
* semantics.cc (finish_goto_stmt): Call check_goto only after
build_stmt has been called and pass it address of its first operand
rather than destination.
* tree.cc (handle_indeterminate_attribute): New function.
(cxx_gnu_attributes): Add entry for indeterminate attribute.
gcc/testsuite/
* g++.dg/cpp1y/vla-initlist1.C: Remove dg-skip-if for powerpc.
Initialize i to 43 for ctor from initializer_list and expect value
43 instead of 42.
* g++.dg/cpp26/attr-indeterminate1.C: New test.
* g++.dg/cpp26/attr-indeterminate2.C: New test.
* g++.dg/cpp26/attr-indeterminate3.C: New test.
* g++.dg/cpp26/attr-indeterminate4.C: New test.
* g++.dg/cpp26/erroneous1.C: New test.
* g++.dg/cpp26/erroneous2.C: New test.
* g++.dg/cpp26/erroneous3.C: New test.
* g++.dg/cpp26/erroneous4.C: New test.
* g++.dg/opt/store-merging-1.C: Add
-ftrivial-auto-var-init=uninitialized to dg-options.
* g++.dg/uninit-pred-loop-1_b.C: Expect a warning for C++26.
* g++.dg/warn/Wuninitialized-13.C: Expect warning on a different
line.
* c-c++-common/ubsan/vla-1.c: Add
-ftrivial-auto-var-init=uninitialized to dg-options.
* c-c++-common/uninit-17.c: For c++26 expect warning on a different
line.
* g++.dg/warn/Warray-bounds-20.C: Expect warning on a different line.
* c-c++-common/analyzer/invalid-shift-1.c: Xfail for c++26 until
PR122044 is fixed.
* g++.dg/analyzer/exception-value-2.C: Skip for c++26 until PR122044
is fixed.
* c-c++-common/goacc-gomp/nesting-1.c: Skip for c++26 until PR121975
is fixed.
* c-c++-common/goacc/kernels-decompose-2.c: Likewise.
* c-c++-common/goacc/kernels-decompose-pr100400-1-1.c: Likewise.
* c-c++-common/goacc/kernels-decompose-pr100400-1-3.c: Likewise.
* c-c++-common/goacc/kernels-decompose-pr104061-1-1.c: Likewise.
* c-c++-common/goacc/kernels-decompose-pr104061-1-3.c: Likewise.
* c-c++-common/goacc/kernels-decompose-pr104061-1-4.c: Likewise.
* c-c++-common/goacc/kernels-decompose-pr104132-1.c: Likewise.
* c-c++-common/goacc/kernels-decompose-pr104133-1.c: Likewise.
* c-c++-common/goacc/kernels-decompose-pr104774-1.c: Likewise.
* c-c++-common/goacc/mdc-1.c: Likewise.

43 files changed:
gcc/c-family/c-opts.cc
gcc/cp/call.cc
gcc/cp/constexpr.cc
gcc/cp/cp-gimplify.cc
gcc/cp/cp-tree.h
gcc/cp/decl.cc
gcc/cp/parser.cc
gcc/cp/semantics.cc
gcc/cp/tree.cc
gcc/doc/invoke.texi
gcc/flag-types.h
gcc/gimplify.cc
gcc/ipa-split.cc
gcc/testsuite/c-c++-common/analyzer/invalid-shift-1.c
gcc/testsuite/c-c++-common/goacc-gomp/nesting-1.c
gcc/testsuite/c-c++-common/goacc/kernels-decompose-2.c
gcc/testsuite/c-c++-common/goacc/kernels-decompose-pr100400-1-1.c
gcc/testsuite/c-c++-common/goacc/kernels-decompose-pr100400-1-3.c
gcc/testsuite/c-c++-common/goacc/kernels-decompose-pr104061-1-1.c
gcc/testsuite/c-c++-common/goacc/kernels-decompose-pr104061-1-3.c
gcc/testsuite/c-c++-common/goacc/kernels-decompose-pr104061-1-4.c
gcc/testsuite/c-c++-common/goacc/kernels-decompose-pr104132-1.c
gcc/testsuite/c-c++-common/goacc/kernels-decompose-pr104133-1.c
gcc/testsuite/c-c++-common/goacc/kernels-decompose-pr104774-1.c
gcc/testsuite/c-c++-common/goacc/mdc-1.c
gcc/testsuite/c-c++-common/ubsan/vla-1.c
gcc/testsuite/c-c++-common/uninit-17.c
gcc/testsuite/g++.dg/analyzer/exception-value-2.C
gcc/testsuite/g++.dg/cpp1y/vla-initlist1.C
gcc/testsuite/g++.dg/cpp26/attr-indeterminate1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp26/attr-indeterminate2.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp26/attr-indeterminate3.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp26/attr-indeterminate4.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp26/erroneous1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp26/erroneous2.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp26/erroneous3.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp26/erroneous4.C [new file with mode: 0644]
gcc/testsuite/g++.dg/opt/store-merging-1.C
gcc/testsuite/g++.dg/uninit-pred-loop-1_b.C
gcc/testsuite/g++.dg/warn/Warray-bounds-20.C
gcc/testsuite/g++.dg/warn/Wuninitialized-13.C
gcc/tree-ssa-uninit.cc
gcc/tree.h

index 0ec30e852158b0d0f40066c2741f9543dc2364d3..54e397cda9a493efa3caf3000ad9b900c9dc40f5 100644 (file)
@@ -913,6 +913,16 @@ c_common_post_options (const char **pfilename)
   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)
index 97c8a48c8400a01497e344fb7823d33a7019f0f2..77dfa7d2982b8f46dd36084254b46a98c37a62f5 100644 (file)
@@ -10488,6 +10488,7 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
   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]()
     {
@@ -10505,6 +10506,8 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
       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));
 
@@ -10513,6 +10516,8 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
          argarray[argarray_size++] = (*args)[arg_index];
          ++arg_index;
          parm = TREE_CHAIN (parm);
+         if (parmd)
+           parmd = DECL_CHAIN (parmd);
        }
     }
   /* Bypass access control for 'this' parameter.  */
@@ -10600,6 +10605,8 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
 
       argarray[argarray_size++] = converted_arg;
       parm = TREE_CHAIN (parm);
+      if (parmd)
+       parmd = DECL_CHAIN (parmd);
     }
 
   auto handle_arg = [fn, flags](tree type,
@@ -10623,6 +10630,27 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
       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);
@@ -10636,8 +10664,13 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
       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);
@@ -10683,7 +10716,12 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
       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 */
@@ -10699,6 +10737,9 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
       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 */
index 558ef6ed4717c397b64d6e2dbc2aa8e102460f1f..016e3db927c98d9e116ede66bca905c167205a5b 100644 (file)
@@ -3009,6 +3009,9 @@ cxx_eval_internal_function (const constexpr_ctx *ctx, tree t,
                                           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),
@@ -7498,6 +7501,13 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t,
   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))
index 2111dcadae7fe0c43bd26eaca1f3b962cdb5dd93..1662336e165c9ed0e2477be637ab6e9ec5477bb3 100644 (file)
@@ -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,38 @@ cp_gimplify_arg (tree *arg_p, gimple_seq *pre_p, location_t call_location,
 
 }
 
+/* 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 (VAR_P (decl)
+      && 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)
+         && !is_empty_class (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 +977,10 @@ cp_gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p)
         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 +992,12 @@ cp_gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p)
        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
index 643fa6f5bcdaffa93d1e3adf71e28dbef3201398..5f8dfad7bf42db994afb7c4b3dd1fd7a167607fe 100644 (file)
@@ -510,6 +510,7 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX];
       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)
@@ -5687,6 +5688,9 @@ decl_template_parm_check (const_tree t, const char *f, int l, const char *fn)
 #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.  */
@@ -7280,7 +7284,7 @@ extern tree duplicate_decls                       (tree, tree,
 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);
index 7927b4375ec887148d91b93add92e1d6ad45c762..885be3b2cecf035e35fefe8ada8b7e67915095ff 100644 (file)
@@ -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"))) named_label_use_entry {
   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,69 @@ sort_labels (const void *a, const void *b)
   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:
+      /* In rare cases LABEL_EXPR can appear as the only substatement
+        of some other statement, e.g. if body etc.  In that case, we know
+        there can't be an older if (0) wrapper with artificial initializers
+        before it.  Replace the LABEL_EXPR statement with a STATEMENT_LIST
+        and insert the LABEL_EXPR into it, later on if (0) will be added
+        before that.  */
+      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;
+       /* In the common case, LABEL_EXPRs appear inside of a STATEMENT_LIST.
+          In that case pass the stmt iterator to adjust_backward_goto, so
+          that it can insert if (0) wrapper artificial initializers before
+          it or reuse the existing ones.  */
+       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 +516,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 +662,11 @@ poplevel_named_label_1 (named_label_entry **slot, cp_binding_level *bl)
                                                     ? 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;
@@ -2941,6 +3057,19 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
        {
           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);
        }
 
@@ -3744,6 +3873,259 @@ 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)))
+    {
+      /* Found IF_STMT added for this or some adjacent
+        LABEL_EXPR/CASE_LABEL_EXPR by an earlier call to this function.
+        The decls are ordered so that we can always reuse it.  Sometimes
+        by no modifications at all and just returning the right label
+        which was added already before, sometimes by adding a label in
+        between two previously added .DEFERRED_INIT calls and sometimes
+        by adding extra statements (.DEFERRED_INIT calls and LABEL_EXPR
+        before that) before the statements in IF_STMT body.  */
+      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)
+           {
+             /* Skip over labels/case labels after .DEFERRED_INIT for the
+                DECL we are looking for.  */
+             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))
+               {
+                 /* Reached the start, we'll need to prepend further
+                    statements.  */
+                 add = true;
+                 iter = tsi_start (then_clause);
+               }
+             else
+               {
+                 /* Found something, assert it is .DEFERRED_INIT for
+                    DECL.  */
+                 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)
+           {
+             /* If reached the start in this or some earlier iteration,
+                prepend .DEFERRED_INIT call for DECL.  */
+             tree t = build_deferred_init_call (decl);
+             STMT_IS_FULL_EXPR_P (t) = 1;
+             tsi_link_before (&iter, t, TSI_CONTINUE_LINKING);
+           }
+       }
+      if (!add)
+       {
+         /* If .DEFERRED_INIT calls for all the decls were already there,
+            skip over case labels and if we find a LABEL_EXPR, return
+            its label.  */
+         while (!tsi_end_p (iter)
+                && !case_label
+                && TREE_CODE (tsi_stmt (iter)) == CASE_LABEL_EXPR)
+           tsi_prev (&iter);
+         if (tsi_end_p (iter))
+           {
+             /* Only case labels were found and we are looking for normal
+                label, we'll need to add it.  */
+             add = true;
+             iter = tsi_start (then_clause);
+           }
+         else if (!case_label
+                  && TREE_CODE (tsi_stmt (iter)) == LABEL_EXPR)
+           /* Return existing label.  */
+           lab = LABEL_EXPR_LABEL (tsi_stmt (iter));
+         else
+           {
+             /* We'll need to add a LABEL_EXPR or move CASE_LABEL_EXPR.  */
+             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
+    {
+      /* No IF_STMT created by this function found.  Create it all
+        from scratch, so a LABEL_EXPR (or moved CASE_LABEL_EXPR)
+        followed by .DEFERRED_INIT calls inside of a new if (0).  */
+      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.  */
 
@@ -3771,13 +4153,19 @@ identify_goto (tree decl, location_t loc, const location_t *locus,
    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_levellevel, 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;
@@ -3785,6 +4173,8 @@ check_previous_goto_1 (tree decl, cp_binding_level* level, tree names,
   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)
     {
@@ -3807,7 +4197,15 @@ check_previous_goto_1 (tree decl, cp_binding_level* level, tree names,
        {
          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)
            {
@@ -3906,7 +4304,30 @@ check_previous_goto_1 (tree decl, cp_binding_level* level, tree names,
          }
     }
 
-  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
@@ -3914,24 +4335,27 @@ check_previous_goto (tree decl, struct named_label_use_entry *use)
 {
   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))
@@ -3939,8 +4363,14 @@ check_goto_1 (named_label_entry *ent, bool computed)
       /* 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> ();
@@ -3949,6 +4379,10 @@ check_goto_1 (named_label_entry *ent, bool computed)
       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;
@@ -3959,11 +4393,12 @@ check_goto_1 (named_label_entry *ent, bool computed)
   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
@@ -3978,8 +4413,14 @@ check_goto_1 (named_label_entry *ent, bool computed)
   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)
@@ -4086,16 +4527,21 @@ check_goto_1 (named_label_entry *ent, bool computed)
            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,
@@ -4106,7 +4552,7 @@ check_goto (tree decl)
        {
          auto ent = *iter;
          if (ent->addressed)
-           check_goto_1 (ent, true);
+           check_goto_1 (ent, NULL);
        }
     }
   else
@@ -4115,7 +4561,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);
     }
 }
 
@@ -4370,7 +4816,8 @@ finish_case_label (location_t loc, tree low_value, tree high_value)
   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);
@@ -4382,6 +4829,9 @@ finish_case_label (location_t loc, tree low_value, tree high_value)
 
   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;
@@ -19327,16 +19777,19 @@ start_preparsed_function (tree decl1, tree attrs, int flags)
   start_function_contracts (decl1);
 
   if (!processing_template_decl
-      && (flag_lifetime_dse > 1)
+      && 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));
+      && !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)
index 04031486bb3bf79ea4298acc88f3dccd48eef3a6..362cddbaf692554a1eecdcaf3f3dfcf3707d919a 100644 (file)
@@ -31186,10 +31186,10 @@ cp_parser_asm_label_list (cp_parser* parser)
          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
index 1937ace70b36847b22ffab135f0a90ef39b4a41e..8d0210ef3b63722056b69a0a066b1002ae506653 100644 (file)
@@ -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.  */
index 03b58ff60059eb60c790f0920ae97c1768599b99..c6ce7e988ceedd7dcac20d050b7bd18fb6847326 100644 (file)
@@ -5591,6 +5591,23 @@ handle_maybe_unused_attribute (tree *node, tree name, tree args, int flags,
   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_attributes[] =
     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,
index 572a1ce6a2432c625651ef6f16a088d00d27c8ed..f93fe43733dc5bfe437af98501c8dba15f85d523 100644 (file)
@@ -14751,27 +14751,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
@@ -14785,7 +14783,10 @@ The values used for pattern initialization might be changed in the future.
 @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
@@ -14798,7 +14799,7 @@ with the bit patterns @code{0x00} or @code{0xFE}, depending on
 @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}
@@ -14809,7 +14810,8 @@ are initialized with @code{false} (zero), even when @samp{pattern} is
 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}
index bf681c3e8153ac88eb41688771b792dd27b6c8c7..44a90becb27ad9dcf44c3100b8fb915e56aa6e5b 100644 (file)
@@ -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.  */
index 9c5aa0638b73fb9fde930e11ec75e23063759ba2..d8725e4c5e203eeca59286c9dfb516559bf52770 100644 (file)
@@ -2103,13 +2103,13 @@ gimple_add_padding_init_for_auto_var (tree decl, bool is_vla,
 /* 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;
@@ -2222,7 +2222,7 @@ gimplify_decl_expr (tree *stmt_p, gimple_seq *seq_p)
       /* 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,
@@ -2316,27 +2316,6 @@ emit_warn_switch_unreachable (gimple *stmt)
   /* 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");
@@ -2384,6 +2363,18 @@ warn_switch_unreachable_and_auto_init_r (gimple_stmt_iterator *gsi_p,
         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;
@@ -2393,24 +2384,41 @@ warn_switch_unreachable_and_auto_init_r (gimple_stmt_iterator *gsi_p,
          *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)
@@ -2490,26 +2498,39 @@ 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
@@ -2662,8 +2683,16 @@ collect_fallthrough_labels (gimple_stmt_iterator *gsi_p,
        }
       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);
@@ -2700,9 +2729,13 @@ should_warn_for_implicit_fallthrough (gimple_stmt_iterator *gsi_p, tree label)
     {
       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;
@@ -2715,7 +2748,10 @@ should_warn_for_implicit_fallthrough (gimple_stmt_iterator *gsi_p, tree label)
   /* 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:; } */
@@ -2892,7 +2928,33 @@ expand_FALLTHROUGH_r (gimple_stmt_iterator *gsi_p, bool *handled_ops_p,
 
          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);
@@ -2927,6 +2989,9 @@ expand_FALLTHROUGH_r (gimple_stmt_iterator *gsi_p, bool *handled_ops_p,
                }
              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;
@@ -6754,7 +6819,8 @@ gimplify_init_constructor (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_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;
@@ -8461,6 +8527,7 @@ gimplify_target_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p)
   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.  */
@@ -8471,6 +8538,7 @@ gimplify_target_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p)
          /* 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
        {
@@ -8482,6 +8550,15 @@ gimplify_target_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p)
          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)))
index 933ca16c9469e4d5fe71591716319d6bce802592..48f6a725ae8cc0434bde2eec9017b5ae56baafd4 100644 (file)
@@ -1400,6 +1400,15 @@ split_function (basic_block return_bb, class split_point *split_point,
   if (fndecl_built_in_p (node->decl))
     set_decl_built_in_function (node->decl, NOT_BUILT_IN, 0);
 
+  /* Drop "clobber *this" attribute from first argument of the split
+     function if any.  Code before that might be initializing the
+     members.  */
+  if (tree arg = DECL_ARGUMENTS (node->decl))
+    if (lookup_attribute ("clobber *this", DECL_ATTRIBUTES (arg)))
+      DECL_ATTRIBUTES (arg)
+       = remove_attribute ("clobber *this",
+                           copy_list (DECL_ATTRIBUTES (arg)));
+
   /* If return_bb contains any clobbers that refer to SSA_NAMEs
      set in the split part, remove them.  Also reset debug stmts that
      refer to SSA_NAMEs set in the split part.  */
index 08e52728748babb0a360ae181832b376de7c77f9..1b67c075eb5b55cb27fd02e7cac9b53f80716124 100644 (file)
@@ -12,10 +12,10 @@ _dl_hwcaps_subdirs_build_bitmask (int subdirs, int active)
 
   uint32_t mask;
   if (subdirs != 32)
-    mask = (1 << subdirs) - 1; /* { dg-message "shift by count \\('33'\\) >= precision of type \\('\[0-9\]+'\\)" } */
+    mask = (1 << subdirs) - 1; /* { dg-message "shift by count \\('33'\\) >= precision of type \\('\[0-9\]+'\\)" "" { xfail c++26 } } */
   else
     mask = -1;
-  return mask ^ ((1U << inactive) - 1); /* { dg-message "shift by negative count \\('-1'\\)" } */
+  return mask ^ ((1U << inactive) - 1); /* { dg-message "shift by negative count \\('-1'\\)" "" { xfail c++26 } } */
 }
 
 void f1 (int);
index 9ef154ed218b4d716c435a42fcfc6b6e42657b8b..929dfca646df33b3a8cb3f80c9816fb769fe60dd 100644 (file)
@@ -1,10 +1,9 @@
-/* { dg-additional-options "--param=openacc-kernels=decompose" }
-
+/* { dg-additional-options "--param=openacc-kernels=decompose" } */
 /* { dg-additional-options "-fopt-info-omp-note" } */
-
-/* { dg-additional-options "--param=openacc-privatization=noisy" }
-   Prune a few: uninteresting, and potentially varying depending on GCC configuration (data types):
-   { dg-prune-output {note: variable 'D\.[0-9]+' declared in block isn't candidate for adjusting OpenACC privatization level: not addressable} } */
+/* { dg-additional-options "--param=openacc-privatization=noisy" } */
+/* { dg-skip-if "PR121975" { c++26 } { "*" } { "" } } */
+/* Prune a few: uninteresting, and potentially varying depending on GCC configuration (data types): */
+/* { dg-prune-output {note: variable 'D\.[0-9]+' declared in block isn't candidate for adjusting OpenACC privatization level: not addressable} } */
 
 
 void
index 3ce9490f02f9d9a17431ef0faee0d48d1196c372..5aac40c038a022bf07586be3eae1bb4c9d121201 100644 (file)
@@ -1,10 +1,13 @@
 /* Test OpenACC 'kernels' construct decomposition.  */
 
+/* { dg-skip-if "PR121975" { c++26 } { "*" } { "" } } */
 /* { dg-additional-options "-fopt-info-omp-all" } */
 
 /* { dg-additional-options "--param=openacc-kernels=decompose" }
 /* { dg-additional-options "-O2" } for 'parloops'.  */
 
+/* { dg-skip-if "PR121975" { c++26 } { "*" } { "" } } */
+
 /* { dg-additional-options "--param=openacc-privatization=noisy" }
    Prune a few: uninteresting, and potentially varying depending on GCC configuration (data types):
    { dg-prune-output {note: variable 'D\.[0-9]+' declared in block isn't candidate for adjusting OpenACC privatization level: not addressable} } */
index 57cb1a8cb87a01895f65eb0179d4a24a1163f408..97fcaf7f28b339d72be6ce601d44e659e36d8675 100644 (file)
@@ -1,3 +1,4 @@
+/* { dg-skip-if "PR121975" { c++26 } { "*" } { "" } } */
 /* { dg-additional-options "--param openacc-kernels=decompose" } */
 
 /* { dg-additional-options "-g0" } */
index 9779f1036f601b95d57404368039a38621a41e8e..f7c80692890b6c2a79ba0a0f0f04545409f0ee51 100644 (file)
@@ -1,3 +1,4 @@
+/* { dg-skip-if "PR121975" { c++26 } { "*" } { "" } } */
 /* { dg-additional-options "--param openacc-kernels=decompose" } */
 
 /* { dg-additional-options "-fchecking" }
index aa0fca7b6ed94026ae745da9322eda85c689311f..5bb68c1549cd1bafc2487348111c7a30995e2dcd 100644 (file)
@@ -1,3 +1,4 @@
+/* { dg-skip-if "PR121975" { c++26 } { "*" } { "" } } */
 /* { dg-additional-options "--param openacc-kernels=decompose" } */
 
 /* { dg-additional-options "-g0" } */
index 70c2ac5b5312bab3a6e10dc8edd71cf215e99db3..43e1cca6ec7e8d8e7d810893710c59a83a446e8a 100644 (file)
@@ -1,3 +1,4 @@
+/* { dg-skip-if "PR121975" { c++26 } { "*" } { "" } } */
 /* { dg-additional-options "--param openacc-kernels=decompose" } */
 
 /* { dg-additional-options "-fcompare-debug" } -- w/o debug compiled first.
index d1cc1a97c9f0b1a9c2bf70d82d87a1876309eaa6..97d7bed2d572550185b09d64f1949690c63e62bd 100644 (file)
@@ -1,3 +1,4 @@
+/* { dg-skip-if "PR121975" { c++26 } { "*" } { "" } } */
 /* { dg-additional-options "--param openacc-kernels=decompose" } */
 
 /* { dg-additional-options "-g -fcompare-debug" } -- w/ debug compiled first.
index 2a663e0ea19a7390cc17d737ffc29a7900fb561d..9094a5710b4bce85b1e64322967802d076927df7 100644 (file)
@@ -1,3 +1,4 @@
+/* { dg-skip-if "PR121975" { c++26 } { "*" } { "" } } */
 /* { dg-additional-options "--param openacc-kernels=decompose" } */
 
 /* { dg-additional-options "-fopt-info-all-omp" } */
index 2724e22a550569c2830c2c123d793351e4bb3587..aa5dd346220ade613be4ab3759b5bcc6c4531d8a 100644 (file)
@@ -1,3 +1,4 @@
+/* { dg-skip-if "PR121975" { c++26 } { "*" } { "" } } */
 /* { dg-additional-options "--param openacc-kernels=decompose" } */
 
 /* { dg-additional-options "-fopt-info-all-omp" } */
index 3ef0c897bcda5534514b27035528695b78cf07b6..e6d4c55ae6dc1cf0059f3cdb0adef4d5274966ec 100644 (file)
@@ -1,3 +1,4 @@
+/* { dg-skip-if "PR121975" { c++26 } { "*" } { "" } } */
 /* { dg-additional-options "--param openacc-kernels=decompose" } */
 
 /* { dg-additional-options "-fopt-info-all-omp" } */
index 923a4ea73a39760e56c241e896c49561f12cac33..8d3e7bfc6cfc6412e36d3b1df6b14190a5beb4cf 100644 (file)
@@ -3,6 +3,7 @@
 
 /* TODO The tree dump scanning has certain expectations.
    { dg-do compile { target { lp64 || llp64 } } } */
+/* { dg-skip-if "PR121975" { c++26 } { "*" } { "" } } */
 /* { dg-additional-options "-fdump-tree-omplower" } */
 
 /* { dg-additional-options -Wuninitialized } */
index b29d904dea393f1fb3f0e23f8afed76651aaa1d1..0073514a3fbb15bd4035a0b18461ecf404fbdea6 100644 (file)
@@ -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;
index b5495366c5bc20aefdea8e6251fd30f48abf4a98..5dd49a9dab549283d20f4dcf481490360b318fe8 100644 (file)
@@ -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);
index ef9dd46b0768b5d778a0901635be99d0b6a03111..5173f5381e8303c8c25cf5eb2b93bc9d5b44921c 100644 (file)
@@ -1,3 +1,5 @@
+// { dg-skip-if "PR122044" { c++26 } { "*" } { "" } }
+
 #include "../../gcc.dg/analyzer/analyzer-decls.h"
 
 struct foo {};
index ba485df65fe9f8295e8a3bad3678810dbf735451..ce35c905452e25b5ef4981f158d093ea91fba80e 100644 (file)
@@ -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 ();
   }
 }
diff --git a/gcc/testsuite/g++.dg/cpp26/attr-indeterminate1.C b/gcc/testsuite/g++.dg/cpp26/attr-indeterminate1.C
new file mode 100644 (file)
index 0000000..58f6dc5
--- /dev/null
@@ -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" }
diff --git a/gcc/testsuite/g++.dg/cpp26/attr-indeterminate2.C b/gcc/testsuite/g++.dg/cpp26/attr-indeterminate2.C
new file mode 100644 (file)
index 0000000..a2704c6
--- /dev/null
@@ -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);
+}
diff --git a/gcc/testsuite/g++.dg/cpp26/attr-indeterminate3.C b/gcc/testsuite/g++.dg/cpp26/attr-indeterminate3.C
new file mode 100644 (file)
index 0000000..8f13390
--- /dev/null
@@ -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 }
diff --git a/gcc/testsuite/g++.dg/cpp26/attr-indeterminate4.C b/gcc/testsuite/g++.dg/cpp26/attr-indeterminate4.C
new file mode 100644 (file)
index 0000000..946e019
--- /dev/null
@@ -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);
+}
diff --git a/gcc/testsuite/g++.dg/cpp26/erroneous1.C b/gcc/testsuite/g++.dg/cpp26/erroneous1.C
new file mode 100644 (file)
index 0000000..78769e6
--- /dev/null
@@ -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 ();
+}
diff --git a/gcc/testsuite/g++.dg/cpp26/erroneous2.C b/gcc/testsuite/g++.dg/cpp26/erroneous2.C
new file mode 100644 (file)
index 0000000..e8c66f4
--- /dev/null
@@ -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;
+}
diff --git a/gcc/testsuite/g++.dg/cpp26/erroneous3.C b/gcc/testsuite/g++.dg/cpp26/erroneous3.C
new file mode 100644 (file)
index 0000000..d48a08e
--- /dev/null
@@ -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:;
+    }
+}
diff --git a/gcc/testsuite/g++.dg/cpp26/erroneous4.C b/gcc/testsuite/g++.dg/cpp26/erroneous4.C
new file mode 100644 (file)
index 0000000..0863480
--- /dev/null
@@ -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);
index c7f294ec386c9f2d9664c5be799a1d803e1153c7..8c4625283785fb7f2ea6a61d6d11d3e7073a0e53 100644 (file)
@@ -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]; };
index b17b9360192012afed23e28a690e7bea7ce7d18f..55b15b75cba5713f9b013e0499de9081bd69e6cd 100644 (file)
@@ -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;
index 36f8046fed7bd48b1a9faf9bbd42073f8227348b..643e8014d36ed3129615928c30c27636b85c8117 100644 (file)
@@ -26,7 +26,7 @@ struct D1: virtual B, virtual C
   /* The warning would ideally point to the assignment but instead points
      to the opening brace.  */
   D1 ()
-  {                           // { dg-warning "\\\[-Warray-bounds" "brace" }
+  {
     ci = 0;                   // { dg-warning "\\\[-Warray-bounds" "assign" { xfail lp64 } }
   }
 };
@@ -35,11 +35,11 @@ void sink (void*);
 
 void warn_derived_ctor_access_new_decl ()
 {
-  char a[sizeof (D1)];        // { dg-message "at offset 1 into object 'a' of size 40" "LP64 note" { target lp64} }
-                              // { dg-message "at offset 1 into object 'a' of size 20" "LP64 note" { target ilp32} .-1 }
+  char a[sizeof (D1)];        // { dg-message "at offset 1 into object 'a' of size 40" "LP64 note" { target lp64 } }
+                              // { dg-message "at offset 1 into object 'a' of size 20" "LP32 note" { target ilp32 } .-1 }
   char *p = a;
   ++p;
-  D1 *q = new (p) D1;          // { dg-warning "-Warray-bounds" }
+  D1 *q = new (p) D1;          // { dg-warning "\\\[-Warray-bounds" }
   sink (q);
 }
 
@@ -47,14 +47,14 @@ void warn_derived_ctor_access_new_alloc ()
 {
   char *p = (char*)operator new (sizeof (D1));    // { dg-message "at offset 1 into object of size \\d+ allocated by '\[^\n\r]*operator new\[^\n\r]*'" "note" }
   ++p;
-  D1 *q = new (p) D1;          // { dg-warning "-Warray-bounds" }
+  D1 *q = new (p) D1;          // { dg-warning "\\\[-Warray-bounds" }
   sink (q);
 }
 
 void warn_derived_ctor_access_new_array_decl ()
 {
   char b[sizeof (D1) * 2];    // { dg-message "at offset \\d+ into object 'b' of size 80" "LP64 note" { target { lp64 } xfail { lp64 } } }
-                              // { dg-message "at offset \\d+ into object 'b' of size 40" "LP64 note" { target { ilp32 } xfail { ilp32 } } .-1 }
+                              // { dg-message "at offset \\d+ into object 'b' of size 40" "LP32 note" { target { ilp32 } xfail { ilp32 } } .-1 }
   char *p = b;
   ++p;
   D1 *q = new (p) D1[2];
index b74a2fa98ecb52fb6b5e154f69d09efca2e728da..47a75454cff51c8db9400c41732a098c4746a86d 100644 (file)
@@ -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;
 };
index cb82001770e855641b8c76bf14455150ee569545..45e789c9563efce69dc4e0f3683bf5a7bb8e16dc 100644 (file)
@@ -641,6 +641,7 @@ maybe_warn_operand (ao_ref &ref, gimple *stmt, tree lhs, tree rhs,
     return NULL_TREE;
 
   bool found_alloc = false;
+  bool found_clobber_deref_this = false;
 
   if (fentry_reached)
     {
@@ -662,12 +663,30 @@ maybe_warn_operand (ao_ref &ref, gimple *stmt, tree lhs, tree rhs,
              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;
            }
 
+         /* The C++ FE for -flifetime-dse=2 marks this parameters
+            of certain constructors with "clobber *this" attribute.
+            Emit uninitialized warnings if we read from what this points
+            to.  This is similar to access (write_only, 1) attribute,
+            except it is a -Wuninitialized warning rather than
+            -Wmaybe-uninitialized and doesn't talk about access
+            attribute.  */
+         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 +721,7 @@ maybe_warn_operand (ao_ref &ref, gimple *stmt, tree lhs, tree rhs,
   /* 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)
        {
index 6e46374357c10b99a61856cf4070aa0ff5cfc161..4a4b8ef7f0a769011a2c1f61f73ccec57bac3b01 100644 (file)
@@ -900,6 +900,19 @@ extern void omp_clause_range_check_failed (const_tree, const char *, int,
 #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.