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
diff --git a/lib/Sema/SemaCXXScopeSpec.cpp b/lib/Sema/SemaCXXScopeSpec.cpp
index 352e553..fb6c88e 100644
--- a/lib/Sema/SemaCXXScopeSpec.cpp
+++ b/lib/Sema/SemaCXXScopeSpec.cpp
@@ -288,18 +288,45 @@
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<NestedNameSpecifier *>(SS.getScopeRep());
@@ -307,11 +334,10 @@
// 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 @@
LookupCtx = computeDeclContext(SS, EnteringContext);
isDependent = isDependentScopeSpecifier(SS);
}
-
+
LookupResult Found;
bool ObjectTypeSearchedInScope = false;
if (LookupCtx) {
@@ -332,11 +358,11 @@
// 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 @@
// 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 @@
// 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 @@
OuterDecl->getCanonicalDecl() != SD->getCanonicalDecl() &&
(!isa<TypeDecl>(OuterDecl) || !isa<TypeDecl>(SD) ||
!Context.hasSameType(
- Context.getTypeDeclType(cast<TypeDecl>(OuterDecl)),
+ 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);
+ 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<NamespaceDecl>(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<NamespaceAliasDecl>(SD))
return NestedNameSpecifier::Create(Context, Prefix,
-
+
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.
@@ -441,15 +474,33 @@
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,