LLZK 3.0.0
An open-source IR for Zero Knowledge (ZK) circuits
Loading...
Searching...
No Matches
KnownTargetVerifier.h
Go to the documentation of this file.
1//===-- KnownTargetVerifier.h - Shared call verification --------*- C++ -*-===//
2//
3// Part of the LLZK Project, under the Apache License v2.0.
4// See LICENSE.txt for license information.
5// Copyright 2026 Project LLZK
6// SPDX-License-Identifier: Apache-2.0
7//
8//===----------------------------------------------------------------------===//
9
10#pragma once
11
13#include "llzk/Util/Compare.h"
18
19#include <llvm/ADT/DenseSet.h>
20
21namespace llzk {
22
27template <typename OriginOp, typename TargetOp> class KnownTargetVerifier {
28public:
30 : origin(op), tgt(*target), tgtType(tgt.getFunctionType()),
31 targetNamespace(target.getNamespace()), targetViaInclude(target.viaInclude()) {}
32
34 TargetOp getTarget() const { return tgt; }
35
37 mlir::FunctionType getTargetType() const { return tgtType; }
38
40 template <typename T>
41 mlir::LogicalResult verifyTypesMatch(
42 mlir::ValueTypeRange<T> originTypes, mlir::ArrayRef<mlir::Type> tgtTypes, const char *aspect
43 ) {
44 if (tgtTypes.size() != originTypes.size()) {
45 return origin->emitOpError()
46 .append("incorrect number of ", aspect, "s for callee, expected ", tgtTypes.size())
47 .attachNote(tgt.getLoc())
48 .append("callee defined here");
49 }
50 for (unsigned i = 0, e = tgtTypes.size(); i != e; ++i) {
51 if (!typesUnify(originTypes[i], tgtTypes[i], targetNamespace)) {
52 auto diag =
53 origin->emitOpError().append(aspect, " type mismatch: expected type ", tgtTypes[i]);
54 if (targetViaInclude) {
55 diag.append(" from included target \"", origin->getCalleeAttr(), '"');
56 }
57 return diag.append(", but found ", originTypes[i], " for ", aspect, " number ", i);
58 }
59 }
60 return mlir::success();
61 }
62
66 mlir::LogicalResult verifyTemplateInstantiation(polymorphic::TemplateOp tgtOpParent) {
67 auto realParams = tgtOpParent.getConstOps<polymorphic::TemplateParamOp>();
68 mlir::ArrayAttr callParams = origin->getTemplateParamsAttr();
69
70 // When there is no instantiation list, just ensure that it's not required.
71 if (isNullOrEmpty(callParams)) {
72 llvm::SmallDenseSet<mlir::SymbolRefAttr> referencedInSignature;
73 llzk::getSymbolsUsedIn(tgtType.getInputs(), referencedInSignature);
74 llzk::getSymbolsUsedIn(tgtType.getResults(), referencedInSignature);
75
76 bool allParamsReferenced = llvm::all_of(realParams, [&](polymorphic::TemplateParamOp p) {
77 return referencedInSignature.contains(mlir::FlatSymbolRefAttr::get(p.getNameAttr()));
78 });
79 if (allParamsReferenced) {
80 return mlir::success();
81 }
82 return origin->emitOpError().append(
83 "must provide template instantiation parameters when calling \"@", tgt.getSymName(),
84 "\" because not all template parameters of \"@", tgtOpParent.getSymName(),
85 "\" appear in the function type signature"
86 );
87 }
88
89 // Ensure `forceIntAttrTypes()` was successful on the call site's template parameters.
90 if (mlir::failed(llzk::forceIntAttrTypes(callParams.getValue(), [this] {
91 return llzk::InFlightDiagnosticWrapper(this->origin->emitOpError());
92 }))) {
93 return mlir::failure();
94 }
95
96 // The instantiation list is present. Check it has exactly one entry per template param.
97 size_t numTemplateParams = llvm::range_size(realParams);
98 if (callParams.size() != numTemplateParams) {
99 return origin->emitOpError().append(
100 "template instantiation has ", callParams.size(), " parameter(s) but \"@",
101 tgtOpParent.getSymName(), "\" expects ", numTemplateParams, " template parameter(s)"
102 );
103 }
104
105 // Check type compatibility of each provided value with the declared parameter type (if any).
106 if (mlir::failed(origin->verifyTemplateParamValuesCompatibility(realParams))) {
107 return mlir::failure();
108 }
109
110 // Check that the provided instantiation values are consistent with what type unification
111 // of the target function types against the call's operand and result types would determine.
112 mlir::FailureOr<UnificationMap> unifyResult =
113 origin->unifyTypeSignatureWithNamespace(tgtType, targetNamespace);
114 // Verification continues after input/output errors to aggregate diagnostics.
115 if (mlir::failed(unifyResult)) {
116 return mlir::failure();
117 }
118 return origin->verifyTemplateParamsMatchInferred(realParams, unifyResult.value());
119 }
120
121private:
122 OriginOp *origin;
123 TargetOp tgt;
124 mlir::FunctionType tgtType;
125 std::vector<llvm::StringRef> targetNamespace;
126 bool targetViaInclude;
127};
128
129} // namespace llzk
This file defines methods symbol lookup across LLZK operations and included files.
mlir::LogicalResult verifyTypesMatch(mlir::ValueTypeRange< T > originTypes, mlir::ArrayRef< mlir::Type > tgtTypes, const char *aspect)
Check arity and namespace-aware type compatibility, emitting call-site diagnostics.
mlir::LogicalResult verifyTemplateInstantiation(polymorphic::TemplateOp tgtOpParent)
Verify instantiations against the enclosing template.
TargetOp getTarget() const
Return the resolved target for operation-specific checks.
mlir::FunctionType getTargetType() const
Return the target signature used for input and output verification.
KnownTargetVerifier(OriginOp *op, const SymbolLookupResult< TargetOp > &target)
::llvm::StringRef getSymName()
Definition Ops.cpp.inc:1270
inline ::llvm::iterator_range<::mlir::Region::op_iterator< OpT > > getConstOps()
Return ops of type OpT within the body region.
Definition Ops.h.inc:1102
void getSymbolsUsedIn(mlir::Type t, llvm::SmallDenseSet< mlir::SymbolRefAttr > &symbolsUsed)
Add all symbols used within the given Type to the provided set.
FailureOr< SmallVector< Attribute > > forceIntAttrTypes(ArrayRef< Attribute > attrList, EmitErrorFn emitError)
bool isNullOrEmpty(mlir::ArrayAttr a)
bool typesUnify(Type lhs, Type rhs, ArrayRef< StringRef > rhsReversePrefix, UnificationMap *unifications)