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