diff options
author | Douglas Gregor <dgregor@apple.com> | 2010-02-24 18:44:31 +0000 |
---|---|---|
committer | Douglas Gregor <dgregor@apple.com> | 2010-02-24 18:44:31 +0000 |
commit | d4dca08d6b7ed2e3e3718caa6fd735960b135e9a (patch) | |
tree | 727039d6619e433a1997dc3907b3dbfafaa72793 | |
parent | 5edcc3752563ae0ed5b26f8c89a472ac84af9cf7 (diff) |
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.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@97045 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r-- | include/clang/Parse/Action.h | 56 | ||||
-rw-r--r-- | include/clang/Parse/Parser.h | 12 | ||||
-rw-r--r-- | lib/Parse/ParseExpr.cpp | 24 | ||||
-rw-r--r-- | lib/Parse/ParseExprCXX.cpp | 140 | ||||
-rw-r--r-- | lib/Sema/Sema.h | 13 | ||||
-rw-r--r-- | lib/Sema/SemaExprCXX.cpp | 151 | ||||
-rw-r--r-- | lib/Sema/TreeTransform.h | 4 |
7 files changed, 373 insertions, 27 deletions
diff --git a/include/clang/Parse/Action.h b/include/clang/Parse/Action.h index 10341118e4..022b1c0154 100644 --- a/include/clang/Parse/Action.h +++ b/include/clang/Parse/Action.h @@ -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(); } diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h index a29e47f1ab..4a367484c9 100644 --- a/include/clang/Parse/Parser.h +++ b/include/clang/Parse/Parser.h @@ -964,7 +964,7 @@ private: bool ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS, TypeTy *ObjectType, bool EnteringContext, - bool InMemberAccessExpr = false); + bool *MayBePseudoDestructor = 0); //===--------------------------------------------------------------------===// // C++ 5.2p1: C++ Casts @@ -975,6 +975,13 @@ private: 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); diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index 8105eeb875..46faebfd3f 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -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 '++' diff --git a/lib/Parse/ParseExprCXX.cpp b/lib/Parse/ParseExprCXX.cpp index a8fbaff015..07de34dcb5 100644 --- a/lib/Parse/ParseExprCXX.cpp +++ b/lib/Parse/ParseExprCXX.cpp @@ -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: { diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index 2190e3b5bf..f09fc543d1 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -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. diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index 9d41f9d0db..1ea205671e 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -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)) diff --git a/lib/Sema/TreeTransform.h b/lib/Sema/TreeTransform.h index 11c19eb23c..40237d21d1 100644 --- a/lib/Sema/TreeTransform.h +++ b/lib/Sema/TreeTransform.h @@ -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(); |