MLIR 23.0.0git
NanobindAdaptors.h
Go to the documentation of this file.
1//===- NanobindAdaptors.h - Interop with MLIR APIs via nanobind -----------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8// This file contains adaptors for clients of the core MLIR Python APIs to
9// interop via MLIR CAPI types, using nanobind. The facilities here do not
10// depend on implementation details of the MLIR Python API and do not introduce
11// C++-level dependencies with it (requiring only Python and CAPI-level
12// dependencies).
13//
14// It is encouraged to be used both in-tree and out-of-tree. For in-tree use
15// cases, it should be used for dialect implementations (versus relying on
16// Pybind-based internals of the core libraries).
17//===----------------------------------------------------------------------===//
18
19#ifndef MLIR_BINDINGS_PYTHON_NANOBINDADAPTORS_H
20#define MLIR_BINDINGS_PYTHON_NANOBINDADAPTORS_H
21
22#include <cstdint>
23#include <memory>
24#include <optional>
25
26#include "mlir-c/Diagnostics.h"
27#include "mlir-c/IR.h"
28// clang-format off
30#include "mlir-c/Bindings/Python/Interop.h" // This is expected after nanobind.
31// clang-format on
33
34namespace mlir {
35namespace python {
36namespace {
37
38nanobind::module_ &irModule() {
39 static SafeInit<nanobind::module_> init([]() {
40 return std::make_unique<nanobind::module_>(
41 nanobind::module_::import_(MAKE_MLIR_PYTHON_QUALNAME("ir")));
42 });
43 return init.get();
44}
45
46} // namespace
47} // namespace python
48} // namespace mlir
49
50// Raw CAPI type casters need to be declared before use, so always include them
51// first.
52namespace nanobind {
53namespace detail {
54
55/// Helper to convert a presumed MLIR API object to a capsule, accepting either
56/// an explicit Capsule (which can happen when two C APIs are communicating
57/// directly via Python) or indirectly by querying the MLIR_PYTHON_CAPI_PTR_ATTR
58/// attribute (through which supported MLIR Python API objects export their
59/// contained API pointer as a capsule). Throws a type error if the object is
60/// neither. This is intended to be used from type casters, which are invoked
61/// with a raw handle (unowned). The returned object's lifetime may not extend
62/// beyond the apiObject handle without explicitly having its refcount increased
63/// (i.e. on return).
64static std::optional<nanobind::object>
65mlirApiObjectToCapsule(nanobind::handle apiObject) {
66 if (PyCapsule_CheckExact(apiObject.ptr()))
67 return nanobind::borrow<nanobind::object>(apiObject);
68 nanobind::object api =
69 nanobind::getattr(apiObject, MLIR_PYTHON_CAPI_PTR_ATTR, nanobind::none());
70 if (api.is_none())
71 return {};
72 return api;
73}
74
75/// Clears the Python error indicator if the given condition `val` is false and
76/// returns the condition. This is needed in `from_python` of the type casters
77/// below, where a failed conversion from a Python capsule sets the Python error
78/// indicator but the caller of the caster expects or may even need a clean
79/// error indicator.
80inline bool pyErrClearIfFalse(bool val) {
81 if (!val)
82 PyErr_Clear();
83 return val;
84}
85
86// Note: Currently all of the following support cast from nanobind::object to
87// the Mlir* C-API type, but only a few light-weight, context-bound ones
88// implicitly cast the other way because the use case has not yet emerged and
89// ownership is unclear.
90
91/// Casts object <-> MlirAffineMap.
92template <>
93struct type_caster<MlirAffineMap> {
94 NB_TYPE_CASTER(MlirAffineMap,
95 const_name(MAKE_MLIR_PYTHON_QUALNAME("ir.AffineMap")))
96 bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept {
97 if (auto capsule = mlirApiObjectToCapsule(src)) {
98 value = mlirPythonCapsuleToAffineMap(capsule->ptr());
100 }
101 return false;
102 }
103 static handle from_cpp(MlirAffineMap v, rv_policy,
104 cleanup_list *cleanup) noexcept {
105 nanobind::object capsule =
106 nanobind::steal<nanobind::object>(mlirPythonAffineMapToCapsule(v));
107 return mlir::python::irModule()
108 .attr("AffineMap")
109 .attr(MLIR_PYTHON_CAPI_FACTORY_ATTR)(capsule)
110 .release();
111 }
112};
113
114/// Casts object <-> MlirAttribute.
115template <>
116struct type_caster<MlirAttribute> {
117 NB_TYPE_CASTER(MlirAttribute,
118 const_name(MAKE_MLIR_PYTHON_QUALNAME("ir.Attribute")))
119 bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept {
120 if (auto capsule = mlirApiObjectToCapsule(src)) {
121 value = mlirPythonCapsuleToAttribute(capsule->ptr());
122 return pyErrClearIfFalse(!mlirAttributeIsNull(value));
123 }
124 return false;
125 }
126 static handle from_cpp(MlirAttribute v, rv_policy,
127 cleanup_list *cleanup) noexcept {
128 nanobind::object capsule =
129 nanobind::steal<nanobind::object>(mlirPythonAttributeToCapsule(v));
130 return mlir::python::irModule()
131 .attr("Attribute")
132 .attr(MLIR_PYTHON_CAPI_FACTORY_ATTR)(capsule)
134 .release();
135 }
136};
137
138/// Casts object -> MlirBlock.
139template <>
140struct type_caster<MlirBlock> {
141 NB_TYPE_CASTER(MlirBlock, const_name(MAKE_MLIR_PYTHON_QUALNAME("ir.Block")))
142 bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept {
143 if (auto capsule = mlirApiObjectToCapsule(src)) {
144 value = mlirPythonCapsuleToBlock(capsule->ptr());
145 return pyErrClearIfFalse(!mlirBlockIsNull(value));
146 }
147 return false;
148 }
149};
150
151/// Casts object -> MlirContext.
152template <>
153struct type_caster<MlirContext> {
154 NB_TYPE_CASTER(MlirContext,
155 const_name(MAKE_MLIR_PYTHON_QUALNAME("ir.Context")))
156 bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept {
157 if (src.is_none()) {
158 // Gets the current thread-bound context.
159 src = mlir::python::irModule().attr("Context").attr("current");
160 }
161 // If there is no context, including thread-bound, emit a warning (since
162 // this function is not allowed to throw) and fail to cast.
163 if (src.is_none()) {
164 PyErr_WarnEx(
165 PyExc_RuntimeWarning,
166 "Passing None as MLIR Context is only allowed inside "
167 "the " MAKE_MLIR_PYTHON_QUALNAME("ir.Context") " context manager.",
168 /*stacklevel=*/1);
169 return false;
170 }
171 if (std::optional<nanobind::object> capsule = mlirApiObjectToCapsule(src)) {
172 value = mlirPythonCapsuleToContext(capsule->ptr());
173 return pyErrClearIfFalse(!mlirContextIsNull(value));
174 }
175 return false;
176 }
177};
178
179/// Casts object <-> MlirDialectRegistry.
180template <>
181struct type_caster<MlirDialectRegistry> {
182 NB_TYPE_CASTER(MlirDialectRegistry,
183 const_name(MAKE_MLIR_PYTHON_QUALNAME("ir.DialectRegistry")))
184 bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept {
185 if (auto capsule = mlirApiObjectToCapsule(src)) {
186 value = mlirPythonCapsuleToDialectRegistry(capsule->ptr());
188 }
189 return false;
190 }
191 static handle from_cpp(MlirDialectRegistry v, rv_policy,
192 cleanup_list *cleanup) noexcept {
193 nanobind::object capsule = nanobind::steal<nanobind::object>(
195 return mlir::python::irModule()
196 .attr("DialectRegistry")
197 .attr(MLIR_PYTHON_CAPI_FACTORY_ATTR)(capsule)
198 .release();
199 }
200};
201
202/// Casts object <-> MlirLocation.
203template <>
204struct type_caster<MlirLocation> {
205 NB_TYPE_CASTER(MlirLocation,
206 const_name(MAKE_MLIR_PYTHON_QUALNAME("ir.Location")))
207 bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept {
208 if (src.is_none()) {
209 // Gets the current thread-bound context.
210 src = mlir::python::irModule().attr("Location").attr("current");
211 }
212 if (auto capsule = mlirApiObjectToCapsule(src)) {
213 value = mlirPythonCapsuleToLocation(capsule->ptr());
215 }
216 return false;
217 }
218 static handle from_cpp(MlirLocation v, rv_policy,
219 cleanup_list *cleanup) noexcept {
220 nanobind::object capsule =
221 nanobind::steal<nanobind::object>(mlirPythonLocationToCapsule(v));
222 return mlir::python::irModule()
223 .attr("Location")
224 .attr(MLIR_PYTHON_CAPI_FACTORY_ATTR)(capsule)
225 .release();
226 }
227};
228
229/// Casts object <-> MlirModule.
230template <>
231struct type_caster<MlirModule> {
232 NB_TYPE_CASTER(MlirModule, const_name(MAKE_MLIR_PYTHON_QUALNAME("ir.Module")))
233 bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept {
234 if (auto capsule = mlirApiObjectToCapsule(src)) {
235 value = mlirPythonCapsuleToModule(capsule->ptr());
236 return pyErrClearIfFalse(!mlirModuleIsNull(value));
237 }
238 return false;
239 }
240 static handle from_cpp(MlirModule v, rv_policy,
241 cleanup_list *cleanup) noexcept {
242 nanobind::object capsule =
243 nanobind::steal<nanobind::object>(mlirPythonModuleToCapsule(v));
244 return mlir::python::irModule()
245 .attr("Module")
246 .attr(MLIR_PYTHON_CAPI_FACTORY_ATTR)(capsule)
247 .release();
248 };
249};
250
251/// Casts object <-> MlirFrozenRewritePatternSet.
252template <>
253struct type_caster<MlirFrozenRewritePatternSet> {
255 MlirFrozenRewritePatternSet,
256 const_name(MAKE_MLIR_PYTHON_QUALNAME("rewrite.FrozenRewritePatternSet")))
257 bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept {
258 if (auto capsule = mlirApiObjectToCapsule(src)) {
259 value = mlirPythonCapsuleToFrozenRewritePatternSet(capsule->ptr());
260 return pyErrClearIfFalse(value.ptr != nullptr);
261 }
262 return false;
263 }
264 static handle from_cpp(MlirFrozenRewritePatternSet v, rv_policy,
265 handle) noexcept {
266 nanobind::object capsule = nanobind::steal<nanobind::object>(
268 return nanobind::module_::import_(MAKE_MLIR_PYTHON_QUALNAME("rewrite"))
269 .attr("FrozenRewritePatternSet")
270 .attr(MLIR_PYTHON_CAPI_FACTORY_ATTR)(capsule)
271 .release();
272 };
273};
274
275/// Casts object <-> MlirOperation.
276template <>
277struct type_caster<MlirOperation> {
278 NB_TYPE_CASTER(MlirOperation,
279 const_name(MAKE_MLIR_PYTHON_QUALNAME("ir.Operation")))
280 bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept {
281 if (auto capsule = mlirApiObjectToCapsule(src)) {
282 value = mlirPythonCapsuleToOperation(capsule->ptr());
283 return pyErrClearIfFalse(!mlirOperationIsNull(value));
284 }
285 return false;
286 }
287 static handle from_cpp(MlirOperation v, rv_policy,
288 cleanup_list *cleanup) noexcept {
289 if (v.ptr == nullptr)
290 return nanobind::none();
291 nanobind::object capsule =
292 nanobind::steal<nanobind::object>(mlirPythonOperationToCapsule(v));
293 return mlir::python::irModule()
294 .attr("Operation")
295 .attr(MLIR_PYTHON_CAPI_FACTORY_ATTR)(capsule)
296 .release();
297 };
298};
299
300/// Casts object <-> MlirValue.
301template <>
302struct type_caster<MlirValue> {
303 NB_TYPE_CASTER(MlirValue, const_name(MAKE_MLIR_PYTHON_QUALNAME("ir.Value")))
304 bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept {
305 if (auto capsule = mlirApiObjectToCapsule(src)) {
306 value = mlirPythonCapsuleToValue(capsule->ptr());
307 return pyErrClearIfFalse(!mlirValueIsNull(value));
308 }
309 return false;
310 }
311 static handle from_cpp(MlirValue v, rv_policy,
312 cleanup_list *cleanup) noexcept {
313 if (v.ptr == nullptr)
314 return nanobind::none();
315 nanobind::object capsule =
316 nanobind::steal<nanobind::object>(mlirPythonValueToCapsule(v));
317 return mlir::python::irModule()
318 .attr("Value")
319 .attr(MLIR_PYTHON_CAPI_FACTORY_ATTR)(capsule)
321 .release();
322 };
323};
324
325/// Casts object -> MlirPassManager.
326template <>
327struct type_caster<MlirPassManager> {
328 NB_TYPE_CASTER(MlirPassManager, const_name(MAKE_MLIR_PYTHON_QUALNAME(
329 "passmanager.PassManager")))
330 bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept {
331 if (auto capsule = mlirApiObjectToCapsule(src)) {
332 value = mlirPythonCapsuleToPassManager(capsule->ptr());
334 }
335 return false;
336 }
337};
338
339/// Casts object <-> MlirTypeID.
340template <>
341struct type_caster<MlirTypeID> {
342 NB_TYPE_CASTER(MlirTypeID, const_name(MAKE_MLIR_PYTHON_QUALNAME("ir.TypeID")))
343 bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept {
344 if (auto capsule = mlirApiObjectToCapsule(src)) {
345 value = mlirPythonCapsuleToTypeID(capsule->ptr());
346 return pyErrClearIfFalse(!mlirTypeIDIsNull(value));
347 }
348 return false;
349 }
350 static handle from_cpp(MlirTypeID v, rv_policy,
351 cleanup_list *cleanup) noexcept {
352 if (v.ptr == nullptr)
353 return nanobind::none();
354 nanobind::object capsule =
355 nanobind::steal<nanobind::object>(mlirPythonTypeIDToCapsule(v));
356 return mlir::python::irModule()
357 .attr("TypeID")
358 .attr(MLIR_PYTHON_CAPI_FACTORY_ATTR)(capsule)
359 .release();
360 };
361};
362
363/// Casts object <-> MlirType.
364template <>
365struct type_caster<MlirType> {
366 NB_TYPE_CASTER(MlirType, const_name(MAKE_MLIR_PYTHON_QUALNAME("ir.Type")))
367 bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept {
368 if (auto capsule = mlirApiObjectToCapsule(src)) {
369 value = mlirPythonCapsuleToType(capsule->ptr());
370 return pyErrClearIfFalse(!mlirTypeIsNull(value));
371 }
372 return false;
373 }
374 static handle from_cpp(MlirType t, rv_policy,
375 cleanup_list *cleanup) noexcept {
376 nanobind::object capsule =
377 nanobind::steal<nanobind::object>(mlirPythonTypeToCapsule(t));
378 return mlir::python::irModule()
379 .attr("Type")
380 .attr(MLIR_PYTHON_CAPI_FACTORY_ATTR)(capsule)
382 .release();
383 }
384};
385
386/// Casts MlirStringRef -> object.
387template <>
388struct type_caster<MlirStringRef> {
389 NB_TYPE_CASTER(MlirStringRef, const_name("str"))
390 static handle from_cpp(MlirStringRef s, rv_policy,
391 cleanup_list *cleanup) noexcept {
392 return nanobind::str(s.data, s.length).release();
393 }
394};
395
396} // namespace detail
397} // namespace nanobind
398
399namespace mlir {
400namespace python {
402
403/// Provides a facility like nanobind::class_ for defining a new class in a
404/// scope, but this allows extension of an arbitrary Python class, defining
405/// methods on it is a similar way. Classes defined in this way are very similar
406/// to if defined in Python in the usual way but use nanobind machinery to
407/// do it. These are not "real" nanobind classes but pure Python classes
408/// with no relation to a concrete C++ class.
409///
410/// Derived from a discussion upstream:
411/// https://github.com/pybind/pybind11/issues/1193
412/// (plus a fair amount of extra curricular poking)
413/// TODO: If this proves useful, see about including it in nanobind.
415public:
416 pure_subclass(nanobind::handle scope, const char *derivedClassName,
417 const nanobind::object &superClass) {
418 nanobind::object pyType =
419 nanobind::borrow<nanobind::object>((PyObject *)&PyType_Type);
420 nanobind::object metaclass = pyType(superClass);
421 nanobind::dict attributes;
422
423 thisClass = metaclass(derivedClassName, nanobind::make_tuple(superClass),
424 attributes);
425 scope.attr(derivedClassName) = thisClass;
426 thisClass.attr("__module__") = scope.attr("__name__");
427 }
428
429 template <typename Func, typename... Extra>
430 pure_subclass &def(const char *name, Func &&f, const Extra &...extra) {
431 nanobind::object cf = nanobind::cpp_function(
432 std::forward<Func>(f), nanobind::name(name), nanobind::is_method(),
433 nanobind::scope(thisClass), extra...);
434 thisClass.attr(name) = cf;
435 return *this;
436 }
437
438 template <typename Func, typename... Extra>
439 pure_subclass &def_property_readonly(const char *name, Func &&f,
440 const Extra &...extra) {
441 nanobind::object cf = nanobind::cpp_function(
442 std::forward<Func>(f), nanobind::name(name), nanobind::is_method(),
443 nanobind::scope(thisClass), extra...);
444 auto builtinProperty =
445 nanobind::borrow<nanobind::object>((PyObject *)&PyProperty_Type);
446 thisClass.attr(name) = builtinProperty(cf);
447 return *this;
448 }
449
450 template <typename Func, typename... Extra>
451 pure_subclass &def_staticmethod(const char *name, Func &&f,
452 const Extra &...extra) {
453 static_assert(!std::is_member_function_pointer<Func>::value,
454 "def_staticmethod(...) called with a non-static member "
455 "function pointer");
456 nanobind::object cf = nanobind::cpp_function(
457 std::forward<Func>(f),
458 nanobind::name(name), // nanobind::scope(thisClass),
459 extra...);
460 thisClass.attr(name) = cf;
461 return *this;
462 }
463
464 template <typename Func, typename... Extra>
465 pure_subclass &def_classmethod(const char *name, Func &&f,
466 const Extra &...extra) {
467 static_assert(!std::is_member_function_pointer<Func>::value,
468 "def_classmethod(...) called with a non-static member "
469 "function pointer");
470 nanobind::object cf = nanobind::cpp_function(
471 std::forward<Func>(f),
472 nanobind::name(name), // nanobind::scope(thisClass),
473 extra...);
474 static SafeInit<nanobind::object> classmethodFn([]() {
475 return std::make_unique<nanobind::object>(
476 nanobind::module_::import_("builtins").attr("classmethod"));
477 });
478 thisClass.attr(name) = classmethodFn.get()(cf);
479 return *this;
480 }
481
482 nanobind::object get_class() const { return thisClass; }
483
484protected:
485 nanobind::object superClass;
486 nanobind::object thisClass;
487};
488
489/// Creates a custom subclass of mlir.ir.Attribute, implementing a casting
490/// constructor and type checking methods.
492public:
493 using IsAFunctionTy = bool (*)(MlirAttribute);
494 using GetTypeIDFunctionTy = MlirTypeID (*)();
495
496 /// Subclasses by looking up the super-class dynamically.
497 mlir_attribute_subclass(nanobind::handle scope, const char *attrClassName,
498 IsAFunctionTy isaFunction,
499 GetTypeIDFunctionTy getTypeIDFunction = nullptr)
500 : mlir_attribute_subclass(scope, attrClassName, isaFunction,
501 irModule().attr("Attribute"),
502 getTypeIDFunction) {}
503
504 /// Subclasses with a provided mlir.ir.Attribute super-class. This must
505 /// be used if the subclass is being defined in the same extension module
506 /// as the mlir.ir class (otherwise, it will trigger a recursive
507 /// initialization).
508 mlir_attribute_subclass(nanobind::handle scope, const char *typeClassName,
509 IsAFunctionTy isaFunction,
510 const nanobind::object &superCls,
511 GetTypeIDFunctionTy getTypeIDFunction = nullptr)
512 : pure_subclass(scope, typeClassName, superCls) {
513 // Casting constructor. Note that it is hard, if not impossible, to properly
514 // call chain to parent `__init__` in nanobind due to its special handling
515 // for init functions that don't have a fully constructed self-reference,
516 // which makes it impossible to forward it to `__init__` of a superclass.
517 // Instead, provide a custom `__new__` and call that of a superclass, which
518 // eventually calls `__init__` of the superclass. Since attribute subclasses
519 // have no additional members, we can just return the instance thus created
520 // without amending it.
521 std::string captureTypeName(
522 typeClassName); // As string in case if typeClassName is not static.
523 nanobind::object newCf = nanobind::cpp_function(
524 [superCls, isaFunction, captureTypeName](
525 nanobind::object cls, nanobind::object otherAttribute) {
526 MlirAttribute rawAttribute;
527 if (!nanobind::try_cast<MlirAttribute>(otherAttribute,
528 rawAttribute) ||
529 !isaFunction(rawAttribute)) {
530 auto origRepr =
531 nanobind::cast<std::string>(nanobind::repr(otherAttribute));
532 throw std::invalid_argument(nanobind::detail::join(
533 "Cannot cast attribute to ", captureTypeName, " (from ",
534 origRepr, ")"));
535 }
536 nanobind::object self = superCls.attr("__new__")(cls, otherAttribute);
537 return self;
538 },
539 nanobind::name("__new__"), nanobind::arg("cls"),
540 nanobind::arg("cast_from_attr"));
541 thisClass.attr("__new__") = newCf;
542
543 // 'isinstance' method.
544 static const char kIsinstanceSig[] =
545 "def isinstance(other_attribute: " MAKE_MLIR_PYTHON_QUALNAME(
546 "ir") ".Attribute) -> bool";
548 "isinstance",
549 [isaFunction](MlirAttribute other) { return isaFunction(other); },
550 nanobind::arg("other_attribute"), nanobind::sig(kIsinstanceSig));
551 def("__repr__", [superCls, captureTypeName](nanobind::object self) {
552 return nanobind::cast<std::string>(
553 nanobind::repr(superCls(self))
554 .attr("replace")(superCls.attr("__name__"), captureTypeName));
555 });
556 if (getTypeIDFunction) {
558 "get_static_typeid",
559 [getTypeIDFunction]() { return getTypeIDFunction(); },
560 // clang-format off
561 nanobind::sig("def get_static_typeid() -> " MAKE_MLIR_PYTHON_QUALNAME("ir.TypeID"))
562 // clang-format on
563 );
564 nanobind::module_::import_(MAKE_MLIR_PYTHON_QUALNAME("ir"))
566 getTypeIDFunction())(nanobind::cpp_function(
567 [thisClass = thisClass](const nanobind::object &mlirAttribute) {
568 return thisClass(mlirAttribute);
569 }));
570 }
571 }
572};
573
574/// Creates a custom subclass of mlir.ir.Type, implementing a casting
575/// constructor and type checking methods.
577public:
578 using IsAFunctionTy = bool (*)(MlirType);
579 using GetTypeIDFunctionTy = MlirTypeID (*)();
580
581 /// Subclasses by looking up the super-class dynamically.
582 mlir_type_subclass(nanobind::handle scope, const char *typeClassName,
583 IsAFunctionTy isaFunction,
584 GetTypeIDFunctionTy getTypeIDFunction = nullptr)
585 : mlir_type_subclass(scope, typeClassName, isaFunction,
586 irModule().attr("Type"), getTypeIDFunction) {}
587
588 /// Subclasses with a provided mlir.ir.Type super-class. This must
589 /// be used if the subclass is being defined in the same extension module
590 /// as the mlir.ir class (otherwise, it will trigger a recursive
591 /// initialization).
592 mlir_type_subclass(nanobind::handle scope, const char *typeClassName,
593 IsAFunctionTy isaFunction,
594 const nanobind::object &superCls,
595 GetTypeIDFunctionTy getTypeIDFunction = nullptr)
596 : pure_subclass(scope, typeClassName, superCls) {
597 // Casting constructor. Note that it is hard, if not impossible, to properly
598 // call chain to parent `__init__` in nanobind due to its special handling
599 // for init functions that don't have a fully constructed self-reference,
600 // which makes it impossible to forward it to `__init__` of a superclass.
601 // Instead, provide a custom `__new__` and call that of a superclass, which
602 // eventually calls `__init__` of the superclass. Since attribute subclasses
603 // have no additional members, we can just return the instance thus created
604 // without amending it.
605 std::string captureTypeName(
606 typeClassName); // As string in case if typeClassName is not static.
607 nanobind::object newCf = nanobind::cpp_function(
608 [superCls, isaFunction, captureTypeName](nanobind::object cls,
609 nanobind::object otherType) {
610 MlirType rawType;
611 if (!nanobind::try_cast<MlirType>(otherType, rawType) ||
612 !isaFunction(rawType)) {
613 auto origRepr =
614 nanobind::cast<std::string>(nanobind::repr(otherType));
615 throw std::invalid_argument(
616 nanobind::detail::join("Cannot cast type to ", captureTypeName,
617 " (from ", origRepr, ")"));
618 }
619 nanobind::object self = superCls.attr("__new__")(cls, otherType);
620 return self;
621 },
622 nanobind::name("__new__"), nanobind::arg("cls"),
623 nanobind::arg("cast_from_type"));
624 thisClass.attr("__new__") = newCf;
625
626 // 'isinstance' method.
627 static const char kIsinstanceSig[] =
628 // clang-format off
629 "def isinstance(other_type: " MAKE_MLIR_PYTHON_QUALNAME("ir.Type") ") -> bool";
630 // clang-format on
632 "isinstance",
633 [isaFunction](MlirType other) { return isaFunction(other); },
634 nanobind::arg("other_type"), nanobind::sig(kIsinstanceSig));
635 def("__repr__", [superCls, captureTypeName](nanobind::object self) {
636 return nanobind::cast<std::string>(
637 nanobind::repr(superCls(self))
638 .attr("replace")(superCls.attr("__name__"), captureTypeName));
639 });
640 if (getTypeIDFunction) {
641 // 'get_static_typeid' method.
642 // This is modeled as a static method instead of a static property because
643 // `def_property_readonly_static` is not available in `pure_subclass` and
644 // we do not want to introduce the complexity that pybind uses to
645 // implement it.
647 "get_static_typeid",
648 [getTypeIDFunction]() { return getTypeIDFunction(); },
649 // clang-format off
650 nanobind::sig("def get_static_typeid() -> " MAKE_MLIR_PYTHON_QUALNAME("ir.TypeID"))
651 // clang-format on
652 );
653 nanobind::module_::import_(MAKE_MLIR_PYTHON_QUALNAME("ir"))
655 getTypeIDFunction())(nanobind::cpp_function(
656 [thisClass = thisClass](const nanobind::object &mlirType) {
657 return thisClass(mlirType);
658 }));
659 }
660 }
661};
662
663/// Creates a custom subclass of mlir.ir.Value, implementing a casting
664/// constructor and type checking methods.
666public:
667 using IsAFunctionTy = bool (*)(MlirValue);
668
669 /// Subclasses by looking up the super-class dynamically.
670 mlir_value_subclass(nanobind::handle scope, const char *valueClassName,
671 IsAFunctionTy isaFunction)
672 : mlir_value_subclass(scope, valueClassName, isaFunction,
673 irModule().attr("Value")) {}
674
675 /// Subclasses with a provided mlir.ir.Value super-class. This must
676 /// be used if the subclass is being defined in the same extension module
677 /// as the mlir.ir class (otherwise, it will trigger a recursive
678 /// initialization).
679 mlir_value_subclass(nanobind::handle scope, const char *valueClassName,
680 IsAFunctionTy isaFunction,
681 const nanobind::object &superCls)
682 : pure_subclass(scope, valueClassName, superCls) {
683 // Casting constructor. Note that it is hard, if not impossible, to properly
684 // call chain to parent `__init__` in nanobind due to its special handling
685 // for init functions that don't have a fully constructed self-reference,
686 // which makes it impossible to forward it to `__init__` of a superclass.
687 // Instead, provide a custom `__new__` and call that of a superclass, which
688 // eventually calls `__init__` of the superclass. Since attribute subclasses
689 // have no additional members, we can just return the instance thus created
690 // without amending it.
691 std::string captureValueName(
692 valueClassName); // As string in case if valueClassName is not static.
693 nanobind::object newCf = nanobind::cpp_function(
694 [superCls, isaFunction, captureValueName](nanobind::object cls,
695 nanobind::object otherValue) {
696 MlirValue rawValue;
697 if (!nanobind::try_cast<MlirValue>(otherValue, rawValue) ||
698 !isaFunction(rawValue)) {
699 auto origRepr =
700 nanobind::cast<std::string>(nanobind::repr(otherValue));
701 throw std::invalid_argument(nanobind::detail::join(
702 "Cannot cast value to ", captureValueName, " (from ", origRepr,
703 ")"));
704 }
705 nanobind::object self = superCls.attr("__new__")(cls, otherValue);
706 return self;
707 },
708 nanobind::name("__new__"), nanobind::arg("cls"),
709 nanobind::arg("cast_from_value"));
710 thisClass.attr("__new__") = newCf;
711
712 // 'isinstance' method.
713 static const char kIsinstanceSig[] =
714 // clang-format off
715 "def isinstance(other_value: " MAKE_MLIR_PYTHON_QUALNAME("ir.Value") ") -> bool";
716 // clang-format on
718 "isinstance",
719 [isaFunction](MlirValue other) { return isaFunction(other); },
720 nanobind::arg("other_value"), nanobind::sig(kIsinstanceSig));
721 }
722};
723
724} // namespace nanobind_adaptors
725
726} // namespace python
727} // namespace mlir
728
729#endif // MLIR_BINDINGS_PYTHON_NANOBINDADAPTORS_H
static PyObject * mlirPythonTypeIDToCapsule(MlirTypeID typeID)
Creates a capsule object encapsulating the raw C-API MlirTypeID.
Definition Interop.h:348
#define MLIR_PYTHON_MAYBE_DOWNCAST_ATTR
Attribute on MLIR Python objects that expose a function for downcasting the corresponding Python obje...
Definition Interop.h:118
static MlirBlock mlirPythonCapsuleToBlock(PyObject *capsule)
Extracts an MlirBlock from a capsule as produced from mlirPythonBlockToCapsule.
Definition Interop.h:206
static MlirOperation mlirPythonCapsuleToOperation(PyObject *capsule)
Extracts an MlirOperations from a capsule as produced from mlirPythonOperationToCapsule.
Definition Interop.h:338
static MlirFrozenRewritePatternSet mlirPythonCapsuleToFrozenRewritePatternSet(PyObject *capsule)
Extracts an MlirFrozenRewritePatternSet from a capsule as produced from mlirPythonFrozenRewritePatter...
Definition Interop.h:302
#define MLIR_PYTHON_CAPI_PTR_ATTR
Attribute on MLIR Python objects that expose their C-API pointer.
Definition Interop.h:97
static MlirAttribute mlirPythonCapsuleToAttribute(PyObject *capsule)
Extracts an MlirAttribute from a capsule as produced from mlirPythonAttributeToCapsule.
Definition Interop.h:189
static PyObject * mlirPythonTypeToCapsule(MlirType type)
Creates a capsule object encapsulating the raw C-API MlirType.
Definition Interop.h:367
static MlirAffineMap mlirPythonCapsuleToAffineMap(PyObject *capsule)
Extracts an MlirAffineMap from a capsule as produced from mlirPythonAffineMapToCapsule.
Definition Interop.h:395
static PyObject * mlirPythonOperationToCapsule(MlirOperation operation)
Creates a capsule object encapsulating the raw C-API MlirOperation.
Definition Interop.h:330
static PyObject * mlirPythonAttributeToCapsule(MlirAttribute attribute)
Creates a capsule object encapsulating the raw C-API MlirAttribute.
Definition Interop.h:180
#define MLIR_PYTHON_CAPI_FACTORY_ATTR
Attribute on MLIR Python objects that exposes a factory function for constructing the corresponding P...
Definition Interop.h:110
static MlirModule mlirPythonCapsuleToModule(PyObject *capsule)
Extracts an MlirModule from a capsule as produced from mlirPythonModuleToCapsule.
Definition Interop.h:282
static MlirContext mlirPythonCapsuleToContext(PyObject *capsule)
Extracts a MlirContext from a capsule as produced from mlirPythonContextToCapsule.
Definition Interop.h:224
static MlirTypeID mlirPythonCapsuleToTypeID(PyObject *capsule)
Extracts an MlirTypeID from a capsule as produced from mlirPythonTypeIDToCapsule.
Definition Interop.h:357
static PyObject * mlirPythonAffineMapToCapsule(MlirAffineMap affineMap)
Creates a capsule object encapsulating the raw C-API MlirAffineMap.
Definition Interop.h:386
static PyObject * mlirPythonLocationToCapsule(MlirLocation loc)
Creates a capsule object encapsulating the raw C-API MlirLocation.
Definition Interop.h:255
static MlirDialectRegistry mlirPythonCapsuleToDialectRegistry(PyObject *capsule)
Extracts an MlirDialectRegistry from a capsule as produced from mlirPythonDialectRegistryToCapsule.
Definition Interop.h:245
#define MAKE_MLIR_PYTHON_QUALNAME(local)
Definition Interop.h:57
static MlirType mlirPythonCapsuleToType(PyObject *capsule)
Extracts an MlirType from a capsule as produced from mlirPythonTypeToCapsule.
Definition Interop.h:376
static MlirValue mlirPythonCapsuleToValue(PyObject *capsule)
Extracts an MlirValue from a capsule as produced from mlirPythonValueToCapsule.
Definition Interop.h:454
static PyObject * mlirPythonValueToCapsule(MlirValue value)
Creates a capsule object encapsulating the raw C-API MlirValue.
Definition Interop.h:445
static MlirPassManager mlirPythonCapsuleToPassManager(PyObject *capsule)
Extracts an MlirPassManager from a capsule as produced from mlirPythonPassManagerToCapsule.
Definition Interop.h:320
static PyObject * mlirPythonModuleToCapsule(MlirModule module)
Creates a capsule object encapsulating the raw C-API MlirModule.
Definition Interop.h:273
static PyObject * mlirPythonFrozenRewritePatternSetToCapsule(MlirFrozenRewritePatternSet pm)
Creates a capsule object encapsulating the raw C-API MlirFrozenRewritePatternSet.
Definition Interop.h:293
static MlirLocation mlirPythonCapsuleToLocation(PyObject *capsule)
Extracts an MlirLocation from a capsule as produced from mlirPythonLocationToCapsule.
Definition Interop.h:264
#define MLIR_PYTHON_CAPI_TYPE_CASTER_REGISTER_ATTR
Attribute on main C extension module (_mlir) that corresponds to the type caster registration binding...
Definition Interop.h:130
static PyObject * mlirPythonDialectRegistryToCapsule(MlirDialectRegistry registry)
Creates a capsule object encapsulating the raw C-API MlirDialectRegistry.
Definition Interop.h:235
if(!isCopyOut)
Safely calls Python initialization code on first use, avoiding deadlocks.
mlir_attribute_subclass(nanobind::handle scope, const char *attrClassName, IsAFunctionTy isaFunction, GetTypeIDFunctionTy getTypeIDFunction=nullptr)
Subclasses by looking up the super-class dynamically.
mlir_attribute_subclass(nanobind::handle scope, const char *typeClassName, IsAFunctionTy isaFunction, const nanobind::object &superCls, GetTypeIDFunctionTy getTypeIDFunction=nullptr)
Subclasses with a provided mlir.ir.Attribute super-class.
mlir_type_subclass(nanobind::handle scope, const char *typeClassName, IsAFunctionTy isaFunction, const nanobind::object &superCls, GetTypeIDFunctionTy getTypeIDFunction=nullptr)
Subclasses with a provided mlir.ir.Type super-class.
mlir_type_subclass(nanobind::handle scope, const char *typeClassName, IsAFunctionTy isaFunction, GetTypeIDFunctionTy getTypeIDFunction=nullptr)
Subclasses by looking up the super-class dynamically.
mlir_value_subclass(nanobind::handle scope, const char *valueClassName, IsAFunctionTy isaFunction, const nanobind::object &superCls)
Subclasses with a provided mlir.ir.Value super-class.
mlir_value_subclass(nanobind::handle scope, const char *valueClassName, IsAFunctionTy isaFunction)
Subclasses by looking up the super-class dynamically.
pure_subclass & def(const char *name, Func &&f, const Extra &...extra)
pure_subclass & def_property_readonly(const char *name, Func &&f, const Extra &...extra)
pure_subclass(nanobind::handle scope, const char *derivedClassName, const nanobind::object &superClass)
pure_subclass & def_classmethod(const char *name, Func &&f, const Extra &...extra)
pure_subclass & def_staticmethod(const char *name, Func &&f, const Extra &...extra)
static bool mlirPassManagerIsNull(MlirPassManager passManager)
Checks if a PassManager is null.
Definition Pass.h:65
static bool mlirAffineMapIsNull(MlirAffineMap affineMap)
Checks whether an affine map is null.
Definition AffineMap.h:47
static bool mlirTypeIsNull(MlirType type)
Checks whether a type is null.
Definition IR.h:1165
static bool mlirContextIsNull(MlirContext context)
Checks whether a context is null.
Definition IR.h:104
static bool mlirBlockIsNull(MlirBlock block)
Checks whether a block is null.
Definition IR.h:946
static bool mlirLocationIsNull(MlirLocation location)
Checks if the location is null.
Definition IR.h:370
static bool mlirDialectRegistryIsNull(MlirDialectRegistry registry)
Checks if the dialect registry is null.
Definition IR.h:244
static bool mlirTypeIDIsNull(MlirTypeID typeID)
Checks whether a type id is null.
Definition Support.h:201
Include the generated interface declarations.
bool pyErrClearIfFalse(bool val)
Clears the Python error indicator if the given condition val is false and returns the condition.
std::string join(const Ts &...args)
Helper function to concatenate arguments into a std::string.
static std::optional< nanobind::object > mlirApiObjectToCapsule(nanobind::handle apiObject)
Helper to convert a presumed MLIR API object to a capsule, accepting either an explicit Capsule (whic...
A pointer to a sized fragment of a string, not necessarily null-terminated.
Definition Support.h:78
static handle from_cpp(MlirAffineMap v, rv_policy, cleanup_list *cleanup) noexcept
NB_TYPE_CASTER(MlirAffineMap, const_name(MAKE_MLIR_PYTHON_QUALNAME("ir.AffineMap"))) bool from_python(handle src
static handle from_cpp(MlirAttribute v, rv_policy, cleanup_list *cleanup) noexcept
NB_TYPE_CASTER(MlirAttribute, const_name(MAKE_MLIR_PYTHON_QUALNAME("ir.Attribute"))) bool from_python(handle src
NB_TYPE_CASTER(MlirBlock, const_name(MAKE_MLIR_PYTHON_QUALNAME("ir.Block"))) bool from_python(handle src
NB_TYPE_CASTER(MlirContext, const_name(MAKE_MLIR_PYTHON_QUALNAME("ir.Context"))) bool from_python(handle src
NB_TYPE_CASTER(MlirDialectRegistry, const_name(MAKE_MLIR_PYTHON_QUALNAME("ir.DialectRegistry"))) bool from_python(handle src
static handle from_cpp(MlirDialectRegistry v, rv_policy, cleanup_list *cleanup) noexcept
NB_TYPE_CASTER(MlirFrozenRewritePatternSet, const_name(MAKE_MLIR_PYTHON_QUALNAME("rewrite.FrozenRewritePatternSet"))) bool from_python(handle src
static handle from_cpp(MlirFrozenRewritePatternSet v, rv_policy, handle) noexcept
static handle from_cpp(MlirLocation v, rv_policy, cleanup_list *cleanup) noexcept
NB_TYPE_CASTER(MlirLocation, const_name(MAKE_MLIR_PYTHON_QUALNAME("ir.Location"))) bool from_python(handle src
NB_TYPE_CASTER(MlirModule, const_name(MAKE_MLIR_PYTHON_QUALNAME("ir.Module"))) bool from_python(handle src
static handle from_cpp(MlirModule v, rv_policy, cleanup_list *cleanup) noexcept
NB_TYPE_CASTER(MlirOperation, const_name(MAKE_MLIR_PYTHON_QUALNAME("ir.Operation"))) bool from_python(handle src
static handle from_cpp(MlirOperation v, rv_policy, cleanup_list *cleanup) noexcept
NB_TYPE_CASTER(MlirPassManager, const_name(MAKE_MLIR_PYTHON_QUALNAME("passmanager.PassManager"))) bool from_python(handle src
static handle from_cpp(MlirStringRef s, rv_policy, cleanup_list *cleanup) noexcept
static handle from_cpp(MlirTypeID v, rv_policy, cleanup_list *cleanup) noexcept
NB_TYPE_CASTER(MlirTypeID, const_name(MAKE_MLIR_PYTHON_QUALNAME("ir.TypeID"))) bool from_python(handle src
NB_TYPE_CASTER(MlirType, const_name(MAKE_MLIR_PYTHON_QUALNAME("ir.Type"))) bool from_python(handle src
static handle from_cpp(MlirType t, rv_policy, cleanup_list *cleanup) noexcept
uint8_t cleanup_list *cleanup noexcept
NB_TYPE_CASTER(MlirValue, const_name(MAKE_MLIR_PYTHON_QUALNAME("ir.Value"))) bool from_python(handle src
static handle from_cpp(MlirValue v, rv_policy, cleanup_list *cleanup) noexcept