forked from OSchip/llvm-project
Issue a warning if a throwing operator new or operator new[] returns a null
pointer, since this invokes undefined behavior. Based on a patch by Artyom Skrobov! Handling of dependent exception specifications and some additional testcases by me. llvm-svn: 199452
This commit is contained in:
parent
e9d2bfc7e9
commit
2c6b449098
|
@ -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; }
|
||||
|
||||
|
|
|
@ -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">;
|
||||
|
|
|
@ -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; "
|
||||
|
|
|
@ -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)))
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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}}
|
|
@ -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];
|
||||
|
|
Loading…
Reference in New Issue