Nuitka
The Python compiler
Loading...
Searching...
No Matches
HelpersTypes.c
1// Copyright 2026, Kay Hayen, mailto:kay.hayen@gmail.com find license text at end of file
2
3// This file is included from another C file, help IDEs to still parse it on
4// its own.
5#ifdef __IDE_ONLY__
6#include "nuitka/prelude.h"
7#endif
8
9// Our replacement for "PyType_IsSubtype"
10bool Nuitka_Type_IsSubtype(PyTypeObject *a, PyTypeObject *b) {
11 CHECK_OBJECT(a);
12 CHECK_OBJECT(b);
13
14#if PYTHON_VERSION < 0x300
15 if (!(a->tp_flags & Py_TPFLAGS_HAVE_CLASS)) {
16 return b == a || b == &PyBaseObject_Type;
17 }
18#endif
19
20 PyObject *mro = a->tp_mro;
21 CHECK_OBJECT_X(mro);
22
23 if (likely(mro != NULL)) {
24 assert(PyTuple_Check(mro));
25
26 Py_ssize_t n = PyTuple_GET_SIZE(mro);
27
28 for (Py_ssize_t i = 0; i < n; i++) {
29 if (PyTuple_GET_ITEM(mro, i) == (PyObject *)b) {
30 return true;
31 }
32 }
33
34 return false;
35 } else {
36 // Fallback for uninitialized classes to base class scan
37 do {
38 if (a == b) {
39 return true;
40 }
41 a = a->tp_base;
42 } while (a != NULL);
43
44 return (b == &PyBaseObject_Type);
45 }
46}
47
48// TODO: We cannot really do this, until Nuitka_TypeLookup (_PyType_Lookup) is
49// not also a call to an API, we just become wasteful here. What will make sense
50// is to make specialized variants for not sub class checks, like
51// PyExc_GeneratorExit and PyExc_StopIteration by caching the descriptor
52// "checker" for them and then calling the "func" behind them more or less
53// directly. These could be created during startup and be very fast to use.
54
55#if 0
56int Nuitka_Object_IsSubclass(PyThreadState *tstate, PyObject *derived, PyObject *cls)
57{
58 // TODO: Checking for a type is nothing the core does, could have a second variant
59 if (PyType_CheckExact(cls)) {
60 // Only a quick test for an exact match, but then give up.
61 if (derived == cls) {
62 return 1;
63 }
64
65 // Too hard for us.
66 return PyObject_IsSubclass(derived, cls);
67 }
68
69 // TODO: Checking for a tuple is nothing the core does, could have a second variant
70 if (PyTuple_Check(cls)) {
71 if (Py_EnterRecursiveCall(" in __subclasscheck__")) {
72 return -1;
73 }
74
75 Py_ssize_t n = PyTuple_GET_SIZE(cls);
76 int r = 0;
77
78 for (Py_ssize_t i = 0; i < n; ++i) {
79 PyObject *item = PyTuple_GET_ITEM(cls, i);
80
81 r = Nuitka_Object_IsSubclass(tstate, derived, item);
82
83 if (r != 0) {
84 break;
85 }
86 }
87
88 Py_LeaveRecursiveCall();
89
90 return r;
91 }
92
93 // TODO: For many of our uses, we know it.
94 PyObject *checker = Nuitka_TypeLookup((PyTypeObject *)cls, const_str_plain___subclasscheck__);
95
96 if (checker != NULL) {
97 descrgetfunc f = Py_TYPE(checker)->tp_descr_get;
98
99 if (f == NULL) {
100 Py_INCREF(checker);
101 } else {
102 checker = f(checker, cls, (PyObject *)(Py_TYPE(cls)));
103 }
104 }
105
106 if (checker != NULL) {
107 int ok = -1;
108
109 if (Py_EnterRecursiveCall(" in __subclasscheck__")) {
110 Py_DECREF(checker);
111 return ok;
112 }
113
114 PyObject *res = CALL_FUNCTION_WITH_SINGLE_ARG(checker, derived);
115
116 Py_LeaveRecursiveCall();
117
118 Py_DECREF(checker);
119
120 if (res != NULL) {
121 ok = CHECK_IF_TRUE(res);
122 Py_DECREF(res);
123 }
124 return ok;
125 } else if (HAS_ERROR_OCCURRED(tstate)) {
126 return -1;
127 }
128
129 // Too hard for us.
130 return PyObject_IsSubclass(derived, cls);
131}
132#endif
133
134getattrofunc PyObject_GenericGetAttr_resolved;
135setattrofunc PyObject_GenericSetAttr_resolved;
136
137// Our wrapper for "PyType_Ready" that takes care of trying to avoid DLL entry
138// points for generic attributes. spell-checker: ignore aiter
139void Nuitka_PyType_Ready(PyTypeObject *type, PyTypeObject *base, bool generic_get_attr, bool generic_set_attr,
140 bool self_iter, bool await_self_iter, bool await_self_aiter) {
141 assert(type->tp_base == NULL);
142
143 PyObject_GenericGetAttr_resolved = PyBaseObject_Type.tp_getattro;
144 PyObject_GenericSetAttr_resolved = PyBaseObject_Type.tp_setattro;
145
146 type->tp_base = base;
147
148#if PYTHON_VERSION >= 0x3d0
149 if (base != NULL) {
150 // Assert that we don't accidentally miss setting pre-header flags
151 // that the base type has, as it causes GC segfaults.
152 assert(_PyType_PreHeaderSize(type) == _PyType_PreHeaderSize(base));
153 }
154#endif
155
156 if (generic_get_attr) {
157 assert(type->tp_getattro == NULL);
158 type->tp_getattro = PyObject_GenericGetAttr_resolved;
159 }
160
161 if (generic_set_attr) {
162 assert(type->tp_setattro == NULL);
163 type->tp_setattro = PyObject_GenericSetAttr_resolved;
164 }
165
166 if (self_iter) {
167 assert(type->tp_iter == NULL);
168 type->tp_iter = PyObject_SelfIter;
169 }
170
171#if PYTHON_VERSION >= 0x350
172 if (await_self_iter) {
173 assert(type->tp_as_async->am_await == NULL);
174 type->tp_as_async->am_await = PyObject_SelfIter;
175 }
176
177 if (await_self_aiter) {
178 assert(type->tp_as_async->am_aiter == NULL);
179 type->tp_as_async->am_aiter = PyObject_SelfIter;
180 }
181#else
182 assert(!await_self_iter);
183 assert(!await_self_aiter);
184#endif
185
186#if PYTHON_VERSION >= 0x3a0
187 type->tp_flags |= Py_TPFLAGS_IMMUTABLETYPE;
188#endif
189
190 NUITKA_MAY_BE_UNUSED int res = PyType_Ready(type);
191 assert(res >= 0);
192}
193
194#if PYTHON_VERSION >= 0x3c0
195
196typedef struct {
197 PyObject_HEAD PyObject *name;
198 PyObject *type_params;
199 PyObject *compute_value;
200 PyObject *value;
201 PyObject *module;
202} typealiasobject;
203
204static PyTypeObject *getTypeAliasType(void) {
205 static PyTypeObject *type_alias_type = NULL;
206
207 if (type_alias_type == NULL) {
208
209 PyObject *typing_module = PyImport_ImportModule("_typing");
210 CHECK_OBJECT(typing_module);
211
212 type_alias_type = (PyTypeObject *)PyObject_GetAttrString(typing_module, "TypeAliasType");
213 CHECK_OBJECT(type_alias_type);
214 }
215
216 return type_alias_type;
217}
218
219PyObject *MAKE_TYPE_ALIAS(PyObject *name, PyObject *type_params, PyObject *compute_value, PyObject *module_name) {
220 // TODO: For Python 3.13 we can use the intrinsic.
221
222 typealiasobject *ta = Nuitka_GC_New(getTypeAliasType());
223
224 // TODO: Lets follow Python new inline function in the future, this is 3.12
225 // only code, so we can use it here.
226 ta->name = Py_NewRef(name);
227 ta->type_params = Py_IsNone(type_params) ? NULL : Py_XNewRef(type_params);
228 ta->compute_value = Py_NewRef(compute_value);
229 ta->value = NULL;
230 ta->module = Py_NewRef(module_name);
231
232 Nuitka_GC_Track(ta);
233
234 return (PyObject *)ta;
235}
236
237typedef struct {
238 PyObject_HEAD PyObject *name;
239 PyObject *bound;
240 PyObject *evaluate_bound;
241 PyObject *constraints;
242 PyObject *evaluate_constraints;
243#if PYTHON_VERSION >= 0x3d0
244 PyObject *default_value;
245 PyObject *evaluate_default;
246#endif
247 bool covariant;
248 bool contravariant;
249 bool infer_variance;
250} typevarobject;
251
252typedef struct {
253 PyObject_HEAD PyObject *name;
254#if PYTHON_VERSION >= 0x3d0
255 PyObject *default_value;
256 PyObject *evaluate_default;
257#endif
258} typevartupleobject; // Following CPython, spell-checker: ignore typevartupleobject
259
260typedef struct {
261 PyObject_HEAD PyObject *name;
262 PyObject *bound;
263#if PYTHON_VERSION >= 0x3d0
264 PyObject *default_value;
265 PyObject *evaluate_default;
266#endif
267 bool covariant;
268 bool contravariant;
269 bool infer_variance;
270} paramspecobject;
271
272static typevarobject *_Nuitka_typevar_alloc(PyThreadState *tstate, PyObject *name, PyObject *bound,
273 PyObject *evaluate_bound, PyObject *constraints,
274 PyObject *evaluate_constraints, bool covariant, bool contravariant,
275 bool infer_variance, PyObject *module) {
276 PyTypeObject *tp = _Py_INTERP_CACHED_OBJECT(tstate->interp, typevar_type);
277 typevarobject *result = Nuitka_GC_New(tp);
278
279 result->name = Py_NewRef(name);
280
281 result->bound = Py_XNewRef(bound);
282 result->evaluate_bound = Py_XNewRef(evaluate_bound);
283 result->constraints = Py_XNewRef(constraints);
284 result->evaluate_constraints = Py_XNewRef(evaluate_constraints);
285#if PYTHON_VERSION >= 0x3d0
286 result->default_value = NULL;
287 result->evaluate_default = NULL;
288#endif
289
290 result->covariant = covariant;
291 result->contravariant = contravariant;
292 result->infer_variance = infer_variance;
293
294 Nuitka_GC_Track(result);
295
296 // TODO: Not seen yet.
297 if (unlikely(module != NULL)) {
298 if (PyObject_SetAttrString((PyObject *)result, "__module__", module) < 0) {
299 Py_DECREF(result);
300 return NULL;
301 }
302 }
303
304 return result;
305}
306
307static typevartupleobject *_Nuitka_typevartuple_alloc(PyThreadState *tstate, PyObject *name, PyObject *module,
308 PyObject *default_value) {
309 PyTypeObject *tp = _Py_INTERP_CACHED_OBJECT(tstate->interp, typevartuple_type);
310 typevartupleobject *tvt = Nuitka_GC_New(tp);
311 if (tvt == NULL) {
312 return NULL;
313 }
314 tvt->name = Py_NewRef(name);
315#if PYTHON_VERSION >= 0x3d0
316 tvt->default_value = Py_XNewRef(default_value);
317 tvt->evaluate_default = NULL;
318#endif
319 Nuitka_GC_Track(tvt);
320 if (module != NULL) {
321 if (PyObject_SetAttrString((PyObject *)tvt, "__module__", module) < 0) {
322 Py_DECREF(tvt);
323 return NULL;
324 }
325 }
326 return tvt;
327}
328
329static paramspecobject *_Nuitka_paramspec_alloc(PyThreadState *tstate, PyObject *name, PyObject *bound,
330 PyObject *default_value, bool covariant, bool contravariant,
331 bool infer_variance, PyObject *module) {
332 PyTypeObject *tp = _Py_INTERP_CACHED_OBJECT(tstate->interp, paramspec_type);
333 paramspecobject *ps = Nuitka_GC_New(tp);
334 if (ps == NULL) {
335 return NULL;
336 }
337 ps->name = Py_NewRef(name);
338 ps->bound = Py_XNewRef(bound);
339 ps->covariant = covariant;
340 ps->contravariant = contravariant;
341 ps->infer_variance = infer_variance;
342#if PYTHON_VERSION >= 0x3d0
343 ps->default_value = Py_XNewRef(default_value);
344 ps->evaluate_default = NULL;
345#endif
346 Nuitka_GC_Track(ps);
347 if (module != NULL) {
348 if (PyObject_SetAttrString((PyObject *)ps, "__module__", module) < 0) {
349 Py_DECREF(ps);
350 return NULL;
351 }
352 }
353 return ps;
354}
355
356PyObject *MAKE_TYPE_VAR(PyThreadState *tstate, PyObject *name) {
357 // TODO: For Python 3.13 this would work.
358 // return _PyIntrinsics_UnaryFunctions[INTRINSIC_TYPEVAR].func(tstate, name);
359
360 return (PyObject *)_Nuitka_typevar_alloc(tstate, name, NULL, NULL, NULL, NULL, false, false, true, NULL);
361}
362
363PyObject *MAKE_TYPE_VAR_TUPLE(PyThreadState *tstate, PyObject *name) {
364 return (PyObject *)_Nuitka_typevartuple_alloc(tstate, name, NULL, NULL);
365}
366
367PyObject *MAKE_PARAM_SPEC(PyThreadState *tstate, PyObject *name) {
368 return (PyObject *)_Nuitka_paramspec_alloc(tstate, name, NULL, NULL, false, false, true, NULL);
369}
370
371static PyTypeObject *_getTypeGenericAliasType(void) {
372 static PyTypeObject *type_generic_alias_type = NULL;
373
374 if (type_generic_alias_type == NULL) {
375
376 PyObject *typing_module = IMPORT_HARD_TYPING();
377
378 type_generic_alias_type = (PyTypeObject *)PyObject_GetAttrString(typing_module, "_GenericAlias");
379 CHECK_OBJECT(type_generic_alias_type);
380 }
381
382 return type_generic_alias_type;
383}
384
385static int _Nuitka_contains_typevartuple(PyTupleObject *params) {
386 Py_ssize_t n = PyTuple_GET_SIZE(params);
387 PyTypeObject *tp = _Py_INTERP_CACHED_OBJECT(PyInterpreterState_Get(), typevartuple_type);
388 for (Py_ssize_t i = 0; i < n; i++) {
389 PyObject *param = PyTuple_GET_ITEM(params, i);
390 if (Py_IS_TYPE(param, tp)) {
391 return 1;
392 }
393 }
394 return 0;
395}
396
397static PyObject *_Nuitka_unpack_param(PyThreadState *tstate, PyObject *self) {
398 static PyObject *typing_unpack = NULL;
399
400 if (typing_unpack == NULL) {
401 typing_unpack = LOOKUP_ATTRIBUTE(tstate, IMPORT_HARD_TYPING(), const_str_plain_Unpack);
402 CHECK_OBJECT(typing_unpack);
403 }
404
405 return LOOKUP_SUBSCRIPT(tstate, typing_unpack, self);
406}
407
408// Match CPython, spell-checker: ignore typevartuple,typevartuples
409static PyObject *_Nuitka_unpack_typevartuples(PyThreadState *tstate, PyObject *params) {
410 assert(PyTuple_Check(params));
411 // TypeVarTuple must be unpacked when passed to Generic, so we do that here.
412 if (_Nuitka_contains_typevartuple((PyTupleObject *)params)) {
413 Py_ssize_t n = PyTuple_GET_SIZE(params);
414 PyObject *new_params = MAKE_TUPLE_EMPTY(tstate, n);
415 CHECK_OBJECT(new_params);
416
417 PyTypeObject *tp = _Py_INTERP_CACHED_OBJECT(tstate->interp, typevartuple_type);
418 for (Py_ssize_t i = 0; i < n; i++) {
419 PyObject *param = PyTuple_GET_ITEM(params, i);
420 if (Py_IS_TYPE(param, tp)) {
421 PyObject *unpacked = _Nuitka_unpack_param(tstate, param);
422 if (unpacked == NULL) {
423 Py_DECREF(new_params);
424 return NULL;
425 }
426 PyTuple_SET_ITEM(new_params, i, unpacked);
427 } else {
428 PyTuple_SET_ITEM(new_params, i, Py_NewRef(param));
429 }
430 }
431 return new_params;
432 } else {
433 return Py_NewRef(params);
434 }
435}
436
437PyObject *MAKE_TYPE_GENERIC(PyThreadState *tstate, PyObject *params) {
438 CHECK_OBJECT(params);
439 PyObject *unpacked_params = _Nuitka_unpack_typevartuples(tstate, params);
440 CHECK_OBJECT(unpacked_params);
441 assert(PyTuple_CheckExact(unpacked_params));
442
443 PyObject *args[2] = {(PyObject *)_Py_INTERP_CACHED_OBJECT(tstate->interp, generic_type), unpacked_params};
444
445 PyObject *called = (PyObject *)_getTypeGenericAliasType();
446
447 PyObject *result = CALL_FUNCTION_WITH_ARGS2(tstate, called, args);
448 Py_DECREF(unpacked_params);
449
450 return MAKE_TUPLE1_0(tstate, result);
451}
452
453#endif
454
455// Part of "Nuitka", an optimizing Python compiler that is compatible and
456// integrates with CPython, but also works on its own.
457//
458// Licensed under the GNU Affero General Public License, Version 3 (the "License");
459// you may not use this file except in compliance with the License.
460// You may obtain a copy of the License at
461//
462// http://www.gnu.org/licenses/agpl.txt
463//
464// Unless required by applicable law or agreed to in writing, software
465// distributed under the License is distributed on an "AS IS" BASIS,
466// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
467// See the License for the specific language governing permissions and
468// limitations under the License.