Skip to content

Commit

Permalink
Finish implementation of C++ DR1310 (http://wg21.link/cwg1310).
Browse files Browse the repository at this point in the history
Diagnose the case when a dependent template name instantiates to an
injected-class-name outside a nested-name-specifier.


git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@292545 91177308-0d34-0410-b5e6-96231b3b80d8
  • Loading branch information
zygoloid committed Jan 20, 2017
1 parent d562f4c commit 8ae50eb
Show file tree
Hide file tree
Showing 9 changed files with 134 additions and 90 deletions.
11 changes: 4 additions & 7 deletions include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -5984,13 +5984,10 @@ class Sema {
const DeclarationNameInfo &NameInfo,
const TemplateArgumentListInfo *TemplateArgs);

TemplateNameKind ActOnDependentTemplateName(Scope *S,
CXXScopeSpec &SS,
SourceLocation TemplateKWLoc,
UnqualifiedId &Name,
ParsedType ObjectType,
bool EnteringContext,
TemplateTy &Template);
TemplateNameKind ActOnDependentTemplateName(
Scope *S, CXXScopeSpec &SS, SourceLocation TemplateKWLoc,
UnqualifiedId &Name, ParsedType ObjectType, bool EnteringContext,
TemplateTy &Template, bool AllowInjectedClassName = false);

DeclResult
ActOnClassTemplateSpecialization(Scope *S, unsigned TagSpec, TagUseKind TUK,
Expand Down
40 changes: 18 additions & 22 deletions lib/Parse/ParseExprCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -310,11 +310,9 @@ bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS,
// Commit to parsing the template-id.
TPA.Commit();
TemplateTy Template;
if (TemplateNameKind TNK
= Actions.ActOnDependentTemplateName(getCurScope(),
SS, TemplateKWLoc, TemplateName,
ObjectType, EnteringContext,
Template)) {
if (TemplateNameKind TNK = Actions.ActOnDependentTemplateName(
getCurScope(), SS, TemplateKWLoc, TemplateName, ObjectType,
EnteringContext, Template, /*AllowInjectedClassName*/ true)) {
if (AnnotateTemplateIdToken(Template, TNK, SS, TemplateKWLoc,
TemplateName, false))
return true;
Expand Down Expand Up @@ -509,12 +507,10 @@ bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS,
Diag(Tok.getLocation(), DiagID)
<< II.getName()
<< FixItHint::CreateInsertion(Tok.getLocation(), "template ");

if (TemplateNameKind TNK
= Actions.ActOnDependentTemplateName(getCurScope(),
SS, SourceLocation(),
TemplateName, ObjectType,
EnteringContext, Template)) {

if (TemplateNameKind TNK = Actions.ActOnDependentTemplateName(
getCurScope(), SS, SourceLocation(), TemplateName, ObjectType,
EnteringContext, Template, /*AllowInjectedClassName*/ true)) {
// Consume the identifier.
ConsumeToken();
if (AnnotateTemplateIdToken(Template, TNK, SS, SourceLocation(),
Expand Down Expand Up @@ -2020,9 +2016,11 @@ bool Parser::ParseUnqualifiedIdTemplateId(CXXScopeSpec &SS,
case UnqualifiedId::IK_OperatorFunctionId:
case UnqualifiedId::IK_LiteralOperatorId:
if (AssumeTemplateId) {
TNK = Actions.ActOnDependentTemplateName(getCurScope(), SS, TemplateKWLoc,
Id, ObjectType, EnteringContext,
Template);
// We defer the injected-class-name checks until we've found whether
// this template-id is used to form a nested-name-specifier or not.
TNK = Actions.ActOnDependentTemplateName(
getCurScope(), SS, TemplateKWLoc, Id, ObjectType, EnteringContext,
Template, /*AllowInjectedClassName*/ true);
if (TNK == TNK_Non_template)
return true;
} else {
Expand Down Expand Up @@ -2051,10 +2049,9 @@ bool Parser::ParseUnqualifiedIdTemplateId(CXXScopeSpec &SS,
Diag(Id.StartLocation, diag::err_missing_dependent_template_keyword)
<< Name
<< FixItHint::CreateInsertion(Id.StartLocation, "template ");
TNK = Actions.ActOnDependentTemplateName(getCurScope(),
SS, TemplateKWLoc, Id,
ObjectType, EnteringContext,
Template);
TNK = Actions.ActOnDependentTemplateName(
getCurScope(), SS, TemplateKWLoc, Id, ObjectType, EnteringContext,
Template, /*AllowInjectedClassName*/ true);
if (TNK == TNK_Non_template)
return true;
}
Expand All @@ -2077,10 +2074,9 @@ bool Parser::ParseUnqualifiedIdTemplateId(CXXScopeSpec &SS,
bool MemberOfUnknownSpecialization;
TemplateName.setIdentifier(Name, NameLoc);
if (ObjectType) {
TNK = Actions.ActOnDependentTemplateName(getCurScope(),
SS, TemplateKWLoc, TemplateName,
ObjectType, EnteringContext,
Template);
TNK = Actions.ActOnDependentTemplateName(
getCurScope(), SS, TemplateKWLoc, TemplateName, ObjectType,
EnteringContext, Template, /*AllowInjectedClassName*/ true);
if (TNK == TNK_Non_template)
return true;
} else {
Expand Down
21 changes: 20 additions & 1 deletion lib/Sema/SemaTemplate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3241,7 +3241,8 @@ TemplateNameKind Sema::ActOnDependentTemplateName(Scope *S,
UnqualifiedId &Name,
ParsedType ObjectType,
bool EnteringContext,
TemplateTy &Result) {
TemplateTy &Result,
bool AllowInjectedClassName) {
if (TemplateKWLoc.isValid() && S && !S->getTemplateParamParent())
Diag(TemplateKWLoc,
getLangOpts().CPlusPlus11 ?
Expand Down Expand Up @@ -3289,6 +3290,24 @@ TemplateNameKind Sema::ActOnDependentTemplateName(Scope *S,
return TNK_Non_template;
} else {
// We found something; return it.
auto *LookupRD = dyn_cast<CXXRecordDecl>(LookupCtx);
if (!AllowInjectedClassName && SS.isSet() && LookupRD &&
Name.getKind() == UnqualifiedId::IK_Identifier && Name.Identifier &&
LookupRD->getIdentifier() == Name.Identifier) {
// C++14 [class.qual]p2:
// In a lookup in which function names are not ignored and the
// nested-name-specifier nominates a class C, if the name specified
// [...] is the injected-class-name of C, [...] the name is instead
// considered to name the constructor
//
// We don't get here if naming the constructor would be valid, so we
// just reject immediately and recover by treating the
// injected-class-name as naming the template.
Diag(Name.getLocStart(),
diag::ext_out_of_line_qualified_id_type_names_constructor)
<< Name.Identifier << 0 /*injected-class-name used as template name*/
<< 1 /*'template' keyword was used*/;
}
return TNK;
}
}
Expand Down
19 changes: 10 additions & 9 deletions lib/Sema/SemaTemplateInstantiate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -806,7 +806,8 @@ namespace {
TransformTemplateName(CXXScopeSpec &SS, TemplateName Name,
SourceLocation NameLoc,
QualType ObjectType = QualType(),
NamedDecl *FirstQualifierInScope = nullptr);
NamedDecl *FirstQualifierInScope = nullptr,
bool AllowInjectedClassName = false);

const LoopHintAttr *TransformLoopHintAttr(const LoopHintAttr *LH);

Expand Down Expand Up @@ -1040,11 +1041,10 @@ TemplateInstantiator::RebuildElaboratedType(SourceLocation KeywordLoc,
T);
}

TemplateName TemplateInstantiator::TransformTemplateName(CXXScopeSpec &SS,
TemplateName Name,
SourceLocation NameLoc,
QualType ObjectType,
NamedDecl *FirstQualifierInScope) {
TemplateName TemplateInstantiator::TransformTemplateName(
CXXScopeSpec &SS, TemplateName Name, SourceLocation NameLoc,
QualType ObjectType, NamedDecl *FirstQualifierInScope,
bool AllowInjectedClassName) {
if (TemplateTemplateParmDecl *TTP
= dyn_cast_or_null<TemplateTemplateParmDecl>(Name.getAsTemplateDecl())) {
if (TTP->getDepth() < TemplateArgs.getNumLevels()) {
Expand Down Expand Up @@ -1095,9 +1095,10 @@ TemplateName TemplateInstantiator::TransformTemplateName(CXXScopeSpec &SS,
Arg = getPackSubstitutedTemplateArgument(getSema(), Arg);
return Arg.getAsTemplate();
}

return inherited::TransformTemplateName(SS, Name, NameLoc, ObjectType,
FirstQualifierInScope);

return inherited::TransformTemplateName(SS, Name, NameLoc, ObjectType,
FirstQualifierInScope,
AllowInjectedClassName);
}

ExprResult
Expand Down
53 changes: 29 additions & 24 deletions lib/Sema/TreeTransform.h
Original file line number Diff line number Diff line change
Expand Up @@ -505,7 +505,8 @@ class TreeTransform {
TransformTemplateName(CXXScopeSpec &SS, TemplateName Name,
SourceLocation NameLoc,
QualType ObjectType = QualType(),
NamedDecl *FirstQualifierInScope = nullptr);
NamedDecl *FirstQualifierInScope = nullptr,
bool AllowInjectedClassName = false);

/// \brief Transform the given template argument.
///
Expand Down Expand Up @@ -916,14 +917,15 @@ class TreeTransform {
NestedNameSpecifierLoc QualifierLoc,
const IdentifierInfo *Name,
SourceLocation NameLoc,
TemplateArgumentListInfo &Args) {
TemplateArgumentListInfo &Args,
bool AllowInjectedClassName) {
// Rebuild the template name.
// TODO: avoid TemplateName abstraction
CXXScopeSpec SS;
SS.Adopt(QualifierLoc);
TemplateName InstName
= getDerived().RebuildTemplateName(SS, *Name, NameLoc, QualType(),
nullptr);
nullptr, AllowInjectedClassName);

if (InstName.isNull())
return QualType();
Expand Down Expand Up @@ -1088,7 +1090,8 @@ class TreeTransform {
const IdentifierInfo &Name,
SourceLocation NameLoc,
QualType ObjectType,
NamedDecl *FirstQualifierInScope);
NamedDecl *FirstQualifierInScope,
bool AllowInjectedClassName);

/// \brief Build a new template name given a nested name specifier and the
/// overloaded operator name that is referred to as a template.
Expand All @@ -1100,7 +1103,8 @@ class TreeTransform {
TemplateName RebuildTemplateName(CXXScopeSpec &SS,
OverloadedOperatorKind Operator,
SourceLocation NameLoc,
QualType ObjectType);
QualType ObjectType,
bool AllowInjectedClassName);

/// \brief Build a new template name given a template template parameter pack
/// and the
Expand Down Expand Up @@ -3601,7 +3605,8 @@ TreeTransform<Derived>::TransformTemplateName(CXXScopeSpec &SS,
TemplateName Name,
SourceLocation NameLoc,
QualType ObjectType,
NamedDecl *FirstQualifierInScope) {
NamedDecl *FirstQualifierInScope,
bool AllowInjectedClassName) {
if (QualifiedTemplateName *QTN = Name.getAsQualifiedTemplateName()) {
TemplateDecl *Template = QTN->getTemplateDecl();
assert(Template && "qualified template name must refer to a template");
Expand Down Expand Up @@ -3638,11 +3643,12 @@ TreeTransform<Derived>::TransformTemplateName(CXXScopeSpec &SS,
*DTN->getIdentifier(),
NameLoc,
ObjectType,
FirstQualifierInScope);
FirstQualifierInScope,
AllowInjectedClassName);
}

return getDerived().RebuildTemplateName(SS, DTN->getOperator(), NameLoc,
ObjectType);
ObjectType, AllowInjectedClassName);
}

if (TemplateDecl *Template = Name.getAsTemplateDecl()) {
Expand Down Expand Up @@ -4152,11 +4158,9 @@ TypeSourceInfo *TreeTransform<Derived>::TransformTSIInObjectScope(
TemplateSpecializationTypeLoc SpecTL =
TL.castAs<TemplateSpecializationTypeLoc>();

TemplateName Template
= getDerived().TransformTemplateName(SS,
SpecTL.getTypePtr()->getTemplateName(),
SpecTL.getTemplateNameLoc(),
ObjectType, UnqualLookup);
TemplateName Template = getDerived().TransformTemplateName(
SS, SpecTL.getTypePtr()->getTemplateName(), SpecTL.getTemplateNameLoc(),
ObjectType, UnqualLookup, /*AllowInjectedClassName*/true);
if (Template.isNull())
return nullptr;

Expand All @@ -4170,7 +4174,8 @@ TypeSourceInfo *TreeTransform<Derived>::TransformTSIInObjectScope(
= getDerived().RebuildTemplateName(SS,
*SpecTL.getTypePtr()->getIdentifier(),
SpecTL.getTemplateNameLoc(),
ObjectType, UnqualLookup);
ObjectType, UnqualLookup,
/*AllowInjectedClassName*/true);
if (Template.isNull())
return nullptr;

Expand Down Expand Up @@ -5878,12 +5883,10 @@ TransformDependentTemplateSpecializationType(TypeLocBuilder &TLB,
NewTemplateArgs))
return QualType();

QualType Result
= getDerived().RebuildDependentTemplateSpecializationType(T->getKeyword(),
QualifierLoc,
T->getIdentifier(),
TL.getTemplateNameLoc(),
NewTemplateArgs);
QualType Result = getDerived().RebuildDependentTemplateSpecializationType(
T->getKeyword(), QualifierLoc, T->getIdentifier(),
TL.getTemplateNameLoc(), NewTemplateArgs,
/*AllowInjectedClassName*/ false);
if (Result.isNull())
return QualType();

Expand Down Expand Up @@ -12015,7 +12018,8 @@ TreeTransform<Derived>::RebuildTemplateName(CXXScopeSpec &SS,
const IdentifierInfo &Name,
SourceLocation NameLoc,
QualType ObjectType,
NamedDecl *FirstQualifierInScope) {
NamedDecl *FirstQualifierInScope,
bool AllowInjectedClassName) {
UnqualifiedId TemplateName;
TemplateName.setIdentifier(&Name, NameLoc);
Sema::TemplateTy Template;
Expand All @@ -12024,7 +12028,7 @@ TreeTransform<Derived>::RebuildTemplateName(CXXScopeSpec &SS,
SS, TemplateKWLoc, TemplateName,
ParsedType::make(ObjectType),
/*EnteringContext=*/false,
Template);
Template, AllowInjectedClassName);
return Template.get();
}

Expand All @@ -12033,7 +12037,8 @@ TemplateName
TreeTransform<Derived>::RebuildTemplateName(CXXScopeSpec &SS,
OverloadedOperatorKind Operator,
SourceLocation NameLoc,
QualType ObjectType) {
QualType ObjectType,
bool AllowInjectedClassName) {
UnqualifiedId Name;
// FIXME: Bogus location information.
SourceLocation SymbolLocations[3] = { NameLoc, NameLoc, NameLoc };
Expand All @@ -12044,7 +12049,7 @@ TreeTransform<Derived>::RebuildTemplateName(CXXScopeSpec &SS,
SS, TemplateKWLoc, Name,
ParsedType::make(ObjectType),
/*EnteringContext=*/false,
Template);
Template, AllowInjectedClassName);
return Template.get();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,7 @@ template void in_instantiation_x0<X0>(); // expected-note {{instantiation of}}

template<typename T> void in_instantiation_x1() {
typename T::X1 x1; // expected-warning{{qualified reference to 'X1' is a constructor name rather than a type in this context}}
// FIXME: Matching the behavior of other compilers, we do not treat this case
// as naming the constructor.
typename T::template X1<int> x1i;
typename T::template X1<int> x1i; // expected-warning{{qualified reference to 'X1' is a constructor name rather than a template name in this context}}
typename T::X0 x0;
}
template void in_instantiation_x1<X1<int> >(); // expected-note {{instantiation of}}
Expand Down
9 changes: 5 additions & 4 deletions test/CXX/drs/dr10xx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,11 @@ namespace dr1004 { // dr1004: 5
}
};

// FIXME: Is this example (from the standard) really OK, or does name lookup
// of "T::template A" name the constructor?
template<class T, template<class> class U = T::template A> struct Third { };
Third<A<int> > t;
// This example (from the standard) is actually ill-formed, because
// name lookup of "T::template A" names the constructor.
// FIXME: Only issue one diagnostic for this case.
template<class T, template<class> class U = T::template A> struct Third { }; // expected-error 2{{is a constructor name}}
Third<A<int> > t; // expected-note {{in instantiation of}} expected-note {{while substituting}} expected-note {{while checking}}
}

namespace dr1048 { // dr1048: 3.6
Expand Down
Loading

0 comments on commit 8ae50eb

Please sign in to comment.