diff --git a/compiler/plc_driver/src/pipelines.rs b/compiler/plc_driver/src/pipelines.rs index 2606a50067..05008c1930 100644 --- a/compiler/plc_driver/src/pipelines.rs +++ b/compiler/plc_driver/src/pipelines.rs @@ -40,7 +40,7 @@ pub struct ParsedProject(Vec); impl ParsedProject { /// Parses a giving project, transforming it to a `ParsedProject` - /// Reprots parsing diagnostics such as Syntax error on the fly + /// Reports parsing diagnostics such as Syntax error on the fly pub fn parse( project: &Project, encoding: Option<&'static Encoding>, diff --git a/src/codegen/tests/initialization_test/snapshots/rusty__codegen__tests__initialization_test__type_initializers__skipped_field_members_for_array_of_structs_are_zero_initialized.snap b/src/codegen/tests/initialization_test/snapshots/rusty__codegen__tests__initialization_test__type_initializers__skipped_field_members_for_array_of_structs_are_zero_initialized.snap new file mode 100644 index 0000000000..8e1d26f2ea --- /dev/null +++ b/src/codegen/tests/initialization_test/snapshots/rusty__codegen__tests__initialization_test__type_initializers__skipped_field_members_for_array_of_structs_are_zero_initialized.snap @@ -0,0 +1,22 @@ +--- +source: src/codegen/tests/initialization_test/type_initializers.rs +expression: res +--- +; ModuleID = 'main' +source_filename = "main" + +%main = type { [3 x %STRUCT1] } +%STRUCT1 = type { i32, [2 x %STRUCT2] } +%STRUCT2 = type { i32, i32 } + +@main_instance = global %main { [3 x %STRUCT1] [%STRUCT1 { i32 0, [2 x %STRUCT2] [%STRUCT2 { i32 1, i32 0 }, %STRUCT2 zeroinitializer] }, %STRUCT1 { i32 2, [2 x %STRUCT2] [%STRUCT2 { i32 1, i32 1 }, %STRUCT2 zeroinitializer] }, %STRUCT1 { i32 1, [2 x %STRUCT2] [%STRUCT2 { i32 1, i32 0 }, %STRUCT2 { i32 0, i32 2 }] }] } +@__STRUCT1__init = unnamed_addr constant %STRUCT1 zeroinitializer +@__STRUCT2__init = unnamed_addr constant %STRUCT2 zeroinitializer +@__main.var_init1__init = unnamed_addr constant [3 x %STRUCT1] [%STRUCT1 { i32 0, [2 x %STRUCT2] [%STRUCT2 { i32 1, i32 0 }, %STRUCT2 zeroinitializer] }, %STRUCT1 { i32 2, [2 x %STRUCT2] [%STRUCT2 { i32 1, i32 1 }, %STRUCT2 zeroinitializer] }, %STRUCT1 { i32 1, [2 x %STRUCT2] [%STRUCT2 { i32 1, i32 0 }, %STRUCT2 { i32 0, i32 2 }] }] + +define void @main(%main* %0) { +entry: + %var_init1 = getelementptr inbounds %main, %main* %0, i32 0, i32 0 + ret void +} + diff --git a/src/codegen/tests/initialization_test/type_initializers.rs b/src/codegen/tests/initialization_test/type_initializers.rs index 9610712a87..beffef7d12 100644 --- a/src/codegen/tests/initialization_test/type_initializers.rs +++ b/src/codegen/tests/initialization_test/type_initializers.rs @@ -604,5 +604,35 @@ fn enums_with_inline_initializer_are_initialized() { END_FUNCTION "#, ); + + insta::assert_snapshot!(res); +} + +#[test] +fn skipped_field_members_for_array_of_structs_are_zero_initialized() { + let res = codegen( + r#" + TYPE STRUCT1 : STRUCT + idx: DINT; + arr: ARRAY[1..2] OF STRUCT2; + END_STRUCT END_TYPE + + TYPE STRUCT2 : STRUCT + x: DINT; + y: DINT; + END_STRUCT END_TYPE + + PROGRAM main + VAR + var_init1 : ARRAY[1..3] OF STRUCT1 := [ + (idx := 0, arr := [(x := 1)]), + (idx := 2, arr := [(x := 1, y := 1)]), + (idx := 1, arr := [(x := 1), (y := 2)]) + ]; + END_VAR + END_PROGRAM + "#, + ); + insta::assert_snapshot!(res); } diff --git a/src/resolver.rs b/src/resolver.rs index b4a61d3137..f2f8bcd980 100644 --- a/src/resolver.rs +++ b/src/resolver.rs @@ -330,15 +330,9 @@ pub enum StatementAnnotation { } impl StatementAnnotation { - /// constructs a new StatementAnnotation::Value with the given type_name - /// this is a convinience method to take a &str and clones it itself - pub fn value(type_name: &str) -> Self { - StatementAnnotation::new_value(type_name.to_string()) - } - - /// constructs a new StatementAnnotation::Value with the given type_name - pub fn new_value(type_name: String) -> Self { - StatementAnnotation::Value { resulting_type: type_name } + /// Constructs a new [`StatementAnnotation::Value`] with the given type name + pub fn value(type_name: impl Into) -> Self { + StatementAnnotation::Value { resulting_type: type_name.into() } } pub fn is_const(&self) -> bool { @@ -979,8 +973,8 @@ impl<'i> TypeAnnotator<'i> { } AstStatement::ExpressionList(expressions) => { - let name = inner_data_type.get_name().to_string(); - let hint = StatementAnnotation::Value { resulting_type: name }; + let name = inner_data_type.get_name(); + let hint = StatementAnnotation::value(name); for expression in expressions { self.annotation_map.annotate_type_hint(expression, hint.clone()); @@ -994,14 +988,19 @@ impl<'i> TypeAnnotator<'i> { } AstStatement::Assignment(Assignment { left, right, .. }) if left.is_reference() => { - let AstStatement::Literal(AstLiteral::Array(array)) = right.as_ref().get_stmt() - else { - return; - }; - let Some(elements) = array.elements() else { return }; + if let AstStatement::Literal(AstLiteral::Array(array)) = right.as_ref().get_stmt() { + let Some(elements) = array.elements() else { return }; + + if let Some(datatype) = self.annotation_map.get_type(left, self.index).cloned() { + self.type_hint_for_array_of_structs(&datatype, elements, &ctx); + } + } - if let Some(datatype) = self.annotation_map.get_type(left, self.index).cloned() { - self.type_hint_for_array_of_structs(&datatype, elements, &ctx); + // https://github.com/PLC-lang/rusty/issues/1019 + if inner_data_type.information.is_struct() { + let name = inner_data_type.get_name(); + let hint = StatementAnnotation::value(name); + self.annotation_map.annotate_type_hint(statement, hint); } } @@ -1189,11 +1188,11 @@ impl<'i> TypeAnnotator<'i> { let ctx = VisitorContext { qualifier: None, ..ctx.clone() }; visit_all_statements!(self, &ctx, &data.index); let access_type = get_direct_access_type(&data.access); - self.annotate(statement, StatementAnnotation::Value { resulting_type: access_type.into() }); + self.annotate(statement, StatementAnnotation::value(access_type)); } AstStatement::HardwareAccess(data, ..) => { let access_type = get_direct_access_type(&data.access); - self.annotate(statement, StatementAnnotation::Value { resulting_type: access_type.into() }); + self.annotate(statement, StatementAnnotation::value(access_type)); } AstStatement::BinaryExpression(data, ..) => { visit_all_statements!(self, ctx, &data.left, &data.right); @@ -1286,7 +1285,7 @@ impl<'i> TypeAnnotator<'i> { }; if let Some(statement_type) = statement_type { - self.annotate(statement, StatementAnnotation::new_value(statement_type)); + self.annotate(statement, StatementAnnotation::value(statement_type)); } } AstStatement::UnaryExpression(data, ..) => { @@ -1310,7 +1309,7 @@ impl<'i> TypeAnnotator<'i> { }; if let Some(statement_type) = statement_type { - self.annotate(statement, StatementAnnotation::new_value(statement_type)); + self.annotate(statement, StatementAnnotation::value(statement_type)); } } @@ -1394,7 +1393,7 @@ impl<'i> TypeAnnotator<'i> { vec![] }; for (stmt, annotation) in statement_to_annotation { - self.annotate(stmt, StatementAnnotation::new_value(annotation)); + self.annotate(stmt, StatementAnnotation::value(annotation)); } } AstStatement::ReferenceExpr(data, ..) => { @@ -1514,7 +1513,7 @@ impl<'i> TypeAnnotator<'i> { .map(|base| self.annotation_map.get_type_or_void(base, self.index).get_name().to_string()) { let ptr_type = add_pointer_type(&mut self.annotation_map.new_index, inner_type); - self.annotate(stmt, StatementAnnotation::new_value(ptr_type)) + self.annotate(stmt, StatementAnnotation::value(ptr_type)) } } _ => {} @@ -1816,7 +1815,7 @@ impl<'i> TypeAnnotator<'i> { AstLiteral::String(StringValue { is_wide, value, .. }) => { let string_type_name = register_string_type(&mut self.annotation_map.new_index, *is_wide, value.len()); - self.annotate(statement, StatementAnnotation::new_value(string_type_name)); + self.annotate(statement, StatementAnnotation::value(string_type_name)); //collect literals so we can generate global constants later if ctx.is_in_a_body() { diff --git a/src/resolver/tests/resolve_literals_tests.rs b/src/resolver/tests/resolve_literals_tests.rs index 557533ed07..18868548fa 100644 --- a/src/resolver/tests/resolve_literals_tests.rs +++ b/src/resolver/tests/resolve_literals_tests.rs @@ -1,3 +1,4 @@ +use plc_ast::literals::AstLiteral; use plc_ast::{ ast::{AstStatement, ReferenceAccess, ReferenceExpr, TypeNature}, provider::IdProvider, @@ -507,3 +508,57 @@ fn expression_list_as_array_initilization_is_annotated_correctly() { unreachable!(); } } + +#[test] +fn struct_field_members_assignments_are_annotated_correctly_in_array_of_structs() { + let id_provider = IdProvider::default(); + let (unit, mut index) = index_with_ids( + " + TYPE STRUCT1 : STRUCT + x : DINT; + arr : ARRAY[0..1] OF STRUCT2; + END_STRUCT END_TYPE + + TYPE STRUCT2 : STRUCT + y : INT; + z : INT; + END_STRUCT END_TYPE + + PROGRAM main + VAR + var_init1 : ARRAY[0..1] OF STRUCT1 := [ + (x := 0, arr := [(y := 0), (z := 0)]) + ]; + END_VAR + END_PROGRAM + ", + id_provider.clone(), + ); + + let annotations = annotate_with_ids(&unit, &mut index, id_provider); + let var = unit.units[0].variable_blocks[0].variables[0].initializer.clone().unwrap(); + + // (x := 0, arr := [(y := 0), (z := 0)]) + let AstStatement::Literal(AstLiteral::Array(arr)) = &var.stmt else { panic!() }; + let AstStatement::ParenExpression(expr) = &arr.elements().unwrap().stmt else { panic!() }; + let AstStatement::ExpressionList(elements) = &expr.stmt else { panic!() }; + + // x := 0 + let x = &elements[0]; + assert_eq!(&annotations.get_type_hint(&x, &index).unwrap().name, "STRUCT1"); + + // arr := [(y := 0), (z := 0)] + let AstStatement::Assignment(assignment) = &elements[1].stmt else { panic!() }; + + // [(y := 0), (z := 0)] + let AstStatement::Literal(AstLiteral::Array(arr)) = &assignment.right.stmt else { panic!() }; + let AstStatement::ExpressionList(elements) = &arr.elements.as_ref().unwrap().stmt else { panic!() }; + + // y := 0 + let AstStatement::ParenExpression(y) = &elements[0].stmt else { panic!() }; + assert_eq!(&annotations.get_type_hint(&y, &index).unwrap().name, "STRUCT2"); + + // z := 0 + let AstStatement::ParenExpression(z) = &elements[1].stmt else { panic!() }; + assert_eq!(&annotations.get_type_hint(&z, &index).unwrap().name, "STRUCT2"); +}