]> granicus.if.org Git - clang/commitdiff
Issue a warning if a throwing operator new or operator new[] returns a null
authorRichard Smith <richard-llvm@metafoo.co.uk>
Fri, 17 Jan 2014 02:09:33 +0000 (02:09 +0000)
committerRichard Smith <richard-llvm@metafoo.co.uk>
Fri, 17 Jan 2014 02:09:33 +0000 (02:09 +0000)
pointer, since this invokes undefined behavior. Based on a patch by Artyom
Skrobov! Handling of dependent exception specifications and some additional
testcases by me.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@199452 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/AST/Type.h
include/clang/Basic/DiagnosticGroups.td
include/clang/Basic/DiagnosticSemaKinds.td
lib/AST/Type.cpp
lib/Sema/SemaStmt.cpp
test/SemaCXX/new-delete.cpp
test/SemaCXX/new-null.cpp [new file with mode: 0644]
test/SemaCXX/warn-new-overaligned.cpp

index 13c4fe908cdaf6d2ddbcd48234730f3231124597..3a14c01f43fcfeb0d0dceb2c86ed6857a70f6d1c 100644 (file)
@@ -3036,15 +3036,10 @@ public:
       return 0;
     return reinterpret_cast<FunctionDecl * const *>(arg_type_end())[1];
   }
-  bool isNothrow(const ASTContext &Ctx) const {
-    ExceptionSpecificationType EST = getExceptionSpecType();
-    assert(EST != EST_Unevaluated && EST != EST_Uninstantiated);
-    if (EST == EST_DynamicNone || EST == EST_BasicNoexcept)
-      return true;
-    if (EST != EST_ComputedNoexcept)
-      return false;
-    return getNoexceptSpec(Ctx) == NR_Nothrow;
-  }
+  /// \brief Determine whether this function type has a non-throwing exception
+  /// specification. If this depends on template arguments, returns
+  /// \c ResultIfDependent.
+  bool isNothrow(const ASTContext &Ctx, bool ResultIfDependent = false) const;
 
   bool isVariadic() const { return Variadic; }
 
index bf77259ed4c05e14d5f76b79a8d593a07cd95370..e1320bea5a81192a1919a813767a71e443de72ad 100644 (file)
@@ -226,6 +226,7 @@ def : DiagGroup<"overflow">;
 def ForwardClassReceiver : DiagGroup<"receiver-forward-class">;
 def MethodAccess : DiagGroup<"objc-method-access">;
 def ObjCReceiver : DiagGroup<"receiver-expr">;
+def OperatorNewReturnsNull : DiagGroup<"new-returns-null">;
 def OverlengthStrings : DiagGroup<"overlength-strings">;
 def OverloadedVirtual : DiagGroup<"overloaded-virtual">;
 def PrivateExtern : DiagGroup<"private-extern">;
index 1de0c0fd119a3a22a353b681248fbc8c63f75660..77807cec83fcabbbc329dcb256d268084814793b 100644 (file)
@@ -5996,6 +5996,9 @@ def err_operator_new_delete_too_few_parameters : Error<
   "%0 must have at least one parameter">;
 def err_operator_new_delete_template_too_few_parameters : Error<
   "%0 template must have at least two parameters">;
+def warn_operator_new_returns_null : Warning<
+  "%0 should not return a null pointer unless it is declared 'throw()'"
+  "%select{| or 'noexcept'}1">, InGroup<OperatorNewReturnsNull>;
 
 def err_operator_new_dependent_param_type : Error<
   "%0 cannot take a dependent type as first parameter; "
index ff64a994e33b89da118bd1dc35da9ab042436005..3d4a7454f77f91f432e7bcae2641b47917a2f657 100644 (file)
@@ -1689,6 +1689,31 @@ FunctionProtoType::getNoexceptSpec(const ASTContext &ctx) const {
   return value.getBoolValue() ? NR_Nothrow : NR_Throw;
 }
 
+bool FunctionProtoType::isNothrow(const ASTContext &Ctx,
+                                  bool ResultIfDependent) const {
+  ExceptionSpecificationType EST = getExceptionSpecType();
+  assert(EST != EST_Unevaluated && EST != EST_Uninstantiated);
+  if (EST == EST_DynamicNone || EST == EST_BasicNoexcept)
+    return true;
+
+  if (EST == EST_Dynamic && ResultIfDependent == true) {
+    // A dynamic exception specification is throwing unless every exception
+    // type is an (unexpanded) pack expansion type.
+    for (unsigned I = 0, N = NumExceptions; I != N; ++I)
+      if (!getExceptionType(I)->getAs<PackExpansionType>())
+        return false;
+    return ResultIfDependent;
+  }
+
+  if (EST != EST_ComputedNoexcept)
+    return false;
+
+  NoexceptResult NR = getNoexceptSpec(Ctx);
+  if (NR == NR_Dependent)
+    return ResultIfDependent;
+  return NR == NR_Nothrow;
+}
+
 bool FunctionProtoType::isTemplateVariadic() const {
   for (unsigned ArgIdx = getNumArgs(); ArgIdx; --ArgIdx)
     if (isa<PackExpansionType>(getArgType(ArgIdx - 1)))
index e4fe9c324c0c9ec3860ca83ca6f779fac8a3048e..50a9d8111da38e4a2739f9c370ac9acdc3db3c0e 100644 (file)
@@ -2936,6 +2936,28 @@ Sema::ActOnReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) {
       }
 
       CheckReturnStackAddr(RetValExp, FnRetType, ReturnLoc);
+
+      // C++11 [basic.stc.dynamic.allocation]p4:
+      //   If an allocation function declared with a non-throwing
+      //   exception-specification fails to allocate storage, it shall return
+      //   a null pointer. Any other allocation function that fails to allocate
+      //   storage shall indicate failure only by throwing an exception [...]
+      if (const FunctionDecl *FD = getCurFunctionDecl()) {
+        OverloadedOperatorKind Op = FD->getOverloadedOperator();
+        if (Op == OO_New || Op == OO_Array_New) {
+          const FunctionProtoType *Proto
+            = FD->getType()->castAs<FunctionProtoType>();
+          bool ReturnValueNonNull;
+
+          if (!Proto->isNothrow(Context, /*ResultIfDependent*/true) &&
+              !RetValExp->isValueDependent() &&
+              RetValExp->EvaluateAsBooleanCondition(ReturnValueNonNull,
+                                                    Context) &&
+              !ReturnValueNonNull)
+            Diag(ReturnLoc, diag::warn_operator_new_returns_null)
+              << FD << getLangOpts().CPlusPlus11;
+        }
+      }
     }
 
     if (RetValExp) {
index 7facd10ca5fc8d93de9deca0338f79ee6a084265..59e2574fa14318724981bbe6b06a0cba4b2be907 100644 (file)
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -fsyntax-only -verify %s -triple=i686-pc-linux-gnu
+// RUN: %clang_cc1 -fsyntax-only -verify %s -triple=i686-pc-linux-gnu -Wno-new-returns-null
 
 #include <stddef.h>
 
diff --git a/test/SemaCXX/new-null.cpp b/test/SemaCXX/new-null.cpp
new file mode 100644 (file)
index 0000000..b2be0c5
--- /dev/null
@@ -0,0 +1,63 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++98
+// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++11
+
+typedef __SIZE_TYPE__ size_t;
+
+#if __cplusplus >= 201103L
+struct S1 {
+   void *operator new(size_t n) {
+     return nullptr; // expected-warning {{'operator new' should not return a null pointer unless it is declared 'throw()' or 'noexcept'}}
+   }
+   void *operator new[](size_t n) noexcept {
+     return __null;
+   }
+};
+#endif
+
+struct S2 {
+   static size_t x;
+   void *operator new(size_t n) throw() {
+     return 0;
+   }
+   void *operator new[](size_t n) {
+     return (void*)0;
+#if __cplusplus >= 201103L
+     // expected-warning@-2 {{'operator new[]' should not return a null pointer unless it is declared 'throw()' or 'noexcept'}}
+#else
+     // expected-warning-re@-4 {{'operator new[]' should not return a null pointer unless it is declared 'throw()'{{$}}}}
+#endif
+   }
+};
+
+struct S3 {
+   void *operator new(size_t n) {
+     return 1-1;
+#if __cplusplus >= 201103L
+     // expected-error@-2 {{cannot initialize return object of type 'void *' with an rvalue of type 'int'}}
+#else
+     // expected-warning@-4 {{expression which evaluates to zero treated as a null pointer constant of type 'void *'}}
+     // expected-warning@-5 {{'operator new' should not return a null pointer unless it is declared 'throw()'}}
+#endif
+   }
+   void *operator new[](size_t n) {
+     return (void*)(1-1); // expected-warning {{'operator new[]' should not return a null pointer unless it is declared 'throw()'}}
+   }
+};
+
+#if __cplusplus >= 201103L
+template<bool B> struct S4 {
+  void *operator new(size_t n) noexcept(B) {
+    return 0; // expected-warning {{'operator new' should not return a null pointer}}
+  }
+};
+template struct S4<true>;
+template struct S4<false>; // expected-note {{in instantiation of}}
+#endif
+
+template<typename ...T> struct S5 { // expected-warning 0-1{{extension}}
+  void *operator new(size_t n) throw(T...) {
+    return 0; // expected-warning {{'operator new' should not return a null pointer}}
+  }
+};
+template struct S5<>;
+template struct S5<int>; // expected-note {{in instantiation of}}
index 42a4e3523ad2f6d4e1ed782b093572c30e690530..710973ccb32b5df419ae579a6155123287148d30 100644 (file)
@@ -38,7 +38,7 @@ struct Test {
   } __attribute__((aligned(256)));
 
   void* operator new(unsigned long) {
-    return 0;
+    return 0; // expected-warning {{'operator new' should not return a null pointer unless it is declared 'throw()'}}
   }
 
   SeparateCacheLines<int> high_contention_data[10];
@@ -59,7 +59,7 @@ struct Test {
   } __attribute__((aligned(256)));
 
   void* operator new[](unsigned long) {
-    return 0;
+    return 0; // expected-warning {{'operator new[]' should not return a null pointer unless it is declared 'throw()'}}
   }
 
   SeparateCacheLines<int> high_contention_data[10];