aboutsummaryrefslogtreecommitdiff
path: root/gcc/cp
diff options
context:
space:
mode:
authorNathan Sidwell <nathan@acm.org>2022-10-31 06:11:28 -0400
committerNathan Sidwell <nathan@acm.org>2022-11-01 17:44:36 -0400
commit2b0e81d5cc2f7e1d773f6c502bd65b097f392675 (patch)
tree56bc63afdde5a1c5b0aef088a4dafdcb200c08c0 /gcc/cp
parent4f8aac77e05d0ae0b7f242fd1aa344d36ff52ceb (diff)
downloadgcc-2b0e81d5cc2f7e1d773f6c502bd65b097f392675.zip
gcc-2b0e81d5cc2f7e1d773f6c502bd65b097f392675.tar.gz
gcc-2b0e81d5cc2f7e1d773f6c502bd65b097f392675.tar.bz2
c++: per-scope, per-signature lambda discriminators
This implements ABI-compliant lambda discriminators. Not only do we have per-scope counters, but we also distinguish by lambda signature. Only lambdas with the same signature will need non-zero discriminators. As the discriminator is signature-dependent, we have to process the lambda function's declaration before we can determine it. For templated and generic lambdas the signature is that of the uninstantiated lambda -- not separate for each instantiation. With this change, gcc and clang now produce the same lambda manglings for all these testcases. gcc/cp/ * cp-tree.h (LAMBDA_EXPR_SCOPE_SIG_DISCRIMINATOR): New. (struct tree_lambda_expr): Add discriminator_sig bitfield. (recrd_lambda_scope_sig_discriminator): Declare. * lambda.cc (struct lambda_sig_count): New. (lambda_discriminator): Add signature vector. (start_lambda_scope): Adjust. (compare_lambda_template_head, compare_lambda_sig): New. (record_lambda_scope_sig_discriminator): New. * mangle.cc (write_closure_type): Use the scope-sig discriminator for ABI >= 18. Emit abi mangling warning if needed. * module.cc (trees_out::core_vals): Stream the new discriminator. (trees_in::core_vals): Likewise. * parser.cc (cp_parser_lambda_declarator_opt): Call record_lambda_scope_sig_discriminator. * pt.cc (tsubst_lambda_expr): Likewise. libcc1/ * libcp1plugin.cc (plugin_start_lambda_closure_class_type): Initialize the per-scope, per-signature discriminator. gcc/testsuite/ * g++.dg/abi/lambda-sig1-18.C: New. * g++.dg/abi/lambda-sig1-18vs17.C: New. * g++.dg/cpp1y/lambda-mangle-1-18.C: New.
Diffstat (limited to 'gcc/cp')
-rw-r--r--gcc/cp/cp-tree.h7
-rw-r--r--gcc/cp/lambda.cc148
-rw-r--r--gcc/cp/mangle.cc8
-rw-r--r--gcc/cp/module.cc2
-rw-r--r--gcc/cp/parser.cc1
-rw-r--r--gcc/cp/pt.cc1
6 files changed, 164 insertions, 3 deletions
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 4c0bacb..d13bb3d 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -1501,9 +1501,12 @@ enum cp_lambda_default_capture_mode_type {
(((struct tree_lambda_expr *)LAMBDA_EXPR_CHECK (NODE))->extra_scope)
/* Lambdas in the same extra scope might need a discriminating count.
- This is a single per-scope count. */
+ For ABI 17, we have single per-scope count, for ABI 18, we have
+ per-scope, per-signature numbering. */
#define LAMBDA_EXPR_SCOPE_ONLY_DISCRIMINATOR(NODE) \
(((struct tree_lambda_expr *)LAMBDA_EXPR_CHECK (NODE))->discriminator_scope)
+#define LAMBDA_EXPR_SCOPE_SIG_DISCRIMINATOR(NODE) \
+ (((struct tree_lambda_expr *)LAMBDA_EXPR_CHECK (NODE))->discriminator_sig)
/* During parsing of the lambda, a vector of capture proxies which need
to be pushed once we're done processing a nested lambda. */
@@ -1533,6 +1536,7 @@ struct GTY (()) tree_lambda_expr
location_t locus;
enum cp_lambda_default_capture_mode_type default_capture_mode : 2;
unsigned discriminator_scope : 15; // Per-scope discriminator
+ unsigned discriminator_sig : 15; // Per-scope, per-signature discriminator
};
/* Non-zero if this template specialization has access violations that
@@ -7783,6 +7787,7 @@ extern void start_lambda_scope (tree decl);
extern void finish_lambda_scope (void);
extern void record_lambda_scope (tree lambda);
extern void record_lambda_scope_discriminator (tree lambda);
+extern void record_lambda_scope_sig_discriminator (tree lambda, tree fn);
extern tree start_lambda_function (tree fn, tree lambda_expr);
extern void finish_lambda_function (tree body);
extern bool regenerated_lambda_fn_p (tree);
diff --git a/gcc/cp/lambda.cc b/gcc/cp/lambda.cc
index d2673e2..c7a9268 100644
--- a/gcc/cp/lambda.cc
+++ b/gcc/cp/lambda.cc
@@ -1447,13 +1447,21 @@ is_lambda_ignored_entity (tree val)
/* Lambdas that appear in variable initializer or default argument
scope get that in their mangling, so we need to record it. Also,
multiple lambdas in the same scope may need a mangling
- discriminator. Record in the same data structure. */
+ discriminator. In ABI <= 17, there is a single per-scope sequence
+ number. In ABI >= 18, there are per-scope per-signature sequence
+ numbers. */
+struct GTY(()) lambda_sig_count
+{
+ tree fn; // The lambda fn whose sig this is.
+ unsigned count;
+};
struct GTY(()) lambda_discriminator
{
tree scope;
unsigned nesting; // Inside a function, VAR_DECLs get the function
// as scope. This counts that nesting.
unsigned count; // The per-scope counter.
+ vec<lambda_sig_count, va_gc> *discriminators; // Per-signature counters
};
// The current scope.
static GTY(()) lambda_discriminator lambda_scope;
@@ -1475,6 +1483,7 @@ start_lambda_scope (tree decl)
lambda_scope.scope = decl;
lambda_scope.nesting = 0;
lambda_scope.count = 0;
+ lambda_scope.discriminators = nullptr;
}
}
@@ -1504,6 +1513,116 @@ record_lambda_scope (tree lambda)
}
}
+// Compare lambda template heads TMPL_A and TMPL_B, used for both
+// templated lambdas, and template template parameters of said lambda.
+
+static bool
+compare_lambda_template_head (tree tmpl_a, tree tmpl_b)
+{
+ // We only need one level of template parms
+ tree inner_a = INNERMOST_TEMPLATE_PARMS (DECL_TEMPLATE_PARMS (tmpl_a));
+ tree inner_b = INNERMOST_TEMPLATE_PARMS (DECL_TEMPLATE_PARMS (tmpl_b));
+
+ // We only compare explicit template parms, ignoring trailing
+ // synthetic ones.
+ int len_a = TREE_VEC_LENGTH (inner_a);
+ int len_b = TREE_VEC_LENGTH (inner_b);
+
+ for (int ix = 0, len = MAX (len_a, len_b); ix != len; ix++)
+ {
+ tree parm_a = NULL_TREE;
+ if (ix < len_a)
+ {
+ parm_a = TREE_VEC_ELT (inner_a, ix);
+ if (parm_a == error_mark_node)
+ return false;
+ parm_a = TREE_VALUE (parm_a);
+ if (DECL_VIRTUAL_P (parm_a))
+ parm_a = NULL_TREE;
+ }
+
+ tree parm_b = NULL_TREE;
+ if (ix < len_b)
+ {
+ parm_b = TREE_VEC_ELT (inner_b, ix);
+ if (parm_b == error_mark_node)
+ return false;
+ parm_b = TREE_VALUE (parm_b);
+ if (DECL_VIRTUAL_P (parm_b))
+ parm_b = NULL_TREE;
+ }
+
+ if (!parm_a && !parm_b)
+ // we're done
+ break;
+
+ if (!(parm_a && parm_b))
+ return false;
+
+ if (TREE_CODE (parm_a) != TREE_CODE (parm_b))
+ return false;
+
+ if (TREE_CODE (parm_a) == PARM_DECL)
+ {
+ if (TEMPLATE_PARM_PARAMETER_PACK (DECL_INITIAL (parm_a))
+ != TEMPLATE_PARM_PARAMETER_PACK (DECL_INITIAL (parm_b)))
+ return false;
+
+ if (!same_type_p (TREE_TYPE (parm_a), TREE_TYPE (parm_b)))
+ return false;
+ }
+ else
+ {
+ if (TEMPLATE_TYPE_PARAMETER_PACK (TREE_TYPE (parm_a))
+ != TEMPLATE_TYPE_PARAMETER_PACK (TREE_TYPE (parm_b)))
+ return false;
+
+ if (TREE_CODE (parm_a) != TEMPLATE_DECL)
+ gcc_checking_assert (TREE_CODE (parm_a) == TYPE_DECL);
+ else if (!compare_lambda_template_head (parm_a, parm_b))
+ return false;
+ }
+ }
+
+ return true;
+}
+
+// Compare lambda signatures FN_A and FN_B, they may be TEMPLATE_DECLs too.
+
+static bool
+compare_lambda_sig (tree fn_a, tree fn_b)
+{
+ if (TREE_CODE (fn_a) == TEMPLATE_DECL
+ && TREE_CODE (fn_b) == TEMPLATE_DECL)
+ {
+ if (!compare_lambda_template_head (fn_a, fn_b))
+ return false;
+ fn_a = DECL_TEMPLATE_RESULT (fn_a);
+ fn_b = DECL_TEMPLATE_RESULT (fn_b);
+ }
+ else if (TREE_CODE (fn_a) == TEMPLATE_DECL
+ || TREE_CODE (fn_b) == TEMPLATE_DECL)
+ return false;
+
+ if (fn_a == error_mark_node
+ || fn_b == error_mark_node)
+ return false;
+
+ for (tree args_a = TREE_CHAIN (TYPE_ARG_TYPES (TREE_TYPE (fn_a))),
+ args_b = TREE_CHAIN (TYPE_ARG_TYPES (TREE_TYPE (fn_b)));
+ args_a || args_b;
+ args_a = TREE_CHAIN (args_a), args_b = TREE_CHAIN (args_b))
+ {
+ if (!args_a || !args_b)
+ return false;
+ // This check also deals with differing varadicness
+ if (!same_type_p (TREE_VALUE (args_a), TREE_VALUE (args_b)))
+ return false;
+ }
+
+ return true;
+}
+
// Record the per-scope discriminator of LAMBDA. If the extra scope
// is empty, we must use the empty scope counter, which might not be
// the live one.
@@ -1517,6 +1636,33 @@ record_lambda_scope_discriminator (tree lambda)
LAMBDA_EXPR_SCOPE_ONLY_DISCRIMINATOR (lambda) = slot->count++;
}
+// Record the per-scope per-signature discriminator of LAMBDA. If the
+// extra scope is empty, we must use the empty scope counter, which
+// might not be the live one.
+
+void
+record_lambda_scope_sig_discriminator (tree lambda, tree fn)
+{
+ auto *slot = (vec_safe_is_empty (lambda_scope_stack)
+ || LAMBDA_EXPR_EXTRA_SCOPE (lambda)
+ ? &lambda_scope : lambda_scope_stack->begin ());
+ gcc_checking_assert (LAMBDA_EXPR_EXTRA_SCOPE (lambda) == slot->scope);
+
+ // A linear search, we're not expecting this to be a big list, and
+ // this avoids needing a signature hash function.
+ lambda_sig_count *sig;
+ if (unsigned ix = vec_safe_length (slot->discriminators))
+ for (sig = slot->discriminators->begin (); ix--; sig++)
+ if (compare_lambda_sig (fn, sig->fn))
+ goto found;
+ {
+ lambda_sig_count init = {fn, 0};
+ sig = vec_safe_push (slot->discriminators, init);
+ }
+ found:
+ LAMBDA_EXPR_SCOPE_SIG_DISCRIMINATOR (lambda) = sig->count++;
+}
+
tree
start_lambda_function (tree fco, tree lambda_expr)
{
diff --git a/gcc/cp/mangle.cc b/gcc/cp/mangle.cc
index a62c975..e97428e 100644
--- a/gcc/cp/mangle.cc
+++ b/gcc/cp/mangle.cc
@@ -1810,7 +1810,13 @@ write_closure_type_name (const tree type)
write_method_parms (parms, /*method_p=*/1, fn);
write_char ('E');
- write_compact_number (LAMBDA_EXPR_SCOPE_ONLY_DISCRIMINATOR (lambda));
+ if ((LAMBDA_EXPR_SCOPE_SIG_DISCRIMINATOR (lambda)
+ != LAMBDA_EXPR_SCOPE_ONLY_DISCRIMINATOR (lambda))
+ && abi_warn_or_compat_version_crosses (18))
+ G.need_abi_warning = true;
+ write_compact_number (abi_version_at_least (18)
+ ? LAMBDA_EXPR_SCOPE_SIG_DISCRIMINATOR (lambda)
+ : LAMBDA_EXPR_SCOPE_ONLY_DISCRIMINATOR (lambda));
}
/* Convert NUMBER to ascii using base BASE and generating at least
diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc
index df05b52..0e9af31 100644
--- a/gcc/cp/module.cc
+++ b/gcc/cp/module.cc
@@ -6328,6 +6328,7 @@ trees_out::core_vals (tree t)
{
WU (((lang_tree_node *)t)->lambda_expression.default_capture_mode);
WU (((lang_tree_node *)t)->lambda_expression.discriminator_scope);
+ WU (((lang_tree_node *)t)->lambda_expression.discriminator_sig);
}
break;
@@ -6820,6 +6821,7 @@ trees_in::core_vals (tree t)
RUC (cp_lambda_default_capture_mode_type,
((lang_tree_node *)t)->lambda_expression.default_capture_mode);
RU (((lang_tree_node *)t)->lambda_expression.discriminator_scope);
+ RU (((lang_tree_node *)t)->lambda_expression.discriminator_sig);
break;
case OVERLOAD:
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index e0e3cf3..fd59de4 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -11712,6 +11712,7 @@ cp_parser_lambda_declarator_opt (cp_parser* parser, tree lambda_expr)
fco = finish_fully_implicit_template (parser, fco);
finish_member_declaration (fco);
+ record_lambda_scope_sig_discriminator (lambda_expr, fco);
obstack_free (&declarator_obstack, p);
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index fc6279c..c3fc56a 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -19912,6 +19912,7 @@ tsubst_lambda_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl)
goto out;
}
finish_member_declaration (inst);
+ record_lambda_scope_sig_discriminator (r, inst);
tree fn = oldtmpl ? DECL_TEMPLATE_RESULT (inst) : inst;