From 0d5b0a1e5edb33458a8e6efdbc67d609927a7d32 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Wed, 24 Feb 2010 21:29:12 +0000 Subject: [PATCH] ActOnPseudoDestructorExpr now performs all semantic analysis for pseudo-destructor expressions, and builds the CXXPseudoDestructorExpr node directly. Currently, this only affects pseudo-destructor expressions when they are parsed, but not after template instantiation. That's coming next... Improve parsing of pseudo-destructor-names. When parsing the nested-name-specifier and we hit the sequence of tokens X :: ~, query the actual module to determine whether X is a type-name (in which case the X :: is part of the pseudo-destructor-name but not the nested-name-specifier) or not (in which case the X :: is part of the nested-name-specifier). llvm-svn: 97058 --- .../clang/Basic/DiagnosticSemaKinds.td | 7 +- clang/include/clang/Parse/Action.h | 15 +- clang/lib/Parse/ParseExprCXX.cpp | 8 +- clang/lib/Sema/Sema.h | 22 +- clang/lib/Sema/SemaCXXScopeSpec.cpp | 48 ++++ clang/lib/Sema/SemaExpr.cpp | 19 +- clang/lib/Sema/SemaExprCXX.cpp | 249 +++++++++++++++--- clang/test/SemaCXX/pseudo-destructors.cpp | 8 +- 8 files changed, 312 insertions(+), 64 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 07b0fdedcd58..de7624756f21 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -2078,7 +2078,12 @@ def err_pseudo_dtor_call_with_args : Error< def err_dtor_expr_without_call : Error< "%select{destructor reference|pseudo-destructor expression}0 must be " "called immediately with '()'">; - +def err_pseudo_dtor_destructor_non_type : Error< + "%0 does not refer to a type name in pseudo-destructor expression; expected " + "the name of type %1">; +def err_pseudo_dtor_template : Error< + "specialization of template %0 does not refer to a scalar type in pseudo-" + "destructor expression">; def err_invalid_use_of_function_type : Error< "a function type is not allowed here">; def err_invalid_use_of_array_type : Error<"an array type is not allowed here">; diff --git a/clang/include/clang/Parse/Action.h b/clang/include/clang/Parse/Action.h index 022b1c0154d5..e4a9512414bb 100644 --- a/clang/include/clang/Parse/Action.h +++ b/clang/include/clang/Parse/Action.h @@ -327,13 +327,26 @@ public: return false; } + /// \brief Determine whether the given name refers to a non-type nested name + /// specifier, e.g., the name of a namespace or namespace alias. + /// + /// This actual is used in the parsing of pseudo-destructor names to + /// distinguish a nested-name-specifier and a "type-name ::" when we + /// see the token sequence "X :: ~". + virtual bool isNonTypeNestedNameSpecifier(Scope *S, const CXXScopeSpec &SS, + SourceLocation IdLoc, + IdentifierInfo &II, + TypeTy *ObjectType) { + return false; + } + /// ActOnCXXGlobalScopeSpecifier - Return the object that represents the /// global scope ('::'). virtual CXXScopeTy *ActOnCXXGlobalScopeSpecifier(Scope *S, SourceLocation CCLoc) { return 0; } - + /// \brief Parsed an identifier followed by '::' in a C++ /// nested-name-specifier. /// diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp index 07de34dcb523..a72ab70d6cc8 100644 --- a/clang/lib/Parse/ParseExprCXX.cpp +++ b/clang/lib/Parse/ParseExprCXX.cpp @@ -240,7 +240,9 @@ 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) { - if (CheckForDestructor && GetLookAheadToken(2).is(tok::tilde)) { + if (CheckForDestructor && GetLookAheadToken(2).is(tok::tilde) && + !Actions.isNonTypeNestedNameSpecifier(CurScope, SS, Tok.getLocation(), + II, ObjectType)) { *MayBePseudoDestructor = true; return HasScopeSpecifier; } @@ -261,7 +263,9 @@ bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS, } if (Next.is(tok::coloncolon)) { - if (CheckForDestructor && GetLookAheadToken(2).is(tok::tilde)) { + if (CheckForDestructor && GetLookAheadToken(2).is(tok::tilde) && + !Actions.isNonTypeNestedNameSpecifier(CurScope, SS, Tok.getLocation(), + II, ObjectType)) { *MayBePseudoDestructor = true; return HasScopeSpecifier; } diff --git a/clang/lib/Sema/Sema.h b/clang/lib/Sema/Sema.h index f09fc543d187..57e527e94a62 100644 --- a/clang/lib/Sema/Sema.h +++ b/clang/lib/Sema/Sema.h @@ -2178,6 +2178,20 @@ public: TypeTy *&ObjectType, bool &MayBePseudoDestructor); + OwningExprResult DiagnoseDtorReference(SourceLocation NameLoc, + ExprArg MemExpr); + + OwningExprResult ActOnDependentPseudoDestructorExpr(Scope *S, + ExprArg Base, + SourceLocation OpLoc, + tok::TokenKind OpKind, + const CXXScopeSpec &SS, + UnqualifiedId &FirstTypeName, + SourceLocation CCLoc, + SourceLocation TildeLoc, + UnqualifiedId &SecondTypeName, + bool HasTrailingLParen); + virtual OwningExprResult ActOnPseudoDestructorExpr(Scope *S, ExprArg Base, SourceLocation OpLoc, tok::TokenKind OpKind, @@ -2187,7 +2201,7 @@ public: SourceLocation TildeLoc, UnqualifiedId &SecondTypeName, bool HasTrailingLParen); - + /// MaybeCreateCXXExprWithTemporaries - If the list of temporaries is /// non-empty, will create a new CXXExprWithTemporaries expression. /// Otherwise, just returs the passed in expression. @@ -2215,7 +2229,11 @@ public: bool MayBePseudoDestructor = false); NamedDecl *FindFirstQualifierInScope(Scope *S, NestedNameSpecifier *NNS); - + virtual bool isNonTypeNestedNameSpecifier(Scope *S, const CXXScopeSpec &SS, + SourceLocation IdLoc, + IdentifierInfo &II, + TypeTy *ObjectType); + CXXScopeTy *BuildCXXNestedNameSpecifier(Scope *S, const CXXScopeSpec &SS, SourceLocation IdLoc, diff --git a/clang/lib/Sema/SemaCXXScopeSpec.cpp b/clang/lib/Sema/SemaCXXScopeSpec.cpp index 315938d2d492..4f8a41426c0c 100644 --- a/clang/lib/Sema/SemaCXXScopeSpec.cpp +++ b/clang/lib/Sema/SemaCXXScopeSpec.cpp @@ -332,6 +332,54 @@ NamedDecl *Sema::FindFirstQualifierInScope(Scope *S, NestedNameSpecifier *NNS) { return 0; } +bool Sema::isNonTypeNestedNameSpecifier(Scope *S, const CXXScopeSpec &SS, + SourceLocation IdLoc, + IdentifierInfo &II, + TypeTy *ObjectTypePtr) { + QualType ObjectType = GetTypeFromParser(ObjectTypePtr); + LookupResult Found(*this, &II, IdLoc, LookupNestedNameSpecifierName); + + // Determine where to perform name lookup + DeclContext *LookupCtx = 0; + bool isDependent = false; + if (!ObjectType.isNull()) { + // This nested-name-specifier occurs in a member access expression, e.g., + // x->B::f, and we are looking into the type of the object. + assert(!SS.isSet() && "ObjectType and scope specifier cannot coexist"); + LookupCtx = computeDeclContext(ObjectType); + isDependent = ObjectType->isDependentType(); + } else if (SS.isSet()) { + // This nested-name-specifier occurs after another nested-name-specifier, + // so long into the context associated with the prior nested-name-specifier. + LookupCtx = computeDeclContext(SS, false); + isDependent = isDependentScopeSpecifier(SS); + Found.setContextRange(SS.getRange()); + } + + if (LookupCtx) { + // Perform "qualified" name lookup into the declaration context we + // computed, which is either the type of the base of a member access + // expression or the declaration context associated with a prior + // nested-name-specifier. + + // The declaration context must be complete. + if (!LookupCtx->isDependentContext() && RequireCompleteDeclContext(SS)) + return false; + + LookupQualifiedName(Found, LookupCtx); + } else if (isDependent) { + return false; + } else { + LookupName(Found, S); + } + Found.suppressDiagnostics(); + + if (NamedDecl *ND = Found.getAsSingle()) + return isa(ND) || isa(ND); + + return false; +} + /// \brief Build a new nested-name-specifier for "identifier::", as described /// by ActOnCXXNestedNameSpecifier. /// diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 24edd7204ef8..c3116a3885eb 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -3178,23 +3178,6 @@ Sema::LookupMemberExpr(LookupResult &R, Expr *&BaseExpr, return ExprError(); } -static Sema::OwningExprResult DiagnoseDtorReference(Sema &SemaRef, - SourceLocation NameLoc, - Sema::ExprArg MemExpr) { - Expr *E = (Expr *) MemExpr.get(); - SourceLocation ExpectedLParenLoc = SemaRef.PP.getLocForEndOfToken(NameLoc); - SemaRef.Diag(E->getLocStart(), diag::err_dtor_expr_without_call) - << isa(E) - << CodeModificationHint::CreateInsertion(ExpectedLParenLoc, "()"); - - return SemaRef.ActOnCallExpr(/*Scope*/ 0, - move(MemExpr), - /*LPLoc*/ ExpectedLParenLoc, - Sema::MultiExprArg(SemaRef, 0, 0), - /*CommaLocs*/ 0, - /*RPLoc*/ ExpectedLParenLoc); -} - /// The main callback when the parser finds something like /// expression . [nested-name-specifier] identifier /// expression -> [nested-name-specifier] identifier @@ -3265,7 +3248,7 @@ Sema::OwningExprResult Sema::ActOnMemberAccessExpr(Scope *S, ExprArg BaseArg, // call now. if (!HasTrailingLParen && Id.getKind() == UnqualifiedId::IK_DestructorName) - return DiagnoseDtorReference(*this, NameLoc, move(Result)); + return DiagnoseDtorReference(NameLoc, move(Result)); return move(Result); } diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index 1ea205671e7b..9172956b5153 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -2410,31 +2410,41 @@ Sema::ActOnStartCXXMemberReference(Scope *S, ExprArg Base, SourceLocation OpLoc, 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"); +Sema::OwningExprResult Sema::DiagnoseDtorReference(SourceLocation NameLoc, + ExprArg MemExpr) { + Expr *E = (Expr *) MemExpr.get(); + SourceLocation ExpectedLParenLoc = PP.getLocForEndOfToken(NameLoc); + Diag(E->getLocStart(), diag::err_dtor_expr_without_call) + << isa(E) + << CodeModificationHint::CreateInsertion(ExpectedLParenLoc, "()"); + + return ActOnCallExpr(/*Scope*/ 0, + move(MemExpr), + /*LPLoc*/ ExpectedLParenLoc, + Sema::MultiExprArg(*this, 0, 0), + /*CommaLocs*/ 0, + /*RPLoc*/ ExpectedLParenLoc); +} +Sema::OwningExprResult +Sema::ActOnDependentPseudoDestructorExpr(Scope *S, + ExprArg Base, + SourceLocation OpLoc, + tok::TokenKind OpKind, + const CXXScopeSpec &SS, + UnqualifiedId &FirstTypeName, + SourceLocation CCLoc, + SourceLocation TildeLoc, + UnqualifiedId &SecondTypeName, + bool HasTrailingLParen) { Expr *BaseE = (Expr *)Base.get(); - QualType ObjectType; - if (BaseE->isTypeDependent()) - ObjectType = Context.DependentTy; - + QualType ObjectType = BaseE->getType(); + assert(ObjectType->isDependentType()); + // 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 ::". @@ -2443,13 +2453,13 @@ Sema::OwningExprResult Sema::ActOnPseudoDestructorExpr(Scope *S, ExprArg Base, 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); + = ActOnCXXNestedNameSpecifier(S, SS, + FirstTypeName.StartLocation, + CCLoc, + *FirstTypeName.Identifier, + true, + ObjectType.getAsOpaquePtr(), + false); if (SS.getBeginLoc().isInvalid()) ExtendedSS.setBeginLoc(FirstTypeName.StartLocation); ExtendedSS.setEndLoc(CCLoc); @@ -2468,11 +2478,11 @@ Sema::OwningExprResult Sema::ActOnPseudoDestructorExpr(Scope *S, ExprArg Base, TemplateId->RAngleLoc); if (!T.isInvalid()) { CXXScopeTy *FinalScope - = ActOnCXXNestedNameSpecifier(S, SS, T.get(), - SourceRange(TemplateId->TemplateNameLoc, - TemplateId->RAngleLoc), - CCLoc, - true); + = ActOnCXXNestedNameSpecifier(S, SS, T.get(), + SourceRange(TemplateId->TemplateNameLoc, + TemplateId->RAngleLoc), + CCLoc, + true); if (SS.getBeginLoc().isInvalid()) ExtendedSS.setBeginLoc(TemplateId->TemplateNameLoc); ExtendedSS.setEndLoc(CCLoc); @@ -2480,7 +2490,7 @@ Sema::OwningExprResult Sema::ActOnPseudoDestructorExpr(Scope *S, ExprArg Base, } } } - + // Produce a destructor name based on the second type-name (which // follows the tilde). TypeTy *DestructedType; @@ -2490,7 +2500,7 @@ Sema::OwningExprResult Sema::ActOnPseudoDestructorExpr(Scope *S, ExprArg Base, bool isDependent = isDependentScopeSpecifier(ExtendedSS); if (isDependent || computeDeclContext(ExtendedSS)) LookupSS = &ExtendedSS; - + DestructedType = getTypeName(*SecondTypeName.Identifier, SecondTypeName.StartLocation, S, LookupSS, true, ObjectType.getTypePtr()); @@ -2514,13 +2524,13 @@ Sema::OwningExprResult Sema::ActOnPseudoDestructorExpr(Scope *S, ExprArg Base, 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 @@ -2528,7 +2538,7 @@ Sema::OwningExprResult Sema::ActOnPseudoDestructorExpr(Scope *S, ExprArg Base, TemplateIdAnnotation *TemplateId = SecondTypeName.TemplateId; ASTTemplateArgsPtr TemplateArgsPtr(*this, TemplateId->getTemplateArgs(), - TemplateId->NumArgs); + TemplateId->NumArgs); EndLoc = TemplateId->RAngleLoc; TypeResult T = ActOnTemplateIdType(TemplateTy::make(TemplateId->Template), TemplateId->TemplateNameLoc, @@ -2540,7 +2550,7 @@ Sema::OwningExprResult Sema::ActOnPseudoDestructorExpr(Scope *S, ExprArg Base, 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! @@ -2548,6 +2558,169 @@ Sema::OwningExprResult Sema::ActOnPseudoDestructorExpr(Scope *S, ExprArg Base, Destructor.setDestructorName(TildeLoc, DestructedType, EndLoc); return ActOnMemberAccessExpr(S, move(Base), OpLoc, OpKind, ExtendedSS, Destructor, DeclPtrTy(), HasTrailingLParen); + +} + +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(); + if (BaseE->isTypeDependent()) + return ActOnDependentPseudoDestructorExpr(S, move(Base), OpLoc, OpKind, + SS, FirstTypeName, CCLoc, + TildeLoc, SecondTypeName, + HasTrailingLParen); + + // C++ [expr.pseudo]p2: + // The left-hand side of the dot operator shall be of scalar type. The + // left-hand side of the arrow operator shall be of pointer to scalar type. + // This scalar type is the object type. + QualType ObjectType = BaseE->getType(); + if (OpKind == tok::arrow) { + if (const PointerType *Ptr = ObjectType->getAs()) { + ObjectType = Ptr->getPointeeType(); + } else { + // The user wrote "p->" when she probably meant "p."; fix it. + Diag(OpLoc, diag::err_typecheck_member_reference_suggestion) + << ObjectType << true + << CodeModificationHint::CreateReplacement(OpLoc, "."); + if (isSFINAEContext()) + return ExprError(); + + OpKind = tok::period; + } + } + + if (!ObjectType->isScalarType()) { + Diag(OpLoc, diag::err_pseudo_dtor_base_not_scalar) + << ObjectType << BaseE->getSourceRange(); + return ExprError(); + } + + // + + // C++ [expr.pseudo]p2: + // [...] The cv-unqualified versions of the object type and of the type + // designated by the pseudo-destructor-name shall be the same type. + QualType DestructedType; + TypeSourceInfo *DestructedTypeInfo = 0; + if (SecondTypeName.getKind() == UnqualifiedId::IK_Identifier) { + TypeTy *T = getTypeName(*SecondTypeName.Identifier, + SecondTypeName.StartLocation, + S, &SS); + if (!T) { + Diag(SecondTypeName.StartLocation, + diag::err_pseudo_dtor_destructor_non_type) + << SecondTypeName.Identifier << ObjectType; + if (isSFINAEContext()) + return ExprError(); + + // Recover by assuming we had the right type all along. + DestructedType = ObjectType; + } else { + DestructedType = GetTypeFromParser(T, &DestructedTypeInfo); + + if (!DestructedType->isDependentType() && + !Context.hasSameUnqualifiedType(DestructedType, ObjectType)) { + // The types mismatch. Recover by assuming we had the right type + // all along. + Diag(SecondTypeName.StartLocation, diag::err_pseudo_dtor_type_mismatch) + << ObjectType << DestructedType << BaseE->getSourceRange(); + + DestructedType = ObjectType; + DestructedTypeInfo = 0; + } + } + } else { + // FIXME: C++0x template aliases would allow a template-id here. For now, + // just diagnose this as an error. + TemplateIdAnnotation *TemplateId = SecondTypeName.TemplateId; + Diag(TemplateId->TemplateNameLoc, diag::err_pseudo_dtor_template) + << TemplateId->Name << ObjectType + << SourceRange(TemplateId->TemplateNameLoc, TemplateId->RAngleLoc); + if (isSFINAEContext()) + return ExprError(); + + // Recover by assuming we had the right type all along. + DestructedType = ObjectType; + } + + // C++ [expr.pseudo]p2: + // [...] Furthermore, the two type-names in a pseudo-destructor-name of the + // form + // + // ::[opt] nested-name-specifier[opt] type-name :: ~ type-name + // + // shall designate the same scalar type. + QualType ScopeType; + if (FirstTypeName.getKind() == UnqualifiedId::IK_TemplateId || + FirstTypeName.Identifier) { + if (FirstTypeName.getKind() == UnqualifiedId::IK_Identifier) { + TypeTy *T = getTypeName(*FirstTypeName.Identifier, + FirstTypeName.StartLocation, + S, &SS); + if (!T) { + Diag(FirstTypeName.StartLocation, + diag::err_pseudo_dtor_destructor_non_type) + << FirstTypeName.Identifier << ObjectType; + if (isSFINAEContext()) + return ExprError(); + } else { + // FIXME: Drops source-location information. + ScopeType = GetTypeFromParser(T); + + if (!ScopeType->isDependentType() && + !Context.hasSameUnqualifiedType(DestructedType, ScopeType)) { + // The types mismatch. Recover by assuming we don't have a scoping + // type. + Diag(FirstTypeName.StartLocation, diag::err_pseudo_dtor_type_mismatch) + << ObjectType << ScopeType << BaseE->getSourceRange(); + + ScopeType = QualType(); + } + } + } else { + // FIXME: C++0x template aliases would allow a template-id here. For now, + // just diagnose this as an error. + TemplateIdAnnotation *TemplateId = FirstTypeName.TemplateId; + Diag(TemplateId->TemplateNameLoc, diag::err_pseudo_dtor_template) + << TemplateId->Name << ObjectType + << SourceRange(TemplateId->TemplateNameLoc, TemplateId->RAngleLoc); + if (isSFINAEContext()) + return ExprError(); + + // Recover by assuming we have no scoping type. + DestructedType = ObjectType; + } + } + + // FIXME: Drops the scope type. + OwningExprResult Result + = Owned(new (Context) CXXPseudoDestructorExpr(Context, + Base.takeAs(), + OpKind == tok::arrow, + OpLoc, + (NestedNameSpecifier *) SS.getScopeRep(), + SS.getRange(), + DestructedType, + SecondTypeName.StartLocation)); + if (HasTrailingLParen) + return move(Result); + + return DiagnoseDtorReference(SecondTypeName.StartLocation, move(Result)); } CXXMemberCallExpr *Sema::BuildCXXMemberCallExpr(Expr *Exp, diff --git a/clang/test/SemaCXX/pseudo-destructors.cpp b/clang/test/SemaCXX/pseudo-destructors.cpp index de9ca2d30038..472e5b42fb29 100644 --- a/clang/test/SemaCXX/pseudo-destructors.cpp +++ b/clang/test/SemaCXX/pseudo-destructors.cpp @@ -11,6 +11,7 @@ void g(); namespace N { typedef Foo Wibble; + typedef int OtherInteger; } void f(A* a, Foo *f, int *i, double *d) { @@ -35,8 +36,11 @@ void f(A* a, Foo *f, int *i, double *d) { i->~Integer(); i->Integer::~Integer(); - - i->Integer::~Double(); // expected-error{{the type of object expression ('int') does not match the type being destroyed ('double') in pseudo-destructor expression}} + i->N::~OtherInteger(); + i->N::OtherInteger::~OtherInteger(); + i->N::OtherInteger::~Integer(); // expected-error{{'Integer' does not refer to a type name in pseudo-destructor expression; expected the name of type 'int'}} + i->N::~Integer(); // expected-error{{'Integer' does not refer to a type name in pseudo-destructor expression; expected the name of type 'int'}} + i->Integer::~Double(); // expected-error{{the type of object expression ('int') does not match the type being destroyed ('Double' (aka 'double')) in pseudo-destructor expression}} } typedef int Integer;