Skip to content

Commit eebaa9b

Browse files
bpo-38249: Expand Py_UNREACHABLE() to __builtin_unreachable() in the release mode. (GH-16329)
Co-authored-by: Victor Stinner <[email protected]>
1 parent 6d0ee60 commit eebaa9b

File tree

8 files changed

+39
-15
lines changed

8 files changed

+39
-15
lines changed

Doc/c-api/intro.rst

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,11 +107,24 @@ complete listing.
107107

108108
.. c:macro:: Py_UNREACHABLE()
109109
110-
Use this when you have a code path that you do not expect to be reached.
110+
Use this when you have a code path that cannot be reached by design.
111111
For example, in the ``default:`` clause in a ``switch`` statement for which
112112
all possible values are covered in ``case`` statements. Use this in places
113113
where you might be tempted to put an ``assert(0)`` or ``abort()`` call.
114114

115+
In release mode, the macro helps the compiler to optimize the code, and
116+
avoids a warning about unreachable code. For example, the macro is
117+
implemented with ``__builtin_unreachable()`` on GCC in release mode.
118+
119+
A use for ``Py_UNREACHABLE()`` is following a call a function that
120+
never returns but that is not declared :c:macro:`_Py_NO_RETURN`.
121+
122+
If a code path is very unlikely code but can be reached under exceptional
123+
case, this macro must not be used. For example, under low memory condition
124+
or if a system call returns a value out of the expected range. In this
125+
case, it's better to report the error to the caller. If the error cannot
126+
be reported to caller, :c:func:`Py_FatalError` can be used.
127+
115128
.. versionadded:: 3.7
116129

117130
.. c:macro:: Py_ABS(x)

Include/pymacro.h

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@
101101
#endif
102102

103103
#if defined(RANDALL_WAS_HERE)
104-
#define Py_UNREACHABLE() \
104+
# define Py_UNREACHABLE() \
105105
Py_FatalError( \
106106
"If you're seeing this, the code is in what I thought was\n" \
107107
"an unreachable state.\n\n" \
@@ -113,13 +113,17 @@
113113
"I'm so sorry.\n" \
114114
"https://xkcd.com/2200")
115115
#elif defined(Py_DEBUG)
116-
#define Py_UNREACHABLE() \
116+
# define Py_UNREACHABLE() \
117117
Py_FatalError( \
118118
"We've reached an unreachable state. Anything is possible.\n" \
119119
"The limits were in our heads all along. Follow your dreams.\n" \
120120
"https://xkcd.com/2200")
121+
#elif defined(__GNUC__) || defined(__clang__) || defined(__INTEL_COMPILER)
122+
# define Py_UNREACHABLE() __builtin_unreachable()
123+
#elif defined(_MSC_VER)
124+
# define Py_UNREACHABLE() __assume(0)
121125
#else
122-
#define Py_UNREACHABLE() \
126+
# define Py_UNREACHABLE() \
123127
Py_FatalError("Unreachable C code path reached")
124128
#endif
125129

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
:c:macro:`Py_UNREACHABLE` is now implemented with
2+
``__builtin_unreachable()`` and analogs in release mode.

Modules/_tracemalloc.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -712,7 +712,7 @@ tracemalloc_realloc(void *ctx, void *ptr, size_t new_size)
712712
713713
The GIL and the table lock ensures that only one thread is
714714
allocating memory. */
715-
Py_UNREACHABLE();
715+
Py_FatalError("tracemalloc_realloc() failed to allocate a trace");
716716
}
717717
TABLES_UNLOCK();
718718
}

Objects/stringlib/eq.h

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,14 @@
66
Py_LOCAL_INLINE(int)
77
unicode_eq(PyObject *aa, PyObject *bb)
88
{
9+
assert(PyUnicode_Check(aa));
10+
assert(PyUnicode_Check(bb));
11+
assert(PyUnicode_IS_READY(aa));
12+
assert(PyUnicode_IS_READY(bb));
13+
914
PyUnicodeObject *a = (PyUnicodeObject *)aa;
1015
PyUnicodeObject *b = (PyUnicodeObject *)bb;
1116

12-
if (PyUnicode_READY(a) == -1 || PyUnicode_READY(b) == -1) {
13-
Py_UNREACHABLE();
14-
}
15-
1617
if (PyUnicode_GET_LENGTH(a) != PyUnicode_GET_LENGTH(b))
1718
return 0;
1819
if (PyUnicode_GET_LENGTH(a) == 0)

Python/formatter_unicode.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -574,7 +574,7 @@ calc_number_widths(NumberFieldWidths *spec, Py_ssize_t n_prefix,
574574
spec->n_lpadding = n_padding;
575575
break;
576576
default:
577-
/* Shouldn't get here, but treat it as '>' */
577+
/* Shouldn't get here */
578578
Py_UNREACHABLE();
579579
}
580580
}

Python/peephole.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -511,8 +511,12 @@ PyCode_Optimize(PyObject *code, PyObject* consts, PyObject *names,
511511
if (instrsize(j) > ilen) {
512512
goto exitUnchanged;
513513
}
514-
assert(ilen <= INT_MAX);
515514
/* If instrsize(j) < ilen, we'll emit EXTENDED_ARG 0 */
515+
if (ilen > 4) {
516+
/* Can only happen when PyCode_Optimize() is called with
517+
malformed bytecode. */
518+
goto exitUnchanged;
519+
}
516520
write_op_arg(codestr + h, opcode, j, (int)ilen);
517521
h += ilen;
518522
}

Python/pytime.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -746,7 +746,7 @@ _PyTime_GetSystemClock(void)
746746
_PyTime_t t;
747747
if (pygettimeofday(&t, NULL, 0) < 0) {
748748
/* should not happen, _PyTime_Init() checked the clock at startup */
749-
Py_UNREACHABLE();
749+
Py_FatalError("pygettimeofday() failed");
750750
}
751751
return t;
752752
}
@@ -776,7 +776,7 @@ pymonotonic(_PyTime_t *tp, _Py_clock_info_t *info, int raise)
776776
return -1;
777777
}
778778
/* Hello, time traveler! */
779-
Py_UNREACHABLE();
779+
Py_FatalError("pymonotonic: integer overflow");
780780
}
781781
*tp = t * MS_TO_NS;
782782

@@ -918,7 +918,7 @@ _PyTime_GetMonotonicClock(void)
918918
if (pymonotonic(&t, NULL, 0) < 0) {
919919
/* should not happen, _PyTime_Init() checked that monotonic clock at
920920
startup */
921-
Py_UNREACHABLE();
921+
Py_FatalError("pymonotonic() failed");
922922
}
923923
return t;
924924
}
@@ -1019,7 +1019,7 @@ _PyTime_GetPerfCounter(void)
10191019
{
10201020
_PyTime_t t;
10211021
if (_PyTime_GetPerfCounterWithInfo(&t, NULL)) {
1022-
Py_UNREACHABLE();
1022+
Py_FatalError("_PyTime_GetPerfCounterWithInfo() failed");
10231023
}
10241024
return t;
10251025
}

0 commit comments

Comments
 (0)