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