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/LLVM.h>
37#include <mlir/Support/LogicalResult.h>
39#include <llvm/ADT/ArrayRef.h>
40#include <llvm/ADT/STLExtras.h>
41#include <llvm/ADT/SmallVectorExtras.h>
42#include <llvm/ADT/Twine.h>
64bool isValidTarget(Operation *op) {
65 if (
auto fnOp = dyn_cast<FuncDefOp>(op)) {
67 return fnOp->getParentOfType<
StructDefOp>() ==
nullptr;
70 return isa<StructDefOp>(op);
74 return llvm::any_of(unifications, [](
const auto &entry) {
return !entry.second; });
77struct TargetTypeInfo {
78 FunctionType funcType {};
79 ArrayAttr argAttrs {};
82FailureOr<TargetTypeInfo> getTargetTypeInfo(Operation *op) {
83 if (
auto fnOp = dyn_cast<FuncDefOp>(op)) {
85 FunctionType fnTy = fnOp.getFunctionType();
86 ArrayRef<Type> curInputs = fnTy.getInputs(), curResults = fnTy.getResults();
87 SmallVector<Type> newInputs;
88 newInputs.reserve(curInputs.size() + curResults.size());
89 newInputs.insert(newInputs.end(), curInputs.begin(), curInputs.end());
90 newInputs.insert(newInputs.end(), curResults.begin(), curResults.end());
92 auto newFnTy = fnTy.clone(newInputs, {});
94 ArrayAttr curArgAttrs = fnOp.getArgAttrsAttr(), curResAttrs = fnOp.getResAttrsAttr();
95 ArrayAttr newArgAttrsAttr {};
96 if (curArgAttrs || curResAttrs) {
97 auto *ctx = op->getContext();
98 SmallVector<Attribute> newArgAttrs;
100 newArgAttrs.reserve(newInputs.size());
102 newArgAttrs.insert(newArgAttrs.end(), curArgAttrs.begin(), curArgAttrs.end());
105 newArgAttrs.insert(newArgAttrs.end(), curInputs.size(), DictionaryAttr::get(ctx));
108 newArgAttrs.insert(newArgAttrs.end(), curResAttrs.begin(), curResAttrs.end());
111 newArgAttrs.insert(newArgAttrs.end(), curResults.size(), DictionaryAttr::get(ctx));
113 newArgAttrsAttr = ArrayAttr::get(ctx, newArgAttrs);
116 return TargetTypeInfo {
118 .argAttrs = newArgAttrsAttr,
121 if (
auto structOp = dyn_cast<StructDefOp>(op)) {
122 if (
FuncDefOp fnOp = structOp.getConstrainFuncOp(); fnOp && structOp.getComputeFuncOp()) {
123 return TargetTypeInfo {
124 .funcType = fnOp.getFunctionType(),
125 .argAttrs = fnOp.getArgAttrsAttr(),
128 FuncDefOp productFn = structOp.getProductFuncOp();
134 ArrayRef<Type> curInputs = fnTy.getInputs();
136 SmallVector<Type> newInputs;
137 newInputs.reserve(curInputs.size() + 1);
138 newInputs.push_back(structOp.getType());
139 newInputs.insert(newInputs.end(), curInputs.begin(), curInputs.end());
141 auto newFnTy = fnTy.clone(newInputs, {});
143 auto *ctx = op->getContext();
145 ArrayAttr newArgAttrsAttr = curArgAttrs;
147 SmallVector<Attribute> newArgAttrs;
148 newArgAttrs.reserve(curArgAttrs.size() + 1);
149 newArgAttrs.push_back(DictionaryAttr::get(ctx));
150 newArgAttrs.insert(newArgAttrs.end(), curArgAttrs.begin(), curArgAttrs.end());
151 newArgAttrsAttr = ArrayAttr::get(ctx, newArgAttrs);
153 return TargetTypeInfo {
155 .argAttrs = newArgAttrsAttr,
163enum class ForbiddenRequireConditionKind : uint8_t {
169struct ForbiddenRequireCondition {
170 ForbiddenRequireConditionKind kind;
171 llvm::SmallSetVector<Location, 2> sourceLocs;
176struct ForbiddenIncludedPrecondition {
177 std::optional<Location> calleePreconditionLoc = std::nullopt;
178 ForbiddenRequireConditionKind kind;
179 llvm::SmallSetVector<Location, 2> sourceLocs;
183struct ForbiddenIncludedPreconditions {
185 llvm::SmallVector<ForbiddenIncludedPrecondition> failures;
188std::optional<ForbiddenRequireCondition> classifyForbiddenConditionProvenance(
194 return ForbiddenRequireCondition {
195 .kind = ForbiddenRequireConditionKind::StructMember,
200 return ForbiddenRequireCondition {
201 .kind = ForbiddenRequireConditionKind::FunctionReturn,
208std::optional<ForbiddenIncludedPreconditions>
209classifyForbiddenIncludedPrecondition(ModuleOp module,
IncludeOp includeOp) {
210 SymbolTableCollection tables;
212 if (failed(calleeTarget)) {
221 ForbiddenIncludedPreconditions result {.includeOp = includeOp, .failures = {}};
222 for (
const auto &failure : summary.failures) {
226 result.failures.push_back(
227 ForbiddenIncludedPrecondition {
228 .calleePreconditionLoc = failure.preconditionLoc,
229 .kind = ForbiddenRequireConditionKind::StructMember,
230 .sourceLocs = failure.influenceInfo.structMemberLocs,
238 result.failures.push_back(
239 ForbiddenIncludedPrecondition {
240 .calleePreconditionLoc = failure.preconditionLoc,
241 .kind = ForbiddenRequireConditionKind::FunctionReturn,
247 return result.failures.empty() ? std::nullopt
248 : std::optional<ForbiddenIncludedPreconditions>(result);
253LogicalResult emitForbiddenPrecondition(
255 llvm::ArrayRef<Location> sourceLocs = {}
258 case ForbiddenRequireConditionKind::StructMember: {
259 InFlightDiagnostic diag =
260 preCondOp->emitOpError(
"condition cannot be derived from a struct member value");
261 for (
auto sourceLoc : sourceLocs) {
262 diag.attachNote(sourceLoc) <<
"forbidden struct member value originates here";
266 case ForbiddenRequireConditionKind::FunctionReturn: {
267 return preCondOp->emitOpError(
"condition cannot be derived from a function return value");
270 llvm_unreachable(
"unknown forbidden require condition kind");
273LogicalResult emitForbiddenIncludedPreconditions(
274 IncludeOp includeOp, llvm::ArrayRef<ForbiddenIncludedPrecondition> failures
276 bool sawStructMember =
false;
277 bool sawFunctionReturn =
false;
278 for (
const ForbiddenIncludedPrecondition &failure : failures) {
279 sawStructMember |= failure.kind == ForbiddenRequireConditionKind::StructMember;
280 sawFunctionReturn |= failure.kind == ForbiddenRequireConditionKind::FunctionReturn;
283 InFlightDiagnostic diag = [&]() -> InFlightDiagnostic {
284 if (sawStructMember && sawFunctionReturn) {
285 return includeOp.emitOpError(
286 "includes preconditions whose conditions cannot be derived from forbidden sources"
289 if (sawStructMember) {
290 return includeOp.emitOpError(
291 "includes preconditions whose conditions cannot be derived from a struct member value"
294 return includeOp.emitOpError(
295 "includes preconditions whose conditions cannot be derived from a function return value"
299 for (
const ForbiddenIncludedPrecondition &failure : failures) {
300 if (failure.calleePreconditionLoc) {
301 diag.attachNote(failure.calleePreconditionLoc) <<
"included precondition triggered here";
303 for (Location sourceLoc : failure.sourceLocs) {
304 diag.attachNote(sourceLoc) <<
"forbidden struct member value originates here";
318void ContractOp::initializeEmptyBody(
319 OpBuilder &builder, OperationState &state, FunctionType functionType
321 Region *body = state.addRegion();
322 auto *entryBlock =
new Block();
324 SmallVector<Location> argLocs(functionType.getNumInputs(), state.location);
325 entryBlock->addArguments(functionType.getInputs(), argLocs);
326 body->push_back(entryBlock);
328 ContractOp::ensureTerminator(*body, builder, state.location);
332 OpBuilder &odsBuilder, OperationState &odsState, StringRef name, llvm::StringRef target
334 build(odsBuilder, odsState, name, SymbolRefAttr::get(odsBuilder.getContext(), target));
338 ::mlir::OpBuilder &odsBuilder, ::mlir::OperationState &odsState, ::llvm::StringRef name,
339 ::mlir::SymbolRefAttr target
344 SymbolTableCollection tables;
346 FailureOr<SymbolLookupResultUntyped> targetRes =
348 if (failed(targetRes)) {
351 Operation *targetOp = targetRes->get();
352 if (!isValidTarget(targetOp)) {
355 FailureOr<TargetTypeInfo> infoRes = getTargetTypeInfo(targetOp);
356 if (failed(infoRes)) {
359 TargetTypeInfo &info = *infoRes;
360 build(odsBuilder, odsState, name, target, info.funcType, info.argAttrs);
364 if (index < this->getNumArguments()) {
365 DictionaryAttr res = function_interface_impl::getArgAttrDict(*
this, index);
366 return res ? res.contains(PublicAttr::name) :
false;
374 if (index >= getNumArguments()) {
384 assert(index < getNumArguments() &&
"argument index out of range");
399 if (failed(rootRes)) {
400 return emitOpError().append(
"could not lookup root module");
402 FailureOr<SymbolLookupResultUntyped> targetRes =
404 if (failed(targetRes)) {
405 return emitOpError().append(
"could not find target \"@",
getTarget(),
"\"");
416 Operation *targetOp = targetRes->get();
417 if (!isValidTarget(targetOp)) {
419 .append(
"target \"",
getTargetAttr(),
"\" is not a supported contract target")
420 .attachNote(targetOp->getLoc())
421 .append(
"target defined here");
425 if (targetParentTemplate != contractParentTemplate) {
426 InFlightDiagnostic diag = emitOpError().append(
427 "contract nested in template \"@", contractParentTemplate.getSymName(),
428 "\" must target a symbol in the same template"
430 if (targetParentTemplate) {
431 diag.attachNote(targetParentTemplate.getLoc()).append(
"target template defined here");
433 diag.attachNote(targetOp->getLoc()).append(
"target defined here");
438 FailureOr<TargetTypeInfo> targetInfoRes = getTargetTypeInfo(targetOp);
439 if (failed(targetInfoRes)) {
443 if (isa<StructDefOp>(targetOp)) {
447 .append(
"unsupported target type \"", targetOp->getName(),
"\"")
448 .attachNote(targetOp->getLoc())
449 .append(
"target defined here");
451 TargetTypeInfo &targetInfo = *targetInfoRes;
454 functionTypesUnify(contractTy, targetInfo.funcType, targetRes->getNamespace(), &unifications);
455 if (!unifies || hasConflictingUnifications(unifications)) {
457 .append(
"contract type does not match target type")
458 .attachNote(targetOp->getLoc())
459 .append(
"target defined here");
464 "contract arg attributes ",
getArgAttrsAttr(),
" does not match target arg attributes ",
467 .attachNote(targetOp->getLoc())
468 .append(
"target defined here");
480 SmallVector<OpAsmParser::Argument> entryArgs;
481 SmallVector<DictionaryAttr> resultAttrs;
482 SmallVector<Type> resultTypes;
483 auto &builder = parser.getBuilder();
487 if (parser.parseSymbolName(nameAttr, SymbolTable::getSymbolAttrName(), result.attributes)) {
492 if (parser.parseKeyword(
"for")) {
496 SymbolRefAttr targetAttr;
497 if (parser.parseCustomAttributeWithFallback(
498 targetAttr, parser.getBuilder().getType<::mlir::NoneType>()
508 SMLoc signatureLocation = parser.getCurrentLocation();
509 bool isVariadic =
false;
511 if (function_interface_impl::parseFunctionSignature(
512 parser,
false, entryArgs, isVariadic, resultTypes, resultAttrs
516 assert(isVariadic ==
false);
518 if (!resultTypes.empty() || !resultAttrs.empty()) {
522 std::string errorMessage;
523 SmallVector<Type> argTypes;
524 argTypes.reserve(entryArgs.size());
525 for (
auto &arg : entryArgs) {
526 argTypes.push_back(arg.type);
528 Type type = builder.getFunctionType(argTypes, resultTypes);
530 return parser.emitError(signatureLocation)
531 <<
"failed to construct function type" << (errorMessage.empty() ?
"" :
": ")
534 result.addAttribute(typeAttrName, TypeAttr::get(type));
537 NamedAttrList parsedAttributes;
538 SMLoc attributeDictLocation = parser.getCurrentLocation();
539 if (parser.parseOptionalAttrDictWithKeyword(parsedAttributes)) {
545 for (StringRef disallowed :
546 {SymbolTable::getVisibilityAttrName(), SymbolTable::getSymbolAttrName(),
547 typeAttrName.getValue()}) {
548 if (parsedAttributes.get(disallowed)) {
549 return parser.emitError(attributeDictLocation,
"'")
551 <<
"' is an inferred attribute and should not be specified in the "
552 "explicit attribute dictionary";
555 result.attributes.append(parsedAttributes);
558 function_interface_impl::addArgAndResultAttrs(
559 builder, result, entryArgs, resultAttrs, argAttrsName,
560 StringAttr::get(parser.getContext())
564 auto *body = result.addRegion();
565 SMLoc loc = parser.getCurrentLocation();
566 if (parser.parseRegion(
575 return parser.emitError(loc,
"expected non-empty contract body");
578 ContractOp::ensureTerminator(*body, parser.getBuilder(), result.location);
590 p.printAttributeWithoutType(
getTarget());
594 function_interface_impl::printFunctionSignature(
595 p, *
this, argTypes,
false, ArrayRef<Type>()
597 function_interface_impl::printFunctionAttributes(
602 Region &body = getRegion();
614 return emitOpError() <<
'\'' <<
ARG_NAME_ATTR_NAME <<
"' is only valid on function arguments";
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);
628 auto argName = llvm::dyn_cast<StringAttr>(argNameAttr);
631 <<
" must be a string attribute";
633 if (!llvm::isa<NoneType>(argName.getType())) {
635 <<
" must not have an explicit type";
637 if (argName.getValue().empty()) {
639 <<
" must not be empty";
641 if (!seenNames.insert(argName).second) {
643 << argName.getValue() <<
"\" on argument " << i;
652 WalkResult res = this->walk<WalkOrder::PreOrder>([
this](Operation *op) {
653 if (isa<ModuleOp, TemplateOp, FuncDefOp, StructDefOp>(op)) {
655 "cannot be nested within '", getOperation()->getName(),
"' operations"
657 return WalkResult::interrupt();
659 return WalkResult::advance();
661 return failure(res.wasInterrupted());
671 SmallVector<PreconditionOpInterface> preconditionOps =
672 walkCollect<PreconditionOpInterface>(*
this);
673 SmallVector<IncludeOp> includeOps = walkCollect<IncludeOp>(*
this);
674 if (preconditionOps.empty() && includeOps.empty()) {
678 ModuleOp module = getOperation()->getParentOfType<ModuleOp>();
680 return emitOpError(
"must have a parent module to analyze condition provenance");
684 if (
auto forbidden = classifyForbiddenConditionProvenance(module, preCond, *
this)) {
685 return emitForbiddenPrecondition(
686 preCond, forbidden->kind, forbidden->sourceLocs.getArrayRef()
692 if (
auto forbidden = classifyForbiddenIncludedPrecondition(module, includeOp)) {
693 return emitForbiddenIncludedPreconditions(forbidden->includeOp, forbidden->failures);
700FailureOr<SymbolLookupResult<StructDefOp>>
713FailureOr<SymbolLookupResult<ContractTargetOpInterface>>
724 return getArgument(0);
732 OpBuilder &odsBuilder, OperationState &odsState, SymbolRefAttr callee, ValueRange argOperands,
733 ArrayRef<Attribute> templateParams
735 odsState.addOperands(argOperands);
739 props.setCallee(callee);
744 OpBuilder &odsBuilder, OperationState &odsState, SymbolRefAttr callee,
745 ArrayRef<ValueRange> mapOperands, DenseI32ArrayAttr numDimsPerMap, ValueRange argOperands,
746 ArrayRef<Attribute> templateParams
748 odsState.addOperands(argOperands);
750 odsBuilder, odsState, mapOperands, numDimsPerMap,
753 props.setCallee(callee);
762 if (
auto intAttr = llvm::dyn_cast<IntegerAttr>(paramFromIncludeOp)) {
764 std::optional<Type> declaredType = targetParam.
getTypeOpt();
765 if (!declaredType || !llvm::isa<TypeVarType>(*declaredType)) {
766 auto diag = this->emitOpError().append(
767 "wildcard `?` can only be used for template parameters with `!poly.tvar` "
768 "type restriction, but parameter \"@",
769 targetParam.getName(),
"\" has "
772 diag.append(
"type restriction ", *declaredType);
774 diag.append(
"no type restriction");
781 if (std::optional<Type> declaredType = targetParam.
getTypeOpt()) {
783 bool compatible =
false;
784 if (llvm::isa<TypeVarType>(*declaredType)) {
785 compatible = llvm::isa<TypeAttr>(paramFromIncludeOp);
786 }
else if (llvm::isa<FeltType>(*declaredType)) {
787 compatible = llvm::isa<FeltConstAttr, IntegerAttr>(paramFromIncludeOp) &&
789 }
else if (llvm::isa<IndexType, IntegerType>(*declaredType)) {
793 compatible = llvm::isa<IntegerAttr>(paramFromIncludeOp) &&
796 llvm_unreachable(
"inconsistent with `isValidConstReadType()`");
799 return this->emitOpError().append(
800 "instantiation value '", paramFromIncludeOp,
"' is not compatible with parameter \"@",
801 targetParam.getName(),
"\" type restriction ", *declaredType
809 llvm::iterator_range<Region::op_iterator<TemplateParamOp>> targetParamDefs
813 assert((callParams.size() == llvm::range_size(targetParamDefs)) &&
"pre-condition");
815 for (
auto [paramOp, attr] : llvm::zip_equal(targetParamDefs, callParams.getValue())) {
824 llvm::iterator_range<Region::op_iterator<TemplateParamOp>> targetParamDefs,
829 assert((callParams.size() == llvm::range_size(targetParamDefs)) &&
"pre-condition");
831 for (
auto [paramOp, attr] : llvm::zip_equal(targetParamDefs, callParams.getValue())) {
833 if (
auto intAttr = llvm::dyn_cast<IntegerAttr>(attr)) {
838 auto it = unifications.find({FlatSymbolRefAttr::get(paramOp.getNameAttr()),
Side::RHS});
839 if (it != unifications.end() && !
typeParamsUnify({attr}, {it->second})) {
840 return this->emitOpError().append(
841 "template instantiation value '", attr,
"' for parameter \"@", paramOp.getName(),
842 "\" conflicts with value '", it->second,
"' inferred from function type signature"
851struct IncludeOpVerifier {
852 explicit IncludeOpVerifier(
IncludeOp *c) : includeOp(c) {}
853 virtual ~IncludeOpVerifier() =
default;
855 LogicalResult verify() {
858 LogicalResult aggregateResult = success();
859 if (failed(verifyInputs())) {
860 aggregateResult = failure();
862 if (failed(verifyTemplateParams())) {
863 aggregateResult = failure();
865 return aggregateResult;
869 IncludeOp *includeOp;
871 virtual LogicalResult verifyInputs() = 0;
872 virtual LogicalResult verifyTemplateParams() = 0;
874 LogicalResult verifyNoTemplateInstantiations() {
876 return includeOp->emitOpError().append(
877 "can only have template instantiations when targeting a templated contract"
884struct KnownTargetVerifier :
public IncludeOpVerifier {
885 KnownTargetVerifier(IncludeOp *c, SymbolLookupResult<ContractOp> &&tgtRes)
886 : IncludeOpVerifier(c), tgt(*tgtRes), tgtType(tgt.getFunctionType()),
887 includeSymNames(tgtRes.getNamespace()) {}
889 LogicalResult verifyInputs()
override {
890 return verifyTypesMatch(includeOp->
getArgOperands().getTypes(), tgtType.getInputs(),
"operand");
893 LogicalResult verifyTemplateParams()
override {
894 Operation *tgtOp = tgt.getOperation();
904 auto realParams = tgtOpParent.getConstOps<TemplateParamOp>();
909 llvm::SmallDenseSet<SymbolRefAttr> referencedInSignature;
913 bool allParamsReferenced = llvm::all_of(realParams, [&](TemplateParamOp p) {
914 return referencedInSignature.contains(FlatSymbolRefAttr::get(p.getNameAttr()));
916 if (allParamsReferenced) {
919 return includeOp->emitOpError().append(
920 "must provide template instantiation parameters when calling \"@", tgt.getSymName(),
921 "\" because not all template parameters of \"@", tgtOpParent.getSymName(),
922 "\" appear in the function type signature"
928 return llzk::InFlightDiagnosticWrapper(this->includeOp->emitOpError());
934 size_t numTemplateParams = llvm::range_size(realParams);
935 if (callParams.size() != numTemplateParams) {
936 return includeOp->emitOpError().append(
937 "template instantiation has ", callParams.size(),
" parameter(s) but \"@",
938 tgtOpParent.getSymName(),
"\" expects ", numTemplateParams,
" template parameter(s)"
953 if (failed(unifyResult)) {
959 return verifyNoTemplateInstantiations();
964 template <
typename T>
966 verifyTypesMatch(ValueTypeRange<T> includeOpTypes, ArrayRef<Type> tgtTypes,
const char *aspect) {
967 if (tgtTypes.size() != includeOpTypes.size()) {
968 return includeOp->emitOpError()
969 .append(
"incorrect number of ", aspect,
"s for callee, expected ", tgtTypes.size())
970 .attachNote(tgt.getLoc())
971 .append(
"callee defined here");
973 for (
unsigned i = 0, e = tgtTypes.size(); i != e; ++i) {
974 if (!
typesUnify(includeOpTypes[i], tgtTypes[i], includeSymNames)) {
975 return includeOp->emitOpError().append(
976 aspect,
" type mismatch: expected type ", tgtTypes[i],
", but found ",
977 includeOpTypes[i],
" for ", aspect,
" number ", i
985 FunctionType tgtType;
986 std::vector<llvm::StringRef> includeSymNames;
1000 return emitOpError(
"requires a 'callee' symbol reference attribute");
1005 if (calleeAttr.getNestedReferences().size() == 1) {
1007 if (
auto constParam = parent.getConstNamed<
TemplateParamOp>(calleeAttr.getRootReference())) {
1008 return this->emitError(
"expected parameterized callee to target a struct function")
1009 .attachNote(constParam->getLoc())
1023 if (failed(tgtOpt)) {
1025 << calleeAttr <<
'"';
1027 return KnownTargetVerifier(
this, std::move(*tgtOpt)).verify();
1031 return FunctionType::get(getContext(),
getArgOperands().getTypes(), {});
1037 return unifications;
1042FailureOr<SymbolLookupResult<ContractOp>>
1044 Operation *thisOp = this->getOperation();
1046 assert(succeeded(root));
1051 SymbolTableCollection tables;
1053 return succeeded(callee) && callee->get().hasStructTarget();
1057 SymbolTableCollection tables;
1059 assert(succeeded(callee) &&
"include callee must resolve");
1060 if (!callee->get().hasStructTarget()) {
1063 assert(getNumOperands() > 0 &&
"include op must have a self operand");
1064 return getOperand(0);
1076 llvm::SmallVector<ValueRange, 4> output;
1077 output.reserve(input.size());
1078 output.insert(output.end(), input.begin(), input.end());
1083 FailureOr<SymbolLookupResult<ContractOp>> res =
1088 if (res->isManaged()) {
1090 "IncludeOp::resolveCallableInTable: cannot return "
1091 "pointer to a managed Operation since it would cause memory errors. "
1092 "Consider running -llzk-inline-includes to avoid encountering managed Operations."
1100 SymbolTableCollection tables;
1109 OpBuilder &odsBuilder, OperationState &odsState, StringRef loop_name,
1110 ArrayRef<Type> loop_arg_types, ArrayRef<Location> loop_arg_locs
1113 odsBuilder.getStringAttr(loop_name);
1115 odsBuilder.getTypeArrayAttr(loop_arg_types);
1116 auto region = std::make_unique<Region>();
1117 auto &block = region->emplaceBlock();
1118 block.addArguments(loop_arg_types, loop_arg_locs);
1119 odsState.regions.push_back(std::move(region));
1126 auto bodyArgTypes = op->getBody()->getArgumentTypes();
1128 if (targetArgTypes.size() != declaredTypes.size()) {
1129 return op->emitOpError() <<
"target has " << targetArgTypes.size()
1130 <<
" arguments but invariant declared " << declaredTypes.size();
1132 if (bodyArgTypes.size() != declaredTypes.size()) {
1133 return op->emitOpError() <<
"invariant body has " << targetArgTypes.size()
1134 <<
" arguments but declared " << declaredTypes.size();
1137 bool failed =
false;
1138 for (
auto [n, types] :
1139 llvm::enumerate(llvm::zip_equal(targetArgTypes, bodyArgTypes, declaredTypes))) {
1140 auto [targetType, bodyArgType, declaredType] = types;
1142 if (targetType != mlir::cast<TypeAttr>(declaredType).getValue()) {
1144 op->emitOpError() <<
"target argument #" << n <<
" expected type " << targetType
1145 <<
" but invariant declared type " << declaredType;
1147 if (bodyArgType != mlir::cast<TypeAttr>(declaredType).getValue()) {
1149 op->emitOpError() <<
"invariant argument #" << n <<
" expected type " << targetType
1150 <<
" but invariant declared type " << declaredType;
1154 return failure(failed);
1160 if (failed(invariantTarget)) {
1164 return verifyArgTypes(*invariantTarget,
this);
1168 if (failed(parser.parseKeyword(
"for"))) {
1173 StringAttr loopNameAttr;
1174 if (parser.parseSymbolName(loopNameAttr)) {
1180 bool isVariadic =
false;
1181 SmallVector<OpAsmParser::Argument> entryArgs;
1182 SmallVector<DictionaryAttr> resultAttrs;
1183 SmallVector<Type> resultTypes;
1185 if (function_interface_impl::parseFunctionSignature(
1186 parser,
false, entryArgs, isVariadic, resultTypes, resultAttrs
1190 assert(isVariadic ==
false);
1192 if (!resultTypes.empty() || !resultAttrs.empty()) {
1196 SmallVector<Type> argTypes = llvm::map_to_vector(entryArgs, [](
auto arg) {
return arg.type; });
1198 parser.getBuilder().getTypeArrayAttr(argTypes);
1200 auto *body = result.addRegion();
1201 SMLoc loc = parser.getCurrentLocation();
1202 if (parser.parseRegion(
1209 if (body->empty()) {
1210 return parser.emitError(loc,
"expected non-empty invariant body");
1221 llvm::interleave(getBody()->getArguments(), [&p](
auto arg) {
1222 p.printRegionArgument(arg);
1223 }, [&p]() { p <<
", "; });
1234 return this->getOperation()->getParentOfType<
ContractOp>();
1239 if (failed(target)) {
1242 SmallVector<InvariantTargetOpInterface> matches;
1243 for (
auto invariantTarget : target->get().getLoops()) {
1244 auto targetLabel = invariantTarget.getLabel();
1245 if (succeeded(targetLabel) && *targetLabel ==
getLoopName()) {
1246 matches.push_back(invariantTarget);
1250 if (matches.size() == 0) {
1251 return emitOpError() <<
"no invariant target with label \"" <<
getLoopName()
1252 <<
"\" found in contract target " << target->get().getNameAttr();
1254 if (matches.size() > 1) {
1255 return emitOpError() <<
"ambiguous label \"" <<
getLoopName() <<
"\" matched " << matches.size()
1256 <<
" invariant targets in contract target " << target->get().getNameAttr();
This file contains an analysis and utilities for determining if a verif precondition is dependent,...
::mlir::FunctionType getFunctionType()
::mlir::ArrayAttr getArgAttrsAttr()
::std::optional<::mlir::Type > getTypeOpt()
::mlir::StringAttr getFunctionTypeAttrName()
::llvm::LogicalResult verifyRegions()
void setArgNameAttr(unsigned index, const ::mlir::StringAttr &attr)
Set the function.arg_name attribute for the argument at the given index.
bool hasArgName(unsigned index)
Return true iff the argument at the given index has a function.arg_name attribute.
bool hasArgPublicAttr(unsigned index)
Return true iff the argument at the given index has pub attribute.
static constexpr ::llvm::StringLiteral getOperationName()
::mlir::StringAttr getTargetAttrName()
::llvm::LogicalResult verifySymbolUses(::mlir::SymbolTableCollection &symbolTable)
::llvm::LogicalResult verify()
void print(::mlir::OpAsmPrinter &p)
::mlir::FunctionType getFunctionType()
void setArgName(unsigned index, ::llvm::StringRef name)
Set the function.arg_name attribute for the argument at the given index from a string.
::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.
::llvm::ArrayRef<::mlir::Type > getArgumentTypes()
Required by FunctionOpInterface.
::mlir::StringAttr getArgAttrsAttrName()
::mlir::SymbolRefAttr getTargetAttr()
::mlir::SymbolRefAttr getFullyQualifiedName(bool requireParent=true)
Return the full name for this contract from the root module, including all surrounding symbol table n...
::mlir::FailureOr< SymbolLookupResult<::llzk::verif::ContractTargetOpInterface > > getTargetOp()
::mlir::FailureOr<::mlir::Value > getSelfValue()
Return the "self" value (i.e.
::mlir::ArrayAttr getArgAttrsAttr()
::mlir::ParseResult parse(::mlir::OpAsmParser &parser, ::mlir::OperationState &result)
::mlir::SymbolRefAttr getTarget()
FoldAdaptor::Properties Properties
::mlir::FailureOr< SymbolLookupResult< component::StructDefOp > > getStructTarget()
::mlir::FailureOr< SymbolLookupResult< function::FuncDefOp > > getFuncTarget()
static void build(::mlir::OpBuilder &odsBuilder, ::mlir::OperationState &odsState, ::mlir::StringAttr sym_name, ::mlir::SymbolRefAttr target, ::mlir::TypeAttr function_type, ::mlir::ArrayAttr arg_attrs={})
::llvm::StringRef getSymName()
::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()
::llvm::LogicalResult verifySymbolUses(::mlir::SymbolTableCollection &symbolTable)
::mlir::SymbolRefAttr getCalleeAttr()
FoldAdaptor::Properties Properties
::mlir::Operation::operand_range getArgOperands()
void setCalleeAttr(::mlir::SymbolRefAttr attr)
::mlir::FailureOr<::llzk::SymbolLookupResult<::llzk::verif::ContractOp > > getCalleeTarget(::mlir::SymbolTableCollection &tables)
Resolve and return the target Contract for this CallOp.
::mlir::Operation * resolveCallable()
Required by CallOpInterface.
::mlir::SymbolRefAttr getCallee()
::mlir::Value getSelfValue()
Return the "self" value (i.e.
void setCalleeFromCallable(::mlir::CallInterfaceCallable callee)
Set the callee for this operation.
bool contractTargetsStruct()
Return true iff the contract targets a struct type.
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.
::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...
::mlir::FailureOr< UnificationMap > unifyTypeSignature(::mlir::FunctionType other)
Attempt type unfication between the inferred FunctionType from this CallOp (as LHS) and the given Fun...
::mlir::Operation * resolveCallableInTable(::mlir::SymbolTableCollection *symbolTable)
Required by CallOpInterface.
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 ...
::mlir::CallInterfaceCallable getCallableForCallee()
Return the callee of this operation.
::mlir::FailureOr<::llzk::verif::InvariantTargetOpInterface > getTarget()
Returns the loop target.
::mlir::ParseResult parse(::mlir::OpAsmParser &parser, ::mlir::OperationState &result)
void print(::mlir::OpAsmPrinter &p)
::mlir::ArrayAttr getLoopArgTypes()
::llvm::StringRef getLoopName()
::llzk::verif::ContractOp getParentContract()
Returns the contract operation that contains this invariant.
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={})
FoldAdaptor::Properties Properties
::llvm::LogicalResult verify()
::mlir::Region & getRegion()
::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.
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.
mlir::FailureOr< SymbolLookupResultUntyped > lookupTopLevelSymbol(mlir::SymbolTableCollection &tables, mlir::SymbolRefAttr symbol, mlir::Operation *origin, bool reportMissing=true)
constexpr char FUNC_NAME_CONSTRAIN[]
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.
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'.
constexpr char FUNC_NAME_PRODUCT[]
constexpr T checkedCast(U u) noexcept
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 ...
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 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)
bool isValidConstReadType(Type type)
Summary of forbidden precondition influence along with representative source locations for each forbi...
ForbiddenPreconditionInfluence influence
llvm::SmallSetVector< mlir::Location, 2 > structMemberLocs