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_;
242 SmallVector<StringAttr> templatePath_;
244 SmallVector<StringAttr> oldParamOrder_;
246 DenseSet<StringAttr> removedParams_;
248 DenseMap<StringAttr, Type> replacements_;
250 bool trimResolvedParams_;
251
252public:
258 TypeVarReplacementConverter(
259 MLIRContext *c, ArrayRef<StringAttr> templatePath, ArrayRef<StringAttr> oldParamOrder,
260 const DenseMap<StringAttr, Type> &replacements, bool trimResolvedParams = true
261 )
262 : ctx_(c), templatePath_(templatePath), oldParamOrder_(oldParamOrder),
263 replacements_(replacements), trimResolvedParams_(trimResolvedParams) {
264 for (const auto &entry : replacements_) {
265 removedParams_.insert(entry.first);
266 }
267 }
268
273 Type convertType(Type ty) const {
274 DenseSet<StringAttr> resolvingParams;
275 return convertType(ty, resolvingParams);
276 }
277
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 });
301 }
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);
307 }
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 }
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 });
447public:
449 ///
450 /// If the list still has the original arity, entries corresponding to resolved `poly.param`s are
451 /// removed. Remaining entries are recursively converted in case they mention a now-concrete type
452 /// variable. A fully removed list is represented as `nullptr` so the printer elides the template
453 /// argument syntax. A removed argument must agree with the inferred replacement before it is
454 /// dropped.
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();
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 }
498private:
500 Attribute convertTemplateArgAttr(Attribute attr) const {
501 DenseSet<StringAttr> resolvingParams;
502 return convertTemplateArgAttr(attr, resolvingParams);
504
506
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 return createOp.emitError()
751 << "cannot rewrite initialized array.new: initializer " << index << " converts to "
752 << newElementValueTy << ", but expected " << newElemTy;
753 }
754 newElementValueTypes.push_back(newElementValueTy);
755 }
756
757 OpBuilder builder(createOp);
758 Location loc = createOp.getLoc();
759 CreateArrayOp newCreate = CreateArrayOp::create(builder, loc, newResultTy);
760 ArrayIndexGen idxGen = ArrayIndexGen::from(oldResultTy);
761 for (auto [index, element] : llvm::enumerate(createOp.getElements())) {
762 Type newElementValueTy = newElementValueTypes[index];
763 if (element.getType() != newElementValueTy) {
764 element.setType(newElementValueTy);
765 }
766 std::optional<SmallVector<Value>> indices =
767 idxGen.delinearize(checkedCast<int64_t>(index), loc, builder);
768 assert(indices && "static array initializer index should delinearize");
769 InsertArrayOp::create(builder, loc, newCreate.getResult(), ValueRange(*indices), element);
770 }
771 createOp.getResult().replaceAllUsesWith(newCreate.getResult());
772 createOp.erase();
773 return true;
774 }
775 }
776
777 if (auto readOp = llvm::dyn_cast<ReadArrayOp>(op)) {
778 Type newResultTy = converter.convertType(readOp.getResult().getType());
779 if (converterFailed(converter)) {
780 return failure();
781 }
782 if (auto newArrayTy = llvm::dyn_cast<ArrayType>(newResultTy)) {
783 OpBuilder builder(readOp);
784 auto extractOp = ExtractArrayOp::create(
785 builder, readOp.getLoc(), newArrayTy, readOp.getArrRef(), readOp.getIndices()
786 );
787 readOp.getResult().replaceAllUsesWith(extractOp.getResult());
788 readOp.erase();
789 return true;
790 }
791 }
792
793 if (auto writeOp = llvm::dyn_cast<WriteArrayOp>(op)) {
794 Type newRvalueTy = converter.convertType(writeOp.getRvalue().getType());
795 if (converterFailed(converter)) {
796 return failure();
797 }
798 if (llvm::isa<ArrayType>(newRvalueTy)) {
799 OpBuilder builder(writeOp);
801 builder, writeOp.getLoc(), writeOp.getArrRef(), writeOp.getIndices(), writeOp.getRvalue()
802 );
803 writeOp.erase();
804 return true;
805 }
806 }
807
808 bool changed = false;
809
810 if (auto func = llvm::dyn_cast<FuncDefOp>(op)) {
811 FunctionType oldFuncTy = func.getFunctionType();
812 updateFuncSignature(func, converter);
813 if (converterFailed(converter)) {
814 return failure();
815 }
816 changed |= oldFuncTy != func.getFunctionType();
817 }
818 if (auto contract = llvm::dyn_cast<verif::ContractOp>(op)) {
819 FunctionType oldFuncTy = contract.getFunctionType();
820 updateContractSignature(contract, converter);
821 if (converterFailed(converter)) {
822 return failure();
823 }
824 changed |= oldFuncTy != contract.getFunctionType();
825 changed |= convertContractTarget(contract, converter);
826 if (converterFailed(converter)) {
827 return failure();
828 }
829 }
830
831 for (Region &region : op->getRegions()) {
832 for (Block &block : region.getBlocks()) {
833 for (BlockArgument arg : block.getArguments()) {
834 Type newTy = converter.convertType(arg.getType());
835 if (converterFailed(converter)) {
836 return failure();
837 }
838 if (newTy != arg.getType()) {
839 arg.setType(newTy);
840 changed = true;
841 }
842 }
843 }
844 }
845
846 for (Value result : op->getResults()) {
847 Type newTy = converter.convertType(result.getType());
848 if (converterFailed(converter)) {
849 return failure();
850 }
851 if (newTy != result.getType()) {
852 result.setType(newTy);
853 changed = true;
854 }
855 }
856
857 if (auto callOp = llvm::dyn_cast<CallOp>(op)) {
858 changed |= convertCallCallee(callOp, converter);
859 if (converterFailed(converter)) {
860 return failure();
861 }
862 }
863
864 SmallVector<NamedAttribute> newAttrs;
865 bool attrsChanged = false;
866 newAttrs.reserve(op->getAttrs().size());
867 for (NamedAttribute attr : op->getAttrs()) {
868 Attribute newAttr = converter.convertAttr(attr.getValue());
869 if (converterFailed(converter)) {
870 return failure();
871 }
872 newAttrs.emplace_back(attr.getName(), newAttr);
873 attrsChanged |= newAttr != attr.getValue();
874 }
875 if (attrsChanged) {
876 op->setAttrs(DictionaryAttr::get(op->getContext(), newAttrs));
877 changed = true;
878 }
879
880 return changed;
881}
882
884template <typename ConverterT>
885static FailureOr<bool> convertOperationTypesInAndTrack(Operation *root, ConverterT &converter) {
886 bool changed = false;
887 WalkResult res = root->walk([&converter, &changed](Operation *op) {
888 startConverterOperation(converter, op);
889 FailureOr<bool> opChanged = convertOperationTypes(op, converter);
890 if (failed(opChanged)) {
891 return WalkResult::interrupt();
892 }
893 changed |= *opChanged;
894 return WalkResult::advance();
895 });
896 if (res.wasInterrupted()) {
897 return failure();
898 }
899 return changed;
900}
901
903template <typename ConverterT>
904static LogicalResult convertOperationTypesIn(Operation *root, ConverterT &converter) {
905 return failure(failed(convertOperationTypesInAndTrack(root, converter)));
906}
907
914static void removeIdentityCasts(Operation *root) {
915 for (UnifiableCastOp castOp : walkCollect<UnifiableCastOp>(*root)) {
916 if (castOp.getInput().getType() != castOp.getResult().getType()) {
917 continue;
918 }
919 castOp.getResult().replaceAllUsesWith(castOp.getInput());
920 castOp.erase();
921 }
922}
923
925template <typename ConverterT>
926static LogicalResult convertTemplateExprTypesIn(TemplateOp templateOp, ConverterT &converter) {
927 for (TemplateExprOp expr : templateOp.getConstOps<TemplateExprOp>()) {
928 if (failed(convertOperationTypesIn(expr.getOperation(), converter))) {
929 return failure();
930 }
931 removeIdentityCasts(expr.getOperation());
932 }
933 return success();
934}
935
941static std::string buildSpecializedTemplateCloneCacheKey(
942 StringRef templateName, ArrayRef<StringAttr> oldParamOrder,
943 const DenseMap<Attribute, Attribute> &paramNameToConcrete
944) {
945 std::string key;
946 llvm::raw_string_ostream os(key);
947 os << templateName.size() << ':' << templateName;
948 for (StringAttr paramName : oldParamOrder) {
949 os << '|';
950 os << paramName.getValue().size() << ':' << paramName.getValue() << '=';
951 auto concreteIt = paramNameToConcrete.find(FlatSymbolRefAttr::get(paramName));
952 if (concreteIt == paramNameToConcrete.end()) {
953 os << '_';
954 continue;
955 }
956
957 std::string attrText;
958 llvm::raw_string_ostream attrOs(attrText);
959 concreteIt->second.print(attrOs);
960 os << attrText.size() << ':' << attrText;
961 }
962 return key;
963}
964
966static bool canCopyTemplateExprToSpecialization(
967 TemplateExprOp expr, const DenseSet<StringAttr> &availableNames
968) {
969 bool canCopy = true;
970 expr->walk([&](ConstReadOp readOp) {
971 if (!availableNames.contains(readOp.getConstNameAttr().getAttr())) {
972 canCopy = false;
973 return WalkResult::interrupt();
974 }
975 return WalkResult::advance();
976 });
977 return canCopy;
978}
979
984static void copyPreservedTemplateExprs(
985 TemplateOp parentTemplate, Block &newTemplateBody, ArrayRef<Attribute> remainingNames
986) {
987 DenseSet<StringAttr> availableNames;
988 for (Attribute name : remainingNames) {
989 FlatSymbolRefAttr nameSym = llvm::cast<FlatSymbolRefAttr>(name);
990 availableNames.insert(nameSym.getAttr());
991 }
992
993 for (TemplateExprOp expr : parentTemplate.getConstOps<TemplateExprOp>()) {
994 if (canCopyTemplateExprToSpecialization(expr, availableNames)) {
995 newTemplateBody.push_back(expr->clone());
996 }
997 }
998}
999
1001static FailureOr<TemplateOp> getOrCreateSpecializedTemplateClone(
1002 TemplateOp parentTemplate, ArrayRef<StringAttr> oldParamOrder,
1003 const DenseMap<Attribute, Attribute> &paramNameToConcrete, ArrayAttr callParams,
1004 SymbolTableCollection &tables, llvm::StringMap<StringAttr> &templateClones,
1005 InstantiationLayout &layout
1006) {
1007 FailureOr<InstantiationLayout> layoutResult =
1008 buildInstantiationLayout(parentTemplate, callParams, paramNameToConcrete);
1009 if (failed(layoutResult)) {
1010 return failure();
1011 }
1012 layout = std::move(*layoutResult);
1013 std::string cacheKey = buildSpecializedTemplateCloneCacheKey(
1014 parentTemplate.getSymName(), oldParamOrder, paramNameToConcrete
1015 );
1016
1017 ModuleOp parentModule = getParentOfType<ModuleOp>(parentTemplate);
1018 if (!parentModule) {
1019 return failure();
1020 }
1021 SymbolTable &moduleSymbols = tables.getSymbolTable(parentModule);
1022
1023 auto cachedName = templateClones.find(cacheKey);
1024 if (cachedName != templateClones.end()) {
1025 Operation *existing = moduleSymbols.lookup(cachedName->second);
1026 if (auto existingTemplate = llvm::dyn_cast_or_null<TemplateOp>(existing)) {
1027 return existingTemplate;
1028 }
1029 return failure();
1030 }
1031
1032 TemplateOp newTemplate = parentTemplate.cloneWithoutRegions();
1033 newTemplate.setSymName(layout.templateNameWithAttrs);
1035 newTemplate, layout.remainingNames.empty() ? ArrayAttr() : layout.namePattern
1036 );
1037 assert(newTemplate->getNumRegions() > 0 && "region exists");
1038 newTemplate.getBodyRegion().emplaceBlock();
1039 Block &newTemplateBody = newTemplate.getBodyRegion().front();
1040 SymbolTable &parentTemplateSymbols = tables.getSymbolTable(parentTemplate);
1041 for (Attribute name : layout.remainingNames) {
1042 FlatSymbolRefAttr nameSym = llvm::cast<FlatSymbolRefAttr>(name);
1043 Operation *paramOp = parentTemplateSymbols.lookup(nameSym.getAttr());
1044 assert(paramOp && "symbol must exist");
1045 newTemplateBody.push_back(paramOp->clone());
1046 }
1047 copyPreservedTemplateExprs(parentTemplate, newTemplateBody, layout.remainingNames);
1048
1049 moduleSymbols.insert(newTemplate, Block::iterator(parentTemplate));
1050 templateClones.try_emplace(cacheKey, newTemplate.getSymNameAttr());
1051 return newTemplate;
1052}
1053
1060class ConcreteStructInstantiationConverter {
1062 struct StructInstantiationTypes {
1064 StructType localType;
1066 StructType remoteType;
1067 };
1068
1070 MLIRContext *ctx_;
1072 ModuleOp module_;
1074 SymbolTableCollection &tables_;
1076 DenseMap<StructType, StructInstantiationTypes> instantiations_;
1078 SpecializedTemplateCloneCache templateClones_;
1080 DenseSet<SymbolRefAttr> instantiatedCloneNames_;
1082 DenseMap<StructType, StructType> activeLocalStructReplacements_;
1084 DenseMap<StringAttr, Type> activeTypeReplacements_;
1086 bool hasFailure = false;
1087
1088public:
1090 ConcreteStructInstantiationConverter(MLIRContext *c, ModuleOp m, SymbolTableCollection &t)
1091 : ctx_(c), module_(m), tables_(t) {}
1092
1094 bool hadFailure() const { return hasFailure; }
1095
1097 Type convertType(Type ty) {
1098 if (!ty || hasFailure) {
1099 return ty;
1100 }
1101 if (auto tvarTy = llvm::dyn_cast<TypeVarType>(ty)) {
1102 auto it = activeTypeReplacements_.find(tvarTy.getNameRef().getAttr());
1103 return it == activeTypeReplacements_.end() ? ty : it->second;
1104 }
1105 if (auto arrTy = llvm::dyn_cast<ArrayType>(ty)) {
1106 return convertArrayElementType(arrTy, [this](Type elemTy) { return convertType(elemTy); });
1107 }
1108 if (auto podTy = llvm::dyn_cast<PodType>(ty)) {
1109 return convertPodType(podTy, ctx_, *this);
1110 }
1111 if (auto funcTy = llvm::dyn_cast<FunctionType>(ty)) {
1112 return convertFunctionType(funcTy, *this);
1113 }
1114 if (auto structTy = llvm::dyn_cast<StructType>(ty)) {
1115 return convertStructType(structTy);
1116 }
1117 return ty;
1118 }
1119
1121 Attribute convertAttr(Attribute attr) {
1122 if (hasFailure) {
1123 return attr;
1124 }
1125 return convertTypeOrArrayAttr(attr, ctx_, [this](Type ty) {
1126 return convertType(ty);
1127 }, [this](Attribute nested) { return convertTemplateArgAttr(nested); });
1128 }
1129
1131 SymbolRefAttr convertCallCallee(CallOp callOp) {
1132 SymbolRefAttr callee = callOp.getCalleeAttr();
1133 if (!callee || callee.getNestedReferences().empty()) {
1134 return callee;
1135 }
1136
1137 StructType targetStructTy = getStructFunctionTargetType(callOp);
1138 if (!targetStructTy) {
1139 return callee;
1140 }
1141 auto convertedStructTy = llvm::dyn_cast<StructType>(convertType(targetStructTy));
1142 if (!convertedStructTy) {
1143 return callee;
1144 }
1145
1146 SymbolRefAttr convertedStructName = convertedStructTy.getNameRef();
1147 if (convertedStructName == getPrefixAsSymbolRefAttr(callee)) {
1148 return callee;
1149 }
1150 SmallVector<FlatSymbolRefAttr> pieces = getPieces(convertedStructName);
1151 pieces.push_back(FlatSymbolRefAttr::get(callee.getLeafReference()));
1152 return asSymbolRefAttr(pieces);
1153 }
1154
1156 SymbolRefAttr convertContractTarget(verif::ContractOp contract) {
1157 SymbolRefAttr target = contract.getTargetAttr();
1158 if (!target || failed(contract.getStructTarget(tables_))) {
1159 return target;
1160 }
1161
1162 FunctionType contractTy = contract.getFunctionType();
1163 if (contractTy.getNumInputs() == 0) {
1164 return target;
1165 }
1166 auto selfTy = llvm::dyn_cast<StructType>(contractTy.getInput(0));
1167 if (!selfTy || selfTy.getNameRef() == target) {
1168 return target;
1169 }
1170 return instantiatedCloneNames_.contains(selfTy.getNameRef()) ? selfTy.getNameRef() : target;
1171 }
1172
1173private:
1175 static StructType getStructFunctionTargetType(CallOp callOp) {
1176 StringAttr calleeLeaf = callOp.getCallee().getLeafReference();
1177 if (calleeLeaf == FUNC_NAME_CONSTRAIN) {
1178 return dyn_cast<StructType>(callOp.getSelfValueFromConstrain().getType());
1179 }
1180 if (calleeLeaf == FUNC_NAME_COMPUTE || calleeLeaf == FUNC_NAME_PRODUCT) {
1181 return callOp.getSingleResultTypeOfWitnessGen();
1182 }
1183 return {};
1184 }
1185
1187 Attribute convertTemplateArgAttr(Attribute attr) {
1188 if (StringAttr symbolName = getFlatSymbolName(attr)) {
1189 auto it = activeTypeReplacements_.find(symbolName);
1190 if (it != activeTypeReplacements_.end()) {
1191 return TypeAttr::get(it->second);
1192 }
1193 }
1194 return convertAttr(attr);
1195 }
1196
1198 StructType convertStructType(StructType structTy) {
1199 ArrayAttr params = structTy.getParams();
1200 if (isNullOrEmpty(params)) {
1201 return structTy;
1202 }
1203
1204 SmallVector<Attribute> newParams;
1205 bool changed = false;
1206 for (Attribute attr : params.getValue()) {
1207 Attribute newAttr = convertTemplateArgAttr(attr);
1208 newParams.push_back(newAttr);
1209 changed |= newAttr != attr;
1210 }
1211 StructType convertedTy =
1212 changed ? StructType::get(structTy.getNameRef(), ArrayAttr::get(ctx_, newParams))
1213 : structTy;
1214
1215 if (auto it = activeLocalStructReplacements_.find(convertedTy);
1216 it != activeLocalStructReplacements_.end()) {
1217 return it->second;
1218 }
1219 if (instantiatedCloneNames_.contains(convertedTy.getNameRef())) {
1220 return convertedTy;
1221 }
1222
1223 if (llvm::any_of(newParams, [](Attribute attr) {
1224 return !isConcreteStructParamAttr(attr, /*allowStructParams=*/false);
1225 })) {
1226 return convertedTy;
1227 }
1228
1229 FailureOr<StructType> cloneTy = getOrCreateStructClone(convertedTy, newParams);
1230 if (failed(cloneTy)) {
1231 hasFailure = true;
1232 return convertedTy;
1233 }
1234 return *cloneTy;
1235 }
1236
1238 FailureOr<StructType>
1239 getOrCreateStructClone(StructType concreteStructTy, ArrayRef<Attribute> concreteParams) {
1240 if (auto it = instantiations_.find(concreteStructTy); it != instantiations_.end()) {
1241 return it->second.remoteType;
1242 }
1243
1244 FailureOr<SymbolLookupResult<StructDefOp>> lookup =
1245 concreteStructTy.getDefinition(tables_, module_, /*emitError=*/false);
1246 if (failed(lookup)) {
1247 return failure();
1248 }
1249
1250 StructDefOp origStruct = lookup->get();
1251 TemplateOp parentTemplate = getParentOfType<TemplateOp>(origStruct);
1252 if (!parentTemplate) {
1253 return failure();
1254 }
1255 StructType typeAtDef = origStruct.getType();
1256 ArrayAttr paramNames = typeAtDef.getParams();
1257 if (!paramNames || paramNames.size() != concreteParams.size()) {
1258 return failure();
1259 }
1260
1261 DenseMap<StringAttr, Type> typeReplacements;
1262 DenseMap<Attribute, Attribute> paramNameToConcrete;
1263 SmallVector<StringAttr> oldParamOrder;
1264 oldParamOrder.reserve(paramNames.size());
1265 SmallVector<Attribute> convertedSourceParams;
1266 convertedSourceParams.reserve(concreteParams.size());
1267 for (auto [paramName, concreteAttr] : llvm::zip_equal(paramNames.getValue(), concreteParams)) {
1268 auto paramSym = llvm::dyn_cast<FlatSymbolRefAttr>(paramName);
1269 if (!paramSym) {
1270 return failure();
1271 }
1272 oldParamOrder.push_back(paramSym.getAttr());
1273 auto concreteType = llvm::dyn_cast<TypeAttr>(concreteAttr);
1274 if (concreteType) {
1275 paramNameToConcrete.try_emplace(FlatSymbolRefAttr::get(paramSym.getAttr()), concreteAttr);
1276 typeReplacements.try_emplace(paramSym.getAttr(), concreteType.getValue());
1277 convertedSourceParams.push_back(concreteType);
1278 } else {
1279 convertedSourceParams.push_back(paramName);
1280 }
1281 }
1282 if (paramNameToConcrete.empty()) {
1283 return concreteStructTy;
1284 }
1285
1286 InstantiationLayout layout;
1287 ArrayAttr concreteParamArray = ArrayAttr::get(ctx_, concreteParams);
1288 FailureOr<TemplateOp> newTemplate = getOrCreateSpecializedTemplateClone(
1289 parentTemplate, oldParamOrder, paramNameToConcrete, concreteParamArray, tables_,
1290 templateClones_[parentTemplate.getOperation()], layout
1291 );
1292 if (failed(newTemplate)) {
1293 return failure();
1294 }
1295
1296 SymbolTable &templateSymbols = tables_.getSymbolTable(*newTemplate);
1297 StructDefOp clone = origStruct.clone();
1298 templateSymbols.insert(clone);
1299 StructType localTy =
1301 StructType remoteTy =
1303 instantiations_.try_emplace(concreteStructTy, StructInstantiationTypes {localTy, remoteTy});
1304 instantiatedCloneNames_.insert(clone.getFullyQualifiedName());
1305
1306 DenseMap<StringAttr, Type> previousReplacements = std::move(activeTypeReplacements_);
1307 DenseMap<StructType, StructType> previousLocalStructReplacements =
1308 std::move(activeLocalStructReplacements_);
1309 activeTypeReplacements_ = std::move(typeReplacements);
1310 activeLocalStructReplacements_.clear();
1311 activeLocalStructReplacements_.try_emplace(concreteStructTy, localTy);
1312 activeLocalStructReplacements_.try_emplace(
1313 StructType::get(typeAtDef.getNameRef(), ArrayAttr::get(ctx_, convertedSourceParams)),
1314 localTy
1315 );
1316 activeLocalStructReplacements_.try_emplace(
1317 StructType::get(localTy.getNameRef(), ArrayAttr::get(ctx_, convertedSourceParams)), localTy
1318 );
1319 activeLocalStructReplacements_.try_emplace(
1320 StructType::get(localTy.getNameRef(), ArrayAttr::get(ctx_, concreteParams)), localTy
1321 );
1322 SymbolRefAttr cloneNameRef = clone.getFullyQualifiedName();
1323 if (failed(convertTemplateExprTypesIn(*newTemplate, *this))) {
1324 activeLocalStructReplacements_ = std::move(previousLocalStructReplacements);
1325 activeTypeReplacements_ = std::move(previousReplacements);
1326 instantiations_.erase(concreteStructTy);
1327 instantiatedCloneNames_.erase(cloneNameRef);
1328 clone.erase();
1329 return failure();
1330 }
1331 if (failed(convertOperationTypesIn(clone.getOperation(), *this))) {
1332 activeLocalStructReplacements_ = std::move(previousLocalStructReplacements);
1333 activeTypeReplacements_ = std::move(previousReplacements);
1334 instantiations_.erase(concreteStructTy);
1335 instantiatedCloneNames_.erase(cloneNameRef);
1336 clone.erase();
1337 return failure();
1338 }
1339 removeIdentityCasts(clone.getOperation());
1340 activeLocalStructReplacements_ = std::move(previousLocalStructReplacements);
1341 activeTypeReplacements_ = std::move(previousReplacements);
1342 return remoteTy;
1343 }
1344};
1345
1347struct TemplateInferenceInfo {
1349 TemplateOp templateOp;
1351 SmallVector<StringAttr> templatePath;
1353 SmallVector<StringAttr> oldParamOrder;
1355 DenseMap<StringAttr, TemplateParamOp> typeVarParams;
1357 DenseMap<StringAttr, InferredType> replacements;
1359 DenseMap<StringAttr, InferredType> templateScopeReplacements;
1361 DenseMap<Operation *, DenseMap<StringAttr, InferredType>> functionReplacements;
1362};
1363
1365static DenseMap<Attribute, Attribute>
1366buildParamNameToCallArg(ArrayAttr callParams, ArrayRef<StringAttr> oldParamOrder) {
1367 DenseMap<Attribute, Attribute> paramNameToCallArg;
1368 if (!callParams || callParams.size() != oldParamOrder.size()) {
1369 return paramNameToCallArg;
1370 }
1371 for (auto [paramName, attr] : llvm::zip_equal(oldParamOrder, callParams.getValue())) {
1372 paramNameToCallArg.try_emplace(FlatSymbolRefAttr::get(paramName), attr);
1373 }
1374 return paramNameToCallArg;
1375}
1376
1383static Type
1384substituteExplicitCallTypeArgs(Type ty, ArrayAttr callParams, ArrayRef<StringAttr> oldParamOrder) {
1385 if (!callParams || callParams.size() != oldParamOrder.size()) {
1386 return ty;
1387 }
1388
1389 DenseMap<StringAttr, Type> callTypeArgs;
1390 for (auto [paramName, attr] : llvm::zip_equal(oldParamOrder, callParams.getValue())) {
1391 if (auto tyAttr = llvm::dyn_cast<TypeAttr>(attr)) {
1392 callTypeArgs.try_emplace(paramName, tyAttr.getValue());
1393 }
1394 }
1395 TypeVarReplacementConverter converter(
1396 ty.getContext(), ArrayRef<StringAttr> {}, oldParamOrder, callTypeArgs,
1397 /*trimResolvedParams=*/false
1398 );
1399 return converter.convertType(ty);
1400}
1401
1403class ExplicitCallTemplateParamSubstituter {
1405 const DenseMap<Attribute, Attribute> &paramNameToCallArg_;
1407 DenseSet<Attribute> resolvingParams_;
1408
1409public:
1410 explicit ExplicitCallTemplateParamSubstituter(
1411 const DenseMap<Attribute, Attribute> &paramNameToCallArg
1412 )
1413 : paramNameToCallArg_(paramNameToCallArg) {}
1414
1415 Type substituteType(Type ty);
1416 Attribute substituteAttr(Attribute attr);
1417};
1418
1420Attribute ExplicitCallTemplateParamSubstituter::substituteAttr(Attribute attr) {
1421 if (!attr) {
1422 return attr;
1423 }
1424 auto it = paramNameToCallArg_.find(attr);
1425 if (it != paramNameToCallArg_.end()) {
1426 return it->second;
1427 }
1428 if (auto tyAttr = llvm::dyn_cast<TypeAttr>(attr)) {
1429 Type newTy = substituteType(tyAttr.getValue());
1430 return newTy == tyAttr.getValue() ? attr : TypeAttr::get(newTy);
1431 }
1432 if (auto arrAttr = llvm::dyn_cast<ArrayAttr>(attr)) {
1433 SmallVector<Attribute> newAttrs;
1434 bool changed = false;
1435 for (Attribute nested : arrAttr.getValue()) {
1436 Attribute newNested = substituteAttr(nested);
1437 newAttrs.push_back(newNested);
1438 changed |= newNested != nested;
1439 }
1440 return changed ? ArrayAttr::get(attr.getContext(), newAttrs) : attr;
1441 }
1442 return attr;
1443}
1444
1452Type ExplicitCallTemplateParamSubstituter::substituteType(Type ty) {
1453 if (!ty) {
1454 return ty;
1455 }
1456 if (auto tvarTy = llvm::dyn_cast<TypeVarType>(ty)) {
1457 Attribute paramRef = tvarTy.getNameRef();
1458 auto it = paramNameToCallArg_.find(paramRef);
1459 if (it == paramNameToCallArg_.end()) {
1460 return ty;
1461 }
1462 auto tyAttr = llvm::dyn_cast<TypeAttr>(it->second);
1463 if (!tyAttr || tyAttr.getValue() == ty) {
1464 return ty;
1465 }
1466 if (!resolvingParams_.insert(paramRef).second) {
1467 return ty;
1468 }
1469 Type replacement = substituteType(tyAttr.getValue());
1470 resolvingParams_.erase(paramRef);
1471 return replacement;
1472 }
1473 if (auto arrTy = llvm::dyn_cast<ArrayType>(ty)) {
1474 SmallVector<Attribute> newDims;
1475 bool changed = false;
1476 for (Attribute dim : arrTy.getDimensionSizes()) {
1477 Attribute newDim = substituteAttr(dim);
1478 newDims.push_back(newDim);
1479 changed |= newDim != dim;
1480 }
1481 Type newElemTy = substituteType(arrTy.getElementType());
1482 if (!changed && newElemTy == arrTy.getElementType()) {
1483 return arrTy;
1484 }
1486 arrTy.cloneWith(arrTy.getElementType(), newDims), newElemTy
1487 );
1488 }
1489 if (auto structTy = llvm::dyn_cast<StructType>(ty)) {
1490 ArrayAttr params = structTy.getParams();
1491 if (!params) {
1492 return structTy;
1493 }
1494 SmallVector<Attribute> newParams;
1495 bool changed = false;
1496 for (Attribute param : params.getValue()) {
1497 Attribute newParam = substituteAttr(param);
1498 newParams.push_back(newParam);
1499 changed |= newParam != param;
1500 }
1501 return changed ? getStructTypeWithParams(structTy.getNameRef(), ty.getContext(), newParams)
1502 : structTy;
1503 }
1504 if (auto podTy = llvm::dyn_cast<PodType>(ty)) {
1505 SmallVector<RecordAttr> newRecords;
1506 bool changed = false;
1507 for (RecordAttr record : podTy.getRecords()) {
1508 Type newRecordTy = substituteType(record.getType());
1509 newRecords.push_back(RecordAttr::get(ty.getContext(), record.getName(), newRecordTy));
1510 changed |= newRecordTy != record.getType();
1511 }
1512 return changed ? PodType::get(ty.getContext(), newRecords) : podTy;
1513 }
1514 if (auto funcTy = llvm::dyn_cast<FunctionType>(ty)) {
1515 SmallVector<Type> newInputs;
1516 SmallVector<Type> newResults;
1517 bool changed = false;
1518 newInputs.reserve(funcTy.getNumInputs());
1519 newResults.reserve(funcTy.getNumResults());
1520 for (Type inputTy : funcTy.getInputs()) {
1521 Type newInputTy = substituteType(inputTy);
1522 newInputs.push_back(newInputTy);
1523 changed |= newInputTy != inputTy;
1524 }
1525 for (Type resultTy : funcTy.getResults()) {
1526 Type newResultTy = substituteType(resultTy);
1527 newResults.push_back(newResultTy);
1528 changed |= newResultTy != resultTy;
1529 }
1530 return changed ? FunctionType::get(ty.getContext(), newInputs, newResults) : funcTy;
1531 }
1532 return ty;
1533}
1534
1536static Type substituteExplicitCallTemplateParams(
1537 Type ty, ArrayAttr callParams, ArrayRef<StringAttr> oldParamOrder
1538) {
1539 auto paramNameToCallArg = buildParamNameToCallArg(callParams, oldParamOrder);
1540 ExplicitCallTemplateParamSubstituter substituter(paramNameToCallArg);
1541 return substituter.substituteType(ty);
1542}
1543
1545static bool symbolMatchesParam(SymbolRefAttr symRef, StringAttr paramName) {
1546 return symRef && symRef.getNestedReferences().empty() && symRef.getRootReference() == paramName;
1547}
1548
1550class ParamMentionChecker {
1551 StringAttr paramName_;
1552
1553public:
1554 explicit ParamMentionChecker(StringAttr paramName) : paramName_(paramName) {}
1555
1557 bool typeMentions(Type ty) const {
1558 if (!ty) {
1559 return false;
1560 }
1561 if (auto tvarTy = llvm::dyn_cast<TypeVarType>(ty)) {
1562 return tvarTy.getNameRef().getAttr() == paramName_;
1563 }
1564 if (auto arrayTy = llvm::dyn_cast<ArrayType>(ty)) {
1565 return typeMentions(arrayTy.getElementType()) ||
1566 llvm::any_of(arrayTy.getDimensionSizes(), [this](Attribute dim) {
1567 return attrMentions(dim, /*allowSymbolRefs=*/true);
1568 });
1569 }
1570 if (auto structTy = llvm::dyn_cast<StructType>(ty)) {
1571 ArrayAttr params = structTy.getParams();
1572 return params && attrMentions(params, /*allowSymbolRefs=*/true);
1573 }
1574 if (auto podTy = llvm::dyn_cast<PodType>(ty)) {
1575 return llvm::any_of(podTy.getRecords(), [this](RecordAttr record) {
1576 return typeMentions(record.getType());
1577 });
1578 }
1579 if (auto funcTy = llvm::dyn_cast<FunctionType>(ty)) {
1580 return llvm::any_of(funcTy.getInputs(), [this](Type input) {
1581 return typeMentions(input);
1582 }) || llvm::any_of(funcTy.getResults(), [this](Type result) { return typeMentions(result); });
1583 }
1584 return false;
1585 }
1586
1592 bool attrMentions(Attribute attr, bool allowSymbolRefs) const {
1593 if (!attr) {
1594 return false;
1595 }
1596 if (auto typeAttr = llvm::dyn_cast<TypeAttr>(attr)) {
1597 return typeMentions(typeAttr.getValue());
1598 }
1599 if (auto arrayAttr = llvm::dyn_cast<ArrayAttr>(attr)) {
1600 return llvm::any_of(arrayAttr, [this](Attribute nested) {
1601 return attrMentions(nested, /*allowSymbolRefs=*/true);
1602 });
1603 }
1604 return allowSymbolRefs && symbolMatchesParam(llvm::dyn_cast<SymbolRefAttr>(attr), paramName_);
1605 }
1606};
1607
1609static bool operationMentionsParam(Operation *op, StringAttr paramName) {
1610 ParamMentionChecker mentions(paramName);
1611 if (llvm::any_of(op->getResultTypes(), [&mentions](Type ty) {
1612 return mentions.typeMentions(ty);
1613 })) {
1614 return true;
1615 }
1616 for (Region &region : op->getRegions()) {
1617 for (Block &block : region.getBlocks()) {
1618 if (llvm::any_of(block.getArgumentTypes(), [&mentions](Type ty) {
1619 return mentions.typeMentions(ty);
1620 })) {
1621 return true;
1622 }
1623 }
1624 }
1625 return llvm::any_of(op->getAttrs(), [&mentions](NamedAttribute attr) {
1626 return mentions.attrMentions(attr.getValue(), /*allowSymbolRefs=*/false);
1627 });
1628}
1629
1631static SmallVector<StringAttr> getMentionedTypeVarParams(
1632 Attribute attr, const DenseMap<StringAttr, TemplateParamOp> &typeVarParams
1633) {
1634 SmallVector<StringAttr> mentionedParams;
1635 for (const auto &entry : typeVarParams) {
1636 if (ParamMentionChecker(entry.first).attrMentions(attr, /*allowSymbolRefs=*/true)) {
1637 mentionedParams.push_back(entry.first);
1638 }
1639 }
1640 return mentionedParams;
1641}
1642
1644static inline bool attrMentionsAnyTypeVarParam(
1645 Attribute attr, const DenseMap<StringAttr, TemplateParamOp> &typeVarParams
1646) {
1647 return !getMentionedTypeVarParams(attr, typeVarParams).empty();
1648}
1649
1651static bool funcMentionsParam(FuncDefOp func, StringAttr paramName) {
1652 if (operationMentionsParam(func.getOperation(), paramName)) {
1653 return true;
1654 }
1655 return walkContains<Operation *>(func, [paramName](Operation *op) {
1656 return operationMentionsParam(op, paramName);
1657 });
1658}
1659
1661static bool contractMentionsParam(verif::ContractOp contract, StringAttr paramName) {
1662 if (operationMentionsParam(contract.getOperation(), paramName)) {
1663 return true;
1664 }
1665 return walkContains<Operation *>(contract, [paramName](Operation *op) {
1666 return operationMentionsParam(op, paramName);
1667 });
1668}
1669
1671static inline bool targetMentionsParam(FuncDefOp func, StringAttr paramName) {
1672 return funcMentionsParam(func, paramName);
1673}
1674
1676static inline bool targetMentionsParam(verif::ContractOp contract, StringAttr paramName) {
1677 return contractMentionsParam(contract, paramName);
1678}
1679
1681static inline bool exprMentionsParam(TemplateExprOp expr, StringAttr paramName) {
1682 return walkContains<Operation *>(expr, [paramName](Operation *op) {
1683 return operationMentionsParam(op, paramName);
1684 });
1685}
1686
1689static bool structUseCoveredByFunctionProof(
1690 StructDefOp structOp, StringAttr paramName, Type replacementType,
1691 const DenseMap<Operation *, DenseMap<StringAttr, InferredType>> &functionReplacements
1692) {
1693 bool sawMention = false;
1694 bool missingProof = false;
1695 structOp.walk([&](FuncDefOp func) {
1696 if (func->getParentOfType<StructDefOp>() != structOp || !funcMentionsParam(func, paramName)) {
1697 return;
1698 }
1699 sawMention = true;
1700 auto funcIt = functionReplacements.find(func.getOperation());
1701 if (funcIt == functionReplacements.end()) {
1702 missingProof = true;
1703 return;
1704 }
1705 auto replacementIt = funcIt->second.find(paramName);
1706 if (replacementIt == funcIt->second.end() || replacementIt->second.type != replacementType) {
1707 missingProof = true;
1708 }
1709 });
1710 return sawMention && !missingProof;
1711}
1712
1720static bool hasUncoveredNonFunctionMention(
1721 TemplateOp templateOp, StringAttr paramName, Type replacementType,
1722 const DenseMap<Operation *, DenseMap<StringAttr, InferredType>> &functionReplacements
1723) {
1724 WalkResult result =
1725 templateOp.walk([paramName, replacementType, &functionReplacements](Operation *op) {
1726 if (llvm::isa<FuncDefOp, TemplateExprOp, TemplateParamOp, verif::ContractOp>(op) ||
1727 op->getParentOfType<FuncDefOp, TemplateExprOp, verif::ContractOp>()) {
1728 return WalkResult::advance();
1729 }
1730 if (!operationMentionsParam(op, paramName)) {
1731 return WalkResult::advance();
1732 }
1733 if (StructDefOp structOp = op->getParentOfType<StructDefOp>()) {
1734 if (structUseCoveredByFunctionProof(
1735 structOp, paramName, replacementType, functionReplacements
1736 )) {
1737 return WalkResult::advance();
1738 }
1739 }
1740 return WalkResult::interrupt();
1741 });
1742 return result.wasInterrupted();
1743}
1744
1751class TypeVarInferenceCollector {
1753 TemplateInferenceInfo &inferenceInfo;
1755 DenseMap<StringAttr, InferredType> &replacements;
1757 DenseMap<Value, InferredType> byValue;
1759 DenseMap<StringAttr, DenseSet<StringAttr>> paramRelations;
1761 bool changedInIteration = false;
1762
1763public:
1765 TypeVarInferenceCollector(
1766 TemplateInferenceInfo &info, DenseMap<StringAttr, InferredType> &scopeReplacements
1767 )
1768 : inferenceInfo(info), replacements(scopeReplacements) {}
1769
1771 LogicalResult collect(Operation *root) {
1772 do {
1773 changedInIteration = false;
1774 auto result = root->walk([this](UnifiableCastOp castOp) -> WalkResult {
1775 return collectTypePairInferences(
1776 castOp.getInput().getType(), castOp.getResult().getType(), castOp.getInput(),
1777 castOp.getResult(), castOp.getLoc()
1778 );
1779 });
1780 if (result.wasInterrupted()) {
1781 return failure();
1782 }
1783 } while (changedInIteration);
1784 return success();
1785 }
1786
1788 LogicalResult collectTypeInferences(Type lhs, Type rhs, Location loc) {
1789 do {
1790 changedInIteration = false;
1791 if (failed(collectTypePairInferences(lhs, rhs, Value(), Value(), loc))) {
1792 return failure();
1793 }
1794 } while (changedInIteration);
1795 return success();
1796 }
1797
1806 LogicalResult collectStructTemplateParamInferences(
1807 Operation *root, ModuleOp module, DenseMap<Operation *, TemplateInferenceInfo *> &infos,
1808 SymbolTableCollection &tables
1809 ) {
1810 auto result = root->walk([&](Operation *op) -> WalkResult {
1811 return collectOperationStructTemplateParamInferences(op, module, infos, tables);
1812 });
1813 return failure(result.wasInterrupted());
1814 }
1815
1817 LogicalResult collectOperationStructTemplateParamInferences(
1818 Operation *op, ModuleOp module, DenseMap<Operation *, TemplateInferenceInfo *> &infos,
1819 SymbolTableCollection &tables
1820 ) {
1821 Location loc = op->getLoc();
1822
1823 if (auto func = llvm::dyn_cast<FuncDefOp>(op)) {
1824 if (failed(collectStructTemplateParamInferences(
1825 func.getFunctionType(), loc, module, infos, tables
1826 ))) {
1827 return failure();
1828 }
1829 }
1830
1831 for (Region &region : op->getRegions()) {
1832 for (Block &block : region.getBlocks()) {
1833 for (Type argTy : block.getArgumentTypes()) {
1834 if (failed(collectStructTemplateParamInferences(argTy, loc, module, infos, tables))) {
1835 return failure();
1836 }
1837 }
1838 }
1839 }
1840
1841 for (Type resultTy : op->getResultTypes()) {
1842 if (failed(collectStructTemplateParamInferences(resultTy, loc, module, infos, tables))) {
1843 return failure();
1844 }
1845 }
1846
1847 if (auto callOp = llvm::dyn_cast<CallOp>(op)) {
1848 if (failed(collectCallableTemplateParamInferences(callOp, infos, tables))) {
1849 return failure();
1850 }
1851 }
1852 if (auto includeOp = llvm::dyn_cast<verif::IncludeOp>(op)) {
1853 if (failed(collectIncludeTemplateParamInferences(includeOp, infos, tables))) {
1854 return failure();
1855 }
1856 }
1857
1858 for (NamedAttribute attr : op->getAttrs()) {
1859 if (failed(
1860 collectStructTemplateParamInferences(attr.getValue(), loc, module, infos, tables)
1861 )) {
1862 return failure();
1863 }
1864 }
1865 return success();
1866 }
1867
1868private:
1874 LogicalResult collectStructTemplateParamInferences(
1875 Attribute attr, Location loc, ModuleOp module,
1876 DenseMap<Operation *, TemplateInferenceInfo *> &infos, SymbolTableCollection &tables
1877 ) {
1878 if (!attr) {
1879 return success();
1880 }
1881 if (auto tyAttr = llvm::dyn_cast<TypeAttr>(attr)) {
1882 return collectStructTemplateParamInferences(tyAttr.getValue(), loc, module, infos, tables);
1883 }
1884 if (auto arrAttr = llvm::dyn_cast<ArrayAttr>(attr)) {
1885 for (Attribute nested : arrAttr.getValue()) {
1886 if (failed(collectStructTemplateParamInferences(nested, loc, module, infos, tables))) {
1887 return failure();
1888 }
1889 }
1890 }
1891 return success();
1892 }
1893
1902 LogicalResult collectStructTemplateParamInferences(
1903 Type ty, Location loc, ModuleOp module, DenseMap<Operation *, TemplateInferenceInfo *> &infos,
1904 SymbolTableCollection &tables
1905 ) {
1906 if (!ty) {
1907 return success();
1908 }
1909 if (auto arrTy = llvm::dyn_cast<ArrayType>(ty)) {
1910 return collectStructTemplateParamInferences(
1911 arrTy.getElementType(), loc, module, infos, tables
1912 );
1913 }
1914 if (auto podTy = llvm::dyn_cast<PodType>(ty)) {
1915 for (RecordAttr record : podTy.getRecords()) {
1916 if (failed(
1917 collectStructTemplateParamInferences(record.getType(), loc, module, infos, tables)
1918 )) {
1919 return failure();
1920 }
1921 }
1922 return success();
1923 }
1924 if (auto funcTy = llvm::dyn_cast<FunctionType>(ty)) {
1925 for (Type inputTy : funcTy.getInputs()) {
1926 if (failed(collectStructTemplateParamInferences(inputTy, loc, module, infos, tables))) {
1927 return failure();
1928 }
1929 }
1930 for (Type resultTy : funcTy.getResults()) {
1931 if (failed(collectStructTemplateParamInferences(resultTy, loc, module, infos, tables))) {
1932 return failure();
1933 }
1934 }
1935 return success();
1936 }
1937 auto structTy = llvm::dyn_cast<StructType>(ty);
1938 if (!structTy) {
1939 return success();
1940 }
1941
1942 ArrayAttr params = structTy.getParams();
1943 if (!params) {
1944 return success();
1945 }
1946 for (Attribute attr : params.getValue()) {
1947 if (failed(collectStructTemplateParamInferences(attr, loc, module, infos, tables))) {
1948 return failure();
1949 }
1950 }
1951
1952 FailureOr<SymbolLookupResult<StructDefOp>> lookup =
1953 structTy.getDefinition(tables, module, /*reportMissing=*/false);
1954 if (failed(lookup)) {
1955 return success();
1956 }
1957 TemplateOp parentTemplate = getParentOfType<TemplateOp>(lookup->get().getOperation());
1958 if (!parentTemplate) {
1959 return success();
1960 }
1961 TemplateInferenceInfo *targetInfo = infos.lookup(parentTemplate.getOperation());
1962 if (!targetInfo || targetInfo->replacements.empty() ||
1963 params.size() != targetInfo->oldParamOrder.size()) {
1964 return success();
1965 }
1966
1967 for (auto [paramName, attr] : llvm::zip_equal(targetInfo->oldParamOrder, params.getValue())) {
1968 auto replacementIt = targetInfo->replacements.find(paramName);
1969 if (replacementIt == targetInfo->replacements.end()) {
1970 continue;
1971 }
1972 Type expectedTy = substituteExplicitCallTemplateParams(
1973 replacementIt->second.type, params, targetInfo->oldParamOrder
1974 );
1975 Attribute expectedAttr = TypeAttr::get(expectedTy);
1976 if (failed(collectTemplateArgInferences(attr, expectedAttr, loc))) {
1977 return failure();
1978 }
1979 }
1980 return success();
1981 }
1982
1989 SmallVector<Attribute>
1990 getInferredOmittedTemplateArgs(const UnificationMap &unifyResult, StringAttr targetParamName) {
1991 SmallVector<Attribute> inferredAttrs;
1992 auto targetRef = FlatSymbolRefAttr::get(targetParamName);
1993 auto inferredIt = unifyResult.find({targetRef, Side::RHS});
1994 if (inferredIt != unifyResult.end()) {
1995 inferredAttrs.push_back(inferredIt->second);
1996 }
1997 for (const auto &entry : unifyResult) {
1998 if (entry.first.second == Side::LHS && entry.second == targetRef) {
1999 inferredAttrs.push_back(entry.first.first);
2000 }
2001 }
2002 return inferredAttrs;
2003 }
2004
2006 template <typename TargetOp>
2007 TemplateInferenceInfo *
2008 getTargetTemplateInfo(TargetOp targetOp, DenseMap<Operation *, TemplateInferenceInfo *> &infos) {
2009 TemplateOp parentTemplate = getParentOfType<TemplateOp>(targetOp.getOperation());
2010 if (!parentTemplate) {
2011 return nullptr;
2012 }
2013 TemplateInferenceInfo *targetInfo = infos.lookup(parentTemplate.getOperation());
2014 if (!targetInfo || targetInfo->replacements.empty()) {
2015 return nullptr;
2016 }
2017 return targetInfo;
2018 }
2019
2027 template <typename CallableOp>
2028 LogicalResult collectCallableUseTemplateParamInferences(
2029 CallableOp callableOp, FunctionType targetSignature, TemplateInferenceInfo &targetInfo
2030 ) {
2031 ArrayAttr params = callableOp.getTemplateParamsAttr();
2032 if (!isNullOrEmpty(params)) {
2033 if (params.size() != targetInfo.oldParamOrder.size()) {
2034 return success();
2035 }
2036 struct DeferredExplicitArgInference {
2037 Attribute attr;
2038 Attribute expectedAttr;
2039 SmallVector<StringAttr> mentionedParams;
2040 };
2041 SmallVector<DeferredExplicitArgInference> deferredExplicitArgInferences;
2042 bool deferredToSignature = false;
2043 for (auto [paramName, attr] : llvm::zip_equal(targetInfo.oldParamOrder, params)) {
2044 auto replacementIt = targetInfo.replacements.find(paramName);
2045 if (replacementIt == targetInfo.replacements.end()) {
2046 continue;
2047 }
2048 Type expectedTy = substituteExplicitCallTemplateParams(
2049 replacementIt->second.type, params, targetInfo.oldParamOrder
2050 );
2051 Attribute expectedAttr = TypeAttr::get(expectedTy);
2052 if (attrMentionsAnyTypeVarParam(attr, inferenceInfo.typeVarParams)) {
2053 deferredToSignature = true;
2054 SmallVector<StringAttr> mentionedParams =
2055 getMentionedTypeVarParams(attr, inferenceInfo.typeVarParams);
2056 if (!ParamMentionChecker(paramName).typeMentions(targetSignature)) {
2057 if (failed(collectTemplateArgInferences(attr, expectedAttr, callableOp.getLoc()))) {
2058 return failure();
2059 }
2060 } else {
2061 deferredExplicitArgInferences.push_back(
2062 {attr, expectedAttr, std::move(mentionedParams)}
2063 );
2064 }
2065 continue;
2066 }
2067 if (failed(collectTemplateArgInferences(attr, expectedAttr, callableOp.getLoc()))) {
2068 return failure();
2069 }
2070 }
2071 if (deferredToSignature) {
2072 DenseMap<StringAttr, Type> targetReplacements;
2073 for (const auto &entry : targetInfo.replacements) {
2074 targetReplacements.try_emplace(entry.first, entry.second.type);
2075 }
2076 TypeVarReplacementConverter converter(
2077 callableOp.getContext(), targetInfo.templatePath, targetInfo.oldParamOrder,
2078 targetReplacements, /*trimResolvedParams=*/false
2079 );
2080 Type rewrittenTy = substituteExplicitCallTemplateParams(
2081 converter.convertType(targetSignature), params, targetInfo.oldParamOrder
2082 );
2083 if (failed(collectTypeInferences(
2084 callableOp.getTypeSignature(), llvm::cast<FunctionType>(rewrittenTy),
2085 callableOp.getLoc()
2086 ))) {
2087 return failure();
2088 }
2089
2090 for (const DeferredExplicitArgInference &deferred : deferredExplicitArgInferences) {
2091 bool signatureCanInferAllParams =
2092 llvm::all_of(deferred.mentionedParams, [&](StringAttr mentionedParam) {
2093 return ParamMentionChecker(mentionedParam).typeMentions(callableOp.getTypeSignature());
2094 });
2095 bool signatureHasInferredAllParams =
2096 llvm::all_of(deferred.mentionedParams, [&](StringAttr mentionedParam) {
2097 return replacements.contains(mentionedParam);
2098 });
2099 if (signatureCanInferAllParams && signatureHasInferredAllParams) {
2100 continue;
2101 }
2102 if (failed(collectTemplateArgInferences(
2103 deferred.attr, deferred.expectedAttr, callableOp.getLoc()
2104 ))) {
2105 return failure();
2106 }
2107 }
2108 }
2109 return success();
2110 }
2111
2112 FailureOr<UnificationMap> unifyResult = callableOp.unifyTypeSignature(targetSignature);
2113 if (failed(unifyResult)) {
2114 return success();
2115 }
2116
2117 for (StringAttr paramName : targetInfo.oldParamOrder) {
2118 auto replacementIt = targetInfo.replacements.find(paramName);
2119 if (replacementIt == targetInfo.replacements.end()) {
2120 continue;
2121 }
2122 SmallVector<Attribute> inferredAttrs =
2123 getInferredOmittedTemplateArgs(*unifyResult, paramName);
2124 if (inferredAttrs.empty()) {
2125 continue;
2126 }
2127
2128 Attribute expectedAttr = TypeAttr::get(replacementIt->second.type);
2129 for (Attribute inferredAttr : inferredAttrs) {
2130 if (!inferredAttr || !typeParamsUnify({inferredAttr}, {expectedAttr})) {
2131 InFlightDiagnostic diag = callableOp.emitError()
2132 << "implicit template argument for inferred parameter @"
2133 << paramName.getValue() << " must match inferred type "
2134 << replacementIt->second.type << ", but found ";
2135 if (auto typeAttr = llvm::dyn_cast_if_present<TypeAttr>(inferredAttr)) {
2136 diag << typeAttr.getValue();
2137 } else {
2138 diag << inferredAttr;
2139 }
2140 return diag;
2141 }
2142 if (failed(collectTemplateArgInferences(inferredAttr, expectedAttr, callableOp.getLoc()))) {
2143 return failure();
2144 }
2145 }
2146 }
2147 return success();
2148 }
2149
2159 LogicalResult collectCallableTemplateParamInferences(
2160 CallOp callOp, DenseMap<Operation *, TemplateInferenceInfo *> &infos,
2161 SymbolTableCollection &tables
2162 ) {
2163 FailureOr<SymbolLookupResult<FuncDefOp>> target = callOp.getCalleeTarget(tables);
2164 if (failed(target)) {
2165 return success();
2166 }
2167 FuncDefOp targetFunc = target->get();
2168 TemplateInferenceInfo *targetInfo = getTargetTemplateInfo(targetFunc, infos);
2169 if (!targetInfo) {
2170 return success();
2171 }
2172 return collectCallableUseTemplateParamInferences(
2173 callOp, targetFunc.getFunctionType(), *targetInfo
2174 );
2175 }
2176
2183 LogicalResult collectIncludeTemplateParamInferences(
2184 verif::IncludeOp includeOp, DenseMap<Operation *, TemplateInferenceInfo *> &infos,
2185 SymbolTableCollection &tables
2186 ) {
2187 FailureOr<SymbolLookupResult<verif::ContractOp>> target = includeOp.getCalleeTarget(tables);
2188 if (failed(target)) {
2189 return success();
2190 }
2191 verif::ContractOp targetContract = target->get();
2192 TemplateInferenceInfo *targetInfo = getTargetTemplateInfo(targetContract, infos);
2193 if (!targetInfo) {
2194 return success();
2195 }
2196 return collectCallableUseTemplateParamInferences(
2197 includeOp, targetContract.getFunctionType(), *targetInfo
2198 );
2199 }
2200
2214 LogicalResult recordInference(StringAttr paramName, Type inferredTy, Value value, Location loc) {
2215 if (!inferenceInfo.typeVarParams.contains(paramName)) {
2216 return success();
2217 }
2218 if (auto inferredTvar = llvm::dyn_cast<TypeVarType>(inferredTy)) {
2219 return recordParamRelation(paramName, inferredTvar.getNameRef().getAttr(), loc);
2220 }
2221 if (!isTypeVarFreeType(inferredTy)) {
2222 return success();
2223 }
2224
2225 auto reportConflict = [&](StringRef kind, Location originalLoc, Type originalTy) {
2226 InFlightDiagnostic diag = emitError(loc) << "conflicting inferred type for " << kind << " @"
2227 << paramName.getValue() << ": " << originalTy
2228 << " vs " << inferredTy;
2229 diag.attachNote(originalLoc) << "previous inference here";
2230 return diag;
2231 };
2232
2233 auto byParamIt = replacements.find(paramName);
2234 bool learnedParamInference = false;
2235 if (byParamIt == replacements.end()) {
2236 replacements.try_emplace(paramName, InferredType {inferredTy, loc});
2237 changedInIteration = true;
2238 learnedParamInference = true;
2239 } else if (byParamIt->second.type != inferredTy) {
2240 return reportConflict("template parameter", byParamIt->second.loc, byParamIt->second.type);
2241 }
2242
2243 if (value) {
2244 auto byValueIt = byValue.find(value);
2245 if (byValueIt == byValue.end()) {
2246 byValue.try_emplace(value, InferredType {inferredTy, loc});
2247 changedInIteration = true;
2248 } else if (byValueIt->second.type != inferredTy) {
2249 return reportConflict("SSA value using", byValueIt->second.loc, byValueIt->second.type);
2250 }
2251 }
2252
2253 if (learnedParamInference) {
2254 auto relatedIt = paramRelations.find(paramName);
2255 if (relatedIt != paramRelations.end()) {
2256 for (StringAttr relatedParam : relatedIt->second) {
2257 if (failed(recordInference(relatedParam, inferredTy, Value(), loc))) {
2258 return failure();
2259 }
2260 }
2261 }
2262 }
2263 return success();
2264 }
2265
2267 LogicalResult recordParamRelation(StringAttr lhsParam, StringAttr rhsParam, Location loc) {
2268 if (lhsParam == rhsParam || !inferenceInfo.typeVarParams.contains(rhsParam)) {
2269 return success();
2270 }
2271
2272 bool inserted = paramRelations[lhsParam].insert(rhsParam).second;
2273 inserted |= paramRelations[rhsParam].insert(lhsParam).second;
2274 if (!inserted) {
2275 return success();
2276 }
2277
2278 changedInIteration = true;
2279
2280 auto lhsIt = replacements.find(lhsParam);
2281 if (lhsIt != replacements.end() &&
2282 failed(recordInference(rhsParam, lhsIt->second.type, Value(), loc))) {
2283 return failure();
2284 }
2285 auto rhsIt = replacements.find(rhsParam);
2286 if (rhsIt != replacements.end() &&
2287 failed(recordInference(lhsParam, rhsIt->second.type, Value(), loc))) {
2288 return failure();
2289 }
2290 return success();
2291 }
2292
2300 LogicalResult
2301 collectTypePairInferences(Type lhs, Type rhs, Value lhsValue, Value rhsValue, Location loc) {
2302 if (auto lhsTvar = llvm::dyn_cast<TypeVarType>(lhs)) {
2303 if (failed(recordInference(lhsTvar.getNameRef().getAttr(), rhs, lhsValue, loc))) {
2304 return failure();
2305 }
2306 if (rhsValue) {
2307 auto rhsValueIt = byValue.find(rhsValue);
2308 if (rhsValueIt != byValue.end() &&
2309 failed(recordInference(
2310 lhsTvar.getNameRef().getAttr(), rhsValueIt->second.type, lhsValue, loc
2311 ))) {
2312 return failure();
2313 }
2314 }
2315 }
2316 if (auto rhsTvar = llvm::dyn_cast<TypeVarType>(rhs)) {
2317 if (failed(recordInference(rhsTvar.getNameRef().getAttr(), lhs, rhsValue, loc))) {
2318 return failure();
2319 }
2320 if (lhsValue) {
2321 auto lhsValueIt = byValue.find(lhsValue);
2322 if (lhsValueIt != byValue.end() &&
2323 failed(recordInference(
2324 rhsTvar.getNameRef().getAttr(), lhsValueIt->second.type, rhsValue, loc
2325 ))) {
2326 return failure();
2327 }
2328 }
2329 }
2330
2331 if (auto lhsArr = llvm::dyn_cast<ArrayType>(lhs)) {
2332 if (auto rhsArr = llvm::dyn_cast<ArrayType>(rhs)) {
2333 return collectTypePairInferences(
2334 lhsArr.getElementType(), rhsArr.getElementType(), Value(), Value(), loc
2335 );
2336 }
2337 }
2338
2339 if (auto lhsStruct = llvm::dyn_cast<StructType>(lhs)) {
2340 if (auto rhsStruct = llvm::dyn_cast<StructType>(rhs)) {
2341 ArrayRef<Attribute> lhsParams =
2342 lhsStruct.getParams() ? lhsStruct.getParams().getValue() : ArrayRef<Attribute> {};
2343 ArrayRef<Attribute> rhsParams =
2344 rhsStruct.getParams() ? rhsStruct.getParams().getValue() : ArrayRef<Attribute> {};
2345 if (lhsParams.size() != rhsParams.size()) {
2346 return success();
2347 }
2348 for (auto [lhsAttr, rhsAttr] : llvm::zip_equal(lhsParams, rhsParams)) {
2349 if (failed(collectTemplateArgInferences(lhsAttr, rhsAttr, loc))) {
2350 return failure();
2351 }
2352 }
2353 }
2354 }
2355
2356 if (auto lhsPod = llvm::dyn_cast<PodType>(lhs)) {
2357 if (auto rhsPod = llvm::dyn_cast<PodType>(rhs)) {
2358 ArrayRef<RecordAttr> lhsRecords = lhsPod.getRecords();
2359 ArrayRef<RecordAttr> rhsRecords = rhsPod.getRecords();
2360 if (lhsRecords.size() != rhsRecords.size()) {
2361 return success();
2362 }
2363 for (auto [lhsRecord, rhsRecord] : llvm::zip_equal(lhsRecords, rhsRecords)) {
2364 if (lhsRecord.getName() != rhsRecord.getName()) {
2365 return success();
2366 }
2367 if (failed(collectTypePairInferences(
2368 lhsRecord.getType(), rhsRecord.getType(), Value(), Value(), loc
2369 ))) {
2370 return failure();
2371 }
2372 }
2373 }
2374 }
2375
2376 if (auto lhsFunc = llvm::dyn_cast<FunctionType>(lhs)) {
2377 if (auto rhsFunc = llvm::dyn_cast<FunctionType>(rhs)) {
2378 if (lhsFunc.getNumInputs() != rhsFunc.getNumInputs() ||
2379 lhsFunc.getNumResults() != rhsFunc.getNumResults()) {
2380 return success();
2381 }
2382 for (auto [lhsInput, rhsInput] :
2383 llvm::zip_equal(lhsFunc.getInputs(), rhsFunc.getInputs())) {
2384 if (failed(collectTypePairInferences(lhsInput, rhsInput, Value(), Value(), loc))) {
2385 return failure();
2386 }
2387 }
2388 for (auto [lhsResult, rhsResult] :
2389 llvm::zip_equal(lhsFunc.getResults(), rhsFunc.getResults())) {
2390 if (failed(collectTypePairInferences(lhsResult, rhsResult, Value(), Value(), loc))) {
2391 return failure();
2392 }
2393 }
2394 }
2395 }
2396
2397 return success();
2398 }
2399
2407 LogicalResult collectTemplateArgInferences(Attribute lhsAttr, Attribute rhsAttr, Location loc) {
2408 auto lhsTyAttr = llvm::dyn_cast<TypeAttr>(lhsAttr);
2409 auto rhsTyAttr = llvm::dyn_cast<TypeAttr>(rhsAttr);
2410 if (lhsTyAttr && rhsTyAttr) {
2411 return collectTypePairInferences(
2412 lhsTyAttr.getValue(), rhsTyAttr.getValue(), Value(), Value(), loc
2413 );
2414 }
2415
2416 if (StringAttr lhsSymbolName = getFlatSymbolName(lhsAttr)) {
2417 if (StringAttr rhsSymbolName = getFlatSymbolName(rhsAttr)) {
2418 return recordParamRelation(lhsSymbolName, rhsSymbolName, loc);
2419 }
2420 if (rhsTyAttr && failed(recordInference(lhsSymbolName, rhsTyAttr.getValue(), Value(), loc))) {
2421 return failure();
2422 }
2423 }
2424 if (StringAttr rhsSymbolName = getFlatSymbolName(rhsAttr)) {
2425 if (lhsTyAttr && failed(recordInference(rhsSymbolName, lhsTyAttr.getValue(), Value(), loc))) {
2426 return failure();
2427 }
2428 }
2429 return success();
2430 }
2431};
2432
2434static DenseMap<StringAttr, Type>
2435getFunctionProofReplacements(const TemplateInferenceInfo &info, FuncDefOp func) {
2436 DenseMap<StringAttr, Type> replacements;
2437 auto funcIt = info.functionReplacements.find(func.getOperation());
2438 if (funcIt == info.functionReplacements.end()) {
2439 return replacements;
2440 }
2441 for (const auto &entry : funcIt->second) {
2442 replacements.try_emplace(entry.first, entry.second.type);
2443 }
2444 return replacements;
2445}
2446
2448static std::optional<unsigned>
2449getParamIndex(ArrayRef<StringAttr> paramOrder, StringAttr paramName) {
2450 for (auto indexedParam : llvm::enumerate(paramOrder)) {
2451 if (indexedParam.value() == paramName) {
2452 return indexedParam.index();
2453 }
2454 }
2455 return std::nullopt;
2456}
2457
2460static DenseMap<Attribute, Attribute>
2461buildParamNameToConcrete(const DenseMap<StringAttr, Type> &replacements) {
2462 DenseMap<Attribute, Attribute> paramNameToConcrete;
2463 for (const auto &entry : replacements) {
2464 paramNameToConcrete.try_emplace(
2465 FlatSymbolRefAttr::get(entry.first), TypeAttr::get(entry.second)
2466 );
2467 }
2468 return paramNameToConcrete;
2469}
2470
2474static bool callParamsMatchReplacements(
2475 ArrayAttr callParams, ArrayRef<StringAttr> oldParamOrder,
2476 const DenseMap<StringAttr, Type> &replacements,
2477 const DenseMap<StringAttr, InferredType> *wildcardAllowedReplacements = nullptr
2478) {
2479 if (!callParams || callParams.size() != oldParamOrder.size()) {
2480 return false;
2481 }
2482 for (const auto &entry : replacements) {
2483 std::optional<unsigned> index = getParamIndex(oldParamOrder, entry.first);
2484 if (!index) {
2485 return false;
2486 }
2487 Type expectedTy = substituteExplicitCallTypeArgs(entry.second, callParams, oldParamOrder);
2488 Attribute attr = callParams[*index];
2489 if (auto intAttr = llvm::dyn_cast<IntegerAttr>(attr);
2490 intAttr && isDynamic(intAttr) && wildcardAllowedReplacements &&
2491 wildcardAllowedReplacements->contains(entry.first)) {
2492 continue;
2493 }
2494 if (!templateArgUnifiesWithType(attr, expectedTy)) {
2495 return false;
2496 }
2497 }
2498 return true;
2499}
2500
2507static LogicalResult diagnoseCallParamsMismatch(
2508 Operation *callableOp, ArrayAttr callParams, ArrayRef<StringAttr> oldParamOrder,
2509 const DenseMap<StringAttr, Type> &replacements,
2510 const DenseMap<StringAttr, InferredType> *wildcardAllowedReplacements, bool explicitCallParams
2511) {
2512 if (callParamsMatchReplacements(
2513 callParams, oldParamOrder, replacements, wildcardAllowedReplacements
2514 )) {
2515 return success();
2516 }
2517 for (const auto &entry : replacements) {
2518 std::optional<unsigned> index = getParamIndex(oldParamOrder, entry.first);
2519 if (!index || !callParams || *index >= callParams.size()) {
2520 continue;
2521 }
2522 Attribute attr = callParams[*index];
2523 Type expectedTy = substituteExplicitCallTypeArgs(entry.second, callParams, oldParamOrder);
2524 if (auto intAttr = llvm::dyn_cast<IntegerAttr>(attr);
2525 intAttr && isDynamic(intAttr) && wildcardAllowedReplacements &&
2526 wildcardAllowedReplacements->contains(entry.first)) {
2527 continue;
2528 }
2529 if (templateArgUnifiesWithType(attr, expectedTy)) {
2530 continue;
2531 }
2532
2533 InFlightDiagnostic diag = callableOp->emitError()
2534 << (explicitCallParams ? "explicit" : "implicit")
2535 << " template argument for inferred parameter @"
2536 << entry.first.getValue() << " must match inferred type "
2537 << expectedTy << ", but found ";
2538 if (auto typeAttr = llvm::dyn_cast<TypeAttr>(attr)) {
2539 diag << typeAttr.getValue();
2540 } else {
2541 diag << attr;
2542 }
2543 return diag;
2544 }
2545 return callableOp->emitError() << (explicitCallParams ? "explicit" : "implicit")
2546 << " template arguments do not match inferred callee types";
2547}
2548
2552static ArrayAttr expandCurrentTemplateParamsToOriginalOrder(
2553 ArrayAttr callParams, const TemplateInferenceInfo &info
2554) {
2555 if (!callParams || callParams.size() == info.oldParamOrder.size()) {
2556 return callParams;
2557 }
2558
2559 unsigned currentParamCount = llvm::count_if(info.oldParamOrder, [&info](StringAttr paramName) {
2560 return !info.replacements.contains(paramName);
2561 });
2562 if (callParams.size() != currentParamCount) {
2563 return callParams;
2564 }
2565
2566 SmallVector<Attribute> expandedParams;
2567 expandedParams.reserve(info.oldParamOrder.size());
2568 unsigned currentIndex = 0;
2569 for (StringAttr paramName : info.oldParamOrder) {
2570 auto replacementIt = info.replacements.find(paramName);
2571 if (replacementIt != info.replacements.end()) {
2572 expandedParams.push_back(TypeAttr::get(replacementIt->second.type));
2573 continue;
2574 }
2575 expandedParams.push_back(callParams[currentIndex++]);
2576 }
2577 return ArrayAttr::get(callParams.getContext(), expandedParams);
2578}
2579
2586template <typename TargetOp>
2587static DenseMap<StringAttr, Type> getConcreteCallSiteReplacements(
2588 ArrayAttr callParams, const TemplateInferenceInfo &info, TargetOp targetOp
2589) {
2590 DenseMap<StringAttr, Type> replacements;
2591 if (!callParams || callParams.size() != info.oldParamOrder.size()) {
2592 return replacements;
2593 }
2594
2595 bool sawResidualReplacement = false;
2596 for (const auto &entry : info.typeVarParams) {
2597 StringAttr paramName = entry.first;
2598 if (info.replacements.contains(paramName)) {
2599 continue;
2600 }
2601 if (!targetMentionsParam(targetOp, paramName)) {
2602 continue;
2603 }
2604 std::optional<unsigned> index = getParamIndex(info.oldParamOrder, paramName);
2605 assert(index && "eligible type-variable parameter must appear in parameter order");
2606 Attribute attr = callParams[*index];
2607 auto tyAttr = llvm::dyn_cast<TypeAttr>(attr);
2608 if (!tyAttr) {
2609 return DenseMap<StringAttr, Type>();
2610 }
2611 replacements.try_emplace(paramName, tyAttr.getValue());
2612 sawResidualReplacement = true;
2613 }
2614 if (!sawResidualReplacement) {
2615 return DenseMap<StringAttr, Type>();
2616 }
2617
2618 for (const auto &entry : info.replacements) {
2619 replacements.try_emplace(entry.first, entry.second.type);
2620 }
2621
2622 TypeVarReplacementConverter converter(
2623 info.templatePath.front().getContext(), info.templatePath, info.oldParamOrder, replacements,
2624 /*trimResolvedParams=*/false
2625 );
2626 for (const auto &entry : replacements) {
2627 if (!isTypeVarFreeType(converter.convertType(entry.second))) {
2628 return DenseMap<StringAttr, Type>();
2629 }
2630 }
2631 return replacements;
2632}
2633
2635static bool isWildcardTemplateArg(Attribute attr) {
2636 auto intAttr = llvm::dyn_cast_if_present<IntegerAttr>(attr);
2637 return intAttr && isDynamic(intAttr);
2638}
2639
2641static void appendUniqueType(SmallVectorImpl<Type> &types, Type ty) {
2642 if (!llvm::any_of(types, [ty](Type existing) { return existing == ty; })) {
2643 types.push_back(ty);
2644 }
2645}
2646
2648static void collectRhsTypeVarCandidates(
2649 Type lhsTy, Type rhsTy, SymbolRefAttr targetRef, SmallVectorImpl<Type> &candidates
2650);
2651
2653static void collectRhsTypeVarCandidates(
2654 ArrayRef<Attribute> lhsAttrs, ArrayRef<Attribute> rhsAttrs, SymbolRefAttr targetRef,
2655 SmallVectorImpl<Type> &candidates
2656) {
2657 if (lhsAttrs.size() != rhsAttrs.size()) {
2658 return;
2659 }
2660 for (auto [lhsAttr, rhsAttr] : llvm::zip_equal(lhsAttrs, rhsAttrs)) {
2661 collectRhsTypeVarCandidates(lhsAttr, rhsAttr, targetRef, candidates);
2662 }
2663}
2664
2666static void collectRhsTypeVarCandidates(
2667 Attribute lhsAttr, Attribute rhsAttr, SymbolRefAttr targetRef, SmallVectorImpl<Type> &candidates
2668) {
2669 if (auto rhsTypeAttr = llvm::dyn_cast_if_present<TypeAttr>(rhsAttr)) {
2670 if (auto lhsTypeAttr = llvm::dyn_cast_if_present<TypeAttr>(lhsAttr)) {
2671 collectRhsTypeVarCandidates(
2672 lhsTypeAttr.getValue(), rhsTypeAttr.getValue(), targetRef, candidates
2673 );
2674 }
2675 return;
2676 }
2677
2678 auto lhsArray = llvm::dyn_cast_if_present<ArrayAttr>(lhsAttr);
2679 auto rhsArray = llvm::dyn_cast_if_present<ArrayAttr>(rhsAttr);
2680 if (!lhsArray || !rhsArray || lhsArray.size() != rhsArray.size()) {
2681 return;
2682 }
2683 collectRhsTypeVarCandidates(lhsArray.getValue(), rhsArray.getValue(), targetRef, candidates);
2684}
2685
2691static void collectRhsTypeVarCandidates(
2692 Type lhsTy, Type rhsTy, SymbolRefAttr targetRef, SmallVectorImpl<Type> &candidates
2693) {
2694 if (auto rhsTvar = llvm::dyn_cast<TypeVarType>(rhsTy);
2695 rhsTvar && rhsTvar.getNameRef() == targetRef) {
2696 appendUniqueType(candidates, lhsTy);
2697 return;
2698 }
2699
2700 if (auto lhsArray = llvm::dyn_cast<ArrayType>(lhsTy)) {
2701 if (auto rhsArray = llvm::dyn_cast<ArrayType>(rhsTy)) {
2702 collectRhsTypeVarCandidates(
2703 lhsArray.getElementType(), rhsArray.getElementType(), targetRef, candidates
2704 );
2705 collectRhsTypeVarCandidates(
2706 lhsArray.getDimensionSizes(), rhsArray.getDimensionSizes(), targetRef, candidates
2707 );
2708 }
2709 return;
2710 }
2711
2712 if (auto lhsStruct = llvm::dyn_cast<StructType>(lhsTy)) {
2713 if (auto rhsStruct = llvm::dyn_cast<StructType>(rhsTy)) {
2714 collectRhsTypeVarCandidates(
2715 lhsStruct.getParams(), rhsStruct.getParams(), targetRef, candidates
2716 );
2717 }
2718 return;
2719 }
2720
2721 if (auto lhsPod = llvm::dyn_cast<PodType>(lhsTy)) {
2722 if (auto rhsPod = llvm::dyn_cast<PodType>(rhsTy);
2723 rhsPod && lhsPod.getRecords().size() == rhsPod.getRecords().size()) {
2724 for (auto [lhsRecord, rhsRecord] :
2725 llvm::zip_equal(lhsPod.getRecords(), rhsPod.getRecords())) {
2726 collectRhsTypeVarCandidates(
2727 lhsRecord.getType(), rhsRecord.getType(), targetRef, candidates
2728 );
2729 }
2730 }
2731 return;
2732 }
2733
2734 if (auto lhsFunc = llvm::dyn_cast<FunctionType>(lhsTy)) {
2735 if (auto rhsFunc = llvm::dyn_cast<FunctionType>(rhsTy)) {
2736 for (auto [lhsInput, rhsInput] : llvm::zip_equal(lhsFunc.getInputs(), rhsFunc.getInputs())) {
2737 collectRhsTypeVarCandidates(lhsInput, rhsInput, targetRef, candidates);
2738 }
2739 for (auto [lhsResult, rhsResult] :
2740 llvm::zip_equal(lhsFunc.getResults(), rhsFunc.getResults())) {
2741 collectRhsTypeVarCandidates(lhsResult, rhsResult, targetRef, candidates);
2742 }
2743 }
2744 }
2745}
2746
2748static SmallVector<Type>
2749getConflictingRhsTypeVarCandidates(FunctionType lhs, FunctionType rhs, SymbolRefAttr targetRef) {
2750 SmallVector<Type> candidates;
2751 if (lhs.getNumInputs() != rhs.getNumInputs() || lhs.getNumResults() != rhs.getNumResults()) {
2752 return candidates;
2753 }
2754 for (auto [lhsInput, rhsInput] : llvm::zip_equal(lhs.getInputs(), rhs.getInputs())) {
2755 collectRhsTypeVarCandidates(lhsInput, rhsInput, targetRef, candidates);
2756 }
2757 for (auto [lhsResult, rhsResult] : llvm::zip_equal(lhs.getResults(), rhs.getResults())) {
2758 collectRhsTypeVarCandidates(lhsResult, rhsResult, targetRef, candidates);
2759 }
2760 return candidates;
2761}
2762
2764template <typename CallableOp, typename TargetOp>
2765static LogicalResult
2766emitConflictingInferredTypes(CallableOp callableOp, TargetOp targetOp, StringAttr paramName) {
2767 InFlightDiagnostic diag = callableOp.emitError()
2768 << "conflicting inferred types for @" << paramName.getValue();
2769 SmallVector<Type> candidates = getConflictingRhsTypeVarCandidates(
2770 callableOp.getTypeSignature(), targetOp.getFunctionType(), FlatSymbolRefAttr::get(paramName)
2771 );
2772 if (candidates.size() >= 2) {
2773 diag << ": " << candidates.front();
2774 for (Type candidate : llvm::drop_begin(candidates)) {
2775 diag << " vs " << candidate;
2776 }
2777 }
2778 return diag;
2779}
2780
2782template <typename CallableOp, typename TargetOp>
2783static FailureOr<Attribute> getInferredRhsTemplateArg(
2784 CallableOp callableOp, TargetOp targetOp, const UnificationMap &unifyResult,
2785 StringAttr paramName, StringRef argKind
2786) {
2787 auto inferredIt = unifyResult.find({FlatSymbolRefAttr::get(paramName), Side::RHS});
2788 if (inferredIt == unifyResult.end()) {
2789 return callableOp.emitError() << "could not infer " << argKind
2790 << " template argument for parameter @" << paramName.getValue()
2791 << " from callee signature";
2792 }
2793 if (!inferredIt->second) {
2794 return emitConflictingInferredTypes(callableOp, targetOp, paramName);
2795 }
2796 return inferredIt->second;
2797}
2798
2800template <typename CallableOp, typename TargetOp>
2801static FailureOr<ArrayAttr> getCallSignatureTemplateParams(
2802 CallableOp callableOp, const TemplateInferenceInfo &info, TargetOp targetOp
2803) {
2804 FailureOr<UnificationMap> unifyResult = callableOp.unifyTypeSignature(targetOp.getFunctionType());
2805 if (failed(unifyResult)) {
2806 return callableOp.emitError()
2807 << "could not infer omitted template arguments from callee signature";
2808 }
2809
2810 SmallVector<Attribute> params;
2811 params.reserve(info.oldParamOrder.size());
2812 for (StringAttr paramName : info.oldParamOrder) {
2813 auto replacementIt = info.replacements.find(paramName);
2814 if (replacementIt != info.replacements.end()) {
2815 params.push_back(TypeAttr::get(replacementIt->second.type));
2816 continue;
2817 }
2818 FailureOr<Attribute> inferredAttr =
2819 getInferredRhsTemplateArg(callableOp, targetOp, *unifyResult, paramName, "omitted");
2820 if (failed(inferredAttr)) {
2821 return failure();
2822 }
2823 params.push_back(*inferredAttr);
2824 }
2825 return ArrayAttr::get(callableOp.getContext(), params);
2826}
2827
2830template <typename TargetOp>
2831static bool hasResidualFunctionTvarWildcard(
2832 ArrayAttr callParams, const TemplateInferenceInfo &info, TargetOp targetOp
2833) {
2834 if (!callParams || callParams.size() != info.oldParamOrder.size()) {
2835 return false;
2836 }
2837 for (const auto &entry : info.typeVarParams) {
2838 StringAttr paramName = entry.first;
2839 if (info.replacements.contains(paramName)) {
2840 continue;
2841 }
2842 if (!targetMentionsParam(targetOp, paramName)) {
2843 continue;
2844 }
2845 std::optional<unsigned> index = getParamIndex(info.oldParamOrder, paramName);
2846 assert(index && "eligible type-variable parameter must appear in parameter order");
2847 if (isWildcardTemplateArg(callParams[*index])) {
2848 return true;
2849 }
2850 }
2851 return false;
2852}
2853
2856template <typename CallableOp, typename TargetOp>
2857static FailureOr<ArrayAttr> materializeResidualFunctionTvarWildcards(
2858 CallableOp callableOp, ArrayAttr callParams, const TemplateInferenceInfo &info,
2859 TargetOp targetOp
2860) {
2861 FailureOr<UnificationMap> unifyResult = callableOp.unifyTypeSignature(targetOp.getFunctionType());
2862 if (failed(unifyResult)) {
2863 return callableOp.emitError()
2864 << "could not infer wildcard template arguments from callee signature";
2865 }
2866
2867 SmallVector<Attribute> params(callParams.begin(), callParams.end());
2868 for (const auto &entry : info.typeVarParams) {
2869 StringAttr paramName = entry.first;
2870 if (info.replacements.contains(paramName)) {
2871 continue;
2872 }
2873 if (!targetMentionsParam(targetOp, paramName)) {
2874 continue;
2875 }
2876 std::optional<unsigned> index = getParamIndex(info.oldParamOrder, paramName);
2877 assert(index && "eligible type-variable parameter must appear in parameter order");
2878 if (!isWildcardTemplateArg(params[*index])) {
2879 continue;
2880 }
2881
2882 auto inferredIt = unifyResult->find({FlatSymbolRefAttr::get(paramName), Side::RHS});
2883 if (inferredIt == unifyResult->end()) {
2884 auto proofIt = info.functionReplacements.find(targetOp.getOperation());
2885 if (proofIt == info.functionReplacements.end()) {
2886 continue;
2887 }
2888 auto replacementIt = proofIt->second.find(paramName);
2889 if (replacementIt == proofIt->second.end()) {
2890 continue;
2891 }
2892 params[*index] = TypeAttr::get(replacementIt->second.type);
2893 continue;
2894 }
2895 if (!inferredIt->second) {
2896 return emitConflictingInferredTypes(callableOp, targetOp, paramName);
2897 }
2898 params[*index] = inferredIt->second;
2899 }
2900 return ArrayAttr::get(callableOp.getContext(), params);
2901}
2902
2908template <typename TargetOp>
2909static bool hasResidualFunctionTvar(const TemplateInferenceInfo &info, TargetOp targetOp) {
2910 return llvm::any_of(info.typeVarParams, [&](const auto &entry) {
2911 return !info.replacements.contains(entry.first) && targetMentionsParam(targetOp, entry.first);
2912 });
2913}
2914
2916struct CallableSpecializationInputs {
2917 ArrayAttr params;
2918 bool explicitParams = false;
2919 bool paramsChanged = false;
2920 DenseMap<StringAttr, Type> replacements;
2921};
2922
2924template <typename CallableOp, typename TargetOp>
2925static LogicalResult prepareCallableSpecialization(
2926 CallableOp callableOp, const TemplateInferenceInfo &info, TargetOp targetOp,
2927 CallableSpecializationInputs &inputs, bool &shouldSpecialize
2928) {
2929 shouldSpecialize = false;
2930 inputs.params = {};
2931 inputs.explicitParams = false;
2932 inputs.paramsChanged = false;
2933 inputs.replacements.clear();
2934 inputs.params = callableOp.getTemplateParamsAttr();
2935 inputs.explicitParams = !isNullOrEmpty(inputs.params);
2936 if (isNullOrEmpty(inputs.params)) {
2937 FailureOr<ArrayAttr> inferredParams =
2938 getCallSignatureTemplateParams(callableOp, info, targetOp);
2939 if (failed(inferredParams)) {
2940 return failure();
2941 }
2942 inputs.params = *inferredParams;
2943 inputs.paramsChanged = true;
2944 } else {
2945 inputs.params = expandCurrentTemplateParamsToOriginalOrder(inputs.params, info);
2946 if (hasResidualFunctionTvarWildcard(inputs.params, info, targetOp)) {
2947 FailureOr<ArrayAttr> materializedParams =
2948 materializeResidualFunctionTvarWildcards(callableOp, inputs.params, info, targetOp);
2949 if (failed(materializedParams)) {
2950 return failure();
2951 }
2952 inputs.params = *materializedParams;
2953 inputs.paramsChanged = true;
2954 }
2955 }
2956
2957 inputs.replacements = getConcreteCallSiteReplacements(inputs.params, info, targetOp);
2958 shouldSpecialize = !inputs.replacements.empty();
2959 return success();
2960}
2961
2968static std::string buildTemplateLocalFunctionCloneCacheKey(
2969 StringRef functionName, ArrayRef<StringAttr> oldParamOrder,
2970 const DenseMap<StringAttr, Type> &replacements
2971) {
2972 std::string key;
2973 llvm::raw_string_ostream os(key);
2974 os << functionName.size() << ':' << functionName;
2975 for (StringAttr paramName : oldParamOrder) {
2976 os << '|';
2977 os << paramName.getValue().size() << ':' << paramName.getValue() << '=';
2978 auto replacementIt = replacements.find(paramName);
2979 if (replacementIt == replacements.end()) {
2980 os << '_';
2981 continue;
2982 }
2983
2984 std::string typeText;
2985 llvm::raw_string_ostream typeOs(typeText);
2986 replacementIt->second.print(typeOs);
2987 os << typeText.size() << ':' << typeText;
2988 }
2989 return key;
2990}
2991
2993static SymbolRefAttr getSpecializedFunctionCloneCallee(
2994 SymbolRefAttr originalCallee, StringAttr templateName, StringAttr cloneName
2995) {
2996 SmallVector<FlatSymbolRefAttr> pieces = getPieces(originalCallee);
2997 assert(pieces.size() >= 2 && "callee must include at least template and function names");
2998 pieces.pop_back();
2999 pieces.pop_back();
3000 pieces.push_back(FlatSymbolRefAttr::get(templateName));
3001 pieces.push_back(FlatSymbolRefAttr::get(cloneName));
3002 return asSymbolRefAttr(pieces);
3003}
3004
3006template <typename CallableOp, typename ConfigureCloneFn>
3007static FailureOr<SymbolRefAttr> getOrCreateSpecializedCallableClone(
3008 TemplateOp templateOp, CallableOp callable, SymbolRefAttr originalCallee,
3009 const TemplateInferenceInfo &info, const DenseMap<StringAttr, Type> &replacements,
3010 SymbolTableCollection &tables, llvm::StringMap<SymbolRefAttr> &cloneCallees,
3011 llvm::StringMap<StringAttr> &templateClones, InstantiationLayout &layout, ArrayAttr callParams,
3012 ConfigureCloneFn configureClone
3013) {
3014 DenseMap<Attribute, Attribute> paramNameToConcrete = buildParamNameToConcrete(replacements);
3015 FailureOr<TemplateOp> newTemplate = getOrCreateSpecializedTemplateClone(
3016 templateOp, info.oldParamOrder, paramNameToConcrete, callParams, tables, templateClones,
3017 layout
3018 );
3019 if (failed(newTemplate)) {
3020 return failure();
3021 }
3022
3023 std::string cacheKey = buildTemplateLocalFunctionCloneCacheKey(
3024 callable.getSymName(), info.oldParamOrder, replacements
3025 );
3026 auto cachedCallee = cloneCallees.find(cacheKey);
3027 if (cachedCallee != cloneCallees.end()) {
3028 return cachedCallee->second;
3029 }
3030
3031 SymbolTable &templateSymbols = tables.getSymbolTable(*newTemplate);
3032
3033 auto clone = llvm::cast<CallableOp>(callable.getOperation()->clone());
3034 configureClone(clone);
3035 templateSymbols.insert(clone);
3036
3037 TypeVarReplacementConverter converter(
3038 templateOp.getContext(), info.templatePath, info.oldParamOrder, replacements,
3039 /*trimResolvedParams=*/false
3040 );
3041 if (failed(convertTemplateExprTypesIn(*newTemplate, converter))) {
3042 clone.erase();
3043 return failure();
3044 }
3045 if (failed(convertOperationTypesIn(clone.getOperation(), converter))) {
3046 clone.erase();
3047 return failure();
3048 }
3049 removeIdentityCasts(clone.getOperation());
3050 SymbolRefAttr cloneCallee = getSpecializedFunctionCloneCallee(
3051 originalCallee, newTemplate->getSymNameAttr(), clone.getSymNameAttr()
3052 );
3053 cloneCallees.try_emplace(cacheKey, cloneCallee);
3054 return cloneCallee;
3055}
3056
3058static FailureOr<SymbolRefAttr> getOrCreateSpecializedFunctionClone(
3059 TemplateOp templateOp, FuncDefOp func, SymbolRefAttr originalCallee,
3060 const TemplateInferenceInfo &info, const DenseMap<StringAttr, Type> &replacements,
3061 SymbolTableCollection &tables, llvm::StringMap<SymbolRefAttr> &cloneCallees,
3062 llvm::StringMap<StringAttr> &templateClones, InstantiationLayout &layout, ArrayAttr callParams
3063) {
3064 return getOrCreateSpecializedCallableClone(
3065 templateOp, func, originalCallee, info, replacements, tables, cloneCallees, templateClones,
3066 layout, callParams, [](FuncDefOp) {}
3067 );
3068}
3069
3071static FailureOr<SymbolRefAttr> getOrCreateSpecializedContractClone(
3072 TemplateOp templateOp, verif::ContractOp contract, SymbolRefAttr originalCallee,
3073 SymbolRefAttr specializedTarget, const TemplateInferenceInfo &info,
3074 const DenseMap<StringAttr, Type> &replacements, SymbolTableCollection &tables,
3075 llvm::StringMap<SymbolRefAttr> &cloneCallees, llvm::StringMap<StringAttr> &templateClones,
3076 InstantiationLayout &layout, ArrayAttr callParams
3077) {
3078 return getOrCreateSpecializedCallableClone(
3079 templateOp, contract, originalCallee, info, replacements, tables, cloneCallees,
3080 templateClones, layout, callParams,
3081 [specializedTarget](verif::ContractOp clone) { clone.setTargetAttr(specializedTarget); }
3082 );
3083}
3084
3090static LogicalResult removeResolvedParams(TemplateInferenceInfo &info) {
3091 if (info.replacements.empty()) {
3092 return success();
3093 }
3094
3095 DenseMap<StringAttr, Type> replacements;
3096 for (const auto &entry : info.replacements) {
3097 replacements.try_emplace(entry.first, entry.second.type);
3098 }
3099 DenseMap<Attribute, Attribute> paramNameToConcrete = buildParamNameToConcrete(replacements);
3100 FailureOr<InstantiationLayout> layout =
3101 buildInstantiationLayout(info.templateOp, ArrayAttr(), paramNameToConcrete);
3102 if (failed(layout)) {
3103 return failure();
3104 }
3105
3106 setInstantiationNamePattern(info.templateOp, ArrayAttr());
3107 for (auto paramOp : llvm::make_early_inc_range(info.templateOp.getConstOps<TemplateParamOp>())) {
3108 auto name = paramOp.getSymNameAttr();
3109 if (info.replacements.contains(name)) {
3110 paramOp.erase();
3111 }
3112 }
3114 info.templateOp, layout->remainingNames.empty() ? ArrayAttr() : layout->namePattern
3115 );
3116 return success();
3117}
3118
3120template <typename CallableOp>
3121static LogicalResult
3122validateWildcardCallableSignature(CallableOp callableOp, FunctionType targetTy) {
3123 if (succeeded(callableOp.unifyTypeSignature(targetTy))) {
3124 return success();
3125 }
3126 return callableOp.emitError() << "call signature " << callableOp.getTypeSignature()
3127 << " does not match inferred callee signature " << targetTy;
3128}
3129
3131static void updateCallableResultTypesIfNeeded(
3132 CallOp callOp, const TypeVarReplacementConverter &converter, bool &modified
3133) {
3134 for (Value result : callOp.getResults()) {
3135 Type newTy = converter.convertType(result.getType());
3136 if (newTy != result.getType()) {
3137 result.setType(newTy);
3138 modified = true;
3139 }
3140 }
3141}
3142
3144static void
3145updateCallableResultTypesIfNeeded(verif::IncludeOp, const TypeVarReplacementConverter &, bool &) {}
3146
3148template <typename CallableOp>
3149static FailureOr<bool> updateCallableTemplateParamsFor(
3150 ModuleOp module, DenseMap<Operation *, const TypeVarReplacementConverter *> &converters,
3151 SymbolTableCollection &tables
3152) {
3153 bool modified = false;
3154 bool failedConversion = false;
3155 module.walk([&](CallableOp callableOp) {
3156 if (failedConversion) {
3157 return;
3158 }
3159 auto target = callableOp.getCalleeTarget(tables);
3160 if (failed(target)) {
3161 return;
3162 }
3163 auto targetOp = target->get();
3164 TemplateOp parentTemplate = getParentOfType<TemplateOp>(targetOp.getOperation());
3165 if (!parentTemplate) {
3166 return;
3167 }
3168 const TypeVarReplacementConverter *converter = converters.lookup(parentTemplate.getOperation());
3169 if (!converter) {
3170 return;
3171 }
3172
3173 TemplateOp callableTemplate = getParentOfType<TemplateOp>(callableOp.getOperation());
3174 bool resolveTemplateSymbolArgs =
3175 callableTemplate && callableTemplate.getOperation() == parentTemplate.getOperation();
3176 ArrayAttr oldParams = callableOp.getTemplateParamsAttr();
3177 FailureOr<ArrayAttr> newParams = converter->convertTemplateParams(
3178 oldParams, callableOp, resolveTemplateSymbolArgs,
3179 /*allowCurrentTemplateTypeVarSymbols=*/true
3180 );
3181 if (failed(newParams)) {
3182 failedConversion = true;
3183 return;
3184 }
3185 if (converter->hasWildcardForRemovedParam(oldParams)) {
3186 auto inferredTargetTy =
3187 llvm::cast<FunctionType>(converter->convertType(targetOp.getFunctionType()));
3188 if (failed(validateWildcardCallableSignature(callableOp, inferredTargetTy))) {
3189 failedConversion = true;
3190 return;
3191 }
3192 }
3193 if (oldParams != *newParams) {
3194 callableOp.setTemplateParamsAttr(*newParams);
3195 modified = true;
3196 }
3197
3198 updateCallableResultTypesIfNeeded(callableOp, *converter, modified);
3199 });
3200 if (failedConversion) {
3201 return failure();
3202 }
3203 return modified;
3204}
3205
3213static FailureOr<bool> updateCallableTemplateParams(
3214 ModuleOp module, DenseMap<Operation *, const TypeVarReplacementConverter *> &converters
3215) {
3216 SymbolTableCollection tables;
3217 FailureOr<bool> callsModified =
3218 updateCallableTemplateParamsFor<CallOp>(module, converters, tables);
3219 if (failed(callsModified)) {
3220 return failure();
3221 }
3222 FailureOr<bool> includesModified =
3223 updateCallableTemplateParamsFor<verif::IncludeOp>(module, converters, tables);
3224 if (failed(includesModified)) {
3225 return failure();
3226 }
3227 return *callsModified || *includesModified;
3228}
3229
3236class ReferencedStructTemplateParamConverter {
3238 MLIRContext *ctx_;
3240 ModuleOp module_;
3242 SymbolTableCollection &tables_;
3244 DenseMap<Operation *, const TypeVarReplacementConverter *> &converters_;
3246 Operation *diagnosticOp_ = nullptr;
3248 bool hasFailure = false;
3249
3250public:
3251 ReferencedStructTemplateParamConverter(
3252 MLIRContext *ctx, ModuleOp module, SymbolTableCollection &tables,
3253 DenseMap<Operation *, const TypeVarReplacementConverter *> &converters
3254 )
3255 : ctx_(ctx), module_(module), tables_(tables), converters_(converters) {}
3256
3258 void startOperation(Operation *op) {
3259 diagnosticOp_ = op;
3260 hasFailure = false;
3261 }
3262
3264 bool hadFailure() const { return hasFailure; }
3265
3267 Type convertType(Type ty) {
3268 if (!ty || hasFailure) {
3269 return ty;
3270 }
3271 if (auto arrTy = llvm::dyn_cast<ArrayType>(ty)) {
3272 return convertArrayElementType(arrTy, [this](Type elemTy) { return convertType(elemTy); });
3273 }
3274 if (auto structTy = llvm::dyn_cast<StructType>(ty)) {
3275 return convertStructType(structTy);
3276 }
3277 if (auto podTy = llvm::dyn_cast<PodType>(ty)) {
3278 return convertPodType(podTy, ctx_, *this);
3279 }
3280 if (auto funcTy = llvm::dyn_cast<FunctionType>(ty)) {
3281 return convertFunctionType(funcTy, *this);
3282 }
3283 return ty;
3284 }
3285
3287 Attribute convertAttr(Attribute attr) {
3288 if (hasFailure) {
3289 return attr;
3290 }
3291 return convertTypeOrArrayAttr(attr, ctx_, [this](Type ty) {
3292 return convertType(ty);
3293 }, [this](Attribute nested) { return convertAttr(nested); });
3294 }
3295
3296private:
3298 StructType convertStructType(StructType structTy) {
3299 ArrayAttr params = structTy.getParams();
3300 if (!params) {
3301 return structTy;
3302 }
3303
3304 SmallVector<Attribute> newParams;
3305 bool changed = false;
3306 for (Attribute attr : params.getValue()) {
3307 Attribute newAttr = convertAttr(attr);
3308 newParams.push_back(newAttr);
3309 changed |= newAttr != attr;
3310 if (hasFailure) {
3311 return structTy;
3312 }
3313 }
3314 StructType convertedTy =
3315 changed ? getStructTypeWithParams(structTy.getNameRef(), ctx_, newParams) : structTy;
3316
3317 FailureOr<SymbolLookupResult<StructDefOp>> lookup = lookupTopLevelSymbol<StructDefOp>(
3318 tables_, convertedTy.getNameRef(), module_, /*reportMissing=*/false
3319 );
3320 if (failed(lookup)) {
3321 return convertedTy;
3322 }
3323 TemplateOp parentTemplate = getParentOfType<TemplateOp>(lookup->get().getOperation());
3324 if (!parentTemplate) {
3325 return convertedTy;
3326 }
3327 const TypeVarReplacementConverter *converter =
3328 converters_.lookup(parentTemplate.getOperation());
3329 if (!converter) {
3330 return convertedTy;
3331 }
3332
3333 TemplateOp useTemplate = getParentOfType<TemplateOp>(diagnosticOp_);
3334 bool resolveTemplateSymbolArgs =
3335 useTemplate && useTemplate.getOperation() == parentTemplate.getOperation();
3336 FailureOr<ArrayAttr> trimmedParams = converter->convertTemplateParams(
3337 convertedTy.getParams(), diagnosticOp_, resolveTemplateSymbolArgs,
3338 /*allowCurrentTemplateTypeVarSymbols=*/false
3339 );
3340 if (failed(trimmedParams)) {
3341 hasFailure = true;
3342 return convertedTy;
3343 }
3344 if (convertedTy.getParams() == *trimmedParams) {
3345 return convertedTy;
3346 }
3347 return getStructTypeWithParams(convertedTy.getNameRef(), *trimmedParams);
3348 }
3349};
3350
3352static FailureOr<bool> updateStructTemplateParams(
3353 ModuleOp module, DenseMap<Operation *, const TypeVarReplacementConverter *> &converters
3354) {
3355 SymbolTableCollection tables;
3356 ReferencedStructTemplateParamConverter converter(module.getContext(), module, tables, converters);
3357 return convertOperationTypesInAndTrack(module.getOperation(), converter);
3358}
3359
3361static LogicalResult instantiateConcreteStructUses(ModuleOp module) {
3362 SymbolTableCollection tables;
3363 ConcreteStructInstantiationConverter converter(module.getContext(), module, tables);
3364 return convertOperationTypesIn(module.getOperation(), converter);
3365}
3366
3368static TemplateOp
3369getContractTargetTemplate(verif::ContractOp contract, SymbolTableCollection &tables) {
3370 if (FailureOr<SymbolLookupResult<FuncDefOp>> funcTarget = contract.getFuncTarget(tables);
3371 succeeded(funcTarget)) {
3372 return getParentOfType<TemplateOp>(funcTarget->get().getOperation());
3373 }
3374 if (FailureOr<SymbolLookupResult<StructDefOp>> structTarget = contract.getStructTarget(tables);
3375 succeeded(structTarget)) {
3376 return getParentOfType<TemplateOp>(structTarget->get().getOperation());
3377 }
3378 return {};
3379}
3380
3387static LogicalResult updateExternalContractTemplateParams(
3388 ModuleOp module, DenseMap<Operation *, const TypeVarReplacementConverter *> &converters
3389) {
3390 bool failedConversion = false;
3391 SymbolTableCollection tables;
3392 module.walk([&](verif::ContractOp contract) {
3393 if (failedConversion) {
3394 return;
3395 }
3396 TemplateOp targetTemplate = getContractTargetTemplate(contract, tables);
3397 if (!targetTemplate) {
3398 return;
3399 }
3400 if (getParentOfType<TemplateOp>(contract.getOperation()) == targetTemplate) {
3401 return;
3402 }
3403 const TypeVarReplacementConverter *converter = converters.lookup(targetTemplate.getOperation());
3404 if (!converter) {
3405 return;
3406 }
3407
3408 auto validationResult = contract.walk([converter](Operation *op) -> WalkResult {
3409 return converter->validateOperation(op);
3410 });
3411 if (validationResult.wasInterrupted() ||
3412 failed(convertOperationTypesIn(contract.getOperation(), *converter))) {
3413 failedConversion = true;
3414 return;
3415 }
3416 removeIdentityCasts(contract.getOperation());
3417 });
3418 return failure(failedConversion);
3419}
3420
3429static LogicalResult specializeFunctionLocalCallables(
3430 ModuleOp module, DenseMap<Operation *, const TemplateInferenceInfo *> &infoByTemplate,
3431 SpecializedCallableCloneCache &functionCloneCache,
3432 SpecializedCallableCloneCache &contractCloneCache,
3433 SpecializedTemplateCloneCache &templateCloneCache
3434) {
3435 bool failedClone = false;
3436 SymbolTableCollection tables;
3437 module.walk([&](CallOp callOp) {
3438 if (failedClone) {
3439 return;
3440 }
3441 FailureOr<SymbolLookupResult<FuncDefOp>> target = callOp.getCalleeTarget(tables);
3442 if (failed(target)) {
3443 return;
3444 }
3445 FuncDefOp targetFunc = target->get();
3446 auto parentTemplate = llvm::dyn_cast_or_null<TemplateOp>(targetFunc->getParentOp());
3447 if (!parentTemplate) {
3448 return;
3449 }
3450 const TemplateInferenceInfo *info = infoByTemplate.lookup(parentTemplate.getOperation());
3451 if (!info) {
3452 return;
3453 }
3454 if (!hasResidualFunctionTvar(*info, targetFunc)) {
3455 return;
3456 }
3457
3458 CallableSpecializationInputs inputs;
3459 bool shouldSpecialize = false;
3460 if (failed(
3461 prepareCallableSpecialization(callOp, *info, targetFunc, inputs, shouldSpecialize)
3462 )) {
3463 failedClone = true;
3464 return;
3465 }
3466 if (!shouldSpecialize) {
3467 return;
3468 }
3469 DenseMap<StringAttr, Type> inferredReplacements =
3470 getFunctionProofReplacements(*info, targetFunc);
3471 if (failed(diagnoseCallParamsMismatch(
3472 callOp.getOperation(), inputs.params, info->oldParamOrder, inferredReplacements,
3473 &info->replacements, /*explicitCallParams=*/inputs.explicitParams
3474 ))) {
3475 failedClone = true;
3476 return;
3477 }
3478
3479 InstantiationLayout layout;
3480 FailureOr<SymbolRefAttr> cloneCallee = getOrCreateSpecializedFunctionClone(
3481 parentTemplate, targetFunc, callOp.getCalleeAttr(), *info, inputs.replacements, tables,
3482 functionCloneCache[parentTemplate.getOperation()],
3483 templateCloneCache[parentTemplate.getOperation()], layout, inputs.params
3484 );
3485 if (failed(cloneCallee)) {
3486 failedClone = true;
3487 return;
3488 }
3489
3490 callOp.setCalleeAttr(*cloneCallee);
3492 });
3493 module.walk([&](verif::IncludeOp includeOp) {
3494 if (failedClone) {
3495 return;
3496 }
3497 FailureOr<SymbolLookupResult<verif::ContractOp>> target = includeOp.getCalleeTarget(tables);
3498 if (failed(target)) {
3499 return;
3500 }
3501 verif::ContractOp targetContract = target->get();
3502 auto parentTemplate = llvm::dyn_cast_or_null<TemplateOp>(targetContract->getParentOp());
3503 if (!parentTemplate) {
3504 return;
3505 }
3506 const TemplateInferenceInfo *info = infoByTemplate.lookup(parentTemplate.getOperation());
3507 if (!info) {
3508 return;
3509 }
3510 if (!hasResidualFunctionTvar(*info, targetContract)) {
3511 return;
3512 }
3513
3514 FailureOr<SymbolLookupResult<FuncDefOp>> targetFuncResult =
3515 targetContract.getFuncTarget(tables);
3516 if (failed(targetFuncResult)) {
3517 return;
3518 }
3519 FuncDefOp targetFunc = targetFuncResult->get();
3520 if (getParentOfType<TemplateOp>(targetFunc.getOperation()) != parentTemplate) {
3521 return;
3522 }
3523
3524 CallableSpecializationInputs inputs;
3525 bool shouldSpecialize = false;
3526 if (failed(prepareCallableSpecialization(
3527 includeOp, *info, targetContract, inputs, shouldSpecialize
3528 ))) {
3529 failedClone = true;
3530 return;
3531 }
3532 if (!shouldSpecialize) {
3533 return;
3534 }
3535 DenseMap<StringAttr, Type> inferredReplacements =
3536 getFunctionProofReplacements(*info, targetFunc);
3537 if (failed(diagnoseCallParamsMismatch(
3538 includeOp.getOperation(), inputs.params, info->oldParamOrder, inferredReplacements,
3539 &info->replacements, /*explicitCallParams=*/inputs.explicitParams
3540 ))) {
3541 failedClone = true;
3542 return;
3543 }
3544
3545 InstantiationLayout targetLayout;
3546 FailureOr<SymbolRefAttr> specializedTarget = getOrCreateSpecializedFunctionClone(
3547 parentTemplate, targetFunc, targetContract.getTargetAttr(), *info, inputs.replacements,
3548 tables, functionCloneCache[parentTemplate.getOperation()],
3549 templateCloneCache[parentTemplate.getOperation()], targetLayout, inputs.params
3550 );
3551 if (failed(specializedTarget)) {
3552 failedClone = true;
3553 return;
3554 }
3555 InstantiationLayout contractLayout;
3556 FailureOr<SymbolRefAttr> cloneCallee = getOrCreateSpecializedContractClone(
3557 parentTemplate, targetContract, includeOp.getCalleeAttr(), *specializedTarget, *info,
3558 inputs.replacements, tables, contractCloneCache[parentTemplate.getOperation()],
3559 templateCloneCache[parentTemplate.getOperation()], contractLayout, inputs.params
3560 );
3561 if (failed(cloneCallee)) {
3562 failedClone = true;
3563 return;
3564 }
3565
3566 includeOp.setCalleeAttr(*cloneCallee);
3567 includeOp.setTemplateParamsAttr(contractLayout.rewrittenCallParams);
3568 });
3569 return failure(failedClone);
3570}
3571
3573static bool sameReplacementTypes(
3574 const DenseMap<StringAttr, InferredType> &lhs, const DenseMap<StringAttr, InferredType> &rhs
3575) {
3576 if (lhs.size() != rhs.size()) {
3577 return false;
3578 }
3579 for (const auto &entry : lhs) {
3580 auto rhsIt = rhs.find(entry.first);
3581 if (rhsIt == rhs.end() || rhsIt->second.type != entry.second.type) {
3582 return false;
3583 }
3584 }
3585 return true;
3586}
3587
3594static LogicalResult collectContractTargetFunctionInferences(
3595 TemplateInferenceInfo &info, verif::ContractOp contract, ModuleOp module,
3596 DenseMap<Operation *, TemplateInferenceInfo *> *infoByTemplate, SymbolTableCollection &tables,
3597 bool &changed
3598) {
3599 FailureOr<SymbolLookupResult<FuncDefOp>> target = contract.getFuncTarget(tables);
3600 if (failed(target)) {
3601 return success();
3602 }
3603 FuncDefOp targetFunc = target->get();
3604 if (getParentOfType<TemplateOp>(targetFunc.getOperation()) != info.templateOp) {
3605 return success();
3606 }
3607
3608 DenseMap<StringAttr, InferredType> funcReplacements;
3609 auto funcIt = info.functionReplacements.find(targetFunc.getOperation());
3610 if (funcIt != info.functionReplacements.end()) {
3611 funcReplacements = funcIt->second;
3612 }
3613 DenseMap<StringAttr, InferredType> oldFuncReplacements = funcReplacements;
3614
3615 TypeVarInferenceCollector collector(info, funcReplacements);
3616 if (failed(collector.collect(contract.getOperation()))) {
3617 return failure();
3618 }
3619 if (infoByTemplate && failed(collector.collectStructTemplateParamInferences(
3620 contract.getOperation(), module, *infoByTemplate, tables
3621 ))) {
3622 return failure();
3623 }
3624
3625 if (!sameReplacementTypes(oldFuncReplacements, funcReplacements)) {
3626 changed = true;
3627 }
3628 if (funcReplacements.empty()) {
3629 info.functionReplacements.erase(targetFunc.getOperation());
3630 } else {
3631 info.functionReplacements[targetFunc.getOperation()] = std::move(funcReplacements);
3632 }
3633 return success();
3634}
3635
3642static LogicalResult collectContractTargetStructInferences(
3643 TemplateInferenceInfo &info, verif::ContractOp contract, ModuleOp module,
3644 DenseMap<Operation *, TemplateInferenceInfo *> *infoByTemplate, SymbolTableCollection &tables,
3645 bool &changed
3646) {
3647 FailureOr<SymbolLookupResult<StructDefOp>> target = contract.getStructTarget(tables);
3648 if (failed(target)) {
3649 return success();
3650 }
3651 StructDefOp targetStruct = target->get();
3652 if (getParentOfType<TemplateOp>(targetStruct.getOperation()) != info.templateOp) {
3653 return success();
3654 }
3655
3656 DenseMap<StringAttr, InferredType> oldTemplateScopeReplacements = info.templateScopeReplacements;
3657
3658 TypeVarInferenceCollector collector(info, info.templateScopeReplacements);
3659 FunctionType contractTy = contract.getFunctionType();
3660 if (contractTy.getNumInputs() > 0) {
3661 StructType targetSelfTy = targetStruct.getType();
3662 if (auto contractSelfTy = llvm::dyn_cast<StructType>(contractTy.getInput(0));
3663 contractSelfTy && contractSelfTy.getNameRef() == targetSelfTy.getNameRef() &&
3664 failed(collector.collectTypeInferences(contractSelfTy, targetSelfTy, contract.getLoc()))) {
3665 return failure();
3666 }
3667 }
3668 if (failed(collector.collect(contract.getOperation()))) {
3669 return failure();
3670 }
3671 if (infoByTemplate && failed(collector.collectStructTemplateParamInferences(
3672 contract.getOperation(), module, *infoByTemplate, tables
3673 ))) {
3674 return failure();
3675 }
3676
3677 if (!sameReplacementTypes(oldTemplateScopeReplacements, info.templateScopeReplacements)) {
3678 changed = true;
3679 }
3680 return success();
3681}
3682
3692static LogicalResult recomputeTemplateWideReplacements(TemplateInferenceInfo &info) {
3693 DenseMap<StringAttr, unsigned> mentionCounts;
3694 DenseMap<StringAttr, unsigned> proofCounts;
3695 DenseMap<StringAttr, InferredType> commonReplacements;
3696 DenseSet<StringAttr> incompatibleReplacements;
3697
3698 for (FuncDefOp func : walkCollect<FuncDefOp>(info.templateOp)) {
3699 for (const auto &entry : info.typeVarParams) {
3700 if (funcMentionsParam(func, entry.first)) {
3701 ++mentionCounts[entry.first];
3702 }
3703 }
3704
3705 auto funcIt = info.functionReplacements.find(func.getOperation());
3706 if (funcIt == info.functionReplacements.end()) {
3707 continue;
3708 }
3709 for (const auto &entry : funcIt->second) {
3710 ++proofCounts[entry.first];
3711 auto commonIt = commonReplacements.find(entry.first);
3712 if (commonIt == commonReplacements.end()) {
3713 commonReplacements.try_emplace(entry.first, entry.second);
3714 } else if (commonIt->second.type != entry.second.type) {
3715 incompatibleReplacements.insert(entry.first);
3716 }
3717 }
3718 }
3719
3720 for (TemplateExprOp expr : walkCollect<TemplateExprOp>(info.templateOp)) {
3721 for (const auto &entry : info.typeVarParams) {
3722 if (exprMentionsParam(expr, entry.first)) {
3723 ++mentionCounts[entry.first];
3724 }
3725 }
3726 }
3727
3728 info.replacements = info.templateScopeReplacements;
3729 for (const auto &entry : info.templateScopeReplacements) {
3730 auto commonIt = commonReplacements.find(entry.first);
3731 if (commonIt != commonReplacements.end() && commonIt->second.type != entry.second.type) {
3732 InFlightDiagnostic diag = emitError(entry.second.loc)
3733 << "conflicting inferred type for template parameter @"
3734 << entry.first.getValue() << ": " << commonIt->second.type << " vs "
3735 << entry.second.type;
3736 diag.attachNote(commonIt->second.loc) << "previous function-local inference here";
3737 return diag;
3738 }
3739 }
3740
3741 for (const auto &entry : commonReplacements) {
3742 StringAttr paramName = entry.first;
3743 if (info.replacements.contains(paramName)) {
3744 continue;
3745 }
3746 if (!hasUncoveredNonFunctionMention(
3747 info.templateOp, paramName, entry.second.type, info.functionReplacements
3748 ) &&
3749 !incompatibleReplacements.contains(paramName) &&
3750 proofCounts.lookup(paramName) == mentionCounts.lookup(paramName)) {
3751 info.replacements.try_emplace(paramName, entry.second);
3752 }
3753 }
3754 return success();
3755}
3756
3759static LogicalResult inferStructTemplateParamUses(
3760 ModuleOp module, MutableArrayRef<TemplateInferenceInfo> templateInfos,
3761 DenseMap<Operation *, TemplateInferenceInfo *> &infoByTemplate
3762) {
3763 bool changed = false;
3764 do {
3765 changed = false;
3766 SymbolTableCollection tables;
3767 for (TemplateInferenceInfo &info : templateInfos) {
3768 for (FuncDefOp func : walkCollect<FuncDefOp>(info.templateOp)) {
3769 DenseMap<StringAttr, InferredType> funcReplacements;
3770 auto funcIt = info.functionReplacements.find(func.getOperation());
3771 if (funcIt != info.functionReplacements.end()) {
3772 funcReplacements = funcIt->second;
3773 }
3774 DenseMap<StringAttr, InferredType> oldFuncReplacements = funcReplacements;
3775
3776 TypeVarInferenceCollector collector(info, funcReplacements);
3777 if (failed(collector.collect(func.getOperation())) ||
3778 failed(collector.collectStructTemplateParamInferences(
3779 func.getOperation(), module, infoByTemplate, tables
3780 ))) {
3781 return failure();
3782 }
3783
3784 if (!sameReplacementTypes(oldFuncReplacements, funcReplacements)) {
3785 changed = true;
3786 }
3787 if (funcReplacements.empty()) {
3788 info.functionReplacements.erase(func.getOperation());
3789 } else {
3790 info.functionReplacements[func.getOperation()] = std::move(funcReplacements);
3791 }
3792 }
3793
3794 for (verif::ContractOp contract : walkCollect<verif::ContractOp>(info.templateOp)) {
3795 if (failed(collectContractTargetFunctionInferences(
3796 info, contract, module, &infoByTemplate, tables, changed
3797 )) ||
3798 failed(collectContractTargetStructInferences(
3799 info, contract, module, &infoByTemplate, tables, changed
3800 ))) {
3801 return failure();
3802 }
3803 }
3804
3805 for (TemplateExprOp expr : walkCollect<TemplateExprOp>(info.templateOp)) {
3806 DenseMap<StringAttr, InferredType> oldTemplateScopeReplacements =
3807 info.templateScopeReplacements;
3808
3809 TypeVarInferenceCollector collector(info, info.templateScopeReplacements);
3810 if (failed(collector.collect(expr.getOperation())) ||
3811 failed(collector.collectStructTemplateParamInferences(
3812 expr.getOperation(), module, infoByTemplate, tables
3813 ))) {
3814 return failure();
3815 }
3816
3817 if (!sameReplacementTypes(oldTemplateScopeReplacements, info.templateScopeReplacements)) {
3818 changed = true;
3819 }
3820 }
3821
3822 DenseMap<StringAttr, InferredType> oldTemplateScopeReplacements =
3823 info.templateScopeReplacements;
3824 TypeVarInferenceCollector collector(info, info.templateScopeReplacements);
3825 auto nonFunctionResult = info.templateOp.walk([&](Operation *op) -> WalkResult {
3826 if (llvm::isa<FuncDefOp, TemplateExprOp, verif::ContractOp>(op) ||
3827 op->getParentOfType<FuncDefOp, TemplateExprOp, verif::ContractOp>()) {
3828 return WalkResult::advance();
3829 }
3830 return collector.collectOperationStructTemplateParamInferences(
3831 op, module, infoByTemplate, tables
3832 );
3833 });
3834 if (nonFunctionResult.wasInterrupted()) {
3835 return failure();
3836 }
3837 if (!sameReplacementTypes(oldTemplateScopeReplacements, info.templateScopeReplacements)) {
3838 changed = true;
3839 }
3840
3841 DenseMap<StringAttr, InferredType> oldReplacements = info.replacements;
3842 if (failed(recomputeTemplateWideReplacements(info))) {
3843 return failure();
3844 }
3845 if (!sameReplacementTypes(oldReplacements, info.replacements)) {
3846 changed = true;
3847 }
3848 }
3849 } while (changed);
3850 return success();
3851}
3852
3855static LogicalResult collectExternalContractTargetInferences(
3856 ModuleOp module, MutableArrayRef<TemplateInferenceInfo> templateInfos,
3857 DenseMap<Operation *, TemplateInferenceInfo *> &infoByTemplate
3858) {
3859 bool changed = false;
3860 do {
3861 changed = false;
3862 bool failedCollection = false;
3863 SymbolTableCollection tables;
3864 module.walk([&](verif::ContractOp contract) {
3865 TemplateOp targetTemplate = getContractTargetTemplate(contract, tables);
3866 if (!targetTemplate) {
3867 return;
3868 }
3869 if (getParentOfType<TemplateOp>(contract.getOperation()) == targetTemplate) {
3870 return;
3871 }
3872 TemplateInferenceInfo *info = infoByTemplate.lookup(targetTemplate.getOperation());
3873 if (!info) {
3874 return;
3875 }
3876 if (failed(collectContractTargetFunctionInferences(
3877 *info, contract, module, &infoByTemplate, tables, changed
3878 )) ||
3879 failed(collectContractTargetStructInferences(
3880 *info, contract, module, &infoByTemplate, tables, changed
3881 ))) {
3882 failedCollection = true;
3883 }
3884 });
3885 if (failedCollection) {
3886 return failure();
3887 }
3888
3889 for (TemplateInferenceInfo &info : templateInfos) {
3890 DenseMap<StringAttr, InferredType> oldReplacements = info.replacements;
3891 if (failed(recomputeTemplateWideReplacements(info))) {
3892 return failure();
3893 }
3894 if (!sameReplacementTypes(oldReplacements, info.replacements)) {
3895 changed = true;
3896 }
3897 }
3898 } while (changed);
3899 return success();
3900}
3901
3909static FailureOr<TemplateInferenceInfo> buildInfo(TemplateOp templateOp) {
3910 TemplateInferenceInfo info;
3911 info.templateOp = templateOp;
3912 FailureOr<SymbolRefAttr> templatePath =
3913 getPathFromRoot(llvm::cast<SymbolOpInterface>(templateOp.getOperation()));
3914 if (failed(templatePath)) {
3915 return failure();
3916 }
3917 info.templatePath = getStringPieces(*templatePath);
3918 for (TemplateParamOp paramOp : templateOp.getConstOps<TemplateParamOp>()) {
3919 auto name = paramOp.getSymNameAttr();
3920 info.oldParamOrder.push_back(name);
3921 if (isTypeVarParam(paramOp)) {
3922 info.typeVarParams.try_emplace(name, paramOp);
3923 }
3924 }
3925
3926 for (FuncDefOp func : walkCollect<FuncDefOp>(templateOp)) {
3927 DenseMap<StringAttr, InferredType> funcReplacements;
3928 if (failed(TypeVarInferenceCollector(info, funcReplacements).collect(func.getOperation()))) {
3929 return failure();
3930 }
3931 if (!funcReplacements.empty()) {
3932 info.functionReplacements.try_emplace(func.getOperation(), funcReplacements);
3933 }
3934 }
3935
3936 for (TemplateExprOp expr : walkCollect<TemplateExprOp>(templateOp)) {
3937 if (failed(TypeVarInferenceCollector(info, info.templateScopeReplacements)
3938 .collect(expr.getOperation()))) {
3939 return failure();
3940 }
3941 }
3942
3943 bool changed = false;
3944 SymbolTableCollection tables;
3945 ModuleOp module = templateOp->getParentOfType<ModuleOp>();
3946 for (verif::ContractOp contract : walkCollect<verif::ContractOp>(templateOp)) {
3947 if (failed(collectContractTargetFunctionInferences(
3948 info, contract, module, /*infoByTemplate=*/nullptr, tables, changed
3949 )) ||
3950 failed(collectContractTargetStructInferences(
3951 info, contract, module, /*infoByTemplate=*/nullptr, tables, changed
3952 ))) {
3953 return failure();
3954 }
3955 }
3956
3957 if (failed(recomputeTemplateWideReplacements(info))) {
3958 return failure();
3959 }
3960 return info;
3961}
3962
3970class PassImpl : public llzk::polymorphic::impl::TypeVarInferencePassBase<PassImpl> {
3971public:
3972 using Base = TypeVarInferencePassBase<PassImpl>;
3973 using Base::Base;
3974
3975private:
3976 void runOnOperation() override {
3977 ModuleOp module = getOperation();
3978
3979 // Collect all template-local inferences before mutating IR. Conflicts are
3980 // reported during collection and abort the pass.
3981 // Note: SmallVector doesn't work because the element size is too large.
3982 std::vector<TemplateInferenceInfo> templateInfos;
3983 WalkResult collectResult = module.walk([&templateInfos](TemplateOp templateOp) {
3984 if (templateOp.getConstOps<TemplateParamOp>().empty()) {
3985 return WalkResult::advance();
3986 }
3987 FailureOr<TemplateInferenceInfo> info = buildInfo(templateOp);
3988 if (failed(info)) {
3989 return WalkResult::interrupt();
3990 }
3991 templateInfos.push_back(std::move(*info));
3992 return WalkResult::advance();
3993 });
3994 if (collectResult.wasInterrupted()) {
3995 signalPassFailure();
3996 return;
3997 }
3998 if (templateInfos.empty()) {
3999 return;
4000 }
4001
4002 DenseMap<Operation *, TemplateInferenceInfo *> mutableInfoByTemplate;
4003 for (TemplateInferenceInfo &info : templateInfos) {
4004 mutableInfoByTemplate.try_emplace(info.templateOp.getOperation(), &info);
4005 }
4006 if (failed(
4007 collectExternalContractTargetInferences(module, templateInfos, mutableInfoByTemplate)
4008 )) {
4009 signalPassFailure();
4010 return;
4011 }
4012 if (failed(inferStructTemplateParamUses(module, templateInfos, mutableInfoByTemplate))) {
4013 signalPassFailure();
4014 return;
4015 }
4016
4017 SmallVector<TemplateInferenceInfo *> rewrites;
4018 DenseMap<Operation *, const TemplateInferenceInfo *> infoByTemplate;
4019 for (TemplateInferenceInfo &info : templateInfos) {
4020 infoByTemplate.try_emplace(info.templateOp.getOperation(), &info);
4021 if (!info.replacements.empty() || !info.functionReplacements.empty()) {
4022 rewrites.push_back(&info);
4023 }
4024 }
4025
4026 // Rewrite each affected template in place, but keep the converters alive so
4027 // call-site rewriting can use the same positional template-parameter map.
4028 SmallVector<std::unique_ptr<TypeVarReplacementConverter>> converterStorage;
4029 DenseMap<Operation *, const TypeVarReplacementConverter *> convertersByTemplate;
4030 for (TemplateInferenceInfo *info : rewrites) {
4031 DenseMap<StringAttr, Type> replacements;
4032 for (const auto &entry : info->replacements) {
4033 replacements.try_emplace(entry.first, entry.second.type);
4034 }
4035 auto converter = std::make_unique<TypeVarReplacementConverter>(
4036 module.getContext(), info->templatePath, info->oldParamOrder, replacements
4037 );
4038 convertersByTemplate.try_emplace(info->templateOp.getOperation(), converter.get());
4039 converterStorage.push_back(std::move(converter));
4040 }
4041
4042 // Clone concrete call-site instantiations before trimming template
4043 // arguments; the clone decision needs the original explicit arguments.
4044 SpecializedCallableCloneCache functionCloneCache;
4045 SpecializedCallableCloneCache contractCloneCache;
4046 SpecializedTemplateCloneCache templateCloneCache;
4047 if (failed(specializeFunctionLocalCallables(
4048 module, infoByTemplate, functionCloneCache, contractCloneCache, templateCloneCache
4049 ))) {
4050 signalPassFailure();
4051 return;
4052 }
4053
4054 // Trim call/include argument lists before rewriting caller bodies, so a
4055 // caller-local type variable in an explicit argument for an erased callee
4056 // parameter is not rewritten in the caller's scope before it can be dropped.
4057 if (failed(updateCallableTemplateParams(module, convertersByTemplate))) {
4058 signalPassFailure();
4059 return;
4060 }
4061
4062 for (TemplateInferenceInfo *info : rewrites) {
4063 const TypeVarReplacementConverter *converter =
4064 convertersByTemplate.lookup(info->templateOp.getOperation());
4065 assert(converter && "rewritten template must have a converter");
4066 auto validationResult = info->templateOp.walk([converter](Operation *op) -> WalkResult {
4067 return converter->validateOperation(op);
4068 });
4069 if (validationResult.wasInterrupted()) {
4070 signalPassFailure();
4071 return;
4072 }
4073 if (failed(convertOperationTypesIn(info->templateOp.getOperation(), *converter))) {
4074 signalPassFailure();
4075 return;
4076 }
4077 removeIdentityCasts(info->templateOp.getOperation());
4078 }
4079
4080 if (failed(updateExternalContractTemplateParams(module, convertersByTemplate))) {
4081 signalPassFailure();
4082 return;
4083 }
4084
4085 // Calls that still target rewritten templates are updated after all target
4086 // templates have their converters registered.
4087 if (failed(updateCallableTemplateParams(module, convertersByTemplate))) {
4088 signalPassFailure();
4089 return;
4090 }
4091 // Re-run specialization after rewriting because template bodies may now
4092 // contain concrete forwarded calls into otherwise unconstrained templates.
4093 if (failed(specializeFunctionLocalCallables(
4094 module, infoByTemplate, functionCloneCache, contractCloneCache, templateCloneCache
4095 ))) {
4096 signalPassFailure();
4097 return;
4098 }
4099 if (failed(updateStructTemplateParams(module, convertersByTemplate))) {
4100 signalPassFailure();
4101 return;
4102 }
4103
4104 // Erase resolved parameters after all argument-list conversion has used
4105 // the original order, but before concrete struct instantiation verifies
4106 // rewritten owned-struct arities.
4107 for (TemplateInferenceInfo *info : rewrites) {
4108 if (failed(removeResolvedParams(*info))) {
4109 signalPassFailure();
4110 return;
4111 }
4112 }
4113
4114 if (failed(instantiateConcreteStructUses(module))) {
4115 signalPassFailure();
4116 return;
4117 }
4118 }
4119};
4120
4121} // 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.
std::optional< llvm::SmallVector< mlir::Value > > delinearize(int64_t, mlir::Location, mlir::OpBuilder &) const
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
static CreateArrayOp create(::mlir::OpBuilder &builder, ::mlir::Location location, ::llzk::array::ArrayType result, ::mlir::ValueRange elements={})
Definition Ops.cpp.inc:718
::mlir::TypedValue<::llzk::array::ArrayType > getResult()
Definition Ops.h.inc:612
::mlir::Operation::operand_range getElements()
Definition Ops.h.inc:592
static ExtractArrayOp create(::mlir::OpBuilder &builder, ::mlir::Location location, ::mlir::Type result, ::mlir::Value arr_ref, ::mlir::ValueRange indices)
Definition Ops.cpp.inc:1050
static InsertArrayOp create(::mlir::OpBuilder &builder, ::mlir::Location location, ::mlir::Value arr_ref, ::mlir::ValueRange indices, ::mlir::Value rvalue)
Definition Ops.cpp.inc:1442
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:218
::mlir::StringAttr getSymNameAttr()
Definition Ops.h.inc:1404
::mlir::SymbolRefAttr getNameRef() const
static StructType get(::mlir::SymbolRefAttr structName)
Definition Types.cpp.inc:81
::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:456
::mlir::SymbolRefAttr getCallee()
Definition Ops.cpp.inc:566
void setTemplateParamsAttr(::mlir::ArrayAttr attr)
Definition Ops.h.inc:490
::mlir::Value getSelfValueFromConstrain()
Return the "self" value (i.e.
Definition Ops.cpp:1020
::llzk::component::StructType getSingleResultTypeOfWitnessGen()
Assuming the callee contains witness generation code, return the single StructType result.
Definition Ops.cpp:1049
void setCalleeAttr(::mlir::SymbolRefAttr attr)
Definition Ops.h.inc:486
::mlir::FailureOr<::llzk::SymbolLookupResult<::llzk::function::FuncDefOp > > getCalleeTarget(::mlir::SymbolTableCollection &tables)
Resolve and return the target FuncDefOp for this CallOp.
Definition Ops.cpp:1037
::mlir::FunctionType getFunctionType()
Definition Ops.cpp.inc:1229
static PodType get(::mlir::MLIRContext *context, ::llvm::ArrayRef<::llzk::pod::RecordAttr > records)
Definition Types.cpp.inc:70
::llvm::ArrayRef<::llzk::pod::RecordAttr > getRecords() const
::mlir::FlatSymbolRefAttr getConstNameAttr()
Definition Ops.h.inc:599
::mlir::Region & getBodyRegion()
Definition Ops.h.inc:1039
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:1150
::mlir::StringAttr getSymNameAttr()
Definition Ops.h.inc:1052
void setSymName(::llvm::StringRef attrValue)
Definition Ops.cpp.inc:1275
::llvm::StringRef getSymName()
Definition Ops.cpp.inc:1270
inline ::llvm::iterator_range<::mlir::Region::op_iterator< OpT > > getConstOps()
Return ops of type OpT within the body region.
Definition Ops.h.inc:1102
::std::optional<::mlir::Type > getTypeOpt()
Definition Ops.cpp.inc:1637
::mlir::TypedValue<::mlir::Type > getInput()
Definition Ops.h.inc:1538
::mlir::TypedValue<::mlir::Type > getResult()
Definition Ops.h.inc:1557
::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:867
::mlir::FunctionType getFunctionType()
Definition Ops.cpp.inc:931
::mlir::Region & getBody()
Definition Ops.h.inc:824
::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:842
::mlir::SymbolRefAttr getCalleeAttr()
Definition Ops.h.inc:1850
void setCalleeAttr(::mlir::SymbolRefAttr attr)
Definition Ops.h.inc:1880
::mlir::FailureOr<::llzk::SymbolLookupResult<::llzk::verif::ContractOp > > getCalleeTarget(::mlir::SymbolTableCollection &tables)
Resolve and return the target Contract for this CallOp.
Definition Ops.cpp:858
void setTemplateParamsAttr(::mlir::ArrayAttr attr)
Definition Ops.h.inc:1884
Common interface for constant parameter-like symbol operations that can be defined within a TemplateO...
component::StructType getStructTypeWithParams(mlir::SymbolRefAttr nameRef, mlir::ArrayAttr params)
Build a struct type while representing an empty parameter list as absent.
Definition SharedImpl.h:117
FailureOr< InstantiationLayout > buildInstantiationLayout(TemplateOp parentTemplate, ArrayAttr callParams, const DenseMap< Attribute, Attribute > &paramNameToConcrete)
array::ArrayType flattenInstantiatedArrayType(array::ArrayType inputTy, mlir::Type convertedElemTy)
Merge nested array dimensions produced by replacing an array element type.
void setInstantiationNamePattern(TemplateOp templateOp, ArrayAttr namePattern)
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:208
bool isNullOrEmpty(mlir::ArrayAttr a)
OpClass getParentOfType(mlir::Operation *op)
Return the closest surrounding parent/ancestor operation that is of type 'OpClass'.
Definition OpHelpers.h:53
constexpr char FUNC_NAME_PRODUCT[]
Definition Constants.h:18
constexpr T checkedCast(U u) noexcept
Definition Compare.h:94
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:133
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:132
mlir::ArrayAttr namePattern
The refined literal chunks; fully concrete generated templates clear this state.
Definition SharedImpl.h:139
mlir::SmallVector< mlir::Attribute > remainingNames
Definition SharedImpl.h:133