Skip to content

Commit

Permalink
[clang] fix canonicalization of nested name specifiers
Browse files Browse the repository at this point in the history
See PR47174.

When canonicalizing nested name specifiers of the type kind,
the prefix for 'DependentTemplateSpecialization' types was being
dropped, leading to malformed types which would cause failures
when rebuilding template names.

Signed-off-by: Matheus Izvekov <mizvekov@gmail.com>

Reviewed By: rsmith

Differential Revision: https://reviews.llvm.org/D107311

(cherry picked from commit 219790c)
  • Loading branch information
mizvekov authored and tstellar committed Aug 11, 2021
1 parent 4178fa1 commit 168ece2
Show file tree
Hide file tree
Showing 5 changed files with 39 additions and 17 deletions.
3 changes: 1 addition & 2 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -7828,8 +7828,7 @@ class Sema final {
TemplateArgumentLoc &Arg,
SmallVectorImpl<TemplateArgument> &Converted);

bool CheckTemplateArgument(TemplateTypeParmDecl *Param,
TypeSourceInfo *Arg);
bool CheckTemplateArgument(TypeSourceInfo *Arg);
ExprResult CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
QualType InstantiatedParamType, Expr *Arg,
TemplateArgument &Converted,
Expand Down
20 changes: 12 additions & 8 deletions clang/lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6066,9 +6066,11 @@ ASTContext::getCanonicalNestedNameSpecifier(NestedNameSpecifier *NNS) const {
NNS->getAsNamespaceAlias()->getNamespace()
->getOriginalNamespace());

// The difference between TypeSpec and TypeSpecWithTemplate is that the
// latter will have the 'template' keyword when printed.
case NestedNameSpecifier::TypeSpec:
case NestedNameSpecifier::TypeSpecWithTemplate: {
QualType T = getCanonicalType(QualType(NNS->getAsType(), 0));
const Type *T = getCanonicalType(NNS->getAsType());

// If we have some kind of dependent-named type (e.g., "typename T::type"),
// break it apart into its prefix and identifier, then reconsititute those
Expand All @@ -6078,14 +6080,16 @@ ASTContext::getCanonicalNestedNameSpecifier(NestedNameSpecifier *NNS) const {
// typedef typename T::type T1;
// typedef typename T1::type T2;
if (const auto *DNT = T->getAs<DependentNameType>())
return NestedNameSpecifier::Create(*this, DNT->getQualifier(),
const_cast<IdentifierInfo *>(DNT->getIdentifier()));

// Otherwise, just canonicalize the type, and force it to be a TypeSpec.
// FIXME: Why are TypeSpec and TypeSpecWithTemplate distinct in the
// first place?
return NestedNameSpecifier::Create(
*this, DNT->getQualifier(),
const_cast<IdentifierInfo *>(DNT->getIdentifier()));
if (const auto *DTST = T->getAs<DependentTemplateSpecializationType>())
return NestedNameSpecifier::Create(*this, DTST->getQualifier(), true,
const_cast<Type *>(T));

// TODO: Set 'Template' parameter to true for other template types.
return NestedNameSpecifier::Create(*this, nullptr, false,
const_cast<Type *>(T.getTypePtr()));
const_cast<Type *>(T));
}

case NestedNameSpecifier::Global:
Expand Down
5 changes: 3 additions & 2 deletions clang/lib/Sema/SemaDeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12472,6 +12472,8 @@ bool Sema::CheckUsingDeclRedeclaration(SourceLocation UsingLoc,
return false;
}

const NestedNameSpecifier *CNNS =
Context.getCanonicalNestedNameSpecifier(Qual);
for (LookupResult::iterator I = Prev.begin(), E = Prev.end(); I != E; ++I) {
NamedDecl *D = *I;

Expand All @@ -12497,8 +12499,7 @@ bool Sema::CheckUsingDeclRedeclaration(SourceLocation UsingLoc,
// using decls differ if they name different scopes (but note that
// template instantiation can cause this check to trigger when it
// didn't before instantiation).
if (Context.getCanonicalNestedNameSpecifier(Qual) !=
Context.getCanonicalNestedNameSpecifier(DQual))
if (CNNS != Context.getCanonicalNestedNameSpecifier(DQual))
continue;

Diag(NameLoc, diag::err_using_decl_redeclaration) << SS.getRange();
Expand Down
9 changes: 4 additions & 5 deletions clang/lib/Sema/SemaTemplate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1079,7 +1079,7 @@ NamedDecl *Sema::ActOnTypeParameter(Scope *S, bool Typename,
return Param;

// Check the template argument itself.
if (CheckTemplateArgument(Param, DefaultTInfo)) {
if (CheckTemplateArgument(DefaultTInfo)) {
Param->setInvalidDecl();
return Param;
}
Expand Down Expand Up @@ -5042,7 +5042,7 @@ bool Sema::CheckTemplateTypeArgument(TemplateTypeParmDecl *Param,
}
}

if (CheckTemplateArgument(Param, TSI))
if (CheckTemplateArgument(TSI))
return true;

// Add the converted template type argument.
Expand Down Expand Up @@ -5661,7 +5661,7 @@ bool Sema::CheckTemplateArgumentList(
TemplateArgumentListInfo NewArgs = TemplateArgs;

// Make sure we get the template parameter list from the most
// recentdeclaration, since that is the only one that has is guaranteed to
// recent declaration, since that is the only one that is guaranteed to
// have all the default template argument information.
TemplateParameterList *Params =
cast<TemplateDecl>(Template->getMostRecentDecl())
Expand Down Expand Up @@ -6208,8 +6208,7 @@ bool UnnamedLocalNoLinkageFinder::VisitNestedNameSpecifier(
///
/// This routine implements the semantics of C++ [temp.arg.type]. It
/// returns true if an error occurred, and false otherwise.
bool Sema::CheckTemplateArgument(TemplateTypeParmDecl *Param,
TypeSourceInfo *ArgInfo) {
bool Sema::CheckTemplateArgument(TypeSourceInfo *ArgInfo) {
assert(ArgInfo && "invalid TypeSourceInfo");
QualType Arg = ArgInfo->getType();
SourceRange SR = ArgInfo->getTypeLoc().getSourceRange();
Expand Down
19 changes: 19 additions & 0 deletions clang/test/CXX/temp/temp.constr/temp.constr.normal/p1.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,4 +81,23 @@ template <True T, Bar2 U>
requires true struct S4; // expected-note {{template is declared here}}
template <True T, True U>
requires true struct S4<T, U>; // expected-error {{class template partial specialization is not more specialized than the primary template}}

struct X {
template<int> struct Y {
using type = int;
};
};

template<class T> concept C1 = sizeof(T) != 0;
template<class T> concept C2 = C1<typename T::template Y<1>::type>;

template<class T> requires C1<T> void t1() = delete; // expected-note {{candidate function}}
template<class T> requires C1<T> && C2<T> void t1() = delete; // expected-note {{candidate function}}
template void t1<X>();
void t1() { t1<X>(); } // expected-error {{call to deleted function 't1'}}

template<class T> requires C1<T> void t2() {}; // expected-note 2 {{candidate function}}
template<class T> requires C2<T> void t2() {}; // expected-note 2 {{candidate function}}
template void t2<X>(); // expected-error {{partial ordering for explicit instantiation of 't2' is ambiguous}}
void t2() { t2<X>(); } // expected-error {{call to 't2' is ambiguous}}
} // namespace PR47174

0 comments on commit 168ece2

Please sign in to comment.