]> granicus.if.org Git - clang/commitdiff
For P0784R7: add support for new (std::nothrow).
authorRichard Smith <richard-llvm@metafoo.co.uk>
Fri, 27 Sep 2019 01:26:49 +0000 (01:26 +0000)
committerRichard Smith <richard-llvm@metafoo.co.uk>
Fri, 27 Sep 2019 01:26:49 +0000 (01:26 +0000)
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@373037 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/AST/Type.h
lib/AST/Decl.cpp
lib/AST/ExprConstant.cpp
lib/AST/Type.cpp
test/SemaCXX/constant-expression-cxx2a.cpp

index fcc74198ab0be3316dd9e382c533f00fb868f86d..3968e1aaf4c862e28b15c28c0a47b65d1b0e0df0 100644 (file)
@@ -2056,6 +2056,7 @@ public:
   bool isCARCBridgableType() const;
   bool isTemplateTypeParmType() const;          // C++ template type parameter
   bool isNullPtrType() const;                   // C++11 std::nullptr_t
+  bool isNothrowT() const;                      // C++   std::nothrow_t
   bool isAlignValT() const;                     // C++17 std::align_val_t
   bool isStdByteType() const;                   // C++17 std::byte
   bool isAtomicType() const;                    // C11 _Atomic()
index 135b5f71c7e273f909be5ad0babce2c305405406..e95338138fe750a8e326841544f2189897451628 100644 (file)
@@ -2977,8 +2977,7 @@ bool FunctionDecl::isReplaceableGlobalAllocationFunction(bool *IsAligned) const
     Ty = Ty->getPointeeType();
     if (Ty.getCVRQualifiers() != Qualifiers::Const)
       return false;
-    const CXXRecordDecl *RD = Ty->getAsCXXRecordDecl();
-    if (RD && isNamed(RD, "nothrow_t") && RD->isInStdNamespace())
+    if (Ty->isNothrowT())
       Consume();
   }
 
index ed3d1fa905b19a96f9021ab4fbf7efdcec79986d..a2a6168ddbffba5d4a99b4d9b7fde0048506e524 100644 (file)
@@ -8043,6 +8043,9 @@ static bool EvaluateArrayNewInitList(EvalInfo &Info, LValue &This,
                                      QualType AllocType);
 
 bool PointerExprEvaluator::VisitCXXNewExpr(const CXXNewExpr *E) {
+  if (!Info.getLangOpts().CPlusPlus2a)
+    Info.CCEDiag(E, diag::note_constexpr_new);
+
   // We cannot speculatively evaluate a delete expression.
   if (Info.SpeculativeEvaluationDepth)
     return false;
@@ -8054,17 +8057,27 @@ bool PointerExprEvaluator::VisitCXXNewExpr(const CXXNewExpr *E) {
     return false;
   }
 
-  // FIXME: There is no restriction on this, but it's not clear that it
-  // makes any sense. We get here for cases such as:
-  //
-  //   new (std::align_val_t{N}) X(int)
-  //
-  // (which should presumably be valid only if N is a multiple of
-  // alignof(int).
-  if (E->getNumPlacementArgs())
-    return Error(E, diag::note_constexpr_new_placement);
-  if (!Info.getLangOpts().CPlusPlus2a)
-    Info.CCEDiag(E, diag::note_constexpr_new);
+  bool IsNothrow = false;
+  if (E->getNumPlacementArgs()) {
+    // The only new-placement list we support is of the form (std::nothrow).
+    //
+    // FIXME: There is no restriction on this, but it's not clear that any
+    // other form makes any sense. We get here for cases such as:
+    //
+    //   new (std::align_val_t{N}) X(int)
+    //
+    // (which should presumably be valid only if N is a multiple of
+    // alignof(int), and in any case can't be deallocated unless N is
+    // alignof(X) and X has new-extended alignment).
+    if (E->getNumPlacementArgs() != 1 ||
+        !E->getPlacementArg(0)->getType()->isNothrowT())
+      return Error(E, diag::note_constexpr_new_placement);
+
+    LValue Nothrow;
+    if (!EvaluateLValue(E->getPlacementArg(0), Nothrow, Info))
+      return false;
+    IsNothrow = true;
+  }
 
   const Expr *Init = E->getInitializer();
   const InitListExpr *ResizedArrayILE = nullptr;
@@ -8087,6 +8100,9 @@ bool PointerExprEvaluator::VisitCXXNewExpr(const CXXNewExpr *E) {
     //   -- [...] its value before converting to size_t [or] applying the
     //      second standard conversion sequence is less than zero
     if (ArrayBound.isSigned() && ArrayBound.isNegative()) {
+      if (IsNothrow)
+        return ZeroInitialization(E);
+
       Info.FFDiag(*ArraySize, diag::note_constexpr_new_negative)
           << ArrayBound << (*ArraySize)->getSourceRange();
       return false;
@@ -8097,6 +8113,9 @@ bool PointerExprEvaluator::VisitCXXNewExpr(const CXXNewExpr *E) {
     if (ConstantArrayType::getNumAddressingBits(Info.Ctx, AllocType,
                                                 ArrayBound) >
         ConstantArrayType::getMaxSizeBits(Info.Ctx)) {
+      if (IsNothrow)
+        return ZeroInitialization(E);
+
       Info.FFDiag(*ArraySize, diag::note_constexpr_new_too_large)
         << ArrayBound << (*ArraySize)->getSourceRange();
       return false;
@@ -8114,6 +8133,9 @@ bool PointerExprEvaluator::VisitCXXNewExpr(const CXXNewExpr *E) {
       llvm::APInt InitBound = CAT->getSize().zextOrSelf(Bits);
       llvm::APInt AllocBound = ArrayBound.zextOrSelf(Bits);
       if (InitBound.ugt(AllocBound)) {
+        if (IsNothrow)
+          return ZeroInitialization(E);
+
         Info.FFDiag(*ArraySize, diag::note_constexpr_new_too_small)
             << AllocBound.toString(10, /*Signed=*/false)
             << InitBound.toString(10, /*Signed=*/false)
index 411cb749a7b8dcd6216e63cdf1f8f3b080862a3e..849ce4e62353c8f251778a6cb309d48dd6047a9b 100644 (file)
@@ -2490,6 +2490,15 @@ bool QualType::isCXX11PODType(const ASTContext &Context) const {
   return false;
 }
 
+bool Type::isNothrowT() const {
+  if (const auto *RD = getAsCXXRecordDecl()) {
+    IdentifierInfo *II = RD->getIdentifier();
+    if (II && II->isStr("nothrow_t") && RD->isInStdNamespace())
+      return true;
+  }
+  return false;
+}
+
 bool Type::isAlignValT() const {
   if (const auto *ET = getAs<EnumType>()) {
     IdentifierInfo *II = ET->getDecl()->getIdentifier();
index 73e49515368dc0970e0a01249385fec5885c4f11..648e24f7dba0bb3b5df9f7cf21aad05bb0f2f8f2 100644 (file)
@@ -926,6 +926,24 @@ namespace dynamic_alloc {
   static_assert(erroneous_array_bound(-1)); // expected-error {{constant expression}} expected-note {{in call}}
   static_assert(erroneous_array_bound(1LL << 62)); // expected-error {{constant expression}} expected-note {{in call}}
 
+  constexpr bool erroneous_array_bound_nothrow(long long n) {
+    int *p = new (std::nothrow) int[n];
+    bool result = p != 0;
+    delete[] p;
+    return result;
+  }
+  static_assert(erroneous_array_bound_nothrow(3));
+  static_assert(erroneous_array_bound_nothrow(0));
+  static_assert(!erroneous_array_bound_nothrow(-1));
+  static_assert(!erroneous_array_bound_nothrow(1LL << 62));
+
+  constexpr bool evaluate_nothrow_arg() {
+    bool ok = false;
+    delete new ((ok = true, std::nothrow)) int;
+    return ok;
+  }
+  static_assert(evaluate_nothrow_arg());
+
   constexpr void double_delete() { // expected-error {{never produces a constant expression}}
     int *p = new int;
     delete p;