diff --git a/compiler/plc_ast/src/ast.rs b/compiler/plc_ast/src/ast.rs index 4f31adc0ac..c5253394f1 100644 --- a/compiler/plc_ast/src/ast.rs +++ b/compiler/plc_ast/src/ast.rs @@ -318,7 +318,7 @@ impl PouType { } pub fn is_function_or_init(&self) -> bool { - matches!(self, PouType::Function | PouType::Init | PouType::ProjectInit) + matches!(self, PouType::Function | PouType::Init | PouType::ProjectInit | PouType::Method { .. }) } } diff --git a/compiler/plc_diagnostics/src/diagnostics/diagnostics_registry.rs b/compiler/plc_diagnostics/src/diagnostics/diagnostics_registry.rs index 598ce05459..f8a9f510b6 100644 --- a/compiler/plc_diagnostics/src/diagnostics/diagnostics_registry.rs +++ b/compiler/plc_diagnostics/src/diagnostics/diagnostics_registry.rs @@ -150,7 +150,7 @@ lazy_static! { E046, Error, include_str!("./error_codes/E046.md"), E047, Warning, include_str!("./error_codes/E047.md"), // VLAs are always by reference E048, Error, include_str!("./error_codes/E048.md"), - E049, Error, include_str!("./error_codes/E049.md"), + E049, Warning, include_str!("./error_codes/E049.md"), E050, Error, include_str!("./error_codes/E050.md"), E051, Error, include_str!("./error_codes/E051.md"), E052, Error, include_str!("./error_codes/E052.md"), diff --git a/src/codegen/generators/expression_generator.rs b/src/codegen/generators/expression_generator.rs index d75f872aad..c6dc13afd2 100644 --- a/src/codegen/generators/expression_generator.rs +++ b/src/codegen/generators/expression_generator.rs @@ -558,7 +558,7 @@ impl<'ink, 'b> ExpressionCodeGenerator<'ink, 'b> { // after the call we need to copy the values for assigned outputs // this is only necessary for outputs defined as `rusty::index::ArgumentType::ByVal` (PROGRAM, FUNCTION_BLOCK) // FUNCTION outputs are defined as `rusty::index::ArgumentType::ByRef` // FIXME(mhasel): for standard-compliance functions also need to support VAR_OUTPUT - if !pou.is_function() { + if !(pou.is_function() || pou.is_method()) { let parameter_struct = match arguments_list.first() { Some(v) => v.into_pointer_value(), None => self.generate_lvalue(operator)?, @@ -833,52 +833,54 @@ impl<'ink, 'b> ExpressionCodeGenerator<'ink, 'b> { operator: &AstNode, function_context: &'b FunctionContext<'ink, 'b>, ) -> Result>, Diagnostic> { - let arguments_list = if matches!(pou, PouIndexEntry::Function { .. }) { - // we're calling a function - let declared_parameters = self.index.get_declared_parameters(implementation.get_type_name()); - self.generate_function_arguments(pou, passed_parameters, declared_parameters)? - } else { - // no function - let (class_ptr, call_ptr) = match pou { - PouIndexEntry::Method { .. } => { - let class_ptr = self.generate_lvalue(operator).or_else(|_| { - // this might be a local method - function_context - .function - .get_first_param() - .map(|class_ptr| class_ptr.into_pointer_value()) - .ok_or_else(|| Diagnostic::cannot_generate_call_statement(operator)) - })?; - let call_ptr = - self.allocate_function_struct_instance(implementation.get_call_name(), operator)?; - (Some(class_ptr), call_ptr) - } - // TODO: find a more reliable way to make sure if this is a call into a local action!! - PouIndexEntry::Action { .. } - if try_from!(operator, ReferenceExpr).is_some_and(|it| it.base.is_none()) => - { - // special handling for local actions, get the parameter from the function context + match pou { + PouIndexEntry::Function { .. } => { + // we're calling a function + let declared_parameters = self.index.get_declared_parameters(implementation.get_type_name()); + self.generate_function_arguments(pou, passed_parameters, declared_parameters) + } + PouIndexEntry::Method { .. } => { + let class_ptr = self.generate_lvalue(operator).or_else(|_| { + // this might be a local method function_context .function .get_first_param() - .map(|call_ptr| (None, call_ptr.into_pointer_value())) - .ok_or_else(|| Diagnostic::cannot_generate_call_statement(operator))? - } - _ => { - let call_ptr = self.generate_lvalue(operator)?; - (None, call_ptr) - } - }; - - // generate the pou call assignments - self.generate_stateful_pou_arguments( - implementation.get_call_name(), - class_ptr, - call_ptr, - passed_parameters, - )? - }; - Ok(arguments_list) + .map(|class_ptr| class_ptr.into_pointer_value()) + .ok_or_else(|| Diagnostic::cannot_generate_call_statement(operator)) + })?; + let declared_parameters = self.index.get_declared_parameters(implementation.get_type_name()); + let mut parameters = + self.generate_function_arguments(pou, passed_parameters, declared_parameters)?; + parameters.insert(0, class_ptr.into()); + Ok(parameters) + } + PouIndexEntry::Action { .. } + if try_from!(operator, ReferenceExpr).is_some_and(|it| it.base.is_none()) => + { + // special handling for local actions, get the parameter from the function context + let call_ptr = function_context + .function + .get_first_param() + .map(|call_ptr| call_ptr.into_pointer_value()) + .ok_or_else(|| Diagnostic::cannot_generate_call_statement(operator))?; + + self.generate_stateful_pou_arguments( + implementation.get_call_name(), + None, + call_ptr, + passed_parameters, + ) + } + _ => { + let call_ptr = self.generate_lvalue(operator)?; + self.generate_stateful_pou_arguments( + implementation.get_call_name(), + None, + call_ptr, + passed_parameters, + ) + } + } } fn generate_function_arguments( @@ -1142,27 +1144,6 @@ impl<'ink, 'b> ExpressionCodeGenerator<'ink, 'b> { } } - // TODO: will be deleted once methods work properly (like functions) - /// generates a new instance of a function called `function_name` and returns a PointerValue to it - /// - /// - `function_name` the name of the function as registered in the index - /// - `context` the statement used to report a possible Diagnostic on - fn allocate_function_struct_instance( - &self, - function_name: &str, - context: &AstNode, - ) -> Result, Diagnostic> { - let instance_name = format!("{function_name}_instance"); // TODO: Naming convention (see plc_util/src/convention.rs) - let function_type = self - .llvm_index - .find_associated_pou_type(function_name) //Using find instead of get to control the compile error - .ok_or_else(|| { - Diagnostic::codegen_error(format!("No type associated with '{instance_name:}'"), context) - })?; - - Ok(self.llvm.create_local_variable(&instance_name, &function_type)) - } - /// generates the assignments of a pou-call's parameters /// the call parameters are passed to the pou using a struct-instance with all the parameters /// diff --git a/src/codegen/generators/pou_generator.rs b/src/codegen/generators/pou_generator.rs index d0d9566756..4a99c9a82e 100644 --- a/src/codegen/generators/pou_generator.rs +++ b/src/codegen/generators/pou_generator.rs @@ -205,8 +205,10 @@ impl<'ink, 'cg> PouGenerator<'ink, 'cg> { new_llvm_index: &mut LlvmTypedIndex<'ink>, ) -> Result, Diagnostic> { let declared_parameters = self.index.get_declared_parameters(implementation.get_call_name()); - let parameters = self - .collect_parameters_for_implementation(implementation)? + let mut parameters = self.collect_parameters_for_implementation(implementation)?; + // if we are handling a method, take the first parameter as the instance + let instance = if implementation.is_method() { Some(parameters.remove(0)) } else { None }; + let mut parameters = parameters .iter() .enumerate() .map(|(i, p)| { @@ -257,6 +259,10 @@ impl<'ink, 'cg> PouGenerator<'ink, 'cg> { } }) .collect::>(); + // insert the instance as the first parameter + if let Some(instance) = instance { + parameters.insert(0, instance); + } let return_type = self .index @@ -350,15 +356,6 @@ impl<'ink, 'cg> PouGenerator<'ink, 'cg> { ) -> Result>, Diagnostic> { if !implementation.implementation_type.is_function_or_init() { let mut parameters = vec![]; - if implementation.get_implementation_type() == &ImplementationType::Method { - let class_name = - implementation.get_associated_class_name().expect("Method needs to have a class-name"); - let instance_members_struct_type: StructType = - self.llvm_index.get_associated_type(class_name).map(|it| it.into_struct_type())?; - parameters.push( - instance_members_struct_type.ptr_type(AddressSpace::from(ADDRESS_SPACE_GENERIC)).into(), - ); - } let instance_struct_type: StructType = self .llvm_index .get_associated_pou_type(implementation.get_type_name()) @@ -368,12 +365,24 @@ impl<'ink, 'cg> PouGenerator<'ink, 'cg> { Ok(parameters) } else { let declared_params = self.index.get_declared_parameters(implementation.get_call_name()); - //find the function's parameters - declared_params + let mut parameters = declared_params .iter() .map(|v| self.llvm_index.get_associated_type(v.get_type_name()).map(Into::into)) - .collect::, _>>() + .collect::, _>>()?; + + if implementation.get_implementation_type() == &ImplementationType::Method { + let class_name = + implementation.get_associated_class_name().expect("Method needs to have a class-name"); + let instance_members_struct_type: StructType = + self.llvm_index.get_associated_type(class_name).map(|it| it.into_struct_type())?; + parameters.insert( + 0, + instance_members_struct_type.ptr_type(AddressSpace::from(ADDRESS_SPACE_GENERIC)).into(), + ); + } + + Ok(parameters) } } @@ -551,6 +560,10 @@ impl<'ink, 'cg> PouGenerator<'ink, 'cg> { // cannot use index from members because return and temp variables may not be considered for index in build_struct_gep // eagerly handle the return-variable let mut params_iter = function_context.function.get_param_iter(); + // if we are in a method, skip the first parameter (the instance) + if matches!(function_context.linking_context.get_implementation_type(), ImplementationType::Method) { + params_iter.next(); + } if let Some(ret_v) = members.iter().find(|it| it.is_return()) { let return_type = index.get_associated_type(ret_v.get_type_name())?; let return_variable = self.llvm.create_local_variable(type_name, &return_type); diff --git a/src/codegen/tests/code_gen_tests.rs b/src/codegen/tests/code_gen_tests.rs index 23d0510043..bb17d717fc 100644 --- a/src/codegen/tests/code_gen_tests.rs +++ b/src/codegen/tests/code_gen_tests.rs @@ -951,6 +951,132 @@ fn fb_method_in_pou() { insta::assert_snapshot!(result) } +#[test] +fn fb_method_with_var_in_out() { + let prg = codegen( + r" + FUNCTION_BLOCK MyClass + VAR + x, y : INT; + END_VAR + + METHOD testMethod + VAR_IN_OUT myMethodArg : INT; END_VAR + myMethodArg := x; + END_METHOD + END_FUNCTION_BLOCK + + PROGRAM prg + VAR + cl : MyClass; + x : INT; + END_VAR + cl.testMethod(x); + END_PROGRAM + ", + ); + insta::assert_snapshot!(prg); +} + +#[test] +fn fb_method_with_var_input_defaults() { + let prg = codegen( + r" + FUNCTION_BLOCK MyClass + VAR + x, y : INT; + END_VAR + + METHOD testMethod + VAR_INPUT + myMethodArg : INT := 3; + END_VAR + x := myMethodArg; + END_METHOD + END_FUNCTION_BLOCK + + PROGRAM prg + VAR + cl : MyClass; + END_VAR + cl.testMethod(); + END_PROGRAM + ", + ); + insta::assert_snapshot!(prg); +} + +//A test for a method with an initialized input variable +#[test] +fn method_codegen_with_initialized_input() { + let prg = codegen( + r#" + FUNCTION_BLOCK fb + METHOD meth : DINT + VAR_INPUT + a : DINT := 5; + END_VAR + END_METHOD + meth(); + meth(4); + END_FUNCTION_BLOCK + + FUNCTION foo : DINT END_FUNCTION + "#, + ); + insta::assert_snapshot!(prg); +} + +//A test for a method with multiple input variables +#[test] +fn method_codegen_with_multiple_input() { + let prg = codegen( + r#" + FUNCTION_BLOCK fb + METHOD meth : DINT + VAR_INPUT + a : DINT := 6; + b : DINT; + c : DINT := 10; + END_VAR + END_METHOD + meth(1,2,3); + meth(5,7); //skip the last parameter it should have value 10 + meth(a := 3, b := 4); //skip the last parameter it should have value 10 + meth(b := 4); //skip the first and last parameter they should have value 6 and 10 + END_FUNCTION_BLOCK + "#, + ); + insta::assert_snapshot!(prg); +} + +#[test] +fn fb_method_called_as_function() { + let prg = codegen( + r" + FUNCTION_BLOCK MyClass + VAR + x, y : INT; + END_VAR + + METHOD testMethod : INT + VAR_INPUT myMethodArg : INT; END_VAR + VAR myMethodLocalVar : INT; END_VAR + + x := myMethodArg; + y := x + 1; + myMethodLocalVar := y + 1; + testMethod := myMethodLocalVar + 1; + END_METHOD + + testMethod(1); + testMethod(myMethodArg:= 3); + END_FUNCTION_BLOCK", + ); + + insta::assert_snapshot!(prg); +} + #[test] fn fb_method_called_locally() { let result = codegen( @@ -985,34 +1111,31 @@ fn fb_method_called_locally() { source_filename = "" %foo = type { i32 } - %foo.addToBar = type { i16 } @__foo__init = unnamed_addr constant %foo { i32 42 } define void @foo(%foo* %0) { entry: %bar = getelementptr inbounds %foo, %foo* %0, i32 0, i32 0 - %foo.addToBar_instance = alloca %foo.addToBar, align 8 - %1 = getelementptr inbounds %foo.addToBar, %foo.addToBar* %foo.addToBar_instance, i32 0, i32 0 - store i16 42, i16* %1, align 2 - %call = call i32 @foo.addToBar(%foo* %0, %foo.addToBar* %foo.addToBar_instance) + %call = call i32 @foo.addToBar(%foo* %0, i16 42) ret void } - define i32 @foo.addToBar(%foo* %0, %foo.addToBar* %1) { + define i32 @foo.addToBar(%foo* %0, i16 %1) { entry: %bar = getelementptr inbounds %foo, %foo* %0, i32 0, i32 0 - %in = getelementptr inbounds %foo.addToBar, %foo.addToBar* %1, i32 0, i32 0 - %addToBar = alloca i32, align 4 - store i32 0, i32* %addToBar, align 4 + %foo.addToBar = alloca i32, align 4 + %in = alloca i16, align 2 + store i16 %1, i16* %in, align 2 + store i32 0, i32* %foo.addToBar, align 4 %load_in = load i16, i16* %in, align 2 %2 = sext i16 %load_in to i32 %load_bar = load i32, i32* %bar, align 4 %tmpVar = add i32 %2, %load_bar store i32 %tmpVar, i32* %bar, align 4 %load_bar1 = load i32, i32* %bar, align 4 - store i32 %load_bar1, i32* %addToBar, align 4 - %foo.addToBar_ret = load i32, i32* %addToBar, align 4 + store i32 %load_bar1, i32* %foo.addToBar, align 4 + %foo.addToBar_ret = load i32, i32* %foo.addToBar, align 4 ret i32 %foo.addToBar_ret } @@ -1024,10 +1147,7 @@ fn fb_method_called_locally() { call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 1 %0, i8* align 1 bitcast (%foo* @__foo__init to i8*), i64 ptrtoint (%foo* getelementptr (%foo, %foo* null, i32 1) to i64), i1 false) store i32 0, i32* %x, align 4 call void @__init_foo(%foo* %fb) - %foo.addToBar_instance = alloca %foo.addToBar, align 8 - %1 = getelementptr inbounds %foo.addToBar, %foo.addToBar* %foo.addToBar_instance, i32 0, i32 0 - store i16 3, i16* %1, align 2 - %call = call i32 @foo.addToBar(%foo* %fb, %foo.addToBar* %foo.addToBar_instance) + %call = call i32 @foo.addToBar(%foo* %fb, i16 3) store i32 %call, i32* %x, align 4 ret void } @@ -1101,36 +1221,33 @@ fn fb_local_method_var_shadows_parent_var() { source_filename = "" %foo = type { i32 } - %foo.addToBar = type { i16, i32 } @__foo__init = unnamed_addr constant %foo { i32 42 } define void @foo(%foo* %0) { entry: %bar = getelementptr inbounds %foo, %foo* %0, i32 0, i32 0 - %foo.addToBar_instance = alloca %foo.addToBar, align 8 - %1 = getelementptr inbounds %foo.addToBar, %foo.addToBar* %foo.addToBar_instance, i32 0, i32 0 - store i16 42, i16* %1, align 2 - %call = call i32 @foo.addToBar(%foo* %0, %foo.addToBar* %foo.addToBar_instance) + %call = call i32 @foo.addToBar(%foo* %0, i16 42) ret void } - define i32 @foo.addToBar(%foo* %0, %foo.addToBar* %1) { + define i32 @foo.addToBar(%foo* %0, i16 %1) { entry: %bar = getelementptr inbounds %foo, %foo* %0, i32 0, i32 0 - %in = getelementptr inbounds %foo.addToBar, %foo.addToBar* %1, i32 0, i32 0 - %bar1 = getelementptr inbounds %foo.addToBar, %foo.addToBar* %1, i32 0, i32 1 - %addToBar = alloca i32, align 4 + %foo.addToBar = alloca i32, align 4 + %in = alloca i16, align 2 + store i16 %1, i16* %in, align 2 + %bar1 = alloca i32, align 4 store i32 69, i32* %bar1, align 4 - store i32 0, i32* %addToBar, align 4 + store i32 0, i32* %foo.addToBar, align 4 %load_in = load i16, i16* %in, align 2 %2 = sext i16 %load_in to i32 %load_bar = load i32, i32* %bar1, align 4 %tmpVar = add i32 %2, %load_bar store i32 %tmpVar, i32* %bar1, align 4 %load_bar2 = load i32, i32* %bar1, align 4 - store i32 %load_bar2, i32* %addToBar, align 4 - %foo.addToBar_ret = load i32, i32* %addToBar, align 4 + store i32 %load_bar2, i32* %foo.addToBar, align 4 + %foo.addToBar_ret = load i32, i32* %foo.addToBar, align 4 ret i32 %foo.addToBar_ret } @@ -1142,10 +1259,7 @@ fn fb_local_method_var_shadows_parent_var() { call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 1 %0, i8* align 1 bitcast (%foo* @__foo__init to i8*), i64 ptrtoint (%foo* getelementptr (%foo, %foo* null, i32 1) to i64), i1 false) store i32 0, i32* %x, align 4 call void @__init_foo(%foo* %fb) - %foo.addToBar_instance = alloca %foo.addToBar, align 8 - %1 = getelementptr inbounds %foo.addToBar, %foo.addToBar* %foo.addToBar_instance, i32 0, i32 0 - store i16 3, i16* %1, align 2 - %call = call i32 @foo.addToBar(%foo* %fb, %foo.addToBar* %foo.addToBar_instance) + %call = call i32 @foo.addToBar(%foo* %fb, i16 3) store i32 %call, i32* %x, align 4 ret void } @@ -1216,34 +1330,31 @@ fn prog_method_called_locally() { source_filename = "" %foo = type { i32 } - %foo.addToBar = type { i16 } @foo_instance = global %foo { i32 42 } define void @foo(%foo* %0) { entry: %bar = getelementptr inbounds %foo, %foo* %0, i32 0, i32 0 - %foo.addToBar_instance = alloca %foo.addToBar, align 8 - %1 = getelementptr inbounds %foo.addToBar, %foo.addToBar* %foo.addToBar_instance, i32 0, i32 0 - store i16 42, i16* %1, align 2 - %call = call i32 @foo.addToBar(%foo* %0, %foo.addToBar* %foo.addToBar_instance) + %call = call i32 @foo.addToBar(%foo* %0, i16 42) ret void } - define i32 @foo.addToBar(%foo* %0, %foo.addToBar* %1) { + define i32 @foo.addToBar(%foo* %0, i16 %1) { entry: %bar = getelementptr inbounds %foo, %foo* %0, i32 0, i32 0 - %in = getelementptr inbounds %foo.addToBar, %foo.addToBar* %1, i32 0, i32 0 - %addToBar = alloca i32, align 4 - store i32 0, i32* %addToBar, align 4 + %foo.addToBar = alloca i32, align 4 + %in = alloca i16, align 2 + store i16 %1, i16* %in, align 2 + store i32 0, i32* %foo.addToBar, align 4 %load_in = load i16, i16* %in, align 2 %2 = sext i16 %load_in to i32 %load_bar = load i32, i32* %bar, align 4 %tmpVar = add i32 %2, %load_bar store i32 %tmpVar, i32* %bar, align 4 %load_bar1 = load i32, i32* %bar, align 4 - store i32 %load_bar1, i32* %addToBar, align 4 - %foo.addToBar_ret = load i32, i32* %addToBar, align 4 + store i32 %load_bar1, i32* %foo.addToBar, align 4 + %foo.addToBar_ret = load i32, i32* %foo.addToBar, align 4 ret i32 %foo.addToBar_ret } @@ -1251,10 +1362,7 @@ fn prog_method_called_locally() { entry: %x = alloca i32, align 4 store i32 0, i32* %x, align 4 - %foo.addToBar_instance = alloca %foo.addToBar, align 8 - %0 = getelementptr inbounds %foo.addToBar, %foo.addToBar* %foo.addToBar_instance, i32 0, i32 0 - store i16 3, i16* %0, align 2 - %call = call i32 @foo.addToBar(%foo* @foo_instance, %foo.addToBar* %foo.addToBar_instance) + %call = call i32 @foo.addToBar(%foo* @foo_instance, i16 3) store i32 %call, i32* %x, align 4 ret void } @@ -1328,36 +1436,33 @@ fn prog_local_method_var_shadows_parent_var() { source_filename = "" %foo = type { i32 } - %foo.addToBar = type { i16, i32 } @foo_instance = global %foo { i32 42 } define void @foo(%foo* %0) { entry: %bar = getelementptr inbounds %foo, %foo* %0, i32 0, i32 0 - %foo.addToBar_instance = alloca %foo.addToBar, align 8 - %1 = getelementptr inbounds %foo.addToBar, %foo.addToBar* %foo.addToBar_instance, i32 0, i32 0 - store i16 42, i16* %1, align 2 - %call = call i32 @foo.addToBar(%foo* %0, %foo.addToBar* %foo.addToBar_instance) + %call = call i32 @foo.addToBar(%foo* %0, i16 42) ret void } - define i32 @foo.addToBar(%foo* %0, %foo.addToBar* %1) { + define i32 @foo.addToBar(%foo* %0, i16 %1) { entry: %bar = getelementptr inbounds %foo, %foo* %0, i32 0, i32 0 - %in = getelementptr inbounds %foo.addToBar, %foo.addToBar* %1, i32 0, i32 0 - %bar1 = getelementptr inbounds %foo.addToBar, %foo.addToBar* %1, i32 0, i32 1 - %addToBar = alloca i32, align 4 + %foo.addToBar = alloca i32, align 4 + %in = alloca i16, align 2 + store i16 %1, i16* %in, align 2 + %bar1 = alloca i32, align 4 store i32 69, i32* %bar1, align 4 - store i32 0, i32* %addToBar, align 4 + store i32 0, i32* %foo.addToBar, align 4 %load_in = load i16, i16* %in, align 2 %2 = sext i16 %load_in to i32 %load_bar = load i32, i32* %bar1, align 4 %tmpVar = add i32 %2, %load_bar store i32 %tmpVar, i32* %bar1, align 4 %load_bar2 = load i32, i32* %bar1, align 4 - store i32 %load_bar2, i32* %addToBar, align 4 - %foo.addToBar_ret = load i32, i32* %addToBar, align 4 + store i32 %load_bar2, i32* %foo.addToBar, align 4 + %foo.addToBar_ret = load i32, i32* %foo.addToBar, align 4 ret i32 %foo.addToBar_ret } @@ -1365,10 +1470,7 @@ fn prog_local_method_var_shadows_parent_var() { entry: %x = alloca i32, align 4 store i32 0, i32* %x, align 4 - %foo.addToBar_instance = alloca %foo.addToBar, align 8 - %0 = getelementptr inbounds %foo.addToBar, %foo.addToBar* %foo.addToBar_instance, i32 0, i32 0 - store i16 3, i16* %0, align 2 - %call = call i32 @foo.addToBar(%foo* @foo_instance, %foo.addToBar* %foo.addToBar_instance) + %call = call i32 @foo.addToBar(%foo* @foo_instance, i16 3) store i32 %call, i32* %x, align 4 ret void } @@ -4116,3 +4218,53 @@ fn function_with_array_string_return() { insta::assert_snapshot!(res, @r###" "###); } + +#[test] +fn method_with_aggregate_return_type() { + let res = codegen( + " + FUNCTION_BLOCK fb_with_method + VAR_TEMP + ret : STRING; + END_VAR + METHOD method_with_aggregagte_return: STRING + VAR_INPUT + in: STRING; + END_VAR + method_with_aggregagte_return := in; + END_METHOD + + ret := method_with_aggregagte_return('Hello'); + END_FUNCTION_BLOCK + ", + ); + + insta::assert_snapshot!(res); +} + +#[test] +fn methods_var_output() { + let res = codegen( + " + FUNCTION_BLOCK foo + METHOD baz + VAR_OUTPUT + out : STRING; + END_VAR + out := 'hello'; + END_METHOD + END_FUNCTION_BLOCK + + FUNCTION main + VAR + s: STRING; + fb: foo; + END_VAR + fb.baz(out => s); + fb.baz(s); + END_FUNCTION + ", + ); + + insta::assert_snapshot!(res); +} diff --git a/src/codegen/tests/debug_tests.rs b/src/codegen/tests/debug_tests.rs index 8f5a170a8a..acec439f6f 100644 --- a/src/codegen/tests/debug_tests.rs +++ b/src/codegen/tests/debug_tests.rs @@ -197,7 +197,7 @@ fn switch_case_debug_info() { x2 : INT; x3 : INT; END_VAR - + WHILE TRUE DO x1 := x1 + 1; @@ -364,16 +364,15 @@ fn dbg_declare_has_valid_metadata_references_for_methods() { // We want to make sure the `dbg.declare` for the method `foo` references a non-empty metadata field, i.e. // `!` should not be `! = {}`. Concretely, `!17` should be non-empty - assert!(codegen.contains(r#"call void @llvm.dbg.declare(metadata %fb.foo* %1, metadata !17, metadata !DIExpression()), !dbg !16"#)); - assert!(codegen - .contains(r#"!17 = !DILocalVariable(name: "fb.foo", scope: !15, file: !2, line: 3, type: !18)"#)); + assert!(codegen.contains( + r#"call void @llvm.dbg.declare(metadata %fb* %0, metadata !13, metadata !DIExpression()), !dbg !16"# + )); assert_snapshot!(codegen, @r#" ; ModuleID = '' source_filename = "" %fb = type {} - %fb.foo = type {} @__fb__init = unnamed_addr constant %fb zeroinitializer, !dbg !0 @@ -383,10 +382,9 @@ fn dbg_declare_has_valid_metadata_references_for_methods() { ret void, !dbg !14 } - define void @fb.foo(%fb* %0, %fb.foo* %1) !dbg !15 { + define void @fb.foo(%fb* %0) !dbg !15 { entry: call void @llvm.dbg.declare(metadata %fb* %0, metadata !13, metadata !DIExpression()), !dbg !16 - call void @llvm.dbg.declare(metadata %fb.foo* %1, metadata !17, metadata !DIExpression()), !dbg !16 ret void, !dbg !16 } @@ -415,8 +413,6 @@ fn dbg_declare_has_valid_metadata_references_for_methods() { !14 = !DILocation(line: 5, column: 8, scope: !10) !15 = distinct !DISubprogram(name: "fb.foo", linkageName: "fb.foo", scope: !2, file: !2, line: 3, type: !11, scopeLine: 4, flags: DIFlagPublic, spFlags: DISPFlagDefinition, unit: !7, retainedNodes: !4) !16 = !DILocation(line: 4, column: 8, scope: !15) - !17 = !DILocalVariable(name: "fb.foo", scope: !15, file: !2, line: 3, type: !18) - !18 = !DICompositeType(tag: DW_TAG_structure_type, name: "fb.foo", scope: !2, file: !2, line: 3, flags: DIFlagPublic, elements: !4, identifier: "fb.foo") ; ModuleID = '__initializers' source_filename = "__initializers" diff --git a/src/codegen/tests/initialization_test/complex_initializers.rs b/src/codegen/tests/initialization_test/complex_initializers.rs index e1e2cc6fe0..a0d7adf72a 100644 --- a/src/codegen/tests/initialization_test/complex_initializers.rs +++ b/src/codegen/tests/initialization_test/complex_initializers.rs @@ -790,8 +790,6 @@ fn stateful_pous_methods_and_structs_get_init_functions() { %foo = type {} %cl = type {} %myStruct = type {} - %foo.m = type {} - %cl.m = type {} @prog_instance = global %prog zeroinitializer @__foo__init = unnamed_addr constant %foo zeroinitializer @@ -808,7 +806,7 @@ fn stateful_pous_methods_and_structs_get_init_functions() { ret void } - define void @foo.m(%foo* %0, %foo.m* %1) { + define void @foo.m(%foo* %0) { entry: ret void } @@ -818,7 +816,7 @@ fn stateful_pous_methods_and_structs_get_init_functions() { ret void } - define void @cl.m(%cl* %0, %cl.m* %1) { + define void @cl.m(%cl* %0) { entry: ret void } @@ -1641,7 +1639,6 @@ fn initializing_method_variables_with_refs() { source_filename = "" %foo = type {} - %foo.bar = type { i32, i32* } @__foo__init = unnamed_addr constant %foo zeroinitializer @@ -1650,10 +1647,10 @@ fn initializing_method_variables_with_refs() { ret void } - define void @foo.bar(%foo* %0, %foo.bar* %1) { + define void @foo.bar(%foo* %0) { entry: - %x = getelementptr inbounds %foo.bar, %foo.bar* %1, i32 0, i32 0 - %px = getelementptr inbounds %foo.bar, %foo.bar* %1, i32 0, i32 1 + %x = alloca i32, align 4 + %px = alloca i32*, align 8 store i32 10, i32* %x, align 4 store i32* %x, i32** %px, align 8 store i32* %x, i32** %px, align 8 @@ -1707,7 +1704,6 @@ fn initializing_method_variables_with_refs_referencing_parent_pou_variable() { source_filename = "" %foo = type { i32 } - %foo.bar = type { i32* } @__foo__init = unnamed_addr constant %foo { i32 5 } @@ -1717,10 +1713,10 @@ fn initializing_method_variables_with_refs_referencing_parent_pou_variable() { ret void } - define void @foo.bar(%foo* %0, %foo.bar* %1) { + define void @foo.bar(%foo* %0) { entry: %x = getelementptr inbounds %foo, %foo* %0, i32 0, i32 0 - %px = getelementptr inbounds %foo.bar, %foo.bar* %1, i32 0, i32 0 + %px = alloca i32*, align 8 store i32* %x, i32** %px, align 8 store i32* %x, i32** %px, align 8 ret void @@ -1773,7 +1769,6 @@ fn initializing_method_variables_with_refs_referencing_global_variable() { source_filename = "" %foo = type {} - %foo.bar = type { i32* } @x = global i32 0 @__foo__init = unnamed_addr constant %foo zeroinitializer @@ -1783,9 +1778,9 @@ fn initializing_method_variables_with_refs_referencing_global_variable() { ret void } - define void @foo.bar(%foo* %0, %foo.bar* %1) { + define void @foo.bar(%foo* %0) { entry: - %px = getelementptr inbounds %foo.bar, %foo.bar* %1, i32 0, i32 0 + %px = alloca i32*, align 8 store i32* @x, i32** %px, align 8 store i32* @x, i32** %px, align 8 ret void @@ -1839,7 +1834,6 @@ fn initializing_method_variables_with_refs_shadowing() { source_filename = "" %foo = type {} - %foo.bar = type { i32, i32* } @x = global i32 0 @__foo__init = unnamed_addr constant %foo zeroinitializer @@ -1849,10 +1843,10 @@ fn initializing_method_variables_with_refs_shadowing() { ret void } - define void @foo.bar(%foo* %0, %foo.bar* %1) { + define void @foo.bar(%foo* %0) { entry: - %x = getelementptr inbounds %foo.bar, %foo.bar* %1, i32 0, i32 0 - %px = getelementptr inbounds %foo.bar, %foo.bar* %1, i32 0, i32 1 + %x = alloca i32, align 4 + %px = alloca i32*, align 8 store i32 0, i32* %x, align 4 store i32* %x, i32** %px, align 8 store i32* %x, i32** %px, align 8 @@ -1903,7 +1897,6 @@ fn initializing_method_variables_with_alias() { source_filename = "" %foo = type {} - %foo.bar = type { i32, i32* } @__foo__init = unnamed_addr constant %foo zeroinitializer @@ -1912,10 +1905,10 @@ fn initializing_method_variables_with_alias() { ret void } - define void @foo.bar(%foo* %0, %foo.bar* %1) { + define void @foo.bar(%foo* %0) { entry: - %x = getelementptr inbounds %foo.bar, %foo.bar* %1, i32 0, i32 0 - %px = getelementptr inbounds %foo.bar, %foo.bar* %1, i32 0, i32 1 + %x = alloca i32, align 4 + %px = alloca i32*, align 8 store i32 0, i32* %x, align 4 store i32* null, i32** %px, align 8 store i32* %x, i32** %px, align 8 @@ -1966,7 +1959,6 @@ fn initializing_method_variables_with_reference_to() { source_filename = "" %foo = type {} - %foo.bar = type { i32, i32* } @__foo__init = unnamed_addr constant %foo zeroinitializer @@ -1975,10 +1967,10 @@ fn initializing_method_variables_with_reference_to() { ret void } - define void @foo.bar(%foo* %0, %foo.bar* %1) { + define void @foo.bar(%foo* %0) { entry: - %x = getelementptr inbounds %foo.bar, %foo.bar* %1, i32 0, i32 0 - %px = getelementptr inbounds %foo.bar, %foo.bar* %1, i32 0, i32 1 + %x = alloca i32, align 4 + %px = alloca i32*, align 8 store i32 0, i32* %x, align 4 store i32* null, i32** %px, align 8 store i32* %x, i32** %px, align 8 @@ -2037,7 +2029,6 @@ fn methods_call_init_functions_for_their_members() { %foo = type { i32, i32* } %bar = type {} - %bar.baz = type { %foo } @__foo__init = unnamed_addr constant %foo zeroinitializer @__bar__init = unnamed_addr constant %bar zeroinitializer @@ -2054,11 +2045,11 @@ fn methods_call_init_functions_for_their_members() { ret void } - define void @bar.baz(%bar* %0, %bar.baz* %1) { + define void @bar.baz(%bar* %0) { entry: - %fb = getelementptr inbounds %bar.baz, %bar.baz* %1, i32 0, i32 0 - %2 = bitcast %foo* %fb to i8* - call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 1 %2, i8* align 1 bitcast (%foo* @__foo__init to i8*), i64 ptrtoint (%foo* getelementptr (%foo, %foo* null, i32 1) to i64), i1 false) + %fb = alloca %foo, align 8 + %1 = bitcast %foo* %fb to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 1 %1, i8* align 1 bitcast (%foo* @__foo__init to i8*), i64 ptrtoint (%foo* getelementptr (%foo, %foo* null, i32 1) to i64), i1 false) call void @__init_foo(%foo* %fb) ret void } diff --git a/src/codegen/tests/snapshots/rusty__codegen__tests__code_gen_tests__class_member_access_from_method.snap b/src/codegen/tests/snapshots/rusty__codegen__tests__code_gen_tests__class_member_access_from_method.snap index bac268b86d..c0c72221fc 100644 --- a/src/codegen/tests/snapshots/rusty__codegen__tests__code_gen_tests__class_member_access_from_method.snap +++ b/src/codegen/tests/snapshots/rusty__codegen__tests__code_gen_tests__class_member_access_from_method.snap @@ -1,13 +1,11 @@ --- source: src/codegen/tests/code_gen_tests.rs expression: result -snapshot_kind: text --- ; ModuleID = '' source_filename = "" %MyClass = type { i16, i16 } -%MyClass.testMethod = type { i16, i16 } @__MyClass__init = unnamed_addr constant %MyClass zeroinitializer @@ -18,12 +16,13 @@ entry: ret void } -define void @MyClass.testMethod(%MyClass* %0, %MyClass.testMethod* %1) { +define void @MyClass.testMethod(%MyClass* %0, i16 %1) { entry: %x = getelementptr inbounds %MyClass, %MyClass* %0, i32 0, i32 0 %y = getelementptr inbounds %MyClass, %MyClass* %0, i32 0, i32 1 - %myMethodArg = getelementptr inbounds %MyClass.testMethod, %MyClass.testMethod* %1, i32 0, i32 0 - %myMethodLocalVar = getelementptr inbounds %MyClass.testMethod, %MyClass.testMethod* %1, i32 0, i32 1 + %myMethodArg = alloca i16, align 2 + store i16 %1, i16* %myMethodArg, align 2 + %myMethodLocalVar = alloca i16, align 2 store i16 0, i16* %myMethodLocalVar, align 2 %load_myMethodArg = load i16, i16* %myMethodArg, align 2 store i16 %load_myMethodArg, i16* %x, align 2 diff --git a/src/codegen/tests/snapshots/rusty__codegen__tests__code_gen_tests__class_method_in_pou.snap b/src/codegen/tests/snapshots/rusty__codegen__tests__code_gen_tests__class_method_in_pou.snap index 6a3fafa3e8..f65f238e7a 100644 --- a/src/codegen/tests/snapshots/rusty__codegen__tests__code_gen_tests__class_method_in_pou.snap +++ b/src/codegen/tests/snapshots/rusty__codegen__tests__code_gen_tests__class_method_in_pou.snap @@ -1,14 +1,12 @@ --- source: src/codegen/tests/code_gen_tests.rs expression: result -snapshot_kind: text --- ; ModuleID = '' source_filename = "" %MyClass = type { i16, i16 } %prg = type { %MyClass, i16 } -%MyClass.testMethod = type { i16, i16 } @__MyClass__init = unnamed_addr constant %MyClass zeroinitializer @prg_instance = global %prg zeroinitializer @@ -20,12 +18,13 @@ entry: ret void } -define void @MyClass.testMethod(%MyClass* %0, %MyClass.testMethod* %1) { +define void @MyClass.testMethod(%MyClass* %0, i16 %1) { entry: %x = getelementptr inbounds %MyClass, %MyClass* %0, i32 0, i32 0 %y = getelementptr inbounds %MyClass, %MyClass* %0, i32 0, i32 1 - %myMethodArg = getelementptr inbounds %MyClass.testMethod, %MyClass.testMethod* %1, i32 0, i32 0 - %myMethodLocalVar = getelementptr inbounds %MyClass.testMethod, %MyClass.testMethod* %1, i32 0, i32 1 + %myMethodArg = alloca i16, align 2 + store i16 %1, i16* %myMethodArg, align 2 + %myMethodLocalVar = alloca i16, align 2 store i16 0, i16* %myMethodLocalVar, align 2 %load_myMethodArg = load i16, i16* %myMethodArg, align 2 store i16 %load_myMethodArg, i16* %x, align 2 @@ -46,16 +45,10 @@ entry: %x1 = getelementptr inbounds %MyClass, %MyClass* %cl, i32 0, i32 0 %load_x = load i16, i16* %x1, align 2 store i16 %load_x, i16* %x, align 2 - %MyClass.testMethod_instance = alloca %MyClass.testMethod, align 8 - %1 = getelementptr inbounds %MyClass.testMethod, %MyClass.testMethod* %MyClass.testMethod_instance, i32 0, i32 0 %load_x2 = load i16, i16* %x, align 2 - store i16 %load_x2, i16* %1, align 2 - call void @MyClass.testMethod(%MyClass* %cl, %MyClass.testMethod* %MyClass.testMethod_instance) - %MyClass.testMethod_instance3 = alloca %MyClass.testMethod, align 8 - %2 = getelementptr inbounds %MyClass.testMethod, %MyClass.testMethod* %MyClass.testMethod_instance3, i32 0, i32 0 - %load_x4 = load i16, i16* %x, align 2 - store i16 %load_x4, i16* %2, align 2 - call void @MyClass.testMethod(%MyClass* %cl, %MyClass.testMethod* %MyClass.testMethod_instance3) + call void @MyClass.testMethod(%MyClass* %cl, i16 %load_x2) + %load_x3 = load i16, i16* %x, align 2 + call void @MyClass.testMethod(%MyClass* %cl, i16 %load_x3) ret void } ; ModuleID = '__initializers' diff --git a/src/codegen/tests/snapshots/rusty__codegen__tests__code_gen_tests__fb_method_called_as_function.snap b/src/codegen/tests/snapshots/rusty__codegen__tests__code_gen_tests__fb_method_called_as_function.snap new file mode 100644 index 0000000000..9ef4849160 --- /dev/null +++ b/src/codegen/tests/snapshots/rusty__codegen__tests__code_gen_tests__fb_method_called_as_function.snap @@ -0,0 +1,74 @@ +--- +source: src/codegen/tests/code_gen_tests.rs +expression: prg +--- +; ModuleID = '' +source_filename = "" + +%MyClass = type { i16, i16 } + +@__MyClass__init = unnamed_addr constant %MyClass zeroinitializer + +define void @MyClass(%MyClass* %0) { +entry: + %x = getelementptr inbounds %MyClass, %MyClass* %0, i32 0, i32 0 + %y = getelementptr inbounds %MyClass, %MyClass* %0, i32 0, i32 1 + %call = call i16 @MyClass.testMethod(%MyClass* %0, i16 1) + %call1 = call i16 @MyClass.testMethod(%MyClass* %0, i16 3) + ret void +} + +define i16 @MyClass.testMethod(%MyClass* %0, i16 %1) { +entry: + %x = getelementptr inbounds %MyClass, %MyClass* %0, i32 0, i32 0 + %y = getelementptr inbounds %MyClass, %MyClass* %0, i32 0, i32 1 + %MyClass.testMethod = alloca i16, align 2 + %myMethodArg = alloca i16, align 2 + store i16 %1, i16* %myMethodArg, align 2 + %myMethodLocalVar = alloca i16, align 2 + store i16 0, i16* %myMethodLocalVar, align 2 + store i16 0, i16* %MyClass.testMethod, align 2 + %load_myMethodArg = load i16, i16* %myMethodArg, align 2 + store i16 %load_myMethodArg, i16* %x, align 2 + %load_x = load i16, i16* %x, align 2 + %2 = sext i16 %load_x to i32 + %tmpVar = add i32 %2, 1 + %3 = trunc i32 %tmpVar to i16 + store i16 %3, i16* %y, align 2 + %load_y = load i16, i16* %y, align 2 + %4 = sext i16 %load_y to i32 + %tmpVar1 = add i32 %4, 1 + %5 = trunc i32 %tmpVar1 to i16 + store i16 %5, i16* %myMethodLocalVar, align 2 + %load_myMethodLocalVar = load i16, i16* %myMethodLocalVar, align 2 + %6 = sext i16 %load_myMethodLocalVar to i32 + %tmpVar2 = add i32 %6, 1 + %7 = trunc i32 %tmpVar2 to i16 + store i16 %7, i16* %MyClass.testMethod, align 2 + %MyClass.testMethod_ret = load i16, i16* %MyClass.testMethod, align 2 + ret i16 %MyClass.testMethod_ret +} +; ModuleID = '__initializers' +source_filename = "__initializers" + +%MyClass = type { i16, i16 } + +@__MyClass__init = external global %MyClass + +define void @__init_myclass(%MyClass* %0) { +entry: + %self = alloca %MyClass*, align 8 + store %MyClass* %0, %MyClass** %self, align 8 + ret void +} + +declare void @MyClass(%MyClass*) +; ModuleID = '__init___testproject' +source_filename = "__init___testproject" + +@llvm.global_ctors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 0, void ()* @__init___testproject, i8* null }] + +define void @__init___testproject() { +entry: + ret void +} diff --git a/src/codegen/tests/snapshots/rusty__codegen__tests__code_gen_tests__fb_method_in_pou.snap b/src/codegen/tests/snapshots/rusty__codegen__tests__code_gen_tests__fb_method_in_pou.snap index 6a3fafa3e8..f65f238e7a 100644 --- a/src/codegen/tests/snapshots/rusty__codegen__tests__code_gen_tests__fb_method_in_pou.snap +++ b/src/codegen/tests/snapshots/rusty__codegen__tests__code_gen_tests__fb_method_in_pou.snap @@ -1,14 +1,12 @@ --- source: src/codegen/tests/code_gen_tests.rs expression: result -snapshot_kind: text --- ; ModuleID = '' source_filename = "" %MyClass = type { i16, i16 } %prg = type { %MyClass, i16 } -%MyClass.testMethod = type { i16, i16 } @__MyClass__init = unnamed_addr constant %MyClass zeroinitializer @prg_instance = global %prg zeroinitializer @@ -20,12 +18,13 @@ entry: ret void } -define void @MyClass.testMethod(%MyClass* %0, %MyClass.testMethod* %1) { +define void @MyClass.testMethod(%MyClass* %0, i16 %1) { entry: %x = getelementptr inbounds %MyClass, %MyClass* %0, i32 0, i32 0 %y = getelementptr inbounds %MyClass, %MyClass* %0, i32 0, i32 1 - %myMethodArg = getelementptr inbounds %MyClass.testMethod, %MyClass.testMethod* %1, i32 0, i32 0 - %myMethodLocalVar = getelementptr inbounds %MyClass.testMethod, %MyClass.testMethod* %1, i32 0, i32 1 + %myMethodArg = alloca i16, align 2 + store i16 %1, i16* %myMethodArg, align 2 + %myMethodLocalVar = alloca i16, align 2 store i16 0, i16* %myMethodLocalVar, align 2 %load_myMethodArg = load i16, i16* %myMethodArg, align 2 store i16 %load_myMethodArg, i16* %x, align 2 @@ -46,16 +45,10 @@ entry: %x1 = getelementptr inbounds %MyClass, %MyClass* %cl, i32 0, i32 0 %load_x = load i16, i16* %x1, align 2 store i16 %load_x, i16* %x, align 2 - %MyClass.testMethod_instance = alloca %MyClass.testMethod, align 8 - %1 = getelementptr inbounds %MyClass.testMethod, %MyClass.testMethod* %MyClass.testMethod_instance, i32 0, i32 0 %load_x2 = load i16, i16* %x, align 2 - store i16 %load_x2, i16* %1, align 2 - call void @MyClass.testMethod(%MyClass* %cl, %MyClass.testMethod* %MyClass.testMethod_instance) - %MyClass.testMethod_instance3 = alloca %MyClass.testMethod, align 8 - %2 = getelementptr inbounds %MyClass.testMethod, %MyClass.testMethod* %MyClass.testMethod_instance3, i32 0, i32 0 - %load_x4 = load i16, i16* %x, align 2 - store i16 %load_x4, i16* %2, align 2 - call void @MyClass.testMethod(%MyClass* %cl, %MyClass.testMethod* %MyClass.testMethod_instance3) + call void @MyClass.testMethod(%MyClass* %cl, i16 %load_x2) + %load_x3 = load i16, i16* %x, align 2 + call void @MyClass.testMethod(%MyClass* %cl, i16 %load_x3) ret void } ; ModuleID = '__initializers' diff --git a/src/codegen/tests/snapshots/rusty__codegen__tests__code_gen_tests__fb_method_with_var_in_out.snap b/src/codegen/tests/snapshots/rusty__codegen__tests__code_gen_tests__fb_method_with_var_in_out.snap new file mode 100644 index 0000000000..45b2c23fd0 --- /dev/null +++ b/src/codegen/tests/snapshots/rusty__codegen__tests__code_gen_tests__fb_method_with_var_in_out.snap @@ -0,0 +1,89 @@ +--- +source: src/codegen/tests/code_gen_tests.rs +expression: prg +--- +; ModuleID = '' +source_filename = "" + +%MyClass = type { i16, i16 } +%prg = type { %MyClass, i16 } + +@__MyClass__init = unnamed_addr constant %MyClass zeroinitializer +@prg_instance = global %prg zeroinitializer + +define void @MyClass(%MyClass* %0) { +entry: + %x = getelementptr inbounds %MyClass, %MyClass* %0, i32 0, i32 0 + %y = getelementptr inbounds %MyClass, %MyClass* %0, i32 0, i32 1 + ret void +} + +define void @MyClass.testMethod(%MyClass* %0, i16* %1) { +entry: + %x = getelementptr inbounds %MyClass, %MyClass* %0, i32 0, i32 0 + %y = getelementptr inbounds %MyClass, %MyClass* %0, i32 0, i32 1 + %myMethodArg = alloca i16*, align 8 + store i16* %1, i16** %myMethodArg, align 8 + %deref = load i16*, i16** %myMethodArg, align 8 + %load_x = load i16, i16* %x, align 2 + store i16 %load_x, i16* %deref, align 2 + ret void +} + +define void @prg(%prg* %0) { +entry: + %cl = getelementptr inbounds %prg, %prg* %0, i32 0, i32 0 + %x = getelementptr inbounds %prg, %prg* %0, i32 0, i32 1 + call void @MyClass.testMethod(%MyClass* %cl, i16* %x) + ret void +} +; ModuleID = '__initializers' +source_filename = "__initializers" + +%MyClass = type { i16, i16 } +%prg = type { %MyClass, i16 } + +@__MyClass__init = external global %MyClass +@prg_instance = external global %prg + +define void @__init_myclass(%MyClass* %0) { +entry: + %self = alloca %MyClass*, align 8 + store %MyClass* %0, %MyClass** %self, align 8 + ret void +} + +declare void @MyClass(%MyClass*) + +define void @__init_prg(%prg* %0) { +entry: + %self = alloca %prg*, align 8 + store %prg* %0, %prg** %self, align 8 + %deref = load %prg*, %prg** %self, align 8 + %cl = getelementptr inbounds %prg, %prg* %deref, i32 0, i32 0 + call void @__init_myclass(%MyClass* %cl) + ret void +} + +declare void @prg(%prg*) +; ModuleID = '__init___testproject' +source_filename = "__init___testproject" + +%prg = type { %MyClass, i16 } +%MyClass = type { i16, i16 } + +@prg_instance = external global %prg +@__MyClass__init = external global %MyClass +@llvm.global_ctors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 0, void ()* @__init___testproject, i8* null }] + +define void @__init___testproject() { +entry: + call void @__init_prg(%prg* @prg_instance) + ret void +} + +declare void @__init_prg(%prg*) + +declare void @prg(%prg*) + +declare void @MyClass(%MyClass*) diff --git a/src/codegen/tests/snapshots/rusty__codegen__tests__code_gen_tests__fb_method_with_var_input_defaults.snap b/src/codegen/tests/snapshots/rusty__codegen__tests__code_gen_tests__fb_method_with_var_input_defaults.snap new file mode 100644 index 0000000000..37143801a4 --- /dev/null +++ b/src/codegen/tests/snapshots/rusty__codegen__tests__code_gen_tests__fb_method_with_var_input_defaults.snap @@ -0,0 +1,87 @@ +--- +source: src/codegen/tests/code_gen_tests.rs +expression: prg +--- +; ModuleID = '' +source_filename = "" + +%MyClass = type { i16, i16 } +%prg = type { %MyClass } + +@__MyClass__init = unnamed_addr constant %MyClass zeroinitializer +@prg_instance = global %prg zeroinitializer + +define void @MyClass(%MyClass* %0) { +entry: + %x = getelementptr inbounds %MyClass, %MyClass* %0, i32 0, i32 0 + %y = getelementptr inbounds %MyClass, %MyClass* %0, i32 0, i32 1 + ret void +} + +define void @MyClass.testMethod(%MyClass* %0, i16 %1) { +entry: + %x = getelementptr inbounds %MyClass, %MyClass* %0, i32 0, i32 0 + %y = getelementptr inbounds %MyClass, %MyClass* %0, i32 0, i32 1 + %myMethodArg = alloca i16, align 2 + store i16 %1, i16* %myMethodArg, align 2 + %load_myMethodArg = load i16, i16* %myMethodArg, align 2 + store i16 %load_myMethodArg, i16* %x, align 2 + ret void +} + +define void @prg(%prg* %0) { +entry: + %cl = getelementptr inbounds %prg, %prg* %0, i32 0, i32 0 + call void @MyClass.testMethod(%MyClass* %cl, i16 3) + ret void +} +; ModuleID = '__initializers' +source_filename = "__initializers" + +%MyClass = type { i16, i16 } +%prg = type { %MyClass } + +@__MyClass__init = external global %MyClass +@prg_instance = external global %prg + +define void @__init_myclass(%MyClass* %0) { +entry: + %self = alloca %MyClass*, align 8 + store %MyClass* %0, %MyClass** %self, align 8 + ret void +} + +declare void @MyClass(%MyClass*) + +define void @__init_prg(%prg* %0) { +entry: + %self = alloca %prg*, align 8 + store %prg* %0, %prg** %self, align 8 + %deref = load %prg*, %prg** %self, align 8 + %cl = getelementptr inbounds %prg, %prg* %deref, i32 0, i32 0 + call void @__init_myclass(%MyClass* %cl) + ret void +} + +declare void @prg(%prg*) +; ModuleID = '__init___testproject' +source_filename = "__init___testproject" + +%prg = type { %MyClass } +%MyClass = type { i16, i16 } + +@prg_instance = external global %prg +@__MyClass__init = external global %MyClass +@llvm.global_ctors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 0, void ()* @__init___testproject, i8* null }] + +define void @__init___testproject() { +entry: + call void @__init_prg(%prg* @prg_instance) + ret void +} + +declare void @__init_prg(%prg*) + +declare void @prg(%prg*) + +declare void @MyClass(%MyClass*) diff --git a/src/codegen/tests/snapshots/rusty__codegen__tests__code_gen_tests__method_codegen_return.snap b/src/codegen/tests/snapshots/rusty__codegen__tests__code_gen_tests__method_codegen_return.snap index f097839012..7127c454a8 100644 --- a/src/codegen/tests/snapshots/rusty__codegen__tests__code_gen_tests__method_codegen_return.snap +++ b/src/codegen/tests/snapshots/rusty__codegen__tests__code_gen_tests__method_codegen_return.snap @@ -1,13 +1,11 @@ --- source: src/codegen/tests/code_gen_tests.rs expression: result -snapshot_kind: text --- ; ModuleID = '' source_filename = "" %MyClass = type {} -%MyClass.testMethod = type { i16 } @__MyClass__init = unnamed_addr constant %MyClass zeroinitializer @@ -16,13 +14,14 @@ entry: ret void } -define i16 @MyClass.testMethod(%MyClass* %0, %MyClass.testMethod* %1) { +define i16 @MyClass.testMethod(%MyClass* %0, i16 %1) { entry: - %myMethodArg = getelementptr inbounds %MyClass.testMethod, %MyClass.testMethod* %1, i32 0, i32 0 - %testMethod = alloca i16, align 2 - store i16 0, i16* %testMethod, align 2 - store i16 1, i16* %testMethod, align 2 - %MyClass.testMethod_ret = load i16, i16* %testMethod, align 2 + %MyClass.testMethod = alloca i16, align 2 + %myMethodArg = alloca i16, align 2 + store i16 %1, i16* %myMethodArg, align 2 + store i16 0, i16* %MyClass.testMethod, align 2 + store i16 1, i16* %MyClass.testMethod, align 2 + %MyClass.testMethod_ret = load i16, i16* %MyClass.testMethod, align 2 ret i16 %MyClass.testMethod_ret } ; ModuleID = '__initializers' diff --git a/src/codegen/tests/snapshots/rusty__codegen__tests__code_gen_tests__method_codegen_void.snap b/src/codegen/tests/snapshots/rusty__codegen__tests__code_gen_tests__method_codegen_void.snap index 9e7e8ca927..240191b241 100644 --- a/src/codegen/tests/snapshots/rusty__codegen__tests__code_gen_tests__method_codegen_void.snap +++ b/src/codegen/tests/snapshots/rusty__codegen__tests__code_gen_tests__method_codegen_void.snap @@ -1,13 +1,11 @@ --- source: src/codegen/tests/code_gen_tests.rs expression: result -snapshot_kind: text --- ; ModuleID = '' source_filename = "" %MyClass = type {} -%MyClass.testMethod = type { i16, i16 } @__MyClass__init = unnamed_addr constant %MyClass zeroinitializer @@ -16,10 +14,11 @@ entry: ret void } -define void @MyClass.testMethod(%MyClass* %0, %MyClass.testMethod* %1) { +define void @MyClass.testMethod(%MyClass* %0, i16 %1) { entry: - %myMethodArg = getelementptr inbounds %MyClass.testMethod, %MyClass.testMethod* %1, i32 0, i32 0 - %myMethodLocalVar = getelementptr inbounds %MyClass.testMethod, %MyClass.testMethod* %1, i32 0, i32 1 + %myMethodArg = alloca i16, align 2 + store i16 %1, i16* %myMethodArg, align 2 + %myMethodLocalVar = alloca i16, align 2 store i16 0, i16* %myMethodLocalVar, align 2 store i16 1, i16* %myMethodLocalVar, align 2 ret void diff --git a/src/codegen/tests/snapshots/rusty__codegen__tests__code_gen_tests__method_codegen_with_initialized_input.snap b/src/codegen/tests/snapshots/rusty__codegen__tests__code_gen_tests__method_codegen_with_initialized_input.snap new file mode 100644 index 0000000000..9f66db8be5 --- /dev/null +++ b/src/codegen/tests/snapshots/rusty__codegen__tests__code_gen_tests__method_codegen_with_initialized_input.snap @@ -0,0 +1,59 @@ +--- +source: src/codegen/tests/code_gen_tests.rs +expression: prg +--- +; ModuleID = '' +source_filename = "" + +%fb = type {} + +@__fb__init = unnamed_addr constant %fb zeroinitializer + +define void @fb(%fb* %0) { +entry: + %call = call i32 @fb.meth(%fb* %0, i32 5) + %call1 = call i32 @fb.meth(%fb* %0, i32 4) + ret void +} + +define i32 @fb.meth(%fb* %0, i32 %1) { +entry: + %fb.meth = alloca i32, align 4 + %a = alloca i32, align 4 + store i32 %1, i32* %a, align 4 + store i32 0, i32* %fb.meth, align 4 + %fb.meth_ret = load i32, i32* %fb.meth, align 4 + ret i32 %fb.meth_ret +} + +define i32 @foo() { +entry: + %foo = alloca i32, align 4 + store i32 0, i32* %foo, align 4 + %foo_ret = load i32, i32* %foo, align 4 + ret i32 %foo_ret +} +; ModuleID = '__initializers' +source_filename = "__initializers" + +%fb = type {} + +@__fb__init = external global %fb + +define void @__init_fb(%fb* %0) { +entry: + %self = alloca %fb*, align 8 + store %fb* %0, %fb** %self, align 8 + ret void +} + +declare void @fb(%fb*) +; ModuleID = '__init___testproject' +source_filename = "__init___testproject" + +@llvm.global_ctors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 0, void ()* @__init___testproject, i8* null }] + +define void @__init___testproject() { +entry: + ret void +} diff --git a/src/codegen/tests/snapshots/rusty__codegen__tests__code_gen_tests__method_codegen_with_multiple_input.snap b/src/codegen/tests/snapshots/rusty__codegen__tests__code_gen_tests__method_codegen_with_multiple_input.snap new file mode 100644 index 0000000000..d688e238ce --- /dev/null +++ b/src/codegen/tests/snapshots/rusty__codegen__tests__code_gen_tests__method_codegen_with_multiple_input.snap @@ -0,0 +1,57 @@ +--- +source: src/codegen/tests/code_gen_tests.rs +expression: prg +--- +; ModuleID = '' +source_filename = "" + +%fb = type {} + +@__fb__init = unnamed_addr constant %fb zeroinitializer + +define void @fb(%fb* %0) { +entry: + %call = call i32 @fb.meth(%fb* %0, i32 1, i32 2, i32 3) + %call1 = call i32 @fb.meth(%fb* %0, i32 5, i32 7, i32 10) + %call2 = call i32 @fb.meth(%fb* %0, i32 3, i32 4, i32 10) + %call3 = call i32 @fb.meth(%fb* %0, i32 6, i32 4, i32 10) + ret void +} + +define i32 @fb.meth(%fb* %0, i32 %1, i32 %2, i32 %3) { +entry: + %fb.meth = alloca i32, align 4 + %a = alloca i32, align 4 + store i32 %1, i32* %a, align 4 + %b = alloca i32, align 4 + store i32 %2, i32* %b, align 4 + %c = alloca i32, align 4 + store i32 %3, i32* %c, align 4 + store i32 0, i32* %fb.meth, align 4 + %fb.meth_ret = load i32, i32* %fb.meth, align 4 + ret i32 %fb.meth_ret +} +; ModuleID = '__initializers' +source_filename = "__initializers" + +%fb = type {} + +@__fb__init = external global %fb + +define void @__init_fb(%fb* %0) { +entry: + %self = alloca %fb*, align 8 + store %fb* %0, %fb** %self, align 8 + ret void +} + +declare void @fb(%fb*) +; ModuleID = '__init___testproject' +source_filename = "__init___testproject" + +@llvm.global_ctors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 0, void ()* @__init___testproject, i8* null }] + +define void @__init___testproject() { +entry: + ret void +} diff --git a/src/codegen/tests/snapshots/rusty__codegen__tests__code_gen_tests__method_with_aggregate_return_type.snap b/src/codegen/tests/snapshots/rusty__codegen__tests__code_gen_tests__method_with_aggregate_return_type.snap new file mode 100644 index 0000000000..5c1fc0fdf8 --- /dev/null +++ b/src/codegen/tests/snapshots/rusty__codegen__tests__code_gen_tests__method_with_aggregate_return_type.snap @@ -0,0 +1,78 @@ +--- +source: src/codegen/tests/code_gen_tests.rs +expression: res +--- +; ModuleID = '' +source_filename = "" + +%fb_with_method = type {} + +@__fb_with_method__init = unnamed_addr constant %fb_with_method zeroinitializer +@utf08_literal_0 = private unnamed_addr constant [6 x i8] c"Hello\00" + +define void @fb_with_method(%fb_with_method* %0) { +entry: + %ret = alloca [81 x i8], align 1 + %1 = bitcast [81 x i8]* %ret to i8* + call void @llvm.memset.p0i8.i64(i8* align 1 %1, i8 0, i64 ptrtoint ([81 x i8]* getelementptr ([81 x i8], [81 x i8]* null, i32 1) to i64), i1 false) + %__method_with_aggregagte_return0 = alloca [81 x i8], align 1 + %2 = bitcast [81 x i8]* %__method_with_aggregagte_return0 to i8* + call void @llvm.memset.p0i8.i64(i8* align 1 %2, i8 0, i64 ptrtoint ([81 x i8]* getelementptr ([81 x i8], [81 x i8]* null, i32 1) to i64), i1 false) + %3 = bitcast [81 x i8]* %__method_with_aggregagte_return0 to i8* + call void @fb_with_method.method_with_aggregagte_return(%fb_with_method* %0, i8* %3, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @utf08_literal_0, i32 0, i32 0)) + %4 = bitcast [81 x i8]* %ret to i8* + %5 = bitcast [81 x i8]* %__method_with_aggregagte_return0 to i8* + call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 1 %4, i8* align 1 %5, i32 80, i1 false) + ret void +} + +define void @fb_with_method.method_with_aggregagte_return(%fb_with_method* %0, i8* %1, i8* %2) { +entry: + %ret = alloca [81 x i8], align 1 + %method_with_aggregagte_return = alloca i8*, align 8 + store i8* %1, i8** %method_with_aggregagte_return, align 8 + %in = alloca [81 x i8], align 1 + %bitcast = bitcast [81 x i8]* %in to i8* + call void @llvm.memset.p0i8.i64(i8* align 1 %bitcast, i8 0, i64 81, i1 false) + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 1 %bitcast, i8* align 1 %2, i64 80, i1 false) + %deref = load i8*, i8** %method_with_aggregagte_return, align 8 + %3 = bitcast [81 x i8]* %in to i8* + call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 1 %deref, i8* align 1 %3, i32 80, i1 false) + ret void +} + +; Function Attrs: argmemonly nofree nounwind willreturn writeonly +declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i1 immarg) #0 + +; Function Attrs: argmemonly nofree nounwind willreturn +declare void @llvm.memcpy.p0i8.p0i8.i64(i8* noalias nocapture writeonly, i8* noalias nocapture readonly, i64, i1 immarg) #1 + +; Function Attrs: argmemonly nofree nounwind willreturn +declare void @llvm.memcpy.p0i8.p0i8.i32(i8* noalias nocapture writeonly, i8* noalias nocapture readonly, i32, i1 immarg) #1 + +attributes #0 = { argmemonly nofree nounwind willreturn writeonly } +attributes #1 = { argmemonly nofree nounwind willreturn } +; ModuleID = '__initializers' +source_filename = "__initializers" + +%fb_with_method = type {} + +@__fb_with_method__init = external global %fb_with_method + +define void @__init_fb_with_method(%fb_with_method* %0) { +entry: + %self = alloca %fb_with_method*, align 8 + store %fb_with_method* %0, %fb_with_method** %self, align 8 + ret void +} + +declare void @fb_with_method(%fb_with_method*) +; ModuleID = '__init___testproject' +source_filename = "__init___testproject" + +@llvm.global_ctors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 0, void ()* @__init___testproject, i8* null }] + +define void @__init___testproject() { +entry: + ret void +} diff --git a/src/codegen/tests/snapshots/rusty__codegen__tests__code_gen_tests__methods_var_output.snap b/src/codegen/tests/snapshots/rusty__codegen__tests__code_gen_tests__methods_var_output.snap new file mode 100644 index 0000000000..5cf1b961bc --- /dev/null +++ b/src/codegen/tests/snapshots/rusty__codegen__tests__code_gen_tests__methods_var_output.snap @@ -0,0 +1,80 @@ +--- +source: src/codegen/tests/code_gen_tests.rs +expression: res +--- +; ModuleID = '' +source_filename = "" + +%foo = type {} + +@__foo__init = unnamed_addr constant %foo zeroinitializer +@utf08_literal_0 = private unnamed_addr constant [6 x i8] c"hello\00" + +define void @foo(%foo* %0) { +entry: + ret void +} + +define void @foo.baz(%foo* %0, [81 x i8]* %1) { +entry: + %out = alloca [81 x i8]*, align 8 + store [81 x i8]* %1, [81 x i8]** %out, align 8 + %deref = load [81 x i8]*, [81 x i8]** %out, align 8 + %2 = bitcast [81 x i8]* %deref to i8* + call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 1 %2, i8* align 1 getelementptr inbounds ([6 x i8], [6 x i8]* @utf08_literal_0, i32 0, i32 0), i32 6, i1 false) + ret void +} + +define void @main() { +entry: + %s = alloca [81 x i8], align 1 + %fb = alloca %foo, align 8 + %0 = bitcast [81 x i8]* %s to i8* + call void @llvm.memset.p0i8.i64(i8* align 1 %0, i8 0, i64 ptrtoint ([81 x i8]* getelementptr ([81 x i8], [81 x i8]* null, i32 1) to i64), i1 false) + %1 = bitcast %foo* %fb to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 1 %1, i8* align 1 bitcast (%foo* @__foo__init to i8*), i64 ptrtoint (%foo* getelementptr (%foo, %foo* null, i32 1) to i64), i1 false) + call void @__init_foo(%foo* %fb) + %2 = bitcast [81 x i8]* %s to i8* + call void @foo.baz(%foo* %fb, i8* %2) + %3 = bitcast [81 x i8]* %s to i8* + call void @foo.baz(%foo* %fb, i8* %3) + ret void +} + +declare void @__init_foo(%foo*) + +; Function Attrs: argmemonly nofree nounwind willreturn +declare void @llvm.memcpy.p0i8.p0i8.i32(i8* noalias nocapture writeonly, i8* noalias nocapture readonly, i32, i1 immarg) #0 + +; Function Attrs: argmemonly nofree nounwind willreturn writeonly +declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i1 immarg) #1 + +; Function Attrs: argmemonly nofree nounwind willreturn +declare void @llvm.memcpy.p0i8.p0i8.i64(i8* noalias nocapture writeonly, i8* noalias nocapture readonly, i64, i1 immarg) #0 + +attributes #0 = { argmemonly nofree nounwind willreturn } +attributes #1 = { argmemonly nofree nounwind willreturn writeonly } +; ModuleID = '__initializers' +source_filename = "__initializers" + +%foo = type {} + +@__foo__init = external global %foo + +define void @__init_foo(%foo* %0) { +entry: + %self = alloca %foo*, align 8 + store %foo* %0, %foo** %self, align 8 + ret void +} + +declare void @foo(%foo*) +; ModuleID = '__init___testproject' +source_filename = "__init___testproject" + +@llvm.global_ctors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 0, void ()* @__init___testproject, i8* null }] + +define void @__init___testproject() { +entry: + ret void +} diff --git a/src/index.rs b/src/index.rs index 0e0c0e543a..8660388bc6 100644 --- a/src/index.rs +++ b/src/index.rs @@ -441,6 +441,10 @@ impl ImplementationIndexEntry { pub(crate) fn is_init(&self) -> bool { matches!(self.get_implementation_type(), ImplementationType::Init | ImplementationType::ProjectInit) } + + pub fn is_method(&self) -> bool { + matches!(self.get_implementation_type(), ImplementationType::Method) + } } impl From<&PouType> for ImplementationType { @@ -459,10 +463,14 @@ impl From<&PouType> for ImplementationType { } impl ImplementationType { + // TODO: this now also takes methods into accounts, find a better name pub fn is_function_or_init(&self) -> bool { matches!( self, - ImplementationType::Function | ImplementationType::Init | ImplementationType::ProjectInit, + ImplementationType::Function + | ImplementationType::Init + | ImplementationType::ProjectInit + | ImplementationType::Method, ) } @@ -754,7 +762,7 @@ impl PouIndexEntry { match self { PouIndexEntry::Program { instance_struct_name, .. } | PouIndexEntry::FunctionBlock { instance_struct_name, .. } - | PouIndexEntry::Method { instance_struct_name, .. } + // | PouIndexEntry::Method { instance_struct_name, .. } | PouIndexEntry::Action { instance_struct_name, .. } | PouIndexEntry::Class { instance_struct_name, .. } => Some(instance_struct_name.as_str()), _ => None, //functions have no struct type diff --git a/src/index/indexer/pou_indexer.rs b/src/index/indexer/pou_indexer.rs index d95dbe37bd..463a1ac40b 100644 --- a/src/index/indexer/pou_indexer.rs +++ b/src/index/indexer/pou_indexer.rs @@ -250,7 +250,9 @@ fn get_declaration_type_for(block: &VariableBlock, pou_type: &PouType) -> Argume } else if block.variable_block_type == VariableBlockType::Output { // outputs differ depending on pou type match pou_type { - PouType::Function => ArgumentType::ByRef(get_variable_type_from_block(block)), + PouType::Function | PouType::Method { .. } => { + ArgumentType::ByRef(get_variable_type_from_block(block)) + } _ => ArgumentType::ByVal(get_variable_type_from_block(block)), } } else { diff --git a/src/lowering/calls.rs b/src/lowering/calls.rs index 7d06eff9f3..2f7b6224f5 100644 --- a/src/lowering/calls.rs +++ b/src/lowering/calls.rs @@ -261,7 +261,7 @@ 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 + //If the function has an formal arguments (foo(x := 1)), we need to add an assignment to the reference let reference = if stmt .parameters .as_ref() diff --git a/src/tests/adr/initializer_functions_adr.rs b/src/tests/adr/initializer_functions_adr.rs index f8f344d747..d2275ab9ad 100644 --- a/src/tests/adr/initializer_functions_adr.rs +++ b/src/tests/adr/initializer_functions_adr.rs @@ -807,7 +807,6 @@ fn initializing_method_variables() { source_filename = "" %foo = type {} - %foo.bar = type { i32, i32* } @__foo__init = unnamed_addr constant %foo zeroinitializer @@ -816,10 +815,10 @@ fn initializing_method_variables() { ret void } - define void @foo.bar(%foo* %0, %foo.bar* %1) { + define void @foo.bar(%foo* %0) { entry: - %x = getelementptr inbounds %foo.bar, %foo.bar* %1, i32 0, i32 0 - %px = getelementptr inbounds %foo.bar, %foo.bar* %1, i32 0, i32 1 + %x = alloca i32, align 4 + %px = alloca i32*, align 8 store i32 10, i32* %x, align 4 store i32* %x, i32** %px, align 8 store i32* %x, i32** %px, align 8 @@ -882,8 +881,6 @@ fn initializing_method_variables() { source_filename = "" %foo = type { i32 } - %foo.bar = type { i32* } - %foo.baz = type { i32* } @y = global i32 0 @__foo__init = unnamed_addr constant %foo { i32 5 } @@ -894,19 +891,19 @@ fn initializing_method_variables() { ret void } - define void @foo.bar(%foo* %0, %foo.bar* %1) { + define void @foo.bar(%foo* %0) { entry: %x = getelementptr inbounds %foo, %foo* %0, i32 0, i32 0 - %px = getelementptr inbounds %foo.bar, %foo.bar* %1, i32 0, i32 0 + %px = alloca i32*, align 8 store i32* %x, i32** %px, align 8 store i32* %x, i32** %px, align 8 ret void } - define void @foo.baz(%foo* %0, %foo.baz* %1) { + define void @foo.baz(%foo* %0) { entry: %x = getelementptr inbounds %foo, %foo* %0, i32 0, i32 0 - %px = getelementptr inbounds %foo.baz, %foo.baz* %1, i32 0, i32 0 + %px = alloca i32*, align 8 store i32* @y, i32** %px, align 8 store i32* @y, i32** %px, align 8 ret void @@ -958,7 +955,6 @@ fn initializing_method_variables() { source_filename = "" %foo = type { i32 } - %foo.bar = type { i32, i32* } @__foo__init = unnamed_addr constant %foo { i32 5 } @@ -968,11 +964,11 @@ fn initializing_method_variables() { ret void } - define void @foo.bar(%foo* %0, %foo.bar* %1) { + define void @foo.bar(%foo* %0) { entry: %x = getelementptr inbounds %foo, %foo* %0, i32 0, i32 0 - %x1 = getelementptr inbounds %foo.bar, %foo.bar* %1, i32 0, i32 0 - %px = getelementptr inbounds %foo.bar, %foo.bar* %1, i32 0, i32 1 + %x1 = alloca i32, align 4 + %px = alloca i32*, align 8 store i32 10, i32* %x1, align 4 store i32* %x1, i32** %px, align 8 store i32* %x1, i32** %px, align 8 diff --git a/src/validation/statement.rs b/src/validation/statement.rs index a1508d3435..666f5cd717 100644 --- a/src/validation/statement.rs +++ b/src/validation/statement.rs @@ -1346,19 +1346,11 @@ fn validate_call( return; } - let declared_in_out_params: Vec<&VariableIndexEntry> = match pou { - PouIndexEntry::Method { .. } => { - // Methods require both INPUT and IN_OUT arguments to be passed - parameters.into_iter().filter(|p| p.is_input() || p.is_inout()).collect() - } - - // ...other stateful POUs only require IN_OUT arguments (for now?) and fall-back to default - // values for INPUT variables if not present - _ => parameters.into_iter().filter(|param| param.is_inout()).collect(), - }; + let declared_in_out_params: Vec<&VariableIndexEntry> = + parameters.into_iter().filter(|param| param.is_inout()).collect(); if !declared_in_out_params.is_empty() { - // Check if all (INPUT and) IN_OUT arguments were passed by cross-checking with the parameters + // Check if all IN_OUT arguments were passed by cross-checking with the parameters declared_in_out_params.into_iter().for_each(|p| { if !variable_location_in_parent.contains(&p.get_location_in_parent()) { validator.push_diagnostic( diff --git a/src/validation/tests/interface_validation_tests.rs b/src/validation/tests/interface_validation_tests.rs index bb8ace4ba9..615bca1ccc 100644 --- a/src/validation/tests/interface_validation_tests.rs +++ b/src/validation/tests/interface_validation_tests.rs @@ -510,7 +510,7 @@ fn interfaces_with_same_method_name_but_different_signatures_parameter_list_decl "; let diagnostics = parse_and_validate_buffered(source); - insta::assert_snapshot!(diagnostics, @r###" + insta::assert_snapshot!(diagnostics, @r" error[E111]: Method `foo` is defined with different signatures in interfaces `interfaceA` and `interfaceB` ┌─ :19:20 │ @@ -523,6 +523,15 @@ fn interfaces_with_same_method_name_but_different_signatures_parameter_list_decl 19 │ FUNCTION_BLOCK fb IMPLEMENTS interfaceA, interfaceB │ ^^ Method `foo` is defined with different signatures in interfaces `interfaceA` and `interfaceB` + error[E112]: Interface implementation mismatch: Expected parameter `a` to have `INT` as its type but got `__auto_pointer_to_INT` + ┌─ :12:16 + │ + 5 │ a : INT; + │ - see also + · + 12 │ METHOD foo : INT + │ ^^^ Interface implementation mismatch: Expected parameter `a` to have `INT` as its type but got `__auto_pointer_to_INT` + error[E112]: Interface implementation mismatch: Expected parameter `a` to have `Input` as its declaration type but got `Output` ┌─ :12:16 │ @@ -540,6 +549,5 @@ fn interfaces_with_same_method_name_but_different_signatures_parameter_list_decl · 12 │ METHOD foo : INT │ ^^^ Parameter `b : INT` missing in method `interfaceB.foo` - - "###); + "); } diff --git a/src/validation/tests/pou_validation_tests.rs b/src/validation/tests/pou_validation_tests.rs index 4fec323da9..69e5f224fe 100644 --- a/src/validation/tests/pou_validation_tests.rs +++ b/src/validation/tests/pou_validation_tests.rs @@ -265,32 +265,7 @@ fn method_input_arguments_are_not_optional() { ", ); - assert_snapshot!(diagnostic, @r###" - error[E030]: Argument `in1` is missing - ┌─ :17:13 - │ - 17 │ fbInstance.foo(); - │ ^^^^^^^^^^^^^^ Argument `in1` is missing - - error[E030]: Argument `in2` is missing - ┌─ :17:13 - │ - 17 │ fbInstance.foo(); - │ ^^^^^^^^^^^^^^ Argument `in2` is missing - - error[E030]: Argument `in2` is missing - ┌─ :18:13 - │ - 18 │ fbInstance.foo(in1 := TRUE); - │ ^^^^^^^^^^^^^^ Argument `in2` is missing - - error[E030]: Argument `in1` is missing - ┌─ :19:13 - │ - 19 │ fbInstance.foo(in2 := TRUE); - │ ^^^^^^^^^^^^^^ Argument `in1` is missing - - "###); + assert_snapshot!(diagnostic, @""); } #[test] diff --git a/src/validation/tests/snapshots/rusty__validation__tests__reference_resolve_tests__reference_to_private_variable_in_intermediate_fb.snap b/src/validation/tests/snapshots/rusty__validation__tests__reference_resolve_tests__reference_to_private_variable_in_intermediate_fb.snap index de57ecefd5..921e79e03b 100644 --- a/src/validation/tests/snapshots/rusty__validation__tests__reference_resolve_tests__reference_to_private_variable_in_intermediate_fb.snap +++ b/src/validation/tests/snapshots/rusty__validation__tests__reference_resolve_tests__reference_to_private_variable_in_intermediate_fb.snap @@ -2,10 +2,8 @@ source: src/validation/tests/reference_resolve_tests.rs expression: "&diagnostics" --- -error[E049]: Illegal access to private member fb1.f +warning[E049]: Illegal access to private member fb1.f ┌─ :19:19 │ 19 │ a.f.x := 7; │ ^ Illegal access to private member fb1.f - - diff --git a/src/validation/tests/snapshots/rusty__validation__tests__reference_resolve_tests__reference_to_private_variable_is_illegal.snap b/src/validation/tests/snapshots/rusty__validation__tests__reference_resolve_tests__reference_to_private_variable_is_illegal.snap index c6b445611d..1ced3566f2 100644 --- a/src/validation/tests/snapshots/rusty__validation__tests__reference_resolve_tests__reference_to_private_variable_is_illegal.snap +++ b/src/validation/tests/snapshots/rusty__validation__tests__reference_resolve_tests__reference_to_private_variable_is_illegal.snap @@ -2,10 +2,8 @@ source: src/validation/tests/reference_resolve_tests.rs expression: "&diagnostics" --- -error[E049]: Illegal access to private member prg.s +warning[E049]: Illegal access to private member prg.s ┌─ :9:21 │ 9 │ prg.s := 7; │ ^ Illegal access to private member prg.s - - diff --git a/tests/lit/single/methods/aggregate_return_complex.st b/tests/lit/single/methods/aggregate_return_complex.st index d57f269911..e736c1df2a 100644 --- a/tests/lit/single/methods/aggregate_return_complex.st +++ b/tests/lit/single/methods/aggregate_return_complex.st @@ -1,4 +1,6 @@ // RUN: (%COMPILE %s && %RUN) | %CHECK %s +// XFAIL: * +// failing because of #1389 FUNCTION_BLOCK fb_with_method VAR_TEMP ret : ARRAY[0..1] OF STRING; diff --git a/tests/lit/single/methods/method_initial_values.st b/tests/lit/single/methods/method_initial_values.st new file mode 100644 index 0000000000..470c601ce8 --- /dev/null +++ b/tests/lit/single/methods/method_initial_values.st @@ -0,0 +1,21 @@ +// RUN: (%COMPILE %s && %RUN) | %CHECK %s +FUNCTION_BLOCK MyClass + VAR + x, y : INT; + END_VAR + + METHOD testMethod + VAR_INPUT + myMethodArg : INT := 3; + END_VAR + x := myMethodArg; + END_METHOD +END_FUNCTION_BLOCK + +FUNCTION main +VAR +cl : MyClass; +END_VAR + cl.testMethod(); + printf('%d$N', cl.x); // CHECK: 3 +END_FUNCTION diff --git a/tests/lit/single/methods/method_var_output.st b/tests/lit/single/methods/method_var_output.st new file mode 100644 index 0000000000..57d87b3acb --- /dev/null +++ b/tests/lit/single/methods/method_var_output.st @@ -0,0 +1,23 @@ +// RUN: (%COMPILE %s && %RUN) | %CHECK %s +FUNCTION_BLOCK foo +METHOD baz +VAR_OUTPUT + out : STRING; +END_VAR + out := 'hello'; +END_METHOD +END_FUNCTION_BLOCK + + +FUNCTION main +VAR + s: STRING; + fb: foo; +END_VAR + fb.baz(out => s); + printf('%s$n', REF(s)); // CHECK: hello + s := 'reset'; + printf('%s$n', REF(s)); // CHECK: reset + fb.baz(s); + printf('%s$n', REF(s)); // CHECK: hello +END_FUNCTION \ No newline at end of file