Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 19 additions & 2 deletions src/codegen/generators/expression_generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2224,7 +2224,7 @@ impl<'ink, 'b> ExpressionCodeGenerator<'ink, 'b> {
initializer: &AstStatement,
) -> Result<BasicValueEnum<'ink>, Diagnostic> {
let array_value = self.generate_literal_array_value(
flatten_expression_list(initializer),
initializer,
self.get_type_hint_info_for(initializer)?,
&initializer.get_location(),
)?;
Expand All @@ -2238,7 +2238,7 @@ impl<'ink, 'b> ExpressionCodeGenerator<'ink, 'b> {
/// i16-array-value
fn generate_literal_array_value(
&self,
elements: Vec<&AstStatement>,
elements: &AstStatement,
data_type: &DataTypeInformation,
location: &SourceRange,
) -> Result<BasicValueEnum<'ink>, Diagnostic> {
Expand Down Expand Up @@ -2266,6 +2266,23 @@ impl<'ink, 'b> ExpressionCodeGenerator<'ink, 'b> {
))
}?;

// for arrays of struct we cannot flatten the expression list
// to generate the passed structs we need an expression list of assignments
// flatten_expression_list will will return a vec of only assignments
let elements = if self
.index
.get_effective_type_or_void_by_name(inner_type.get_name())
.information
.is_struct()
{
match elements {
AstStatement::ExpressionList { expressions, .. } => expressions.iter().collect(),
_ => unreachable!("This should always be an expression list"),
}
} else {
flatten_expression_list(elements)
};

let llvm_type = self.llvm_index.get_associated_type(inner_type.get_name())?;
let mut v = Vec::new();
for e in elements {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
source: src/codegen/tests/initialization_test/type_initializers.rs
expression: result
---
; ModuleID = 'main'
source_filename = "main"

%myStruct = type { i32, i32 }
%main = type { [2 x %myStruct], [2 x %myStruct] }

@str = unnamed_addr constant %myStruct { i32 40, i32 50 }
@alias_str = unnamed_addr constant %myStruct { i32 40, i32 50 }
@main_instance = global %main { [2 x %myStruct] [%myStruct { i32 20, i32 30 }, %myStruct { i32 40, i32 50 }], [2 x %myStruct] [%myStruct { i32 20, i32 30 }, %myStruct { i32 40, i32 50 }] }
@__myStruct__init = unnamed_addr constant %myStruct zeroinitializer
@__main.arr__init = unnamed_addr constant [2 x %myStruct] [%myStruct { i32 20, i32 30 }, %myStruct { i32 40, i32 50 }]
@__main.alias_arr__init = unnamed_addr constant [2 x %myStruct] [%myStruct { i32 20, i32 30 }, %myStruct { i32 40, i32 50 }]

define void @main(%main* %0) {
entry:
%arr = getelementptr inbounds %main, %main* %0, i32 0, i32 0
%alias_arr = getelementptr inbounds %main, %main* %0, i32 0, i32 1
ret void
}

28 changes: 28 additions & 0 deletions src/codegen/tests/initialization_test/type_initializers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,34 @@ fn struct_initializer_uses_fallback_to_field_default() {
assert!(diagnostics.is_empty());
}

#[test]
fn array_of_struct_initialization() {
let source = "
TYPE myStruct : STRUCT
a,b : DINT;
END_STRUCT
END_TYPE

TYPE AliasMyStruct : myStruct; END_TYPE

VAR_GLOBAL CONSTANT
str : myStruct := (a := 40, b := 50);
alias_str : AliasMyStruct := (a := 40, b := 50);
END_VAR

PROGRAM main
VAR
arr : ARRAY[0..1] OF myStruct := ((a := 20, b := 30), str);
alias_arr : ARRAY[0..1] OF AliasMyStruct := ((a := 20, b := 30), alias_str);
END_VAR
END_PROGRAM
";
let (result, diagnostics) = codegen(source);

insta::assert_snapshot!(result);
assert!(diagnostics.is_empty());
}

#[test]
fn type_defaults_are_used_for_uninitialized_constants() {
let result = codegen_without_unwrap(
Expand Down
23 changes: 23 additions & 0 deletions src/resolver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -811,6 +811,29 @@ impl<'i> TypeAnnotator<'i> {
StatementAnnotation::value(expected_type.get_name()),
);
self.update_expected_types(expected_type, initializer);

// handle annotation for array of struct
if let DataTypeInformation::Array {
inner_type_name, ..
} = expected_type.get_type_information()
{
let struct_type = self
.index
.get_effective_type_or_void_by_name(inner_type_name);
if struct_type.get_type_information().is_struct() {
if let AstStatement::ExpressionList { expressions, .. } = initializer {
for e in expressions {
// annotate with the arrays inner_type
self.annotation_map.annotate_type_hint(
e,
StatementAnnotation::Value {
resulting_type: struct_type.get_name().to_string(),
},
)
}
}
}
}
}
}
}
Expand Down
47 changes: 47 additions & 0 deletions src/resolver/tests/resolve_expressions_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3774,3 +3774,50 @@ fn multiple_negative_annotates_correctly() {
}
}
}

#[test]
fn array_of_struct_with_inital_values_annotated_correctly() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 I like your thoughtful testing!

let id_provider = IdProvider::default();
// GIVEN
let (unit, mut index) = index_with_ids(
"
TYPE myStruct : STRUCT
a,b : DINT;
END_STRUCT
END_TYPE

PROGRAM main
VAR
arr : ARRAY[0..1] OF myStruct := ((a:= 10, b:= 20), (a:= 30, b:= 40));
END_VAR
END_PROGRAM",
id_provider.clone(),
);

let mut annotations = annotate_with_ids(&unit, &mut index, id_provider);
index.import(std::mem::take(&mut annotations.new_index));

let container_name = &unit.implementations[0].name; // main
let members = index.get_container_members(container_name);
// there is only one member => arr
assert_eq!(1, members.len());

if let Some(AstStatement::ExpressionList { expressions, .. }) = index
.get_const_expressions()
.maybe_get_constant_statement(&members[0].initial_value)
{
// we initialized the array with 2 structs
assert_eq!(2, expressions.len());
let target_type = index
.find_effective_type_by_name("myStruct")
.expect("at this point we should have the type");
for e in expressions {
let type_hint = annotations
.get_type_hint(e, &index)
.expect("we should have a type hint");
assert_eq!(target_type, type_hint);
}
} else {
panic!("No initial value, initial value should be an expression list")
}
}
43 changes: 43 additions & 0 deletions tests/correctness/initial_values.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1413,3 +1413,46 @@ fn initial_value_of_function_return_struct() {
let _: i32 = compile_and_run(function.to_string(), &mut maintype);
assert_eq!([11, 22, 33], [maintype.a, maintype.b, maintype.c]);
}

#[test]
fn initial_value_in_array_of_struct() {
let function = "
TYPE myStruct : STRUCT
a,b : DINT;
END_STRUCT
END_TYPE

VAR_GLOBAL CONSTANT
str : myStruct := (a := 30, b := 40);
END_VAR

PROGRAM main
VAR_TEMP
arr : ARRAY[0..1] OF myStruct := ((a:= 10, b:= 20), str);
END_VAR
VAR
a,b,c,d: DINT;
END_VAR
a := arr[0].a;
b := arr[0].b;
c := arr[1].a;
d := arr[1].b;
END_PROGRAM
";

#[allow(dead_code)]
#[derive(Default)]
struct MainType {
a: i32,
b: i32,
c: i32,
d: i32,
}
let mut maintype = MainType::default();

let _: i32 = compile_and_run(function.to_string(), &mut maintype);
assert_eq!(
[10, 20, 30, 40],
[maintype.a, maintype.b, maintype.c, maintype.d]
);
}