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//
68class __SafeToReturn {
69public:
70 static int safe_to_return() {return 0;};
71 static int used() {return 0;};
72};
73
74class __YouCannotUseAReturnStatementHere {
75private:
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;};
82public:
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
89typedef __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