LLZK 3.0.0
An open-source IR for Zero Knowledge (ZK) circuits
Loading...
Searching...
No Matches
ConstraintStorage.h
Go to the documentation of this file.
1//===-- ConstraintStorage.h -------------------------------------*- C++ -*-===//
2//
3// Part of the LLZK Project, under the Apache License v2.0.
4// See LICENSE.txt for license information.
5// Copyright 2026 Project LLZK
6// SPDX-License-Identifier: Apache-2.0
7//
8//===----------------------------------------------------------------------===//
9
10#pragma once
11
14
21
26 mlir::ArrayAttr family;
27 llvm::SmallVector<int64_t> indices;
29 mlir::Type memberType;
30 bool signal = false;
31};
32
36 mlir::OpBuilder &builder;
37 mlir::SymbolTableCollection &tables;
39 StructSpecializationResolver specializations;
40
42 mlir::LogicalResult
43 resolveFamily(component::StructType type, StorageDescription &info, mlir::Operation *site) {
44 for (mlir::Attribute record : info.family) {
45 auto entry = llvm::dyn_cast<mlir::DictionaryAttr>(record);
46 auto origin = entry ? entry.getAs<mlir::TypeAttr>(FAMILY_TYPE_KEY) : mlir::TypeAttr();
47 auto indices = entry ? entry.getAs<mlir::ArrayAttr>(FAMILY_INDICES_KEY) : mlir::ArrayAttr();
48 if (!origin || !indices || origin.getValue() != type ||
49 indices.size() != info.indices.size()) {
50 continue;
51 }
52 bool matches = llvm::all_of(llvm::zip(indices, info.indices), [](auto pair) {
53 auto attr = llvm::dyn_cast<mlir::IntegerAttr>(std::get<0>(pair));
54 return attr && attr.getValue().isSignedIntN(64) && attr.getInt() == std::get<1>(pair);
55 });
56 if (!matches) {
57 continue;
58 }
59 auto id = entry.getAs<mlir::IntegerAttr>(FAMILY_SPECIALIZATION_KEY);
60 if (!id || !id.getValue().isSignedIntN(64) || id.getInt() < 0) {
61 return constraintError(site, "invalid family specialization ID");
62 }
63 auto target =
64 specializations.resolve(tables, type, id.getInt(), site, "constraint flattening");
65 if (mlir::failed(target)) {
66 return mlir::failure();
67 }
68 if (info.definition && info.definition != *target) {
69 return constraintError(site, "ambiguous concrete struct family element");
70 }
71 info.definition = *target;
72 }
73 return mlir::success();
74 }
75
77 mlir::LogicalResult
78 resolveComponent(mlir::Value value, StorageDescription &info, mlir::Operation *site) {
79 auto type = llvm::dyn_cast<component::StructType>(value.getType());
80 if (!type) {
81 return mlir::success();
82 }
83 if (info.family && mlir::failed(resolveFamily(type, info, site))) {
84 return mlir::failure();
85 }
86 if (info.definition) {
87 return mlir::success();
88 }
89 auto target = lookupTopLevelSymbol<component::StructDefOp>(tables, type.getNameRef(), site);
90 if (mlir::failed(target)) {
91 return mlir::failure();
92 }
93 if (isInTemplate(target->get()) || (type.getParams() && !type.getParams().empty())) {
94 return constraintError(site, "requires a concrete struct or matching poly.family element");
95 }
96 info.definition = target->get();
97 return mlir::success();
98 }
99
101 mlir::LogicalResult
102 memberInfo(component::MemberReadOp read, StorageDescription &info, mlir::Operation *site) {
103 auto resolved = describe(read.getComponent(), site);
104 if (mlir::failed(resolved)) {
105 return mlir::failure();
106 }
107 auto parent = *resolved;
108 if (!parent.definition || read.getTableOffset()) {
109 return constraintError(site, "member read requires concrete non-column component storage");
110 }
111 auto member = parent.definition.getMemberDef(builder.getStringAttr(read.getMemberName()));
112 if (!member || member.getColumn()) {
113 return constraintError(site, "unknown or unsupported column member");
114 }
115 if (parent.definition != main && !member.hasPublicAttr()) {
116 return constraintError(site, "run llzk-expose-child-members before reading child storage");
117 }
119 tables, llvm::cast<component::StructType>(read.getComponent().getType()).getNameRef(), site
120 );
121 if (mlir::failed(rolled)) {
122 return mlir::failure();
123 }
124 if (!typesUnify(read.getType(), member.getType())) {
125 return constraintError(site, "member read type does not match the concrete specialization");
126 }
127 auto declared = rolled->get().getMemberDef(builder.getStringAttr(read.getMemberName()));
128 if (rolled->get() != parent.definition &&
129 (!declared || !declared.hasPublicAttr() ||
130 !typesUnify(read.getType(), declared.getType(), rolled->getNamespace()))) {
131 return constraintError(
132 site, "concrete family member cannot be represented through the rolled storage type"
133 );
134 }
135 info.memberType = member.getType();
136 info.family = member->getAttrOfType<mlir::ArrayAttr>(FAMILY_ATTR);
137 info.signal = bool(member.getSignal()) || (parent.definition == main && member.hasPublicAttr());
138 return mlir::success();
139 }
140
141public:
143 mlir::ModuleOp rootModule, component::StructDefOp mainDefinition,
144 mlir::OpBuilder &outputBuilder, mlir::SymbolTableCollection &symbolTables,
145 ConstraintScalarFacts &scalarFacts
146 )
147 : main(mainDefinition), builder(outputBuilder), tables(symbolTables), facts(scalarFacts),
148 specializations(rootModule) {}
149
153 mlir::FailureOr<StorageDescription> describe(mlir::Value value, mlir::Operation *site) {
155 if (auto argument = llvm::dyn_cast<mlir::BlockArgument>(value)) {
156 info.definition = argument.getArgNumber() == 0 ? main : component::StructDefOp();
157 info.signal = argument.getArgNumber() != 0;
158 } else if (auto member = value.getDefiningOp<component::MemberReadOp>()) {
159 if (mlir::failed(memberInfo(member, info, site))) {
160 return mlir::failure();
161 }
162 } else if (auto element = value.getDefiningOp<array::ReadArrayOp>()) {
163 auto parent = describe(element.getArrRef(), site);
164 if (mlir::failed(parent)) {
165 return mlir::failure();
166 }
167 info = *parent;
168 info.definition = {};
169 if (info.family) {
170 for (mlir::Value index : element.getIndices()) {
171 auto coordinate = facts.integer(index);
172 if (!coordinate) {
173 return constraintError(site, "array path requires compile-time indices");
174 }
175 info.indices.push_back(*coordinate);
176 }
177 }
178 } else if (auto record = value.getDefiningOp<pod::ReadPodOp>()) {
179 auto parent = describe(record.getPodRef(), site);
180 if (mlir::failed(parent)) {
181 return mlir::failure();
182 }
183 info = *parent;
184 info.definition = {};
185 if (llvm::isa<array::ArrayType>(value.getType())) {
186 // Discovery records family coordinates relative to the nearest array.
187 info.indices.clear();
188 }
189 }
190 if (mlir::failed(resolveComponent(value, info, site))) {
191 return mlir::failure();
192 }
193 return info;
194 }
195};
196
197} // namespace llzk::polymorphic::detail
::mlir::TypedValue<::llzk::component::StructType > getComponent()
Definition Ops.h.inc:855
::std::optional<::mlir::Attribute > getTableOffset()
Definition Ops.cpp.inc:1132
::llvm::StringRef getMemberName()
Definition Ops.cpp.inc:1127
::mlir::SymbolRefAttr getNameRef() const
::mlir::ArrayAttr getParams() const
Resolve concrete struct specializations referenced by poly.family metadata.
Evaluate scalar dependencies of structural decisions, assertions, and constant arithmetic.
mlir::FailureOr< StorageDescription > describe(mlir::Value value, mlir::Operation *site)
Resolve component metadata from immutable emitted storage access chains.
ConstraintStorage(mlir::ModuleOp rootModule, component::StructDefOp mainDefinition, mlir::OpBuilder &outputBuilder, mlir::SymbolTableCollection &symbolTables, ConstraintScalarFacts &scalarFacts)
Common interface for constant parameter-like symbol operations that can be defined within a TemplateO...
mlir::LogicalResult constraintError(mlir::Operation *site, const llvm::Twine &message)
Emit the common diagnostic prefix for constraint expansion and storage helpers.
constexpr llvm::StringLiteral FAMILY_ATTR
Definition Ops.h:40
constexpr llvm::StringLiteral FAMILY_SPECIALIZATION_KEY
Definition Ops.h:43
constexpr llvm::StringLiteral FAMILY_INDICES_KEY
Definition Ops.h:42
bool isInTemplate(Operation *op)
Definition Ops.cpp:34
constexpr llvm::StringLiteral FAMILY_TYPE_KEY
The original affine-parameterized struct family, paired with element indices and a concrete specializ...
Definition Ops.h:46
mlir::FailureOr< SymbolLookupResultUntyped > lookupTopLevelSymbol(mlir::SymbolTableCollection &tables, mlir::SymbolRefAttr symbol, mlir::Operation *origin, bool reportMissing=true)
bool typesUnify(Type lhs, Type rhs, ArrayRef< StringRef > rhsReversePrefix, UnificationMap *unifications)
Transient description derived from immutable storage accesses, not an environment.
mlir::Type memberType
Concrete declared member shape, which may refine a dynamic read type.