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 | e610adae17eadd435a319efcfb27d305a81f6d2f (patch) | |
| tree | 08b5b9c3a66b10746d9c93c0134a48950feb755d /clang/lib | |
| parent | 03ac201ad95b5e78047670bf3f709e952584201e (diff) | |
| download | bcm5719-llvm-e610adae17eadd435a319efcfb27d305a81f6d2f.tar.gz bcm5719-llvm-e610adae17eadd435a319efcfb27d305a81f6d2f.zip | |
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
Diffstat (limited to 'clang/lib')
| -rw-r--r-- | clang/lib/Parse/ParseExpr.cpp | 24 | ||||
| -rw-r--r-- | clang/lib/Parse/ParseExprCXX.cpp | 140 | ||||
| -rw-r--r-- | clang/lib/Sema/Sema.h | 13 | ||||
| -rw-r--r-- | clang/lib/Sema/SemaExprCXX.cpp | 151 | ||||
| -rw-r--r-- | clang/lib/Sema/TreeTransform.h | 4 |
5 files changed, 308 insertions, 24 deletions
diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp index 8105eeb8751..46faebfd3f0 100644 --- a/clang/lib/Parse/ParseExpr.cpp +++ b/clang/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/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp index a8fbaff0159..07de34dcb52 100644 --- a/clang/lib/Parse/ParseExprCXX.cpp +++ b/clang/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/clang/lib/Sema/Sema.h b/clang/lib/Sema/Sema.h index 2190e3b5bfa..f09fc543d18 100644 --- a/clang/lib/Sema/Sema.h +++ b/clang/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/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index 9d41f9d0dbf..1ea205671e7 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/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/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index 11c19eb23ce..40237d21d12 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/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(); |

