forked from OSchip/llvm-project
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:
parent
59a1cd4a06
commit
2b6ca46c6b
|
@ -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; }
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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<NestedNameSpecifier *>(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()) {
|
||||
|
@ -336,7 +362,7 @@ Sema::CXXScopeTy *Sema::ActOnCXXNestedNameSpecifier(Scope *S,
|
|||
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.
|
||||
// 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;
|
||||
|
@ -401,7 +434,7 @@ Sema::CXXScopeTy *Sema::ActOnCXXNestedNameSpecifier(Scope *S,
|
|||
Diag(IdLoc, diag::err_nested_name_member_ref_lookup_ambiguous)
|
||||
<< &II;
|
||||
Diag(SD->getLocation(), diag::note_ambig_member_ref_object_type)
|
||||
<< QualType::getFromOpaquePtr(ObjectTypePtr);
|
||||
<< 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,
|
||||
|
@ -450,6 +483,24 @@ Sema::CXXScopeTy *Sema::ActOnCXXNestedNameSpecifier(Scope *S,
|
|||
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,
|
||||
|
|
|
@ -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<NestedNameSpecifier *>(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<PointerType>())
|
||||
BaseType = PT->getPointeeType();
|
||||
else if (BaseType->isObjCObjectPointerType())
|
||||
|
@ -2051,16 +2061,25 @@ Sema::BuildMemberReferenceExpr(Scope *S, ExprArg Base, SourceLocation OpLoc,
|
|||
const PointerType *PT = BaseType->getAs<PointerType>();
|
||||
|
||||
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,
|
||||
BaseExpr, false,
|
||||
OpLoc,
|
||||
(NestedNameSpecifier *)(SS? SS->getScopeRep() : 0),
|
||||
Qualifier,
|
||||
SS? SS->getRange() : SourceRange(),
|
||||
FirstQualifierInScope,
|
||||
MemberName,
|
||||
MemberLoc));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle field access to simple records. This also handles access to fields
|
||||
// of the ObjC 'id' struct.
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -359,8 +359,10 @@ namespace {
|
|||
}
|
||||
|
||||
Decl *TemplateInstantiator::TransformDecl(Decl *D) {
|
||||
if (TemplateTemplateParmDecl *TTP
|
||||
= dyn_cast_or_null<TemplateTemplateParmDecl>(D)) {
|
||||
if (!D)
|
||||
return 0;
|
||||
|
||||
if (TemplateTemplateParmDecl *TTP = dyn_cast<TemplateTemplateParmDecl>(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<NamedDecl>(D));
|
||||
return SemaRef.FindInstantiatedDecl(cast<NamedDecl>(D));
|
||||
}
|
||||
|
||||
Decl *TemplateInstantiator::TransformDefinition(Decl *D) {
|
||||
|
|
|
@ -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<typename Derived>
|
|||
NestedNameSpecifier *
|
||||
TreeTransform<Derived>::TransformNestedNameSpecifier(NestedNameSpecifier *NNS,
|
||||
SourceRange Range,
|
||||
QualType ObjectType) {
|
||||
QualType ObjectType,
|
||||
NamedDecl *FirstQualifierInScope) {
|
||||
if (!NNS)
|
||||
return 0;
|
||||
|
||||
|
@ -1599,13 +1605,15 @@ TreeTransform<Derived>::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<Derived>::TransformNestedNameSpecifier(NestedNameSpecifier *NNS,
|
|||
|
||||
return getDerived().RebuildNestedNameSpecifier(Prefix, Range,
|
||||
*NNS->getAsIdentifier(),
|
||||
ObjectType);
|
||||
ObjectType,
|
||||
FirstQualifierInScope);
|
||||
|
||||
case NestedNameSpecifier::Namespace: {
|
||||
NamespaceDecl *NS
|
||||
|
@ -4055,11 +4064,16 @@ TreeTransform<Derived>::TransformCXXUnresolvedMemberExpr(
|
|||
if (Base.isInvalid())
|
||||
return SemaRef.ExprError();
|
||||
|
||||
NamedDecl *FirstQualifierInScope
|
||||
= cast_or_null<NamedDecl>(
|
||||
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();
|
||||
}
|
||||
|
@ -4070,7 +4084,8 @@ TreeTransform<Derived>::TransformCXXUnresolvedMemberExpr(
|
|||
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<Derived>::TransformCXXUnresolvedMemberExpr(
|
|||
Qualifier,
|
||||
E->getQualifierRange(),
|
||||
Name,
|
||||
E->getMemberLoc());
|
||||
E->getMemberLoc(),
|
||||
FirstQualifierInScope);
|
||||
}
|
||||
|
||||
template<typename Derived>
|
||||
|
@ -4435,15 +4451,17 @@ NestedNameSpecifier *
|
|||
TreeTransform<Derived>::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<NestedNameSpecifier *>(
|
||||
SemaRef.ActOnCXXNestedNameSpecifier(0, SS, Range.getEnd(),
|
||||
SemaRef.BuildCXXNestedNameSpecifier(0, SS, Range.getEnd(),
|
||||
Range.getEnd(), II,
|
||||
ObjectType.getAsOpaquePtr(),
|
||||
ObjectType,
|
||||
FirstQualifierInScope,
|
||||
false));
|
||||
}
|
||||
|
||||
|
|
|
@ -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<A::sub>(); // xpected-note 2{{instantiation}}
|
||||
fun5<A::sub>(); // expected-note{{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
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
// RUN: clang-cc -fsyntax-only -verify %s
|
||||
// XFAIL
|
||||
template<typename T>
|
||||
void call_f0(T x) {
|
||||
x.Base::f0();
|
||||
|
@ -30,7 +29,8 @@ void test_f0_through_typedef(X0 x0) {
|
|||
template<typename TheBase, typename T>
|
||||
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<Base>(x0);
|
||||
call_f0_through_typedef2<OtherBase>(x1);
|
||||
call_f0_through_typedef2<Base>(x1); // expected-note{{here}}
|
||||
call_f0_through_typedef2<OtherBase>(x1); // expected-note{{instantiation}}
|
||||
call_f0_through_typedef2<Base>(x1); // expected-note{{instantiation}}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -380,7 +380,14 @@ welcome!</p>
|
|||
<td></td>
|
||||
</tr>
|
||||
<tr><td> 3.4.4 [basic.lookup.elab]</td><td></td><td></td><td></td><td></td><td></td></tr>
|
||||
<tr><td> 3.4.5 [basic.lookup.classref]</td><td></td><td></td><td></td><td></td><td></td></tr>
|
||||
<tr>
|
||||
<td> 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> 3.4.6 [basic.lookup.udir]</td><td></td><td></td><td></td><td></td><td></td></tr>
|
||||
<tr><td> 3.5 [basic.link]</td><td></td><td></td><td></td><td></td><td></td></tr>
|
||||
<tr><td> 3.6 [basic.start]</td><td></td><td></td><td></td><td></td><td></td></tr>
|
||||
|
|
Loading…
Reference in New Issue