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] | | | | | |