1//===-- Ops.td ---------------------------------------------*- tablegen -*-===//
3// Part of the LLZK Project, under the Apache License v2.0.
4// See LICENSE.txt for license information.
5// Copyright 2025 Veridise Inc.
6// SPDX-License-Identifier: Apache-2.0
8// Adapted from mlir/include/mlir/Dialect/Func/IR/FuncOps.td
9// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
10// See https://llvm.org/LICENSE.txt for license information.
11// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
13//===----------------------------------------------------------------------===//
18include "llzk/Dialect/Function/IR/Dialect.td"
19include "llzk/Dialect/Verif/IR/OpInterfaces.td"
20include "llzk/Dialect/Shared/OpTraits.td"
21include "llzk/Dialect/Shared/Types.td"
23include "mlir/IR/OpAsmInterface.td"
24include "mlir/IR/SymbolInterfaces.td"
25include "mlir/Interfaces/CallInterfaces.td"
26include "mlir/Interfaces/ControlFlowInterfaces.td"
27include "mlir/Interfaces/FunctionInterfaces.td"
28include "mlir/Interfaces/InferTypeOpInterface.td"
29include "mlir/Interfaces/SideEffectInterfaces.td"
31class FunctionDialectOp<string mnemonic, list<Trait> traits = []>
32 : Op<FunctionDialect, mnemonic, traits>;
34//===----------------------------------------------------------------------===//
36//===----------------------------------------------------------------------===//
40 "def", [ParentOneOf<["::mlir::ModuleOp",
41 "::llzk::component::StructDefOp",
42 "::llzk::polymorphic::TemplateOp"]>,
43 DeclareOpInterfaceMethods<SymbolUserOpInterface>, AffineScope,
44 AutomaticAllocationScope, FunctionOpInterface,
45 IsolatedFromAbove, ContractTarget]> {
46 // NOTE: Cannot have SymbolTable trait because that would cause global
47 // functions without a body to produce "Operations with a 'SymbolTable' must
48 // have exactly one block"
49 let summary = "An operation with a name containing a single `SSACFG` region";
51 Operations within the function cannot implicitly capture values defined
52 outside of the function, i.e., functions are `IsolatedFromAbove`. All
53 external references must use function arguments (which are passed by value)
54 or reference external members or globals by symbol name.
56 Functions appearing within a `struct.def` have specific semantics and must
57 be named `compute`, `constrain`, or `product`. Functions appearing at the
58 module level (i.e. not within a `struct.def`) have no name restrictions and
59 their body may be elided to denote an external function declaration.
61 Modules and `struct.def` ops are not allowed to be nested within functions.
63 Function arguments may carry an optional `function.arg_name` attribute to
64 preserve the source-level argument name independently from the printed SSA
65 block argument name. The value must be a non-empty, untyped `StringAttr`,
66 and all attached `function.arg_name` values must be unique within the
67 function. Typed string attributes such as `"x" : i1` are rejected. The
68 attribute is only valid on function arguments.
69 Argument-splitting transforms preserve this metadata by deriving unique
70 names for generated arguments, such as `input[0]` for array elements or
71 `self.member` for struct members.
73 Function results may similarly carry an optional `function.res_name`
74 attribute. The value must be a non-empty, untyped `StringAttr`, all attached
75 result names must be unique within the function, and the attribute is only
76 valid on function results.
81 // External function definitions.
82 function.def private @abort()
83 function.def private @scribble(
84 !array.type<5 x !felt.type> {function.arg_name = "input"},
85 !struct.type<@Hello> {function.arg_name = "state"}) -> i1
87 // A function that returns its argument twice:
88 function.def @count(%x: !felt.type {function.arg_name = "x"})
89 -> (!felt.type {function.res_name = "first"},
90 !felt.type {function.res_name = "second"}) {
91 function.return %x, %x: !felt.type, !felt.type
94 // Function definition within a component
96 function.def @compute(%a: !felt.type {function.arg_name = "a"}) { function.return }
97 function.def @constrain(%a: !felt.type {function.arg_name = "a"}) { function.return }
102 // Duplicated from the pre-defined `func` dialect. We don't store the
103 // visibility attribute but, since we use `function_interface_impl` for
104 // parsing/printing, there is still the requirement that global functions
105 // declared without a body must specify the `private` visibility.
106 // Additionally, the default parsing/printing functions allow attributes on
107 // the arguments, results, and function itself.
109 // // Argument attribute
110 // function.def private @example_fn_arg(%x: i1 {llzk.pub})
111 // function.def private @example_fn_arg_name(%x: i1 {function.arg_name =
114 // // Result attribute
115 // function.def @example_fn_result() -> (i1 {dialectName.attrName = 0 :
118 // // Function attribute
119 // function.def @example_fn_attr() attributes {dialectName.attrName =
122 let arguments = (ins SymbolNameAttr:$sym_name,
123 TypeAttrOf<FunctionType>:$function_type,
124 OptionalAttr<DictArrayAttr>:$arg_attrs,
125 OptionalAttr<DictArrayAttr>:$res_attrs);
126 let regions = (region AnyRegion:$body);
128 let builders = [OpBuilder<(ins "::llvm::StringRef":$name,
129 "::mlir::FunctionType":$type,
130 CArg<"::llvm::ArrayRef<::mlir::NamedAttribute>", "{}">:$attrs,
131 CArg<"::llvm::ArrayRef<::mlir::DictionaryAttr>", "{}">:$argAttrs)>];
133 let extraClassDeclaration = [{
134 static FuncDefOp create(::mlir::Location location, ::llvm::StringRef name, ::mlir::FunctionType type,
135 ::llvm::ArrayRef<::mlir::NamedAttribute> attrs = {});
136 static FuncDefOp create(::mlir::Location location, ::llvm::StringRef name, ::mlir::FunctionType type,
137 ::mlir::Operation::dialect_attr_range attrs);
138 static FuncDefOp create(::mlir::Location location, ::llvm::StringRef name, ::mlir::FunctionType type,
139 ::llvm::ArrayRef<::mlir::NamedAttribute> attrs,
140 ::llvm::ArrayRef<::mlir::DictionaryAttr> argAttrs);
142 /// Create a deep copy of this function and all of its blocks, remapping any
143 /// operands that use values outside of the function using the map that is
144 /// provided (leaving them alone if no entry is present). If the mapper
145 /// contains entries for function arguments, these arguments are not
146 /// included in the new function. Replaces references to cloned sub-values
147 /// with the corresponding value that is copied, and adds those mappings to
149 FuncDefOp clone(::mlir::IRMapping &mapper);
152 /// Clone the internal blocks and attributes from this function into dest.
153 /// Any cloned blocks are appended to the back of dest. This function
154 /// asserts that the attributes of the current function and dest are
156 void cloneInto(FuncDefOp dest, ::mlir::IRMapping &mapper);
158 /// Return `true` iff the function def has the `allow_constraint` attribute.
159 inline bool hasAllowConstraintAttr() {
160 return getOperation()->hasAttr(llzk::function::AllowConstraintAttr::name);
163 /// Add (resp. remove) the `allow_constraint` attribute to (resp. from) the function def.
164 void setAllowConstraintAttr(bool newValue = true);
166 /// Return `true` iff the function def has the `allow_witness` attribute.
167 inline bool hasAllowWitnessAttr() {
168 return getOperation()->hasAttr(llzk::function::AllowWitnessAttr::name);
171 /// Add (resp. remove) the `allow_witness` attribute to (resp. from) the function def.
172 void setAllowWitnessAttr(bool newValue = true);
174 /// Return `true` iff the function def has the `allow_non_native_field_ops` attribute.
175 inline bool hasAllowNonNativeFieldOpsAttr() {
176 return getOperation()->hasAttr(llzk::function::AllowNonNativeFieldOpsAttr::name);
179 /// Add (resp. remove) the `allow_non_native_field_ops` attribute to (resp. from) the function def.
180 void setAllowNonNativeFieldOpsAttr(bool newValue = true);
182 /// Return `true` iff the argument at the given index has `pub` attribute.
183 bool hasArgPublicAttr(unsigned index);
185 /// Return `true` iff the argument at the given index has a `function.arg_name` attribute.
186 bool hasArgName(unsigned index);
188 /// Return the `function.arg_name` attribute for the argument at the given index.
189 ::std::optional<::mlir::StringAttr> getArgNameAttr(unsigned index);
191 /// Set the `function.arg_name` attribute for the argument at the given index.
192 void setArgNameAttr(unsigned index, const ::mlir::StringAttr &attr);
194 /// Set the `function.arg_name` attribute for the argument at the given index from a string.
195 void setArgName(unsigned index, ::llvm::StringRef name);
197 /// Return `true` iff the result at the given index has a `function.res_name` attribute.
198 bool hasResName(unsigned index);
200 /// Return the `function.res_name` attribute for the result at the given index.
201 ::std::optional<::mlir::StringAttr> getResNameAttr(unsigned index);
203 /// Set the `function.res_name` attribute for the result at the given index.
204 void setResNameAttr(unsigned index, const ::mlir::StringAttr &attr);
206 /// Set the `function.res_name` attribute for the result at the given index from a string.
207 void setResName(unsigned index, ::llvm::StringRef name);
209 /// Required by FunctionOpInterface.
210 /// Returns the region on the current operation that is callable. This may
211 /// return null in the case of an external callable object, e.g. an external
213 ::mlir::Region *getCallableRegion() { return isExternal() ? nullptr : &getBody(); }
215 /// Required by FunctionOpInterface.
216 /// Returns the argument types of this function.
217 ::llvm::ArrayRef<::mlir::Type> getArgumentTypes() { return getFunctionType().getInputs(); }
219 /// Required by FunctionOpInterface.
220 /// Returns the result types of this function.
221 ::llvm::ArrayRef<::mlir::Type> getResultTypes() { return getFunctionType().getResults(); }
223 /// Required by SymbolOpInterface.
224 bool isDeclaration() { return isExternal(); }
226 /// Return the full name for this function from the root module, including
227 /// all surrounding symbol table names (i.e., modules and structs).
228 ::mlir::SymbolRefAttr getFullyQualifiedName(bool requireParent = true);
230 /// Return `true` iff the function name is `FUNC_NAME_COMPUTE` (if needed, a check
231 /// that this FuncDefOp is located within a StructDefOp must be done separately).
232 inline bool nameIsCompute() { return FUNC_NAME_COMPUTE == getSymName(); }
234 /// Return `true` iff the function name is `FUNC_NAME_CONSTRAIN` (if needed, a
235 /// check that this FuncDefOp is located within a StructDefOp must be done separately).
236 inline bool nameIsConstrain() { return FUNC_NAME_CONSTRAIN == getSymName(); }
238 /// Return `true` iff the function name is `FUNC_NAME_PRODUCT` (if needed, a
239 /// check that this FuncDefOp is located within a StructDefOp must be done separately).
240 inline bool nameIsProduct() { return FUNC_NAME_PRODUCT == getSymName(); }
242 /// Return `true` iff the function is within a StructDefOp
243 inline bool isInStruct() { return ::llzk::component::isInStruct(*this); }
245 /// Return `true` iff the function is within a StructDefOp and named `FUNC_NAME_COMPUTE`.
246 inline bool isStructCompute() { return isInStruct() && nameIsCompute(); }
248 /// Return `true` iff the function is within a StructDefOp and named `FUNC_NAME_CONSTRAIN`.
249 inline bool isStructConstrain() { return isInStruct() && nameIsConstrain(); }
251 /// Return `true` iff the function is within a StructDefOp and named `FUNC_NAME_PRODUCT`.
252 inline bool isStructProduct() { return isInStruct() && nameIsProduct(); }
254 /// Return the "self" value (i.e. the return value) from the function (which must be
255 /// named `FUNC_NAME_COMPUTE`).
256 ::mlir::Value getSelfValueFromCompute();
258 /// Return the "self" value (i.e. the first parameter) from the function (which must be
259 /// named `FUNC_NAME_CONSTRAIN`).
260 ::mlir::Value getSelfValueFromConstrain();
262 /// Assuming the name is `FUNC_NAME_COMPUTE`, return the single StructType result.
263 ::llzk::component::StructType getSingleResultTypeOfCompute();
266 let hasCustomAssemblyFormat = 1;
270//===----------------------------------------------------------------------===//
272//===----------------------------------------------------------------------===//
275 : FunctionDialectOp<"return", [HasParent<"::llzk::function::FuncDefOp">,
276 Pure, MemRefsNormalizable, ReturnLike,
278 let summary = "Function return operation";
280 The `function.return` operation represents a return operation within a function.
281 The operation takes variable number of operands and produces no results.
282 The operand number and types must match the signature of the function
283 that contains the operation.
288 function.def @foo() : (!felt.type, index) {
290 return %0, %1 : !felt.type, index
295 let arguments = (ins Variadic<AnyLLZKType>:$operands);
297 let builders = [OpBuilder<(ins), [{
298 build($_builder, $_state, std::nullopt);
301 let assemblyFormat = "attr-dict ($operands^ `:` type($operands))?";
305//===----------------------------------------------------------------------===//
307//===----------------------------------------------------------------------===//
309def CallOp : FunctionDialectOp<
310 "call", [MemRefsNormalizable, AttrSizedOperandSegments,
311 VerifySizesForMultiAffineOps<1>,
312 DeclareOpInterfaceMethods<CallOpInterface>,
313 DeclareOpInterfaceMethods<SymbolUserOpInterface>]> {
314 let summary = "call operation";
316 The `function.call` operation represents a call to another function. The operands
317 and result types of the call must match the specified function type. The
318 callee is encoded as a symbol reference attribute named "callee" which must
319 be the full path to the target function from the root module (i.e., the module
320 containing the [llzk::LANG_ATTR_NAME] attribute).
324 // Call a global function defined in the root module.
325 function.call @do_stuff(%0) : (!struct.type<@Bob>) -> ()
326 %1, %2 = function.call @split(%x) : (index) -> (index, index)
328 // Call a function within a component
329 %2 = function.call @OtherStruct::@compute(%3, %4) : (index, index) -> !struct.type<@OtherStruct>
330 function.call @OtherStruct::@constrain(%5, %6) : (!struct.type<@OtherStruct>, !felt.type) -> ()
333 When the return StructType of a `compute()` function uses AffineMapAttr to
334 express struct parameter(s) that depend on a loop variable, the optional
335 instantiation parameter list of this operation must be used to instatiate
336 all AffineMap used as parameters to the StructType.
340 #M = affine_map<(i)[] -> (5*i+1)>
341 %r = function.call @A::@compute(%x){(%i)} : (!felt.type) -> !struct.type<@A<[#M]>>
344 When the call targets a free function within a `poly.template` region, the optional
345 template parameter list can be used to instantiate all `poly.param` symbols within
346 the template. If all `poly.param` symbols are used within the function signature,
347 this can be elided. Otherwise, it is required to instantiate the function. The `?`
348 wildcard can be used for any `poly.param` with a `poly.tvar` type restriction, even
349 those that cannot be inferred from the function signature. The wildcard allows for
350 inference of the type within the function body itself during the flattening pass
351 but may fail if the type cannot be inferred from the function body.
354 // See `VerifySizesForMultiAffineOps` for more explanation of these arguments.
356 // Call target function reference.
357 SymbolRefAttr:$callee,
358 // List of arguments to call the target function.
359 Variadic<AnyLLZKType>:$argOperands,
360 // List of parameters to instantiate all `poly.param` symbols when the
361 // callee is a free function inside a `poly.template` region.
362 OptionalAttr<ArrayAttr>:$templateParams,
363 // List of AffineMap operand groups where each group provides the
364 // arguments to instantiate the next (left-to-right) AffineMap used as a
365 // struct parameter in the result StructType.
366 VariadicOfVariadic<Index, "mapOpGroupSizes">:$mapOperands,
367 // Within each group in '$mapOperands', denotes the number of values that
368 // are AffineMap "dimensional" arguments with the remaining values being
369 // AffineMap "symbolic" arguments.
370 DefaultValuedAttr<DenseI32ArrayAttr, "{}">:$numDimsPerMap,
371 // Denotes the size of each variadic group in '$mapOperands'.
372 DenseI32ArrayAttr:$mapOpGroupSizes);
373 let results = (outs Variadic<AnyLLZKType>);
375 let assemblyFormat = [{
377 ( `<` custom<TemplateParams>($templateParams)^ `>` )?
378 `` `(` $argOperands `)`
379 ( `{` custom<MultiDimAndSymbolList>($mapOperands, $numDimsPerMap)^ `}` )?
380 `:` functional-type($argOperands, results)
381 custom<AttrDictWithWarnings>(attr-dict, prop-dict)
384 let useCustomPropertiesEncoding = 1;
386 // NOTE: In CreateArrayOp, the `verify()` function is declared in order to
387 // call `verifyAffineMapInstantiations()`. However, in this op that check must
388 // happen within `verifySymbolUses()` instead because the target FuncDefOp
389 // must be resolved to determine if a target function named
390 // "compute"/"constrain" is defined within a StructDefOp or within a ModuleOp
391 // because the verification differs for those cases.
393 // Define builders manually so inference of operand layout attributes is not
395 let skipDefaultBuilders = 1;
397 [OpBuilder<(ins "::mlir::TypeRange":$resultTypes,
398 "::mlir::SymbolRefAttr":$callee,
399 CArg<"::mlir::ValueRange", "{}">:$argOperands,
400 CArg<"::llvm::ArrayRef<::mlir::Attribute>", "{}">:$templateParams)>,
401 OpBuilder<(ins "::mlir::TypeRange":$resultTypes,
402 "::mlir::SymbolRefAttr":$callee,
403 "::llvm::ArrayRef<::mlir::ValueRange>":$mapOperands,
404 "::mlir::DenseI32ArrayAttr":$numDimsPerMap,
405 CArg<"::mlir::ValueRange", "{}">:$argOperands,
406 CArg<"::llvm::ArrayRef<::mlir::Attribute>", "{}">:$templateParams)>,
407 OpBuilder<(ins "::mlir::TypeRange":$resultTypes,
408 "::mlir::SymbolRefAttr":$callee,
409 "::llvm::ArrayRef<::mlir::ValueRange>":$mapOperands,
410 "::llvm::ArrayRef<int32_t>":$numDimsPerMap,
411 CArg<"::mlir::ValueRange", "{}">:$argOperands,
412 CArg<"::llvm::ArrayRef<::mlir::Attribute>",
413 "{}">:$templateParams),
415 build($_builder, $_state, resultTypes, callee, mapOperands,
416 $_builder.getDenseI32ArrayAttr(numDimsPerMap),
417 argOperands, templateParams);
419 OpBuilder<(ins "::llzk::function::FuncDefOp":$callee,
420 CArg<"::mlir::ValueRange", "{}">:$argOperands,
421 CArg<"::llvm::ArrayRef<::mlir::Attribute>",
422 "{}">:$templateParams),
424 build($_builder, $_state, callee.getResultTypes(),
425 callee.getFullyQualifiedName(false),
426 argOperands, templateParams);
428 OpBuilder<(ins "::llzk::function::FuncDefOp":$callee,
429 "::llvm::ArrayRef<::mlir::ValueRange>":$mapOperands,
430 "::mlir::DenseI32ArrayAttr":$numDimsPerMap,
431 CArg<"::mlir::ValueRange", "{}">:$argOperands,
432 CArg<"::llvm::ArrayRef<::mlir::Attribute>",
433 "{}">:$templateParams),
435 build($_builder, $_state, callee.getResultTypes(),
436 callee.getFullyQualifiedName(false), mapOperands, numDimsPerMap,
437 argOperands, templateParams);
439 OpBuilder<(ins "::llzk::function::FuncDefOp":$callee,
440 "::llvm::ArrayRef<::mlir::ValueRange>":$mapOperands,
441 "::llvm::ArrayRef<int32_t>":$numDimsPerMap,
442 CArg<"::mlir::ValueRange", "{}">:$argOperands,
443 CArg<"::llvm::ArrayRef<::mlir::Attribute>",
444 "{}">:$templateParams),
446 build($_builder, $_state, callee, mapOperands,
447 $_builder.getDenseI32ArrayAttr(numDimsPerMap),
448 argOperands, templateParams);
451 let extraClassDeclaration = [{
452 /// Required by CallOpInterface
453 ::mlir::Operation *resolveCallableInTable(::mlir::SymbolTableCollection *symbolTable);
455 /// Required by CallOpInterface
456 ::mlir::Operation *resolveCallable();
458 /// Return the FunctionType inferred from the arg operands and result types of this CallOp.
459 /// This is not necessarily the same as the callee's FunctionType but should unify with it
460 /// or else IR verification will fail.
461 ::mlir::FunctionType getTypeSignature();
463 /// Attempt type unfication between the inferred FunctionType from this CallOp (as LHS) and
464 /// the given FunctionType (as RHS). If successful, return a UnificationMap containing the
465 /// unifications that were made. Otherwise, return failure.
466 ::mlir::FailureOr<UnificationMap> unifyTypeSignature(::mlir::FunctionType other);
468 /// Return `true` iff the callee function name is `FUNC_NAME_COMPUTE` (this
469 /// does not check if the callee function is located within a StructDefOp).
470 inline bool calleeIsCompute() {
471 return FUNC_NAME_COMPUTE == getCallee().getLeafReference();
474 /// Return `true` iff the callee function name is `FUNC_NAME_PRODUCT` (this
475 /// does not check if the callee function is located within a StructDefOp).
476 inline bool calleeIsProduct() {
477 return FUNC_NAME_PRODUCT == getCallee().getLeafReference();
480 /// Return `true` iff the callee function can contain witness generation code
481 /// (this does not check if the callee function is located within a StructDefOp)
482 inline bool calleeContainsWitnessGen() {
483 return calleeIsCompute() || calleeIsProduct();
486 /// Return `true` iff the callee function name is `FUNC_NAME_CONSTRAIN` (this
487 /// does not check if the callee function is located within a StructDefOp).
488 inline bool calleeIsConstrain() { return FUNC_NAME_CONSTRAIN == getCallee().getLeafReference(); }
490 /// Return `true` iff the callee function name is `FUNC_NAME_COMPUTE` within a StructDefOp.
491 bool calleeIsStructCompute();
493 /// Return `true` iff the callee function name is `FUNC_NAME_PRODUCT` within a StructDefOp.
494 bool calleeIsStructProduct();
496 /// Return `true` iff the callee function name is `FUNC_NAME_CONSTRAIN` within a StructDefOp.
497 bool calleeIsStructConstrain();
499 /// Return the "self" value (i.e. the return value) from the callee function (which must be
500 /// named `FUNC_NAME_COMPUTE`).
501 ::mlir::Value getSelfValueFromCompute();
503 /// Return the "self" value (i.e. the first parameter) from the callee function (which must be
504 /// named `FUNC_NAME_CONSTRAIN`).
505 ::mlir::Value getSelfValueFromConstrain();
507 /// Resolve and return the target FuncDefOp for this CallOp.
508 ::mlir::FailureOr<::llzk::SymbolLookupResult<::llzk::function::FuncDefOp>>
509 getCalleeTarget(::mlir::SymbolTableCollection &tables);
511 /// Assuming the callee is `FUNC_NAME_COMPUTE`, return the single StructType result.
512 ::llzk::component::StructType getSingleResultTypeOfCompute();
514 /// Assuming the callee contains witness generation code, return the single StructType result.
515 ::llzk::component::StructType getSingleResultTypeOfWitnessGen();
517 /// Allocate consecutive storage of the ValueRange instances in the parameter
518 /// so it can be passed to the builders as an `ArrayRef<ValueRange>`.
519 static ::llvm::SmallVector<::mlir::ValueRange> toVectorOfValueRange(::mlir::OperandRangeRange);
521 /// Check type compatibility of the given template parameter value from this `CallOp` against
522 /// the declared type on the given `TemplateParamOp` (if any).
523 ::mlir::LogicalResult verifyTemplateParamCompatibility(
524 ::mlir::Attribute paramFromCallOp, ::llzk::polymorphic::TemplateParamOp targetParam
527 /// Check type compatibility of each template parameter value provided in this `CallOp` against
528 /// the declared type on each `TemplateParamOp` (if any).
530 /// Pre-condition assertions:
531 /// - `!isNullOrEmpty(getTemplateParamsAttr())`
532 /// - `getTemplateParamsAttr().size() == llvm::range_size(targetParamDefs)`
533 ::mlir::LogicalResult verifyTemplateParamCompatibility(
534 ::llvm::iterator_range<::mlir::Region::op_iterator<::llzk::polymorphic::TemplateParamOp>> targetParamDefs
537 /// Verify that each template parameter value provided in this `CallOp` is consistent with
538 /// the value inferred for the target `TemplateParamOp` in the given `UnificationMap`. The
539 /// `UnificationMap` is expected to contain the unification results of this `CallOp` against
540 /// the target function type signature.
542 /// Pre-condition assertions:
543 /// - `!isNullOrEmpty(getTemplateParamsAttr())`
544 /// - `getTemplateParamsAttr().size() == llvm::range_size(targetParamDefs)`
545 ::mlir::LogicalResult verifyTemplateParamsMatchInferred(
546 ::llvm::iterator_range<::mlir::Region::op_iterator<::llzk::polymorphic::TemplateParamOp>> targetParamDefs,
547 const UnificationMap &unifications
552#endif // LLZK_FUNC_OPS