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.getValue().print(ss, false);
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 return true;
733 }
734 if (overrideSuccess && overrideSuccess(lhs, rhs)) {
735 return true;
736 }
737 // A type variable can be any type, thus it unifies with anything.
738 if (TypeVarType lhsTvar = llvm::dyn_cast<TypeVarType>(lhs)) {
739 track(Side::LHS, lhsTvar.getNameRef(), rhs);
740 return true;
741 }
742 if (TypeVarType rhsTvar = llvm::dyn_cast<TypeVarType>(rhs)) {
743 track(Side::RHS, rhsTvar.getNameRef(), lhs);
744 return true;
745 }
746 if (llvm::isa<StructType>(lhs) && llvm::isa<StructType>(rhs)) {
747 return structTypesUnify(llvm::cast<StructType>(lhs), llvm::cast<StructType>(rhs));
748 }
749 if (llvm::isa<ArrayType>(lhs) && llvm::isa<ArrayType>(rhs)) {
750 return arrayTypesUnify(llvm::cast<ArrayType>(lhs), llvm::cast<ArrayType>(rhs));
751 }
752 if (llvm::isa<PodType>(lhs) && llvm::isa<PodType>(rhs)) {
753 return podTypesUnify(llvm::cast<PodType>(lhs), llvm::cast<PodType>(rhs));
754 }
755 if (llvm::isa<FunctionType>(lhs) && llvm::isa<FunctionType>(rhs)) {
756 return functionTypesUnify(llvm::cast<FunctionType>(lhs), llvm::cast<FunctionType>(rhs));
757 }
758 return false;
759 }
760
761private:
762 template <typename Tracker, typename Key, typename Val>
763 inline void track(Tracker &tracker, Side side, Key keyHead, Val val) {
764 auto key = std::make_pair(keyHead, side);
765 auto it = tracker.find(key);
766 if (it == tracker.end()) {
767 tracker.try_emplace(key, val);
768 } else if (it->getSecond() != val) {
769 it->second = nullptr;
770 }
771 }
772
773 void track(Side side, SymbolRefAttr symRef, Type ty) {
774 if (unifications) {
775 Attribute attr;
776 if (TypeVarType tvar = dyn_cast<TypeVarType>(ty)) {
777 // If 'ty' is TypeVarType<@S>, just map to @S directly.
778 attr = tvar.getNameRef();
779 } else {
780 // Otherwise wrap as a TypeAttr.
781 attr = TypeAttr::get(ty);
782 }
783 assert(symRef);
784 assert(attr);
785 track(*unifications, side, symRef, attr);
786 }
787 }
788
789 void track(Side side, SymbolRefAttr symRef, Attribute attr) {
790 if (unifications) {
791 // If 'attr' is TypeAttr<TypeVarType<@S>>, just map to @S directly.
792 if (TypeAttr tyAttr = dyn_cast<TypeAttr>(attr)) {
793 if (TypeVarType tvar = dyn_cast<TypeVarType>(tyAttr.getValue())) {
794 attr = tvar.getNameRef();
795 }
796 }
797 assert(symRef);
798 assert(attr);
799 // If 'attr' is a SymbolRefAttr, map in both directions for the correctness of
800 // `isMoreConcreteUnification()` which relies on RHS check while other external
801 // checks on the UnificationMap may do LHS checks, and in the case of both being
802 // SymbolRefAttr, unification in either direction is possible.
803 if (SymbolRefAttr otherSymAttr = dyn_cast<SymbolRefAttr>(attr)) {
804 track(*unifications, reverse(side), otherSymAttr, symRef);
805 }
806 track(*unifications, side, symRef, attr);
807 }
808 }
809
810 void track(Side side, AffineMapAttr affineAttr, IntegerAttr intAttr) {
811 if (affineToIntTracker) {
812 assert(affineAttr);
813 assert(intAttr);
814 assert(!isDynamic(intAttr));
815 track(*affineToIntTracker, side, affineAttr, intAttr);
816 }
817 }
818
819 bool paramAttrUnify(Attribute lhsAttr, Attribute rhsAttr, bool unifyDynamicSize = false) {
822 // Straightforward equality check.
823 if (lhsAttr == rhsAttr) {
824 return true;
825 }
826 // AffineMapAttr can unify with IntegerAttr (other than kDynamic) because struct parameter
827 // instantiation will result in conversion of AffineMapAttr to IntegerAttr.
828 if (AffineMapAttr lhsAffine = llvm::dyn_cast<AffineMapAttr>(lhsAttr)) {
829 if (IntegerAttr rhsInt = llvm::dyn_cast<IntegerAttr>(rhsAttr)) {
830 if (!isDynamic(rhsInt)) {
831 track(Side::LHS, lhsAffine, rhsInt);
832 return true;
833 }
834 }
835 }
836 if (AffineMapAttr rhsAffine = llvm::dyn_cast<AffineMapAttr>(rhsAttr)) {
837 if (IntegerAttr lhsInt = llvm::dyn_cast<IntegerAttr>(lhsAttr)) {
838 if (!isDynamic(lhsInt)) {
839 track(Side::RHS, rhsAffine, lhsInt);
840 return true;
841 }
842 }
843 }
844 // If either side is ShapedType::kDynamic then, similarly to Symbols, assume they unify.
845 // NOTE: Dynamic array dimensions (i.e. '?') are allowed in LLZK but should generally be
846 // restricted to scenarios where it can be replaced with a concrete value during the flattening
847 // pass, such as a `unifiable_cast` where the other side of the cast has concrete dimensions or
848 // extern functions with varargs.
849 if (unifyDynamicSize) {
850 auto dyn_cast_if_dynamic = [](Attribute attr) -> IntegerAttr {
851 if (IntegerAttr intAttr = llvm::dyn_cast<IntegerAttr>(attr)) {
852 if (isDynamic(intAttr)) {
853 return intAttr;
854 }
855 }
856 return nullptr;
857 };
858 auto is_const_like = [](Attribute attr) {
859 return llvm::isa_and_present<IntegerAttr, SymbolRefAttr, AffineMapAttr>(attr);
860 };
861 if (IntegerAttr lhsIntAttr = dyn_cast_if_dynamic(lhsAttr)) {
862 if (is_const_like(rhsAttr)) {
863 return true;
864 }
865 }
866 if (IntegerAttr rhsIntAttr = dyn_cast_if_dynamic(rhsAttr)) {
867 if (is_const_like(lhsAttr)) {
868 if (staticLhsWithWildcardRhsTracker) {
869 *staticLhsWithWildcardRhsTracker = true;
870 }
871 return true;
872 }
873 }
874 }
875 // If either side is a SymbolRefAttr, assume they unify because either flattening or a pass with
876 // a more involved value analysis is required to check if they are actually the same value.
877 if (SymbolRefAttr lhsSymRef = llvm::dyn_cast<SymbolRefAttr>(lhsAttr)) {
878 track(Side::LHS, lhsSymRef, rhsAttr);
879 return true;
880 }
881 if (SymbolRefAttr rhsSymRef = llvm::dyn_cast<SymbolRefAttr>(rhsAttr)) {
882 track(Side::RHS, rhsSymRef, lhsAttr);
883 return true;
884 }
885 // If both are type refs, check for unification of the types.
886 if (TypeAttr lhsTy = llvm::dyn_cast<TypeAttr>(lhsAttr)) {
887 if (TypeAttr rhsTy = llvm::dyn_cast<TypeAttr>(rhsAttr)) {
888 return typesUnify(lhsTy.getValue(), rhsTy.getValue());
889 }
890 }
891 // Otherwise, they do not unify.
892 return false;
893 }
894};
895
896} // namespace
897
899 const ArrayRef<Attribute> &lhsParams, const ArrayRef<Attribute> &rhsParams,
900 UnificationMap *unifications
901) {
902 return UnifierImpl(unifications).typeParamsUnify(lhsParams, rhsParams);
903}
904
908 const ArrayAttr &lhsParams, const ArrayAttr &rhsParams, UnificationMap *unifications
909) {
910 return UnifierImpl(unifications).typeParamsUnify(lhsParams, rhsParams);
911}
912
914 ArrayType lhs, ArrayType rhs, ArrayRef<StringRef> rhsReversePrefix, UnificationMap *unifications
915) {
916 return UnifierImpl(unifications, rhsReversePrefix).arrayTypesUnify(lhs, rhs);
917}
918
920 StructType lhs, StructType rhs, ArrayRef<StringRef> rhsReversePrefix,
921 UnificationMap *unifications
922) {
923 return UnifierImpl(unifications, rhsReversePrefix).structTypesUnify(lhs, rhs);
924}
925
927 PodType lhs, PodType rhs, ArrayRef<StringRef> rhsReversePrefix, UnificationMap *unifications
928) {
929 return UnifierImpl(unifications, rhsReversePrefix).podTypesUnify(lhs, rhs);
930}
931
933 FunctionType lhs, FunctionType rhs, ArrayRef<StringRef> rhsReversePrefix,
934 UnificationMap *unifications
935) {
936 return UnifierImpl(unifications, rhsReversePrefix).functionTypesUnify(lhs, rhs);
937}
938
940 Type lhs, Type rhs, ArrayRef<StringRef> rhsReversePrefix, UnificationMap *unifications
941) {
942 return UnifierImpl(unifications, rhsReversePrefix).typesUnify(lhs, rhs);
943}
944
946 Type oldTy, Type newTy, llvm::function_ref<bool(Type oldTy, Type newTy)> knownOldToNew
947) {
948 UnificationMap unifications;
949 AffineInstantiations affineInstantiations;
950 bool staticLhsBecomesWildcardRhs = false;
951 // Run type unification with the addition that affine map can become integer in the new type.
952 if (!UnifierImpl(&unifications)
953 .trackAffineToInt(&affineInstantiations)
954 .trackStaticLhsWithWildcardRhs(&staticLhsBecomesWildcardRhs)
955 .withOverrides(knownOldToNew)
956 .typesUnify(oldTy, newTy)) {
957 return false;
958 }
959
960 // If a statically-known LHS value becomes a wildcard in the RHS, it is not more concrete.
961 if (staticLhsBecomesWildcardRhs) {
962 return false;
963 }
964
965 // If either map contains RHS-keyed mappings then the old type is more concrete than the new. In
966 // the UnificationMap, a RHS key would indicate that the new type contains a SymbolRef (i.e. the
967 // second-least concrete attribute kind, only to wildcard which was checked above) where the old
968 // type contained some other attribute. In the AffineInstantiations map, a RHS key would indicate
969 // that the new type contains an AffineMapAttr where the old type contains an IntegerAttr.
970 auto entryIsRHS = [](const auto &entry) { return entry.first.second == Side::RHS; };
971 return !llvm::any_of(unifications, entryIsRHS) && !llvm::any_of(affineInstantiations, entryIsRHS);
972}
973
974FailureOr<IntegerAttr> forceIntType(IntegerAttr attr, EmitErrorFn emitError) {
975 if (llvm::isa<IndexType>(attr.getType())) {
976 return attr;
977 }
978 // Ensure the APInt is the right bitwidth for IndexType or else
979 // IntegerAttr::verify(..) will report an error.
980 APInt value = attr.getValue();
981 auto compare = value.getBitWidth() <=> IndexType::kInternalStorageBitWidth;
982 if (compare < 0) {
983 value = attr.getType().isSignedInteger() ? value.sext(IndexType::kInternalStorageBitWidth)
984 : value.zext(IndexType::kInternalStorageBitWidth);
985 } else if (compare > 0) {
986 // The source integer type may be wider than index storage even when its value is
987 // representable. Signed values need a signed representability check: getActiveBits()
988 // treats their two's-complement representation as unsigned, rejecting negative values
989 // and accepting some out-of-range positive values after truncation.
990 bool isRepresentable = attr.getType().isSignedInteger()
991 ? value.isSignedIntN(IndexType::kInternalStorageBitWidth)
992 : value.isIntN(IndexType::kInternalStorageBitWidth);
993 if (!isRepresentable) {
994 return emitError().append("value is too large for `index` type: ", debug::toStringOne(value));
995 }
996 value = value.trunc(IndexType::kInternalStorageBitWidth);
997 }
998 return IntegerAttr::get(IndexType::get(attr.getContext()), value);
999}
1000
1001FailureOr<Attribute> forceIntAttrType(Attribute attr, EmitErrorFn emitError) {
1002 if (IntegerAttr intAttr = llvm::dyn_cast_if_present<IntegerAttr>(attr)) {
1003 return forceIntType(intAttr, emitError);
1004 }
1005 return attr;
1006}
1007
1008FailureOr<SmallVector<Attribute>>
1009forceIntAttrTypes(ArrayRef<Attribute> attrList, EmitErrorFn emitError) {
1010 SmallVector<Attribute> result;
1011 for (Attribute attr : attrList) {
1012 FailureOr<Attribute> forced = forceIntAttrType(attr, emitError);
1013 if (failed(forced)) {
1014 return failure();
1015 }
1016 result.push_back(*forced);
1017 }
1018 return result;
1019}
1020
1021LogicalResult verifyIntAttrType(EmitErrorFn emitError, Attribute in) {
1022 if (IntegerAttr intAttr = llvm::dyn_cast_if_present<IntegerAttr>(in)) {
1023 Type attrTy = intAttr.getType();
1024 if (!AllowedTypes().onlyInt().isValidTypeImpl(attrTy)) {
1025 if (emitError) {
1026 emitError()
1027 .append("IntegerAttr must have type 'index' or 'i1' but found '", attrTy, '\'')
1028 .report();
1029 }
1030 return failure();
1031 }
1032 }
1033 return success();
1034}
1035
1036LogicalResult verifyAffineMapAttrType(EmitErrorFn emitError, Attribute in) {
1037 if (AffineMapAttr affineAttr = llvm::dyn_cast_if_present<AffineMapAttr>(in)) {
1038 AffineMap map = affineAttr.getValue();
1039 if (map.getNumResults() != 1) {
1040 if (emitError) {
1041 emitError()
1042 .append(
1043 "AffineMapAttr must yield a single result, but found ", map.getNumResults(),
1044 " results"
1045 )
1046 .report();
1047 }
1048 return failure();
1049 }
1050 }
1051 return success();
1052}
1053
1054LogicalResult verifyStructTypeParams(EmitErrorFn emitError, ArrayAttr params) {
1055 return success(AllowedTypes().areValidStructTypeParams(params, emitError));
1056}
1057
1058LogicalResult verifyArrayDimSizes(EmitErrorFn emitError, ArrayRef<Attribute> dimensionSizes) {
1059 return success(AllowedTypes().areValidArrayDimSizes(dimensionSizes, emitError));
1060}
1061
1062LogicalResult
1063verifyArrayType(EmitErrorFn emitError, Type elementType, ArrayRef<Attribute> dimensionSizes) {
1064 return success(AllowedTypes().isValidArrayTypeImpl(elementType, dimensionSizes, emitError));
1065}
1066
1067void assertValidAttrForParamOfType(Attribute attr) {
1068 // Must be the union of valid attribute types within ArrayType, StructType, and TypeVarType.
1069 using TypeVarAttrs = TypeList<SymbolRefAttr>; // per ODS spec of TypeVarType
1070 if (!TypeListUnion<ArrayDimensionTypes, StructParamTypes, TypeVarAttrs>::matches(attr)) {
1071 llvm::report_fatal_error(
1072 "Legal type parameters are inconsistent. Encountered " +
1073 attr.getAbstractAttribute().getName()
1074 );
1075 }
1076}
1077
1078LogicalResult
1079verifySubArrayType(EmitErrorFn emitError, ArrayType arrayType, ArrayType subArrayType) {
1080 ArrayRef<Attribute> dimsFromArr = arrayType.getDimensionSizes();
1081 size_t numArrDims = dimsFromArr.size();
1082 ArrayRef<Attribute> dimsFromSubArr = subArrayType.getDimensionSizes();
1083 size_t numSubArrDims = dimsFromSubArr.size();
1084
1085 if (numArrDims < numSubArrDims) {
1086 return emitError().append(
1087 "subarray type ", subArrayType, " has more dimensions than array type ", arrayType
1088 );
1089 }
1090
1091 size_t toDrop = numArrDims - numSubArrDims;
1092 ArrayRef<Attribute> dimsFromArrReduced = dimsFromArr.drop_front(toDrop);
1093
1094 // Ensure dimension sizes are compatible (ignoring the indexed dimensions)
1095 if (!typeParamsUnify(dimsFromArrReduced, dimsFromSubArr)) {
1096 std::string message;
1097 llvm::raw_string_ostream ss(message);
1098 auto appendOne = [&ss](Attribute a) { appendWithoutType(ss, a); };
1099 ss << "cannot unify array dimensions [";
1100 llvm::interleaveComma(dimsFromArrReduced, ss, appendOne);
1101 ss << "] with [";
1102 llvm::interleaveComma(dimsFromSubArr, ss, appendOne);
1103 ss << "]";
1104 return emitError().append(message);
1105 }
1106
1107 // Ensure element types of the arrays are compatible
1108 if (!typesUnify(arrayType.getElementType(), subArrayType.getElementType())) {
1109 return emitError().append(
1110 "incorrect array element type; expected: ", arrayType.getElementType(),
1111 ", found: ", subArrayType.getElementType()
1112 );
1113 }
1114
1115 return success();
1116}
1117
1118LogicalResult
1119verifySubArrayOrElementType(EmitErrorFn emitError, ArrayType arrayType, Type subArrayOrElemType) {
1120 if (auto subArrayType = llvm::dyn_cast<ArrayType>(subArrayOrElemType)) {
1121 return verifySubArrayType(emitError, arrayType, subArrayType);
1122 }
1123 if (!typesUnify(arrayType.getElementType(), subArrayOrElemType)) {
1124 return emitError().append(
1125 "incorrect array element type; expected: ", arrayType.getElementType(),
1126 ", found: ", subArrayOrElemType
1127 );
1128 }
1129
1130 return success();
1131}
1132
1134 return TypeSwitch<Type, bool>(ty)
1135 .Case<FeltType>([](auto) { return true; })
1136 .Case<ArrayType>([](auto arrTy) {
1137 return isFeltOrSimpleFeltAggregate(arrTy.getElementType());
1138 })
1139 .Case<PodType>([](auto podTy) {
1140 for (auto record : podTy.getRecords()) {
1141 if (!isFeltOrSimpleFeltAggregate(record.getType())) {
1142 return false;
1143 }
1144 }
1145 return true;
1146 }).Default([](auto) { return false; });
1147}
1148
1149bool isValidMainSignalType(Type pType) {
1150 if (auto arrayParamTy = llvm::dyn_cast<ArrayType>(pType)) {
1151 return llvm::isa<FeltType>(arrayParamTy.getElementType());
1152 }
1153 return llvm::isa<FeltType>(pType);
1154}
1155
1156} // 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: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:271
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:217
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:167
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: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)