| 1 | // Licensed to the .NET Foundation under one or more agreements. |
| 2 | // The .NET Foundation licenses this file to you under the MIT license. |
| 3 | // See the LICENSE file in the project root for more information. |
| 4 | |
| 5 | |
| 6 | #ifndef _DEBUGRETURN_H_ |
| 7 | #define _DEBUGRETURN_H_ |
| 8 | |
| 9 | // Note that with OACR Prefast is run over checked (_DEBUG is defined) sources |
| 10 | // so we have to first check the _PREFAST_ define followed by the _DEBUG define |
| 11 | // |
| 12 | #ifdef _PREFAST_ |
| 13 | |
| 14 | // Use prefast to detect gotos out of no-return blocks. The gotos out of no-return blocks |
| 15 | // should be reported as memory leaks by prefast. The (nothrow) is because PREfix sees the |
| 16 | // throw from the new statement, and doesn't like these macros used in a destructor (and |
| 17 | // the NULL returned by failure works just fine in delete[]) |
| 18 | |
| 19 | #define DEBUG_ASSURE_NO_RETURN_BEGIN(arg) { char* __noReturnInThisBlock_##arg = ::new (nothrow) char[1]; |
| 20 | #define DEBUG_ASSURE_NO_RETURN_END(arg) ::delete[] __noReturnInThisBlock_##arg; } |
| 21 | |
| 22 | #define DEBUG_OK_TO_RETURN_BEGIN(arg) { ::delete[] __noReturnInThisBlock_##arg; |
| 23 | #define DEBUG_OK_TO_RETURN_END(arg) __noReturnInThisBlock_##arg = ::new (nothrow) char[1]; } |
| 24 | |
| 25 | #define DEBUG_ASSURE_SAFE_TO_RETURN TRUE |
| 26 | #define return return |
| 27 | |
| 28 | #else // !_PREFAST_ |
| 29 | |
| 30 | // This is disabled in build 190024315 (a pre-release build after VS 2015 Update 3) and |
| 31 | // earlier because those builds only support C++11 constexpr, which doesn't allow the |
| 32 | // use of 'if' statements within the body of a constexpr function. Later builds support |
| 33 | // C++14 constexpr. |
| 34 | #if defined(_DEBUG) && (!defined(_MSC_FULL_VER) || _MSC_FULL_VER > 190024315) |
| 35 | |
| 36 | // Code to generate a compile-time error if return statements appear where they |
| 37 | // shouldn't. |
| 38 | // |
| 39 | // Here's the way it works... |
| 40 | // |
| 41 | // We create two classes with a safe_to_return() method. The method is static, |
| 42 | // returns void, and does nothing. One class has the method as public, the other |
| 43 | // as private. We introduce a global scope typedef for __ReturnOK that refers to |
| 44 | // the class with the public method. So, by default, the expression |
| 45 | // |
| 46 | // __ReturnOK::safe_to_return() |
| 47 | // |
| 48 | // quietly compiles and does nothing. When we enter a block in which we want to |
| 49 | // inhibit returns, we introduce a new typedef that defines __ReturnOK as the |
| 50 | // class with the private method. Inside this scope, |
| 51 | // |
| 52 | // __ReturnOK::safe_to_return() |
| 53 | // |
| 54 | // generates a compile-time error. |
| 55 | // |
| 56 | // To cause the method to be called, we have to #define the return keyword. |
| 57 | // The simplest working version would be |
| 58 | // |
| 59 | // #define return if (0) __ReturnOK::safe_to_return(); else return |
| 60 | // |
| 61 | // but we've used |
| 62 | // |
| 63 | // #define return for (;1;__ReturnOK::safe_to_return()) return |
| 64 | // |
| 65 | // because it happens to generate somewhat faster code in a checked build. (They |
| 66 | // both introduce no overhead in a fastchecked build.) |
| 67 | // |
| 68 | class __SafeToReturn { |
| 69 | public: |
| 70 | static int safe_to_return() {return 0;}; |
| 71 | static int used() {return 0;}; |
| 72 | }; |
| 73 | |
| 74 | class __YouCannotUseAReturnStatementHere { |
| 75 | private: |
| 76 | // If you got here, and you're wondering what you did wrong -- you're using |
| 77 | // a return statement where it's not allowed. Likely, it's inside one of: |
| 78 | // GCPROTECT_BEGIN ... GCPROTECT_END |
| 79 | // HELPER_METHOD_FRAME_BEGIN ... HELPER_METHOD_FRAME_END |
| 80 | // |
| 81 | static int safe_to_return() {return 0;}; |
| 82 | public: |
| 83 | // Some compilers warn if all member functions in a class are private |
| 84 | // or if a typedef is unused. Rather than disable the warning, we'll work |
| 85 | // around it here. |
| 86 | static int used() {return 0;}; |
| 87 | }; |
| 88 | |
| 89 | typedef __SafeToReturn __ReturnOK; |
| 90 | |
| 91 | // Use this to ensure that it is safe to return from a given scope |
| 92 | #define DEBUG_ASSURE_SAFE_TO_RETURN __ReturnOK::safe_to_return() |
| 93 | |
| 94 | // Unfortunately, the only way to make this work is to #define all return statements -- |
| 95 | // even the ones at global scope. This actually generates better code that appears. |
| 96 | // The call is dead, and does not appear in the generated code, even in a checked |
| 97 | // build. (And, in fastchecked, there is no penalty at all.) |
| 98 | // |
| 99 | #ifdef _MSC_VER |
| 100 | #define return if (0 && __ReturnOK::safe_to_return()) { } else return |
| 101 | #else // _MSC_VER |
| 102 | #define return for (;1;__ReturnOK::safe_to_return()) return |
| 103 | #endif // _MSC_VER |
| 104 | |
| 105 | #define DEBUG_ASSURE_NO_RETURN_BEGIN(arg) { typedef __YouCannotUseAReturnStatementHere __ReturnOK; if (0 && __ReturnOK::used()) { } else { |
| 106 | #define DEBUG_ASSURE_NO_RETURN_END(arg) } } |
| 107 | |
| 108 | #define DEBUG_OK_TO_RETURN_BEGIN(arg) { typedef __SafeToReturn __ReturnOK; if (0 && __ReturnOK::used()) { } else { |
| 109 | #define DEBUG_OK_TO_RETURN_END(arg) } } |
| 110 | |
| 111 | #else // defined(_DEBUG) && (!defined(_MSC_FULL_VER) || _MSC_FULL_VER > 190024315) |
| 112 | |
| 113 | #define DEBUG_ASSURE_SAFE_TO_RETURN TRUE |
| 114 | |
| 115 | #define DEBUG_ASSURE_NO_RETURN_BEGIN(arg) { |
| 116 | #define DEBUG_ASSURE_NO_RETURN_END(arg) } |
| 117 | |
| 118 | #define DEBUG_OK_TO_RETURN_BEGIN(arg) { |
| 119 | #define DEBUG_OK_TO_RETURN_END(arg) } |
| 120 | |
| 121 | #endif // defined(_DEBUG) && (!defined(_MSC_FULL_VER) || _MSC_FULL_VER > 190024315) |
| 122 | |
| 123 | #endif // !_PREFAST_ |
| 124 | |
| 125 | #endif // _DEBUGRETURN_H_ |
| 126 | |