From 5aeaabf35eaddf9e5bfb05c0ec901a8aecfaa36a Mon Sep 17 00:00:00 2001 From: Roy Jacobson Date: Thu, 3 Mar 2022 06:17:07 -0800 Subject: [PATCH] [Concepts] Check constraints for explicit template instantiations The standard requires[0] member function constraints to be checked when explicitly instantiating classes. This patch adds this constraints check. This issue is tracked as #46029 [1]. Note that there's an related open CWG issue (2421[2]) about what to do when multiple candidates have satisfied constraints. This is particularly an issue because mangling doesn't contain function constraints, and so the following code still ICEs with definition with same mangled name '_ZN1BIiE1fEv' as another definition: template struct B { int f() requires std::same_as { return 0; } int f() requires (std::same_as && !std::same_as) { return 1; } }; template struct B; Also note that the constraints checking while instantiating *functions* is still not implemented. I started looking at it but It's a bit more complicated. I believe in such a case we have to consider the partial constraints order and potentially choose the best candidate out of the set of multiple valid ones. [0]: https://eel.is/c++draft/temp.explicit#10 [1]: https://github.com/llvm/llvm-project/issues/46029 [2]: https://cplusplus.github.io/CWG/issues/2421.html Differential Revision: https://reviews.llvm.org/D120255 --- clang/lib/Sema/SemaTemplateInstantiate.cpp | 8 ++++ .../constraints-explicit-instantiation.cpp | 38 +++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 clang/test/AST/constraints-explicit-instantiation.cpp diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index 267566520297..46da97968a93 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -3230,6 +3230,14 @@ Sema::InstantiateClassMembers(SourceLocation PointOfInstantiation, if (FunctionDecl *Pattern = Function->getInstantiatedFromMemberFunction()) { + if (Function->getTrailingRequiresClause()) { + ConstraintSatisfaction Satisfaction; + if (CheckFunctionConstraints(Function, Satisfaction) || + !Satisfaction.IsSatisfied) { + continue; + } + } + if (Function->hasAttr()) continue; diff --git a/clang/test/AST/constraints-explicit-instantiation.cpp b/clang/test/AST/constraints-explicit-instantiation.cpp new file mode 100644 index 000000000000..10b6432f2db8 --- /dev/null +++ b/clang/test/AST/constraints-explicit-instantiation.cpp @@ -0,0 +1,38 @@ +// RUN: %clang_cc1 -std=c++20 -ast-dump %s | FileCheck %s + +namespace PR46029 { + +template +void canary1(); +template +void canary2(); + +template +struct A { + void f() requires(N == 1) { + static_assert(N == 1); + canary1(); + } + void f() requires(N == 2) { + static_assert(N == 2); + canary2(); + } +}; + +// This checks that `canary1<1>` and `canaray2<2>` are instantiated, thus +// indirectly validating that the correct candidates of `A::f` were really +// instantiated each time. +// The `static_assert`s validate we don't instantiate wrong candidates. + +// CHECK:{{.*}}FunctionTemplateDecl {{.*}} canary1 +// CHECK: {{.*}}TemplateArgument integral +// CHECK-SAME: {{1$}} +template struct A<1>; + +// CHECK: {{.*}}FunctionTemplateDecl {{.*}} canary2 +// CHECK: {{.*}}TemplateArgument integral +// CHECK-SAME: {{2$}} +template struct A<2>; + +template struct A<3>; +}