Nuitka
The Python compiler
Loading...
Searching...
No Matches
HelpersTypes.c
1// Copyright 2025, 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 (generic_get_attr) {
149 assert(type->tp_getattro == NULL);
150 type->tp_getattro = PyObject_GenericGetAttr_resolved;
151 }
152
153 if (generic_set_attr) {
154 assert(type->tp_setattro == NULL);
155 type->tp_setattro = PyObject_GenericSetAttr_resolved;
156 }
157
158 if (self_iter) {
159 assert(type->tp_iter == NULL);
160 type->tp_iter = PyObject_SelfIter;
161 }
162
163#if PYTHON_VERSION >= 0x350
164 if (await_self_iter) {
165 assert(type->tp_as_async->am_await == NULL);
166 type->tp_as_async->am_await = PyObject_SelfIter;
167 }
168
169 if (await_self_aiter) {
170 assert(type->tp_as_async->am_aiter == NULL);
171 type->tp_as_async->am_aiter = PyObject_SelfIter;
172 }
173#else
174 assert(!await_self_iter);
175 assert(!await_self_aiter);
176#endif
177
178#if PYTHON_VERSION >= 0x3a0
179 type->tp_flags |= Py_TPFLAGS_IMMUTABLETYPE;
180#endif
181
182 NUITKA_MAY_BE_UNUSED int res = PyType_Ready(type);
183 assert(res >= 0);
184}
185
186#if PYTHON_VERSION >= 0x3c0
187
188typedef struct {
189 PyObject_HEAD PyObject *name;
190 PyObject *type_params;
191 PyObject *compute_value;
192 PyObject *value;
193 PyObject *module;
194} typealiasobject;
195
196static PyTypeObject *getTypeAliasType(void) {
197 static PyTypeObject *type_alias_type = NULL;
198
199 if (type_alias_type == NULL) {
200
201 PyObject *typing_module = PyImport_ImportModule("_typing");
202 CHECK_OBJECT(typing_module);
203
204 type_alias_type = (PyTypeObject *)PyObject_GetAttrString(typing_module, "TypeAliasType");
205 CHECK_OBJECT(type_alias_type);
206 }
207
208 return type_alias_type;
209}
210
211PyObject *MAKE_TYPE_ALIAS(PyObject *name, PyObject *type_params, PyObject *value, PyObject *module_name) {
212 // TODO: For Python 3.13 we can use the intrinsic.
213
214 typealiasobject *ta = Nuitka_GC_New(getTypeAliasType());
215
216 // TODO: Lets follow Python new inline function in the future, this is 3.12
217 // only code, so we can use it here.
218 ta->name = Py_NewRef(name);
219 ta->type_params = Py_IsNone(type_params) ? NULL : Py_XNewRef(type_params);
220 ta->compute_value = NULL;
221 ta->value = Py_XNewRef(value);
222 ta->module = Py_NewRef(module_name);
223
224 Nuitka_GC_Track(ta);
225
226 return (PyObject *)ta;
227}
228
229typedef struct {
230 PyObject_HEAD PyObject *name;
231 PyObject *bound;
232 PyObject *evaluate_bound;
233 PyObject *constraints;
234 PyObject *evaluate_constraints;
235 bool covariant;
236 bool contravariant;
237 bool infer_variance;
238} typevarobject;
239
240static typevarobject *_Nuitka_typevar_alloc(PyThreadState *tstate, PyObject *name, PyObject *bound,
241 PyObject *evaluate_bound, PyObject *constraints,
242 PyObject *evaluate_constraints, bool covariant, bool contravariant,
243 bool infer_variance, PyObject *module) {
244 PyTypeObject *tp = tstate->interp->cached_objects.typevar_type;
245 typevarobject *result = Nuitka_GC_New(tp);
246
247 result->name = Py_NewRef(name);
248
249 result->bound = Py_XNewRef(bound);
250 result->evaluate_bound = Py_XNewRef(evaluate_bound);
251 result->constraints = Py_XNewRef(constraints);
252 result->evaluate_constraints = Py_XNewRef(evaluate_constraints);
253
254 result->covariant = covariant;
255 result->contravariant = contravariant;
256 result->infer_variance = infer_variance;
257
258 Nuitka_GC_Track(result);
259
260 // TODO: Not seen yet.
261 if (unlikely(module != NULL)) {
262 if (PyObject_SetAttrString((PyObject *)result, "__module__", module) < 0) {
263 Py_DECREF(result);
264 return NULL;
265 }
266 }
267
268 return result;
269}
270
271PyObject *MAKE_TYPE_VAR(PyThreadState *tstate, PyObject *name) {
272 // TODO: For Python 3.13 this would work.
273 // return _PyIntrinsics_UnaryFunctions[INTRINSIC_TYPEVAR].func(tstate, name);
274
275 return (PyObject *)_Nuitka_typevar_alloc(tstate, name, NULL, NULL, NULL, NULL, false, false, true, NULL);
276}
277
278static PyTypeObject *_getTypeGenericAliasType(void) {
279 static PyTypeObject *type_generic_alias_type = NULL;
280
281 if (type_generic_alias_type == NULL) {
282
283 PyObject *typing_module = PyImport_ImportModule("_typing");
284 CHECK_OBJECT(typing_module);
285
286 type_generic_alias_type = (PyTypeObject *)PyObject_GetAttrString(typing_module, "_GenericAlias");
287 CHECK_OBJECT(type_generic_alias_type);
288 }
289
290 return type_generic_alias_type;
291}
292
293static PyObject *_Nuitka_unpack_typevartuples(PyObject *params) {
294 assert(PyTuple_Check(params));
295
296 // TODO: Not implemented yet.
297
298 return Py_NewRef(params);
299}
300
301PyObject *MAKE_TYPE_GENERIC(PyThreadState *tstate, PyObject *params) {
302 CHECK_OBJECT(params);
303 PyObject *unpacked_params = _Nuitka_unpack_typevartuples(params);
304 CHECK_OBJECT(unpacked_params);
305
306 PyObject *args[2] = {(PyObject *)tstate->interp->cached_objects.generic_type, unpacked_params};
307
308 PyObject *called = (PyObject *)_getTypeGenericAliasType();
309
310 PyObject *result = CALL_FUNCTION_WITH_ARGS2(tstate, called, args);
311 Py_DECREF(unpacked_params);
312 return result;
313}
314
315#endif
316// Part of "Nuitka", an optimizing Python compiler that is compatible and
317// integrates with CPython, but also works on its own.
318//
319// Licensed under the Apache License, Version 2.0 (the "License");
320// you may not use this file except in compliance with the License.
321// You may obtain a copy of the License at
322//
323// http://www.apache.org/licenses/LICENSE-2.0
324//
325// Unless required by applicable law or agreed to in writing, software
326// distributed under the License is distributed on an "AS IS" BASIS,
327// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
328// See the License for the specific language governing permissions and
329// limitations under the License.