From f5e0612b8ce090ac7f075307c802f5e4e3704d50 Mon Sep 17 00:00:00 2001 From: Andy Kaylor Date: Mon, 18 Aug 2025 13:51:11 -0700 Subject: [PATCH 1/6] [CIR] Add CIR vtable attribute This adds the #cir.vtable attribute definition and verification. Generation of the vtable will be implemented in a later change. --- .../include/clang/CIR/Dialect/IR/CIRAttrs.td | 40 +++++++++++++++ clang/lib/CIR/Dialect/IR/CIRAttrs.cpp | 38 ++++++++++++++ clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 3 +- clang/test/CIR/IR/invalid-vtable.cir | 50 ++++++++++++++++++- clang/test/CIR/IR/vtable-attr.cir | 12 +++++ 5 files changed, 141 insertions(+), 2 deletions(-) create mode 100644 clang/test/CIR/IR/vtable-attr.cir diff --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td index 89b4d25b37ba6..c57fabeccc296 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td +++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td @@ -496,6 +496,46 @@ def CIR_GlobalViewAttr : CIR_Attr<"GlobalView", "global_view", [ }]; } +//===----------------------------------------------------------------------===// +// VTableAttr +//===----------------------------------------------------------------------===// + +def VTableAttr : CIR_Attr<"VTable", "vtable", [TypedAttrInterface]> { + let summary = "Represents a C++ vtable"; + let description = [{ + Wraps a #cir.const_record containing vtable data. + + Example: + ``` + cir.global linkonce_odr @_ZTV1B = #cir.vtable<< + {#cir.const_array<[#cir.null : !cir.ptr, + #cir.global_view<@_ZTI1B> : !cir.ptr, + #cir.global_view<@_ZN1BD1Ev> : !cir.ptr, + #cir.global_view<@_ZN1BD0Ev> : !cir.ptr, + #cir.global_view<@_ZNK1A5quackEv> : !cir.ptr]> + : !cir.array x 5>}>> + : !cir.record<"", !cir.array x 5>> + ``` + }]; + + // `vtable_data` is a const record with one element, containing an array of + // vtable information. + let parameters = (ins AttributeSelfTypeParameter<"">:$type, + "mlir::ArrayAttr":$vtable_data); + + let builders = [ + AttrBuilderWithInferredContext<(ins "mlir::Type":$type, + "mlir::ArrayAttr":$vtable_data), [{ + return $_get(type.getContext(), type, vtable_data); + }]> + ]; + + let genVerifyDecl = 1; + let assemblyFormat = [{ + `<` custom($vtable_data) `>` + }]; +} + //===----------------------------------------------------------------------===// // ConstComplexAttr //===----------------------------------------------------------------------===// diff --git a/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp b/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp index 5f53a6335f37d..9050db5efc977 100644 --- a/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp @@ -424,6 +424,44 @@ cir::ConstVectorAttr::verify(function_ref emitError, return elementTypeCheck; } +//===----------------------------------------------------------------------===// +// CIR VTableAttr +//===----------------------------------------------------------------------===// + +LogicalResult cir::VTableAttr::verify( + llvm::function_ref emitError, mlir::Type type, + mlir::ArrayAttr vtableData) { + auto sTy = mlir::dyn_cast_if_present(type); + if (!sTy) + return emitError() << "expected !cir.record type result"; + if (sTy.getMembers().empty() || vtableData.empty()) + return emitError() << "expected record type with one or more subtype"; + + for (size_t i = 0; i < sTy.getMembers().size(); ++i) { + auto constArrayAttr = mlir::dyn_cast(vtableData[i]); + if (!constArrayAttr) + return emitError() << "expected constant array subtype"; + + if (cir::ConstRecordAttr::verify(emitError, type, vtableData).failed()) + return failure(); + + LogicalResult eltTypeCheck = success(); + auto arrayElts = mlir::cast(constArrayAttr.getElts()); + arrayElts.walkImmediateSubElements( + [&](Attribute attr) { + if (mlir::isa(attr)) + return; + + eltTypeCheck = emitError() + << "expected GlobalViewAttr or ConstPtrAttr"; + }, + [&](Type type) {}); + if (eltTypeCheck.failed()) + return eltTypeCheck; + } + return success(); +} + //===----------------------------------------------------------------------===// // CIR Dialect //===----------------------------------------------------------------------===// diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp index 72feee8709dc4..0a7a8793943aa 100644 --- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp @@ -342,7 +342,8 @@ static LogicalResult checkConstantTypes(mlir::Operation *op, mlir::Type opType, if (mlir::isa(attrType)) + cir::GlobalViewAttr, cir::PoisonAttr, cir::VTableAttr>( + attrType)) return success(); assert(isa(attrType) && "What else could we be looking at here?"); diff --git a/clang/test/CIR/IR/invalid-vtable.cir b/clang/test/CIR/IR/invalid-vtable.cir index b3afb581b2048..0c8a36d26a0a1 100644 --- a/clang/test/CIR/IR/invalid-vtable.cir +++ b/clang/test/CIR/IR/invalid-vtable.cir @@ -1,4 +1,4 @@ -// RUN: cir-opt %s -verify-diagnostics +// RUN: cir-opt %s -verify-diagnostics -split-input-file !s8i = !cir.int !u32i = !cir.int @@ -7,3 +7,51 @@ cir.func @reference_unknown_vtable() { %0 = cir.vtable.address_point(@some_vtable, address_point = ) : !cir.vptr cir.return } + +// ----- + +!rec_S = !cir.record +!u8i = !cir.int +!rec_anon_struct = !cir.record x 4>}> +module { + // expected-error @below {{expected !cir.record type result}} + cir.global external @_ZTV1S = #cir.vtable<{#cir.const_array<[#cir.ptr : !cir.ptr, #cir.ptr : !cir.ptr, #cir.global_view<@_ZN1S3keyEv> : !cir.ptr, #cir.global_view<@_ZN1S6nonKeyEv> : !cir.ptr]> : !cir.array x 4>}> : !cir.ptr + cir.func private dso_local @_ZN1S3keyEv(%arg0: !cir.ptr) + cir.func private dso_local @_ZN1S6nonKeyEv(%arg0: !cir.ptr) +} + +// ----- + +!rec_S = !cir.record +!u8i = !cir.int +!rec_anon_struct = !cir.record +module { + // expected-error @below {{expected record type with one or more subtype}} + cir.global external @_ZTV1S = #cir.vtable<{}> : !rec_anon_struct {alignment = 8 : i64} + cir.func private dso_local @_ZN1S3keyEv(%arg0: !cir.ptr) + cir.func private dso_local @_ZN1S6nonKeyEv(%arg0: !cir.ptr) +} + +// ----- + +!rec_S = !cir.record +!u8i = !cir.int +!rec_anon_struct = !cir.record}> +module { + // expected-error @below {{expected constant array subtype}} + cir.global external @_ZTV1S = #cir.vtable<{#cir.ptr : !cir.ptr}> : !rec_anon_struct {alignment = 8 : i64} + cir.func private dso_local @_ZN1S3keyEv(%arg0: !cir.ptr) + cir.func private dso_local @_ZN1S6nonKeyEv(%arg0: !cir.ptr) +} + +// ----- + +!rec_S = !cir.record +!u64i = !cir.int +!rec_anon_struct = !cir.record}> +module { + // expected-error @below {{expected GlobalViewAttr or ConstPtrAttr}} + cir.global external @_ZTV1S = #cir.vtable<{#cir.const_array<[#cir.int<1> : !u64i, #cir.int<1> : !u64i, #cir.int<3> : !u64i, #cir.int<4> : !u64i]> : !cir.array}> : !rec_anon_struct {alignment = 8 : i64} + cir.func private dso_local @_ZN1S3keyEv(%arg0: !cir.ptr) + cir.func private dso_local @_ZN1S6nonKeyEv(%arg0: !cir.ptr) +} diff --git a/clang/test/CIR/IR/vtable-attr.cir b/clang/test/CIR/IR/vtable-attr.cir new file mode 100644 index 0000000000000..4826bdcc90953 --- /dev/null +++ b/clang/test/CIR/IR/vtable-attr.cir @@ -0,0 +1,12 @@ +// RUN: cir-opt %s | FileCheck %s + +!rec_S = !cir.record +!u8i = !cir.int +!rec_anon_struct = !cir.record x 4>}> +module { + cir.global external @_ZTV1S = #cir.vtable<{#cir.const_array<[#cir.ptr : !cir.ptr, #cir.ptr : !cir.ptr, #cir.global_view<@_ZN1S3keyEv> : !cir.ptr, #cir.global_view<@_ZN1S6nonKeyEv> : !cir.ptr]> : !cir.array x 4>}> : !rec_anon_struct {alignment = 8 : i64} + // CHECK: cir.global external @_ZTV1S = #cir.vtable<{#cir.const_array<[#cir.ptr : !cir.ptr, #cir.ptr : !cir.ptr, #cir.global_view<@_ZN1S3keyEv> : !cir.ptr, #cir.global_view<@_ZN1S6nonKeyEv> : !cir.ptr]> : !cir.array x 4>}> : !rec_anon_struct {alignment = 8 : i64} + + cir.func private dso_local @_ZN1S3keyEv(%arg0: !cir.ptr) + cir.func private dso_local @_ZN1S6nonKeyEv(%arg0: !cir.ptr) +} From d37efd2fbe1c9633fe8a11722d8d7a1d5f834f41 Mon Sep 17 00:00:00 2001 From: Andy Kaylor Date: Wed, 20 Aug 2025 13:04:35 -0700 Subject: [PATCH 2/6] Address review feedback --- .../include/clang/CIR/Dialect/IR/CIRAttrs.td | 52 ++++++++++++++----- clang/lib/CIR/Dialect/IR/CIRAttrs.cpp | 4 +- 2 files changed, 41 insertions(+), 15 deletions(-) diff --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td index c57fabeccc296..8cd8f83c15834 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td +++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td @@ -500,28 +500,54 @@ def CIR_GlobalViewAttr : CIR_Attr<"GlobalView", "global_view", [ // VTableAttr //===----------------------------------------------------------------------===// -def VTableAttr : CIR_Attr<"VTable", "vtable", [TypedAttrInterface]> { +def CIR_VTableAttr : CIR_Attr<"VTable", "vtable", [TypedAttrInterface]> { let summary = "Represents a C++ vtable"; let description = [{ - Wraps a #cir.const_record containing vtable data. + Wraps a #cir.const_record containing one or more vtable arrays. - Example: + In most cases, the anonymous record type wrapped by this attribute will + contain a single array corresponding to the vtable for one class. However, + in the case of multiple inheritence, the anonymous structure may contain + multiple arrays, each of which is a vtable. + + Example 1 (single vtable): + ```mlir + cir.global linkonce_odr @_ZTV6Mother = + #cir.vtable<{ + #cir.const_array<[ + #cir.ptr : !cir.ptr, + #cir.global_view<@_ZTI6Mother> : !cir.ptr, + #cir.global_view<@_ZN6Mother9MotherFooEv> : !cir.ptr, + #cir.global_view<@_ZN6Mother10MotherFoo2Ev> : !cir.ptr + ]> : !cir.array x 4> + }> : !rec_anon_struct1 ``` - cir.global linkonce_odr @_ZTV1B = #cir.vtable<< - {#cir.const_array<[#cir.null : !cir.ptr, - #cir.global_view<@_ZTI1B> : !cir.ptr, - #cir.global_view<@_ZN1BD1Ev> : !cir.ptr, - #cir.global_view<@_ZN1BD0Ev> : !cir.ptr, - #cir.global_view<@_ZNK1A5quackEv> : !cir.ptr]> - : !cir.array x 5>}>> - : !cir.record<"", !cir.array x 5>> + + Example 2 (multiple vtables): + ```mlir + cir.global linkonce_odr @_ZTV5Child = + #cir.vtable<{ + #cir.const_array<[ + #cir.ptr : !cir.ptr, + #cir.global_view<@_ZTI5Child> : !cir.ptr, + #cir.global_view<@_ZN5Child9MotherFooEv> : !cir.ptr, + #cir.global_view<@_ZN6Mother10MotherFoo2Ev> : !cir.ptr + ]> : !cir.array x 4>, + #cir.const_array<[ + #cir.ptr<-8 : i64> : !cir.ptr, + #cir.global_view<@_ZTI5Child> : !cir.ptr, + #cir.global_view<@_ZN6Father9FatherFooEv> : !cir.ptr + ]> : !cir.array x 3> + }> : !rec_anon_struct2 ``` }]; // `vtable_data` is a const record with one element, containing an array of // vtable information. - let parameters = (ins AttributeSelfTypeParameter<"">:$type, - "mlir::ArrayAttr":$vtable_data); + let parameters = (ins + AttributeSelfTypeParameter<"">:$type, + "mlir::ArrayAttr":$vtable_data + ); let builders = [ AttrBuilderWithInferredContext<(ins "mlir::Type":$type, diff --git a/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp b/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp index 9050db5efc977..3581ebb74a0a4 100644 --- a/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp @@ -448,14 +448,14 @@ LogicalResult cir::VTableAttr::verify( LogicalResult eltTypeCheck = success(); auto arrayElts = mlir::cast(constArrayAttr.getElts()); arrayElts.walkImmediateSubElements( - [&](Attribute attr) { + [&](mlir::Attribute attr) { if (mlir::isa(attr)) return; eltTypeCheck = emitError() << "expected GlobalViewAttr or ConstPtrAttr"; }, - [&](Type type) {}); + [&](mlir::Type type) {}); if (eltTypeCheck.failed()) return eltTypeCheck; } From 98c808fc6bb2a4ab48b311f617bdffeacd812742 Mon Sep 17 00:00:00 2001 From: Andy Kaylor Date: Wed, 20 Aug 2025 15:29:05 -0700 Subject: [PATCH 3/6] Use range for vtable data verification --- clang/lib/CIR/Dialect/IR/CIRAttrs.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp b/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp index 3581ebb74a0a4..4814369505c2c 100644 --- a/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp @@ -437,14 +437,14 @@ LogicalResult cir::VTableAttr::verify( if (sTy.getMembers().empty() || vtableData.empty()) return emitError() << "expected record type with one or more subtype"; - for (size_t i = 0; i < sTy.getMembers().size(); ++i) { - auto constArrayAttr = mlir::dyn_cast(vtableData[i]); + if (cir::ConstRecordAttr::verify(emitError, type, vtableData).failed()) + return failure(); + + for (const auto &data : vtableData.getAsRange()) { + const auto &constArrayAttr = mlir::dyn_cast(data); if (!constArrayAttr) return emitError() << "expected constant array subtype"; - if (cir::ConstRecordAttr::verify(emitError, type, vtableData).failed()) - return failure(); - LogicalResult eltTypeCheck = success(); auto arrayElts = mlir::cast(constArrayAttr.getElts()); arrayElts.walkImmediateSubElements( From 32180c9946ccd27a814dc1835c78efa0ace16cef Mon Sep 17 00:00:00 2001 From: Andy Kaylor Date: Wed, 20 Aug 2025 15:46:14 -0700 Subject: [PATCH 4/6] Add multi-vtable test --- clang/test/CIR/IR/vtable-attr.cir | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/clang/test/CIR/IR/vtable-attr.cir b/clang/test/CIR/IR/vtable-attr.cir index 4826bdcc90953..3854208ff78cc 100644 --- a/clang/test/CIR/IR/vtable-attr.cir +++ b/clang/test/CIR/IR/vtable-attr.cir @@ -1,12 +1,19 @@ // RUN: cir-opt %s | FileCheck %s +!rec_Q = !cir.record !rec_S = !cir.record +!rec_S2 = !cir.record !u8i = !cir.int !rec_anon_struct = !cir.record x 4>}> +!rec_anon_struct1 = !cir.record x 4>, !cir.array x 3>}> module { cir.global external @_ZTV1S = #cir.vtable<{#cir.const_array<[#cir.ptr : !cir.ptr, #cir.ptr : !cir.ptr, #cir.global_view<@_ZN1S3keyEv> : !cir.ptr, #cir.global_view<@_ZN1S6nonKeyEv> : !cir.ptr]> : !cir.array x 4>}> : !rec_anon_struct {alignment = 8 : i64} // CHECK: cir.global external @_ZTV1S = #cir.vtable<{#cir.const_array<[#cir.ptr : !cir.ptr, #cir.ptr : !cir.ptr, #cir.global_view<@_ZN1S3keyEv> : !cir.ptr, #cir.global_view<@_ZN1S6nonKeyEv> : !cir.ptr]> : !cir.array x 4>}> : !rec_anon_struct {alignment = 8 : i64} + cir.global external @_ZTV2S2 = #cir.vtable<{#cir.const_array<[#cir.ptr : !cir.ptr, #cir.ptr : !cir.ptr, #cir.global_view<@_ZN1S3keyEv> : !cir.ptr, #cir.global_view<@_ZN1S6nonKeyEv> : !cir.ptr]> : !cir.array x 4>, #cir.const_array<[#cir.ptr<-8 : i64> : !cir.ptr, #cir.ptr : !cir.ptr, #cir.global_view<@_ZN2S23keyEv> : !cir.ptr]> : !cir.array x 3>}> : !rec_anon_struct1 {alignment = 8 : i64} + // CHECK: cir.global external @_ZTV2S2 = #cir.vtable<{#cir.const_array<[#cir.ptr : !cir.ptr, #cir.ptr : !cir.ptr, #cir.global_view<@_ZN1S3keyEv> : !cir.ptr, #cir.global_view<@_ZN1S6nonKeyEv> : !cir.ptr]> : !cir.array x 4>, #cir.const_array<[#cir.ptr<-8 : i64> : !cir.ptr, #cir.ptr : !cir.ptr, #cir.global_view<@_ZN2S23keyEv> : !cir.ptr]> : !cir.array x 3>}> : !rec_anon_struct1 {alignment = 8 : i64} + cir.func private dso_local @_ZN1S3keyEv(%arg0: !cir.ptr) cir.func private dso_local @_ZN1S6nonKeyEv(%arg0: !cir.ptr) + cir.func private dso_local @_ZN2S23keyEv(%arg0: !cir.ptr) } From 1736a32bbd5d6a693f82e122fae13792d97cad51 Mon Sep 17 00:00:00 2001 From: Andy Kaylor Date: Wed, 20 Aug 2025 15:52:11 -0700 Subject: [PATCH 5/6] One more test case --- clang/test/CIR/IR/invalid-vtable.cir | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/clang/test/CIR/IR/invalid-vtable.cir b/clang/test/CIR/IR/invalid-vtable.cir index 0c8a36d26a0a1..60aa9b29e2677 100644 --- a/clang/test/CIR/IR/invalid-vtable.cir +++ b/clang/test/CIR/IR/invalid-vtable.cir @@ -55,3 +55,19 @@ module { cir.func private dso_local @_ZN1S3keyEv(%arg0: !cir.ptr) cir.func private dso_local @_ZN1S6nonKeyEv(%arg0: !cir.ptr) } + +// ----- + +!rec_Q = !cir.record +!rec_S = !cir.record +!rec_S2 = !cir.record +!u8i = !cir.int +!rec_anon_struct = !cir.record x 4>, !cir.ptr}> +module { + // expected-error @below {{expected constant array subtype}} + cir.global external @_ZTV2S2 = #cir.vtable<{#cir.const_array<[#cir.ptr : !cir.ptr, #cir.ptr : !cir.ptr, #cir.global_view<@_ZN1S3keyEv> : !cir.ptr, #cir.global_view<@_ZN1S6nonKeyEv> : !cir.ptr]> : !cir.array x 4>, #cir.ptr : !cir.ptr}> : !rec_anon_struct {alignment = 8 : i64} + + cir.func private dso_local @_ZN1S3keyEv(%arg0: !cir.ptr) + cir.func private dso_local @_ZN1S6nonKeyEv(%arg0: !cir.ptr) + cir.func private dso_local @_ZN2S23keyEv(%arg0: !cir.ptr) +} From 6f964184d2c6594e613bef4a976c7ac78853f817 Mon Sep 17 00:00:00 2001 From: Andy Kaylor Date: Thu, 21 Aug 2025 09:31:10 -0700 Subject: [PATCH 6/6] Rename vtable_data to data --- clang/include/clang/CIR/Dialect/IR/CIRAttrs.td | 10 +++++----- clang/lib/CIR/Dialect/IR/CIRAttrs.cpp | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td index 8cd8f83c15834..77bd6a75a15b4 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td +++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td @@ -542,23 +542,23 @@ def CIR_VTableAttr : CIR_Attr<"VTable", "vtable", [TypedAttrInterface]> { ``` }]; - // `vtable_data` is a const record with one element, containing an array of + // `data` is a const record with one element, containing an array of // vtable information. let parameters = (ins AttributeSelfTypeParameter<"">:$type, - "mlir::ArrayAttr":$vtable_data + "mlir::ArrayAttr":$data ); let builders = [ AttrBuilderWithInferredContext<(ins "mlir::Type":$type, - "mlir::ArrayAttr":$vtable_data), [{ - return $_get(type.getContext(), type, vtable_data); + "mlir::ArrayAttr":$data), [{ + return $_get(type.getContext(), type, data); }]> ]; let genVerifyDecl = 1; let assemblyFormat = [{ - `<` custom($vtable_data) `>` + `<` custom($data) `>` }]; } diff --git a/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp b/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp index 4814369505c2c..95faad6746955 100644 --- a/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp @@ -430,18 +430,18 @@ cir::ConstVectorAttr::verify(function_ref emitError, LogicalResult cir::VTableAttr::verify( llvm::function_ref emitError, mlir::Type type, - mlir::ArrayAttr vtableData) { + mlir::ArrayAttr data) { auto sTy = mlir::dyn_cast_if_present(type); if (!sTy) return emitError() << "expected !cir.record type result"; - if (sTy.getMembers().empty() || vtableData.empty()) + if (sTy.getMembers().empty() || data.empty()) return emitError() << "expected record type with one or more subtype"; - if (cir::ConstRecordAttr::verify(emitError, type, vtableData).failed()) + if (cir::ConstRecordAttr::verify(emitError, type, data).failed()) return failure(); - for (const auto &data : vtableData.getAsRange()) { - const auto &constArrayAttr = mlir::dyn_cast(data); + for (const auto &element : data.getAsRange()) { + const auto &constArrayAttr = mlir::dyn_cast(element); if (!constArrayAttr) return emitError() << "expected constant array subtype";