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