зеркало из https://github.com/microsoft/clang-1.git
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:
Родитель
9efa767be8
Коммит
a71d819bb8
|
@ -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);
|
||||
}
|
Загрузка…
Ссылка в новой задаче