LLZK 0.1.0
An open-source IR for Zero Knowledge (ZK) circuits
Loading...
Searching...
No Matches
SymbolHelper.cpp
Go to the documentation of this file.
1//===-- SymbolHelper.cpp - LLZK Symbol Helpers ------------------*- 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//===----------------------------------------------------------------------===//
13//===----------------------------------------------------------------------===//
14
22
23#include <mlir/IR/BuiltinOps.h>
24#include <mlir/IR/Operation.h>
25
26#include <llvm/ADT/TypeSwitch.h>
27#include <llvm/Support/Debug.h>
28
29#define DEBUG_TYPE "llzk-symbol-helpers"
30
31using namespace mlir;
32
33namespace llzk {
34
35using namespace array;
36using namespace component;
37using namespace function;
38using namespace global;
39using namespace polymorphic;
40
41namespace {
42
43// NOTE: These may be used in SymbolRefAttr instances returned from these functions but there is no
44// restriction that the same value cannot be used as a symbol name in user code so these should not
45// be used in such a way that relies on that assumption. That's why they are (currently) defined in
46// this anonymous namespace rather than within the header file.
47constexpr char POSITION_IS_ROOT_INDICATOR[] = "<<symbol lookup root>>";
48constexpr char UNNAMED_SYMBOL_INDICATOR[] = "<<unnamed symbol>>";
49
50enum RootSelector : std::uint8_t { CLOSEST, FURTHEST };
51
52class RootPathBuilder {
53 RootSelector _whichRoot;
54 Operation *_origin;
55 ModuleOp *_foundRoot;
56
57public:
58 RootPathBuilder(RootSelector whichRoot, Operation *origin, ModuleOp *foundRoot)
59 : _whichRoot(whichRoot), _origin(origin), _foundRoot(foundRoot) {}
60
68 FailureOr<ModuleOp> collectPathToRoot(Operation *from, std::vector<FlatSymbolRefAttr> &path) {
69 Operation *check = from;
70 ModuleOp currRoot = nullptr;
71 do {
72 if (ModuleOp m = llvm::dyn_cast_if_present<ModuleOp>(check)) {
73 // We need this attribute restriction because some stages of parsing have
74 // an extra module wrapping the top-level module from the input file.
75 // This module, even if it has a name, does not contribute to path names.
76 if (m->hasAttr(LANG_ATTR_NAME)) {
77 if (_whichRoot == RootSelector::CLOSEST) {
78 return m;
79 }
80 currRoot = m;
81 }
82 if (StringAttr modName = m.getSymNameAttr()) {
83 path.push_back(FlatSymbolRefAttr::get(modName));
84 } else if (!currRoot) {
85 return _origin->emitOpError()
86 .append(
87 "has ancestor '", ModuleOp::getOperationName(), "' without \"", LANG_ATTR_NAME,
88 "\" attribute or a name"
89 )
90 .attachNote(m.getLoc())
91 .append("unnamed '", ModuleOp::getOperationName(), "' here");
92 }
93 }
94 } while ((check = check->getParentOp()));
95
96 if (_whichRoot == RootSelector::FURTHEST && currRoot) {
97 return currRoot;
98 }
99
100 return _origin->emitOpError().append(
101 "has no ancestor '", ModuleOp::getOperationName(), "' with \"", LANG_ATTR_NAME,
102 "\" attribute"
103 );
104 }
105
108 FailureOr<SymbolRefAttr> buildPathFromRootToAnyOp(
109 Operation *position, std::vector<FlatSymbolRefAttr> &&path
110
111 ) {
112 // Collect the rest of the path to the root module
113 FailureOr<ModuleOp> rootMod = collectPathToRoot(position, path);
114 if (failed(rootMod)) {
115 return failure();
116 }
117 if (_foundRoot) {
118 *_foundRoot = rootMod.value();
119 }
120 // Special case for empty path (because asSymbolRefAttr() cannot handle it).
121 if (path.empty()) {
122 // ASSERT: This can only occur when the given `position` is the discovered root ModuleOp
123 // itself.
124 assert(position == rootMod.value().getOperation() && "empty path only at root itself");
125 return getFlatSymbolRefAttr(_origin->getContext(), POSITION_IS_ROOT_INDICATOR);
126 }
127 // Reverse the vector and convert it to a SymbolRefAttr
128 std::vector<FlatSymbolRefAttr> reversedVec(path.rbegin(), path.rend());
129 return asSymbolRefAttr(reversedVec);
130 }
131
134 FailureOr<SymbolRefAttr>
135 buildPathFromRootToStruct(StructDefOp to, std::vector<FlatSymbolRefAttr> &&path) {
136 // Add the name of the struct (its name is not optional) and then delegate to helper
137 path.push_back(FlatSymbolRefAttr::get(to.getSymNameAttr()));
138 return buildPathFromRootToAnyOp(to, std::move(path));
139 }
140
141 FailureOr<SymbolRefAttr> getPathFromRootToStruct(StructDefOp to) {
142 std::vector<FlatSymbolRefAttr> path;
143 return buildPathFromRootToStruct(to, std::move(path));
144 }
145
146 FailureOr<SymbolRefAttr> getPathFromRootToMember(MemberDefOp to) {
147 std::vector<FlatSymbolRefAttr> path;
148 // Add the name of the member (its name is not optional)
149 path.push_back(FlatSymbolRefAttr::get(to.getSymNameAttr()));
150 // Delegate to the parent handler (must be StructDefOp per ODS)
151 return buildPathFromRootToStruct(to.getParentOp<StructDefOp>(), std::move(path));
152 }
153
154 FailureOr<SymbolRefAttr> getPathFromRootToFunc(FuncDefOp to) {
155 std::vector<FlatSymbolRefAttr> path;
156 // Add the name of the function (its name is not optional)
157 path.push_back(FlatSymbolRefAttr::get(to.getSymNameAttr()));
158
159 // Delegate based on the type of the parent op
160 Operation *current = to.getOperation();
161 Operation *parent = current->getParentOp();
162 if (StructDefOp parentStruct = llvm::dyn_cast_if_present<StructDefOp>(parent)) {
163 return buildPathFromRootToStruct(parentStruct, std::move(path));
164 } else if (ModuleOp parentMod = llvm::dyn_cast_if_present<ModuleOp>(parent)) {
165 return buildPathFromRootToAnyOp(parentMod, std::move(path));
166 } else {
167 // This is an error in the compiler itself. In current implementation,
168 // FuncDefOp must have either StructDefOp or ModuleOp as its parent.
169 return current->emitError().append("orphaned '", FuncDefOp::getOperationName(), '\'');
170 }
171 }
172
173 FailureOr<SymbolRefAttr> getPathFromRootToAnySymbol(SymbolOpInterface to) {
174 return TypeSwitch<Operation *, FailureOr<SymbolRefAttr>>(to.getOperation())
175 // This more general function must check for the specific cases first.
176 .Case<FuncDefOp>([this](FuncDefOp toOp) { return getPathFromRootToFunc(toOp); })
177 .Case<MemberDefOp>([this](MemberDefOp toOp) { return getPathFromRootToMember(toOp); })
178 .Case<StructDefOp>([this](StructDefOp toOp) { return getPathFromRootToStruct(toOp); })
179
180 // If it's a module, immediately delegate to `buildPathFromRootToAnyOp()` since
181 // it will already add the module name to the path.
182 .Case<ModuleOp>([this](ModuleOp toOp) {
183 std::vector<FlatSymbolRefAttr> path;
184 return buildPathFromRootToAnyOp(toOp, std::move(path));
185 })
186
187 // For any other symbol, append the name of the symbol and then delegate to
188 // `buildPathFromRootToAnyOp()`.
189 .Default([this, &to](Operation *) {
190 std::vector<FlatSymbolRefAttr> path;
191 if (StringAttr name = llzk::getSymbolName(to)) {
192 path.push_back(FlatSymbolRefAttr::get(name));
193 } else {
194 // This can only happen if the symbol is optional. Add a placeholder name.
195 assert(to.isOptionalSymbol());
196 path.push_back(FlatSymbolRefAttr::get(to.getContext(), UNNAMED_SYMBOL_INDICATOR));
197 }
198 return buildPathFromRootToAnyOp(to, std::move(path));
199 });
200 }
201};
202
203} // namespace
204
205llvm::SmallVector<StringRef> getNames(SymbolRefAttr ref) {
206 llvm::SmallVector<StringRef> names;
207 names.push_back(ref.getRootReference().getValue());
208 for (const FlatSymbolRefAttr &r : ref.getNestedReferences()) {
209 names.push_back(r.getValue());
210 }
211 return names;
212}
213
214llvm::SmallVector<FlatSymbolRefAttr> getPieces(SymbolRefAttr ref) {
215 llvm::SmallVector<FlatSymbolRefAttr> pieces;
216 pieces.push_back(FlatSymbolRefAttr::get(ref.getRootReference()));
217 for (const FlatSymbolRefAttr &r : ref.getNestedReferences()) {
218 pieces.push_back(r);
219 }
220 return pieces;
221}
222
223namespace {
224
225SymbolRefAttr changeLeafImpl(
226 StringAttr origRoot, ArrayRef<FlatSymbolRefAttr> origTail, FlatSymbolRefAttr newLeaf,
227 size_t drop = 1
228) {
229 llvm::SmallVector<FlatSymbolRefAttr> newTail;
230 newTail.append(origTail.begin(), origTail.drop_back(drop).end());
231 newTail.push_back(newLeaf);
232 return SymbolRefAttr::get(origRoot, newTail);
233}
234
235} // namespace
236
237SymbolRefAttr replaceLeaf(SymbolRefAttr orig, FlatSymbolRefAttr newLeaf) {
238 ArrayRef<FlatSymbolRefAttr> origTail = orig.getNestedReferences();
239 if (origTail.empty()) {
240 // If there is no tail, the root is the leaf so replace the whole thing
241 return newLeaf;
242 } else {
243 return changeLeafImpl(orig.getRootReference(), origTail, newLeaf);
244 }
245}
246
247SymbolRefAttr appendLeaf(SymbolRefAttr orig, FlatSymbolRefAttr newLeaf) {
248 return changeLeafImpl(orig.getRootReference(), orig.getNestedReferences(), newLeaf, 0);
249}
250
251SymbolRefAttr appendLeafName(SymbolRefAttr orig, const Twine &newLeafSuffix) {
252 ArrayRef<FlatSymbolRefAttr> origTail = orig.getNestedReferences();
253 if (origTail.empty()) {
254 // If there is no tail, the root is the leaf so append on the root instead
256 orig.getContext(), orig.getRootReference().getValue() + newLeafSuffix
257 );
258 } else {
259 return changeLeafImpl(
260 orig.getRootReference(), origTail,
261 getFlatSymbolRefAttr(orig.getContext(), origTail.back().getValue() + newLeafSuffix)
262 );
263 }
264}
265
266FailureOr<ModuleOp> getRootModule(Operation *from) {
267 std::vector<FlatSymbolRefAttr> path;
268 return RootPathBuilder(RootSelector::CLOSEST, from, nullptr).collectPathToRoot(from, path);
269}
270
271FailureOr<SymbolRefAttr> getPathFromRoot(SymbolOpInterface to, ModuleOp *foundRoot) {
272 return RootPathBuilder(RootSelector::CLOSEST, to, foundRoot).getPathFromRootToAnySymbol(to);
273}
274
275FailureOr<SymbolRefAttr> getPathFromRoot(StructDefOp &to, ModuleOp *foundRoot) {
276 return RootPathBuilder(RootSelector::CLOSEST, to, foundRoot).getPathFromRootToStruct(to);
277}
278
279FailureOr<SymbolRefAttr> getPathFromRoot(MemberDefOp &to, ModuleOp *foundRoot) {
280 return RootPathBuilder(RootSelector::CLOSEST, to, foundRoot).getPathFromRootToMember(to);
281}
282
283FailureOr<SymbolRefAttr> getPathFromRoot(FuncDefOp &to, ModuleOp *foundRoot) {
284 return RootPathBuilder(RootSelector::CLOSEST, to, foundRoot).getPathFromRootToFunc(to);
285}
286
287FailureOr<ModuleOp> getTopRootModule(Operation *from) {
288 std::vector<FlatSymbolRefAttr> path;
289 return RootPathBuilder(RootSelector::FURTHEST, from, nullptr).collectPathToRoot(from, path);
290}
291
292FailureOr<SymbolRefAttr> getPathFromTopRoot(SymbolOpInterface to, ModuleOp *foundRoot) {
293 return RootPathBuilder(RootSelector::FURTHEST, to, foundRoot).getPathFromRootToAnySymbol(to);
294}
295
296FailureOr<SymbolRefAttr> getPathFromTopRoot(StructDefOp &to, ModuleOp *foundRoot) {
297 return RootPathBuilder(RootSelector::FURTHEST, to, foundRoot).getPathFromRootToStruct(to);
298}
299
300FailureOr<SymbolRefAttr> getPathFromTopRoot(MemberDefOp &to, ModuleOp *foundRoot) {
301 return RootPathBuilder(RootSelector::FURTHEST, to, foundRoot).getPathFromRootToMember(to);
302}
303
304FailureOr<SymbolRefAttr> getPathFromTopRoot(FuncDefOp &to, ModuleOp *foundRoot) {
305 return RootPathBuilder(RootSelector::FURTHEST, to, foundRoot).getPathFromRootToFunc(to);
306}
307
308FailureOr<StructType> getMainInstanceType(Operation *lookupFrom) {
309 FailureOr<ModuleOp> rootOpt = getRootModule(lookupFrom);
310 if (failed(rootOpt)) {
311 return failure();
312 }
313 ModuleOp root = rootOpt.value();
314 if (Attribute a = root->getAttr(MAIN_ATTR_NAME)) {
315 // If the attribute is present, it must be a TypeAttr of concrete StructType.
316 if (TypeAttr ta = llvm::dyn_cast<TypeAttr>(a)) {
317 if (StructType st = llvm::dyn_cast<StructType>(ta.getValue())) {
318 if (isConcreteType(st)) {
319 return success(st);
320 }
321 }
322 }
323 return rootOpt->emitError().append(
324 '"', MAIN_ATTR_NAME, "\" on top-level module must be a concrete '", StructType::name,
325 "' attribute. Found: ", a
326 );
327 }
328 // The attribute is optional so it's okay if not present.
329 return success(nullptr);
330}
331
332FailureOr<SymbolLookupResult<StructDefOp>>
333getMainInstanceDef(SymbolTableCollection &symbolTable, Operation *lookupFrom) {
334 FailureOr<StructType> mainStructTypeOpt = getMainInstanceType(lookupFrom);
335 if (failed(mainStructTypeOpt)) {
336 return failure();
337 }
338 if (StructType st = mainStructTypeOpt.value()) {
339 return st.getDefinition(symbolTable, lookupFrom);
340 } else {
341 return success(nullptr);
342 }
343}
344
345LogicalResult verifyParamOfType(
346 SymbolTableCollection &tables, SymbolRefAttr param, Type parameterizedType, Operation *origin
347) {
348 // Most often, StructType and ArrayType SymbolRefAttr parameters will be defined as parameters of
349 // the StructDefOp that the current Operation is nested within. These are always flat references
350 // (i.e., contain no nested references).
351 if (param.getNestedReferences().empty()) {
352 FailureOr<StructDefOp> getParentRes = getParentOfType<StructDefOp>(origin);
353 if (succeeded(getParentRes)) {
354 if (getParentRes->hasParamNamed(param.getRootReference())) {
355 return success();
356 }
357 }
358 }
359 // Otherwise, see if the symbol can be found via lookup from the `origin` Operation.
360 auto lookupRes = lookupTopLevelSymbol(tables, param, origin);
361 if (failed(lookupRes)) {
362 return failure(); // lookupTopLevelSymbol() already emits a sufficient error message
363 }
364 Operation *foundOp = lookupRes->get();
365 if (!llvm::isa<GlobalDefOp>(foundOp)) {
366 return origin->emitError() << "ref \"" << param << "\" in type " << parameterizedType
367 << " refers to a '" << foundOp->getName()
368 << "' which is not allowed";
369 }
370 return success();
371}
372
373LogicalResult verifyParamsOfType(
374 SymbolTableCollection &tables, ArrayRef<Attribute> tyParams, Type parameterizedType,
375 Operation *origin
376) {
377 // Rather than immediately returning on failure, we check all params and aggregate to provide as
378 // many errors are possible in a single verifier run.
379 LogicalResult paramCheckResult = success();
380 LLVM_DEBUG({
381 llvm::dbgs() << "[verifyParamOfType] parameterizedType = " << parameterizedType << '\n';
382 });
383 for (Attribute attr : tyParams) {
384 LLVM_DEBUG({ llvm::dbgs() << "[verifyParamOfType] checking attribute " << attr << '\n'; });
386 if (SymbolRefAttr symRefParam = llvm::dyn_cast<SymbolRefAttr>(attr)) {
387 if (failed(verifyParamOfType(tables, symRefParam, parameterizedType, origin))) {
388 LLVM_DEBUG({
389 llvm::dbgs() << "[verifyParamOfType] failed to verify symbol attribute\n";
390 });
391 paramCheckResult = failure();
392 }
393 } else if (TypeAttr typeParam = llvm::dyn_cast<TypeAttr>(attr)) {
394 if (failed(verifyTypeResolution(tables, origin, typeParam.getValue()))) {
395 LLVM_DEBUG({
396 llvm::dbgs() << "[verifyParamOfType] failed to verify type attribute\n";
397 });
398 paramCheckResult = failure();
399 }
400 }
401 LLVM_DEBUG({ llvm::dbgs() << "[verifyParamOfType] verified attribute\n"; });
402 // IntegerAttr and AffineMapAttr cannot contain symbol references
403 }
404 return paramCheckResult;
405}
406
407FailureOr<StructDefOp>
408verifyStructTypeResolution(SymbolTableCollection &tables, StructType ty, Operation *origin) {
409 auto res = ty.getDefinition(tables, origin);
410 if (failed(res)) {
411 return failure();
412 }
413 StructDefOp defForType = res.value().get();
414 if (!structTypesUnify(ty, defForType.getType({}), res->getNamespace())) {
415 return origin->emitError()
416 .append(
417 "Cannot unify parameters of type ", ty, " with parameters of '",
418 StructDefOp::getOperationName(), "' \"", defForType.getHeaderString(), '"'
419 )
420 .attachNote(defForType.getLoc())
421 .append("type parameters must unify with parameters defined here");
422 }
423 // If there are any SymbolRefAttr parameters on the StructType, ensure those refs are valid.
424 if (ArrayAttr tyParams = ty.getParams()) {
425 if (failed(verifyParamsOfType(tables, tyParams.getValue(), ty, origin))) {
426 return failure(); // verifyParamsOfType() already emits a sufficient error message
427 }
428 }
429 return defForType;
430}
431
432LogicalResult verifyTypeResolution(SymbolTableCollection &tables, Operation *origin, Type ty) {
433 if (StructType sTy = llvm::dyn_cast<StructType>(ty)) {
434 return verifyStructTypeResolution(tables, sTy, origin);
435 } else if (ArrayType aTy = llvm::dyn_cast<ArrayType>(ty)) {
436 if (failed(verifyParamsOfType(tables, aTy.getDimensionSizes(), aTy, origin))) {
437 return failure();
438 }
439 return verifyTypeResolution(tables, origin, aTy.getElementType());
440 } else if (TypeVarType vTy = llvm::dyn_cast<TypeVarType>(ty)) {
441 return verifyParamOfType(tables, vTy.getNameRef(), vTy, origin);
442 } else {
443 return success();
444 }
445}
446
447} // namespace llzk
within a display generated by the Derivative if and wherever such third party notices normally appear The contents of the NOTICE file are for informational purposes only and do not modify the License You may add Your own attribution notices within Derivative Works that You alongside or as an addendum to the NOTICE text from the provided that such additional attribution notices cannot be construed as modifying the License You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for or distribution of Your or for any such Derivative Works as a provided Your and distribution of the Work otherwise complies with the conditions stated in this License Submission of Contributions Unless You explicitly state any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this without any additional terms or conditions Notwithstanding the nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions Trademarks This License does not grant permission to use the trade names
Definition LICENSE.txt:139
Apache License January AND DISTRIBUTION Definitions License shall mean the terms and conditions for and distribution as defined by Sections through of this document Licensor shall mean the copyright owner or entity authorized by the copyright owner that is granting the License Legal Entity shall mean the union of the acting entity and all other entities that control are controlled by or are under common control with that entity For the purposes of this definition control direct or to cause the direction or management of such whether by contract or including but not limited to software source documentation and configuration files Object form shall mean any form resulting from mechanical transformation or translation of a Source including but not limited to compiled object generated and conversions to other media types Work shall mean the work of whether in Source or Object made available under the as indicated by a copyright notice that is included in or attached to the whether in Source or Object that is based or other modifications as a an original work of authorship For the purposes of this Derivative Works shall not include works that remain separable from
Definition LICENSE.txt:45
#define check(x)
Definition Ops.cpp:171
This file defines methods symbol lookup across LLZK operations and included files.
StructType getType(::std::optional<::mlir::ArrayAttr > constParams={})
Gets the StructType representing this struct.
static constexpr ::llvm::StringLiteral getOperationName()
Definition Ops.h.inc:1189
::std::string getHeaderString()
Generate header string, in the same format as the assemblyFormat.
Definition Ops.cpp:162
::mlir::FailureOr< SymbolLookupResult< StructDefOp > > getDefinition(::mlir::SymbolTableCollection &symbolTable, ::mlir::Operation *op, bool reportMissing=true) const
Gets the struct op that defines this struct.
Definition Types.cpp:46
::mlir::ArrayAttr getParams() const
static constexpr ::llvm::StringLiteral name
Definition Types.h.inc:38
static constexpr ::llvm::StringLiteral getOperationName()
Definition Ops.h.inc:583
void assertValidAttrForParamOfType(Attribute attr)
SymbolRefAttr appendLeafName(SymbolRefAttr orig, const Twine &newLeafSuffix)
constexpr char LANG_ATTR_NAME[]
Name of the attribute on the top-level ModuleOp that identifies the ModuleOp as the root module and s...
Definition Constants.h:23
bool isConcreteType(Type type, bool allowStructParams)
mlir::FlatSymbolRefAttr getFlatSymbolRefAttr(mlir::MLIRContext *context, const mlir::Twine &twine)
Construct a FlatSymbolRefAttr with the given content.
mlir::FailureOr< SymbolLookupResultUntyped > lookupTopLevelSymbol(mlir::SymbolTableCollection &tables, mlir::SymbolRefAttr symbol, mlir::Operation *origin, bool reportMissing=true)
FailureOr< StructType > getMainInstanceType(Operation *lookupFrom)
llvm::SmallVector< StringRef > getNames(SymbolRefAttr ref)
mlir::StringAttr getSymbolName(mlir::Operation *symbol)
Returns the name of the given symbol operation, or nullptr if no symbol is present.
bool structTypesUnify(StructType lhs, StructType rhs, ArrayRef< StringRef > rhsReversePrefix, UnificationMap *unifications)
FailureOr< ModuleOp > getRootModule(Operation *from)
SymbolRefAttr appendLeaf(SymbolRefAttr orig, FlatSymbolRefAttr newLeaf)
SymbolRefAttr replaceLeaf(SymbolRefAttr orig, FlatSymbolRefAttr newLeaf)
FailureOr< StructDefOp > verifyStructTypeResolution(SymbolTableCollection &tables, StructType ty, Operation *origin)
FailureOr< ModuleOp > getTopRootModule(Operation *from)
LogicalResult verifyTypeResolution(SymbolTableCollection &tables, Operation *origin, Type ty)
LogicalResult verifyParamsOfType(SymbolTableCollection &tables, ArrayRef< Attribute > tyParams, Type parameterizedType, Operation *origin)
mlir::SymbolRefAttr asSymbolRefAttr(mlir::StringAttr root, mlir::SymbolRefAttr tail)
Build a SymbolRefAttr that prepends tail with root, i.e., root::tail.
mlir::FailureOr< OpClass > getParentOfType(mlir::Operation *op)
Return the closest surrounding parent operation that is of type 'OpClass'.
Definition OpHelpers.h:45
FailureOr< SymbolLookupResult< StructDefOp > > getMainInstanceDef(SymbolTableCollection &symbolTable, Operation *lookupFrom)
FailureOr< SymbolRefAttr > getPathFromTopRoot(SymbolOpInterface to, ModuleOp *foundRoot)
LogicalResult verifyParamOfType(SymbolTableCollection &tables, SymbolRefAttr param, Type parameterizedType, Operation *origin)
llvm::SmallVector< FlatSymbolRefAttr > getPieces(SymbolRefAttr ref)
FailureOr< SymbolRefAttr > getPathFromRoot(SymbolOpInterface to, ModuleOp *foundRoot)
constexpr char MAIN_ATTR_NAME[]
Name of the attribute on the top-level ModuleOp that specifies the type of the main struct.
Definition Constants.h:28