diff --git a/src/codegen/tests/.code_gen_tests.rs.pending-snap b/src/codegen/tests/.code_gen_tests.rs.pending-snap deleted file mode 100644 index 12a30608af..0000000000 --- a/src/codegen/tests/.code_gen_tests.rs.pending-snap +++ /dev/null @@ -1 +0,0 @@ -{"run_id":"1737024959-262323271","line":4115,"new":{"module_name":"rusty__codegen__tests__code_gen_tests","snapshot_name":"function_with_array_string_return","metadata":{"source":"src/codegen/tests/code_gen_tests.rs","assertion_line":4115,"expression":"res"},"snapshot":"; ModuleID = ''\nsource_filename = \"\"\n\n@utf08_literal_0 = private unnamed_addr constant [6 x i8] c\"hello\\00\"\n@utf08_literal_1 = private unnamed_addr constant [6 x i8] c\"world\\00\"\n\ndefine void @foo([81 x i8]* %0) {\nentry:\n %foo = alloca [81 x i8]*, align 8\n store [81 x i8]* %0, [81 x i8]** %foo, align 8\n %deref = load [81 x i8]*, [81 x i8]** %foo, align 8\n %tmpVar = getelementptr inbounds [81 x i8], [81 x i8]* %deref, i32 0, i32 0\n call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 1 %tmpVar, i8* align 1 getelementptr inbounds ([6 x i8], [6 x i8]* @utf08_literal_0, i32 0, i32 0), i32 6, i1 false)\n %deref1 = load [81 x i8]*, [81 x i8]** %foo, align 8\n %tmpVar2 = getelementptr inbounds [81 x i8], [81 x i8]* %deref1, i32 0, i32 1\n call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 1 %tmpVar2, i8* align 1 getelementptr inbounds ([6 x i8], [6 x i8]* @utf08_literal_1, i32 0, i32 0), i32 6, i1 false)\n ret void\n}\n\ndefine void @main() {\nentry:\n %__foo0 = alloca [2 x [81 x i8]], align 1\n %0 = bitcast [2 x [81 x i8]]* %__foo0 to i8*\n call void @llvm.memset.p0i8.i64(i8* align 1 %0, i8 0, i64 ptrtoint ([2 x [81 x i8]]* getelementptr ([2 x [81 x i8]], [2 x [81 x i8]]* null, i32 1) to i64), i1 false)\n %1 = bitcast [2 x [81 x i8]]* %__foo0 to [81 x i8]*\n call void @foo([81 x i8]* %1)\n %load___foo0 = load [2 x [81 x i8]], [2 x [81 x i8]]* %__foo0, align 1\n ret void\n}\n\n; Function Attrs: argmemonly nofree nounwind willreturn\ndeclare void @llvm.memcpy.p0i8.p0i8.i32(i8* noalias nocapture writeonly, i8* noalias nocapture readonly, i32, i1 immarg) #0\n\n; Function Attrs: argmemonly nofree nounwind willreturn writeonly\ndeclare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i1 immarg) #1\n\nattributes #0 = { argmemonly nofree nounwind willreturn }\nattributes #1 = { argmemonly nofree nounwind willreturn writeonly }\n; ModuleID = '__init___testproject'\nsource_filename = \"__init___testproject\"\n\n@llvm.global_ctors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 0, void ()* @__init___testproject, i8* null }]\n\ndefine void @__init___testproject() {\nentry:\n ret void\n}"},"old":{"module_name":"rusty__codegen__tests__code_gen_tests","metadata":{},"snapshot":""}} diff --git a/src/lowering/calls.rs b/src/lowering/calls.rs index d8e9e7464a..7d06eff9f3 100644 --- a/src/lowering/calls.rs +++ b/src/lowering/calls.rs @@ -6,8 +6,9 @@ use std::{borrow::BorrowMut, sync::atomic::AtomicI32}; use plc_ast::{ ast::{ - steal_expression_list, AccessModifier, Allocation, Assignment, AstFactory, AstNode, AstStatement, - CallStatement, CompilationUnit, LinkageType, Pou, Variable, VariableBlock, VariableBlockType, + flatten_expression_list, steal_expression_list, AccessModifier, Allocation, Assignment, AstFactory, + AstNode, AstStatement, CallStatement, CompilationUnit, LinkageType, Pou, Variable, VariableBlock, + VariableBlockType, }, control_statements::{AstControlStatement, ConditionalBlock, LoopStatement}, mut_visitor::{AstVisitorMut, WalkerMut}, @@ -228,9 +229,11 @@ impl AstVisitorMut for AggregateTypeLowerer { //If there's a call name in the function, it is a generic and needs to be replaced. //HACK: this is because we don't lower generics let function_entry = index.find_pou(&qualified_name).expect("Function not found"); + let return_name = Pou::calc_return_name(function_entry.get_name()).to_string(); let return_type = index.get_effective_type_or_void_by_name(&return_type_name); - let generic_function = generic_name.as_deref().and_then(|it| index.find_pou(it)); + let generic_function: Option<&crate::index::PouIndexEntry> = + generic_name.as_deref().and_then(|it| index.find_pou(it)); let is_generic_function = generic_function.is_some_and(|it| it.is_generic()); //TODO: needs to be on the function if return_type.is_aggregate_type() && !function_entry.is_builtin() { @@ -258,6 +261,26 @@ impl AstVisitorMut for AggregateTypeLowerer { None, original_location.clone(), ); + //If the function has an implicit call (foo(x := 1)), we need to add an assignment to the reference + let reference = if stmt + .parameters + .as_ref() + .map(|it| flatten_expression_list(it)) + .is_some_and(|it| it.iter().any(|it| it.is_assignment())) + { + let left = AstFactory::create_member_reference( + AstFactory::create_identifier( + &return_name, + original_location.clone(), + self.id_provider.next_id(), + ), + None, + self.id_provider.next_id(), + ); + AstFactory::create_assignment(left, reference, self.id_provider.next_id()) + } else { + reference + }; //TODO : we are creating th expression list twice in case of no params let mut parameters = stmt.parameters.as_mut().map(|it| steal_expression_list(it.borrow_mut())).unwrap_or_default(); @@ -1116,4 +1139,29 @@ mod tests { let unit = &units[0].0; assert_debug_snapshot!(unit); } + + #[test] + fn function_with_explicit_call_statement_has_explicit_return() { + let id_provider = IdProvider::default(); + let (unit, index, ..) = index_and_lower( + r#" + FUNCTION foo : STRING + VAR_INPUT + x : DINT; + END_VAR + foo := 'hello'; + END_FUNCTION + + FUNCTION main + foo(x := 1); + END_FUNCTION + "#, + id_provider.clone(), + ); + + assert_debug_snapshot!(index.find_pou_type("foo").unwrap()); + let (_, _, units) = annotate_and_lower_with_ids(unit, index, id_provider.clone()); + let unit = &units[0].0; + assert_debug_snapshot!(unit.implementations[1]); + } } diff --git a/src/lowering/snapshots/rusty__lowering__calls__tests__complex_call_statement_in_call.snap b/src/lowering/snapshots/rusty__lowering__calls__tests__complex_call_statement_in_call.snap index 92ee0dda89..06b692fdb6 100644 --- a/src/lowering/snapshots/rusty__lowering__calls__tests__complex_call_statement_in_call.snap +++ b/src/lowering/snapshots/rusty__lowering__calls__tests__complex_call_statement_in_call.snap @@ -1,7 +1,6 @@ --- source: src/lowering/calls.rs expression: "unit.implementations[1]" -snapshot_kind: text --- Implementation { name: "main", @@ -27,13 +26,23 @@ Implementation { parameters: Some( ExpressionList { expressions: [ - ReferenceExpr { - kind: Member( - Identifier { - name: "__complexFunc0", - }, - ), - base: None, + Assignment { + left: ReferenceExpr { + kind: Member( + Identifier { + name: "complexFunc", + }, + ), + base: None, + }, + right: ReferenceExpr { + kind: Member( + Identifier { + name: "__complexFunc0", + }, + ), + base: None, + }, }, Assignment { left: ReferenceExpr { @@ -69,13 +78,23 @@ Implementation { parameters: Some( ExpressionList { expressions: [ - ReferenceExpr { - kind: Member( - Identifier { - name: "__complexFunc1", - }, - ), - base: None, + Assignment { + left: ReferenceExpr { + kind: Member( + Identifier { + name: "complexFunc", + }, + ), + base: None, + }, + right: ReferenceExpr { + kind: Member( + Identifier { + name: "__complexFunc1", + }, + ), + base: None, + }, }, Assignment { left: ReferenceExpr { diff --git a/src/lowering/snapshots/rusty__lowering__calls__tests__complex_call_statement_in_call_with_implicit_literal_parameter.snap b/src/lowering/snapshots/rusty__lowering__calls__tests__complex_call_statement_in_call_with_implicit_literal_parameter.snap index 6a3af07e2b..82bc111c8d 100644 --- a/src/lowering/snapshots/rusty__lowering__calls__tests__complex_call_statement_in_call_with_implicit_literal_parameter.snap +++ b/src/lowering/snapshots/rusty__lowering__calls__tests__complex_call_statement_in_call_with_implicit_literal_parameter.snap @@ -1,7 +1,6 @@ --- source: src/lowering/calls.rs expression: "unit.implementations[1]" -snapshot_kind: text --- Implementation { name: "main", @@ -59,13 +58,23 @@ Implementation { parameters: Some( ExpressionList { expressions: [ - ReferenceExpr { - kind: Member( - Identifier { - name: "__complexFunc1", - }, - ), - base: None, + Assignment { + left: ReferenceExpr { + kind: Member( + Identifier { + name: "complexFunc", + }, + ), + base: None, + }, + right: ReferenceExpr { + kind: Member( + Identifier { + name: "__complexFunc1", + }, + ), + base: None, + }, }, Assignment { left: ReferenceExpr { diff --git a/src/lowering/snapshots/rusty__lowering__calls__tests__complex_call_statement_in_call_with_implicit_parameter.snap b/src/lowering/snapshots/rusty__lowering__calls__tests__complex_call_statement_in_call_with_implicit_parameter.snap index ad7057e669..7436aef007 100644 --- a/src/lowering/snapshots/rusty__lowering__calls__tests__complex_call_statement_in_call_with_implicit_parameter.snap +++ b/src/lowering/snapshots/rusty__lowering__calls__tests__complex_call_statement_in_call_with_implicit_parameter.snap @@ -1,7 +1,6 @@ --- source: src/lowering/calls.rs expression: "unit.implementations[1]" -snapshot_kind: text --- Implementation { name: "main", @@ -63,13 +62,23 @@ Implementation { parameters: Some( ExpressionList { expressions: [ - ReferenceExpr { - kind: Member( - Identifier { - name: "__complexFunc1", - }, - ), - base: None, + Assignment { + left: ReferenceExpr { + kind: Member( + Identifier { + name: "complexFunc", + }, + ), + base: None, + }, + right: ReferenceExpr { + kind: Member( + Identifier { + name: "__complexFunc1", + }, + ), + base: None, + }, }, Assignment { left: ReferenceExpr { diff --git a/src/lowering/snapshots/rusty__lowering__calls__tests__function_with_explicit_call_statement_has_explicit_return-2.snap b/src/lowering/snapshots/rusty__lowering__calls__tests__function_with_explicit_call_statement_has_explicit_return-2.snap new file mode 100644 index 0000000000..0b43d7de4a --- /dev/null +++ b/src/lowering/snapshots/rusty__lowering__calls__tests__function_with_explicit_call_statement_has_explicit_return-2.snap @@ -0,0 +1,104 @@ +--- +source: src/lowering/calls.rs +expression: "unit.implementations[1]" +--- +Implementation { + name: "main", + type_name: "main", + linkage: Internal, + pou_type: Function, + statements: [ + ExpressionList { + expressions: [ + Allocation { + name: "__foo0", + reference_type: "STRING", + }, + CallStatement { + operator: ReferenceExpr { + kind: Member( + Identifier { + name: "foo", + }, + ), + base: None, + }, + parameters: Some( + ExpressionList { + expressions: [ + Assignment { + left: ReferenceExpr { + kind: Member( + Identifier { + name: "foo", + }, + ), + base: None, + }, + right: ReferenceExpr { + kind: Member( + Identifier { + name: "__foo0", + }, + ), + base: None, + }, + }, + Assignment { + left: ReferenceExpr { + kind: Member( + Identifier { + name: "x", + }, + ), + base: None, + }, + right: LiteralInteger { + value: 1, + }, + }, + ], + }, + ), + }, + ReferenceExpr { + kind: Member( + Identifier { + name: "__foo0", + }, + ), + base: None, + }, + ], + }, + ], + location: SourceLocation { + span: Range( + TextLocation { + line: 9, + column: 12, + offset: 171, + }..TextLocation { + line: 9, + column: 24, + offset: 183, + }, + ), + }, + name_location: SourceLocation { + span: Range( + TextLocation { + line: 8, + column: 17, + offset: 154, + }..TextLocation { + line: 8, + column: 21, + offset: 158, + }, + ), + }, + overriding: false, + generic: false, + access: None, +} diff --git a/src/lowering/snapshots/rusty__lowering__calls__tests__function_with_explicit_call_statement_has_explicit_return.snap b/src/lowering/snapshots/rusty__lowering__calls__tests__function_with_explicit_call_statement_has_explicit_return.snap new file mode 100644 index 0000000000..d9748896e4 --- /dev/null +++ b/src/lowering/snapshots/rusty__lowering__calls__tests__function_with_explicit_call_statement_has_explicit_return.snap @@ -0,0 +1,86 @@ +--- +source: src/lowering/calls.rs +expression: "index.find_pou_type(\"foo\").unwrap()" +--- +DataType { + name: "foo", + initial_value: None, + information: Struct { + name: "foo", + members: [ + VariableIndexEntry { + name: "foo", + qualified_name: "foo.foo", + initial_value: None, + argument_type: ByRef( + InOut, + ), + is_constant: false, + is_var_external: false, + data_type_name: "__auto_pointer_to_STRING", + location_in_parent: 0, + linkage: Internal, + binding: None, + source_location: SourceLocation { + span: Range( + TextLocation { + line: 1, + column: 17, + offset: 18, + }..TextLocation { + line: 1, + column: 20, + offset: 21, + }, + ), + }, + varargs: None, + }, + VariableIndexEntry { + name: "x", + qualified_name: "foo.x", + initial_value: None, + argument_type: ByVal( + Input, + ), + is_constant: false, + is_var_external: false, + data_type_name: "DINT", + location_in_parent: 1, + linkage: Internal, + binding: None, + source_location: SourceLocation { + span: Range( + TextLocation { + line: 3, + column: 12, + offset: 61, + }..TextLocation { + line: 3, + column: 13, + offset: 62, + }, + ), + }, + varargs: None, + }, + ], + source: Pou( + Function, + ), + }, + nature: Any, + location: SourceLocation { + span: Range( + TextLocation { + line: 1, + column: 17, + offset: 18, + }..TextLocation { + line: 1, + column: 20, + offset: 21, + }, + ), + }, +} diff --git a/tests/lit/single/complex_return_types/function_with_explicit_calls.st b/tests/lit/single/complex_return_types/function_with_explicit_calls.st new file mode 100644 index 0000000000..e71bb05a7a --- /dev/null +++ b/tests/lit/single/complex_return_types/function_with_explicit_calls.st @@ -0,0 +1,14 @@ +// RUN: (%COMPILE %s && %RUN) | %CHECK %s +FUNCTION foo : STRING + VAR_INPUT + x : DINT; + END_VAR + foo := 'hello'; +END_FUNCTION +FUNCTION main : DINT +VAR + res : STRING; +END_VAR + res := foo(x := 1); + printf('%s$N', ADR(res)); // CHECK: hello +END_FUNCTION diff --git a/tests/lit/single/methods/fb_method_with_explicit_return.st b/tests/lit/single/methods/fb_method_with_explicit_return.st new file mode 100644 index 0000000000..ba6e22732a --- /dev/null +++ b/tests/lit/single/methods/fb_method_with_explicit_return.st @@ -0,0 +1,17 @@ +// RUN: (%COMPILE %s && %RUN) | %CHECK %s +FUNCTION_BLOCK fb + METHOD foo : STRING + VAR_INPUT + x : DINT; + END_VAR + foo := 'hello'; + END_METHOD +END_FUNCTION_BLOCK +FUNCTION main : DINT +VAR + my_fb : fb; + res : STRING; +END_VAR + res := my_fb.foo(x := 1); + printf('%s$N', ADR(res)); // CHECK: hello +END_FUNCTION