Skip to content

Commit

Permalink
Improve modeling of variable template specializations with dependent
Browse files Browse the repository at this point in the history
arguments.

Don't build a variable template specialization declaration until its
scope and template arguments are non-dependent.

No functionality change intended, but the AST representation is now more
consistent with how we model other templates.
  • Loading branch information
zygoloid committed Aug 10, 2020
1 parent dee812a commit 6170072
Show file tree
Hide file tree
Showing 5 changed files with 122 additions and 144 deletions.
2 changes: 2 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Expand Up @@ -4957,6 +4957,8 @@ def err_mismatched_exception_spec_explicit_instantiation : Error<
def ext_mismatched_exception_spec_explicit_instantiation : ExtWarn<
err_mismatched_exception_spec_explicit_instantiation.Text>,
InGroup<MicrosoftExceptionSpec>;
def err_explicit_instantiation_dependent : Error<
"explicit instantiation has dependent template arguments">;

// C++ typename-specifiers
def err_typename_nested_not_found : Error<"no type named %0 in %1">;
Expand Down
10 changes: 6 additions & 4 deletions clang/include/clang/Sema/Sema.h
Expand Up @@ -7342,11 +7342,17 @@ class Sema final {
SourceLocation TemplateKWLoc, TemplateParameterList *TemplateParams,
StorageClass SC, bool IsPartialSpecialization);

/// Get the specialization of the given variable template corresponding to
/// the specified argument list, or a null-but-valid result if the arguments
/// are dependent.
DeclResult CheckVarTemplateId(VarTemplateDecl *Template,
SourceLocation TemplateLoc,
SourceLocation TemplateNameLoc,
const TemplateArgumentListInfo &TemplateArgs);

/// Form a reference to the specialization of the given variable template
/// corresponding to the specified argument list, or a null-but-valid result
/// if the arguments are dependent.
ExprResult CheckVarTemplateId(const CXXScopeSpec &SS,
const DeclarationNameInfo &NameInfo,
VarTemplateDecl *Template,
Expand Down Expand Up @@ -9169,10 +9175,6 @@ class Sema final {
bool InstantiatingVarTemplate = false,
VarTemplateSpecializationDecl *PrevVTSD = nullptr);

VarDecl *getVarTemplateSpecialization(
VarTemplateDecl *VarTempl, const TemplateArgumentListInfo *TemplateArgs,
const DeclarationNameInfo &MemberNameInfo, SourceLocation TemplateKWLoc);

void InstantiateVariableInitializer(
VarDecl *Var, VarDecl *OldVar,
const MultiLevelTemplateArgumentList &TemplateArgs);
Expand Down
73 changes: 30 additions & 43 deletions clang/lib/Sema/SemaExprMember.cpp
Expand Up @@ -944,28 +944,6 @@ static bool IsInFnTryBlockHandler(const Scope *S) {
return false;
}

VarDecl *
Sema::getVarTemplateSpecialization(VarTemplateDecl *VarTempl,
const TemplateArgumentListInfo *TemplateArgs,
const DeclarationNameInfo &MemberNameInfo,
SourceLocation TemplateKWLoc) {
if (!TemplateArgs) {
diagnoseMissingTemplateArguments(TemplateName(VarTempl),
MemberNameInfo.getBeginLoc());
return nullptr;
}

DeclResult VDecl = CheckVarTemplateId(VarTempl, TemplateKWLoc,
MemberNameInfo.getLoc(), *TemplateArgs);
if (VDecl.isInvalid())
return nullptr;
VarDecl *Var = cast<VarDecl>(VDecl.get());
if (!Var->getTemplateSpecializationKind())
Var->setTemplateSpecializationKind(TSK_ImplicitInstantiation,
MemberNameInfo.getLoc());
return Var;
}

ExprResult
Sema::BuildMemberReferenceExpr(Expr *BaseExpr, QualType BaseExprType,
SourceLocation OpLoc, bool IsArrow,
Expand Down Expand Up @@ -1097,19 +1075,11 @@ Sema::BuildMemberReferenceExpr(Expr *BaseExpr, QualType BaseExprType,
if (!BaseExpr) {
// If this is not an instance member, convert to a non-member access.
if (!MemberDecl->isCXXInstanceMember()) {
// If this is a variable template, get the instantiated variable
// declaration corresponding to the supplied template arguments
// (while emitting diagnostics as necessary) that will be referenced
// by this expression.
assert((!TemplateArgs || isa<VarTemplateDecl>(MemberDecl)) &&
"How did we get template arguments here sans a variable template");
if (isa<VarTemplateDecl>(MemberDecl)) {
MemberDecl = getVarTemplateSpecialization(
cast<VarTemplateDecl>(MemberDecl), TemplateArgs,
R.getLookupNameInfo(), TemplateKWLoc);
if (!MemberDecl)
return ExprError();
}
// We might have a variable template specialization (or maybe one day a
// member concept-id).
if (TemplateArgs || TemplateKWLoc.isValid())
return BuildTemplateIdExpr(SS, TemplateKWLoc, R, /*ADL*/false, TemplateArgs);

return BuildDeclarationNameExpr(SS, R.getLookupNameInfo(), MemberDecl,
FoundDecl, TemplateArgs);
}
Expand Down Expand Up @@ -1168,14 +1138,32 @@ Sema::BuildMemberReferenceExpr(Expr *BaseExpr, QualType BaseExprType,
MemberNameInfo, Enum->getType(), VK_RValue,
OK_Ordinary);
}

if (VarTemplateDecl *VarTempl = dyn_cast<VarTemplateDecl>(MemberDecl)) {
if (VarDecl *Var = getVarTemplateSpecialization(
VarTempl, TemplateArgs, MemberNameInfo, TemplateKWLoc))
return BuildMemberExpr(
BaseExpr, IsArrow, OpLoc, &SS, TemplateKWLoc, Var, FoundDecl,
/*HadMultipleCandidates=*/false, MemberNameInfo,
Var->getType().getNonReferenceType(), VK_LValue, OK_Ordinary);
return ExprError();
if (!TemplateArgs) {
diagnoseMissingTemplateArguments(TemplateName(VarTempl), MemberLoc);
return ExprError();
}

DeclResult VDecl = CheckVarTemplateId(VarTempl, TemplateKWLoc,
MemberNameInfo.getLoc(), *TemplateArgs);
if (VDecl.isInvalid())
return ExprError();

// Non-dependent member, but dependent template arguments.
if (!VDecl.get())
return ActOnDependentMemberExpr(
BaseExpr, BaseExpr->getType(), IsArrow, OpLoc, SS, TemplateKWLoc,
FirstQualifierInScope, MemberNameInfo, TemplateArgs);

VarDecl *Var = cast<VarDecl>(VDecl.get());
if (!Var->getTemplateSpecializationKind())
Var->setTemplateSpecializationKind(TSK_ImplicitInstantiation, MemberLoc);

return BuildMemberExpr(
BaseExpr, IsArrow, OpLoc, &SS, TemplateKWLoc, Var, FoundDecl,
/*HadMultipleCandidates=*/false, MemberNameInfo,
Var->getType().getNonReferenceType(), VK_LValue, OK_Ordinary);
}

// We found something that we didn't expect. Complain.
Expand Down Expand Up @@ -1852,7 +1840,6 @@ Sema::BuildImplicitMemberExpr(const CXXScopeSpec &SS,

// If this is known to be an instance access, go ahead and build an
// implicit 'this' expression now.
// 'this' expression now.
QualType ThisTy = getCurrentThisType();
assert(!ThisTy.isNull() && "didn't correctly pre-flight capture of 'this'");

Expand Down
161 changes: 83 additions & 78 deletions clang/lib/Sema/SemaTemplate.cpp
Expand Up @@ -4362,6 +4362,13 @@ Sema::CheckVarTemplateId(VarTemplateDecl *Template, SourceLocation TemplateLoc,
Converted, /*UpdateArgsWithConversion=*/true))
return true;

// Produce a placeholder value if the specialization is dependent.
bool InstantiationDependent = false;
if (Template->getDeclContext()->isDependentContext() ||
TemplateSpecializationType::anyDependentTemplateArguments(
TemplateArgs, InstantiationDependent))
return DeclResult();

// Find the variable template specialization declaration that
// corresponds to these arguments.
void *InsertPos = nullptr;
Expand Down Expand Up @@ -4389,84 +4396,75 @@ Sema::CheckVarTemplateId(VarTemplateDecl *Template, SourceLocation TemplateLoc,

// 1. Attempt to find the closest partial specialization that this
// specializes, if any.
// If any of the template arguments is dependent, then this is probably
// a placeholder for an incomplete declarative context; which must be
// complete by instantiation time. Thus, do not search through the partial
// specializations yet.
// TODO: Unify with InstantiateClassTemplateSpecialization()?
// Perhaps better after unification of DeduceTemplateArguments() and
// getMoreSpecializedPartialSpecialization().
bool InstantiationDependent = false;
if (!TemplateSpecializationType::anyDependentTemplateArguments(
TemplateArgs, InstantiationDependent)) {

SmallVector<VarTemplatePartialSpecializationDecl *, 4> PartialSpecs;
Template->getPartialSpecializations(PartialSpecs);
SmallVector<VarTemplatePartialSpecializationDecl *, 4> PartialSpecs;
Template->getPartialSpecializations(PartialSpecs);

for (unsigned I = 0, N = PartialSpecs.size(); I != N; ++I) {
VarTemplatePartialSpecializationDecl *Partial = PartialSpecs[I];
TemplateDeductionInfo Info(FailedCandidates.getLocation());
for (unsigned I = 0, N = PartialSpecs.size(); I != N; ++I) {
VarTemplatePartialSpecializationDecl *Partial = PartialSpecs[I];
TemplateDeductionInfo Info(FailedCandidates.getLocation());

if (TemplateDeductionResult Result =
DeduceTemplateArguments(Partial, TemplateArgList, Info)) {
// Store the failed-deduction information for use in diagnostics, later.
// TODO: Actually use the failed-deduction info?
FailedCandidates.addCandidate().set(
DeclAccessPair::make(Template, AS_public), Partial,
MakeDeductionFailureInfo(Context, Result, Info));
(void)Result;
} else {
Matched.push_back(PartialSpecMatchResult());
Matched.back().Partial = Partial;
Matched.back().Args = Info.take();
}
if (TemplateDeductionResult Result =
DeduceTemplateArguments(Partial, TemplateArgList, Info)) {
// Store the failed-deduction information for use in diagnostics, later.
// TODO: Actually use the failed-deduction info?
FailedCandidates.addCandidate().set(
DeclAccessPair::make(Template, AS_public), Partial,
MakeDeductionFailureInfo(Context, Result, Info));
(void)Result;
} else {
Matched.push_back(PartialSpecMatchResult());
Matched.back().Partial = Partial;
Matched.back().Args = Info.take();
}
}

if (Matched.size() >= 1) {
SmallVector<MatchResult, 4>::iterator Best = Matched.begin();
if (Matched.size() == 1) {
// -- If exactly one matching specialization is found, the
// instantiation is generated from that specialization.
// We don't need to do anything for this.
} else {
// -- If more than one matching specialization is found, the
// partial order rules (14.5.4.2) are used to determine
// whether one of the specializations is more specialized
// than the others. If none of the specializations is more
// specialized than all of the other matching
// specializations, then the use of the variable template is
// ambiguous and the program is ill-formed.
for (SmallVector<MatchResult, 4>::iterator P = Best + 1,
PEnd = Matched.end();
P != PEnd; ++P) {
if (getMoreSpecializedPartialSpecialization(P->Partial, Best->Partial,
PointOfInstantiation) ==
P->Partial)
Best = P;
}
if (Matched.size() >= 1) {
SmallVector<MatchResult, 4>::iterator Best = Matched.begin();
if (Matched.size() == 1) {
// -- If exactly one matching specialization is found, the
// instantiation is generated from that specialization.
// We don't need to do anything for this.
} else {
// -- If more than one matching specialization is found, the
// partial order rules (14.5.4.2) are used to determine
// whether one of the specializations is more specialized
// than the others. If none of the specializations is more
// specialized than all of the other matching
// specializations, then the use of the variable template is
// ambiguous and the program is ill-formed.
for (SmallVector<MatchResult, 4>::iterator P = Best + 1,
PEnd = Matched.end();
P != PEnd; ++P) {
if (getMoreSpecializedPartialSpecialization(P->Partial, Best->Partial,
PointOfInstantiation) ==
P->Partial)
Best = P;
}

// Determine if the best partial specialization is more specialized than
// the others.
for (SmallVector<MatchResult, 4>::iterator P = Matched.begin(),
PEnd = Matched.end();
P != PEnd; ++P) {
if (P != Best && getMoreSpecializedPartialSpecialization(
P->Partial, Best->Partial,
PointOfInstantiation) != Best->Partial) {
AmbiguousPartialSpec = true;
break;
}
// Determine if the best partial specialization is more specialized than
// the others.
for (SmallVector<MatchResult, 4>::iterator P = Matched.begin(),
PEnd = Matched.end();
P != PEnd; ++P) {
if (P != Best && getMoreSpecializedPartialSpecialization(
P->Partial, Best->Partial,
PointOfInstantiation) != Best->Partial) {
AmbiguousPartialSpec = true;
break;
}
}

// Instantiate using the best variable template partial specialization.
InstantiationPattern = Best->Partial;
InstantiationArgs = Best->Args;
} else {
// -- If no match is found, the instantiation is generated
// from the primary template.
// InstantiationPattern = Template->getTemplatedDecl();
}

// Instantiate using the best variable template partial specialization.
InstantiationPattern = Best->Partial;
InstantiationArgs = Best->Args;
} else {
// -- If no match is found, the instantiation is generated
// from the primary template.
// InstantiationPattern = Template->getTemplatedDecl();
}

// 2. Create the canonical declaration.
Expand Down Expand Up @@ -4514,6 +4512,9 @@ Sema::CheckVarTemplateId(const CXXScopeSpec &SS,
if (Decl.isInvalid())
return ExprError();

if (!Decl.get())
return ExprResult();

VarDecl *Var = cast<VarDecl>(Decl.get());
if (!Var->getTemplateSpecializationKind())
Var->setTemplateSpecializationKind(TSK_ImplicitInstantiation,
Expand Down Expand Up @@ -4601,18 +4602,14 @@ ExprResult Sema::BuildTemplateIdExpr(const CXXScopeSpec &SS,
}
}

auto AnyDependentArguments = [&]() -> bool {
bool InstantiationDependent;
return TemplateArgs &&
TemplateSpecializationType::anyDependentTemplateArguments(
*TemplateArgs, InstantiationDependent);
};

// In C++1y, check variable template ids.
if (R.getAsSingle<VarTemplateDecl>() && !AnyDependentArguments()) {
return CheckVarTemplateId(SS, R.getLookupNameInfo(),
R.getAsSingle<VarTemplateDecl>(),
TemplateKWLoc, TemplateArgs);
if (R.getAsSingle<VarTemplateDecl>()) {
ExprResult Res = CheckVarTemplateId(SS, R.getLookupNameInfo(),
R.getAsSingle<VarTemplateDecl>(),
TemplateKWLoc, TemplateArgs);
if (Res.isInvalid() || Res.isUsable())
return Res;
// Result is dependent. Carry on to build an UnresolvedLookupEpxr.
}

if (R.getAsSingle<ConceptDecl>()) {
Expand Down Expand Up @@ -9921,6 +9918,14 @@ DeclResult Sema::ActOnExplicitInstantiation(Scope *S,
if (Res.isInvalid())
return true;

if (!Res.isUsable()) {
// We somehow specified dependent template arguments in an explicit
// instantiation. This should probably only happen during error
// recovery.
Diag(D.getIdentifierLoc(), diag::err_explicit_instantiation_dependent);
return true;
}

// Ignore access control bits, we don't need them for redeclaration
// checking.
Prev = cast<VarDecl>(Res.get());
Expand Down
20 changes: 1 addition & 19 deletions clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
Expand Up @@ -5134,15 +5134,6 @@ void Sema::InstantiateVariableDefinition(SourceLocation PointOfInstantiation,
VarTemplateSpecializationDecl *VarSpec =
dyn_cast<VarTemplateSpecializationDecl>(Var);
if (VarSpec) {
// If this is a variable template specialization, make sure that it is
// non-dependent.
bool InstantiationDependent = false;
assert(!TemplateSpecializationType::anyDependentTemplateArguments(
VarSpec->getTemplateArgsInfo(), InstantiationDependent) &&
"Only instantiate variable template specializations that are "
"not type-dependent");
(void)InstantiationDependent;

// If this is a static data member template, there might be an
// uninstantiated initializer on the declaration. If so, instantiate
// it now.
Expand Down Expand Up @@ -5963,16 +5954,7 @@ NamedDecl *Sema::FindInstantiatedDecl(SourceLocation Loc, NamedDecl *D,
return nullptr;
DeclContext::lookup_result Found = ParentDC->lookup(Name);

if (auto *VTSD = dyn_cast<VarTemplateSpecializationDecl>(D)) {
VarTemplateDecl *Templ = cast_or_null<VarTemplateDecl>(
findInstantiationOf(Context, VTSD->getSpecializedTemplate(),
Found.begin(), Found.end()));
if (!Templ)
return nullptr;
Result = getVarTemplateSpecialization(
Templ, &VTSD->getTemplateArgsInfo(), NewNameInfo, SourceLocation());
} else
Result = findInstantiationOf(Context, D, Found.begin(), Found.end());
Result = findInstantiationOf(Context, D, Found.begin(), Found.end());
} else {
// Since we don't have a name for the entity we're looking for,
// our only option is to walk through all of the declarations to
Expand Down

0 comments on commit 6170072

Please sign in to comment.