From c68afe2cbe7f875a9243c411077602fb5f5dc74b Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Thu, 3 Sep 2009 21:38:09 +0000 Subject: [PATCH] Improve template instantiation for member access expressions that involve qualified names, e.g., x->Base::f. We now maintain enough information in the AST to compare the results of the name lookup of "Base" in the scope of the postfix-expression (determined at template definition time) and in the type of the object expression. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@80953 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/ExprCXX.h | 26 +++++ lib/Sema/Sema.h | 27 +++-- lib/Sema/SemaCXXScopeSpec.cpp | 125 ++++++++++++++++------- lib/Sema/SemaExpr.cpp | 29 +++++- lib/Sema/SemaExprCXX.cpp | 13 ++- lib/Sema/SemaTemplateInstantiate.cpp | 8 +- lib/Sema/TreeTransform.h | 50 ++++++--- test/SemaCXX/qual-id-test.cpp | 26 +++-- test/SemaTemplate/member-access-expr.cpp | 8 +- www/cxx_status.html | 9 +- 10 files changed, 236 insertions(+), 85 deletions(-) diff --git a/include/clang/AST/ExprCXX.h b/include/clang/AST/ExprCXX.h index a308889af0..f450834bc4 100644 --- a/include/clang/AST/ExprCXX.h +++ b/include/clang/AST/ExprCXX.h @@ -1306,6 +1306,15 @@ class CXXUnresolvedMemberExpr : public Expr { /// \brief The source range covering the nested name specifier. SourceRange QualifierRange; + /// \brief In a qualified member access expression such as t->Base::f, this + /// member stores the resolves of name lookup in the context of the member + /// access expression, to be used at instantiation time. + /// + /// FIXME: This member, along with the Qualifier and QualifierRange, could + /// be stuck into a structure that is optionally allocated at the end of + /// the CXXUnresolvedMemberExpr, to save space in the common case. + NamedDecl *FirstQualifierFoundInScope; + /// \brief The member to which this member expression refers, which /// can be name, overloaded operator, or destructor. /// FIXME: could also be a template-id @@ -1320,11 +1329,13 @@ public: SourceLocation OperatorLoc, NestedNameSpecifier *Qualifier, SourceRange QualifierRange, + NamedDecl *FirstQualifierFoundInScope, DeclarationName Member, SourceLocation MemberLoc) : Expr(CXXUnresolvedMemberExprClass, C.DependentTy, true, true), Base(Base), IsArrow(IsArrow), OperatorLoc(OperatorLoc), Qualifier(Qualifier), QualifierRange(QualifierRange), + FirstQualifierFoundInScope(FirstQualifierFoundInScope), Member(Member), MemberLoc(MemberLoc) { } /// \brief Retrieve the base object of this member expressions, @@ -1349,6 +1360,21 @@ public: /// that qualifies the member name. SourceRange getQualifierRange() const { return QualifierRange; } + /// \brief Retrieve the first part of the nested-name-specifier that was + /// found in the scope of the member access expression when the member access + /// was initially parsed. + /// + /// This function only returns a useful result when member access expression + /// uses a qualified member name, e.g., "x.Base::f". Here, the declaration + /// returned by this function describes what was found by unqualified name + /// lookup for the identifier "Base" within the scope of the member access + /// expression itself. At template instantiation time, this information is + /// combined with the results of name lookup into the type of the object + /// expression itself (the class type of x). + NamedDecl *getFirstQualifierFoundInScope() const { + return FirstQualifierFoundInScope; + } + /// \brief Retrieve the name of the member that this expression /// refers to. DeclarationName getMember() const { return Member; } diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index a992936d18..766d18bde1 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -1610,13 +1610,15 @@ public: SourceLocation MemberLoc, DeclarationName MemberName, DeclPtrTy ImplDecl, - const CXXScopeSpec *SS = 0) { + const CXXScopeSpec *SS = 0, + NamedDecl *FirstQualifierInScope = 0) { // FIXME: Temporary helper while we migrate existing calls to // BuildMemberReferenceExpr to support explicitly-specified template // arguments. return BuildMemberReferenceExpr(S, move(Base), OpLoc, OpKind, MemberLoc, MemberName, false, SourceLocation(), 0, 0, - SourceLocation(), ImplDecl, SS); + SourceLocation(), ImplDecl, SS, + FirstQualifierInScope); } OwningExprResult BuildMemberReferenceExpr(Scope *S, ExprArg Base, @@ -1630,7 +1632,8 @@ public: unsigned NumExplicitTemplateArgs, SourceLocation RAngleLoc, DeclPtrTy ImplDecl, - const CXXScopeSpec *SS = 0); + const CXXScopeSpec *SS, + NamedDecl *FirstQualifierInScope = 0); virtual OwningExprResult ActOnMemberReferenceExpr(Scope *S, ExprArg Base, SourceLocation OpLoc, @@ -2047,12 +2050,18 @@ public: virtual CXXScopeTy *ActOnCXXGlobalScopeSpecifier(Scope *S, SourceLocation CCLoc); - /// 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 - /// nested-name part ("foo::"), 'IdLoc' is the source location of 'bar', - /// 'CCLoc' is the location of '::' and 'II' is the identifier for 'bar'. - /// Returns a CXXScopeTy* object representing the C++ scope. + NamedDecl *FindFirstQualifierInScope(Scope *S, NestedNameSpecifier *NNS); + + + CXXScopeTy *BuildCXXNestedNameSpecifier(Scope *S, + const CXXScopeSpec &SS, + SourceLocation IdLoc, + SourceLocation CCLoc, + IdentifierInfo &II, + QualType ObjectType, + NamedDecl *ScopeLookupResult, + bool EnteringContext); + virtual CXXScopeTy *ActOnCXXNestedNameSpecifier(Scope *S, const CXXScopeSpec &SS, SourceLocation IdLoc, diff --git a/lib/Sema/SemaCXXScopeSpec.cpp b/lib/Sema/SemaCXXScopeSpec.cpp index 352e553edb..fb6c88ebc4 100644 --- a/lib/Sema/SemaCXXScopeSpec.cpp +++ b/lib/Sema/SemaCXXScopeSpec.cpp @@ -288,18 +288,45 @@ bool isAcceptableNestedNameSpecifier(ASTContext &Context, NamedDecl *SD) { 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 -/// nested-name part ("foo::"), 'IdLoc' is the source location of 'bar', -/// 'CCLoc' is the location of '::' and 'II' is the identifier for 'bar'. -/// Returns a CXXScopeTy* object representing the C++ scope. -Sema::CXXScopeTy *Sema::ActOnCXXNestedNameSpecifier(Scope *S, +/// \brief If the given nested-name-specifier begins with a bare identifier +/// (e.g., Base::), perform name lookup for that identifier as a +/// nested-name-specifier within the given scope, and return the result of that +/// name lookup. +NamedDecl *Sema::FindFirstQualifierInScope(Scope *S, NestedNameSpecifier *NNS) { + if (!S || !NNS) + return 0; + + while (NNS->getPrefix()) + NNS = NNS->getPrefix(); + + if (NNS->getKind() != NestedNameSpecifier::Identifier) + return 0; + + LookupResult Found + = LookupName(S, NNS->getAsIdentifier(), LookupNestedNameSpecifierName); + assert(!Found.isAmbiguous() && "Cannot handle ambiguities here yet"); + + NamedDecl *Result = Found; + if (isAcceptableNestedNameSpecifier(Context, Result)) + return Result; + + return 0; +} + +/// \brief Build a new nested-name-specifier for "identifier::", as described +/// by ActOnCXXNestedNameSpecifier. +/// +/// This routine differs only slightly from ActOnCXXNestedNameSpecifier, in +/// that it contains an extra parameter \p ScopeLookupResult, which provides +/// the result of name lookup within the scope of the nested-name-specifier +/// that was computed at template definitino time. +Sema::CXXScopeTy *Sema::BuildCXXNestedNameSpecifier(Scope *S, const CXXScopeSpec &SS, SourceLocation IdLoc, SourceLocation CCLoc, IdentifierInfo &II, - TypeTy *ObjectTypePtr, + QualType ObjectType, + NamedDecl *ScopeLookupResult, bool EnteringContext) { NestedNameSpecifier *Prefix = static_cast(SS.getScopeRep()); @@ -307,11 +334,10 @@ Sema::CXXScopeTy *Sema::ActOnCXXNestedNameSpecifier(Scope *S, // Determine where to perform name lookup DeclContext *LookupCtx = 0; bool isDependent = false; - if (ObjectTypePtr) { + if (!ObjectType.isNull()) { // 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()) { @@ -320,7 +346,7 @@ Sema::CXXScopeTy *Sema::ActOnCXXNestedNameSpecifier(Scope *S, LookupCtx = computeDeclContext(SS, EnteringContext); isDependent = isDependentScopeSpecifier(SS); } - + LookupResult Found; bool ObjectTypeSearchedInScope = false; if (LookupCtx) { @@ -332,11 +358,11 @@ Sema::CXXScopeTy *Sema::ActOnCXXNestedNameSpecifier(Scope *S, // The declaration context must be complete. if (!LookupCtx->isDependentContext() && RequireCompleteDeclContext(SS)) return 0; - + Found = LookupQualifiedName(LookupCtx, &II, LookupNestedNameSpecifierName, false); - if (ObjectTypePtr && Found.getKind() == LookupResult::NotFound && S) { + if (!ObjectType.isNull() && Found.getKind() == LookupResult::NotFound) { // C++ [basic.lookup.classref]p4: // If the id-expression in a class member access is a qualified-id of // the form @@ -354,12 +380,14 @@ Sema::CXXScopeTy *Sema::ActOnCXXNestedNameSpecifier(Scope *S, // 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? Both EDG and GCC do it; GCC - // requires the lookup to be successful, EDG doesn't. - Found = LookupName(S, &II, LookupNestedNameSpecifierName); + // unqualified name lookup in the given scope (if available) or + // reconstruct the result from when name lookup was performed at template + // definition time. + if (S) + Found = LookupName(S, &II, LookupNestedNameSpecifierName); + else + Found = LookupResult::CreateLookupResult(Context, ScopeLookupResult); + ObjectTypeSearchedInScope = true; } } else if (isDependent) { @@ -379,16 +407,21 @@ Sema::CXXScopeTy *Sema::ActOnCXXNestedNameSpecifier(Scope *S, // FIXME: Deal with ambiguities cleanly. NamedDecl *SD = Found; if (isAcceptableNestedNameSpecifier(Context, SD)) { - if (ObjectTypePtr && !ObjectTypeSearchedInScope && S) { + if (!ObjectType.isNull() && !ObjectTypeSearchedInScope) { // 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); + // see if we can find the same name there. As above, if there is no + // scope, reconstruct the result from the template instantiation itself. + LookupResult FoundOuter; + if (S) + FoundOuter = LookupName(S, &II, LookupNestedNameSpecifierName); + else + FoundOuter = LookupResult::CreateLookupResult(Context, + ScopeLookupResult); // FIXME: Handle ambiguities in FoundOuter! NamedDecl *OuterDecl = FoundOuter; @@ -396,35 +429,35 @@ Sema::CXXScopeTy *Sema::ActOnCXXNestedNameSpecifier(Scope *S, OuterDecl->getCanonicalDecl() != SD->getCanonicalDecl() && (!isa(OuterDecl) || !isa(SD) || !Context.hasSameType( - Context.getTypeDeclType(cast(OuterDecl)), + Context.getTypeDeclType(cast(OuterDecl)), Context.getTypeDeclType(cast(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); + Diag(IdLoc, diag::err_nested_name_member_ref_lookup_ambiguous) + << &II; + Diag(SD->getLocation(), diag::note_ambig_member_ref_object_type) + << ObjectType; + 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. - } + // 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(SD)) return NestedNameSpecifier::Create(Context, Prefix, Namespace); - + // 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(SD)) return NestedNameSpecifier::Create(Context, Prefix, - + Alias->getNamespace()); QualType T = Context.getTypeDeclType(cast(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. @@ -441,15 +474,33 @@ Sema::CXXScopeTy *Sema::ActOnCXXNestedNameSpecifier(Scope *S, return 0; } else DiagID = diag::err_undeclared_var_use; - + if (SS.isSet()) Diag(IdLoc, DiagID) << &II << SS.getRange(); else Diag(IdLoc, DiagID) << &II; - + return 0; } +/// 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 +/// nested-name part ("foo::"), 'IdLoc' is the source location of 'bar', +/// 'CCLoc' is the location of '::' and 'II' is the identifier for 'bar'. +/// Returns a CXXScopeTy* object representing the C++ scope. +Sema::CXXScopeTy *Sema::ActOnCXXNestedNameSpecifier(Scope *S, + const CXXScopeSpec &SS, + SourceLocation IdLoc, + SourceLocation CCLoc, + IdentifierInfo &II, + TypeTy *ObjectTypePtr, + bool EnteringContext) { + return BuildCXXNestedNameSpecifier(S, SS, IdLoc, CCLoc, II, + QualType::getFromOpaquePtr(ObjectTypePtr), + /*ScopeLookupResult=*/0, EnteringContext); +} + Sema::CXXScopeTy *Sema::ActOnCXXNestedNameSpecifier(Scope *S, const CXXScopeSpec &SS, TypeTy *Ty, diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index 394bc97927..720217a910 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -1989,7 +1989,8 @@ Sema::BuildMemberReferenceExpr(Scope *S, ExprArg Base, SourceLocation OpLoc, const TemplateArgument *ExplicitTemplateArgs, unsigned NumExplicitTemplateArgs, SourceLocation RAngleLoc, - DeclPtrTy ObjCImpDecl, const CXXScopeSpec *SS) { + DeclPtrTy ObjCImpDecl, const CXXScopeSpec *SS, + NamedDecl *FirstQualifierInScope) { if (SS && SS->isInvalid()) return ExprError(); @@ -2022,14 +2023,23 @@ Sema::BuildMemberReferenceExpr(Scope *S, ExprArg Base, SourceLocation OpLoc, // Get the type being accessed in BaseType. If this is an arrow, the BaseExpr // must have pointer type, and the accessed type is the pointee. if (OpKind == tok::arrow) { - if (BaseType->isDependentType()) + if (BaseType->isDependentType()) { + NestedNameSpecifier *Qualifier = 0; + if (SS) { + Qualifier = static_cast(SS->getScopeRep()); + if (!FirstQualifierInScope) + FirstQualifierInScope = FindFirstQualifierInScope(S, Qualifier); + } + return Owned(new (Context) CXXUnresolvedMemberExpr(Context, BaseExpr, true, OpLoc, - (NestedNameSpecifier *)(SS? SS->getScopeRep() : 0), + Qualifier, SS? SS->getRange() : SourceRange(), + FirstQualifierInScope, MemberName, MemberLoc)); + } else if (const PointerType *PT = BaseType->getAs()) BaseType = PT->getPointeeType(); else if (BaseType->isObjCObjectPointerType()) @@ -2051,14 +2061,23 @@ Sema::BuildMemberReferenceExpr(Scope *S, ExprArg Base, SourceLocation OpLoc, const PointerType *PT = BaseType->getAs(); if (!PT || (getLangOptions().ObjC1 && - !PT->getPointeeType()->isRecordType())) + !PT->getPointeeType()->isRecordType())) { + NestedNameSpecifier *Qualifier = 0; + if (SS) { + Qualifier = static_cast(SS->getScopeRep()); + if (!FirstQualifierInScope) + FirstQualifierInScope = FindFirstQualifierInScope(S, Qualifier); + } + return Owned(new (Context) CXXUnresolvedMemberExpr(Context, BaseExpr, false, OpLoc, - (NestedNameSpecifier *)(SS? SS->getScopeRep() : 0), + Qualifier, SS? SS->getRange() : SourceRange(), + FirstQualifierInScope, MemberName, MemberLoc)); + } } } diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index 87330ab0b6..b88ee89752 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -1789,9 +1789,20 @@ Sema::ActOnStartCXXMemberReference(Scope *S, ExprArg Base, SourceLocation OpLoc, // 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()) + if (!BaseType->isRecordType()) { + // C++ [basic.lookup.classref]p2: + // [...] If the type of the object expression is of pointer to scalar + // type, the unqualified-id is looked up in the context of the complete + // postfix-expression. + ObjectType = 0; return move(Base); + } + // C++ [basic.lookup.classref]p2: + // If the id-expression in a class member access (5.2.5) is an + // unqualified-id, and the type of the object expres- sion is of a class + // type C (or of pointer to a class type C), the unqualified-id is looked + // up in the scope of class C. [...] ObjectType = BaseType.getAsOpaquePtr(); return move(Base); } diff --git a/lib/Sema/SemaTemplateInstantiate.cpp b/lib/Sema/SemaTemplateInstantiate.cpp index c03f2e19aa..0c47e996c8 100644 --- a/lib/Sema/SemaTemplateInstantiate.cpp +++ b/lib/Sema/SemaTemplateInstantiate.cpp @@ -359,8 +359,10 @@ namespace { } Decl *TemplateInstantiator::TransformDecl(Decl *D) { - if (TemplateTemplateParmDecl *TTP - = dyn_cast_or_null(D)) { + if (!D) + return 0; + + if (TemplateTemplateParmDecl *TTP = dyn_cast(D)) { if (TTP->getDepth() < TemplateArgs.getNumLevels()) { assert(TemplateArgs(TTP->getDepth(), TTP->getPosition()).getAsDecl() && "Wrong kind of template template argument"); @@ -381,7 +383,7 @@ Decl *TemplateInstantiator::TransformDecl(Decl *D) { "Reducing depth of template template parameters is not yet implemented"); } - return SemaRef.FindInstantiatedDecl(cast_or_null(D)); + return SemaRef.FindInstantiatedDecl(cast(D)); } Decl *TemplateInstantiator::TransformDefinition(Decl *D) { diff --git a/lib/Sema/TreeTransform.h b/lib/Sema/TreeTransform.h index 20259a7f79..7babcf76b7 100644 --- a/lib/Sema/TreeTransform.h +++ b/lib/Sema/TreeTransform.h @@ -15,6 +15,7 @@ #include "Sema.h" #include "clang/Sema/SemaDiagnostic.h" +#include "clang/AST/Decl.h" #include "clang/AST/Expr.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/ExprObjC.h" @@ -243,7 +244,8 @@ public: /// alternate behavior. NestedNameSpecifier *TransformNestedNameSpecifier(NestedNameSpecifier *NNS, SourceRange Range, - QualType ObjectType = QualType()); + QualType ObjectType = QualType(), + NamedDecl *FirstQualifierInScope = 0); /// \brief Transform the given template name. /// @@ -499,7 +501,8 @@ public: NestedNameSpecifier *RebuildNestedNameSpecifier(NestedNameSpecifier *Prefix, SourceRange Range, IdentifierInfo &II, - QualType ObjectType); + QualType ObjectType, + NamedDecl *FirstQualifierInScope); /// \brief Build a new nested-name-specifier given the prefix and the /// namespace named in the next step in the nested-name-specifier. @@ -1454,7 +1457,8 @@ public: NestedNameSpecifier *Qualifier, SourceRange QualifierRange, DeclarationName Name, - SourceLocation MemberLoc) { + SourceLocation MemberLoc, + NamedDecl *FirstQualifierInScope) { OwningExprResult Base = move(BaseE); tok::TokenKind OpKind = IsArrow? tok::arrow : tok::period; @@ -1467,7 +1471,8 @@ public: MemberLoc, Name, /*FIXME?*/Sema::DeclPtrTy::make((Decl*)0), - &SS); + &SS, + FirstQualifierInScope); return move(Base); } @@ -1591,7 +1596,8 @@ template NestedNameSpecifier * TreeTransform::TransformNestedNameSpecifier(NestedNameSpecifier *NNS, SourceRange Range, - QualType ObjectType) { + QualType ObjectType, + NamedDecl *FirstQualifierInScope) { if (!NNS) return 0; @@ -1599,13 +1605,15 @@ TreeTransform::TransformNestedNameSpecifier(NestedNameSpecifier *NNS, NestedNameSpecifier *Prefix = NNS->getPrefix(); if (Prefix) { Prefix = getDerived().TransformNestedNameSpecifier(Prefix, Range, - ObjectType); + ObjectType, + FirstQualifierInScope); if (!Prefix) return 0; - // Clear out the object type; it only applies to the first element in - // the nested-name-specifier. + // Clear out the object type and the first qualifier in scope; they only + // apply to the first element in the nested-name-specifier. ObjectType = QualType(); + FirstQualifierInScope = 0; } switch (NNS->getKind()) { @@ -1618,7 +1626,8 @@ TreeTransform::TransformNestedNameSpecifier(NestedNameSpecifier *NNS, return getDerived().RebuildNestedNameSpecifier(Prefix, Range, *NNS->getAsIdentifier(), - ObjectType); + ObjectType, + FirstQualifierInScope); case NestedNameSpecifier::Namespace: { NamespaceDecl *NS @@ -4055,22 +4064,28 @@ TreeTransform::TransformCXXUnresolvedMemberExpr( if (Base.isInvalid()) return SemaRef.ExprError(); + NamedDecl *FirstQualifierInScope + = cast_or_null( + getDerived().TransformDecl(E->getFirstQualifierFoundInScope())); + NestedNameSpecifier *Qualifier = 0; if (E->getQualifier()) { Qualifier = getDerived().TransformNestedNameSpecifier(E->getQualifier(), E->getQualifierRange(), - QualType::getFromOpaquePtr(ObjectType)); + QualType::getFromOpaquePtr(ObjectType), + FirstQualifierInScope); if (!Qualifier) return SemaRef.ExprError(); } // FIXME: Transform the declaration name DeclarationName Name = E->getMember(); - + if (!getDerived().AlwaysRebuild() && Base.get() == E->getBase() && Qualifier == E->getQualifier() && - Name == E->getMember()) + Name == E->getMember() && + FirstQualifierInScope == E->getFirstQualifierFoundInScope()) return SemaRef.Owned(E->Retain()); return getDerived().RebuildCXXUnresolvedMemberExpr(move(Base), @@ -4079,7 +4094,8 @@ TreeTransform::TransformCXXUnresolvedMemberExpr( Qualifier, E->getQualifierRange(), Name, - E->getMemberLoc()); + E->getMemberLoc(), + FirstQualifierInScope); } template @@ -4435,15 +4451,17 @@ NestedNameSpecifier * TreeTransform::RebuildNestedNameSpecifier(NestedNameSpecifier *Prefix, SourceRange Range, IdentifierInfo &II, - QualType ObjectType) { + QualType ObjectType, + NamedDecl *FirstQualifierInScope) { CXXScopeSpec SS; // FIXME: The source location information is all wrong. SS.setRange(Range); SS.setScopeRep(Prefix); return static_cast( - SemaRef.ActOnCXXNestedNameSpecifier(0, SS, Range.getEnd(), + SemaRef.BuildCXXNestedNameSpecifier(0, SS, Range.getEnd(), Range.getEnd(), II, - ObjectType.getAsOpaquePtr(), + ObjectType, + FirstQualifierInScope, false)); } diff --git a/test/SemaCXX/qual-id-test.cpp b/test/SemaCXX/qual-id-test.cpp index 9032d0e956..ecb6aa9b35 100644 --- a/test/SemaCXX/qual-id-test.cpp +++ b/test/SemaCXX/qual-id-test.cpp @@ -107,18 +107,26 @@ namespace C a.A::B::base::x(); a->A::member::foo(); - a.bad::x(); // xpected-error{{direct or virtual}} - a.sub::x(); - a.base::x(); - a.B::base::x(); // xpected-error{{use of undeclared identifier 'B'}} - a->member::foo(); + a.bad::x(); // expected-error{{direct or virtual}} } - + void test_fun5() { - // FIXME: Enable the following once we get the nested-name-specifier lookup - // right during template instantiation. - // fun5(); // xpected-note 2{{instantiation}} + fun5(); // expected-note{{instantiation}} } + + template + void fun6() { + T a; + a.sub::x(); + a.base::x(); + a->member::foo(); + a.B::base::x(); // expected-error{{use of undeclared identifier 'B'}} + } + + void test_fun6() { + fun6(); // expected-note{{instantiation}} + } + } // PR4703 diff --git a/test/SemaTemplate/member-access-expr.cpp b/test/SemaTemplate/member-access-expr.cpp index f41dc2120a..408e2bb53c 100644 --- a/test/SemaTemplate/member-access-expr.cpp +++ b/test/SemaTemplate/member-access-expr.cpp @@ -1,5 +1,4 @@ // RUN: clang-cc -fsyntax-only -verify %s -// XFAIL template void call_f0(T x) { x.Base::f0(); @@ -30,7 +29,8 @@ void test_f0_through_typedef(X0 x0) { template void call_f0_through_typedef2(T x) { typedef TheBase CrazyBase; // expected-note{{current scope}} - x.CrazyBase::f0(); // expected-error{{ambiguous}} + x.CrazyBase::f0(); // expected-error{{ambiguous}} \ + // expected-error 2{{no member named}} } struct OtherBase { }; @@ -41,8 +41,8 @@ struct X1 : Base, OtherBase { void test_f0_through_typedef2(X0 x0, X1 x1) { call_f0_through_typedef2(x0); - call_f0_through_typedef2(x1); - call_f0_through_typedef2(x1); // expected-note{{here}} + call_f0_through_typedef2(x1); // expected-note{{instantiation}} + call_f0_through_typedef2(x1); // expected-note{{instantiation}} } diff --git a/www/cxx_status.html b/www/cxx_status.html index d01c5a7752..8df8fd339f 100644 --- a/www/cxx_status.html +++ b/www/cxx_status.html @@ -380,7 +380,14 @@ welcome!

    3.4.4 [basic.lookup.elab] -    3.4.5 [basic.lookup.classref] + +     3.4.5 [basic.lookup.classref] + N/A + N/A + + N/A + Missing ambiguity/consistency checks for paragraphs 3 (~type-name) and 7 (conversion-type-id) +     3.4.6 [basic.lookup.udir]   3.5 [basic.link]   3.6 [basic.start]