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