Improved handling for dependent, qualified member access expressions, e.g.,

t->Base::f

where t has a dependent type. We save the nested-name-specifier in the
CXXUnresolvedMemberExpr then, during instantiation, substitute into
the nested-name-specifier with the (transformed) object type of t, so
that we get name lookup into the type of the object expression.

Note that we do not yet retain information about name lookup into the
lexical scope of the member access expression, so several regression
tests are still disabled.

llvm-svn: 80925
This commit is contained in:
Douglas Gregor 2009-09-03 16:14:30 +00:00
parent a7326b5ba5
commit c26e0f626b
6 changed files with 103 additions and 52 deletions

View File

@ -1300,23 +1300,31 @@ class CXXUnresolvedMemberExpr : public Expr {
/// \brief The location of the '->' or '.' operator.
SourceLocation OperatorLoc;
/// \brief The nested-name-specifier that precedes the member name, if any.
NestedNameSpecifier *Qualifier;
/// \brief The source range covering the nested name specifier.
SourceRange QualifierRange;
/// \brief The member to which this member expression refers, which
/// can be name, overloaded operator, or destructor.
/// FIXME: could also be a template-id, and we might have a
/// nested-name-specifier as well.
/// FIXME: could also be a template-id
DeclarationName Member;
/// \brief The location of the member name.
SourceLocation MemberLoc;
public:
CXXUnresolvedMemberExpr(ASTContext &C,
Expr *Base, bool IsArrow,
SourceLocation OperatorLoc,
NestedNameSpecifier *Qualifier,
SourceRange QualifierRange,
DeclarationName Member,
SourceLocation MemberLoc)
: Expr(CXXUnresolvedMemberExprClass, C.DependentTy, true, true),
Base(Base), IsArrow(IsArrow), OperatorLoc(OperatorLoc),
Qualifier(Qualifier), QualifierRange(QualifierRange),
Member(Member), MemberLoc(MemberLoc) { }
/// \brief Retrieve the base object of this member expressions,
@ -1333,6 +1341,14 @@ public:
SourceLocation getOperatorLoc() const { return OperatorLoc; }
void setOperatorLoc(SourceLocation L) { OperatorLoc = L; }
/// \brief Retrieve the nested-name-specifier that qualifies the member
/// name.
NestedNameSpecifier *getQualifier() const { return Qualifier; }
/// \brief Retrieve the source range covering the nested-name-specifier
/// that qualifies the member name.
SourceRange getQualifierRange() const { return QualifierRange; }
/// \brief Retrieve the name of the member that this expression
/// refers to.
DeclarationName getMember() const { return Member; }

View File

@ -1138,6 +1138,8 @@ StmtPrinter::VisitCXXUnresolvedConstructExpr(
void StmtPrinter::VisitCXXUnresolvedMemberExpr(CXXUnresolvedMemberExpr *Node) {
PrintExpr(Node->getBase());
OS << (Node->isArrow() ? "->" : ".");
if (NestedNameSpecifier *Qualifier = Node->getQualifier())
Qualifier->print(OS, Policy);
OS << Node->getMember().getAsString();
}

View File

@ -550,6 +550,7 @@ StmtProfiler::VisitCXXUnresolvedConstructExpr(CXXUnresolvedConstructExpr *S) {
void StmtProfiler::VisitCXXUnresolvedMemberExpr(CXXUnresolvedMemberExpr *S) {
VisitExpr(S);
ID.AddBoolean(S->isArrow());
VisitNestedNameSpecifier(S->getQualifier());
VisitName(S->getMember());
}

View File

@ -2025,7 +2025,9 @@ Sema::BuildMemberReferenceExpr(Scope *S, ExprArg Base, SourceLocation OpLoc,
if (BaseType->isDependentType())
return Owned(new (Context) CXXUnresolvedMemberExpr(Context,
BaseExpr, true,
OpLoc,
OpLoc,
(NestedNameSpecifier *)(SS? SS->getScopeRep() : 0),
SS? SS->getRange() : SourceRange(),
MemberName,
MemberLoc));
else if (const PointerType *PT = BaseType->getAs<PointerType>())
@ -2053,6 +2055,8 @@ Sema::BuildMemberReferenceExpr(Scope *S, ExprArg Base, SourceLocation OpLoc,
return Owned(new (Context) CXXUnresolvedMemberExpr(Context,
BaseExpr, false,
OpLoc,
(NestedNameSpecifier *)(SS? SS->getScopeRep() : 0),
SS? SS->getRange() : SourceRange(),
MemberName,
MemberLoc));
}
@ -2082,21 +2086,6 @@ Sema::BuildMemberReferenceExpr(Scope *S, ExprArg Base, SourceLocation OpLoc,
LookupResult Result
= LookupQualifiedName(DC, MemberName, LookupMemberName, false);
if (SS && SS->isSet()) {
QualType BaseTypeCanon
= Context.getCanonicalType(BaseType).getUnqualifiedType();
QualType MemberTypeCanon
= Context.getCanonicalType(
Context.getTypeDeclType(
dyn_cast<TypeDecl>(Result.getAsDecl()->getDeclContext())));
if (BaseTypeCanon != MemberTypeCanon &&
!IsDerivedFrom(BaseTypeCanon, MemberTypeCanon))
return ExprError(Diag(SS->getBeginLoc(),
diag::err_not_direct_base_or_virtual)
<< MemberTypeCanon << BaseTypeCanon);
}
if (!Result)
return ExprError(Diag(MemberLoc, diag::err_typecheck_no_member_deprecated)
<< MemberName << BaseExpr->getSourceRange());
@ -2106,6 +2095,21 @@ Sema::BuildMemberReferenceExpr(Scope *S, ExprArg Base, SourceLocation OpLoc,
return ExprError();
}
if (SS && SS->isSet()) {
QualType BaseTypeCanon
= Context.getCanonicalType(BaseType).getUnqualifiedType();
QualType MemberTypeCanon
= Context.getCanonicalType(
Context.getTypeDeclType(
dyn_cast<TypeDecl>(Result.getAsDecl()->getDeclContext())));
if (BaseTypeCanon != MemberTypeCanon &&
!IsDerivedFrom(BaseTypeCanon, MemberTypeCanon))
return ExprError(Diag(SS->getBeginLoc(),
diag::err_not_direct_base_or_virtual)
<< MemberTypeCanon << BaseTypeCanon);
}
NamedDecl *MemberDecl = Result;
// If the decl being referenced had an error, return an error for this

View File

@ -242,7 +242,8 @@ public:
/// nested-name-specifier. Subclasses may override this function to provide
/// alternate behavior.
NestedNameSpecifier *TransformNestedNameSpecifier(NestedNameSpecifier *NNS,
SourceRange Range);
SourceRange Range,
QualType ObjectType = QualType());
/// \brief Transform the given template name.
///
@ -497,7 +498,8 @@ public:
/// different behavior.
NestedNameSpecifier *RebuildNestedNameSpecifier(NestedNameSpecifier *Prefix,
SourceRange Range,
IdentifierInfo &II);
IdentifierInfo &II,
QualType ObjectType);
/// \brief Build a new nested-name-specifier given the prefix and the
/// namespace named in the next step in the nested-name-specifier.
@ -1449,23 +1451,23 @@ public:
OwningExprResult RebuildCXXUnresolvedMemberExpr(ExprArg BaseE,
bool IsArrow,
SourceLocation OperatorLoc,
NestedNameSpecifier *Qualifier,
SourceRange QualifierRange,
DeclarationName Name,
SourceLocation MemberLoc) {
OwningExprResult Base = move(BaseE);
tok::TokenKind OpKind = IsArrow? tok::arrow : tok::period;
CXXScopeSpec SS;
Sema::TypeTy *ObjectType = 0;
Base = SemaRef.ActOnStartCXXMemberReference(0, move(Base), OperatorLoc,
OpKind, ObjectType);
if (Base.isInvalid())
return SemaRef.ExprError();
SS.setRange(QualifierRange);
SS.setScopeRep(Qualifier);
Base = SemaRef.BuildMemberReferenceExpr(/*Scope=*/0,
move(Base), OperatorLoc, OpKind,
MemberLoc,
Name,
/*FIXME?*/Sema::DeclPtrTy::make((Decl*)0));
/*FIXME?*/Sema::DeclPtrTy::make((Decl*)0),
&SS);
return move(Base);
}
@ -1588,27 +1590,35 @@ Sema::OwningExprResult TreeTransform<Derived>::TransformExpr(Expr *E,
template<typename Derived>
NestedNameSpecifier *
TreeTransform<Derived>::TransformNestedNameSpecifier(NestedNameSpecifier *NNS,
SourceRange Range) {
SourceRange Range,
QualType ObjectType) {
if (!NNS)
return 0;
// Transform the prefix of this nested name specifier.
NestedNameSpecifier *Prefix = NNS->getPrefix();
if (Prefix) {
Prefix = getDerived().TransformNestedNameSpecifier(Prefix, Range);
Prefix = getDerived().TransformNestedNameSpecifier(Prefix, Range,
ObjectType);
if (!Prefix)
return 0;
// Clear out the object type; it only applies to the first element in
// the nested-name-specifier.
ObjectType = QualType();
}
switch (NNS->getKind()) {
case NestedNameSpecifier::Identifier:
assert(Prefix &&
"Can't have an identifier nested-name-specifier with no prefix");
if (!getDerived().AlwaysRebuild() && Prefix == NNS->getPrefix())
assert((Prefix || !ObjectType.isNull()) &&
"Identifier nested-name-specifier with no prefix or object type");
if (!getDerived().AlwaysRebuild() && Prefix == NNS->getPrefix() &&
ObjectType.isNull())
return NNS;
return getDerived().RebuildNestedNameSpecifier(Prefix, Range,
*NNS->getAsIdentifier());
*NNS->getAsIdentifier(),
ObjectType);
case NestedNameSpecifier::Namespace: {
NamespaceDecl *NS
@ -4037,18 +4047,38 @@ TreeTransform<Derived>::TransformCXXUnresolvedMemberExpr(
if (Base.isInvalid())
return SemaRef.ExprError();
Sema::TypeTy *ObjectType = 0;
Base = SemaRef.ActOnStartCXXMemberReference(0, move(Base),
E->getOperatorLoc(),
E->isArrow()? tok::arrow : tok::period,
ObjectType);
if (Base.isInvalid())
return SemaRef.ExprError();
NestedNameSpecifier *Qualifier = 0;
if (E->getQualifier()) {
Qualifier = getDerived().TransformNestedNameSpecifier(E->getQualifier(),
E->getQualifierRange(),
QualType::getFromOpaquePtr(ObjectType));
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())
return SemaRef.Owned(E->Retain());
return getDerived().RebuildCXXUnresolvedMemberExpr(move(Base),
E->isArrow(),
E->getOperatorLoc(),
E->getMember(),
Qualifier,
E->getQualifierRange(),
Name,
E->getMemberLoc());
}
@ -4404,7 +4434,8 @@ template<typename Derived>
NestedNameSpecifier *
TreeTransform<Derived>::RebuildNestedNameSpecifier(NestedNameSpecifier *Prefix,
SourceRange Range,
IdentifierInfo &II) {
IdentifierInfo &II,
QualType ObjectType) {
CXXScopeSpec SS;
// FIXME: The source location information is all wrong.
SS.setRange(Range);
@ -4412,7 +4443,7 @@ TreeTransform<Derived>::RebuildNestedNameSpecifier(NestedNameSpecifier *Prefix,
return static_cast<NestedNameSpecifier *>(
SemaRef.ActOnCXXNestedNameSpecifier(0, SS, Range.getEnd(),
Range.getEnd(), II,
/*FIXME:ObjectType=*/0,
ObjectType.getAsOpaquePtr(),
false));
}

View File

@ -103,25 +103,22 @@ namespace C
a.x();
a->foo();
#if 0
// FIXME: We need the notion of identifiers as dependent
// nested-name-specifiers without a prefix for this code to work.
// Things that work for the wrong reason
a.A::sub::x();
a.A::B::base::x();
a->A::member::foo();
// Things that work, but shouldn't
a.bad::x();
// Things that fail, but shouldn't
a.sub::x(); // xpected-error{{use of undeclared identifier 'sub'}}
a.base::x(); // xpected-error{{use of undeclared identifier 'base'}}
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(); // xpected-error{{use of undeclared identifier 'member'}}
#endif
a->member::foo();
}
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}}
}
}
// PR4703