forked from OSchip/llvm-project
Support nested-name-specifiers for C++ member access expressions, e.g.,
this->Base::foo from James Porter! llvm-svn: 78278
This commit is contained in:
parent
3c7b95d9aa
commit
d806156d54
|
@ -236,6 +236,27 @@ public:
|
||||||
return 0;
|
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
|
/// ActOnCXXEnterDeclaratorScope - Called when a C++ scope specifier (global
|
||||||
/// scope or nested-name-specifier) is parsed, part of a declarator-id.
|
/// 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
|
/// After this method is called, according to [C++ 3.4.3p3], names should be
|
||||||
|
@ -820,7 +841,8 @@ public:
|
||||||
tok::TokenKind OpKind,
|
tok::TokenKind OpKind,
|
||||||
SourceLocation MemberLoc,
|
SourceLocation MemberLoc,
|
||||||
IdentifierInfo &Member,
|
IdentifierInfo &Member,
|
||||||
DeclPtrTy ObjCImpDecl) {
|
DeclPtrTy ObjCImpDecl,
|
||||||
|
const CXXScopeSpec *SS = 0) {
|
||||||
return ExprEmpty();
|
return ExprEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -532,7 +532,8 @@ namespace {
|
||||||
tok::TokenKind OpKind,
|
tok::TokenKind OpKind,
|
||||||
SourceLocation MemberLoc,
|
SourceLocation MemberLoc,
|
||||||
IdentifierInfo &Member,
|
IdentifierInfo &Member,
|
||||||
DeclPtrTy ImplDecl) {
|
DeclPtrTy ImplDecl,
|
||||||
|
const CXXScopeSpec *SS=0) {
|
||||||
Out << __FUNCTION__ << "\n";
|
Out << __FUNCTION__ << "\n";
|
||||||
return ExprEmpty();
|
return ExprEmpty();
|
||||||
}
|
}
|
||||||
|
|
|
@ -919,6 +919,16 @@ Parser::ParsePostfixExpressionSuffix(OwningExprResult LHS) {
|
||||||
tok::TokenKind OpKind = Tok.getKind();
|
tok::TokenKind OpKind = Tok.getKind();
|
||||||
SourceLocation OpLoc = ConsumeToken(); // Eat the "." or "->" token.
|
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)) {
|
if (Tok.isNot(tok::identifier)) {
|
||||||
Diag(Tok, diag::err_expected_ident);
|
Diag(Tok, diag::err_expected_ident);
|
||||||
return ExprError();
|
return ExprError();
|
||||||
|
@ -928,8 +938,12 @@ Parser::ParsePostfixExpressionSuffix(OwningExprResult LHS) {
|
||||||
LHS = Actions.ActOnMemberReferenceExpr(CurScope, move(LHS), OpLoc,
|
LHS = Actions.ActOnMemberReferenceExpr(CurScope, move(LHS), OpLoc,
|
||||||
OpKind, Tok.getLocation(),
|
OpKind, Tok.getLocation(),
|
||||||
*Tok.getIdentifierInfo(),
|
*Tok.getIdentifierInfo(),
|
||||||
ObjCImpDecl);
|
ObjCImpDecl, &SS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (getLang().CPlusPlus)
|
||||||
|
Actions.ActOnCXXExitMemberScope(CurScope, MemberSS);
|
||||||
|
|
||||||
ConsumeToken();
|
ConsumeToken();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -833,9 +833,8 @@ public:
|
||||||
SourceLocation *CommaLocs,
|
SourceLocation *CommaLocs,
|
||||||
SourceLocation RParenLoc);
|
SourceLocation RParenLoc);
|
||||||
|
|
||||||
ExprResult BuildOverloadedArrowExpr(Scope *S, Expr *Base, SourceLocation OpLoc,
|
OwningExprResult BuildOverloadedArrowExpr(Scope *S, ExprArg Base,
|
||||||
SourceLocation MemberLoc,
|
SourceLocation OpLoc);
|
||||||
IdentifierInfo &Member);
|
|
||||||
|
|
||||||
/// Helpers for dealing with blocks and functions.
|
/// Helpers for dealing with blocks and functions.
|
||||||
void CheckFallThroughForFunctionDef(Decl *D, Stmt *Body);
|
void CheckFallThroughForFunctionDef(Decl *D, Stmt *Body);
|
||||||
|
@ -1519,7 +1518,8 @@ public:
|
||||||
tok::TokenKind OpKind,
|
tok::TokenKind OpKind,
|
||||||
SourceLocation MemberLoc,
|
SourceLocation MemberLoc,
|
||||||
IdentifierInfo &Member,
|
IdentifierInfo &Member,
|
||||||
DeclPtrTy ImplDecl);
|
DeclPtrTy ImplDecl,
|
||||||
|
const CXXScopeSpec *SS = 0);
|
||||||
virtual void ActOnDefaultCtorInitializers(DeclPtrTy CDtorDecl);
|
virtual void ActOnDefaultCtorInitializers(DeclPtrTy CDtorDecl);
|
||||||
bool ConvertArgumentsForCall(CallExpr *Call, Expr *Fn,
|
bool ConvertArgumentsForCall(CallExpr *Call, Expr *Fn,
|
||||||
FunctionDecl *FDecl,
|
FunctionDecl *FDecl,
|
||||||
|
@ -1882,6 +1882,23 @@ public:
|
||||||
SourceRange TypeRange,
|
SourceRange TypeRange,
|
||||||
SourceLocation CCLoc);
|
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
|
/// ActOnCXXEnterDeclaratorScope - Called when a C++ scope specifier (global
|
||||||
/// scope or nested-name-specifier) is parsed, part of a declarator-id.
|
/// 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
|
/// After this method is called, according to [C++ 3.4.3p3], names should be
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#include "Sema.h"
|
#include "Sema.h"
|
||||||
#include "clang/AST/ASTContext.h"
|
#include "clang/AST/ASTContext.h"
|
||||||
#include "clang/AST/DeclTemplate.h"
|
#include "clang/AST/DeclTemplate.h"
|
||||||
|
#include "clang/AST/ExprCXX.h"
|
||||||
#include "clang/AST/NestedNameSpecifier.h"
|
#include "clang/AST/NestedNameSpecifier.h"
|
||||||
#include "clang/Parse/DeclSpec.h"
|
#include "clang/Parse/DeclSpec.h"
|
||||||
#include "llvm/ADT/STLExtras.h"
|
#include "llvm/ADT/STLExtras.h"
|
||||||
|
@ -337,6 +338,55 @@ Sema::CXXScopeTy *Sema::ActOnCXXNestedNameSpecifier(Scope *S,
|
||||||
T.getTypePtr());
|
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
|
/// ActOnCXXEnterDeclaratorScope - Called when a C++ scope specifier (global
|
||||||
/// scope or nested-name-specifier) is parsed, part of a declarator-id.
|
/// 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
|
/// After this method is called, according to [C++ 3.4.3p3], names should be
|
||||||
|
|
|
@ -2103,7 +2103,11 @@ Action::OwningExprResult
|
||||||
Sema::ActOnMemberReferenceExpr(Scope *S, ExprArg Base, SourceLocation OpLoc,
|
Sema::ActOnMemberReferenceExpr(Scope *S, ExprArg Base, SourceLocation OpLoc,
|
||||||
tok::TokenKind OpKind, SourceLocation MemberLoc,
|
tok::TokenKind OpKind, SourceLocation MemberLoc,
|
||||||
IdentifierInfo &Member,
|
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>();
|
Expr *BaseExpr = Base.takeAs<Expr>();
|
||||||
assert(BaseExpr && "no record expression");
|
assert(BaseExpr && "no record expression");
|
||||||
|
|
||||||
|
@ -2126,9 +2130,6 @@ Sema::ActOnMemberReferenceExpr(Scope *S, ExprArg Base, SourceLocation OpLoc,
|
||||||
BaseType = PT->getPointeeType();
|
BaseType = PT->getPointeeType();
|
||||||
else if (BaseType->isObjCObjectPointerType())
|
else if (BaseType->isObjCObjectPointerType())
|
||||||
;
|
;
|
||||||
else if (getLangOptions().CPlusPlus && BaseType->isRecordType())
|
|
||||||
return Owned(BuildOverloadedArrowExpr(S, BaseExpr, OpLoc,
|
|
||||||
MemberLoc, Member));
|
|
||||||
else
|
else
|
||||||
return ExprError(Diag(MemberLoc,
|
return ExprError(Diag(MemberLoc,
|
||||||
diag::err_typecheck_member_reference_arrow)
|
diag::err_typecheck_member_reference_arrow)
|
||||||
|
@ -2164,12 +2165,38 @@ Sema::ActOnMemberReferenceExpr(Scope *S, ExprArg Base, SourceLocation OpLoc,
|
||||||
BaseExpr->getSourceRange()))
|
BaseExpr->getSourceRange()))
|
||||||
return ExprError();
|
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.
|
// 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
|
LookupResult Result
|
||||||
= LookupQualifiedName(RDecl, DeclarationName(&Member),
|
= LookupQualifiedName(DC, DeclarationName(&Member),
|
||||||
LookupMemberName, false);
|
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)
|
if (!Result)
|
||||||
return ExprError(Diag(MemberLoc, diag::err_typecheck_no_member)
|
return ExprError(Diag(MemberLoc, diag::err_typecheck_no_member)
|
||||||
<< &Member << BaseExpr->getSourceRange());
|
<< &Member << BaseExpr->getSourceRange());
|
||||||
|
|
|
@ -4553,10 +4553,9 @@ Sema::BuildCallToObjectOfClassType(Scope *S, Expr *Object,
|
||||||
/// BuildOverloadedArrowExpr - Build a call to an overloaded @c operator->
|
/// BuildOverloadedArrowExpr - Build a call to an overloaded @c operator->
|
||||||
/// (if one exists), where @c Base is an expression of class type and
|
/// (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.
|
/// @c Member is the name of the member we're trying to find.
|
||||||
Action::ExprResult
|
Sema::OwningExprResult
|
||||||
Sema::BuildOverloadedArrowExpr(Scope *S, Expr *Base, SourceLocation OpLoc,
|
Sema::BuildOverloadedArrowExpr(Scope *S, ExprArg BaseIn, SourceLocation OpLoc) {
|
||||||
SourceLocation MemberLoc,
|
Expr *Base = static_cast<Expr *>(BaseIn.get());
|
||||||
IdentifierInfo &Member) {
|
|
||||||
assert(Base->getType()->isRecordType() && "left-hand side must have class type");
|
assert(Base->getType()->isRecordType() && "left-hand side must have class type");
|
||||||
|
|
||||||
// C++ [over.ref]p1:
|
// C++ [over.ref]p1:
|
||||||
|
@ -4569,15 +4568,13 @@ Sema::BuildOverloadedArrowExpr(Scope *S, Expr *Base, SourceLocation OpLoc,
|
||||||
DeclarationName OpName = Context.DeclarationNames.getCXXOperatorName(OO_Arrow);
|
DeclarationName OpName = Context.DeclarationNames.getCXXOperatorName(OO_Arrow);
|
||||||
OverloadCandidateSet CandidateSet;
|
OverloadCandidateSet CandidateSet;
|
||||||
const RecordType *BaseRecord = Base->getType()->getAs<RecordType>();
|
const RecordType *BaseRecord = Base->getType()->getAs<RecordType>();
|
||||||
|
|
||||||
DeclContext::lookup_const_iterator Oper, OperEnd;
|
DeclContext::lookup_const_iterator Oper, OperEnd;
|
||||||
for (llvm::tie(Oper, OperEnd)
|
for (llvm::tie(Oper, OperEnd)
|
||||||
= BaseRecord->getDecl()->lookup(OpName); Oper != OperEnd; ++Oper)
|
= BaseRecord->getDecl()->lookup(OpName); Oper != OperEnd; ++Oper)
|
||||||
AddMethodCandidate(cast<CXXMethodDecl>(*Oper), Base, 0, 0, CandidateSet,
|
AddMethodCandidate(cast<CXXMethodDecl>(*Oper), Base, 0, 0, CandidateSet,
|
||||||
/*SuppressUserConversions=*/false);
|
/*SuppressUserConversions=*/false);
|
||||||
|
|
||||||
ExprOwningPtr<Expr> BasePtr(this, Base);
|
|
||||||
|
|
||||||
// Perform overload resolution.
|
// Perform overload resolution.
|
||||||
OverloadCandidateSet::iterator Best;
|
OverloadCandidateSet::iterator Best;
|
||||||
switch (BestViableFunction(CandidateSet, OpLoc, Best)) {
|
switch (BestViableFunction(CandidateSet, OpLoc, Best)) {
|
||||||
|
@ -4588,34 +4585,34 @@ Sema::BuildOverloadedArrowExpr(Scope *S, Expr *Base, SourceLocation OpLoc,
|
||||||
case OR_No_Viable_Function:
|
case OR_No_Viable_Function:
|
||||||
if (CandidateSet.empty())
|
if (CandidateSet.empty())
|
||||||
Diag(OpLoc, diag::err_typecheck_member_reference_arrow)
|
Diag(OpLoc, diag::err_typecheck_member_reference_arrow)
|
||||||
<< BasePtr->getType() << BasePtr->getSourceRange();
|
<< Base->getType() << Base->getSourceRange();
|
||||||
else
|
else
|
||||||
Diag(OpLoc, diag::err_ovl_no_viable_oper)
|
Diag(OpLoc, diag::err_ovl_no_viable_oper)
|
||||||
<< "operator->" << BasePtr->getSourceRange();
|
<< "operator->" << Base->getSourceRange();
|
||||||
PrintOverloadCandidates(CandidateSet, /*OnlyViable=*/false);
|
PrintOverloadCandidates(CandidateSet, /*OnlyViable=*/false);
|
||||||
return true;
|
return ExprError();
|
||||||
|
|
||||||
case OR_Ambiguous:
|
case OR_Ambiguous:
|
||||||
Diag(OpLoc, diag::err_ovl_ambiguous_oper)
|
Diag(OpLoc, diag::err_ovl_ambiguous_oper)
|
||||||
<< "operator->" << BasePtr->getSourceRange();
|
<< "operator->" << Base->getSourceRange();
|
||||||
PrintOverloadCandidates(CandidateSet, /*OnlyViable=*/true);
|
PrintOverloadCandidates(CandidateSet, /*OnlyViable=*/true);
|
||||||
return true;
|
return ExprError();
|
||||||
|
|
||||||
case OR_Deleted:
|
case OR_Deleted:
|
||||||
Diag(OpLoc, diag::err_ovl_deleted_oper)
|
Diag(OpLoc, diag::err_ovl_deleted_oper)
|
||||||
<< Best->Function->isDeleted()
|
<< Best->Function->isDeleted()
|
||||||
<< "operator->" << BasePtr->getSourceRange();
|
<< "operator->" << Base->getSourceRange();
|
||||||
PrintOverloadCandidates(CandidateSet, /*OnlyViable=*/true);
|
PrintOverloadCandidates(CandidateSet, /*OnlyViable=*/true);
|
||||||
return true;
|
return ExprError();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert the object parameter.
|
// Convert the object parameter.
|
||||||
CXXMethodDecl *Method = cast<CXXMethodDecl>(Best->Function);
|
CXXMethodDecl *Method = cast<CXXMethodDecl>(Best->Function);
|
||||||
if (PerformObjectArgumentInitialization(Base, Method))
|
if (PerformObjectArgumentInitialization(Base, Method))
|
||||||
return true;
|
return ExprError();
|
||||||
|
|
||||||
// No concerns about early exits now.
|
// No concerns about early exits now.
|
||||||
BasePtr.take();
|
BaseIn.release();
|
||||||
|
|
||||||
// Build the operator call.
|
// Build the operator call.
|
||||||
Expr *FnExpr = new (Context) DeclRefExpr(Method, Method->getType(),
|
Expr *FnExpr = new (Context) DeclRefExpr(Method, Method->getType(),
|
||||||
|
@ -4624,8 +4621,7 @@ Sema::BuildOverloadedArrowExpr(Scope *S, Expr *Base, SourceLocation OpLoc,
|
||||||
Base = new (Context) CXXOperatorCallExpr(Context, OO_Arrow, FnExpr, &Base, 1,
|
Base = new (Context) CXXOperatorCallExpr(Context, OO_Arrow, FnExpr, &Base, 1,
|
||||||
Method->getResultType().getNonReferenceType(),
|
Method->getResultType().getNonReferenceType(),
|
||||||
OpLoc);
|
OpLoc);
|
||||||
return ActOnMemberReferenceExpr(S, ExprArg(*this, Base), OpLoc, tok::arrow,
|
return Owned(Base);
|
||||||
MemberLoc, Member, DeclPtrTy()).release();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// FixOverloadedFunctionReference - E is an expression that refers to
|
/// FixOverloadedFunctionReference - E is an expression that refers to
|
||||||
|
|
|
@ -1270,14 +1270,18 @@ TemplateExprInstantiator::VisitCXXUnresolvedMemberExpr(
|
||||||
if (Base.isInvalid())
|
if (Base.isInvalid())
|
||||||
return SemaRef.ExprError();
|
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.
|
// FIXME: Instantiate the declaration name.
|
||||||
return SemaRef.ActOnMemberReferenceExpr(/*Scope=*/0,
|
Base = SemaRef.ActOnMemberReferenceExpr(/*Scope=*/0,
|
||||||
move(Base), E->getOperatorLoc(),
|
move(Base), E->getOperatorLoc(),
|
||||||
E->isArrow()? tok::arrow
|
OpKind,
|
||||||
: tok::period,
|
|
||||||
E->getMemberLoc(),
|
E->getMemberLoc(),
|
||||||
/*FIXME:*/*E->getMember().getAsIdentifierInfo(),
|
/*FIXME:*/*E->getMember().getAsIdentifierInfo(),
|
||||||
/*FIXME?*/Sema::DeclPtrTy::make((Decl*)0));
|
/*FIXME?*/Sema::DeclPtrTy::make((Decl*)0));
|
||||||
|
SemaRef.ActOnCXXExitMemberScope(0, SS);
|
||||||
|
return move(Base);
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
|
|
|
@ -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'}}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue