LLZK 3.0.0
An open-source IR for Zero Knowledge (ZK) circuits
Loading...
Searching...
No Matches
TypeVarInferencePass.cpp
Go to the documentation of this file.
1//===-- TypeVarInferencePass.cpp -------------------------------*- 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//===----------------------------------------------------------------------===//
29//===----------------------------------------------------------------------===//
30
39#include "llzk/Util/Constants.h"
44#include "llzk/Util/Walk.h"
45
46#include <mlir/IR/BuiltinOps.h>
47#include <mlir/IR/PatternMatch.h>
48
49#include <llvm/ADT/DenseMap.h>
50#include <llvm/ADT/DenseSet.h>
51#include <llvm/ADT/SmallVector.h>
52#include <llvm/ADT/StringMap.h>
53#include <llvm/Support/raw_ostream.h>
54
55#include <memory>
56#include <vector>
57
58// Include the generated base pass class definitions.
59namespace llzk::polymorphic {
60#define GEN_PASS_DEF_TYPEVARINFERENCEPASS
62} // namespace llzk::polymorphic
63
64#include "SharedImpl.h"
65
66#define DEBUG_TYPE "llzk-infer-tvar"
67
68using namespace mlir;
69using namespace llzk;
70using namespace llzk::array;
71using namespace llzk::component;
72using namespace llzk::function;
73using namespace llzk::pod;
74using namespace llzk::polymorphic;
75using namespace llzk::polymorphic::detail;
76
77namespace {
78
83struct InferredType {
84 Type type;
85 Location loc;
86};
87
97using SpecializedCallableCloneCache = DenseMap<Operation *, llvm::StringMap<SymbolRefAttr>>;
98using SpecializedTemplateCloneCache = DenseMap<Operation *, llvm::StringMap<StringAttr>>;
99
105static SmallVector<StringAttr> getStringPieces(SymbolRefAttr ref) {
106 return llvm::to_vector(llvm::map_range(getPieces(ref), [](FlatSymbolRefAttr piece) {
107 return piece.getAttr();
108 }));
109}
110
112static StringAttr getFlatSymbolName(Attribute attr) {
113 auto symRef = llvm::dyn_cast_if_present<SymbolRefAttr>(attr);
114 if (!symRef || !symRef.getNestedReferences().empty()) {
115 return {};
116 }
117 return symRef.getRootReference();
118}
119
127static bool isTypeVarParam(TemplateParamOp op) {
128 std::optional<Type> declaredTy = op.getTypeOpt();
129 if (!declaredTy) {
130 return false;
131 }
132 auto tvarTy = llvm::dyn_cast<TypeVarType>(*declaredTy);
133 return tvarTy && tvarTy.getRefName() == op.getName();
134}
135
142static bool isCurrentTemplateTypeVarParamSymbol(StringAttr symbolName, Operation *op) {
144 if (!templateOp) {
145 return false;
146 }
147 TemplateParamOp paramOp = templateOp.getConstNamed<TemplateParamOp>(symbolName);
148 return paramOp && isTypeVarParam(paramOp);
149}
150
152template <typename ConverterT>
153static bool
154convertTypeRange(TypeRange oldTypes, SmallVectorImpl<Type> &newTypes, ConverterT &converter) {
155 bool changed = false;
156 newTypes.reserve(oldTypes.size());
157 for (Type oldTy : oldTypes) {
158 Type newTy = converter.convertType(oldTy);
159 newTypes.push_back(newTy);
160 changed |= newTy != oldTy;
161 }
162 return changed;
163}
164
166template <typename ConverterT>
167static Type convertPodType(PodType podTy, MLIRContext *ctx, ConverterT &converter) {
168 SmallVector<RecordAttr> newRecords;
169 bool changed = false;
170 for (RecordAttr record : podTy.getRecords()) {
171 Type newRecordTy = converter.convertType(record.getType());
172 newRecords.push_back(RecordAttr::get(ctx, record.getName(), newRecordTy));
173 changed |= newRecordTy != record.getType();
174 }
175 return changed ? PodType::get(ctx, newRecords) : podTy;
176}
177
179template <typename ConvertElementFn>
180static Type convertArrayElementType(ArrayType arrTy, ConvertElementFn convertElement) {
181 Type newElemTy = convertElement(arrTy.getElementType());
182 if (newElemTy == arrTy.getElementType()) {
183 return arrTy;
184 }
186}
187
189template <typename ConverterT>
190static Type convertFunctionType(FunctionType funcTy, ConverterT &converter) {
191 SmallVector<Type> newInputs;
192 SmallVector<Type> newResults;
193 bool changed = convertTypeRange(funcTy.getInputs(), newInputs, converter);
194 changed |= convertTypeRange(funcTy.getResults(), newResults, converter);
195 return changed ? FunctionType::get(funcTy.getContext(), newInputs, newResults) : funcTy;
196}
197
199template <typename ConvertTypeFn, typename ConvertNestedAttrFn>
200static Attribute convertTypeOrArrayAttr(
201 Attribute attr, MLIRContext *ctx, ConvertTypeFn convertType,
202 ConvertNestedAttrFn convertNestedAttr
203) {
204 if (!attr) {
205 return attr;
206 }
207 if (auto tyAttr = llvm::dyn_cast<TypeAttr>(attr)) {
208 Type newTy = convertType(tyAttr.getValue());
209 return newTy == tyAttr.getValue() ? attr : TypeAttr::get(newTy);
210 }
211 if (auto arrAttr = llvm::dyn_cast<ArrayAttr>(attr)) {
212 SmallVector<Attribute> newAttrs;
213 bool changed = false;
214 for (Attribute nested : arrAttr.getValue()) {
215 Attribute newNested = convertNestedAttr(nested);
216 newAttrs.push_back(newNested);
217 changed |= newNested != nested;
218 }
219 return changed ? ArrayAttr::get(ctx, newAttrs) : attr;
220 }
221 return attr;
222}
223
226static bool templateArgUnifiesWithType(Attribute attr, Type expectedTy) {
227 Attribute expectedAttr = TypeAttr::get(expectedTy);
228 return typeParamsUnify(ArrayRef<Attribute> {attr}, ArrayRef<Attribute> {expectedAttr});
229}
230
238class TypeVarReplacementConverter {
240 MLIRContext *ctx_;
241 /// Fully-qualified symbol path of the template currently being rewritten.
242 SmallVector<StringAttr> templatePath_;
244 SmallVector<StringAttr> oldParamOrder_;
246 DenseSet<StringAttr> removedParams_;
248 DenseMap<StringAttr, Type> replacements_;
250 bool trimResolvedParams_;
251
252public:
253 /// Create a converter for a single template rewrite.
254 ///
255 /// `oldParamOrder` is captured before erasing any `poly.param` operations, so
256 /// call-site template argument lists can be trimmed using their original
257 /// positional layout.
258 TypeVarReplacementConverter(
259 MLIRContext *c, ArrayRef<StringAttr> templatePath, ArrayRef<StringAttr> oldParamOrder,
260 const DenseMap<StringAttr, Type> &replacements, bool trimResolvedParams = true
262 : ctx_(c), templatePath_(templatePath), oldParamOrder_(oldParamOrder),
263 replacements_(replacements), trimResolvedParams_(trimResolvedParams) {
264 for (const auto &entry : replacements_) {
265 removedParams_.insert(entry.first);
266 }
268
270
272 /// or unsupported types are returned unchanged.
273 Type convertType(Type ty) const {
274 DenseSet<StringAttr> resolvingParams;
275 return convertType(ty, resolvingParams);
276 }
278private:
280 Type convertType(Type ty, DenseSet<StringAttr> &resolvingParams) const {
281 if (!ty) {
282 return ty;
283 }
284 if (auto tvarTy = llvm::dyn_cast<TypeVarType>(ty)) {
285 StringAttr paramName = tvarTy.getNameRef().getAttr();
286 auto it = replacements_.find(paramName);
287 if (it == replacements_.end()) {
288 return ty;
289 }
290 if (!resolvingParams.insert(paramName).second) {
291 return ty;
292 }
293 Type replacement = convertType(it->second, resolvingParams);
294 resolvingParams.erase(paramName);
295 return replacement;
296 }
297 if (auto arrTy = llvm::dyn_cast<ArrayType>(ty)) {
298 return convertArrayElementType(arrTy, [this, &resolvingParams](Type elemTy) {
299 return convertType(elemTy, resolvingParams);
300 });
302 if (auto structTy = llvm::dyn_cast<StructType>(ty)) {
303 return convertStructType(structTy, resolvingParams);
304 }
305 if (auto podTy = llvm::dyn_cast<PodType>(ty)) {
306 return convertPodType(podTy, ctx_, *this);
308 if (auto funcTy = llvm::dyn_cast<FunctionType>(ty)) {
309 return convertFunctionType(funcTy, *this);
310 }
311 return ty;
312 }
313
314public:
320 Attribute convertAttr(Attribute attr) const {
321 DenseSet<StringAttr> resolvingParams;
322 return convertAttr(attr, resolvingParams);
323 }
324
327 LogicalResult validateOperation(Operation *op) const {
328 if (auto createOp = llvm::dyn_cast<CreateArrayOp>(op)) {
329 if (failed(validateCreateArrayOp(createOp))) {
330 return failure();
331 }
332 }
333 if (auto func = llvm::dyn_cast<FuncDefOp>(op)) {
334 if (failed(validateType(func.getFunctionType(), op))) {
335 return failure();
336 }
337 }
338 for (Region &region : op->getRegions()) {
339 for (Block &block : region.getBlocks()) {
340 for (Type argTy : block.getArgumentTypes()) {
341 if (failed(validateType(argTy, op))) {
342 return failure();
343 }
344 }
345 }
346 }
347 for (Type resultTy : op->getResultTypes()) {
348 if (failed(validateType(resultTy, op))) {
349 return failure();
350 }
351 }
352 for (NamedAttribute attr : op->getAttrs()) {
353 if (failed(validateAttr(attr.getValue(), op))) {
354 return failure();
355 }
356 }
357 return success();
358 }
359
360private:
369 LogicalResult validateCreateArrayOp(CreateArrayOp createOp) const {
370 if (createOp.getElements().empty()) {
371 return success();
372 }
373
374 ArrayType oldResultTy = createOp.getType();
375 auto newResultTy = llvm::dyn_cast<ArrayType>(convertType(oldResultTy));
376 auto newElemTy = llvm::dyn_cast<ArrayType>(convertType(oldResultTy.getElementType()));
377 if (!newResultTy || !newElemTy || newResultTy == oldResultTy || oldResultTy.hasStaticShape()) {
378 return success();
379 }
380
381 return createOp.emitError()
382 << "cannot rewrite initialized array.new with non-static initializer shape "
383 << oldResultTy;
384 }
385
387 LogicalResult validateType(Type ty, Operation *diagnosticOp) const {
388 if (!ty) {
389 return success();
390 }
391 if (auto arrTy = llvm::dyn_cast<ArrayType>(ty)) {
392 return validateType(arrTy.getElementType(), diagnosticOp);
393 }
394 if (auto structTy = llvm::dyn_cast<StructType>(ty)) {
395 return validateStructType(structTy, diagnosticOp);
396 }
397 if (auto podTy = llvm::dyn_cast<PodType>(ty)) {
398 for (RecordAttr record : podTy.getRecords()) {
399 if (failed(validateType(record.getType(), diagnosticOp))) {
400 return failure();
401 }
402 }
403 return success();
404 }
405 if (auto funcTy = llvm::dyn_cast<FunctionType>(ty)) {
406 for (Type inputTy : funcTy.getInputs()) {
407 if (failed(validateType(inputTy, diagnosticOp))) {
408 return failure();
409 }
410 }
411 for (Type resultTy : funcTy.getResults()) {
412 if (failed(validateType(resultTy, diagnosticOp))) {
413 return failure();
414 }
415 }
416 }
417 return success();
418 }
419
421 LogicalResult validateAttr(Attribute attr, Operation *diagnosticOp) const {
422 if (!attr) {
423 return success();
424 }
425 if (auto tyAttr = llvm::dyn_cast<TypeAttr>(attr)) {
426 return validateType(tyAttr.getValue(), diagnosticOp);
427 }
428 if (auto arrAttr = llvm::dyn_cast<ArrayAttr>(attr)) {
429 for (Attribute nested : arrAttr.getValue()) {
430 if (failed(validateAttr(nested, diagnosticOp))) {
431 return failure();
432 }
433 }
434 }
435 return success();
436 }
437
439 Attribute convertAttr(Attribute attr, DenseSet<StringAttr> &resolvingParams) const {
440 return convertTypeOrArrayAttr(attr, ctx_, [this, &resolvingParams](Type ty) {
441 return convertType(ty, resolvingParams);
442 }, [this, &resolvingParams](Attribute nested) {
443 return convertTemplateArgAttr(nested, resolvingParams);
444 });
445 }
446
447public:
455 FailureOr<ArrayAttr> convertTemplateParams(
456 ArrayAttr params, Operation *diagnosticOp, bool resolveTemplateSymbolArgs = true,
457 bool allowCurrentTemplateTypeVarSymbols = false
458 ) const {
459 if (!params) {
460 return ArrayAttr();
461 }
462 if (params.size() != oldParamOrder_.size()) {
463 return params;
464 }
465 SmallVector<Attribute> kept;
466 for (auto [paramName, attr] : llvm::zip_equal(oldParamOrder_, params.getValue())) {
467 if (removedParams_.contains(paramName)) {
468 if (failed(checkRemovedTemplateParam(
469 paramName, attr, diagnosticOp, resolveTemplateSymbolArgs,
470 allowCurrentTemplateTypeVarSymbols
471 ))) {
472 return failure();
473 }
474 continue;
475 }
476 kept.push_back(resolveTemplateSymbolArgs ? convertTemplateArgAttr(attr) : attr);
477 }
478 return kept.empty() ? ArrayAttr() : ArrayAttr::get(ctx_, kept);
479 }
480
483 bool hasWildcardForRemovedParam(ArrayAttr params) const {
484 if (!params || params.size() != oldParamOrder_.size()) {
485 return false;
486 }
487 for (auto [paramName, attr] : llvm::zip_equal(oldParamOrder_, params.getValue())) {
488 if (!removedParams_.contains(paramName)) {
489 continue;
490 }
491 if (auto intAttr = llvm::dyn_cast<IntegerAttr>(attr); intAttr && isDynamic(intAttr)) {
492 return true;
493 }
494 }
495 return false;
496 }
497
498private:
500 Attribute convertTemplateArgAttr(Attribute attr) const {
501 DenseSet<StringAttr> resolvingParams;
502 return convertTemplateArgAttr(attr, resolvingParams);
503 }
504
511 Attribute convertTemplateArgAttr(Attribute attr, DenseSet<StringAttr> &resolvingParams) const {
512 if (StringAttr symbolName = getFlatSymbolName(attr)) {
513 auto it = replacements_.find(symbolName);
514 if (it != replacements_.end()) {
515 if (!resolvingParams.insert(symbolName).second) {
516 return attr;
517 }
518 Type replacement = convertType(it->second, resolvingParams);
519 resolvingParams.erase(symbolName);
520 return TypeAttr::get(replacement);
521 }
522 }
523 return convertAttr(attr, resolvingParams);
524 }
525
527 LogicalResult checkRemovedTemplateParam(
528 StringAttr paramName, Attribute attr, Operation *diagnosticOp, bool resolveTemplateSymbolArgs,
529 bool allowCurrentTemplateTypeVarSymbols = false
530 ) const {
531 auto replacementIt = replacements_.find(paramName);
532 assert(replacementIt != replacements_.end() && "removed parameter must have a replacement");
533 if (auto intAttr = llvm::dyn_cast<IntegerAttr>(attr); intAttr && isDynamic(intAttr)) {
534 return success();
535 }
536 Attribute convertedAttr = resolveTemplateSymbolArgs ? convertTemplateArgAttr(attr) : attr;
537 if (StringAttr symbolName = getFlatSymbolName(convertedAttr)) {
538 if (allowCurrentTemplateTypeVarSymbols &&
539 isCurrentTemplateTypeVarParamSymbol(symbolName, diagnosticOp)) {
540 return success();
541 }
542 return emitRemovedTemplateParamMismatch(paramName, attr, replacementIt->second, diagnosticOp);
543 }
544 if (templateArgUnifiesWithType(convertedAttr, convertType(replacementIt->second))) {
545 return success();
546 }
547
548 return emitRemovedTemplateParamMismatch(paramName, attr, replacementIt->second, diagnosticOp);
549 }
550
552 LogicalResult emitRemovedTemplateParamMismatch(
553 StringAttr paramName, Attribute attr, Type replacementTy, Operation *diagnosticOp
554 ) const {
555 InFlightDiagnostic diag = diagnosticOp->emitError()
556 << "explicit template argument for inferred parameter @"
557 << paramName.getValue() << " must match inferred type "
558 << replacementTy << ", but found ";
559 if (auto typeAttr = llvm::dyn_cast<TypeAttr>(attr)) {
560 diag << typeAttr.getValue();
561 } else {
562 diag << attr;
563 }
564 return diag;
565 }
566
573 bool isOwnedStructType(StructType structTy) const {
574 SmallVector<StringAttr> structPath = getStringPieces(structTy.getNameRef());
575 return structPath.size() > templatePath_.size() &&
576 std::equal(templatePath_.begin(), templatePath_.end(), structPath.begin());
577 }
578
584 StructType convertStructType(StructType structTy, DenseSet<StringAttr> &resolvingParams) const {
585 ArrayAttr params = structTy.getParams();
586 if (!params) {
587 return structTy;
588 }
589
590 SmallVector<Attribute> newParams;
591 bool changed = false;
592 bool removeOwnedParams = isOwnedStructType(structTy) && params.size() == oldParamOrder_.size();
593 for (auto [index, attr] : llvm::enumerate(params.getValue())) {
594 if (trimResolvedParams_ && removeOwnedParams &&
595 removedParams_.contains(oldParamOrder_[index])) {
596 changed = true;
597 continue;
598 }
599 Attribute newAttr = convertTemplateArgAttr(attr, resolvingParams);
600 newParams.push_back(newAttr);
601 changed |= newAttr != attr;
602 }
603 return changed ? getStructTypeWithParams(structTy.getNameRef(), ctx_, newParams) : structTy;
604 }
605
607 LogicalResult validateStructType(StructType structTy, Operation *diagnosticOp) const {
608 ArrayAttr params = structTy.getParams();
609 if (!params) {
610 return success();
611 }
612 bool removeOwnedParams = isOwnedStructType(structTy) && params.size() == oldParamOrder_.size();
613 for (auto indexedAttr : llvm::enumerate(params.getValue())) {
614 unsigned index = indexedAttr.index();
615 Attribute attr = indexedAttr.value();
616 if (trimResolvedParams_ && removeOwnedParams &&
617 removedParams_.contains(oldParamOrder_[index])) {
618 if (failed(checkRemovedTemplateParam(
619 oldParamOrder_[index], attr, diagnosticOp, /*resolveTemplateSymbolArgs=*/true,
620 /*allowCurrentTemplateTypeVarSymbols=*/false
621 ))) {
622 return failure();
623 }
624 continue;
625 }
626 if (failed(validateAttr(attr, diagnosticOp))) {
627 return failure();
628 }
629 }
630 return success();
631 }
632};
633
635template <typename ConverterT, typename SetSignatureFn>
636static void updateCallableSignature(
637 FunctionType oldFuncTy, Region &body, ConverterT &converter, SetSignatureFn setSignature
638) {
639 Type converted = converter.convertType(oldFuncTy);
640 auto newFuncTy = llvm::cast<FunctionType>(converted);
641 if (oldFuncTy == newFuncTy) {
642 return;
643 }
644
645 setSignature(newFuncTy);
646 if (body.empty()) {
647 return;
648 }
649
650 Block &entryBlock = body.front();
651 assert(entryBlock.getNumArguments() == newFuncTy.getNumInputs());
652 for (auto [arg, newTy] : llvm::zip_equal(entryBlock.getArguments(), newFuncTy.getInputs())) {
653 arg.setType(newTy);
654 }
655}
656
662template <typename ConverterT>
663static void updateFuncSignature(FuncDefOp func, ConverterT &converter) {
664 updateCallableSignature(
665 func.getFunctionType(), func.getFunctionBody(), converter,
666 [func](FunctionType newFuncTy) mutable { func.setType(newFuncTy); }
667 );
668}
669
671template <typename ConverterT>
672static void updateContractSignature(verif::ContractOp contract, ConverterT &converter) {
673 updateCallableSignature(
674 contract.getFunctionType(), contract.getBody(), converter,
675 [contract](FunctionType newFuncTy) mutable { contract.setFunctionType(newFuncTy); }
676 );
677}
678
681template <typename ConverterT> static bool convertCallCallee(CallOp callOp, ConverterT &converter) {
682 if constexpr (requires { converter.convertCallCallee(callOp); }) {
683 SymbolRefAttr oldCallee = callOp.getCalleeAttr();
684 SymbolRefAttr newCallee = converter.convertCallCallee(callOp);
685 if (newCallee != oldCallee) {
686 callOp.setCalleeAttr(newCallee);
687 return true;
688 }
689 }
690 return false;
691}
692
695template <typename ConverterT>
696static bool convertContractTarget(verif::ContractOp contract, ConverterT &converter) {
697 if constexpr (requires { converter.convertContractTarget(contract); }) {
698 SymbolRefAttr oldTarget = contract.getTargetAttr();
699 SymbolRefAttr newTarget = converter.convertContractTarget(contract);
700 if (newTarget != oldTarget) {
701 contract.setTargetAttr(newTarget);
702 return true;
703 }
704 }
705 return false;
706}
707
709template <typename ConverterT> static bool converterFailed(ConverterT &converter) {
710 if constexpr (requires { converter.hadFailure(); }) {
711 return converter.hadFailure();
712 }
713 return false;
714}
715
717template <typename ConverterT>
718static void startConverterOperation(ConverterT &converter, Operation *op) {
719 if constexpr (requires { converter.startOperation(op); }) {
720 converter.startOperation(op);
721 }
722}
723
729template <typename ConverterT>
730static FailureOr<bool> convertOperationTypes(Operation *op, ConverterT &converter) {
731 if (auto createOp = llvm::dyn_cast<CreateArrayOp>(op)) {
732 ArrayType oldResultTy = createOp.getType();
733 auto newResultTy = llvm::dyn_cast<ArrayType>(converter.convertType(oldResultTy));
734 auto newElemTy = llvm::dyn_cast<ArrayType>(converter.convertType(oldResultTy.getElementType()));
735 if (converterFailed(converter)) {
736 return failure();
737 }
738 if (newResultTy && newElemTy && newResultTy != oldResultTy && !createOp.getElements().empty() &&
739 oldResultTy.hasStaticShape()) {
740 // Validate every init value in the `array.new` before creating replacement ops to avoid
741 // dangling IR if one of the init values is invalid.
742 SmallVector<Type> newElementValueTypes;
743 newElementValueTypes.reserve(createOp.getElements().size());
744 for (auto [index, element] : llvm::enumerate(createOp.getElements())) {
745 Type newElementValueTy = converter.convertType(element.getType());
746 if (converterFailed(converter)) {
747 return failure();
748 }
749 if (newElementValueTy != newElemTy) {
750 createOp.emitError() << "cannot rewrite initialized array.new: initializer " << index
751 << " converts to " << newElementValueTy << ", but expected "
752 << newElemTy;
753 return failure();
754 }
755 newElementValueTypes.push_back(newElementValueTy);
756 }
757
758 OpBuilder builder(createOp);
759 Location loc = createOp.getLoc();
760 CreateArrayOp newCreate = builder.create<CreateArrayOp>(loc, newResultTy);
761 ArrayIndexGen idxGen = ArrayIndexGen::from(oldResultTy);
762 for (auto [index, element] : llvm::enumerate(createOp.getElements())) {
763 Type newElementValueTy = newElementValueTypes[index];
764 if (element.getType() != newElementValueTy) {
765 element.setType(newElementValueTy);
766 }
767 std::optional<SmallVector<Value>> indices =
768 idxGen.delinearize(checkedCast<int64_t>(index), loc, builder);
769 assert(indices && "static array initializer index should delinearize");
770 builder.create<InsertArrayOp>(loc, newCreate.getResult(), ValueRange(*indices), element);
771 }
772 createOp.getResult().replaceAllUsesWith(newCreate.getResult());
773 createOp.erase();
774 return true;
775 }
776 }
777
778 if (auto readOp = llvm::dyn_cast<ReadArrayOp>(op)) {
779 Type newResultTy = converter.convertType(readOp.getResult().getType());
780 if (converterFailed(converter)) {
781 return failure();
782 }
783 if (auto newArrayTy = llvm::dyn_cast<ArrayType>(newResultTy)) {
784 OpBuilder builder(readOp);
785 auto extractOp = builder.create<ExtractArrayOp>(
786 readOp.getLoc(), newArrayTy, readOp.getArrRef(), readOp.getIndices()
787 );
788 readOp.getResult().replaceAllUsesWith(extractOp.getResult());
789 readOp.erase();
790 return true;
791 }
792 }
793
794 if (auto writeOp = llvm::dyn_cast<WriteArrayOp>(op)) {
795 Type newRvalueTy = converter.convertType(writeOp.getRvalue().getType());
796 if (converterFailed(converter)) {
797 return failure();
798 }
799 if (llvm::isa<ArrayType>(newRvalueTy)) {
800 OpBuilder builder(writeOp);
801 builder.create<InsertArrayOp>(
802 writeOp.getLoc(), writeOp.getArrRef(), writeOp.getIndices(), writeOp.getRvalue()
803 );
804 writeOp.erase();
805 return true;
806 }
807 }
808
809 bool changed = false;
810
811 if (auto func = llvm::dyn_cast<FuncDefOp>(op)) {
812 FunctionType oldFuncTy = func.getFunctionType();
813 updateFuncSignature(func, converter);
814 if (converterFailed(converter)) {
815 return failure();
816 }
817 changed |= oldFuncTy != func.getFunctionType();
818 }
819 if (auto contract = llvm::dyn_cast<verif::ContractOp>(op)) {
820 FunctionType oldFuncTy = contract.getFunctionType();
821 updateContractSignature(contract, converter);
822 if (converterFailed(converter)) {
823 return failure();
824 }
825 changed |= oldFuncTy != contract.getFunctionType();
826 changed |= convertContractTarget(contract, converter);
827 if (converterFailed(converter)) {
828 return failure();
829 }
830 }
831
832 for (Region &region : op->getRegions()) {
833 for (Block &block : region.getBlocks()) {
834 for (BlockArgument arg : block.getArguments()) {
835 Type newTy = converter.convertType(arg.getType());
836 if (converterFailed(converter)) {
837 return failure();
838 }
839 if (newTy != arg.getType()) {
840 arg.setType(newTy);
841 changed = true;
842 }
843 }
844 }
845 }
846
847 for (Value result : op->getResults()) {
848 Type newTy = converter.convertType(result.getType());
849 if (converterFailed(converter)) {
850 return failure();
851 }
852 if (newTy != result.getType()) {
853 result.setType(newTy);
854 changed = true;
855 }
856 }
857
858 if (auto callOp = llvm::dyn_cast<CallOp>(op)) {
859 changed |= convertCallCallee(callOp, converter);
860 if (converterFailed(converter)) {
861 return failure();
862 }
863 }
864
865 SmallVector<NamedAttribute> newAttrs;
866 bool attrsChanged = false;
867 newAttrs.reserve(op->getAttrs().size());
868 for (NamedAttribute attr : op->getAttrs()) {
869 Attribute newAttr = converter.convertAttr(attr.getValue());
870 if (converterFailed(converter)) {
871 return failure();
872 }
873 newAttrs.emplace_back(attr.getName(), newAttr);
874 attrsChanged |= newAttr != attr.getValue();
875 }
876 if (attrsChanged) {
877 op->setAttrs(DictionaryAttr::get(op->getContext(), newAttrs));
878 changed = true;
879 }
880
881 return changed;
882}
883
885template <typename ConverterT>
886static FailureOr<bool> convertOperationTypesInAndTrack(Operation *root, ConverterT &converter) {
887 bool changed = false;
888 WalkResult res = root->walk([&converter, &changed](Operation *op) {
889 startConverterOperation(converter, op);
890 FailureOr<bool> opChanged = convertOperationTypes(op, converter);
891 if (failed(opChanged)) {
892 return WalkResult::interrupt();
893 }
894 changed |= *opChanged;
895 return WalkResult::advance();
896 });
897 if (res.wasInterrupted()) {
898 return failure();
899 }
900 return changed;
901}
902
904template <typename ConverterT>
905static LogicalResult convertOperationTypesIn(Operation *root, ConverterT &converter) {
906 return failure(failed(convertOperationTypesInAndTrack(root, converter)));
907}
908
915static void removeIdentityCasts(Operation *root) {
916 for (UnifiableCastOp castOp : walkCollect<UnifiableCastOp>(*root)) {
917 if (castOp.getInput().getType() != castOp.getResult().getType()) {
918 continue;
919 }
920 castOp.getResult().replaceAllUsesWith(castOp.getInput());
921 castOp.erase();
922 }
923}
924
926template <typename ConverterT>
927static LogicalResult convertTemplateExprTypesIn(TemplateOp templateOp, ConverterT &converter) {
928 for (TemplateExprOp expr : templateOp.getConstOps<TemplateExprOp>()) {
929 if (failed(convertOperationTypesIn(expr.getOperation(), converter))) {
930 return failure();
931 }
932 removeIdentityCasts(expr.getOperation());
933 }
934 return success();
935}
936
942static std::string buildSpecializedTemplateCloneCacheKey(
943 StringRef templateName, ArrayRef<StringAttr> oldParamOrder,
944 const DenseMap<Attribute, Attribute> &paramNameToConcrete
945) {
946 std::string key;
947 llvm::raw_string_ostream os(key);
948 os << templateName.size() << ':' << templateName;
949 for (StringAttr paramName : oldParamOrder) {
950 os << '|';
951 os << paramName.getValue().size() << ':' << paramName.getValue() << '=';
952 auto concreteIt = paramNameToConcrete.find(FlatSymbolRefAttr::get(paramName));
953 if (concreteIt == paramNameToConcrete.end()) {
954 os << '_';
955 continue;
956 }
957
958 std::string attrText;
959 llvm::raw_string_ostream attrOs(attrText);
960 concreteIt->second.print(attrOs);
961 os << attrText.size() << ':' << attrText;
962 }
963 return key;
964}
965
967static bool canCopyTemplateExprToSpecialization(
968 TemplateExprOp expr, const DenseSet<StringAttr> &availableNames
969) {
970 bool canCopy = true;
971 expr->walk([&](ConstReadOp readOp) {
972 if (!availableNames.contains(readOp.getConstNameAttr().getAttr())) {
973 canCopy = false;
974 return WalkResult::interrupt();
975 }
976 return WalkResult::advance();
977 });
978 return canCopy;
979}
980
985static void copyPreservedTemplateExprs(
986 TemplateOp parentTemplate, Block &newTemplateBody, ArrayRef<Attribute> remainingNames
987) {
988 DenseSet<StringAttr> availableNames;
989 for (Attribute name : remainingNames) {
990 FlatSymbolRefAttr nameSym = llvm::cast<FlatSymbolRefAttr>(name);
991 availableNames.insert(nameSym.getAttr());
992 }
993
994 for (TemplateExprOp expr : parentTemplate.getConstOps<TemplateExprOp>()) {
995 if (canCopyTemplateExprToSpecialization(expr, availableNames)) {
996 newTemplateBody.push_back(expr->clone());
997 }
998 }
999}
1000
1002static FailureOr<TemplateOp> getOrCreateSpecializedTemplateClone(
1003 TemplateOp parentTemplate, ArrayRef<StringAttr> oldParamOrder,
1004 const DenseMap<Attribute, Attribute> &paramNameToConcrete, ArrayAttr callParams,
1005 SymbolTableCollection &tables, llvm::StringMap<StringAttr> &templateClones,
1006 InstantiationLayout &layout
1007) {
1008 layout = buildInstantiationLayout(parentTemplate, callParams, paramNameToConcrete);
1009 std::string cacheKey = buildSpecializedTemplateCloneCacheKey(
1010 parentTemplate.getSymName(), oldParamOrder, paramNameToConcrete
1011 );
1012
1013 ModuleOp parentModule = getParentOfType<ModuleOp>(parentTemplate);
1014 if (!parentModule) {
1015 return failure();
1016 }
1017 SymbolTable &moduleSymbols = tables.getSymbolTable(parentModule);
1018
1019 auto cachedName = templateClones.find(cacheKey);
1020 if (cachedName != templateClones.end()) {
1021 Operation *existing = moduleSymbols.lookup(cachedName->second);
1022 if (auto existingTemplate = llvm::dyn_cast_or_null<TemplateOp>(existing)) {
1023 return existingTemplate;
1024 }
1025 return failure();
1026 }
1027
1028 TemplateOp newTemplate = parentTemplate.cloneWithoutRegions();
1029 newTemplate.setSymName(layout.templateNameWithAttrs);
1030 assert(newTemplate->getNumRegions() > 0 && "region exists");
1031 newTemplate.getBodyRegion().emplaceBlock();
1032 Block &newTemplateBody = newTemplate.getBodyRegion().front();
1033 SymbolTable &parentTemplateSymbols = tables.getSymbolTable(parentTemplate);
1034 for (Attribute name : layout.remainingNames) {
1035 FlatSymbolRefAttr nameSym = llvm::cast<FlatSymbolRefAttr>(name);
1036 Operation *paramOp = parentTemplateSymbols.lookup(nameSym.getAttr());
1037 assert(paramOp && "symbol must exist");
1038 newTemplateBody.push_back(paramOp->clone());
1039 }
1040 copyPreservedTemplateExprs(parentTemplate, newTemplateBody, layout.remainingNames);
1041
1042 moduleSymbols.insert(newTemplate, Block::iterator(parentTemplate));
1043 templateClones.try_emplace(cacheKey, newTemplate.getSymNameAttr());
1044 return newTemplate;
1045}
1046
1053class ConcreteStructInstantiationConverter {
1055 struct StructInstantiationTypes {
1057 StructType localType;
1059 StructType remoteType;
1060 };
1061
1063 MLIRContext *ctx_;
1065 ModuleOp module_;
1067 SymbolTableCollection &tables_;
1069 DenseMap<StructType, StructInstantiationTypes> instantiations_;
1071 SpecializedTemplateCloneCache templateClones_;
1073 DenseSet<SymbolRefAttr> instantiatedCloneNames_;
1075 DenseMap<StructType, StructType> activeLocalStructReplacements_;
1077 DenseMap<StringAttr, Type> activeTypeReplacements_;
1079 bool hasFailure = false;
1080
1081public:
1083 ConcreteStructInstantiationConverter(MLIRContext *c, ModuleOp m, SymbolTableCollection &t)
1084 : ctx_(c), module_(m), tables_(t) {}
1085
1087 bool hadFailure() const { return hasFailure; }
1088
1090 Type convertType(Type ty) {
1091 if (!ty || hasFailure) {
1092 return ty;
1093 }
1094 if (auto tvarTy = llvm::dyn_cast<TypeVarType>(ty)) {
1095 auto it = activeTypeReplacements_.find(tvarTy.getNameRef().getAttr());
1096 return it == activeTypeReplacements_.end() ? ty : it->second;
1097 }
1098 if (auto arrTy = llvm::dyn_cast<ArrayType>(ty)) {
1099 return convertArrayElementType(arrTy, [this](Type elemTy) { return convertType(elemTy); });
1100 }
1101 if (auto podTy = llvm::dyn_cast<PodType>(ty)) {
1102 return convertPodType(podTy, ctx_, *this);
1103 }
1104 if (auto funcTy = llvm::dyn_cast<FunctionType>(ty)) {
1105 return convertFunctionType(funcTy, *this);
1106 }
1107 if (auto structTy = llvm::dyn_cast<StructType>(ty)) {
1108 return convertStructType(structTy);
1109 }
1110 return ty;
1111 }
1112
1114 Attribute convertAttr(Attribute attr) {
1115 if (hasFailure) {
1116 return attr;
1117 }
1118 return convertTypeOrArrayAttr(attr, ctx_, [this](Type ty) {
1119 return convertType(ty);
1120 }, [this](Attribute nested) { return convertTemplateArgAttr(nested); });
1121 }
1122
1124 SymbolRefAttr convertCallCallee(CallOp callOp) {
1125 SymbolRefAttr callee = callOp.getCalleeAttr();
1126 if (!callee || callee.getNestedReferences().empty()) {
1127 return callee;
1128 }
1129
1130 StructType targetStructTy = getStructFunctionTargetType(callOp);
1131 if (!targetStructTy) {
1132 return callee;
1133 }
1134 auto convertedStructTy = llvm::dyn_cast<StructType>(convertType(targetStructTy));
1135 if (!convertedStructTy) {
1136 return callee;
1137 }
1138
1139 SymbolRefAttr convertedStructName = convertedStructTy.getNameRef();
1140 if (convertedStructName == getPrefixAsSymbolRefAttr(callee)) {
1141 return callee;
1142 }
1143 SmallVector<FlatSymbolRefAttr> pieces = getPieces(convertedStructName);
1144 pieces.push_back(FlatSymbolRefAttr::get(callee.getLeafReference()));
1145 return asSymbolRefAttr(pieces);
1146 }
1147
1149 SymbolRefAttr convertContractTarget(verif::ContractOp contract) {
1150 SymbolRefAttr target = contract.getTargetAttr();
1151 if (!target || failed(contract.getStructTarget(tables_))) {
1152 return target;
1153 }
1154
1155 FunctionType contractTy = contract.getFunctionType();
1156 if (contractTy.getNumInputs() == 0) {
1157 return target;
1158 }
1159 auto selfTy = llvm::dyn_cast<StructType>(contractTy.getInput(0));
1160 if (!selfTy || selfTy.getNameRef() == target) {
1161 return target;
1162 }
1163 return instantiatedCloneNames_.contains(selfTy.getNameRef()) ? selfTy.getNameRef() : target;
1164 }
1165
1166private:
1168 static StructType getStructFunctionTargetType(CallOp callOp) {
1169 StringAttr calleeLeaf = callOp.getCallee().getLeafReference();
1170 if (calleeLeaf == FUNC_NAME_CONSTRAIN) {
1171 return dyn_cast<StructType>(callOp.getSelfValueFromConstrain().getType());
1172 }
1173 if (calleeLeaf == FUNC_NAME_COMPUTE || calleeLeaf == FUNC_NAME_PRODUCT) {
1174 return callOp.getSingleResultTypeOfWitnessGen();
1175 }
1176 return {};
1177 }
1178
1180 Attribute convertTemplateArgAttr(Attribute attr) {
1181 if (StringAttr symbolName = getFlatSymbolName(attr)) {
1182 auto it = activeTypeReplacements_.find(symbolName);
1183 if (it != activeTypeReplacements_.end()) {
1184 return TypeAttr::get(it->second);
1185 }
1186 }
1187 return convertAttr(attr);
1188 }
1189
1191 StructType convertStructType(StructType structTy) {
1192 ArrayAttr params = structTy.getParams();
1193 if (isNullOrEmpty(params)) {
1194 return structTy;
1195 }
1196
1197 SmallVector<Attribute> newParams;
1198 bool changed = false;
1199 for (Attribute attr : params.getValue()) {
1200 Attribute newAttr = convertTemplateArgAttr(attr);
1201 newParams.push_back(newAttr);
1202 changed |= newAttr != attr;
1203 }
1204 StructType convertedTy =
1205 changed ? StructType::get(structTy.getNameRef(), ArrayAttr::get(ctx_, newParams))
1206 : structTy;
1207
1208 if (auto it = activeLocalStructReplacements_.find(convertedTy);
1209 it != activeLocalStructReplacements_.end()) {
1210 return it->second;
1211 }
1212 if (instantiatedCloneNames_.contains(convertedTy.getNameRef())) {
1213 return convertedTy;
1214 }
1215
1216 if (llvm::any_of(newParams, [](Attribute attr) {
1217 return !isConcreteStructParamAttr(attr, /*allowStructParams=*/false);
1218 })) {
1219 return convertedTy;
1220 }
1221
1222 FailureOr<StructType> cloneTy = getOrCreateStructClone(convertedTy, newParams);
1223 if (failed(cloneTy)) {
1224 hasFailure = true;
1225 return convertedTy;
1226 }
1227 return *cloneTy;
1228 }
1229
1231 FailureOr<StructType>
1232 getOrCreateStructClone(StructType concreteStructTy, ArrayRef<Attribute> concreteParams) {
1233 if (auto it = instantiations_.find(concreteStructTy); it != instantiations_.end()) {
1234 return it->second.remoteType;
1235 }
1236
1237 FailureOr<SymbolLookupResult<StructDefOp>> lookup =
1238 concreteStructTy.getDefinition(tables_, module_, /*emitError=*/false);
1239 if (failed(lookup)) {
1240 return failure();
1241 }
1242
1243 StructDefOp origStruct = lookup->get();
1244 TemplateOp parentTemplate = getParentOfType<TemplateOp>(origStruct);
1245 if (!parentTemplate) {
1246 return failure();
1247 }
1248 StructType typeAtDef = origStruct.getType();
1249 ArrayAttr paramNames = typeAtDef.getParams();
1250 if (!paramNames || paramNames.size() != concreteParams.size()) {
1251 return failure();
1252 }
1253
1254 DenseMap<StringAttr, Type> typeReplacements;
1255 DenseMap<Attribute, Attribute> paramNameToConcrete;
1256 SmallVector<StringAttr> oldParamOrder;
1257 oldParamOrder.reserve(paramNames.size());
1258 SmallVector<Attribute> convertedSourceParams;
1259 convertedSourceParams.reserve(concreteParams.size());
1260 for (auto [paramName, concreteAttr] : llvm::zip_equal(paramNames.getValue(), concreteParams)) {
1261 auto paramSym = llvm::dyn_cast<FlatSymbolRefAttr>(paramName);
1262 if (!paramSym) {
1263 return failure();
1264 }
1265 oldParamOrder.push_back(paramSym.getAttr());
1266 auto concreteType = llvm::dyn_cast<TypeAttr>(concreteAttr);
1267 if (concreteType) {
1268 paramNameToConcrete.try_emplace(FlatSymbolRefAttr::get(paramSym.getAttr()), concreteAttr);
1269 typeReplacements.try_emplace(paramSym.getAttr(), concreteType.getValue());
1270 convertedSourceParams.push_back(concreteType);
1271 } else {
1272 convertedSourceParams.push_back(paramName);
1273 }
1274 }
1275 if (paramNameToConcrete.empty()) {
1276 return concreteStructTy;
1277 }
1278
1279 InstantiationLayout layout;
1280 ArrayAttr concreteParamArray = ArrayAttr::get(ctx_, concreteParams);
1281 FailureOr<TemplateOp> newTemplate = getOrCreateSpecializedTemplateClone(
1282 parentTemplate, oldParamOrder, paramNameToConcrete, concreteParamArray, tables_,
1283 templateClones_[parentTemplate.getOperation()], layout
1284 );
1285 if (failed(newTemplate)) {
1286 return failure();
1287 }
1288
1289 SymbolTable &templateSymbols = tables_.getSymbolTable(*newTemplate);
1290 StructDefOp clone = origStruct.clone();
1291 templateSymbols.insert(clone);
1292 StructType localTy =
1294 StructType remoteTy =
1296 instantiations_.try_emplace(concreteStructTy, StructInstantiationTypes {localTy, remoteTy});
1297 instantiatedCloneNames_.insert(clone.getFullyQualifiedName());
1298
1299 DenseMap<StringAttr, Type> previousReplacements = std::move(activeTypeReplacements_);
1300 DenseMap<StructType, StructType> previousLocalStructReplacements =
1301 std::move(activeLocalStructReplacements_);
1302 activeTypeReplacements_ = std::move(typeReplacements);
1303 activeLocalStructReplacements_.clear();
1304 activeLocalStructReplacements_.try_emplace(concreteStructTy, localTy);
1305 activeLocalStructReplacements_.try_emplace(
1306 StructType::get(typeAtDef.getNameRef(), ArrayAttr::get(ctx_, convertedSourceParams)),
1307 localTy
1308 );
1309 activeLocalStructReplacements_.try_emplace(
1310 StructType::get(localTy.getNameRef(), ArrayAttr::get(ctx_, convertedSourceParams)), localTy
1311 );
1312 activeLocalStructReplacements_.try_emplace(
1313 StructType::get(localTy.getNameRef(), ArrayAttr::get(ctx_, concreteParams)), localTy
1314 );
1315 SymbolRefAttr cloneNameRef = clone.getFullyQualifiedName();
1316 if (failed(convertTemplateExprTypesIn(*newTemplate, *this))) {
1317 activeLocalStructReplacements_ = std::move(previousLocalStructReplacements);
1318 activeTypeReplacements_ = std::move(previousReplacements);
1319 instantiations_.erase(concreteStructTy);
1320 instantiatedCloneNames_.erase(cloneNameRef);
1321 clone.erase();
1322 return failure();
1323 }
1324 if (failed(convertOperationTypesIn(clone.getOperation(), *this))) {
1325 activeLocalStructReplacements_ = std::move(previousLocalStructReplacements);
1326 activeTypeReplacements_ = std::move(previousReplacements);
1327 instantiations_.erase(concreteStructTy);
1328 instantiatedCloneNames_.erase(cloneNameRef);
1329 clone.erase();
1330 return failure();
1331 }
1332 removeIdentityCasts(clone.getOperation());
1333 activeLocalStructReplacements_ = std::move(previousLocalStructReplacements);
1334 activeTypeReplacements_ = std::move(previousReplacements);
1335 return remoteTy;
1336 }
1337};
1338
1340struct TemplateInferenceInfo {
1342 TemplateOp templateOp;
1344 SmallVector<StringAttr> templatePath;
1346 SmallVector<StringAttr> oldParamOrder;
1348 DenseMap<StringAttr, TemplateParamOp> typeVarParams;
1350 DenseMap<StringAttr, InferredType> replacements;
1352 DenseMap<StringAttr, InferredType> templateScopeReplacements;
1354 DenseMap<Operation *, DenseMap<StringAttr, InferredType>> functionReplacements;
1355};
1356
1358static DenseMap<Attribute, Attribute>
1359buildParamNameToCallArg(ArrayAttr callParams, ArrayRef<StringAttr> oldParamOrder) {
1360 DenseMap<Attribute, Attribute> paramNameToCallArg;
1361 if (!callParams || callParams.size() != oldParamOrder.size()) {
1362 return paramNameToCallArg;
1363 }
1364 for (auto [paramName, attr] : llvm::zip_equal(oldParamOrder, callParams.getValue())) {
1365 paramNameToCallArg.try_emplace(FlatSymbolRefAttr::get(paramName), attr);
1366 }
1367 return paramNameToCallArg;
1368}
1369
1376static Type
1377substituteExplicitCallTypeArgs(Type ty, ArrayAttr callParams, ArrayRef<StringAttr> oldParamOrder) {
1378 if (!callParams || callParams.size() != oldParamOrder.size()) {
1379 return ty;
1380 }
1381
1382 DenseMap<StringAttr, Type> callTypeArgs;
1383 for (auto [paramName, attr] : llvm::zip_equal(oldParamOrder, callParams.getValue())) {
1384 if (auto tyAttr = llvm::dyn_cast<TypeAttr>(attr)) {
1385 callTypeArgs.try_emplace(paramName, tyAttr.getValue());
1386 }
1387 }
1388 TypeVarReplacementConverter converter(
1389 ty.getContext(), ArrayRef<StringAttr> {}, oldParamOrder, callTypeArgs,
1390 /*trimResolvedParams=*/false
1391 );
1392 return converter.convertType(ty);
1393}
1394
1396class ExplicitCallTemplateParamSubstituter {
1398 const DenseMap<Attribute, Attribute> &paramNameToCallArg_;
1400 DenseSet<Attribute> resolvingParams_;
1401
1402public:
1403 explicit ExplicitCallTemplateParamSubstituter(
1404 const DenseMap<Attribute, Attribute> &paramNameToCallArg
1405 )
1406 : paramNameToCallArg_(paramNameToCallArg) {}
1407
1408 Type substituteType(Type ty);
1409 Attribute substituteAttr(Attribute attr);
1410};
1411
1413Attribute ExplicitCallTemplateParamSubstituter::substituteAttr(Attribute attr) {
1414 if (!attr) {
1415 return attr;
1416 }
1417 auto it = paramNameToCallArg_.find(attr);
1418 if (it != paramNameToCallArg_.end()) {
1419 return it->second;
1420 }
1421 if (auto tyAttr = llvm::dyn_cast<TypeAttr>(attr)) {
1422 Type newTy = substituteType(tyAttr.getValue());
1423 return newTy == tyAttr.getValue() ? attr : TypeAttr::get(newTy);
1424 }
1425 if (auto arrAttr = llvm::dyn_cast<ArrayAttr>(attr)) {
1426 SmallVector<Attribute> newAttrs;
1427 bool changed = false;
1428 for (Attribute nested : arrAttr.getValue()) {
1429 Attribute newNested = substituteAttr(nested);
1430 newAttrs.push_back(newNested);
1431 changed |= newNested != nested;
1432 }
1433 return changed ? ArrayAttr::get(attr.getContext(), newAttrs) : attr;
1434 }
1435 return attr;
1436}
1437
1445Type ExplicitCallTemplateParamSubstituter::substituteType(Type ty) {
1446 if (!ty) {
1447 return ty;
1448 }
1449 if (auto tvarTy = llvm::dyn_cast<TypeVarType>(ty)) {
1450 Attribute paramRef = tvarTy.getNameRef();
1451 auto it = paramNameToCallArg_.find(paramRef);
1452 if (it == paramNameToCallArg_.end()) {
1453 return ty;
1454 }
1455 auto tyAttr = llvm::dyn_cast<TypeAttr>(it->second);
1456 if (!tyAttr || tyAttr.getValue() == ty) {
1457 return ty;
1458 }
1459 if (!resolvingParams_.insert(paramRef).second) {
1460 return ty;
1461 }
1462 Type replacement = substituteType(tyAttr.getValue());
1463 resolvingParams_.erase(paramRef);
1464 return replacement;
1465 }
1466 if (auto arrTy = llvm::dyn_cast<ArrayType>(ty)) {
1467 SmallVector<Attribute> newDims;
1468 bool changed = false;
1469 for (Attribute dim : arrTy.getDimensionSizes()) {
1470 Attribute newDim = substituteAttr(dim);
1471 newDims.push_back(newDim);
1472 changed |= newDim != dim;
1473 }
1474 Type newElemTy = substituteType(arrTy.getElementType());
1475 if (!changed && newElemTy == arrTy.getElementType()) {
1476 return arrTy;
1477 }
1479 arrTy.cloneWith(arrTy.getElementType(), newDims), newElemTy
1480 );
1481 }
1482 if (auto structTy = llvm::dyn_cast<StructType>(ty)) {
1483 ArrayAttr params = structTy.getParams();
1484 if (!params) {
1485 return structTy;
1486 }
1487 SmallVector<Attribute> newParams;
1488 bool changed = false;
1489 for (Attribute param : params.getValue()) {
1490 Attribute newParam = substituteAttr(param);
1491 newParams.push_back(newParam);
1492 changed |= newParam != param;
1493 }
1494 return changed ? getStructTypeWithParams(structTy.getNameRef(), ty.getContext(), newParams)
1495 : structTy;
1496 }
1497 if (auto podTy = llvm::dyn_cast<PodType>(ty)) {
1498 SmallVector<RecordAttr> newRecords;
1499 bool changed = false;
1500 for (RecordAttr record : podTy.getRecords()) {
1501 Type newRecordTy = substituteType(record.getType());
1502 newRecords.push_back(RecordAttr::get(ty.getContext(), record.getName(), newRecordTy));
1503 changed |= newRecordTy != record.getType();
1504 }
1505 return changed ? PodType::get(ty.getContext(), newRecords) : podTy;
1506 }
1507 if (auto funcTy = llvm::dyn_cast<FunctionType>(ty)) {
1508 SmallVector<Type> newInputs;
1509 SmallVector<Type> newResults;
1510 bool changed = false;
1511 newInputs.reserve(funcTy.getNumInputs());
1512 newResults.reserve(funcTy.getNumResults());
1513 for (Type inputTy : funcTy.getInputs()) {
1514 Type newInputTy = substituteType(inputTy);
1515 newInputs.push_back(newInputTy);
1516 changed |= newInputTy != inputTy;
1517 }
1518 for (Type resultTy : funcTy.getResults()) {
1519 Type newResultTy = substituteType(resultTy);
1520 newResults.push_back(newResultTy);
1521 changed |= newResultTy != resultTy;
1522 }
1523 return changed ? FunctionType::get(ty.getContext(), newInputs, newResults) : funcTy;
1524 }
1525 return ty;
1526}
1527
1529static Type substituteExplicitCallTemplateParams(
1530 Type ty, ArrayAttr callParams, ArrayRef<StringAttr> oldParamOrder
1531) {
1532 auto paramNameToCallArg = buildParamNameToCallArg(callParams, oldParamOrder);
1533 ExplicitCallTemplateParamSubstituter substituter(paramNameToCallArg);
1534 return substituter.substituteType(ty);
1535}
1536
1538static bool symbolMatchesParam(SymbolRefAttr symRef, StringAttr paramName) {
1539 return symRef && symRef.getNestedReferences().empty() && symRef.getRootReference() == paramName;
1540}
1541
1543class ParamMentionChecker {
1544 StringAttr paramName_;
1545
1546public:
1547 explicit ParamMentionChecker(StringAttr paramName) : paramName_(paramName) {}
1548
1550 bool typeMentions(Type ty) const {
1551 if (!ty) {
1552 return false;
1553 }
1554 if (auto tvarTy = llvm::dyn_cast<TypeVarType>(ty)) {
1555 return tvarTy.getNameRef().getAttr() == paramName_;
1556 }
1557 if (auto arrayTy = llvm::dyn_cast<ArrayType>(ty)) {
1558 return typeMentions(arrayTy.getElementType()) ||
1559 llvm::any_of(arrayTy.getDimensionSizes(), [this](Attribute dim) {
1560 return attrMentions(dim, /*allowSymbolRefs=*/true);
1561 });
1562 }
1563 if (auto structTy = llvm::dyn_cast<StructType>(ty)) {
1564 ArrayAttr params = structTy.getParams();
1565 return params && attrMentions(params, /*allowSymbolRefs=*/true);
1566 }
1567 if (auto podTy = llvm::dyn_cast<PodType>(ty)) {
1568 return llvm::any_of(podTy.getRecords(), [this](RecordAttr record) {
1569 return typeMentions(record.getType());
1570 });
1571 }
1572 if (auto funcTy = llvm::dyn_cast<FunctionType>(ty)) {
1573 return llvm::any_of(funcTy.getInputs(), [this](Type input) {
1574 return typeMentions(input);
1575 }) || llvm::any_of(funcTy.getResults(), [this](Type result) { return typeMentions(result); });
1576 }
1577 return false;
1578 }
1579
1585 bool attrMentions(Attribute attr, bool allowSymbolRefs) const {
1586 if (!attr) {
1587 return false;
1588 }
1589 if (auto typeAttr = llvm::dyn_cast<TypeAttr>(attr)) {
1590 return typeMentions(typeAttr.getValue());
1591 }
1592 if (auto arrayAttr = llvm::dyn_cast<ArrayAttr>(attr)) {
1593 return llvm::any_of(arrayAttr, [this](Attribute nested) {
1594 return attrMentions(nested, /*allowSymbolRefs=*/true);
1595 });
1596 }
1597 return allowSymbolRefs && symbolMatchesParam(llvm::dyn_cast<SymbolRefAttr>(attr), paramName_);
1598 }
1599};
1600
1602static bool operationMentionsParam(Operation *op, StringAttr paramName) {
1603 ParamMentionChecker mentions(paramName);
1604 if (llvm::any_of(op->getResultTypes(), [&mentions](Type ty) {
1605 return mentions.typeMentions(ty);
1606 })) {
1607 return true;
1608 }
1609 for (Region &region : op->getRegions()) {
1610 for (Block &block : region.getBlocks()) {
1611 if (llvm::any_of(block.getArgumentTypes(), [&mentions](Type ty) {
1612 return mentions.typeMentions(ty);
1613 })) {
1614 return true;
1615 }
1616 }
1617 }
1618 return llvm::any_of(op->getAttrs(), [&mentions](NamedAttribute attr) {
1619 return mentions.attrMentions(attr.getValue(), /*allowSymbolRefs=*/false);
1620 });
1621}
1622
1624static SmallVector<StringAttr> getMentionedTypeVarParams(
1625 Attribute attr, const DenseMap<StringAttr, TemplateParamOp> &typeVarParams
1626) {
1627 SmallVector<StringAttr> mentionedParams;
1628 for (const auto &entry : typeVarParams) {
1629 if (ParamMentionChecker(entry.first).attrMentions(attr, /*allowSymbolRefs=*/true)) {
1630 mentionedParams.push_back(entry.first);
1631 }
1632 }
1633 return mentionedParams;
1634}
1635
1637static bool attrMentionsAnyTypeVarParam(
1638 Attribute attr, const DenseMap<StringAttr, TemplateParamOp> &typeVarParams
1639) {
1640 return !getMentionedTypeVarParams(attr, typeVarParams).empty();
1641}
1642
1644static bool funcMentionsParam(FuncDefOp func, StringAttr paramName) {
1645 if (operationMentionsParam(func.getOperation(), paramName)) {
1646 return true;
1647 }
1648 WalkResult result = func.walk([paramName](Operation *op) {
1649 return operationMentionsParam(op, paramName) ? WalkResult::interrupt() : WalkResult::advance();
1650 });
1651 return result.wasInterrupted();
1652}
1653
1655static bool contractMentionsParam(verif::ContractOp contract, StringAttr paramName) {
1656 if (operationMentionsParam(contract.getOperation(), paramName)) {
1657 return true;
1658 }
1659 WalkResult result = contract.walk([paramName](Operation *op) {
1660 return operationMentionsParam(op, paramName) ? WalkResult::interrupt() : WalkResult::advance();
1661 });
1662 return result.wasInterrupted();
1663}
1664
1666static bool targetMentionsParam(FuncDefOp func, StringAttr paramName) {
1667 return funcMentionsParam(func, paramName);
1668}
1669
1671static bool targetMentionsParam(verif::ContractOp contract, StringAttr paramName) {
1672 return contractMentionsParam(contract, paramName);
1673}
1674
1676static bool exprMentionsParam(TemplateExprOp expr, StringAttr paramName) {
1677 WalkResult result = expr.walk([paramName](Operation *op) {
1678 return operationMentionsParam(op, paramName) ? WalkResult::interrupt() : WalkResult::advance();
1679 });
1680 return result.wasInterrupted();
1681}
1682
1685static bool structUseCoveredByFunctionProof(
1686 StructDefOp structOp, StringAttr paramName, Type replacementType,
1687 const DenseMap<Operation *, DenseMap<StringAttr, InferredType>> &functionReplacements
1688) {
1689 bool sawMention = false;
1690 bool missingProof = false;
1691 structOp.walk([&](FuncDefOp func) {
1692 if (func->getParentOfType<StructDefOp>() != structOp || !funcMentionsParam(func, paramName)) {
1693 return;
1694 }
1695 sawMention = true;
1696 auto funcIt = functionReplacements.find(func.getOperation());
1697 if (funcIt == functionReplacements.end()) {
1698 missingProof = true;
1699 return;
1700 }
1701 auto replacementIt = funcIt->second.find(paramName);
1702 if (replacementIt == funcIt->second.end() || replacementIt->second.type != replacementType) {
1703 missingProof = true;
1704 }
1705 });
1706 return sawMention && !missingProof;
1707}
1708
1716static bool hasUncoveredNonFunctionMention(
1717 TemplateOp templateOp, StringAttr paramName, Type replacementType,
1718 const DenseMap<Operation *, DenseMap<StringAttr, InferredType>> &functionReplacements
1719) {
1720 WalkResult result =
1721 templateOp.walk([paramName, replacementType, &functionReplacements](Operation *op) {
1722 if (llvm::isa<FuncDefOp, TemplateExprOp, TemplateParamOp, verif::ContractOp>(op) ||
1724 return WalkResult::advance();
1725 }
1726 if (!operationMentionsParam(op, paramName)) {
1727 return WalkResult::advance();
1728 }
1729 if (StructDefOp structOp = op->getParentOfType<StructDefOp>()) {
1730 if (structUseCoveredByFunctionProof(
1731 structOp, paramName, replacementType, functionReplacements
1732 )) {
1733 return WalkResult::advance();
1734 }
1735 }
1736 return WalkResult::interrupt();
1737 });
1738 return result.wasInterrupted();
1739}
1740
1747class TypeVarInferenceCollector {
1749 TemplateInferenceInfo &inferenceInfo;
1751 DenseMap<StringAttr, InferredType> &replacements;
1753 DenseMap<Value, InferredType> byValue;
1755 DenseMap<StringAttr, DenseSet<StringAttr>> paramRelations;
1757 bool changedInIteration = false;
1758
1759public:
1761 TypeVarInferenceCollector(
1762 TemplateInferenceInfo &info, DenseMap<StringAttr, InferredType> &scopeReplacements
1763 )
1764 : inferenceInfo(info), replacements(scopeReplacements) {}
1765
1767 LogicalResult collect(Operation *root) {
1768 do {
1769 changedInIteration = false;
1770 WalkResult result = root->walk([this](UnifiableCastOp castOp) {
1771 return WalkResult(collectTypePairInferences(
1772 castOp.getInput().getType(), castOp.getResult().getType(), castOp.getInput(),
1773 castOp.getResult(), castOp.getLoc()
1774 ));
1775 });
1776 if (result.wasInterrupted()) {
1777 return failure();
1778 }
1779 } while (changedInIteration);
1780 return success();
1781 }
1782
1784 LogicalResult collectTypeInferences(Type lhs, Type rhs, Location loc) {
1785 do {
1786 changedInIteration = false;
1787 if (failed(collectTypePairInferences(lhs, rhs, Value(), Value(), loc))) {
1788 return failure();
1789 }
1790 } while (changedInIteration);
1791 return success();
1792 }
1793
1802 LogicalResult collectStructTemplateParamInferences(
1803 Operation *root, ModuleOp module, DenseMap<Operation *, TemplateInferenceInfo *> &infos,
1804 SymbolTableCollection &tables
1805 ) {
1806 WalkResult result = root->walk([&](Operation *op) {
1807 return failed(collectOperationStructTemplateParamInferences(op, module, infos, tables))
1808 ? WalkResult::interrupt()
1809 : WalkResult::advance();
1810 });
1811 return failure(result.wasInterrupted());
1812 }
1813
1815 LogicalResult collectOperationStructTemplateParamInferences(
1816 Operation *op, ModuleOp module, DenseMap<Operation *, TemplateInferenceInfo *> &infos,
1817 SymbolTableCollection &tables
1818 ) {
1819 Location loc = op->getLoc();
1820
1821 if (auto func = llvm::dyn_cast<FuncDefOp>(op)) {
1822 if (failed(collectStructTemplateParamInferences(
1823 func.getFunctionType(), loc, module, infos, tables
1824 ))) {
1825 return failure();
1826 }
1827 }
1828
1829 for (Region &region : op->getRegions()) {
1830 for (Block &block : region.getBlocks()) {
1831 for (Type argTy : block.getArgumentTypes()) {
1832 if (failed(collectStructTemplateParamInferences(argTy, loc, module, infos, tables))) {
1833 return failure();
1834 }
1835 }
1836 }
1837 }
1838
1839 for (Type resultTy : op->getResultTypes()) {
1840 if (failed(collectStructTemplateParamInferences(resultTy, loc, module, infos, tables))) {
1841 return failure();
1842 }
1843 }
1844
1845 if (auto callOp = llvm::dyn_cast<CallOp>(op)) {
1846 if (failed(collectCallableTemplateParamInferences(callOp, infos, tables))) {
1847 return failure();
1848 }
1849 }
1850 if (auto includeOp = llvm::dyn_cast<verif::IncludeOp>(op)) {
1851 if (failed(collectIncludeTemplateParamInferences(includeOp, infos, tables))) {
1852 return failure();
1853 }
1854 }
1855
1856 for (NamedAttribute attr : op->getAttrs()) {
1857 if (failed(
1858 collectStructTemplateParamInferences(attr.getValue(), loc, module, infos, tables)
1859 )) {
1860 return failure();
1861 }
1862 }
1863 return success();
1864 }
1865
1866private:
1872 LogicalResult collectStructTemplateParamInferences(
1873 Attribute attr, Location loc, ModuleOp module,
1874 DenseMap<Operation *, TemplateInferenceInfo *> &infos, SymbolTableCollection &tables
1875 ) {
1876 if (!attr) {
1877 return success();
1878 }
1879 if (auto tyAttr = llvm::dyn_cast<TypeAttr>(attr)) {
1880 return collectStructTemplateParamInferences(tyAttr.getValue(), loc, module, infos, tables);
1881 }
1882 if (auto arrAttr = llvm::dyn_cast<ArrayAttr>(attr)) {
1883 for (Attribute nested : arrAttr.getValue()) {
1884 if (failed(collectStructTemplateParamInferences(nested, loc, module, infos, tables))) {
1885 return failure();
1886 }
1887 }
1888 }
1889 return success();
1890 }
1891
1900 LogicalResult collectStructTemplateParamInferences(
1901 Type ty, Location loc, ModuleOp module, DenseMap<Operation *, TemplateInferenceInfo *> &infos,
1902 SymbolTableCollection &tables
1903 ) {
1904 if (!ty) {
1905 return success();
1906 }
1907 if (auto arrTy = llvm::dyn_cast<ArrayType>(ty)) {
1908 return collectStructTemplateParamInferences(
1909 arrTy.getElementType(), loc, module, infos, tables
1910 );
1911 }
1912 if (auto podTy = llvm::dyn_cast<PodType>(ty)) {
1913 for (RecordAttr record : podTy.getRecords()) {
1914 if (failed(
1915 collectStructTemplateParamInferences(record.getType(), loc, module, infos, tables)
1916 )) {
1917 return failure();
1918 }
1919 }
1920 return success();
1921 }
1922 if (auto funcTy = llvm::dyn_cast<FunctionType>(ty)) {
1923 for (Type inputTy : funcTy.getInputs()) {
1924 if (failed(collectStructTemplateParamInferences(inputTy, loc, module, infos, tables))) {
1925 return failure();
1926 }
1927 }
1928 for (Type resultTy : funcTy.getResults()) {
1929 if (failed(collectStructTemplateParamInferences(resultTy, loc, module, infos, tables))) {
1930 return failure();
1931 }
1932 }
1933 return success();
1934 }
1935 auto structTy = llvm::dyn_cast<StructType>(ty);
1936 if (!structTy) {
1937 return success();
1938 }
1939
1940 ArrayAttr params = structTy.getParams();
1941 if (!params) {
1942 return success();
1943 }
1944 for (Attribute attr : params.getValue()) {
1945 if (failed(collectStructTemplateParamInferences(attr, loc, module, infos, tables))) {
1946 return failure();
1947 }
1948 }
1949
1950 FailureOr<SymbolLookupResult<StructDefOp>> lookup =
1951 structTy.getDefinition(tables, module, /*reportMissing=*/false);
1952 if (failed(lookup)) {
1953 return success();
1954 }
1955 TemplateOp parentTemplate = getParentOfType<TemplateOp>(lookup->get().getOperation());
1956 if (!parentTemplate) {
1957 return success();
1958 }
1959 TemplateInferenceInfo *targetInfo = infos.lookup(parentTemplate.getOperation());
1960 if (!targetInfo || targetInfo->replacements.empty() ||
1961 params.size() != targetInfo->oldParamOrder.size()) {
1962 return success();
1963 }
1964
1965 for (auto [paramName, attr] : llvm::zip_equal(targetInfo->oldParamOrder, params.getValue())) {
1966 auto replacementIt = targetInfo->replacements.find(paramName);
1967 if (replacementIt == targetInfo->replacements.end()) {
1968 continue;
1969 }
1970 Type expectedTy = substituteExplicitCallTemplateParams(
1971 replacementIt->second.type, params, targetInfo->oldParamOrder
1972 );
1973 Attribute expectedAttr = TypeAttr::get(expectedTy);
1974 if (failed(collectTemplateArgInferences(attr, expectedAttr, loc))) {
1975 return failure();
1976 }
1977 }
1978 return success();
1979 }
1980
1987 SmallVector<Attribute>
1988 getInferredOmittedTemplateArgs(const UnificationMap &unifyResult, StringAttr targetParamName) {
1989 SmallVector<Attribute> inferredAttrs;
1990 auto targetRef = FlatSymbolRefAttr::get(targetParamName);
1991 auto inferredIt = unifyResult.find({targetRef, Side::RHS});
1992 if (inferredIt != unifyResult.end()) {
1993 inferredAttrs.push_back(inferredIt->second);
1994 }
1995 for (const auto &entry : unifyResult) {
1996 if (entry.first.second == Side::LHS && entry.second == targetRef) {
1997 inferredAttrs.push_back(entry.first.first);
1998 }
1999 }
2000 return inferredAttrs;
2001 }
2002
2004 template <typename TargetOp>
2005 TemplateInferenceInfo *
2006 getTargetTemplateInfo(TargetOp targetOp, DenseMap<Operation *, TemplateInferenceInfo *> &infos) {
2007 TemplateOp parentTemplate = getParentOfType<TemplateOp>(targetOp.getOperation());
2008 if (!parentTemplate) {
2009 return nullptr;
2010 }
2011 TemplateInferenceInfo *targetInfo = infos.lookup(parentTemplate.getOperation());
2012 if (!targetInfo || targetInfo->replacements.empty()) {
2013 return nullptr;
2014 }
2015 return targetInfo;
2016 }
2017
2025 template <typename CallableOp>
2026 LogicalResult collectCallableUseTemplateParamInferences(
2027 CallableOp callableOp, FunctionType targetSignature, TemplateInferenceInfo &targetInfo
2028 ) {
2029 ArrayAttr params = callableOp.getTemplateParamsAttr();
2030 if (!isNullOrEmpty(params)) {
2031 if (params.size() != targetInfo.oldParamOrder.size()) {
2032 return success();
2033 }
2034 struct DeferredExplicitArgInference {
2035 Attribute attr;
2036 Attribute expectedAttr;
2037 SmallVector<StringAttr> mentionedParams;
2038 };
2039 SmallVector<DeferredExplicitArgInference> deferredExplicitArgInferences;
2040 bool deferredToSignature = false;
2041 for (auto [paramName, attr] : llvm::zip_equal(targetInfo.oldParamOrder, params)) {
2042 auto replacementIt = targetInfo.replacements.find(paramName);
2043 if (replacementIt == targetInfo.replacements.end()) {
2044 continue;
2045 }
2046 Type expectedTy = substituteExplicitCallTemplateParams(
2047 replacementIt->second.type, params, targetInfo.oldParamOrder
2048 );
2049 Attribute expectedAttr = TypeAttr::get(expectedTy);
2050 if (attrMentionsAnyTypeVarParam(attr, inferenceInfo.typeVarParams)) {
2051 deferredToSignature = true;
2052 SmallVector<StringAttr> mentionedParams =
2053 getMentionedTypeVarParams(attr, inferenceInfo.typeVarParams);
2054 if (!ParamMentionChecker(paramName).typeMentions(targetSignature)) {
2055 if (failed(collectTemplateArgInferences(attr, expectedAttr, callableOp.getLoc()))) {
2056 return failure();
2057 }
2058 } else {
2059 deferredExplicitArgInferences.push_back(
2060 {attr, expectedAttr, std::move(mentionedParams)}
2061 );
2062 }
2063 continue;
2064 }
2065 if (failed(collectTemplateArgInferences(attr, expectedAttr, callableOp.getLoc()))) {
2066 return failure();
2067 }
2068 }
2069 if (deferredToSignature) {
2070 DenseMap<StringAttr, Type> targetReplacements;
2071 for (const auto &entry : targetInfo.replacements) {
2072 targetReplacements.try_emplace(entry.first, entry.second.type);
2073 }
2074 TypeVarReplacementConverter converter(
2075 callableOp.getContext(), targetInfo.templatePath, targetInfo.oldParamOrder,
2076 targetReplacements, /*trimResolvedParams=*/false
2077 );
2078 Type rewrittenTy = substituteExplicitCallTemplateParams(
2079 converter.convertType(targetSignature), params, targetInfo.oldParamOrder
2080 );
2081 if (failed(collectTypeInferences(
2082 callableOp.getTypeSignature(), llvm::cast<FunctionType>(rewrittenTy),
2083 callableOp.getLoc()
2084 ))) {
2085 return failure();
2086 }
2087
2088 for (const DeferredExplicitArgInference &deferred : deferredExplicitArgInferences) {
2089 bool signatureCanInferAllParams =
2090 llvm::all_of(deferred.mentionedParams, [&](StringAttr mentionedParam) {
2091 return ParamMentionChecker(mentionedParam).typeMentions(callableOp.getTypeSignature());
2092 });
2093 bool signatureHasInferredAllParams =
2094 llvm::all_of(deferred.mentionedParams, [&](StringAttr mentionedParam) {
2095 return replacements.contains(mentionedParam);
2096 });
2097 if (signatureCanInferAllParams && signatureHasInferredAllParams) {
2098 continue;
2099 }
2100 if (failed(collectTemplateArgInferences(
2101 deferred.attr, deferred.expectedAttr, callableOp.getLoc()
2102 ))) {
2103 return failure();
2104 }
2105 }
2106 }
2107 return success();
2108 }
2109
2110 FailureOr<UnificationMap> unifyResult = callableOp.unifyTypeSignature(targetSignature);
2111 if (failed(unifyResult)) {
2112 return success();
2113 }
2114
2115 for (StringAttr paramName : targetInfo.oldParamOrder) {
2116 auto replacementIt = targetInfo.replacements.find(paramName);
2117 if (replacementIt == targetInfo.replacements.end()) {
2118 continue;
2119 }
2120 SmallVector<Attribute> inferredAttrs =
2121 getInferredOmittedTemplateArgs(*unifyResult, paramName);
2122 if (inferredAttrs.empty()) {
2123 continue;
2124 }
2125
2126 Attribute expectedAttr = TypeAttr::get(replacementIt->second.type);
2127 for (Attribute inferredAttr : inferredAttrs) {
2128 if (!inferredAttr || !typeParamsUnify({inferredAttr}, {expectedAttr})) {
2129 InFlightDiagnostic diag = callableOp.emitError()
2130 << "implicit template argument for inferred parameter @"
2131 << paramName.getValue() << " must match inferred type "
2132 << replacementIt->second.type << ", but found ";
2133 if (auto typeAttr = llvm::dyn_cast_if_present<TypeAttr>(inferredAttr)) {
2134 diag << typeAttr.getValue();
2135 } else {
2136 diag << inferredAttr;
2137 }
2138 return diag;
2139 }
2140 if (failed(collectTemplateArgInferences(inferredAttr, expectedAttr, callableOp.getLoc()))) {
2141 return failure();
2142 }
2143 }
2144 }
2145 return success();
2146 }
2147
2157 LogicalResult collectCallableTemplateParamInferences(
2158 CallOp callOp, DenseMap<Operation *, TemplateInferenceInfo *> &infos,
2159 SymbolTableCollection &tables
2160 ) {
2161 FailureOr<SymbolLookupResult<FuncDefOp>> target = callOp.getCalleeTarget(tables);
2162 if (failed(target)) {
2163 return success();
2164 }
2165 FuncDefOp targetFunc = target->get();
2166 TemplateInferenceInfo *targetInfo = getTargetTemplateInfo(targetFunc, infos);
2167 if (!targetInfo) {
2168 return success();
2169 }
2170 return collectCallableUseTemplateParamInferences(
2171 callOp, targetFunc.getFunctionType(), *targetInfo
2172 );
2173 }
2174
2181 LogicalResult collectIncludeTemplateParamInferences(
2182 verif::IncludeOp includeOp, DenseMap<Operation *, TemplateInferenceInfo *> &infos,
2183 SymbolTableCollection &tables
2184 ) {
2185 FailureOr<SymbolLookupResult<verif::ContractOp>> target = includeOp.getCalleeTarget(tables);
2186 if (failed(target)) {
2187 return success();
2188 }
2189 verif::ContractOp targetContract = target->get();
2190 TemplateInferenceInfo *targetInfo = getTargetTemplateInfo(targetContract, infos);
2191 if (!targetInfo) {
2192 return success();
2193 }
2194 return collectCallableUseTemplateParamInferences(
2195 includeOp, targetContract.getFunctionType(), *targetInfo
2196 );
2197 }
2198
2212 LogicalResult recordInference(StringAttr paramName, Type inferredTy, Value value, Location loc) {
2213 if (!inferenceInfo.typeVarParams.contains(paramName)) {
2214 return success();
2215 }
2216 if (auto inferredTvar = llvm::dyn_cast<TypeVarType>(inferredTy)) {
2217 return recordParamRelation(paramName, inferredTvar.getNameRef().getAttr(), loc);
2218 }
2219 if (!isTypeVarFreeType(inferredTy)) {
2220 return success();
2221 }
2222
2223 auto reportConflict = [&](StringRef kind, Location originalLoc, Type originalTy) {
2224 InFlightDiagnostic diag = emitError(loc) << "conflicting inferred type for " << kind << " @"
2225 << paramName.getValue() << ": " << originalTy
2226 << " vs " << inferredTy;
2227 diag.attachNote(originalLoc) << "previous inference here";
2228 return diag;
2229 };
2230
2231 auto byParamIt = replacements.find(paramName);
2232 bool learnedParamInference = false;
2233 if (byParamIt == replacements.end()) {
2234 replacements.try_emplace(paramName, InferredType {inferredTy, loc});
2235 changedInIteration = true;
2236 learnedParamInference = true;
2237 } else if (byParamIt->second.type != inferredTy) {
2238 return reportConflict("template parameter", byParamIt->second.loc, byParamIt->second.type);
2239 }
2240
2241 if (value) {
2242 auto byValueIt = byValue.find(value);
2243 if (byValueIt == byValue.end()) {
2244 byValue.try_emplace(value, InferredType {inferredTy, loc});
2245 changedInIteration = true;
2246 } else if (byValueIt->second.type != inferredTy) {
2247 return reportConflict("SSA value using", byValueIt->second.loc, byValueIt->second.type);
2248 }
2249 }
2250
2251 if (learnedParamInference) {
2252 auto relatedIt = paramRelations.find(paramName);
2253 if (relatedIt != paramRelations.end()) {
2254 for (StringAttr relatedParam : relatedIt->second) {
2255 if (failed(recordInference(relatedParam, inferredTy, Value(), loc))) {
2256 return failure();
2257 }
2258 }
2259 }
2260 }
2261 return success();
2262 }
2263
2265 LogicalResult recordParamRelation(StringAttr lhsParam, StringAttr rhsParam, Location loc) {
2266 if (lhsParam == rhsParam || !inferenceInfo.typeVarParams.contains(rhsParam)) {
2267 return success();
2268 }
2269
2270 bool inserted = paramRelations[lhsParam].insert(rhsParam).second;
2271 inserted |= paramRelations[rhsParam].insert(lhsParam).second;
2272 if (!inserted) {
2273 return success();
2274 }
2275
2276 changedInIteration = true;
2277
2278 auto lhsIt = replacements.find(lhsParam);
2279 if (lhsIt != replacements.end() &&
2280 failed(recordInference(rhsParam, lhsIt->second.type, Value(), loc))) {
2281 return failure();
2282 }
2283 auto rhsIt = replacements.find(rhsParam);
2284 if (rhsIt != replacements.end() &&
2285 failed(recordInference(lhsParam, rhsIt->second.type, Value(), loc))) {
2286 return failure();
2287 }
2288 return success();
2289 }
2290
2298 LogicalResult
2299 collectTypePairInferences(Type lhs, Type rhs, Value lhsValue, Value rhsValue, Location loc) {
2300 if (auto lhsTvar = llvm::dyn_cast<TypeVarType>(lhs)) {
2301 if (failed(recordInference(lhsTvar.getNameRef().getAttr(), rhs, lhsValue, loc))) {
2302 return failure();
2303 }
2304 if (rhsValue) {
2305 auto rhsValueIt = byValue.find(rhsValue);
2306 if (rhsValueIt != byValue.end() &&
2307 failed(recordInference(
2308 lhsTvar.getNameRef().getAttr(), rhsValueIt->second.type, lhsValue, loc
2309 ))) {
2310 return failure();
2311 }
2312 }
2313 }
2314 if (auto rhsTvar = llvm::dyn_cast<TypeVarType>(rhs)) {
2315 if (failed(recordInference(rhsTvar.getNameRef().getAttr(), lhs, rhsValue, loc))) {
2316 return failure();
2317 }
2318 if (lhsValue) {
2319 auto lhsValueIt = byValue.find(lhsValue);
2320 if (lhsValueIt != byValue.end() &&
2321 failed(recordInference(
2322 rhsTvar.getNameRef().getAttr(), lhsValueIt->second.type, rhsValue, loc
2323 ))) {
2324 return failure();
2325 }
2326 }
2327 }
2328
2329 if (auto lhsArr = llvm::dyn_cast<ArrayType>(lhs)) {
2330 if (auto rhsArr = llvm::dyn_cast<ArrayType>(rhs)) {
2331 return collectTypePairInferences(
2332 lhsArr.getElementType(), rhsArr.getElementType(), Value(), Value(), loc
2333 );
2334 }
2335 }
2336
2337 if (auto lhsStruct = llvm::dyn_cast<StructType>(lhs)) {
2338 if (auto rhsStruct = llvm::dyn_cast<StructType>(rhs)) {
2339 ArrayRef<Attribute> lhsParams =
2340 lhsStruct.getParams() ? lhsStruct.getParams().getValue() : ArrayRef<Attribute> {};
2341 ArrayRef<Attribute> rhsParams =
2342 rhsStruct.getParams() ? rhsStruct.getParams().getValue() : ArrayRef<Attribute> {};
2343 if (lhsParams.size() != rhsParams.size()) {
2344 return success();
2345 }
2346 for (auto [lhsAttr, rhsAttr] : llvm::zip_equal(lhsParams, rhsParams)) {
2347 if (failed(collectTemplateArgInferences(lhsAttr, rhsAttr, loc))) {
2348 return failure();
2349 }
2350 }
2351 }
2352 }
2353
2354 if (auto lhsPod = llvm::dyn_cast<PodType>(lhs)) {
2355 if (auto rhsPod = llvm::dyn_cast<PodType>(rhs)) {
2356 ArrayRef<RecordAttr> lhsRecords = lhsPod.getRecords();
2357 ArrayRef<RecordAttr> rhsRecords = rhsPod.getRecords();
2358 if (lhsRecords.size() != rhsRecords.size()) {
2359 return success();
2360 }
2361 for (auto [lhsRecord, rhsRecord] : llvm::zip_equal(lhsRecords, rhsRecords)) {
2362 if (lhsRecord.getName() != rhsRecord.getName()) {
2363 return success();
2364 }
2365 if (failed(collectTypePairInferences(
2366 lhsRecord.getType(), rhsRecord.getType(), Value(), Value(), loc
2367 ))) {
2368 return failure();
2369 }
2370 }
2371 }
2372 }
2373
2374 if (auto lhsFunc = llvm::dyn_cast<FunctionType>(lhs)) {
2375 if (auto rhsFunc = llvm::dyn_cast<FunctionType>(rhs)) {
2376 if (lhsFunc.getNumInputs() != rhsFunc.getNumInputs() ||
2377 lhsFunc.getNumResults() != rhsFunc.getNumResults()) {
2378 return success();
2379 }
2380 for (auto [lhsInput, rhsInput] :
2381 llvm::zip_equal(lhsFunc.getInputs(), rhsFunc.getInputs())) {
2382 if (failed(collectTypePairInferences(lhsInput, rhsInput, Value(), Value(), loc))) {
2383 return failure();
2384 }
2385 }
2386 for (auto [lhsResult, rhsResult] :
2387 llvm::zip_equal(lhsFunc.getResults(), rhsFunc.getResults())) {
2388 if (failed(collectTypePairInferences(lhsResult, rhsResult, Value(), Value(), loc))) {
2389 return failure();
2390 }
2391 }
2392 }
2393 }
2394
2395 return success();
2396 }
2397
2405 LogicalResult collectTemplateArgInferences(Attribute lhsAttr, Attribute rhsAttr, Location loc) {
2406 auto lhsTyAttr = llvm::dyn_cast<TypeAttr>(lhsAttr);
2407 auto rhsTyAttr = llvm::dyn_cast<TypeAttr>(rhsAttr);
2408 if (lhsTyAttr && rhsTyAttr) {
2409 return collectTypePairInferences(
2410 lhsTyAttr.getValue(), rhsTyAttr.getValue(), Value(), Value(), loc
2411 );
2412 }
2413
2414 if (StringAttr lhsSymbolName = getFlatSymbolName(lhsAttr)) {
2415 if (StringAttr rhsSymbolName = getFlatSymbolName(rhsAttr)) {
2416 return recordParamRelation(lhsSymbolName, rhsSymbolName, loc);
2417 }
2418 if (rhsTyAttr && failed(recordInference(lhsSymbolName, rhsTyAttr.getValue(), Value(), loc))) {
2419 return failure();
2420 }
2421 }
2422 if (StringAttr rhsSymbolName = getFlatSymbolName(rhsAttr)) {
2423 if (lhsTyAttr && failed(recordInference(rhsSymbolName, lhsTyAttr.getValue(), Value(), loc))) {
2424 return failure();
2425 }
2426 }
2427 return success();
2428 }
2429};
2430
2432static DenseMap<StringAttr, Type>
2433getFunctionProofReplacements(const TemplateInferenceInfo &info, FuncDefOp func) {
2434 DenseMap<StringAttr, Type> replacements;
2435 auto funcIt = info.functionReplacements.find(func.getOperation());
2436 if (funcIt == info.functionReplacements.end()) {
2437 return replacements;
2438 }
2439 for (const auto &entry : funcIt->second) {
2440 replacements.try_emplace(entry.first, entry.second.type);
2441 }
2442 return replacements;
2443}
2444
2446static std::optional<unsigned>
2447getParamIndex(ArrayRef<StringAttr> paramOrder, StringAttr paramName) {
2448 for (auto indexedParam : llvm::enumerate(paramOrder)) {
2449 if (indexedParam.value() == paramName) {
2450 return indexedParam.index();
2451 }
2452 }
2453 return std::nullopt;
2454}
2455
2458static DenseMap<Attribute, Attribute>
2459buildParamNameToConcrete(const DenseMap<StringAttr, Type> &replacements) {
2460 DenseMap<Attribute, Attribute> paramNameToConcrete;
2461 for (const auto &entry : replacements) {
2462 paramNameToConcrete.try_emplace(
2463 FlatSymbolRefAttr::get(entry.first), TypeAttr::get(entry.second)
2464 );
2465 }
2466 return paramNameToConcrete;
2467}
2468
2472static bool callParamsMatchReplacements(
2473 ArrayAttr callParams, ArrayRef<StringAttr> oldParamOrder,
2474 const DenseMap<StringAttr, Type> &replacements,
2475 const DenseMap<StringAttr, InferredType> *wildcardAllowedReplacements = nullptr
2476) {
2477 if (!callParams || callParams.size() != oldParamOrder.size()) {
2478 return false;
2479 }
2480 for (const auto &entry : replacements) {
2481 std::optional<unsigned> index = getParamIndex(oldParamOrder, entry.first);
2482 if (!index) {
2483 return false;
2484 }
2485 Type expectedTy = substituteExplicitCallTypeArgs(entry.second, callParams, oldParamOrder);
2486 Attribute attr = callParams[*index];
2487 if (auto intAttr = llvm::dyn_cast<IntegerAttr>(attr);
2488 intAttr && isDynamic(intAttr) && wildcardAllowedReplacements &&
2489 wildcardAllowedReplacements->contains(entry.first)) {
2490 continue;
2491 }
2492 if (!templateArgUnifiesWithType(attr, expectedTy)) {
2493 return false;
2494 }
2495 }
2496 return true;
2497}
2498
2505static LogicalResult diagnoseCallParamsMismatch(
2506 Operation *callableOp, ArrayAttr callParams, ArrayRef<StringAttr> oldParamOrder,
2507 const DenseMap<StringAttr, Type> &replacements,
2508 const DenseMap<StringAttr, InferredType> *wildcardAllowedReplacements, bool explicitCallParams
2509) {
2510 if (callParamsMatchReplacements(
2511 callParams, oldParamOrder, replacements, wildcardAllowedReplacements
2512 )) {
2513 return success();
2514 }
2515 for (const auto &entry : replacements) {
2516 std::optional<unsigned> index = getParamIndex(oldParamOrder, entry.first);
2517 if (!index || !callParams || *index >= callParams.size()) {
2518 continue;
2519 }
2520 Attribute attr = callParams[*index];
2521 Type expectedTy = substituteExplicitCallTypeArgs(entry.second, callParams, oldParamOrder);
2522 if (auto intAttr = llvm::dyn_cast<IntegerAttr>(attr);
2523 intAttr && isDynamic(intAttr) && wildcardAllowedReplacements &&
2524 wildcardAllowedReplacements->contains(entry.first)) {
2525 continue;
2526 }
2527 if (templateArgUnifiesWithType(attr, expectedTy)) {
2528 continue;
2529 }
2530
2531 InFlightDiagnostic diag = callableOp->emitError()
2532 << (explicitCallParams ? "explicit" : "implicit")
2533 << " template argument for inferred parameter @"
2534 << entry.first.getValue() << " must match inferred type "
2535 << expectedTy << ", but found ";
2536 if (auto typeAttr = llvm::dyn_cast<TypeAttr>(attr)) {
2537 diag << typeAttr.getValue();
2538 } else {
2539 diag << attr;
2540 }
2541 return diag;
2542 }
2543 return callableOp->emitError() << (explicitCallParams ? "explicit" : "implicit")
2544 << " template arguments do not match inferred callee types";
2545}
2546
2550static ArrayAttr expandCurrentTemplateParamsToOriginalOrder(
2551 ArrayAttr callParams, const TemplateInferenceInfo &info
2552) {
2553 if (!callParams || callParams.size() == info.oldParamOrder.size()) {
2554 return callParams;
2555 }
2556
2557 unsigned currentParamCount = llvm::count_if(info.oldParamOrder, [&info](StringAttr paramName) {
2558 return !info.replacements.contains(paramName);
2559 });
2560 if (callParams.size() != currentParamCount) {
2561 return callParams;
2562 }
2563
2564 SmallVector<Attribute> expandedParams;
2565 expandedParams.reserve(info.oldParamOrder.size());
2566 unsigned currentIndex = 0;
2567 for (StringAttr paramName : info.oldParamOrder) {
2568 auto replacementIt = info.replacements.find(paramName);
2569 if (replacementIt != info.replacements.end()) {
2570 expandedParams.push_back(TypeAttr::get(replacementIt->second.type));
2571 continue;
2572 }
2573 expandedParams.push_back(callParams[currentIndex++]);
2574 }
2575 return ArrayAttr::get(callParams.getContext(), expandedParams);
2576}
2577
2584template <typename TargetOp>
2585static DenseMap<StringAttr, Type> getConcreteCallSiteReplacements(
2586 ArrayAttr callParams, const TemplateInferenceInfo &info, TargetOp targetOp
2587) {
2588 DenseMap<StringAttr, Type> replacements;
2589 if (!callParams || callParams.size() != info.oldParamOrder.size()) {
2590 return replacements;
2591 }
2592
2593 bool sawResidualReplacement = false;
2594 for (const auto &entry : info.typeVarParams) {
2595 StringAttr paramName = entry.first;
2596 if (info.replacements.contains(paramName)) {
2597 continue;
2598 }
2599 if (!targetMentionsParam(targetOp, paramName)) {
2600 continue;
2601 }
2602 std::optional<unsigned> index = getParamIndex(info.oldParamOrder, paramName);
2603 assert(index && "eligible type-variable parameter must appear in parameter order");
2604 Attribute attr = callParams[*index];
2605 auto tyAttr = llvm::dyn_cast<TypeAttr>(attr);
2606 if (!tyAttr) {
2607 return DenseMap<StringAttr, Type>();
2608 }
2609 replacements.try_emplace(paramName, tyAttr.getValue());
2610 sawResidualReplacement = true;
2611 }
2612 if (!sawResidualReplacement) {
2613 return DenseMap<StringAttr, Type>();
2614 }
2615
2616 for (const auto &entry : info.replacements) {
2617 replacements.try_emplace(entry.first, entry.second.type);
2618 }
2619
2620 TypeVarReplacementConverter converter(
2621 info.templatePath.front().getContext(), info.templatePath, info.oldParamOrder, replacements,
2622 /*trimResolvedParams=*/false
2623 );
2624 for (const auto &entry : replacements) {
2625 if (!isTypeVarFreeType(converter.convertType(entry.second))) {
2626 return DenseMap<StringAttr, Type>();
2627 }
2628 }
2629 return replacements;
2630}
2631
2633static bool isWildcardTemplateArg(Attribute attr) {
2634 auto intAttr = llvm::dyn_cast_if_present<IntegerAttr>(attr);
2635 return intAttr && isDynamic(intAttr);
2636}
2637
2639static void appendUniqueType(SmallVectorImpl<Type> &types, Type ty) {
2640 if (!llvm::any_of(types, [ty](Type existing) { return existing == ty; })) {
2641 types.push_back(ty);
2642 }
2643}
2644
2646static void collectRhsTypeVarCandidates(
2647 Type lhsTy, Type rhsTy, SymbolRefAttr targetRef, SmallVectorImpl<Type> &candidates
2648);
2649
2651static void collectRhsTypeVarCandidates(
2652 ArrayRef<Attribute> lhsAttrs, ArrayRef<Attribute> rhsAttrs, SymbolRefAttr targetRef,
2653 SmallVectorImpl<Type> &candidates
2654) {
2655 if (lhsAttrs.size() != rhsAttrs.size()) {
2656 return;
2657 }
2658 for (auto [lhsAttr, rhsAttr] : llvm::zip_equal(lhsAttrs, rhsAttrs)) {
2659 collectRhsTypeVarCandidates(lhsAttr, rhsAttr, targetRef, candidates);
2660 }
2661}
2662
2664static void collectRhsTypeVarCandidates(
2665 Attribute lhsAttr, Attribute rhsAttr, SymbolRefAttr targetRef, SmallVectorImpl<Type> &candidates
2666) {
2667 if (auto rhsTypeAttr = llvm::dyn_cast_if_present<TypeAttr>(rhsAttr)) {
2668 if (auto lhsTypeAttr = llvm::dyn_cast_if_present<TypeAttr>(lhsAttr)) {
2669 collectRhsTypeVarCandidates(
2670 lhsTypeAttr.getValue(), rhsTypeAttr.getValue(), targetRef, candidates
2671 );
2672 }
2673 return;
2674 }
2675
2676 auto lhsArray = llvm::dyn_cast_if_present<ArrayAttr>(lhsAttr);
2677 auto rhsArray = llvm::dyn_cast_if_present<ArrayAttr>(rhsAttr);
2678 if (!lhsArray || !rhsArray || lhsArray.size() != rhsArray.size()) {
2679 return;
2680 }
2681 collectRhsTypeVarCandidates(lhsArray.getValue(), rhsArray.getValue(), targetRef, candidates);
2682}
2683
2689static void collectRhsTypeVarCandidates(
2690 Type lhsTy, Type rhsTy, SymbolRefAttr targetRef, SmallVectorImpl<Type> &candidates
2691) {
2692 if (auto rhsTvar = llvm::dyn_cast<TypeVarType>(rhsTy);
2693 rhsTvar && rhsTvar.getNameRef() == targetRef) {
2694 appendUniqueType(candidates, lhsTy);
2695 return;
2696 }
2697
2698 if (auto lhsArray = llvm::dyn_cast<ArrayType>(lhsTy)) {
2699 if (auto rhsArray = llvm::dyn_cast<ArrayType>(rhsTy)) {
2700 collectRhsTypeVarCandidates(
2701 lhsArray.getElementType(), rhsArray.getElementType(), targetRef, candidates
2702 );
2703 collectRhsTypeVarCandidates(
2704 lhsArray.getDimensionSizes(), rhsArray.getDimensionSizes(), targetRef, candidates
2705 );
2706 }
2707 return;
2708 }
2709
2710 if (auto lhsStruct = llvm::dyn_cast<StructType>(lhsTy)) {
2711 if (auto rhsStruct = llvm::dyn_cast<StructType>(rhsTy)) {
2712 collectRhsTypeVarCandidates(
2713 lhsStruct.getParams(), rhsStruct.getParams(), targetRef, candidates
2714 );
2715 }
2716 return;
2717 }
2718
2719 if (auto lhsPod = llvm::dyn_cast<PodType>(lhsTy)) {
2720 if (auto rhsPod = llvm::dyn_cast<PodType>(rhsTy);
2721 rhsPod && lhsPod.getRecords().size() == rhsPod.getRecords().size()) {
2722 for (auto [lhsRecord, rhsRecord] :
2723 llvm::zip_equal(lhsPod.getRecords(), rhsPod.getRecords())) {
2724 collectRhsTypeVarCandidates(
2725 lhsRecord.getType(), rhsRecord.getType(), targetRef, candidates
2726 );
2727 }
2728 }
2729 return;
2730 }
2731
2732 if (auto lhsFunc = llvm::dyn_cast<FunctionType>(lhsTy)) {
2733 if (auto rhsFunc = llvm::dyn_cast<FunctionType>(rhsTy)) {
2734 for (auto [lhsInput, rhsInput] : llvm::zip_equal(lhsFunc.getInputs(), rhsFunc.getInputs())) {
2735 collectRhsTypeVarCandidates(lhsInput, rhsInput, targetRef, candidates);
2736 }
2737 for (auto [lhsResult, rhsResult] :
2738 llvm::zip_equal(lhsFunc.getResults(), rhsFunc.getResults())) {
2739 collectRhsTypeVarCandidates(lhsResult, rhsResult, targetRef, candidates);
2740 }
2741 }
2742 }
2743}
2744
2746static SmallVector<Type>
2747getConflictingRhsTypeVarCandidates(FunctionType lhs, FunctionType rhs, SymbolRefAttr targetRef) {
2748 SmallVector<Type> candidates;
2749 if (lhs.getNumInputs() != rhs.getNumInputs() || lhs.getNumResults() != rhs.getNumResults()) {
2750 return candidates;
2751 }
2752 for (auto [lhsInput, rhsInput] : llvm::zip_equal(lhs.getInputs(), rhs.getInputs())) {
2753 collectRhsTypeVarCandidates(lhsInput, rhsInput, targetRef, candidates);
2754 }
2755 for (auto [lhsResult, rhsResult] : llvm::zip_equal(lhs.getResults(), rhs.getResults())) {
2756 collectRhsTypeVarCandidates(lhsResult, rhsResult, targetRef, candidates);
2757 }
2758 return candidates;
2759}
2760
2762template <typename CallableOp, typename TargetOp>
2763static LogicalResult
2764emitConflictingInferredTypes(CallableOp callableOp, TargetOp targetOp, StringAttr paramName) {
2765 InFlightDiagnostic diag = callableOp.emitError()
2766 << "conflicting inferred types for @" << paramName.getValue();
2767 SmallVector<Type> candidates = getConflictingRhsTypeVarCandidates(
2768 callableOp.getTypeSignature(), targetOp.getFunctionType(), FlatSymbolRefAttr::get(paramName)
2769 );
2770 if (candidates.size() >= 2) {
2771 diag << ": " << candidates.front();
2772 for (Type candidate : llvm::drop_begin(candidates)) {
2773 diag << " vs " << candidate;
2774 }
2775 }
2776 return diag;
2777}
2778
2780template <typename CallableOp, typename TargetOp>
2781static FailureOr<Attribute> getInferredRhsTemplateArg(
2782 CallableOp callableOp, TargetOp targetOp, const UnificationMap &unifyResult,
2783 StringAttr paramName, StringRef argKind
2784) {
2785 auto inferredIt = unifyResult.find({FlatSymbolRefAttr::get(paramName), Side::RHS});
2786 if (inferredIt == unifyResult.end()) {
2787 return callableOp.emitError() << "could not infer " << argKind
2788 << " template argument for parameter @" << paramName.getValue()
2789 << " from callee signature";
2790 }
2791 if (!inferredIt->second) {
2792 return emitConflictingInferredTypes(callableOp, targetOp, paramName);
2793 }
2794 return inferredIt->second;
2795}
2796
2798template <typename CallableOp, typename TargetOp>
2799static FailureOr<ArrayAttr> getCallSignatureTemplateParams(
2800 CallableOp callableOp, const TemplateInferenceInfo &info, TargetOp targetOp
2801) {
2802 FailureOr<UnificationMap> unifyResult = callableOp.unifyTypeSignature(targetOp.getFunctionType());
2803 if (failed(unifyResult)) {
2804 return callableOp.emitError()
2805 << "could not infer omitted template arguments from callee signature";
2806 }
2807
2808 SmallVector<Attribute> params;
2809 params.reserve(info.oldParamOrder.size());
2810 for (StringAttr paramName : info.oldParamOrder) {
2811 auto replacementIt = info.replacements.find(paramName);
2812 if (replacementIt != info.replacements.end()) {
2813 params.push_back(TypeAttr::get(replacementIt->second.type));
2814 continue;
2815 }
2816 FailureOr<Attribute> inferredAttr =
2817 getInferredRhsTemplateArg(callableOp, targetOp, *unifyResult, paramName, "omitted");
2818 if (failed(inferredAttr)) {
2819 return failure();
2820 }
2821 params.push_back(*inferredAttr);
2822 }
2823 return ArrayAttr::get(callableOp.getContext(), params);
2824}
2825
2828template <typename TargetOp>
2829static bool hasResidualFunctionTvarWildcard(
2830 ArrayAttr callParams, const TemplateInferenceInfo &info, TargetOp targetOp
2831) {
2832 if (!callParams || callParams.size() != info.oldParamOrder.size()) {
2833 return false;
2834 }
2835 for (const auto &entry : info.typeVarParams) {
2836 StringAttr paramName = entry.first;
2837 if (info.replacements.contains(paramName)) {
2838 continue;
2839 }
2840 if (!targetMentionsParam(targetOp, paramName)) {
2841 continue;
2842 }
2843 std::optional<unsigned> index = getParamIndex(info.oldParamOrder, paramName);
2844 assert(index && "eligible type-variable parameter must appear in parameter order");
2845 if (isWildcardTemplateArg(callParams[*index])) {
2846 return true;
2847 }
2848 }
2849 return false;
2850}
2851
2854template <typename CallableOp, typename TargetOp>
2855static FailureOr<ArrayAttr> materializeResidualFunctionTvarWildcards(
2856 CallableOp callableOp, ArrayAttr callParams, const TemplateInferenceInfo &info,
2857 TargetOp targetOp
2858) {
2859 FailureOr<UnificationMap> unifyResult = callableOp.unifyTypeSignature(targetOp.getFunctionType());
2860 if (failed(unifyResult)) {
2861 return callableOp.emitError()
2862 << "could not infer wildcard template arguments from callee signature";
2863 }
2864
2865 SmallVector<Attribute> params(callParams.begin(), callParams.end());
2866 for (const auto &entry : info.typeVarParams) {
2867 StringAttr paramName = entry.first;
2868 if (info.replacements.contains(paramName)) {
2869 continue;
2870 }
2871 if (!targetMentionsParam(targetOp, paramName)) {
2872 continue;
2873 }
2874 std::optional<unsigned> index = getParamIndex(info.oldParamOrder, paramName);
2875 assert(index && "eligible type-variable parameter must appear in parameter order");
2876 if (!isWildcardTemplateArg(params[*index])) {
2877 continue;
2878 }
2879
2880 auto inferredIt = unifyResult->find({FlatSymbolRefAttr::get(paramName), Side::RHS});
2881 if (inferredIt == unifyResult->end()) {
2882 auto proofIt = info.functionReplacements.find(targetOp.getOperation());
2883 if (proofIt == info.functionReplacements.end()) {
2884 continue;
2885 }
2886 auto replacementIt = proofIt->second.find(paramName);
2887 if (replacementIt == proofIt->second.end()) {
2888 continue;
2889 }
2890 params[*index] = TypeAttr::get(replacementIt->second.type);
2891 continue;
2892 }
2893 if (!inferredIt->second) {
2894 return emitConflictingInferredTypes(callableOp, targetOp, paramName);
2895 }
2896 params[*index] = inferredIt->second;
2897 }
2898 return ArrayAttr::get(callableOp.getContext(), params);
2899}
2900
2906template <typename TargetOp>
2907static bool hasResidualFunctionTvar(const TemplateInferenceInfo &info, TargetOp targetOp) {
2908 return llvm::any_of(info.typeVarParams, [&](const auto &entry) {
2909 return !info.replacements.contains(entry.first) && targetMentionsParam(targetOp, entry.first);
2910 });
2911}
2912
2914struct CallableSpecializationInputs {
2915 ArrayAttr params;
2916 bool explicitParams = false;
2917 bool paramsChanged = false;
2918 DenseMap<StringAttr, Type> replacements;
2919};
2920
2922template <typename CallableOp, typename TargetOp>
2923static LogicalResult prepareCallableSpecialization(
2924 CallableOp callableOp, const TemplateInferenceInfo &info, TargetOp targetOp,
2925 CallableSpecializationInputs &inputs, bool &shouldSpecialize
2926) {
2927 shouldSpecialize = false;
2928 inputs.params = {};
2929 inputs.explicitParams = false;
2930 inputs.paramsChanged = false;
2931 inputs.replacements.clear();
2932 inputs.params = callableOp.getTemplateParamsAttr();
2933 inputs.explicitParams = !isNullOrEmpty(inputs.params);
2934 if (isNullOrEmpty(inputs.params)) {
2935 FailureOr<ArrayAttr> inferredParams =
2936 getCallSignatureTemplateParams(callableOp, info, targetOp);
2937 if (failed(inferredParams)) {
2938 return failure();
2939 }
2940 inputs.params = *inferredParams;
2941 inputs.paramsChanged = true;
2942 } else {
2943 inputs.params = expandCurrentTemplateParamsToOriginalOrder(inputs.params, info);
2944 if (hasResidualFunctionTvarWildcard(inputs.params, info, targetOp)) {
2945 FailureOr<ArrayAttr> materializedParams =
2946 materializeResidualFunctionTvarWildcards(callableOp, inputs.params, info, targetOp);
2947 if (failed(materializedParams)) {
2948 return failure();
2949 }
2950 inputs.params = *materializedParams;
2951 inputs.paramsChanged = true;
2952 }
2953 }
2954
2955 inputs.replacements = getConcreteCallSiteReplacements(inputs.params, info, targetOp);
2956 shouldSpecialize = !inputs.replacements.empty();
2957 return success();
2958}
2959
2966static std::string buildTemplateLocalFunctionCloneCacheKey(
2967 StringRef functionName, ArrayRef<StringAttr> oldParamOrder,
2968 const DenseMap<StringAttr, Type> &replacements
2969) {
2970 std::string key;
2971 llvm::raw_string_ostream os(key);
2972 os << functionName.size() << ':' << functionName;
2973 for (StringAttr paramName : oldParamOrder) {
2974 os << '|';
2975 os << paramName.getValue().size() << ':' << paramName.getValue() << '=';
2976 auto replacementIt = replacements.find(paramName);
2977 if (replacementIt == replacements.end()) {
2978 os << '_';
2979 continue;
2980 }
2981
2982 std::string typeText;
2983 llvm::raw_string_ostream typeOs(typeText);
2984 replacementIt->second.print(typeOs);
2985 os << typeText.size() << ':' << typeText;
2986 }
2987 return key;
2988}
2989
2991static SymbolRefAttr getSpecializedFunctionCloneCallee(
2992 SymbolRefAttr originalCallee, StringAttr templateName, StringAttr cloneName
2993) {
2994 SmallVector<FlatSymbolRefAttr> pieces = getPieces(originalCallee);
2995 assert(pieces.size() >= 2 && "callee must include at least template and function names");
2996 pieces.pop_back();
2997 pieces.pop_back();
2998 pieces.push_back(FlatSymbolRefAttr::get(templateName));
2999 pieces.push_back(FlatSymbolRefAttr::get(cloneName));
3000 return asSymbolRefAttr(pieces);
3001}
3002
3004template <typename CallableOp, typename ConfigureCloneFn>
3005static FailureOr<SymbolRefAttr> getOrCreateSpecializedCallableClone(
3006 TemplateOp templateOp, CallableOp callable, SymbolRefAttr originalCallee,
3007 const TemplateInferenceInfo &info, const DenseMap<StringAttr, Type> &replacements,
3008 SymbolTableCollection &tables, llvm::StringMap<SymbolRefAttr> &cloneCallees,
3009 llvm::StringMap<StringAttr> &templateClones, InstantiationLayout &layout, ArrayAttr callParams,
3010 ConfigureCloneFn configureClone
3011) {
3012 DenseMap<Attribute, Attribute> paramNameToConcrete = buildParamNameToConcrete(replacements);
3013 FailureOr<TemplateOp> newTemplate = getOrCreateSpecializedTemplateClone(
3014 templateOp, info.oldParamOrder, paramNameToConcrete, callParams, tables, templateClones,
3015 layout
3016 );
3017 if (failed(newTemplate)) {
3018 return failure();
3019 }
3020
3021 std::string cacheKey = buildTemplateLocalFunctionCloneCacheKey(
3022 callable.getSymName(), info.oldParamOrder, replacements
3023 );
3024 auto cachedCallee = cloneCallees.find(cacheKey);
3025 if (cachedCallee != cloneCallees.end()) {
3026 return cachedCallee->second;
3027 }
3028
3029 SymbolTable &templateSymbols = tables.getSymbolTable(*newTemplate);
3030
3031 auto clone = llvm::cast<CallableOp>(callable.getOperation()->clone());
3032 configureClone(clone);
3033 templateSymbols.insert(clone);
3034
3035 TypeVarReplacementConverter converter(
3036 templateOp.getContext(), info.templatePath, info.oldParamOrder, replacements,
3037 /*trimResolvedParams=*/false
3038 );
3039 if (failed(convertTemplateExprTypesIn(*newTemplate, converter))) {
3040 clone.erase();
3041 return failure();
3042 }
3043 if (failed(convertOperationTypesIn(clone.getOperation(), converter))) {
3044 clone.erase();
3045 return failure();
3046 }
3047 removeIdentityCasts(clone.getOperation());
3048 SymbolRefAttr cloneCallee = getSpecializedFunctionCloneCallee(
3049 originalCallee, newTemplate->getSymNameAttr(), clone.getSymNameAttr()
3050 );
3051 cloneCallees.try_emplace(cacheKey, cloneCallee);
3052 return cloneCallee;
3053}
3054
3056static FailureOr<SymbolRefAttr> getOrCreateSpecializedFunctionClone(
3057 TemplateOp templateOp, FuncDefOp func, SymbolRefAttr originalCallee,
3058 const TemplateInferenceInfo &info, const DenseMap<StringAttr, Type> &replacements,
3059 SymbolTableCollection &tables, llvm::StringMap<SymbolRefAttr> &cloneCallees,
3060 llvm::StringMap<StringAttr> &templateClones, InstantiationLayout &layout, ArrayAttr callParams
3061) {
3062 return getOrCreateSpecializedCallableClone(
3063 templateOp, func, originalCallee, info, replacements, tables, cloneCallees, templateClones,
3064 layout, callParams, [](FuncDefOp) {}
3065 );
3066}
3067
3069static FailureOr<SymbolRefAttr> getOrCreateSpecializedContractClone(
3070 TemplateOp templateOp, verif::ContractOp contract, SymbolRefAttr originalCallee,
3071 SymbolRefAttr specializedTarget, const TemplateInferenceInfo &info,
3072 const DenseMap<StringAttr, Type> &replacements, SymbolTableCollection &tables,
3073 llvm::StringMap<SymbolRefAttr> &cloneCallees, llvm::StringMap<StringAttr> &templateClones,
3074 InstantiationLayout &layout, ArrayAttr callParams
3075) {
3076 return getOrCreateSpecializedCallableClone(
3077 templateOp, contract, originalCallee, info, replacements, tables, cloneCallees,
3078 templateClones, layout, callParams,
3079 [specializedTarget](verif::ContractOp clone) { clone.setTargetAttr(specializedTarget); }
3080 );
3081}
3082
3087static void removeResolvedParams(TemplateInferenceInfo &info) {
3088 for (auto paramOp : llvm::make_early_inc_range(info.templateOp.getConstOps<TemplateParamOp>())) {
3089 auto name = paramOp.getSymNameAttr();
3090 if (info.replacements.contains(name)) {
3091 paramOp.erase();
3092 }
3093 }
3094}
3095
3097template <typename CallableOp>
3098static LogicalResult
3099validateWildcardCallableSignature(CallableOp callableOp, FunctionType targetTy) {
3100 if (succeeded(callableOp.unifyTypeSignature(targetTy))) {
3101 return success();
3102 }
3103 return callableOp.emitError() << "call signature " << callableOp.getTypeSignature()
3104 << " does not match inferred callee signature " << targetTy;
3105}
3106
3108static void updateCallableResultTypesIfNeeded(
3109 CallOp callOp, const TypeVarReplacementConverter &converter, bool &modified
3110) {
3111 for (Value result : callOp.getResults()) {
3112 Type newTy = converter.convertType(result.getType());
3113 if (newTy != result.getType()) {
3114 result.setType(newTy);
3115 modified = true;
3116 }
3117 }
3118}
3119
3121static void
3122updateCallableResultTypesIfNeeded(verif::IncludeOp, const TypeVarReplacementConverter &, bool &) {}
3123
3125template <typename CallableOp>
3126static FailureOr<bool> updateCallableTemplateParamsFor(
3127 ModuleOp module, DenseMap<Operation *, const TypeVarReplacementConverter *> &converters,
3128 SymbolTableCollection &tables
3129) {
3130 bool modified = false;
3131 bool failedConversion = false;
3132 module.walk([&](CallableOp callableOp) {
3133 if (failedConversion) {
3134 return;
3135 }
3136 auto target = callableOp.getCalleeTarget(tables);
3137 if (failed(target)) {
3138 return;
3139 }
3140 auto targetOp = target->get();
3141 TemplateOp parentTemplate = getParentOfType<TemplateOp>(targetOp.getOperation());
3142 if (!parentTemplate) {
3143 return;
3144 }
3145 const TypeVarReplacementConverter *converter = converters.lookup(parentTemplate.getOperation());
3146 if (!converter) {
3147 return;
3148 }
3149
3150 TemplateOp callableTemplate = getParentOfType<TemplateOp>(callableOp.getOperation());
3151 bool resolveTemplateSymbolArgs =
3152 callableTemplate && callableTemplate.getOperation() == parentTemplate.getOperation();
3153 ArrayAttr oldParams = callableOp.getTemplateParamsAttr();
3154 FailureOr<ArrayAttr> newParams = converter->convertTemplateParams(
3155 oldParams, callableOp, resolveTemplateSymbolArgs,
3156 /*allowCurrentTemplateTypeVarSymbols=*/true
3157 );
3158 if (failed(newParams)) {
3159 failedConversion = true;
3160 return;
3161 }
3162 if (converter->hasWildcardForRemovedParam(oldParams)) {
3163 auto inferredTargetTy =
3164 llvm::cast<FunctionType>(converter->convertType(targetOp.getFunctionType()));
3165 if (failed(validateWildcardCallableSignature(callableOp, inferredTargetTy))) {
3166 failedConversion = true;
3167 return;
3168 }
3169 }
3170 if (oldParams != *newParams) {
3171 callableOp.setTemplateParamsAttr(*newParams);
3172 modified = true;
3173 }
3174
3175 updateCallableResultTypesIfNeeded(callableOp, *converter, modified);
3176 });
3177 if (failedConversion) {
3178 return failure();
3179 }
3180 return modified;
3181}
3182
3190static FailureOr<bool> updateCallableTemplateParams(
3191 ModuleOp module, DenseMap<Operation *, const TypeVarReplacementConverter *> &converters
3192) {
3193 SymbolTableCollection tables;
3194 FailureOr<bool> callsModified =
3195 updateCallableTemplateParamsFor<CallOp>(module, converters, tables);
3196 if (failed(callsModified)) {
3197 return failure();
3198 }
3199 FailureOr<bool> includesModified =
3200 updateCallableTemplateParamsFor<verif::IncludeOp>(module, converters, tables);
3201 if (failed(includesModified)) {
3202 return failure();
3203 }
3204 return *callsModified || *includesModified;
3205}
3206
3213class ReferencedStructTemplateParamConverter {
3215 MLIRContext *ctx_;
3217 ModuleOp module_;
3219 SymbolTableCollection &tables_;
3221 DenseMap<Operation *, const TypeVarReplacementConverter *> &converters_;
3223 Operation *diagnosticOp_ = nullptr;
3225 bool hasFailure = false;
3226
3227public:
3228 ReferencedStructTemplateParamConverter(
3229 MLIRContext *ctx, ModuleOp module, SymbolTableCollection &tables,
3230 DenseMap<Operation *, const TypeVarReplacementConverter *> &converters
3231 )
3232 : ctx_(ctx), module_(module), tables_(tables), converters_(converters) {}
3233
3235 void startOperation(Operation *op) {
3236 diagnosticOp_ = op;
3237 hasFailure = false;
3238 }
3239
3241 bool hadFailure() const { return hasFailure; }
3242
3244 Type convertType(Type ty) {
3245 if (!ty || hasFailure) {
3246 return ty;
3247 }
3248 if (auto arrTy = llvm::dyn_cast<ArrayType>(ty)) {
3249 return convertArrayElementType(arrTy, [this](Type elemTy) { return convertType(elemTy); });
3250 }
3251 if (auto structTy = llvm::dyn_cast<StructType>(ty)) {
3252 return convertStructType(structTy);
3253 }
3254 if (auto podTy = llvm::dyn_cast<PodType>(ty)) {
3255 return convertPodType(podTy, ctx_, *this);
3256 }
3257 if (auto funcTy = llvm::dyn_cast<FunctionType>(ty)) {
3258 return convertFunctionType(funcTy, *this);
3259 }
3260 return ty;
3261 }
3262
3264 Attribute convertAttr(Attribute attr) {
3265 if (hasFailure) {
3266 return attr;
3267 }
3268 return convertTypeOrArrayAttr(attr, ctx_, [this](Type ty) {
3269 return convertType(ty);
3270 }, [this](Attribute nested) { return convertAttr(nested); });
3271 }
3272
3273private:
3275 StructType convertStructType(StructType structTy) {
3276 ArrayAttr params = structTy.getParams();
3277 if (!params) {
3278 return structTy;
3279 }
3280
3281 SmallVector<Attribute> newParams;
3282 bool changed = false;
3283 for (Attribute attr : params.getValue()) {
3284 Attribute newAttr = convertAttr(attr);
3285 newParams.push_back(newAttr);
3286 changed |= newAttr != attr;
3287 if (hasFailure) {
3288 return structTy;
3289 }
3290 }
3291 StructType convertedTy =
3292 changed ? getStructTypeWithParams(structTy.getNameRef(), ctx_, newParams) : structTy;
3293
3294 FailureOr<SymbolLookupResult<StructDefOp>> lookup = lookupTopLevelSymbol<StructDefOp>(
3295 tables_, convertedTy.getNameRef(), module_, /*reportMissing=*/false
3296 );
3297 if (failed(lookup)) {
3298 return convertedTy;
3299 }
3300 TemplateOp parentTemplate = getParentOfType<TemplateOp>(lookup->get().getOperation());
3301 if (!parentTemplate) {
3302 return convertedTy;
3303 }
3304 const TypeVarReplacementConverter *converter =
3305 converters_.lookup(parentTemplate.getOperation());
3306 if (!converter) {
3307 return convertedTy;
3308 }
3309
3310 TemplateOp useTemplate = getParentOfType<TemplateOp>(diagnosticOp_);
3311 bool resolveTemplateSymbolArgs =
3312 useTemplate && useTemplate.getOperation() == parentTemplate.getOperation();
3313 FailureOr<ArrayAttr> trimmedParams = converter->convertTemplateParams(
3314 convertedTy.getParams(), diagnosticOp_, resolveTemplateSymbolArgs,
3315 /*allowCurrentTemplateTypeVarSymbols=*/false
3316 );
3317 if (failed(trimmedParams)) {
3318 hasFailure = true;
3319 return convertedTy;
3320 }
3321 if (convertedTy.getParams() == *trimmedParams) {
3322 return convertedTy;
3323 }
3324 return getStructTypeWithParams(convertedTy.getNameRef(), *trimmedParams);
3325 }
3326};
3327
3329static FailureOr<bool> updateStructTemplateParams(
3330 ModuleOp module, DenseMap<Operation *, const TypeVarReplacementConverter *> &converters
3331) {
3332 SymbolTableCollection tables;
3333 ReferencedStructTemplateParamConverter converter(module.getContext(), module, tables, converters);
3334 return convertOperationTypesInAndTrack(module.getOperation(), converter);
3335}
3336
3338static LogicalResult instantiateConcreteStructUses(ModuleOp module) {
3339 SymbolTableCollection tables;
3340 ConcreteStructInstantiationConverter converter(module.getContext(), module, tables);
3341 return convertOperationTypesIn(module.getOperation(), converter);
3342}
3343
3345static TemplateOp
3346getContractTargetTemplate(verif::ContractOp contract, SymbolTableCollection &tables) {
3347 if (FailureOr<SymbolLookupResult<FuncDefOp>> funcTarget = contract.getFuncTarget(tables);
3348 succeeded(funcTarget)) {
3349 return getParentOfType<TemplateOp>(funcTarget->get().getOperation());
3350 }
3351 if (FailureOr<SymbolLookupResult<StructDefOp>> structTarget = contract.getStructTarget(tables);
3352 succeeded(structTarget)) {
3353 return getParentOfType<TemplateOp>(structTarget->get().getOperation());
3354 }
3355 return {};
3356}
3357
3364static LogicalResult updateExternalContractTemplateParams(
3365 ModuleOp module, DenseMap<Operation *, const TypeVarReplacementConverter *> &converters
3366) {
3367 bool failedConversion = false;
3368 SymbolTableCollection tables;
3369 module.walk([&](verif::ContractOp contract) {
3370 if (failedConversion) {
3371 return;
3372 }
3373 TemplateOp targetTemplate = getContractTargetTemplate(contract, tables);
3374 if (!targetTemplate) {
3375 return;
3376 }
3377 if (getParentOfType<TemplateOp>(contract.getOperation()) == targetTemplate) {
3378 return;
3379 }
3380 const TypeVarReplacementConverter *converter = converters.lookup(targetTemplate.getOperation());
3381 if (!converter) {
3382 return;
3383 }
3384
3385 WalkResult validationResult = contract.walk([converter](Operation *op) {
3386 return failed(converter->validateOperation(op)) ? WalkResult::interrupt()
3387 : WalkResult::advance();
3388 });
3389 if (validationResult.wasInterrupted() ||
3390 failed(convertOperationTypesIn(contract.getOperation(), *converter))) {
3391 failedConversion = true;
3392 return;
3393 }
3394 removeIdentityCasts(contract.getOperation());
3395 });
3396 return failure(failedConversion);
3397}
3398
3407static LogicalResult specializeFunctionLocalCallables(
3408 ModuleOp module, DenseMap<Operation *, const TemplateInferenceInfo *> &infoByTemplate,
3409 SpecializedCallableCloneCache &functionCloneCache,
3410 SpecializedCallableCloneCache &contractCloneCache,
3411 SpecializedTemplateCloneCache &templateCloneCache
3412) {
3413 bool failedClone = false;
3414 SymbolTableCollection tables;
3415 module.walk([&](CallOp callOp) {
3416 if (failedClone) {
3417 return;
3418 }
3419 FailureOr<SymbolLookupResult<FuncDefOp>> target = callOp.getCalleeTarget(tables);
3420 if (failed(target)) {
3421 return;
3422 }
3423 FuncDefOp targetFunc = target->get();
3424 auto parentTemplate = llvm::dyn_cast_or_null<TemplateOp>(targetFunc->getParentOp());
3425 if (!parentTemplate) {
3426 return;
3427 }
3428 const TemplateInferenceInfo *info = infoByTemplate.lookup(parentTemplate.getOperation());
3429 if (!info) {
3430 return;
3431 }
3432 if (!hasResidualFunctionTvar(*info, targetFunc)) {
3433 return;
3434 }
3435
3436 CallableSpecializationInputs inputs;
3437 bool shouldSpecialize = false;
3438 if (failed(
3439 prepareCallableSpecialization(callOp, *info, targetFunc, inputs, shouldSpecialize)
3440 )) {
3441 failedClone = true;
3442 return;
3443 }
3444 if (!shouldSpecialize) {
3445 return;
3446 }
3447 DenseMap<StringAttr, Type> inferredReplacements =
3448 getFunctionProofReplacements(*info, targetFunc);
3449 if (failed(diagnoseCallParamsMismatch(
3450 callOp.getOperation(), inputs.params, info->oldParamOrder, inferredReplacements,
3451 &info->replacements, /*explicitCallParams=*/inputs.explicitParams
3452 ))) {
3453 failedClone = true;
3454 return;
3455 }
3456
3457 InstantiationLayout layout;
3458 FailureOr<SymbolRefAttr> cloneCallee = getOrCreateSpecializedFunctionClone(
3459 parentTemplate, targetFunc, callOp.getCalleeAttr(), *info, inputs.replacements, tables,
3460 functionCloneCache[parentTemplate.getOperation()],
3461 templateCloneCache[parentTemplate.getOperation()], layout, inputs.params
3462 );
3463 if (failed(cloneCallee)) {
3464 failedClone = true;
3465 return;
3466 }
3467
3468 callOp.setCalleeAttr(*cloneCallee);
3470 });
3471 module.walk([&](verif::IncludeOp includeOp) {
3472 if (failedClone) {
3473 return;
3474 }
3475 FailureOr<SymbolLookupResult<verif::ContractOp>> target = includeOp.getCalleeTarget(tables);
3476 if (failed(target)) {
3477 return;
3478 }
3479 verif::ContractOp targetContract = target->get();
3480 auto parentTemplate = llvm::dyn_cast_or_null<TemplateOp>(targetContract->getParentOp());
3481 if (!parentTemplate) {
3482 return;
3483 }
3484 const TemplateInferenceInfo *info = infoByTemplate.lookup(parentTemplate.getOperation());
3485 if (!info) {
3486 return;
3487 }
3488 if (!hasResidualFunctionTvar(*info, targetContract)) {
3489 return;
3490 }
3491
3492 FailureOr<SymbolLookupResult<FuncDefOp>> targetFuncResult =
3493 targetContract.getFuncTarget(tables);
3494 if (failed(targetFuncResult)) {
3495 return;
3496 }
3497 FuncDefOp targetFunc = targetFuncResult->get();
3498 if (getParentOfType<TemplateOp>(targetFunc.getOperation()) != parentTemplate) {
3499 return;
3500 }
3501
3502 CallableSpecializationInputs inputs;
3503 bool shouldSpecialize = false;
3504 if (failed(prepareCallableSpecialization(
3505 includeOp, *info, targetContract, inputs, shouldSpecialize
3506 ))) {
3507 failedClone = true;
3508 return;
3509 }
3510 if (!shouldSpecialize) {
3511 return;
3512 }
3513 DenseMap<StringAttr, Type> inferredReplacements =
3514 getFunctionProofReplacements(*info, targetFunc);
3515 if (failed(diagnoseCallParamsMismatch(
3516 includeOp.getOperation(), inputs.params, info->oldParamOrder, inferredReplacements,
3517 &info->replacements, /*explicitCallParams=*/inputs.explicitParams
3518 ))) {
3519 failedClone = true;
3520 return;
3521 }
3522
3523 InstantiationLayout targetLayout;
3524 FailureOr<SymbolRefAttr> specializedTarget = getOrCreateSpecializedFunctionClone(
3525 parentTemplate, targetFunc, targetContract.getTargetAttr(), *info, inputs.replacements,
3526 tables, functionCloneCache[parentTemplate.getOperation()],
3527 templateCloneCache[parentTemplate.getOperation()], targetLayout, inputs.params
3528 );
3529 if (failed(specializedTarget)) {
3530 failedClone = true;
3531 return;
3532 }
3533 InstantiationLayout contractLayout;
3534 FailureOr<SymbolRefAttr> cloneCallee = getOrCreateSpecializedContractClone(
3535 parentTemplate, targetContract, includeOp.getCalleeAttr(), *specializedTarget, *info,
3536 inputs.replacements, tables, contractCloneCache[parentTemplate.getOperation()],
3537 templateCloneCache[parentTemplate.getOperation()], contractLayout, inputs.params
3538 );
3539 if (failed(cloneCallee)) {
3540 failedClone = true;
3541 return;
3542 }
3543
3544 includeOp.setCalleeAttr(*cloneCallee);
3545 includeOp.setTemplateParamsAttr(contractLayout.rewrittenCallParams);
3546 });
3547 return failure(failedClone);
3548}
3549
3551static bool sameReplacementTypes(
3552 const DenseMap<StringAttr, InferredType> &lhs, const DenseMap<StringAttr, InferredType> &rhs
3553) {
3554 if (lhs.size() != rhs.size()) {
3555 return false;
3556 }
3557 for (const auto &entry : lhs) {
3558 auto rhsIt = rhs.find(entry.first);
3559 if (rhsIt == rhs.end() || rhsIt->second.type != entry.second.type) {
3560 return false;
3561 }
3562 }
3563 return true;
3564}
3565
3572static LogicalResult collectContractTargetFunctionInferences(
3573 TemplateInferenceInfo &info, verif::ContractOp contract, ModuleOp module,
3574 DenseMap<Operation *, TemplateInferenceInfo *> *infoByTemplate, SymbolTableCollection &tables,
3575 bool &changed
3576) {
3577 FailureOr<SymbolLookupResult<FuncDefOp>> target = contract.getFuncTarget(tables);
3578 if (failed(target)) {
3579 return success();
3580 }
3581 FuncDefOp targetFunc = target->get();
3582 if (getParentOfType<TemplateOp>(targetFunc.getOperation()) != info.templateOp) {
3583 return success();
3584 }
3585
3586 DenseMap<StringAttr, InferredType> funcReplacements;
3587 auto funcIt = info.functionReplacements.find(targetFunc.getOperation());
3588 if (funcIt != info.functionReplacements.end()) {
3589 funcReplacements = funcIt->second;
3590 }
3591 DenseMap<StringAttr, InferredType> oldFuncReplacements = funcReplacements;
3592
3593 TypeVarInferenceCollector collector(info, funcReplacements);
3594 if (failed(collector.collect(contract.getOperation()))) {
3595 return failure();
3596 }
3597 if (infoByTemplate && failed(collector.collectStructTemplateParamInferences(
3598 contract.getOperation(), module, *infoByTemplate, tables
3599 ))) {
3600 return failure();
3601 }
3602
3603 if (!sameReplacementTypes(oldFuncReplacements, funcReplacements)) {
3604 changed = true;
3605 }
3606 if (funcReplacements.empty()) {
3607 info.functionReplacements.erase(targetFunc.getOperation());
3608 } else {
3609 info.functionReplacements[targetFunc.getOperation()] = std::move(funcReplacements);
3610 }
3611 return success();
3612}
3613
3620static LogicalResult collectContractTargetStructInferences(
3621 TemplateInferenceInfo &info, verif::ContractOp contract, ModuleOp module,
3622 DenseMap<Operation *, TemplateInferenceInfo *> *infoByTemplate, SymbolTableCollection &tables,
3623 bool &changed
3624) {
3625 FailureOr<SymbolLookupResult<StructDefOp>> target = contract.getStructTarget(tables);
3626 if (failed(target)) {
3627 return success();
3628 }
3629 StructDefOp targetStruct = target->get();
3630 if (getParentOfType<TemplateOp>(targetStruct.getOperation()) != info.templateOp) {
3631 return success();
3632 }
3633
3634 DenseMap<StringAttr, InferredType> oldTemplateScopeReplacements = info.templateScopeReplacements;
3635
3636 TypeVarInferenceCollector collector(info, info.templateScopeReplacements);
3637 FunctionType contractTy = contract.getFunctionType();
3638 if (contractTy.getNumInputs() > 0) {
3639 StructType targetSelfTy = targetStruct.getType();
3640 if (auto contractSelfTy = llvm::dyn_cast<StructType>(contractTy.getInput(0));
3641 contractSelfTy && contractSelfTy.getNameRef() == targetSelfTy.getNameRef() &&
3642 failed(collector.collectTypeInferences(contractSelfTy, targetSelfTy, contract.getLoc()))) {
3643 return failure();
3644 }
3645 }
3646 if (failed(collector.collect(contract.getOperation()))) {
3647 return failure();
3648 }
3649 if (infoByTemplate && failed(collector.collectStructTemplateParamInferences(
3650 contract.getOperation(), module, *infoByTemplate, tables
3651 ))) {
3652 return failure();
3653 }
3654
3655 if (!sameReplacementTypes(oldTemplateScopeReplacements, info.templateScopeReplacements)) {
3656 changed = true;
3657 }
3658 return success();
3659}
3660
3670static LogicalResult recomputeTemplateWideReplacements(TemplateInferenceInfo &info) {
3671 DenseMap<StringAttr, unsigned> mentionCounts;
3672 DenseMap<StringAttr, unsigned> proofCounts;
3673 DenseMap<StringAttr, InferredType> commonReplacements;
3674 DenseSet<StringAttr> incompatibleReplacements;
3675
3676 for (FuncDefOp func : walkCollect<FuncDefOp>(info.templateOp)) {
3677 for (const auto &entry : info.typeVarParams) {
3678 if (funcMentionsParam(func, entry.first)) {
3679 ++mentionCounts[entry.first];
3680 }
3681 }
3682
3683 auto funcIt = info.functionReplacements.find(func.getOperation());
3684 if (funcIt == info.functionReplacements.end()) {
3685 continue;
3686 }
3687 for (const auto &entry : funcIt->second) {
3688 ++proofCounts[entry.first];
3689 auto commonIt = commonReplacements.find(entry.first);
3690 if (commonIt == commonReplacements.end()) {
3691 commonReplacements.try_emplace(entry.first, entry.second);
3692 } else if (commonIt->second.type != entry.second.type) {
3693 incompatibleReplacements.insert(entry.first);
3694 }
3695 }
3696 }
3697
3698 for (TemplateExprOp expr : walkCollect<TemplateExprOp>(info.templateOp)) {
3699 for (const auto &entry : info.typeVarParams) {
3700 if (exprMentionsParam(expr, entry.first)) {
3701 ++mentionCounts[entry.first];
3702 }
3703 }
3704 }
3705
3706 info.replacements = info.templateScopeReplacements;
3707 for (const auto &entry : info.templateScopeReplacements) {
3708 auto commonIt = commonReplacements.find(entry.first);
3709 if (commonIt != commonReplacements.end() && commonIt->second.type != entry.second.type) {
3710 InFlightDiagnostic diag = emitError(entry.second.loc)
3711 << "conflicting inferred type for template parameter @"
3712 << entry.first.getValue() << ": " << commonIt->second.type << " vs "
3713 << entry.second.type;
3714 diag.attachNote(commonIt->second.loc) << "previous function-local inference here";
3715 return diag;
3716 }
3717 }
3718
3719 for (const auto &entry : commonReplacements) {
3720 StringAttr paramName = entry.first;
3721 if (info.replacements.contains(paramName)) {
3722 continue;
3723 }
3724 if (!hasUncoveredNonFunctionMention(
3725 info.templateOp, paramName, entry.second.type, info.functionReplacements
3726 ) &&
3727 !incompatibleReplacements.contains(paramName) &&
3728 proofCounts.lookup(paramName) == mentionCounts.lookup(paramName)) {
3729 info.replacements.try_emplace(paramName, entry.second);
3730 }
3731 }
3732 return success();
3733}
3734
3737static LogicalResult inferStructTemplateParamUses(
3738 ModuleOp module, MutableArrayRef<TemplateInferenceInfo> templateInfos,
3739 DenseMap<Operation *, TemplateInferenceInfo *> &infoByTemplate
3740) {
3741 bool changed = false;
3742 do {
3743 changed = false;
3744 SymbolTableCollection tables;
3745 for (TemplateInferenceInfo &info : templateInfos) {
3746 for (FuncDefOp func : walkCollect<FuncDefOp>(info.templateOp)) {
3747 DenseMap<StringAttr, InferredType> funcReplacements;
3748 auto funcIt = info.functionReplacements.find(func.getOperation());
3749 if (funcIt != info.functionReplacements.end()) {
3750 funcReplacements = funcIt->second;
3751 }
3752 DenseMap<StringAttr, InferredType> oldFuncReplacements = funcReplacements;
3753
3754 TypeVarInferenceCollector collector(info, funcReplacements);
3755 if (failed(collector.collect(func.getOperation())) ||
3756 failed(collector.collectStructTemplateParamInferences(
3757 func.getOperation(), module, infoByTemplate, tables
3758 ))) {
3759 return failure();
3760 }
3761
3762 if (!sameReplacementTypes(oldFuncReplacements, funcReplacements)) {
3763 changed = true;
3764 }
3765 if (funcReplacements.empty()) {
3766 info.functionReplacements.erase(func.getOperation());
3767 } else {
3768 info.functionReplacements[func.getOperation()] = std::move(funcReplacements);
3769 }
3770 }
3771
3772 for (verif::ContractOp contract : walkCollect<verif::ContractOp>(info.templateOp)) {
3773 if (failed(collectContractTargetFunctionInferences(
3774 info, contract, module, &infoByTemplate, tables, changed
3775 )) ||
3776 failed(collectContractTargetStructInferences(
3777 info, contract, module, &infoByTemplate, tables, changed
3778 ))) {
3779 return failure();
3780 }
3781 }
3782
3783 for (TemplateExprOp expr : walkCollect<TemplateExprOp>(info.templateOp)) {
3784 DenseMap<StringAttr, InferredType> oldTemplateScopeReplacements =
3785 info.templateScopeReplacements;
3786
3787 TypeVarInferenceCollector collector(info, info.templateScopeReplacements);
3788 if (failed(collector.collect(expr.getOperation())) ||
3789 failed(collector.collectStructTemplateParamInferences(
3790 expr.getOperation(), module, infoByTemplate, tables
3791 ))) {
3792 return failure();
3793 }
3794
3795 if (!sameReplacementTypes(oldTemplateScopeReplacements, info.templateScopeReplacements)) {
3796 changed = true;
3797 }
3798 }
3799
3800 DenseMap<StringAttr, InferredType> oldTemplateScopeReplacements =
3801 info.templateScopeReplacements;
3802 TypeVarInferenceCollector collector(info, info.templateScopeReplacements);
3803 WalkResult nonFunctionResult = info.templateOp.walk([&](Operation *op) {
3804 if (llvm::isa<FuncDefOp, TemplateExprOp, verif::ContractOp>(op) ||
3806 return WalkResult::advance();
3807 }
3808 if (failed(collector.collectOperationStructTemplateParamInferences(
3809 op, module, infoByTemplate, tables
3810 ))) {
3811 return WalkResult::interrupt();
3812 }
3813 return WalkResult::advance();
3814 });
3815 if (nonFunctionResult.wasInterrupted()) {
3816 return failure();
3817 }
3818 if (!sameReplacementTypes(oldTemplateScopeReplacements, info.templateScopeReplacements)) {
3819 changed = true;
3820 }
3821
3822 DenseMap<StringAttr, InferredType> oldReplacements = info.replacements;
3823 if (failed(recomputeTemplateWideReplacements(info))) {
3824 return failure();
3825 }
3826 if (!sameReplacementTypes(oldReplacements, info.replacements)) {
3827 changed = true;
3828 }
3829 }
3830 } while (changed);
3831 return success();
3832}
3833
3836static LogicalResult collectExternalContractTargetInferences(
3837 ModuleOp module, MutableArrayRef<TemplateInferenceInfo> templateInfos,
3838 DenseMap<Operation *, TemplateInferenceInfo *> &infoByTemplate
3839) {
3840 bool changed = false;
3841 do {
3842 changed = false;
3843 bool failedCollection = false;
3844 SymbolTableCollection tables;
3845 module.walk([&](verif::ContractOp contract) {
3846 TemplateOp targetTemplate = getContractTargetTemplate(contract, tables);
3847 if (!targetTemplate) {
3848 return;
3849 }
3850 if (getParentOfType<TemplateOp>(contract.getOperation()) == targetTemplate) {
3851 return;
3852 }
3853 TemplateInferenceInfo *info = infoByTemplate.lookup(targetTemplate.getOperation());
3854 if (!info) {
3855 return;
3856 }
3857 if (failed(collectContractTargetFunctionInferences(
3858 *info, contract, module, &infoByTemplate, tables, changed
3859 )) ||
3860 failed(collectContractTargetStructInferences(
3861 *info, contract, module, &infoByTemplate, tables, changed
3862 ))) {
3863 failedCollection = true;
3864 }
3865 });
3866 if (failedCollection) {
3867 return failure();
3868 }
3869
3870 for (TemplateInferenceInfo &info : templateInfos) {
3871 DenseMap<StringAttr, InferredType> oldReplacements = info.replacements;
3872 if (failed(recomputeTemplateWideReplacements(info))) {
3873 return failure();
3874 }
3875 if (!sameReplacementTypes(oldReplacements, info.replacements)) {
3876 changed = true;
3877 }
3878 }
3879 } while (changed);
3880 return success();
3881}
3882
3890static FailureOr<TemplateInferenceInfo> buildInfo(TemplateOp templateOp) {
3891 TemplateInferenceInfo info;
3892 info.templateOp = templateOp;
3893 FailureOr<SymbolRefAttr> templatePath =
3894 getPathFromRoot(llvm::cast<SymbolOpInterface>(templateOp.getOperation()));
3895 if (failed(templatePath)) {
3896 return failure();
3897 }
3898 info.templatePath = getStringPieces(*templatePath);
3899 for (TemplateParamOp paramOp : templateOp.getConstOps<TemplateParamOp>()) {
3900 auto name = paramOp.getSymNameAttr();
3901 info.oldParamOrder.push_back(name);
3902 if (isTypeVarParam(paramOp)) {
3903 info.typeVarParams.try_emplace(name, paramOp);
3904 }
3905 }
3906
3907 for (FuncDefOp func : walkCollect<FuncDefOp>(templateOp)) {
3908 DenseMap<StringAttr, InferredType> funcReplacements;
3909 if (failed(TypeVarInferenceCollector(info, funcReplacements).collect(func.getOperation()))) {
3910 return failure();
3911 }
3912 if (!funcReplacements.empty()) {
3913 info.functionReplacements.try_emplace(func.getOperation(), funcReplacements);
3914 }
3915 }
3916
3917 for (TemplateExprOp expr : walkCollect<TemplateExprOp>(templateOp)) {
3918 if (failed(TypeVarInferenceCollector(info, info.templateScopeReplacements)
3919 .collect(expr.getOperation()))) {
3920 return failure();
3921 }
3922 }
3923
3924 bool changed = false;
3925 SymbolTableCollection tables;
3926 ModuleOp module = templateOp->getParentOfType<ModuleOp>();
3927 for (verif::ContractOp contract : walkCollect<verif::ContractOp>(templateOp)) {
3928 if (failed(collectContractTargetFunctionInferences(
3929 info, contract, module, /*infoByTemplate=*/nullptr, tables, changed
3930 )) ||
3931 failed(collectContractTargetStructInferences(
3932 info, contract, module, /*infoByTemplate=*/nullptr, tables, changed
3933 ))) {
3934 return failure();
3935 }
3936 }
3937
3938 if (failed(recomputeTemplateWideReplacements(info))) {
3939 return failure();
3940 }
3941 return info;
3942}
3943
3951class PassImpl : public llzk::polymorphic::impl::TypeVarInferencePassBase<PassImpl> {
3952public:
3953 using Base = TypeVarInferencePassBase<PassImpl>;
3954 using Base::Base;
3955
3956private:
3957 void runOnOperation() override {
3958 ModuleOp module = getOperation();
3959
3960 // Collect all template-local inferences before mutating IR. Conflicts are
3961 // reported during collection and abort the pass.
3962 // Note: SmallVector doesn't work because the element size is too large.
3963 std::vector<TemplateInferenceInfo> templateInfos;
3964 WalkResult collectResult = module.walk([&templateInfos](TemplateOp templateOp) {
3965 if (templateOp.getConstOps<TemplateParamOp>().empty()) {
3966 return WalkResult::advance();
3967 }
3968 FailureOr<TemplateInferenceInfo> info = buildInfo(templateOp);
3969 if (failed(info)) {
3970 return WalkResult::interrupt();
3971 }
3972 templateInfos.push_back(std::move(*info));
3973 return WalkResult::advance();
3974 });
3975 if (collectResult.wasInterrupted()) {
3976 signalPassFailure();
3977 return;
3978 }
3979 if (templateInfos.empty()) {
3980 return;
3981 }
3982
3983 DenseMap<Operation *, TemplateInferenceInfo *> mutableInfoByTemplate;
3984 for (TemplateInferenceInfo &info : templateInfos) {
3985 mutableInfoByTemplate.try_emplace(info.templateOp.getOperation(), &info);
3986 }
3987 if (failed(
3988 collectExternalContractTargetInferences(module, templateInfos, mutableInfoByTemplate)
3989 )) {
3990 signalPassFailure();
3991 return;
3992 }
3993 if (failed(inferStructTemplateParamUses(module, templateInfos, mutableInfoByTemplate))) {
3994 signalPassFailure();
3995 return;
3996 }
3997
3998 SmallVector<TemplateInferenceInfo *> rewrites;
3999 DenseMap<Operation *, const TemplateInferenceInfo *> infoByTemplate;
4000 for (TemplateInferenceInfo &info : templateInfos) {
4001 infoByTemplate.try_emplace(info.templateOp.getOperation(), &info);
4002 if (!info.replacements.empty() || !info.functionReplacements.empty()) {
4003 rewrites.push_back(&info);
4004 }
4005 }
4006
4007 // Rewrite each affected template in place, but keep the converters alive so
4008 // call-site rewriting can use the same positional template-parameter map.
4009 SmallVector<std::unique_ptr<TypeVarReplacementConverter>> converterStorage;
4010 DenseMap<Operation *, const TypeVarReplacementConverter *> convertersByTemplate;
4011 for (TemplateInferenceInfo *info : rewrites) {
4012 DenseMap<StringAttr, Type> replacements;
4013 for (const auto &entry : info->replacements) {
4014 replacements.try_emplace(entry.first, entry.second.type);
4015 }
4016 auto converter = std::make_unique<TypeVarReplacementConverter>(
4017 module.getContext(), info->templatePath, info->oldParamOrder, replacements
4018 );
4019 convertersByTemplate.try_emplace(info->templateOp.getOperation(), converter.get());
4020 converterStorage.push_back(std::move(converter));
4021 }
4022
4023 // Clone concrete call-site instantiations before trimming template
4024 // arguments; the clone decision needs the original explicit arguments.
4025 SpecializedCallableCloneCache functionCloneCache;
4026 SpecializedCallableCloneCache contractCloneCache;
4027 SpecializedTemplateCloneCache templateCloneCache;
4028 if (failed(specializeFunctionLocalCallables(
4029 module, infoByTemplate, functionCloneCache, contractCloneCache, templateCloneCache
4030 ))) {
4031 signalPassFailure();
4032 return;
4033 }
4034
4035 // Trim call/include argument lists before rewriting caller bodies, so a
4036 // caller-local type variable in an explicit argument for an erased callee
4037 // parameter is not rewritten in the caller's scope before it can be dropped.
4038 if (failed(updateCallableTemplateParams(module, convertersByTemplate))) {
4039 signalPassFailure();
4040 return;
4041 }
4042
4043 for (TemplateInferenceInfo *info : rewrites) {
4044 const TypeVarReplacementConverter *converter =
4045 convertersByTemplate.lookup(info->templateOp.getOperation());
4046 assert(converter && "rewritten template must have a converter");
4047 WalkResult validationResult = info->templateOp.walk([converter](Operation *op) {
4048 return failed(converter->validateOperation(op)) ? WalkResult::interrupt()
4049 : WalkResult::advance();
4050 });
4051 if (validationResult.wasInterrupted()) {
4052 signalPassFailure();
4053 return;
4054 }
4055 if (failed(convertOperationTypesIn(info->templateOp.getOperation(), *converter))) {
4056 signalPassFailure();
4057 return;
4058 }
4059 removeIdentityCasts(info->templateOp.getOperation());
4060 }
4061
4062 if (failed(updateExternalContractTemplateParams(module, convertersByTemplate))) {
4063 signalPassFailure();
4064 return;
4065 }
4066
4067 // Calls that still target rewritten templates are updated after all target
4068 // templates have their converters registered.
4069 if (failed(updateCallableTemplateParams(module, convertersByTemplate))) {
4070 signalPassFailure();
4071 return;
4072 }
4073 // Re-run specialization after rewriting because template bodies may now
4074 // contain concrete forwarded calls into otherwise unconstrained templates.
4075 if (failed(specializeFunctionLocalCallables(
4076 module, infoByTemplate, functionCloneCache, contractCloneCache, templateCloneCache
4077 ))) {
4078 signalPassFailure();
4079 return;
4080 }
4081 if (failed(updateStructTemplateParams(module, convertersByTemplate))) {
4082 signalPassFailure();
4083 return;
4084 }
4085
4086 // Erase resolved parameters after all argument-list conversion has used
4087 // the original order, but before concrete struct instantiation verifies
4088 // rewritten owned-struct arities.
4089 for (TemplateInferenceInfo *info : rewrites) {
4090 removeResolvedParams(*info);
4091 }
4092
4093 if (failed(instantiateConcreteStructUses(module))) {
4094 signalPassFailure();
4095 return;
4096 }
4097 }
4098};
4099
4100} // namespace
Common private implementation for poly dialect passes.
This file defines methods symbol lookup across LLZK operations and included files.
Helper for converting between linear and multi-dimensional indexing with checks to ensure indices are...
static ArrayIndexGen from(ArrayType)
Construct new ArrayIndexGen. Will assert if hasStaticShape() is false.
ArrayType cloneWith(std::optional<::llvm::ArrayRef< int64_t > > shape, ::mlir::Type elementType) const
Clone this type with the given shape and element type.
::mlir::Type getElementType() const
::llvm::ArrayRef<::mlir::Attribute > getDimensionSizes() const
::mlir::TypedValue<::llzk::array::ArrayType > getResult()
Definition Ops.h.inc:408
::mlir::Operation::operand_range getElements()
Definition Ops.h.inc:388
StructType getType(::std::optional<::mlir::ArrayAttr > constParams={})
Gets the StructType representing this struct.
::mlir::SymbolRefAttr getFullyQualifiedName()
Return the full name for this struct from the root module, including any surrounding module scopes.
Definition Ops.cpp:220
::mlir::StringAttr getSymNameAttr()
Definition Ops.h.inc:1207
::mlir::SymbolRefAttr getNameRef() const
static StructType get(::mlir::SymbolRefAttr structName)
Definition Types.cpp.inc:79
::mlir::FailureOr< SymbolLookupResult< StructDefOp > > getDefinition(::mlir::SymbolTableCollection &symbolTable, ::mlir::Operation *op, bool reportMissing=true) const
Gets the struct op that defines this struct.
Definition Types.cpp:26
::mlir::ArrayAttr getParams() const
::mlir::SymbolRefAttr getCalleeAttr()
Definition Ops.h.inc:292
::mlir::SymbolRefAttr getCallee()
Definition Ops.cpp.inc:470
void setTemplateParamsAttr(::mlir::ArrayAttr attr)
Definition Ops.h.inc:316
::mlir::Value getSelfValueFromConstrain()
Return the "self" value (i.e.
Definition Ops.cpp:1198
::llzk::component::StructType getSingleResultTypeOfWitnessGen()
Assuming the callee contains witness generation code, return the single StructType result.
Definition Ops.cpp:1215
void setCalleeAttr(::mlir::SymbolRefAttr attr)
Definition Ops.h.inc:312
::mlir::FailureOr<::llzk::SymbolLookupResult<::llzk::function::FuncDefOp > > getCalleeTarget(::mlir::SymbolTableCollection &tables)
Resolve and return the target FuncDefOp for this CallOp.
Definition Ops.cpp:1203
::mlir::FunctionType getFunctionType()
Definition Ops.cpp.inc:984
static PodType get(::mlir::MLIRContext *context, ::llvm::ArrayRef<::llzk::pod::RecordAttr > records)
Definition Types.cpp.inc:68
::llvm::ArrayRef<::llzk::pod::RecordAttr > getRecords() const
::mlir::FlatSymbolRefAttr getConstNameAttr()
Definition Ops.h.inc:464
::mlir::Region & getBodyRegion()
Definition Ops.h.inc:872
OpT getConstNamed(::mlir::StringRef find)
Return the op of type OpT with the given name within the body region if it exists,...
Definition Ops.h.inc:969
::mlir::StringAttr getSymNameAttr()
Definition Ops.h.inc:885
void setSymName(::llvm::StringRef attrValue)
Definition Ops.cpp.inc:1064
::llvm::StringRef getSymName()
Definition Ops.cpp.inc:1059
inline ::llvm::iterator_range<::mlir::Region::op_iterator< OpT > > getConstOps()
Return ops of type OpT within the body region.
Definition Ops.h.inc:921
::std::optional<::mlir::Type > getTypeOpt()
Definition Ops.cpp.inc:1337
::mlir::TypedValue<::mlir::Type > getInput()
Definition Ops.h.inc:1327
::mlir::TypedValue<::mlir::Type > getResult()
Definition Ops.h.inc:1346
::mlir::FailureOr< SymbolLookupResult< function::FuncDefOp > > getFuncTarget(::mlir::SymbolTableCollection &tables)
Return the FuncDefOp that this contract targets, or failure if it does not target a function or the f...
void setTargetAttr(::mlir::SymbolRefAttr attr)
Definition Ops.h.inc:481
::mlir::FunctionType getFunctionType()
Definition Ops.cpp.inc:558
::mlir::Region & getBody()
Definition Ops.h.inc:443
::mlir::FailureOr< SymbolLookupResult< component::StructDefOp > > getStructTarget(::mlir::SymbolTableCollection &tables)
Return the StructDefOp that this contract targets, or failure if it does not target a struct or the s...
::mlir::SymbolRefAttr getTargetAttr()
Definition Ops.h.inc:461
::mlir::SymbolRefAttr getCalleeAttr()
Definition Ops.h.inc:1290
void setCalleeAttr(::mlir::SymbolRefAttr attr)
Definition Ops.h.inc:1310
::mlir::FailureOr<::llzk::SymbolLookupResult<::llzk::verif::ContractOp > > getCalleeTarget(::mlir::SymbolTableCollection &tables)
Resolve and return the target Contract for this CallOp.
Definition Ops.cpp:1043
void setTemplateParamsAttr(::mlir::ArrayAttr attr)
Definition Ops.h.inc:1314
component::StructType getStructTypeWithParams(mlir::SymbolRefAttr nameRef, mlir::ArrayAttr params)
Build a struct type while representing an empty parameter list as absent.
Definition SharedImpl.h:121
InstantiationLayout buildInstantiationLayout(TemplateOp parentTemplate, mlir::ArrayAttr callParams, const llvm::DenseMap< mlir::Attribute, mlir::Attribute > &paramNameToConcrete)
Derive the instantiated template name and the remaining explicit parameters that should stay on the r...
Definition SharedImpl.h:145
array::ArrayType flattenInstantiatedArrayType(array::ArrayType inputTy, mlir::Type convertedElemTy)
Merge nested array dimensions produced by replacing an array element type.
constexpr char FUNC_NAME_COMPUTE[]
Symbol name for the witness generation (and resp.
Definition Constants.h:16
mlir::SymbolRefAttr getPrefixAsSymbolRefAttr(mlir::SymbolRefAttr symbol)
Return SymbolRefAttr like the one given but with the leaf/final element removed.
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
mlir::DenseMap< std::pair< mlir::SymbolRefAttr, Side >, mlir::Attribute > UnificationMap
Optional result from type unifications.
Definition TypeHelper.h:223
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
bool isDynamic(IntegerAttr intAttr)
mlir::SymbolRefAttr asSymbolRefAttr(mlir::StringAttr root, mlir::SymbolRefAttr tail)
Build a SymbolRefAttr that prepends tail with root, i.e., root::tail.
bool isTypeVarFreeType(Type type)
bool typeParamsUnify(const ArrayRef< Attribute > &lhsParams, const ArrayRef< Attribute > &rhsParams, UnificationMap *unifications)
bool isConcreteStructParamAttr(mlir::Attribute attr, bool allowStructParams=true)
Return true if attr is a concrete argument for a parameterized struct type.
Definition TypeHelper.h:139
bool hasParentThatIsa(mlir::Operation *op)
Return true if the parameter has a parent/ancestor op that is an instance of one of the template type...
Definition OpHelpers.h:62
llvm::SmallVector< FlatSymbolRefAttr > getPieces(SymbolRefAttr ref)
FailureOr< SymbolRefAttr > getPathFromRoot(SymbolOpInterface to, ModuleOp *foundRoot)
Groups the information needed after concrete parameters have been chosen to decide how to name a new ...
Definition SharedImpl.h:136
mlir::SmallVector< mlir::Attribute > remainingNames
Definition SharedImpl.h:137