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