Skip to content

Commit 7483711

Browse files
authored
[clang] Reland: Instantiate concepts with sugared template arguments (#101782)
1 parent 22c06aa commit 7483711

File tree

19 files changed

+73
-58
lines changed

19 files changed

+73
-58
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ Improvements to Clang's diagnostics
148148
- Clang now diagnoses undefined behavior in constant expressions more consistently. This includes invalid shifts, and signed overflow in arithmetic.
149149

150150
- -Wdangling-assignment-gsl is enabled by default.
151+
- Clang now does a better job preserving the template arguments as written when specializing concepts.
151152

152153
Improvements to Clang's time-trace
153154
----------------------------------

clang/include/clang/Sema/Template.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,8 @@ enum class TemplateSubstitutionKind : char {
234234
/// Replaces the current 'innermost' level with the provided argument list.
235235
/// This is useful for type deduction cases where we need to get the entire
236236
/// list from the AST, but then add the deduced innermost list.
237-
void replaceInnermostTemplateArguments(Decl *AssociatedDecl, ArgList Args) {
237+
void replaceInnermostTemplateArguments(Decl *AssociatedDecl, ArgList Args,
238+
bool Final = false) {
238239
assert((!TemplateArgumentLists.empty() || NumRetainedOuterLevels) &&
239240
"Replacing in an empty list?");
240241

@@ -246,8 +247,7 @@ enum class TemplateSubstitutionKind : char {
246247
TemplateArgumentLists[0].Args = Args;
247248
} else {
248249
--NumRetainedOuterLevels;
249-
TemplateArgumentLists.push_back(
250-
{{AssociatedDecl, /*Final=*/false}, Args});
250+
TemplateArgumentLists.push_back({{AssociatedDecl, Final}, Args});
251251
}
252252
}
253253

clang/lib/Sema/SemaConcept.cpp

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -414,7 +414,8 @@ DiagRecursiveConstraintEval(Sema &S, llvm::FoldingSetNodeID &ID,
414414
E->Profile(ID, S.Context, /*Canonical=*/true);
415415
for (const auto &List : MLTAL)
416416
for (const auto &TemplateArg : List.Args)
417-
TemplateArg.Profile(ID, S.Context);
417+
S.Context.getCanonicalTemplateArgument(TemplateArg)
418+
.Profile(ID, S.Context);
418419

419420
// Note that we have to do this with our own collection, because there are
420421
// times where a constraint-expression check can cause us to need to evaluate
@@ -638,8 +639,8 @@ bool Sema::CheckConstraintSatisfaction(
638639
// here.
639640
llvm::SmallVector<TemplateArgument, 4> FlattenedArgs;
640641
for (auto List : TemplateArgsLists)
641-
FlattenedArgs.insert(FlattenedArgs.end(), List.Args.begin(),
642-
List.Args.end());
642+
for (const TemplateArgument &Arg : List.Args)
643+
FlattenedArgs.emplace_back(Context.getCanonicalTemplateArgument(Arg));
643644

644645
llvm::FoldingSetNodeID ID;
645646
ConstraintSatisfaction::Profile(ID, Context, Template, FlattenedArgs);
@@ -823,6 +824,8 @@ Sema::SetupConstraintCheckingTemplateArgumentsAndScope(
823824
/*RelativeToPrimary=*/true,
824825
/*Pattern=*/nullptr,
825826
/*ForConstraintInstantiation=*/true);
827+
if (TemplateArgs)
828+
MLTAL.replaceInnermostTemplateArguments(FD, *TemplateArgs, /*Final=*/true);
826829
if (SetupConstraintScope(FD, TemplateArgs, MLTAL, Scope))
827830
return std::nullopt;
828831

@@ -1476,7 +1479,7 @@ static bool substituteParameterMappings(Sema &S, NormalizedConstraint &N,
14761479
const ConceptSpecializationExpr *CSE) {
14771480
MultiLevelTemplateArgumentList MLTAL = S.getTemplateInstantiationArgs(
14781481
CSE->getNamedConcept(), CSE->getNamedConcept()->getLexicalDeclContext(),
1479-
/*Final=*/false, CSE->getTemplateArguments(),
1482+
/*Final=*/true, CSE->getTemplateArguments(),
14801483
/*RelativeToPrimary=*/true,
14811484
/*Pattern=*/nullptr,
14821485
/*ForConstraintInstantiation=*/true);

clang/lib/Sema/SemaExprCXX.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9330,14 +9330,13 @@ Sema::BuildExprRequirement(
93309330
// be satisfied.
93319331
TemplateParameterList *TPL =
93329332
ReturnTypeRequirement.getTypeConstraintTemplateParameterList();
9333-
QualType MatchedType =
9334-
Context.getReferenceQualifiedType(E).getCanonicalType();
9333+
QualType MatchedType = Context.getReferenceQualifiedType(E);
93359334
llvm::SmallVector<TemplateArgument, 1> Args;
93369335
Args.push_back(TemplateArgument(MatchedType));
93379336

93389337
auto *Param = cast<TemplateTypeParmDecl>(TPL->getParam(0));
93399338

9340-
MultiLevelTemplateArgumentList MLTAL(Param, Args, /*Final=*/false);
9339+
MultiLevelTemplateArgumentList MLTAL(Param, Args, /*Final=*/true);
93419340
MLTAL.addOuterRetainedLevels(TPL->getDepth());
93429341
const TypeConstraint *TC = Param->getTypeConstraint();
93439342
assert(TC && "Type Constraint cannot be null here");

clang/lib/Sema/SemaTemplate.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4358,13 +4358,13 @@ Sema::CheckConceptTemplateId(const CXXScopeSpec &SS,
43584358

43594359
auto *CSD = ImplicitConceptSpecializationDecl::Create(
43604360
Context, NamedConcept->getDeclContext(), NamedConcept->getLocation(),
4361-
CanonicalConverted);
4361+
SugaredConverted);
43624362
ConstraintSatisfaction Satisfaction;
43634363
bool AreArgsDependent =
43644364
TemplateSpecializationType::anyDependentTemplateArguments(
4365-
*TemplateArgs, CanonicalConverted);
4366-
MultiLevelTemplateArgumentList MLTAL(NamedConcept, CanonicalConverted,
4367-
/*Final=*/false);
4365+
*TemplateArgs, SugaredConverted);
4366+
MultiLevelTemplateArgumentList MLTAL(NamedConcept, SugaredConverted,
4367+
/*Final=*/true);
43684368
LocalInstantiationScope Scope(*this);
43694369

43704370
EnterExpressionEvaluationContext EECtx{
@@ -5582,7 +5582,7 @@ bool Sema::CheckTemplateArgumentList(
55825582
CXXThisScopeRAII(*this, RD, ThisQuals, RD != nullptr);
55835583

55845584
MultiLevelTemplateArgumentList MLTAL = getTemplateInstantiationArgs(
5585-
Template, NewContext, /*Final=*/false, CanonicalConverted,
5585+
Template, NewContext, /*Final=*/true, SugaredConverted,
55865586
/*RelativeToPrimary=*/true,
55875587
/*Pattern=*/nullptr,
55885588
/*ForConceptInstantiation=*/true);

clang/lib/Sema/SemaTemplateDeduction.cpp

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3078,7 +3078,7 @@ CheckDeducedArgumentConstraints(Sema &S, TemplateDeclT *Template,
30783078
// If we don't need to replace the deduced template arguments,
30793079
// we can add them immediately as the inner-most argument list.
30803080
if (!DeducedArgsNeedReplacement(Template))
3081-
Innermost = CanonicalDeducedArgs;
3081+
Innermost = SugaredDeducedArgs;
30823082

30833083
MultiLevelTemplateArgumentList MLTAL = S.getTemplateInstantiationArgs(
30843084
Template, Template->getDeclContext(), /*Final=*/false, Innermost,
@@ -3090,7 +3090,7 @@ CheckDeducedArgumentConstraints(Sema &S, TemplateDeclT *Template,
30903090
// not class-scope explicit specialization, so replace with Deduced Args
30913091
// instead of adding to inner-most.
30923092
if (!Innermost)
3093-
MLTAL.replaceInnermostTemplateArguments(Template, CanonicalDeducedArgs);
3093+
MLTAL.replaceInnermostTemplateArguments(Template, SugaredDeducedArgs);
30943094

30953095
if (S.CheckConstraintSatisfaction(Template, AssociatedConstraints, MLTAL,
30963096
Info.getLocation(),
@@ -3913,13 +3913,13 @@ TemplateDeductionResult Sema::FinishTemplateArgumentDeduction(
39133913
(CanonicalBuilder.size() ==
39143914
FunctionTemplate->getTemplateParameters()->size())) {
39153915
if (CheckInstantiatedFunctionTemplateConstraints(
3916-
Info.getLocation(), Specialization, CanonicalBuilder,
3916+
Info.getLocation(), Specialization, SugaredBuilder,
39173917
Info.AssociatedConstraintsSatisfaction))
39183918
return TemplateDeductionResult::MiscellaneousDeductionFailure;
39193919

39203920
if (!Info.AssociatedConstraintsSatisfaction.IsSatisfied) {
3921-
Info.reset(Info.takeSugared(),
3922-
TemplateArgumentList::CreateCopy(Context, CanonicalBuilder));
3921+
Info.reset(TemplateArgumentList::CreateCopy(Context, SugaredBuilder),
3922+
Info.takeCanonical());
39233923
return TemplateDeductionResult::ConstraintsNotSatisfied;
39243924
}
39253925
}
@@ -4993,8 +4993,8 @@ static bool CheckDeducedPlaceholderConstraints(Sema &S, const AutoType &Type,
49934993
/*PartialTemplateArgs=*/false,
49944994
SugaredConverted, CanonicalConverted))
49954995
return true;
4996-
MultiLevelTemplateArgumentList MLTAL(Concept, CanonicalConverted,
4997-
/*Final=*/false);
4996+
MultiLevelTemplateArgumentList MLTAL(Concept, SugaredConverted,
4997+
/*Final=*/true);
49984998
// Build up an EvaluationContext with an ImplicitConceptSpecializationDecl so
49994999
// that the template arguments of the constraint can be preserved. For
50005000
// example:
@@ -5008,7 +5008,7 @@ static bool CheckDeducedPlaceholderConstraints(Sema &S, const AutoType &Type,
50085008
S, Sema::ExpressionEvaluationContext::Unevaluated,
50095009
ImplicitConceptSpecializationDecl::Create(
50105010
S.getASTContext(), Concept->getDeclContext(), Concept->getLocation(),
5011-
CanonicalConverted));
5011+
SugaredConverted));
50125012
if (S.CheckConstraintSatisfaction(Concept, {Concept->getConstraintExpr()},
50135013
MLTAL, TypeLoc.getLocalSourceRange(),
50145014
Satisfaction))

clang/lib/Serialization/ASTReaderDecl.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2391,7 +2391,7 @@ void ASTDeclReader::VisitImplicitConceptSpecializationDecl(
23912391
VisitDecl(D);
23922392
llvm::SmallVector<TemplateArgument, 4> Args;
23932393
for (unsigned I = 0; I < D->NumTemplateArgs; ++I)
2394-
Args.push_back(Record.readTemplateArgument(/*Canonicalize=*/true));
2394+
Args.push_back(Record.readTemplateArgument(/*Canonicalize=*/false));
23952395
D->setTemplateArguments(Args);
23962396
}
23972397

clang/test/AST/ast-dump-concepts.cpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,9 @@ struct Foo {
2020
// CHECK: TemplateTypeParmDecl {{.*}} referenced Concept {{.*}} 'binary_concept'
2121
// CHECK-NEXT: `-ConceptSpecializationExpr {{.*}} <col:13, col:31> 'bool' Concept {{.*}} 'binary_concept'
2222
// CHECK-NEXT: |-ImplicitConceptSpecializationDecl {{.*}} <line:13:9> col:9
23-
// CHECK-NEXT: | |-TemplateArgument type 'type-parameter-1-0'
24-
// CHECK-NEXT: | | `-TemplateTypeParmType {{.*}} 'type-parameter-1-0' dependent {{.*}}depth 1 index 0
23+
// CHECK-NEXT: | |-TemplateArgument type 'R'
24+
// CHECK-NEXT: | | `-TemplateTypeParmType {{.*}} 'R' dependent {{.*}}depth 1 index 0
25+
// CHECK-NEXT: | | `-TemplateTypeParm {{.*}} 'R'
2526
// CHECK-NEXT: | `-TemplateArgument type 'int'
2627
// CHECK-NEXT: | `-BuiltinType {{.*}} 'int'
2728
// CHECK-NEXT: |-TemplateArgument {{.*}} type 'R'
@@ -35,8 +36,9 @@ struct Foo {
3536
// CHECK: TemplateTypeParmDecl {{.*}} referenced Concept {{.*}} 'unary_concept'
3637
// CHECK-NEXT: `-ConceptSpecializationExpr {{.*}} <col:13> 'bool'
3738
// CHECK-NEXT: |-ImplicitConceptSpecializationDecl {{.*}} <line:10:9> col:9
38-
// CHECK-NEXT: | `-TemplateArgument type 'type-parameter-1-0'
39-
// CHECK-NEXT: | `-TemplateTypeParmType {{.*}} 'type-parameter-1-0' dependent {{.*}}depth 1 index 0
39+
// CHECK-NEXT: | `-TemplateArgument type 'R'
40+
// CHECK-NEXT: | `-TemplateTypeParmType {{.*}} 'R' dependent {{.*}}depth 1 index 0
41+
// CHECK-NEXT: | `-TemplateTypeParm {{.*}} 'R'
4042
template <unary_concept R>
4143
Foo(R);
4244

clang/test/CXX/expr/expr.prim/expr.prim.req/compound-requirement.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,14 @@ using r2i2 = r2<A>; // expected-error{{constraints not satisfied for class templ
3535
using r2i3 = r2<D>;
3636
using r2i4 = r2<const D>; // expected-error{{constraints not satisfied for class template 'r2' [with T = const D]}}
3737

38-
template<typename T> requires requires { { sizeof(T) }; } // expected-note{{because 'sizeof(T)' would be invalid: invalid application of 'sizeof' to an incomplete type 'void'}} expected-note{{because 'sizeof(T)' would be invalid: invalid application of 'sizeof' to an incomplete type 'nonexistent'}}
38+
template<typename T> requires requires { { sizeof(T) }; } // expected-note{{because 'sizeof(T)' would be invalid: invalid application of 'sizeof' to an incomplete type 'void'}} expected-note{{because 'sizeof(T)' would be invalid: invalid application of 'sizeof' to an incomplete type 'class nonexistent'}}
3939
struct r3 {};
4040

4141
using r3i1 = r3<int>;
4242
using r3i2 = r3<A>;
4343
using r3i3 = r3<A &>;
4444
using r3i4 = r3<void>; // expected-error{{constraints not satisfied for class template 'r3' [with T = void]}}
45-
using r3i4 = r3<class nonexistent>; // expected-error{{constraints not satisfied for class template 'r3' [with T = nonexistent]}}
45+
using r3i4 = r3<class nonexistent>; // expected-error{{constraints not satisfied for class template 'r3' [with T = class nonexistent]}}
4646

4747
// Non-dependent expressions
4848

@@ -149,7 +149,7 @@ namespace std_example {
149149
template<typename T> constexpr bool is_same_v<T, T> = true;
150150

151151
template<typename T, typename U> concept same_as = is_same_v<T, U>;
152-
// expected-note@-1 {{because 'is_same_v<int, int *>' evaluated to false}}
152+
// expected-note@-1 {{because 'is_same_v<int, typename T2::inner>' evaluated to false}}
153153

154154
static_assert(C1<int>);
155155
static_assert(C1<int*>);
@@ -173,9 +173,9 @@ namespace std_example {
173173
int operator *() { return 0; }
174174
};
175175
static_assert(C2<T1>);
176-
template<C2 T> struct C2_check {}; // expected-note{{because 'int' does not satisfy 'C2'}} expected-note{{because 'std_example::T2' does not satisfy 'C2'}}
176+
template<C2 T> struct C2_check {}; // expected-note{{because 'int' does not satisfy 'C2'}} expected-note{{because 'T2' does not satisfy 'C2'}}
177177
using c2c1 = C2_check<int>; // expected-error{{constraints not satisfied for class template 'C2_check' [with T = int]}}
178-
using c2c2 = C2_check<T2>; // expected-error{{constraints not satisfied for class template 'C2_check' [with T = std_example::T2]}}
178+
using c2c2 = C2_check<T2>; // expected-error{{constraints not satisfied for class template 'C2_check' [with T = T2]}}
179179

180180
template<typename T>
181181
void g(T t) noexcept(sizeof(T) == 1) {}

clang/test/CXX/expr/expr.prim/expr.prim.req/nested-requirement.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ using r4i = X<void>::r4<int>; // expected-error{{constraints not satisfied for c
2727

2828
// C++ [expr.prim.req.nested] Examples
2929
namespace std_example {
30-
template<typename U> concept C1 = sizeof(U) == 1; // expected-note{{because 'sizeof(int) == 1' (4 == 1) evaluated to false}}
30+
template<typename U> concept C1 = sizeof(U) == 1; // expected-note{{because 'sizeof(decltype(+t)) == 1' (4 == 1) evaluated to false}}
3131
template<typename T> concept D =
3232
requires (T t) {
3333
requires C1<decltype (+t)>; // expected-note{{because 'decltype(+t)' (aka 'int') does not satisfy 'C1'}}

clang/test/CXX/expr/expr.prim/expr.prim.req/simple-requirement.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,14 @@ using r2i4 = r2<const D>; // expected-error{{constraints not satisfied for class
3939

4040
template<typename T> requires requires { sizeof(T); }
4141
// expected-note@-1{{because 'sizeof(T)' would be invalid: invalid application of 'sizeof' to an incomplete type 'void'}}
42-
// expected-note@-2{{because 'sizeof(T)' would be invalid: invalid application of 'sizeof' to an incomplete type 'nonexistent'}}
42+
// expected-note@-2{{because 'sizeof(T)' would be invalid: invalid application of 'sizeof' to an incomplete type 'class nonexistent'}}
4343
struct r3 {};
4444

4545
using r3i1 = r3<int>;
4646
using r3i2 = r3<A>;
4747
using r3i3 = r3<A &>;
4848
using r3i4 = r3<void>; // expected-error{{constraints not satisfied for class template 'r3' [with T = void]}}
49-
using r3i4 = r3<class nonexistent>; // expected-error{{constraints not satisfied for class template 'r3' [with T = nonexistent]}}
49+
using r3i4 = r3<class nonexistent>; // expected-error{{constraints not satisfied for class template 'r3' [with T = class nonexistent]}}
5050

5151
template<typename T> requires requires (T t) { 0; "a"; (void)'a'; }
5252
struct r4 {};

clang/test/CXX/expr/expr.prim/expr.prim.req/type-requirement.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -182,14 +182,14 @@ namespace std_example {
182182
static_assert(C1<has_inner_and_type> && C2<has_inner_and_type> && C3<has_inner_and_type>);
183183
template<C1 T> struct C1_check {};
184184
// expected-note@-1 {{because 'int' does not satisfy 'C1'}}
185-
// expected-note@-2 {{because 'std_example::has_type' does not satisfy 'C1'}}
185+
// expected-note@-2 {{because 'has_type' does not satisfy 'C1'}}
186186
template<C2 T> struct C2_check {};
187-
// expected-note@-1 {{because 'std_example::has_inner' does not satisfy 'C2'}}
187+
// expected-note@-1 {{because 'has_inner' does not satisfy 'C2'}}
188188
template<C3 T> struct C3_check {};
189189
// expected-note@-1 {{because 'void' does not satisfy 'C3'}}
190190
using c1 = C1_check<int>; // expected-error{{constraints not satisfied for class template 'C1_check' [with T = int]}}
191-
using c2 = C1_check<has_type>; // expected-error{{constraints not satisfied for class template 'C1_check' [with T = std_example::has_type]}}
192-
using c3 = C2_check<has_inner>; // expected-error{{constraints not satisfied for class template 'C2_check' [with T = std_example::has_inner]}}
191+
using c2 = C1_check<has_type>; // expected-error{{constraints not satisfied for class template 'C1_check' [with T = has_type]}}
192+
using c3 = C2_check<has_inner>; // expected-error{{constraints not satisfied for class template 'C2_check' [with T = has_inner]}}
193193
using c4 = C3_check<void>; // expected-error{{constraints not satisfied for class template 'C3_check' [with T = void]}}
194194
}
195195

@@ -199,10 +199,10 @@ template <typename T> concept C = requires { requires requires { T::a; }; };
199199
// expected-note@-1 {{because 'T::a' would be invalid: no member named 'a' in 'PR48656::T1'}}
200200

201201
template <C...> struct A {};
202-
// expected-note@-1 {{because 'PR48656::T1' does not satisfy 'C'}}
202+
// expected-note@-1 {{because 'T1' does not satisfy 'C'}}
203203

204204
struct T1 {};
205-
template struct A<T1>; // expected-error {{constraints not satisfied for class template 'A' [with $0 = <PR48656::T1>]}}
205+
template struct A<T1>; // expected-error {{constraints not satisfied for class template 'A' [with $0 = <T1>]}}
206206

207207
struct T2 { static constexpr bool a = false; };
208208
template struct A<T2>;

clang/test/CXX/temp/temp.constr/temp.constr.normal/p1.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ template<typename T> requires Bar<T> && true struct S<T> { };
88

99
template<typename T> concept True2 = sizeof(T) >= 0;
1010
template<typename T> concept Foo2 = True2<T*>;
11-
// expected-error@-1{{'type name' declared as a pointer to a reference of type 'type-parameter-0-0 &'}}
11+
// expected-error@-1{{'type name' declared as a pointer to a reference of type 'T &'}}
1212
template<typename T> concept Bar2 = Foo2<T&>;
1313
// expected-note@-1{{while substituting into concept arguments here; substitution failures not allowed in concept arguments}}
1414
template<typename T> requires Bar2<T> struct S2 { };

clang/test/CXX/temp/temp.param/p10-2a.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,8 @@ concept OneOf = (is_same_v<T, Ts> || ...);
9494
// expected-note@-5 {{and 'is_same_v<short, char>' evaluated to false}}
9595
// expected-note@-6 3{{because 'is_same_v<int, char[1]>' evaluated to false}}
9696
// expected-note@-7 3{{and 'is_same_v<int, char[2]>' evaluated to false}}
97-
// expected-note@-8 2{{because 'is_same_v<std::nullptr_t, char>' evaluated to false}}
98-
// expected-note@-9 2{{and 'is_same_v<std::nullptr_t, int>' evaluated to false}}
97+
// expected-note@-8 2{{because 'is_same_v<decltype(nullptr), char>' evaluated to false}}
98+
// expected-note@-9 2{{and 'is_same_v<decltype(nullptr), int>' evaluated to false}}
9999

100100
template<OneOf<char[1], char[2]> T, OneOf<int, long, char> U>
101101
// expected-note@-1 2{{because 'OneOf<char, char[1], char[2]>' evaluated to false}}

0 commit comments

Comments
 (0)