[cfe-commits] r78278 - in /cfe/trunk: include/clang/Parse/Action.h lib/Frontend/PrintParserCallbacks.cpp lib/Parse/ParseExpr.cpp lib/Sema/Sema.h lib/Sema/SemaCXXScopeSpec.cpp lib/Sema/SemaExpr.cpp lib/Sema/SemaOverload.cpp lib/Sema/SemaTemplateInstantiateExpr.cpp test/SemaCXX/qual-id-test.cpp
Douglas Gregor
dgregor at apple.com
Wed Aug 5 20:17:16 PDT 2009
Author: dgregor
Date: Wed Aug 5 22:17:00 2009
New Revision: 78278
URL: http://llvm.org/viewvc/llvm-project?rev=78278&view=rev
Log:
Support nested-name-specifiers for C++ member access expressions, e.g.,
this->Base::foo
from James Porter!
Added:
cfe/trunk/test/SemaCXX/qual-id-test.cpp (with props)
Modified:
cfe/trunk/include/clang/Parse/Action.h
cfe/trunk/lib/Frontend/PrintParserCallbacks.cpp
cfe/trunk/lib/Parse/ParseExpr.cpp
cfe/trunk/lib/Sema/Sema.h
cfe/trunk/lib/Sema/SemaCXXScopeSpec.cpp
cfe/trunk/lib/Sema/SemaExpr.cpp
cfe/trunk/lib/Sema/SemaOverload.cpp
cfe/trunk/lib/Sema/SemaTemplateInstantiateExpr.cpp
Modified: cfe/trunk/include/clang/Parse/Action.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Parse/Action.h?rev=78278&r1=78277&r2=78278&view=diff
==============================================================================
--- cfe/trunk/include/clang/Parse/Action.h (original)
+++ cfe/trunk/include/clang/Parse/Action.h Wed Aug 5 22:17:00 2009
@@ -236,6 +236,27 @@
return 0;
}
+ /// ActOnCXXEnterMemberScope - Called when a C++ class member accessor ('.'
+ /// or '->') is parsed. After this method is called, according to
+ /// [C++ 3.4.5p4], qualified-ids should be looked up in the contexts of both
+ /// the entire postfix-expression and the scope of the class of the object
+ /// expression.
+ /// 'SS' should be an empty CXXScopeSpec to be filled with the class's scope.
+ virtual OwningExprResult ActOnCXXEnterMemberScope(Scope *S,
+ CXXScopeSpec &SS,
+ ExprArg Base,
+ tok::TokenKind OpKind) {
+ return ExprEmpty();
+ }
+
+ /// ActOnCXXExitMemberScope - Called when a postfix-expression that previously
+ /// invoked ActOnCXXEnterMemberScope() is finished. 'SS' is the same
+ /// CXXScopeSpec that was passed to ActOnCXXEnterMemberScope. Used to
+ /// indicate that names should revert to being looked up in the defining
+ /// scope.
+ virtual void ActOnCXXExitMemberScope(Scope *S, const CXXScopeSpec &SS) {
+ }
+
/// ActOnCXXEnterDeclaratorScope - Called when a C++ scope specifier (global
/// scope or nested-name-specifier) is parsed, part of a declarator-id.
/// After this method is called, according to [C++ 3.4.3p3], names should be
@@ -820,7 +841,8 @@
tok::TokenKind OpKind,
SourceLocation MemberLoc,
IdentifierInfo &Member,
- DeclPtrTy ObjCImpDecl) {
+ DeclPtrTy ObjCImpDecl,
+ const CXXScopeSpec *SS = 0) {
return ExprEmpty();
}
Modified: cfe/trunk/lib/Frontend/PrintParserCallbacks.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Frontend/PrintParserCallbacks.cpp?rev=78278&r1=78277&r2=78278&view=diff
==============================================================================
--- cfe/trunk/lib/Frontend/PrintParserCallbacks.cpp (original)
+++ cfe/trunk/lib/Frontend/PrintParserCallbacks.cpp Wed Aug 5 22:17:00 2009
@@ -532,7 +532,8 @@
tok::TokenKind OpKind,
SourceLocation MemberLoc,
IdentifierInfo &Member,
- DeclPtrTy ImplDecl) {
+ DeclPtrTy ImplDecl,
+ const CXXScopeSpec *SS=0) {
Out << __FUNCTION__ << "\n";
return ExprEmpty();
}
Modified: cfe/trunk/lib/Parse/ParseExpr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseExpr.cpp?rev=78278&r1=78277&r2=78278&view=diff
==============================================================================
--- cfe/trunk/lib/Parse/ParseExpr.cpp (original)
+++ cfe/trunk/lib/Parse/ParseExpr.cpp Wed Aug 5 22:17:00 2009
@@ -919,6 +919,16 @@
tok::TokenKind OpKind = Tok.getKind();
SourceLocation OpLoc = ConsumeToken(); // Eat the "." or "->" token.
+ CXXScopeSpec MemberSS;
+ CXXScopeSpec SS;
+ if (getLang().CPlusPlus && !LHS.isInvalid()) {
+ LHS = Actions.ActOnCXXEnterMemberScope(CurScope, MemberSS, move(LHS),
+ OpKind);
+ if (LHS.isInvalid())
+ break;
+ ParseOptionalCXXScopeSpecifier(SS);
+ }
+
if (Tok.isNot(tok::identifier)) {
Diag(Tok, diag::err_expected_ident);
return ExprError();
@@ -928,8 +938,12 @@
LHS = Actions.ActOnMemberReferenceExpr(CurScope, move(LHS), OpLoc,
OpKind, Tok.getLocation(),
*Tok.getIdentifierInfo(),
- ObjCImpDecl);
+ ObjCImpDecl, &SS);
}
+
+ if (getLang().CPlusPlus)
+ Actions.ActOnCXXExitMemberScope(CurScope, MemberSS);
+
ConsumeToken();
break;
}
Modified: cfe/trunk/lib/Sema/Sema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/Sema.h?rev=78278&r1=78277&r2=78278&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/Sema.h (original)
+++ cfe/trunk/lib/Sema/Sema.h Wed Aug 5 22:17:00 2009
@@ -833,9 +833,8 @@
SourceLocation *CommaLocs,
SourceLocation RParenLoc);
- ExprResult BuildOverloadedArrowExpr(Scope *S, Expr *Base, SourceLocation OpLoc,
- SourceLocation MemberLoc,
- IdentifierInfo &Member);
+ OwningExprResult BuildOverloadedArrowExpr(Scope *S, ExprArg Base,
+ SourceLocation OpLoc);
/// Helpers for dealing with blocks and functions.
void CheckFallThroughForFunctionDef(Decl *D, Stmt *Body);
@@ -1519,7 +1518,8 @@
tok::TokenKind OpKind,
SourceLocation MemberLoc,
IdentifierInfo &Member,
- DeclPtrTy ImplDecl);
+ DeclPtrTy ImplDecl,
+ const CXXScopeSpec *SS = 0);
virtual void ActOnDefaultCtorInitializers(DeclPtrTy CDtorDecl);
bool ConvertArgumentsForCall(CallExpr *Call, Expr *Fn,
FunctionDecl *FDecl,
@@ -1882,6 +1882,23 @@
SourceRange TypeRange,
SourceLocation CCLoc);
+ /// ActOnCXXEnterMemberScope - Called when a C++ class member accessor ('.'
+ /// or '->') is parsed. After this method is called, according to
+ /// [C++ 3.4.5p4], qualified-ids should be looked up in the contexts of both
+ /// the entire postfix-expression and the scope of the class of the object
+ /// expression.
+ /// 'SS' should be an empty CXXScopeSpec to be filled with the class's scope.
+ virtual OwningExprResult ActOnCXXEnterMemberScope(Scope *S, CXXScopeSpec &SS,
+ ExprArg Base,
+ tok::TokenKind OpKind);
+
+ /// ActOnCXXExitMemberScope - Called when a postfix-expression that previously
+ /// invoked ActOnCXXEnterMemberScope() is finished. 'SS' is the same
+ /// CXXScopeSpec that was passed to ActOnCXXEnterMemberScope. Used to
+ /// indicate that names should revert to being looked up in the defining
+ /// scope.
+ virtual void ActOnCXXExitMemberScope(Scope *S, const CXXScopeSpec &SS);
+
/// ActOnCXXEnterDeclaratorScope - Called when a C++ scope specifier (global
/// scope or nested-name-specifier) is parsed, part of a declarator-id.
/// After this method is called, according to [C++ 3.4.3p3], names should be
Modified: cfe/trunk/lib/Sema/SemaCXXScopeSpec.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaCXXScopeSpec.cpp?rev=78278&r1=78277&r2=78278&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaCXXScopeSpec.cpp (original)
+++ cfe/trunk/lib/Sema/SemaCXXScopeSpec.cpp Wed Aug 5 22:17:00 2009
@@ -14,6 +14,7 @@
#include "Sema.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/DeclTemplate.h"
+#include "clang/AST/ExprCXX.h"
#include "clang/AST/NestedNameSpecifier.h"
#include "clang/Parse/DeclSpec.h"
#include "llvm/ADT/STLExtras.h"
@@ -337,6 +338,55 @@
T.getTypePtr());
}
+Action::OwningExprResult
+Sema::ActOnCXXEnterMemberScope(Scope *S, CXXScopeSpec &SS, ExprArg Base,
+ tok::TokenKind OpKind) {
+ Expr *BaseExpr = (Expr*)Base.get();
+ assert(BaseExpr && "no record expansion");
+
+ QualType BaseType = BaseExpr->getType();
+ // FIXME: handle dependent types
+ if (BaseType->isDependentType())
+ return move(Base);
+
+ // C++ [over.match.oper]p8:
+ // [...] When operator->returns, the operator-> is applied to the value
+ // returned, with the original second operand.
+ if (OpKind == tok::arrow) {
+ while (BaseType->isRecordType()) {
+ Base = BuildOverloadedArrowExpr(S, move(Base), BaseExpr->getExprLoc());
+ BaseExpr = (Expr*)Base.get();
+ if (BaseExpr == NULL)
+ return ExprError();
+ BaseType = BaseExpr->getType();
+ }
+ }
+
+ if (BaseType->isPointerType())
+ BaseType = BaseType->getPointeeType();
+
+ // 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())
+ return move(Base);
+
+ SS.setRange(BaseExpr->getSourceRange());
+ SS.setScopeRep(
+ NestedNameSpecifier::Create(Context, 0, false, BaseType.getTypePtr())
+ );
+
+ if (S)
+ ActOnCXXEnterDeclaratorScope(S,SS);
+ return move(Base);
+}
+
+void Sema::ActOnCXXExitMemberScope(Scope *S, const CXXScopeSpec &SS) {
+ if (S && SS.isSet())
+ ActOnCXXExitDeclaratorScope(S,SS);
+}
+
+
/// ActOnCXXEnterDeclaratorScope - Called when a C++ scope specifier (global
/// scope or nested-name-specifier) is parsed, part of a declarator-id.
/// After this method is called, according to [C++ 3.4.3p3], names should be
Modified: cfe/trunk/lib/Sema/SemaExpr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExpr.cpp?rev=78278&r1=78277&r2=78278&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaExpr.cpp (original)
+++ cfe/trunk/lib/Sema/SemaExpr.cpp Wed Aug 5 22:17:00 2009
@@ -2103,7 +2103,11 @@
Sema::ActOnMemberReferenceExpr(Scope *S, ExprArg Base, SourceLocation OpLoc,
tok::TokenKind OpKind, SourceLocation MemberLoc,
IdentifierInfo &Member,
- DeclPtrTy ObjCImpDecl) {
+ DeclPtrTy ObjCImpDecl, const CXXScopeSpec *SS) {
+ // FIXME: handle the CXXScopeSpec for proper lookup of qualified-ids
+ if (SS && SS->isInvalid())
+ return ExprError();
+
Expr *BaseExpr = Base.takeAs<Expr>();
assert(BaseExpr && "no record expression");
@@ -2126,9 +2130,6 @@
BaseType = PT->getPointeeType();
else if (BaseType->isObjCObjectPointerType())
;
- else if (getLangOptions().CPlusPlus && BaseType->isRecordType())
- return Owned(BuildOverloadedArrowExpr(S, BaseExpr, OpLoc,
- MemberLoc, Member));
else
return ExprError(Diag(MemberLoc,
diag::err_typecheck_member_reference_arrow)
@@ -2164,12 +2165,38 @@
BaseExpr->getSourceRange()))
return ExprError();
+ DeclContext *DC = RDecl;
+ if (SS && SS->isSet()) {
+ // If the member name was a qualified-id, look into the
+ // nested-name-specifier.
+ DC = computeDeclContext(*SS, false);
+
+ // FIXME: If DC is not computable, we should build a
+ // CXXUnresolvedMemberExpr.
+ assert(DC && "Cannot handle non-computable dependent contexts in lookup");
+ }
+
// The record definition is complete, now make sure the member is valid.
- // FIXME: Qualified name lookup for C++ is a bit more complicated than this.
LookupResult Result
- = LookupQualifiedName(RDecl, DeclarationName(&Member),
+ = LookupQualifiedName(DC, DeclarationName(&Member),
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)
<< &Member << BaseExpr->getSourceRange());
Modified: cfe/trunk/lib/Sema/SemaOverload.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaOverload.cpp?rev=78278&r1=78277&r2=78278&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaOverload.cpp (original)
+++ cfe/trunk/lib/Sema/SemaOverload.cpp Wed Aug 5 22:17:00 2009
@@ -4553,10 +4553,9 @@
/// BuildOverloadedArrowExpr - Build a call to an overloaded @c operator->
/// (if one exists), where @c Base is an expression of class type and
/// @c Member is the name of the member we're trying to find.
-Action::ExprResult
-Sema::BuildOverloadedArrowExpr(Scope *S, Expr *Base, SourceLocation OpLoc,
- SourceLocation MemberLoc,
- IdentifierInfo &Member) {
+Sema::OwningExprResult
+Sema::BuildOverloadedArrowExpr(Scope *S, ExprArg BaseIn, SourceLocation OpLoc) {
+ Expr *Base = static_cast<Expr *>(BaseIn.get());
assert(Base->getType()->isRecordType() && "left-hand side must have class type");
// C++ [over.ref]p1:
@@ -4569,15 +4568,13 @@
DeclarationName OpName = Context.DeclarationNames.getCXXOperatorName(OO_Arrow);
OverloadCandidateSet CandidateSet;
const RecordType *BaseRecord = Base->getType()->getAs<RecordType>();
-
+
DeclContext::lookup_const_iterator Oper, OperEnd;
for (llvm::tie(Oper, OperEnd)
= BaseRecord->getDecl()->lookup(OpName); Oper != OperEnd; ++Oper)
AddMethodCandidate(cast<CXXMethodDecl>(*Oper), Base, 0, 0, CandidateSet,
/*SuppressUserConversions=*/false);
- ExprOwningPtr<Expr> BasePtr(this, Base);
-
// Perform overload resolution.
OverloadCandidateSet::iterator Best;
switch (BestViableFunction(CandidateSet, OpLoc, Best)) {
@@ -4588,34 +4585,34 @@
case OR_No_Viable_Function:
if (CandidateSet.empty())
Diag(OpLoc, diag::err_typecheck_member_reference_arrow)
- << BasePtr->getType() << BasePtr->getSourceRange();
+ << Base->getType() << Base->getSourceRange();
else
Diag(OpLoc, diag::err_ovl_no_viable_oper)
- << "operator->" << BasePtr->getSourceRange();
+ << "operator->" << Base->getSourceRange();
PrintOverloadCandidates(CandidateSet, /*OnlyViable=*/false);
- return true;
+ return ExprError();
case OR_Ambiguous:
Diag(OpLoc, diag::err_ovl_ambiguous_oper)
- << "operator->" << BasePtr->getSourceRange();
+ << "operator->" << Base->getSourceRange();
PrintOverloadCandidates(CandidateSet, /*OnlyViable=*/true);
- return true;
+ return ExprError();
case OR_Deleted:
Diag(OpLoc, diag::err_ovl_deleted_oper)
<< Best->Function->isDeleted()
- << "operator->" << BasePtr->getSourceRange();
+ << "operator->" << Base->getSourceRange();
PrintOverloadCandidates(CandidateSet, /*OnlyViable=*/true);
- return true;
+ return ExprError();
}
// Convert the object parameter.
CXXMethodDecl *Method = cast<CXXMethodDecl>(Best->Function);
if (PerformObjectArgumentInitialization(Base, Method))
- return true;
+ return ExprError();
// No concerns about early exits now.
- BasePtr.take();
+ BaseIn.release();
// Build the operator call.
Expr *FnExpr = new (Context) DeclRefExpr(Method, Method->getType(),
@@ -4624,8 +4621,7 @@
Base = new (Context) CXXOperatorCallExpr(Context, OO_Arrow, FnExpr, &Base, 1,
Method->getResultType().getNonReferenceType(),
OpLoc);
- return ActOnMemberReferenceExpr(S, ExprArg(*this, Base), OpLoc, tok::arrow,
- MemberLoc, Member, DeclPtrTy()).release();
+ return Owned(Base);
}
/// FixOverloadedFunctionReference - E is an expression that refers to
Modified: cfe/trunk/lib/Sema/SemaTemplateInstantiateExpr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaTemplateInstantiateExpr.cpp?rev=78278&r1=78277&r2=78278&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaTemplateInstantiateExpr.cpp (original)
+++ cfe/trunk/lib/Sema/SemaTemplateInstantiateExpr.cpp Wed Aug 5 22:17:00 2009
@@ -1270,14 +1270,18 @@
if (Base.isInvalid())
return SemaRef.ExprError();
+ tok::TokenKind OpKind = E->isArrow() ? tok::arrow : tok::period;
+ CXXScopeSpec SS;
+ Base = SemaRef.ActOnCXXEnterMemberScope(0, SS, move(Base), OpKind);
// FIXME: Instantiate the declaration name.
- return SemaRef.ActOnMemberReferenceExpr(/*Scope=*/0,
+ Base = SemaRef.ActOnMemberReferenceExpr(/*Scope=*/0,
move(Base), E->getOperatorLoc(),
- E->isArrow()? tok::arrow
- : tok::period,
+ OpKind,
E->getMemberLoc(),
/*FIXME:*/*E->getMember().getAsIdentifierInfo(),
- /*FIXME?*/Sema::DeclPtrTy::make((Decl*)0));
+ /*FIXME?*/Sema::DeclPtrTy::make((Decl*)0));
+ SemaRef.ActOnCXXExitMemberScope(0, SS);
+ return move(Base);
}
//----------------------------------------------------------------------------
Added: cfe/trunk/test/SemaCXX/qual-id-test.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/qual-id-test.cpp?rev=78278&view=auto
==============================================================================
--- cfe/trunk/test/SemaCXX/qual-id-test.cpp (added)
+++ cfe/trunk/test/SemaCXX/qual-id-test.cpp Wed Aug 5 22:17:00 2009
@@ -0,0 +1,106 @@
+// RUN: clang-cc -fsyntax-only -verify %s
+namespace A
+{
+ namespace B
+ {
+ struct base
+ {
+ void x() {}
+ void y() {}
+ };
+ }
+
+ struct member
+ {
+ void foo();
+ };
+
+ struct middleman
+ {
+ member * operator->() { return 0; }
+ };
+
+ struct sub : B::base
+ {
+ void x() {}
+ middleman operator->() { return middleman(); }
+ };
+}
+
+struct bad
+{
+ int x();
+};
+
+namespace C
+{
+ void fun()
+ {
+ A::sub a;
+
+ a.x();
+
+ a.sub::x();
+ a.base::x();
+
+ a.B::base::x(); // expected-error{{use of undeclared identifier 'B'}}
+
+ a.A::sub::x();
+ a.A::B::base::x();
+
+ a.bad::x(); // expected-error{{type 'struct bad' is not a direct or virtual base of ''struct A::sub''}}
+
+ a->foo();
+ a->member::foo();
+ a->A::member::foo();
+ }
+
+ void fun2()
+ {
+ A::sub *a;
+
+ a->x();
+
+ a->sub::x();
+ a->base::x();
+
+ a->B::base::x(); // expected-error{{use of undeclared identifier 'B'}}
+
+ a->A::sub::x();
+ a->A::B::base::x();
+
+ a->bad::x(); // expected-error{{type 'struct bad' is not a direct or virtual base of ''struct A::sub''}}
+
+ (*a)->foo();
+ (*a)->member::foo();
+ (*a)->A::member::foo();
+ }
+
+ void fun3()
+ {
+ int i;
+ i.foo(); // expected-error{{member reference base type 'int' is not a structure or union}}
+ }
+
+ template<typename T>
+ void fun4()
+ {
+ T a;
+ a.x();
+ a->foo();
+
+ // 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(); // expected-error{{use of undeclared identifier 'sub'}}
+ a.base::x(); // expected-error{{use of undeclared identifier 'base'}}
+ a.B::base::x(); // expected-error{{use of undeclared identifier 'B'}}
+ a->member::foo(); // expected-error{{use of undeclared identifier 'member'}}
+ }
+}
Propchange: cfe/trunk/test/SemaCXX/qual-id-test.cpp
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: cfe/trunk/test/SemaCXX/qual-id-test.cpp
------------------------------------------------------------------------------
svn:keywords = Id
Propchange: cfe/trunk/test/SemaCXX/qual-id-test.cpp
------------------------------------------------------------------------------
svn:mime-type = text/plain
More information about the cfe-commits
mailing list