From: John McCall Date: Tue, 1 Dec 2009 22:10:20 +0000 (+0000) Subject: Rework how we support C++ implicit member accesses. If we can resolve an X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=aa81e1658d87b9011125c632aa902d154ae4b02c;p=clang Rework how we support C++ implicit member accesses. If we can resolve an implicit member access to a specific declaration, go ahead and create it as a DeclRefExpr or a MemberExpr (with implicit CXXThisExpr base) as appropriate. Otherwise, create an UnresolvedMemberExpr or DependentScopeMemberExpr with a null base expression. By representing implicit accesses directly in the AST, we get the ability to correctly delay the decision about whether it's actually an instance member access or not until resolution is complete. This permits us to correctly avoid diagnosing the 'problem' of 'MyType::foo()' where the relationship to the type isn't really known until instantiation. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@90266 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/AST/ExprCXX.h b/include/clang/AST/ExprCXX.h index 23844ce5d9..66f4b5d2b5 100644 --- a/include/clang/AST/ExprCXX.h +++ b/include/clang/AST/ExprCXX.h @@ -1410,18 +1410,26 @@ public: /// \brief Represents a C++ member access expression where the actual /// member referenced could not be resolved because the base /// expression or the member name was dependent. +/// +/// Like UnresolvedMemberExprs, these can be either implicit or +/// explicit accesses. It is only possible to get one of these with +/// an implicit access if a qualifier is provided. class CXXDependentScopeMemberExpr : public Expr { /// \brief The expression for the base pointer or class reference, - /// e.g., the \c x in x.f. + /// e.g., the \c x in x.f. Can be null in implicit accesses. Stmt *Base; + /// \brief The type of the base expression. Never null, even for + /// implicit accesses. + QualType BaseType; + /// \brief Whether this member expression used the '->' operator or /// the '.' operator. bool IsArrow : 1; /// \brief Whether this member expression has explicitly-specified template /// arguments. - bool HasExplicitTemplateArgumentList : 1; + bool HasExplicitTemplateArgs : 1; /// \brief The location of the '->' or '.' operator. SourceLocation OperatorLoc; @@ -1452,9 +1460,7 @@ class CXXDependentScopeMemberExpr : public Expr { /// \brief Retrieve the explicit template argument list that followed the /// member template name, if any. ExplicitTemplateArgumentList *getExplicitTemplateArgumentList() { - if (!HasExplicitTemplateArgumentList) - return 0; - + assert(HasExplicitTemplateArgs); return reinterpret_cast(this + 1); } @@ -1466,7 +1472,7 @@ class CXXDependentScopeMemberExpr : public Expr { } CXXDependentScopeMemberExpr(ASTContext &C, - Expr *Base, bool IsArrow, + Expr *Base, QualType BaseType, bool IsArrow, SourceLocation OperatorLoc, NestedNameSpecifier *Qualifier, SourceRange QualifierRange, @@ -1477,7 +1483,8 @@ class CXXDependentScopeMemberExpr : public Expr { public: CXXDependentScopeMemberExpr(ASTContext &C, - Expr *Base, bool IsArrow, + Expr *Base, QualType BaseType, + bool IsArrow, SourceLocation OperatorLoc, NestedNameSpecifier *Qualifier, SourceRange QualifierRange, @@ -1485,15 +1492,15 @@ public: DeclarationName Member, SourceLocation MemberLoc) : Expr(CXXDependentScopeMemberExprClass, C.DependentTy, true, true), - Base(Base), IsArrow(IsArrow), HasExplicitTemplateArgumentList(false), - OperatorLoc(OperatorLoc), + Base(Base), BaseType(BaseType), IsArrow(IsArrow), + HasExplicitTemplateArgs(false), OperatorLoc(OperatorLoc), Qualifier(Qualifier), QualifierRange(QualifierRange), FirstQualifierFoundInScope(FirstQualifierFoundInScope), Member(Member), MemberLoc(MemberLoc) { } static CXXDependentScopeMemberExpr * Create(ASTContext &C, - Expr *Base, bool IsArrow, + Expr *Base, QualType BaseType, bool IsArrow, SourceLocation OperatorLoc, NestedNameSpecifier *Qualifier, SourceRange QualifierRange, @@ -1502,11 +1509,21 @@ public: SourceLocation MemberLoc, const TemplateArgumentListInfo *TemplateArgs); + /// \brief True if this is an implicit access, i.e. one in which the + /// member being accessed was not written in the source. The source + /// location of the operator is invalid in this case. + bool isImplicitAccess() const { return Base == 0; } + /// \brief Retrieve the base object of this member expressions, /// e.g., the \c x in \c x.m. - Expr *getBase() { return cast(Base); } + Expr *getBase() const { + assert(!isImplicitAccess()); + return cast(Base); + } void setBase(Expr *E) { Base = E; } + QualType getBaseType() const { return BaseType; } + /// \brief Determine whether this member expression used the '->' /// operator; otherwise, it used the '.' operator. bool isArrow() const { return IsArrow; } @@ -1551,60 +1568,59 @@ public: /// \brief Determines whether this member expression actually had a C++ /// template argument list explicitly specified, e.g., x.f. - bool hasExplicitTemplateArgumentList() const { - return HasExplicitTemplateArgumentList; + bool hasExplicitTemplateArgs() const { + return HasExplicitTemplateArgs; } /// \brief Copies the template arguments (if present) into the given /// structure. void copyTemplateArgumentsInto(TemplateArgumentListInfo &List) const { - if (hasExplicitTemplateArgumentList()) - getExplicitTemplateArgumentList()->copyInto(List); + assert(HasExplicitTemplateArgs); + getExplicitTemplateArgumentList()->copyInto(List); } /// \brief Retrieve the location of the left angle bracket following the /// member name ('<'), if any. SourceLocation getLAngleLoc() const { - if (!HasExplicitTemplateArgumentList) - return SourceLocation(); - + assert(HasExplicitTemplateArgs); return getExplicitTemplateArgumentList()->LAngleLoc; } /// \brief Retrieve the template arguments provided as part of this /// template-id. const TemplateArgumentLoc *getTemplateArgs() const { - if (!HasExplicitTemplateArgumentList) - return 0; - + assert(HasExplicitTemplateArgs); return getExplicitTemplateArgumentList()->getTemplateArgs(); } /// \brief Retrieve the number of template arguments provided as part of this /// template-id. unsigned getNumTemplateArgs() const { - if (!HasExplicitTemplateArgumentList) - return 0; - + assert(HasExplicitTemplateArgs); return getExplicitTemplateArgumentList()->NumTemplateArgs; } /// \brief Retrieve the location of the right angle bracket following the /// template arguments ('>'). SourceLocation getRAngleLoc() const { - if (!HasExplicitTemplateArgumentList) - return SourceLocation(); - + assert(HasExplicitTemplateArgs); return getExplicitTemplateArgumentList()->RAngleLoc; } virtual SourceRange getSourceRange() const { - if (HasExplicitTemplateArgumentList) - return SourceRange(Base->getSourceRange().getBegin(), - getRAngleLoc()); + SourceRange Range; + if (!isImplicitAccess()) + Range.setBegin(Base->getSourceRange().getBegin()); + else if (getQualifier()) + Range.setBegin(getQualifierRange().getBegin()); + else + Range.setBegin(MemberLoc); - return SourceRange(Base->getSourceRange().getBegin(), - MemberLoc); + if (hasExplicitTemplateArgs()) + Range.setEnd(getRAngleLoc()); + else + Range.setEnd(MemberLoc); + return Range; } static bool classof(const Stmt *T) { @@ -1618,17 +1634,31 @@ public: }; /// \brief Represents a C++ member access expression for which lookup -/// produced a set of overloaded functions. These are replaced with -/// MemberExprs in the final AST. +/// produced a set of overloaded functions. +/// +/// The member access may be explicit or implicit: +/// struct A { +/// int a, b; +/// int explicitAccess() { return this->a + this->A::b; } +/// int implicitAccess() { return a + A::b; } +/// }; +/// +/// In the final AST, an explicit access always becomes a MemberExpr. +/// An implicit access may become either a MemberExpr or a +/// DeclRefExpr, depending on whether the member is static. class UnresolvedMemberExpr : public Expr { /// The results. These are undesugared, which is to say, they may /// include UsingShadowDecls. UnresolvedSet Results; /// \brief The expression for the base pointer or class reference, - /// e.g., the \c x in x.f. + /// e.g., the \c x in x.f. This can be null if this is an 'unbased' + /// member expression Stmt *Base; + /// \brief The type of the base expression; never null. + QualType BaseType; + /// \brief Whether this member expression used the '->' operator or /// the '.' operator. bool IsArrow : 1; @@ -1672,7 +1702,7 @@ class UnresolvedMemberExpr : public Expr { UnresolvedMemberExpr(QualType T, bool Dependent, bool HasUnresolvedUsing, - Expr *Base, bool IsArrow, + Expr *Base, QualType BaseType, bool IsArrow, SourceLocation OperatorLoc, NestedNameSpecifier *Qualifier, SourceRange QualifierRange, @@ -1683,7 +1713,7 @@ class UnresolvedMemberExpr : public Expr { public: static UnresolvedMemberExpr * Create(ASTContext &C, bool Dependent, bool HasUnresolvedUsing, - Expr *Base, bool IsArrow, + Expr *Base, QualType BaseType, bool IsArrow, SourceLocation OperatorLoc, NestedNameSpecifier *Qualifier, SourceRange QualifierRange, @@ -1704,11 +1734,21 @@ public: unsigned getNumDecls() const { return Results.size(); } + /// \brief True if this is an implicit access, i.e. one in which the + /// member being accessed was not written in the source. The source + /// location of the operator is invalid in this case. + bool isImplicitAccess() const { return Base == 0; } + /// \brief Retrieve the base object of this member expressions, /// e.g., the \c x in \c x.m. - Expr *getBase() { return cast(Base); } + Expr *getBase() { + assert(!isImplicitAccess()); + return cast(Base); + } void setBase(Expr *E) { Base = E; } + QualType getBaseType() const { return BaseType; } + /// \brief Determine whether this member expression used the '->' /// operator; otherwise, it used the '.' operator. bool isArrow() const { return IsArrow; } @@ -1772,7 +1812,14 @@ public: } virtual SourceRange getSourceRange() const { - SourceRange Range = Base->getSourceRange(); + SourceRange Range; + if (!isImplicitAccess()) + Range.setBegin(Base->getSourceRange().getBegin()); + else if (getQualifier()) + Range.setBegin(getQualifierRange().getBegin()); + else + Range.setBegin(MemberLoc); + if (hasExplicitTemplateArgs()) Range.setEnd(getRAngleLoc()); else diff --git a/lib/AST/ExprCXX.cpp b/lib/AST/ExprCXX.cpp index d1a0390a0a..7a6fbdca8b 100644 --- a/lib/AST/ExprCXX.cpp +++ b/lib/AST/ExprCXX.cpp @@ -518,7 +518,8 @@ Stmt::child_iterator CXXUnresolvedConstructExpr::child_end() { } CXXDependentScopeMemberExpr::CXXDependentScopeMemberExpr(ASTContext &C, - Expr *Base, bool IsArrow, + Expr *Base, QualType BaseType, + bool IsArrow, SourceLocation OperatorLoc, NestedNameSpecifier *Qualifier, SourceRange QualifierRange, @@ -527,8 +528,8 @@ CXXDependentScopeMemberExpr::CXXDependentScopeMemberExpr(ASTContext &C, SourceLocation MemberLoc, const TemplateArgumentListInfo *TemplateArgs) : Expr(CXXDependentScopeMemberExprClass, C.DependentTy, true, true), - Base(Base), IsArrow(IsArrow), - HasExplicitTemplateArgumentList(TemplateArgs), + Base(Base), BaseType(BaseType), IsArrow(IsArrow), + HasExplicitTemplateArgs(TemplateArgs != 0), OperatorLoc(OperatorLoc), Qualifier(Qualifier), QualifierRange(QualifierRange), FirstQualifierFoundInScope(FirstQualifierFoundInScope), @@ -539,7 +540,7 @@ CXXDependentScopeMemberExpr::CXXDependentScopeMemberExpr(ASTContext &C, CXXDependentScopeMemberExpr * CXXDependentScopeMemberExpr::Create(ASTContext &C, - Expr *Base, bool IsArrow, + Expr *Base, QualType BaseType, bool IsArrow, SourceLocation OperatorLoc, NestedNameSpecifier *Qualifier, SourceRange QualifierRange, @@ -548,22 +549,22 @@ CXXDependentScopeMemberExpr::Create(ASTContext &C, SourceLocation MemberLoc, const TemplateArgumentListInfo *TemplateArgs) { if (!TemplateArgs) - return new (C) CXXDependentScopeMemberExpr(C, Base, IsArrow, OperatorLoc, - Qualifier, QualifierRange, - FirstQualifierFoundInScope, - Member, MemberLoc); + return new (C) CXXDependentScopeMemberExpr(C, Base, BaseType, + IsArrow, OperatorLoc, + Qualifier, QualifierRange, + FirstQualifierFoundInScope, + Member, MemberLoc); std::size_t size = sizeof(CXXDependentScopeMemberExpr); if (TemplateArgs) size += ExplicitTemplateArgumentList::sizeFor(*TemplateArgs); void *Mem = C.Allocate(size, llvm::alignof()); - return new (Mem) CXXDependentScopeMemberExpr(C, Base, IsArrow, OperatorLoc, - Qualifier, QualifierRange, - FirstQualifierFoundInScope, - Member, - MemberLoc, - TemplateArgs); + return new (Mem) CXXDependentScopeMemberExpr(C, Base, BaseType, + IsArrow, OperatorLoc, + Qualifier, QualifierRange, + FirstQualifierFoundInScope, + Member, MemberLoc, TemplateArgs); } Stmt::child_iterator CXXDependentScopeMemberExpr::child_begin() { @@ -571,12 +572,15 @@ Stmt::child_iterator CXXDependentScopeMemberExpr::child_begin() { } Stmt::child_iterator CXXDependentScopeMemberExpr::child_end() { + if (isImplicitAccess()) + return child_iterator(&Base); return child_iterator(&Base + 1); } UnresolvedMemberExpr::UnresolvedMemberExpr(QualType T, bool Dependent, bool HasUnresolvedUsing, - Expr *Base, bool IsArrow, + Expr *Base, QualType BaseType, + bool IsArrow, SourceLocation OperatorLoc, NestedNameSpecifier *Qualifier, SourceRange QualifierRange, @@ -584,7 +588,8 @@ UnresolvedMemberExpr::UnresolvedMemberExpr(QualType T, bool Dependent, SourceLocation MemberLoc, const TemplateArgumentListInfo *TemplateArgs) : Expr(UnresolvedMemberExprClass, T, Dependent, Dependent), - Base(Base), IsArrow(IsArrow), HasUnresolvedUsing(HasUnresolvedUsing), + Base(Base), BaseType(BaseType), IsArrow(IsArrow), + HasUnresolvedUsing(HasUnresolvedUsing), HasExplicitTemplateArgs(TemplateArgs != 0), OperatorLoc(OperatorLoc), Qualifier(Qualifier), QualifierRange(QualifierRange), @@ -596,7 +601,7 @@ UnresolvedMemberExpr::UnresolvedMemberExpr(QualType T, bool Dependent, UnresolvedMemberExpr * UnresolvedMemberExpr::Create(ASTContext &C, bool Dependent, bool HasUnresolvedUsing, - Expr *Base, bool IsArrow, + Expr *Base, QualType BaseType, bool IsArrow, SourceLocation OperatorLoc, NestedNameSpecifier *Qualifier, SourceRange QualifierRange, @@ -610,8 +615,8 @@ UnresolvedMemberExpr::Create(ASTContext &C, bool Dependent, void *Mem = C.Allocate(size, llvm::alignof()); return new (Mem) UnresolvedMemberExpr( Dependent ? C.DependentTy : C.OverloadTy, - Dependent, HasUnresolvedUsing, Base, IsArrow, - OperatorLoc, Qualifier, QualifierRange, + Dependent, HasUnresolvedUsing, Base, BaseType, + IsArrow, OperatorLoc, Qualifier, QualifierRange, Member, MemberLoc, TemplateArgs); } @@ -620,5 +625,7 @@ Stmt::child_iterator UnresolvedMemberExpr::child_begin() { } Stmt::child_iterator UnresolvedMemberExpr::child_end() { + if (isImplicitAccess()) + return child_iterator(&Base); return child_iterator(&Base + 1); } diff --git a/lib/AST/StmtPrinter.cpp b/lib/AST/StmtPrinter.cpp index 205ea0d182..a7e42af04d 100644 --- a/lib/AST/StmtPrinter.cpp +++ b/lib/AST/StmtPrinter.cpp @@ -1145,17 +1145,19 @@ StmtPrinter::VisitCXXUnresolvedConstructExpr( void StmtPrinter::VisitCXXDependentScopeMemberExpr( CXXDependentScopeMemberExpr *Node) { - PrintExpr(Node->getBase()); - OS << (Node->isArrow() ? "->" : "."); + if (!Node->isImplicitAccess()) { + PrintExpr(Node->getBase()); + OS << (Node->isArrow() ? "->" : "."); + } if (NestedNameSpecifier *Qualifier = Node->getQualifier()) Qualifier->print(OS, Policy); - else if (Node->hasExplicitTemplateArgumentList()) + else if (Node->hasExplicitTemplateArgs()) // FIXME: Track use of "template" keyword explicitly? OS << "template "; OS << Node->getMember().getAsString(); - if (Node->hasExplicitTemplateArgumentList()) { + if (Node->hasExplicitTemplateArgs()) { OS << TemplateSpecializationType::PrintTemplateArgumentList( Node->getTemplateArgs(), Node->getNumTemplateArgs(), @@ -1164,8 +1166,10 @@ void StmtPrinter::VisitCXXDependentScopeMemberExpr( } void StmtPrinter::VisitUnresolvedMemberExpr(UnresolvedMemberExpr *Node) { - PrintExpr(Node->getBase()); - OS << (Node->isArrow() ? "->" : "."); + if (!Node->isImplicitAccess()) { + PrintExpr(Node->getBase()); + OS << (Node->isArrow() ? "->" : "."); + } if (NestedNameSpecifier *Qualifier = Node->getQualifier()) Qualifier->print(OS, Policy); diff --git a/lib/AST/StmtProfile.cpp b/lib/AST/StmtProfile.cpp index d832a4649e..1b6b022901 100644 --- a/lib/AST/StmtProfile.cpp +++ b/lib/AST/StmtProfile.cpp @@ -554,18 +554,24 @@ StmtProfiler::VisitCXXUnresolvedConstructExpr(CXXUnresolvedConstructExpr *S) { void StmtProfiler::VisitCXXDependentScopeMemberExpr(CXXDependentScopeMemberExpr *S) { - VisitExpr(S); - ID.AddBoolean(S->isArrow()); + ID.AddBoolean(S->isImplicitAccess()); + if (!S->isImplicitAccess()) { + VisitExpr(S); + ID.AddBoolean(S->isArrow()); + } VisitNestedNameSpecifier(S->getQualifier()); VisitName(S->getMember()); - ID.AddBoolean(S->hasExplicitTemplateArgumentList()); - if (S->hasExplicitTemplateArgumentList()) + ID.AddBoolean(S->hasExplicitTemplateArgs()); + if (S->hasExplicitTemplateArgs()) VisitTemplateArguments(S->getTemplateArgs(), S->getNumTemplateArgs()); } void StmtProfiler::VisitUnresolvedMemberExpr(UnresolvedMemberExpr *S) { - VisitExpr(S); - ID.AddBoolean(S->isArrow()); + ID.AddBoolean(S->isImplicitAccess()); + if (!S->isImplicitAccess()) { + VisitExpr(S); + ID.AddBoolean(S->isArrow()); + } VisitNestedNameSpecifier(S->getQualifier()); VisitName(S->getMemberName()); ID.AddBoolean(S->hasExplicitTemplateArgs()); diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index f961406e3e..4b437995f1 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -1023,7 +1023,7 @@ public: SourceLocation RLoc, ExprArg Base,ExprArg Idx); - ExprResult + OwningExprResult BuildCallToMemberFunction(Scope *S, Expr *MemExpr, SourceLocation LParenLoc, Expr **Args, unsigned NumArgs, SourceLocation *CommaLocs, @@ -1451,9 +1451,10 @@ public: FieldDecl *Field, Expr *BaseObjectExpr = 0, SourceLocation OpLoc = SourceLocation()); - OwningExprResult BuildImplicitMemberReferenceExpr(const CXXScopeSpec &SS, - LookupResult &R, - const TemplateArgumentListInfo *TemplateArgs); + OwningExprResult BuildImplicitMemberExpr(const CXXScopeSpec &SS, + LookupResult &R, + const TemplateArgumentListInfo *TemplateArgs, + bool IsDefiniteInstance); bool UseArgumentDependentLookup(const CXXScopeSpec &SS, const LookupResult &R, bool HasTrailingLParen); @@ -1525,6 +1526,7 @@ public: SourceLocation RLoc); OwningExprResult BuildMemberReferenceExpr(ExprArg Base, + QualType BaseType, SourceLocation OpLoc, bool IsArrow, const CXXScopeSpec &SS, @@ -1534,6 +1536,7 @@ public: const TemplateArgumentListInfo *TemplateArgs); OwningExprResult BuildMemberReferenceExpr(ExprArg Base, + QualType BaseType, SourceLocation OpLoc, bool IsArrow, const CXXScopeSpec &SS, LookupResult &R, @@ -1551,6 +1554,7 @@ public: const LookupResult &R); OwningExprResult ActOnDependentMemberExpr(ExprArg Base, + QualType BaseType, bool IsArrow, SourceLocation OpLoc, const CXXScopeSpec &SS, @@ -1592,6 +1596,11 @@ public: MultiExprArg Args, SourceLocation *CommaLocs, SourceLocation RParenLoc); + OwningExprResult BuildResolvedCallExpr(Expr *Fn, + NamedDecl *NDecl, + SourceLocation LParenLoc, + Expr **Args, unsigned NumArgs, + SourceLocation RParenLoc); virtual OwningExprResult ActOnCastExpr(Scope *S, SourceLocation LParenLoc, TypeTy *Ty, SourceLocation RParenLoc, diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index bf14d0d303..8e14fcdd6b 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -703,18 +703,173 @@ static bool IsDependentIdExpression(Sema &SemaRef, const CXXScopeSpec &SS) { // We can't look into record types unless they're fully-formed. if (!IsFullyFormedScope(SemaRef, cast(DC))) return true; - // We can always look into fully-formed record types, but if we're - // in a dependent but not fully-formed context, we can't decide - // whether the qualifier names a base class. We shouldn't be trying - // to decide that yet anyway, but we are, so we need to delay that - // decision. - CXXRecordDecl *CurRecord; - if (CXXMethodDecl *CurMethod = dyn_cast(SemaRef.CurContext)) - CurRecord = cast(CurMethod->getParent()); - else - CurRecord = dyn_cast(SemaRef.CurContext); + return false; +} + +/// Determines if the given class is provably not derived from all of +/// the prospective base classes. +static bool IsProvablyNotDerivedFrom(Sema &SemaRef, + CXXRecordDecl *Record, + const llvm::SmallPtrSet &Bases) { + Record = Record->getCanonicalDecl(); + if (Bases.count(Record)) + return false; + + for (CXXRecordDecl::base_class_iterator I = Record->bases_begin(), + E = Record->bases_end(); I != E; ++I) { + CanQualType BaseT = SemaRef.Context.getCanonicalType((*I).getType()); + CanQual BaseRT = BaseT->getAs(); + if (!BaseRT) return false; + + CXXRecordDecl *BaseRecord = cast(BaseRT->getDecl()); + if (!BaseRecord->isDefinition()) + return false; + + if (!IsProvablyNotDerivedFrom(SemaRef, BaseRecord, Bases)) + return false; + } + + return true; +} + +static bool IsInstanceMember(NamedDecl *D) { + assert(isa(D->getDeclContext()) && + "checking whether non-member is instance member"); + + if (isa(D)) return true; + + if (isa(D)) + return !cast(D)->isStatic(); + + if (isa(D)) { + D = cast(D)->getTemplatedDecl(); + return !cast(D)->isStatic(); + } + + return false; +} + +enum IMAKind { + /// The reference is definitely not an instance member access. + IMA_Static, + + /// The reference may be an implicit instance member access. + IMA_Mixed, + + /// The reference may be to an instance member, but it is invalid if + /// so, because the context is not an instance method. + IMA_Mixed_StaticContext, + + /// The reference may be to an instance member, but it is invalid if + /// so, because the context is from an unrelated class. + IMA_Mixed_Unrelated, + + /// The reference is definitely an implicit instance member access. + IMA_Instance, + + /// The reference may be to an unresolved using declaration. + IMA_Unresolved, + + /// The reference may be to an unresolved using declaration and the + /// context is not an instance method. + IMA_Unresolved_StaticContext, + + /// The reference is to a member of an anonymous structure in a + /// non-class context. + IMA_AnonymousMember, + + /// All possible referrents are instance members and the current + /// context is not an instance method. + IMA_Error_StaticContext, + + /// All possible referrents are instance members of an unrelated + /// class. + IMA_Error_Unrelated +}; - return CurRecord && !IsFullyFormedScope(SemaRef, CurRecord); +/// The given lookup names class member(s) and is not being used for +/// an address-of-member expression. Classify the type of access +/// according to whether it's possible that this reference names an +/// instance member. This is best-effort; it is okay to +/// conservatively answer "yes", in which case some errors will simply +/// not be caught until template-instantiation. +static IMAKind ClassifyImplicitMemberAccess(Sema &SemaRef, + const LookupResult &R) { + assert(!R.empty() && isa((*R.begin())->getDeclContext())); + + bool isStaticContext = + (!isa(SemaRef.CurContext) || + cast(SemaRef.CurContext)->isStatic()); + + if (R.isUnresolvableResult()) + return isStaticContext ? IMA_Unresolved_StaticContext : IMA_Unresolved; + + // Collect all the declaring classes of instance members we find. + bool hasNonInstance = false; + llvm::SmallPtrSet Classes; + for (LookupResult::iterator I = R.begin(), E = R.end(); I != E; ++I) { + NamedDecl *D = (*I)->getUnderlyingDecl(); + if (IsInstanceMember(D)) { + CXXRecordDecl *R = cast(D->getDeclContext()); + + // If this is a member of an anonymous record, move out to the + // innermost non-anonymous struct or union. If there isn't one, + // that's a special case. + while (R->isAnonymousStructOrUnion()) { + R = dyn_cast(R->getParent()); + if (!R) return IMA_AnonymousMember; + } + Classes.insert(R->getCanonicalDecl()); + } + else + hasNonInstance = true; + } + + // If we didn't find any instance members, it can't be an implicit + // member reference. + if (Classes.empty()) + return IMA_Static; + + // If the current context is not an instance method, it can't be + // an implicit member reference. + if (isStaticContext) + return (hasNonInstance ? IMA_Mixed_StaticContext : IMA_Error_StaticContext); + + // If we can prove that the current context is unrelated to all the + // declaring classes, it can't be an implicit member reference (in + // which case it's an error if any of those members are selected). + if (IsProvablyNotDerivedFrom(SemaRef, + cast(SemaRef.CurContext)->getParent(), + Classes)) + return (hasNonInstance ? IMA_Mixed_Unrelated : IMA_Error_Unrelated); + + return (hasNonInstance ? IMA_Mixed : IMA_Instance); +} + +/// Diagnose a reference to a field with no object available. +static void DiagnoseInstanceReference(Sema &SemaRef, + const CXXScopeSpec &SS, + const LookupResult &R) { + SourceLocation Loc = R.getNameLoc(); + SourceRange Range(Loc); + if (SS.isSet()) Range.setBegin(SS.getRange().getBegin()); + + if (R.getAsSingle()) { + if (CXXMethodDecl *MD = dyn_cast(SemaRef.CurContext)) { + if (MD->isStatic()) { + // "invalid use of member 'x' in static member function" + SemaRef.Diag(Loc, diag::err_invalid_member_use_in_static_method) + << Range << R.getLookupName(); + return; + } + } + + SemaRef.Diag(Loc, diag::err_invalid_non_static_member_use) + << R.getLookupName() << Range; + return; + } + + SemaRef.Diag(Loc, diag::err_member_call_without_object) << Range; } Sema::OwningExprResult Sema::ActOnIdExpression(Scope *S, @@ -847,23 +1002,41 @@ Sema::OwningExprResult Sema::ActOnIdExpression(Scope *S, } } - // &SomeClass::foo is an abstract member reference, regardless of - // the nature of foo, but &SomeClass::foo(...) is not. If this is - // *not* an abstract member reference, and any of the results is a - // class member (which necessarily means they're all class members), - // then we make an implicit member reference instead. - // - // This check considers all the same information as the "needs ADL" - // check, but there's no simple logical relationship other than the - // fact that they can never be simultaneously true. We could - // calculate them both in one pass if that proves important for - // performance. - if (!ADL) { + // Check whether this might be a C++ implicit instance member access. + // C++ [expr.prim.general]p6: + // Within the definition of a non-static member function, an + // identifier that names a non-static member is transformed to a + // class member access expression. + // But note that &SomeClass::foo is grammatically distinct, even + // though we don't parse it that way. + if (!R.empty() && (*R.begin())->getDeclContext()->isRecord()) { bool isAbstractMemberPointer = (isAddressOfOperand && !SS.isEmpty()); - if (!isAbstractMemberPointer && !R.empty() && - isa((*R.begin())->getDeclContext())) { - return BuildImplicitMemberReferenceExpr(SS, R, TemplateArgs); + if (!isAbstractMemberPointer) { + switch (ClassifyImplicitMemberAccess(*this, R)) { + case IMA_Instance: + return BuildImplicitMemberExpr(SS, R, TemplateArgs, true); + + case IMA_AnonymousMember: + assert(R.isSingleResult()); + return BuildAnonymousStructUnionMemberReference(R.getNameLoc(), + R.getAsSingle()); + + case IMA_Mixed: + case IMA_Mixed_Unrelated: + case IMA_Unresolved: + return BuildImplicitMemberExpr(SS, R, TemplateArgs, false); + + case IMA_Static: + case IMA_Mixed_StaticContext: + case IMA_Unresolved_StaticContext: + break; + + case IMA_Error_StaticContext: + case IMA_Error_Unrelated: + DiagnoseInstanceReference(*this, SS, R); + return ExprError(); + } } } @@ -1041,35 +1214,15 @@ static MemberExpr *BuildMemberExpr(ASTContext &C, Expr *Base, bool isArrow, Member, Loc, TemplateArgs, Ty); } -/// Return true if all the decls in the given result are instance -/// methods. -static bool IsOnlyInstanceMethods(const LookupResult &R) { - for (LookupResult::iterator I = R.begin(), E = R.end(); I != E; ++I) { - NamedDecl *D = (*I)->getUnderlyingDecl(); - - CXXMethodDecl *Method; - if (isa(D)) - Method = cast(cast(D) - ->getTemplatedDecl()); - else if (isa(D)) - Method = cast(D); - else - return false; - - if (Method->isStatic()) - return false; - } - - return true; -} - -/// Builds an implicit member access expression from the given -/// unqualified lookup set, which is known to contain only class -/// members. +/// Builds an implicit member access expression. The current context +/// is known to be an instance method, and the given unqualified lookup +/// set is known to contain only instance members, at least one of which +/// is from an appropriate type. Sema::OwningExprResult -Sema::BuildImplicitMemberReferenceExpr(const CXXScopeSpec &SS, - LookupResult &R, - const TemplateArgumentListInfo *TemplateArgs) { +Sema::BuildImplicitMemberExpr(const CXXScopeSpec &SS, + LookupResult &R, + const TemplateArgumentListInfo *TemplateArgs, + bool IsKnownInstance) { assert(!R.empty() && !R.isAmbiguous()); SourceLocation Loc = R.getNameLoc(); @@ -1082,44 +1235,18 @@ Sema::BuildImplicitMemberReferenceExpr(const CXXScopeSpec &SS, if (cast(FD->getDeclContext())->isAnonymousStructOrUnion()) return BuildAnonymousStructUnionMemberReference(Loc, FD); - QualType ThisType; - if (isImplicitMemberReference(R, ThisType)) { - Expr *This = new (Context) CXXThisExpr(SourceLocation(), ThisType); - return BuildMemberReferenceExpr(ExprArg(*this, This), - /*OpLoc*/ SourceLocation(), - /*IsArrow*/ true, - SS, R, TemplateArgs); + // If this is known to be an instance access, go ahead and build a + // 'this' expression now. + QualType ThisType = cast(CurContext)->getThisType(Context); + Expr *This = 0; // null signifies implicit access + if (IsKnownInstance) { + This = new (Context) CXXThisExpr(SourceLocation(), ThisType); } - // Diagnose now if none of the available methods are static. - if (IsOnlyInstanceMethods(R)) - return ExprError(Diag(Loc, diag::err_member_call_without_object)); - - if (R.getAsSingle()) { - if (CXXMethodDecl *MD = dyn_cast(CurContext)) { - if (MD->isStatic()) { - // "invalid use of member 'x' in static member function" - Diag(Loc, diag::err_invalid_member_use_in_static_method) - << R.getLookupName(); - return ExprError(); - } - } - - // Any other ways we could have found the field in a well-formed - // program would have been turned into implicit member expressions - // above. - Diag(Loc, diag::err_invalid_non_static_member_use) - << R.getLookupName(); - return ExprError(); - } - - // We're not in an implicit member-reference context, but the lookup - // results might not require an instance. Try to build a non-member - // decl reference. - if (TemplateArgs) - return BuildTemplateIdExpr(SS, R, /* ADL */ false, *TemplateArgs); - - return BuildDeclarationNameExpr(SS, R, /*ADL*/ false); + return BuildMemberReferenceExpr(ExprArg(*this, This), ThisType, + /*OpLoc*/ SourceLocation(), + /*IsArrow*/ true, + SS, R, TemplateArgs); } bool Sema::UseArgumentDependentLookup(const CXXScopeSpec &SS, @@ -1205,8 +1332,6 @@ Sema::OwningExprResult Sema::BuildDeclarationNameExpr(const CXXScopeSpec &SS, LookupResult &R, bool NeedsADL) { - assert(R.getResultKind() != LookupResult::FoundUnresolvedValue); - // If this isn't an overloaded result and we don't need ADL, just // build an ordinary singleton decl ref. if (!NeedsADL && !R.isOverloadedResult()) @@ -1968,7 +2093,8 @@ static Decl *FindGetterNameDecl(const ObjCObjectPointerType *QIdTy, } Sema::OwningExprResult -Sema::ActOnDependentMemberExpr(ExprArg Base, bool IsArrow, SourceLocation OpLoc, +Sema::ActOnDependentMemberExpr(ExprArg Base, QualType BaseType, + bool IsArrow, SourceLocation OpLoc, const CXXScopeSpec &SS, NamedDecl *FirstQualifierInScope, DeclarationName Name, SourceLocation NameLoc, @@ -1985,20 +2111,21 @@ Sema::ActOnDependentMemberExpr(ExprArg Base, bool IsArrow, SourceLocation OpLoc, // accessing the 'f' property if T is an Obj-C interface. The extra check // allows this, while still reporting an error if T is a struct pointer. if (!IsArrow) { - const PointerType *PT = BaseExpr->getType()->getAs(); + const PointerType *PT = BaseType->getAs(); if (PT && (!getLangOptions().ObjC1 || PT->getPointeeType()->isRecordType())) { + assert(BaseExpr && "cannot happen with implicit member accesses"); Diag(NameLoc, diag::err_typecheck_member_reference_struct_union) - << BaseExpr->getType() << BaseExpr->getSourceRange(); + << BaseType << BaseExpr->getSourceRange(); return ExprError(); } } - assert(BaseExpr->getType()->isDependentType()); + assert(BaseType->isDependentType()); // 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. - return Owned(CXXDependentScopeMemberExpr::Create(Context, BaseExpr, + return Owned(CXXDependentScopeMemberExpr::Create(Context, BaseExpr, BaseType, IsArrow, OpLoc, static_cast(SS.getScopeRep()), SS.getRange(), @@ -2041,34 +2168,71 @@ bool Sema::CheckQualifiedMemberReference(Expr *BaseExpr, NestedNameSpecifier *Qualifier, SourceRange QualifierRange, const LookupResult &R) { - QualType BaseTypeCanon - = Context.getCanonicalType(BaseType).getUnqualifiedType(); - - bool FoundValid = false; + const RecordType *BaseRT = BaseType->getAs(); + if (!BaseRT) { + // We can't check this yet because the base type is still + // dependent. + assert(BaseType->isDependentType()); + return false; + } + CXXRecordDecl *BaseRecord = cast(BaseRT->getDecl()); for (LookupResult::iterator I = R.begin(), E = R.end(); I != E; ++I) { - TypeDecl* TyD = cast((*I)->getUnderlyingDecl()->getDeclContext()); - CanQualType MemberTypeCanon - = Context.getCanonicalType(Context.getTypeDeclType(TyD)); + // If this is an implicit member reference and we find a + // non-instance member, it's not an error. + if (!BaseExpr && !IsInstanceMember((*I)->getUnderlyingDecl())) + return false; - if (BaseTypeCanon == MemberTypeCanon || - IsDerivedFrom(BaseTypeCanon, MemberTypeCanon)) { - FoundValid = true; - break; - } + // Note that we use the DC of the decl, not the underlying decl. + CXXRecordDecl *RecordD = cast((*I)->getDeclContext()); + while (RecordD->isAnonymousStructOrUnion()) + RecordD = cast(RecordD->getParent()); + + llvm::SmallPtrSet MemberRecord; + MemberRecord.insert(RecordD->getCanonicalDecl()); + + if (!IsProvablyNotDerivedFrom(*this, BaseRecord, MemberRecord)) + return false; } - if (!FoundValid) { - DiagnoseQualifiedMemberReference(*this, BaseExpr, BaseType, + DiagnoseQualifiedMemberReference(*this, BaseExpr, BaseType, Qualifier, QualifierRange, R); + return true; +} + +static bool +LookupMemberExprInRecord(Sema &SemaRef, LookupResult &R, + SourceRange BaseRange, const RecordType *RTy, + SourceLocation OpLoc, const CXXScopeSpec &SS) { + RecordDecl *RDecl = RTy->getDecl(); + if (SemaRef.RequireCompleteType(OpLoc, QualType(RTy, 0), + PDiag(diag::err_typecheck_incomplete_tag) + << BaseRange)) return true; + + DeclContext *DC = RDecl; + if (SS.isSet()) { + // If the member name was a qualified-id, look into the + // nested-name-specifier. + DC = SemaRef.computeDeclContext(SS, false); + + assert(DC && "Cannot handle non-computable dependent contexts in lookup"); + + if (!isa(DC)) { + SemaRef.Diag(R.getNameLoc(), diag::err_qualified_member_nonclass) + << DC << SS.getRange(); + return true; + } } + // The record definition is complete, now look up the member. + SemaRef.LookupQualifiedName(R, DC); + return false; } Sema::OwningExprResult -Sema::BuildMemberReferenceExpr(ExprArg BaseArg, +Sema::BuildMemberReferenceExpr(ExprArg BaseArg, QualType BaseType, SourceLocation OpLoc, bool IsArrow, const CXXScopeSpec &SS, NamedDecl *FirstQualifierInScope, @@ -2076,38 +2240,52 @@ Sema::BuildMemberReferenceExpr(ExprArg BaseArg, const TemplateArgumentListInfo *TemplateArgs) { Expr *Base = BaseArg.takeAs(); - if (Base->getType()->isDependentType()) - return ActOnDependentMemberExpr(ExprArg(*this, Base), + if (BaseType->isDependentType()) + return ActOnDependentMemberExpr(ExprArg(*this, Base), BaseType, IsArrow, OpLoc, SS, FirstQualifierInScope, Name, NameLoc, TemplateArgs); LookupResult R(*this, Name, NameLoc, LookupMemberName); - OwningExprResult Result = - LookupMemberExpr(R, Base, IsArrow, OpLoc, - SS, FirstQualifierInScope, - /*ObjCImpDecl*/ DeclPtrTy()); - if (Result.isInvalid()) { - Owned(Base); - return ExprError(); - } + // Implicit member accesses. + if (!Base) { + QualType RecordTy = BaseType; + if (IsArrow) RecordTy = RecordTy->getAs()->getPointeeType(); + if (LookupMemberExprInRecord(*this, R, SourceRange(), + RecordTy->getAs(), + OpLoc, SS)) + return ExprError(); + + // Explicit member accesses. + } else { + OwningExprResult Result = + LookupMemberExpr(R, Base, IsArrow, OpLoc, + SS, FirstQualifierInScope, + /*ObjCImpDecl*/ DeclPtrTy()); - if (Result.get()) - return move(Result); + if (Result.isInvalid()) { + Owned(Base); + return ExprError(); + } + + if (Result.get()) + return move(Result); + } - return BuildMemberReferenceExpr(ExprArg(*this, Base), OpLoc, - IsArrow, SS, R, TemplateArgs); + return BuildMemberReferenceExpr(ExprArg(*this, Base), BaseType, + OpLoc, IsArrow, SS, R, TemplateArgs); } Sema::OwningExprResult -Sema::BuildMemberReferenceExpr(ExprArg Base, SourceLocation OpLoc, - bool IsArrow, const CXXScopeSpec &SS, +Sema::BuildMemberReferenceExpr(ExprArg Base, QualType BaseExprType, + SourceLocation OpLoc, bool IsArrow, + const CXXScopeSpec &SS, LookupResult &R, const TemplateArgumentListInfo *TemplateArgs) { Expr *BaseExpr = Base.takeAs(); - QualType BaseType = BaseExpr->getType(); + QualType BaseType = BaseExprType; if (IsArrow) { assert(BaseType->isPointerType()); BaseType = BaseType->getAs()->getPointeeType(); @@ -2128,7 +2306,8 @@ Sema::BuildMemberReferenceExpr(ExprArg Base, SourceLocation OpLoc, : BaseType->getAs()->getDecl()); Diag(R.getNameLoc(), diag::err_no_member) - << MemberName << DC << BaseExpr->getSourceRange(); + << MemberName << DC + << (BaseExpr ? BaseExpr->getSourceRange() : SourceRange()); return ExprError(); } @@ -2142,15 +2321,15 @@ Sema::BuildMemberReferenceExpr(ExprArg Base, SourceLocation OpLoc, // Construct an unresolved result if we in fact got an unresolved // result. if (R.isOverloadedResult() || R.isUnresolvableResult()) { - bool Dependent = R.isUnresolvableResult(); - Dependent = Dependent || - UnresolvedLookupExpr::ComputeDependence(R.begin(), R.end(), - TemplateArgs); + bool Dependent = + R.isUnresolvableResult() || + UnresolvedLookupExpr::ComputeDependence(R.begin(), R.end(), TemplateArgs); UnresolvedMemberExpr *MemExpr = UnresolvedMemberExpr::Create(Context, Dependent, R.isUnresolvableResult(), - BaseExpr, IsArrow, OpLoc, + BaseExpr, BaseExprType, + IsArrow, OpLoc, Qualifier, SS.getRange(), MemberName, MemberLoc, TemplateArgs); @@ -2171,6 +2350,15 @@ Sema::BuildMemberReferenceExpr(ExprArg Base, SourceLocation OpLoc, if (MemberDecl->isInvalidDecl()) return ExprError(); + // Handle the implicit-member-access case. + if (!BaseExpr) { + // If this is not an instance member, convert to a non-member access. + if (!IsInstanceMember(MemberDecl)) + return BuildDeclarationNameExpr(SS, R.getNameLoc(), MemberDecl); + + BaseExpr = new (Context) CXXThisExpr(SourceLocation(), BaseExprType); + } + bool ShouldCheckUse = true; if (CXXMethodDecl *MD = dyn_cast(MemberDecl)) { // Don't diagnose the use of a virtual member function unless it's @@ -2403,31 +2591,9 @@ Sema::LookupMemberExpr(LookupResult &R, Expr *&BaseExpr, // Handle field access to simple records. This also handles access // to fields of the ObjC 'id' struct. if (const RecordType *RTy = BaseType->getAs()) { - RecordDecl *RDecl = RTy->getDecl(); - if (RequireCompleteType(OpLoc, BaseType, - PDiag(diag::err_typecheck_incomplete_tag) - << BaseExpr->getSourceRange())) + if (LookupMemberExprInRecord(*this, R, BaseExpr->getSourceRange(), + RTy, OpLoc, SS)) return ExprError(); - - DeclContext *DC = RDecl; - if (SS.isSet()) { - // If the member name was a qualified-id, look into the - // nested-name-specifier. - DC = computeDeclContext(SS, false); - - if (!isa(DC)) { - Diag(MemberLoc, diag::err_qualified_member_nonclass) - << DC << SS.getRange(); - return ExprError(); - } - - // FIXME: If DC is not computable, we should build a - // CXXDependentScopeMemberExpr. - assert(DC && "Cannot handle non-computable dependent contexts in lookup"); - } - - // The record definition is complete, now make sure the member is valid. - LookupQualifiedName(R, DC); return Owned((Expr*) 0); } @@ -2739,7 +2905,7 @@ Sema::OwningExprResult Sema::ActOnMemberAccessExpr(Scope *S, ExprArg BaseArg, Expr *Base = BaseArg.takeAs(); OwningExprResult Result(*this); if (Base->getType()->isDependentType()) { - Result = ActOnDependentMemberExpr(ExprArg(*this, Base), + Result = ActOnDependentMemberExpr(ExprArg(*this, Base), Base->getType(), IsArrow, OpLoc, SS, FirstQualifierInScope, Name, NameLoc, @@ -2772,8 +2938,8 @@ Sema::OwningExprResult Sema::ActOnMemberAccessExpr(Scope *S, ExprArg BaseArg, } } - Result = BuildMemberReferenceExpr(ExprArg(*this, Base), OpLoc, - IsArrow, SS, R, TemplateArgs); + Result = BuildMemberReferenceExpr(ExprArg(*this, Base), Base->getType(), + OpLoc, IsArrow, SS, R, TemplateArgs); } return move(Result); @@ -3070,16 +3236,16 @@ Sema::ActOnCallExpr(Scope *S, ExprArg fn, SourceLocation LParenLoc, isa(*MemE->decls_begin())); (void)MemE; - return Owned(BuildCallToMemberFunction(S, Fn, LParenLoc, Args, NumArgs, - CommaLocs, RParenLoc)); + return BuildCallToMemberFunction(S, Fn, LParenLoc, Args, NumArgs, + CommaLocs, RParenLoc); } // Determine whether this is a call to a member function. if (MemberExpr *MemExpr = dyn_cast(NakedFn)) { NamedDecl *MemDecl = MemExpr->getMemberDecl(); if (isa(MemDecl)) - return Owned(BuildCallToMemberFunction(S, Fn, LParenLoc, Args, NumArgs, - CommaLocs, RParenLoc)); + return BuildCallToMemberFunction(S, Fn, LParenLoc, Args, NumArgs, + CommaLocs, RParenLoc); } // Determine whether this is a call to a pointer-to-member function. @@ -3171,13 +3337,28 @@ Sema::ActOnCallExpr(Scope *S, ExprArg fn, SourceLocation LParenLoc, } else { assert(Fns.size() <= 1 && "overloaded without Overloaded flag"); if (Fns.empty()) - NDecl = FDecl = 0; + NDecl = 0; else { NDecl = Fns[0]; - FDecl = dyn_cast(NDecl); } } + return BuildResolvedCallExpr(Fn, NDecl, LParenLoc, Args, NumArgs, RParenLoc); +} + +/// BuildCallExpr - Build a call to a resolved expression, i.e. an +/// expression not of \p OverloadTy. The expression should +/// unary-convert to an expression of function-pointer or +/// block-pointer type. +/// +/// \param NDecl the declaration being called, if available +Sema::OwningExprResult +Sema::BuildResolvedCallExpr(Expr *Fn, NamedDecl *NDecl, + SourceLocation LParenLoc, + Expr **Args, unsigned NumArgs, + SourceLocation RParenLoc) { + FunctionDecl *FDecl = dyn_cast_or_null(NDecl); + // Promote the function operand. UsualUnaryConversions(Fn); diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index 6ea6a14557..726e4a01a4 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -5122,7 +5122,7 @@ Sema::CreateOverloadedArraySubscriptExpr(SourceLocation LLoc, /// parameter). The caller needs to validate that the member /// expression refers to a member function or an overloaded member /// function. -Sema::ExprResult +Sema::OwningExprResult Sema::BuildCallToMemberFunction(Scope *S, Expr *MemExprE, SourceLocation LParenLoc, Expr **Args, unsigned NumArgs, SourceLocation *CommaLocs, @@ -5131,22 +5131,35 @@ Sema::BuildCallToMemberFunction(Scope *S, Expr *MemExprE, // argument and the member function we're referring to. Expr *NakedMemExpr = MemExprE->IgnoreParens(); - // Extract the object argument. - Expr *ObjectArg; - MemberExpr *MemExpr; CXXMethodDecl *Method = 0; if (isa(NakedMemExpr)) { MemExpr = cast(NakedMemExpr); - ObjectArg = MemExpr->getBase(); Method = cast(MemExpr->getMemberDecl()); } else { UnresolvedMemberExpr *UnresExpr = cast(NakedMemExpr); - ObjectArg = UnresExpr->getBase(); + + // Mock up an object argument. + Expr *ObjectArg; + if (UnresExpr->isImplicitAccess()) { + // It would be nice to avoid creating this, but the overload APIs are written + // to work on expressions. + ObjectArg = new(Context) CXXThisExpr(SourceLocation(), + UnresExpr->getBaseType()); + } else { + ObjectArg = UnresExpr->getBase(); + } // Add overload candidates OverloadCandidateSet CandidateSet; + // FIXME: avoid copy. + TemplateArgumentListInfo TemplateArgsBuffer, *TemplateArgs = 0; + if (UnresExpr->hasExplicitTemplateArgs()) { + UnresExpr->copyTemplateArgumentsInto(TemplateArgsBuffer); + TemplateArgs = &TemplateArgsBuffer; + } + for (UnresolvedMemberExpr::decls_iterator I = UnresExpr->decls_begin(), E = UnresExpr->decls_end(); I != E; ++I) { @@ -5156,20 +5169,14 @@ Sema::BuildCallToMemberFunction(Scope *S, Expr *MemExprE, if ((Method = dyn_cast(Func))) { // If explicit template arguments were provided, we can't call a // non-template member function. - if (UnresExpr->hasExplicitTemplateArgs()) + if (TemplateArgs) continue; AddMethodCandidate(Method, ObjectArg, Args, NumArgs, CandidateSet, /*SuppressUserConversions=*/false); } else { - // FIXME: avoid copy. - TemplateArgumentListInfo TemplateArgs; - if (UnresExpr->hasExplicitTemplateArgs()) - UnresExpr->copyTemplateArgumentsInto(TemplateArgs); - AddMethodTemplateCandidate(cast(Func), - (UnresExpr->hasExplicitTemplateArgs() - ? &TemplateArgs : 0), + TemplateArgs, ObjectArg, Args, NumArgs, CandidateSet, /*SuppressUsedConversions=*/false); @@ -5190,14 +5197,14 @@ Sema::BuildCallToMemberFunction(Scope *S, Expr *MemExprE, << DeclName << MemExprE->getSourceRange(); PrintOverloadCandidates(CandidateSet, /*OnlyViable=*/false); // FIXME: Leaking incoming expressions! - return true; + return ExprError(); case OR_Ambiguous: Diag(UnresExpr->getMemberLoc(), diag::err_ovl_ambiguous_member_call) << DeclName << MemExprE->getSourceRange(); PrintOverloadCandidates(CandidateSet, /*OnlyViable=*/false); // FIXME: Leaking incoming expressions! - return true; + return ExprError(); case OR_Deleted: Diag(UnresExpr->getMemberLoc(), diag::err_ovl_deleted_member_call) @@ -5205,16 +5212,29 @@ Sema::BuildCallToMemberFunction(Scope *S, Expr *MemExprE, << DeclName << MemExprE->getSourceRange(); PrintOverloadCandidates(CandidateSet, /*OnlyViable=*/false); // FIXME: Leaking incoming expressions! - return true; + return ExprError(); } MemExprE = FixOverloadedFunctionReference(MemExprE, Method); + + // Clean up the 'this' expression we created above; FixOFR doesn't + // actually use it. + if (UnresExpr->isImplicitAccess()) + Owned(ObjectArg); + + // If overload resolution picked a static member, build a + // non-member call based on that function. + if (Method->isStatic()) { + return BuildResolvedCallExpr(MemExprE, Method, LParenLoc, + Args, NumArgs, RParenLoc); + } + MemExpr = cast(MemExprE->IgnoreParens()); } assert(Method && "Member call to something that isn't a method?"); ExprOwningPtr - TheCall(this, new (Context) CXXMemberCallExpr(Context, MemExpr, Args, + TheCall(this, new (Context) CXXMemberCallExpr(Context, MemExprE, Args, NumArgs, Method->getResultType().getNonReferenceType(), RParenLoc)); @@ -5222,24 +5242,25 @@ Sema::BuildCallToMemberFunction(Scope *S, Expr *MemExprE, // Check for a valid return type. if (CheckCallReturnType(Method->getResultType(), MemExpr->getMemberLoc(), TheCall.get(), Method)) - return true; + return ExprError(); // Convert the object argument (for a non-static member function call). + Expr *ObjectArg = MemExpr->getBase(); if (!Method->isStatic() && PerformObjectArgumentInitialization(ObjectArg, Method)) - return true; + return ExprError(); MemExpr->setBase(ObjectArg); // Convert the rest of the arguments const FunctionProtoType *Proto = cast(Method->getType()); if (ConvertArgumentsForCall(&*TheCall, MemExpr, Method, Proto, Args, NumArgs, RParenLoc)) - return true; + return ExprError(); if (CheckFunctionCall(Method, TheCall.get())) - return true; + return ExprError(); - return MaybeBindToTemporary(TheCall.release()).release(); + return MaybeBindToTemporary(TheCall.release()); } /// BuildCallToObjectOfClassType - Build a call to an object of class @@ -5632,19 +5653,11 @@ Expr *Sema::FixOverloadedFunctionReference(Expr *E, FunctionDecl *Fn) { } if (UnresolvedLookupExpr *ULE = dyn_cast(E)) { + // FIXME: avoid copy. + TemplateArgumentListInfo TemplateArgsBuffer, *TemplateArgs = 0; if (ULE->hasExplicitTemplateArgs()) { - // FIXME: avoid copy. - TemplateArgumentListInfo TemplateArgs; - if (ULE->hasExplicitTemplateArgs()) - ULE->copyTemplateArgumentsInto(TemplateArgs); - - return DeclRefExpr::Create(Context, - ULE->getQualifier(), - ULE->getQualifierRange(), - Fn, - ULE->getNameLoc(), - Fn->getType(), - &TemplateArgs); + ULE->copyTemplateArgumentsInto(TemplateArgsBuffer); + TemplateArgs = &TemplateArgsBuffer; } return DeclRefExpr::Create(Context, @@ -5652,23 +5665,43 @@ Expr *Sema::FixOverloadedFunctionReference(Expr *E, FunctionDecl *Fn) { ULE->getQualifierRange(), Fn, ULE->getNameLoc(), - Fn->getType()); + Fn->getType(), + TemplateArgs); } if (UnresolvedMemberExpr *MemExpr = dyn_cast(E)) { // FIXME: avoid copy. - TemplateArgumentListInfo TemplateArgs; - if (MemExpr->hasExplicitTemplateArgs()) - MemExpr->copyTemplateArgumentsInto(TemplateArgs); + TemplateArgumentListInfo TemplateArgsBuffer, *TemplateArgs = 0; + if (MemExpr->hasExplicitTemplateArgs()) { + MemExpr->copyTemplateArgumentsInto(TemplateArgsBuffer); + TemplateArgs = &TemplateArgsBuffer; + } + + Expr *Base; + + // If we're filling in + if (MemExpr->isImplicitAccess()) { + if (cast(Fn)->isStatic()) { + return DeclRefExpr::Create(Context, + MemExpr->getQualifier(), + MemExpr->getQualifierRange(), + Fn, + MemExpr->getMemberLoc(), + Fn->getType(), + TemplateArgs); + } else + Base = new (Context) CXXThisExpr(SourceLocation(), + MemExpr->getBaseType()); + } else + Base = MemExpr->getBase()->Retain(); - return MemberExpr::Create(Context, MemExpr->getBase()->Retain(), + return MemberExpr::Create(Context, Base, MemExpr->isArrow(), MemExpr->getQualifier(), MemExpr->getQualifierRange(), Fn, MemExpr->getMemberLoc(), - (MemExpr->hasExplicitTemplateArgs() - ? &TemplateArgs : 0), + TemplateArgs, Fn->getType()); } diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index f47577e909..5f5d891a22 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -361,13 +361,13 @@ Sema::ActOnDependentIdExpression(const CXXScopeSpec &SS, QualType ThisType; if (CheckForImplicitMember && IsImplicitDependentMemberReference(*this, Qualifier, ThisType)) { - Expr *This = new (Context) CXXThisExpr(SourceLocation(), ThisType); - // Since the 'this' expression is synthesized, we don't need to // perform the double-lookup check. NamedDecl *FirstQualifierInScope = 0; - return Owned(CXXDependentScopeMemberExpr::Create(Context, This, true, + return Owned(CXXDependentScopeMemberExpr::Create(Context, + /*This*/ 0, ThisType, + /*IsArrow*/ true, /*Op*/ SourceLocation(), Qualifier, SS.getRange(), FirstQualifierInScope, diff --git a/lib/Sema/TreeTransform.h b/lib/Sema/TreeTransform.h index 28b2174035..145e3c70a6 100644 --- a/lib/Sema/TreeTransform.h +++ b/lib/Sema/TreeTransform.h @@ -863,11 +863,14 @@ public: SS.setScopeRep(Qualifier); } + QualType BaseType = ((Expr*) Base.get())->getType(); + DeclarationName Name = SemaRef.Context.DeclarationNames.getCXXDestructorName( SemaRef.Context.getCanonicalType(DestroyedType)); - return getSema().BuildMemberReferenceExpr(move(Base), OperatorLoc, isArrow, + return getSema().BuildMemberReferenceExpr(move(Base), BaseType, + OperatorLoc, isArrow, SS, /*FIXME: FirstQualifier*/ 0, Name, DestroyedTypeLoc, /*TemplateArgs*/ 0); @@ -964,7 +967,11 @@ public: SS.setScopeRep(Qualifier); } - return getSema().BuildMemberReferenceExpr(move(Base), OpLoc, isArrow, + QualType BaseType = ((Expr*) Base.get())->getType(); + + // FIXME: wait, this is re-performing lookup? + return getSema().BuildMemberReferenceExpr(move(Base), BaseType, + OpLoc, isArrow, SS, FirstQualifierInScope, Member->getDeclName(), MemberLoc, ExplicitTemplateArgs); @@ -1042,8 +1049,10 @@ public: SourceLocation OpLoc, SourceLocation AccessorLoc, IdentifierInfo &Accessor) { + CXXScopeSpec SS; - return getSema().BuildMemberReferenceExpr(move(Base), + QualType BaseType = ((Expr*) Base.get())->getType(); + return getSema().BuildMemberReferenceExpr(move(Base), BaseType, OpLoc, /*IsArrow*/ false, SS, /*FirstQualifierInScope*/ 0, DeclarationName(&Accessor), @@ -1522,6 +1531,7 @@ public: /// By default, performs semantic analysis to build the new expression. /// Subclasses may override this routine to provide different behavior. OwningExprResult RebuildCXXDependentScopeMemberExpr(ExprArg BaseE, + QualType BaseType, bool IsArrow, SourceLocation OperatorLoc, NestedNameSpecifier *Qualifier, @@ -1534,7 +1544,8 @@ public: SS.setRange(QualifierRange); SS.setScopeRep(Qualifier); - return SemaRef.BuildMemberReferenceExpr(move(BaseE), OperatorLoc, IsArrow, + return SemaRef.BuildMemberReferenceExpr(move(BaseE), BaseType, + OperatorLoc, IsArrow, SS, FirstQualifierInScope, Name, MemberLoc, TemplateArgs); } @@ -1544,19 +1555,19 @@ public: /// By default, performs semantic analysis to build the new expression. /// Subclasses may override this routine to provide different behavior. OwningExprResult RebuildUnresolvedMemberExpr(ExprArg BaseE, + QualType BaseType, SourceLocation OperatorLoc, bool IsArrow, NestedNameSpecifier *Qualifier, SourceRange QualifierRange, LookupResult &R, const TemplateArgumentListInfo *TemplateArgs) { - OwningExprResult Base = move(BaseE); - CXXScopeSpec SS; SS.setRange(QualifierRange); SS.setScopeRep(Qualifier); - return SemaRef.BuildMemberReferenceExpr(move(Base), OperatorLoc, IsArrow, + return SemaRef.BuildMemberReferenceExpr(move(BaseE), BaseType, + OperatorLoc, IsArrow, SS, R, TemplateArgs); } @@ -4819,18 +4830,32 @@ TreeTransform::TransformCXXDependentScopeMemberExpr( CXXDependentScopeMemberExpr *E, bool isAddressOfOperand) { // Transform the base of the expression. - OwningExprResult Base = getDerived().TransformExpr(E->getBase()); - if (Base.isInvalid()) - return SemaRef.ExprError(); + OwningExprResult Base(SemaRef, (Expr*) 0); + Expr *OldBase; + QualType BaseType; + QualType ObjectType; + if (!E->isImplicitAccess()) { + OldBase = E->getBase(); + Base = getDerived().TransformExpr(OldBase); + if (Base.isInvalid()) + return SemaRef.ExprError(); - // Start the member reference and compute the object's type. - Sema::TypeTy *ObjectType = 0; - Base = SemaRef.ActOnStartCXXMemberReference(0, move(Base), - E->getOperatorLoc(), + // Start the member reference and compute the object's type. + Sema::TypeTy *ObjectTy = 0; + Base = SemaRef.ActOnStartCXXMemberReference(0, move(Base), + E->getOperatorLoc(), E->isArrow()? tok::arrow : tok::period, - ObjectType); - if (Base.isInvalid()) - return SemaRef.ExprError(); + ObjectTy); + if (Base.isInvalid()) + return SemaRef.ExprError(); + + ObjectType = QualType::getFromOpaquePtr(ObjectTy); + BaseType = ((Expr*) Base.get())->getType(); + } else { + OldBase = 0; + BaseType = getDerived().TransformType(E->getBaseType()); + ObjectType = BaseType->getAs()->getPointeeType(); + } // Transform the first part of the nested-name-specifier that qualifies // the member name. @@ -4843,29 +4868,31 @@ TreeTransform::TransformCXXDependentScopeMemberExpr( if (E->getQualifier()) { Qualifier = getDerived().TransformNestedNameSpecifier(E->getQualifier(), E->getQualifierRange(), - QualType::getFromOpaquePtr(ObjectType), - FirstQualifierInScope); + ObjectType, + FirstQualifierInScope); if (!Qualifier) return SemaRef.ExprError(); } DeclarationName Name = getDerived().TransformDeclarationName(E->getMember(), E->getMemberLoc(), - QualType::getFromOpaquePtr(ObjectType)); + ObjectType); if (!Name) return SemaRef.ExprError(); - if (!E->hasExplicitTemplateArgumentList()) { + if (!E->hasExplicitTemplateArgs()) { // This is a reference to a member without an explicitly-specified // template argument list. Optimize for this common case. if (!getDerived().AlwaysRebuild() && - Base.get() == E->getBase() && + Base.get() == OldBase && + BaseType == E->getBaseType() && Qualifier == E->getQualifier() && Name == E->getMember() && FirstQualifierInScope == E->getFirstQualifierFoundInScope()) return SemaRef.Owned(E->Retain()); return getDerived().RebuildCXXDependentScopeMemberExpr(move(Base), + BaseType, E->isArrow(), E->getOperatorLoc(), Qualifier, @@ -4885,6 +4912,7 @@ TreeTransform::TransformCXXDependentScopeMemberExpr( } return getDerived().RebuildCXXDependentScopeMemberExpr(move(Base), + BaseType, E->isArrow(), E->getOperatorLoc(), Qualifier, @@ -4900,9 +4928,16 @@ Sema::OwningExprResult TreeTransform::TransformUnresolvedMemberExpr(UnresolvedMemberExpr *Old, bool isAddressOfOperand) { // Transform the base of the expression. - OwningExprResult Base = getDerived().TransformExpr(Old->getBase()); - if (Base.isInvalid()) - return SemaRef.ExprError(); + OwningExprResult Base(SemaRef, (Expr*) 0); + QualType BaseType; + if (!Old->isImplicitAccess()) { + Base = getDerived().TransformExpr(Old->getBase()); + if (Base.isInvalid()) + return SemaRef.ExprError(); + BaseType = ((Expr*) Base.get())->getType(); + } else { + BaseType = getDerived().TransformType(Old->getBaseType()); + } NestedNameSpecifier *Qualifier = 0; if (Old->getQualifier()) { @@ -4951,6 +4986,7 @@ TreeTransform::TransformUnresolvedMemberExpr(UnresolvedMemberExpr *Old, } return getDerived().RebuildUnresolvedMemberExpr(move(Base), + BaseType, Old->getOperatorLoc(), Old->isArrow(), Qualifier, diff --git a/test/SemaTemplate/instantiate-method.cpp b/test/SemaTemplate/instantiate-method.cpp index 2351d882f9..231e2812f6 100644 --- a/test/SemaTemplate/instantiate-method.cpp +++ b/test/SemaTemplate/instantiate-method.cpp @@ -95,9 +95,7 @@ struct X0 : X0Base { template struct X1 : X0 { int &f2() { - // FIXME: We should be able to do this lookup and diagnose the error - // *despite* the fact that we can't decide the relationship yet. - return X0Base::f(); // expected-FIXME-error{{call to non-static member function without an object argument}} + return X0Base::f(); } }; diff --git a/test/SemaTemplate/qualified-id.cpp b/test/SemaTemplate/qualified-id.cpp index a07f05ca78..ab57950acf 100644 --- a/test/SemaTemplate/qualified-id.cpp +++ b/test/SemaTemplate/qualified-id.cpp @@ -18,3 +18,14 @@ namespace test1 { } }; } + +namespace test2 { + class Impl { + int foo(); + }; + template class Magic : public Impl { + int foo() { + return Impl::foo(); + } + }; +}