diff options
author | Richard Smith <richard-llvm@metafoo.co.uk> | 2013-03-26 01:15:19 +0000 |
---|---|---|
committer | Richard Smith <richard-llvm@metafoo.co.uk> | 2013-03-26 01:15:19 +0000 |
commit | 2db075b1d3b16f0100fe06408dfb4ab7d50700a4 (patch) | |
tree | eb14674e7e49bbf59405881541c6518a9a0ddad4 | |
parent | c2e935f28079862b212f6b2af3057a5f203dcbc0 (diff) |
Implement special-case name lookup for inheriting constructors: member
using-declarations with names which look constructor-like are interpreted as
constructor names.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@177957 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r-- | include/clang/Parse/Parser.h | 3 | ||||
-rw-r--r-- | include/clang/Sema/Sema.h | 4 | ||||
-rw-r--r-- | lib/Parse/ParseDeclCXX.cpp | 36 | ||||
-rw-r--r-- | lib/Parse/ParseExprCXX.cpp | 22 | ||||
-rw-r--r-- | lib/Sema/SemaExprCXX.cpp | 37 | ||||
-rw-r--r-- | test/CXX/basic/basic.lookup/basic.lookup.qual/class.qual/p2.cpp | 94 |
6 files changed, 181 insertions, 15 deletions
diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h index 1b81f169ba..de18b86da0 100644 --- a/include/clang/Parse/Parser.h +++ b/include/clang/Parse/Parser.h @@ -1290,7 +1290,8 @@ private: ParsedType ObjectType, bool EnteringContext, bool *MayBePseudoDestructor = 0, - bool IsTypename = false); + bool IsTypename = false, + IdentifierInfo **LastII = 0); void CheckForLParenAfterColonColon(); diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 0963c0e20f..d345e50d85 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -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, diff --git a/lib/Parse/ParseDeclCXX.cpp b/lib/Parse/ParseDeclCXX.cpp index aa2c0f512a..d7f8e982aa 100644 --- a/lib/Parse/ParseDeclCXX.cpp +++ b/lib/Parse/ParseDeclCXX.cpp @@ -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; } diff --git a/lib/Parse/ParseExprCXX.cpp b/lib/Parse/ParseExprCXX.cpp index f72e68e2a1..22938afede 100644 --- a/lib/Parse/ParseExprCXX.cpp +++ b/lib/Parse/ParseExprCXX.cpp @@ -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 = &II; + // We have an identifier followed by a '::'. Lookup this name // as the name in a nested-name-specifier. SourceLocation IdLoc = ConsumeToken(); diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index 4b426bf62e..1724624fe3 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -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, diff --git a/test/CXX/basic/basic.lookup/basic.lookup.qual/class.qual/p2.cpp b/test/CXX/basic/basic.lookup/basic.lookup.qual/class.qual/p2.cpp index 7ecedd5a6a..24f75c9bfe 100644 --- a/test/CXX/basic/basic.lookup/basic.lookup.qual/class.qual/p2.cpp +++ b/test/CXX/basic/basic.lookup/basic.lookup.qual/class.qual/p2.cpp @@ -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 +} |