LLZK 3.0.0
An open-source IR for Zero Knowledge (ZK) circuits
Loading...
Searching...
No Matches
Ops.cpp
Go to the documentation of this file.
1//===-- Ops.cpp - Verif operation implementations ---------------*- C++ -*-===//
2//
3// Part of the LLZK Project, under the Apache License v2.0.
4// See LICENSE.txt for license information.
5// Copyright 2026 Project LLZK
6// SPDX-License-Identifier: Apache-2.0
7//
8//===----------------------------------------------------------------------===//
9
11
20#include "llzk/Util/Compare.h"
24#include "llzk/Util/Walk.h"
25
26#include <mlir/Dialect/Arith/IR/Arith.h>
27#include <mlir/Dialect/SCF/IR/SCF.h>
28#include <mlir/Dialect/Utils/IndexingUtils.h>
29#include <mlir/IR/Attributes.h>
30#include <mlir/IR/BuiltinOps.h>
31#include <mlir/IR/Diagnostics.h>
32#include <mlir/IR/SymbolTable.h>
33#include <mlir/IR/ValueRange.h>
34#include <mlir/Interfaces/CallInterfaces.h>
35#include <mlir/Interfaces/FunctionImplementation.h>
36#include <mlir/Support/LLVM.h>
37#include <mlir/Support/LogicalResult.h>
38
39#include <llvm/ADT/ArrayRef.h>
40#include <llvm/ADT/STLExtras.h>
41#include <llvm/ADT/SmallVectorExtras.h>
42#include <llvm/ADT/Twine.h>
43
44#include <memory>
45
46// TableGen'd implementation files
48
49// TableGen'd implementation files
50#define GET_OP_CLASSES
52
53using namespace mlir;
54using namespace llzk::polymorphic;
55using namespace llzk::component;
56using namespace llzk::function;
57
58namespace {
59
60using namespace llzk::verif;
61
62// Check if the op is a valid contract target.
63bool isValidTarget(Operation *op) {
64 if (auto fnOp = dyn_cast<FuncDefOp>(op)) {
65 // Cannot target struct functions directly
66 return fnOp->getParentOfType<StructDefOp>() == nullptr;
67 }
68 // Only other supported target currently is a struct
69 return isa<StructDefOp>(op);
70}
71
72inline bool hasConflictingUnifications(const llzk::UnificationMap &unifications) {
73 return llvm::any_of(unifications, [](const auto &entry) { return !entry.second; });
74}
75
76struct TargetTypeInfo {
77 FunctionType funcType {};
78 ArrayAttr argAttrs {};
79};
80
81FailureOr<TargetTypeInfo> getTargetTypeInfo(Operation *op) {
82 if (auto fnOp = dyn_cast<FuncDefOp>(op)) {
83 // Recreate the function type with return types appended to the arguments.
84 FunctionType fnTy = fnOp.getFunctionType();
85 ArrayRef<Type> curInputs = fnTy.getInputs(), curResults = fnTy.getResults();
86 SmallVector<Type> newInputs;
87 newInputs.reserve(curInputs.size() + curResults.size());
88 newInputs.insert(newInputs.end(), curInputs.begin(), curInputs.end());
89 newInputs.insert(newInputs.end(), curResults.begin(), curResults.end());
90 // And no return types
91 auto newFnTy = fnTy.clone(newInputs, {});
92 // Do the same appending to the return attrs
93 ArrayAttr curArgAttrs = fnOp.getArgAttrsAttr(), curResAttrs = fnOp.getResAttrsAttr();
94 ArrayAttr newArgAttrsAttr {};
95 if (curArgAttrs || curResAttrs) {
96 auto *ctx = op->getContext();
97 SmallVector<Attribute> newArgAttrs;
98 // Since there are some attributes, it must match the length of the input arguments
99 newArgAttrs.reserve(newInputs.size());
100 if (curArgAttrs) {
101 newArgAttrs.insert(newArgAttrs.end(), curArgAttrs.begin(), curArgAttrs.end());
102 } else {
103 // Pad
104 newArgAttrs.insert(newArgAttrs.end(), curInputs.size(), DictionaryAttr::get(ctx));
105 }
106 if (curResAttrs) {
107 newArgAttrs.insert(newArgAttrs.end(), curResAttrs.begin(), curResAttrs.end());
108 } else {
109 // pad
110 newArgAttrs.insert(newArgAttrs.end(), curResults.size(), DictionaryAttr::get(ctx));
111 }
112 newArgAttrsAttr = ArrayAttr::get(ctx, newArgAttrs);
113 }
114
115 return TargetTypeInfo {
116 .funcType = newFnTy,
117 .argAttrs = newArgAttrsAttr,
118 };
119 }
120 if (auto structOp = dyn_cast<StructDefOp>(op)) {
121 if (FuncDefOp fnOp = structOp.getConstrainFuncOp(); fnOp && structOp.getComputeFuncOp()) {
122 return TargetTypeInfo {
123 .funcType = fnOp.getFunctionType(),
124 .argAttrs = fnOp.getArgAttrsAttr(),
125 };
126 } else {
127 FuncDefOp productFn = structOp.getProductFuncOp();
128 if (!productFn) {
129 return failure();
130 }
131 // Augment the product function signature to accept the self argument.
132 FunctionType fnTy = productFn.getFunctionType();
133 ArrayRef<Type> curInputs = fnTy.getInputs();
134 // Accept the self struct type in addition to existing inputs
135 SmallVector<Type> newInputs;
136 newInputs.reserve(curInputs.size() + 1);
137 newInputs.push_back(structOp.getType());
138 newInputs.insert(newInputs.end(), curInputs.begin(), curInputs.end());
139 // And no return types
140 auto newFnTy = fnTy.clone(newInputs, {});
141 // We also need to expand the arg attributes by one
142 auto *ctx = op->getContext();
143 ArrayAttr curArgAttrs = productFn.getArgAttrsAttr();
144 ArrayAttr newArgAttrsAttr = curArgAttrs;
145 if (curArgAttrs) {
146 SmallVector<Attribute> newArgAttrs;
147 newArgAttrs.reserve(curArgAttrs.size() + 1);
148 newArgAttrs.push_back(DictionaryAttr::get(ctx));
149 newArgAttrs.insert(newArgAttrs.end(), curArgAttrs.begin(), curArgAttrs.end());
150 newArgAttrsAttr = ArrayAttr::get(ctx, newArgAttrs);
151 }
152 return TargetTypeInfo {
153 .funcType = newFnTy,
154 .argAttrs = newArgAttrsAttr,
155 };
156 }
157 }
158
159 return failure();
160}
161
162enum class ForbiddenRequireConditionKind : uint8_t {
165};
166
168struct ForbiddenRequireCondition {
169 ForbiddenRequireConditionKind kind;
170 llvm::SmallSetVector<Location, 2> sourceLocs;
171};
172
175struct ForbiddenIncludedPrecondition {
176 std::optional<Location> calleePreconditionLoc = std::nullopt;
177 ForbiddenRequireConditionKind kind;
178 llvm::SmallSetVector<Location, 2> sourceLocs;
179};
180
182struct ForbiddenIncludedPreconditions {
183 IncludeOp includeOp;
184 llvm::SmallVector<ForbiddenIncludedPrecondition> failures;
185};
186
187std::optional<ForbiddenRequireCondition> classifyForbiddenConditionProvenance(
188 ModuleOp module, PreconditionOpInterface preCondOp, ContractOp contract
189) {
191 analyzeForbiddenPreconditionOpInfluenceInfo(module, contract, preCondOp);
193 return ForbiddenRequireCondition {
194 .kind = ForbiddenRequireConditionKind::StructMember,
195 .sourceLocs = influence.structMemberLocs,
196 };
197 }
199 return ForbiddenRequireCondition {
200 .kind = ForbiddenRequireConditionKind::FunctionReturn,
201 .sourceLocs = {},
202 };
203 }
204 return std::nullopt;
205}
206
207std::optional<ForbiddenIncludedPreconditions>
208classifyForbiddenIncludedPrecondition(ModuleOp module, IncludeOp includeOp) {
209 SymbolTableCollection tables;
210 auto calleeTarget = includeOp.getCalleeTarget(tables);
211 if (failed(calleeTarget)) {
212 return std::nullopt;
213 }
214 ContractOp parentContract = includeOp->getParentOfType<ContractOp>();
215 auto summary = analyzeForbiddenIncludedOpSummary(module, parentContract, includeOp);
216 if (!summary) {
217 return std::nullopt;
218 }
219
220 ForbiddenIncludedPreconditions result {.includeOp = includeOp, .failures = {}};
221 for (const auto &failure : summary.failures) {
222 if (hasInfluence(
223 failure.influenceInfo.influence, ForbiddenPreconditionInfluence::StructMember
224 )) {
225 result.failures.push_back(
226 ForbiddenIncludedPrecondition {
227 .calleePreconditionLoc = failure.preconditionLoc,
228 .kind = ForbiddenRequireConditionKind::StructMember,
229 .sourceLocs = failure.influenceInfo.structMemberLocs,
230 }
231 );
232 continue;
233 }
234 if (hasInfluence(
235 failure.influenceInfo.influence, ForbiddenPreconditionInfluence::FunctionReturn
236 )) {
237 result.failures.push_back(
238 ForbiddenIncludedPrecondition {
239 .calleePreconditionLoc = failure.preconditionLoc,
240 .kind = ForbiddenRequireConditionKind::FunctionReturn,
241 .sourceLocs = {},
242 }
243 );
244 }
245 }
246 return result.failures.empty() ? std::nullopt
247 : std::optional<ForbiddenIncludedPreconditions>(result);
248}
249
250// Map a classified restriction failure to the verifier diagnostic emitted on
251// the offending require op.
252LogicalResult emitForbiddenPrecondition(
253 PreconditionOpInterface preCondOp, ForbiddenRequireConditionKind kind,
254 llvm::ArrayRef<Location> sourceLocs = {}
255) {
256 switch (kind) {
257 case ForbiddenRequireConditionKind::StructMember: {
258 InFlightDiagnostic diag =
259 preCondOp->emitOpError("condition cannot be derived from a struct member value");
260 for (auto sourceLoc : sourceLocs) {
261 diag.attachNote(sourceLoc) << "forbidden struct member value originates here";
262 }
263 return diag;
264 }
265 case ForbiddenRequireConditionKind::FunctionReturn: {
266 return preCondOp->emitOpError("condition cannot be derived from a function return value");
267 }
268 }
269 llvm_unreachable("unknown forbidden require condition kind");
270}
271
272LogicalResult emitForbiddenIncludedPreconditions(
273 IncludeOp includeOp, llvm::ArrayRef<ForbiddenIncludedPrecondition> failures
274) {
275 bool sawStructMember = false;
276 bool sawFunctionReturn = false;
277 for (const ForbiddenIncludedPrecondition &failure : failures) {
278 sawStructMember |= failure.kind == ForbiddenRequireConditionKind::StructMember;
279 sawFunctionReturn |= failure.kind == ForbiddenRequireConditionKind::FunctionReturn;
280 }
281
282 InFlightDiagnostic diag = [&]() -> InFlightDiagnostic {
283 if (sawStructMember && sawFunctionReturn) {
284 return includeOp.emitOpError(
285 "includes preconditions whose conditions cannot be derived from forbidden sources"
286 );
287 }
288 if (sawStructMember) {
289 return includeOp.emitOpError(
290 "includes preconditions whose conditions cannot be derived from a struct member value"
291 );
292 }
293 return includeOp.emitOpError(
294 "includes preconditions whose conditions cannot be derived from a function return value"
295 );
296 }();
297
298 for (const ForbiddenIncludedPrecondition &failure : failures) {
299 if (failure.calleePreconditionLoc) {
300 diag.attachNote(failure.calleePreconditionLoc) << "included precondition triggered here";
301 }
302 for (Location sourceLoc : failure.sourceLocs) {
303 diag.attachNote(sourceLoc) << "forbidden struct member value originates here";
304 }
305 }
306 return diag;
307}
308
309} // namespace
310
311namespace llzk::verif {
312
313//===------------------------------------------------------------------===//
314// ContractOp
315//===------------------------------------------------------------------===//
316
317void ContractOp::initializeEmptyBody(
318 OpBuilder &builder, OperationState &state, FunctionType functionType
319) {
320 Region *body = state.addRegion();
321 auto *entryBlock = new Block();
322
323 SmallVector<Location> argLocs(functionType.getNumInputs(), state.location);
324 entryBlock->addArguments(functionType.getInputs(), argLocs);
325 body->push_back(entryBlock);
326
327 ContractOp::ensureTerminator(*body, builder, state.location);
328}
329
331 OpBuilder &odsBuilder, OperationState &odsState, StringRef name, llvm::StringRef target
332) {
333 build(odsBuilder, odsState, name, SymbolRefAttr::get(odsBuilder.getContext(), target));
334}
335
337 ::mlir::OpBuilder &odsBuilder, ::mlir::OperationState &odsState, ::llvm::StringRef name,
338 ::mlir::SymbolRefAttr target
339) {
340 // Any errors here in the construction from the target information are not
341 // reported here, but will instead be reported when the verify function fails
342 // to verify this op.
343 SymbolTableCollection tables;
344 // Find the target of the contract
345 FailureOr<SymbolLookupResultUntyped> targetRes =
346 lookupTopLevelSymbol(tables, target, odsBuilder.getBlock()->getParentOp());
347 if (failed(targetRes)) {
348 return;
349 }
350 Operation *targetOp = targetRes->get();
351 if (!isValidTarget(targetOp)) {
352 return;
353 }
354 FailureOr<TargetTypeInfo> infoRes = getTargetTypeInfo(targetOp);
355 if (failed(infoRes)) {
356 return;
357 }
358 TargetTypeInfo &info = *infoRes;
359 build(odsBuilder, odsState, name, target, info.funcType, info.argAttrs);
360}
361
362bool ContractOp::hasArgPublicAttr(unsigned index) {
363 if (index < this->getNumArguments()) {
364 DictionaryAttr res = function_interface_impl::getArgAttrDict(*this, index);
365 return res ? res.contains(PublicAttr::name) : false;
366 }
367 return false;
368}
369
370bool ContractOp::hasArgName(unsigned index) { return static_cast<bool>(getArgNameAttr(index)); }
371
372std::optional<StringAttr> ContractOp::getArgNameAttr(unsigned index) {
373 if (index >= getNumArguments()) {
374 return std::nullopt;
375 }
376 if (StringAttr attr = getArgAttrOfType<StringAttr>(index, ARG_NAME_ATTR_NAME)) {
377 return attr;
378 }
379 return std::nullopt;
380}
381
382void ContractOp::setArgNameAttr(unsigned index, const StringAttr &attr) {
383 assert(index < getNumArguments() && "argument index out of range");
384 setArgAttr(index, ARG_NAME_ATTR_NAME, attr);
385}
386
387void ContractOp::setArgName(unsigned index, llvm::StringRef name) {
388 setArgNameAttr(index, StringAttr::get(getContext(), name));
389}
390
391SymbolRefAttr ContractOp::getFullyQualifiedName(bool requireParent) {
392 return llzk::getFullyQualifiedName(*this, requireParent);
393}
394
395LogicalResult ContractOp::verifySymbolUses(SymbolTableCollection &tables) {
396 // Verify the target of the contract
397 FailureOr<ModuleOp> rootRes = getRootModule(getOperation());
398 if (failed(rootRes)) {
399 return emitOpError().append("could not lookup root module");
400 }
401 FailureOr<SymbolLookupResultUntyped> targetRes =
402 lookupTopLevelSymbol(tables, getTargetAttr(), rootRes->getOperation());
403 if (failed(targetRes)) {
404 return emitOpError().append("could not find target \"@", getTarget(), "\"");
405 }
406
407 FunctionType contractTy = getFunctionType();
408 // Verify the symbols in the contract argument
409 if (failed(verifyTypeResolution(tables, *this, contractTy))) {
410 // verifyTypeResolution already reports error messages
411 return failure();
412 }
413
414 // Verify the target symbol
415 Operation *targetOp = targetRes->get();
416 if (!isValidTarget(targetOp)) {
417 return emitOpError()
418 .append("target \"", getTargetAttr(), "\" is not a supported contract target")
419 .attachNote(targetOp->getLoc())
420 .append("target defined here");
421 }
422 if (auto contractParentTemplate = getParentOfType<TemplateOp>(*this)) {
423 auto targetParentTemplate = getParentOfType<TemplateOp>(targetOp);
424 if (targetParentTemplate != contractParentTemplate) {
425 InFlightDiagnostic diag = emitOpError().append(
426 "contract nested in template \"@", contractParentTemplate.getSymName(),
427 "\" must target a symbol in the same template"
428 );
429 if (targetParentTemplate) {
430 diag.attachNote(targetParentTemplate.getLoc()).append("target template defined here");
431 } else {
432 diag.attachNote(targetOp->getLoc()).append("target defined here");
433 }
434 return diag;
435 }
436 }
437 FailureOr<TargetTypeInfo> targetInfoRes = getTargetTypeInfo(targetOp);
438 if (failed(targetInfoRes)) {
439 // The struct verifier reports malformed struct bodies; avoid cascading diagnostics here.
440 // Returning failure() would actually cause the expected diagnostic from the first failure
441 // to be suppressed, so we return success() to avoid that.
442 if (isa<StructDefOp>(targetOp)) {
443 return success();
444 }
445 return emitOpError()
446 .append("unsupported target type \"", targetOp->getName(), "\"")
447 .attachNote(targetOp->getLoc())
448 .append("target defined here");
449 }
450 TargetTypeInfo &targetInfo = *targetInfoRes;
451 UnificationMap unifications;
452 bool unifies =
453 functionTypesUnify(contractTy, targetInfo.funcType, targetRes->getNamespace(), &unifications);
454 if (!unifies || hasConflictingUnifications(unifications)) {
455 return emitOpError()
456 .append("contract type does not match target type")
457 .attachNote(targetOp->getLoc())
458 .append("target defined here");
459 }
460 if (targetInfo.argAttrs != getArgAttrsAttr()) {
461 return emitOpError()
462 .append(
463 "contract arg attributes ", getArgAttrsAttr(), " does not match target arg attributes ",
464 targetInfo.argAttrs
465 )
466 .attachNote(targetOp->getLoc())
467 .append("target defined here");
468 }
469
470 return success();
471}
472
473// Parse the ContractOp syntax using the built-in parsing of function-like
474// operations. We'll verify contract-specific restrictions in `verify`.
475ParseResult ContractOp::parse(OpAsmParser &parser, OperationState &result) {
476 StringAttr typeAttrName = getFunctionTypeAttrName(result.name);
477 StringAttr argAttrsName = getArgAttrsAttrName(result.name);
478 StringAttr resAttrsName = getResAttrsAttrName(result.name);
479
480 SmallVector<OpAsmParser::Argument> entryArgs;
481 SmallVector<DictionaryAttr> resultAttrs;
482 SmallVector<Type> resultTypes;
483 auto &builder = parser.getBuilder();
484
485 // Parse the name as a symbol.
486 StringAttr nameAttr;
487 if (parser.parseSymbolName(nameAttr, SymbolTable::getSymbolAttrName(), result.attributes)) {
488 return failure();
489 }
490
491 // Parse the target symbol
492 if (parser.parseKeyword("for")) {
493 return failure();
494 }
495
496 SymbolRefAttr targetAttr;
497 if (parser.parseCustomAttributeWithFallback(
498 targetAttr, parser.getBuilder().getType<::mlir::NoneType>()
499 )) {
500 return failure();
501 }
502 if (!targetAttr) {
503 return failure();
504 }
505 result.getOrAddProperties<ContractOp::Properties>().target = targetAttr;
506
507 // Parse the function signature.
508 SMLoc signatureLocation = parser.getCurrentLocation();
509 bool isVariadic = false;
510
511 if (function_interface_impl::parseFunctionSignatureWithArguments(
512 parser, /*allowVariadic*/ false, entryArgs, isVariadic, resultTypes, resultAttrs
513 )) {
514 return failure();
515 }
516 assert(isVariadic == false);
517 // There should be no return types or attributes.
518 if (!resultTypes.empty() || !resultAttrs.empty()) {
519 return failure();
520 }
521
522 std::string errorMessage;
523 SmallVector<Type> argTypes;
524 argTypes.reserve(entryArgs.size());
525 for (auto &arg : entryArgs) {
526 argTypes.push_back(arg.type);
527 }
528 Type type = builder.getFunctionType(argTypes, resultTypes);
529 if (!type) {
530 return parser.emitError(signatureLocation)
531 << "failed to construct function type" << (errorMessage.empty() ? "" : ": ")
532 << errorMessage;
533 }
534 result.addAttribute(typeAttrName, TypeAttr::get(type));
535
536 // If function attributes are present, parse them.
537 NamedAttrList parsedAttributes;
538 SMLoc attributeDictLocation = parser.getCurrentLocation();
539 if (parser.parseOptionalAttrDictWithKeyword(parsedAttributes)) {
540 return failure();
541 }
542
543 // Disallow attributes that are inferred from elsewhere in the attribute
544 // dictionary.
545 for (StringRef disallowed :
546 {SymbolTable::getVisibilityAttrName(), SymbolTable::getSymbolAttrName(),
547 typeAttrName.getValue()}) {
548 if (parsedAttributes.get(disallowed)) {
549 return parser.emitError(attributeDictLocation, "'")
550 << disallowed
551 << "' is an inferred attribute and should not be specified in the "
552 "explicit attribute dictionary";
553 }
554 }
555 result.attributes.append(parsedAttributes);
556
557 // Add the attributes to the function arguments.
558 call_interface_impl::addArgAndResultAttrs(
559 builder, result, entryArgs, resultAttrs, argAttrsName, resAttrsName
560 );
561
562 // Parse the required contract body.
563 auto *body = result.addRegion();
564 SMLoc loc = parser.getCurrentLocation();
565 if (parser.parseRegion(
566 *body, entryArgs,
567 /*enableNameShadowing=*/false
568 )) {
569 return failure();
570 }
571
572 // Contract body was parsed, make sure its not empty.
573 if (body->empty()) {
574 return parser.emitError(loc, "expected non-empty contract body");
575 }
576
577 ContractOp::ensureTerminator(*body, parser.getBuilder(), result.location);
578
579 return success();
580}
581
582void ContractOp::print(OpAsmPrinter &p) {
583 // Print the operation and the contract name.
584 p << ' ';
585 p.printSymbolName(getSymName());
586
587 // Print the name of the contract's target.
588 p << " for ";
589 p.printAttributeWithoutType(getTarget());
590 p << ' ';
591
592 ArrayRef<Type> argTypes = getArgumentTypes();
593 function_interface_impl::printFunctionSignature(
594 p, *this, argTypes, /*isVariadic*/ false, /*resultTypes*/ ArrayRef<Type>()
595 );
596 function_interface_impl::printFunctionAttributes(
597 p, *this,
598 /*elided*/
600 );
601 // Print the body.
602 Region &body = getRegion();
603 p << ' ';
604 p.printRegion(
605 body, /*printEntryBlockArgs=*/false,
606 /*printBlockTerminators=*/false
607 );
608}
609
610LogicalResult ContractOp::verify() {
611 OwningEmitErrorFn emitErrorFunc = getEmitOpErrFn(this);
612
613 if ((*this)->hasAttr(ARG_NAME_ATTR_NAME)) {
614 return emitOpError() << '\'' << ARG_NAME_ATTR_NAME << "' is only valid on function arguments";
615 }
616
617 if (ArrayAttr argAttrs = getAllArgAttrs()) {
618 llvm::DenseSet<StringAttr> seenNames;
619 for (auto [i, attr] : llvm::enumerate(argAttrs)) {
620 auto dictAttr = llvm::dyn_cast<DictionaryAttr>(attr);
621 if (!dictAttr) {
622 continue;
623 }
624 Attribute argNameAttr = dictAttr.get(ARG_NAME_ATTR_NAME);
625 if (!argNameAttr) {
626 continue;
627 }
628 auto argName = llvm::dyn_cast<StringAttr>(argNameAttr);
629 if (!argName) {
630 return emitOpError() << '\'' << ARG_NAME_ATTR_NAME << "' on argument " << i
631 << " must be a string attribute";
632 }
633 if (!llvm::isa<NoneType>(argName.getType())) {
634 return emitOpError() << '\'' << ARG_NAME_ATTR_NAME << "' on argument " << i
635 << " must not have an explicit type";
636 }
637 if (argName.getValue().empty()) {
638 return emitOpError() << '\'' << ARG_NAME_ATTR_NAME << "' on argument " << i
639 << " must not be empty";
640 }
641 if (!seenNames.insert(argName).second) {
642 return emitOpError() << "duplicate '" << ARG_NAME_ATTR_NAME << "' value \""
643 << argName.getValue() << "\" on argument " << i;
644 }
645 }
646 }
647
648 // Unlike for FuncDefOps, we don't verify that the inputs are valid LLZK types,
649 // as we will check that the args match the target arguments in `verifySymbolUses()`
650
651 // Ensure that the contract does not contain nested modules, structs, or functions.
652 WalkResult res = this->walk<WalkOrder::PreOrder>([this](Operation *op) {
653 if (isa<ModuleOp, TemplateOp, FuncDefOp, StructDefOp>(op)) {
654 getEmitOpErrFn(op)().append(
655 "cannot be nested within '", getOperation()->getName(), "' operations"
656 );
657 return WalkResult::interrupt();
658 }
659 return WalkResult::advance();
660 });
661 return failure(res.wasInterrupted());
662}
663
665 // Verify precondition restrictions in the region verifier so that ops contained
666 // within the contract are verified before these checks. This avoids segfaults
667 // when there are malformed inner ops and instead allows appropriate inner diagnostics
668 // to be generated first. In sum, we can rest assured that the ops we traverse and
669 // analyze here have already been verified.
670
671 SmallVector<PreconditionOpInterface> preconditionOps =
672 walkCollect<PreconditionOpInterface>(*this);
673 SmallVector<IncludeOp> includeOps = walkCollect<IncludeOp>(*this);
674 if (preconditionOps.empty() && includeOps.empty()) {
675 return success();
676 }
677
678 ModuleOp module = getOperation()->getParentOfType<ModuleOp>();
679 if (!module) {
680 return emitOpError("must have a parent module to analyze condition provenance");
681 }
682
683 for (PreconditionOpInterface preCond : preconditionOps) {
684 if (auto forbidden = classifyForbiddenConditionProvenance(module, preCond, *this)) {
685 return emitForbiddenPrecondition(
686 preCond, forbidden->kind, forbidden->sourceLocs.getArrayRef()
687 );
688 }
689 }
690
691 for (IncludeOp includeOp : includeOps) {
692 if (auto forbidden = classifyForbiddenIncludedPrecondition(module, includeOp)) {
693 return emitForbiddenIncludedPreconditions(forbidden->includeOp, forbidden->failures);
694 }
695 }
696
697 return success();
698}
699
700FailureOr<SymbolLookupResult<StructDefOp>>
701ContractOp::getStructTarget(SymbolTableCollection &tables) {
703 tables, getTarget(), getParentOfType<ModuleOp>(getOperation()), /*reportMissing*/ false
704 );
705}
706
707FailureOr<SymbolLookupResult<FuncDefOp>> ContractOp::getFuncTarget(SymbolTableCollection &tables) {
709 tables, getTarget(), getParentOfType<ModuleOp>(getOperation()), /*reportMissing*/ false
710 );
711}
712
713FailureOr<SymbolLookupResult<ContractTargetOpInterface>>
714ContractOp::getTargetOp(SymbolTableCollection &tables) {
716 tables, getTarget(), getParentOfType<ModuleOp>(getOperation()), /*reportMissing*/ false
717 );
718}
719
720FailureOr<Value> ContractOp::getSelfValue() {
721 if (failed(getStructTarget()) || getNumArguments() == 0) {
722 return failure();
723 }
724 return getArgument(0);
725}
726
727//===------------------------------------------------------------------===//
728// IncludeOp
729//===------------------------------------------------------------------===//
730
732 OpBuilder &odsBuilder, OperationState &odsState, SymbolRefAttr callee, ValueRange argOperands,
733 ArrayRef<Attribute> templateParams
734) {
735 odsState.addOperands(argOperands);
737 odsBuilder, odsState, llzk::checkedCast<int32_t>(argOperands.size())
738 );
739 props.setCallee(callee);
740 addTemplateParams<IncludeOp>(odsBuilder, props, templateParams);
741}
742
744 OpBuilder &odsBuilder, OperationState &odsState, SymbolRefAttr callee,
745 ArrayRef<ValueRange> mapOperands, DenseI32ArrayAttr numDimsPerMap, ValueRange argOperands,
746 ArrayRef<Attribute> templateParams
747) {
748 odsState.addOperands(argOperands);
750 odsBuilder, odsState, mapOperands, numDimsPerMap,
751 llzk::checkedCast<int32_t>(argOperands.size())
752 );
753 props.setCallee(callee);
754 addTemplateParams<IncludeOp>(odsBuilder, props, templateParams);
755}
756
757namespace {
758
759struct IncludeOpVerifier {
760 explicit IncludeOpVerifier(IncludeOp *c) : includeOp(c) {}
761 virtual ~IncludeOpVerifier() = default;
762
763 LogicalResult verify() {
764 // Rather than immediately returning on failure, we check all verifier steps and aggregate to
765 // provide as many errors are possible in a single verifier run.
766 LogicalResult aggregateResult = success();
767 if (failed(verifyInputs())) {
768 aggregateResult = failure();
769 }
770 if (failed(verifyTemplateParams())) {
771 aggregateResult = failure();
772 }
773 return aggregateResult;
774 }
775
776protected:
777 IncludeOp *includeOp;
778
779 virtual LogicalResult verifyInputs() = 0;
780 virtual LogicalResult verifyTemplateParams() = 0;
781
782 LogicalResult verifyNoTemplateInstantiations() {
783 if (!isNullOrEmpty(includeOp->getTemplateParamsAttr())) {
784 return includeOp->emitOpError().append(
785 "can only have template instantiations when targeting a templated contract"
786 );
787 }
788 return success();
789 }
790};
791
792struct KnownTargetVerifier : public IncludeOpVerifier {
793 KnownTargetVerifier(IncludeOp *c, SymbolLookupResult<ContractOp> &&tgtRes)
794 : IncludeOpVerifier(c), shared(c, tgtRes) {}
795
796 LogicalResult verifyInputs() override {
797 return shared.verifyTypesMatch(
798 includeOp->getArgOperands().getTypes(), shared.getTargetType().getInputs(), "operand"
799 );
800 }
801
802 LogicalResult verifyTemplateParams() override {
803 Operation *tgtOp = shared.getTarget().getOperation();
804 if (TemplateOp tgtOpParent = getParentOfType<TemplateOp>(tgtOp)) {
805 return shared.verifyTemplateInstantiation(tgtOpParent);
806 } else {
807 // Non-template contracts cannot contain template parameter instantiations.
808 return verifyNoTemplateInstantiations();
809 }
810 }
811
812private:
813 llzk::KnownTargetVerifier<IncludeOp, ContractOp> shared;
814};
815
816} // namespace
817
818LogicalResult IncludeOp::verifySymbolUses(SymbolTableCollection &tables) {
819 // First, verify symbol resolution in all input and output types.
820 if (failed(verifyTypeResolution(tables, *this, getTypeSignature()))) {
821 return failure(); // verifyTypeResolution() already emits a sufficient error message
822 }
823
824 // Check that the callee attribute was specified.
825 SymbolRefAttr calleeAttr = getCalleeAttr();
826 if (!calleeAttr) {
827 return emitOpError("requires a 'callee' symbol reference attribute");
828 }
829
830 // If the callee references a parameter of the template where this call appears, perform
831 // the subset of checks that can be done even though the target is unknown.
832 if (calleeAttr.getNestedReferences().size() == 1) {
833 if (TemplateOp parent = getParentOfType<TemplateOp>(*this)) {
834 if (auto constParam = parent.getConstNamed<TemplateParamOp>(calleeAttr.getRootReference())) {
835 return this->emitError("expected parameterized callee to target a struct function")
836 .attachNote(constParam->getLoc())
837 .append(
838 " (i.e. \"@", FUNC_NAME_PRODUCT, "\", \"@", FUNC_NAME_COMPUTE, "\", or \"@",
840 );
841 }
842 }
843 }
844
845 // Otherwise, callee must be specified via full path from the root module. Perform the full set of
846 // checks against the known target function.
848 tables, calleeAttr, getParentOfType<ModuleOp>(getOperation())
849 );
850 if (failed(tgtOpt)) {
851 return this->emitError() << "expected '" << ContractOp::getOperationName() << "' named \""
852 << calleeAttr << '"';
853 }
854 return KnownTargetVerifier(this, std::move(*tgtOpt)).verify();
855}
856
857FailureOr<SymbolLookupResult<ContractOp>>
858IncludeOp::getCalleeTarget(SymbolTableCollection &tables) {
859 Operation *thisOp = this->getOperation();
860 auto root = getRootModule(thisOp);
861 assert(succeeded(root));
862 return llzk::lookupSymbolIn<ContractOp>(tables, getCallee(), root->getOperation(), thisOp);
863}
864
866 SymbolTableCollection tables;
867 auto callee = getCalleeTarget(tables);
868 return succeeded(callee) && callee->get().hasStructTarget();
869}
870
872 SymbolTableCollection tables;
873 auto callee = getCalleeTarget(tables);
874 assert(succeeded(callee) && "include callee must resolve");
875 if (!callee->get().hasStructTarget()) {
876 return nullptr;
877 }
878 assert(getNumOperands() > 0 && "include op must have a self operand");
879 return getOperand(0);
880}
881
883CallInterfaceCallable IncludeOp::getCallableForCallee() { return getCalleeAttr(); }
884
886void IncludeOp::setCalleeFromCallable(CallInterfaceCallable callee) {
887 setCalleeAttr(llvm::cast<SymbolRefAttr>(callee));
888}
889
890Operation *IncludeOp::resolveCallableInTable(SymbolTableCollection *symbolTable) {
891 FailureOr<SymbolLookupResult<ContractOp>> res =
892 llzk::resolveCallable<ContractOp>(*symbolTable, *this);
893 if (failed(res)) {
894 return nullptr;
895 }
896 if (res->isManaged()) {
897 this->emitWarning(
898 "IncludeOp::resolveCallableInTable: cannot return "
899 "pointer to a managed Operation since it would cause memory errors. "
900 "Consider running -llzk-inline-includes to avoid encountering managed Operations."
901 );
902 return nullptr;
903 }
904 return res->get();
905}
906
908 SymbolTableCollection tables;
909 return resolveCallableInTable(&tables);
910}
911
912//===------------------------------------------------------------------===//
913// InvariantOp
914//===------------------------------------------------------------------===//
915
917 OpBuilder &odsBuilder, OperationState &odsState, StringRef loop_name,
918 ArrayRef<Type> loop_arg_types, ArrayRef<Location> loop_arg_locs
919) {
920 odsState.getOrAddProperties<InvariantOp::Properties>().loop_name =
921 odsBuilder.getStringAttr(loop_name);
922 odsState.getOrAddProperties<InvariantOp::Properties>().loop_arg_types =
923 odsBuilder.getTypeArrayAttr(loop_arg_types);
924 auto region = std::make_unique<Region>();
925 auto &block = region->emplaceBlock();
926 block.addArguments(loop_arg_types, loop_arg_locs);
927 odsState.regions.push_back(std::move(region));
928}
929
930namespace {
931static LogicalResult verifyArgTypes(InvariantTargetOpInterface target, InvariantOp *op) {
932 auto targetArgTypes = target.getArgumentTypes();
933 auto declaredTypes = op->getLoopArgTypes().getValue();
934 auto bodyArgTypes = op->getBody()->getArgumentTypes();
935
936 if (targetArgTypes.size() != declaredTypes.size()) {
937 return op->emitOpError() << "target has " << targetArgTypes.size()
938 << " arguments but invariant declared " << declaredTypes.size();
939 }
940 if (bodyArgTypes.size() != declaredTypes.size()) {
941 return op->emitOpError() << "invariant body has " << targetArgTypes.size()
942 << " arguments but declared " << declaredTypes.size();
943 }
944
945 bool failed = false;
946 for (auto [n, types] :
947 llvm::enumerate(llvm::zip_equal(targetArgTypes, bodyArgTypes, declaredTypes))) {
948 auto [targetType, bodyArgType, declaredType] = types;
949
950 if (targetType != llvm::cast<TypeAttr>(declaredType).getValue()) {
951 failed = true;
952 op->emitOpError() << "target argument #" << n << " expected type " << targetType
953 << " but invariant declared type " << declaredType;
954 }
955 if (bodyArgType != llvm::cast<TypeAttr>(declaredType).getValue()) {
956 failed = true;
957 op->emitOpError() << "invariant argument #" << n << " expected type " << targetType
958 << " but invariant declared type " << declaredType;
959 }
960 }
961
962 return failure(failed);
963}
964} // namespace
965
966LogicalResult InvariantOp::verify() {
967 auto invariantTarget = getTarget();
968 if (failed(invariantTarget)) {
969 return failure();
970 }
971
972 return verifyArgTypes(*invariantTarget, this);
973}
974
975ParseResult InvariantOp::parse(OpAsmParser &parser, OperationState &result) {
976 if (failed(parser.parseKeyword("for"))) {
977 return failure();
978 }
979
980 // Parse the loop label as a symbol.
981 StringAttr loopNameAttr;
982 if (parser.parseSymbolName(loopNameAttr)) {
983 return failure();
984 }
985 result.getOrAddProperties<InvariantOp::Properties>().loop_name = loopNameAttr;
986
987 // Parse the function signature.
988 bool isVariadic = false;
989 SmallVector<OpAsmParser::Argument> entryArgs;
990 SmallVector<DictionaryAttr> resultAttrs;
991 SmallVector<Type> resultTypes;
992
993 if (function_interface_impl::parseFunctionSignatureWithArguments(
994 parser, /*allowVariadic*/ false, entryArgs, isVariadic, resultTypes, resultAttrs
995 )) {
996 return failure();
997 }
998 assert(isVariadic == false);
999 // There should be no return types or attributes.
1000 if (!resultTypes.empty() || !resultAttrs.empty()) {
1001 return failure();
1002 }
1003
1004 SmallVector<Type> argTypes = llvm::map_to_vector(entryArgs, [](auto arg) { return arg.type; });
1005 result.getOrAddProperties<InvariantOp::Properties>().loop_arg_types =
1006 parser.getBuilder().getTypeArrayAttr(argTypes);
1007
1008 auto *body = result.addRegion();
1009 SMLoc loc = parser.getCurrentLocation();
1010 if (parser.parseRegion(
1011 *body, entryArgs,
1012 /*enableNameShadowing=*/false
1013 )) {
1014 return failure();
1015 }
1016
1017 if (body->empty()) {
1018 return parser.emitError(loc, "expected non-empty invariant body");
1019 }
1020
1021 return success();
1022}
1023
1024void InvariantOp::print(OpAsmPrinter &p) {
1025 // Print the name of the invariants's target.
1026 p << " for ";
1027 p.printSymbolName(getLoopName());
1028 p << '(';
1029 llvm::interleave(getBody()->getArguments(), [&p](auto arg) {
1030 p.printRegionArgument(arg);
1031 }, [&p]() { p << ", "; });
1032 p << ") ";
1033 // Print the body.
1034 Region &body = getRegion();
1035 p.printRegion(
1036 body, /*printEntryBlockArgs=*/false,
1037 /*printBlockTerminators=*/true
1038 );
1039}
1040
1042 return this->getOperation()->getParentOfType<ContractOp>();
1043}
1044
1045FailureOr<InvariantTargetOpInterface> InvariantOp::getTarget() {
1046 auto target = getParentContract().getTargetOp();
1047 if (failed(target)) {
1048 return failure();
1049 }
1050 SmallVector<InvariantTargetOpInterface> matches;
1051 for (auto invariantTarget : target->get().getLoops()) {
1052 auto targetLabel = invariantTarget.getLabel();
1053 if (succeeded(targetLabel) && *targetLabel == getLoopName()) {
1054 matches.push_back(invariantTarget);
1055 }
1056 }
1057
1058 if (matches.size() == 0) {
1059 return emitOpError() << "no invariant target with label \"" << getLoopName()
1060 << "\" found in contract target " << target->get().getNameAttr();
1061 }
1062 if (matches.size() > 1) {
1063 return emitOpError() << "ambiguous label \"" << getLoopName() << "\" matched " << matches.size()
1064 << " invariant targets in contract target " << target->get().getNameAttr();
1065 }
1066 return matches[0];
1067}
1068
1069} // namespace llzk::verif
This file contains an analysis and utilities for determining if a verif precondition is dependent,...
::mlir::FunctionType getFunctionType()
Definition Ops.cpp.inc:1229
::mlir::ArrayAttr getArgAttrsAttr()
Definition Ops.h.inc:894
::mlir::StringAttr getFunctionTypeAttrName()
Definition Ops.h.inc:768
::mlir::StringAttr getResAttrsAttrName()
Definition Ops.h.inc:776
::llvm::LogicalResult verifyRegions()
Definition Ops.cpp:664
void setArgNameAttr(unsigned index, const ::mlir::StringAttr &attr)
Set the function.arg_name attribute for the argument at the given index.
Definition Ops.cpp:382
bool hasArgName(unsigned index)
Return true iff the argument at the given index has a function.arg_name attribute.
Definition Ops.cpp:370
bool hasArgPublicAttr(unsigned index)
Return true iff the argument at the given index has pub attribute.
Definition Ops.cpp:362
static constexpr ::llvm::StringLiteral getOperationName()
Definition Ops.h.inc:800
::mlir::StringAttr getTargetAttrName()
Definition Ops.h.inc:792
::llvm::LogicalResult verifySymbolUses(::mlir::SymbolTableCollection &symbolTable)
Definition Ops.cpp:395
::llvm::LogicalResult verify()
Definition Ops.cpp:610
static void build(::mlir::OpBuilder &odsBuilder, ::mlir::OperationState &odsState, ::mlir::StringAttr sym_name, ::mlir::SymbolRefAttr target, ::mlir::TypeAttr function_type, ::mlir::ArrayAttr arg_attrs={}, ::mlir::ArrayAttr res_attrs={})
Definition Ops.cpp.inc:954
void print(::mlir::OpAsmPrinter &p)
Definition Ops.cpp:582
::mlir::FunctionType getFunctionType()
Definition Ops.cpp.inc:931
void setArgName(unsigned index, ::llvm::StringRef name)
Set the function.arg_name attribute for the argument at the given index from a string.
Definition Ops.cpp:387
::mlir::FailureOr< SymbolLookupResult<::llzk::verif::ContractTargetOpInterface > > getTargetOp(::mlir::SymbolTableCollection &tables)
Return the operation that this contract targets, or failure if it does not target an operation that i...
::std::optional<::mlir::StringAttr > getArgNameAttr(unsigned index)
Return the function.arg_name attribute for the argument at the given index.
Definition Ops.cpp:372
::llvm::ArrayRef<::mlir::Type > getArgumentTypes()
Required by FunctionOpInterface.
Definition Ops.h.inc:973
::mlir::StringAttr getArgAttrsAttrName()
Definition Ops.h.inc:760
::mlir::SymbolRefAttr getTargetAttr()
Definition Ops.h.inc:842
::mlir::SymbolRefAttr getFullyQualifiedName(bool requireParent=true)
Return the full name for this contract from the root module, including all surrounding symbol table n...
Definition Ops.cpp:391
::mlir::FailureOr< SymbolLookupResult<::llzk::verif::ContractTargetOpInterface > > getTargetOp()
Definition Ops.h.inc:1019
::mlir::FailureOr<::mlir::Value > getSelfValue()
Return the "self" value (i.e.
Definition Ops.cpp:720
::mlir::ArrayAttr getArgAttrsAttr()
Definition Ops.h.inc:852
::mlir::ParseResult parse(::mlir::OpAsmParser &parser, ::mlir::OperationState &result)
Definition Ops.cpp:475
::mlir::SymbolRefAttr getTarget()
Definition Ops.cpp.inc:926
FoldAdaptor::Properties Properties
Definition Ops.h.inc:754
::mlir::FailureOr< SymbolLookupResult< component::StructDefOp > > getStructTarget()
Definition Ops.h.inc:994
::mlir::FailureOr< SymbolLookupResult< function::FuncDefOp > > getFuncTarget()
Definition Ops.h.inc:1010
::llvm::StringRef getSymName()
Definition Ops.cpp.inc:921
::llvm::LogicalResult verifySymbolUses(::mlir::SymbolTableCollection &symbolTable)
Definition Ops.cpp:818
::mlir::SymbolRefAttr getCalleeAttr()
Definition Ops.h.inc:1850
FoldAdaptor::Properties Properties
Definition Ops.h.inc:1741
::mlir::Operation::operand_range getArgOperands()
Definition Ops.h.inc:1821
void setCalleeAttr(::mlir::SymbolRefAttr attr)
Definition Ops.h.inc:1880
::mlir::FailureOr<::llzk::SymbolLookupResult<::llzk::verif::ContractOp > > getCalleeTarget(::mlir::SymbolTableCollection &tables)
Resolve and return the target Contract for this CallOp.
Definition Ops.cpp:858
::mlir::Operation * resolveCallable()
Definition Ops.cpp:907
::mlir::SymbolRefAttr getCallee()
Definition Ops.cpp.inc:2289
::mlir::Value getSelfValue()
Definition Ops.cpp:871
void setCalleeFromCallable(::mlir::CallInterfaceCallable callee)
Set the callee for this operation.
Definition Ops.cpp:886
bool contractTargetsStruct()
Return true iff the contract targets a struct type.
Definition Ops.cpp:865
static void build(::mlir::OpBuilder &odsBuilder, ::mlir::OperationState &odsState, ::mlir::SymbolRefAttr callee, ::mlir::ValueRange argOperands={}, ::llvm::ArrayRef<::mlir::Attribute > templateParams={})
::mlir::Operation * resolveCallableInTable(::mlir::SymbolTableCollection *symbolTable)
Definition Ops.cpp:890
::mlir::CallInterfaceCallable getCallableForCallee()
Return the callee of this operation.
Definition Ops.cpp:883
::mlir::FailureOr<::llzk::verif::InvariantTargetOpInterface > getTarget()
Returns the loop target.
Definition Ops.cpp:1045
::mlir::ParseResult parse(::mlir::OpAsmParser &parser, ::mlir::OperationState &result)
Definition Ops.cpp:975
void print(::mlir::OpAsmPrinter &p)
Definition Ops.cpp:1024
::mlir::ArrayAttr getLoopArgTypes()
Definition Ops.cpp.inc:3028
::llvm::StringRef getLoopName()
Definition Ops.cpp.inc:3023
::llzk::verif::ContractOp getParentContract()
Returns the contract operation that contains this invariant.
Definition Ops.cpp:1041
static void build(::mlir::OpBuilder &odsBuilder, ::mlir::OperationState &odsState, ::mlir::StringRef loop_name, ::llvm::ArrayRef<::mlir::Type > loop_arg_types={}, ::llvm::ArrayRef<::mlir::Location > loop_arg_locs={})
Definition Ops.cpp:916
FoldAdaptor::Properties Properties
Definition Ops.h.inc:2278
::llvm::LogicalResult verify()
Definition Ops.cpp:966
::mlir::Region & getRegion()
Definition Ops.h.inc:2324
::mlir::SmallVector<::mlir::Type > getArgumentTypes()
Gets the types of the values that the invariant binds inside its body.
OpClass::Properties & buildInstantiationAttrs(mlir::OpBuilder &odsBuilder, mlir::OperationState &odsState, mlir::ArrayRef< mlir::ValueRange > mapOperands, mlir::DenseI32ArrayAttr numDimsPerMap, int32_t firstSegmentSize=0)
Utility for build() functions that initializes the operandSegmentSizes, mapOpGroupSizes,...
OpClass::Properties & buildInstantiationAttrsEmpty(mlir::OpBuilder &odsBuilder, mlir::OperationState &odsState, int32_t firstSegmentSize=0)
Utility for build() functions that initializes the operandSegmentSizes, mapOpGroupSizes,...
constexpr char ARG_NAME_ATTR_NAME[]
Attribute name for source-level function argument names.
Definition Ops.h:36
detail::IncludedContractSummary analyzeForbiddenIncludedOpSummary(mlir::ModuleOp module, verif::ContractOp contract, verif::IncludeOp includeOp)
Analyze whether a specific include op triggers forbidden preconditions in the callee,...
bool hasInfluence(ForbiddenPreconditionInfluence influence, ForbiddenPreconditionInfluence flag)
Return true when influence contains the requested flag.
ForbiddenPreconditionInfluenceInfo analyzeForbiddenPreconditionOpInfluenceInfo(mlir::ModuleOp module, verif::ContractOp contract, verif::PreconditionOpInterface preCondOp)
Analyze whether a precondition op depends on forbidden sources, including both its condition operand ...
constexpr char FUNC_NAME_COMPUTE[]
Symbol name for the witness generation (and resp.
Definition Constants.h:16
mlir::FailureOr< SymbolLookupResultUntyped > lookupTopLevelSymbol(mlir::SymbolTableCollection &tables, mlir::SymbolRefAttr symbol, mlir::Operation *origin, bool reportMissing=true)
constexpr char FUNC_NAME_CONSTRAIN[]
Definition Constants.h:17
FailureOr< ModuleOp > getRootModule(Operation *from)
mlir::DenseMap< std::pair< mlir::SymbolRefAttr, Side >, mlir::Attribute > UnificationMap
Optional result from type unifications.
Definition TypeHelper.h:208
bool isNullOrEmpty(mlir::ArrayAttr a)
OpClass getParentOfType(mlir::Operation *op)
Return the closest surrounding parent/ancestor operation that is of type 'OpClass'.
Definition OpHelpers.h:53
constexpr char FUNC_NAME_PRODUCT[]
Definition Constants.h:18
constexpr T checkedCast(U u) noexcept
Definition Compare.h:94
mlir::FailureOr< SymbolLookupResult< T > > resolveCallable(mlir::SymbolTableCollection &symbolTable, mlir::CallOpInterface call)
Based on mlir::CallOpInterface::resolveCallable, but using LLZK lookup helpers.
LogicalResult verifyTypeResolution(SymbolTableCollection &tables, Operation *origin, Type ty)
OwningEmitErrorFn getEmitOpErrFn(mlir::Operation *op)
mlir::FailureOr< SymbolLookupResultUntyped > lookupSymbolIn(mlir::SymbolTableCollection &tables, mlir::SymbolRefAttr symbol, Within &&lookupWithin, mlir::Operation *origin, bool reportMissing=true)
std::function< InFlightDiagnosticWrapper()> OwningEmitErrorFn
This type is required in cases like the functions below to take ownership of the lambda so it is not ...
mlir::SymbolRefAttr getFullyQualifiedName(mlir::SymbolOpInterface symbol, bool requireParent=true)
Return the full name for this symbol from the root module, including any surrounding symbol table nam...
bool functionTypesUnify(FunctionType lhs, FunctionType rhs, ArrayRef< StringRef > rhsReversePrefix, UnificationMap *unifications)
void addTemplateParams(mlir::OpBuilder &odsBuilder, typename OpClass::Properties &props, llvm::ArrayRef< mlir::Attribute > templateParams)
Summary of forbidden precondition influence along with representative source locations for each forbi...
llvm::SmallSetVector< mlir::Location, 2 > structMemberLocs