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
This commit is contained in:
Douglas Gregor 2010-02-24 21:29:12 +00:00
parent 02ec121de8
commit 0d5b0a1e5e
8 changed files with 312 additions and 64 deletions

View File

@ -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">;

View File

@ -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.
///

View File

@ -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;
}

View File

@ -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,

View File

@ -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<NamedDecl>())
return isa<NamespaceDecl>(ND) || isa<NamespaceAliasDecl>(ND);
return false;
}
/// \brief Build a new nested-name-specifier for "identifier::", as described
/// by ActOnCXXNestedNameSpecifier.
///

View File

@ -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<CXXPseudoDestructorExpr>(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);
}

View File

@ -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<CXXPseudoDestructorExpr>(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<PointerType>()) {
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<Expr>(),
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,

View File

@ -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;