[Mlir-commits] [mlir] core: Add `irdl.base` operation (PR #76302)

Fehr Mathieu llvmlistbot at llvm.org
Sat Dec 23 14:09:56 PST 2023


https://github.com/math-fehr created https://github.com/llvm/llvm-project/pull/76302

None

>From 443b17e99354da596a31f4597e864345396fdaa9 Mon Sep 17 00:00:00 2001
From: Mathieu Fehr <mathieu.fehr at gmail.com>
Date: Sat, 23 Dec 2023 17:11:46 +0000
Subject: [PATCH 1/3] WIP

---
 mlir/include/mlir/Dialect/IRDL/IR/IRDLOps.td  | 31 ++++++++++
 .../include/mlir/Dialect/IRDL/IRDLVerifiers.h | 42 +++++++++++++
 mlir/lib/Dialect/IRDL/IR/IRDL.cpp             |  9 +++
 mlir/lib/Dialect/IRDL/IR/IRDLOps.cpp          | 61 +++++++++++++++++++
 mlir/lib/Dialect/IRDL/IRDLVerifiers.cpp       | 33 ++++++++++
 mlir/test/Dialect/IRDL/testd.irdl.mlir        | 48 ++++++++++++---
 mlir/test/Dialect/IRDL/testd.mlir             | 16 ++---
 7 files changed, 225 insertions(+), 15 deletions(-)

diff --git a/mlir/include/mlir/Dialect/IRDL/IR/IRDLOps.td b/mlir/include/mlir/Dialect/IRDL/IR/IRDLOps.td
index 681425f8174426..ae1f893be72ec2 100644
--- a/mlir/include/mlir/Dialect/IRDL/IR/IRDLOps.td
+++ b/mlir/include/mlir/Dialect/IRDL/IR/IRDLOps.td
@@ -451,6 +451,37 @@ def IRDL_IsOp : IRDL_ConstraintOp<"is",
   let assemblyFormat = " $expected ` ` attr-dict ";
 }
 
+def IRDL_BaseOp : IRDL_ConstraintOp<"base",
+    [ParentOneOf<["TypeOp", "AttributeOp", "OperationOp"]>]> {
+  let summary = "Constraints an attribute/type base";
+  let description = [{
+    `irdl.base` defines a constraint that accepts only a single type
+    or attribute base, e.g. an `IntegerType`. The attribute base is defined
+    either by a simpolic reference to the corresponding IRDL definition,
+    or by the name of the base. Named bases are prefixed with `!` or `#`
+    depending on whether they are types or attributes.
+
+    Example:
+
+    ```mlir
+    irdl.type @complex {
+      %0 = irdl.base "!builtin.integer"
+      irdl.parameters(%0)
+    }
+    ```
+
+    The above program defines a `complex` type that expects a single parameter,
+    which is a type with base name `builtin.integer`, which is the name of
+    an `IntegerType` type.
+  }];
+
+  let arguments = (ins OptionalAttr<SymbolRefAttr>:$base_ref,
+                       OptionalAttr<StrAttr>:$base_name);
+  let results = (outs IRDL_AttributeType:$output);
+  let assemblyFormat = " ($base_ref^)? ($base_name^)? ` ` attr-dict";
+  let hasVerifier = 1;
+}
+
 def IRDL_ParametricOp : IRDL_ConstraintOp<"parametric",
     [ParentOneOf<["TypeOp", "AttributeOp", "OperationOp"]>, Pure]> {
   let summary = "Constraints an attribute/type base and its parameters";
diff --git a/mlir/include/mlir/Dialect/IRDL/IRDLVerifiers.h b/mlir/include/mlir/Dialect/IRDL/IRDLVerifiers.h
index f8ce77cbc50e9e..9d8320ddb28b53 100644
--- a/mlir/include/mlir/Dialect/IRDL/IRDLVerifiers.h
+++ b/mlir/include/mlir/Dialect/IRDL/IRDLVerifiers.h
@@ -99,6 +99,48 @@ class IsConstraint : public Constraint {
   Attribute expectedAttribute;
 };
 
+/// A constraint that checks that an attribute is of a
+/// specific attribute definition.
+class BaseAttrConstraint : public Constraint {
+public:
+  BaseAttrConstraint(TypeID baseTypeID, StringRef baseName)
+      : baseTypeID(baseTypeID), baseName(baseName) {}
+
+  virtual ~BaseAttrConstraint() = default;
+
+  LogicalResult verify(function_ref<InFlightDiagnostic()> emitError,
+                       Attribute attr,
+                       ConstraintVerifier &context) const override;
+
+private:
+  /// The expected base attribute typeID.
+  TypeID baseTypeID;
+
+  /// The base attribute name, which is only used for error reporting.
+  StringRef baseName;
+};
+
+/// A constraint that checks that an attribute is of a
+/// specific type definition.
+class BaseTypeConstraint : public Constraint {
+public:
+  BaseTypeConstraint(TypeID baseTypeID, StringRef baseName)
+      : baseTypeID(baseTypeID), baseName(baseName) {}
+
+  virtual ~BaseTypeConstraint() = default;
+
+  LogicalResult verify(function_ref<InFlightDiagnostic()> emitError,
+                       Attribute attr,
+                       ConstraintVerifier &context) const override;
+
+private:
+  /// The expected base type typeID.
+  TypeID baseTypeID;
+
+  /// The base type name, which is only used for error reporting.
+  StringRef baseName;
+};
+
 /// A constraint that checks that an attribute is of a
 /// specific dynamic attribute definition, and that all of its parameters
 /// satisfy the given constraints.
diff --git a/mlir/lib/Dialect/IRDL/IR/IRDL.cpp b/mlir/lib/Dialect/IRDL/IR/IRDL.cpp
index 33c6bb869a643f..1d65af3da27335 100644
--- a/mlir/lib/Dialect/IRDL/IR/IRDL.cpp
+++ b/mlir/lib/Dialect/IRDL/IR/IRDL.cpp
@@ -117,6 +117,15 @@ LogicalResult AttributesOp::verify() {
   return success();
 }
 
+LogicalResult BaseOp::verify() {
+  std::optional<StringRef> baseName = getBaseName();
+  std::optional<SymbolRefAttr> base = getBaseRef();
+  if (baseName.has_value() == base.has_value())
+    return emitOpError() << "the base type or attribute should be specified by "
+                            "either a name or a reference";
+  return success();
+}
+
 /// Parse a value with its variadicity first. By default, the variadicity is
 /// single.
 ///
diff --git a/mlir/lib/Dialect/IRDL/IR/IRDLOps.cpp b/mlir/lib/Dialect/IRDL/IR/IRDLOps.cpp
index e172039712f24c..8fe47ed42a4c0a 100644
--- a/mlir/lib/Dialect/IRDL/IR/IRDLOps.cpp
+++ b/mlir/lib/Dialect/IRDL/IR/IRDLOps.cpp
@@ -37,6 +37,67 @@ std::unique_ptr<Constraint> IsOp::getVerifier(
   return std::make_unique<IsConstraint>(getExpectedAttr());
 }
 
+std::unique_ptr<Constraint> BaseOp::getVerifier(
+    ArrayRef<Value> valueToConstr,
+    DenseMap<TypeOp, std::unique_ptr<DynamicTypeDefinition>> const &types,
+    DenseMap<AttributeOp, std::unique_ptr<DynamicAttrDefinition>> const
+        &attrs) {
+  MLIRContext *ctx = getContext();
+
+  // Case where the input is a symbol reference.
+  // This corresponds to the case where the base is an IRDL type or attribute.
+  if (auto baseRef = getBaseRef()) {
+    Operation *defOp =
+        SymbolTable::lookupNearestSymbolFrom(getOperation(), baseRef.value());
+    if (!defOp) {
+      emitError() << baseRef.value()
+                  << " does not refer to any existing symbol";
+      return nullptr;
+    }
+
+    // A type base constraint.
+    if (auto typeOp = dyn_cast<TypeOp>(defOp)) {
+      DynamicTypeDefinition *typeDef = types.at(typeOp).get();
+      auto name = StringAttr::get(ctx, typeDef->getDialect()->getNamespace() +
+                                           "." + typeDef->getName().str());
+      return std::make_unique<BaseTypeConstraint>(typeDef->getTypeID(), name);
+    }
+
+    // An attribute base constraint.
+    if (auto attrOp = dyn_cast<AttributeOp>(defOp)) {
+      DynamicAttrDefinition *attrDef = attrs.at(attrOp).get();
+      auto name = StringAttr::get(ctx, attrDef->getDialect()->getNamespace() +
+                                           "." + attrDef->getName().str());
+      return std::make_unique<BaseAttrConstraint>(attrDef->getTypeID(), name);
+    }
+
+    llvm_unreachable("verifier should ensure that the referenced operation is "
+                     "either a type or an attribute definition");
+  }
+
+  // Case where the input is string literal.
+  // This corresponds to the case where the base is a registered type or
+  // attribute.
+  StringRef baseName = getBaseName().value();
+  if (baseName[0] == '!') {
+    auto abstractType = AbstractType::lookup(baseName.drop_front(1), ctx);
+    if (!abstractType) {
+      emitError() << "no registered type with name " << baseName;
+      return nullptr;
+    }
+    return std::make_unique<BaseTypeConstraint>(abstractType->get().getTypeID(),
+                                                abstractType->get().getName());
+  }
+
+  auto abstractAttr = AbstractAttribute::lookup(baseName.drop_front(1), ctx);
+  if (!abstractAttr) {
+    emitError() << "no registered attribute with name " << baseName;
+    return nullptr;
+  }
+  return std::make_unique<BaseAttrConstraint>(abstractAttr->get().getTypeID(),
+                                              abstractAttr->get().getName());
+}
+
 std::unique_ptr<Constraint> ParametricOp::getVerifier(
     ArrayRef<Value> valueToConstr,
     DenseMap<TypeOp, std::unique_ptr<DynamicTypeDefinition>> const &types,
diff --git a/mlir/lib/Dialect/IRDL/IRDLVerifiers.cpp b/mlir/lib/Dialect/IRDL/IRDLVerifiers.cpp
index 90b068ba35831b..2310c11ea0e8ed 100644
--- a/mlir/lib/Dialect/IRDL/IRDLVerifiers.cpp
+++ b/mlir/lib/Dialect/IRDL/IRDLVerifiers.cpp
@@ -69,6 +69,39 @@ LogicalResult IsConstraint::verify(function_ref<InFlightDiagnostic()> emitError,
   return failure();
 }
 
+LogicalResult
+BaseAttrConstraint::verify(function_ref<InFlightDiagnostic()> emitError,
+                           Attribute attr, ConstraintVerifier &context) const {
+  if (attr.getTypeID() == baseTypeID)
+    return success();
+
+  if (emitError)
+    return emitError() << "expected base attribute '" << baseName
+                       << "' but got '" << attr.getAbstractAttribute().getName()
+                       << "'";
+  return failure();
+}
+
+LogicalResult
+BaseTypeConstraint::verify(function_ref<InFlightDiagnostic()> emitError,
+                           Attribute attr, ConstraintVerifier &context) const {
+  auto typeAttr = dyn_cast<TypeAttr>(attr);
+  if (!typeAttr) {
+    if (emitError)
+      return emitError() << "expected type, got attribute '" << attr;
+    return failure();
+  }
+
+  Type type = typeAttr.getValue();
+  if (type.getTypeID() == baseTypeID)
+    return success();
+
+  if (emitError)
+    return emitError() << "expected base type '" << baseName << "' but got '"
+                       << type.getAbstractType().getName() << "'";
+  return failure();
+}
+
 LogicalResult DynParametricAttrConstraint::verify(
     function_ref<InFlightDiagnostic()> emitError, Attribute attr,
     ConstraintVerifier &context) const {
diff --git a/mlir/test/Dialect/IRDL/testd.irdl.mlir b/mlir/test/Dialect/IRDL/testd.irdl.mlir
index 684286e4afeb0f..3922bf6aab7b07 100644
--- a/mlir/test/Dialect/IRDL/testd.irdl.mlir
+++ b/mlir/test/Dialect/IRDL/testd.irdl.mlir
@@ -11,6 +11,15 @@ irdl.dialect @testd {
     irdl.parameters(%0)
   }
 
+  // CHECK: irdl.attribute @parametric_attr {
+  // CHECK:  %[[v0:[^ ]*]] = irdl.any
+  // CHECK:  irdl.parameters(%[[v0]])
+  // CHECK: }
+  irdl.attribute @parametric_attr {
+    %0 = irdl.any
+    irdl.parameters(%0)
+  }
+
   // CHECK: irdl.type @attr_in_type_out {
   // CHECK:   %[[v0:[^ ]*]] = irdl.any
   // CHECK:   irdl.parameters(%[[v0]])
@@ -66,15 +75,40 @@ irdl.dialect @testd {
     irdl.results(%0)
   }
 
-  // CHECK: irdl.operation @dynbase {
-  // CHECK:   %[[v0:[^ ]*]] = irdl.any
-  // CHECK:   %[[v1:[^ ]*]] = irdl.parametric @parametric<%[[v0]]>
+  // CHECK: irdl.operation @dyn_type_base {
+  // CHECK:   %[[v1:[^ ]*]] = irdl.base @parametric
   // CHECK:   irdl.results(%[[v1]])
   // CHECK: }
-  irdl.operation @dynbase {
-    %0 = irdl.any
-    %1 = irdl.parametric @parametric<%0>
-    irdl.results(%1)
+  irdl.operation @dyn_type_base {
+    %0 = irdl.base @parametric
+    irdl.results(%0)
+  }
+
+  // CHECK: irdl.operation @dyn_attr_base {
+  // CHECK:   %[[v1:[^ ]*]] = irdl.base @parametric_attr
+  // CHECK:   irdl.results(%[[v1]])
+  // CHECK: }
+  irdl.operation @dyn_attr_base {
+    %0 = irdl.base @parametric_attr
+    irdl.results(%0)
+  }
+
+  // CHECK: irdl.operation @type_base {
+  // CHECK:   %[[v1:[^ ]*]] = irdl.base "!builtin.integer"
+  // CHECK:   irdl.results(%[[v1]])
+  // CHECK: }
+  irdl.operation @type_base {
+    %0 = irdl.base "!builtin.integer"
+    irdl.results(%0)
+  }
+
+  // CHECK: irdl.operation @attr_base {
+  // CHECK:   %[[v1:[^ ]*]] = irdl.base "#builtin.integer"
+  // CHECK:   irdl.results(%[[v1]])
+  // CHECK: }
+  irdl.operation @attr_base {
+    %0 = irdl.base "#builtin.integer"
+    irdl.results(%0)
   }
 
   // CHECK: irdl.operation @dynparams {
diff --git a/mlir/test/Dialect/IRDL/testd.mlir b/mlir/test/Dialect/IRDL/testd.mlir
index bb1e9f46356411..721a92d4241e62 100644
--- a/mlir/test/Dialect/IRDL/testd.mlir
+++ b/mlir/test/Dialect/IRDL/testd.mlir
@@ -124,20 +124,20 @@ func.func @succeededAnyConstraint() {
 //===----------------------------------------------------------------------===//
 
 func.func @succeededDynBaseConstraint() {
-  // CHECK: "testd.dynbase"() : () -> !testd.parametric<i32>
-  "testd.dynbase"() : () -> !testd.parametric<i32>
-  // CHECK: "testd.dynbase"() : () -> !testd.parametric<i64>
-  "testd.dynbase"() : () -> !testd.parametric<i64>
-  // CHECK: "testd.dynbase"() : () -> !testd.parametric<!testd.parametric<i64>>
-  "testd.dynbase"() : () -> !testd.parametric<!testd.parametric<i64>>
+  // CHECK: "testd.dyn_type_base"() : () -> !testd.parametric<i32>
+  "testd.dyn_type_base"() : () -> !testd.parametric<i32>
+  // CHECK: "testd.dyn_type_base"() : () -> !testd.parametric<i64>
+  "testd.dyn_type_base"() : () -> !testd.parametric<i64>
+  // CHECK: "testd.dyn_type_base"() : () -> !testd.parametric<!testd.parametric<i64>>
+  "testd.dyn_type_base"() : () -> !testd.parametric<!testd.parametric<i64>>
   return
 }
 
 // -----
 
 func.func @failedDynBaseConstraint() {
-  // expected-error at +1 {{expected base type 'testd.parametric' but got 'i32'}}
-  "testd.dynbase"() : () -> i32
+  // expected-error at +1 {{expected base type 'testd.parametric' but got 'builtin.integer'}}
+  "testd.dyn_type_base"() : () -> i32
   return
 }
 

>From 04a9c0b7f917e397758cadfbe48f7975c56ad9ce Mon Sep 17 00:00:00 2001
From: Mathieu Fehr <mathieu.fehr at gmail.com>
Date: Sat, 23 Dec 2023 20:55:09 +0000
Subject: [PATCH 2/3] Add tests for irdl.base

---
 mlir/test/Dialect/IRDL/testd.irdl.mlir | 16 ++++-----
 mlir/test/Dialect/IRDL/testd.mlir      | 47 ++++++++++++++++++++++++--
 2 files changed, 53 insertions(+), 10 deletions(-)

diff --git a/mlir/test/Dialect/IRDL/testd.irdl.mlir b/mlir/test/Dialect/IRDL/testd.irdl.mlir
index 3922bf6aab7b07..f828d95bdb81d5 100644
--- a/mlir/test/Dialect/IRDL/testd.irdl.mlir
+++ b/mlir/test/Dialect/IRDL/testd.irdl.mlir
@@ -86,29 +86,29 @@ irdl.dialect @testd {
 
   // CHECK: irdl.operation @dyn_attr_base {
   // CHECK:   %[[v1:[^ ]*]] = irdl.base @parametric_attr
-  // CHECK:   irdl.results(%[[v1]])
+  // CHECK:   irdl.attributes {"attr1" = %[[v1]]}
   // CHECK: }
   irdl.operation @dyn_attr_base {
     %0 = irdl.base @parametric_attr
-    irdl.results(%0)
+    irdl.attributes {"attr1" = %0}
   }
 
-  // CHECK: irdl.operation @type_base {
+  // CHECK: irdl.operation @named_type_base {
   // CHECK:   %[[v1:[^ ]*]] = irdl.base "!builtin.integer"
   // CHECK:   irdl.results(%[[v1]])
   // CHECK: }
-  irdl.operation @type_base {
+  irdl.operation @named_type_base {
     %0 = irdl.base "!builtin.integer"
     irdl.results(%0)
   }
 
-  // CHECK: irdl.operation @attr_base {
+  // CHECK: irdl.operation @named_attr_base {
   // CHECK:   %[[v1:[^ ]*]] = irdl.base "#builtin.integer"
-  // CHECK:   irdl.results(%[[v1]])
+  // CHECK:   irdl.attributes {"attr1" = %[[v1]]}
   // CHECK: }
-  irdl.operation @attr_base {
+  irdl.operation @named_attr_base {
     %0 = irdl.base "#builtin.integer"
-    irdl.results(%0)
+    irdl.attributes {"attr1" = %0}
   }
 
   // CHECK: irdl.operation @dynparams {
diff --git a/mlir/test/Dialect/IRDL/testd.mlir b/mlir/test/Dialect/IRDL/testd.mlir
index 721a92d4241e62..333bb96eb2e60f 100644
--- a/mlir/test/Dialect/IRDL/testd.mlir
+++ b/mlir/test/Dialect/IRDL/testd.mlir
@@ -120,7 +120,7 @@ func.func @succeededAnyConstraint() {
 // -----
 
 //===----------------------------------------------------------------------===//
-// Dynamic base constraint
+// Base constraints
 //===----------------------------------------------------------------------===//
 
 func.func @succeededDynBaseConstraint() {
@@ -130,12 +130,16 @@ func.func @succeededDynBaseConstraint() {
   "testd.dyn_type_base"() : () -> !testd.parametric<i64>
   // CHECK: "testd.dyn_type_base"() : () -> !testd.parametric<!testd.parametric<i64>>
   "testd.dyn_type_base"() : () -> !testd.parametric<!testd.parametric<i64>>
+  // CHECK: "testd.dyn_attr_base"() {attr1 = #testd.parametric_attr<i32>} : () -> ()
+  "testd.dyn_attr_base"() {attr1 = #testd.parametric_attr<i32>} : () -> ()
+  // CHECK: "testd.dyn_attr_base"() {attr1 = #testd.parametric_attr<i64>} : () -> ()
+  "testd.dyn_attr_base"() {attr1 = #testd.parametric_attr<i64>} : () -> ()
   return
 }
 
 // -----
 
-func.func @failedDynBaseConstraint() {
+func.func @failedDynTypeBaseConstraint() {
   // expected-error at +1 {{expected base type 'testd.parametric' but got 'builtin.integer'}}
   "testd.dyn_type_base"() : () -> i32
   return
@@ -143,6 +147,45 @@ func.func @failedDynBaseConstraint() {
 
 // -----
 
+func.func @failedDynAttrBaseConstraintNotType() {
+  // expected-error at +1 {{expected base attribute 'testd.parametric_attr' but got 'builtin.type'}}
+  "testd.dyn_attr_base"() {attr1 = i32}: () -> ()
+  return
+}
+
+// -----
+
+
+func.func @succeededNamedBaseConstraint() {
+  // CHECK: "testd.named_type_base"() : () -> i32
+  "testd.named_type_base"() : () -> i32
+  // CHECK: "testd.named_type_base"() : () -> i64
+  "testd.named_type_base"() : () -> i64
+  // CHECK: "testd.named_attr_base"() {attr1 = 0 : i32} : () -> ()
+  "testd.named_attr_base"() {attr1 = 0 : i32} : () -> ()
+  // CHECK: "testd.named_attr_base"() {attr1 = 0 : i64} : () -> ()
+  "testd.named_attr_base"() {attr1 = 0 : i64} : () -> ()
+  return
+}
+
+// -----
+
+func.func @failedNamedTypeBaseConstraint() {
+  // expected-error at +1 {{expected base type 'builtin.integer' but got 'builtin.vector'}}
+  "testd.named_type_base"() : () -> vector<i32>
+  return
+}
+
+// -----
+
+func.func @failedDynAttrBaseConstraintNotType() {
+  // expected-error at +1 {{expected base attribute 'builtin.integer' but got 'builtin.type'}}
+  "testd.named_attr_base"() {attr1 = i32}: () -> ()
+  return
+}
+
+// -----
+
 //===----------------------------------------------------------------------===//
 // Dynamic parameters constraint
 //===----------------------------------------------------------------------===//

>From 8547b47addf73b98322c59070605d54c984cd428 Mon Sep 17 00:00:00 2001
From: Mathieu Fehr <mathieu.fehr at gmail.com>
Date: Sat, 23 Dec 2023 22:11:02 +0000
Subject: [PATCH 3/3] Add invalid IR tests

---
 mlir/include/mlir/Dialect/IRDL/IR/IRDLOps.td |  3 +-
 mlir/lib/Dialect/IRDL/IR/IRDL.cpp            | 28 ++++++++++++-
 mlir/test/Dialect/IRDL/invalid.irdl.mlir     | 43 ++++++++++++++++++++
 3 files changed, 71 insertions(+), 3 deletions(-)
 create mode 100644 mlir/test/Dialect/IRDL/invalid.irdl.mlir

diff --git a/mlir/include/mlir/Dialect/IRDL/IR/IRDLOps.td b/mlir/include/mlir/Dialect/IRDL/IR/IRDLOps.td
index ae1f893be72ec2..ea1c93f288423b 100644
--- a/mlir/include/mlir/Dialect/IRDL/IR/IRDLOps.td
+++ b/mlir/include/mlir/Dialect/IRDL/IR/IRDLOps.td
@@ -452,7 +452,8 @@ def IRDL_IsOp : IRDL_ConstraintOp<"is",
 }
 
 def IRDL_BaseOp : IRDL_ConstraintOp<"base",
-    [ParentOneOf<["TypeOp", "AttributeOp", "OperationOp"]>]> {
+    [ParentOneOf<["TypeOp", "AttributeOp", "OperationOp"]>,
+     DeclareOpInterfaceMethods<SymbolUserOpInterface>]> {
   let summary = "Constraints an attribute/type base";
   let description = [{
     `irdl.base` defines a constraint that accepts only a single type
diff --git a/mlir/lib/Dialect/IRDL/IR/IRDL.cpp b/mlir/lib/Dialect/IRDL/IR/IRDL.cpp
index 1d65af3da27335..47a53a8922a594 100644
--- a/mlir/lib/Dialect/IRDL/IR/IRDL.cpp
+++ b/mlir/lib/Dialect/IRDL/IR/IRDL.cpp
@@ -119,13 +119,37 @@ LogicalResult AttributesOp::verify() {
 
 LogicalResult BaseOp::verify() {
   std::optional<StringRef> baseName = getBaseName();
-  std::optional<SymbolRefAttr> base = getBaseRef();
-  if (baseName.has_value() == base.has_value())
+  std::optional<SymbolRefAttr> baseRef = getBaseRef();
+  if (baseName.has_value() == baseRef.has_value())
     return emitOpError() << "the base type or attribute should be specified by "
                             "either a name or a reference";
+
+  if (baseName && (baseName->size() == 0 ||
+                   ((*baseName)[0] != '!' && (*baseName)[0] != '#')))
+    return emitOpError() << "the base type or attribute name should start with "
+                            "'!' or '#'";
+
   return success();
 }
 
+LogicalResult BaseOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
+  std::optional<SymbolRefAttr> baseRef = getBaseRef();
+  if (!baseRef)
+    return success();
+
+  TypeOp typeOp = symbolTable.lookupNearestSymbolFrom<TypeOp>(*this, *baseRef);
+  if (typeOp)
+    return success();
+
+  AttributeOp attrOp =
+      symbolTable.lookupNearestSymbolFrom<AttributeOp>(*this, *baseRef);
+  if (attrOp)
+    return success();
+
+  return emitOpError() << "'" << *baseRef
+                       << "' does not refer to a type or attribute definition";
+}
+
 /// Parse a value with its variadicity first. By default, the variadicity is
 /// single.
 ///
diff --git a/mlir/test/Dialect/IRDL/invalid.irdl.mlir b/mlir/test/Dialect/IRDL/invalid.irdl.mlir
new file mode 100644
index 00000000000000..139fbeaad31130
--- /dev/null
+++ b/mlir/test/Dialect/IRDL/invalid.irdl.mlir
@@ -0,0 +1,43 @@
+// RUN: mlir-opt %s -verify-diagnostics -split-input-file
+
+// Testing invalid IRDL IRs
+
+func.func private @foo()
+
+irdl.dialect @testd {
+  irdl.type @type {
+    // expected-error at +1 {{'@foo' does not refer to a type or attribute definition}}
+    %0 = irdl.base @foo
+    irdl.parameters(%0)
+  }
+}
+
+// -----
+
+irdl.dialect @testd {
+  irdl.type @type {
+    // expected-error at +1 {{the base type or attribute name should start with '!' or '#'}}
+    %0 = irdl.base "builtin.integer"
+    irdl.parameters(%0)
+  }
+}
+
+// -----
+
+irdl.dialect @testd {
+  irdl.type @type {
+    // expected-error at +1 {{the base type or attribute name should start with '!' or '#'}}
+    %0 = irdl.base ""
+    irdl.parameters(%0)
+  }
+}
+
+// -----
+
+irdl.dialect @testd {
+  irdl.type @type {
+    // expected-error at +1 {{the base type or attribute should be specified by either a name}}
+    %0 = irdl.base
+    irdl.parameters(%0)
+  }
+}
\ No newline at end of file



More information about the Mlir-commits mailing list