[Mlir-commits] [mlir] [MLIR][Python] Add Python and C API of `mlir::DynamicType` (PR #182751)

llvmlistbot at llvm.org llvmlistbot at llvm.org
Sun Feb 22 20:06:40 PST 2026


https://github.com/PragmaTwice updated https://github.com/llvm/llvm-project/pull/182751

>From 28e5faa7b7ddf92f19593fdb57978da8c8371115 Mon Sep 17 00:00:00 2001
From: PragmaTwice <twice at apache.org>
Date: Sun, 22 Feb 2026 23:49:48 +0800
Subject: [PATCH 1/3] [MLIR][Python] Add Python and C API of mlir::DynamicType

---
 mlir/include/mlir-c/ExtensibleDialect.h     | 28 ++++++++
 mlir/include/mlir/Bindings/Python/IRTypes.h | 12 ++++
 mlir/lib/Bindings/Python/IRTypes.cpp        | 57 +++++++++++++++++
 mlir/lib/CAPI/IR/ExtensibleDialect.cpp      | 38 +++++++++++
 mlir/test/python/dialects/irdl.py           | 71 +++++++++++++++++++++
 5 files changed, 206 insertions(+)

diff --git a/mlir/include/mlir-c/ExtensibleDialect.h b/mlir/include/mlir-c/ExtensibleDialect.h
index 6901b2ec15d37..bfc06d7f10d57 100644
--- a/mlir/include/mlir-c/ExtensibleDialect.h
+++ b/mlir/include/mlir-c/ExtensibleDialect.h
@@ -32,6 +32,7 @@ extern "C" {
   typedef struct name name
 
 DEFINE_C_API_STRUCT(MlirDynamicOpTrait, void);
+DEFINE_C_API_STRUCT(MlirDynamicTypeDefinition, void);
 
 #undef DEFINE_C_API_STRUCT
 
@@ -73,6 +74,33 @@ typedef struct {
 MLIR_CAPI_EXPORTED MlirDynamicOpTrait mlirDynamicOpTraitCreate(
     MlirTypeID typeID, MlirDynamicOpTraitCallbacks callbacks, void *userData);
 
+/// Check if the given dialect is an extensible dialect.
+MLIR_CAPI_EXPORTED bool mlirDialectIsAExtensibleDialect(MlirDialect dialect);
+
+/// Look up a registered type definition by type name in the given dialect.
+/// Note that the dialect must be an extensible dialect.
+MLIR_CAPI_EXPORTED MlirDynamicTypeDefinition
+mlirExtensibleDialectLookupTypeDefinition(MlirDialect dialect,
+                                          MlirStringRef typeName);
+
+/// Check if the given type is a dynamic type.
+MLIR_CAPI_EXPORTED bool mlirTypeIsADynamicType(MlirType type);
+
+/// Get the type ID of a dynamic type.
+MLIR_CAPI_EXPORTED MlirTypeID mlirDynamicTypeGetTypeID(void);
+
+/// Get a dynamic type by instantiating the given type definition with the
+/// provided attributes.
+MLIR_CAPI_EXPORTED MlirType mlirDynamicTypeGet(
+    MlirDynamicTypeDefinition typeDef, MlirAttribute *attrs, intptr_t numAttrs);
+
+/// Get the number of parameters in the given dynamic type.
+MLIR_CAPI_EXPORTED intptr_t mlirDynamicTypeGetNumParams(MlirType type);
+
+/// Get the parameter at the given index in the provided dynamic type.
+MLIR_CAPI_EXPORTED MlirAttribute mlirDynamicTypeGetParam(MlirType type,
+                                                         intptr_t index);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/mlir/include/mlir/Bindings/Python/IRTypes.h b/mlir/include/mlir/Bindings/Python/IRTypes.h
index 4a8c5904b55c8..b29483ef78e5d 100644
--- a/mlir/include/mlir/Bindings/Python/IRTypes.h
+++ b/mlir/include/mlir/Bindings/Python/IRTypes.h
@@ -446,6 +446,18 @@ class MLIR_PYTHON_API_EXPORTED PyOpaqueType
   static void bindDerived(ClassTy &c);
 };
 
+class MLIR_PYTHON_API_EXPORTED PyDynamicType
+    : public PyConcreteType<PyDynamicType> {
+public:
+  static constexpr IsAFunctionTy isaFunction = mlirTypeIsADynamicType;
+  static constexpr GetTypeIDFunctionTy getTypeIdFunction =
+      mlirDynamicTypeGetTypeID;
+  static constexpr const char *pyClassName = "DynamicType";
+  using PyConcreteType::PyConcreteType;
+
+  static void bindDerived(ClassTy &c);
+};
+
 MLIR_PYTHON_API_EXPORTED void populateIRTypes(nanobind::module_ &m);
 } // namespace MLIR_BINDINGS_PYTHON_DOMAIN
 } // namespace python
diff --git a/mlir/lib/Bindings/Python/IRTypes.cpp b/mlir/lib/Bindings/Python/IRTypes.cpp
index ec1c223c99f22..f0a8b0b0bfb86 100644
--- a/mlir/lib/Bindings/Python/IRTypes.cpp
+++ b/mlir/lib/Bindings/Python/IRTypes.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 // clang-format off
+#include "mlir-c/ExtensibleDialect.h"
 #include "mlir/Bindings/Python/IRCore.h"
 #include "mlir/Bindings/Python/IRTypes.h"
 // clang-format on
@@ -841,6 +842,61 @@ void PyOpaqueType::bindDerived(ClassTy &c) {
       "Returns the data for the Opaque type as a string.");
 }
 
+void PyDynamicType::bindDerived(ClassTy &c) {
+  c.def_static(
+      "get",
+      [](const std::string &fullTypeName, const std::vector<PyAttribute> &attrs,
+         DefaultingPyMlirContext context) {
+        size_t dotPos = fullTypeName.find('.');
+        if (dotPos == std::string::npos) {
+          throw nb::value_error("Expected full type name to be in the format "
+                                "'<dialectName>.<typeName>'.");
+        }
+
+        std::string dialectName = fullTypeName.substr(0, dotPos);
+        std::string typeName = fullTypeName.substr(dotPos + 1);
+        PyDialects dialects(context->getRef());
+        MlirDialect dialect = dialects.getDialectForKey(dialectName, false);
+        if (!mlirDialectIsAExtensibleDialect(dialect)) {
+          throw nb::value_error(
+              ("Dialect '" + dialectName + "' is not an extensible dialect.")
+                  .c_str());
+        }
+
+        MlirDynamicTypeDefinition typeDef =
+            mlirExtensibleDialectLookupTypeDefinition(
+                dialect, toMlirStringRef(typeName));
+        if (typeDef.ptr == nullptr) {
+          throw nb::value_error(("Dialect '" + dialectName +
+                                 "' does not contain a type named '" +
+                                 typeName + "'.")
+                                    .c_str());
+        }
+
+        std::vector<MlirAttribute> mlirAttrs;
+        mlirAttrs.reserve(attrs.size());
+        for (const auto &attr : attrs)
+          mlirAttrs.push_back(attr.get());
+        MlirType t =
+            mlirDynamicTypeGet(typeDef, mlirAttrs.data(), mlirAttrs.size());
+        return PyDynamicType(context->getRef(), t);
+      },
+      nb::arg("full_type_name"), nb::arg("attributes"),
+      nb::arg("context") = nb::none(), "Create a dynamic type.");
+  c.def_prop_ro(
+      "params",
+      [](PyDynamicType &self) {
+        size_t numParams = mlirDynamicTypeGetNumParams(self);
+        std::vector<PyAttribute> params;
+        params.reserve(numParams);
+        for (size_t i = 0; i < numParams; ++i)
+          params.emplace_back(self.getContext(),
+                              mlirDynamicTypeGetParam(self, i));
+        return params;
+      },
+      "Returns the parameters of the dynamic type as a list of attributes.");
+}
+
 void populateIRTypes(nb::module_ &m) {
   PyIntegerType::bind(m);
   PyFloatType::bind(m);
@@ -872,6 +928,7 @@ void populateIRTypes(nb::module_ &m) {
   PyTupleType::bind(m);
   PyFunctionType::bind(m);
   PyOpaqueType::bind(m);
+  PyDynamicType::bind(m);
 }
 } // namespace MLIR_BINDINGS_PYTHON_DOMAIN
 } // namespace python
diff --git a/mlir/lib/CAPI/IR/ExtensibleDialect.cpp b/mlir/lib/CAPI/IR/ExtensibleDialect.cpp
index 2d6608ff1d1cd..cec7e6300721a 100644
--- a/mlir/lib/CAPI/IR/ExtensibleDialect.cpp
+++ b/mlir/lib/CAPI/IR/ExtensibleDialect.cpp
@@ -15,6 +15,7 @@
 using namespace mlir;
 
 DEFINE_C_API_PTR_METHODS(MlirDynamicOpTrait, DynamicOpTrait)
+DEFINE_C_API_PTR_METHODS(MlirDynamicTypeDefinition, DynamicTypeDefinition)
 
 bool mlirDynamicOpTraitAttach(MlirDynamicOpTrait dynamicOpTrait,
                               MlirStringRef opName, MlirContext context) {
@@ -85,3 +86,40 @@ MlirDynamicOpTrait mlirDynamicOpTraitCreate(
   return wrap(
       new mlir::ExternalDynamicOpTrait(unwrap(typeID), callbacks, userData));
 }
+
+bool mlirDialectIsAExtensibleDialect(MlirDialect dialect) {
+  return llvm::isa<mlir::ExtensibleDialect>(unwrap(dialect));
+}
+
+MlirDynamicTypeDefinition
+mlirExtensibleDialectLookupTypeDefinition(MlirDialect dialect,
+                                          MlirStringRef typeName) {
+  return wrap(llvm::cast<mlir::ExtensibleDialect>(unwrap(dialect))
+                  ->lookupTypeDefinition(unwrap(typeName)));
+}
+
+bool mlirTypeIsADynamicType(MlirType type) {
+  return llvm::isa<mlir::DynamicType>(unwrap(type));
+}
+
+MlirTypeID mlirDynamicTypeGetTypeID() {
+  return wrap(mlir::DynamicType::getTypeID());
+}
+
+MlirType mlirDynamicTypeGet(MlirDynamicTypeDefinition typeDef,
+                            MlirAttribute *attrs, intptr_t numAttrs) {
+  llvm::SmallVector<mlir::Attribute> attributes;
+  attributes.reserve(numAttrs);
+  for (intptr_t i = 0; i < numAttrs; ++i)
+    attributes.push_back(unwrap(attrs[i]));
+
+  return wrap(mlir::DynamicType::get(unwrap(typeDef), attributes));
+}
+
+intptr_t mlirDynamicTypeGetNumParams(MlirType type) {
+  return llvm::cast<mlir::DynamicType>(unwrap(type)).getParams().size();
+}
+
+MlirAttribute mlirDynamicTypeGetParam(MlirType type, intptr_t index) {
+  return wrap(llvm::cast<mlir::DynamicType>(unwrap(type)).getParams()[index]);
+}
diff --git a/mlir/test/python/dialects/irdl.py b/mlir/test/python/dialects/irdl.py
index ed62db9b69968..369065047b6e2 100644
--- a/mlir/test/python/dialects/irdl.py
+++ b/mlir/test/python/dialects/irdl.py
@@ -64,3 +64,74 @@ def testIRDL():
         # CHECK:   "irdl_test.test_op"(%cst) : (f32) -> ()
         # CHECK: }
         m.dump()
+
+
+ at run
+def testIRDLTypes():
+    with Context() as ctx, Location.unknown():
+        module = Module.create()
+        with InsertionPoint(module.body):
+            irdl_test = dialect("irdl_type_test")
+            with InsertionPoint(irdl_test.body):
+                type1 = type_("type1")
+                with InsertionPoint(type1.body):
+                    iattr = base(base_name="#builtin.integer")
+                    parameters([iattr], ["val"])
+                type2 = type_("type2")
+                with InsertionPoint(type2.body):
+                    iattr = base(base_name="#builtin.integer")
+                    unit = is_(UnitAttr.get())
+                    parameters([iattr, unit], ["val1", "val2"])
+                op1 = operation_("op1")
+                with InsertionPoint(op1.body):
+                    t1 = base(base_ref=["irdl_type_test", "type1"])
+                    results_([t1], ["res"], [Variadicity.single])
+
+        # CHECK: module {
+        # CHECK:   irdl.dialect @irdl_type_test {
+        # CHECK:     irdl.type @type1 {
+        # CHECK:       %0 = irdl.base "#builtin.integer"
+        # CHECK:       irdl.parameters(val: %0)
+        # CHECK:     }
+        # CHECK:     irdl.type @type2 {
+        # CHECK:       %0 = irdl.base "#builtin.integer"
+        # CHECK:       %1 = irdl.is unit
+        # CHECK:       irdl.parameters(val1: %0, val2: %1)
+        # CHECK:     }
+        # CHECK:     irdl.operation @op1 {
+        # CHECK:       %0 = irdl.base @irdl_type_test::@type1
+        # CHECK:       irdl.results(res: %0)
+        # CHECK:     }
+        # CHECK:   }
+        # CHECK: }
+        module.operation.verify()
+        module.dump()
+
+        load_dialects(module)
+
+        i32 = IntegerType.get(32)
+        t1 = DynamicType.get("irdl_type_test.type1", [IntegerAttr.get(i32, 42)])
+        # CHECK: !irdl_type_test.type1<42 : i32>
+        t1.dump()
+        # CHECK: 1
+        print(len(t1.params), file=sys.stderr)
+        # CHECK: 42 : i32
+        t1.params[0].dump()
+        t2 = DynamicType.get(
+            "irdl_type_test.type2", [IntegerAttr.get(i32, 33), UnitAttr.get()]
+        )
+        # CHECK: !irdl_type_test.type2<33 : i32, unit>
+        t2.dump()
+        # CHECK: 2
+        print(len(t2.params), file=sys.stderr)
+        # CHECK: 33 : i32
+        t2.params[0].dump()
+        # CHECK: unit
+        t2.params[1].dump()
+
+        m = Module.create()
+        with InsertionPoint(m.body):
+            Operation.create("irdl_type_test.op1", results=[t1])
+
+        # CHECK: %0 = "irdl_type_test.op1"() : () -> !irdl_type_test.type1<42 : i32>
+        m.dump()

>From 55eb54fb7333b43645f7264c746c2bccc4e2a132 Mon Sep 17 00:00:00 2001
From: PragmaTwice <twice at apache.org>
Date: Mon, 23 Feb 2026 12:06:07 +0800
Subject: [PATCH 2/3] add DynamicType.type_name

---
 mlir/include/mlir-c/ExtensibleDialect.h | 12 ++++++++++++
 mlir/lib/Bindings/Python/IRTypes.cpp    |  8 ++++++++
 mlir/lib/CAPI/IR/ExtensibleDialect.cpp  | 14 ++++++++++++++
 mlir/test/python/dialects/irdl.py       |  4 ++++
 4 files changed, 38 insertions(+)

diff --git a/mlir/include/mlir-c/ExtensibleDialect.h b/mlir/include/mlir-c/ExtensibleDialect.h
index bfc06d7f10d57..eabcd080c5d4b 100644
--- a/mlir/include/mlir-c/ExtensibleDialect.h
+++ b/mlir/include/mlir-c/ExtensibleDialect.h
@@ -101,6 +101,18 @@ MLIR_CAPI_EXPORTED intptr_t mlirDynamicTypeGetNumParams(MlirType type);
 MLIR_CAPI_EXPORTED MlirAttribute mlirDynamicTypeGetParam(MlirType type,
                                                          intptr_t index);
 
+/// Get the type definition of the given dynamic type.
+MLIR_CAPI_EXPORTED MlirDynamicTypeDefinition
+mlirDynamicTypeGetTypeDef(MlirType type);
+
+/// Get the name of the given dynamic type definition.
+MLIR_CAPI_EXPORTED MlirStringRef
+mlirDynamicTypeDefinitionGetName(MlirDynamicTypeDefinition typeDef);
+
+/// Get the dialect that the given dynamic type definition belongs to.
+MLIR_CAPI_EXPORTED MlirDialect
+mlirDynamicTypeDefinitionGetDialect(MlirDynamicTypeDefinition typeDef);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/mlir/lib/Bindings/Python/IRTypes.cpp b/mlir/lib/Bindings/Python/IRTypes.cpp
index f0a8b0b0bfb86..b86f9f91ace0a 100644
--- a/mlir/lib/Bindings/Python/IRTypes.cpp
+++ b/mlir/lib/Bindings/Python/IRTypes.cpp
@@ -895,6 +895,14 @@ void PyDynamicType::bindDerived(ClassTy &c) {
         return params;
       },
       "Returns the parameters of the dynamic type as a list of attributes.");
+  c.def_prop_ro("type_name", [](PyDynamicType &self) {
+    MlirDynamicTypeDefinition typeDef = mlirDynamicTypeGetTypeDef(self);
+    MlirStringRef name = mlirDynamicTypeDefinitionGetName(typeDef);
+    MlirDialect dialect = mlirDynamicTypeDefinitionGetDialect(typeDef);
+    MlirStringRef dialectNamespace = mlirDialectGetNamespace(dialect);
+    return std::string(dialectNamespace.data, dialectNamespace.length) + "." +
+           std::string(name.data, name.length);
+  });
 }
 
 void populateIRTypes(nb::module_ &m) {
diff --git a/mlir/lib/CAPI/IR/ExtensibleDialect.cpp b/mlir/lib/CAPI/IR/ExtensibleDialect.cpp
index cec7e6300721a..1659b0afd7354 100644
--- a/mlir/lib/CAPI/IR/ExtensibleDialect.cpp
+++ b/mlir/lib/CAPI/IR/ExtensibleDialect.cpp
@@ -123,3 +123,17 @@ intptr_t mlirDynamicTypeGetNumParams(MlirType type) {
 MlirAttribute mlirDynamicTypeGetParam(MlirType type, intptr_t index) {
   return wrap(llvm::cast<mlir::DynamicType>(unwrap(type)).getParams()[index]);
 }
+
+MlirDynamicTypeDefinition mlirDynamicTypeGetTypeDef(MlirType type) {
+  return wrap(llvm::cast<mlir::DynamicType>(unwrap(type)).getTypeDef());
+}
+
+MlirStringRef
+mlirDynamicTypeDefinitionGetName(MlirDynamicTypeDefinition typeDef) {
+  return wrap(unwrap(typeDef)->getName());
+}
+
+MlirDialect
+mlirDynamicTypeDefinitionGetDialect(MlirDynamicTypeDefinition typeDef) {
+  return wrap(unwrap(typeDef)->getDialect());
+}
diff --git a/mlir/test/python/dialects/irdl.py b/mlir/test/python/dialects/irdl.py
index 369065047b6e2..da5d51d1b082a 100644
--- a/mlir/test/python/dialects/irdl.py
+++ b/mlir/test/python/dialects/irdl.py
@@ -113,6 +113,8 @@ def testIRDLTypes():
         t1 = DynamicType.get("irdl_type_test.type1", [IntegerAttr.get(i32, 42)])
         # CHECK: !irdl_type_test.type1<42 : i32>
         t1.dump()
+        # CHECK: irdl_type_test.type1
+        print(t1.type_name, file=sys.stderr)
         # CHECK: 1
         print(len(t1.params), file=sys.stderr)
         # CHECK: 42 : i32
@@ -122,6 +124,8 @@ def testIRDLTypes():
         )
         # CHECK: !irdl_type_test.type2<33 : i32, unit>
         t2.dump()
+        # CHECK: irdl_type_test.type2
+        print(t2.type_name, file=sys.stderr)
         # CHECK: 2
         print(len(t2.params), file=sys.stderr)
         # CHECK: 33 : i32

>From 9b373a82b762c1e6ffa6ec79a4421e103de39cfd Mon Sep 17 00:00:00 2001
From: Twice <twice at apache.org>
Date: Mon, 23 Feb 2026 12:06:32 +0800
Subject: [PATCH 3/3] Update mlir/lib/Bindings/Python/IRTypes.cpp

Co-authored-by: Rolf Morel <rolfmorel at gmail.com>
---
 mlir/lib/Bindings/Python/IRTypes.cpp | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/mlir/lib/Bindings/Python/IRTypes.cpp b/mlir/lib/Bindings/Python/IRTypes.cpp
index b86f9f91ace0a..aa448fa44d9bf 100644
--- a/mlir/lib/Bindings/Python/IRTypes.cpp
+++ b/mlir/lib/Bindings/Python/IRTypes.cpp
@@ -857,11 +857,10 @@ void PyDynamicType::bindDerived(ClassTy &c) {
         std::string typeName = fullTypeName.substr(dotPos + 1);
         PyDialects dialects(context->getRef());
         MlirDialect dialect = dialects.getDialectForKey(dialectName, false);
-        if (!mlirDialectIsAExtensibleDialect(dialect)) {
+        if (!mlirDialectIsAExtensibleDialect(dialect))
           throw nb::value_error(
               ("Dialect '" + dialectName + "' is not an extensible dialect.")
                   .c_str());
-        }
 
         MlirDynamicTypeDefinition typeDef =
             mlirExtensibleDialectLookupTypeDefinition(



More information about the Mlir-commits mailing list