Skip to content

Commit

Permalink
fix(resolver): Annotate assignments with a hint when dealing with str…
Browse files Browse the repository at this point in the history
…ucts (PLC-lang#1020)

Fixes PLC-lang#1019

This commit adds a type-hint on struct field assignments when dealing with array of structs. For example given structs 
- `STRUCT1 { idx: DINT, arr: ARRAY[...] OF STRUCT2 }` and 
- `STRUCT2 { x: DINT, y: DINT }`
The following snippet `ARRAY[1..3] OF STRUCT1 := [(idx := 0, arr := [(x := 1)])];` will result in `idx := 0` having a type-hint on `STRUCT1` and `x := 1` on `STRUCT2`
  • Loading branch information
volsa authored Nov 21, 2023
1 parent a26aa0b commit 477598f
Show file tree
Hide file tree
Showing 5 changed files with 132 additions and 26 deletions.
2 changes: 1 addition & 1 deletion compiler/plc_driver/src/pipelines.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ pub struct ParsedProject(Vec<CompilationUnit>);

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<T: SourceContainer>(
project: &Project<T>,
encoding: Option<&'static Encoding>,
Expand Down
Original file line number Diff line number Diff line change
@@ -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
}

30 changes: 30 additions & 0 deletions src/codegen/tests/initialization_test/type_initializers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
49 changes: 24 additions & 25 deletions src/resolver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<String>) -> Self {
StatementAnnotation::Value { resulting_type: type_name.into() }
}

pub fn is_const(&self) -> bool {
Expand Down Expand Up @@ -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());
Expand All @@ -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);
}
}

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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, ..) => {
Expand All @@ -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));
}
}

Expand Down Expand Up @@ -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, ..) => {
Expand Down Expand Up @@ -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))
}
}
_ => {}
Expand Down Expand Up @@ -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() {
Expand Down
55 changes: 55 additions & 0 deletions src/resolver/tests/resolve_literals_tests.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use plc_ast::literals::AstLiteral;
use plc_ast::{
ast::{AstStatement, ReferenceAccess, ReferenceExpr, TypeNature},
provider::IdProvider,
Expand Down Expand Up @@ -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");
}

0 comments on commit 477598f

Please sign in to comment.