Implement special-case name lookup for inheriting constructors: member

using-declarations with names which look constructor-like are interpreted as
constructor names.

llvm-svn: 177957
This commit is contained in:
Richard Smith 2013-03-26 01:15:19 +00:00
parent 1ee6d8cef4
commit 7447af48b1
6 changed files with 181 additions and 15 deletions

View File

@ -1290,7 +1290,8 @@ private:
ParsedType ObjectType,
bool EnteringContext,
bool *MayBePseudoDestructor = 0,
bool IsTypename = false);
bool IsTypename = false,
IdentifierInfo **LastII = 0);
void CheckForLParenAfterColonColon();

View File

@ -3756,6 +3756,10 @@ public:
bool AllowExplicit = false,
bool IsListInitialization = false);
ParsedType getInheritingConstructorName(CXXScopeSpec &SS,
SourceLocation NameLoc,
IdentifierInfo &Name);
ParsedType getDestructorName(SourceLocation TildeLoc,
IdentifierInfo &II, SourceLocation NameLoc,
Scope *S, CXXScopeSpec &SS,

View File

@ -468,7 +468,10 @@ Decl *Parser::ParseUsingDeclaration(unsigned Context,
}
// Parse nested-name-specifier.
ParseOptionalCXXScopeSpecifier(SS, ParsedType(), /*EnteringContext=*/false);
IdentifierInfo *LastII = 0;
ParseOptionalCXXScopeSpecifier(SS, ParsedType(), /*EnteringContext=*/false,
/*MayBePseudoDtor=*/0, /*IsTypename=*/false,
/*LastII=*/&LastII);
// Check nested-name specifier.
if (SS.isInvalid()) {
@ -476,18 +479,31 @@ Decl *Parser::ParseUsingDeclaration(unsigned Context,
return 0;
}
SourceLocation TemplateKWLoc;
UnqualifiedId Name;
// Parse the unqualified-id. We allow parsing of both constructor and
// destructor names and allow the action module to diagnose any semantic
// errors.
SourceLocation TemplateKWLoc;
UnqualifiedId Name;
if (ParseUnqualifiedId(SS,
/*EnteringContext=*/false,
/*AllowDestructorName=*/true,
/*AllowConstructorName=*/true,
ParsedType(),
TemplateKWLoc,
Name)) {
//
// C++11 [class.qual]p2:
// [...] in a using-declaration that is a member-declaration, if the name
// specified after the nested-name-specifier is the same as the identifier
// or the simple-template-id's template-name in the last component of the
// nested-name-specifier, the name is [...] considered to name the
// constructor.
if (getLangOpts().CPlusPlus11 && Context == Declarator::MemberContext &&
Tok.is(tok::identifier) && NextToken().is(tok::semi) &&
SS.isNotEmpty() && LastII == Tok.getIdentifierInfo() &&
!SS.getScopeRep()->getAsNamespace() &&
!SS.getScopeRep()->getAsNamespaceAlias()) {
SourceLocation IdLoc = ConsumeToken();
ParsedType Type = Actions.getInheritingConstructorName(SS, IdLoc, *LastII);
Name.setConstructorName(Type, IdLoc, IdLoc);
} else if (ParseUnqualifiedId(SS, /*EnteringContext=*/ false,
/*AllowDestructorName=*/ true,
/*AllowConstructorName=*/ true, ParsedType(),
TemplateKWLoc, Name)) {
SkipUntil(tok::semi);
return 0;
}

View File

@ -168,19 +168,26 @@ void Parser::CheckForLParenAfterColonColon() {
/// if we do end up determining that we are parsing a destructor name,
/// the last component of the nested-name-specifier is not parsed as
/// part of the scope specifier.
/// member access expression, e.g., the \p T:: in \p p->T::m.
///
/// \param IsTypename If \c true, this nested-name-specifier is known to be
/// part of a type name. This is used to improve error recovery.
///
/// \param LastII When non-NULL, points to an IdentifierInfo* that will be
/// filled in with the leading identifier in the last component of the
/// nested-name-specifier, if any.
///
/// \returns true if there was an error parsing a scope specifier
bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS,
ParsedType ObjectType,
bool EnteringContext,
bool *MayBePseudoDestructor,
bool IsTypename) {
bool IsTypename,
IdentifierInfo **LastII) {
assert(getLangOpts().CPlusPlus &&
"Call sites of this function should be guarded by checking for C++");
if (Tok.is(tok::annot_cxxscope)) {
assert(!LastII && "want last identifier but have already annotated scope");
Actions.RestoreNestedNameSpecifierAnnotation(Tok.getAnnotationValue(),
Tok.getAnnotationRange(),
SS);
@ -188,6 +195,9 @@ bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS,
return false;
}
if (LastII)
*LastII = 0;
bool HasScopeSpecifier = false;
if (Tok.is(tok::coloncolon)) {
@ -334,6 +344,9 @@ bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS,
return false;
}
if (LastII)
*LastII = TemplateId->Name;
// Consume the template-id token.
ConsumeToken();
@ -405,6 +418,9 @@ bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS,
return false;
}
if (LastII)
*LastII = ⅈ
// We have an identifier followed by a '::'. Lookup this name
// as the name in a nested-name-specifier.
SourceLocation IdLoc = ConsumeToken();

View File

@ -38,6 +38,43 @@
using namespace clang;
using namespace sema;
/// \brief Handle the result of the special case name lookup for inheriting
/// constructor declarations. 'NS::X::X' and 'NS::X<...>::X' are treated as
/// constructor names in member using declarations, even if 'X' is not the
/// name of the corresponding type.
ParsedType Sema::getInheritingConstructorName(CXXScopeSpec &SS,
SourceLocation NameLoc,
IdentifierInfo &Name) {
NestedNameSpecifier *NNS = SS.getScopeRep();
// Convert the nested-name-specifier into a type.
QualType Type;
switch (NNS->getKind()) {
case NestedNameSpecifier::TypeSpec:
case NestedNameSpecifier::TypeSpecWithTemplate:
Type = QualType(NNS->getAsType(), 0);
break;
case NestedNameSpecifier::Identifier:
// Strip off the last layer of the nested-name-specifier and build a
// typename type for it.
assert(NNS->getAsIdentifier() == &Name && "not a constructor name");
Type = Context.getDependentNameType(ETK_None, NNS->getPrefix(),
NNS->getAsIdentifier());
break;
case NestedNameSpecifier::Global:
case NestedNameSpecifier::Namespace:
case NestedNameSpecifier::NamespaceAlias:
llvm_unreachable("Nested name specifier is not a type for inheriting ctor");
}
// This reference to the type is located entirely at the location of the
// final identifier in the qualified-id.
return CreateParsedType(Type,
Context.getTrivialTypeSourceInfo(Type, NameLoc));
}
ParsedType Sema::getDestructorName(SourceLocation TildeLoc,
IdentifierInfo &II,
SourceLocation NameLoc,

View File

@ -1,4 +1,5 @@
// RUN: %clang_cc1 -fsyntax-only -verify %s
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++98 %s
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s
struct X0 {
X0 f1();
X0 f2();
@ -25,3 +26,94 @@ struct X0::X0 X0::f2() { return X0(); }
template<typename T> X1<T>::X1<T> X1<T>::f2() { } // expected-error{{qualified reference to 'X1' is a constructor name rather than a template name wherever a constructor can be declared}}
template<typename T> X1<T>::X1<T> (X1<T>::f2)(int) { } // expected-error{{qualified reference to 'X1' is a constructor name rather than a template name wherever a constructor can be declared}}
template<typename T> struct X1<T>::X1<T> (X1<T>::f2)(float) { }
// We have a special case for lookup within using-declarations that are
// member-declarations: foo::bar::baz::baz always names baz's constructor
// in such a context, even if looking up 'baz' within foo::bar::baz would
// not find the injected-class-name. Likewise foo::bar::baz<T>::baz also
// names the constructor.
namespace InhCtor {
struct A {
A(int);
protected:
int T();
};
typedef A T;
struct B : A {
// This is a using-declaration for 'int A::T()' in C++98, but is an
// inheriting constructor declaration in C++11.
using InhCtor::T::T;
};
#if __cplusplus < 201103L
B b(123); // expected-error {{no matching constructor}}
// expected-note@-7 2{{candidate constructor}}
int n = b.T(); // ok, accessible
#else
B b(123); // ok, inheriting constructor
int n = b.T(); // expected-error {{'T' is a protected member of 'InhCtor::A'}}
// expected-note@-15 {{declared protected here}}
template<typename T>
struct S : T {
struct U : S {
using S::S;
};
using T::T;
};
S<A>::U ua(0);
S<B>::U ub(0);
template<typename T>
struct X : T {
using T::Z::U::U;
};
template<typename T>
struct X2 : T {
using T::Z::template V<int>::V;
};
struct Y {
struct Z {
typedef Y U;
template<typename T> using V = Y;
};
Y(int);
};
X<Y> xy(0);
namespace Repeat {
struct A {
struct T {
T(int);
};
};
struct Z : A {
// FIXME: Core wording says this is invalid, but we and g++ accept.
using A::A::A;
};
template<typename T>
struct ZT : T::T {
// FIXME: Core wording says this is invalid, but we and g++ accept.
using T::T::T;
};
}
namespace NS {
struct NS {};
}
struct DerivedFromNS : NS::NS {
// No special case unless the NNS names a class.
using InhCtor::NS::NS; // expected-error {{using declaration in class refers into 'InhCtor::NS::', which is not a class}}
};
typedef int I;
struct UsingInt {
using I::I; // expected-error {{expected a class or namespace}}
};
template<typename T> struct UsingIntTemplate {
using T::T; // expected-error {{type 'int' cannot be used prior to '::' because it has no members}}
};
UsingIntTemplate<int> uit; // expected-note {{here}}
#endif
}