aboutsummaryrefslogtreecommitdiff
path: root/gcc/cp
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/cp')
-rw-r--r--gcc/cp/call.cc45
-rw-r--r--gcc/cp/constexpr.cc10
-rw-r--r--gcc/cp/cp-gimplify.cc69
-rw-r--r--gcc/cp/cp-tree.h9
-rw-r--r--gcc/cp/decl.cc529
-rw-r--r--gcc/cp/module.cc112
-rw-r--r--gcc/cp/name-lookup.cc58
-rw-r--r--gcc/cp/name-lookup.h16
-rw-r--r--gcc/cp/parser.cc9
-rw-r--r--gcc/cp/pt.cc7
-rw-r--r--gcc/cp/semantics.cc8
-rw-r--r--gcc/cp/tree.cc19
12 files changed, 817 insertions, 74 deletions
diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc
index 97c8a48..77dfa7d 100644
--- a/gcc/cp/call.cc
+++ b/gcc/cp/call.cc
@@ -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 */
diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc
index 558ef6e..016e3db 100644
--- a/gcc/cp/constexpr.cc
+++ b/gcc/cp/constexpr.cc
@@ -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))
diff --git a/gcc/cp/cp-gimplify.cc b/gcc/cp/cp-gimplify.cc
index 2111dca..1662336 100644
--- a/gcc/cp/cp-gimplify.cc
+++ b/gcc/cp/cp-gimplify.cc
@@ -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
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 6ee945f..5f8dfad 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -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)
@@ -1856,7 +1857,8 @@ struct GTY(()) tree_tu_local_entity {
location_t loc;
};
-/* The name of a translation-unit-local entity. */
+/* The human-readable name of a translation-unit-local entity as
+ an IDENTIFIER_NODE. */
#define TU_LOCAL_ENTITY_NAME(NODE) \
(((struct tree_tu_local_entity *)TU_LOCAL_ENTITY_CHECK (NODE))->name)
@@ -5686,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. */
@@ -7279,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);
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index 7927b43..885be3b 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -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_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;
@@ -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)
diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc
index be873c1..5f814f3 100644
--- a/gcc/cp/module.cc
+++ b/gcc/cp/module.cc
@@ -2341,6 +2341,7 @@ public:
EK_PARTIAL, /* A partial specialization. */
EK_USING, /* A using declaration (at namespace scope). */
EK_NAMESPACE, /* A namespace. */
+ EK_TU_LOCAL, /* A TU-local decl for ADL. */
EK_REDIRECT, /* Redirect to a template_decl. */
EK_EXPLICIT_HWM,
EK_BINDING = EK_EXPLICIT_HWM, /* Implicitly encoded. */
@@ -2351,6 +2352,8 @@ public:
EK_BITS = 3 /* Only need to encode below EK_EXPLICIT_HWM. */
};
+ static_assert (EK_EXPLICIT_HWM < (1u << EK_BITS),
+ "not enough bits reserved for entity_kind");
private:
/* Placement of bit fields in discriminator. */
@@ -2375,7 +2378,10 @@ private:
awkward. */
DB_TYPE_SPEC_BIT, /* Specialization in the type table. */
DB_FRIEND_SPEC_BIT, /* An instantiated template friend. */
+ DB_HWM,
};
+ static_assert (DB_HWM <= sizeof(discriminator) * CHAR_BIT,
+ "not enough bits in discriminator");
public:
/* The first slot is special for EK_SPECIALIZATIONS it is a
@@ -2700,7 +2706,9 @@ depset::entity_kind_name () const
/* Same order as entity_kind. */
static const char *const names[] =
{"decl", "specialization", "partial", "using",
- "namespace", "redirect", "binding"};
+ "namespace", "tu-local", "redirect", "binding"};
+ static_assert (ARRAY_SIZE (names) == EK_EXPLICIT_HWM + 1,
+ "names must have an entry for every explicit entity_kind");
entity_kind kind = get_entity_kind ();
gcc_checking_assert (kind < ARRAY_SIZE (names));
return names[kind];
@@ -9986,6 +9994,16 @@ trees_out::find_tu_local_decl (tree t)
return cp_walk_tree_without_duplicates (&t, walker, this);
}
+/* Get the name for TU-local decl T to be used in diagnostics. */
+
+static tree
+name_for_tu_local_decl (tree t)
+{
+ int flags = (TFF_SCOPE | TFF_DECL_SPECIFIERS);
+ const char *str = decl_as_string (t, flags);
+ return get_identifier (str);
+}
+
/* Stream out tree node T. We automatically create local back
references, which is essentially a single pass lisp
self-referential structure pretty-printer. */
@@ -10030,8 +10048,7 @@ trees_out::tree_node (tree t)
&& dump ("Writing TU-local entity:%d %C:%N",
tag, TREE_CODE (t), t);
}
- /* TODO: Get a more descriptive name? */
- tree_node (DECL_NAME (local_decl));
+ tree_node (name_for_tu_local_decl (local_decl));
if (state)
state->write_location (*this, DECL_SOURCE_LOCATION (local_decl));
goto done;
@@ -14194,6 +14211,8 @@ depset::hash::make_dependency (tree decl, entity_kind ek)
gcc_checking_assert (!is_key_order ());
if (ek == EK_USING)
gcc_checking_assert (TREE_CODE (decl) == OVERLOAD);
+ if (ek == EK_TU_LOCAL)
+ gcc_checking_assert (DECL_DECLARES_FUNCTION_P (decl));
if (TREE_CODE (decl) == TEMPLATE_DECL)
/* The template should have copied these from its result decl. */
@@ -14394,7 +14413,8 @@ depset::hash::make_dependency (tree decl, entity_kind ek)
dump (dumper::DEPEND)
&& dump ("%s on %s %C:%N found",
ek == EK_REDIRECT ? "Redirect"
- : for_binding ? "Binding" : "Dependency",
+ : (for_binding || ek == EK_TU_LOCAL) ? "Binding"
+ : "Dependency",
dep->entity_kind_name (), TREE_CODE (decl), decl);
return dep;
@@ -14565,9 +14585,21 @@ depset::hash::add_binding_entity (tree decl, WMB_Flags flags, void *data_)
than trying to clear out bindings after the fact. */
return false;
+ bool internal_decl = false;
if (!header_module_p () && data->hash->is_tu_local_entity (decl))
- /* Ignore TU-local entitites. */
- return false;
+ {
+ /* A TU-local entity. For ADL we still need to create bindings
+ for internal-linkage functions attached to a named module. */
+ if (DECL_DECLARES_FUNCTION_P (inner)
+ && DECL_LANG_SPECIFIC (inner)
+ && DECL_MODULE_ATTACH_P (inner))
+ {
+ gcc_checking_assert (!DECL_MODULE_EXPORT_P (inner));
+ internal_decl = true;
+ }
+ else
+ return false;
+ }
if ((TREE_CODE (decl) == VAR_DECL
|| TREE_CODE (decl) == TYPE_DECL)
@@ -14662,8 +14694,13 @@ depset::hash::add_binding_entity (tree decl, WMB_Flags flags, void *data_)
OVL_EXPORT_P (decl) = true;
}
- depset *dep = data->hash->make_dependency
- (decl, flags & WMB_Using ? EK_USING : EK_FOR_BINDING);
+ entity_kind ek = EK_FOR_BINDING;
+ if (internal_decl)
+ ek = EK_TU_LOCAL;
+ else if (flags & WMB_Using)
+ ek = EK_USING;
+
+ depset *dep = data->hash->make_dependency (decl, ek);
if (flags & WMB_Hidden)
dep->set_hidden_binding ();
data->binding->deps.safe_push (dep);
@@ -15114,9 +15151,12 @@ depset::hash::find_dependencies (module_state *module)
walker.begin ();
if (current->get_entity_kind () == EK_USING)
walker.tree_node (OVL_FUNCTION (decl));
+ else if (current->get_entity_kind () == EK_TU_LOCAL)
+ /* We only stream its name and location. */
+ module->note_location (DECL_SOURCE_LOCATION (decl));
else if (TREE_VISITED (decl))
/* A global tree. */;
- else if (item->get_entity_kind () == EK_NAMESPACE)
+ else if (current->get_entity_kind () == EK_NAMESPACE)
{
module->note_location (DECL_SOURCE_LOCATION (decl));
add_namespace_context (current, CP_DECL_CONTEXT (decl));
@@ -15231,6 +15271,12 @@ binding_cmp (const void *a_, const void *b_)
return a_implicit ? -1 : +1; /* Implicit first. */
}
+ /* TU-local before non-TU-local. */
+ bool a_internal = a->get_entity_kind () == depset::EK_TU_LOCAL;
+ bool b_internal = b->get_entity_kind () == depset::EK_TU_LOCAL;
+ if (a_internal != b_internal)
+ return a_internal ? -1 : +1; /* Internal first. */
+
/* Hidden before non-hidden. */
bool a_hidden = a->is_hidden ();
bool b_hidden = b->is_hidden ();
@@ -15565,12 +15611,15 @@ sort_cluster (depset::hash *original, depset *scc[], unsigned size)
use_lwm--;
break;
}
- /* We must have copied a using, so move it too. */
+ /* We must have copied a using or TU-local, so move it too. */
dep = scc[ix];
- gcc_checking_assert (dep->get_entity_kind () == depset::EK_USING);
+ gcc_checking_assert
+ (dep->get_entity_kind () == depset::EK_USING
+ || dep->get_entity_kind () == depset::EK_TU_LOCAL);
/* FALLTHROUGH */
case depset::EK_USING:
+ case depset::EK_TU_LOCAL:
if (--use_lwm != ix)
{
scc[ix] = scc[use_lwm];
@@ -16570,6 +16619,7 @@ enum ct_bind_flags
cbf_export = 0x1, /* An exported decl. */
cbf_hidden = 0x2, /* A hidden (friend) decl. */
cbf_using = 0x4, /* A using decl. */
+ cbf_internal = 0x8, /* A TU-local decl. */
};
/* DEP belongs to a different cluster, seed it to prevent
@@ -16641,7 +16691,9 @@ module_state::write_cluster (elf_out *to, depset *scc[], unsigned size,
gcc_checking_assert (dep->is_import ()
|| TREE_VISITED (dep->get_entity ())
|| (dep->get_entity_kind ()
- == depset::EK_USING));
+ == depset::EK_USING)
+ || (dep->get_entity_kind ()
+ == depset::EK_TU_LOCAL));
}
}
break;
@@ -16654,6 +16706,7 @@ module_state::write_cluster (elf_out *to, depset *scc[], unsigned size,
/* FALLTHROUGH */
case depset::EK_USING:
+ case depset::EK_TU_LOCAL:
gcc_checking_assert (!b->is_import ()
&& !b->is_unreached ());
dump (dumper::CLUSTER)
@@ -16726,7 +16779,9 @@ module_state::write_cluster (elf_out *to, depset *scc[], unsigned size,
depset *dep = b->deps[jx];
tree bound = dep->get_entity ();
unsigned flags = 0;
- if (dep->get_entity_kind () == depset::EK_USING)
+ if (dep->get_entity_kind () == depset::EK_TU_LOCAL)
+ flags |= cbf_internal;
+ else if (dep->get_entity_kind () == depset::EK_USING)
{
tree ovl = bound;
bound = OVL_FUNCTION (bound);
@@ -16752,7 +16807,13 @@ module_state::write_cluster (elf_out *to, depset *scc[], unsigned size,
gcc_checking_assert (DECL_P (bound));
sec.i (flags);
- sec.tree_node (bound);
+ if (flags & cbf_internal)
+ {
+ sec.tree_node (name_for_tu_local_decl (bound));
+ write_location (sec, DECL_SOURCE_LOCATION (bound));
+ }
+ else
+ sec.tree_node (bound);
}
/* Terminate the list. */
@@ -16761,6 +16822,7 @@ module_state::write_cluster (elf_out *to, depset *scc[], unsigned size,
break;
case depset::EK_USING:
+ case depset::EK_TU_LOCAL:
dump () && dump ("Depset:%u %s %C:%N", ix, b->entity_kind_name (),
TREE_CODE (decl), decl);
break;
@@ -16878,6 +16940,7 @@ module_state::read_cluster (unsigned snum)
tree name = sec.tree_node ();
tree decls = NULL_TREE;
tree visible = NULL_TREE;
+ tree internal = NULL_TREE;
tree type = NULL_TREE;
bool dedup = false;
bool global_p = is_header ();
@@ -16893,6 +16956,23 @@ module_state::read_cluster (unsigned snum)
if ((flags & cbf_hidden)
&& (flags & (cbf_using | cbf_export)))
sec.set_overrun ();
+ if ((flags & cbf_internal)
+ && flags != cbf_internal)
+ sec.set_overrun ();
+
+ if (flags & cbf_internal)
+ {
+ tree name = sec.tree_node ();
+ location_t loc = read_location (sec);
+ if (sec.get_overrun ())
+ break;
+
+ tree decl = make_node (TU_LOCAL_ENTITY);
+ TU_LOCAL_ENTITY_NAME (decl) = name;
+ TU_LOCAL_ENTITY_LOCATION (decl) = loc;
+ internal = tree_cons (NULL_TREE, decl, internal);
+ continue;
+ }
tree decl = sec.tree_node ();
if (sec.get_overrun ())
@@ -16987,7 +17067,7 @@ module_state::read_cluster (unsigned snum)
}
}
- if (!decls)
+ if (!decls && !internal)
sec.set_overrun ();
if (sec.get_overrun ())
@@ -16996,7 +17076,7 @@ module_state::read_cluster (unsigned snum)
dump () && dump ("Binding of %P", ns, name);
if (!set_module_binding (ns, name, mod, global_p,
is_module () || is_partition (),
- decls, type, visible))
+ decls, type, visible, internal))
sec.set_overrun ();
}
break;
diff --git a/gcc/cp/name-lookup.cc b/gcc/cp/name-lookup.cc
index 32af7a6..8d7fc06 100644
--- a/gcc/cp/name-lookup.cc
+++ b/gcc/cp/name-lookup.cc
@@ -353,6 +353,8 @@ append_imported_binding_slot (tree *slot, tree name, unsigned ix)
tree new_vec = make_binding_vec (name, want);
BINDING_VECTOR_NUM_CLUSTERS (new_vec) = have + 1;
+ BINDING_VECTOR_INTERNAL_DECLS (new_vec)
+ = BINDING_VECTOR_INTERNAL_DECLS (*slot);
BINDING_VECTOR_GLOBAL_DUPS_P (new_vec)
= BINDING_VECTOR_GLOBAL_DUPS_P (*slot);
BINDING_VECTOR_PARTITION_DUPS_P (new_vec)
@@ -1304,10 +1306,34 @@ name_lookup::adl_namespace_fns (tree scope, bitmap imports,
}
/* For lookups on the instantiation path we can see any
- module-linkage declaration; otherwise we should only
- see exported decls. */
+ declarations visible at any point on the path;
+ otherwise we should only see exported decls. */
if (on_inst_path)
- bind = STAT_DECL (bind);
+ {
+ /* If there are any internal functions visible, naming
+ them outside that module is ill-formed. */
+ auto_diagnostic_group d;
+ if (MODULE_BINDING_INTERNAL_DECLS_P (bind)
+ && pedwarn (input_location, OPT_Wexternal_tu_local,
+ "overload set for argument-dependent "
+ "lookup of %<%D::%D%> in module %qs "
+ "contains TU-local entities",
+ scope, name, module_name (mod, false)))
+ {
+ tree *tu_locals
+ = BINDING_VECTOR_INTERNAL_DECLS (val)->get (mod);
+ gcc_checking_assert (tu_locals && *tu_locals);
+ for (tree t = *tu_locals; t; t = TREE_CHAIN (t))
+ {
+ tree decl = TREE_VALUE (t);
+ inform (TU_LOCAL_ENTITY_LOCATION (decl),
+ "ignoring %qD declared here "
+ "with internal linkage",
+ TU_LOCAL_ENTITY_NAME (decl));
+ }
+ }
+ bind = STAT_DECL (bind);
+ }
else
bind = STAT_VISIBLE (bind);
}
@@ -4427,18 +4453,21 @@ import_module_binding (tree ns, tree name, unsigned mod, unsigned snum)
/* An import of MODULE is binding NS::NAME. There should be no
existing binding for >= MODULE. GLOBAL_P indicates whether the
bindings include global module entities. PARTITION_P is true if
- it is part of the current module. VALUE and TYPE are the value
- and type bindings. VISIBLE are the value bindings being exported. */
+ it is part of the current module. VALUE and TYPE are the value
+ and type bindings. VISIBLE are the value bindings being exported.
+ INTERNAL is a TREE_LIST of any TU-local names visible for ADL. */
bool
set_module_binding (tree ns, tree name, unsigned mod, bool global_p,
- bool partition_p, tree value, tree type, tree visible)
+ bool partition_p, tree value, tree type, tree visible,
+ tree internal)
{
- if (!value)
+ if (!value && !internal)
/* Bogus BMIs could give rise to nothing to bind. */
return false;
- gcc_assert (TREE_CODE (value) != NAMESPACE_DECL
+ gcc_assert (!value
+ || TREE_CODE (value) != NAMESPACE_DECL
|| DECL_NAMESPACE_ALIAS (value));
gcc_checking_assert (mod);
@@ -4450,7 +4479,7 @@ set_module_binding (tree ns, tree name, unsigned mod, bool global_p,
return false;
tree bind = value;
- if (type || visible != bind || partition_p || global_p)
+ if (type || visible != bind || internal || partition_p || global_p)
{
bind = stat_hack (bind, type);
STAT_VISIBLE (bind) = visible;
@@ -4459,6 +4488,17 @@ set_module_binding (tree ns, tree name, unsigned mod, bool global_p,
STAT_TYPE_VISIBLE_P (bind) = true;
}
+ /* If this has internal declarations, track them for diagnostics. */
+ if (internal)
+ {
+ if (!BINDING_VECTOR_INTERNAL_DECLS (*slot))
+ BINDING_VECTOR_INTERNAL_DECLS (*slot)
+ = module_tree_map_t::create_ggc ();
+ bool existed = BINDING_VECTOR_INTERNAL_DECLS (*slot)->put (mod, internal);
+ gcc_checking_assert (!existed);
+ MODULE_BINDING_INTERNAL_DECLS_P (bind) = true;
+ }
+
/* Note if this is this-module and/or global binding. */
if (partition_p)
MODULE_BINDING_PARTITION_P (bind) = true;
diff --git a/gcc/cp/name-lookup.h b/gcc/cp/name-lookup.h
index 5b142e7..3815b8c 100644
--- a/gcc/cp/name-lookup.h
+++ b/gcc/cp/name-lookup.h
@@ -142,15 +142,23 @@ struct GTY(()) binding_cluster
#define BINDING_VECTOR_CLUSTER(NODE,IX) \
(((tree_binding_vec *)BINDING_VECTOR_CHECK (NODE))->vec[IX])
+struct module_tree_map_traits
+ : simple_hashmap_traits<int_hash<unsigned, 0>, tree> {};
+typedef hash_map<unsigned, tree, module_tree_map_traits> module_tree_map_t;
+
struct GTY(()) tree_binding_vec {
struct tree_base base;
tree name;
+ module_tree_map_t *internal_decls;
binding_cluster GTY((length ("%h.base.u.dependence_info.base"))) vec[1];
};
/* The name of a module vector. */
#define BINDING_VECTOR_NAME(NODE) \
(((tree_binding_vec *)BINDING_VECTOR_CHECK (NODE))->name)
+/* A collection of internal functions for ADL in this binding. */
+#define BINDING_VECTOR_INTERNAL_DECLS(NODE) \
+ (((tree_binding_vec *)BINDING_VECTOR_CHECK (NODE))->internal_decls)
/* tree_binding_vec does uses base.u.dependence_info.base field for
length. It does not have lang_flag etc available! */
@@ -166,6 +174,11 @@ struct GTY(()) tree_binding_vec {
#define BINDING_VECTOR_PARTITION_DUPS_P(NODE) \
(BINDING_VECTOR_CHECK (NODE)->base.volatile_flag)
+/* True if the binding slot has internal-linkage functions that should
+ cause ADL to error. */
+#define MODULE_BINDING_INTERNAL_DECLS_P(NODE) \
+ (OVERLOAD_CHECK (NODE)->base.private_flag)
+
/* These two flags indicate the provenence of the bindings on this
particular vector slot. We can of course determine this from slot
number, but that's a relatively expensive lookup. This avoids
@@ -491,7 +504,8 @@ extern bool import_module_binding (tree ctx, tree name, unsigned mod,
unsigned snum);
extern bool set_module_binding (tree ctx, tree name, unsigned mod,
bool global_p, bool partition_p,
- tree value, tree type, tree visible);
+ tree value, tree type, tree visible,
+ tree internal);
extern void add_module_namespace_decl (tree ns, tree decl);
enum WMB_Flags
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index 8d1187f..362cddb 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -28124,6 +28124,7 @@ cp_parser_class_specifier (cp_parser* parser)
bool in_switch_statement_p;
bool saved_in_unbraced_linkage_specification_p;
bool saved_in_unbraced_export_declaration_p;
+ bool saved_auto_is_implicit_function_template_parm_p;
tree old_scope = NULL_TREE;
tree scope = NULL_TREE;
cp_token *closing_brace;
@@ -28181,6 +28182,10 @@ cp_parser_class_specifier (cp_parser* parser)
saved_in_unbraced_export_declaration_p
= parser->in_unbraced_export_declaration_p;
parser->in_unbraced_export_declaration_p = false;
+ saved_auto_is_implicit_function_template_parm_p
+ = parser->auto_is_implicit_function_template_parm_p;
+ parser->auto_is_implicit_function_template_parm_p = false;
+
/* 'this' from an enclosing non-static member function is unavailable. */
tree saved_ccp = current_class_ptr;
tree saved_ccr = current_class_ref;
@@ -28571,6 +28576,8 @@ cp_parser_class_specifier (cp_parser* parser)
= saved_in_unbraced_linkage_specification_p;
parser->in_unbraced_export_declaration_p
= saved_in_unbraced_export_declaration_p;
+ parser->auto_is_implicit_function_template_parm_p
+ = saved_auto_is_implicit_function_template_parm_p;
current_class_ptr = saved_ccp;
current_class_ref = saved_ccr;
@@ -31179,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
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index bd60d51..9b32049 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -8928,8 +8928,11 @@ convert_template_argument (tree parm,
orig_arg = TREE_OPERAND (orig_arg, 0);
if (!uses_template_parms (t)
- && !(force_conv ? uses_template_parms (orig_arg)
- : type_dependent_expression_p (orig_arg)))
+ && !type_dependent_expression_p (orig_arg)
+ && !(force_conv
+ && !(same_type_ignoring_top_level_qualifiers_p
+ (TREE_TYPE (orig_arg), t))
+ && value_dependent_expression_p (orig_arg)))
/* We used to call digest_init here. However, digest_init
will report errors, which we don't want when complain
is zero. More importantly, digest_init will try too
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 1937ace..8d0210e 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -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. */
diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc
index 03b58ff..c6ce7e9 100644
--- a/gcc/cp/tree.cc
+++ b/gcc/cp/tree.cc
@@ -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,