https://gcc.gnu.org/g:4c01f40985eafccc3dd058441325b58009defd09
commit r15-7203-g4c01f40985eafccc3dd058441325b58009defd09 Author: Nathaniel Shead <nathanielosh...@gmail.com> Date: Sun Jan 5 23:45:05 2025 +1100 c++/modules: Diagnose TU-local lambdas, give mangling scope to lambdas in concepts This fills in a hole left in r15-6378-g9016c5ac94c557 with regards to detection of TU-local lambdas. Now that LAMBDA_EXPR_EXTRA_SCOPE is properly set for most lambdas we can use it to detect lambdas that are TU-local. CWG2988 suggests that lambdas in concept definitions should not be considered TU-local, since they are always unevaluated and should never be emitted. This patch gives these lambdas a mangling scope (though it will never be actually used in name mangling). PR c++/116568 gcc/cp/ChangeLog: * cp-tree.h (finish_concept_definition): Adjust parameters. (start_concept_definition): Declare. * module.cc (depset::hash::is_tu_local_entity): Use LAMBDA_EXPR_EXTRA_SCOPE to detect TU-local lambdas. * parser.cc (cp_parser_concept_definition): Start a lambda scope for concept definitions. * pt.cc (tsubst_lambda_expr): Namespace-scope lambdas may now have extra scope. (finish_concept_definition): Split into... (start_concept_definition): ...this new function. gcc/testsuite/ChangeLog: * g++.dg/modules/internal-4_b.C: Remove XFAIL, add lambda alias testcase. * g++.dg/modules/lambda-9.h: New test. * g++.dg/modules/lambda-9_a.H: New test. * g++.dg/modules/lambda-9_b.C: New test. Signed-off-by: Nathaniel Shead <nathanielosh...@gmail.com> Reviewed-by: Jason Merrill <ja...@redhat.com> Diff: --- gcc/cp/cp-tree.h | 3 ++- gcc/cp/module.cc | 15 +++++++++------ gcc/cp/parser.cc | 14 +++++++++++++- gcc/cp/pt.cc | 21 +++++++++++++++------ gcc/testsuite/g++.dg/modules/internal-4_b.C | 5 +++-- gcc/testsuite/g++.dg/modules/lambda-9.h | 3 +++ gcc/testsuite/g++.dg/modules/lambda-9_a.H | 4 ++++ gcc/testsuite/g++.dg/modules/lambda-9_b.C | 6 ++++++ 8 files changed, 55 insertions(+), 16 deletions(-) diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 5d79cf927d92..4aa9f9f9aa91 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -8689,7 +8689,8 @@ struct diagnosing_failed_constraint extern cp_expr finish_constraint_or_expr (location_t, cp_expr, cp_expr); extern cp_expr finish_constraint_and_expr (location_t, cp_expr, cp_expr); extern cp_expr finish_constraint_primary_expr (cp_expr); -extern tree finish_concept_definition (cp_expr, tree, tree); +extern tree start_concept_definition (cp_expr); +extern tree finish_concept_definition (tree, tree, tree); extern tree combine_constraint_expressions (tree, tree); extern tree append_constraint (tree, tree); extern tree get_constraints (const_tree); diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc index 17215594fd3f..1c1eb2e37e2d 100644 --- a/gcc/cp/module.cc +++ b/gcc/cp/module.cc @@ -11329,7 +11329,8 @@ trees_out::key_mergeable (int tag, merge_kind mk, tree decl, tree inner, gcc_checking_assert (TREE_CODE (scope) == VAR_DECL || TREE_CODE (scope) == FIELD_DECL || TREE_CODE (scope) == PARM_DECL - || TREE_CODE (scope) == TYPE_DECL); + || TREE_CODE (scope) == TYPE_DECL + || TREE_CODE (scope) == CONCEPT_DECL); /* Lambdas attached to fields are keyed to the class. */ if (TREE_CODE (scope) == FIELD_DECL) scope = TYPE_NAME (DECL_CONTEXT (scope)); @@ -13435,9 +13436,10 @@ depset::hash::is_tu_local_entity (tree decl, bool explain/*=false*/) tree main_decl = TYPE_MAIN_DECL (type); if (!DECL_CLASS_SCOPE_P (main_decl) && !decl_function_context (main_decl) - /* FIXME: Lambdas defined outside initializers. We'll need to more - thoroughly set LAMBDA_TYPE_EXTRA_SCOPE to check this. */ - && !LAMBDA_TYPE_P (type)) + /* LAMBDA_EXPR_EXTRA_SCOPE will be set for lambdas defined in + contexts where they would not be TU-local. */ + && !(LAMBDA_TYPE_P (type) + && LAMBDA_TYPE_EXTRA_SCOPE (type))) { if (explain) inform (loc, "%qT has no name and is not defined within a class, " @@ -20309,11 +20311,12 @@ maybe_key_decl (tree ctx, tree decl) return; /* We only need to deal with lambdas attached to var, field, - parm, or type decls. */ + parm, type, or concept decls. */ if (TREE_CODE (ctx) != VAR_DECL && TREE_CODE (ctx) != FIELD_DECL && TREE_CODE (ctx) != PARM_DECL - && TREE_CODE (ctx) != TYPE_DECL) + && TREE_CODE (ctx) != TYPE_DECL + && TREE_CODE (ctx) != CONCEPT_DECL) return; /* For fields, key it to the containing type to handle deduplication diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc index 2f88e5fcd914..7ddb7f119a4c 100644 --- a/gcc/cp/parser.cc +++ b/gcc/cp/parser.cc @@ -31830,8 +31830,20 @@ cp_parser_concept_definition (cp_parser *parser) return error_mark_node; } + tree decl = start_concept_definition (id); + if (decl == error_mark_node) + { + cp_parser_skip_to_end_of_statement (parser); + cp_parser_consume_semicolon_at_end_of_statement (parser); + return error_mark_node; + } + processing_constraint_expression_sentinel parsing_constraint; + + start_lambda_scope (decl); tree init = cp_parser_constraint_expression (parser); + finish_lambda_scope (); + if (init == error_mark_node) cp_parser_skip_to_end_of_statement (parser); @@ -31839,7 +31851,7 @@ cp_parser_concept_definition (cp_parser *parser) but continue as if it were. */ cp_parser_consume_semicolon_at_end_of_statement (parser); - return finish_concept_definition (id, init, attrs); + return finish_concept_definition (decl, init, attrs); } // -------------------------------------------------------------------------- // diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc index 0f739d89b35c..6442ae947c32 100644 --- a/gcc/cp/pt.cc +++ b/gcc/cp/pt.cc @@ -20231,7 +20231,7 @@ tsubst_lambda_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl) if (LAMBDA_EXPR_EXTRA_SCOPE (t)) record_lambda_scope (r); - else if (TYPE_NAMESPACE_SCOPE_P (TREE_TYPE (t))) + if (TYPE_NAMESPACE_SCOPE_P (TREE_TYPE (t))) /* If we're pushed into another scope (PR105652), fix it. */ TYPE_CONTEXT (type) = DECL_CONTEXT (TYPE_NAME (type)) = TYPE_CONTEXT (TREE_TYPE (t)); @@ -30027,12 +30027,10 @@ placeholder_type_constraint_dependent_p (tree t) return false; } -/* Build and return a concept definition. Like other templates, the - CONCEPT_DECL node is wrapped by a TEMPLATE_DECL. This returns the - the TEMPLATE_DECL. */ +/* Prepare and return a concept definition. */ tree -finish_concept_definition (cp_expr id, tree init, tree attrs) +start_concept_definition (cp_expr id) { gcc_assert (identifier_p (id)); gcc_assert (processing_template_decl); @@ -30064,9 +30062,20 @@ finish_concept_definition (cp_expr id, tree init, tree attrs) /* Initially build the concept declaration; its type is bool. */ tree decl = build_lang_decl_loc (loc, CONCEPT_DECL, *id, boolean_type_node); DECL_CONTEXT (decl) = current_scope (); - DECL_INITIAL (decl) = init; TREE_PUBLIC (decl) = true; + return decl; +} + +/* Finish building a concept definition. Like other templates, the + CONCEPT_DECL node is wrapped by a TEMPLATE_DECL. This returns the + the TEMPLATE_DECL. */ + +tree +finish_concept_definition (tree decl, tree init, tree attrs) +{ + DECL_INITIAL (decl) = init; + if (attrs) cplus_decl_attributes (&decl, attrs, 0); diff --git a/gcc/testsuite/g++.dg/modules/internal-4_b.C b/gcc/testsuite/g++.dg/modules/internal-4_b.C index 86bec294fc83..81fc65a69bdd 100644 --- a/gcc/testsuite/g++.dg/modules/internal-4_b.C +++ b/gcc/testsuite/g++.dg/modules/internal-4_b.C @@ -80,13 +80,14 @@ void in_function_body() { struct {} x; } // OK auto in_initializer = []{}; // OK #if __cplusplus >= 202002L -decltype([]{}) d_lambda; // { dg-error "exposes TU-local entity" "" { xfail *-*-* } } +decltype([]{}) d_lambda; // { dg-error "exposes TU-local entity" "" { target c++20 } } +using alias_lambda = decltype([]{}); // { dg-error "exposes TU-local entity" "" { target c++20 } } template <typename T> concept in_constraint_expression = requires { // Strictly by the standard this is currently ill-formed // (this is a constraint-expression not an initializer) - // but I don't think that is intended. + // but I don't think that is intended; see CWG2988. []{}; // { dg-bogus "exposes TU-local entity" } }; #endif diff --git a/gcc/testsuite/g++.dg/modules/lambda-9.h b/gcc/testsuite/g++.dg/modules/lambda-9.h new file mode 100644 index 000000000000..fccd36da6d13 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/lambda-9.h @@ -0,0 +1,3 @@ +// CWG2988 +template <typename T> +concept C = requires { []{}; }; diff --git a/gcc/testsuite/g++.dg/modules/lambda-9_a.H b/gcc/testsuite/g++.dg/modules/lambda-9_a.H new file mode 100644 index 000000000000..da0fa3b31f2d --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/lambda-9_a.H @@ -0,0 +1,4 @@ +// { dg-additional-options "-fmodule-header -std=c++20" } +// { dg-module-cmi {} } + +#include "lambda-9.h" diff --git a/gcc/testsuite/g++.dg/modules/lambda-9_b.C b/gcc/testsuite/g++.dg/modules/lambda-9_b.C new file mode 100644 index 000000000000..6f2186421f7d --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/lambda-9_b.C @@ -0,0 +1,6 @@ +// { dg-additional-options "-fmodules -std=c++20 -fno-module-lazy" } + +#include "lambda-9.h" +import "lambda-9_a.H"; + +static_assert(C<int>);