diff options
Diffstat (limited to 'src/inc/debugreturn.h')
-rw-r--r-- | src/inc/debugreturn.h | 127 |
1 files changed, 127 insertions, 0 deletions
diff --git a/src/inc/debugreturn.h b/src/inc/debugreturn.h new file mode 100644 index 0000000000..dbcbd2bb46 --- /dev/null +++ b/src/inc/debugreturn.h @@ -0,0 +1,127 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + + +#ifndef _DEBUGRETURN_H_ +#define _DEBUGRETURN_H_ + +// Note that with OACR Prefast is run over checked (_DEBUG is defined) sources +// so we have to first check the _PREFAST_ define followed by the _DEBUG define +// +#ifdef _PREFAST_ + +// Use prefast to detect gotos out of no-return blocks. The gotos out of no-return blocks +// should be reported as memory leaks by prefast. The (nothrow) is because PREfix sees the +// throw from the new statement, and doesn't like these macros used in a destructor (and +// the NULL returned by failure works just fine in delete[]) + +#define DEBUG_ASSURE_NO_RETURN_BEGIN(arg) { char* __noReturnInThisBlock_##arg = ::new (nothrow) char[1]; +#define DEBUG_ASSURE_NO_RETURN_END(arg) ::delete[] __noReturnInThisBlock_##arg; } + +#define DEBUG_OK_TO_RETURN_BEGIN(arg) { ::delete[] __noReturnInThisBlock_##arg; +#define DEBUG_OK_TO_RETURN_END(arg) __noReturnInThisBlock_##arg = ::new (nothrow) char[1]; } + +#define DEBUG_ASSURE_SAFE_TO_RETURN TRUE +#define return return + +#else // !_PREFAST_ + +// This is disabled in VS2015 Update 3 and earlier because only C++11 constexpr is supported, +// which doesn't allow the use of 'if' statements within the body of a constexpr function. +#if defined(_DEBUG) && (!defined(_MSC_FULL_VER) || _MSC_FULL_VER > 190024210) + +// Code to generate a compile-time error if return statements appear where they +// shouldn't. +// +// Here's the way it works... +// +// We create two classes with a safe_to_return() method. The method is static, +// returns void, and does nothing. One class has the method as public, the other +// as private. We introduce a global scope typedef for __ReturnOK that refers to +// the class with the public method. So, by default, the expression +// +// __ReturnOK::safe_to_return() +// +// quietly compiles and does nothing. When we enter a block in which we want to +// inhibit returns, we introduce a new typedef that defines __ReturnOK as the +// class with the private method. Inside this scope, +// +// __ReturnOK::safe_to_return() +// +// generates a compile-time error. +// +// To cause the method to be called, we have to #define the return keyword. +// The simplest working version would be +// +// #define return if (0) __ReturnOK::safe_to_return(); else return +// +// but we've used +// +// #define return for (;1;__ReturnOK::safe_to_return()) return +// +// because it happens to generate somewhat faster code in a checked build. (They +// both introduce no overhead in a fastchecked build.) +// +class __SafeToReturn { +public: + static int safe_to_return() {return 0;}; + static int used() {return 0;}; +}; + +class __YouCannotUseAReturnStatementHere { +private: + // If you got here, and you're wondering what you did wrong -- you're using + // a return statement where it's not allowed. Likely, it's inside one of: + // GCPROTECT_BEGIN ... GCPROTECT_END + // HELPER_METHOD_FRAME_BEGIN ... HELPER_METHOD_FRAME_END + // + static int safe_to_return() {return 0;}; +public: + // Some compilers warn if all member functions in a class are private + // or if a typedef is unused. Rather than disable the warning, we'll work + // around it here. + static int used() {return 0;}; +}; + +typedef __SafeToReturn __ReturnOK; + +// Use this to ensure that it is safe to return from a given scope +#define DEBUG_ASSURE_SAFE_TO_RETURN __ReturnOK::safe_to_return() + +// Unfortunately, the only way to make this work is to #define all return statements -- +// even the ones at global scope. This actually generates better code that appears. +// The call is dead, and does not appear in the generated code, even in a checked +// build. (And, in fastchecked, there is no penalty at all.) +// +#ifdef _MSC_VER +#define return if (0 && __ReturnOK::safe_to_return()) { } else return +#else // _MSC_VER +#define return for (;1;__ReturnOK::safe_to_return()) return +#endif // _MSC_VER + +#define DEBUG_ASSURE_NO_RETURN_BEGIN(arg) { typedef __YouCannotUseAReturnStatementHere __ReturnOK; if (0 && __ReturnOK::used()) { } else { +#define DEBUG_ASSURE_NO_RETURN_END(arg) } } + +// rotor_pal.h defaulted these to empty macros; this file redefines them +#undef DEBUG_OK_TO_RETURN_BEGIN +#undef DEBUG_OK_TO_RETURN_END + +#define DEBUG_OK_TO_RETURN_BEGIN(arg) { typedef __SafeToReturn __ReturnOK; if (0 && __ReturnOK::used()) { } else { +#define DEBUG_OK_TO_RETURN_END(arg) } } + +#else // defined(_DEBUG) && (!defined(_MSC_FULL_VER) || _MSC_FULL_VER > 190024210) + +#define DEBUG_ASSURE_SAFE_TO_RETURN TRUE + +#define DEBUG_ASSURE_NO_RETURN_BEGIN(arg) { +#define DEBUG_ASSURE_NO_RETURN_END(arg) } + +#define DEBUG_OK_TO_RETURN_BEGIN(arg) { +#define DEBUG_OK_TO_RETURN_END(arg) } + +#endif // defined(_DEBUG) && (!defined(_MSC_FULL_VER) || _MSC_FULL_VER > 190024210) + +#endif // !_PREFAST_ + +#endif // _DEBUGRETURN_H_ |