Skip to content

Commit

Permalink
Teach TreeTransform to substitute into resolved TemplateArguments.
Browse files Browse the repository at this point in the history
This comes up when substituting into an already-substituted template
argument during constraint satisfaction checking.
  • Loading branch information
zygoloid committed Mar 19, 2020
1 parent 0e9368c commit b20ab41
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 47 deletions.
85 changes: 38 additions & 47 deletions clang/lib/Sema/TreeTransform.h
Original file line number Diff line number Diff line change
Expand Up @@ -4057,50 +4057,8 @@ template<typename Derived>
void TreeTransform<Derived>::InventTemplateArgumentLoc(
const TemplateArgument &Arg,
TemplateArgumentLoc &Output) {
SourceLocation Loc = getDerived().getBaseLocation();
switch (Arg.getKind()) {
case TemplateArgument::Null:
llvm_unreachable("null template argument in TreeTransform");
break;

case TemplateArgument::Type:
Output = TemplateArgumentLoc(Arg,
SemaRef.Context.getTrivialTypeSourceInfo(Arg.getAsType(), Loc));

break;

case TemplateArgument::Template:
case TemplateArgument::TemplateExpansion: {
NestedNameSpecifierLocBuilder Builder;
TemplateName Template = Arg.getAsTemplateOrTemplatePattern();
if (DependentTemplateName *DTN = Template.getAsDependentTemplateName())
Builder.MakeTrivial(SemaRef.Context, DTN->getQualifier(), Loc);
else if (QualifiedTemplateName *QTN = Template.getAsQualifiedTemplateName())
Builder.MakeTrivial(SemaRef.Context, QTN->getQualifier(), Loc);

if (Arg.getKind() == TemplateArgument::Template)
Output = TemplateArgumentLoc(Arg,
Builder.getWithLocInContext(SemaRef.Context),
Loc);
else
Output = TemplateArgumentLoc(Arg,
Builder.getWithLocInContext(SemaRef.Context),
Loc, Loc);

break;
}

case TemplateArgument::Expression:
Output = TemplateArgumentLoc(Arg, Arg.getAsExpr());
break;

case TemplateArgument::Declaration:
case TemplateArgument::Integral:
case TemplateArgument::Pack:
case TemplateArgument::NullPtr:
Output = TemplateArgumentLoc(Arg, TemplateArgumentLocInfo());
break;
}
Output = getSema().getTrivialTemplateArgumentLoc(
Arg, QualType(), getDerived().getBaseLocation());
}

template<typename Derived>
Expand All @@ -4110,12 +4068,45 @@ bool TreeTransform<Derived>::TransformTemplateArgument(
const TemplateArgument &Arg = Input.getArgument();
switch (Arg.getKind()) {
case TemplateArgument::Null:
case TemplateArgument::Integral:
case TemplateArgument::Pack:
case TemplateArgument::Declaration:
case TemplateArgument::NullPtr:
llvm_unreachable("Unexpected TemplateArgument");

case TemplateArgument::Integral:
case TemplateArgument::NullPtr:
case TemplateArgument::Declaration: {
// Transform a resolved template argument straight to a resolved template
// argument. We get here when substituting into an already-substituted
// template type argument during concept satisfaction checking.
QualType T = Arg.getNonTypeTemplateArgumentType();
QualType NewT = getDerived().TransformType(T);
if (NewT.isNull())
return true;

ValueDecl *D = Arg.getKind() == TemplateArgument::Declaration
? Arg.getAsDecl()
: nullptr;
ValueDecl *NewD = D ? cast_or_null<ValueDecl>(getDerived().TransformDecl(
getDerived().getBaseLocation(), D))
: nullptr;
if (D && !NewD)
return true;

if (NewT == T && D == NewD)
Output = Input;
else if (Arg.getKind() == TemplateArgument::Integral)
Output = TemplateArgumentLoc(
TemplateArgument(getSema().Context, Arg.getAsIntegral(), NewT),
TemplateArgumentLocInfo());
else if (Arg.getKind() == TemplateArgument::NullPtr)
Output = TemplateArgumentLoc(TemplateArgument(NewT, /*IsNullPtr=*/true),
TemplateArgumentLocInfo());
else
Output = TemplateArgumentLoc(TemplateArgument(NewD, NewT),
TemplateArgumentLocInfo());

return false;
}

case TemplateArgument::Type: {
TypeSourceInfo *DI = Input.getTypeSourceInfo();
if (!DI)
Expand Down
34 changes: 34 additions & 0 deletions clang/test/SemaTemplate/subst-into-subst.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// RUN: %clang_cc1 -std=c++2a -verify %s

// When forming and checking satisfaction of atomic constraints, we will
// substitute still-dependent template arguments into an expression, and later
// substitute into the result. This creates some unique situations; check that
// they work.

namespace SubstIntoResolvedTypeTemplateArg {
template<int, class> struct X {};

template<class T> concept A = true;
template<class T> concept B = sizeof(T) != 0;
template<class T> concept C = B<X<1, T>>;

int f(A auto); // expected-note {{candidate}}
int f(C auto); // expected-note {{candidate}}
int k1 = f(0); // expected-error {{ambiguous}}

template<class T> concept D = A<T> && B<X<1, T>>;
int f(D auto);
int k2 = f(0); // ok

// The atomic constraint formed from B<X<(int)'\1', T>> is identical to the
// one formed from C, even though the template arguments are written as
// different expressions; the "equivalent" rules are used rather than the
// "identical" rules when matching template arguments in concept-ids.
template<class T> concept E = A<T> && B<X<(int)'\1', T>>;
int g(C auto);
int g(E auto); // expected-note {{candidate}}
int k3 = g(0);

int g(D auto); // expected-note {{candidate}}
int k4 = g(0); // expected-error {{ambiguous}}
}

0 comments on commit b20ab41

Please sign in to comment.