Rework parsing of pseudo-destructor expressions and explicit

destructor calls, e.g., 

  p->T::~T

We now detect when the member access that we've parsed, e.g.,

  p-> or x.

may be a pseudo-destructor expression, either because the type of p or
x is a scalar or because it is dependent (and, therefore, may become a
scalar at template instantiation time). 

We then parse the pseudo-destructor grammar specifically:

  ::[opt] nested-name-specifier[opt] type-name :: ∼ type-name

and hand those results to a new action, ActOnPseudoDestructorExpr,
which will cope with both dependent member accesses of destructors and
with pseudo-destructor expressions.

This commit affects the parsing of pseudo-destructors, only; the
semantic actions still go through the semantic actions for member
access expressions. That will change soon.

llvm-svn: 97045
This commit is contained in:
Douglas Gregor 2010-02-24 18:44:31 +00:00
parent 03ac201ad9
commit e610adae17
7 changed files with 373 additions and 27 deletions

View File

@ -1614,12 +1614,66 @@ public:
/// with the type into which name lookup should look to find the member in
/// the member access expression.
///
/// \param MayBePseudoDestructor Originally false. The action should
/// set this true if the expression may end up being a
/// pseudo-destructor expression, indicating to the parser that it
/// shoudl be parsed as a pseudo-destructor rather than as a member
/// access expression. Note that this should apply both when the
/// object type is a scalar and when the object type is dependent.
///
/// \returns the (possibly modified) \p Base expression
virtual OwningExprResult ActOnStartCXXMemberReference(Scope *S,
ExprArg Base,
SourceLocation OpLoc,
tok::TokenKind OpKind,
TypeTy *&ObjectType) {
TypeTy *&ObjectType,
bool &MayBePseudoDestructor) {
return ExprEmpty();
}
/// \brief Parsed a C++ pseudo-destructor expression or a dependent
/// member access expression that has the same syntactic form as a
/// pseudo-destructor expression.
///
/// \param S The scope in which the member access expression occurs.
///
/// \param Base The expression in which a member is being accessed, e.g., the
/// "x" in "x.f".
///
/// \param OpLoc The location of the member access operator ("." or "->")
///
/// \param OpKind The kind of member access operator ("." or "->")
///
/// \param SS The nested-name-specifier that precedes the type names
/// in the grammar. Note that this nested-name-specifier will not
/// cover the last "type-name ::" in the grammar, because it isn't
/// necessarily a nested-name-specifier.
///
/// \param FirstTypeName The type name that follows the optional
/// nested-name-specifier but precedes the '::', e.g., the first
/// type-name in "type-name :: type-name". This type name may be
/// empty. This will be either an identifier or a template-id.
///
/// \param CCLoc The location of the '::' in "type-name ::
/// typename". May be invalid, if there is no \p FirstTypeName.
///
/// \param TildeLoc The location of the '~'.
///
/// \param SecondTypeName The type-name following the '~', which is
/// the name of the type being destroyed. This will be either an
/// identifier or a template-id.
///
/// \param HasTrailingLParen Whether the next token in the stream is
/// a left parentheses.
virtual OwningExprResult ActOnPseudoDestructorExpr(Scope *S, ExprArg Base,
SourceLocation OpLoc,
tok::TokenKind OpKind,
const CXXScopeSpec &SS,
UnqualifiedId &FirstTypeName,
SourceLocation CCLoc,
SourceLocation TildeLoc,
UnqualifiedId &SecondTypeName,
bool HasTrailingLParen) {
return ExprEmpty();
}

View File

@ -964,7 +964,7 @@ private:
bool ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS,
TypeTy *ObjectType,
bool EnteringContext,
bool InMemberAccessExpr = false);
bool *MayBePseudoDestructor = 0);
//===--------------------------------------------------------------------===//
// C++ 5.2p1: C++ Casts
@ -974,6 +974,13 @@ private:
// C++ 5.2p1: C++ Type Identification
OwningExprResult ParseCXXTypeid();
//===--------------------------------------------------------------------===//
// C++ 5.2.4: C++ Pseudo-Destructor Expressions
OwningExprResult ParseCXXPseudoDestructor(ExprArg Base, SourceLocation OpLoc,
tok::TokenKind OpKind,
CXXScopeSpec &SS,
Action::TypeTy *ObjectType);
//===--------------------------------------------------------------------===//
// C++ 9.3.2: C++ 'this' pointer
OwningExprResult ParseCXXThis();
@ -1415,7 +1422,8 @@ private:
SourceLocation NameLoc,
bool EnteringContext,
TypeTy *ObjectType,
UnqualifiedId &Id);
UnqualifiedId &Id,
bool AssumeTemplateId = false);
bool ParseUnqualifiedIdOperator(CXXScopeSpec &SS, bool EnteringContext,
TypeTy *ObjectType,
UnqualifiedId &Result);

View File

@ -996,12 +996,16 @@ Parser::ParsePostfixExpressionSuffix(OwningExprResult LHS) {
CXXScopeSpec SS;
Action::TypeTy *ObjectType = 0;
bool MayBePseudoDestructor = false;
if (getLang().CPlusPlus && !LHS.isInvalid()) {
LHS = Actions.ActOnStartCXXMemberReference(CurScope, move(LHS),
OpLoc, OpKind, ObjectType);
OpLoc, OpKind, ObjectType,
MayBePseudoDestructor);
if (LHS.isInvalid())
break;
ParseOptionalCXXScopeSpecifier(SS, ObjectType, false, true);
ParseOptionalCXXScopeSpecifier(SS, ObjectType, false,
&MayBePseudoDestructor);
}
if (Tok.is(tok::code_completion)) {
@ -1012,6 +1016,17 @@ Parser::ParsePostfixExpressionSuffix(OwningExprResult LHS) {
ConsumeToken();
}
if (MayBePseudoDestructor) {
LHS = ParseCXXPseudoDestructor(move(LHS), OpLoc, OpKind, SS,
ObjectType);
break;
}
// Either the action has told is that this cannot be a
// pseudo-destructor expression (based on the type of base
// expression), or we didn't see a '~' in the right place. We
// can still parse a destructor name here, but in that case it
// names a real destructor.
UnqualifiedId Name;
if (ParseUnqualifiedId(SS,
/*EnteringContext=*/false,
@ -1022,10 +1037,9 @@ Parser::ParsePostfixExpressionSuffix(OwningExprResult LHS) {
return ExprError();
if (!LHS.isInvalid())
LHS = Actions.ActOnMemberAccessExpr(CurScope, move(LHS), OpLoc, OpKind,
SS, Name, ObjCImpDecl,
LHS = Actions.ActOnMemberAccessExpr(CurScope, move(LHS), OpLoc,
OpKind, SS, Name, ObjCImpDecl,
Tok.is(tok::l_paren));
break;
}
case tok::plusplus: // postfix-expression: postfix-expression '++'

View File

@ -45,14 +45,21 @@ using namespace clang;
/// \param EnteringContext whether we will be entering into the context of
/// the nested-name-specifier after parsing it.
///
/// \param InMemberAccessExpr Whether this scope specifier is within a
/// \param MayBePseudoDestructor When non-NULL, points to a flag that
/// indicates whether this nested-name-specifier may be part of a
/// pseudo-destructor name. In this case, the flag will be set false
/// if we don't actually end up parsing a destructor name. Moreorover,
/// if we do end up determining that we are parsing a destructor name,
/// the last component of the nested-name-specifier is not parsed as
/// part of the scope specifier.
/// member access expression, e.g., the \p T:: in \p p->T::m.
///
/// \returns true if a scope specifier was parsed.
bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS,
Action::TypeTy *ObjectType,
bool EnteringContext,
bool InMemberAccessExpr) {
bool *MayBePseudoDestructor) {
assert(getLang().CPlusPlus &&
"Call sites of this function should be guarded by checking for C++");
@ -79,6 +86,12 @@ bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS,
HasScopeSpecifier = true;
}
bool CheckForDestructor = false;
if (MayBePseudoDestructor && *MayBePseudoDestructor) {
CheckForDestructor = true;
*MayBePseudoDestructor = false;
}
while (true) {
if (HasScopeSpecifier) {
// C++ [basic.lookup.classref]p5:
@ -173,8 +186,11 @@ bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS,
// convert it into a type within the nested-name-specifier.
TemplateIdAnnotation *TemplateId
= static_cast<TemplateIdAnnotation *>(Tok.getAnnotationValue());
bool MayBePseudoDestructor
= InMemberAccessExpr && GetLookAheadToken(2).is(tok::tilde);
if (CheckForDestructor && GetLookAheadToken(2).is(tok::tilde)) {
*MayBePseudoDestructor = true;
return HasScopeSpecifier;
}
if (TemplateId->Kind == TNK_Type_template ||
TemplateId->Kind == TNK_Dependent_template_name) {
AnnotateTemplateIdTokenAsType(&SS);
@ -197,7 +213,7 @@ bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS,
TypeToken.getAnnotationValue(),
TypeToken.getAnnotationRange(),
CCLoc,
MayBePseudoDestructor));
false));
else
SS.setScopeRep(0);
SS.setEndLoc(CCLoc);
@ -224,11 +240,13 @@ bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS,
// If we get foo:bar, this is almost certainly a typo for foo::bar. Recover
// and emit a fixit hint for it.
if (Next.is(tok::colon) && !ColonIsSacred) {
bool MayBePseudoDestructor
= InMemberAccessExpr && GetLookAheadToken(2).is(tok::tilde);
if (CheckForDestructor && GetLookAheadToken(2).is(tok::tilde)) {
*MayBePseudoDestructor = true;
return HasScopeSpecifier;
}
if (Actions.IsInvalidUnlessNestedName(CurScope, SS, II,
MayBePseudoDestructor, ObjectType,
false, ObjectType,
EnteringContext) &&
// If the token after the colon isn't an identifier, it's still an
// error, but they probably meant something else strange so don't
@ -243,6 +261,11 @@ bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS,
}
if (Next.is(tok::coloncolon)) {
if (CheckForDestructor && GetLookAheadToken(2).is(tok::tilde)) {
*MayBePseudoDestructor = true;
return HasScopeSpecifier;
}
// We have an identifier followed by a '::'. Lookup this name
// as the name in a nested-name-specifier.
SourceLocation IdLoc = ConsumeToken();
@ -258,11 +281,9 @@ bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS,
if (SS.isInvalid())
continue;
bool MayBePseudoDestructor = InMemberAccessExpr && Tok.is(tok::tilde);
SS.setScopeRep(
Actions.ActOnCXXNestedNameSpecifier(CurScope, SS, IdLoc, CCLoc, II,
MayBePseudoDestructor, ObjectType,
false, ObjectType,
EnteringContext));
SS.setEndLoc(CCLoc);
continue;
@ -298,6 +319,12 @@ bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS,
break;
}
// Even if we didn't see any pieces of a nested-name-specifier, we
// still check whether there is a tilde in this position, which
// indicates a potential pseudo-destructor.
if (CheckForDestructor && Tok.is(tok::tilde))
*MayBePseudoDestructor = true;
return HasScopeSpecifier;
}
@ -493,6 +520,77 @@ Parser::OwningExprResult Parser::ParseCXXTypeid() {
return move(Result);
}
/// \brief Parse a C++ pseudo-destructor expression after the base,
/// . or -> operator, and nested-name-specifier have already been
/// parsed.
///
/// postfix-expression: [C++ 5.2]
/// postfix-expression . pseudo-destructor-name
/// postfix-expression -> pseudo-destructor-name
///
/// pseudo-destructor-name:
/// ::[opt] nested-name-specifier[opt] type-name :: ~type-name
/// ::[opt] nested-name-specifier template simple-template-id ::
/// ~type-name
/// ::[opt] nested-name-specifier[opt] ~type-name
///
Parser::OwningExprResult
Parser::ParseCXXPseudoDestructor(ExprArg Base, SourceLocation OpLoc,
tok::TokenKind OpKind,
CXXScopeSpec &SS,
Action::TypeTy *ObjectType) {
// We're parsing either a pseudo-destructor-name or a dependent
// member access that has the same form as a
// pseudo-destructor-name. We parse both in the same way and let
// the action model sort them out.
//
// Note that the ::[opt] nested-name-specifier[opt] has already
// been parsed, and if there was a simple-template-id, it has
// been coalesced into a template-id annotation token.
UnqualifiedId FirstTypeName;
SourceLocation CCLoc;
if (Tok.is(tok::identifier)) {
FirstTypeName.setIdentifier(Tok.getIdentifierInfo(), Tok.getLocation());
ConsumeToken();
assert(Tok.is(tok::coloncolon) &&"ParseOptionalCXXScopeSpecifier fail");
CCLoc = ConsumeToken();
} else if (Tok.is(tok::annot_template_id)) {
FirstTypeName.setTemplateId(
(TemplateIdAnnotation *)Tok.getAnnotationValue());
ConsumeToken();
assert(Tok.is(tok::coloncolon) &&"ParseOptionalCXXScopeSpecifier fail");
CCLoc = ConsumeToken();
} else {
FirstTypeName.setIdentifier(0, SourceLocation());
}
// Parse the tilde.
assert(Tok.is(tok::tilde) && "ParseOptionalCXXScopeSpecifier fail");
SourceLocation TildeLoc = ConsumeToken();
if (!Tok.is(tok::identifier)) {
Diag(Tok, diag::err_destructor_tilde_identifier);
return ExprError();
}
// Parse the second type.
UnqualifiedId SecondTypeName;
IdentifierInfo *Name = Tok.getIdentifierInfo();
SourceLocation NameLoc = ConsumeToken();
SecondTypeName.setIdentifier(Name, NameLoc);
// If there is a '<', the second type name is a template-id. Parse
// it as such.
if (Tok.is(tok::less) &&
ParseUnqualifiedIdTemplateId(SS, Name, NameLoc, false, ObjectType,
SecondTypeName, /*AssumeTemplateName=*/true))
return ExprError();
return Actions.ActOnPseudoDestructorExpr(CurScope, move(Base), OpLoc, OpKind,
SS, FirstTypeName, CCLoc,
TildeLoc, SecondTypeName,
Tok.is(tok::l_paren));
}
/// ParseCXXBoolLiteral - This handles the C++ Boolean literals.
///
/// boolean-literal: [C++ 2.13.5]
@ -818,13 +916,17 @@ bool Parser::ParseCXXTypeSpecifierSeq(DeclSpec &DS) {
/// that precedes the '<'. If template arguments were parsed successfully,
/// will be updated with the template-id.
///
/// \param AssumeTemplateId When true, this routine will assume that the name
/// refers to a template without performing name lookup to verify.
///
/// \returns true if a parse error occurred, false otherwise.
bool Parser::ParseUnqualifiedIdTemplateId(CXXScopeSpec &SS,
IdentifierInfo *Name,
SourceLocation NameLoc,
bool EnteringContext,
TypeTy *ObjectType,
UnqualifiedId &Id) {
UnqualifiedId &Id,
bool AssumeTemplateId) {
assert(Tok.is(tok::less) && "Expected '<' to finish parsing a template-id");
TemplateTy Template;
@ -833,8 +935,16 @@ bool Parser::ParseUnqualifiedIdTemplateId(CXXScopeSpec &SS,
case UnqualifiedId::IK_Identifier:
case UnqualifiedId::IK_OperatorFunctionId:
case UnqualifiedId::IK_LiteralOperatorId:
TNK = Actions.isTemplateName(CurScope, SS, Id, ObjectType, EnteringContext,
Template);
if (AssumeTemplateId) {
Template = Actions.ActOnDependentTemplateName(SourceLocation(), SS,
Id, ObjectType,
EnteringContext);
TNK = TNK_Dependent_template_name;
if (!Template.get())
return true;
} else
TNK = Actions.isTemplateName(CurScope, SS, Id, ObjectType,
EnteringContext, Template);
break;
case UnqualifiedId::IK_ConstructorName: {

View File

@ -2175,7 +2175,18 @@ public:
ExprArg Base,
SourceLocation OpLoc,
tok::TokenKind OpKind,
TypeTy *&ObjectType);
TypeTy *&ObjectType,
bool &MayBePseudoDestructor);
virtual OwningExprResult ActOnPseudoDestructorExpr(Scope *S, ExprArg Base,
SourceLocation OpLoc,
tok::TokenKind OpKind,
const CXXScopeSpec &SS,
UnqualifiedId &FirstTypeName,
SourceLocation CCLoc,
SourceLocation TildeLoc,
UnqualifiedId &SecondTypeName,
bool HasTrailingLParen);
/// MaybeCreateCXXExprWithTemporaries - If the list of temporaries is
/// non-empty, will create a new CXXExprWithTemporaries expression.

View File

@ -21,6 +21,7 @@
#include "clang/Basic/TargetInfo.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Parse/DeclSpec.h"
#include "clang/Parse/Template.h"
#include "llvm/ADT/STLExtras.h"
using namespace clang;
@ -2325,7 +2326,8 @@ FullExpr Sema::CreateFullExpr(Expr *SubExpr) {
Sema::OwningExprResult
Sema::ActOnStartCXXMemberReference(Scope *S, ExprArg Base, SourceLocation OpLoc,
tok::TokenKind OpKind, TypeTy *&ObjectType) {
tok::TokenKind OpKind, TypeTy *&ObjectType,
bool &MayBePseudoDestructor) {
// Since this might be a postfix expression, get rid of ParenListExprs.
Base = MaybeConvertParenListExprToParenExpr(S, move(Base));
@ -2333,6 +2335,7 @@ Sema::ActOnStartCXXMemberReference(Scope *S, ExprArg Base, SourceLocation OpLoc,
assert(BaseExpr && "no record expansion");
QualType BaseType = BaseExpr->getType();
MayBePseudoDestructor = false;
if (BaseType->isDependentType()) {
// If we have a pointer to a dependent type and are using the -> operator,
// the object type is the type that the pointer points to. We might still
@ -2342,6 +2345,7 @@ Sema::ActOnStartCXXMemberReference(Scope *S, ExprArg Base, SourceLocation OpLoc,
BaseType = Ptr->getPointeeType();
ObjectType = BaseType.getAsOpaquePtr();
MayBePseudoDestructor = true;
return move(Base);
}
@ -2383,7 +2387,11 @@ Sema::ActOnStartCXXMemberReference(Scope *S, ExprArg Base, SourceLocation OpLoc,
// [...] If the type of the object expression is of pointer to scalar
// type, the unqualified-id is looked up in the context of the complete
// postfix-expression.
//
// This also indicates that we should be parsing a
// pseudo-destructor-name.
ObjectType = 0;
MayBePseudoDestructor = true;
return move(Base);
}
@ -2399,10 +2407,149 @@ Sema::ActOnStartCXXMemberReference(Scope *S, ExprArg Base, SourceLocation OpLoc,
// type C (or of pointer to a class type C), the unqualified-id is looked
// up in the scope of class C. [...]
ObjectType = BaseType.getAsOpaquePtr();
return move(Base);
}
Sema::OwningExprResult Sema::ActOnPseudoDestructorExpr(Scope *S, ExprArg Base,
SourceLocation OpLoc,
tok::TokenKind OpKind,
const CXXScopeSpec &SS,
UnqualifiedId &FirstTypeName,
SourceLocation CCLoc,
SourceLocation TildeLoc,
UnqualifiedId &SecondTypeName,
bool HasTrailingLParen) {
assert((FirstTypeName.getKind() == UnqualifiedId::IK_TemplateId ||
FirstTypeName.getKind() == UnqualifiedId::IK_Identifier) &&
"Invalid first type name in pseudo-destructor");
assert((SecondTypeName.getKind() == UnqualifiedId::IK_TemplateId ||
SecondTypeName.getKind() == UnqualifiedId::IK_Identifier) &&
"Invalid second type name in pseudo-destructor");
Expr *BaseE = (Expr *)Base.get();
QualType ObjectType;
if (BaseE->isTypeDependent())
ObjectType = Context.DependentTy;
// The nested-name-specifier provided by the parser, then extended
// by the "type-name ::" in the pseudo-destructor-name, if present.
CXXScopeSpec ExtendedSS = SS;
if (FirstTypeName.getKind() == UnqualifiedId::IK_TemplateId ||
FirstTypeName.Identifier) {
// We have a pseudo-destructor with a "type-name ::".
// FIXME: As a temporary hack, we go ahead and resolve this to part of
// a nested-name-specifier.
if (FirstTypeName.getKind() == UnqualifiedId::IK_Identifier) {
// Resolve the identifier to a nested-name-specifier.
CXXScopeTy *FinalScope
= ActOnCXXNestedNameSpecifier(S, SS,
FirstTypeName.StartLocation,
CCLoc,
*FirstTypeName.Identifier,
true,
ObjectType.getAsOpaquePtr(),
false);
if (SS.getBeginLoc().isInvalid())
ExtendedSS.setBeginLoc(FirstTypeName.StartLocation);
ExtendedSS.setEndLoc(CCLoc);
ExtendedSS.setScopeRep(FinalScope);
} else {
// Resolve the template-id to a type, and that to a
// nested-name-specifier.
TemplateIdAnnotation *TemplateId = FirstTypeName.TemplateId;
ASTTemplateArgsPtr TemplateArgsPtr(*this,
TemplateId->getTemplateArgs(),
TemplateId->NumArgs);
TypeResult T = ActOnTemplateIdType(TemplateTy::make(TemplateId->Template),
TemplateId->TemplateNameLoc,
TemplateId->LAngleLoc,
TemplateArgsPtr,
TemplateId->RAngleLoc);
if (!T.isInvalid()) {
CXXScopeTy *FinalScope
= ActOnCXXNestedNameSpecifier(S, SS, T.get(),
SourceRange(TemplateId->TemplateNameLoc,
TemplateId->RAngleLoc),
CCLoc,
true);
if (SS.getBeginLoc().isInvalid())
ExtendedSS.setBeginLoc(TemplateId->TemplateNameLoc);
ExtendedSS.setEndLoc(CCLoc);
ExtendedSS.setScopeRep(FinalScope);
}
}
}
// Produce a destructor name based on the second type-name (which
// follows the tilde).
TypeTy *DestructedType;
SourceLocation EndLoc;
if (SecondTypeName.getKind() == UnqualifiedId::IK_Identifier) {
const CXXScopeSpec *LookupSS = &SS;
bool isDependent = isDependentScopeSpecifier(ExtendedSS);
if (isDependent || computeDeclContext(ExtendedSS))
LookupSS = &ExtendedSS;
DestructedType = getTypeName(*SecondTypeName.Identifier,
SecondTypeName.StartLocation,
S, LookupSS, true, ObjectType.getTypePtr());
if (!DestructedType && isDependent) {
// We didn't find our type, but that's okay: it's dependent
// anyway.
// FIXME: We should not be building a typename type here!
NestedNameSpecifier *NNS = 0;
SourceRange Range;
if (SS.isSet()) {
NNS = (NestedNameSpecifier *)ExtendedSS.getScopeRep();
Range = SourceRange(ExtendedSS.getRange().getBegin(),
SecondTypeName.StartLocation);
} else {
NNS = NestedNameSpecifier::Create(Context, SecondTypeName.Identifier);
Range = SourceRange(SecondTypeName.StartLocation);
}
DestructedType = CheckTypenameType(NNS, *SecondTypeName.Identifier,
Range).getAsOpaquePtr();
if (!DestructedType)
return ExprError();
}
if (!DestructedType) {
// FIXME: Crummy diagnostic.
Diag(SecondTypeName.StartLocation, diag::err_destructor_class_name);
return ExprError();
}
EndLoc = SecondTypeName.EndLocation;
} else {
// Resolve the template-id to a type, and that to a
// nested-name-specifier.
TemplateIdAnnotation *TemplateId = SecondTypeName.TemplateId;
ASTTemplateArgsPtr TemplateArgsPtr(*this,
TemplateId->getTemplateArgs(),
TemplateId->NumArgs);
EndLoc = TemplateId->RAngleLoc;
TypeResult T = ActOnTemplateIdType(TemplateTy::make(TemplateId->Template),
TemplateId->TemplateNameLoc,
TemplateId->LAngleLoc,
TemplateArgsPtr,
TemplateId->RAngleLoc);
if (T.isInvalid() || !T.get())
return ExprError();
DestructedType = T.get();
}
// Form a (possibly fake) destructor name and let the member access
// expression code deal with this.
// FIXME: Don't do this! It's totally broken!
UnqualifiedId Destructor;
Destructor.setDestructorName(TildeLoc, DestructedType, EndLoc);
return ActOnMemberAccessExpr(S, move(Base), OpLoc, OpKind, ExtendedSS,
Destructor, DeclPtrTy(), HasTrailingLParen);
}
CXXMemberCallExpr *Sema::BuildCXXMemberCallExpr(Expr *Exp,
CXXMethodDecl *Method) {
if (PerformObjectArgumentInitialization(Exp, Method))

View File

@ -5041,10 +5041,12 @@ TreeTransform<Derived>::TransformCXXDependentScopeMemberExpr(
// Start the member reference and compute the object's type.
Sema::TypeTy *ObjectTy = 0;
bool MayBePseudoDestructor = false;
Base = SemaRef.ActOnStartCXXMemberReference(0, move(Base),
E->getOperatorLoc(),
E->isArrow()? tok::arrow : tok::period,
ObjectTy);
ObjectTy,
MayBePseudoDestructor);
if (Base.isInvalid())
return SemaRef.ExprError();