forked from OSchip/llvm-project
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:
parent
1ee6d8cef4
commit
7447af48b1
|
@ -1290,7 +1290,8 @@ private:
|
|||
ParsedType ObjectType,
|
||||
bool EnteringContext,
|
||||
bool *MayBePseudoDestructor = 0,
|
||||
bool IsTypename = false);
|
||||
bool IsTypename = false,
|
||||
IdentifierInfo **LastII = 0);
|
||||
|
||||
void CheckForLParenAfterColonColon();
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue