Skip to content

Commit

Permalink
[Concepts] Instantiate invented template type parameter type-constrai…
Browse files Browse the repository at this point in the history
…nt along with function parameters

We previously instantiated type-constraints of template type parameters along with the type parameter itself,
this caused problems when the type-constraints created by abbreviated templates refreneced other parameters
in the abbreviated templates.

When encountering a template type parameter with a type constraint, if it is implicit, delay instantiation of
the type-constraint until the function parameter which created the invented template type parameter is
instantiated.

Reland after fixing bug caused by another flow reaching SubstParmVarDecl and instantiating the TypeConstraint
a second time.

(cherry picked from commit 84959ae)
  • Loading branch information
saarraz committed Feb 3, 2020
1 parent c822edc commit 1ac1c4b
Show file tree
Hide file tree
Showing 3 changed files with 184 additions and 20 deletions.
129 changes: 129 additions & 0 deletions clang/lib/Sema/SemaTemplateInstantiate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "clang/AST/DeclTemplate.h"
#include "clang/AST/Expr.h"
#include "clang/AST/PrettyDeclStackTrace.h"
#include "clang/AST/TypeVisitor.h"
#include "clang/Basic/LangOptions.h"
#include "clang/Basic/Stack.h"
#include "clang/Sema/DeclSpec.h"
Expand Down Expand Up @@ -2145,6 +2146,94 @@ void Sema::SubstExceptionSpec(FunctionDecl *New, const FunctionProtoType *Proto,
UpdateExceptionSpec(New, ESI);
}

namespace {

struct GetContainedInventedTypeParmVisitor :
public TypeVisitor<GetContainedInventedTypeParmVisitor,
TemplateTypeParmDecl *> {
using TypeVisitor<GetContainedInventedTypeParmVisitor,
TemplateTypeParmDecl *>::Visit;

TemplateTypeParmDecl *Visit(QualType T) {
if (T.isNull())
return nullptr;
return Visit(T.getTypePtr());
}
// The deduced type itself.
TemplateTypeParmDecl *VisitTemplateTypeParmType(
const TemplateTypeParmType *T) {
if (!T->getDecl()->isImplicit())
return nullptr;
return T->getDecl();
}

// Only these types can contain 'auto' types, and subsequently be replaced
// by references to invented parameters.

TemplateTypeParmDecl *VisitElaboratedType(const ElaboratedType *T) {
return Visit(T->getNamedType());
}

TemplateTypeParmDecl *VisitPointerType(const PointerType *T) {
return Visit(T->getPointeeType());
}

TemplateTypeParmDecl *VisitBlockPointerType(const BlockPointerType *T) {
return Visit(T->getPointeeType());
}

TemplateTypeParmDecl *VisitReferenceType(const ReferenceType *T) {
return Visit(T->getPointeeTypeAsWritten());
}

TemplateTypeParmDecl *VisitMemberPointerType(const MemberPointerType *T) {
return Visit(T->getPointeeType());
}

TemplateTypeParmDecl *VisitArrayType(const ArrayType *T) {
return Visit(T->getElementType());
}

TemplateTypeParmDecl *VisitDependentSizedExtVectorType(
const DependentSizedExtVectorType *T) {
return Visit(T->getElementType());
}

TemplateTypeParmDecl *VisitVectorType(const VectorType *T) {
return Visit(T->getElementType());
}

TemplateTypeParmDecl *VisitFunctionProtoType(const FunctionProtoType *T) {
return VisitFunctionType(T);
}

TemplateTypeParmDecl *VisitFunctionType(const FunctionType *T) {
return Visit(T->getReturnType());
}

TemplateTypeParmDecl *VisitParenType(const ParenType *T) {
return Visit(T->getInnerType());
}

TemplateTypeParmDecl *VisitAttributedType(const AttributedType *T) {
return Visit(T->getModifiedType());
}

TemplateTypeParmDecl *VisitMacroQualifiedType(const MacroQualifiedType *T) {
return Visit(T->getUnderlyingType());
}

TemplateTypeParmDecl *VisitAdjustedType(const AdjustedType *T) {
return Visit(T->getOriginalType());
}

TemplateTypeParmDecl *VisitPackExpansionType(const PackExpansionType *T) {
return Visit(T->getPattern());
}
};

} // namespace

ParmVarDecl *Sema::SubstParmVarDecl(ParmVarDecl *OldParm,
const MultiLevelTemplateArgumentList &TemplateArgs,
int indexAdjustment,
Expand Down Expand Up @@ -2192,6 +2281,46 @@ ParmVarDecl *Sema::SubstParmVarDecl(ParmVarDecl *OldParm,
return nullptr;
}

// In abbreviated templates, TemplateTypeParmDecls with possible
// TypeConstraints are created when the parameter list is originally parsed.
// The TypeConstraints can therefore reference other functions parameters in
// the abbreviated function template, which is why we must instantiate them
// here, when the instantiated versions of those referenced parameters are in
// scope.
if (TemplateTypeParmDecl *TTP =
GetContainedInventedTypeParmVisitor().Visit(OldDI->getType())) {
if (const TypeConstraint *TC = TTP->getTypeConstraint()) {
auto *Inst = cast<TemplateTypeParmDecl>(
FindInstantiatedDecl(TTP->getLocation(), TTP, TemplateArgs));
// We will first get here when instantiating the abbreviated function
// template's described function, but we might also get here later.
// Make sure we do not instantiate the TypeConstraint more than once.
if (Inst && !Inst->hasTypeConstraint()) {
// TODO: Concepts: do not instantiate the constraint (delayed constraint
// substitution)
const ASTTemplateArgumentListInfo *TemplArgInfo
= TC->getTemplateArgsAsWritten();
TemplateArgumentListInfo InstArgs;

if (TemplArgInfo) {
InstArgs.setLAngleLoc(TemplArgInfo->LAngleLoc);
InstArgs.setRAngleLoc(TemplArgInfo->RAngleLoc);
if (Subst(TemplArgInfo->getTemplateArgs(),
TemplArgInfo->NumTemplateArgs, InstArgs, TemplateArgs))
return nullptr;
}
if (AttachTypeConstraint(
TC->getNestedNameSpecifierLoc(), TC->getConceptNameInfo(),
TC->getNamedConcept(), &InstArgs, Inst,
TTP->isParameterPack()
? cast<CXXFoldExpr>(TC->getImmediatelyDeclaredConstraint())
->getEllipsisLoc()
: SourceLocation()))
return nullptr;
}
}
}

ParmVarDecl *NewParm = CheckParameter(Context.getTranslationUnitDecl(),
OldParm->getInnerLocStart(),
OldParm->getLocation(),
Expand Down
46 changes: 26 additions & 20 deletions clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2525,28 +2525,34 @@ Decl *TemplateDeclInstantiator::VisitTemplateTypeParmDecl(
Inst->setAccess(AS_public);
Inst->setImplicit(D->isImplicit());
if (auto *TC = D->getTypeConstraint()) {
// TODO: Concepts: do not instantiate the constraint (delayed constraint
// substitution)
const ASTTemplateArgumentListInfo *TemplArgInfo
= TC->getTemplateArgsAsWritten();
TemplateArgumentListInfo InstArgs;

if (TemplArgInfo) {
InstArgs.setLAngleLoc(TemplArgInfo->LAngleLoc);
InstArgs.setRAngleLoc(TemplArgInfo->RAngleLoc);
if (SemaRef.Subst(TemplArgInfo->getTemplateArgs(),
TemplArgInfo->NumTemplateArgs,
InstArgs, TemplateArgs))
if (!D->isImplicit()) {
// Invented template parameter type constraints will be instantiated with
// the corresponding auto-typed parameter as it might reference other
// parameters.

// TODO: Concepts: do not instantiate the constraint (delayed constraint
// substitution)
const ASTTemplateArgumentListInfo *TemplArgInfo
= TC->getTemplateArgsAsWritten();
TemplateArgumentListInfo InstArgs;

if (TemplArgInfo) {
InstArgs.setLAngleLoc(TemplArgInfo->LAngleLoc);
InstArgs.setRAngleLoc(TemplArgInfo->RAngleLoc);
if (SemaRef.Subst(TemplArgInfo->getTemplateArgs(),
TemplArgInfo->NumTemplateArgs,
InstArgs, TemplateArgs))
return nullptr;
}
if (SemaRef.AttachTypeConstraint(
TC->getNestedNameSpecifierLoc(), TC->getConceptNameInfo(),
TC->getNamedConcept(), &InstArgs, Inst,
D->isParameterPack()
? cast<CXXFoldExpr>(TC->getImmediatelyDeclaredConstraint())
->getEllipsisLoc()
: SourceLocation()))
return nullptr;
}
if (SemaRef.AttachTypeConstraint(
TC->getNestedNameSpecifierLoc(), TC->getConceptNameInfo(),
TC->getNamedConcept(), &InstArgs, Inst,
D->isParameterPack()
? cast<CXXFoldExpr>(TC->getImmediatelyDeclaredConstraint())
->getEllipsisLoc()
: SourceLocation()))
return nullptr;
}
if (D->hasDefaultArgument() && !D->defaultArgumentWasInherited()) {
TypeSourceInfo *InstantiatedDefaultArg =
Expand Down
29 changes: 29 additions & 0 deletions clang/test/SemaTemplate/instantiate-abbreviated-template.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// RUN: %clang_cc1 -std=c++2a -x c++ %s -verify
// expected-no-diagnostics

template<typename...>
concept C = true;

template<typename T>
struct S {
template<typename U>
static void foo1(U a, auto b);
static void foo2(T a, C<T> auto b);
static void foo3(T a, C<decltype(a)> auto b);
static void foo4(T a, C<decltype(a)> auto b, const C<decltype(b)> auto &&c);
};

using sf1 = decltype(S<int>::foo1(1, 2));
using sf2 = decltype(S<int>::foo2(1, 2));
using sf3 = decltype(S<int>::foo3(1, 2));
using sf4 = decltype(S<int>::foo4(1, 2, 3));


template<typename... T>
struct G {
static void foo1(auto a, const C<decltype(a)> auto &&... b);
static void foo2(auto a, const C<decltype(a), T> auto &&... b);
};

using gf1 = decltype(G<int, char>::foo1('a', 1, 2, 3, 4));
using gf2 = decltype(G<int, char>::foo2('a', 1, 2));

0 comments on commit 1ac1c4b

Please sign in to comment.