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// Note: Currently all of the following support cast from nanobind::object to
76// the Mlir* C-API type, but only a few light-weight, context-bound ones
77// implicitly cast the other way because the use case has not yet emerged and
78// ownership is unclear.
79
80/// Casts object <-> MlirAffineMap.
81template <>
82struct type_caster<MlirAffineMap> {
83 NB_TYPE_CASTER(MlirAffineMap,
84 const_name(MAKE_MLIR_PYTHON_QUALNAME("ir.AffineMap")))
85 bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept {
86 if (auto capsule = mlirApiObjectToCapsule(src)) {
87 value = mlirPythonCapsuleToAffineMap(capsule->ptr());
88 return !mlirAffineMapIsNull(value);
89 }
90 return false;
91 }
92 static handle from_cpp(MlirAffineMap v, rv_policy,
93 cleanup_list *cleanup) noexcept {
94 nanobind::object capsule =
95 nanobind::steal<nanobind::object>(mlirPythonAffineMapToCapsule(v));
96 return mlir::python::irModule()
97 .attr("AffineMap")
98 .attr(MLIR_PYTHON_CAPI_FACTORY_ATTR)(capsule)
99 .release();
100 }
101};
102
103/// Casts object <-> MlirAttribute.
104template <>
105struct type_caster<MlirAttribute> {
106 NB_TYPE_CASTER(MlirAttribute,
107 const_name(MAKE_MLIR_PYTHON_QUALNAME("ir.Attribute")))
108 bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept {
109 if (auto capsule = mlirApiObjectToCapsule(src)) {
110 value = mlirPythonCapsuleToAttribute(capsule->ptr());
111 return !mlirAttributeIsNull(value);
112 }
113 return false;
114 }
115 static handle from_cpp(MlirAttribute v, rv_policy,
116 cleanup_list *cleanup) noexcept {
117 nanobind::object capsule =
118 nanobind::steal<nanobind::object>(mlirPythonAttributeToCapsule(v));
119 return mlir::python::irModule()
120 .attr("Attribute")
121 .attr(MLIR_PYTHON_CAPI_FACTORY_ATTR)(capsule)
123 .release();
124 }
125};
126
127/// Casts object -> MlirBlock.
128template <>
129struct type_caster<MlirBlock> {
130 NB_TYPE_CASTER(MlirBlock, const_name(MAKE_MLIR_PYTHON_QUALNAME("ir.Block")))
131 bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept {
132 if (auto capsule = mlirApiObjectToCapsule(src)) {
133 value = mlirPythonCapsuleToBlock(capsule->ptr());
134 return !mlirBlockIsNull(value);
135 }
136 return false;
137 }
138};
139
140/// Casts object -> MlirContext.
141template <>
142struct type_caster<MlirContext> {
143 NB_TYPE_CASTER(MlirContext,
144 const_name(MAKE_MLIR_PYTHON_QUALNAME("ir.Context")))
145 bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept {
146 if (src.is_none()) {
147 // Gets the current thread-bound context.
148 src = mlir::python::irModule().attr("Context").attr("current");
149 }
150 // If there is no context, including thread-bound, emit a warning (since
151 // this function is not allowed to throw) and fail to cast.
152 if (src.is_none()) {
153 PyErr_WarnEx(
154 PyExc_RuntimeWarning,
155 "Passing None as MLIR Context is only allowed inside "
156 "the " MAKE_MLIR_PYTHON_QUALNAME("ir.Context") " context manager.",
157 /*stacklevel=*/1);
158 return false;
159 }
160 if (std::optional<nanobind::object> capsule = mlirApiObjectToCapsule(src)) {
161 value = mlirPythonCapsuleToContext(capsule->ptr());
162 return !mlirContextIsNull(value);
163 }
164 return false;
165 }
166};
167
168/// Casts object <-> MlirDialectRegistry.
169template <>
170struct type_caster<MlirDialectRegistry> {
171 NB_TYPE_CASTER(MlirDialectRegistry,
172 const_name(MAKE_MLIR_PYTHON_QUALNAME("ir.DialectRegistry")))
173 bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept {
174 if (auto capsule = mlirApiObjectToCapsule(src)) {
175 value = mlirPythonCapsuleToDialectRegistry(capsule->ptr());
176 return !mlirDialectRegistryIsNull(value);
177 }
178 return false;
179 }
180 static handle from_cpp(MlirDialectRegistry v, rv_policy,
181 cleanup_list *cleanup) noexcept {
182 nanobind::object capsule = nanobind::steal<nanobind::object>(
184 return mlir::python::irModule()
185 .attr("DialectRegistry")
186 .attr(MLIR_PYTHON_CAPI_FACTORY_ATTR)(capsule)
187 .release();
188 }
189};
190
191/// Casts object <-> MlirLocation.
192template <>
193struct type_caster<MlirLocation> {
194 NB_TYPE_CASTER(MlirLocation,
195 const_name(MAKE_MLIR_PYTHON_QUALNAME("ir.Location")))
196 bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept {
197 if (src.is_none()) {
198 // Gets the current thread-bound context.
199 src = mlir::python::irModule().attr("Location").attr("current");
200 }
201 if (auto capsule = mlirApiObjectToCapsule(src)) {
202 value = mlirPythonCapsuleToLocation(capsule->ptr());
203 return !mlirLocationIsNull(value);
204 }
205 return false;
206 }
207 static handle from_cpp(MlirLocation v, rv_policy,
208 cleanup_list *cleanup) noexcept {
209 nanobind::object capsule =
210 nanobind::steal<nanobind::object>(mlirPythonLocationToCapsule(v));
211 return mlir::python::irModule()
212 .attr("Location")
213 .attr(MLIR_PYTHON_CAPI_FACTORY_ATTR)(capsule)
214 .release();
215 }
216};
217
218/// Casts object <-> MlirModule.
219template <>
220struct type_caster<MlirModule> {
221 NB_TYPE_CASTER(MlirModule, const_name(MAKE_MLIR_PYTHON_QUALNAME("ir.Module")))
222 bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept {
223 if (auto capsule = mlirApiObjectToCapsule(src)) {
224 value = mlirPythonCapsuleToModule(capsule->ptr());
225 return !mlirModuleIsNull(value);
226 }
227 return false;
228 }
229 static handle from_cpp(MlirModule v, rv_policy,
230 cleanup_list *cleanup) noexcept {
231 nanobind::object capsule =
232 nanobind::steal<nanobind::object>(mlirPythonModuleToCapsule(v));
233 return mlir::python::irModule()
234 .attr("Module")
235 .attr(MLIR_PYTHON_CAPI_FACTORY_ATTR)(capsule)
236 .release();
237 };
238};
239
240/// Casts object <-> MlirFrozenRewritePatternSet.
241template <>
242struct type_caster<MlirFrozenRewritePatternSet> {
244 MlirFrozenRewritePatternSet,
245 const_name(MAKE_MLIR_PYTHON_QUALNAME("rewrite.FrozenRewritePatternSet")))
246 bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept {
247 if (auto capsule = mlirApiObjectToCapsule(src)) {
248 value = mlirPythonCapsuleToFrozenRewritePatternSet(capsule->ptr());
249 return value.ptr != nullptr;
250 }
251 return false;
252 }
253 static handle from_cpp(MlirFrozenRewritePatternSet v, rv_policy,
254 handle) noexcept {
255 nanobind::object capsule = nanobind::steal<nanobind::object>(
257 return nanobind::module_::import_(MAKE_MLIR_PYTHON_QUALNAME("rewrite"))
258 .attr("FrozenRewritePatternSet")
259 .attr(MLIR_PYTHON_CAPI_FACTORY_ATTR)(capsule)
260 .release();
261 };
262};
263
264/// Casts object <-> MlirOperation.
265template <>
266struct type_caster<MlirOperation> {
267 NB_TYPE_CASTER(MlirOperation,
268 const_name(MAKE_MLIR_PYTHON_QUALNAME("ir.Operation")))
269 bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept {
270 if (auto capsule = mlirApiObjectToCapsule(src)) {
271 value = mlirPythonCapsuleToOperation(capsule->ptr());
272 return !mlirOperationIsNull(value);
273 }
274 return false;
275 }
276 static handle from_cpp(MlirOperation v, rv_policy,
277 cleanup_list *cleanup) noexcept {
278 if (v.ptr == nullptr)
279 return nanobind::none();
280 nanobind::object capsule =
281 nanobind::steal<nanobind::object>(mlirPythonOperationToCapsule(v));
282 return mlir::python::irModule()
283 .attr("Operation")
284 .attr(MLIR_PYTHON_CAPI_FACTORY_ATTR)(capsule)
285 .release();
286 };
287};
288
289/// Casts object <-> MlirValue.
290template <>
291struct type_caster<MlirValue> {
292 NB_TYPE_CASTER(MlirValue, const_name(MAKE_MLIR_PYTHON_QUALNAME("ir.Value")))
293 bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept {
294 if (auto capsule = mlirApiObjectToCapsule(src)) {
295 value = mlirPythonCapsuleToValue(capsule->ptr());
296 return !mlirValueIsNull(value);
297 }
298 return false;
299 }
300 static handle from_cpp(MlirValue v, rv_policy,
301 cleanup_list *cleanup) noexcept {
302 if (v.ptr == nullptr)
303 return nanobind::none();
304 nanobind::object capsule =
305 nanobind::steal<nanobind::object>(mlirPythonValueToCapsule(v));
306 return mlir::python::irModule()
307 .attr("Value")
308 .attr(MLIR_PYTHON_CAPI_FACTORY_ATTR)(capsule)
310 .release();
311 };
312};
313
314/// Casts object -> MlirPassManager.
315template <>
316struct type_caster<MlirPassManager> {
317 NB_TYPE_CASTER(MlirPassManager, const_name(MAKE_MLIR_PYTHON_QUALNAME(
318 "passmanager.PassManager")))
319 bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept {
320 if (auto capsule = mlirApiObjectToCapsule(src)) {
321 value = mlirPythonCapsuleToPassManager(capsule->ptr());
322 return !mlirPassManagerIsNull(value);
323 }
324 return false;
325 }
326};
327
328/// Casts object <-> MlirTypeID.
329template <>
330struct type_caster<MlirTypeID> {
331 NB_TYPE_CASTER(MlirTypeID, const_name(MAKE_MLIR_PYTHON_QUALNAME("ir.TypeID")))
332 bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept {
333 if (auto capsule = mlirApiObjectToCapsule(src)) {
334 value = mlirPythonCapsuleToTypeID(capsule->ptr());
335 return !mlirTypeIDIsNull(value);
336 }
337 return false;
338 }
339 static handle from_cpp(MlirTypeID v, rv_policy,
340 cleanup_list *cleanup) noexcept {
341 if (v.ptr == nullptr)
342 return nanobind::none();
343 nanobind::object capsule =
344 nanobind::steal<nanobind::object>(mlirPythonTypeIDToCapsule(v));
345 return mlir::python::irModule()
346 .attr("TypeID")
347 .attr(MLIR_PYTHON_CAPI_FACTORY_ATTR)(capsule)
348 .release();
349 };
350};
351
352/// Casts object <-> MlirType.
353template <>
354struct type_caster<MlirType> {
355 NB_TYPE_CASTER(MlirType, const_name(MAKE_MLIR_PYTHON_QUALNAME("ir.Type")))
356 bool from_python(handle src, uint8_t flags, cleanup_list *cleanup) noexcept {
357 if (auto capsule = mlirApiObjectToCapsule(src)) {
358 value = mlirPythonCapsuleToType(capsule->ptr());
359 return !mlirTypeIsNull(value);
360 }
361 return false;
362 }
363 static handle from_cpp(MlirType t, rv_policy,
364 cleanup_list *cleanup) noexcept {
365 nanobind::object capsule =
366 nanobind::steal<nanobind::object>(mlirPythonTypeToCapsule(t));
367 return mlir::python::irModule()
368 .attr("Type")
369 .attr(MLIR_PYTHON_CAPI_FACTORY_ATTR)(capsule)
371 .release();
372 }
373};
374
375/// Casts MlirStringRef -> object.
376template <>
377struct type_caster<MlirStringRef> {
378 NB_TYPE_CASTER(MlirStringRef, const_name("str"))
379 static handle from_cpp(MlirStringRef s, rv_policy,
380 cleanup_list *cleanup) noexcept {
381 return nanobind::str(s.data, s.length).release();
382 }
383};
384
385} // namespace detail
386} // namespace nanobind
387
388namespace mlir {
389namespace python {
391
392/// Provides a facility like nanobind::class_ for defining a new class in a
393/// scope, but this allows extension of an arbitrary Python class, defining
394/// methods on it is a similar way. Classes defined in this way are very similar
395/// to if defined in Python in the usual way but use nanobind machinery to
396/// do it. These are not "real" nanobind classes but pure Python classes
397/// with no relation to a concrete C++ class.
398///
399/// Derived from a discussion upstream:
400/// https://github.com/pybind/pybind11/issues/1193
401/// (plus a fair amount of extra curricular poking)
402/// TODO: If this proves useful, see about including it in nanobind.
404public:
405 pure_subclass(nanobind::handle scope, const char *derivedClassName,
406 const nanobind::object &superClass) {
407 nanobind::object pyType =
408 nanobind::borrow<nanobind::object>((PyObject *)&PyType_Type);
409 nanobind::object metaclass = pyType(superClass);
410 nanobind::dict attributes;
411
412 thisClass = metaclass(derivedClassName, nanobind::make_tuple(superClass),
413 attributes);
414 scope.attr(derivedClassName) = thisClass;
415 thisClass.attr("__module__") = scope.attr("__name__");
416 }
417
418 template <typename Func, typename... Extra>
419 pure_subclass &def(const char *name, Func &&f, const Extra &...extra) {
420 nanobind::object cf = nanobind::cpp_function(
421 std::forward<Func>(f), nanobind::name(name), nanobind::is_method(),
422 nanobind::scope(thisClass), extra...);
423 thisClass.attr(name) = cf;
424 return *this;
425 }
426
427 template <typename Func, typename... Extra>
428 pure_subclass &def_property_readonly(const char *name, Func &&f,
429 const Extra &...extra) {
430 nanobind::object cf = nanobind::cpp_function(
431 std::forward<Func>(f), nanobind::name(name), nanobind::is_method(),
432 nanobind::scope(thisClass), extra...);
433 auto builtinProperty =
434 nanobind::borrow<nanobind::object>((PyObject *)&PyProperty_Type);
435 thisClass.attr(name) = builtinProperty(cf);
436 return *this;
437 }
438
439 template <typename Func, typename... Extra>
440 pure_subclass &def_staticmethod(const char *name, Func &&f,
441 const Extra &...extra) {
442 static_assert(!std::is_member_function_pointer<Func>::value,
443 "def_staticmethod(...) called with a non-static member "
444 "function pointer");
445 nanobind::object cf = nanobind::cpp_function(
446 std::forward<Func>(f),
447 nanobind::name(name), // nanobind::scope(thisClass),
448 extra...);
449 thisClass.attr(name) = cf;
450 return *this;
451 }
452
453 template <typename Func, typename... Extra>
454 pure_subclass &def_classmethod(const char *name, Func &&f,
455 const Extra &...extra) {
456 static_assert(!std::is_member_function_pointer<Func>::value,
457 "def_classmethod(...) called with a non-static member "
458 "function pointer");
459 nanobind::object cf = nanobind::cpp_function(
460 std::forward<Func>(f),
461 nanobind::name(name), // nanobind::scope(thisClass),
462 extra...);
463 static SafeInit<nanobind::object> classmethodFn([]() {
464 return std::make_unique<nanobind::object>(
465 nanobind::module_::import_("builtins").attr("classmethod"));
466 });
467 thisClass.attr(name) = classmethodFn.get()(cf);
468 return *this;
469 }
470
471 nanobind::object get_class() const { return thisClass; }
472
473protected:
474 nanobind::object superClass;
475 nanobind::object thisClass;
476};
477
478/// Creates a custom subclass of mlir.ir.Attribute, implementing a casting
479/// constructor and type checking methods.
481public:
482 using IsAFunctionTy = bool (*)(MlirAttribute);
483 using GetTypeIDFunctionTy = MlirTypeID (*)();
484
485 /// Subclasses by looking up the super-class dynamically.
486 mlir_attribute_subclass(nanobind::handle scope, const char *attrClassName,
487 IsAFunctionTy isaFunction,
488 GetTypeIDFunctionTy getTypeIDFunction = nullptr)
489 : mlir_attribute_subclass(scope, attrClassName, isaFunction,
490 irModule().attr("Attribute"),
491 getTypeIDFunction) {}
492
493 /// Subclasses with a provided mlir.ir.Attribute super-class. This must
494 /// be used if the subclass is being defined in the same extension module
495 /// as the mlir.ir class (otherwise, it will trigger a recursive
496 /// initialization).
497 mlir_attribute_subclass(nanobind::handle scope, const char *typeClassName,
498 IsAFunctionTy isaFunction,
499 const nanobind::object &superCls,
500 GetTypeIDFunctionTy getTypeIDFunction = nullptr)
501 : pure_subclass(scope, typeClassName, superCls) {
502 // Casting constructor. Note that it is hard, if not impossible, to properly
503 // call chain to parent `__init__` in nanobind due to its special handling
504 // for init functions that don't have a fully constructed self-reference,
505 // which makes it impossible to forward it to `__init__` of a superclass.
506 // Instead, provide a custom `__new__` and call that of a superclass, which
507 // eventually calls `__init__` of the superclass. Since attribute subclasses
508 // have no additional members, we can just return the instance thus created
509 // without amending it.
510 std::string captureTypeName(
511 typeClassName); // As string in case if typeClassName is not static.
512 nanobind::object newCf = nanobind::cpp_function(
513 [superCls, isaFunction, captureTypeName](
514 nanobind::object cls, nanobind::object otherAttribute) {
515 MlirAttribute rawAttribute;
516 if (!nanobind::try_cast<MlirAttribute>(otherAttribute,
517 rawAttribute) ||
518 !isaFunction(rawAttribute)) {
519 auto origRepr =
520 nanobind::cast<std::string>(nanobind::repr(otherAttribute));
521 throw std::invalid_argument(nanobind::detail::join(
522 "Cannot cast attribute to ", captureTypeName, " (from ",
523 origRepr, ")"));
524 }
525 nanobind::object self = superCls.attr("__new__")(cls, otherAttribute);
526 return self;
527 },
528 nanobind::name("__new__"), nanobind::arg("cls"),
529 nanobind::arg("cast_from_attr"));
530 thisClass.attr("__new__") = newCf;
531
532 // 'isinstance' method.
533 static const char kIsinstanceSig[] =
534 "def isinstance(other_attribute: " MAKE_MLIR_PYTHON_QUALNAME(
535 "ir") ".Attribute) -> bool";
537 "isinstance",
538 [isaFunction](MlirAttribute other) { return isaFunction(other); },
539 nanobind::arg("other_attribute"), nanobind::sig(kIsinstanceSig));
540 def("__repr__", [superCls, captureTypeName](nanobind::object self) {
541 return nanobind::cast<std::string>(
542 nanobind::repr(superCls(self))
543 .attr("replace")(superCls.attr("__name__"), captureTypeName));
544 });
545 if (getTypeIDFunction) {
547 "get_static_typeid",
548 [getTypeIDFunction]() { return getTypeIDFunction(); },
549 // clang-format off
550 nanobind::sig("def get_static_typeid() -> " MAKE_MLIR_PYTHON_QUALNAME("ir.TypeID"))
551 // clang-format on
552 );
553 nanobind::module_::import_(MAKE_MLIR_PYTHON_QUALNAME("ir"))
555 getTypeIDFunction())(nanobind::cpp_function(
556 [thisClass = thisClass](const nanobind::object &mlirAttribute) {
557 return thisClass(mlirAttribute);
558 }));
559 }
560 }
561};
562
563/// Creates a custom subclass of mlir.ir.Type, implementing a casting
564/// constructor and type checking methods.
566public:
567 using IsAFunctionTy = bool (*)(MlirType);
568 using GetTypeIDFunctionTy = MlirTypeID (*)();
569
570 /// Subclasses by looking up the super-class dynamically.
571 mlir_type_subclass(nanobind::handle scope, const char *typeClassName,
572 IsAFunctionTy isaFunction,
573 GetTypeIDFunctionTy getTypeIDFunction = nullptr)
574 : mlir_type_subclass(scope, typeClassName, isaFunction,
575 irModule().attr("Type"), getTypeIDFunction) {}
576
577 /// Subclasses with a provided mlir.ir.Type super-class. This must
578 /// be used if the subclass is being defined in the same extension module
579 /// as the mlir.ir class (otherwise, it will trigger a recursive
580 /// initialization).
581 mlir_type_subclass(nanobind::handle scope, const char *typeClassName,
582 IsAFunctionTy isaFunction,
583 const nanobind::object &superCls,
584 GetTypeIDFunctionTy getTypeIDFunction = nullptr)
585 : pure_subclass(scope, typeClassName, superCls) {
586 // Casting constructor. Note that it is hard, if not impossible, to properly
587 // call chain to parent `__init__` in nanobind due to its special handling
588 // for init functions that don't have a fully constructed self-reference,
589 // which makes it impossible to forward it to `__init__` of a superclass.
590 // Instead, provide a custom `__new__` and call that of a superclass, which
591 // eventually calls `__init__` of the superclass. Since attribute subclasses
592 // have no additional members, we can just return the instance thus created
593 // without amending it.
594 std::string captureTypeName(
595 typeClassName); // As string in case if typeClassName is not static.
596 nanobind::object newCf = nanobind::cpp_function(
597 [superCls, isaFunction, captureTypeName](nanobind::object cls,
598 nanobind::object otherType) {
599 MlirType rawType;
600 if (!nanobind::try_cast<MlirType>(otherType, rawType) ||
601 !isaFunction(rawType)) {
602 auto origRepr =
603 nanobind::cast<std::string>(nanobind::repr(otherType));
604 throw std::invalid_argument(
605 nanobind::detail::join("Cannot cast type to ", captureTypeName,
606 " (from ", origRepr, ")"));
607 }
608 nanobind::object self = superCls.attr("__new__")(cls, otherType);
609 return self;
610 },
611 nanobind::name("__new__"), nanobind::arg("cls"),
612 nanobind::arg("cast_from_type"));
613 thisClass.attr("__new__") = newCf;
614
615 // 'isinstance' method.
616 static const char kIsinstanceSig[] =
617 // clang-format off
618 "def isinstance(other_type: " MAKE_MLIR_PYTHON_QUALNAME("ir.Type") ") -> bool";
619 // clang-format on
621 "isinstance",
622 [isaFunction](MlirType other) { return isaFunction(other); },
623 nanobind::arg("other_type"), nanobind::sig(kIsinstanceSig));
624 def("__repr__", [superCls, captureTypeName](nanobind::object self) {
625 return nanobind::cast<std::string>(
626 nanobind::repr(superCls(self))
627 .attr("replace")(superCls.attr("__name__"), captureTypeName));
628 });
629 if (getTypeIDFunction) {
630 // 'get_static_typeid' method.
631 // This is modeled as a static method instead of a static property because
632 // `def_property_readonly_static` is not available in `pure_subclass` and
633 // we do not want to introduce the complexity that pybind uses to
634 // implement it.
636 "get_static_typeid",
637 [getTypeIDFunction]() { return getTypeIDFunction(); },
638 // clang-format off
639 nanobind::sig("def get_static_typeid() -> " MAKE_MLIR_PYTHON_QUALNAME("ir.TypeID"))
640 // clang-format on
641 );
642 nanobind::module_::import_(MAKE_MLIR_PYTHON_QUALNAME("ir"))
644 getTypeIDFunction())(nanobind::cpp_function(
645 [thisClass = thisClass](const nanobind::object &mlirType) {
646 return thisClass(mlirType);
647 }));
648 }
649 }
650};
651
652/// Creates a custom subclass of mlir.ir.Value, implementing a casting
653/// constructor and type checking methods.
655public:
656 using IsAFunctionTy = bool (*)(MlirValue);
657
658 /// Subclasses by looking up the super-class dynamically.
659 mlir_value_subclass(nanobind::handle scope, const char *valueClassName,
660 IsAFunctionTy isaFunction)
661 : mlir_value_subclass(scope, valueClassName, isaFunction,
662 irModule().attr("Value")) {}
663
664 /// Subclasses with a provided mlir.ir.Value super-class. This must
665 /// be used if the subclass is being defined in the same extension module
666 /// as the mlir.ir class (otherwise, it will trigger a recursive
667 /// initialization).
668 mlir_value_subclass(nanobind::handle scope, const char *valueClassName,
669 IsAFunctionTy isaFunction,
670 const nanobind::object &superCls)
671 : pure_subclass(scope, valueClassName, superCls) {
672 // Casting constructor. Note that it is hard, if not impossible, to properly
673 // call chain to parent `__init__` in nanobind due to its special handling
674 // for init functions that don't have a fully constructed self-reference,
675 // which makes it impossible to forward it to `__init__` of a superclass.
676 // Instead, provide a custom `__new__` and call that of a superclass, which
677 // eventually calls `__init__` of the superclass. Since attribute subclasses
678 // have no additional members, we can just return the instance thus created
679 // without amending it.
680 std::string captureValueName(
681 valueClassName); // As string in case if valueClassName is not static.
682 nanobind::object newCf = nanobind::cpp_function(
683 [superCls, isaFunction, captureValueName](nanobind::object cls,
684 nanobind::object otherValue) {
685 MlirValue rawValue;
686 if (!nanobind::try_cast<MlirValue>(otherValue, rawValue) ||
687 !isaFunction(rawValue)) {
688 auto origRepr =
689 nanobind::cast<std::string>(nanobind::repr(otherValue));
690 throw std::invalid_argument(nanobind::detail::join(
691 "Cannot cast value to ", captureValueName, " (from ", origRepr,
692 ")"));
693 }
694 nanobind::object self = superCls.attr("__new__")(cls, otherValue);
695 return self;
696 },
697 nanobind::name("__new__"), nanobind::arg("cls"),
698 nanobind::arg("cast_from_value"));
699 thisClass.attr("__new__") = newCf;
700
701 // 'isinstance' method.
702 static const char kIsinstanceSig[] =
703 // clang-format off
704 "def isinstance(other_value: " MAKE_MLIR_PYTHON_QUALNAME("ir.Value") ") -> bool";
705 // clang-format on
707 "isinstance",
708 [isaFunction](MlirValue other) { return isaFunction(other); },
709 nanobind::arg("other_value"), nanobind::sig(kIsinstanceSig));
710 }
711};
712
713} // namespace nanobind_adaptors
714
715} // namespace python
716} // namespace mlir
717
718#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:1160
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:941
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.
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