[cfe-commits] r151117 - in /cfe/trunk: include/clang/AST/ExprCXX.h include/clang/Sema/Sema.h lib/Parse/ParseDeclCXX.cpp lib/Sema/Sema.cpp lib/Sema/SemaExpr.cpp lib/Sema/SemaExprCXX.cpp lib/Sema/SemaOverload.cpp lib/Sema/TreeTransform.h test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.simple/p5-cxx0x.cpp test/SemaCXX/overload-call.cpp

Richard Smith richard-llvm at metafoo.co.uk
Tue Feb 21 18:04:18 PST 2012


Author: rsmith
Date: Tue Feb 21 20:04:18 2012
New Revision: 151117

URL: http://llvm.org/viewvc/llvm-project?rev=151117&view=rev
Log:
Implement C++11 [expr.call]p11: If the operand to a decltype-specifier is a
function call (or a comma expression with a function call on its right-hand
side), possibly parenthesized, then the return type is not required to be
complete and a temporary is not bound. Other subexpressions inside a decltype
expression do not get this treatment.

This is implemented by deferring the relevant checks for all calls immediately
within a decltype expression, then, when the expression is fully-parsed,
checking the relevant constraints and stripping off any top-level temporary
binding.

Deferring the completion of the return type exposed a bug in overload
resolution where completion of the argument types was not attempted, which
is also fixed by this change.

Added:
    cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.simple/p5-cxx0x.cpp
Modified:
    cfe/trunk/include/clang/AST/ExprCXX.h
    cfe/trunk/include/clang/Sema/Sema.h
    cfe/trunk/lib/Parse/ParseDeclCXX.cpp
    cfe/trunk/lib/Sema/Sema.cpp
    cfe/trunk/lib/Sema/SemaExpr.cpp
    cfe/trunk/lib/Sema/SemaExprCXX.cpp
    cfe/trunk/lib/Sema/SemaOverload.cpp
    cfe/trunk/lib/Sema/TreeTransform.h
    cfe/trunk/test/SemaCXX/overload-call.cpp

Modified: cfe/trunk/include/clang/AST/ExprCXX.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/ExprCXX.h?rev=151117&r1=151116&r2=151117&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/ExprCXX.h (original)
+++ cfe/trunk/include/clang/AST/ExprCXX.h Tue Feb 21 20:04:18 2012
@@ -736,6 +736,9 @@
                               const CXXDestructorDecl *Destructor);
 
   const CXXDestructorDecl *getDestructor() const { return Destructor; }
+  void setDestructor(const CXXDestructorDecl *Dtor) {
+    Destructor = Dtor;
+  }
 };
 
 /// \brief Represents binding an expression to a temporary.

Modified: cfe/trunk/include/clang/Sema/Sema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/Sema.h?rev=151117&r1=151116&r2=151117&view=diff
==============================================================================
--- cfe/trunk/include/clang/Sema/Sema.h (original)
+++ cfe/trunk/include/clang/Sema/Sema.h Tue Feb 21 20:04:18 2012
@@ -59,6 +59,7 @@
   class BlockDecl;
   class CXXBasePath;
   class CXXBasePaths;
+  class CXXBindTemporaryExpr;
   typedef SmallVector<CXXBaseSpecifier*, 4> CXXCastPath;
   class CXXConstructorDecl;
   class CXXConversionDecl;
@@ -562,6 +563,9 @@
     /// \brief Whether the enclosing context needed a cleanup.
     bool ParentNeedsCleanups;
 
+    /// \brief Whether we are in a decltype expression.
+    bool IsDecltype;
+
     /// \brief The number of active cleanup objects when we entered
     /// this expression evaluation context.
     unsigned NumCleanupObjects;
@@ -583,13 +587,22 @@
     /// This mangling information is allocated lazily, since most contexts
     /// do not have lambda expressions.
     LambdaMangleContext *LambdaMangle;
+
+    /// \brief If we are processing a decltype type, a set of call expressions
+    /// for which we have deferred checking the completeness of the return type.
+    llvm::SmallVector<CallExpr*, 8> DelayedDecltypeCalls;
+
+    /// \brief If we are processing a decltype type, a set of temporary binding
+    /// expressions for which we have deferred checking the destructor.
+    llvm::SmallVector<CXXBindTemporaryExpr*, 8> DelayedDecltypeBinds;
     
     ExpressionEvaluationContextRecord(ExpressionEvaluationContext Context,
                                       unsigned NumCleanupObjects,
                                       bool ParentNeedsCleanups,
-                                      Decl *LambdaContextDecl)
+                                      Decl *LambdaContextDecl,
+                                      bool IsDecltype)
       : Context(Context), ParentNeedsCleanups(ParentNeedsCleanups),
-        NumCleanupObjects(NumCleanupObjects), 
+        IsDecltype(IsDecltype), NumCleanupObjects(NumCleanupObjects),
         LambdaContextDecl(LambdaContextDecl), LambdaMangle() { }
     
     ~ExpressionEvaluationContextRecord() {
@@ -2315,7 +2328,8 @@
                              Expr **Args, unsigned NumArgs);
 
   void PushExpressionEvaluationContext(ExpressionEvaluationContext NewContext,
-                                       Decl *LambdaContextDecl = 0);
+                                       Decl *LambdaContextDecl = 0,
+                                       bool IsDecltype = false);
 
   void PopExpressionEvaluationContext();
 
@@ -3446,6 +3460,8 @@
                                    bool EnteringContext,
                                    CXXScopeSpec &SS);
 
+  ExprResult ActOnDecltypeExpression(Expr *E);
+
   bool ActOnCXXNestedNameSpecifierDecltype(CXXScopeSpec &SS,
                                            const DeclSpec &DS, 
                                            SourceLocation ColonColonLoc);
@@ -6572,9 +6588,11 @@
 public:
   EnterExpressionEvaluationContext(Sema &Actions,
                                    Sema::ExpressionEvaluationContext NewContext,
-                                   Decl *LambdaContextDecl = 0)
+                                   Decl *LambdaContextDecl = 0,
+                                   bool IsDecltype = false)
     : Actions(Actions) {
-    Actions.PushExpressionEvaluationContext(NewContext, LambdaContextDecl);
+    Actions.PushExpressionEvaluationContext(NewContext, LambdaContextDecl,
+                                            IsDecltype);
   }
 
   ~EnterExpressionEvaluationContext() {

Modified: cfe/trunk/lib/Parse/ParseDeclCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseDeclCXX.cpp?rev=151117&r1=151116&r2=151117&view=diff
==============================================================================
--- cfe/trunk/lib/Parse/ParseDeclCXX.cpp (original)
+++ cfe/trunk/lib/Parse/ParseDeclCXX.cpp Tue Feb 21 20:04:18 2012
@@ -667,8 +667,8 @@
 
     // C++0x [dcl.type.simple]p4:
     //   The operand of the decltype specifier is an unevaluated operand.
-    EnterExpressionEvaluationContext Unevaluated(Actions,
-                                                 Sema::Unevaluated);
+    EnterExpressionEvaluationContext Unevaluated(Actions, Sema::Unevaluated,
+                                                 0, /*IsDecltype=*/true);
     Result = ParseExpression();
     if (Result.isInvalid()) {
       SkipUntil(tok::r_paren, true, true);
@@ -685,6 +685,12 @@
       return T.getCloseLocation();
     }
 
+    Result = Actions.ActOnDecltypeExpression(Result.take());
+    if (Result.isInvalid()) {
+      DS.SetTypeSpecError();
+      return T.getCloseLocation();
+    }
+
     EndLoc = T.getCloseLocation();
   }
 

Modified: cfe/trunk/lib/Sema/Sema.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/Sema.cpp?rev=151117&r1=151116&r2=151117&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/Sema.cpp (original)
+++ cfe/trunk/lib/Sema/Sema.cpp Tue Feb 21 20:04:18 2012
@@ -111,7 +111,8 @@
                                        &Context);
 
   ExprEvalContexts.push_back(
-        ExpressionEvaluationContextRecord(PotentiallyEvaluated, 0, false, 0));
+        ExpressionEvaluationContextRecord(PotentiallyEvaluated, 0,
+                                          false, 0, false));
 
   FunctionScopes.push_back(new FunctionScopeInfo(Diags));
 }

Modified: cfe/trunk/lib/Sema/SemaExpr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExpr.cpp?rev=151117&r1=151116&r2=151117&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaExpr.cpp (original)
+++ cfe/trunk/lib/Sema/SemaExpr.cpp Tue Feb 21 20:04:18 2012
@@ -9277,12 +9277,14 @@
 
 void
 Sema::PushExpressionEvaluationContext(ExpressionEvaluationContext NewContext,
-                                      Decl *LambdaContextDecl) {
+                                      Decl *LambdaContextDecl,
+                                      bool IsDecltype) {
   ExprEvalContexts.push_back(
              ExpressionEvaluationContextRecord(NewContext,
                                                ExprCleanupObjects.size(),
                                                ExprNeedsCleanups,
-                                               LambdaContextDecl));
+                                               LambdaContextDecl,
+                                               IsDecltype));
   ExprNeedsCleanups = false;
   if (!MaybeODRUseExprs.empty())
     std::swap(MaybeODRUseExprs, ExprEvalContexts.back().SavedMaybeODRUseExprs);
@@ -10280,6 +10282,13 @@
   if (ReturnType->isVoidType() || !ReturnType->isIncompleteType())
     return false;
 
+  // If we're inside a decltype's expression, don't check for a valid return
+  // type or construct temporaries until we know whether this is the last call.
+  if (ExprEvalContexts.back().IsDecltype) {
+    ExprEvalContexts.back().DelayedDecltypeCalls.push_back(CE);
+    return false;
+  }
+
   PartialDiagnostic Note =
     FD ? PDiag(diag::note_function_with_incomplete_return_type_declared_here)
     << FD->getDeclName() : PDiag();

Modified: cfe/trunk/lib/Sema/SemaExprCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExprCXX.cpp?rev=151117&r1=151116&r2=151117&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaExprCXX.cpp (original)
+++ cfe/trunk/lib/Sema/SemaExprCXX.cpp Tue Feb 21 20:04:18 2012
@@ -4315,11 +4315,14 @@
     }
   }
 
-  // That should be enough to guarantee that this type is complete.
+  // That should be enough to guarantee that this type is complete, if we're
+  // not processing a decltype expression.
   CXXRecordDecl *RD = cast<CXXRecordDecl>(RT->getDecl());
   if (RD->isInvalidDecl() || RD->isDependentContext())
     return Owned(E);
-  CXXDestructorDecl *Destructor = LookupDestructor(RD);
+
+  bool IsDecltype = ExprEvalContexts.back().IsDecltype;
+  CXXDestructorDecl *Destructor = IsDecltype ? 0 : LookupDestructor(RD);
 
   if (Destructor) {
     MarkFunctionReferenced(E->getExprLoc(), Destructor);
@@ -4327,18 +4330,22 @@
                           PDiag(diag::err_access_dtor_temp)
                             << E->getType());
     DiagnoseUseOfDecl(Destructor, E->getExprLoc());
-  }
 
-  // If destructor is trivial, we can avoid the extra copy.
-  if (Destructor->isTrivial())
-    return Owned(E);
+    // If destructor is trivial, we can avoid the extra copy.
+    if (Destructor->isTrivial())
+      return Owned(E);
 
-  if (Destructor)
     // We need a cleanup, but we don't need to remember the temporary.
     ExprNeedsCleanups = true;
+  }
 
   CXXTemporary *Temp = CXXTemporary::Create(Context, Destructor);
-  return Owned(CXXBindTemporaryExpr::Create(Context, Temp, E));
+  CXXBindTemporaryExpr *Bind = CXXBindTemporaryExpr::Create(Context, Temp, E);
+
+  if (IsDecltype)
+    ExprEvalContexts.back().DelayedDecltypeBinds.push_back(Bind);
+
+  return Owned(Bind);
 }
 
 ExprResult
@@ -4390,6 +4397,95 @@
   return MaybeCreateExprWithCleanups(E);
 }
 
+/// Process the expression contained within a decltype. For such expressions,
+/// certain semantic checks on temporaries are delayed until this point, and
+/// are omitted for the 'topmost' call in the decltype expression. If the
+/// topmost call bound a temporary, strip that temporary off the expression.
+ExprResult Sema::ActOnDecltypeExpression(Expr *E) {
+  ExpressionEvaluationContextRecord &Rec = ExprEvalContexts.back();
+  assert(Rec.IsDecltype && "not in a decltype expression");
+
+  // C++11 [expr.call]p11:
+  //   If a function call is a prvalue of object type,
+  // -- if the function call is either
+  //   -- the operand of a decltype-specifier, or
+  //   -- the right operand of a comma operator that is the operand of a
+  //      decltype-specifier,
+  //   a temporary object is not introduced for the prvalue.
+
+  // Recursively rebuild ParenExprs and comma expressions to strip out the
+  // outermost CXXBindTemporaryExpr, if any.
+  if (ParenExpr *PE = dyn_cast<ParenExpr>(E)) {
+    ExprResult SubExpr = ActOnDecltypeExpression(PE->getSubExpr());
+    if (SubExpr.isInvalid())
+      return ExprError();
+    if (SubExpr.get() == PE->getSubExpr())
+      return Owned(E);
+    return ActOnParenExpr(PE->getLParen(), PE->getRParen(), SubExpr.take());
+  }
+  if (BinaryOperator *BO = dyn_cast<BinaryOperator>(E)) {
+    if (BO->getOpcode() == BO_Comma) {
+      ExprResult RHS = ActOnDecltypeExpression(BO->getRHS());
+      if (RHS.isInvalid())
+        return ExprError();
+      if (RHS.get() == BO->getRHS())
+        return Owned(E);
+      return Owned(new (Context) BinaryOperator(BO->getLHS(), RHS.take(),
+                                                BO_Comma, BO->getType(),
+                                                BO->getValueKind(),
+                                                BO->getObjectKind(),
+                                                BO->getOperatorLoc()));
+    }
+  }
+
+  CXXBindTemporaryExpr *TopBind = dyn_cast<CXXBindTemporaryExpr>(E);
+  if (TopBind)
+    E = TopBind->getSubExpr();
+
+  // Disable the special decltype handling now.
+  Rec.IsDecltype = false;
+
+  // Perform the semantic checks we delayed until this point.
+  CallExpr *TopCall = dyn_cast<CallExpr>(E);
+  for (unsigned I = 0, N = Rec.DelayedDecltypeCalls.size(); I != N; ++I) {
+    CallExpr *Call = Rec.DelayedDecltypeCalls[I];
+    if (Call == TopCall)
+      continue;
+
+    if (CheckCallReturnType(Call->getCallReturnType(),
+                            Call->getSourceRange().getBegin(),
+                            Call, Call->getDirectCallee()))
+      return ExprError();
+  }
+
+  // Now all relevant types are complete, check the destructors are accessible
+  // and non-deleted, and annotate them on the temporaries.
+  for (unsigned I = 0, N = Rec.DelayedDecltypeBinds.size(); I != N; ++I) {
+    CXXBindTemporaryExpr *Bind = Rec.DelayedDecltypeBinds[I];
+    if (Bind == TopBind)
+      continue;
+
+    CXXTemporary *Temp = Bind->getTemporary();
+
+    CXXRecordDecl *RD =
+      Bind->getType()->getBaseElementTypeUnsafe()->getAsCXXRecordDecl();
+    CXXDestructorDecl *Destructor = LookupDestructor(RD);
+    Temp->setDestructor(Destructor);
+
+    MarkFunctionReferenced(E->getExprLoc(), Destructor);
+    CheckDestructorAccess(E->getExprLoc(), Destructor,
+                          PDiag(diag::err_access_dtor_temp)
+                            << E->getType());
+    DiagnoseUseOfDecl(Destructor, E->getExprLoc());
+
+    // We need a cleanup, but we don't need to remember the temporary.
+    ExprNeedsCleanups = true;
+  }
+
+  // Possibly strip off the top CXXBindTemporaryExpr.
+  return Owned(E);
+}
+
 ExprResult
 Sema::ActOnStartCXXMemberReference(Scope *S, Expr *Base, SourceLocation OpLoc,
                                    tok::TokenKind OpKind, ParsedType &ObjectType,

Modified: cfe/trunk/lib/Sema/SemaOverload.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaOverload.cpp?rev=151117&r1=151116&r2=151117&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaOverload.cpp (original)
+++ cfe/trunk/lib/Sema/SemaOverload.cpp Tue Feb 21 20:04:18 2012
@@ -740,6 +740,8 @@
 /// Return true on unrecoverable error.
 static bool checkPlaceholderForOverload(Sema &S, Expr *&E,
                                         UnbridgedCastsSet *unbridgedCasts = 0) {
+  S.RequireCompleteType(E->getExprLoc(), E->getType(), 0);
+
   if (const BuiltinType *placeholder =  E->getType()->getAsPlaceholderType()) {
     // We can't handle overloaded expressions here because overload
     // resolution might reasonably tweak them.

Modified: cfe/trunk/lib/Sema/TreeTransform.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/TreeTransform.h?rev=151117&r1=151116&r2=151117&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/TreeTransform.h (original)
+++ cfe/trunk/lib/Sema/TreeTransform.h Tue Feb 21 20:04:18 2012
@@ -4305,12 +4305,17 @@
   const DecltypeType *T = TL.getTypePtr();
 
   // decltype expressions are not potentially evaluated contexts
-  EnterExpressionEvaluationContext Unevaluated(SemaRef, Sema::Unevaluated);
+  EnterExpressionEvaluationContext Unevaluated(SemaRef, Sema::Unevaluated, 0,
+                                               /*IsDecltype=*/ true);
 
   ExprResult E = getDerived().TransformExpr(T->getUnderlyingExpr());
   if (E.isInvalid())
     return QualType();
 
+  E = getSema().ActOnDecltypeExpression(E.take());
+  if (E.isInvalid())
+    return QualType();
+
   QualType Result = TL.getType();
   if (getDerived().AlwaysRebuild() ||
       E.get() != T->getUnderlyingExpr()) {

Added: cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.simple/p5-cxx0x.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.simple/p5-cxx0x.cpp?rev=151117&view=auto
==============================================================================
--- cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.simple/p5-cxx0x.cpp (added)
+++ cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.type.simple/p5-cxx0x.cpp Tue Feb 21 20:04:18 2012
@@ -0,0 +1,108 @@
+// RUN: %clang_cc1 -std=c++11 -verify %s
+
+namespace std_example {
+
+template<class T> struct A { ~A() = delete; }; // expected-note {{deleted here}}
+template<class T> auto h() -> A<T>;
+template<class T> auto i(T) -> T;
+template<class T> auto f(T) -> decltype(i(h<T>())); // #1
+template<class T> auto f(T) -> void; // #2
+auto g() -> void {
+  f(42); // ok, calls #2, since #1 is not viable.
+}
+template<class T> auto q(T) -> decltype((h<T>()));
+void r() {
+  // Deduction against q succeeds, but results in a temporary which can't be
+  // destroyed.
+  q(42); // expected-error {{attempt to use a deleted function}}
+}
+
+}
+
+class PD {
+  friend struct A;
+  ~PD(); // expected-note 4{{here}}
+public:
+  typedef int n;
+};
+struct DD {
+  ~DD() = delete; // expected-note 2{{here}}
+  typedef int n;
+};
+
+struct A {
+  decltype(PD()) s; // ok
+  decltype(PD())::n n; // ok
+  decltype(DD()) *p = new decltype(DD()); // ok
+};
+
+// Two errors here: one for the decltype, one for the variable.
+decltype(PD(), PD()) pd1; // expected-error 2{{private destructor}}
+decltype(DD(), DD()) dd1; // expected-error 2{{deleted function}}
+
+decltype(((13, ((DD())))))::n dd_parens; // ok
+decltype(((((42)), PD())))::n pd_parens_comma; // ok
+
+// Ensure parens aren't stripped from a decltype node.
+extern decltype(PD()) pd_ref; // ok
+decltype((pd_ref)) pd_ref3 = pd_ref; // ok, PD &
+decltype(pd_ref) pd_ref2 = pd_ref; // expected-error {{private destructor}}
+
+namespace libcxx_example {
+  struct nat {
+    nat() = delete;
+    nat(const nat&) = delete;
+    nat &operator=(const nat&) = delete;
+    ~nat() = delete;
+  };
+  struct any {
+    any(...);
+  };
+
+  template<typename T, typename U> struct is_same { static const bool value = false; };
+  template<typename T> struct is_same<T, T> { static const bool value = true; };
+
+  template<typename T> T declval();
+
+  void swap(int &a, int &b);
+  nat swap(any, any);
+
+  template<typename T> struct swappable {
+    typedef decltype(swap(declval<T&>(), declval<T&>())) type;
+    static const bool value = !is_same<type, nat>::value;
+    constexpr operator bool() { return value; }
+  };
+
+  static_assert(swappable<int>(), "");
+  static_assert(!swappable<const int>(), "");
+}
+
+namespace RequireCompleteType {
+  template<int N, bool OK> struct S {
+    static_assert(OK, "boom!"); // expected-error 2{{boom!}}
+  };
+
+  template<typename T> T make();
+  template<int N, bool OK> S<N, OK> make();
+  void consume(...);
+
+  decltype(make<0, false>()) *p1; // ok
+  decltype((make<1, false>())) *p2; // ok
+
+  // A complete type is required here in order to detect an overloaded 'operator,'.
+  decltype(123, make<2, false>()) *p3; // expected-note {{here}}
+
+  decltype(consume(make<3, false>())) *p4; // expected-note {{here}}
+
+  decltype(make<decltype(make<4, false>())>()) *p5; // ok
+}
+
+namespace Overload {
+  DD operator+(PD &a, PD &b);
+  decltype(PD()) *pd_ptr;
+  decltype(*pd_ptr + *pd_ptr) *dd_ptr; // ok
+
+  decltype(0, *pd_ptr) pd_ref2 = pd_ref; // ok
+  DD operator,(int a, PD b);
+  decltype(0, *pd_ptr) *dd_ptr2; // expected-error {{private destructor}}
+}

Modified: cfe/trunk/test/SemaCXX/overload-call.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/overload-call.cpp?rev=151117&r1=151116&r2=151117&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/overload-call.cpp (original)
+++ cfe/trunk/test/SemaCXX/overload-call.cpp Tue Feb 21 20:04:18 2012
@@ -534,3 +534,12 @@
     int &ir = (&foo)(0);
   }
 }
+
+namespace IncompleteArg {
+  // Ensure that overload resolution attempts to complete argument types.
+  template<typename T> struct S {
+    friend int f(const S&);
+  };
+  extern S<int> s;
+  int k = f(s);
+}





More information about the cfe-commits mailing list