Skip to content

Commit ba1f3db

Browse files
committed
[Concepts] Correctly form initial parameter mapping for parameter packs, support substitution into SubstNonTypeTemplateParmExpr
We previously would not correctly for the initial parameter mapping for variadic template parameters in Concepts. Testing this lead to the discovery that with the normalization process we would need to substitute into already-substituted-into template arguments, which means we need to add NonTypeTemplateParmExpr support to TemplateInstantiator. We do that by substituting into the replacement and the type separately, and then re-checking the expression against the NTTP with the new type, in order to form any new required implicit casts (for cases where the type of the NTTP was dependent).
1 parent 3a200f3 commit ba1f3db

File tree

7 files changed

+136
-26
lines changed

7 files changed

+136
-26
lines changed

clang/include/clang/Sema/Sema.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7019,7 +7019,7 @@ class Sema final {
70197019
/// Get a template argument mapping the given template parameter to itself,
70207020
/// e.g. for X in \c template<int X>, this would return an expression template
70217021
/// argument referencing X.
7022-
TemplateArgumentLoc getIdentityTemplateArgumentLoc(Decl *Param,
7022+
TemplateArgumentLoc getIdentityTemplateArgumentLoc(NamedDecl *Param,
70237023
SourceLocation Location);
70247024

70257025
void translateTemplateArguments(const ASTTemplateArgsPtr &In,

clang/include/clang/Sema/SemaConcept.h

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,15 @@ struct AtomicConstraint {
4343
if (ParameterMapping->size() != Other.ParameterMapping->size())
4444
return false;
4545

46-
for (unsigned I = 0, S = ParameterMapping->size(); I < S; ++I)
47-
if (!C.getCanonicalTemplateArgument((*ParameterMapping)[I].getArgument())
48-
.structurallyEquals(C.getCanonicalTemplateArgument(
49-
(*Other.ParameterMapping)[I].getArgument())))
46+
for (unsigned I = 0, S = ParameterMapping->size(); I < S; ++I) {
47+
llvm::FoldingSetNodeID IDA, IDB;
48+
C.getCanonicalTemplateArgument((*ParameterMapping)[I].getArgument())
49+
.Profile(IDA, C);
50+
C.getCanonicalTemplateArgument((*Other.ParameterMapping)[I].getArgument())
51+
.Profile(IDB, C);
52+
if (IDA != IDB)
5053
return false;
54+
}
5155
return true;
5256
}
5357

clang/lib/Sema/SemaConcept.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -676,6 +676,10 @@ static bool substituteParameterMappings(Sema &S, NormalizedConstraint &N,
676676
ArgsAsWritten->arguments().back().getSourceRange().getEnd()));
677677
if (S.SubstTemplateArguments(*Atomic.ParameterMapping, MLTAL, SubstArgs))
678678
return true;
679+
Atomic.ParameterMapping.emplace(
680+
MutableArrayRef<TemplateArgumentLoc>(
681+
new (S.Context) TemplateArgumentLoc[SubstArgs.size()],
682+
SubstArgs.size()));
679683
std::copy(SubstArgs.arguments().begin(), SubstArgs.arguments().end(),
680684
N.getAtomicConstraint()->ParameterMapping->begin());
681685
return false;

clang/lib/Sema/SemaTemplateDeduction.cpp

Lines changed: 4 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2488,7 +2488,7 @@ Sema::getTrivialTemplateArgumentLoc(const TemplateArgument &Arg,
24882488
case TemplateArgument::Template:
24892489
case TemplateArgument::TemplateExpansion: {
24902490
NestedNameSpecifierLocBuilder Builder;
2491-
TemplateName Template = Arg.getAsTemplate();
2491+
TemplateName Template = Arg.getAsTemplateOrTemplatePattern();
24922492
if (DependentTemplateName *DTN = Template.getAsDependentTemplateName())
24932493
Builder.MakeTrivial(Context, DTN->getQualifier(), Loc);
24942494
else if (QualifiedTemplateName *QTN =
@@ -2514,27 +2514,10 @@ Sema::getTrivialTemplateArgumentLoc(const TemplateArgument &Arg,
25142514
}
25152515

25162516
TemplateArgumentLoc
2517-
Sema::getIdentityTemplateArgumentLoc(Decl *TemplateParm,
2517+
Sema::getIdentityTemplateArgumentLoc(NamedDecl *TemplateParm,
25182518
SourceLocation Location) {
2519-
if (auto *TTP = dyn_cast<TemplateTypeParmDecl>(TemplateParm))
2520-
return getTrivialTemplateArgumentLoc(
2521-
TemplateArgument(
2522-
Context.getTemplateTypeParmType(TTP->getDepth(), TTP->getIndex(),
2523-
TTP->isParameterPack(), TTP)),
2524-
QualType(), Location.isValid() ? Location : TTP->getLocation());
2525-
else if (auto *TTP = dyn_cast<TemplateTemplateParmDecl>(TemplateParm))
2526-
return getTrivialTemplateArgumentLoc(TemplateArgument(TemplateName(TTP)),
2527-
QualType(),
2528-
Location.isValid() ? Location :
2529-
TTP->getLocation());
2530-
auto *NTTP = cast<NonTypeTemplateParmDecl>(TemplateParm);
2531-
CXXScopeSpec SS;
2532-
DeclarationNameInfo Info(NTTP->getDeclName(),
2533-
Location.isValid() ? Location : NTTP->getLocation());
2534-
Expr *E = BuildDeclarationNameExpr(SS, Info, NTTP).get();
2535-
return getTrivialTemplateArgumentLoc(TemplateArgument(E), NTTP->getType(),
2536-
Location.isValid() ? Location :
2537-
NTTP->getLocation());
2519+
return getTrivialTemplateArgumentLoc(
2520+
Context.getInjectedTemplateArg(TemplateParm), QualType(), Location);
25382521
}
25392522

25402523
/// Convert the given deduced template argument and add it to the set of

clang/lib/Sema/SemaTemplateInstantiate.cpp

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1057,6 +1057,8 @@ namespace {
10571057
NonTypeTemplateParmDecl *D);
10581058
ExprResult TransformSubstNonTypeTemplateParmPackExpr(
10591059
SubstNonTypeTemplateParmPackExpr *E);
1060+
ExprResult TransformSubstNonTypeTemplateParmExpr(
1061+
SubstNonTypeTemplateParmExpr *E);
10601062

10611063
/// Rebuild a DeclRefExpr for a VarDecl reference.
10621064
ExprResult RebuildVarDeclRefExpr(VarDecl *PD, SourceLocation Loc);
@@ -1535,6 +1537,44 @@ TemplateInstantiator::TransformSubstNonTypeTemplateParmPackExpr(
15351537
Arg);
15361538
}
15371539

1540+
ExprResult
1541+
TemplateInstantiator::TransformSubstNonTypeTemplateParmExpr(
1542+
SubstNonTypeTemplateParmExpr *E) {
1543+
ExprResult SubstReplacement = TransformExpr(E->getReplacement());
1544+
if (SubstReplacement.isInvalid())
1545+
return true;
1546+
QualType SubstType = TransformType(E->getType());
1547+
if (SubstType.isNull())
1548+
return true;
1549+
// The type may have been previously dependent and not now, which means we
1550+
// might have to implicit cast the argument to the new type, for example:
1551+
// template<auto T, decltype(T) U>
1552+
// concept C = sizeof(U) == 4;
1553+
// void foo() requires C<2, 'a'> { }
1554+
// When normalizing foo(), we first form the normalized constraints of C:
1555+
// AtomicExpr(sizeof(U) == 4,
1556+
// U=SubstNonTypeTemplateParmExpr(Param=U,
1557+
// Expr=DeclRef(U),
1558+
// Type=decltype(T)))
1559+
// Then we substitute T = 2, U = 'a' into the parameter mapping, and need to
1560+
// produce:
1561+
// AtomicExpr(sizeof(U) == 4,
1562+
// U=SubstNonTypeTemplateParmExpr(Param=U,
1563+
// Expr=ImpCast(
1564+
// decltype(2),
1565+
// SubstNTTPE(Param=U, Expr='a',
1566+
// Type=char)),
1567+
// Type=decltype(2)))
1568+
// The call to CheckTemplateArgument here produces the ImpCast.
1569+
TemplateArgument Converted;
1570+
if (SemaRef.CheckTemplateArgument(E->getParameter(), SubstType,
1571+
SubstReplacement.get(),
1572+
Converted).isInvalid())
1573+
return true;
1574+
return transformNonTypeTemplateParmRef(E->getParameter(),
1575+
E->getExprLoc(), Converted);
1576+
}
1577+
15381578
ExprResult TemplateInstantiator::RebuildVarDeclRefExpr(VarDecl *PD,
15391579
SourceLocation Loc) {
15401580
DeclarationNameInfo NameInfo(PD->getDeclName(), Loc);

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

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,54 @@ template<typename T> requires Bar2<T> struct S2 { };
1616
template<typename T> requires Bar2<T> && true struct S2<T> { };
1717
// expected-error@-1{{class template partial specialization is not more specialized than the primary template}}
1818
// expected-note@-2{{while calculating associated constraint of template 'S2' here}}
19+
20+
namespace type_pack {
21+
template<typename... Args>
22+
concept C1 = ((sizeof(Args) >= 0) && ...);
23+
24+
template<typename A, typename... B>
25+
concept C2 = C1<A, B...>;
26+
27+
template<typename T>
28+
constexpr void foo() requires C2<T, char, T> { }
29+
30+
template<typename T>
31+
constexpr void foo() requires C1<T, char, T> && true { }
32+
33+
static_assert((foo<int>(), true));
34+
}
35+
36+
namespace template_pack {
37+
template<typename T> struct S1 {};
38+
template<typename T> struct S2 {};
39+
40+
template<template<typename> typename... Args>
41+
concept C1 = ((sizeof(Args<int>) >= 0) && ...);
42+
43+
template<template<typename> typename A, template<typename> typename... B>
44+
concept C2 = C1<A, B...>;
45+
46+
template<template<typename> typename T>
47+
constexpr void foo() requires C2<T, S1, T> { }
48+
49+
template<template<typename> typename T>
50+
constexpr void foo() requires C1<T, S1, T> && true { }
51+
52+
static_assert((foo<S2>(), true));
53+
}
54+
55+
namespace non_type_pack {
56+
template<int... Args>
57+
concept C1 = ((Args >= 0) && ...);
58+
59+
template<int A, int... B>
60+
concept C2 = C1<A, B...>;
61+
62+
template<int T>
63+
constexpr void foo() requires C2<T, 2, T> { }
64+
65+
template<int T>
66+
constexpr void foo() requires C1<T, 2, T> && true { }
67+
68+
static_assert((foo<1>(), true));
69+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// RUN: %clang_cc1 -std=c++2a -x c++ %s -verify
2+
3+
template<auto T, decltype(T) U>
4+
concept C1 = sizeof(U) >= 4;
5+
// sizeof(U) >= 4 [U = U (decltype(T))]
6+
7+
template<typename Y, char V>
8+
concept C2 = C1<Y{}, V>;
9+
// sizeof(U) >= 4 [U = V (decltype(Y{}))]
10+
11+
template<char W>
12+
constexpr int foo() requires C2<int, W> { return 1; }
13+
// sizeof(U) >= 4 [U = W (decltype(int{}))]
14+
15+
template<char X>
16+
// expected-note@+1{{candidate function}}
17+
constexpr int foo() requires C1<1, X> && true { return 2; }
18+
// sizeof(U) >= 4 [U = X (decltype(1))]
19+
20+
static_assert(foo<'a'>() == 2);
21+
22+
template<char Z>
23+
// expected-note@+1{{candidate function}}
24+
constexpr int foo() requires C2<long long, Z> && true { return 3; }
25+
// sizeof(U) >= 4 [U = Z (decltype(long long{}))]
26+
27+
static_assert(foo<'a'>() == 3);
28+
// expected-error@-1{{call to 'foo' is ambiguous}}

0 commit comments

Comments
 (0)