Skip to main content

Python/intrinsics.c

cpython 3.14 @ ab2d84fe1023/Python/intrinsics.c

intrinsics.c defines the small runtime helpers that the bytecode interpreter calls through the CALL_INTRINSIC_1 and CALL_INTRINSIC_2 opcodes. These opcodes carry an integer ID rather than a direct function pointer; the eval loop indexes into the _Py_CallIntrinsic1 or _Py_CallIntrinsic2 dispatch tables at runtime.

The file exists because several operations are too complex to inline cleanly in the eval loop but too cheap or semantically special to deserve a full Python-visible builtin. The canonical examples are from module import * (which modifies the caller's namespace), StopIteration-to-RuntimeError conversion inside generators, and the PEP 695 type-parameter attachment helpers.

All intrinsics receive the current tstate plus one (for _1) or two (for _2) Python object arguments, and return a new reference or NULL on error.

Map

LinesSymbolRolegopy
1-30includes, forward declarationsHeaders and local helpers used by the dispatch functions.
31-60intrinsic_printINTRINSIC_PRINT: writes repr(value) to sys.stdout; used in interactive mode for bare expression statements.vm/eval_gen.go:callIntrinsic1
61-100intrinsic_import_starINTRINSIC_IMPORT_STAR: implements from module import * by calling PyObject_GetAttr for each public name.vm/eval_gen.go:callIntrinsic1
101-125intrinsic_stopiteration_errorINTRINSIC_STOPITERATION_ERROR: converts a StopIteration raised inside a generator into a RuntimeError.vm/eval_gen.go:callIntrinsic1
126-145intrinsic_async_gen_value_wrapper_newINTRINSIC_ASYNC_GEN_WRAP: wraps the yielded value in an _PyAsyncGenValueWrapper.vm/eval_gen.go:callIntrinsic1
146-160intrinsic_unary_positiveINTRINSIC_UNARY_POSITIVE: calls PyNumber_Positive (__pos__).vm/eval_gen.go:callIntrinsic1
161-175intrinsic_list_to_tupleINTRINSIC_LIST_TO_TUPLE: converts a list to a tuple, used to materialise *args spreads.vm/eval_gen.go:callIntrinsic1
176-200_Py_CallIntrinsic1, dispatch tableTable of unary intrinsic functions indexed by INTRINSIC_* id; entry point called from the eval loop.vm/eval_gen.go:callIntrinsic1
201-225intrinsic_typevar_with_bound, intrinsic_typevar_with_constraintsINTRINSIC_TYPEVAR_WITH_BOUND / _WITH_CONSTRAINTS: PEP 695 TypeVar constructor helpers.vm/eval_gen.go:callIntrinsic2
226-253intrinsic_set_function_type_params, _Py_CallIntrinsic2INTRINSIC_SET_FUNCTION_TYPE_PARAMS: attaches __type_params__ to a function; binary dispatch table.vm/eval_gen.go:callIntrinsic2

Reading

intrinsic_import_star: from module import * (lines 61 to 100)

cpython 3.14 @ ab2d84fe1023/Python/intrinsics.c#L61-100

This is one of the more involved unary intrinsics. The bytecode compiler emits CALL_INTRINSIC_1 INTRINSIC_IMPORT_STAR after an IMPORT_NAME that corresponds to a from x import * statement. By the time the intrinsic fires, the module object is on the stack.

static PyObject *
intrinsic_import_star(PyThreadState *tstate, PyObject *v)
{
PyObject *locals = _PyEval_GetFrameLocals(tstate->current_frame);
if (locals == NULL) {
return NULL;
}
int err = import_all_from(tstate, locals, v);
Py_DECREF(locals);
if (err < 0) {
return NULL;
}
Py_RETURN_NONE;
}

import_all_from (defined in ceval.c) checks for a module-level __all__ attribute. If present it iterates that sequence; otherwise it falls back to iterating the module's __dict__ and skipping names that start with an underscore. Each exported name is written into locals via PyObject_SetItem.

intrinsic_stopiteration_error: generator StopIteration coercion (lines 101 to 125)

cpython 3.14 @ ab2d84fe1023/Python/intrinsics.c#L101-125

PEP 479 (enforced from Python 3.7) requires that a StopIteration propagating out of a generator body be converted to RuntimeError rather than silently terminating the generator. The eval loop detects this condition and emits CALL_INTRINSIC_1 INTRINSIC_STOPITERATION_ERROR to do the conversion:

static PyObject *
intrinsic_stopiteration_error(PyThreadState *tstate, PyObject *value)
{
PyObject *exc = tstate->current_exception;
assert(exc != NULL);
assert(PyExceptionInstance_Check(exc));
if (!PyErr_GivenExceptionMatches(exc, PyExc_StopIteration)) {
Py_INCREF(exc);
return exc;
}
const char *msg =
"async generator raised StopIteration";
if (!PyCoro_CheckExact(value)) {
msg = "generator raised StopIteration";
}
PyObject *error = _PyErr_FormatFromCause(
PyExc_RuntimeError, "%s", msg);
return error;
}

The value argument is the currently-executing frame's code object (or coroutine), used only to pick the right error message. The actual StopIteration is read from tstate->current_exception and wrapped with _PyErr_FormatFromCause so that the original exception appears as the __cause__ of the new RuntimeError.

_Py_CallIntrinsic1: unary dispatch table (lines 176 to 200)

cpython 3.14 @ ab2d84fe1023/Python/intrinsics.c#L176-200

The eval loop does not call each intrinsic function directly. Instead it indexes into a table, which keeps the CALL_INTRINSIC_1 handler in ceval.c to a single indirect call:

typedef PyObject *(*intrinsic_func1)(PyThreadState *, PyObject *);

static const intrinsic_func1 intrinsic_table1[] = {
[INTRINSIC_1_INVALID] = _PyErr_SetString_Nullptr,
[INTRINSIC_PRINT] = intrinsic_print,
[INTRINSIC_IMPORT_STAR] = intrinsic_import_star,
[INTRINSIC_STOPITERATION_ERROR] = intrinsic_stopiteration_error,
[INTRINSIC_ASYNC_GEN_WRAP] = intrinsic_async_gen_value_wrapper_new,
[INTRINSIC_UNARY_POSITIVE] = intrinsic_unary_positive,
[INTRINSIC_LIST_TO_TUPLE] = intrinsic_list_to_tuple,
[INTRINSIC_TYPEVAR_NEW] = _Py_TypeVar_NewIntrinsic,
[INTRINSIC_PARAMSPEC_NEW] = _Py_ParamSpec_NewIntrinsic,
[INTRINSIC_TYPEVARTUPLE_NEW] = _Py_TypeVarTuple_NewIntrinsic,
[INTRINSIC_SUBSCRIPT_GENERIC] = _Py_subscript_generic,
[INTRINSIC_TYPEALIAS_NEW] = _Py_TypeAlias_NewIntrinsic,
};

PyObject *
_Py_CallIntrinsic1(PyThreadState *tstate, int oparg, PyObject *value)
{
assert(oparg > 0 && oparg < MAX_INTRINSIC_1);
return intrinsic_table1[oparg](tstate, value);
}

MAX_INTRINSIC_1 is checked at compile time via a static assert against the table length, so adding an intrinsic without extending the table produces a build error.

PEP 695 intrinsics: TypeVar, type aliases (lines 201 to 253)

cpython 3.14 @ ab2d84fe1023/Python/intrinsics.c#L201-253

Three binary intrinsics support PEP 695 (generic functions and classes, Python 3.12+). Their second argument is always the new function or class object being defined.

static PyObject *
intrinsic_set_function_type_params(PyThreadState *Py_UNUSED(tstate),
PyObject *func, PyObject *type_params)
{
assert(PyFunction_Check(func));
assert(PyTuple_Check(type_params));
if (PyFunction_SetTypeParams(func, type_params) < 0) {
return NULL;
}
Py_INCREF(func);
return func;
}

intrinsic_typevar_with_bound receives (tstate, typevar, bound) and calls _Py_TypeVar_SetBound; intrinsic_typevar_with_constraints calls _Py_TypeVar_SetConstraints. Both are generated by the compiler when it encounters a TypeVar annotation with bound= or constraints in a PEP 695 type parameter clause.

gopy mirror

All intrinsics are dispatched inline inside vm/eval_gen.go. The two CALL_INTRINSIC_1 and CALL_INTRINSIC_2 cases contain a switch over the intrinsic ID that calls the equivalent Go helpers. PEP 695 intrinsics (INTRINSIC_TYPEVAR_*, INTRINSIC_TYPEALIAS_NEW) are stubbed with TODO comments pending the objects/typevar.go port.