https://gcc.gnu.org/g:020ad96f2e18d483e577343b25f0d3a782065c7e
commit r16-6206-g020ad96f2e18d483e577343b25f0d3a782065c7e Author: Nathaniel Shead <[email protected]> Date: Sun Dec 14 09:32:50 2025 +1100 c++: Don't record lambdas in concept evaluations [PR123075] When evaluating a concept definition in a template, any lambdas in the definition of the concept get instantiated in the context of where the evaluation occurred. This causes two issues: - Any lambdas declared later in the body of the function get the wrong discriminator, which causes ABI divergences with Clang. - Modules streaming gets confused, because the lambda is keyed to an unrelated declaration. Keying the lambda to the concept also doesn't work because we'd really want to key it to a concept instantiation (that doesn't exist) so that merging works correctly. I think really we just want to throw away these lambdas declarations after evaluating the concept. They can (and will) be recreated in importers re-evaluating the concept with the given args regardless. This patch implements this by disabling scope recording for an instantiation of a lambda keyed to a concept, and pushing into an unrelated context so that the lambda's type is not mistakenly added into the scope it was instantiated from. PR c++/123075 gcc/cp/ChangeLog: * constraint.cc (evaluate_concept_check): Push to an unrelated scope, but keep the same access context. * pt.cc (tsubst_lambda_expr): Don't record lambda scopes for lambdas attached to a concept. gcc/testsuite/ChangeLog: * g++.dg/cpp2a/concepts-lambda25.C: New test. * g++.dg/modules/lambda-13.h: New test. * g++.dg/modules/lambda-13_a.H: New test. * g++.dg/modules/lambda-13_b.C: New test. Signed-off-by: Nathaniel Shead <[email protected]> Reviewed-by: Jason Merrill <[email protected]> Reviewed-by: Patrick Palka <[email protected]> Diff: --- gcc/cp/constraint.cc | 20 +++++++++++++++++- gcc/cp/pt.cc | 7 ++++++- gcc/testsuite/g++.dg/cpp2a/concepts-lambda25.C | 28 ++++++++++++++++++++++++++ gcc/testsuite/g++.dg/modules/lambda-13.h | 22 ++++++++++++++++++++ gcc/testsuite/g++.dg/modules/lambda-13_a.H | 6 ++++++ gcc/testsuite/g++.dg/modules/lambda-13_b.C | 6 ++++++ 6 files changed, 87 insertions(+), 2 deletions(-) diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc index 6abd0966fcd1..92a3a780008b 100644 --- a/gcc/cp/constraint.cc +++ b/gcc/cp/constraint.cc @@ -2860,9 +2860,27 @@ evaluate_concept_check (tree check) gcc_assert (concept_check_p (check)); + /* We don't want any declarations instantiated from a concept evaluation + to enter the binding table for the current scope, such as lambdas, so + leave that scope. But maintain the access context (PR104111). */ + tree scope = current_scope (); + if (CLASS_TYPE_P (scope)) + scope = TYPE_MAIN_DECL (scope); + else if (TREE_CODE (scope) != FUNCTION_DECL) + scope = NULL_TREE; + + push_to_top_level (); + if (scope) + push_access_scope (scope); + /* Check for satisfaction without diagnostics. */ sat_info quiet (tf_none, NULL_TREE); - return constraint_satisfaction_value (check, /*args=*/NULL_TREE, quiet); + tree r = constraint_satisfaction_value (check, /*args=*/NULL_TREE, quiet); + + if (scope) + pop_access_scope (scope); + pop_from_top_level (); + return r; } /* Evaluate the requires-expression T, returning either boolean_true_node diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc index 435be711d98b..20a1177ffab7 100644 --- a/gcc/cp/pt.cc +++ b/gcc/cp/pt.cc @@ -20583,7 +20583,12 @@ tsubst_lambda_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl) return error_mark_node; } - if (LAMBDA_EXPR_EXTRA_SCOPE (t)) + if (LAMBDA_EXPR_EXTRA_SCOPE (t) + /* When evaluating a concept we instantiate any lambda bodies + in the context of the evaluation. For ABI reasons don't + record a scope for this instantiated lambda so we don't + throw off the scope counter. */ + && TREE_CODE (LAMBDA_EXPR_EXTRA_SCOPE (t)) != CONCEPT_DECL) record_lambda_scope (r); if (TYPE_NAMESPACE_SCOPE_P (TREE_TYPE (t))) /* If we're pushed into another scope (PR105652), fix it. */ diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-lambda25.C b/gcc/testsuite/g++.dg/cpp2a/concepts-lambda25.C new file mode 100644 index 000000000000..188a52c7fd93 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-lambda25.C @@ -0,0 +1,28 @@ +// PR c++/123075 +// { dg-do compile { target c++20 } } +// { dg-additional-options "-fkeep-inline-functions" } + +template <typename T> +concept r = []{ return true; }(); + +template <typename T, typename U> +inline auto foo() { + static_assert(r<T>); + r<U>; + return []{ return false; }; +} + +template <typename T> +struct S { + static_assert(r<T>); + decltype([]{ return true; }) l; +}; +S<char> s; + +bool use = (foo<int, double>()() || s.l()); + +// There should only be one lambda keyed to 'foo()' and 'S::l' +// { dg-final { scan-assembler {_ZZ3fooIidEDavENKUlvE_clEv:} } } +// { dg-final { scan-assembler {_ZNK1SIcEUlvE_clEv:} } } +// { dg-final { scan-assembler-not {_ZZ3fooIidEDavENKUlvE0_clEv:} } } +// { dg-final { scan-assembler-not {_ZNK1SIcEUlvE0_clEv:} } } diff --git a/gcc/testsuite/g++.dg/modules/lambda-13.h b/gcc/testsuite/g++.dg/modules/lambda-13.h new file mode 100644 index 000000000000..275e6d2269a5 --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/lambda-13.h @@ -0,0 +1,22 @@ +// PR c++/123075 + +template <typename T> +concept r = []{ return true; }(); + +template <typename T> +inline void foo() { + static_assert(r<T>); +} + +template void foo<int>(); + +template <typename T> +struct S { + static_assert(r<T>); +}; + +template struct S<double>; + +enum E { + X = r<E>, +}; diff --git a/gcc/testsuite/g++.dg/modules/lambda-13_a.H b/gcc/testsuite/g++.dg/modules/lambda-13_a.H new file mode 100644 index 000000000000..2a748fef88fb --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/lambda-13_a.H @@ -0,0 +1,6 @@ +// PR c++/123075 +// { dg-do compile { target c++20 } } +// { dg-additional-options "-fmodule-header" } +// { dg-module-cmi {} } + +#include "lambda-13.h" diff --git a/gcc/testsuite/g++.dg/modules/lambda-13_b.C b/gcc/testsuite/g++.dg/modules/lambda-13_b.C new file mode 100644 index 000000000000..fac66bc5c23b --- /dev/null +++ b/gcc/testsuite/g++.dg/modules/lambda-13_b.C @@ -0,0 +1,6 @@ +// PR c++/123075 +// { dg-do compile { target c++20 } } +// { dg-additional-options "-fmodules -fno-module-lazy" } + +#include "lambda-13.h" +import "lambda-13_a.H";
