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)
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]()
{
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));
argarray[argarray_size++] = (*args)[arg_index];
++arg_index;
parm = TREE_CHAIN (parm);
+ if (parmd)
+ parmd = DECL_CHAIN (parmd);
}
}
/* Bypass access control for 'this' parameter. */
argarray[argarray_size++] = converted_arg;
parm = TREE_CHAIN (parm);
+ if (parmd)
+ parmd = DECL_CHAIN (parmd);
}
auto handle_arg = [fn, flags](tree type,
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);
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);
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 */
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 */
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),
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))
}
}
+ 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_)
}
+/* 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
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;
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
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)
#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. */
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);
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 *,
/* 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. */
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
/* 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
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. */
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;
? 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;
{
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);
}
|| 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. */
is OK. DECL is the LABEL_DECL or 0; LEVEL is the binding_level for
the jump context; NAMES are the names in scope in LEVEL at the jump
context; LOCUS is the source position of the jump or 0. COMPUTED
- is a vec of decls if the jump is a computed goto. Returns
- true if all is well. */
+ is a vec of decls if the jump is a computed goto. DIRECT_GOTO is a
+ vec of pointers to LABEL_DECLs that might need adjusting if vacuous
+ initializations are crossed for !!flag_auto_var_init. CASE_LABEL is
+ CASE_LABEL_EXPR to be moved if needed for the check_switch_goto case.
+ Returns non-zero if all is well, 2 if any vacuous initializers were
+ crossed. */
-static bool
-check_previous_goto_1 (tree decl, cp_binding_level* level, tree names,
+static int
+check_previous_goto_1 (tree decl, cp_binding_level *level, tree names,
bool exited_omp, const location_t *locus,
- vec<tree,va_gc> *computed)
+ vec<tree, va_gc> *computed,
+ vec<named_label_fwd_direct_goto, va_gc> *direct_goto,
+ tree case_label)
{
auto_diagnostic_group d;
cp_binding_level *b;
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)
{
{
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)
{
}
}
- 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
{
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))
/* 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> ();
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;
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
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)
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,
{
auto ent = *iter;
if (ent->addressed)
- check_goto_1 (ent, true);
+ check_goto_1 (ent, NULL);
}
}
else
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);
}
}
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);
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;
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)
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
}
}
- 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. */
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[] =
{
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,
@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
@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
@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}
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}
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. */
/* 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;
/* 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,
/* 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");
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;
*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)
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
}
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);
{
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;
/* 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:; } */
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);
}
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;
&& 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;
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. */
/* 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
{
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)))
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. */
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);
-/* { 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
/* 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} } */
+/* { dg-skip-if "PR121975" { c++26 } { "*" } { "" } } */
/* { dg-additional-options "--param openacc-kernels=decompose" } */
/* { dg-additional-options "-g0" } */
+/* { dg-skip-if "PR121975" { c++26 } { "*" } { "" } } */
/* { dg-additional-options "--param openacc-kernels=decompose" } */
/* { dg-additional-options "-fchecking" }
+/* { dg-skip-if "PR121975" { c++26 } { "*" } { "" } } */
/* { dg-additional-options "--param openacc-kernels=decompose" } */
/* { dg-additional-options "-g0" } */
+/* { dg-skip-if "PR121975" { c++26 } { "*" } { "" } } */
/* { dg-additional-options "--param openacc-kernels=decompose" } */
/* { dg-additional-options "-fcompare-debug" } -- w/o debug compiled first.
+/* { dg-skip-if "PR121975" { c++26 } { "*" } { "" } } */
/* { dg-additional-options "--param openacc-kernels=decompose" } */
/* { dg-additional-options "-g -fcompare-debug" } -- w/ debug compiled first.
+/* { dg-skip-if "PR121975" { c++26 } { "*" } { "" } } */
/* { dg-additional-options "--param openacc-kernels=decompose" } */
/* { dg-additional-options "-fopt-info-all-omp" } */
+/* { dg-skip-if "PR121975" { c++26 } { "*" } { "" } } */
/* { dg-additional-options "--param openacc-kernels=decompose" } */
/* { dg-additional-options "-fopt-info-all-omp" } */
+/* { dg-skip-if "PR121975" { c++26 } { "*" } { "" } } */
/* { dg-additional-options "--param openacc-kernels=decompose" } */
/* { dg-additional-options "-fopt-info-all-omp" } */
/* 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 } */
/* { 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;
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);
+// { dg-skip-if "PR122044" { c++26 } { "*" } { "" } }
+
#include "../../gcc.dg/analyzer/analyzer-decls.h"
struct foo {};
// { dg-do run { target c++11 } }
-// { dg-skip-if "power overwrites two slots of array i" { "power*-*-*" } }
// { dg-options "-Wno-vla" }
#include <initializer_list>
struct A
{
int i;
- A(std::initializer_list<int>) { }
+ A(std::initializer_list<int>) : i{43} { }
A(int i): i{i} { }
~A() {}
};
{ int i[x] = { 42, 42, 42, 42 }; }
{
A a[x] = { argc };
- if (a[1].i != 42)
+ if (a[1].i != 43)
__builtin_abort ();
}
}
--- /dev/null
+// 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" }
--- /dev/null
+// 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);
+}
--- /dev/null
+// 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 }
--- /dev/null
+// 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);
+}
--- /dev/null
+// 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 ();
+}
--- /dev/null
+// 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;
+}
--- /dev/null
+// 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:;
+ }
+}
--- /dev/null
+// 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);
// 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]; };
_err;
});
- if (err == 0) return 17;
+ if (err == 0) return 17; /* { dg-warning "'_err' may be used uninitialized" "" { target c++26 } } */
}
return 18;
/* 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 } }
}
};
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);
}
{
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];
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;
};
return NULL_TREE;
bool found_alloc = false;
+ bool found_clobber_deref_this = false;
if (fentry_reached)
{
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;
/* 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)
{
#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.