LLZK 3.0.0
An open-source IR for Zero Knowledge (ZK) circuits
Loading...
Searching...
No Matches
TypeHelper.cpp
Go to the documentation of this file.
1//===-- TypeHelper.cpp ------------------------------------------*- C++ -*-===//
2//
3// Part of the LLZK Project, under the Apache License v2.0.
4// See LICENSE.txt for license information.
5// Copyright 2025 Veridise Inc.
6// SPDX-License-Identifier: Apache-2.0
7//
8//===----------------------------------------------------------------------===//
9
11
21#include "llzk/Util/Compare.h"
22#include "llzk/Util/Debug.h"
25
26#include <llvm/ADT/STLExtras.h>
27#include <llvm/ADT/SmallVector.h>
28#include <llvm/ADT/TypeSwitch.h>
29#include <llvm/Support/Debug.h>
30
31#include <cstdint>
32#include <numeric>
33
34#define DEBUG_TYPE "llzk-type-helpers"
35
36using namespace mlir;
37
38namespace llzk {
39
40using namespace array;
41using namespace component;
42using namespace felt;
43using namespace polymorphic;
44using namespace string;
45using namespace pod;
46
49template <typename Derived, typename ResultType> struct LLZKTypeSwitch {
50 inline ResultType match(Type type) {
51 return llvm::TypeSwitch<Type, ResultType>(type)
52 .template Case<IndexType>([this](auto t) {
53 return static_cast<Derived *>(this)->caseIndex(t);
54 })
55 .template Case<FeltType>([this](auto t) {
56 return static_cast<Derived *>(this)->caseFelt(t);
57 })
58 .template Case<StringType>([this](auto t) {
59 return static_cast<Derived *>(this)->caseString(t);
60 })
61 .template Case<TypeVarType>([this](auto t) {
62 return static_cast<Derived *>(this)->caseTypeVar(t);
63 })
64 .template Case<ArrayType>([this](auto t) {
65 return static_cast<Derived *>(this)->caseArray(t);
66 })
67 .template Case<StructType>([this](auto t) {
68 return static_cast<Derived *>(this)->caseStruct(t);
69 }).template Case<PodType>([this](auto t) {
70 return static_cast<Derived *>(this)->casePod(t);
71 }).template Case<NoneType>([this](auto t) {
72 return static_cast<Derived *>(this)->caseNone(t);
73 }).Default([this](Type t) {
74 if (t.isSignlessInteger(1)) {
75 return static_cast<Derived *>(this)->caseBool(cast<IntegerType>(t));
76 } else {
77 return static_cast<Derived *>(this)->caseInvalid(t);
78 }
79 });
80 }
81
82private:
83 friend Derived;
84 LLZKTypeSwitch() = default;
85};
86
87void BuildShortTypeString::appendSymName(StringRef str) {
88 if (str.empty()) {
89 ss << '?';
90 } else {
91 ss << '@' << str;
92 }
93}
94
95void BuildShortTypeString::appendSymRef(SymbolRefAttr sa) {
96 appendSymName(sa.getRootReference().getValue());
97 for (FlatSymbolRefAttr nestedRef : sa.getNestedReferences()) {
98 ss << "::";
99 appendSymName(nestedRef.getValue());
100 }
101}
102
103BuildShortTypeString &BuildShortTypeString::append(Type type) {
104 struct Impl : LLZKTypeSwitch<Impl, void> {
105 BuildShortTypeString &outer;
106 Impl(BuildShortTypeString &outerRef) : outer(outerRef) {}
107
108 void caseInvalid(Type) { outer.ss << "!INVALID"; }
109 void caseNone(NoneType) { outer.ss << 'n'; }
110 void caseBool(IntegerType) { outer.ss << 'b'; }
111 void caseIndex(IndexType) { outer.ss << 'i'; }
112 void caseFelt(FeltType) { outer.ss << 'f'; }
113 void caseString(StringType) { outer.ss << 's'; }
114 void caseTypeVar(TypeVarType t) {
115 outer.ss << "!t<";
116 outer.appendSymName(llvm::cast<TypeVarType>(t).getRefName());
117 outer.ss << '>';
118 }
119 void caseArray(ArrayType t) {
120 outer.ss << "!a<";
121 outer.append(t.getElementType());
122 outer.ss << ':';
123 outer.append(t.getDimensionSizes());
124 outer.ss << '>';
125 }
126 void casePod(PodType t) {
127 outer.ss << "!r<";
128 for (auto record : t.getRecords()) {
129 outer.appendSymRef(record.getNameSym());
130 }
131 outer.ss << '>';
132 }
133 void caseStruct(StructType t) {
134 outer.ss << "!s<";
135 outer.appendSymRef(t.getNameRef());
136 if (ArrayAttr params = t.getParams()) {
137 outer.ss << '_';
138 outer.append(params.getValue());
139 }
140 outer.ss << '>';
141 }
142 };
143 Impl(*this).match(type);
144 return *this;
145}
146
147BuildShortTypeString &BuildShortTypeString::append(Attribute a) {
148 // Special case for inserting the `PLACEHOLDER`
149 if (a == nullptr) {
150 ss << PLACEHOLDER;
151 return *this;
152 }
153
154 // Adapted from AsmPrinter::Impl::printAttributeImpl()
155 if (auto ia = llvm::dyn_cast<IntegerAttr>(a)) {
156 Type ty = ia.getType();
157 bool isUnsigned = ty.isUnsignedInteger() || ty.isSignlessInteger(1);
158 ia.getValue().print(ss, !isUnsigned);
159 } else if (auto sra = llvm::dyn_cast<SymbolRefAttr>(a)) {
160 appendSymRef(sra);
161 } else if (auto ta = llvm::dyn_cast<TypeAttr>(a)) {
162 append(ta.getValue());
163 } else if (auto ama = llvm::dyn_cast<AffineMapAttr>(a)) {
164 ss << "!m<";
165 // Filter to remove spaces from the affine_map representation
166 filtered_raw_ostream fs(ss, [](char c) { return c == ' '; });
167 ama.getValue().print(fs);
168 fs.flush();
169 ss << '>';
170 } else if (auto aa = llvm::dyn_cast<ArrayAttr>(a)) {
171 append(aa.getValue());
172 } else {
173 // All valid/legal cases must be covered above
175 }
176 return *this;
177}
178
179BuildShortTypeString &BuildShortTypeString::append(ArrayRef<Attribute> attrs) {
180 llvm::interleave(attrs, ss, [this](Attribute a) { append(a); }, "_");
181 return *this;
182}
183
184std::string BuildShortTypeString::from(const std::string &base, ArrayRef<Attribute> attrs) {
185 BuildShortTypeString bldr;
186
187 bldr.ret.reserve(base.size() + attrs.size()); // reserve minimum space required
188
189 // First handle replacements of PLACEHOLDER
190 const auto *END = attrs.end();
191 const auto *IT = attrs.begin();
192 {
193 size_t start = 0;
194 for (size_t pos; (pos = base.find(PLACEHOLDER, start)) != std::string::npos; start = pos + 1) {
195 // Append original up to the PLACEHOLDER
196 bldr.ret.append(base, start, pos - start);
197 // Append the formatted Attribute
198 assert(IT != END && "must have an Attribute for every 'PLACEHOLDER' char");
199 bldr.append(*IT++);
200 }
201 // Append remaining suffix of the original
202 bldr.ret.append(base, start, base.size() - start);
203 }
204
205 // Append any remaining Attributes
206 if (IT != END) {
207 bldr.ss << '_';
208 bldr.append(ArrayRef(IT, END));
209 }
210
211 return bldr.ret;
212}
213
214namespace {
215
216template <typename... Types> class TypeList {
217
219 template <typename StreamType> struct Appender {
220
221 // single
222 template <typename Ty> static inline void append(StreamType &stream) {
223 stream << '\'' << Ty::name << '\'';
224 }
225
226 // multiple
227 template <typename First, typename Second, typename... Rest>
228 static void append(StreamType &stream) {
229 append<First>(stream);
230 stream << ", ";
231 append<Second, Rest...>(stream);
232 }
233
234 // full list with wrapping brackets
235 static inline void append(StreamType &stream) {
236 stream << '[';
237 append<Types...>(stream);
238 stream << ']';
239 }
240 };
241
242public:
243 // Checks if the provided value is an instance of any of `Types`
244 template <typename T> static inline bool matches(const T &value) {
245 return llvm::isa_and_present<Types...>(value);
246 }
247
248 static void reportInvalid(EmitErrorFn emitError, const Twine &foundName, const char *aspect) {
249 InFlightDiagnosticWrapper diag = emitError().append(aspect, " must be one of ");
250 Appender<InFlightDiagnosticWrapper>::append(diag);
251 diag.append(" but found '", foundName, '\'').report();
252 }
253
254 static inline void reportInvalid(EmitErrorFn emitError, Attribute found, const char *aspect) {
255 if (emitError) {
256 reportInvalid(emitError, found ? found.getAbstractAttribute().getName() : "nullptr", aspect);
257 }
258 }
259
260 // Returns a comma-separated list formatted string of the names of `Types`
261 static inline std::string getNames() {
262 return buildStringViaCallback(Appender<llvm::raw_string_ostream>::append);
263 }
264};
265
268template <class... Ts> struct make_unique {
269 using type = TypeList<Ts...>;
270};
271
272template <class... Ts> struct make_unique<TypeList<>, Ts...> : make_unique<Ts...> {};
273
274template <class U, class... Us, class... Ts>
275struct make_unique<TypeList<U, Us...>, Ts...>
276 : std::conditional_t<
277 (std::is_same_v<U, Us> || ...) || (std::is_same_v<U, Ts> || ...),
278 make_unique<TypeList<Us...>, Ts...>, make_unique<TypeList<Us...>, Ts..., U>> {};
279
280template <class... Ts> using TypeListUnion = typename make_unique<Ts...>::type;
281
282// Dimensions in the ArrayType must be one of the following:
283// - Integer constants
284// - SymbolRef (flat ref for struct params, non-flat for global constants from another module)
285// - AffineMap (for array created within a loop where size depends on loop variable)
286using ArrayDimensionTypes = TypeList<IntegerAttr, SymbolRefAttr, AffineMapAttr>;
287
288// Parameters in the StructType must be one of the following:
289// - Integer constants
290// - Field element constants
291// - SymbolRef (flat ref for struct params, non-flat for global constants from another module)
292// - Type
293// - AffineMap (for array of non-homogeneous structs)
294using StructParamTypes =
295 TypeList<IntegerAttr, FeltConstAttr, SymbolRefAttr, TypeAttr, AffineMapAttr>;
296
297class AllowedTypes {
298 struct ColumnCheckData {
299 SymbolTableCollection *symbolTable = nullptr;
300 Operation *op = nullptr;
301 };
302
303 bool no_felt : 1 = false;
304 bool no_string : 1 = false;
305 bool no_struct : 1 = false;
306 bool no_array : 1 = false;
307 bool no_pod : 1 = false;
308 bool no_var : 1 = false;
309 bool no_int : 1 = false;
310 bool no_struct_params : 1 = false;
311 bool must_be_column : 1 = false;
312 bool type_var_free : 1 = false;
313
314 ColumnCheckData columnCheck;
315
319 bool validColumns(StructType s) {
320 if (!must_be_column) {
321 return true;
322 }
323 assert(columnCheck.symbolTable);
324 assert(columnCheck.op);
325 return succeeded(s.hasColumns(*columnCheck.symbolTable, columnCheck.op));
326 }
327
328public:
329 constexpr AllowedTypes &noFelt() {
330 no_felt = true;
331 return *this;
332 }
333
334 constexpr AllowedTypes &noString() {
335 no_string = true;
336 return *this;
337 }
338
339 constexpr AllowedTypes &noStruct() {
340 no_struct = true;
341 return *this;
342 }
343
344 constexpr AllowedTypes &noArray() {
345 no_array = true;
346 return *this;
347 }
348
349 constexpr AllowedTypes &noPod() {
350 no_pod = true;
351 return *this;
352 }
353
354 constexpr AllowedTypes &noVar() {
355 no_var = true;
356 return *this;
357 }
358
359 constexpr AllowedTypes &noInt() {
360 no_int = true;
361 return *this;
362 }
363
364 constexpr AllowedTypes &noStructParams(bool noStructParams = true) {
365 no_struct_params = noStructParams;
366 return *this;
367 }
368
369 constexpr AllowedTypes &typeVarFree() {
370 no_var = true;
371 type_var_free = true;
372 return *this;
373 }
374
375 constexpr AllowedTypes &onlyInt() {
376 no_int = false;
377 return noFelt().noString().noStruct().noArray().noPod().noVar();
378 }
379
380 constexpr AllowedTypes &mustBeColumn(SymbolTableCollection &symbolTable, Operation *op) {
381 must_be_column = true;
382 columnCheck.symbolTable = &symbolTable;
383 columnCheck.op = op;
384 return *this;
385 }
386
387 // This is the main check for allowed types.
388 bool isValidTypeImpl(Type type);
389
390 bool areValidArrayDimSizes(ArrayRef<Attribute> dimensionSizes, EmitErrorFn emitError = nullptr) {
391 // In LLZK, the number of array dimensions must always be known, i.e., `hasRank()==true`
392 if (dimensionSizes.empty()) {
393 if (emitError) {
394 emitError().append("array must have at least one dimension").report();
395 }
396 return false;
397 }
398 // Rather than immediately returning on failure, we check all dimensions and aggregate to
399 // provide as many errors are possible in a single verifier run.
400 bool success = true;
401 for (Attribute a : dimensionSizes) {
402 if (!ArrayDimensionTypes::matches(a)) {
403 ArrayDimensionTypes::reportInvalid(emitError, a, "Array dimension");
404 success = false;
405 } else if (no_var && !type_var_free && !llvm::isa_and_present<IntegerAttr>(a)) {
406 TypeList<IntegerAttr>::reportInvalid(emitError, a, "Concrete array dimension");
407 success = false;
408 } else if (failed(verifyAffineMapAttrType(emitError, a))) {
409 success = false;
410 } else if (failed(verifyIntAttrType(emitError, a))) {
411 success = false;
412 }
413 }
414 return success;
415 }
416
417 bool isValidArrayElemTypeImpl(Type type) {
418 // ArrayType element can be any valid type sans ArrayType itself. Additionally, `NoneType`
419 // is permitted for shape-only arrays that carry no element payload.
420 return llvm::isa<NoneType>(type) || (!llvm::isa<ArrayType>(type) && isValidTypeImpl(type));
421 }
422
423 bool isValidArrayTypeImpl(
424 Type elementType, ArrayRef<Attribute> dimensionSizes, EmitErrorFn emitError = nullptr
425 ) {
426 if (!areValidArrayDimSizes(dimensionSizes, emitError)) {
427 return false;
428 }
429
430 // Ensure array element type is valid
431 if (!isValidArrayElemTypeImpl(elementType)) {
432 if (emitError) {
433 // Print proper message if `elementType` is not a valid LLZK type or
434 // if it's simply not the right kind of type for an array element.
435 if (succeeded(checkValidType(emitError, elementType))) {
436 emitError()
437 .append(
438 '\'', ArrayType::name, "' element type cannot be '",
439 elementType.getAbstractType().getName(), '\''
440 )
441 .report();
442 }
443 }
444 return false;
445 }
446 return true;
447 }
448
449 bool isValidArrayTypeImpl(Type type) {
450 if (ArrayType arrTy = llvm::dyn_cast<ArrayType>(type)) {
451 return isValidArrayTypeImpl(arrTy.getElementType(), arrTy.getDimensionSizes());
452 }
453 return false;
454 }
455
456 // Note: The `no*` flags here refer to Types nested within a TypeAttr parameter (if any) except
457 // for the `no_struct_params` flag which requires that `params` is null or empty.
458 bool areValidStructTypeParams(ArrayAttr params, EmitErrorFn emitError = nullptr) {
459 if (isNullOrEmpty(params)) {
460 return true;
461 }
462 if (no_struct_params) {
463 return false;
464 }
465 bool success = true;
466 for (Attribute p : params) {
467 if (!StructParamTypes::matches(p)) {
468 StructParamTypes::reportInvalid(emitError, p, "Struct parameter");
469 success = false;
470 } else if (TypeAttr tyAttr = llvm::dyn_cast<TypeAttr>(p)) {
471 if (!isValidTypeImpl(tyAttr.getValue())) {
472 if (emitError) {
473 emitError().append("expected a valid LLZK type but found ", tyAttr.getValue()).report();
474 }
475 success = false;
476 }
477 } else if (type_var_free && llvm::isa<SymbolRefAttr>(p)) {
478 TypeList<IntegerAttr, FeltConstAttr, TypeAttr, AffineMapAttr>::reportInvalid(
479 emitError, p, "Type-variable-free struct parameter"
480 );
481 success = false;
482 } else if (no_var && !type_var_free && !llvm::isa<IntegerAttr, FeltConstAttr>(p)) {
483 TypeList<IntegerAttr>::reportInvalid(emitError, p, "Concrete struct parameter");
484 success = false;
485 } else if (failed(verifyAffineMapAttrType(emitError, p))) {
486 success = false;
487 } else if (failed(verifyIntAttrType(emitError, p))) {
488 success = false;
489 }
490 }
491
492 return success;
493 }
494
495 bool areValidPodRecords(ArrayRef<RecordAttr> records) {
496 return llvm::all_of(records, [this](auto record) { return isValidTypeImpl(record.getType()); });
497 }
498};
499
500bool AllowedTypes::isValidTypeImpl(Type type) {
501 assert(
502 !(no_int && no_felt && no_string && no_var && no_struct && no_array && no_pod) &&
503 "All types have been deactivated"
504 );
505 struct Impl : LLZKTypeSwitch<Impl, bool> {
506 AllowedTypes &outer;
507 Impl(AllowedTypes &outerRef) : outer(outerRef) {}
508
509 bool caseBool(IntegerType t) { return !outer.no_int && t.isSignlessInteger(1); }
510 bool caseIndex(IndexType) { return !outer.no_int; }
511 bool caseFelt(FeltType) { return !outer.no_felt; }
512 bool caseString(StringType) { return !outer.no_string; }
513 bool caseTypeVar(TypeVarType) { return !outer.no_var; }
514 bool caseArray(ArrayType t) {
515 return !outer.no_array &&
516 outer.isValidArrayTypeImpl(t.getElementType(), t.getDimensionSizes());
517 }
518 bool casePod(PodType t) { return !outer.no_pod && outer.areValidPodRecords(t.getRecords()); }
519 bool caseStruct(StructType t) {
520 // Note: The `no*` flags here refer to Types nested within a TypeAttr parameter.
521 if (outer.no_struct || !outer.validColumns(t)) {
522 return false;
523 }
524 return !outer.no_struct && outer.areValidStructTypeParams(t.getParams());
525 }
526 bool caseNone(NoneType) { return false; }
527 bool caseInvalid(Type) { return false; }
528 };
529 return Impl(*this).match(type);
530}
531
532} // namespace
533
534bool isValidType(Type type) { return AllowedTypes().isValidTypeImpl(type); }
535
536bool isValidColumnType(Type type, SymbolTableCollection &symbolTable, Operation *op) {
537 return AllowedTypes().noString().noInt().mustBeColumn(symbolTable, op).isValidTypeImpl(type);
538}
539
540bool isValidGlobalType(Type type) { return AllowedTypes().noVar().isValidTypeImpl(type); }
541
542bool isValidEmitEqType(Type type) {
543 return AllowedTypes().noString().noStruct().isValidTypeImpl(type);
544}
545
546// Allowed types must be a subset of StructParamTypes (defined below)
547bool isValidConstReadType(Type type) {
548 return AllowedTypes().noString().noStruct().noArray().noPod().isValidTypeImpl(type);
549}
550
551bool isValidArrayElemType(Type type) { return AllowedTypes().isValidArrayElemTypeImpl(type); }
552
553bool isValidArrayType(Type type) { return AllowedTypes().isValidArrayTypeImpl(type); }
554
555bool isConcreteType(Type type, bool allowStructParams) {
556 return AllowedTypes().noVar().noStructParams(!allowStructParams).isValidTypeImpl(type);
557}
558
559bool isTypeVarFreeType(Type type) { return AllowedTypes().typeVarFree().isValidTypeImpl(type); }
560
561AttrConcreteness classifyAttrConcreteness(Attribute attr, bool allowStructParams) {
562 if (auto tyAttr = llvm::dyn_cast<TypeAttr>(attr)) {
563 return isConcreteType(tyAttr.getValue(), allowStructParams) ? AttrConcreteness::Concrete
565 }
566 if (auto intAttr = llvm::dyn_cast<IntegerAttr>(attr)) {
568 }
569 return llvm::isa<FeltConstAttr>(attr) ? AttrConcreteness::Concrete
571}
572
573bool hasAffineMapAttr(Type type) {
574 return type.walk([](AffineMapAttr) { return WalkResult::interrupt(); }).wasInterrupted();
575}
576
577bool isDynamic(IntegerAttr intAttr) { return ShapedType::isDynamic(fromAPInt(intAttr.getValue())); }
578
579ArrayType flattenArrayElementType(ArrayType outerArrTy, Type elementType) {
580 SmallVector<Attribute> mergedDims(outerArrTy.getDimensionSizes());
581 while (ArrayType nestedArrTy = llvm::dyn_cast<ArrayType>(elementType)) {
582 llvm::append_range(mergedDims, nestedArrTy.getDimensionSizes());
583 elementType = nestedArrTy.getElementType();
584 }
585 return ArrayType::get(elementType, mergedDims);
586}
587
588uint64_t computeEmitEqCardinality(Type type) {
589 struct Impl : LLZKTypeSwitch<Impl, uint64_t> {
590 uint64_t caseNone(NoneType) { return 0; }
591 uint64_t caseBool(IntegerType) { return 1; }
592 uint64_t caseIndex(IndexType) { return 1; }
593 uint64_t caseFelt(FeltType) { return 1; }
594 uint64_t caseArray(ArrayType t) {
595 uint64_t elementCardinality = computeEmitEqCardinality(t.getElementType());
596 if (elementCardinality == 0) {
597 return 0;
598 }
599 int64_t n = t.getNumElements();
600 return llzk::checkedCast<uint64_t>(n) * elementCardinality;
601 }
602 uint64_t caseStruct(StructType) { llvm_unreachable("not a valid EmitEq type"); }
603 uint64_t casePod(PodType t) {
604 return std::accumulate(
605 t.getRecords().begin(), t.getRecords().end(), 0,
606 [](const uint64_t &acc, const RecordAttr &record) {
607 return computeEmitEqCardinality(record.getType()) + acc;
608 }
609 );
610 }
611 uint64_t caseString(StringType) { llvm_unreachable("not a valid EmitEq type"); }
612 uint64_t caseTypeVar(TypeVarType) { llvm_unreachable("tvar has unknown cardinality"); }
613 uint64_t caseInvalid(Type) { llvm_unreachable("not a valid LLZK type"); }
614 };
615 return Impl().match(type);
616}
617
618namespace {
619
628using AffineInstantiations = DenseMap<std::pair<AffineMapAttr, Side>, IntegerAttr>;
629
630struct UnifierImpl {
631 ArrayRef<StringRef> rhsRevPrefix;
632 UnificationMap *unifications;
633 AffineInstantiations *affineToIntTracker;
634 // This optional function can be used to provide an exception to the standard unification
635 // rules and return a true/success result when it otherwise may not.
636 llvm::function_ref<bool(Type oldTy, Type newTy)> overrideSuccess;
637
638 UnifierImpl(UnificationMap *unificationMap, ArrayRef<StringRef> rhsReversePrefix = {})
639 : rhsRevPrefix(rhsReversePrefix), unifications(unificationMap), affineToIntTracker(nullptr),
640 overrideSuccess(nullptr) {}
641
642 UnifierImpl &trackAffineToInt(AffineInstantiations *tracker) {
643 this->affineToIntTracker = tracker;
644 return *this;
645 }
646
647 UnifierImpl &withOverrides(llvm::function_ref<bool(Type oldTy, Type newTy)> overrides) {
648 this->overrideSuccess = overrides;
649 return *this;
650 }
651
654 template <typename Iter1, typename Iter2> bool typeListsUnify(Iter1 lhs, Iter2 rhs) {
655 return (lhs.size() == rhs.size()) &&
656 std::equal(lhs.begin(), lhs.end(), rhs.begin(), [this](Type a, Type b) {
657 return this->typesUnify(a, b);
658 });
659 }
660
663 bool typeParamsUnify(
664 const ArrayRef<Attribute> &lhsParams, const ArrayRef<Attribute> &rhsParams,
665 bool unifyDynamicSize = false
666 ) {
667 auto pred = [this, unifyDynamicSize](auto lhsAttr, auto rhsAttr) {
668 return paramAttrUnify(lhsAttr, rhsAttr, unifyDynamicSize);
669 };
670 return (lhsParams.size() == rhsParams.size()) &&
671 std::equal(lhsParams.begin(), lhsParams.end(), rhsParams.begin(), pred);
672 }
673
678 bool typeParamsUnify(
679 const ArrayAttr &lhsParams, const ArrayAttr &rhsParams, bool unifyDynamicSize = false
680 ) {
681 ArrayRef<Attribute> emptyParams;
682 return typeParamsUnify(
683 lhsParams ? lhsParams.getValue() : emptyParams,
684 rhsParams ? rhsParams.getValue() : emptyParams, unifyDynamicSize
685 );
686 }
687
688 bool arrayTypesUnify(ArrayType lhs, ArrayType rhs) {
689 // Check if the element types of the two arrays can unify
690 if (!typesUnify(lhs.getElementType(), rhs.getElementType())) {
691 return false;
692 }
693 // Check if the dimension size attributes unify between the LHS and RHS
694 return typeParamsUnify(
695 lhs.getDimensionSizes(), rhs.getDimensionSizes(), /*unifyDynamicSize=*/true
696 );
697 }
698
699 bool structTypesUnify(StructType lhs, StructType rhs) {
700 LLVM_DEBUG({
701 llvm::dbgs() << "[structTypesUnify] lhs = " << lhs << ", rhs = " << rhs << '\n';
702 });
703 // Check if it references the same StructDefOp, considering the additional RHS path prefix.
704 SmallVector<StringRef> rhsNames = getNames(rhs.getNameRef());
705 rhsNames.insert(rhsNames.begin(), rhsRevPrefix.rbegin(), rhsRevPrefix.rend());
706 auto lhsNames = getNames(lhs.getNameRef());
707 if (rhsNames != lhsNames) {
708 LLVM_DEBUG({
709 llvm::interleaveComma(
710 lhsNames, llvm::dbgs() << "[structTypesUnify] names do not match\n"
711 << " lhsNames = ["
712 );
713 llvm::interleaveComma(
714 rhsNames, llvm::dbgs() << "]\n"
715 << " rhsNames = ["
716 );
717 llvm::dbgs() << "]\n";
718 });
719 return false;
720 }
721 LLVM_DEBUG({ llvm::dbgs() << "[structTypesUnify] checking unification of parameters\n"; });
722 // Check if the parameters unify between the LHS and RHS
723 return typeParamsUnify(lhs.getParams(), rhs.getParams(), /*unifyDynamicSize=*/false);
724 }
725
726 bool podTypesUnify(PodType lhs, PodType rhs) {
727 // Same number of records, with the same names in the same order and record types unify.
728 auto lhsRecords = lhs.getRecords();
729 auto rhsRecords = rhs.getRecords();
730
731 return lhsRecords.size() == rhsRecords.size() &&
732 llvm::all_of(llvm::zip_equal(lhsRecords, rhsRecords), [this](auto &&records) {
733 auto &&[lhsRecord, rhsRecord] = records;
734 return lhsRecord.getName() == rhsRecord.getName() &&
735 typesUnify(lhsRecord.getType(), rhsRecord.getType());
736 });
737 }
738
739 bool functionTypesUnify(FunctionType lhs, FunctionType rhs) {
740 return typeListsUnify(lhs.getInputs(), rhs.getInputs()) &&
741 typeListsUnify(lhs.getResults(), rhs.getResults());
742 }
743
744 bool typesUnify(Type lhs, Type rhs) {
745 if (lhs == rhs) {
746 return true;
747 }
748 if (overrideSuccess && overrideSuccess(lhs, rhs)) {
749 return true;
750 }
751 // A type variable can be any type, thus it unifies with anything.
752 if (TypeVarType lhsTvar = llvm::dyn_cast<TypeVarType>(lhs)) {
753 track(Side::LHS, lhsTvar.getNameRef(), rhs);
754 return true;
755 }
756 if (TypeVarType rhsTvar = llvm::dyn_cast<TypeVarType>(rhs)) {
757 track(Side::RHS, rhsTvar.getNameRef(), lhs);
758 return true;
759 }
760 if (llvm::isa<StructType>(lhs) && llvm::isa<StructType>(rhs)) {
761 return structTypesUnify(llvm::cast<StructType>(lhs), llvm::cast<StructType>(rhs));
762 }
763 if (llvm::isa<ArrayType>(lhs) && llvm::isa<ArrayType>(rhs)) {
764 return arrayTypesUnify(llvm::cast<ArrayType>(lhs), llvm::cast<ArrayType>(rhs));
765 }
766 if (llvm::isa<PodType>(lhs) && llvm::isa<PodType>(rhs)) {
767 return podTypesUnify(llvm::cast<PodType>(lhs), llvm::cast<PodType>(rhs));
768 }
769 if (llvm::isa<FunctionType>(lhs) && llvm::isa<FunctionType>(rhs)) {
770 return functionTypesUnify(llvm::cast<FunctionType>(lhs), llvm::cast<FunctionType>(rhs));
771 }
772 return false;
773 }
774
775private:
776 template <typename Tracker, typename Key, typename Val>
777 inline void track(Tracker &tracker, Side side, Key keyHead, Val val) {
778 auto key = std::make_pair(keyHead, side);
779 auto it = tracker.find(key);
780 if (it == tracker.end()) {
781 tracker.try_emplace(key, val);
782 } else if (it->getSecond() != val) {
783 it->second = nullptr;
784 }
785 }
786
787 void track(Side side, SymbolRefAttr symRef, Type ty) {
788 if (unifications) {
789 Attribute attr;
790 if (TypeVarType tvar = dyn_cast<TypeVarType>(ty)) {
791 // If 'ty' is TypeVarType<@S>, just map to @S directly.
792 attr = tvar.getNameRef();
793 } else {
794 // Otherwise wrap as a TypeAttr.
795 attr = TypeAttr::get(ty);
796 }
797 assert(symRef);
798 assert(attr);
799 track(*unifications, side, symRef, attr);
800 }
801 }
802
803 void track(Side side, SymbolRefAttr symRef, Attribute attr) {
804 if (unifications) {
805 // If 'attr' is TypeAttr<TypeVarType<@S>>, just map to @S directly.
806 if (TypeAttr tyAttr = dyn_cast<TypeAttr>(attr)) {
807 if (TypeVarType tvar = dyn_cast<TypeVarType>(tyAttr.getValue())) {
808 attr = tvar.getNameRef();
809 }
810 }
811 assert(symRef);
812 assert(attr);
813 // If 'attr' is a SymbolRefAttr, map in both directions for the correctness of
814 // `isMoreConcreteUnification()` which relies on RHS check while other external
815 // checks on the UnificationMap may do LHS checks, and in the case of both being
816 // SymbolRefAttr, unification in either direction is possible.
817 if (SymbolRefAttr otherSymAttr = dyn_cast<SymbolRefAttr>(attr)) {
818 track(*unifications, reverse(side), otherSymAttr, symRef);
819 }
820 track(*unifications, side, symRef, attr);
821 }
822 }
823
824 void track(Side side, AffineMapAttr affineAttr, IntegerAttr intAttr) {
825 if (affineToIntTracker) {
826 assert(affineAttr);
827 assert(intAttr);
828 assert(!isDynamic(intAttr));
829 track(*affineToIntTracker, side, affineAttr, intAttr);
830 }
831 }
832
833 bool paramAttrUnify(Attribute lhsAttr, Attribute rhsAttr, bool unifyDynamicSize = false) {
836 // Straightforward equality check.
837 if (lhsAttr == rhsAttr) {
838 return true;
839 }
840 // AffineMapAttr can unify with IntegerAttr (other than kDynamic) because struct parameter
841 // instantiation will result in conversion of AffineMapAttr to IntegerAttr.
842 if (AffineMapAttr lhsAffine = llvm::dyn_cast<AffineMapAttr>(lhsAttr)) {
843 if (IntegerAttr rhsInt = llvm::dyn_cast<IntegerAttr>(rhsAttr)) {
844 if (!isDynamic(rhsInt)) {
845 track(Side::LHS, lhsAffine, rhsInt);
846 return true;
847 }
848 }
849 }
850 if (AffineMapAttr rhsAffine = llvm::dyn_cast<AffineMapAttr>(rhsAttr)) {
851 if (IntegerAttr lhsInt = llvm::dyn_cast<IntegerAttr>(lhsAttr)) {
852 if (!isDynamic(lhsInt)) {
853 track(Side::RHS, rhsAffine, lhsInt);
854 return true;
855 }
856 }
857 }
858 // If either side is a SymbolRefAttr, assume they unify because either flattening or a pass with
859 // a more involved value analysis is required to check if they are actually the same value.
860 if (SymbolRefAttr lhsSymRef = llvm::dyn_cast<SymbolRefAttr>(lhsAttr)) {
861 track(Side::LHS, lhsSymRef, rhsAttr);
862 return true;
863 }
864 if (SymbolRefAttr rhsSymRef = llvm::dyn_cast<SymbolRefAttr>(rhsAttr)) {
865 track(Side::RHS, rhsSymRef, lhsAttr);
866 return true;
867 }
868 // If either side is ShapedType::kDynamic then, similarly to Symbols, assume they unify.
869 // NOTE: Dynamic array dimensions (i.e. '?') are allowed in LLZK but should generally be
870 // restricted to scenarios where it can be replaced with a concrete value during the flattening
871 // pass, such as a `unifiable_cast` where the other side of the cast has concrete dimensions or
872 // extern functions with varargs.
873 if (unifyDynamicSize) {
874 auto dyn_cast_if_dynamic = [](Attribute attr) -> IntegerAttr {
875 if (IntegerAttr intAttr = llvm::dyn_cast<IntegerAttr>(attr)) {
876 if (isDynamic(intAttr)) {
877 return intAttr;
878 }
879 }
880 return nullptr;
881 };
882 auto is_const_like = [](Attribute attr) {
883 return llvm::isa_and_present<IntegerAttr, SymbolRefAttr, AffineMapAttr>(attr);
884 };
885 if (IntegerAttr lhsIntAttr = dyn_cast_if_dynamic(lhsAttr)) {
886 if (is_const_like(rhsAttr)) {
887 return true;
888 }
889 }
890 if (IntegerAttr rhsIntAttr = dyn_cast_if_dynamic(rhsAttr)) {
891 if (is_const_like(lhsAttr)) {
892 return true;
893 }
894 }
895 }
896 // If both are type refs, check for unification of the types.
897 if (TypeAttr lhsTy = llvm::dyn_cast<TypeAttr>(lhsAttr)) {
898 if (TypeAttr rhsTy = llvm::dyn_cast<TypeAttr>(rhsAttr)) {
899 return typesUnify(lhsTy.getValue(), rhsTy.getValue());
900 }
901 }
902 // Otherwise, they do not unify.
903 return false;
904 }
905};
906
907} // namespace
908
910 const ArrayRef<Attribute> &lhsParams, const ArrayRef<Attribute> &rhsParams,
911 UnificationMap *unifications
912) {
913 return UnifierImpl(unifications).typeParamsUnify(lhsParams, rhsParams);
914}
915
919 const ArrayAttr &lhsParams, const ArrayAttr &rhsParams, UnificationMap *unifications
920) {
921 return UnifierImpl(unifications).typeParamsUnify(lhsParams, rhsParams);
922}
923
925 ArrayType lhs, ArrayType rhs, ArrayRef<StringRef> rhsReversePrefix, UnificationMap *unifications
926) {
927 return UnifierImpl(unifications, rhsReversePrefix).arrayTypesUnify(lhs, rhs);
928}
929
931 StructType lhs, StructType rhs, ArrayRef<StringRef> rhsReversePrefix,
932 UnificationMap *unifications
933) {
934 return UnifierImpl(unifications, rhsReversePrefix).structTypesUnify(lhs, rhs);
935}
936
938 PodType lhs, PodType rhs, ArrayRef<StringRef> rhsReversePrefix, UnificationMap *unifications
939) {
940 return UnifierImpl(unifications, rhsReversePrefix).podTypesUnify(lhs, rhs);
941}
942
944 FunctionType lhs, FunctionType rhs, ArrayRef<StringRef> rhsReversePrefix,
945 UnificationMap *unifications
946) {
947 return UnifierImpl(unifications, rhsReversePrefix).functionTypesUnify(lhs, rhs);
948}
949
951 Type lhs, Type rhs, ArrayRef<StringRef> rhsReversePrefix, UnificationMap *unifications
952) {
953 return UnifierImpl(unifications, rhsReversePrefix).typesUnify(lhs, rhs);
954}
955
957 Type oldTy, Type newTy, llvm::function_ref<bool(Type oldTy, Type newTy)> knownOldToNew
958) {
959 UnificationMap unifications;
960 AffineInstantiations affineInstantiations;
961 // Run type unification with the addition that affine map can become integer in the new type.
962 if (!UnifierImpl(&unifications)
963 .trackAffineToInt(&affineInstantiations)
964 .withOverrides(knownOldToNew)
965 .typesUnify(oldTy, newTy)) {
966 return false;
967 }
968
969 // If either map contains RHS-keyed mappings then the old type is "more concrete" than the new.
970 // In the UnificationMap, a RHS key would indicate that the new type contains a SymbolRef (i.e.
971 // the "least concrete" attribute kind) where the old type contained any other attribute. In the
972 // AffineInstantiations map, a RHS key would indicate that the new type contains an AffineMapAttr
973 // where the old type contains an IntegerAttr.
974 auto entryIsRHS = [](const auto &entry) { return entry.first.second == Side::RHS; };
975 return !llvm::any_of(unifications, entryIsRHS) && !llvm::any_of(affineInstantiations, entryIsRHS);
976}
977
978FailureOr<IntegerAttr> forceIntType(IntegerAttr attr, EmitErrorFn emitError) {
979 if (llvm::isa<IndexType>(attr.getType())) {
980 return attr;
981 }
982 // Ensure the APInt is the right bitwidth for IndexType or else
983 // IntegerAttr::verify(..) will report an error.
984 APInt value = attr.getValue();
985 auto compare = value.getBitWidth() <=> IndexType::kInternalStorageBitWidth;
986 if (compare < 0) {
987 value = value.zext(IndexType::kInternalStorageBitWidth);
988 } else if (compare > 0) {
989 return emitError().append("value is too large for `index` type: ", debug::toStringOne(value));
990 }
991 return IntegerAttr::get(IndexType::get(attr.getContext()), value);
992}
993
994FailureOr<Attribute> forceIntAttrType(Attribute attr, EmitErrorFn emitError) {
995 if (IntegerAttr intAttr = llvm::dyn_cast_if_present<IntegerAttr>(attr)) {
996 return forceIntType(intAttr, emitError);
997 }
998 return attr;
999}
1000
1001FailureOr<SmallVector<Attribute>>
1002forceIntAttrTypes(ArrayRef<Attribute> attrList, EmitErrorFn emitError) {
1003 SmallVector<Attribute> result;
1004 for (Attribute attr : attrList) {
1005 FailureOr<Attribute> forced = forceIntAttrType(attr, emitError);
1006 if (failed(forced)) {
1007 return failure();
1008 }
1009 result.push_back(*forced);
1010 }
1011 return result;
1012}
1013
1014LogicalResult verifyIntAttrType(EmitErrorFn emitError, Attribute in) {
1015 if (IntegerAttr intAttr = llvm::dyn_cast_if_present<IntegerAttr>(in)) {
1016 Type attrTy = intAttr.getType();
1017 if (!AllowedTypes().onlyInt().isValidTypeImpl(attrTy)) {
1018 if (emitError) {
1019 emitError()
1020 .append("IntegerAttr must have type 'index' or 'i1' but found '", attrTy, '\'')
1021 .report();
1022 }
1023 return failure();
1024 }
1025 }
1026 return success();
1027}
1028
1029LogicalResult verifyAffineMapAttrType(EmitErrorFn emitError, Attribute in) {
1030 if (AffineMapAttr affineAttr = llvm::dyn_cast_if_present<AffineMapAttr>(in)) {
1031 AffineMap map = affineAttr.getValue();
1032 if (map.getNumResults() != 1) {
1033 if (emitError) {
1034 emitError()
1035 .append(
1036 "AffineMapAttr must yield a single result, but found ", map.getNumResults(),
1037 " results"
1038 )
1039 .report();
1040 }
1041 return failure();
1042 }
1043 }
1044 return success();
1045}
1046
1047LogicalResult verifyStructTypeParams(EmitErrorFn emitError, ArrayAttr params) {
1048 return success(AllowedTypes().areValidStructTypeParams(params, emitError));
1049}
1050
1051LogicalResult verifyArrayDimSizes(EmitErrorFn emitError, ArrayRef<Attribute> dimensionSizes) {
1052 return success(AllowedTypes().areValidArrayDimSizes(dimensionSizes, emitError));
1053}
1054
1055LogicalResult
1056verifyArrayType(EmitErrorFn emitError, Type elementType, ArrayRef<Attribute> dimensionSizes) {
1057 return success(AllowedTypes().isValidArrayTypeImpl(elementType, dimensionSizes, emitError));
1058}
1059
1060void assertValidAttrForParamOfType(Attribute attr) {
1061 // Must be the union of valid attribute types within ArrayType, StructType, and TypeVarType.
1062 using TypeVarAttrs = TypeList<SymbolRefAttr>; // per ODS spec of TypeVarType
1063 if (!TypeListUnion<ArrayDimensionTypes, StructParamTypes, TypeVarAttrs>::matches(attr)) {
1064 llvm::report_fatal_error(
1065 "Legal type parameters are inconsistent. Encountered " +
1066 attr.getAbstractAttribute().getName()
1067 );
1068 }
1069}
1070
1071LogicalResult
1072verifySubArrayType(EmitErrorFn emitError, ArrayType arrayType, ArrayType subArrayType) {
1073 ArrayRef<Attribute> dimsFromArr = arrayType.getDimensionSizes();
1074 size_t numArrDims = dimsFromArr.size();
1075 ArrayRef<Attribute> dimsFromSubArr = subArrayType.getDimensionSizes();
1076 size_t numSubArrDims = dimsFromSubArr.size();
1077
1078 if (numArrDims < numSubArrDims) {
1079 return emitError().append(
1080 "subarray type ", subArrayType, " has more dimensions than array type ", arrayType
1081 );
1082 }
1083
1084 size_t toDrop = numArrDims - numSubArrDims;
1085 ArrayRef<Attribute> dimsFromArrReduced = dimsFromArr.drop_front(toDrop);
1086
1087 // Ensure dimension sizes are compatible (ignoring the indexed dimensions)
1088 if (!typeParamsUnify(dimsFromArrReduced, dimsFromSubArr)) {
1089 std::string message;
1090 llvm::raw_string_ostream ss(message);
1091 auto appendOne = [&ss](Attribute a) { appendWithoutType(ss, a); };
1092 ss << "cannot unify array dimensions [";
1093 llvm::interleaveComma(dimsFromArrReduced, ss, appendOne);
1094 ss << "] with [";
1095 llvm::interleaveComma(dimsFromSubArr, ss, appendOne);
1096 ss << "]";
1097 return emitError().append(message);
1098 }
1099
1100 // Ensure element types of the arrays are compatible
1101 if (!typesUnify(arrayType.getElementType(), subArrayType.getElementType())) {
1102 return emitError().append(
1103 "incorrect array element type; expected: ", arrayType.getElementType(),
1104 ", found: ", subArrayType.getElementType()
1105 );
1106 }
1107
1108 return success();
1109}
1110
1111LogicalResult
1112verifySubArrayOrElementType(EmitErrorFn emitError, ArrayType arrayType, Type subArrayOrElemType) {
1113 if (auto subArrayType = llvm::dyn_cast<ArrayType>(subArrayOrElemType)) {
1114 return verifySubArrayType(emitError, arrayType, subArrayType);
1115 }
1116 if (!typesUnify(arrayType.getElementType(), subArrayOrElemType)) {
1117 return emitError().append(
1118 "incorrect array element type; expected: ", arrayType.getElementType(),
1119 ", found: ", subArrayOrElemType
1120 );
1121 }
1122
1123 return success();
1124}
1125
1127 return TypeSwitch<Type, bool>(ty)
1128 .Case<FeltType>([](auto) { return true; })
1129 .Case<ArrayType>([](auto arrTy) {
1130 return isFeltOrSimpleFeltAggregate(arrTy.getElementType());
1131 })
1132 .Case<PodType>([](auto podTy) {
1133 for (auto record : podTy.getRecords()) {
1134 if (!isFeltOrSimpleFeltAggregate(record.getType())) {
1135 return false;
1136 }
1137 }
1138 return true;
1139 }).Default([](auto) { return false; });
1140}
1141
1142bool isValidMainSignalType(Type pType) {
1143 if (auto arrayParamTy = llvm::dyn_cast<ArrayType>(pType)) {
1144 return llvm::isa<FeltType>(arrayParamTy.getElementType());
1145 }
1146 return llvm::isa<FeltType>(pType);
1147}
1148
1149} // namespace llzk
Note: If any symbol refs in an input Type/Attribute use any of the special characters that this class...
Definition TypeHelper.h:39
static std::string from(mlir::Type type)
Return a brief string representation of the given LLZK type.
Definition TypeHelper.h:55
::mlir::Type getElementType() const
static ArrayType get(::mlir::Type elementType, ::llvm::ArrayRef<::mlir::Attribute > dimensionSizes)
Definition Types.cpp.inc:83
::llvm::ArrayRef<::mlir::Attribute > getDimensionSizes() const
static constexpr ::llvm::StringLiteral name
Definition Types.h.inc:56
::llvm::ArrayRef<::llzk::pod::RecordAttr > getRecords() const
std::string toStringOne(const T &value)
Definition Debug.h:182
LogicalResult verifyAffineMapAttrType(EmitErrorFn emitError, Attribute in)
void assertValidAttrForParamOfType(Attribute attr)
LogicalResult verifySubArrayType(EmitErrorFn emitError, ArrayType arrayType, ArrayType subArrayType)
Determine if the subArrayType is a valid subarray of arrayType.
FailureOr< Attribute > forceIntAttrType(Attribute attr, EmitErrorFn emitError)
uint64_t computeEmitEqCardinality(Type type)
bool isValidArrayType(Type type)
LogicalResult verifyIntAttrType(EmitErrorFn emitError, Attribute in)
bool typeListsUnify(Iter1 lhs, Iter2 rhs, mlir::ArrayRef< llvm::StringRef > rhsReversePrefix={}, UnificationMap *unifications=nullptr)
Return true iff the two lists of Type instances are equivalent or could be equivalent after full inst...
Definition TypeHelper.h:277
bool isConcreteType(Type type, bool allowStructParams)
bool isValidArrayElemType(Type type)
llvm::SmallVector< StringRef > getNames(SymbolRefAttr ref)
bool isValidGlobalType(Type type)
AttrConcreteness
Concreteness classification for an argument to a parameterized struct type.
Definition TypeHelper.h:123
FailureOr< IntegerAttr > forceIntType(IntegerAttr attr, EmitErrorFn emitError)
Convert an IntegerAttr with a type other than IndexType to use IndexType.
bool structTypesUnify(StructType lhs, StructType rhs, ArrayRef< StringRef > rhsReversePrefix, UnificationMap *unifications)
LogicalResult verifyArrayType(EmitErrorFn emitError, Type elementType, ArrayRef< Attribute > dimensionSizes)
bool isFeltOrSimpleFeltAggregate(Type ty)
LogicalResult verifySubArrayOrElementType(EmitErrorFn emitError, ArrayType arrayType, Type subArrayOrElemType)
bool isValidColumnType(Type type, SymbolTableCollection &symbolTable, Operation *op)
bool isValidMainSignalType(Type pType)
mlir::DenseMap< std::pair< mlir::SymbolRefAttr, Side >, mlir::Attribute > UnificationMap
Optional result from type unifications.
Definition TypeHelper.h:223
llvm::function_ref< InFlightDiagnosticWrapper()> EmitErrorFn
Callback to produce an error diagnostic.
FailureOr< SmallVector< Attribute > > forceIntAttrTypes(ArrayRef< Attribute > attrList, EmitErrorFn emitError)
bool isNullOrEmpty(mlir::ArrayAttr a)
AttrConcreteness classifyAttrConcreteness(Attribute attr, bool allowStructParams)
bool podTypesUnify(PodType lhs, PodType rhs, ArrayRef< StringRef > rhsReversePrefix, UnificationMap *unifications)
constexpr T checkedCast(U u) noexcept
Definition Compare.h:81
ArrayType flattenArrayElementType(ArrayType outerArrTy, Type elementType)
bool isValidEmitEqType(Type type)
bool isValidType(Type type)
bool arrayTypesUnify(ArrayType lhs, ArrayType rhs, ArrayRef< StringRef > rhsReversePrefix, UnificationMap *unifications)
bool isDynamic(IntegerAttr intAttr)
Side reverse(Side in)
Definition TypeHelper.h:173
int64_t fromAPInt(const llvm::APInt &i)
bool isTypeVarFreeType(Type type)
bool typesUnify(Type lhs, Type rhs, ArrayRef< StringRef > rhsReversePrefix, UnificationMap *unifications)
bool typeParamsUnify(const ArrayRef< Attribute > &lhsParams, const ArrayRef< Attribute > &rhsParams, UnificationMap *unifications)
bool isMoreConcreteUnification(Type oldTy, Type newTy, llvm::function_ref< bool(Type oldTy, Type newTy)> knownOldToNew)
bool functionTypesUnify(FunctionType lhs, FunctionType rhs, ArrayRef< StringRef > rhsReversePrefix, UnificationMap *unifications)
LogicalResult verifyStructTypeParams(EmitErrorFn emitError, ArrayAttr params)
void appendWithoutType(mlir::raw_ostream &os, mlir::Attribute a)
std::string buildStringViaCallback(Func &&appendFn, Args &&...args)
Generate a string by calling the given appendFn with an llvm::raw_ostream & as the first argument fol...
bool hasAffineMapAttr(Type type)
mlir::LogicalResult checkValidType(EmitErrorFn emitError, mlir::Type type)
Definition TypeHelper.h:143
bool isValidConstReadType(Type type)
LogicalResult verifyArrayDimSizes(EmitErrorFn emitError, ArrayRef< Attribute > dimensionSizes)
Template pattern for performing some operation by cases based on a given LLZK type.
ResultType match(Type type)