diff --git a/src/codegen/debug.rs b/src/codegen/debug.rs index 373cc4477c..e5488e99c9 100644 --- a/src/codegen/debug.rs +++ b/src/codegen/debug.rs @@ -22,7 +22,8 @@ use plc_source::source_location::SourceLocation; use crate::{ index::{Index, PouIndexEntry, VariableIndexEntry}, typesystem::{ - DataType, DataTypeInformation, Dimension, StringEncoding, CHAR_TYPE, VOID_INTERNAL_NAME, WCHAR_TYPE, + DataType, DataTypeInformation, Dimension, StringEncoding, TypeSize, CHAR_TYPE, VOID_INTERNAL_NAME, + WCHAR_TYPE, }, DebugLevel, OptimizationLevel, }; @@ -584,6 +585,59 @@ impl<'ink> DebugBuilder<'ink> { Ok(()) } + fn create_subrange_type( + &mut self, + name: &str, + referenced_type: &str, + sub_range: &std::ops::Range, + location: &SourceLocation, + index: &Index, + types_index: &LlvmTypedIndex, + ) -> Result<(), Diagnostic> { + // Resolve the range bounds to create a unique typedef name based on backing type + let start_val = sub_range + .start + .as_int_value(index) + .map_err(|err| Diagnostic::codegen_error(err, location.clone()))?; + let end_val = sub_range + .end + .as_int_value(index) + .map_err(|err| Diagnostic::codegen_error(err, location.clone()))?; + let typedef_name = format!("__SUBRANGE_{start_val}_{end_val}__{referenced_type}"); + + // Check if we already have a debug type for this subrange signature + if let Some(existing_type) = self.types.get(&typedef_name.to_lowercase()).cloned() { + self.register_concrete_type(name, existing_type); + return Ok(()); + } + + let inner_dt = index.get_effective_type_by_name(referenced_type)?; + let inner_type = self.get_or_create_debug_type(inner_dt, index, types_index)?; + let file = location + .get_file_name() + .map(|it| self.get_or_create_debug_file(it)) + .unwrap_or_else(|| self.compile_unit.get_file()); + + let llvm_type = types_index.get_associated_type(name)?; + let align_bits = self.target_data.get_preferred_alignment(&llvm_type) * 8; + + let typedef = self.debug_info.create_typedef( + inner_type.into(), + &typedef_name, + file, + location.get_line_plus_one() as u32, + file.as_debug_info_scope(), + align_bits, + ); + + // Register under the canonical name for future reuse + self.register_concrete_type(&typedef_name, DebugType::Derived(typedef)); + // Also register under this specific type name for lookups + self.register_concrete_type(name, DebugType::Derived(typedef)); + + Ok(()) + } + fn create_subroutine_type( &mut self, return_type: Option<&DataType>, @@ -801,10 +855,12 @@ impl<'ink> Debug<'ink> for DebugBuilder<'ink> { self.create_string_type(name, length, *encoding, size, index, types_index) } DataTypeInformation::Alias { name, referenced_type } - | DataTypeInformation::Enum { name, referenced_type, .. } - | DataTypeInformation::SubRange { name, referenced_type, .. } => { + | DataTypeInformation::Enum { name, referenced_type, .. } => { self.create_typedef_type(name, referenced_type, location, index, types_index) } + DataTypeInformation::SubRange { name, referenced_type, sub_range } => { + self.create_subrange_type(name, referenced_type, sub_range, location, index, types_index) + } // Other types are just derived basic types _ => { log::debug!("Type {name} has unsupported debug info generation for {type_info:?}"); diff --git a/src/codegen/tests/debug_tests.rs b/src/codegen/tests/debug_tests.rs index 3d925c2f88..d57a304aea 100644 --- a/src/codegen/tests/debug_tests.rs +++ b/src/codegen/tests/debug_tests.rs @@ -1660,7 +1660,7 @@ fn range_datatype_debug() { let codegen = codegen( r#" TYPE RangeType : - DINT(0..100) := 0; + DINT(0..100) := 0; END_TYPE Function main : DINT @@ -1717,7 +1717,7 @@ fn range_datatype_debug() { !6 = !{null} !7 = !{} !8 = !DILocalVariable(name: "r", scope: !4, file: !3, line: 8, type: !9, align: 32) - !9 = !DIDerivedType(tag: DW_TAG_typedef, name: "RangeType", scope: !3, file: !3, line: 2, baseType: !10, align: 32) + !9 = !DIDerivedType(tag: DW_TAG_typedef, name: "__SUBRANGE_0_100__DINT", scope: !3, file: !3, line: 2, baseType: !10, align: 32) !10 = !DIBasicType(name: "DINT", size: 32, encoding: DW_ATE_signed, flags: DIFlagPublic) !11 = !DILocation(line: 8, column: 12, scope: !4) !12 = !DILocalVariable(name: "main", scope: !4, file: !3, line: 6, type: !10, align: 32) @@ -1727,3 +1727,269 @@ fn range_datatype_debug() { !16 = !DILocation(line: 12, column: 8, scope: !4) "#) } + +#[test] +fn range_datatype_reference_expr_bounds_debug() { + let codegen = codegen( + r#" + VAR_GLOBAL CONSTANT + ZERO : DINT := 0; + END_VAR + + TYPE RangeType : + DINT(ZERO..(100+3)) := 0; + END_TYPE + + Function main : DINT + VAR + r : RangeType; + END_VAR + r := 50; + main := r; + END_FUNCTION + "#, + ); + filtered_assert_snapshot!(codegen, @r#" + ; ModuleID = '' + source_filename = "" + target datalayout = "[filtered]" + target triple = "[filtered]" + + @ZERO = unnamed_addr constant i32 0, !dbg !0 + @llvm.global_ctors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 0, void ()* @__init___Test, i8* null }] + + define i32 @main() !dbg !9 { + entry: + %main = alloca i32, align 4 + %r = alloca i32, align 4 + call void @llvm.dbg.declare(metadata i32* %r, metadata !13, metadata !DIExpression()), !dbg !15 + store i32 0, i32* %r, align 4 + call void @llvm.dbg.declare(metadata i32* %main, metadata !16, metadata !DIExpression()), !dbg !17 + store i32 0, i32* %main, align 4 + store i32 50, i32* %r, align 4, !dbg !18 + %load_r = load i32, i32* %r, align 4, !dbg !19 + store i32 %load_r, i32* %main, align 4, !dbg !19 + %main_ret = load i32, i32* %main, align 4, !dbg !20 + ret i32 %main_ret, !dbg !20 + } + + ; Function Attrs: nofree nosync nounwind readnone speculatable willreturn + declare void @llvm.dbg.declare(metadata, metadata, metadata) #0 + + define void @__init___Test() { + entry: + ret void + } + + attributes #0 = { nofree nosync nounwind readnone speculatable willreturn } + + !llvm.module.flags = !{!5, !6} + !llvm.dbg.cu = !{!7} + + !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) + !1 = distinct !DIGlobalVariable(name: "ZERO", scope: !2, file: !2, line: 3, type: !3, isLocal: false, isDefinition: true) + !2 = !DIFile(filename: "", directory: "") + !3 = !DIDerivedType(tag: DW_TAG_const_type, baseType: !4) + !4 = !DIBasicType(name: "DINT", size: 32, encoding: DW_ATE_signed, flags: DIFlagPublic) + !5 = !{i32 2, !"Dwarf Version", i32 5} + !6 = !{i32 2, !"Debug Info Version", i32 3} + !7 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "RuSTy Structured text Compiler", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, globals: !8, splitDebugInlining: false) + !8 = !{!0} + !9 = distinct !DISubprogram(name: "main", linkageName: "main", scope: !2, file: !2, line: 10, type: !10, scopeLine: 14, flags: DIFlagPublic, spFlags: DISPFlagDefinition, unit: !7, retainedNodes: !12) + !10 = !DISubroutineType(flags: DIFlagPublic, types: !11) + !11 = !{null} + !12 = !{} + !13 = !DILocalVariable(name: "r", scope: !9, file: !2, line: 12, type: !14, align: 32) + !14 = !DIDerivedType(tag: DW_TAG_typedef, name: "__SUBRANGE_0_103__DINT", scope: !2, file: !2, line: 6, baseType: !4, align: 32) + !15 = !DILocation(line: 12, column: 12, scope: !9) + !16 = !DILocalVariable(name: "main", scope: !9, file: !2, line: 10, type: !4, align: 32) + !17 = !DILocation(line: 10, column: 17, scope: !9) + !18 = !DILocation(line: 14, column: 12, scope: !9) + !19 = !DILocation(line: 15, column: 12, scope: !9) + !20 = !DILocation(line: 16, column: 8, scope: !9) + "#); +} + +#[test] +fn range_datatype_fqn_reference_bounds_debug() { + let codegen = codegen( + r#" + TYPE RangeType : + DINT(prog.TEN..(100+3)) := 0; + END_TYPE + + PROGRAM prog + VAR CONSTANT + TEN : DINT := 10; + END_VAR + VAR + r : RangeType; + END_VAR + END_PROGRAM + "#, + ); + filtered_assert_snapshot!(codegen, @r#" + ; ModuleID = '' + source_filename = "" + target datalayout = "[filtered]" + target triple = "[filtered]" + + %prog = type { i32, i32 } + + @llvm.global_ctors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 0, void ()* @__init___Test, i8* null }] + @prog_instance = global %prog { i32 10, i32 0 }, !dbg !0 + + define void @prog(%prog* %0) !dbg !14 { + entry: + call void @llvm.dbg.declare(metadata %prog* %0, metadata !18, metadata !DIExpression()), !dbg !19 + %TEN = getelementptr inbounds %prog, %prog* %0, i32 0, i32 0 + %r = getelementptr inbounds %prog, %prog* %0, i32 0, i32 1 + ret void, !dbg !19 + } + + ; Function Attrs: nofree nosync nounwind readnone speculatable willreturn + declare void @llvm.dbg.declare(metadata, metadata, metadata) #0 + + define void @__init_prog(%prog* %0) { + entry: + %self = alloca %prog*, align 8 + store %prog* %0, %prog** %self, align 8 + ret void + } + + define void @__user_init_prog(%prog* %0) { + entry: + %self = alloca %prog*, align 8 + store %prog* %0, %prog** %self, align 8 + ret void + } + + define void @__init___Test() { + entry: + call void @__init_prog(%prog* @prog_instance) + call void @__user_init_prog(%prog* @prog_instance) + ret void + } + + attributes #0 = { nofree nosync nounwind readnone speculatable willreturn } + + !llvm.module.flags = !{!10, !11} + !llvm.dbg.cu = !{!12} + + !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) + !1 = distinct !DIGlobalVariable(name: "prog", scope: !2, file: !2, line: 6, type: !3, isLocal: false, isDefinition: true) + !2 = !DIFile(filename: "", directory: "") + !3 = !DICompositeType(tag: DW_TAG_structure_type, name: "prog", scope: !2, file: !2, line: 6, size: 64, align: 64, flags: DIFlagPublic, elements: !4, identifier: "prog") + !4 = !{!5, !8} + !5 = !DIDerivedType(tag: DW_TAG_member, name: "TEN", scope: !2, file: !2, line: 8, baseType: !6, size: 32, align: 32, flags: DIFlagPublic) + !6 = !DIDerivedType(tag: DW_TAG_const_type, baseType: !7) + !7 = !DIBasicType(name: "DINT", size: 32, encoding: DW_ATE_signed, flags: DIFlagPublic) + !8 = !DIDerivedType(tag: DW_TAG_member, name: "r", scope: !2, file: !2, line: 11, baseType: !9, size: 32, align: 32, offset: 32, flags: DIFlagPublic) + !9 = !DIDerivedType(tag: DW_TAG_typedef, name: "__SUBRANGE_10_103__DINT", scope: !2, file: !2, line: 2, baseType: !7, align: 32) + !10 = !{i32 2, !"Dwarf Version", i32 5} + !11 = !{i32 2, !"Debug Info Version", i32 3} + !12 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "RuSTy Structured text Compiler", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, globals: !13, splitDebugInlining: false) + !13 = !{!0} + !14 = distinct !DISubprogram(name: "prog", linkageName: "prog", scope: !2, file: !2, line: 6, type: !15, scopeLine: 13, flags: DIFlagPublic, spFlags: DISPFlagDefinition, unit: !12, retainedNodes: !17) + !15 = !DISubroutineType(flags: DIFlagPublic, types: !16) + !16 = !{null, !3} + !17 = !{} + !18 = !DILocalVariable(name: "prog", scope: !14, file: !2, line: 13, type: !3) + !19 = !DILocation(line: 13, column: 8, scope: !14) + "#); +} + +#[test] +fn range_datatype_debug_alias_reused() { + let codegen = codegen( + r#" + TYPE RangeType : + DINT(prog.ZERO..(100+3)) := 0; + END_TYPE + + PROGRAM prog + VAR CONSTANT + ZERO : DINT := 10; + END_VAR + VAR + u : RangeType; // first use of RangeType + v : RangeType; // second use of RangeType, should reuse debug info + w : DINT(10..103); // direct use of same range, should reuse debug info + END_VAR + END_PROGRAM + "#, + ); + filtered_assert_snapshot!(codegen, @r#" + ; ModuleID = '' + source_filename = "" + target datalayout = "[filtered]" + target triple = "[filtered]" + + %prog = type { i32, i32, i32, i32 } + + @llvm.global_ctors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 0, void ()* @__init___Test, i8* null }] + @prog_instance = global %prog { i32 10, i32 0, i32 0, i32 0 }, !dbg !0 + + define void @prog(%prog* %0) !dbg !16 { + entry: + call void @llvm.dbg.declare(metadata %prog* %0, metadata !20, metadata !DIExpression()), !dbg !21 + %ZERO = getelementptr inbounds %prog, %prog* %0, i32 0, i32 0 + %u = getelementptr inbounds %prog, %prog* %0, i32 0, i32 1 + %v = getelementptr inbounds %prog, %prog* %0, i32 0, i32 2 + %w = getelementptr inbounds %prog, %prog* %0, i32 0, i32 3 + ret void, !dbg !21 + } + + ; Function Attrs: nofree nosync nounwind readnone speculatable willreturn + declare void @llvm.dbg.declare(metadata, metadata, metadata) #0 + + define void @__init_prog(%prog* %0) { + entry: + %self = alloca %prog*, align 8 + store %prog* %0, %prog** %self, align 8 + ret void + } + + define void @__user_init_prog(%prog* %0) { + entry: + %self = alloca %prog*, align 8 + store %prog* %0, %prog** %self, align 8 + ret void + } + + define void @__init___Test() { + entry: + call void @__init_prog(%prog* @prog_instance) + call void @__user_init_prog(%prog* @prog_instance) + ret void + } + + attributes #0 = { nofree nosync nounwind readnone speculatable willreturn } + + !llvm.module.flags = !{!12, !13} + !llvm.dbg.cu = !{!14} + + !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) + !1 = distinct !DIGlobalVariable(name: "prog", scope: !2, file: !2, line: 6, type: !3, isLocal: false, isDefinition: true) + !2 = !DIFile(filename: "", directory: "") + !3 = !DICompositeType(tag: DW_TAG_structure_type, name: "prog", scope: !2, file: !2, line: 6, size: 128, align: 64, flags: DIFlagPublic, elements: !4, identifier: "prog") + !4 = !{!5, !8, !10, !11} + !5 = !DIDerivedType(tag: DW_TAG_member, name: "ZERO", scope: !2, file: !2, line: 8, baseType: !6, size: 32, align: 32, flags: DIFlagPublic) + !6 = !DIDerivedType(tag: DW_TAG_const_type, baseType: !7) + !7 = !DIBasicType(name: "DINT", size: 32, encoding: DW_ATE_signed, flags: DIFlagPublic) + !8 = !DIDerivedType(tag: DW_TAG_member, name: "u", scope: !2, file: !2, line: 11, baseType: !9, size: 32, align: 32, offset: 32, flags: DIFlagPublic) + !9 = !DIDerivedType(tag: DW_TAG_typedef, name: "__SUBRANGE_10_103__DINT", scope: !2, file: !2, line: 2, baseType: !7, align: 32) + !10 = !DIDerivedType(tag: DW_TAG_member, name: "v", scope: !2, file: !2, line: 12, baseType: !9, size: 32, align: 32, offset: 64, flags: DIFlagPublic) + !11 = !DIDerivedType(tag: DW_TAG_member, name: "w", scope: !2, file: !2, line: 13, baseType: !9, size: 32, align: 32, offset: 96, flags: DIFlagPublic) + !12 = !{i32 2, !"Dwarf Version", i32 5} + !13 = !{i32 2, !"Debug Info Version", i32 3} + !14 = distinct !DICompileUnit(language: DW_LANG_C, file: !2, producer: "RuSTy Structured text Compiler", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, globals: !15, splitDebugInlining: false) + !15 = !{!0} + !16 = distinct !DISubprogram(name: "prog", linkageName: "prog", scope: !2, file: !2, line: 6, type: !17, scopeLine: 15, flags: DIFlagPublic, spFlags: DISPFlagDefinition, unit: !14, retainedNodes: !19) + !17 = !DISubroutineType(flags: DIFlagPublic, types: !18) + !18 = !{null, !3} + !19 = !{} + !20 = !DILocalVariable(name: "prog", scope: !16, file: !2, line: 15, type: !3) + !21 = !DILocation(line: 15, column: 8, scope: !16) + "#); +} diff --git a/src/codegen/tests/snapshots/rusty__codegen__tests__code_gen_tests__sub_range_check_functions.snap b/src/codegen/tests/snapshots/rusty__codegen__tests__code_gen_tests__sub_range_check_functions.snap index b57788ef67..2c489f4f79 100644 --- a/src/codegen/tests/snapshots/rusty__codegen__tests__code_gen_tests__sub_range_check_functions.snap +++ b/src/codegen/tests/snapshots/rusty__codegen__tests__code_gen_tests__sub_range_check_functions.snap @@ -1,7 +1,6 @@ --- source: src/codegen/tests/code_gen_tests.rs expression: result -snapshot_kind: text --- ; ModuleID = '' source_filename = "" diff --git a/src/index.rs b/src/index.rs index e9a5b41fe9..8d5a038bca 100644 --- a/src/index.rs +++ b/src/index.rs @@ -1336,6 +1336,13 @@ impl Index { } variants.append(&mut variables); } + // import constant expressions in SubRange definitions + DataTypeInformation::SubRange { sub_range, .. } => { + sub_range.start = + self.import_type_size(&mut other.constant_expressions, &sub_range.start); + sub_range.end = + self.import_type_size(&mut other.constant_expressions, &sub_range.end); + } _ => {} } diff --git a/src/index/indexer/user_type_indexer.rs b/src/index/indexer/user_type_indexer.rs index 7ebd0139fa..02c563596e 100644 --- a/src/index/indexer/user_type_indexer.rs +++ b/src/index/indexer/user_type_indexer.rs @@ -328,10 +328,14 @@ impl UserTypeIndexer<'_, '_> { let information = if let Some(AstStatement::RangeStatement(RangeStatement { start, end })) = bounds.as_ref().map(|it| it.get_stmt()) { + let scope = self.current_scope(); + let start_size = self.ast_node_to_type_size(start, &scope); + let end_size = self.ast_node_to_type_size(end, &scope); + DataTypeInformation::SubRange { name: name.into(), referenced_type: referenced_type.into(), - sub_range: Box::new(*start.clone()..*end.clone()), + sub_range: start_size..end_size, } } else { DataTypeInformation::Alias { name: name.into(), referenced_type: referenced_type.into() } @@ -340,6 +344,19 @@ impl UserTypeIndexer<'_, '_> { self.register_type(name, information, TypeNature::Int); } + /// Converts an AstNode to a TypeSize, either as a literal or a const expression + fn ast_node_to_type_size(&mut self, node: &AstNode, scope: &Option) -> TypeSize { + match &node.stmt { + AstStatement::Literal(AstLiteral::Integer(value)) => TypeSize::from_literal(*value as i64), + _ => TypeSize::from_expression(self.index.get_mut_const_expressions().add_constant_expression( + node.clone(), + DINT_TYPE.to_string(), + scope.clone(), + None, + )), + } + } + fn register_type(&mut self, name: &str, information: DataTypeInformation, nature: TypeNature) { self.index.register_type(typesystem::DataType { name: name.into(), diff --git a/src/index/tests/index_tests.rs b/src/index/tests/index_tests.rs index 9545126540..d6f517b118 100644 --- a/src/index/tests/index_tests.rs +++ b/src/index/tests/index_tests.rs @@ -929,7 +929,7 @@ fn sub_range_boundaries_are_registered_at_the_index() { let expected = &DataTypeInformation::SubRange { name: "MyInt".to_string(), referenced_type: "INT".to_string(), - sub_range: Box::new(literal_int(7)..literal_int(1000)), + sub_range: TypeSize::from_literal(7)..TypeSize::from_literal(1000), }; assert_eq!(format!("{expected:?}"), format!("{my_int:?}")); diff --git a/src/resolver.rs b/src/resolver.rs index 330d3d7c18..a8557a0445 100644 --- a/src/resolver.rs +++ b/src/resolver.rs @@ -1293,18 +1293,35 @@ impl<'i> TypeAnnotator<'i> { if let Some(expected_type) = self.annotation_map.get_type(annotated_left_side, self.index).cloned() { // for assignments on SubRanges check if there are range type check functions - if let DataTypeInformation::SubRange { sub_range, .. } = expected_type.get_type_information() { - if let Some(statement) = self - .index - .find_range_check_implementation_for(expected_type.get_type_information()) - .map(|f| { - AstFactory::create_call_to_check_function_ast( - f.get_call_name(), - right_side.clone(), - *sub_range.clone(), - &annotated_left_side.get_location(), - ctx.id_provider.clone(), - ) + if let DataTypeInformation::SubRange { sub_range, referenced_type, .. } = + expected_type.get_type_information() + { + if let Some(statement) = sub_range + .start + .to_ast_node(self.index, &ctx.id_provider) + .zip(sub_range.end.to_ast_node(self.index, &ctx.id_provider)) + .and_then(|(start, end)| { + // Annotate the range bounds with the backing type of the subrange + // so they are correctly typed when passed to the check function + let backing_type = self.index.get_type(referenced_type).ok()?; + self.annotation_map + .annotate_type_hint(&start, StatementAnnotation::value(backing_type.get_name())); + self.annotation_map + .annotate_type_hint(&end, StatementAnnotation::value(backing_type.get_name())); + Some(start..end) + }) + .and_then(|range| { + self.index + .find_range_check_implementation_for(expected_type.get_type_information()) + .map(|f| { + AstFactory::create_call_to_check_function_ast( + f.get_call_name(), + right_side.clone(), + range, + &annotated_left_side.get_location(), + ctx.id_provider.clone(), + ) + }) }) { self.visit_call_statement(&statement, ctx); diff --git a/src/typesystem.rs b/src/typesystem.rs index dd9d8f3de3..1a6fb80246 100644 --- a/src/typesystem.rs +++ b/src/typesystem.rs @@ -9,6 +9,7 @@ use anyhow::{anyhow, Result}; use plc_ast::{ ast::{AstNode, AutoDerefType, Operator, PouType, TypeNature}, literals::{AstLiteral, StringValue}, + provider::IdProvider, }; use plc_source::source_location::SourceLocation; use rustc_hash::FxHashSet; @@ -367,6 +368,22 @@ impl TypeSize { TypeSize::Undetermined => unreachable!(), } } + + /// Converts this TypeSize to an AstNode, creating a literal for integer values + /// or returning the stored const expression + pub fn to_ast_node(&self, index: &Index, id_provider: &IdProvider) -> Option { + match self { + TypeSize::LiteralInteger(v) => Some(AstNode::new_literal( + AstLiteral::new_integer(*v as i128), + id_provider.clone().next_id(), + SourceLocation::internal(), + )), + TypeSize::ConstExpression(id) => { + index.get_const_expressions().get_constant_statement(id).cloned() + } + TypeSize::Undetermined => None, + } + } } /// indicates where this Struct origins from. @@ -437,7 +454,7 @@ pub enum DataTypeInformation { SubRange { name: TypeId, referenced_type: TypeId, - sub_range: Box>, + sub_range: Range, }, Alias { name: TypeId,