Repository: xdslproject/xdsl

Issue:
core: fix misleading .constr class method
This helper:

```python
    @classmethod
    def constr(cls) -> GenericAttrConstraint[Self]:
        from xdsl.irdl import BaseAttr

        return BaseAttr(cls)
```

Leads to a misleading `GenericAttribute[GenericParameter].constr()` which is actually a `BaseAttr(GenericAttribute)` and does not check the parameter.

Maybe should require generic subclasses that have generic parameters to override this.

We'll have more of a discussion of this in a future xDSL meeting.

Discussion:

Solution patch:
diff --git a/xdsl/dialects/builtin.py b/xdsl/dialects/builtin.py
index 3d207bc4c..fd173498f 100644
--- a/xdsl/dialects/builtin.py
+++ b/xdsl/dialects/builtin.py
@@ -189,9 +189,6 @@ class ArrayAttr(GenericData[tuple[AttributeCovT, ...]], Iterable[AttributeCovT])
         return iter(self.data)
 
 
-AnyArrayAttr: TypeAlias = ArrayAttr[Attribute]
-
-
 @irdl_attr_definition
 class StringAttr(Data[str]):
     name = "string"
diff --git a/xdsl/dialects/csl/csl.py b/xdsl/dialects/csl/csl.py
index e47c5bf2a..8cd116991 100644
--- a/xdsl/dialects/csl/csl.py
+++ b/xdsl/dialects/csl/csl.py
@@ -417,7 +417,6 @@ ColorIdAttr: TypeAlias = IntegerAttr[
 QueueIdAttr: TypeAlias = IntegerAttr[Annotated[IntegerType, IntegerType(3)]]
 
 ParamAttr: TypeAlias = FloatAttr | IntegerAttr
-ParamAttrConstr = FloatAttr.constr() | IntegerAttr.constr()
 
 
 @irdl_op_definition
@@ -433,7 +432,7 @@ class VariableOp(IRDLOperation):
 
     name = "csl.variable"
 
-    default = opt_prop_def(ParamAttrConstr)
+    default = opt_prop_def(ParamAttr)
     res = result_def(VarType)
 
     def get_element_type(self):
diff --git a/xdsl/dialects/experimental/air.py b/xdsl/dialects/experimental/air.py
index d23f04ca4..4830ff9ee 100644
--- a/xdsl/dialects/experimental/air.py
+++ b/xdsl/dialects/experimental/air.py
@@ -11,7 +11,6 @@ from __future__ import annotations
 from collections.abc import Sequence
 
 from xdsl.dialects.builtin import (
-    AnyArrayAttr,
     ArrayAttr,
     IndexType,
     IntAttr,
@@ -90,7 +89,7 @@ class ChannelOp(IRDLOperation):
     name = "air.channel"
 
     sym_name = prop_def(SymbolRefAttr)
-    size = prop_def(AnyArrayAttr)
+    size = prop_def(ArrayAttr)
 
     def __init__(
         self, sym_name: SymbolRefAttr, size: ArrayAttr[IntegerAttr]
diff --git a/xdsl/dialects/pdl.py b/xdsl/dialects/pdl.py
index 4f4b6d9bc..0c7e3c987 100644
--- a/xdsl/dialects/pdl.py
+++ b/xdsl/dialects/pdl.py
@@ -6,7 +6,6 @@ from typing import Generic, TypeVar
 from xdsl.dialects.builtin import (
     I16,
     I32,
-    AnyArrayAttr,
     ArrayAttr,
     IntegerAttr,
     IntegerType,
@@ -970,7 +969,7 @@ class TypesOp(IRDLOperation):
     """
 
     name = "pdl.types"
-    constantTypes = opt_prop_def(AnyArrayAttr)
+    constantTypes = opt_prop_def(ArrayAttr)
     result = result_def(RangeType[TypeType])
 
     def __init__(self, constant_types: Iterable[Attribute] | None = None) -> None:
@@ -992,7 +991,7 @@ class TypesOp(IRDLOperation):
             return TypesOp()
         begin_attr_pos = parser.pos
         constant_types = parser.parse_attribute()
-        if not isa(constant_types, AnyArrayAttr):
+        if not isa(constant_types, ArrayAttr):
             parser.raise_error("Array attribute expected", begin_attr_pos, parser.pos)
         return TypesOp(constant_types)
 
diff --git a/xdsl/ir/core.py b/xdsl/ir/core.py
index b9d70f69b..79c01de18 100644
--- a/xdsl/ir/core.py
+++ b/xdsl/ir/core.py
@@ -36,7 +36,7 @@ from xdsl.utils.str_enum import StrEnum
 
 # Used for cyclic dependencies in type hints
 if TYPE_CHECKING:
-    from xdsl.irdl import GenericAttrConstraint, ParamAttrDef
+    from xdsl.irdl import ParamAttrDef
     from xdsl.parser import AttrParser, Parser
     from xdsl.printer import Printer
 
@@ -298,12 +298,6 @@ class Attribute(ABC):
         printer.print_attribute(self)
         return res.getvalue()
 
-    @classmethod
-    def constr(cls) -> GenericAttrConstraint[Self]:
-        from xdsl.irdl import BaseAttr
-
-        return BaseAttr(cls)
-
 
 class TypeAttribute(Attribute):
     """
diff --git a/xdsl/irdl/declarative_assembly_format.py b/xdsl/irdl/declarative_assembly_format.py
index 8206f71d8..db1cd0f6d 100644
--- a/xdsl/irdl/declarative_assembly_format.py
+++ b/xdsl/irdl/declarative_assembly_format.py
@@ -30,14 +30,11 @@ from xdsl.irdl import (
     Successor,
     VarExtractor,
     VariadicDef,
-    VarIRConstruct,
 )
 from xdsl.parser import Parser, UnresolvedOperand
 from xdsl.printer import Printer
 from xdsl.utils.mlir_lexer import PunctuationSpelling
 
-OperandOrResult = Literal[VarIRConstruct.OPERAND, VarIRConstruct.RESULT]
-
 
 @dataclass
 class ParsingState:
@@ -236,56 +233,46 @@ class FormatProgram:
 class Directive(ABC):
     """An assembly format directive"""
 
-
-class AnchorableDirective(Directive, ABC):
-    """
-    Base class for Directive usable as anchors to optional groups.
-    """
-
-    @abstractmethod
     def is_present(self, op: IRDLOperation) -> bool:
         """
         Check if the directive is present in the input.
         """
-        ...
-
+        return True
 
-class FormatDirective(Directive, ABC):
-    """A format directive for operation format."""
+    def is_anchorable(self) -> bool:
+        """
+        Can appear as an anchor in an optional group.
+        """
+        return False
 
-    @abstractmethod
-    def parse(self, parser: Parser, state: ParsingState) -> None: ...
+    def is_variadic_like(self) -> bool:
+        """
+        Variadic-like format directives parse a comma separated list, and cannot be
+        followed by `,` directive.
+        """
+        return False
 
-    @abstractmethod
-    def print(
-        self, printer: Printer, state: PrintingState, op: IRDLOperation
-    ) -> None: ...
+    def is_optional_like(self) -> bool:
+        """
+        Directives that successfully parse the empty string.
+        """
+        return self.is_variadic_like()
 
 
-class OptionallyParsableDirective(FormatDirective, ABC):
-    """
-    Base class for Directive that can be optionally parsed.
-    Those are the ones usable as first element of an optional group.
-    """
+class FormatDirective(Directive, ABC):
+    """A format directive for operation format."""
 
     @abstractmethod
-    def parse_optional(self, parser: Parser, state: ParsingState) -> bool:
+    def parse(self, parser: Parser, state: ParsingState) -> bool:
         """
-        Try parsing the directive and return True if it was present.
+        Parses the directive, returning True if input was consumed.
         """
         ...
 
-    def parse(self, parser: Parser, state: ParsingState) -> None:
-        self.parse_optional(parser, state)
-
-
-class VariadicLikeFormatDirective(
-    OptionallyParsableDirective, AnchorableDirective, ABC
-):
-    """
-    A directive which parses/prints multiple objects separated by commas.
-    Such directives can not be followed by comma literals.
-    """
+    @abstractmethod
+    def print(
+        self, printer: Printer, state: PrintingState, op: IRDLOperation
+    ) -> None: ...
 
     def set_empty(self, state: ParsingState):
         """
@@ -301,22 +288,23 @@ class TypeableDirective(Directive, ABC):
     """
 
     @abstractmethod
-    def parse_single_type(self, parser: Parser, state: ParsingState) -> None: ...
+    def parse_types(self, parser: Parser, state: ParsingState) -> bool:
+        """
+        Parses types for the directive, returning True if input was consumed.
+        """
+        ...
 
     @abstractmethod
-    def get_types(self, op: IRDLOperation) -> Sequence[Attribute]: ...
-
-
-class VariadicTypeableDirective(TypeableDirective, AnchorableDirective, ABC):
-    """
-    Directives which can set or get multiple types.
-    """
+    def parse_single_type(self, parser: Parser, state: ParsingState) -> None:
+        """
+        Parse exactly one type for the directive.
+        """
 
     @abstractmethod
-    def parse_many_types(self, parser: Parser, state: ParsingState) -> bool: ...
+    def get_types(self, op: IRDLOperation) -> Sequence[Attribute]: ...
 
-    @abstractmethod
-    def set_types_empty(self, state: ParsingState) -> None: ...
+    def set_types_empty(self, state: ParsingState) -> None:
+        return
 
 
 @dataclass(frozen=True)
@@ -328,28 +316,8 @@ class TypeDirective(FormatDirective):
 
     inner: TypeableDirective
 
-    def parse(self, parser: Parser, state: ParsingState) -> None:
-        self.inner.parse_single_type(parser, state)
-
-    def print(self, printer: Printer, state: PrintingState, op: IRDLOperation) -> None:
-        if state.should_emit_space or not state.last_was_punctuation:
-            printer.print(" ")
-        printer.print_list(self.inner.get_types(op), printer.print_attribute)
-        state.last_was_punctuation = False
-        state.should_emit_space = True
-
-
-@dataclass(frozen=True)
-class VariadicTypeDirective(VariadicLikeFormatDirective):
-    """
-    A directive which parses the type of a variadic typeable directive, with format:
-      type-directive ::= type(typeable-directive)
-    """
-
-    inner: VariadicTypeableDirective
-
-    def parse_optional(self, parser: Parser, state: ParsingState) -> bool:
-        return self.inner.parse_many_types(parser, state)
+    def parse(self, parser: Parser, state: ParsingState) -> bool:
+        return self.inner.parse_types(parser, state)
 
     def print(self, printer: Printer, state: PrintingState, op: IRDLOperation) -> None:
         types = self.inner.get_types(op)
@@ -364,6 +332,15 @@ class VariadicTypeDirective(VariadicLikeFormatDirective):
     def is_present(self, op: IRDLOperation) -> bool:
         return self.inner.is_present(op)
 
+    def is_anchorable(self) -> bool:
+        return self.inner.is_anchorable()
+
+    def is_variadic_like(self) -> bool:
+        return self.inner.is_variadic_like()
+
+    def is_optional_like(self) -> bool:
+        return self.inner.is_optional_like()
+
     def set_empty(self, state: ParsingState):
         self.inner.set_types_empty(state)
 
@@ -382,15 +359,27 @@ class VariableDirective(Directive, ABC):
     """Index of the variable(operand or result) definition."""
 
 
-class VariadicVariable(VariableDirective, AnchorableDirective, ABC):
+class VariadicVariable(VariableDirective, ABC):
     def is_present(self, op: IRDLOperation) -> bool:
         return bool(getattr(op, self.name))
 
+    def is_anchorable(self) -> bool:
+        return True
+
+    def is_variadic_like(self) -> bool:
+        return True
+
 
-class OptionalVariable(VariableDirective, AnchorableDirective, ABC):
+class OptionalVariable(VariableDirective, ABC):
     def is_present(self, op: IRDLOperation) -> bool:
         return getattr(op, self.name) is not None
 
+    def is_anchorable(self) -> bool:
+        return True
+
+    def is_optional_like(self) -> bool:
+        return True
+
 
 @dataclass(frozen=True)
 class AttrDictDirective(FormatDirective):
@@ -418,7 +407,7 @@ class AttrDictDirective(FormatDirective):
     This is used to keep compatibility with MLIR which allows that.
     """
 
-    def parse(self, parser: Parser, state: ParsingState) -> None:
+    def parse(self, parser: Parser, state: ParsingState) -> bool:
         if self.with_keyword:
             res = parser.parse_optional_attr_dict_with_keyword()
             if res is None:
@@ -435,6 +424,7 @@ class AttrDictDirective(FormatDirective):
                 "dictionary."
             )
         state.attributes |= res
+        return bool(res)
 
     def print(self, printer: Printer, state: PrintingState, op: IRDLOperation) -> None:
         if self.print_properties:
@@ -478,21 +468,37 @@ class AttrDictDirective(FormatDirective):
         state.last_was_punctuation = False
         state.should_emit_space = True
 
+    def is_optional_like(self) -> bool:
+        return True
+
+
+class OperandDirective(FormatDirective, TypeableDirective, ABC):
+    """
+    Base class for operand directives to aid typechecking.
+    """
+
+    pass
+
 
 @dataclass(frozen=True)
-class OperandVariable(VariableDirective, FormatDirective, TypeableDirective):
+class OperandVariable(VariableDirective, OperandDirective):
     """
     An operand variable, with the following format:
       operand-directive ::= dollar-ident
     The directive will request a space to be printed after.
     """
 
-    def parse(self, parser: Parser, state: ParsingState) -> None:
+    def parse(self, parser: Parser, state: ParsingState) -> bool:
         operand = parser.parse_unresolved_operand()
         state.operands[self.index] = operand
+        return True
 
-    def parse_single_type(self, parser: Parser, state: ParsingState) -> None:
+    def parse_types(self, parser: Parser, state: ParsingState) -> bool:
         state.operand_types[self.index] = parser.parse_type()
+        return True
+
+    def parse_single_type(self, parser: Parser, state: ParsingState) -> None:
+        self.parse_types(parser, state)
 
     def print(self, printer: Printer, state: PrintingState, op: IRDLOperation) -> None:
         if state.should_emit_space or not state.last_was_punctuation:
@@ -505,24 +511,15 @@ class OperandVariable(VariableDirective, FormatDirective, TypeableDirective):
         return (getattr(op, self.name).type,)
 
 
-class VariadicOperandDirective(
-    VariadicLikeFormatDirective, VariadicTypeableDirective, ABC
-):
-    """
-    Base class for typechecking.
-    A variadic operand directive cannot follow another variadic operand directive.
-    """
-
-
 @dataclass(frozen=True)
-class VariadicOperandVariable(VariadicVariable, VariadicOperandDirective):
+class VariadicOperandVariable(VariadicVariable, OperandDirective):
     """
     A variadic operand variable, with the following format:
       operand-directive ::= ( percent-ident ( `,` percent-id )* )?
     The directive will request a space to be printed after.
     """
 
-    def parse_optional(self, parser: Parser, state: ParsingState) -> bool:
+    def parse(self, parser: Parser, state: ParsingState) -> bool:
         operands = parser.parse_optional_undelimited_comma_separated_list(
             parser.parse_optional_unresolved_operand, parser.parse_unresolved_operand
         )
@@ -531,10 +528,7 @@ class VariadicOperandVariable(VariadicVariable, VariadicOperandDirective):
         state.operands[self.index] = operands
         return bool(operands)
 
-    def parse_single_type(self, parser: Parser, state: ParsingState) -> None:
-        state.operand_types[self.index] = (parser.parse_type(),)
-
-    def parse_many_types(self, parser: Parser, state: ParsingState) -> bool:
+    def parse_types(self, parser: Parser, state: ParsingState) -> bool:
         types = parser.parse_optional_undelimited_comma_separated_list(
             parser.parse_optional_type, parser.parse_type
         )
@@ -544,6 +538,9 @@ class VariadicOperandVariable(VariadicVariable, VariadicOperandDirective):
         state.operand_types[self.index] = types
         return ret
 
+    def parse_single_type(self, parser: Parser, state: ParsingState) -> None:
+        state.operand_types[self.index] = (parser.parse_type(),)
+
     def print(self, printer: Printer, state: PrintingState, op: IRDLOperation) -> None:
         operand = getattr(op, self.name)
         if not operand:
@@ -564,24 +561,21 @@ class VariadicOperandVariable(VariadicVariable, VariadicOperandDirective):
         state.operand_types[self.index] = ()
 
 
-class OptionalOperandVariable(OptionalVariable, VariadicOperandDirective):
+class OptionalOperandVariable(OptionalVariable, OperandDirective):
     """
     An optional operand variable, with the following format:
       operand-directive ::= ( percent-ident )?
     The directive will request a space to be printed after.
     """
 
-    def parse_optional(self, parser: Parser, state: ParsingState) -> bool:
+    def parse(self, parser: Parser, state: ParsingState) -> bool:
         operand = parser.parse_optional_unresolved_operand()
         if operand is None:
             operand = ()
         state.operands[self.index] = operand
         return bool(operand)
 
-    def parse_single_type(self, parser: Parser, state: ParsingState) -> None:
-        state.operand_types[self.index] = (parser.parse_type(),)
-
-    def parse_many_types(self, parser: Parser, state: ParsingState) -> bool:
+    def parse_types(self, parser: Parser, state: ParsingState) -> bool:
         type = parser.parse_optional_type()
         ret = type is None
         if ret:
@@ -589,6 +583,9 @@ class OptionalOperandVariable(OptionalVariable, VariadicOperandDirective):
         state.operand_types[self.index] = type
         return ret
 
+    def parse_single_type(self, parser: Parser, state: ParsingState) -> None:
+        state.operand_types[self.index] = parser.parse_type()
+
     def print(self, printer: Printer, state: PrintingState, op: IRDLOperation) -> None:
         operand = getattr(op, self.name)
         if not operand:
@@ -616,7 +613,7 @@ _T = TypeVar("_T")
 
 
 @dataclass(frozen=True)
-class OperandsOrResultDirective(VariadicTypeableDirective, ABC):
+class OperandsOrResultDirective(TypeableDirective, ABC):
     """
     Base class for the 'operands' and 'results' directives.
     """
@@ -627,6 +624,12 @@ class OperandsOrResultDirective(VariadicTypeableDirective, ABC):
     representing whether it is optional
     """
 
+    def is_variadic_like(self) -> bool:
+        return True
+
+    def is_anchorable(self) -> bool:
+        return True
+
     def _set_using_variadic_index(
         self,
         field: list[_T | None | Sequence[_T]],
@@ -650,14 +653,14 @@ class OperandsOrResultDirective(VariadicTypeableDirective, ABC):
         field[var_position + 1 :] = set_to[var_position + var_length :]
 
 
-class OperandsDirective(VariadicOperandDirective, OperandsOrResultDirective):
+class OperandsDirective(OperandsOrResultDirective, FormatDirective):
     """
     An operands directive, with the following format:
       operands-directive ::= operands
     Prints each operand of the operation, inserting a comma between each.
     """
 
-    def parse_optional(self, parser: Parser, state: ParsingState) -> bool:
+    def parse(self, parser: Parser, state: ParsingState) -> bool:
         pos_start = parser.pos
         operands = (
             parser.parse_optional_undelimited_comma_separated_list(
@@ -671,14 +674,7 @@ class OperandsDirective(VariadicOperandDirective, OperandsOrResultDirective):
             parser.raise_error(s, at_position=pos_start, end_position=parser.pos)
         return bool(operands)
 
-    def parse_single_type(self, parser: Parser, state: ParsingState) -> None:
-        pos_start = parser.pos
-        if s := self._set_using_variadic_index(
-            state.operand_types, "operand types", (parser.parse_type(),)
-        ):
-            parser.raise_error(s, at_position=pos_start, end_position=parser.pos)
-
-    def parse_many_types(self, parser: Parser, state: ParsingState) -> bool:
+    def parse_types(self, parser: Parser, state: ParsingState) -> bool:
         pos_start = parser.pos
         types = (
             parser.parse_optional_undelimited_comma_separated_list(
@@ -693,6 +689,13 @@ class OperandsDirective(VariadicOperandDirective, OperandsOrResultDirective):
             parser.raise_error(s, at_position=pos_start, end_position=parser.pos)
         return bool(types)
 
+    def parse_single_type(self, parser: Parser, state: ParsingState) -> None:
+        pos_start = parser.pos
+        if s := self._set_using_variadic_index(
+            state.operand_types, "operand types", (parser.parse_type(),)
+        ):
+            parser.raise_error(s, at_position=pos_start, end_position=parser.pos)
+
     def print(self, printer: Printer, state: PrintingState, op: IRDLOperation) -> None:
         if op.operands:
             if state.should_emit_space or not state.last_was_punctuation:
@@ -723,15 +726,19 @@ class ResultVariable(VariableDirective, TypeableDirective):
     parsing is not handled by the custom operation parser.
     """
 
-    def parse_single_type(self, parser: Parser, state: ParsingState) -> None:
+    def parse_types(self, parser: Parser, state: ParsingState) -> bool:
         state.result_types[self.index] = parser.parse_type()
+        return True
+
+    def parse_single_type(self, parser: Parser, state: ParsingState) -> None:
+        self.parse_types(parser, state)
 
     def get_types(self, op: IRDLOperation) -> Sequence[Attribute]:
         return (getattr(op, self.name).type,)
 
 
 @dataclass(frozen=True)
-class VariadicResultVariable(VariadicVariable, VariadicTypeableDirective):
+class VariadicResultVariable(VariadicVariable, TypeableDirective):
     """
     A variadic result variable, with the following format:
       result-directive ::= percent-ident (( `,` percent-id )* )?
@@ -739,10 +746,7 @@ class VariadicResultVariable(VariadicVariable, VariadicTypeableDirective):
     parsing is not handled by the custom operation parser.
     """
 
-    def parse_single_type(self, parser: Parser, state: ParsingState) -> None:
-        state.result_types[self.index] = (parser.parse_type(),)
-
-    def parse_many_types(self, parser: Parser, state: ParsingState) -> bool:
+    def parse_types(self, parser: Parser, state: ParsingState) -> bool:
         types = parser.parse_optional_undelimited_comma_separated_list(
             parser.parse_optional_type, parser.parse_type
         )
@@ -752,6 +756,9 @@ class VariadicResultVariable(VariadicVariable, VariadicTypeableDirective):
         state.result_types[self.index] = types
         return ret
 
+    def parse_single_type(self, parser: Parser, state: ParsingState) -> None:
+        state.result_types[self.index] = (parser.parse_type(),)
+
     def get_types(self, op: IRDLOperation) -> Sequence[Attribute]:
         return getattr(op, self.name).types
 
@@ -759,7 +766,7 @@ class VariadicResultVariable(VariadicVariable, VariadicTypeableDirective):
         state.result_types[self.index] = ()
 
 
-class OptionalResultVariable(OptionalVariable, VariadicTypeableDirective):
+class OptionalResultVariable(OptionalVariable, TypeableDirective):
     """
     An optional result variable, with the following format:
       result-directive ::= ( percent-ident )?
@@ -767,10 +774,7 @@ class OptionalResultVariable(OptionalVariable, VariadicTypeableDirective):
     parsing is not handled by the custom operation parser.
     """
 
-    def parse_single_type(self, parser: Parser, state: ParsingState) -> None:
-        state.result_types[self.index] = (parser.parse_type(),)
-
-    def parse_many_types(self, parser: Parser, state: ParsingState) -> bool:
+    def parse_types(self, parser: Parser, state: ParsingState) -> bool:
         type = parser.parse_optional_type()
         ret = type is None
         if ret:
@@ -778,6 +782,9 @@ class OptionalResultVariable(OptionalVariable, VariadicTypeableDirective):
         state.result_types[self.index] = type
         return ret
 
+    def parse_single_type(self, parser: Parser, state: ParsingState) -> None:
+        state.result_types[self.index] = (parser.parse_type(),)
+
     def get_types(self, op: IRDLOperation) -> Sequence[Attribute]:
         res = getattr(op, self.name)
         if res:
@@ -795,14 +802,7 @@ class ResultsDirective(OperandsOrResultDirective):
     A typeable directive which processes the result types of the operation.
     """
 
-    def parse_single_type(self, parser: Parser, state: ParsingState) -> None:
-        pos_start = parser.pos
-        if s := self._set_using_variadic_index(
-            state.result_types, "result types", (parser.parse_type(),)
-        ):
-            parser.raise_error(s, at_position=pos_start, end_position=parser.pos)
-
-    def parse_many_types(self, parser: Parser, state: ParsingState) -> bool:
+    def parse_types(self, parser: Parser, state: ParsingState) -> bool:
         pos_start = parser.pos
         types = (
             parser.parse_optional_undelimited_comma_separated_list(
@@ -817,6 +817,13 @@ class ResultsDirective(OperandsOrResultDirective):
             parser.raise_error(s, at_position=pos_start, end_position=parser.pos)
         return bool(types)
 
+    def parse_single_type(self, parser: Parser, state: ParsingState) -> None:
+        pos_start = parser.pos
+        if s := self._set_using_variadic_index(
+            state.result_types, "result types", (parser.parse_type(),)
+        ):
+            parser.raise_error(s, at_position=pos_start, end_position=parser.pos)
+
     def set_types_empty(self, state: ParsingState) -> None:
         state.result_types = [() for _ in state.operand_types]
 
@@ -828,7 +835,7 @@ class ResultsDirective(OperandsOrResultDirective):
 
 
 @dataclass(frozen=True)
-class FunctionalTypeDirective(OptionallyParsableDirective):
+class FunctionalTypeDirective(FormatDirective):
     """
     A directive which parses a functional type, with format:
       functional-type-directive ::= functional-type(typeable-directive, typeable-directive)
@@ -843,20 +850,14 @@ class FunctionalTypeDirective(OptionallyParsableDirective):
     operand_typeable_directive: TypeableDirective
     result_typeable_directive: TypeableDirective
 
-    def parse_optional(self, parser: Parser, state: ParsingState) -> bool:
+    def parse(self, parser: Parser, state: ParsingState) -> bool:
         if not parser.parse_optional_punctuation("("):
             return False
-        if isinstance(self.operand_typeable_directive, VariadicTypeableDirective):
-            self.operand_typeable_directive.parse_many_types(parser, state)
-        else:
-            self.operand_typeable_directive.parse_single_type(parser, state)
+        self.operand_typeable_directive.parse_types(parser, state)
         parser.parse_punctuation(")")
         parser.parse_punctuation("->")
         if parser.parse_optional_punctuation("("):
-            if isinstance(self.result_typeable_directive, VariadicTypeableDirective):
-                self.result_typeable_directive.parse_many_types(parser, state)
-            else:
-                self.result_typeable_directive.parse_single_type(parser, state)
+            self.result_typeable_directive.parse_types(parser, state)
             parser.parse_punctuation(")")
         else:
             self.result_typeable_directive.parse_single_type(parser, state)
@@ -882,17 +883,9 @@ class FunctionalTypeDirective(OptionallyParsableDirective):
             state.last_was_punctuation = True
 
 
-class RegionDirective(OptionallyParsableDirective, ABC):
+class RegionDirective(FormatDirective, ABC):
     """
     Baseclass to help keep typechecking simple.
-    RegionDirective is for any RegionVariable, which are all OptionallyParsable.
-    """
-
-
-class VariadicRegionDirective(RegionDirective, VariadicLikeFormatDirective, ABC):
-    """
-    Base class for typechecking.
-    A variadic region directive cannot follow another variadic region directive.
     """
 
 
@@ -904,10 +897,10 @@ class RegionVariable(RegionDirective, VariableDirective):
     The directive will request a space to be printed after.
     """
 
-    def parse_optional(self, parser: Parser, state: ParsingState) -> bool:
-        region = parser.parse_optional_region()
+    def parse(self, parser: Parser, state: ParsingState) -> bool:
+        region = parser.parse_region()
         state.regions[self.index] = region
-        return region is not None
+        return True
 
     def print(self, printer: Printer, state: PrintingState, op: IRDLOperation) -> None:
         if state.should_emit_space or not state.last_was_punctuation:
@@ -918,7 +911,7 @@ class RegionVariable(RegionDirective, VariableDirective):
 
 
 @dataclass(frozen=True)
-class VariadicRegionVariable(VariadicRegionDirective, VariadicVariable):
+class VariadicRegionVariable(RegionDirective, VariadicVariable):
     """
     A variadic region variable, with the following format:
       region-directive ::= dollar-ident
@@ -926,7 +919,7 @@ class VariadicRegionVariable(VariadicRegionDirective, VariadicVariable):
     The directive will request a space to be printed after.
     """
 
-    def parse_optional(self, parser: Parser, state: ParsingState) -> bool:
+    def parse(self, parser: Parser, state: ParsingState) -> bool:
         regions: list[Region] = []
         current_region = parser.parse_optional_region()
         while current_region is not None:
@@ -950,14 +943,14 @@ class VariadicRegionVariable(VariadicRegionDirective, VariadicVariable):
         state.regions[self.index] = ()
 
 
-class OptionalRegionVariable(VariadicRegionDirective, OptionalVariable):
+class OptionalRegionVariable(RegionDirective, OptionalVariable):
     """
     An optional region variable, with the following format:
       region-directive ::= dollar-ident
     The directive will request a space to be printed after.
     """
 
-    def parse_optional(self, parser: Parser, state: ParsingState) -> bool:
+    def parse(self, parser: Parser, state: ParsingState) -> bool:
         region = parser.parse_optional_region()
         if region is None:
             region = list[Region]()
@@ -978,21 +971,21 @@ class OptionalRegionVariable(VariadicRegionDirective, OptionalVariable):
         state.regions[self.index] = ()
 
 
-class VariadicSuccessorDirective(VariadicLikeFormatDirective, ABC):
+class SuccessorDirective(FormatDirective, ABC):
     """
     Base class for type checking.
     A variadic successor directive cannot follow another variadic successor directive.
     """
 
 
-class SuccessorVariable(VariableDirective, OptionallyParsableDirective):
+class SuccessorVariable(VariableDirective, SuccessorDirective):
     """
     A successor variable, with the following format:
       successor-directive ::= dollar-ident
     The directive will request a space to be printed after.
     """
 
-    def parse_optional(self, parser: Parser, state: ParsingState) -> bool:
+    def parse(self, parser: Parser, state: ParsingState) -> bool:
         successor = parser.parse_optional_successor()
 
         state.successors[self.index] = successor
@@ -1007,14 +1000,14 @@ class SuccessorVariable(VariableDirective, OptionallyParsableDirective):
         state.should_emit_space = True
 
 
-class VariadicSuccessorVariable(VariadicSuccessorDirective, VariadicVariable):
+class VariadicSuccessorVariable(VariadicVariable, SuccessorDirective):
     """
     A variadic successor variable, with the following format:
       successor-directive ::= dollar-ident
     The directive will request a space to be printed after.
     """
 
-    def parse_optional(self, parser: Parser, state: ParsingState) -> bool:
+    def parse(self, parser: Parser, state: ParsingState) -> bool:
         successors: list[Successor] = []
         current_successor = parser.parse_optional_successor()
         while current_successor is not None:
@@ -1039,14 +1032,14 @@ class VariadicSuccessorVariable(VariadicSuccessorDirective, VariadicVariable):
         state.successors[self.index] = ()
 
 
-class OptionalSuccessorVariable(VariadicSuccessorDirective, OptionalVariable):
+class OptionalSuccessorVariable(OptionalVariable, SuccessorDirective):
     """
     An optional successor variable, with the following format:
       successor-directive ::= dollar-ident
     The directive will request a space to be printed after.
     """
 
-    def parse_optional(self, parser: Parser, state: ParsingState) -> bool:
+    def parse(self, parser: Parser, state: ParsingState) -> bool:
         successor = parser.parse_optional_successor()
         if successor is None:
             successor = list[Successor]()
@@ -1084,7 +1077,7 @@ class AttributeVariable(FormatDirective):
     unique_type: Attribute | None
     """The known type of the Attribute, if any."""
 
-    def parse(self, parser: Parser, state: ParsingState) -> None:
+    def parse(self, parser: Parser, state: ParsingState) -> bool:
         unique_base = self.unique_base
         if unique_base is None:
             attr = parser.parse_attribute()
@@ -1106,6 +1099,7 @@ class AttributeVariable(FormatDirective):
             state.properties[self.name] = attr
         else:
             state.attributes[self.name] = attr
+        return True
 
     def print(self, printer: Printer, state: PrintingState, op: IRDLOperation) -> None:
         if state.should_emit_space or not state.last_was_punctuation:
@@ -1131,7 +1125,7 @@ class AttributeVariable(FormatDirective):
 
 
 @dataclass(frozen=True)
-class DefaultValuedAttributeVariable(AttributeVariable, AnchorableDirective):
+class DefaultValuedAttributeVariable(AttributeVariable):
     """
     An attribute variable with default value, with the following format:
       result-directive ::= dollar-ident
@@ -1147,8 +1141,11 @@ class DefaultValuedAttributeVariable(AttributeVariable, AnchorableDirective):
             attr = op.attributes.get(self.name)
         return attr is not None and attr != self.default_value
 
+    def is_anchorable(self) -> bool:
+        return True
+
 
-class OptionalAttributeVariable(AttributeVariable, AnchorableDirective):
+class OptionalAttributeVariable(AttributeVariable):
     """
     An optional attribute variable, with the following format:
       operand-directive ::= ( percent-ident )?
@@ -1162,6 +1159,9 @@ class OptionalAttributeVariable(AttributeVariable, AnchorableDirective):
             attr = op.attributes.get(self.name)
         return attr is not None
 
+    def is_anchorable(self) -> bool:
+        return True
+
 
 class OptionalUnitAttrVariable(OptionalAttributeVariable):
     """
@@ -1173,11 +1173,12 @@ class OptionalUnitAttrVariable(OptionalAttributeVariable):
     Also see: https://mlir.llvm.org/docs/DefiningDialects/Operations/#unit-attributes
     """
 
-    def parse(self, parser: Parser, state: ParsingState) -> None:
+    def parse(self, parser: Parser, state: ParsingState) -> bool:
         if self.is_property:
             state.properties[self.name] = UnitAttr()
         else:
             state.attributes[self.name] = UnitAttr()
+        return True
 
     def print(self, printer: Printer, state: PrintingState, op: IRDLOperation) -> None:
         return
@@ -1196,8 +1197,8 @@ class WhitespaceDirective(FormatDirective):
     whitespace: Literal[" ", "\n", ""]
     """The whitespace that should be printed."""
 
-    def parse(self, parser: Parser, state: ParsingState) -> None:
-        pass
+    def parse(self, parser: Parser, state: ParsingState) -> bool:
+        return False
 
     def print(self, printer: Printer, state: PrintingState, op: IRDLOperation) -> None:
         printer.print(self.whitespace)
@@ -1206,7 +1207,7 @@ class WhitespaceDirective(FormatDirective):
 
 
 @dataclass(frozen=True)
-class PunctuationDirective(OptionallyParsableDirective):
+class PunctuationDirective(FormatDirective):
     """
     A punctuation directive, with the following format:
       punctuation-directive ::= punctuation
@@ -1220,12 +1221,9 @@ class PunctuationDirective(OptionallyParsableDirective):
     punctuation: PunctuationSpelling
     """The punctuation that should be printed/parsed."""
 
-    def parse_optional(self, parser: Parser, state: ParsingState) -> bool:
+    def parse(self, parser: Parser, state: ParsingState) -> bool:
         return parser.parse_optional_punctuation(self.punctuation) is not None
 
-    def parse(self, parser: Parser, state: ParsingState) -> None:
-        parser.parse_punctuation(self.punctuation)
-
     def print(self, printer: Printer, state: PrintingState, op: IRDLOperation) -> None:
         emit_space = False
         if state.should_emit_space:
@@ -1243,9 +1241,12 @@ class PunctuationDirective(OptionallyParsableDirective):
         state.should_emit_space = self.punctuation not in ("<", "(", "{", "[")
         state.last_was_punctuation = True
 
+    def is_optional_like(self) -> bool:
+        return True
+
 
 @dataclass(frozen=True)
-class KeywordDirective(OptionallyParsableDirective):
+class KeywordDirective(FormatDirective):
     """
     A keyword directive, with the following format:
       keyword-directive ::= bare-ident
@@ -1256,12 +1257,9 @@ class KeywordDirective(OptionallyParsableDirective):
     keyword: str
     """The identifier that should be printed."""
 
-    def parse_optional(self, parser: Parser, state: ParsingState):
+    def parse(self, parser: Parser, state: ParsingState) -> bool:
         return parser.parse_optional_keyword(self.keyword) is not None
 
-    def parse(self, parser: Parser, state: ParsingState):
-        parser.parse_keyword(self.keyword)
-
     def print(self, printer: Printer, state: PrintingState, op: IRDLOperation) -> None:
         if state.should_emit_space:
             printer.print(" ")
@@ -1269,25 +1267,28 @@ class KeywordDirective(OptionallyParsableDirective):
         state.should_emit_space = True
         state.last_was_punctuation = False
 
+    def is_optional_like(self) -> bool:
+        return True
+
 
 @dataclass(frozen=True)
 class OptionalGroupDirective(FormatDirective):
-    anchor: AnchorableDirective
+    anchor: Directive
     then_whitespace: tuple[WhitespaceDirective, ...]
-    then_first: OptionallyParsableDirective
+    then_first: FormatDirective
     then_elements: tuple[FormatDirective, ...]
 
-    def parse(self, parser: Parser, state: ParsingState) -> None:
+    def parse(self, parser: Parser, state: ParsingState) -> bool:
         # If the first element was parsed, parse the then-elements as usual
-        if self.then_first.parse_optional(parser, state):
+        if ret := self.then_first.parse(parser, state):
             for element in self.then_elements:
                 element.parse(parser, state)
         # Otherwise, just explicitly set the variadic/optional variables and
         # type to empty
         else:
             for element in self.then_elements:
-                if isinstance(element, VariadicLikeFormatDirective):
-                    element.set_empty(state)
+                element.set_empty(state)
+        return ret
 
     def print(self, printer: Printer, state: PrintingState, op: IRDLOperation) -> None:
         if self.anchor.is_present(op):
diff --git a/xdsl/irdl/declarative_assembly_format_parser.py b/xdsl/irdl/declarative_assembly_format_parser.py
index 04b3e46fd..023dd4db3 100644
--- a/xdsl/irdl/declarative_assembly_format_parser.py
+++ b/xdsl/irdl/declarative_assembly_format_parser.py
@@ -6,7 +6,7 @@ https://mlir.llvm.org/docs/DefiningDialects/Operations/#declarative-assembly-for
 from __future__ import annotations
 
 import re
-from collections.abc import Callable, Sequence, Set
+from collections.abc import Sequence, Set
 from dataclasses import dataclass, field
 from itertools import pairwise
 from typing import cast
@@ -38,20 +38,19 @@ from xdsl.irdl import (
     merge_extractor_dicts,
 )
 from xdsl.irdl.declarative_assembly_format import (
-    AnchorableDirective,
     AttrDictDirective,
     AttributeVariable,
     DefaultValuedAttributeVariable,
+    Directive,
     FormatDirective,
     FormatProgram,
     FunctionalTypeDirective,
     KeywordDirective,
-    OperandOrResult,
+    OperandDirective,
     OperandsDirective,
     OperandVariable,
     OptionalAttributeVariable,
     OptionalGroupDirective,
-    OptionallyParsableDirective,
     OptionalOperandVariable,
     OptionalRegionVariable,
     OptionalResultVariable,
@@ -63,19 +62,14 @@ from xdsl.irdl.declarative_assembly_format import (
     RegionVariable,
     ResultsDirective,
     ResultVariable,
+    SuccessorDirective,
     SuccessorVariable,
     TypeableDirective,
     TypeDirective,
-    VariadicLikeFormatDirective,
-    VariadicOperandDirective,
     VariadicOperandVariable,
-    VariadicRegionDirective,
     VariadicRegionVariable,
     VariadicResultVariable,
-    VariadicSuccessorDirective,
     VariadicSuccessorVariable,
-    VariadicTypeableDirective,
-    VariadicTypeDirective,
     WhitespaceDirective,
 )
 from xdsl.parser import BaseParser, ParserState
@@ -142,11 +136,6 @@ class FormatParser(BaseParser):
     """The successor variables that are already parsed."""
     has_attr_dict: bool = field(default=False)
     """True if the attribute dictionary has already been parsed."""
-    type_resolutions: dict[
-        tuple[OperandOrResult, int],
-        tuple[Callable[[Attribute], Attribute], OperandOrResult, int],
-    ]
-    """Map a variable to a way to infer its type"""
 
     def __init__(self, input: str, op_def: OpDef):
         super().__init__(ParserState(FormatLexer(Input(input, "<input>"))))
@@ -158,7 +147,6 @@ class FormatParser(BaseParser):
         self.seen_properties = set[str]()
         self.seen_regions = [False] * len(op_def.regions)
         self.seen_successors = [False] * len(op_def.successors)
-        self.type_resolutions = {}
 
     def parse_format(self) -> FormatProgram:
         """
@@ -188,27 +176,29 @@ class FormatParser(BaseParser):
         directives leads to ambiguous parsing, and should raise an error here.
         """
         for a, b in pairwise(elements):
+            if not a.is_optional_like():
+                continue
             match a, b:
-                case VariadicLikeFormatDirective(), PunctuationDirective(","):
+                case _, PunctuationDirective(",") if a.is_variadic_like():
                     self.raise_error(
                         "A variadic directive cannot be followed by a comma literal."
                     )
-                case VariadicTypeDirective(), VariadicTypeDirective():
+                case TypeDirective(), TypeDirective():
                     self.raise_error(
-                        "A variadic type directive cannot be followed by another "
-                        "variadic type directive."
+                        "An optional/variadic type directive cannot be followed by another "
+                        "type directive."
                     )
-                case VariadicOperandDirective(), VariadicOperandDirective():
+                case OperandDirective(), OperandDirective():
                     self.raise_error(
-                        "A variadic operand variable cannot be followed by another "
-                        "variadic operand variable."
+                        "An optional/variadic operand variable cannot be followed by another "
+                        "operand variable."
                     )
-                case VariadicRegionDirective(), VariadicRegionDirective():
+                case RegionDirective(), RegionDirective():
                     self.raise_error(
-                        "A variadic region variable cannot be followed by another "
-                        "variadic region variable."
+                        "An optional/variadic region variable cannot be followed by another "
+                        "region variable."
                     )
-                case VariadicSuccessorDirective(), VariadicSuccessorDirective():
+                case SuccessorDirective(), SuccessorDirective():
                     self.raise_error(
                         "A variadic successor variable cannot be followed by another "
                         "variadic successor variable."
@@ -638,8 +628,6 @@ class FormatParser(BaseParser):
         self.parse_punctuation("(")
         inner = self.parse_typeable_directive()
         self.parse_punctuation(")")
-        if isinstance(inner, VariadicTypeableDirective):
-            return VariadicTypeDirective(inner)
         return TypeDirective(inner)
 
     def parse_functional_type_directive(self) -> FormatDirective:
@@ -661,7 +649,7 @@ class FormatParser(BaseParser):
           group ::= `(` then-elements `)` `?`
         """
         then_elements = tuple[FormatDirective, ...]()
-        anchor: FormatDirective | None = None
+        anchor: Directive | None = None
 
         while not self.parse_optional_punctuation(")"):
             then_elements += (self.parse_format_directive(),)
@@ -682,14 +670,11 @@ class FormatParser(BaseParser):
             self.raise_error("An optional group must have a non-whitespace directive")
         if anchor is None:
             self.raise_error("Every optional group must have an anchor.")
-        # TODO: allow attribute and region variables when implemented.
-        if not isinstance(
-            then_elements[first_non_whitespace_index], OptionallyParsableDirective
-        ):
+        if not then_elements[first_non_whitespace_index].is_optional_like():
             self.raise_error(
                 "First element of an optional group must be optionally parsable."
             )
-        if not isinstance(anchor, AnchorableDirective):
+        if not anchor.is_anchorable():
             self.raise_error(
                 "An optional group's anchor must be an anchorable directive."
             )
@@ -700,9 +685,7 @@ class FormatParser(BaseParser):
                 tuple[WhitespaceDirective, ...],
                 then_elements[:first_non_whitespace_index],
             ),
-            cast(
-                OptionallyParsableDirective, then_elements[first_non_whitespace_index]
-            ),
+            then_elements[first_non_whitespace_index],
             then_elements[first_non_whitespace_index + 1 :],
         )
 
diff --git a/xdsl/parser/attribute_parser.py b/xdsl/parser/attribute_parser.py
index 9039f9089..793488e51 100644
--- a/xdsl/parser/attribute_parser.py
+++ b/xdsl/parser/attribute_parser.py
@@ -11,7 +11,6 @@ from xdsl.context import MLContext
 from xdsl.dialects.builtin import (
     AffineMapAttr,
     AffineSetAttr,
-    AnyArrayAttr,
     AnyDenseElement,
     AnyFloat,
     AnyFloatConstr,
@@ -1207,7 +1206,7 @@ class AttrParser(BaseParser):
             else None
         )
 
-    def _parse_optional_array_attr(self) -> AnyArrayAttr | None:
+    def _parse_optional_array_attr(self) -> ArrayAttr | None:
         """
         Parse an array attribute, if present, with format:
             array-attr ::= `[` (attribute (`,` attribute)*)? `]`
diff --git a/xdsl/parser/core.py b/xdsl/parser/core.py
index fbdd55d0e..9a1df7812 100644
--- a/xdsl/parser/core.py
+++ b/xdsl/parser/core.py
@@ -145,10 +145,7 @@ class Parser(AttrParser):
             value_names = ", ".join(
                 "%" + name for name in self.forward_ssa_references.keys()
             )
-            if len(self.forward_block_references.keys()) > 1:
-                self.raise_error(f"values {value_names} were used but not defined")
-            else:
-                self.raise_error(f"value {value_names} was used but not defined")
+            self.raise_error(f"values used but not defined: [{value_names}]")
 
         return module_op
 


Test patch:
diff --git a/tests/irdl/test_declarative_assembly_format.py b/tests/irdl/test_declarative_assembly_format.py
index 870b1ba23..f947369d3 100644
--- a/tests/irdl/test_declarative_assembly_format.py
+++ b/tests/irdl/test_declarative_assembly_format.py
@@ -2024,7 +2024,7 @@ def test_multiple_optional_regions():
     """Test that a variadic region variable cannot directly follow another variadic region variable."""
     with pytest.raises(
         PyRDLOpDefinitionError,
-        match="A variadic region variable cannot be followed by another variadic region variable.",
+        match="An optional/variadic region variable cannot be followed by another region variable.",
     ):
 
         @irdl_op_definition
@@ -2546,11 +2546,8 @@ def test_int_var_inference():
     "variadic_def, format",
     [
         (var_operand_def, "$variadic `,` type($variadic) attr-dict"),
-        (var_operand_def, "type($variadic) `,` attr-dict"),
+        (var_operand_def, "$variadic `:` type($variadic) `,` attr-dict"),
         (var_result_def, "type($variadic) `,` attr-dict"),
-        (opt_operand_def, "$variadic `,` type($variadic) attr-dict"),
-        (opt_operand_def, "type($variadic) `,` attr-dict"),
-        (opt_result_def, "type($variadic) `,` attr-dict"),
     ],
 )
 def test_variadic_comma_safeguard(
@@ -2570,20 +2567,38 @@ def test_variadic_comma_safeguard(
 
 
 @pytest.mark.parametrize(
-    "variadic_def_one",
-    [var_operand_def, var_result_def, opt_operand_def, opt_result_def],
-)
-@pytest.mark.parametrize(
-    "variadic_def_two",
-    [var_operand_def, var_result_def, opt_operand_def, opt_result_def],
+    "variadic_def_one, variadic_def_two, format",
+    [
+        (
+            var_operand_def,
+            var_operand_def,
+            "$variadic_one `and` $variadic_two `:` type($variadic_one) type($variadic_two) attr-dict",
+        ),
+        (
+            var_result_def,
+            var_result_def,
+            "type($variadic_one) type($variadic_two) attr-dict",
+        ),
+        (
+            opt_operand_def,
+            opt_operand_def,
+            "$variadic_one `and` $variadic_two `:` type($variadic_one) type($variadic_two) attr-dict",
+        ),
+        (
+            opt_result_def,
+            opt_result_def,
+            "type($variadic_one) type($variadic_two) attr-dict",
+        ),
+    ],
 )
 def test_chained_variadic_types_safeguard(
     variadic_def_one: Callable[[], VarOperand | VarOpResult],
     variadic_def_two: Callable[[], VarOperand | VarOpResult],
+    format: str,
 ):
     with pytest.raises(
         PyRDLOpDefinitionError,
-        match="A variadic type directive cannot be followed by another variadic type directive.",
+        match="An optional/variadic type directive cannot be followed by another type directive.",
     ):
 
         @irdl_op_definition
@@ -2592,7 +2607,7 @@ def test_chained_variadic_types_safeguard(
 
             variadic_one = variadic_def_one()
             variadic_two = variadic_def_two()
-            assembly_format = "type($variadic_one) type($variadic_two) attr-dict"
+            assembly_format = format
 
             irdl_options = [AttrSizedOperandSegments(), AttrSizedResultSegments()]
 
@@ -2605,7 +2620,7 @@ def test_chained_variadic_operands_safeguard(
 ):
     with pytest.raises(
         PyRDLOpDefinitionError,
-        match="A variadic operand variable cannot be followed by another variadic operand variable.",
+        match="An optional/variadic operand variable cannot be followed by another operand variable.",
     ):
 
         @irdl_op_definition