diff options
author | Douglas Gregor <dgregor@apple.com> | 2009-09-02 22:59:36 +0000 |
---|---|---|
committer | Douglas Gregor <dgregor@apple.com> | 2009-09-02 22:59:36 +0000 |
commit | 2dd078ae50ff7be1fb25ebeedde45e9ab691a4f0 (patch) | |
tree | 0e30fbe6d5c2cbab6ad0dc64accd298ad7822f9f /lib | |
parent | 6c35415912d1e43c21fcaa2823934ae993f2718b (diff) |
Rewrite of our handling of name lookup in C++ member access expressions, e.g.,
x->Base::f
We no longer try to "enter" the context of the type that "x" points
to. Instead, we drag that object type through the parser and pass it
into the Sema routines that need to know how to perform lookup within
member access expressions.
We now implement most of the crazy name lookup rules in C++
[basic.lookup.classref] for non-templated code, including performing
lookup both in the context of the type referred to by the member
access and in the scope of the member access itself and then detecting
ambiguities when the two lookups collide (p1 and p4; p3 and p7 are
still TODO). This change also corrects our handling of name lookup
within template arguments of template-ids inside the
nested-name-specifier (p6; we used to look into the scope of the
object expression for them) and fixes PR4703.
I have disabled some tests that involve member access expressions
where the object expression has dependent type, because we don't yet
have the ability to describe dependent nested-name-specifiers starting
with an identifier.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@80843 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib')
-rw-r--r-- | lib/Parse/MinimalAction.cpp | 5 | ||||
-rw-r--r-- | lib/Parse/ParseDecl.cpp | 9 | ||||
-rw-r--r-- | lib/Parse/ParseDeclCXX.cpp | 13 | ||||
-rw-r--r-- | lib/Parse/ParseExpr.cpp | 17 | ||||
-rw-r--r-- | lib/Parse/ParseExprCXX.cpp | 52 | ||||
-rw-r--r-- | lib/Parse/Parser.cpp | 13 | ||||
-rw-r--r-- | lib/Sema/Sema.h | 33 | ||||
-rw-r--r-- | lib/Sema/SemaCXXScopeSpec.cpp | 238 | ||||
-rw-r--r-- | lib/Sema/SemaExprCXX.cpp | 42 | ||||
-rw-r--r-- | lib/Sema/SemaLookup.cpp | 41 | ||||
-rw-r--r-- | lib/Sema/SemaTemplate.cpp | 326 | ||||
-rw-r--r-- | lib/Sema/TreeTransform.h | 13 |
12 files changed, 518 insertions, 284 deletions
diff --git a/lib/Parse/MinimalAction.cpp b/lib/Parse/MinimalAction.cpp index ee23c0038a..d89cc8c3ea 100644 --- a/lib/Parse/MinimalAction.cpp +++ b/lib/Parse/MinimalAction.cpp @@ -160,8 +160,11 @@ bool MinimalAction::isCurrentClassName(const IdentifierInfo &, Scope *, } TemplateNameKind -MinimalAction::isTemplateName(const IdentifierInfo &II, Scope *S, +MinimalAction::isTemplateName(Scope *S, + const IdentifierInfo &II, + SourceLocation IdLoc, const CXXScopeSpec *SS, + TypeTy *ObjectType, bool EnteringScope, TemplateTy &TemplateDecl) { return TNK_Non_template; diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 25bed0937b..25ff53ca9f 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -743,7 +743,7 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, ->Kind == TNK_Type_template) { // We have a qualified template-id, e.g., N::A<int> CXXScopeSpec SS; - ParseOptionalCXXScopeSpecifier(SS, true); + ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/0, true); assert(Tok.is(tok::annot_template_id) && "ParseOptionalCXXScopeSpecifier not working"); AnnotateTemplateIdTokenAsType(&SS); @@ -1596,7 +1596,7 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS, Attr = ParseAttributes(); CXXScopeSpec SS; - if (getLang().CPlusPlus && ParseOptionalCXXScopeSpecifier(SS)) { + if (getLang().CPlusPlus && ParseOptionalCXXScopeSpecifier(SS, 0, false)) { if (Tok.isNot(tok::identifier)) { Diag(Tok, diag::err_expected_ident); if (Tok.isNot(tok::l_brace)) { @@ -2034,7 +2034,7 @@ void Parser::ParseDeclaratorInternal(Declarator &D, (Tok.is(tok::coloncolon) || Tok.is(tok::identifier) || Tok.is(tok::annot_cxxscope))) { CXXScopeSpec SS; - if (ParseOptionalCXXScopeSpecifier(SS, true)) { + if (ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/0, true)) { if(Tok.isNot(tok::star)) { // The scope spec really belongs to the direct-declarator. D.getCXXScopeSpec() = SS; @@ -2191,7 +2191,8 @@ void Parser::ParseDirectDeclarator(Declarator &D) { if (D.mayHaveIdentifier()) { // ParseDeclaratorInternal might already have parsed the scope. bool afterCXXScope = D.getCXXScopeSpec().isSet() || - ParseOptionalCXXScopeSpecifier(D.getCXXScopeSpec(), true); + ParseOptionalCXXScopeSpecifier(D.getCXXScopeSpec(), /*ObjectType=*/0, + true); if (afterCXXScope) { // Change the declaration context for name lookup, until this function // is exited (and the declarator has been parsed). diff --git a/lib/Parse/ParseDeclCXX.cpp b/lib/Parse/ParseDeclCXX.cpp index 31926ce0f5..f50147c599 100644 --- a/lib/Parse/ParseDeclCXX.cpp +++ b/lib/Parse/ParseDeclCXX.cpp @@ -117,7 +117,7 @@ Parser::DeclPtrTy Parser::ParseNamespaceAlias(SourceLocation NamespaceLoc, CXXScopeSpec SS; // Parse (optional) nested-name-specifier. - ParseOptionalCXXScopeSpecifier(SS); + ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/0, false); if (SS.isInvalid() || Tok.isNot(tok::identifier)) { Diag(Tok, diag::err_expected_namespace_name); @@ -216,7 +216,7 @@ Parser::DeclPtrTy Parser::ParseUsingDirective(unsigned Context, CXXScopeSpec SS; // Parse (optional) nested-name-specifier. - ParseOptionalCXXScopeSpecifier(SS); + ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/0, false); AttributeList *AttrList = 0; IdentifierInfo *NamespcName = 0; @@ -273,7 +273,7 @@ Parser::DeclPtrTy Parser::ParseUsingDeclaration(unsigned Context, IsTypeName = false; // Parse nested-name-specifier. - ParseOptionalCXXScopeSpecifier(SS); + ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/0, false); AttributeList *AttrList = 0; @@ -538,7 +538,8 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind, // Parse the (optional) nested-name-specifier. CXXScopeSpec SS; - if (getLang().CPlusPlus && ParseOptionalCXXScopeSpecifier(SS, true)) + if (getLang().CPlusPlus && + ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/0, true)) if (Tok.isNot(tok::identifier) && Tok.isNot(tok::annot_template_id)) Diag(Tok, diag::err_expected_ident); @@ -813,7 +814,7 @@ Parser::BaseResult Parser::ParseBaseSpecifier(DeclPtrTy ClassDecl) { // Parse optional '::' and optional nested-name-specifier. CXXScopeSpec SS; - ParseOptionalCXXScopeSpecifier(SS, true); + ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/0, true); // The location of the base class itself. SourceLocation BaseLoc = Tok.getLocation(); @@ -1299,7 +1300,7 @@ void Parser::ParseConstructorInitializer(DeclPtrTy ConstructorDecl) { Parser::MemInitResult Parser::ParseMemInitializer(DeclPtrTy ConstructorDecl) { // parse '::'[opt] nested-name-specifier[opt] CXXScopeSpec SS; - ParseOptionalCXXScopeSpecifier(SS); + ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/0, false); TypeTy *TemplateTypeTy = 0; if (Tok.is(tok::annot_template_id)) { TemplateIdAnnotation *TemplateId diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index 36b6dd4a93..8fca14ff98 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -923,14 +923,14 @@ Parser::ParsePostfixExpressionSuffix(OwningExprResult LHS) { tok::TokenKind OpKind = Tok.getKind(); SourceLocation OpLoc = ConsumeToken(); // Eat the "." or "->" token. - CXXScopeSpec MemberSS; CXXScopeSpec SS; + Action::TypeTy *ObjectType = 0; if (getLang().CPlusPlus && !LHS.isInvalid()) { - LHS = Actions.ActOnCXXEnterMemberScope(CurScope, MemberSS, move(LHS), - OpKind); + LHS = Actions.ActOnStartCXXMemberReference(CurScope, move(LHS), + OpLoc, OpKind, ObjectType); if (LHS.isInvalid()) break; - ParseOptionalCXXScopeSpecifier(SS); + ParseOptionalCXXScopeSpecifier(SS, ObjectType, false); } if (Tok.is(tok::identifier)) { @@ -947,8 +947,6 @@ Parser::ParsePostfixExpressionSuffix(OwningExprResult LHS) { ConsumeToken(); if (!Tok.is(tok::identifier)) { - if (getLang().CPlusPlus) - Actions.ActOnCXXExitMemberScope(CurScope, MemberSS); Diag(Tok, diag::err_expected_ident); return ExprError(); } @@ -980,8 +978,6 @@ Parser::ParsePostfixExpressionSuffix(OwningExprResult LHS) { Tok.getLocation(), ConvType, &SS); } else { - if (getLang().CPlusPlus) - Actions.ActOnCXXExitMemberScope(CurScope, MemberSS); // Don't emit a diagnostic; ParseConversionFunctionId does it for us return ExprError(); } @@ -1007,14 +1003,9 @@ Parser::ParsePostfixExpressionSuffix(OwningExprResult LHS) { } ConsumeToken(); } else { - if (getLang().CPlusPlus) - Actions.ActOnCXXExitMemberScope(CurScope, MemberSS); Diag(Tok, diag::err_expected_ident); return ExprError(); } - - if (getLang().CPlusPlus) - Actions.ActOnCXXExitMemberScope(CurScope, MemberSS); break; } case tok::plusplus: // postfix-expression: postfix-expression '++' diff --git a/lib/Parse/ParseExprCXX.cpp b/lib/Parse/ParseExprCXX.cpp index e9cca9fe1d..4cd952e393 100644 --- a/lib/Parse/ParseExprCXX.cpp +++ b/lib/Parse/ParseExprCXX.cpp @@ -16,10 +16,11 @@ #include "clang/Parse/DeclSpec.h" using namespace clang; -/// ParseOptionalCXXScopeSpecifier - Parse global scope or -/// nested-name-specifier if present. Returns true if a nested-name-specifier -/// was parsed from the token stream. Note that this routine will not parse -/// ::new or ::delete, it will just leave them in the token stream. +/// \brief Parse global scope or nested-name-specifier if present. +/// +/// Parses a C++ global scope specifier ('::') or nested-name-specifier (which +/// may be preceded by '::'). Note that this routine will not parse ::new or +/// ::delete; it will just leave them in the token stream. /// /// '::'[opt] nested-name-specifier /// '::' @@ -28,9 +29,22 @@ using namespace clang; /// type-name '::' /// namespace-name '::' /// nested-name-specifier identifier '::' -/// nested-name-specifier 'template'[opt] simple-template-id '::' [TODO] +/// nested-name-specifier 'template'[opt] simple-template-id '::' +/// +/// +/// \param SS the scope specifier that will be set to the parsed +/// nested-name-specifier (or empty) +/// +/// \param ObjectType if this nested-name-specifier is being parsed following +/// the "." or "->" of a member access expression, this parameter provides the +/// type of the object whose members are being accessed. /// +/// \param EnteringContext whether we will be entering into the context of +/// the nested-name-specifier after parsing it. +/// +/// \returns true if a scope specifier was parsed. bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS, + Action::TypeTy *ObjectType, bool EnteringContext) { assert(getLang().CPlusPlus && "Call sites of this function should be guarded by checking for C++"); @@ -59,16 +73,28 @@ bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS, } while (true) { + if (HasScopeSpecifier) { + // C++ [basic.lookup.classref]p5: + // If the qualified-id has the form + // ::class-name-or-namespace-name::... + // the class-name-or-namespace-name is looked up in global scope as a + // class-name or namespace-name. + // + // To implement this, we clear out the object type as soon as we've + // seen a leading '::' or part of a nested-name-specifier. + ObjectType = 0; + } + // nested-name-specifier: // nested-name-specifier 'template'[opt] simple-template-id '::' // Parse the optional 'template' keyword, then make sure we have // 'identifier <' after it. if (Tok.is(tok::kw_template)) { - // If we don't have a scope specifier, this isn't a + // If we don't have a scope specifier or an object type, this isn't a // nested-name-specifier, since they aren't allowed to start with // 'template'. - if (!HasScopeSpecifier) + if (!HasScopeSpecifier && !ObjectType) break; SourceLocation TemplateKWLoc = ConsumeToken(); @@ -91,7 +117,8 @@ bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS, TemplateTy Template = Actions.ActOnDependentTemplateName(TemplateKWLoc, *Tok.getIdentifierInfo(), - Tok.getLocation(), SS); + Tok.getLocation(), SS, + ObjectType); if (!Template) break; if (AnnotateTemplateIdToken(Template, TNK_Dependent_template_name, @@ -173,7 +200,7 @@ bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS, SS.setScopeRep( Actions.ActOnCXXNestedNameSpecifier(CurScope, SS, IdLoc, CCLoc, II, - EnteringContext)); + ObjectType, EnteringContext)); SS.setEndLoc(CCLoc); continue; } @@ -182,7 +209,10 @@ bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS, // type-name '<' if (Next.is(tok::less)) { TemplateTy Template; - if (TemplateNameKind TNK = Actions.isTemplateName(II, CurScope, &SS, + if (TemplateNameKind TNK = Actions.isTemplateName(CurScope, II, + Tok.getLocation(), + &SS, + ObjectType, EnteringContext, Template)) { // We have found a template name, so annotate this this token @@ -267,7 +297,7 @@ Parser::OwningExprResult Parser::ParseCXXIdExpression(bool isAddressOfOperand) { // '::' unqualified-id // CXXScopeSpec SS; - ParseOptionalCXXScopeSpecifier(SS); + ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/0, false); // unqualified-id: // identifier diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp index 4d37ac7202..36d5db599d 100644 --- a/lib/Parse/Parser.cpp +++ b/lib/Parse/Parser.cpp @@ -884,7 +884,8 @@ bool Parser::TryAnnotateTypeOrScopeToken(bool EnteringContext) { // simple-template-id SourceLocation TypenameLoc = ConsumeToken(); CXXScopeSpec SS; - bool HadNestedNameSpecifier = ParseOptionalCXXScopeSpecifier(SS, false); + bool HadNestedNameSpecifier + = ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/0, false); if (!HadNestedNameSpecifier) { Diag(Tok.getLocation(), diag::err_expected_qualified_after_typename); return false; @@ -928,7 +929,7 @@ bool Parser::TryAnnotateTypeOrScopeToken(bool EnteringContext) { CXXScopeSpec SS; if (getLang().CPlusPlus) - ParseOptionalCXXScopeSpecifier(SS, EnteringContext); + ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/0, EnteringContext); if (Tok.is(tok::identifier)) { // Determine whether the identifier is a type name. @@ -959,8 +960,10 @@ bool Parser::TryAnnotateTypeOrScopeToken(bool EnteringContext) { if (NextToken().is(tok::less)) { TemplateTy Template; if (TemplateNameKind TNK - = Actions.isTemplateName(*Tok.getIdentifierInfo(), - CurScope, &SS, EnteringContext, Template)) + = Actions.isTemplateName(CurScope, *Tok.getIdentifierInfo(), + Tok.getLocation(), &SS, + /*ObjectType=*/0, EnteringContext, + Template)) if (AnnotateTemplateIdToken(Template, TNK, &SS)) { // If an unrecoverable error occurred, we need to return true here, // because the token stream is in a damaged state. We may not return @@ -1022,7 +1025,7 @@ bool Parser::TryAnnotateCXXScopeToken(bool EnteringContext) { "Cannot be a type or scope token!"); CXXScopeSpec SS; - if (!ParseOptionalCXXScopeSpecifier(SS, EnteringContext)) + if (!ParseOptionalCXXScopeSpecifier(SS, /*ObjectType=*/0, EnteringContext)) return Tok.is(tok::annot_template_id); // Push the current token back into the token stream (or revert it if it is diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index e2205d6df5..6f462e593b 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -1983,6 +1983,12 @@ public: TypeTy *Ty, SourceLocation RParen); + virtual OwningExprResult ActOnStartCXXMemberReference(Scope *S, + ExprArg Base, + SourceLocation OpLoc, + tok::TokenKind OpKind, + TypeTy *&ObjectType); + virtual OwningExprResult ActOnDestructorReferenceExpr(Scope *S, ExprArg Base, SourceLocation OpLoc, @@ -2029,6 +2035,7 @@ public: bool RequireCompleteDeclContext(const CXXScopeSpec &SS); + DeclContext *computeDeclContext(QualType T); DeclContext *computeDeclContext(const CXXScopeSpec &SS, bool EnteringContext = false); bool isDependentScopeSpecifier(const CXXScopeSpec &SS); @@ -2051,6 +2058,7 @@ public: SourceLocation IdLoc, SourceLocation CCLoc, IdentifierInfo &II, + TypeTy *ObjectType, bool EnteringContext); /// ActOnCXXNestedNameSpecifier - Called during parsing of a @@ -2067,23 +2075,6 @@ public: SourceRange TypeRange, SourceLocation CCLoc); - /// ActOnCXXEnterMemberScope - Called when a C++ class member accessor ('.' - /// or '->') is parsed. After this method is called, according to - /// [C++ 3.4.5p4], qualified-ids should be looked up in the contexts of both - /// the entire postfix-expression and the scope of the class of the object - /// expression. - /// 'SS' should be an empty CXXScopeSpec to be filled with the class's scope. - virtual OwningExprResult ActOnCXXEnterMemberScope(Scope *S, CXXScopeSpec &SS, - ExprArg Base, - tok::TokenKind OpKind); - - /// ActOnCXXExitMemberScope - Called when a postfix-expression that previously - /// invoked ActOnCXXEnterMemberScope() is finished. 'SS' is the same - /// CXXScopeSpec that was passed to ActOnCXXEnterMemberScope. Used to - /// indicate that names should revert to being looked up in the defining - /// scope. - virtual void ActOnCXXExitMemberScope(Scope *S, const CXXScopeSpec &SS); - /// ActOnCXXEnterDeclaratorScope - Called when a C++ scope specifier (global /// scope or nested-name-specifier) is parsed, part of a declarator-id. /// After this method is called, according to [C++ 3.4.3p3], names should be @@ -2311,8 +2302,11 @@ public: //===--------------------------------------------------------------------===// // C++ Templates [C++ 14] // - virtual TemplateNameKind isTemplateName(const IdentifierInfo &II, Scope *S, + virtual TemplateNameKind isTemplateName(Scope *S, + const IdentifierInfo &II, + SourceLocation IdLoc, const CXXScopeSpec *SS, + TypeTy *ObjectType, bool EnteringContext, TemplateTy &Template); bool DiagnoseTemplateParameterShadow(SourceLocation Loc, Decl *PrevDecl); @@ -2400,7 +2394,8 @@ public: virtual TemplateTy ActOnDependentTemplateName(SourceLocation TemplateKWLoc, const IdentifierInfo &Name, SourceLocation NameLoc, - const CXXScopeSpec &SS); + const CXXScopeSpec &SS, + TypeTy *ObjectType); bool CheckClassTemplateSpecializationScope(ClassTemplateDecl *ClassTemplate, ClassTemplateSpecializationDecl *PrevDecl, diff --git a/lib/Sema/SemaCXXScopeSpec.cpp b/lib/Sema/SemaCXXScopeSpec.cpp index d89a610cae..251ffea925 100644 --- a/lib/Sema/SemaCXXScopeSpec.cpp +++ b/lib/Sema/SemaCXXScopeSpec.cpp @@ -22,6 +22,20 @@ #include "llvm/Support/raw_ostream.h" using namespace clang; +/// \brief Compute the DeclContext that is associated with the given type. +/// +/// \param T the type for which we are attempting to find a DeclContext. +/// +/// \returns the declaration context represented by the type T, +/// or NULL if the declaration context cannot be computed (e.g., because it is +/// dependent and not the current instantiation). +DeclContext *Sema::computeDeclContext(QualType T) { + if (const TagType *Tag = T->getAs<TagType>()) + return Tag->getDecl(); + + return 0; +} + /// \brief Compute the DeclContext that is associated with the given /// scope specifier. /// @@ -244,6 +258,36 @@ Sema::CXXScopeTy *Sema::ActOnCXXGlobalScopeSpecifier(Scope *S, return NestedNameSpecifier::GlobalSpecifier(Context); } +/// \brief Determines whether the given declaration is an valid acceptable +/// result for name lookup of a nested-name-specifier. +bool isAcceptableNestedNameSpecifier(ASTContext &Context, NamedDecl *SD) { + if (!SD) + return false; + + // Namespace and namespace aliases are fine. + if (isa<NamespaceDecl>(SD) || isa<NamespaceAliasDecl>(SD)) + return true; + + if (!isa<TypeDecl>(SD)) + return false; + + // Determine whether we have a class (or, in C++0x, an enum) or + // a typedef thereof. If so, build the nested-name-specifier. + QualType T = Context.getTypeDeclType(cast<TypeDecl>(SD)); + if (T->isDependentType()) + return true; + else if (TypedefDecl *TD = dyn_cast<TypedefDecl>(SD)) { + if (TD->getUnderlyingType()->isRecordType() || + (Context.getLangOptions().CPlusPlus0x && + TD->getUnderlyingType()->isEnumeralType())) + return true; + } else if (isa<RecordDecl>(SD) || + (Context.getLangOptions().CPlusPlus0x && isa<EnumDecl>(SD))) + return true; + + return false; +} + /// ActOnCXXNestedNameSpecifier - Called during parsing of a /// nested-name-specifier. e.g. for "foo::bar::" we parsed "foo::" and now /// we want to resolve "bar::". 'SS' is empty or the previously parsed @@ -255,58 +299,134 @@ Sema::CXXScopeTy *Sema::ActOnCXXNestedNameSpecifier(Scope *S, SourceLocation IdLoc, SourceLocation CCLoc, IdentifierInfo &II, + TypeTy *ObjectTypePtr, bool EnteringContext) { NestedNameSpecifier *Prefix = static_cast<NestedNameSpecifier *>(SS.getScopeRep()); + + // Determine where to perform name lookup + DeclContext *LookupCtx = 0; + bool isDependent = false; + if (ObjectTypePtr) { + // This nested-name-specifier occurs in a member access expression, e.g., + // x->B::f, and we are looking into the type of the object. + assert(!SS.isSet() && "ObjectType and scope specifier cannot coexist"); + QualType ObjectType = QualType::getFromOpaquePtr(ObjectTypePtr); + LookupCtx = computeDeclContext(ObjectType); + isDependent = ObjectType->isDependentType(); + } else if (SS.isSet()) { + // This nested-name-specifier occurs after another nested-name-specifier, + // so long into the context associated with the prior nested-name-specifier. + LookupCtx = computeDeclContext(SS, EnteringContext); + isDependent = isDependentScopeSpecifier(SS); + } - NamedDecl *SD = LookupParsedName(S, &SS, &II, LookupNestedNameSpecifierName, - false, false, SourceLocation(), - EnteringContext); + LookupResult Found; + bool ObjectTypeSearchedInScope = false; + if (LookupCtx) { + // Perform "qualified" name lookup into the declaration context we + // computed, which is either the type of the base of a member access + // expression or the declaration context associated with a prior + // nested-name-specifier. + + // The declaration context must be complete. + if (!LookupCtx->isDependentContext() && RequireCompleteDeclContext(SS)) + return 0; - if (SD) { + Found = LookupQualifiedName(LookupCtx, &II, LookupNestedNameSpecifierName, + false); + + if (ObjectTypePtr && Found.getKind() == LookupResult::NotFound && S) { + // C++ [basic.lookup.classref]p4: + // If the id-expression in a class member access is a qualified-id of + // the form + // + // class-name-or-namespace-name::... + // + // the class-name-or-namespace-name following the . or -> operator is + // looked up both in the context of the entire postfix-expression and in + // the scope of the class of the object expression. If the name is found + // only in the scope of the class of the object expression, the name + // shall refer to a class-name. If the name is found only in the + // context of the entire postfix-expression, the name shall refer to a + // class-name or namespace-name. [...] + // + // Qualified name lookup into a class will not find a namespace-name, + // so we do not need to diagnoste that case specifically. However, + // this qualified name lookup may find nothing. In that case, perform + // unqualified name lookup in the given scope. + + // FIXME: When we're instantiating a template, do we actually have to + // look in the scope of the template? Seems fishy... + Found = LookupName(S, &II, LookupNestedNameSpecifierName); + ObjectTypeSearchedInScope = true; + } + } else if (isDependent) { + // We were not able to compute the declaration context for a dependent + // base object type or prior nested-name-specifier, so this + // nested-name-specifier refers to an unknown specialization. Just build + // a dependent nested-name-specifier. + return NestedNameSpecifier::Create(Context, Prefix, &II); + } else { + // Perform unqualified name lookup in the current scope. + Found = LookupName(S, &II, LookupNestedNameSpecifierName); + } + + // FIXME: Deal with ambiguities cleanly. + NamedDecl *SD = Found; + if (isAcceptableNestedNameSpecifier(Context, SD)) { + if (ObjectTypePtr && !ObjectTypeSearchedInScope && S) { + // C++ [basic.lookup.classref]p4: + // [...] If the name is found in both contexts, the + // class-name-or-namespace-name shall refer to the same entity. + // + // We already found the name in the scope of the object. Now, look + // into the current scope (the scope of the postfix-expression) to + // see if we can find the same name there. + LookupResult FoundOuter + = LookupName(S, &II, LookupNestedNameSpecifierName); + + // FIXME: Handle ambiguities in FoundOuter! + NamedDecl *OuterDecl = FoundOuter; + if (isAcceptableNestedNameSpecifier(Context, OuterDecl) && + OuterDecl->getCanonicalDecl() != SD->getCanonicalDecl() && + (!isa<TypeDecl>(OuterDecl) || !isa<TypeDecl>(SD) || + !Context.hasSameType( + Context.getTypeDeclType(cast<TypeDecl>(OuterDecl)), + Context.getTypeDeclType(cast<TypeDecl>(SD))))) { + Diag(IdLoc, diag::err_nested_name_member_ref_lookup_ambiguous) + << &II; + Diag(SD->getLocation(), diag::note_ambig_member_ref_object_type) + << QualType::getFromOpaquePtr(ObjectTypePtr); + Diag(OuterDecl->getLocation(), diag::note_ambig_member_ref_scope); + + // Fall through so that we'll pick the name we found in the object type, + // since that's probably what the user wanted anyway. + } + } + if (NamespaceDecl *Namespace = dyn_cast<NamespaceDecl>(SD)) return NestedNameSpecifier::Create(Context, Prefix, Namespace); - if (TypeDecl *Type = dyn_cast<TypeDecl>(SD)) { - // Determine whether we have a class (or, in C++0x, an enum) or - // a typedef thereof. If so, build the nested-name-specifier. - QualType T = Context.getTypeDeclType(Type); - bool AcceptableType = false; - if (T->isDependentType()) - AcceptableType = true; - else if (TypedefDecl *TD = dyn_cast<TypedefDecl>(SD)) { - if (TD->getUnderlyingType()->isRecordType() || - (getLangOptions().CPlusPlus0x && - TD->getUnderlyingType()->isEnumeralType())) - AcceptableType = true; - } else if (isa<RecordDecl>(Type) || - (getLangOptions().CPlusPlus0x && isa<EnumDecl>(Type))) - AcceptableType = true; - - if (AcceptableType) - return NestedNameSpecifier::Create(Context, Prefix, false, - T.getTypePtr()); - } - // FIXME: It would be nice to maintain the namespace alias name, then // see through that alias when resolving the nested-name-specifier down to // a declaration context. if (NamespaceAliasDecl *Alias = dyn_cast<NamespaceAliasDecl>(SD)) return NestedNameSpecifier::Create(Context, Prefix, - Alias->getNamespace()); - // Fall through to produce an error: we found something that isn't - // a class or a namespace. - } else if (SS.isSet() && isDependentScopeSpecifier(SS)) - return NestedNameSpecifier::Create(Context, Prefix, &II); + Alias->getNamespace()); + + QualType T = Context.getTypeDeclType(cast<TypeDecl>(SD)); + return NestedNameSpecifier::Create(Context, Prefix, false, + T.getTypePtr()); + } // If we didn't find anything during our lookup, try again with // ordinary name lookup, which can help us produce better error // messages. if (!SD) - SD = LookupParsedName(S, &SS, &II, LookupOrdinaryName, - false, false, SourceLocation(), - EnteringContext); + SD = LookupName(S, &II, LookupOrdinaryName); + unsigned DiagID; if (SD) DiagID = diag::err_expected_class_or_namespace; @@ -338,58 +458,6 @@ Sema::CXXScopeTy *Sema::ActOnCXXNestedNameSpecifier(Scope *S, T.getTypePtr()); } -Action::OwningExprResult -Sema::ActOnCXXEnterMemberScope(Scope *S, CXXScopeSpec &SS, ExprArg Base, - tok::TokenKind OpKind) { - // Since this might be a postfix expression, get rid of ParenListExprs. - Base = MaybeConvertParenListExprToParenExpr(S, move(Base)); - - Expr *BaseExpr = (Expr*)Base.get(); - assert(BaseExpr && "no record expansion"); - - QualType BaseType = BaseExpr->getType(); - // FIXME: handle dependent types - if (BaseType->isDependentType()) - return move(Base); - - // C++ [over.match.oper]p8: - // [...] When operator->returns, the operator-> is applied to the value - // returned, with the original second operand. - if (OpKind == tok::arrow) { - while (BaseType->isRecordType()) { - Base = BuildOverloadedArrowExpr(S, move(Base), BaseExpr->getExprLoc()); - BaseExpr = (Expr*)Base.get(); - if (BaseExpr == NULL) - return ExprError(); - BaseType = BaseExpr->getType(); - } - } - - if (BaseType->isPointerType()) - BaseType = BaseType->getPointeeType(); - - // We could end up with various non-record types here, such as extended - // vector types or Objective-C interfaces. Just return early and let - // ActOnMemberReferenceExpr do the work. - if (!BaseType->isRecordType()) - return move(Base); - - SS.setRange(BaseExpr->getSourceRange()); - SS.setScopeRep( - NestedNameSpecifier::Create(Context, 0, false, BaseType.getTypePtr()) - ); - - if (S) - ActOnCXXEnterDeclaratorScope(S,SS); - return move(Base); -} - -void Sema::ActOnCXXExitMemberScope(Scope *S, const CXXScopeSpec &SS) { - if (S && SS.isSet()) - ActOnCXXExitDeclaratorScope(S,SS); -} - - /// ActOnCXXEnterDeclaratorScope - Called when a C++ scope specifier (global /// scope or nested-name-specifier) is parsed, part of a declarator-id. /// After this method is called, according to [C++ 3.4.3p3], names should be diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index 0dc4b8d180..87330ab0b6 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -1754,6 +1754,48 @@ Expr *Sema::MaybeCreateCXXExprWithTemporaries(Expr *SubExpr, return E; } +Sema::OwningExprResult +Sema::ActOnStartCXXMemberReference(Scope *S, ExprArg Base, SourceLocation OpLoc, + tok::TokenKind OpKind, TypeTy *&ObjectType) { + // Since this might be a postfix expression, get rid of ParenListExprs. + Base = MaybeConvertParenListExprToParenExpr(S, move(Base)); + + Expr *BaseExpr = (Expr*)Base.get(); + assert(BaseExpr && "no record expansion"); + + QualType BaseType = BaseExpr->getType(); + if (BaseType->isDependentType()) { + // FIXME: member of the current instantiation + ObjectType = BaseType.getAsOpaquePtr(); + return move(Base); + } + + // C++ [over.match.oper]p8: + // [...] When operator->returns, the operator-> is applied to the value + // returned, with the original second operand. + if (OpKind == tok::arrow) { + while (BaseType->isRecordType()) { + Base = BuildOverloadedArrowExpr(S, move(Base), BaseExpr->getExprLoc()); + BaseExpr = (Expr*)Base.get(); + if (BaseExpr == NULL) + return ExprError(); + BaseType = BaseExpr->getType(); + } + } + + if (BaseType->isPointerType()) + BaseType = BaseType->getPointeeType(); + + // We could end up with various non-record types here, such as extended + // vector types or Objective-C interfaces. Just return early and let + // ActOnMemberReferenceExpr do the work. + if (!BaseType->isRecordType()) + return move(Base); + + ObjectType = BaseType.getAsOpaquePtr(); + return move(Base); +} + Sema::OwningExprResult Sema::ActOnDestructorReferenceExpr(Scope *S, ExprArg Base, SourceLocation OpLoc, |