]> granicus.if.org Git - clang/commitdiff
Implement AST, semantics, and CodeGen for C++ pseudo-destructor
authorDouglas Gregor <dgregor@apple.com>
Fri, 4 Sep 2009 17:36:40 +0000 (17:36 +0000)
committerDouglas Gregor <dgregor@apple.com>
Fri, 4 Sep 2009 17:36:40 +0000 (17:36 +0000)
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

13 files changed:
include/clang/AST/ExprCXX.h
include/clang/AST/StmtNodes.def
include/clang/Basic/DiagnosticSemaKinds.td
lib/AST/ExprCXX.cpp
lib/AST/StmtPrinter.cpp
lib/AST/StmtProfile.cpp
lib/CodeGen/CGExpr.cpp
lib/CodeGen/CGExprScalar.cpp
lib/Sema/SemaExpr.cpp
lib/Sema/SemaExprCXX.cpp
lib/Sema/TreeTransform.h
test/SemaCXX/pseudo-destructors.cpp
test/SemaTemplate/member-access-expr.cpp

index f450834bc47ec7d8c73e43a3237f19e29d7b44ad..791d94ded0e6f72c56333a6901be17d922e9ef22 100644 (file)
@@ -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.
 ///
index af345991f1e69ddc406ffd4579616366ecac4d92..8d7e4b5fc0a3f9143c714965b23c988b12818147 100644 (file)
@@ -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)
index 1ef4d7247cb34e0e37bd8a0e3909919b80f51fa3..35d91f7ad0c2ced3455f6398b4eaac747b3105aa 100644 (file)
@@ -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">;
index 006f69890ab9ac70b76738c6b09decde4d20cee6..0ffb15287219a980e8107083550a7c5e4bde7375 100644 (file)
@@ -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(); 
index cde4cdc416a78cf40acb569348f5dc9fd7eb3aba..317486cd71201d06afce20e3d1e1c00a746257cc 100644 (file)
@@ -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();
 }
index 19d313b420717596410d3d8a1d5eb5bbf29f049e..806eb8de81df145abcbefaa5850634fdfab67b7d 100644 (file)
@@ -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);
index 5c8dfa46690cad9623a0b2b34c951011652191a6..4a8253d9cd9bec11ac2cc9a66a4228894ae0aaf4 100644 (file)
@@ -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);
index 4496c538d3c05efda6fe6585bc57fb3205284682..8732dc91309a68a386d3e3554025e0df3e80764f 100644 (file)
@@ -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
index 720217a9103af3184c732d8909cb6c8a0039fd38..58d6a0dbac7bb02898a951ced57417cd7599545d 100644 (file)
@@ -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
index c62159d001ddcccc8809725a5dcc76eccd7f3bf1..4d49f87a9a830c76c64bbd74138926e5248023e9 100644 (file)
@@ -1821,26 +1821,19 @@ Sema::ActOnDestructorReferenceExpr(Scope *S, ExprArg Base,
   if (SS && SS->isInvalid())
     return ExprError();
 
-  Expr *BaseExpr = (Expr *)Base.get();
-  
-  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;
+  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();
+    }
   
-  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);
index 2faaa44daf9cd67d760cf14dd3b8cb682a78464e..22e01ab581411711abf44c596256a77ccacd8846 100644 (file)
@@ -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(
index 8c41f2266d81b46ae2e1c3b240c5b14bf56f372b..f1fa331aa2434a98a79de79ecdde5e2ba3eb35b5 100644 (file)
@@ -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}}
 }
index 40dbffe0cf8362f465934256dd2d82a32144d75e..f4922e8ff5208e5bdbd84b461ba74b949299537e 100644 (file)
@@ -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);
+}
\ No newline at end of file