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