Implement AST, semantics, and CodeGen for C++ pseudo-destructor

expressions, e.g.,

  p->~T()

when p is a pointer to a scalar type. 

We don't currently diagnose errors when pseudo-destructor expressions
are used in any way other than by forming a call.



git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@81009 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Douglas Gregor 2009-09-04 17:36:40 +00:00
Родитель 9efa767be8
Коммит a71d819bb8
13 изменённых файлов: 339 добавлений и 26 удалений

Просмотреть файл

@ -851,6 +851,108 @@ public:
virtual child_iterator child_end();
};
/// \brief Represents a C++ pseudo-destructor (C++ [expr.pseudo]).
///
/// Example:
///
/// \code
/// template<typename T>
/// void destroy(T* ptr) {
/// ptr->~T();
/// }
/// \endcode
///
/// When the template is parsed, the expression \c ptr->~T will be stored as
/// a member reference expression. If it then instantiated with a scalar type
/// as a template argument for T, the resulting expression will be a
/// pseudo-destructor expression.
class CXXPseudoDestructorExpr : public Expr {
/// \brief The base expression (that is being destroyed).
Stmt *Base;
/// \brief Whether the operator was an arrow ('->'); otherwise, it was a
/// period ('.').
bool IsArrow : 1;
/// \brief The location of the '.' or '->' operator.
SourceLocation OperatorLoc;
/// \brief The nested-name-specifier that follows the operator, if present.
NestedNameSpecifier *Qualifier;
/// \brief The source range that covers the nested-name-specifier, if
/// present.
SourceRange QualifierRange;
/// \brief The type being destroyed.
QualType DestroyedType;
/// \brief The location of the type after the '~'.
SourceLocation DestroyedTypeLoc;
public:
CXXPseudoDestructorExpr(ASTContext &Context,
Expr *Base, bool isArrow, SourceLocation OperatorLoc,
NestedNameSpecifier *Qualifier,
SourceRange QualifierRange,
QualType DestroyedType,
SourceLocation DestroyedTypeLoc)
: Expr(CXXPseudoDestructorExprClass,
Context.getPointerType(Context.getFunctionType(Context.VoidTy, 0, 0,
false, 0)),
/*isTypeDependent=*/false,
/*isValueDependent=*/Base->isValueDependent()),
Base(static_cast<Stmt *>(Base)), IsArrow(isArrow),
OperatorLoc(OperatorLoc), Qualifier(Qualifier),
QualifierRange(QualifierRange), DestroyedType(DestroyedType),
DestroyedTypeLoc(DestroyedTypeLoc) { }
void setBase(Expr *E) { Base = E; }
Expr *getBase() const { return cast<Expr>(Base); }
/// \brief Determines whether this member expression actually had
/// a C++ nested-name-specifier prior to the name of the member, e.g.,
/// x->Base::foo.
bool hasQualifier() const { return Qualifier != 0; }
/// \brief If the member name was qualified, retrieves the source range of
/// the nested-name-specifier that precedes the member name. Otherwise,
/// returns an empty source range.
SourceRange getQualifierRange() const { return QualifierRange; }
/// \brief If the member name was qualified, retrieves the
/// nested-name-specifier that precedes the member name. Otherwise, returns
/// NULL.
NestedNameSpecifier *getQualifier() const { return Qualifier; }
/// \brief Determine whether this pseudo-destructor expression was written
/// using an '->' (otherwise, it used a '.').
bool isArrow() const { return IsArrow; }
void setArrow(bool A) { IsArrow = A; }
/// \brief Retrieve the location of the '.' or '->' operator.
SourceLocation getOperatorLoc() const { return OperatorLoc; }
/// \brief Retrieve the type that is being destroyed.
QualType getDestroyedType() const { return DestroyedType; }
/// \brief Retrieve the location of the type being destroyed.
SourceLocation getDestroyedTypeLoc() const { return DestroyedTypeLoc; }
virtual SourceRange getSourceRange() const {
return SourceRange(Base->getLocStart(), DestroyedTypeLoc);
}
static bool classof(const Stmt *T) {
return T->getStmtClass() == CXXPseudoDestructorExprClass;
}
static bool classof(const CXXPseudoDestructorExpr *) { return true; }
// Iterators
virtual child_iterator child_begin();
virtual child_iterator child_end();
};
/// \brief Represents the name of a function that has not been
/// resolved to any declaration.
///

Просмотреть файл

@ -124,6 +124,7 @@ EXPR(CXXZeroInitValueExpr , Expr)
EXPR(CXXConditionDeclExpr , DeclRefExpr)
EXPR(CXXNewExpr , Expr)
EXPR(CXXDeleteExpr , Expr)
EXPR(CXXPseudoDestructorExpr, Expr)
EXPR(UnresolvedFunctionNameExpr , Expr)
EXPR(UnaryTypeTraitExpr , Expr)
EXPR(QualifiedDeclRefExpr , DeclRefExpr)

Просмотреть файл

@ -1588,9 +1588,15 @@ def err_return_in_constructor_handler : Error<
def err_ident_in_pseudo_dtor_not_a_type : Error<
"identifier %0 in pseudo-destructor expression does not name a type">;
def err_type_in_pseudo_dtor_not_a_class_type : Error<
"type %0 in pseudo-destructor expression is not a class type">;
def err_pseudo_dtor_base_not_scalar : Error<
"object expression of non-scalar type %0 cannot be used in a "
"pseudo-destructor expression">;
def err_pseudo_dtor_type_mismatch : Error<
"the type of object expression (%0) does not match the type being destroyed "
"(%1) in pseudo-destructor expression">;
def err_pseudo_dtor_call_with_args : Error<
"call to pseudo-destructor cannot have any arguments">;
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">;

Просмотреть файл

@ -116,6 +116,12 @@ Stmt::child_iterator CXXNewExpr::child_end() {
Stmt::child_iterator CXXDeleteExpr::child_begin() { return &Argument; }
Stmt::child_iterator CXXDeleteExpr::child_end() { return &Argument+1; }
// CXXPseudoDestructorExpr
Stmt::child_iterator CXXPseudoDestructorExpr::child_begin() { return &Base; }
Stmt::child_iterator CXXPseudoDestructorExpr::child_end() {
return &Base + 1;
}
// UnresolvedFunctionNameExpr
Stmt::child_iterator UnresolvedFunctionNameExpr::child_begin() {
return child_iterator();

Просмотреть файл

@ -1107,6 +1107,20 @@ void StmtPrinter::VisitCXXDeleteExpr(CXXDeleteExpr *E) {
PrintExpr(E->getArgument());
}
void StmtPrinter::VisitCXXPseudoDestructorExpr(CXXPseudoDestructorExpr *E) {
PrintExpr(E->getBase());
if (E->isArrow())
OS << "->";
else
OS << '.';
if (E->getQualifier())
E->getQualifier()->print(OS, Policy);
std::string TypeS;
E->getDestroyedType().getAsStringInternal(TypeS, Policy);
OS << TypeS;
}
void StmtPrinter::VisitUnresolvedFunctionNameExpr(UnresolvedFunctionNameExpr *E) {
OS << E->getName().getAsString();
}

Просмотреть файл

@ -502,6 +502,13 @@ void StmtProfiler::VisitCXXNewExpr(CXXNewExpr *S) {
ID.AddInteger(S->getNumConstructorArgs());
}
void StmtProfiler::VisitCXXPseudoDestructorExpr(CXXPseudoDestructorExpr *S) {
VisitExpr(S);
ID.AddBoolean(S->isArrow());
VisitNestedNameSpecifier(S->getQualifier());
VisitType(S->getDestroyedType());
}
void
StmtProfiler::VisitUnresolvedFunctionNameExpr(UnresolvedFunctionNameExpr *S) {
VisitExpr(S);

Просмотреть файл

@ -1234,6 +1234,16 @@ RValue CodeGenFunction::EmitCallExpr(const CallExpr *E) {
if (const CXXMethodDecl *MD = dyn_cast_or_null<CXXMethodDecl>(TargetDecl))
return EmitCXXOperatorMemberCallExpr(CE, MD);
if (isa<CXXPseudoDestructorExpr>(E->getCallee())) {
// C++ [expr.pseudo]p1:
// The result shall only be used as the operand for the function call
// operator (), and the result of such a call has type void. The only
// effect is the evaluation of the postfix-expression before the dot or
// arrow.
EmitScalarExpr(E->getCallee());
return RValue::get(0);
}
llvm::Value *Callee = EmitScalarExpr(E->getCallee());
return EmitCall(Callee, E->getCallee()->getType(),
E->arg_begin(), E->arg_end(), TargetDecl);

Просмотреть файл

@ -305,7 +305,17 @@ public:
CGF.EmitCXXDeleteExpr(E);
return 0;
}
Value *VisitCXXPseudoDestructorExpr(const CXXPseudoDestructorExpr *E) {
// C++ [expr.pseudo]p1:
// The result shall only be used as the operand for the function call
// operator (), and the result of such a call has type void. The only
// effect is the evaluation of the postfix-expression before the dot or
// arrow.
CGF.EmitScalarExpr(E->getBase());
return 0;
}
// Binary Operators.
Value *EmitMul(const BinOpInfo &Ops) {
if (CGF.getContext().getLangOptions().OverflowChecking

Просмотреть файл

@ -1998,7 +1998,7 @@ Sema::BuildMemberReferenceExpr(Scope *S, ExprArg Base, SourceLocation OpLoc,
Base = MaybeConvertParenListExprToParenExpr(S, move(Base));
Expr *BaseExpr = Base.takeAs<Expr>();
assert(BaseExpr && "no record expression");
assert(BaseExpr && "no base expression");
// Perform default conversions.
DefaultFunctionArrayConversion(BaseExpr);
@ -2230,6 +2230,47 @@ Sema::BuildMemberReferenceExpr(Scope *S, ExprArg Base, SourceLocation OpLoc,
<< MemberName << int(OpKind == tok::arrow));
}
// Handle pseudo-destructors (C++ [expr.pseudo]). Since anything referring
// into a record type was handled above, any destructor we see here is a
// pseudo-destructor.
if (MemberName.getNameKind() == DeclarationName::CXXDestructorName) {
// 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.
if (!BaseType->isScalarType())
return Owned(Diag(OpLoc, diag::err_pseudo_dtor_base_not_scalar)
<< BaseType << BaseExpr->getSourceRange());
// [...] The type designated by the pseudo-destructor-name shall be the
// same as the object type.
if (!MemberName.getCXXNameType()->isDependentType() &&
!Context.hasSameUnqualifiedType(BaseType, MemberName.getCXXNameType()))
return Owned(Diag(OpLoc, diag::err_pseudo_dtor_type_mismatch)
<< BaseType << MemberName.getCXXNameType()
<< BaseExpr->getSourceRange() << SourceRange(MemberLoc));
// [...] 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.
//
// FIXME: DPG can't see any way to trigger this particular clause, so it
// isn't checked here.
// FIXME: We've lost the precise spelling of the type by going through
// DeclarationName. Can we do better?
return Owned(new (Context) CXXPseudoDestructorExpr(Context, BaseExpr,
OpKind == tok::arrow,
OpLoc,
(NestedNameSpecifier *)(SS? SS->getScopeRep() : 0),
SS? SS->getRange() : SourceRange(),
MemberName.getCXXNameType(),
MemberLoc));
}
// Handle properties on ObjC 'Class' types.
if (OpKind == tok::period && BaseType->isObjCClassType()) {
// Also must look for a getter name which uses property syntax.
@ -2684,6 +2725,25 @@ Sema::ActOnCallExpr(Scope *S, ExprArg fn, SourceLocation LParenLoc,
DeclarationName UnqualifiedName;
if (getLangOptions().CPlusPlus) {
// If this is a pseudo-destructor expression, build the call immediately.
if (isa<CXXPseudoDestructorExpr>(Fn)) {
if (NumArgs > 0) {
// Pseudo-destructor calls should not have any arguments.
Diag(Fn->getLocStart(), diag::err_pseudo_dtor_call_with_args)
<< CodeModificationHint::CreateRemoval(
SourceRange(Args[0]->getLocStart(),
Args[NumArgs-1]->getLocEnd()));
for (unsigned I = 0; I != NumArgs; ++I)
Args[I]->Destroy(Context);
NumArgs = 0;
}
return Owned(new (Context) CallExpr(Context, Fn, 0, 0, Context.VoidTy,
RParenLoc));
}
// Determine whether this is a dependent call inside a C++ template,
// in which case we won't do any semantic analysis now.
// FIXME: Will need to cache the results of name lookup (including ADL) in

Просмотреть файл

@ -1821,26 +1821,19 @@ Sema::ActOnDestructorReferenceExpr(Scope *S, ExprArg Base,
if (SS && SS->isInvalid())
return ExprError();
Expr *BaseExpr = (Expr *)Base.get();
QualType BaseType;
if (SS && isUnknownSpecialization(*SS))
BaseType = Context.getTypenameType((NestedNameSpecifier *)SS->getScopeRep(),
ClassName);
else {
TypeTy *BaseTy = getTypeName(*ClassName, ClassNameLoc, S, SS);
if (!BaseTy) {
Diag(ClassNameLoc, diag::err_ident_in_pseudo_dtor_not_a_type)
<< ClassName;
return ExprError();
}
if (BaseExpr->isTypeDependent() ||
(SS && isDependentScopeSpecifier(*SS))) {
// FIXME: Return an unresolved ref expr.
return ExprError();
}
TypeTy *BaseTy = getTypeName(*ClassName, ClassNameLoc, S, SS);
if (!BaseTy) {
Diag(ClassNameLoc, diag::err_ident_in_pseudo_dtor_not_a_type)
<< ClassName;
return ExprError();
}
QualType BaseType = GetTypeFromParser(BaseTy);
if (!BaseType->isRecordType()) {
Diag(ClassNameLoc, diag::err_type_in_pseudo_dtor_not_a_class_type)
<< BaseType;
return ExprError();
BaseType = GetTypeFromParser(BaseTy);
}
CanQualType CanBaseType = Context.getCanonicalType(BaseType);

Просмотреть файл

@ -783,6 +783,36 @@ public:
return getSema().ActOnParenExpr(LParen, RParen, move(SubExpr));
}
/// \brief Build a new pseudo-destructor expression.
///
/// By default, performs semantic analysis to build the new expression.
/// Subclasses may override this routine to provide different behavior.
OwningExprResult RebuildCXXPseudoDestructorExpr(ExprArg Base,
SourceLocation OperatorLoc,
bool isArrow,
SourceLocation DestroyedTypeLoc,
QualType DestroyedType,
NestedNameSpecifier *Qualifier,
SourceRange QualifierRange) {
CXXScopeSpec SS;
if (Qualifier) {
SS.setRange(QualifierRange);
SS.setScopeRep(Qualifier);
}
DeclarationName Name
= SemaRef.Context.DeclarationNames.getCXXDestructorName(
SemaRef.Context.getCanonicalType(DestroyedType));
return getSema().BuildMemberReferenceExpr(/*Scope=*/0, move(Base),
OperatorLoc,
isArrow? tok::arrow : tok::period,
DestroyedTypeLoc,
Name,
Sema::DeclPtrTy::make((Decl *)0),
&SS);
}
/// \brief Build a new unary operator expression.
///
/// By default, performs semantic analysis to build the new expression.
@ -3812,6 +3842,43 @@ TreeTransform<Derived>::TransformCXXDeleteExpr(CXXDeleteExpr *E) {
move(Operand));
}
template<typename Derived>
Sema::OwningExprResult
TreeTransform<Derived>::TransformCXXPseudoDestructorExpr(
CXXPseudoDestructorExpr *E) {
OwningExprResult Base = getDerived().TransformExpr(E->getBase());
if (Base.isInvalid())
return SemaRef.ExprError();
NestedNameSpecifier *Qualifier
= getDerived().TransformNestedNameSpecifier(E->getQualifier(),
E->getQualifierRange());
if (E->getQualifier() && !Qualifier)
return SemaRef.ExprError();
QualType DestroyedType;
{
TemporaryBase Rebase(*this, E->getDestroyedTypeLoc(), DeclarationName());
DestroyedType = getDerived().TransformType(E->getDestroyedType());
if (DestroyedType.isNull())
return SemaRef.ExprError();
}
if (!getDerived().AlwaysRebuild() &&
Base.get() == E->getBase() &&
Qualifier == E->getQualifier() &&
DestroyedType == E->getDestroyedType())
return SemaRef.Owned(E->Retain());
return getDerived().RebuildCXXPseudoDestructorExpr(move(Base),
E->getOperatorLoc(),
E->isArrow(),
E->getDestroyedTypeLoc(),
DestroyedType,
Qualifier,
E->getQualifierRange());
}
template<typename Derived>
Sema::OwningExprResult
TreeTransform<Derived>::TransformUnresolvedFunctionNameExpr(

Просмотреть файл

@ -4,10 +4,31 @@ struct A {};
enum Foo { F };
typedef Foo Bar;
void f(A* a) {
typedef int Integer;
void g();
namespace N {
typedef Foo Wibble;
}
void f(A* a, Foo *f, int *i) {
a->~A();
a->A::~A();
a->~foo(); // expected-error{{identifier 'foo' in pseudo-destructor expression does not name a type}}
a->~Bar(); // expected-error{{type 'Bar' (aka 'enum Foo') in pseudo-destructor expression is not a class type}}
// FIXME: the type printed below isn't wonderful
a->~Bar(); // expected-error{{no member named}}
f->~Bar();
f->~Foo();
i->~Bar(); // expected-error{{does not match}}
g().~Bar(); // expected-error{{non-scalar}}
f->::~Bar();
f->N::~Wibble();
f->::~Bar(17, 42); // expected-error{{cannot have any arguments}}
}

Просмотреть файл

@ -59,3 +59,19 @@ void test_convert(X2 x2) {
convert<int>(x2);
convert<long>(x2); // expected-note{{instantiation}}
}
template<typename T>
void destruct(T* ptr) {
ptr->~T();
}
template<typename T>
void destruct_intptr(int *ip) {
ip->~T();
}
void test_destruct(X2 *x2p, int *ip) {
destruct(x2p);
destruct(ip);
destruct_intptr<int>(ip);
}