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.

llvm-svn: 80953
This commit is contained in:
Douglas Gregor 2009-09-03 21:38:09 +00:00
parent 59a1cd4a06
commit 2b6ca46c6b
10 changed files with 236 additions and 85 deletions

View File

@ -1306,6 +1306,15 @@ class CXXUnresolvedMemberExpr : public Expr {
/// \brief The source range covering the nested name specifier. /// \brief The source range covering the nested name specifier.
SourceRange QualifierRange; 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 /// \brief The member to which this member expression refers, which
/// can be name, overloaded operator, or destructor. /// can be name, overloaded operator, or destructor.
/// FIXME: could also be a template-id /// FIXME: could also be a template-id
@ -1320,11 +1329,13 @@ public:
SourceLocation OperatorLoc, SourceLocation OperatorLoc,
NestedNameSpecifier *Qualifier, NestedNameSpecifier *Qualifier,
SourceRange QualifierRange, SourceRange QualifierRange,
NamedDecl *FirstQualifierFoundInScope,
DeclarationName Member, DeclarationName Member,
SourceLocation MemberLoc) SourceLocation MemberLoc)
: Expr(CXXUnresolvedMemberExprClass, C.DependentTy, true, true), : Expr(CXXUnresolvedMemberExprClass, C.DependentTy, true, true),
Base(Base), IsArrow(IsArrow), OperatorLoc(OperatorLoc), Base(Base), IsArrow(IsArrow), OperatorLoc(OperatorLoc),
Qualifier(Qualifier), QualifierRange(QualifierRange), Qualifier(Qualifier), QualifierRange(QualifierRange),
FirstQualifierFoundInScope(FirstQualifierFoundInScope),
Member(Member), MemberLoc(MemberLoc) { } Member(Member), MemberLoc(MemberLoc) { }
/// \brief Retrieve the base object of this member expressions, /// \brief Retrieve the base object of this member expressions,
@ -1349,6 +1360,21 @@ public:
/// that qualifies the member name. /// that qualifies the member name.
SourceRange getQualifierRange() const { return QualifierRange; } 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 /// \brief Retrieve the name of the member that this expression
/// refers to. /// refers to.
DeclarationName getMember() const { return Member; } DeclarationName getMember() const { return Member; }

View File

@ -1610,13 +1610,15 @@ public:
SourceLocation MemberLoc, SourceLocation MemberLoc,
DeclarationName MemberName, DeclarationName MemberName,
DeclPtrTy ImplDecl, DeclPtrTy ImplDecl,
const CXXScopeSpec *SS = 0) { const CXXScopeSpec *SS = 0,
NamedDecl *FirstQualifierInScope = 0) {
// FIXME: Temporary helper while we migrate existing calls to // FIXME: Temporary helper while we migrate existing calls to
// BuildMemberReferenceExpr to support explicitly-specified template // BuildMemberReferenceExpr to support explicitly-specified template
// arguments. // arguments.
return BuildMemberReferenceExpr(S, move(Base), OpLoc, OpKind, MemberLoc, return BuildMemberReferenceExpr(S, move(Base), OpLoc, OpKind, MemberLoc,
MemberName, false, SourceLocation(), 0, 0, MemberName, false, SourceLocation(), 0, 0,
SourceLocation(), ImplDecl, SS); SourceLocation(), ImplDecl, SS,
FirstQualifierInScope);
} }
OwningExprResult BuildMemberReferenceExpr(Scope *S, ExprArg Base, OwningExprResult BuildMemberReferenceExpr(Scope *S, ExprArg Base,
@ -1630,7 +1632,8 @@ public:
unsigned NumExplicitTemplateArgs, unsigned NumExplicitTemplateArgs,
SourceLocation RAngleLoc, SourceLocation RAngleLoc,
DeclPtrTy ImplDecl, DeclPtrTy ImplDecl,
const CXXScopeSpec *SS = 0); const CXXScopeSpec *SS,
NamedDecl *FirstQualifierInScope = 0);
virtual OwningExprResult ActOnMemberReferenceExpr(Scope *S, ExprArg Base, virtual OwningExprResult ActOnMemberReferenceExpr(Scope *S, ExprArg Base,
SourceLocation OpLoc, SourceLocation OpLoc,
@ -2047,12 +2050,18 @@ public:
virtual CXXScopeTy *ActOnCXXGlobalScopeSpecifier(Scope *S, virtual CXXScopeTy *ActOnCXXGlobalScopeSpecifier(Scope *S,
SourceLocation CCLoc); SourceLocation CCLoc);
/// ActOnCXXNestedNameSpecifier - Called during parsing of a NamedDecl *FindFirstQualifierInScope(Scope *S, NestedNameSpecifier *NNS);
/// 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', CXXScopeTy *BuildCXXNestedNameSpecifier(Scope *S,
/// 'CCLoc' is the location of '::' and 'II' is the identifier for 'bar'. const CXXScopeSpec &SS,
/// Returns a CXXScopeTy* object representing the C++ scope. SourceLocation IdLoc,
SourceLocation CCLoc,
IdentifierInfo &II,
QualType ObjectType,
NamedDecl *ScopeLookupResult,
bool EnteringContext);
virtual CXXScopeTy *ActOnCXXNestedNameSpecifier(Scope *S, virtual CXXScopeTy *ActOnCXXNestedNameSpecifier(Scope *S,
const CXXScopeSpec &SS, const CXXScopeSpec &SS,
SourceLocation IdLoc, SourceLocation IdLoc,

View File

@ -288,18 +288,45 @@ bool isAcceptableNestedNameSpecifier(ASTContext &Context, NamedDecl *SD) {
return false; return false;
} }
/// ActOnCXXNestedNameSpecifier - Called during parsing of a /// \brief If the given nested-name-specifier begins with a bare identifier
/// nested-name-specifier. e.g. for "foo::bar::" we parsed "foo::" and now /// (e.g., Base::), perform name lookup for that identifier as a
/// we want to resolve "bar::". 'SS' is empty or the previously parsed /// nested-name-specifier within the given scope, and return the result of that
/// nested-name part ("foo::"), 'IdLoc' is the source location of 'bar', /// name lookup.
/// 'CCLoc' is the location of '::' and 'II' is the identifier for 'bar'. NamedDecl *Sema::FindFirstQualifierInScope(Scope *S, NestedNameSpecifier *NNS) {
/// Returns a CXXScopeTy* object representing the C++ scope. if (!S || !NNS)
Sema::CXXScopeTy *Sema::ActOnCXXNestedNameSpecifier(Scope *S, 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, const CXXScopeSpec &SS,
SourceLocation IdLoc, SourceLocation IdLoc,
SourceLocation CCLoc, SourceLocation CCLoc,
IdentifierInfo &II, IdentifierInfo &II,
TypeTy *ObjectTypePtr, QualType ObjectType,
NamedDecl *ScopeLookupResult,
bool EnteringContext) { bool EnteringContext) {
NestedNameSpecifier *Prefix NestedNameSpecifier *Prefix
= static_cast<NestedNameSpecifier *>(SS.getScopeRep()); = static_cast<NestedNameSpecifier *>(SS.getScopeRep());
@ -307,11 +334,10 @@ Sema::CXXScopeTy *Sema::ActOnCXXNestedNameSpecifier(Scope *S,
// Determine where to perform name lookup // Determine where to perform name lookup
DeclContext *LookupCtx = 0; DeclContext *LookupCtx = 0;
bool isDependent = false; bool isDependent = false;
if (ObjectTypePtr) { if (!ObjectType.isNull()) {
// This nested-name-specifier occurs in a member access expression, e.g., // 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. // x->B::f, and we are looking into the type of the object.
assert(!SS.isSet() && "ObjectType and scope specifier cannot coexist"); assert(!SS.isSet() && "ObjectType and scope specifier cannot coexist");
QualType ObjectType = QualType::getFromOpaquePtr(ObjectTypePtr);
LookupCtx = computeDeclContext(ObjectType); LookupCtx = computeDeclContext(ObjectType);
isDependent = ObjectType->isDependentType(); isDependent = ObjectType->isDependentType();
} else if (SS.isSet()) { } else if (SS.isSet()) {
@ -336,7 +362,7 @@ Sema::CXXScopeTy *Sema::ActOnCXXNestedNameSpecifier(Scope *S,
Found = LookupQualifiedName(LookupCtx, &II, LookupNestedNameSpecifierName, Found = LookupQualifiedName(LookupCtx, &II, LookupNestedNameSpecifierName,
false); false);
if (ObjectTypePtr && Found.getKind() == LookupResult::NotFound && S) { if (!ObjectType.isNull() && Found.getKind() == LookupResult::NotFound) {
// C++ [basic.lookup.classref]p4: // C++ [basic.lookup.classref]p4:
// If the id-expression in a class member access is a qualified-id of // If the id-expression in a class member access is a qualified-id of
// the form // the form
@ -354,12 +380,14 @@ Sema::CXXScopeTy *Sema::ActOnCXXNestedNameSpecifier(Scope *S,
// Qualified name lookup into a class will not find a 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, // so we do not need to diagnoste that case specifically. However,
// this qualified name lookup may find nothing. In that case, perform // this qualified name lookup may find nothing. In that case, perform
// unqualified name lookup in the given scope. // unqualified name lookup in the given scope (if available) or
// reconstruct the result from when name lookup was performed at template
// FIXME: When we're instantiating a template, do we actually have to // definition time.
// look in the scope of the template? Both EDG and GCC do it; GCC if (S)
// requires the lookup to be successful, EDG doesn't.
Found = LookupName(S, &II, LookupNestedNameSpecifierName); Found = LookupName(S, &II, LookupNestedNameSpecifierName);
else
Found = LookupResult::CreateLookupResult(Context, ScopeLookupResult);
ObjectTypeSearchedInScope = true; ObjectTypeSearchedInScope = true;
} }
} else if (isDependent) { } else if (isDependent) {
@ -379,16 +407,21 @@ Sema::CXXScopeTy *Sema::ActOnCXXNestedNameSpecifier(Scope *S,
// FIXME: Deal with ambiguities cleanly. // FIXME: Deal with ambiguities cleanly.
NamedDecl *SD = Found; NamedDecl *SD = Found;
if (isAcceptableNestedNameSpecifier(Context, SD)) { if (isAcceptableNestedNameSpecifier(Context, SD)) {
if (ObjectTypePtr && !ObjectTypeSearchedInScope && S) { if (!ObjectType.isNull() && !ObjectTypeSearchedInScope) {
// C++ [basic.lookup.classref]p4: // C++ [basic.lookup.classref]p4:
// [...] If the name is found in both contexts, the // [...] If the name is found in both contexts, the
// class-name-or-namespace-name shall refer to the same entity. // class-name-or-namespace-name shall refer to the same entity.
// //
// We already found the name in the scope of the object. Now, look // We already found the name in the scope of the object. Now, look
// into the current scope (the scope of the postfix-expression) to // into the current scope (the scope of the postfix-expression) to
// see if we can find the same name there. // see if we can find the same name there. As above, if there is no
LookupResult FoundOuter // scope, reconstruct the result from the template instantiation itself.
= LookupName(S, &II, LookupNestedNameSpecifierName); LookupResult FoundOuter;
if (S)
FoundOuter = LookupName(S, &II, LookupNestedNameSpecifierName);
else
FoundOuter = LookupResult::CreateLookupResult(Context,
ScopeLookupResult);
// FIXME: Handle ambiguities in FoundOuter! // FIXME: Handle ambiguities in FoundOuter!
NamedDecl *OuterDecl = FoundOuter; NamedDecl *OuterDecl = FoundOuter;
@ -401,7 +434,7 @@ Sema::CXXScopeTy *Sema::ActOnCXXNestedNameSpecifier(Scope *S,
Diag(IdLoc, diag::err_nested_name_member_ref_lookup_ambiguous) Diag(IdLoc, diag::err_nested_name_member_ref_lookup_ambiguous)
<< &II; << &II;
Diag(SD->getLocation(), diag::note_ambig_member_ref_object_type) Diag(SD->getLocation(), diag::note_ambig_member_ref_object_type)
<< QualType::getFromOpaquePtr(ObjectTypePtr); << ObjectType;
Diag(OuterDecl->getLocation(), diag::note_ambig_member_ref_scope); Diag(OuterDecl->getLocation(), diag::note_ambig_member_ref_scope);
// Fall through so that we'll pick the name we found in the object type, // Fall through so that we'll pick the name we found in the object type,
@ -450,6 +483,24 @@ Sema::CXXScopeTy *Sema::ActOnCXXNestedNameSpecifier(Scope *S,
return 0; 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, Sema::CXXScopeTy *Sema::ActOnCXXNestedNameSpecifier(Scope *S,
const CXXScopeSpec &SS, const CXXScopeSpec &SS,
TypeTy *Ty, TypeTy *Ty,

View File

@ -1989,7 +1989,8 @@ Sema::BuildMemberReferenceExpr(Scope *S, ExprArg Base, SourceLocation OpLoc,
const TemplateArgument *ExplicitTemplateArgs, const TemplateArgument *ExplicitTemplateArgs,
unsigned NumExplicitTemplateArgs, unsigned NumExplicitTemplateArgs,
SourceLocation RAngleLoc, SourceLocation RAngleLoc,
DeclPtrTy ObjCImpDecl, const CXXScopeSpec *SS) { DeclPtrTy ObjCImpDecl, const CXXScopeSpec *SS,
NamedDecl *FirstQualifierInScope) {
if (SS && SS->isInvalid()) if (SS && SS->isInvalid())
return ExprError(); 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 // 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. // must have pointer type, and the accessed type is the pointee.
if (OpKind == tok::arrow) { if (OpKind == tok::arrow) {
if (BaseType->isDependentType()) if (BaseType->isDependentType()) {
NestedNameSpecifier *Qualifier = 0;
if (SS) {
Qualifier = static_cast<NestedNameSpecifier *>(SS->getScopeRep());
if (!FirstQualifierInScope)
FirstQualifierInScope = FindFirstQualifierInScope(S, Qualifier);
}
return Owned(new (Context) CXXUnresolvedMemberExpr(Context, return Owned(new (Context) CXXUnresolvedMemberExpr(Context,
BaseExpr, true, BaseExpr, true,
OpLoc, OpLoc,
(NestedNameSpecifier *)(SS? SS->getScopeRep() : 0), Qualifier,
SS? SS->getRange() : SourceRange(), SS? SS->getRange() : SourceRange(),
FirstQualifierInScope,
MemberName, MemberName,
MemberLoc)); MemberLoc));
}
else if (const PointerType *PT = BaseType->getAs<PointerType>()) else if (const PointerType *PT = BaseType->getAs<PointerType>())
BaseType = PT->getPointeeType(); BaseType = PT->getPointeeType();
else if (BaseType->isObjCObjectPointerType()) else if (BaseType->isObjCObjectPointerType())
@ -2051,16 +2061,25 @@ Sema::BuildMemberReferenceExpr(Scope *S, ExprArg Base, SourceLocation OpLoc,
const PointerType *PT = BaseType->getAs<PointerType>(); const PointerType *PT = BaseType->getAs<PointerType>();
if (!PT || (getLangOptions().ObjC1 && if (!PT || (getLangOptions().ObjC1 &&
!PT->getPointeeType()->isRecordType())) !PT->getPointeeType()->isRecordType())) {
NestedNameSpecifier *Qualifier = 0;
if (SS) {
Qualifier = static_cast<NestedNameSpecifier *>(SS->getScopeRep());
if (!FirstQualifierInScope)
FirstQualifierInScope = FindFirstQualifierInScope(S, Qualifier);
}
return Owned(new (Context) CXXUnresolvedMemberExpr(Context, return Owned(new (Context) CXXUnresolvedMemberExpr(Context,
BaseExpr, false, BaseExpr, false,
OpLoc, OpLoc,
(NestedNameSpecifier *)(SS? SS->getScopeRep() : 0), Qualifier,
SS? SS->getRange() : SourceRange(), SS? SS->getRange() : SourceRange(),
FirstQualifierInScope,
MemberName, MemberName,
MemberLoc)); MemberLoc));
} }
} }
}
// Handle field access to simple records. This also handles access to fields // Handle field access to simple records. This also handles access to fields
// of the ObjC 'id' struct. // of the ObjC 'id' struct.

View File

@ -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 // We could end up with various non-record types here, such as extended
// vector types or Objective-C interfaces. Just return early and let // vector types or Objective-C interfaces. Just return early and let
// ActOnMemberReferenceExpr do the work. // 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); 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(); ObjectType = BaseType.getAsOpaquePtr();
return move(Base); return move(Base);
} }

View File

@ -359,8 +359,10 @@ namespace {
} }
Decl *TemplateInstantiator::TransformDecl(Decl *D) { Decl *TemplateInstantiator::TransformDecl(Decl *D) {
if (TemplateTemplateParmDecl *TTP if (!D)
= dyn_cast_or_null<TemplateTemplateParmDecl>(D)) { return 0;
if (TemplateTemplateParmDecl *TTP = dyn_cast<TemplateTemplateParmDecl>(D)) {
if (TTP->getDepth() < TemplateArgs.getNumLevels()) { if (TTP->getDepth() < TemplateArgs.getNumLevels()) {
assert(TemplateArgs(TTP->getDepth(), TTP->getPosition()).getAsDecl() && assert(TemplateArgs(TTP->getDepth(), TTP->getPosition()).getAsDecl() &&
"Wrong kind of template template argument"); "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"); "Reducing depth of template template parameters is not yet implemented");
} }
return SemaRef.FindInstantiatedDecl(cast_or_null<NamedDecl>(D)); return SemaRef.FindInstantiatedDecl(cast<NamedDecl>(D));
} }
Decl *TemplateInstantiator::TransformDefinition(Decl *D) { Decl *TemplateInstantiator::TransformDefinition(Decl *D) {

View File

@ -15,6 +15,7 @@
#include "Sema.h" #include "Sema.h"
#include "clang/Sema/SemaDiagnostic.h" #include "clang/Sema/SemaDiagnostic.h"
#include "clang/AST/Decl.h"
#include "clang/AST/Expr.h" #include "clang/AST/Expr.h"
#include "clang/AST/ExprCXX.h" #include "clang/AST/ExprCXX.h"
#include "clang/AST/ExprObjC.h" #include "clang/AST/ExprObjC.h"
@ -243,7 +244,8 @@ public:
/// alternate behavior. /// alternate behavior.
NestedNameSpecifier *TransformNestedNameSpecifier(NestedNameSpecifier *NNS, NestedNameSpecifier *TransformNestedNameSpecifier(NestedNameSpecifier *NNS,
SourceRange Range, SourceRange Range,
QualType ObjectType = QualType()); QualType ObjectType = QualType(),
NamedDecl *FirstQualifierInScope = 0);
/// \brief Transform the given template name. /// \brief Transform the given template name.
/// ///
@ -499,7 +501,8 @@ public:
NestedNameSpecifier *RebuildNestedNameSpecifier(NestedNameSpecifier *Prefix, NestedNameSpecifier *RebuildNestedNameSpecifier(NestedNameSpecifier *Prefix,
SourceRange Range, SourceRange Range,
IdentifierInfo &II, IdentifierInfo &II,
QualType ObjectType); QualType ObjectType,
NamedDecl *FirstQualifierInScope);
/// \brief Build a new nested-name-specifier given the prefix and the /// \brief Build a new nested-name-specifier given the prefix and the
/// namespace named in the next step in the nested-name-specifier. /// namespace named in the next step in the nested-name-specifier.
@ -1454,7 +1457,8 @@ public:
NestedNameSpecifier *Qualifier, NestedNameSpecifier *Qualifier,
SourceRange QualifierRange, SourceRange QualifierRange,
DeclarationName Name, DeclarationName Name,
SourceLocation MemberLoc) { SourceLocation MemberLoc,
NamedDecl *FirstQualifierInScope) {
OwningExprResult Base = move(BaseE); OwningExprResult Base = move(BaseE);
tok::TokenKind OpKind = IsArrow? tok::arrow : tok::period; tok::TokenKind OpKind = IsArrow? tok::arrow : tok::period;
@ -1467,7 +1471,8 @@ public:
MemberLoc, MemberLoc,
Name, Name,
/*FIXME?*/Sema::DeclPtrTy::make((Decl*)0), /*FIXME?*/Sema::DeclPtrTy::make((Decl*)0),
&SS); &SS,
FirstQualifierInScope);
return move(Base); return move(Base);
} }
@ -1591,7 +1596,8 @@ template<typename Derived>
NestedNameSpecifier * NestedNameSpecifier *
TreeTransform<Derived>::TransformNestedNameSpecifier(NestedNameSpecifier *NNS, TreeTransform<Derived>::TransformNestedNameSpecifier(NestedNameSpecifier *NNS,
SourceRange Range, SourceRange Range,
QualType ObjectType) { QualType ObjectType,
NamedDecl *FirstQualifierInScope) {
if (!NNS) if (!NNS)
return 0; return 0;
@ -1599,13 +1605,15 @@ TreeTransform<Derived>::TransformNestedNameSpecifier(NestedNameSpecifier *NNS,
NestedNameSpecifier *Prefix = NNS->getPrefix(); NestedNameSpecifier *Prefix = NNS->getPrefix();
if (Prefix) { if (Prefix) {
Prefix = getDerived().TransformNestedNameSpecifier(Prefix, Range, Prefix = getDerived().TransformNestedNameSpecifier(Prefix, Range,
ObjectType); ObjectType,
FirstQualifierInScope);
if (!Prefix) if (!Prefix)
return 0; return 0;
// Clear out the object type; it only applies to the first element in // Clear out the object type and the first qualifier in scope; they only
// the nested-name-specifier. // apply to the first element in the nested-name-specifier.
ObjectType = QualType(); ObjectType = QualType();
FirstQualifierInScope = 0;
} }
switch (NNS->getKind()) { switch (NNS->getKind()) {
@ -1618,7 +1626,8 @@ TreeTransform<Derived>::TransformNestedNameSpecifier(NestedNameSpecifier *NNS,
return getDerived().RebuildNestedNameSpecifier(Prefix, Range, return getDerived().RebuildNestedNameSpecifier(Prefix, Range,
*NNS->getAsIdentifier(), *NNS->getAsIdentifier(),
ObjectType); ObjectType,
FirstQualifierInScope);
case NestedNameSpecifier::Namespace: { case NestedNameSpecifier::Namespace: {
NamespaceDecl *NS NamespaceDecl *NS
@ -4055,11 +4064,16 @@ TreeTransform<Derived>::TransformCXXUnresolvedMemberExpr(
if (Base.isInvalid()) if (Base.isInvalid())
return SemaRef.ExprError(); return SemaRef.ExprError();
NamedDecl *FirstQualifierInScope
= cast_or_null<NamedDecl>(
getDerived().TransformDecl(E->getFirstQualifierFoundInScope()));
NestedNameSpecifier *Qualifier = 0; NestedNameSpecifier *Qualifier = 0;
if (E->getQualifier()) { if (E->getQualifier()) {
Qualifier = getDerived().TransformNestedNameSpecifier(E->getQualifier(), Qualifier = getDerived().TransformNestedNameSpecifier(E->getQualifier(),
E->getQualifierRange(), E->getQualifierRange(),
QualType::getFromOpaquePtr(ObjectType)); QualType::getFromOpaquePtr(ObjectType),
FirstQualifierInScope);
if (!Qualifier) if (!Qualifier)
return SemaRef.ExprError(); return SemaRef.ExprError();
} }
@ -4070,7 +4084,8 @@ TreeTransform<Derived>::TransformCXXUnresolvedMemberExpr(
if (!getDerived().AlwaysRebuild() && if (!getDerived().AlwaysRebuild() &&
Base.get() == E->getBase() && Base.get() == E->getBase() &&
Qualifier == E->getQualifier() && Qualifier == E->getQualifier() &&
Name == E->getMember()) Name == E->getMember() &&
FirstQualifierInScope == E->getFirstQualifierFoundInScope())
return SemaRef.Owned(E->Retain()); return SemaRef.Owned(E->Retain());
return getDerived().RebuildCXXUnresolvedMemberExpr(move(Base), return getDerived().RebuildCXXUnresolvedMemberExpr(move(Base),
@ -4079,7 +4094,8 @@ TreeTransform<Derived>::TransformCXXUnresolvedMemberExpr(
Qualifier, Qualifier,
E->getQualifierRange(), E->getQualifierRange(),
Name, Name,
E->getMemberLoc()); E->getMemberLoc(),
FirstQualifierInScope);
} }
template<typename Derived> template<typename Derived>
@ -4435,15 +4451,17 @@ NestedNameSpecifier *
TreeTransform<Derived>::RebuildNestedNameSpecifier(NestedNameSpecifier *Prefix, TreeTransform<Derived>::RebuildNestedNameSpecifier(NestedNameSpecifier *Prefix,
SourceRange Range, SourceRange Range,
IdentifierInfo &II, IdentifierInfo &II,
QualType ObjectType) { QualType ObjectType,
NamedDecl *FirstQualifierInScope) {
CXXScopeSpec SS; CXXScopeSpec SS;
// FIXME: The source location information is all wrong. // FIXME: The source location information is all wrong.
SS.setRange(Range); SS.setRange(Range);
SS.setScopeRep(Prefix); SS.setScopeRep(Prefix);
return static_cast<NestedNameSpecifier *>( return static_cast<NestedNameSpecifier *>(
SemaRef.ActOnCXXNestedNameSpecifier(0, SS, Range.getEnd(), SemaRef.BuildCXXNestedNameSpecifier(0, SS, Range.getEnd(),
Range.getEnd(), II, Range.getEnd(), II,
ObjectType.getAsOpaquePtr(), ObjectType,
FirstQualifierInScope,
false)); false));
} }

View File

@ -107,18 +107,26 @@ namespace C
a.A::B::base::x(); a.A::B::base::x();
a->A::member::foo(); a->A::member::foo();
a.bad::x(); // xpected-error{{direct or virtual}} a.bad::x(); // expected-error{{direct or virtual}}
a.sub::x();
a.base::x();
a.B::base::x(); // xpected-error{{use of undeclared identifier 'B'}}
a->member::foo();
} }
void test_fun5() { void test_fun5() {
// FIXME: Enable the following once we get the nested-name-specifier lookup fun5<A::sub>(); // expected-note{{instantiation}}
// right during template instantiation.
// fun5<A::sub>(); // xpected-note 2{{instantiation}}
} }
template<typename T>
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<A::sub>(); // expected-note{{instantiation}}
}
} }
// PR4703 // PR4703

View File

@ -1,5 +1,4 @@
// RUN: clang-cc -fsyntax-only -verify %s // RUN: clang-cc -fsyntax-only -verify %s
// XFAIL
template<typename T> template<typename T>
void call_f0(T x) { void call_f0(T x) {
x.Base::f0(); x.Base::f0();
@ -30,7 +29,8 @@ void test_f0_through_typedef(X0 x0) {
template<typename TheBase, typename T> template<typename TheBase, typename T>
void call_f0_through_typedef2(T x) { void call_f0_through_typedef2(T x) {
typedef TheBase CrazyBase; // expected-note{{current scope}} 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 { }; struct OtherBase { };
@ -41,8 +41,8 @@ struct X1 : Base, OtherBase {
void test_f0_through_typedef2(X0 x0, X1 x1) { void test_f0_through_typedef2(X0 x0, X1 x1) {
call_f0_through_typedef2<Base>(x0); call_f0_through_typedef2<Base>(x0);
call_f0_through_typedef2<OtherBase>(x1); call_f0_through_typedef2<OtherBase>(x1); // expected-note{{instantiation}}
call_f0_through_typedef2<Base>(x1); // expected-note{{here}} call_f0_through_typedef2<Base>(x1); // expected-note{{instantiation}}
} }

View File

@ -380,7 +380,14 @@ welcome!</p>
<td></td> <td></td>
</tr> </tr>
<tr><td>&nbsp;&nbsp;&nbsp;&nbsp;3.4.4 [basic.lookup.elab]</td><td></td><td></td><td></td><td></td><td></td></tr> <tr><td>&nbsp;&nbsp;&nbsp;&nbsp;3.4.4 [basic.lookup.elab]</td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td>&nbsp;&nbsp;&nbsp;&nbsp;3.4.5 [basic.lookup.classref]</td><td></td><td></td><td></td><td></td><td></td></tr> <tr>
<td>&nbsp;&nbsp;&nbsp;&nbsp;3.4.5 [basic.lookup.classref]</td>
<td class="na">N/A</td>
<td class="na">N/A</td>
<td class="advanced"></td>
<td class="na">N/A</td>
<td>Missing ambiguity/consistency checks for paragraphs 3 (~type-name) and 7 (conversion-type-id)</td>
</tr>
<tr><td>&nbsp;&nbsp;&nbsp;&nbsp;3.4.6 [basic.lookup.udir]</td><td></td><td></td><td></td><td></td><td></td></tr> <tr><td>&nbsp;&nbsp;&nbsp;&nbsp;3.4.6 [basic.lookup.udir]</td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td>&nbsp;&nbsp;3.5 [basic.link]</td><td></td><td></td><td></td><td></td><td></td></tr> <tr><td>&nbsp;&nbsp;3.5 [basic.link]</td><td></td><td></td><td></td><td></td><td></td></tr>
<tr><td>&nbsp;&nbsp;3.6 [basic.start]</td><td></td><td></td><td></td><td></td><td></td></tr> <tr><td>&nbsp;&nbsp;3.6 [basic.start]</td><td></td><td></td><td></td><td></td><td></td></tr>