From a8b4e05a159eb3dea87d4c5c355a09d988067b10 Mon Sep 17 00:00:00 2001 From: abroooo <61380142+abroooo@users.noreply.github.com> Date: Tue, 1 Apr 2025 23:59:54 +0200 Subject: [PATCH 01/48] parse this keyword --- compiler/plc_ast/src/ast.rs | 9 ++ compiler/plc_ast/src/mut_visitor.rs | 1 + compiler/plc_ast/src/visitor.rs | 1 + src/lexer/tokens.rs | 3 + src/parser/expressions_parser.rs | 4 + src/parser/tests/expressions_parser_tests.rs | 120 ++++++++++++++++++ .../parse_error_containers_tests.rs | 94 +++++++++++++- 7 files changed, 230 insertions(+), 2 deletions(-) diff --git a/compiler/plc_ast/src/ast.rs b/compiler/plc_ast/src/ast.rs index b32c74f851..c8bb54140c 100644 --- a/compiler/plc_ast/src/ast.rs +++ b/compiler/plc_ast/src/ast.rs @@ -861,6 +861,7 @@ pub enum AstStatement { ReferenceExpr(ReferenceExpr), Identifier(String), Super(Option), + This, DirectAccess(DirectAccess), HardwareAccess(HardwareAccess), BinaryExpression(BinaryExpression), @@ -931,6 +932,7 @@ impl Debug for AstNode { AstStatement::Identifier(name) => f.debug_struct("Identifier").field("name", name).finish(), AstStatement::Super(Some(_)) => f.debug_struct("Super(derefed)").finish(), AstStatement::Super(_) => f.debug_struct("Super").finish(), + AstStatement::This => f.debug_struct("This").finish(), AstStatement::BinaryExpression(BinaryExpression { operator, left, right }) => f .debug_struct("BinaryExpression") .field("operator", operator) @@ -1640,6 +1642,13 @@ impl AstFactory { AstNode::new(AstStatement::Super(deref), id, location.into()) } + pub fn create_this_reference(location: T, id: AstId) -> AstNode + where + T: Into, + { + AstNode::new(AstStatement::This, id, location.into()) + } + pub fn create_unary_expression( operator: Operator, value: AstNode, diff --git a/compiler/plc_ast/src/mut_visitor.rs b/compiler/plc_ast/src/mut_visitor.rs index 7bb0fe9cf6..13755f0ec1 100644 --- a/compiler/plc_ast/src/mut_visitor.rs +++ b/compiler/plc_ast/src/mut_visitor.rs @@ -406,6 +406,7 @@ impl WalkerMut for AstNode { AstStatement::LabelStatement(_) => visitor.visit_label_statement(self), AstStatement::AllocationStatement(_) => visitor.visit_allocation(self), AstStatement::Super(_) => visitor.visit_super(self), + AstStatement::This => {} } } } diff --git a/compiler/plc_ast/src/visitor.rs b/compiler/plc_ast/src/visitor.rs index cb34d175a3..4f9c0c72ce 100644 --- a/compiler/plc_ast/src/visitor.rs +++ b/compiler/plc_ast/src/visitor.rs @@ -611,6 +611,7 @@ impl Walker for AstNode { AstStatement::LabelStatement(stmt) => visitor.visit_label_statement(stmt, node), AstStatement::AllocationStatement(stmt) => visitor.visit_allocation(stmt, node), AstStatement::Super(_) => {} + AstStatement::This => {} } } } diff --git a/src/lexer/tokens.rs b/src/lexer/tokens.rs index efe408e168..403bc1a858 100644 --- a/src/lexer/tokens.rs +++ b/src/lexer/tokens.rs @@ -79,6 +79,9 @@ pub enum Token { #[token("SUPER", ignore(case))] KeywordSuper, + #[token("THIS", ignore(case))] + KeywordThis, + #[token("PROPERTY", ignore(case))] KeywordProperty, diff --git a/src/parser/expressions_parser.rs b/src/parser/expressions_parser.rs index 9be78060b0..cdaac76756 100644 --- a/src/parser/expressions_parser.rs +++ b/src/parser/expressions_parser.rs @@ -287,6 +287,10 @@ fn parse_atomic_leaf_expression(lexer: &mut ParseSession<'_>) -> Option lexer.next_id(), )) } + KeywordThis => { + lexer.advance(); + Some(AstFactory::create_this_reference(lexer.last_location(), lexer.next_id())) + } HardwareAccess((hw_type, access_type)) => parse_hardware_access(lexer, hw_type, access_type), LiteralInteger => parse_literal_number(lexer, false), LiteralIntegerBin => parse_literal_number_with_modifier(lexer, 2, false), diff --git a/src/parser/tests/expressions_parser_tests.rs b/src/parser/tests/expressions_parser_tests.rs index 1c01f92e73..8ed0536f01 100644 --- a/src/parser/tests/expressions_parser_tests.rs +++ b/src/parser/tests/expressions_parser_tests.rs @@ -1885,3 +1885,123 @@ fn super_keyword_can_be_parsed_in_expressions() { ] "###); } + +#[test] +fn this_keyword_can_be_parsed_in_expressions() { + let src = " + FUNCTION_BLOCK fb + this.x; + this^.y; + this; + this^.foo(this.x + this^.y); + this(); + this := REF(fb2); + END_FUNCTION_BLOCK + "; + + let parse_result = parse(src).0; + assert_debug_snapshot!(parse_result.implementations[0].statements, @r#" + [ + ReferenceExpr { + kind: Member( + Identifier { + name: "x", + }, + ), + base: Some( + This, + ), + }, + ReferenceExpr { + kind: Member( + Identifier { + name: "y", + }, + ), + base: Some( + ReferenceExpr { + kind: Deref, + base: Some( + This, + ), + }, + ), + }, + This, + CallStatement { + operator: ReferenceExpr { + kind: Member( + Identifier { + name: "foo", + }, + ), + base: Some( + ReferenceExpr { + kind: Deref, + base: Some( + This, + ), + }, + ), + }, + parameters: Some( + BinaryExpression { + operator: Plus, + left: ReferenceExpr { + kind: Member( + Identifier { + name: "x", + }, + ), + base: Some( + This, + ), + }, + right: ReferenceExpr { + kind: Member( + Identifier { + name: "y", + }, + ), + base: Some( + ReferenceExpr { + kind: Deref, + base: Some( + This, + ), + }, + ), + }, + }, + ), + }, + CallStatement { + operator: This, + parameters: None, + }, + Assignment { + left: This, + right: CallStatement { + operator: ReferenceExpr { + kind: Member( + Identifier { + name: "REF", + }, + ), + base: None, + }, + parameters: Some( + ReferenceExpr { + kind: Member( + Identifier { + name: "fb2", + }, + ), + base: None, + }, + ), + }, + }, + ] + "#); +} diff --git a/src/parser/tests/parse_errors/parse_error_containers_tests.rs b/src/parser/tests/parse_errors/parse_error_containers_tests.rs index e04cf10a19..a66315e65c 100644 --- a/src/parser/tests/parse_errors/parse_error_containers_tests.rs +++ b/src/parser/tests/parse_errors/parse_error_containers_tests.rs @@ -223,7 +223,7 @@ fn super_is_a_reserved_keyword() { error[E007]: Unexpected token: expected KeywordSemicolon but found 'VAR super' ┌─ :4:9 - │ + │ 4 │ ╭ VAR 5 │ │ super : INT; │ ╰─────────────────^ Unexpected token: expected KeywordSemicolon but found 'VAR @@ -238,7 +238,7 @@ fn super_is_a_reserved_keyword() { error[E007]: Unexpected token: expected KeywordSemicolon but found 'END_VAR METHOD super END_METHOD' ┌─ :6:9 - │ + │ 6 │ ╭ END_VAR 7 │ │ METHOD super END_METHOD │ ╰───────────────────────────────^ Unexpected token: expected KeywordSemicolon but found 'END_VAR @@ -257,3 +257,93 @@ fn super_is_a_reserved_keyword() { │ ^^^^^^^^^^^ Unexpected token: expected KeywordSemicolon but found 'END_PROGRAM' "); } + +#[test] +fn this_is_a_reserved_keyword() { + let src = " + INTERFACE this END_INTERFACE + PROGRAM this + VAR + this : INT; + END_VAR + METHOD this END_METHOD + END_PROGRAM + "; + + // TODO(mhasel): the parser produces a lot of noise for keyword errors, + // we need to find a way to handle keywords as identifiers + let diagnostics = parse_and_validate_buffered(src); + assert_snapshot!(diagnostics, @r" + error[E006]: Expected a name for the interface definition but got nothing + ┌─ :2:5 + │ + 2 │ INTERFACE this END_INTERFACE + │ ^^^^^^^^^ Expected a name for the interface definition but got nothing + + error[E006]: Missing expected Token KeywordEndInterface + ┌─ :2:15 + │ + 2 │ INTERFACE this END_INTERFACE + │ ^^^^ Missing expected Token KeywordEndInterface + + error[E007]: Unexpected token: expected StartKeyword but found this + ┌─ :2:15 + │ + 2 │ INTERFACE this END_INTERFACE + │ ^^^^ Unexpected token: expected StartKeyword but found this + + error[E007]: Unexpected token: expected StartKeyword but found END_INTERFACE + ┌─ :2:20 + │ + 2 │ INTERFACE this END_INTERFACE + │ ^^^^^^^^^^^^^ Unexpected token: expected StartKeyword but found END_INTERFACE + + error[E007]: Unexpected token: expected Identifier but found this + ┌─ :3:13 + │ + 3 │ PROGRAM this + │ ^^^^ Unexpected token: expected Identifier but found this + + error[E007]: Unexpected token: expected KeywordSemicolon but found 'VAR + this' + ┌─ :4:9 + │ + 4 │ ╭ VAR + 5 │ │ this : INT; + │ ╰────────────────^ Unexpected token: expected KeywordSemicolon but found 'VAR + this' + + error[E007]: Unexpected token: expected Literal but found END_VAR + ┌─ :6:9 + │ + 6 │ END_VAR + │ ^^^^^^^ Unexpected token: expected Literal but found END_VAR + + error[E007]: Unexpected token: expected KeywordSemicolon but found 'END_VAR + METHOD this END_METHOD' + ┌─ :6:9 + │ + 6 │ ╭ END_VAR + 7 │ │ METHOD this END_METHOD + │ ╰──────────────────────────────^ Unexpected token: expected KeywordSemicolon but found 'END_VAR + METHOD this END_METHOD' + + error[E006]: Missing expected Token [KeywordSemicolon, KeywordColon] + ┌─ :8:5 + │ + 8 │ END_PROGRAM + │ ^^^^^^^^^^^ Missing expected Token [KeywordSemicolon, KeywordColon] + + error[E007]: Unexpected token: expected KeywordSemicolon but found 'END_PROGRAM' + ┌─ :8:5 + │ + 8 │ END_PROGRAM + │ ^^^^^^^^^^^ Unexpected token: expected KeywordSemicolon but found 'END_PROGRAM' + + error[E079]: Case condition used outside of case statement! Did you mean to use ';'? + ┌─ :3:13 + │ + 3 │ PROGRAM this + │ ^^^^ Case condition used outside of case statement! Did you mean to use ';'? + "); +} From 83a78ff1ff1040dad4aee4c69d3b566e757df90a Mon Sep 17 00:00:00 2001 From: abroooo <61380142+abroooo@users.noreply.github.com> Date: Thu, 3 Apr 2025 13:27:31 +0200 Subject: [PATCH 02/48] add some tests --- src/parser/tests/expressions_parser_tests.rs | 1 + src/validation/tests.rs | 1 + .../tests/this_keyword_validation_tests.rs | 25 +++++++++++++++++++ 3 files changed, 27 insertions(+) create mode 100644 src/validation/tests/this_keyword_validation_tests.rs diff --git a/src/parser/tests/expressions_parser_tests.rs b/src/parser/tests/expressions_parser_tests.rs index 8ed0536f01..f7d3ab3d09 100644 --- a/src/parser/tests/expressions_parser_tests.rs +++ b/src/parser/tests/expressions_parser_tests.rs @@ -1888,6 +1888,7 @@ fn super_keyword_can_be_parsed_in_expressions() { #[test] fn this_keyword_can_be_parsed_in_expressions() { + // TODO: `this()` is not valid let src = " FUNCTION_BLOCK fb this.x; diff --git a/src/validation/tests.rs b/src/validation/tests.rs index b6dae4dbe7..df9786cd74 100644 --- a/src/validation/tests.rs +++ b/src/validation/tests.rs @@ -15,5 +15,6 @@ mod recursive_validation_tests; mod reference_resolve_tests; mod statement_validation_tests; mod super_keyword_validation_tests; +mod this_keyword_validation_tests; mod variable_length_array_test; mod variable_validation_tests; diff --git a/src/validation/tests/this_keyword_validation_tests.rs b/src/validation/tests/this_keyword_validation_tests.rs new file mode 100644 index 0000000000..a40d444fb9 --- /dev/null +++ b/src/validation/tests/this_keyword_validation_tests.rs @@ -0,0 +1,25 @@ +use insta::assert_snapshot; +use test_utils::parse_and_validate_buffered; + +#[test] +fn pointer_arithmetic_with_this() { + let diagnostics = parse_and_validate_buffered( + r#" + FUNCTION_BLOCK parent + VAR + x : LINT := 10; + y : LINT := 20; + END_VAR + END_FUNCTION_BLOCK + + FUNCTION_BLOCK child EXTENDS parent + VAR + a : INT; + END_VAR + // Pointer arithmetic with SUPER + a := (THIS + 1)^ + 5; + END_FUNCTION_BLOCK + "#, + ); + assert_snapshot!(diagnostics, @r#""#); +} From add58e07125b375c8d627ee73fa509de661eb254 Mon Sep 17 00:00:00 2001 From: abroooo <61380142+abroooo@users.noreply.github.com> Date: Sun, 6 Apr 2025 23:48:18 +0200 Subject: [PATCH 03/48] add error codes --- .../src/diagnostics/diagnostics_registry.rs | 2 ++ .../src/diagnostics/error_codes/E120.md | 17 +++++++++++++++++ .../src/diagnostics/error_codes/E121.md | 15 +++++++++++++++ 3 files changed, 34 insertions(+) create mode 100644 compiler/plc_diagnostics/src/diagnostics/error_codes/E120.md create mode 100644 compiler/plc_diagnostics/src/diagnostics/error_codes/E121.md diff --git a/compiler/plc_diagnostics/src/diagnostics/diagnostics_registry.rs b/compiler/plc_diagnostics/src/diagnostics/diagnostics_registry.rs index efdcd391dc..7f4ce6cd09 100644 --- a/compiler/plc_diagnostics/src/diagnostics/diagnostics_registry.rs +++ b/compiler/plc_diagnostics/src/diagnostics/diagnostics_registry.rs @@ -221,6 +221,8 @@ lazy_static! { E117, Error, include_str!("./error_codes/E117.md"), // Property with invalid number of GET and/or SET blocks E118, Info, include_str!("./error_codes/E118.md"), // Follow-up error to 112 E119, Error, include_str!("./error_codes/E119.md"), // Invalid use of `SUPER` keyword + E120, Info, include_str!("./error_codes/E120.md"), // Invalid use of `THIS` keyword + E121, Info, include_str!("./error_codes/E121.md"), // `THIS` is read-only ); } diff --git a/compiler/plc_diagnostics/src/diagnostics/error_codes/E120.md b/compiler/plc_diagnostics/src/diagnostics/error_codes/E120.md new file mode 100644 index 0000000000..c2e8e04e4f --- /dev/null +++ b/compiler/plc_diagnostics/src/diagnostics/error_codes/E120.md @@ -0,0 +1,17 @@ +# `THIS` keyword is only allowed in `FUNCTION_BLOCK` + +`THIS` is a keyword in IEC 61131-3 that refers to the instance of a `FUNCTION_BLOCK`. It is used to access the instance variables and methods of the `FUNCTION_BLOCK` from within its own methods. +Therefore it cannot be used in `FUNCTION`s or `PROGRAM`s, as these do not have an instance context like `FUNCTION_BLOCK`s do. + +Errouneus code example: + +```iec61131 +FUNCTION foo + VAR + someVar : STRING := 'this is the instance var'; + END_VAR + + // use of `THIS` is not allowed here + printf('%s', REF(THIS^.someVar)); +END_FUNCTION +``` diff --git a/compiler/plc_diagnostics/src/diagnostics/error_codes/E121.md b/compiler/plc_diagnostics/src/diagnostics/error_codes/E121.md new file mode 100644 index 0000000000..d595cd73cc --- /dev/null +++ b/compiler/plc_diagnostics/src/diagnostics/error_codes/E121.md @@ -0,0 +1,15 @@ +# `THIS` is read-only + +`THIS` is a keyword in IEC 61131-3 that refers to the instance of a `FUNCTION_BLOCK`. It is a pointer to the instance itself which doesn't change. Therefore `THIS` cannot be assigned. +Errouneus code example: + +```iec61131 + FUNCTION_BLOCK foo + END_FUNCTION_BLOCK + FUNCTION_BLOCK bar + VAR + fb : foo; + END_VAR + this := REF(foo); // not allowed + END_FUNCTION_BLOCK +``` From e41bd091f5400a2594b1f6923b611181e32a4c8f Mon Sep 17 00:00:00 2001 From: abroooo <61380142+abroooo@users.noreply.github.com> Date: Sun, 6 Apr 2025 23:49:13 +0200 Subject: [PATCH 04/48] add validation tests --- .../tests/this_keyword_validation_tests.rs | 342 ++++++++++++++++++ 1 file changed, 342 insertions(+) diff --git a/src/validation/tests/this_keyword_validation_tests.rs b/src/validation/tests/this_keyword_validation_tests.rs index a40d444fb9..b5796c6e09 100644 --- a/src/validation/tests/this_keyword_validation_tests.rs +++ b/src/validation/tests/this_keyword_validation_tests.rs @@ -23,3 +23,345 @@ fn pointer_arithmetic_with_this() { ); assert_snapshot!(diagnostics, @r#""#); } + +#[test] +fn cant_chain_this() { + let diagnostics = parse_and_validate_buffered( + r#" + FUNCTION_BLOCK parent + VAR + x : LINT := 10; + y : LINT := 20; + END_VAR + this^.x := this^.this^.y; + this^.this^.x := this^.y; + END_FUNCTION_BLOCK + "#, + ); + assert_snapshot!(diagnostics, @r#""#); +} + +#[test] +fn this_in_method_call_chain() { + let diagnostics = parse_and_validate_buffered( + r#" + FUNCTION_BLOCK FB_Test + VAR + counter : INT := 0; + END_VAR + + METHOD Step + THIS^.Increment(); + END_METHOD + + METHOD Increment + counter := counter + 1; + END_METHOD + END_FUNCTION_BLOCK + "#, + ); + assert_snapshot!(diagnostics, @r#""#); +} + +#[test] +fn this_not_allowed_in_program() { + let diagnostics = parse_and_validate_buffered( + r#" + PROGRAM Main + VAR + x : INT := 5; + END_VAR + x := THIS.x; + END_PROGRAM + "#, + ); + assert_snapshot!(diagnostics, @r###" +"###); +} + +#[test] +fn this_in_function_is_not_allowed() { + let diagnostics = parse_and_validate_buffered( + r#" + FUNCTION SomeFunction : INT + VAR + SomeValue : INT := 5; + END_VAR + SomeFunction := THIS^.SomeValue; + END_FUNCTION + "#, + ); + assert_snapshot!(diagnostics, @r###" +"###); +} + +#[test] +fn cant_assign_to_this() { + let diagnostics = parse_and_validate_buffered( + r#" + FUNCTION_BLOCK parent + VAR + x : LINT := 10; + END_VAR + this^ := 5; + this := REF(x); + END_FUNCTION_BLOCK + "#, + ); + assert_snapshot!(diagnostics, @r#""#); +} + +#[test] +fn basic_use() { + let diagnostics = parse_and_validate_buffered( + r#" + FUNCTION_BLOCK FB_Test + VAR + val : INT := 5; + END_VAR + + METHOD GetVal : INT + GetVal := THIS.val; + END_METHOD + END_FUNCTION_BLOCK + "#, + ); + assert_snapshot!(diagnostics, @r#""#); +} + +#[test] +fn pass_this_to_method() { + // pass `this` pointer of FB1 to a method of another fb called FB2 which calls a method of FB1 + // and changes a value of the passed `this` pointer + let diagnostics = parse_and_validate_buffered( + r#" + FUNCTION_BLOCK FB_Test + METHOD increment_from_other + VAR + test : FB_Test; + END_VAR + test.method2(THIS); + + END_FUNCTION_BLOCK + FUNCTION_BLOCK FB_Test2 + METHOD method1 + VAR + test : FB_Test; + END_VAR + test.method2(THIS); + END_METHOD + METHOD method2 + VAR + test : FB_Test; + END_VAR + test := THIS; + END_METHOD + END_FUNCTION_BLOCK + "#, + ); + assert_snapshot!(diagnostics, @r#""#); +} + +#[test] +fn simple_shadowing() { + let diagnostics = parse_and_validate_buffered( + r#" + FUNCTION_BLOCK FB_Test + VAR + val : INT := 5; + END_VAR + METHOD shadow_val + VAR + val : INT := 10; + local_val: INT; + shadow_val : INT; + END_VAR + local_val := THIS^.val; + shadow_val := val; + END_METHOD + END_FUNCTION_BLOCK + "#, + ); + assert_snapshot!(diagnostics, @r#""#); +} + +#[test] +fn nested_fbs_and_this_passing() { + let diagnostics = parse_and_validate_buffered( + r#" + FUNCTION_BLOCK OuterFB + VAR + Inner : InnerFB; + END_VAR + + METHOD CallInner + Inner.UseOuter(THIS); + END_METHOD + METHOD DoSomething + VAR + x : INT := 5; + END_VAR + x := 10; + END_METHOD + END_FUNCTION_BLOCK + + FUNCTION_BLOCK InnerFB + METHOD UseOuter + VAR_INPUT + ref : OuterFB; + END_VAR + ref.DoSomething(); + END_METHOD + END_FUNCTION_BLOCK + "#, + ); + assert_snapshot!(diagnostics, @r#""#); +} + +#[test] +fn this_as_method_argument() { + let diagnostics = parse_and_validate_buffered( + r#" + FUNCTION_BLOCK FB_Test + VAR + helper : FB_Helper; + END_VAR + METHOD CallHelper + helper.DoSomething(THIS^); + END_METHOD + END_FUNCTION_BLOCK + + FUNCTION_BLOCK FB_Helper + METHOD DoSomething + VAR_INPUT input : FB_Test; END_VAR + END_METHOD + END_FUNCTION_BLOCK + "#, + ); + assert_snapshot!(diagnostics, @r#""#); +} + +#[test] +fn this_in_recursive_method() { + let diagnostics = parse_and_validate_buffered( + r#" + FUNCTION_BLOCK FB_Test + VAR + count : INT := 0; + END_VAR + METHOD recursive_method + IF count < 3 THEN + count := count + 1; + THIS^.recursive_method(); + END_IF + END_METHOD + END_FUNCTION_BLOCK + "#, + ); + assert_snapshot!(diagnostics, @r#""#); +} + +#[test] +fn this_is_read_only() { + let diagnostics = parse_and_validate_buffered( + r#" + FUNCTION_BLOCK FB_Test + END_FUNCTION_BLOCK + FUNCTION_BLOCK FB_Test2 + VAR + test : FB_Test; + END_VAR + this := ADR(test); // this is not allowed + END_FUNCTION_BLOCK + "#, + ); + assert_snapshot!(diagnostics, @r#""#); +} + +#[test] +fn this_chained_with_super() { + let diagnostics = parse_and_validate_buffered( + r#" + FUNCTION_BLOCK parent + METHOD DoSomething : DINT + DoSomething := 5; + END_METHOD + END_FUNCTION_BLOCK + FUNCTION_BLOCK child EXTENDS parent + this^.super^.this^.DoSomething(); + END_FUNCTION_BLOCK + "#, + ); + assert_snapshot!(diagnostics, @r#""#); +} + +#[test] +fn this_in_properties() { + let diagnostics = parse_and_validate_buffered( + r#" + FUNCTION_BLOCK FB_Test + VAR + prop : INT; + END_VAR + PROPERTY GetProp : INT + GetProp := THIS^.prop; + END_PROPERTY + PROPERTY SetProp : INT + SetProp := THIS^.prop; + END_PROPERTY + END_FUNCTION_BLOCK + "#, + ); + assert_snapshot!(diagnostics, @r#""#); +} + +#[test] +fn this_in_property_calling_method() { + let diagnostics = parse_and_validate_buffered( + r#" + FUNCTION_BLOCK FB_Test + VAR + x : INT; + END_VAR + + METHOD DoubleX : INT + DoubleX := 2 * THIS^.x; + END_METHOD + + PROPERTY Value : INT + GET + Value := THIS^.DoubleX(); + END_GET + END_PROPERTY + END_FUNCTION_BLOCK + "#, + ); + assert_snapshot!(diagnostics, @r#""#); +} + +#[test] +fn this_with_adr_pointer() { + let diagnostics = parse_and_validate_buffered( + r#" + FUNCTION_BLOCK FB_Test + VAR + refToSelf : POINTER TO FB_Test; + END_VAR + + METHOD InitRef + refToSelf := ADR(THIS^); + END_METHOD + END_FUNCTION_BLOCK + "#, + ); + assert_snapshot!(diagnostics, @r#""#); +} + +#[test] +fn dummy() { + let diagnostics = parse_and_validate_buffered( + r#" + "#, + ); + assert_snapshot!(diagnostics, @r#""#); +} From c9c3a535c81743663898082de28a0f02a439a2f1 Mon Sep 17 00:00:00 2001 From: abroooo <61380142+abroooo@users.noreply.github.com> Date: Sun, 6 Apr 2025 23:50:29 +0200 Subject: [PATCH 05/48] extend shadowing lit test with `this` test --- tests/lit/single/oop/fb_method_shadowing.st | 26 +++++++++++++++------ 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/tests/lit/single/oop/fb_method_shadowing.st b/tests/lit/single/oop/fb_method_shadowing.st index 1f7571b184..74eebeb43c 100644 --- a/tests/lit/single/oop/fb_method_shadowing.st +++ b/tests/lit/single/oop/fb_method_shadowing.st @@ -7,17 +7,27 @@ END_VAR VAR_INPUT in: INT; END_VAR - VAR + VAR bar: DINT := 17; - END_VAR + END_VAR bar := in + bar; addToBar := bar; END_METHOD - + METHOD addToLocalBar: DINT + VAR_INPUT + in: INT; + END_VAR + VAR + bar: DINT := 17; + END_VAR + this^.bar := in + bar; + addToBar := this^.bar; + END_METHOD + addToBar(3); - printf('%d$N', bar); // CHECK: 42 + printf('%d$N', bar); // CHECK: 42 END_FUNCTION_BLOCK - + FUNCTION main VAR fb: foo; @@ -25,5 +35,7 @@ VAR END_VAR fb(); x := fb.addToBar(3); - printf('%d$N', x); // CHECK: 20 -END_FUNCTION \ No newline at end of file + printf('%d$N', x); // CHECK: 20 + x := fb.addToLocalBar(46); + printf('%d$N', x); // CHECK: 88 +END_FUNCTION From 5b100f281640d9c4c8f9a8795df17562e5825718 Mon Sep 17 00:00:00 2001 From: abroooo <61380142+abroooo@users.noreply.github.com> Date: Sun, 6 Apr 2025 23:51:19 +0200 Subject: [PATCH 06/48] add expression parser test --- src/parser/tests/expressions_parser_tests.rs | 116 +++++++++++++++++++ 1 file changed, 116 insertions(+) diff --git a/src/parser/tests/expressions_parser_tests.rs b/src/parser/tests/expressions_parser_tests.rs index f7d3ab3d09..6afaa68e0b 100644 --- a/src/parser/tests/expressions_parser_tests.rs +++ b/src/parser/tests/expressions_parser_tests.rs @@ -2006,3 +2006,119 @@ fn this_keyword_can_be_parsed_in_expressions() { ] "#); } + +#[test] +fn this_keyword_can_be_mixed_with_super() { + // TODO: `this()` is not valid + let src = " + FUNCTION_BLOCK fb + this^.super^.foo(this^.x + this^.y); + END_FUNCTION_BLOCK + "; + + let parse_result = parse(src).0; + assert_debug_snapshot!(parse_result.implementations[0].statements, @r#" + [ + ReferenceExpr { + kind: Member( + Identifier { + name: "x", + }, + ), + base: Some( + This, + ), + }, + ReferenceExpr { + kind: Member( + Identifier { + name: "y", + }, + ), + base: Some( + ReferenceExpr { + kind: Deref, + base: Some( + This, + ), + }, + ), + }, + This, + CallStatement { + operator: ReferenceExpr { + kind: Member( + Identifier { + name: "foo", + }, + ), + base: Some( + ReferenceExpr { + kind: Deref, + base: Some( + This, + ), + }, + ), + }, + parameters: Some( + BinaryExpression { + operator: Plus, + left: ReferenceExpr { + kind: Member( + Identifier { + name: "x", + }, + ), + base: Some( + This, + ), + }, + right: ReferenceExpr { + kind: Member( + Identifier { + name: "y", + }, + ), + base: Some( + ReferenceExpr { + kind: Deref, + base: Some( + This, + ), + }, + ), + }, + }, + ), + }, + CallStatement { + operator: This, + parameters: None, + }, + Assignment { + left: This, + right: CallStatement { + operator: ReferenceExpr { + kind: Member( + Identifier { + name: "REF", + }, + ), + base: None, + }, + parameters: Some( + ReferenceExpr { + kind: Member( + Identifier { + name: "fb2", + }, + ), + base: None, + }, + ), + }, + }, + ] + "#); +} From 39825c102ee7953de367468f487f81584e4f880f Mon Sep 17 00:00:00 2001 From: abroooo <61380142+abroooo@users.noreply.github.com> Date: Sun, 6 Apr 2025 23:52:01 +0200 Subject: [PATCH 07/48] prepare codegen extension --- src/codegen/generators/expression_generator.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/codegen/generators/expression_generator.rs b/src/codegen/generators/expression_generator.rs index 52b782c90a..58b3e7bc76 100644 --- a/src/codegen/generators/expression_generator.rs +++ b/src/codegen/generators/expression_generator.rs @@ -217,6 +217,10 @@ impl<'ink, 'b> ExpressionCodeGenerator<'ink, 'b> { // generate the expression match expression.get_stmt() { + AstStatement::This => { + // TODO: ask node for type (annotations). + !todo!() + } AstStatement::ReferenceExpr(data) => { let res = self.generate_reference_expression(&data.access, data.base.as_deref(), expression)?; From ab7cd7f90cb2b1622d1ffbf2ccab1569526a5f00 Mon Sep 17 00:00:00 2001 From: abroooo <61380142+abroooo@users.noreply.github.com> Date: Sun, 6 Apr 2025 23:52:30 +0200 Subject: [PATCH 08/48] prepare visitor extension --- compiler/plc_ast/src/mut_visitor.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/compiler/plc_ast/src/mut_visitor.rs b/compiler/plc_ast/src/mut_visitor.rs index 13755f0ec1..49a9a30cc1 100644 --- a/compiler/plc_ast/src/mut_visitor.rs +++ b/compiler/plc_ast/src/mut_visitor.rs @@ -210,7 +210,10 @@ pub trait AstVisitorMut: Sized { fn visit_interface(&mut self, _interface: &mut Interface) {} fn visit_property(&mut self, _property: &mut PropertyBlock) {} + fn visit_super(&mut self, _node: &mut AstNode) {} + + fn visit_this(&mut self, _node: &mut AstNode) {} } impl WalkerMut for AstLiteral { From 02c41b128d9baa24ac1cc1fb3e298fe2fc836dc9 Mon Sep 17 00:00:00 2001 From: abroooo <61380142+abroooo@users.noreply.github.com> Date: Sun, 6 Apr 2025 23:54:24 +0200 Subject: [PATCH 09/48] add test --- compiler/plc_lowering/src/inheritance.rs | 42 ++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/compiler/plc_lowering/src/inheritance.rs b/compiler/plc_lowering/src/inheritance.rs index 421ed391d0..c197e868f8 100644 --- a/compiler/plc_lowering/src/inheritance.rs +++ b/compiler/plc_lowering/src/inheritance.rs @@ -447,4 +447,46 @@ impl AstVisitorMut for SuperKeywordLowerer<'_> { .with_pou(self.ctx.pou.as_deref().unwrap_or_default()); self.annotations.as_mut().unwrap().import(resolver.resolve_statement(node)); } + // TODO: this test is completly in the wrong place => move! + #[test] + fn simple_this_deref() { + let src: SourceCode = " + FUNCTION_BLOCK foo + VAR + x : INT; + y : INT; + END_VAR + y := this^.x; + END_FUNCTION_BLOCK + + " + .into(); + + let (_, project) = parse_and_annotate("test", vec![src]).unwrap(); + let statements = &project.units[0].get_unit().implementations[1].statements; + assert_debug_snapshot!(statements, @r#" + [ + CallStatement { + operator: ReferenceExpr { + kind: Member( + Identifier { + name: "REF", + }, + ), + base: None, + }, + parameters: Some( + ReferenceExpr { + kind: Member( + Identifier { + name: "__foo", + }, + ), + base: None, + }, + ), + }, + ] + "#); + } } From 1e22a634b070832d68ce4943d7198a439140a651 Mon Sep 17 00:00:00 2001 From: abroooo <61380142+abroooo@users.noreply.github.com> Date: Sun, 6 Apr 2025 23:54:57 +0200 Subject: [PATCH 10/48] resolve `this` --- src/resolver.rs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/resolver.rs b/src/resolver.rs index 794684066b..9439e66e84 100644 --- a/src/resolver.rs +++ b/src/resolver.rs @@ -1646,6 +1646,15 @@ impl<'i> TypeAnnotator<'i> { /// annotate an expression statement fn visit_statement_expression(&mut self, ctx: &VisitorContext, statement: &AstNode) { match statement.get_stmt() { + AstStatement::This => { + // TODO: only for `THIS` in FunctionBlock context annotate with ptr_type + if let Some(pou) = ctx.pou.and_then(|name| self.index.find_pou(name)) { + if let PouIndexEntry::FunctionBlock { name, .. } = pou { + let ptr_type = add_pointer_type(&mut self.annotation_map.new_index, name.to_string()); + self.annotate(statement, StatementAnnotation::value(ptr_type)); + } + } + } AstStatement::DirectAccess(data, ..) => { let ctx = VisitorContext { qualifier: None, ..ctx.clone() }; visit_all_statements!(self, &ctx, &data.index); @@ -1986,6 +1995,19 @@ impl<'i> TypeAnnotator<'i> { ctx: &VisitorContext<'_>, ) -> Option { match reference.get_stmt() { + AstStatement::This => { + // Only `THIS` in FunctionBlock context + if let Some(pou) = ctx.pou.and_then(|name| self.index.find_pou(name)) { + if let PouIndexEntry::FunctionBlock { name, .. } = pou { + let ptr_type = add_pointer_type(&mut self.annotation_map.new_index, name.to_string()); + Some(StatementAnnotation::value(ptr_type)) + } else { + None + } + } else { + None + } + } AstStatement::Identifier(name, ..) => ctx .resolve_strategy .iter() From d6fb5600f8ef3679401fbb5bda32639d00184987 Mon Sep 17 00:00:00 2001 From: abroooo <61380142+abroooo@users.noreply.github.com> Date: Mon, 7 Apr 2025 20:07:07 +0200 Subject: [PATCH 11/48] add another parser test --- src/parser/tests/expressions_parser_tests.rs | 44 ++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/src/parser/tests/expressions_parser_tests.rs b/src/parser/tests/expressions_parser_tests.rs index 6afaa68e0b..5f6bfe9402 100644 --- a/src/parser/tests/expressions_parser_tests.rs +++ b/src/parser/tests/expressions_parser_tests.rs @@ -2122,3 +2122,47 @@ fn this_keyword_can_be_mixed_with_super() { ] "#); } + +#[test] +fn this_keyword_can_be_parsed_in_method() { + // TODO: `this()` is not valid + let src = " + FUNCTION_BLOCK fb + METHOD doSomething : INT + doSomething := this^.y; + END_METHOD + END_FUNCTION_BLOCK +somePtr := this; + "; + + let parse_result = parse(src).0; + assert_debug_snapshot!(parse_result.implementations[0].statements, @r#" + [ + Assignment { + left: ReferenceExpr { + kind: Member( + Identifier { + name: "doSomething", + }, + ), + base: None, + }, + right: ReferenceExpr { + kind: Member( + Identifier { + name: "y", + }, + ), + base: Some( + ReferenceExpr { + kind: Deref, + base: Some( + This, + ), + }, + ), + }, + }, + ] + "#); +} From 58ff739148ebd495d39b6f854f6d4f9b03a84efa Mon Sep 17 00:00:00 2001 From: abroooo <61380142+abroooo@users.noreply.github.com> Date: Mon, 7 Apr 2025 20:07:42 +0200 Subject: [PATCH 12/48] this doesnt need ptr_type --- src/resolver.rs | 9 +++++---- src/validation/tests/this_keyword_validation_tests.rs | 1 + 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/resolver.rs b/src/resolver.rs index 9439e66e84..ca79b113fd 100644 --- a/src/resolver.rs +++ b/src/resolver.rs @@ -1650,8 +1650,8 @@ impl<'i> TypeAnnotator<'i> { // TODO: only for `THIS` in FunctionBlock context annotate with ptr_type if let Some(pou) = ctx.pou.and_then(|name| self.index.find_pou(name)) { if let PouIndexEntry::FunctionBlock { name, .. } = pou { - let ptr_type = add_pointer_type(&mut self.annotation_map.new_index, name.to_string()); - self.annotate(statement, StatementAnnotation::value(ptr_type)); + // TODO: check index if ptr already exists + self.annotate(statement, StatementAnnotation::value(name)); } } } @@ -1999,8 +1999,9 @@ impl<'i> TypeAnnotator<'i> { // Only `THIS` in FunctionBlock context if let Some(pou) = ctx.pou.and_then(|name| self.index.find_pou(name)) { if let PouIndexEntry::FunctionBlock { name, .. } = pou { - let ptr_type = add_pointer_type(&mut self.annotation_map.new_index, name.to_string()); - Some(StatementAnnotation::value(ptr_type)) + // TODO: check if ptr already exists and return exisiting one + // check if ptr can be added to index during indexing + Some(StatementAnnotation::value(name)) } else { None } diff --git a/src/validation/tests/this_keyword_validation_tests.rs b/src/validation/tests/this_keyword_validation_tests.rs index b5796c6e09..2eb887d11b 100644 --- a/src/validation/tests/this_keyword_validation_tests.rs +++ b/src/validation/tests/this_keyword_validation_tests.rs @@ -39,6 +39,7 @@ fn cant_chain_this() { "#, ); assert_snapshot!(diagnostics, @r#""#); + panic!("This test should not work"); } #[test] From 78213336fd61fe7a824c632a9e39e56eeabd34c6 Mon Sep 17 00:00:00 2001 From: abroooo <61380142+abroooo@users.noreply.github.com> Date: Tue, 8 Apr 2025 15:14:01 +0200 Subject: [PATCH 13/48] generate expression value --- compiler/plc_ast/src/ast.rs | 4 ++++ src/codegen/generators/expression_generator.rs | 15 ++++++++++++++- src/codegen/tests/oop_tests.rs | 13 +++++++++++++ 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/compiler/plc_ast/src/ast.rs b/compiler/plc_ast/src/ast.rs index c8bb54140c..61a57efdc7 100644 --- a/compiler/plc_ast/src/ast.rs +++ b/compiler/plc_ast/src/ast.rs @@ -1238,6 +1238,10 @@ impl AstNode { ) } + pub fn is_this(&self) -> bool { + matches!(self.stmt, AstStatement::This) + } + pub fn is_paren(&self) -> bool { matches!(self.stmt, AstStatement::ParenExpression { .. }) } diff --git a/src/codegen/generators/expression_generator.rs b/src/codegen/generators/expression_generator.rs index 58b3e7bc76..cdf6ce790e 100644 --- a/src/codegen/generators/expression_generator.rs +++ b/src/codegen/generators/expression_generator.rs @@ -219,7 +219,20 @@ impl<'ink, 'b> ExpressionCodeGenerator<'ink, 'b> { match expression.get_stmt() { AstStatement::This => { // TODO: ask node for type (annotations). - !todo!() + let typebla = self.annotations.get_type(expression, self.index).unwrap().get_name(); + let sth = self.llvm_index.get_associated_pou_type(typebla).unwrap(); + let Some(first_param) = self.function_context.and_then(|fc| fc.function.get_first_param()) + else { + todo!(); + }; + let bc = self.llvm.builder.build_bitcast( + first_param, + sth.ptr_type(AddressSpace::default()).ptr_type(AddressSpace::default()), + "this", + ); + Ok(ExpressionValue::LValue(bc.into_pointer_value())) + // dbg!(first_param); + // todo!() } AstStatement::ReferenceExpr(data) => { let res = diff --git a/src/codegen/tests/oop_tests.rs b/src/codegen/tests/oop_tests.rs index 7d1f78179f..499fde4068 100644 --- a/src/codegen/tests/oop_tests.rs +++ b/src/codegen/tests/oop_tests.rs @@ -586,3 +586,16 @@ fn properties_are_methods() { assert_eq!(property, method); } +#[test] +fn this() { + let property = codegen( + " + FUNCTION_BLOCK fb + VAR + localPrivateVariable : DINT; + END_VAR + this^.localPrivateVariable := 5; + END_FUNCTION_BLOCK + ", + ); +} From 8b0172fb8b12b54316f23481f1b34b261bcd5e6c Mon Sep 17 00:00:00 2001 From: abroooo <61380142+abroooo@users.noreply.github.com> Date: Wed, 9 Apr 2025 13:37:41 +0200 Subject: [PATCH 14/48] resolve this works --- .../generators/expression_generator.rs | 25 ++++++------------- src/codegen/generators/pou_generator.rs | 3 +++ src/resolver.rs | 4 ++- 3 files changed, 14 insertions(+), 18 deletions(-) diff --git a/src/codegen/generators/expression_generator.rs b/src/codegen/generators/expression_generator.rs index cdf6ce790e..53c1a45ea0 100644 --- a/src/codegen/generators/expression_generator.rs +++ b/src/codegen/generators/expression_generator.rs @@ -217,23 +217,14 @@ impl<'ink, 'b> ExpressionCodeGenerator<'ink, 'b> { // generate the expression match expression.get_stmt() { - AstStatement::This => { - // TODO: ask node for type (annotations). - let typebla = self.annotations.get_type(expression, self.index).unwrap().get_name(); - let sth = self.llvm_index.get_associated_pou_type(typebla).unwrap(); - let Some(first_param) = self.function_context.and_then(|fc| fc.function.get_first_param()) - else { - todo!(); - }; - let bc = self.llvm.builder.build_bitcast( - first_param, - sth.ptr_type(AddressSpace::default()).ptr_type(AddressSpace::default()), - "this", - ); - Ok(ExpressionValue::LValue(bc.into_pointer_value())) - // dbg!(first_param); - // todo!() - } + AstStatement::This => Ok(ExpressionValue::LValue( + self.llvm_index + .find_loaded_associated_variable_value(&format!( + "{}.this", + self.function_context.unwrap().linking_context.get_call_name() + )) + .unwrap(), + )), AstStatement::ReferenceExpr(data) => { let res = self.generate_reference_expression(&data.access, data.base.as_deref(), expression)?; diff --git a/src/codegen/generators/pou_generator.rs b/src/codegen/generators/pou_generator.rs index b35c072b00..1ab5534cb4 100644 --- a/src/codegen/generators/pou_generator.rs +++ b/src/codegen/generators/pou_generator.rs @@ -699,6 +699,9 @@ impl<'ink, 'cg> PouGenerator<'ink, 'cg> { location.get_column(), ); } + let alloca = self.llvm.builder.build_alloca(param_pointer.get_type(), "this"); + self.llvm.builder.build_store(alloca, param_pointer); + index.associate_loaded_local_variable(type_name, "this", alloca)?; //Generate reference to parameter // cannot use index from members because return and temp variables may not be considered for index in build_struct_gep let mut var_count = 0; diff --git a/src/resolver.rs b/src/resolver.rs index ca79b113fd..d42c01caf3 100644 --- a/src/resolver.rs +++ b/src/resolver.rs @@ -1648,6 +1648,7 @@ impl<'i> TypeAnnotator<'i> { match statement.get_stmt() { AstStatement::This => { // TODO: only for `THIS` in FunctionBlock context annotate with ptr_type + // TODO: also support methods if let Some(pou) = ctx.pou.and_then(|name| self.index.find_pou(name)) { if let PouIndexEntry::FunctionBlock { name, .. } = pou { // TODO: check index if ptr already exists @@ -1996,7 +1997,8 @@ impl<'i> TypeAnnotator<'i> { ) -> Option { match reference.get_stmt() { AstStatement::This => { - // Only `THIS` in FunctionBlock context + // Only `THIS` in FunctionBlock/methods context + // TODO: also support methods if let Some(pou) = ctx.pou.and_then(|name| self.index.find_pou(name)) { if let PouIndexEntry::FunctionBlock { name, .. } = pou { // TODO: check if ptr already exists and return exisiting one From 3fb8f5ceb83e635d66bfae2ca18ce2498ed9bb9c Mon Sep 17 00:00:00 2001 From: abroooo <61380142+abroooo@users.noreply.github.com> Date: Wed, 9 Apr 2025 15:19:39 +0200 Subject: [PATCH 15/48] cheat validation --- src/validation/statement.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/validation/statement.rs b/src/validation/statement.rs index c4f37a53a2..8e656049cc 100644 --- a/src/validation/statement.rs +++ b/src/validation/statement.rs @@ -913,6 +913,8 @@ pub fn validate_pointer_assignment( ) where T: AnnotationMap, { + // TODO: #THIS fix validation + return; let type_info_lhs = context.index.get_intrinsic_type_information( context.index.find_elementary_pointer_type(type_lhs.get_type_information()), ); @@ -1295,6 +1297,8 @@ fn is_invalid_pointer_assignment( location: &SourceLocation, validator: &mut Validator, ) -> bool { + // TODO: #THIS fix validation + return false; if left_type.is_pointer() & right_type.is_pointer() { return !typesystem::is_same_type_class(left_type, right_type, index); } From de0c84c16a95821f4e743379e62b5a070e3e7320 Mon Sep 17 00:00:00 2001 From: abroooo <61380142+abroooo@users.noreply.github.com> Date: Wed, 9 Apr 2025 15:23:22 +0200 Subject: [PATCH 16/48] do not forget the todo --- src/codegen/generators/pou_generator.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/codegen/generators/pou_generator.rs b/src/codegen/generators/pou_generator.rs index 1ab5534cb4..6c6804f2da 100644 --- a/src/codegen/generators/pou_generator.rs +++ b/src/codegen/generators/pou_generator.rs @@ -699,9 +699,16 @@ impl<'ink, 'cg> PouGenerator<'ink, 'cg> { location.get_column(), ); } + // TODO: add am I a functionblock? let alloca = self.llvm.builder.build_alloca(param_pointer.get_type(), "this"); self.llvm.builder.build_store(alloca, param_pointer); index.associate_loaded_local_variable(type_name, "this", alloca)?; + // TODO: associate loaded variable for methods: + // - am I a method? + // AND + // - am I in a functionblock? + // - yes: find parent, associate loaded var and mangled name with `this` i.e. + // fb.method_name.this //Generate reference to parameter // cannot use index from members because return and temp variables may not be considered for index in build_struct_gep let mut var_count = 0; From daa2601d6c5361f0008b5e15880fc66ab3398555 Mon Sep 17 00:00:00 2001 From: abroooo <61380142+abroooo@users.noreply.github.com> Date: Thu, 10 Apr 2025 00:04:15 +0200 Subject: [PATCH 17/48] a bit of error handling and a bit of progress in resolving --- .../generators/expression_generator.rs | 21 +++++++++++---- src/codegen/generators/pou_generator.rs | 27 ++++++++++++++++--- src/resolver.rs | 8 ++++-- 3 files changed, 46 insertions(+), 10 deletions(-) diff --git a/src/codegen/generators/expression_generator.rs b/src/codegen/generators/expression_generator.rs index 53c1a45ea0..236b5f6edb 100644 --- a/src/codegen/generators/expression_generator.rs +++ b/src/codegen/generators/expression_generator.rs @@ -217,14 +217,25 @@ impl<'ink, 'b> ExpressionCodeGenerator<'ink, 'b> { // generate the expression match expression.get_stmt() { - AstStatement::This => Ok(ExpressionValue::LValue( - self.llvm_index + AstStatement::This => { + // TODO: #THIS meaningful errors + // [ ] add tests + let function_context = self.function_context.ok_or_else(|| { + Diagnostic::codegen_error("Cannot use 'this' without context", expression) + })?; + let this_value = self + .llvm_index .find_loaded_associated_variable_value(&format!( "{}.this", - self.function_context.unwrap().linking_context.get_call_name() + function_context.linking_context.get_call_name() )) - .unwrap(), - )), + .ok_or_else(|| { + let message = format!("Cannot find 'this' for ''"); + Diagnostic::codegen_error(message, expression) + })?; + + Ok(ExpressionValue::LValue(this_value)) + } AstStatement::ReferenceExpr(data) => { let res = self.generate_reference_expression(&data.access, data.base.as_deref(), expression)?; diff --git a/src/codegen/generators/pou_generator.rs b/src/codegen/generators/pou_generator.rs index 6c6804f2da..5722ba09cb 100644 --- a/src/codegen/generators/pou_generator.rs +++ b/src/codegen/generators/pou_generator.rs @@ -700,9 +700,30 @@ impl<'ink, 'cg> PouGenerator<'ink, 'cg> { ); } // TODO: add am I a functionblock? - let alloca = self.llvm.builder.build_alloca(param_pointer.get_type(), "this"); - self.llvm.builder.build_store(alloca, param_pointer); - index.associate_loaded_local_variable(type_name, "this", alloca)?; + // if function_context.linking_context.is_method() { + // println!("is method"); + // let alloca = self.llvm.builder.build_alloca(param_pointer.get_type(), "this"); + // self.llvm.builder.build_store(alloca, param_pointer); + // let parent = function_context.linking_context.get_associated_class_name().unwrap_or(type_name); + // index.associate_loaded_local_variable( + // type_name, + // !format!("{}.this", function_context.linking_context.get_associated_class_name()), + // alloca, + // )?; + // } + if function_context.linking_context.get_implementation_type() == &ImplementationType::FunctionBlock { + println!("is functionblock"); + let alloca = self.llvm.builder.build_alloca(param_pointer.get_type(), "this"); + self.llvm.builder.build_store(alloca, param_pointer); + index.associate_loaded_local_variable(type_name, "this", alloca)?; + } + dbg!(&function_context.linking_context.get_implementation_type()); + dbg!(&function_context.linking_context.get_type_name()); + dbg!(&function_context.linking_context.is_method()); + dbg!(&function_context.linking_context.get_call_name()); + dbg!(&function_context.linking_context.get_associated_class_name()); + dbg!(&function_context.function.get_name()); + dbg!(&function_context.linking_context.get_associated_class_name()); // TODO: associate loaded variable for methods: // - am I a method? // AND diff --git a/src/resolver.rs b/src/resolver.rs index d42c01caf3..d0ca54c7e8 100644 --- a/src/resolver.rs +++ b/src/resolver.rs @@ -1650,7 +1650,9 @@ impl<'i> TypeAnnotator<'i> { // TODO: only for `THIS` in FunctionBlock context annotate with ptr_type // TODO: also support methods if let Some(pou) = ctx.pou.and_then(|name| self.index.find_pou(name)) { - if let PouIndexEntry::FunctionBlock { name, .. } = pou { + if let PouIndexEntry::FunctionBlock { name, .. } | PouIndexEntry::Method { name, .. } = + pou + { // TODO: check index if ptr already exists self.annotate(statement, StatementAnnotation::value(name)); } @@ -2000,7 +2002,9 @@ impl<'i> TypeAnnotator<'i> { // Only `THIS` in FunctionBlock/methods context // TODO: also support methods if let Some(pou) = ctx.pou.and_then(|name| self.index.find_pou(name)) { - if let PouIndexEntry::FunctionBlock { name, .. } = pou { + if let PouIndexEntry::FunctionBlock { name, .. } | PouIndexEntry::Method { name, .. } = + pou + { // TODO: check if ptr already exists and return exisiting one // check if ptr can be added to index during indexing Some(StatementAnnotation::value(name)) From 7f1373543dac5b6f38f521907a6c810d8d9fe695 Mon Sep 17 00:00:00 2001 From: abroooo <61380142+abroooo@users.noreply.github.com> Date: Thu, 10 Apr 2025 11:07:00 +0200 Subject: [PATCH 18/48] `this` works for functionblocks and methods --- .../generators/expression_generator.rs | 20 ++++++++----- src/codegen/generators/pou_generator.rs | 30 ++++++++----------- src/resolver.rs | 11 +++---- 3 files changed, 30 insertions(+), 31 deletions(-) diff --git a/src/codegen/generators/expression_generator.rs b/src/codegen/generators/expression_generator.rs index 236b5f6edb..35e916bacb 100644 --- a/src/codegen/generators/expression_generator.rs +++ b/src/codegen/generators/expression_generator.rs @@ -223,14 +223,18 @@ impl<'ink, 'b> ExpressionCodeGenerator<'ink, 'b> { let function_context = self.function_context.ok_or_else(|| { Diagnostic::codegen_error("Cannot use 'this' without context", expression) })?; - let this_value = self - .llvm_index - .find_loaded_associated_variable_value(&format!( - "{}.this", - function_context.linking_context.get_call_name() - )) - .ok_or_else(|| { - let message = format!("Cannot find 'this' for ''"); + let call_name = function_context.linking_context.get_call_name(); + dbg!(function_context.linking_context.get_type_name()); + dbg!(&call_name); + dbg!(self.annotations.get_call_name(expression)); + let this_name = &format!("{}.this", self.annotations.get_call_name(expression).unwrap()); + dbg!(&this_name); + let this_value = + self.llvm_index.find_loaded_associated_variable_value(&this_name).ok_or_else(|| { + let message = format!( + "Cannot find 'this' for '{}'", + self.annotations.get_call_name(expression).unwrap_or_default() + ); Diagnostic::codegen_error(message, expression) })?; diff --git a/src/codegen/generators/pou_generator.rs b/src/codegen/generators/pou_generator.rs index 5722ba09cb..d3e5d337c6 100644 --- a/src/codegen/generators/pou_generator.rs +++ b/src/codegen/generators/pou_generator.rs @@ -700,30 +700,24 @@ impl<'ink, 'cg> PouGenerator<'ink, 'cg> { ); } // TODO: add am I a functionblock? - // if function_context.linking_context.is_method() { - // println!("is method"); - // let alloca = self.llvm.builder.build_alloca(param_pointer.get_type(), "this"); - // self.llvm.builder.build_store(alloca, param_pointer); - // let parent = function_context.linking_context.get_associated_class_name().unwrap_or(type_name); - // index.associate_loaded_local_variable( - // type_name, - // !format!("{}.this", function_context.linking_context.get_associated_class_name()), - // alloca, - // )?; - // } + let name = &format!("{}.this", type_name); + if function_context.linking_context.is_method() + && self.index.find_pou(type_name).is_some_and(|it| it.is_function_block()) + { + println!("is method"); + dbg!(¶m_pointer); + let alloca = self.llvm.builder.build_alloca(param_pointer.get_type(), "this"); + self.llvm.builder.build_store(alloca, param_pointer); + dbg!(&name); + index.associate_loaded_local_variable(type_name, "this", alloca)?; + // index.associate_loaded_local_variable(type_name, "this", alloca)?; + } if function_context.linking_context.get_implementation_type() == &ImplementationType::FunctionBlock { println!("is functionblock"); let alloca = self.llvm.builder.build_alloca(param_pointer.get_type(), "this"); self.llvm.builder.build_store(alloca, param_pointer); index.associate_loaded_local_variable(type_name, "this", alloca)?; } - dbg!(&function_context.linking_context.get_implementation_type()); - dbg!(&function_context.linking_context.get_type_name()); - dbg!(&function_context.linking_context.is_method()); - dbg!(&function_context.linking_context.get_call_name()); - dbg!(&function_context.linking_context.get_associated_class_name()); - dbg!(&function_context.function.get_name()); - dbg!(&function_context.linking_context.get_associated_class_name()); // TODO: associate loaded variable for methods: // - am I a method? // AND diff --git a/src/resolver.rs b/src/resolver.rs index d0ca54c7e8..610c47a6d1 100644 --- a/src/resolver.rs +++ b/src/resolver.rs @@ -1650,11 +1650,12 @@ impl<'i> TypeAnnotator<'i> { // TODO: only for `THIS` in FunctionBlock context annotate with ptr_type // TODO: also support methods if let Some(pou) = ctx.pou.and_then(|name| self.index.find_pou(name)) { - if let PouIndexEntry::FunctionBlock { name, .. } | PouIndexEntry::Method { name, .. } = - pou - { - // TODO: check index if ptr already exists - self.annotate(statement, StatementAnnotation::value(name)); + match pou { + PouIndexEntry::FunctionBlock { name, .. } + | PouIndexEntry::Method { parent_name: name, .. } => { + self.annotate(statement, StatementAnnotation::value(name)); + } + _ => {} } } } From c2ad79d171e80b2a8f7db57df2509a92a80a7f67 Mon Sep 17 00:00:00 2001 From: abroooo <61380142+abroooo@users.noreply.github.com> Date: Thu, 10 Apr 2025 11:59:00 +0200 Subject: [PATCH 19/48] cleanup --- src/codegen/generators/expression_generator.rs | 11 +---------- src/codegen/generators/pou_generator.rs | 15 ++------------- src/resolver.rs | 3 +-- 3 files changed, 4 insertions(+), 25 deletions(-) diff --git a/src/codegen/generators/expression_generator.rs b/src/codegen/generators/expression_generator.rs index 35e916bacb..883ed8e7d9 100644 --- a/src/codegen/generators/expression_generator.rs +++ b/src/codegen/generators/expression_generator.rs @@ -223,21 +223,12 @@ impl<'ink, 'b> ExpressionCodeGenerator<'ink, 'b> { let function_context = self.function_context.ok_or_else(|| { Diagnostic::codegen_error("Cannot use 'this' without context", expression) })?; - let call_name = function_context.linking_context.get_call_name(); - dbg!(function_context.linking_context.get_type_name()); - dbg!(&call_name); - dbg!(self.annotations.get_call_name(expression)); let this_name = &format!("{}.this", self.annotations.get_call_name(expression).unwrap()); - dbg!(&this_name); let this_value = self.llvm_index.find_loaded_associated_variable_value(&this_name).ok_or_else(|| { - let message = format!( - "Cannot find 'this' for '{}'", - self.annotations.get_call_name(expression).unwrap_or_default() - ); + let message = format!("Cannot find '{}' in associated variable values", this_name); Diagnostic::codegen_error(message, expression) })?; - Ok(ExpressionValue::LValue(this_value)) } AstStatement::ReferenceExpr(data) => { diff --git a/src/codegen/generators/pou_generator.rs b/src/codegen/generators/pou_generator.rs index d3e5d337c6..2fbdbcced5 100644 --- a/src/codegen/generators/pou_generator.rs +++ b/src/codegen/generators/pou_generator.rs @@ -699,31 +699,20 @@ impl<'ink, 'cg> PouGenerator<'ink, 'cg> { location.get_column(), ); } - // TODO: add am I a functionblock? - let name = &format!("{}.this", type_name); + if function_context.linking_context.is_method() && self.index.find_pou(type_name).is_some_and(|it| it.is_function_block()) { - println!("is method"); - dbg!(¶m_pointer); let alloca = self.llvm.builder.build_alloca(param_pointer.get_type(), "this"); self.llvm.builder.build_store(alloca, param_pointer); - dbg!(&name); index.associate_loaded_local_variable(type_name, "this", alloca)?; - // index.associate_loaded_local_variable(type_name, "this", alloca)?; } if function_context.linking_context.get_implementation_type() == &ImplementationType::FunctionBlock { - println!("is functionblock"); let alloca = self.llvm.builder.build_alloca(param_pointer.get_type(), "this"); self.llvm.builder.build_store(alloca, param_pointer); index.associate_loaded_local_variable(type_name, "this", alloca)?; } - // TODO: associate loaded variable for methods: - // - am I a method? - // AND - // - am I in a functionblock? - // - yes: find parent, associate loaded var and mangled name with `this` i.e. - // fb.method_name.this + //Generate reference to parameter // cannot use index from members because return and temp variables may not be considered for index in build_struct_gep let mut var_count = 0; diff --git a/src/resolver.rs b/src/resolver.rs index 610c47a6d1..0c5a284edf 100644 --- a/src/resolver.rs +++ b/src/resolver.rs @@ -1647,10 +1647,9 @@ impl<'i> TypeAnnotator<'i> { fn visit_statement_expression(&mut self, ctx: &VisitorContext, statement: &AstNode) { match statement.get_stmt() { AstStatement::This => { - // TODO: only for `THIS` in FunctionBlock context annotate with ptr_type - // TODO: also support methods if let Some(pou) = ctx.pou.and_then(|name| self.index.find_pou(name)) { match pou { + // TODO: #THIS for method check if parent is of type functionblock PouIndexEntry::FunctionBlock { name, .. } | PouIndexEntry::Method { parent_name: name, .. } => { self.annotate(statement, StatementAnnotation::value(name)); From 0ca734a4210e2bed65e30eddd8376a21439f1905 Mon Sep 17 00:00:00 2001 From: abroooo <61380142+abroooo@users.noreply.github.com> Date: Thu, 10 Apr 2025 14:18:33 +0200 Subject: [PATCH 20/48] add pointer to new index --- src/resolver.rs | 23 ++++++++++++++++++- .../tests/resolve_expressions_tests.rs | 19 +++++++++++++++ src/validation/statement.rs | 4 ---- 3 files changed, 41 insertions(+), 5 deletions(-) diff --git a/src/resolver.rs b/src/resolver.rs index 0c5a284edf..d55c7bea5e 100644 --- a/src/resolver.rs +++ b/src/resolver.rs @@ -1652,7 +1652,28 @@ impl<'i> TypeAnnotator<'i> { // TODO: #THIS for method check if parent is of type functionblock PouIndexEntry::FunctionBlock { name, .. } | PouIndexEntry::Method { parent_name: name, .. } => { - self.annotate(statement, StatementAnnotation::value(name)); + let ptr_name = format!("__THIS_{}", name); + if self + .index + .find_type(&ptr_name) + .or_else(|| self.annotation_map.new_index.find_type(&ptr_name)) + .is_none() + { + let information = DataTypeInformation::Pointer { + name: ptr_name.clone(), + inner_type_name: name.to_string(), + auto_deref: None, + }; + let dt = crate::typesystem::DataType { + name: ptr_name.clone(), + initial_value: None, + information, + nature: TypeNature::Any, + location: SourceLocation::internal(), + }; + self.annotation_map.new_index.register_type(dt); + } + self.annotate(statement, StatementAnnotation::value(ptr_name)); } _ => {} } diff --git a/src/resolver/tests/resolve_expressions_tests.rs b/src/resolver/tests/resolve_expressions_tests.rs index 13081471f9..8408d5a98a 100644 --- a/src/resolver/tests/resolve_expressions_tests.rs +++ b/src/resolver/tests/resolve_expressions_tests.rs @@ -5957,3 +5957,22 @@ fn global_namespace_operator_is_not_resolved() { assert_eq!(annotations.get(node), None); } + +#[test] +fn is_this_there() { + let id_provider = IdProvider::default(); + let (unit, mut index) = index_with_ids( + " + FUNCTION_BLOCK fb + VAR + myvar : INT; + END_VAR + this^.myvar := 8; + END_FUNCTION_BLOCK + ", + id_provider.clone(), + ); + + // let annotations = annotate_with_ids(&unit, &mut index, id_provider); + assert!(index.find_type("__THIS_fb").is_some()); +} diff --git a/src/validation/statement.rs b/src/validation/statement.rs index 8e656049cc..c4f37a53a2 100644 --- a/src/validation/statement.rs +++ b/src/validation/statement.rs @@ -913,8 +913,6 @@ pub fn validate_pointer_assignment( ) where T: AnnotationMap, { - // TODO: #THIS fix validation - return; let type_info_lhs = context.index.get_intrinsic_type_information( context.index.find_elementary_pointer_type(type_lhs.get_type_information()), ); @@ -1297,8 +1295,6 @@ fn is_invalid_pointer_assignment( location: &SourceLocation, validator: &mut Validator, ) -> bool { - // TODO: #THIS fix validation - return false; if left_type.is_pointer() & right_type.is_pointer() { return !typesystem::is_same_type_class(left_type, right_type, index); } From f3c58a28ff6c43c6aa476836ea432127a05151bd Mon Sep 17 00:00:00 2001 From: abroooo <61380142+abroooo@users.noreply.github.com> Date: Fri, 11 Apr 2025 00:04:43 +0200 Subject: [PATCH 21/48] add resolve test --- src/resolver/tests/resolve_expressions_tests.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/resolver/tests/resolve_expressions_tests.rs b/src/resolver/tests/resolve_expressions_tests.rs index 8408d5a98a..038a3caeb6 100644 --- a/src/resolver/tests/resolve_expressions_tests.rs +++ b/src/resolver/tests/resolve_expressions_tests.rs @@ -5968,11 +5968,22 @@ fn is_this_there() { myvar : INT; END_VAR this^.myvar := 8; + myvar := this^.myvar; END_FUNCTION_BLOCK ", id_provider.clone(), ); - // let annotations = annotate_with_ids(&unit, &mut index, id_provider); + let annotations = annotate_with_ids(&unit, &mut index, id_provider); + let AstStatement::Assignment(statement_1) = unit.implementations[0].statements[0].get_stmt() else { + unreachable!() + }; + let AstStatement::Assignment(statement_2) = unit.implementations[0].statements[1].get_stmt() else { + unreachable!() + }; assert!(index.find_type("__THIS_fb").is_some()); + assert_type_and_hint!(&annotations, &index, &statement_1.left, "INT", None); + assert_type_and_hint!(&annotations, &index, &statement_2.left, "INT", None); + assert_type_and_hint!(&annotations, &index, &statement_2.right, "INT", Some("INT")); + // let AstStatement::CallStatement(data) = stmt.get_stmt() else { unreachable!() }; } From 3dcb5825b80015f86088943bc7c1c8fd1eb75cac Mon Sep 17 00:00:00 2001 From: abroooo <61380142+abroooo@users.noreply.github.com> Date: Fri, 11 Apr 2025 14:32:01 +0200 Subject: [PATCH 22/48] fix resolver and work on tests --- .../generators/expression_generator.rs | 5 +- src/codegen/generators/pou_generator.rs | 4 +- src/resolver.rs | 2 +- .../tests/resolve_expressions_tests.rs | 6 +- .../tests/this_keyword_validation_tests.rs | 89 +++++++++++++++---- 5 files changed, 80 insertions(+), 26 deletions(-) diff --git a/src/codegen/generators/expression_generator.rs b/src/codegen/generators/expression_generator.rs index 883ed8e7d9..bc1395b5b8 100644 --- a/src/codegen/generators/expression_generator.rs +++ b/src/codegen/generators/expression_generator.rs @@ -223,7 +223,10 @@ impl<'ink, 'b> ExpressionCodeGenerator<'ink, 'b> { let function_context = self.function_context.ok_or_else(|| { Diagnostic::codegen_error("Cannot use 'this' without context", expression) })?; - let this_name = &format!("{}.this", self.annotations.get_call_name(expression).unwrap()); + let this_name = self.annotations.get_call_name(expression).unwrap(); + let Some(this_name) = self.annotations.get_call_name(expression) else { + todo!("error handling") + }; let this_value = self.llvm_index.find_loaded_associated_variable_value(&this_name).ok_or_else(|| { let message = format!("Cannot find '{}' in associated variable values", this_name); diff --git a/src/codegen/generators/pou_generator.rs b/src/codegen/generators/pou_generator.rs index 2fbdbcced5..c279d50830 100644 --- a/src/codegen/generators/pou_generator.rs +++ b/src/codegen/generators/pou_generator.rs @@ -705,12 +705,12 @@ impl<'ink, 'cg> PouGenerator<'ink, 'cg> { { let alloca = self.llvm.builder.build_alloca(param_pointer.get_type(), "this"); self.llvm.builder.build_store(alloca, param_pointer); - index.associate_loaded_local_variable(type_name, "this", alloca)?; + index.associate_loaded_local_variable(type_name, "__THIS", alloca)?; } if function_context.linking_context.get_implementation_type() == &ImplementationType::FunctionBlock { let alloca = self.llvm.builder.build_alloca(param_pointer.get_type(), "this"); self.llvm.builder.build_store(alloca, param_pointer); - index.associate_loaded_local_variable(type_name, "this", alloca)?; + index.associate_loaded_local_variable(type_name, "__THIS", alloca)?; } //Generate reference to parameter diff --git a/src/resolver.rs b/src/resolver.rs index d55c7bea5e..7932137c55 100644 --- a/src/resolver.rs +++ b/src/resolver.rs @@ -1652,7 +1652,7 @@ impl<'i> TypeAnnotator<'i> { // TODO: #THIS for method check if parent is of type functionblock PouIndexEntry::FunctionBlock { name, .. } | PouIndexEntry::Method { parent_name: name, .. } => { - let ptr_name = format!("__THIS_{}", name); + let ptr_name = format!("{}.__THIS", name); if self .index .find_type(&ptr_name) diff --git a/src/resolver/tests/resolve_expressions_tests.rs b/src/resolver/tests/resolve_expressions_tests.rs index 038a3caeb6..be369d076b 100644 --- a/src/resolver/tests/resolve_expressions_tests.rs +++ b/src/resolver/tests/resolve_expressions_tests.rs @@ -5967,8 +5967,8 @@ fn is_this_there() { VAR myvar : INT; END_VAR - this^.myvar := 8; - myvar := this^.myvar; + this^.myvar := 8; + myvar := this^.myvar; END_FUNCTION_BLOCK ", id_provider.clone(), @@ -5983,7 +5983,5 @@ fn is_this_there() { }; assert!(index.find_type("__THIS_fb").is_some()); assert_type_and_hint!(&annotations, &index, &statement_1.left, "INT", None); - assert_type_and_hint!(&annotations, &index, &statement_2.left, "INT", None); assert_type_and_hint!(&annotations, &index, &statement_2.right, "INT", Some("INT")); - // let AstStatement::CallStatement(data) = stmt.get_stmt() else { unreachable!() }; } diff --git a/src/validation/tests/this_keyword_validation_tests.rs b/src/validation/tests/this_keyword_validation_tests.rs index 2eb887d11b..fb8f577e37 100644 --- a/src/validation/tests/this_keyword_validation_tests.rs +++ b/src/validation/tests/this_keyword_validation_tests.rs @@ -5,23 +5,24 @@ use test_utils::parse_and_validate_buffered; fn pointer_arithmetic_with_this() { let diagnostics = parse_and_validate_buffered( r#" - FUNCTION_BLOCK parent - VAR - x : LINT := 10; - y : LINT := 20; - END_VAR - END_FUNCTION_BLOCK + FUNCTION_BLOCK parent + VAR + x : LINT := 10; + y : LINT := 20; + END_VAR + END_FUNCTION_BLOCK - FUNCTION_BLOCK child EXTENDS parent - VAR - a : INT; - END_VAR - // Pointer arithmetic with SUPER - a := (THIS + 1)^ + 5; - END_FUNCTION_BLOCK + FUNCTION_BLOCK child EXTENDS parent + VAR + a : INT; + END_VAR + // Pointer arithmetic with SUPER + a := (THIS + 1)^ + 5; + END_FUNCTION_BLOCK "#, ); assert_snapshot!(diagnostics, @r#""#); + todo!(); } #[test] @@ -39,7 +40,7 @@ fn cant_chain_this() { "#, ); assert_snapshot!(diagnostics, @r#""#); - panic!("This test should not work"); + panic!("Chaining this is not allowed"); } #[test] @@ -62,6 +63,7 @@ fn this_in_method_call_chain() { "#, ); assert_snapshot!(diagnostics, @r#""#); + todo!(); } #[test] @@ -72,12 +74,13 @@ fn this_not_allowed_in_program() { VAR x : INT := 5; END_VAR - x := THIS.x; + x := THIS^.x; END_PROGRAM "#, ); assert_snapshot!(diagnostics, @r###" "###); + todo!(); } #[test] @@ -94,6 +97,7 @@ fn this_in_function_is_not_allowed() { ); assert_snapshot!(diagnostics, @r###" "###); + todo!(); } #[test] @@ -110,6 +114,7 @@ fn cant_assign_to_this() { "#, ); assert_snapshot!(diagnostics, @r#""#); + todo!(); } #[test] @@ -128,6 +133,7 @@ fn basic_use() { "#, ); assert_snapshot!(diagnostics, @r#""#); + todo!(); } #[test] @@ -161,6 +167,7 @@ fn pass_this_to_method() { "#, ); assert_snapshot!(diagnostics, @r#""#); + todo!(); } #[test] @@ -184,6 +191,7 @@ fn simple_shadowing() { "#, ); assert_snapshot!(diagnostics, @r#""#); + todo!(); } #[test] @@ -209,14 +217,15 @@ fn nested_fbs_and_this_passing() { FUNCTION_BLOCK InnerFB METHOD UseOuter VAR_INPUT - ref : OuterFB; + ref : REF_TO OuterFB; END_VAR - ref.DoSomething(); + ref^.DoSomething(); END_METHOD END_FUNCTION_BLOCK "#, ); assert_snapshot!(diagnostics, @r#""#); + todo!(); } #[test] @@ -240,6 +249,7 @@ fn this_as_method_argument() { "#, ); assert_snapshot!(diagnostics, @r#""#); + todo!(); } #[test] @@ -260,6 +270,7 @@ fn this_in_recursive_method() { "#, ); assert_snapshot!(diagnostics, @r#""#); + todo!(); } #[test] @@ -273,6 +284,7 @@ fn this_is_read_only() { test : FB_Test; END_VAR this := ADR(test); // this is not allowed + this^ := test; END_FUNCTION_BLOCK "#, ); @@ -294,6 +306,7 @@ fn this_chained_with_super() { "#, ); assert_snapshot!(diagnostics, @r#""#); + todo!(); } #[test] @@ -314,6 +327,35 @@ fn this_in_properties() { "#, ); assert_snapshot!(diagnostics, @r#""#); + todo!(); +} + +#[test] +fn this_calling_function_and_passing_this() { + let diagnostics = parse_and_validate_buffered( + r#" + FUNCTION_BLOCK FB_Test + VAR + x : INT; + END_VAR + METHOD return_x : INT + VAR_INPUT + fb_from_foo : FB_Test + END_VAR + return_x := fb_from_foo^.this^.x; + END_METHOD + foo(this); + END_FUNCTION_BLOCK + FUNCTION foo : INT + VAR + pfb: REF_TO FB_TEST + END_VAR + foo := pfb^.return_x(pfb); + END_FUNCTION + "#, + ); + assert_snapshot!(diagnostics, @r#""#); + todo!(); } #[test] @@ -338,10 +380,11 @@ fn this_in_property_calling_method() { "#, ); assert_snapshot!(diagnostics, @r#""#); + todo!(); } #[test] -fn this_with_adr_pointer() { +fn this_with_self_pointer() { let diagnostics = parse_and_validate_buffered( r#" FUNCTION_BLOCK FB_Test @@ -351,13 +394,23 @@ fn this_with_adr_pointer() { METHOD InitRef refToSelf := ADR(THIS^); + refToSelf := REF(THIS^); END_METHOD END_FUNCTION_BLOCK "#, ); assert_snapshot!(diagnostics, @r#""#); + todo!(); } +// TODO: test with incompatible types (refToSelf gets assigned something of different type) +// TODO: global namespaces operator tests +// TODO: .this^ tests +// TODO: codegen tests +// TODO: lit tests +// TODO: resolver tests (parenthesized expressions, nested binary expressions ...) +// TODO: this in variable initializers + #[test] fn dummy() { let diagnostics = parse_and_validate_buffered( From 696840538ecffaaa982710c694bcd775687e0342 Mon Sep 17 00:00:00 2001 From: abroooo <61380142+abroooo@users.noreply.github.com> Date: Fri, 11 Apr 2025 14:32:13 +0200 Subject: [PATCH 23/48] start validation --- src/validation/statement.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/validation/statement.rs b/src/validation/statement.rs index c4f37a53a2..2a6c19a6c9 100644 --- a/src/validation/statement.rs +++ b/src/validation/statement.rs @@ -151,6 +151,20 @@ pub fn visit_statement( .with_location(statement.get_location()).with_error_code("E119")); } } + AstStatement::This => { + dbg!(statement); + // TODO: check if I have an annotation. Only this in fb or method has annoation. If I + // dont have an annotation then something is wrong. + if context.annotations.get_type(statement, context.index).is_none() { + //error + validator.push_diagnostic( + Diagnostic::new("THIS is only allowed in fb and method") + // .with_error_code("E069") + .with_location(statement), + ); + } + // todo!() + } _ => {} } validate_type_nature(validator, statement, context); From 821ef071a3d3b6a7a769998b43e558b72a775499 Mon Sep 17 00:00:00 2001 From: abroooo <61380142+abroooo@users.noreply.github.com> Date: Fri, 11 Apr 2025 23:57:13 +0200 Subject: [PATCH 24/48] validation --- .../src/diagnostics/diagnostics_registry.rs | 2 +- src/resolver/tests/resolve_expressions_tests.rs | 10 +++++++++- src/validation/statement.rs | 2 +- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/compiler/plc_diagnostics/src/diagnostics/diagnostics_registry.rs b/compiler/plc_diagnostics/src/diagnostics/diagnostics_registry.rs index 7f4ce6cd09..521a139afb 100644 --- a/compiler/plc_diagnostics/src/diagnostics/diagnostics_registry.rs +++ b/compiler/plc_diagnostics/src/diagnostics/diagnostics_registry.rs @@ -221,7 +221,7 @@ lazy_static! { E117, Error, include_str!("./error_codes/E117.md"), // Property with invalid number of GET and/or SET blocks E118, Info, include_str!("./error_codes/E118.md"), // Follow-up error to 112 E119, Error, include_str!("./error_codes/E119.md"), // Invalid use of `SUPER` keyword - E120, Info, include_str!("./error_codes/E120.md"), // Invalid use of `THIS` keyword + E120, Error, include_str!("./error_codes/E120.md"), // Invalid use of `THIS` keyword E121, Info, include_str!("./error_codes/E121.md"), // `THIS` is read-only ); } diff --git a/src/resolver/tests/resolve_expressions_tests.rs b/src/resolver/tests/resolve_expressions_tests.rs index be369d076b..87e9794248 100644 --- a/src/resolver/tests/resolve_expressions_tests.rs +++ b/src/resolver/tests/resolve_expressions_tests.rs @@ -5981,7 +5981,15 @@ fn is_this_there() { let AstStatement::Assignment(statement_2) = unit.implementations[0].statements[1].get_stmt() else { unreachable!() }; - assert!(index.find_type("__THIS_fb").is_some()); + assert!(index.find_type("fb.__THIS").is_some()); assert_type_and_hint!(&annotations, &index, &statement_1.left, "INT", None); assert_type_and_hint!(&annotations, &index, &statement_2.right, "INT", Some("INT")); + let AstStatement::ReferenceExpr(ReferenceExpr { base: Some(deref), .. }) = statement_1.left.get_stmt() + else { + unreachable!(); + }; + let AstStatement::ReferenceExpr(ReferenceExpr { base: Some(this), .. }) = deref.get_stmt() else { + unreachable!(); + }; + assert_type_and_hint!(&annotations, &index, this, "fb.__THIS", None); } diff --git a/src/validation/statement.rs b/src/validation/statement.rs index 2a6c19a6c9..981918cbcc 100644 --- a/src/validation/statement.rs +++ b/src/validation/statement.rs @@ -159,7 +159,7 @@ pub fn visit_statement( //error validator.push_diagnostic( Diagnostic::new("THIS is only allowed in fb and method") - // .with_error_code("E069") + .with_error_code("E120") .with_location(statement), ); } From d7c1aa0cfffc1cb931918bc6c4a6d7a279ff963c Mon Sep 17 00:00:00 2001 From: abroooo <61380142+abroooo@users.noreply.github.com> Date: Mon, 14 Apr 2025 14:37:13 +0200 Subject: [PATCH 25/48] this only is only allowed in methods of fbs --- src/resolver.rs | 75 ++++++++++------- .../tests/resolve_expressions_tests.rs | 82 +++++++++++++++++++ 2 files changed, 127 insertions(+), 30 deletions(-) diff --git a/src/resolver.rs b/src/resolver.rs index 7932137c55..134033f2c8 100644 --- a/src/resolver.rs +++ b/src/resolver.rs @@ -1649,31 +1649,38 @@ impl<'i> TypeAnnotator<'i> { AstStatement::This => { if let Some(pou) = ctx.pou.and_then(|name| self.index.find_pou(name)) { match pou { - // TODO: #THIS for method check if parent is of type functionblock + // TODO: #THIS for functionblock and for method if parent is of type functionblock PouIndexEntry::FunctionBlock { name, .. } | PouIndexEntry::Method { parent_name: name, .. } => { - let ptr_name = format!("{}.__THIS", name); - if self - .index - .find_type(&ptr_name) - .or_else(|| self.annotation_map.new_index.find_type(&ptr_name)) - .is_none() - { - let information = DataTypeInformation::Pointer { - name: ptr_name.clone(), - inner_type_name: name.to_string(), - auto_deref: None, - }; - let dt = crate::typesystem::DataType { - name: ptr_name.clone(), - initial_value: None, - information, - nature: TypeNature::Any, - location: SourceLocation::internal(), - }; - self.annotation_map.new_index.register_type(dt); + if let Some(parent_name) = self.index.find_pou(name) { + match parent_name { + PouIndexEntry::FunctionBlock { .. } | PouIndexEntry::Method { .. } => { + let ptr_name = format!("{}.__THIS", name); + if self + .index + .find_type(&ptr_name) + .or_else(|| self.annotation_map.new_index.find_type(&ptr_name)) + .is_none() + { + let information = DataTypeInformation::Pointer { + name: ptr_name.clone(), + inner_type_name: name.to_string(), + auto_deref: None, + }; + let dt = crate::typesystem::DataType { + name: ptr_name.clone(), + initial_value: None, + information, + nature: TypeNature::Any, + location: SourceLocation::internal(), + }; + self.annotation_map.new_index.register_type(dt); + } + self.annotate(statement, StatementAnnotation::value(ptr_name)); + } + _ => {} + } } - self.annotate(statement, StatementAnnotation::value(ptr_name)); } _ => {} } @@ -2023,14 +2030,22 @@ impl<'i> TypeAnnotator<'i> { // Only `THIS` in FunctionBlock/methods context // TODO: also support methods if let Some(pou) = ctx.pou.and_then(|name| self.index.find_pou(name)) { - if let PouIndexEntry::FunctionBlock { name, .. } | PouIndexEntry::Method { name, .. } = - pou - { - // TODO: check if ptr already exists and return exisiting one - // check if ptr can be added to index during indexing - Some(StatementAnnotation::value(name)) - } else { - None + match pou { + // TODO: #THIS for functionblock and for method if parent is of type functionblock + PouIndexEntry::FunctionBlock { name, .. } + | PouIndexEntry::Method { parent_name: name, .. } => { + if let Some(parent_name) = self.index.find_pou(name) { + match parent_name { + PouIndexEntry::FunctionBlock { .. } | PouIndexEntry::Method { .. } => { + Some(StatementAnnotation::value(name)) + } + _ => None, + } + } else { + None + } + } + _ => None, } } else { None diff --git a/src/resolver/tests/resolve_expressions_tests.rs b/src/resolver/tests/resolve_expressions_tests.rs index 87e9794248..41cac7f323 100644 --- a/src/resolver/tests/resolve_expressions_tests.rs +++ b/src/resolver/tests/resolve_expressions_tests.rs @@ -5993,3 +5993,85 @@ fn is_this_there() { }; assert_type_and_hint!(&annotations, &index, this, "fb.__THIS", None); } + +#[test] +fn this_should_not_be_in_programs() { + let id_provider = IdProvider::default(); + let (unit, mut index) = index_with_ids( + " + PROGRAM myProg + VAR + myvar : INT; + END_VAR + this^.myvar := 8; + myvar := this^.myvar; + END_PROGRAM + ", + id_provider.clone(), + ); + let annotations = annotate_with_ids(&unit, &mut index, id_provider); + let AstStatement::Assignment(statement_1) = unit.implementations[0].statements[0].get_stmt() else { + unreachable!() + }; + let AstStatement::Assignment(statement_2) = unit.implementations[0].statements[1].get_stmt() else { + unreachable!() + }; + assert!(index.find_type("fb.__THIS").is_none()); + // TODO: #THIS: should we still have the annotations? + assert_type_and_hint!(&annotations, &index, &statement_1.left, "INT", None); + assert_type_and_hint!(&annotations, &index, &statement_2.right, "INT", Some("INT")); + let AstStatement::ReferenceExpr(ReferenceExpr { base: Some(deref), .. }) = statement_1.left.get_stmt() + else { + unreachable!(); + }; + let AstStatement::ReferenceExpr(ReferenceExpr { base: Some(this), .. }) = deref.get_stmt() else { + unreachable!(); + }; + assert_type_and_hint!(&annotations, &index, this, "fb.__THIS", None); +} + +#[test] +fn is_this_in_methods_of_function_blocks() { + let id_provider = IdProvider::default(); + let (unit, mut index) = index_with_ids( + " + FUNCTION_BLOCK fb + VAR + myvar : INT; + END_VAR + METHOD foo : INT + VAR + myvar : INT; + END_VAR + this^.myvar := 8; + myvar := this^.myvar; + END_METHOD + myvar := this^.myvar; + foo(); + END_FUNCTION_BLOCK + ", + id_provider.clone(), + ); + + let annotations = annotate_with_ids(&unit, &mut index, id_provider); + dbg!(&unit); + let AstStatement::Assignment(statement_1) = unit.implementations[0].statements[0].get_stmt() else { + unreachable!() + }; + let AstStatement::Assignment(statement_2) = unit.implementations[0].statements[1].get_stmt() else { + unreachable!() + }; + dbg!(statement_2); + assert!(index.find_type("fb.__THIS").is_some()); + assert_type_and_hint!(&annotations, &index, &statement_1.left, "INT", None); + assert_type_and_hint!(&annotations, &index, &statement_2.right, "INT", Some("INT")); + let AstStatement::ReferenceExpr(ReferenceExpr { base: Some(deref), .. }) = statement_1.left.get_stmt() + else { + unreachable!(); + }; + let AstStatement::ReferenceExpr(ReferenceExpr { base: Some(this), .. }) = deref.get_stmt() else { + unreachable!(); + }; + assert_type_and_hint!(&annotations, &index, this, "fb.__THIS", None); + assert_eq!(1, 2); +} From 81145c12d9bf6df3f0c0bf3915b57c5cfdc149b3 Mon Sep 17 00:00:00 2001 From: abroooo <61380142+abroooo@users.noreply.github.com> Date: Mon, 14 Apr 2025 22:14:21 +0200 Subject: [PATCH 26/48] refactor and more resolver tests --- src/resolver.rs | 90 ++++++------------- .../tests/resolve_expressions_tests.rs | 76 ++++++++++++++-- 2 files changed, 96 insertions(+), 70 deletions(-) diff --git a/src/resolver.rs b/src/resolver.rs index 134033f2c8..04e468b5e7 100644 --- a/src/resolver.rs +++ b/src/resolver.rs @@ -1647,44 +1647,37 @@ impl<'i> TypeAnnotator<'i> { fn visit_statement_expression(&mut self, ctx: &VisitorContext, statement: &AstNode) { match statement.get_stmt() { AstStatement::This => { - if let Some(pou) = ctx.pou.and_then(|name| self.index.find_pou(name)) { - match pou { - // TODO: #THIS for functionblock and for method if parent is of type functionblock - PouIndexEntry::FunctionBlock { name, .. } - | PouIndexEntry::Method { parent_name: name, .. } => { - if let Some(parent_name) = self.index.find_pou(name) { - match parent_name { - PouIndexEntry::FunctionBlock { .. } | PouIndexEntry::Method { .. } => { - let ptr_name = format!("{}.__THIS", name); - if self - .index - .find_type(&ptr_name) - .or_else(|| self.annotation_map.new_index.find_type(&ptr_name)) - .is_none() - { - let information = DataTypeInformation::Pointer { - name: ptr_name.clone(), - inner_type_name: name.to_string(), - auto_deref: None, - }; - let dt = crate::typesystem::DataType { - name: ptr_name.clone(), - initial_value: None, - information, - nature: TypeNature::Any, - location: SourceLocation::internal(), - }; - self.annotation_map.new_index.register_type(dt); - } - self.annotate(statement, StatementAnnotation::value(ptr_name)); - } - _ => {} - } - } - } - _ => {} + let name = match ctx.pou.and_then(|name| self.index.find_pou(name)) { + Some(PouIndexEntry::FunctionBlock { name, .. }) => name, + Some(PouIndexEntry::Method { parent_name: name, .. }) + if self.index.find_pou(&name).is_some_and(|it| it.is_function_block()) => + { + name } + _ => return, + }; + let ptr_name = format!("{}.__THIS", name); + if self + .index + .find_type(&ptr_name) + .or_else(|| self.annotation_map.new_index.find_type(&ptr_name)) + .is_none() + { + let information = DataTypeInformation::Pointer { + name: ptr_name.clone(), + inner_type_name: name.to_string(), + auto_deref: None, + }; + let dt = crate::typesystem::DataType { + name: ptr_name.clone(), + initial_value: None, + information, + nature: TypeNature::Any, + location: SourceLocation::internal(), + }; + self.annotation_map.new_index.register_type(dt); } + self.annotate(statement, StatementAnnotation::value(ptr_name)); } AstStatement::DirectAccess(data, ..) => { let ctx = VisitorContext { qualifier: None, ..ctx.clone() }; @@ -2026,31 +2019,6 @@ impl<'i> TypeAnnotator<'i> { ctx: &VisitorContext<'_>, ) -> Option { match reference.get_stmt() { - AstStatement::This => { - // Only `THIS` in FunctionBlock/methods context - // TODO: also support methods - if let Some(pou) = ctx.pou.and_then(|name| self.index.find_pou(name)) { - match pou { - // TODO: #THIS for functionblock and for method if parent is of type functionblock - PouIndexEntry::FunctionBlock { name, .. } - | PouIndexEntry::Method { parent_name: name, .. } => { - if let Some(parent_name) = self.index.find_pou(name) { - match parent_name { - PouIndexEntry::FunctionBlock { .. } | PouIndexEntry::Method { .. } => { - Some(StatementAnnotation::value(name)) - } - _ => None, - } - } else { - None - } - } - _ => None, - } - } else { - None - } - } AstStatement::Identifier(name, ..) => ctx .resolve_strategy .iter() diff --git a/src/resolver/tests/resolve_expressions_tests.rs b/src/resolver/tests/resolve_expressions_tests.rs index 41cac7f323..8a8e0754b9 100644 --- a/src/resolver/tests/resolve_expressions_tests.rs +++ b/src/resolver/tests/resolve_expressions_tests.rs @@ -1,4 +1,5 @@ use core::panic; +use std::os::linux::raw::stat; use insta::{assert_debug_snapshot, assert_snapshot}; use plc_ast::{ @@ -6004,7 +6005,6 @@ fn this_should_not_be_in_programs() { myvar : INT; END_VAR this^.myvar := 8; - myvar := this^.myvar; END_PROGRAM ", id_provider.clone(), @@ -6013,13 +6013,10 @@ fn this_should_not_be_in_programs() { let AstStatement::Assignment(statement_1) = unit.implementations[0].statements[0].get_stmt() else { unreachable!() }; - let AstStatement::Assignment(statement_2) = unit.implementations[0].statements[1].get_stmt() else { - unreachable!() - }; - assert!(index.find_type("fb.__THIS").is_none()); + assert!(index.find_type("myProg.__THIS").is_none()); + // TODO: #THIS: should we still have the annotations? - assert_type_and_hint!(&annotations, &index, &statement_1.left, "INT", None); - assert_type_and_hint!(&annotations, &index, &statement_2.right, "INT", Some("INT")); + // Yes, this is the correct since it's the AST which is the result of the parser. let AstStatement::ReferenceExpr(ReferenceExpr { base: Some(deref), .. }) = statement_1.left.get_stmt() else { unreachable!(); @@ -6027,7 +6024,7 @@ fn this_should_not_be_in_programs() { let AstStatement::ReferenceExpr(ReferenceExpr { base: Some(this), .. }) = deref.get_stmt() else { unreachable!(); }; - assert_type_and_hint!(&annotations, &index, this, "fb.__THIS", None); + assert!(annotations.get(this).is_none()); } #[test] @@ -6073,5 +6070,66 @@ fn is_this_in_methods_of_function_blocks() { unreachable!(); }; assert_type_and_hint!(&annotations, &index, this, "fb.__THIS", None); - assert_eq!(1, 2); + // assert_eq!(1, 2); +} + +#[test] +fn just_this() { + let id_provider = IdProvider::default(); + let (unit, mut index) = index_with_ids( + " + FUNCTION_BLOCK fb + this; + END_FUNCTION_BLOCK + ", + id_provider.clone(), + ); + + let annotations = annotate_with_ids(&unit, &mut index, id_provider); + let statement = &unit.implementations[0].statements[0]; + assert!(index.find_type("fb.__THIS").is_some()); + assert_type_and_hint!(&annotations, &index, statement, "fb.__THIS", None); +} + +#[test] +fn this_assignment() { + let id_provider = IdProvider::default(); + let (unit, mut index) = index_with_ids( + " + FUNCTION_BLOCK fb + VAR + x : REF_TO fb; + END_VAR + x := this; + END_FUNCTION_BLOCK + ", + id_provider.clone(), + ); + + let annotations = annotate_with_ids(&unit, &mut index, id_provider); + let statement = &unit.implementations[0].statements[0]; + dbg!(&statement); + assert!(index.find_type("fb.__THIS").is_some()); + assert_type_and_hint!(&annotations, &index, statement, "fb.__THIS", None); +} + +#[test] +fn this_call() { + let id_provider = IdProvider::default(); + let (unit, mut index) = index_with_ids( + " + FUNCTION_BLOCK fb + VAR + END_VAR + this^(); + END_FUNCTION_BLOCK + ", + id_provider.clone(), + ); + + let annotations = annotate_with_ids(&unit, &mut index, id_provider); + let statement = &unit.implementations[0].statements[0]; + dbg!(&statement); + assert!(index.find_type("fb.__THIS").is_some()); + assert_type_and_hint!(&annotations, &index, statement, "fb.__THIS", None); } From 46e30da848a925962385ecc2058a817eb386338c Mon Sep 17 00:00:00 2001 From: abroooo <61380142+abroooo@users.noreply.github.com> Date: Tue, 15 Apr 2025 11:49:08 +0200 Subject: [PATCH 27/48] more resolver tests --- .../tests/resolve_expressions_tests.rs | 108 +++++++++++++++++- 1 file changed, 106 insertions(+), 2 deletions(-) diff --git a/src/resolver/tests/resolve_expressions_tests.rs b/src/resolver/tests/resolve_expressions_tests.rs index 8a8e0754b9..9047009f96 100644 --- a/src/resolver/tests/resolve_expressions_tests.rs +++ b/src/resolver/tests/resolve_expressions_tests.rs @@ -5960,7 +5960,45 @@ fn global_namespace_operator_is_not_resolved() { } #[test] -fn is_this_there() { +fn this_in_assignments_in_methods() { + let id_provider = IdProvider::default(); + let (unit, mut index) = index_with_ids( + " + FUNCTION_BLOCK fb + VAR + myvar : INT; + END_VAR + METHOD foo : INT + this^.myvar := 8; + myvar := this^.myvar; + END_FUNCTION_BLOCK + END_FUNCTION_BLOCK + ", + id_provider.clone(), + ); + + let annotations = annotate_with_ids(&unit, &mut index, id_provider); + let AstStatement::Assignment(statement_1) = unit.implementations[0].statements[0].get_stmt() else { + unreachable!() + }; + let AstStatement::Assignment(statement_2) = unit.implementations[0].statements[1].get_stmt() else { + unreachable!() + }; + assert!(index.find_type("fb.__THIS").is_some()); + assert_type_and_hint!(&annotations, &index, &statement_1.left, "INT", None); + assert_type_and_hint!(&annotations, &index, &statement_2.right, "INT", Some("INT")); + let AstStatement::ReferenceExpr(ReferenceExpr { base: Some(deref), .. }) = statement_1.left.get_stmt() + else { + unreachable!(); + }; + let AstStatement::ReferenceExpr(ReferenceExpr { base: Some(this), .. }) = deref.get_stmt() else { + unreachable!(); + }; + assert_type_and_hint!(&annotations, &index, this, "fb.__THIS", None); +} + +#[test] +fn this_in_assignments() { let id_provider = IdProvider::default(); let (unit, mut index) = index_with_ids( " @@ -6131,5 +6169,71 @@ fn this_call() { let statement = &unit.implementations[0].statements[0]; dbg!(&statement); assert!(index.find_type("fb.__THIS").is_some()); - assert_type_and_hint!(&annotations, &index, statement, "fb.__THIS", None); + // assert_type_and_hint!(&annotations, &index, statement, "fb.__THIS", None); +} + +#[test] +fn this_as_function_parameter() { + let id_provider = IdProvider::default(); + let (unit, mut index) = index_with_ids( + " + FUNCTION_BLOCK FB_Test + foo2(this); + END_FUNCTION_BLOCK + ", + id_provider.clone(), + ); + + let annotations = annotate_with_ids(&unit, &mut index, id_provider); + let statement = &unit.implementations[0].statements[0]; + let AstStatement::CallStatement(CallStatement { parameters: Some(param), .. }) = statement.get_stmt() + else { + unreachable!(); + }; + let Some(StatementAnnotation::Value { resulting_type, .. }) = annotations.get(param) else { + unreachable!() + }; + assert_eq!(resulting_type, "FB_Test.__THIS"); + assert!(index.find_type("fb_test.__THIS").is_some()); +} + +#[test] +fn this_in_conditionals() { + let id_provider = IdProvider::default(); + let (unit, mut index) = index_with_ids( + " + FUNCTION_BLOCK FB_Test + VAR + x : INT; + bo: BOOL; + END_VAR + IF this^.bo THEN + x := 1; + END_IF + END_FUNCTION_BLOCK + ", + id_provider.clone(), + ); + + let annotations = annotate_with_ids(&unit, &mut index, id_provider); + let statement = &unit.implementations[0].statements[0]; + let AstStatement::ControlStatement(AstControlStatement::If(IfStatement { blocks, .. })) = + unit.implementations[0].statements[0].get_stmt() + else { + unreachable!(); + }; + let AstStatement::ReferenceExpr(ReferenceExpr { base: Some(deref), .. }) = blocks[0].condition.get_stmt() + else { + unreachable!(); + }; + let AstStatement::ReferenceExpr(ReferenceExpr { base: Some(base), .. }) = deref.get_stmt() else { + unreachable!() + }; + let StatementAnnotation::Value { resulting_type, .. } = annotations.get(base).expect("damn") else { + unreachable!() + }; + assert_eq!(resulting_type, "FB_Test.__THIS"); + dbg!(resulting_type); + // dbg!(&statement); + assert!(index.find_type("fb_test.__THIS").is_some()); } From 7d1ab662f1cf6e627fdb5fea9490565b6b104860 Mon Sep 17 00:00:00 2001 From: abroooo <61380142+abroooo@users.noreply.github.com> Date: Thu, 17 Apr 2025 08:47:53 +0200 Subject: [PATCH 28/48] validation and tests --- compiler/plc_ast/src/ast.rs | 3 + .../tests/resolve_expressions_tests.rs | 4 - src/validation/statement.rs | 11 ++- .../tests/this_keyword_validation_tests.rs | 91 ++++++++++++++----- 4 files changed, 79 insertions(+), 30 deletions(-) diff --git a/compiler/plc_ast/src/ast.rs b/compiler/plc_ast/src/ast.rs index 61a57efdc7..cfe9a51aa5 100644 --- a/compiler/plc_ast/src/ast.rs +++ b/compiler/plc_ast/src/ast.rs @@ -1288,6 +1288,9 @@ impl AstNode { if self.has_super_metadata() { return false; } + if self.is_this() { + return false; + } self.has_direct_access() || self.is_flat_reference() || self.is_reference() diff --git a/src/resolver/tests/resolve_expressions_tests.rs b/src/resolver/tests/resolve_expressions_tests.rs index 9047009f96..6fe98d8058 100644 --- a/src/resolver/tests/resolve_expressions_tests.rs +++ b/src/resolver/tests/resolve_expressions_tests.rs @@ -1,5 +1,4 @@ use core::panic; -use std::os::linux::raw::stat; use insta::{assert_debug_snapshot, assert_snapshot}; use plc_ast::{ @@ -6169,7 +6168,6 @@ fn this_call() { let statement = &unit.implementations[0].statements[0]; dbg!(&statement); assert!(index.find_type("fb.__THIS").is_some()); - // assert_type_and_hint!(&annotations, &index, statement, "fb.__THIS", None); } #[test] @@ -6233,7 +6231,5 @@ fn this_in_conditionals() { unreachable!() }; assert_eq!(resulting_type, "FB_Test.__THIS"); - dbg!(resulting_type); - // dbg!(&statement); assert!(index.find_type("fb_test.__THIS").is_some()); } diff --git a/src/validation/statement.rs b/src/validation/statement.rs index 981918cbcc..88c1d3b426 100644 --- a/src/validation/statement.rs +++ b/src/validation/statement.rs @@ -158,7 +158,7 @@ pub fn visit_statement( if context.annotations.get_type(statement, context.index).is_none() { //error validator.push_diagnostic( - Diagnostic::new("THIS is only allowed in fb and method") + Diagnostic::new("Invalid use of `THIS`. Usage is only allowed within POU of type `FUNCTION_BLOCK` or type `METHOD`") .with_error_code("E120") .with_location(statement), ); @@ -192,6 +192,14 @@ fn validate_reference_expression( } ReferenceAccess::Member(m) => { if let Some(base) = base { + if m.is_this() { + // this cannot be accessed as a member + validator.push_diagnostic( + Diagnostic::new("`THIS` is not allowed in member-access position.") + .with_location(m.get_location()) + .with_error_code("E120"), + ); + } if m.is_super() || m.has_super_metadata() { // super cannot be accessed as a member validator.push_diagnostic( @@ -1102,6 +1110,7 @@ fn validate_assignment( if !(left_type.is_compatible_with_type(right_type) && is_valid_assignment(left_type, right_type, right, context.index, location, validator)) { + // TODO: #THIS && !left_type.is_this() if left_type.is_pointer() && right_type.is_pointer() { validator.push_diagnostic( Diagnostic::new(format!( diff --git a/src/validation/tests/this_keyword_validation_tests.rs b/src/validation/tests/this_keyword_validation_tests.rs index fb8f577e37..bae5679455 100644 --- a/src/validation/tests/this_keyword_validation_tests.rs +++ b/src/validation/tests/this_keyword_validation_tests.rs @@ -34,17 +34,17 @@ fn cant_chain_this() { x : LINT := 10; y : LINT := 20; END_VAR - this^.x := this^.this^.y; - this^.this^.x := this^.y; + this^.x := this^.this^.this^.y; + this^.this^.this^.x := this^.y; END_FUNCTION_BLOCK "#, ); - assert_snapshot!(diagnostics, @r#""#); - panic!("Chaining this is not allowed"); + dbg!(&diagnostics); + assert_snapshot!(diagnostics, @r""); } #[test] -fn this_in_method_call_chain() { +fn this_in_method_call_chain_is_allowed() { let diagnostics = parse_and_validate_buffered( r#" FUNCTION_BLOCK FB_Test @@ -63,11 +63,10 @@ fn this_in_method_call_chain() { "#, ); assert_snapshot!(diagnostics, @r#""#); - todo!(); } #[test] -fn this_not_allowed_in_program() { +fn this_in_program_is_not_allowed_() { let diagnostics = parse_and_validate_buffered( r#" PROGRAM Main @@ -78,9 +77,13 @@ fn this_not_allowed_in_program() { END_PROGRAM "#, ); - assert_snapshot!(diagnostics, @r###" -"###); - todo!(); + assert_snapshot!(diagnostics, @r" + error[E120]: Invalid use of `THIS`. Usage is only allowed within POU of type `FUNCTION_BLOCK` or type `METHOD` + ┌─ :6:18 + │ + 6 │ x := THIS^.x; + │ ^^^^ Invalid use of `THIS`. Usage is only allowed within POU of type `FUNCTION_BLOCK` or type `METHOD` + "); } #[test] @@ -95,26 +98,63 @@ fn this_in_function_is_not_allowed() { END_FUNCTION "#, ); - assert_snapshot!(diagnostics, @r###" -"###); - todo!(); + assert_snapshot!(diagnostics, @r" + error[E120]: Invalid use of `THIS`. Usage is only allowed within POU of type `FUNCTION_BLOCK` or type `METHOD` + ┌─ :6:25 + │ + 6 │ SomeFunction := THIS^.SomeValue; + │ ^^^^ Invalid use of `THIS`. Usage is only allowed within POU of type `FUNCTION_BLOCK` or type `METHOD` + "); } #[test] -fn cant_assign_to_this() { +fn this_cannot_be_assigned_to() { let diagnostics = parse_and_validate_buffered( r#" FUNCTION_BLOCK parent VAR x : LINT := 10; + p : REF_TO parent; END_VAR this^ := 5; this := REF(x); + this := REF(parent); + this := ADR(parent); // this is not allowed + this^:= parent; END_FUNCTION_BLOCK "#, ); - assert_snapshot!(diagnostics, @r#""#); - todo!(); + assert_snapshot!(diagnostics, @r" + error[E037]: Invalid assignment: cannot assign 'DINT' to 'parent' + ┌─ :7:9 + │ + 7 │ this^ := 5; + │ ^^^^^^^^^^ Invalid assignment: cannot assign 'DINT' to 'parent' + + error[E050]: Expression this is not assignable. + ┌─ :8:9 + │ + 8 │ this := REF(x); + │ ^^^^ Expression this is not assignable. + + warning[E090]: Pointers parent and LINT have different types + ┌─ :8:9 + │ + 8 │ this := REF(x); + │ ^^^^^^^^^^^^^^ Pointers parent and LINT have different types + + error[E050]: Expression this is not assignable. + ┌─ :9:9 + │ + 9 │ this := REF(parent); + │ ^^^^ Expression this is not assignable. + + error[E050]: Expression this is not assignable. + ┌─ :10:9 + │ + 10 │ this := ADR(parent); // this is not allowed + │ ^^^^ Expression this is not assignable. + "); } #[test] @@ -127,17 +167,17 @@ fn basic_use() { END_VAR METHOD GetVal : INT - GetVal := THIS.val; + GetVal := THIS^.val; END_METHOD + val := this^.val; END_FUNCTION_BLOCK "#, ); assert_snapshot!(diagnostics, @r#""#); - todo!(); } #[test] -fn pass_this_to_method() { +fn pass_this_to_method_is_ok() { // pass `this` pointer of FB1 to a method of another fb called FB2 which calls a method of FB1 // and changes a value of the passed `this` pointer let diagnostics = parse_and_validate_buffered( @@ -149,6 +189,7 @@ fn pass_this_to_method() { END_VAR test.method2(THIS); + END_METHOD END_FUNCTION_BLOCK FUNCTION_BLOCK FB_Test2 METHOD method1 @@ -171,7 +212,7 @@ fn pass_this_to_method() { } #[test] -fn simple_shadowing() { +fn shadowing_is_working() { let diagnostics = parse_and_validate_buffered( r#" FUNCTION_BLOCK FB_Test @@ -203,29 +244,29 @@ fn nested_fbs_and_this_passing() { Inner : InnerFB; END_VAR - METHOD CallInner + METHOD CallInner : INT Inner.UseOuter(THIS); END_METHOD - METHOD DoSomething + METHOD doSomething : INT VAR x : INT := 5; END_VAR x := 10; END_METHOD + END_FUNCTION_BLOCK FUNCTION_BLOCK InnerFB - METHOD UseOuter + METHOD UseOuter : INT VAR_INPUT ref : REF_TO OuterFB; END_VAR - ref^.DoSomething(); + ref.doSomething(); END_METHOD END_FUNCTION_BLOCK "#, ); assert_snapshot!(diagnostics, @r#""#); - todo!(); } #[test] From 75290638db8ad401018da73fa9bf2c8126d7f8cf Mon Sep 17 00:00:00 2001 From: abroooo <61380142+abroooo@users.noreply.github.com> Date: Thu, 17 Apr 2025 11:56:47 +0200 Subject: [PATCH 29/48] more validation --- src/validation/statement.rs | 1 + .../tests/this_keyword_validation_tests.rs | 83 ++++++++++++++----- 2 files changed, 62 insertions(+), 22 deletions(-) diff --git a/src/validation/statement.rs b/src/validation/statement.rs index 88c1d3b426..aa7383b760 100644 --- a/src/validation/statement.rs +++ b/src/validation/statement.rs @@ -199,6 +199,7 @@ fn validate_reference_expression( .with_location(m.get_location()) .with_error_code("E120"), ); + return; } if m.is_super() || m.has_super_metadata() { // super cannot be accessed as a member diff --git a/src/validation/tests/this_keyword_validation_tests.rs b/src/validation/tests/this_keyword_validation_tests.rs index bae5679455..3899a8d875 100644 --- a/src/validation/tests/this_keyword_validation_tests.rs +++ b/src/validation/tests/this_keyword_validation_tests.rs @@ -121,6 +121,7 @@ fn this_cannot_be_assigned_to() { this := REF(parent); this := ADR(parent); // this is not allowed this^:= parent; + this := p; END_FUNCTION_BLOCK "#, ); @@ -270,7 +271,7 @@ fn nested_fbs_and_this_passing() { } #[test] -fn this_as_method_argument() { +fn this_as_method_argument_is_ok() { let diagnostics = parse_and_validate_buffered( r#" FUNCTION_BLOCK FB_Test @@ -290,7 +291,6 @@ fn this_as_method_argument() { "#, ); assert_snapshot!(diagnostics, @r#""#); - todo!(); } #[test] @@ -311,7 +311,6 @@ fn this_in_recursive_method() { "#, ); assert_snapshot!(diagnostics, @r#""#); - todo!(); } #[test] @@ -329,11 +328,23 @@ fn this_is_read_only() { END_FUNCTION_BLOCK "#, ); - assert_snapshot!(diagnostics, @r#""#); + assert_snapshot!(diagnostics, @r" + error[E050]: Expression this is not assignable. + ┌─ :8:13 + │ + 8 │ this := ADR(test); // this is not allowed + │ ^^^^ Expression this is not assignable. + + error[E037]: Invalid assignment: cannot assign 'FB_Test' to 'FB_Test2' + ┌─ :9:13 + │ + 9 │ this^ := test; + │ ^^^^^^^^^^^^^ Invalid assignment: cannot assign 'FB_Test' to 'FB_Test2' + "); } #[test] -fn this_chained_with_super() { +fn this_chained_with_super_is_not_ok() { let diagnostics = parse_and_validate_buffered( r#" FUNCTION_BLOCK parent @@ -346,33 +357,45 @@ fn this_chained_with_super() { END_FUNCTION_BLOCK "#, ); - assert_snapshot!(diagnostics, @r#""#); - todo!(); + assert_snapshot!(diagnostics, @r" + error[E119]: `SUPER` is not allowed in member-access position. + ┌─ :8:19 + │ + 8 │ this^.super^.this^.DoSomething(); + │ ^^^^^ `SUPER` is not allowed in member-access position. + + error[E120]: `THIS` is not allowed in member-access position. + ┌─ :8:26 + │ + 8 │ this^.super^.this^.DoSomething(); + │ ^^^^ `THIS` is not allowed in member-access position. + "); } #[test] -fn this_in_properties() { +fn this_in_properties_is_ok() { let diagnostics = parse_and_validate_buffered( r#" FUNCTION_BLOCK FB_Test VAR - prop : INT; + prop_var : INT; END_VAR - PROPERTY GetProp : INT - GetProp := THIS^.prop; - END_PROPERTY - PROPERTY SetProp : INT - SetProp := THIS^.prop; + PROPERTY prop : INT + GET + prop := THIS^.prop_var; + END_GET + SET + THIS^.prop_var := prop; + END_SET END_PROPERTY END_FUNCTION_BLOCK "#, ); assert_snapshot!(diagnostics, @r#""#); - todo!(); } #[test] -fn this_calling_function_and_passing_this() { +fn this_calling_function_and_passing_this_is_ok() { let diagnostics = parse_and_validate_buffered( r#" FUNCTION_BLOCK FB_Test @@ -381,26 +404,25 @@ fn this_calling_function_and_passing_this() { END_VAR METHOD return_x : INT VAR_INPUT - fb_from_foo : FB_Test + fb_from_foo : REF_TO FB_Test; END_VAR - return_x := fb_from_foo^.this^.x; + return_x := fb_from_foo^.x; END_METHOD foo(this); END_FUNCTION_BLOCK FUNCTION foo : INT - VAR - pfb: REF_TO FB_TEST + VAR_INPUT + pfb: REF_TO FB_TEST; END_VAR foo := pfb^.return_x(pfb); END_FUNCTION "#, ); assert_snapshot!(diagnostics, @r#""#); - todo!(); } #[test] -fn this_in_property_calling_method() { +fn this_in_property_calling_method_is_ok() { let diagnostics = parse_and_validate_buffered( r#" FUNCTION_BLOCK FB_Test @@ -444,6 +466,23 @@ fn this_with_self_pointer() { todo!(); } +#[test] +fn this_in_variable_initialization_is_ok() { + let diagnostics = parse_and_validate_buffered( + r#" + FUNCTION_BLOCK FB + VAR CONSTANT + x : INT; + END_VAR + VAR + y : INT := this^.x; + END_VAR + END_FUNCTION_BLOCK + "#, + ); + assert_snapshot!(diagnostics, @r#""#); +} + // TODO: test with incompatible types (refToSelf gets assigned something of different type) // TODO: global namespaces operator tests // TODO: .this^ tests From 7177ca94a6d96b426d9d9580837fbb8ee5bbc7b8 Mon Sep 17 00:00:00 2001 From: abroooo <61380142+abroooo@users.noreply.github.com> Date: Thu, 17 Apr 2025 12:30:07 +0200 Subject: [PATCH 30/48] still more validation --- .../tests/super_keyword_validation_tests.rs | 81 ++++++++++--------- .../tests/this_keyword_validation_tests.rs | 7 +- 2 files changed, 44 insertions(+), 44 deletions(-) diff --git a/src/validation/tests/super_keyword_validation_tests.rs b/src/validation/tests/super_keyword_validation_tests.rs index 3d995fed3b..834fe9c72b 100644 --- a/src/validation/tests/super_keyword_validation_tests.rs +++ b/src/validation/tests/super_keyword_validation_tests.rs @@ -224,6 +224,7 @@ fn super_keyword_is_not_assignable() { END_VAR SUPER^ := super_inst; SUPER := super_ptr; + super^ := 5; (SUPER)^ := super_inst; // FIXME: Immediate deref of `REF` result is not validated and panics in codegen. tracked in #1463 (SUPER) := super_ptr; @@ -353,7 +354,7 @@ fn derefed_super_assigned_to_ptr_is_an_error() { // If this changes to LINT (i.e. 64-bit), the error goes away. // tracked in #1441 VAR - x: DINT; + x: DINT; END_VAR END_FUNCTION_BLOCK @@ -415,7 +416,7 @@ fn super_accessing_private_methods() { END_VAR // I don't think the `PRIVATE` keyword does anything at this time, - // but it can't hurt to have this covered anyway + // but it can't hurt to have this covered anyway METHOD PRIVATE do_something : INT do_something := x * 2; END_METHOD @@ -450,7 +451,7 @@ fn super_with_typed_methods() { process[0] := 100; process[1] := 200; END_METHOD - + METHOD test : INT // Access parent's process method which returns an array test := SUPER^.process()[0] + SUPER^.process()[1]; @@ -471,7 +472,7 @@ fn super_with_mixed_access_patterns() { arr : ARRAY[0..5] OF INT := [1,2,3,4,5,6]; ptr : REF_TO INT; END_VAR - + METHOD get_value : INT get_value := 42; END_METHOD @@ -481,11 +482,11 @@ fn super_with_mixed_access_patterns() { VAR local_idx : INT := 2; END_VAR - + METHOD test // Complex expression with SUPER^, array access, and method call local_idx := SUPER^.arr[SUPER^.get_value() MOD 6]; - + // Using SUPER^ with pointer operations SUPER^.ptr := REF(SUPER^.arr[0]); END_METHOD @@ -522,7 +523,7 @@ fn super_in_multi_level_inheritance() { VAR g_val : INT := 10; END_VAR - + METHOD gp_method : INT gp_method := g_val; END_METHOD @@ -532,11 +533,11 @@ fn super_in_multi_level_inheritance() { VAR p_val : INT := 20; END_VAR - + METHOD p_method : INT p_method := p_val + SUPER^.gp_method(); END_METHOD - + METHOD gp_method : INT // Override grandparent's method gp_method := p_val * 2; END_METHOD @@ -546,12 +547,12 @@ fn super_in_multi_level_inheritance() { VAR c_val : INT := 30; END_VAR - + METHOD test : INT // Access parent's implementation which itself uses SUPER^ test := SUPER^.p_method(); END_METHOD - + METHOD p_method : INT // Override parent's method p_method := c_val * 3; END_METHOD @@ -571,7 +572,7 @@ fn super_with_property_access() { VAR _prop_val : INT := 10; END_VAR - + PROPERTY prop : INT GET prop := _prop_val; @@ -586,7 +587,7 @@ fn super_with_property_access() { VAR local : INT; END_VAR - + PROPERTY prop : INT // Override property GET prop := _prop_val * 2; @@ -595,11 +596,11 @@ fn super_with_property_access() { _prop_val := prop / 2; END_SET END_PROPERTY - + METHOD test // Get using parent's property getter local := SUPER^.prop; - + // Set using parent's property setter SUPER^.prop := 42; END_METHOD @@ -618,7 +619,7 @@ fn super_in_variable_initialization() { VAR value : INT := 10; END_VAR - + METHOD get_init_value : INT get_init_value := 42; END_METHOD @@ -719,12 +720,12 @@ fn super_with_interface_implementations() { VAR count : INT := 0; END_VAR - + METHOD increment : INT count := count + 1; increment := count; END_METHOD - + METHOD get_count : INT get_count := count; END_METHOD @@ -735,7 +736,7 @@ fn super_with_interface_implementations() { count := count + 10; increment := count; END_METHOD - + METHOD test : INT // Use parent's interface implementation SUPER^.increment(); @@ -776,7 +777,7 @@ fn super_in_nested_conditionals() { threshold : INT := 50; value : INT := 10; END_VAR - + METHOD check_value : BOOL check_value := value > threshold; END_METHOD @@ -792,7 +793,7 @@ fn super_in_nested_conditionals() { SUPER^.value := SUPER^.value - 1; END_IF; END_IF; - + // In CASE statement CASE SUPER^.value OF 10: SUPER^.threshold := 40; @@ -873,7 +874,7 @@ fn super_in_fb_instance_array() { VAR children : ARRAY[0..2] OF child; END_VAR - + METHOD test // Should fail - SUPER is only available inside the POU that extends another children[0].SUPER^.value := 20; @@ -922,11 +923,11 @@ fn invalid_super_dereferencing_patterns() { FUNCTION_BLOCK child EXTENDS parent // Multiple dereferencing of SUPER (should be invalid) - SUPER^^.x := 20; - + SUPER^^.x := 20; + // Missing dot between derefs SUPER^^ := 30; - + // Invalid chain with wrong syntax SUPER^.SUPER.x := 40; END_FUNCTION_BLOCK @@ -937,13 +938,13 @@ fn invalid_super_dereferencing_patterns() { error[E068]: Dereferencing requires a pointer-value. ┌─ :10:13 │ - 10 │ SUPER^^.x := 20; + 10 │ SUPER^^.x := 20; │ ^^^^^^^ Dereferencing requires a pointer-value. warning[E049]: Illegal access to private member parent.x ┌─ :10:21 │ - 10 │ SUPER^^.x := 20; + 10 │ SUPER^^.x := 20; │ ^ Illegal access to private member parent.x error[E068]: Dereferencing requires a pointer-value. @@ -986,11 +987,11 @@ fn super_in_paren_expressions() { VAR result: INT; END_VAR - + METHOD test // Using SUPER in parentheses result := (SUPER^.x + 5) * 2; - + // Using SUPER in a nested expression result := (SUPER^.x + (SUPER^.x * 2)) / 3; END_METHOD @@ -1033,16 +1034,16 @@ fn invalid_super_dereferencing_patterns_parenthesized() { VAR result: INT; END_VAR - + METHOD test // Multiple dereferencing of SUPER (SUPER^)^.x := 20; // FIXME: this is currently a bug, not just an issue with super. https://github.com/PLC-lang/rusty/issues/1448 - + (SUPER^)^ := 30; // // Valid deref in parentheses // result := (SUPER)^.x + 5; - + // // Invalid chain with wrong syntax // (SUPER^).SUPER.x := 40; END_METHOD @@ -1088,13 +1089,13 @@ fn incorrect_super_usage_with_ref_to_parameters() { expect_ref_to_parent(SUPER^); // Should be SUPER END_METHOD END_FUNCTION_BLOCK - + FUNCTION expect_parent VAR_INPUT p: parent; END_VAR END_FUNCTION - + FUNCTION expect_ref_to_parent VAR_INPUT p: REF_TO parent; @@ -1183,7 +1184,7 @@ fn super_with_invalid_operations() { p1: REF_TO parent; p2: parent; END_VAR - + // Invalid operations on SUPER p1 := SUPER + SUPER; // Can't add references result := SUPER = SUPER; // Ref comparison (might be allowed but semantically wrong) @@ -1239,7 +1240,7 @@ fn super_dereferencing_with_method_calls() { METHOD test // These should be valid: x := SUPER^.get_value(); - + // These should be invalid: x := SUPER.get_value(); // Trying to call method on pointer p2 := SUPER^.get_value; // Method call missing () @@ -1272,10 +1273,10 @@ fn super_without_deref_accessing_members() { FUNCTION_BLOCK child EXTENDS parent // Trying to access member without dereferencing SUPER SUPER.x := 20; // Should be SUPER^.x - + // Trying to access pointer member without dereferencing SUPER SUPER.ptr^ := 30; // Should be SUPER^.ptr^ - + // Double dereferencing SUPER^.ptr^^ := 40; // Error - can't double-deref END_FUNCTION_BLOCK @@ -1317,7 +1318,7 @@ fn super_with_property_access_errors() { VAR _value: INT; END_VAR - + PROPERTY prop : INT GET prop := _value; @@ -1336,7 +1337,7 @@ fn super_with_property_access_errors() { // Invalid property access SUPER.prop := 10; // Should be SUPER^.prop x := SUPER.prop; // Should be SUPER^.prop - + // Invalid function-style property access SUPER^.prop(); END_FUNCTION_BLOCK diff --git a/src/validation/tests/this_keyword_validation_tests.rs b/src/validation/tests/this_keyword_validation_tests.rs index 3899a8d875..3b4461bf84 100644 --- a/src/validation/tests/this_keyword_validation_tests.rs +++ b/src/validation/tests/this_keyword_validation_tests.rs @@ -232,8 +232,7 @@ fn shadowing_is_working() { END_FUNCTION_BLOCK "#, ); - assert_snapshot!(diagnostics, @r#""#); - todo!(); + assert!(diagnostics.is_empty(), "Expected no diagnostics, but found: {diagnostics:?}"); } #[test] @@ -262,12 +261,12 @@ fn nested_fbs_and_this_passing() { VAR_INPUT ref : REF_TO OuterFB; END_VAR - ref.doSomething(); + ref^.doSomething(); END_METHOD END_FUNCTION_BLOCK "#, ); - assert_snapshot!(diagnostics, @r#""#); + assert!(diagnostics.is_empty(), "Expected no diagnostics, but found: {diagnostics:?}"); } #[test] From 7fa0fca1abd02919d27e6489b5bcf58f3d3cbd0c Mon Sep 17 00:00:00 2001 From: abroooo <61380142+abroooo@users.noreply.github.com> Date: Mon, 28 Apr 2025 14:01:03 +0200 Subject: [PATCH 31/48] believe it or not there is still validation left to do --- src/validation/statement.rs | 6 +++++- .../tests/this_keyword_validation_tests.rs | 19 ++++++++++++------- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/validation/statement.rs b/src/validation/statement.rs index aa7383b760..4740aa8dda 100644 --- a/src/validation/statement.rs +++ b/src/validation/statement.rs @@ -155,7 +155,11 @@ pub fn visit_statement( dbg!(statement); // TODO: check if I have an annotation. Only this in fb or method has annoation. If I // dont have an annotation then something is wrong. - if context.annotations.get_type(statement, context.index).is_none() { + // if context.annotations.get_type(statement, context.index).is_none() { + // if context.annotations.get_type(statement, context.index).is_none() { + if !context.qualifier.is_some_and(|it| { + context.index.find_pou(it).is_some_and(|it| it.is_method() || it.is_function_block()) + }) { //error validator.push_diagnostic( Diagnostic::new("Invalid use of `THIS`. Usage is only allowed within POU of type `FUNCTION_BLOCK` or type `METHOD`") diff --git a/src/validation/tests/this_keyword_validation_tests.rs b/src/validation/tests/this_keyword_validation_tests.rs index 3b4461bf84..ba0dc79695 100644 --- a/src/validation/tests/this_keyword_validation_tests.rs +++ b/src/validation/tests/this_keyword_validation_tests.rs @@ -22,7 +22,6 @@ fn pointer_arithmetic_with_this() { "#, ); assert_snapshot!(diagnostics, @r#""#); - todo!(); } #[test] @@ -41,6 +40,7 @@ fn cant_chain_this() { ); dbg!(&diagnostics); assert_snapshot!(diagnostics, @r""); + panic!(); } #[test] @@ -236,6 +236,8 @@ fn shadowing_is_working() { } #[test] +#[ignore = "reference resolve issue"] +/// #TODO: #THIS reference gh issue fn nested_fbs_and_this_passing() { let diagnostics = parse_and_validate_buffered( r#" @@ -345,6 +347,7 @@ fn this_is_read_only() { #[test] fn this_chained_with_super_is_not_ok() { let diagnostics = parse_and_validate_buffered( + // TODO: #THIS check with Michael (nested) r#" FUNCTION_BLOCK parent METHOD DoSomething : DINT @@ -379,12 +382,14 @@ fn this_in_properties_is_ok() { VAR prop_var : INT; END_VAR - PROPERTY prop : INT + PROPERTY GetProp : INT GET - prop := THIS^.prop_var; + GetProp := THIS^.prop; END_GET + END_PROPERTY + PROPERTY SetProp : INT SET - THIS^.prop_var := prop; + THIS^.prop = SetProp; END_SET END_PROPERTY END_FUNCTION_BLOCK @@ -396,6 +401,7 @@ fn this_in_properties_is_ok() { #[test] fn this_calling_function_and_passing_this_is_ok() { let diagnostics = parse_and_validate_buffered( + // TODO: #THIS check with Michael r#" FUNCTION_BLOCK FB_Test VAR @@ -403,9 +409,9 @@ fn this_calling_function_and_passing_this_is_ok() { END_VAR METHOD return_x : INT VAR_INPUT - fb_from_foo : REF_TO FB_Test; + fb_from_foo : FB_Test; END_VAR - return_x := fb_from_foo^.x; + return_x := fb_from_foo.this^.x; END_METHOD foo(this); END_FUNCTION_BLOCK @@ -442,7 +448,6 @@ fn this_in_property_calling_method_is_ok() { "#, ); assert_snapshot!(diagnostics, @r#""#); - todo!(); } #[test] From 25b5afbcd7d0f45e95058dc39632a6a0ed43d1bb Mon Sep 17 00:00:00 2001 From: abroooo <61380142+abroooo@users.noreply.github.com> Date: Tue, 29 Apr 2025 10:17:03 +0000 Subject: [PATCH 32/48] more tests, more validation, some cleanups --- .../generators/expression_generator.rs | 5 +- src/parser/tests/expressions_parser_tests.rs | 71 +--- .../parse_error_containers_tests.rs | 10 +- src/resolver.rs | 2 +- src/validation/statement.rs | 27 +- .../tests/this_keyword_validation_tests.rs | 313 ++++++++---------- 6 files changed, 180 insertions(+), 248 deletions(-) diff --git a/src/codegen/generators/expression_generator.rs b/src/codegen/generators/expression_generator.rs index bc1395b5b8..ddfc11b68c 100644 --- a/src/codegen/generators/expression_generator.rs +++ b/src/codegen/generators/expression_generator.rs @@ -220,15 +220,14 @@ impl<'ink, 'b> ExpressionCodeGenerator<'ink, 'b> { AstStatement::This => { // TODO: #THIS meaningful errors // [ ] add tests - let function_context = self.function_context.ok_or_else(|| { + self.function_context.ok_or_else(|| { Diagnostic::codegen_error("Cannot use 'this' without context", expression) })?; - let this_name = self.annotations.get_call_name(expression).unwrap(); let Some(this_name) = self.annotations.get_call_name(expression) else { todo!("error handling") }; let this_value = - self.llvm_index.find_loaded_associated_variable_value(&this_name).ok_or_else(|| { + self.llvm_index.find_loaded_associated_variable_value(this_name).ok_or_else(|| { let message = format!("Cannot find '{}' in associated variable values", this_name); Diagnostic::codegen_error(message, expression) })?; diff --git a/src/parser/tests/expressions_parser_tests.rs b/src/parser/tests/expressions_parser_tests.rs index 5f6bfe9402..b6fd377756 100644 --- a/src/parser/tests/expressions_parser_tests.rs +++ b/src/parser/tests/expressions_parser_tests.rs @@ -2019,32 +2019,6 @@ fn this_keyword_can_be_mixed_with_super() { let parse_result = parse(src).0; assert_debug_snapshot!(parse_result.implementations[0].statements, @r#" [ - ReferenceExpr { - kind: Member( - Identifier { - name: "x", - }, - ), - base: Some( - This, - ), - }, - ReferenceExpr { - kind: Member( - Identifier { - name: "y", - }, - ), - base: Some( - ReferenceExpr { - kind: Deref, - base: Some( - This, - ), - }, - ), - }, - This, CallStatement { operator: ReferenceExpr { kind: Member( @@ -2054,9 +2028,16 @@ fn this_keyword_can_be_mixed_with_super() { ), base: Some( ReferenceExpr { - kind: Deref, + kind: Member( + Super(derefed), + ), base: Some( - This, + ReferenceExpr { + kind: Deref, + base: Some( + This, + ), + }, ), }, ), @@ -2071,7 +2052,12 @@ fn this_keyword_can_be_mixed_with_super() { }, ), base: Some( - This, + ReferenceExpr { + kind: Deref, + base: Some( + This, + ), + }, ), }, right: ReferenceExpr { @@ -2092,33 +2078,6 @@ fn this_keyword_can_be_mixed_with_super() { }, ), }, - CallStatement { - operator: This, - parameters: None, - }, - Assignment { - left: This, - right: CallStatement { - operator: ReferenceExpr { - kind: Member( - Identifier { - name: "REF", - }, - ), - base: None, - }, - parameters: Some( - ReferenceExpr { - kind: Member( - Identifier { - name: "fb2", - }, - ), - base: None, - }, - ), - }, - }, ] "#); } diff --git a/src/parser/tests/parse_errors/parse_error_containers_tests.rs b/src/parser/tests/parse_errors/parse_error_containers_tests.rs index a66315e65c..bea34c8e31 100644 --- a/src/parser/tests/parse_errors/parse_error_containers_tests.rs +++ b/src/parser/tests/parse_errors/parse_error_containers_tests.rs @@ -307,7 +307,7 @@ fn this_is_a_reserved_keyword() { error[E007]: Unexpected token: expected KeywordSemicolon but found 'VAR this' ┌─ :4:9 - │ + │ 4 │ ╭ VAR 5 │ │ this : INT; │ ╰────────────────^ Unexpected token: expected KeywordSemicolon but found 'VAR @@ -322,7 +322,7 @@ fn this_is_a_reserved_keyword() { error[E007]: Unexpected token: expected KeywordSemicolon but found 'END_VAR METHOD this END_METHOD' ┌─ :6:9 - │ + │ 6 │ ╭ END_VAR 7 │ │ METHOD this END_METHOD │ ╰──────────────────────────────^ Unexpected token: expected KeywordSemicolon but found 'END_VAR @@ -345,5 +345,11 @@ fn this_is_a_reserved_keyword() { │ 3 │ PROGRAM this │ ^^^^ Case condition used outside of case statement! Did you mean to use ';'? + + error[E120]: Invalid use of `THIS`. Usage is only allowed within POU of type `FUNCTION_BLOCK` or type `METHOD` + ┌─ :3:13 + │ + 3 │ PROGRAM this + │ ^^^^ Invalid use of `THIS`. Usage is only allowed within POU of type `FUNCTION_BLOCK` or type `METHOD` "); } diff --git a/src/resolver.rs b/src/resolver.rs index 04e468b5e7..21d7365e85 100644 --- a/src/resolver.rs +++ b/src/resolver.rs @@ -1650,7 +1650,7 @@ impl<'i> TypeAnnotator<'i> { let name = match ctx.pou.and_then(|name| self.index.find_pou(name)) { Some(PouIndexEntry::FunctionBlock { name, .. }) => name, Some(PouIndexEntry::Method { parent_name: name, .. }) - if self.index.find_pou(&name).is_some_and(|it| it.is_function_block()) => + if self.index.find_pou(name).is_some_and(|it| it.is_function_block()) => { name } diff --git a/src/validation/statement.rs b/src/validation/statement.rs index 4740aa8dda..533efa93f6 100644 --- a/src/validation/statement.rs +++ b/src/validation/statement.rs @@ -152,22 +152,27 @@ pub fn visit_statement( } } AstStatement::This => { - dbg!(statement); - // TODO: check if I have an annotation. Only this in fb or method has annoation. If I - // dont have an annotation then something is wrong. - // if context.annotations.get_type(statement, context.index).is_none() { - // if context.annotations.get_type(statement, context.index).is_none() { if !context.qualifier.is_some_and(|it| { - context.index.find_pou(it).is_some_and(|it| it.is_method() || it.is_function_block()) + context + .index + .find_pou(it) + .and_then(|it| { + if it.is_function_block() { + Some(it) + } else { + context.index.find_pou(it.get_parent_pou_name().unwrap_or_default()) + } + }) + .is_some_and(|it| it.is_function_block()) }) { - //error validator.push_diagnostic( - Diagnostic::new("Invalid use of `THIS`. Usage is only allowed within POU of type `FUNCTION_BLOCK` or type `METHOD`") - .with_error_code("E120") - .with_location(statement), + Diagnostic::new( + "Invalid use of `THIS`. Usage is only allowed within POU of type `FUNCTION_BLOCK`", + ) + .with_error_code("E120") + .with_location(statement), ); } - // todo!() } _ => {} } diff --git a/src/validation/tests/this_keyword_validation_tests.rs b/src/validation/tests/this_keyword_validation_tests.rs index ba0dc79695..a2c4cdbe20 100644 --- a/src/validation/tests/this_keyword_validation_tests.rs +++ b/src/validation/tests/this_keyword_validation_tests.rs @@ -2,30 +2,23 @@ use insta::assert_snapshot; use test_utils::parse_and_validate_buffered; #[test] -fn pointer_arithmetic_with_this() { +fn this_pointer_arithmetic_is_ok() { let diagnostics = parse_and_validate_buffered( r#" - FUNCTION_BLOCK parent - VAR - x : LINT := 10; - y : LINT := 20; - END_VAR - END_FUNCTION_BLOCK - - FUNCTION_BLOCK child EXTENDS parent + FUNCTION_BLOCK fb VAR a : INT; END_VAR - // Pointer arithmetic with SUPER + // Pointer arithmetic with THIS a := (THIS + 1)^ + 5; END_FUNCTION_BLOCK "#, ); - assert_snapshot!(diagnostics, @r#""#); + assert!(diagnostics.is_empty()); } #[test] -fn cant_chain_this() { +fn chaining_this_is_not_allowed() { let diagnostics = parse_and_validate_buffered( r#" FUNCTION_BLOCK parent @@ -38,13 +31,35 @@ fn cant_chain_this() { END_FUNCTION_BLOCK "#, ); - dbg!(&diagnostics); - assert_snapshot!(diagnostics, @r""); - panic!(); + assert_snapshot!(diagnostics, @r" + error[E120]: `THIS` is not allowed in member-access position. + ┌─ :7:26 + │ + 7 │ this^.x := this^.this^.this^.y; + │ ^^^^ `THIS` is not allowed in member-access position. + + error[E120]: `THIS` is not allowed in member-access position. + ┌─ :7:32 + │ + 7 │ this^.x := this^.this^.this^.y; + │ ^^^^ `THIS` is not allowed in member-access position. + + error[E120]: `THIS` is not allowed in member-access position. + ┌─ :8:15 + │ + 8 │ this^.this^.this^.x := this^.y; + │ ^^^^ `THIS` is not allowed in member-access position. + + error[E120]: `THIS` is not allowed in member-access position. + ┌─ :8:21 + │ + 8 │ this^.this^.this^.x := this^.y; + │ ^^^^ `THIS` is not allowed in member-access position. + "); } #[test] -fn this_in_method_call_chain_is_allowed() { +fn this_in_method_call_chain_is_ok() { let diagnostics = parse_and_validate_buffered( r#" FUNCTION_BLOCK FB_Test @@ -62,53 +77,11 @@ fn this_in_method_call_chain_is_allowed() { END_FUNCTION_BLOCK "#, ); - assert_snapshot!(diagnostics, @r#""#); -} - -#[test] -fn this_in_program_is_not_allowed_() { - let diagnostics = parse_and_validate_buffered( - r#" - PROGRAM Main - VAR - x : INT := 5; - END_VAR - x := THIS^.x; - END_PROGRAM - "#, - ); - assert_snapshot!(diagnostics, @r" - error[E120]: Invalid use of `THIS`. Usage is only allowed within POU of type `FUNCTION_BLOCK` or type `METHOD` - ┌─ :6:18 - │ - 6 │ x := THIS^.x; - │ ^^^^ Invalid use of `THIS`. Usage is only allowed within POU of type `FUNCTION_BLOCK` or type `METHOD` - "); -} - -#[test] -fn this_in_function_is_not_allowed() { - let diagnostics = parse_and_validate_buffered( - r#" - FUNCTION SomeFunction : INT - VAR - SomeValue : INT := 5; - END_VAR - SomeFunction := THIS^.SomeValue; - END_FUNCTION - "#, - ); - assert_snapshot!(diagnostics, @r" - error[E120]: Invalid use of `THIS`. Usage is only allowed within POU of type `FUNCTION_BLOCK` or type `METHOD` - ┌─ :6:25 - │ - 6 │ SomeFunction := THIS^.SomeValue; - │ ^^^^ Invalid use of `THIS`. Usage is only allowed within POU of type `FUNCTION_BLOCK` or type `METHOD` - "); + assert!(diagnostics.is_empty()); } #[test] -fn this_cannot_be_assigned_to() { +fn assignment_to_this_is_not_allowed() { let diagnostics = parse_and_validate_buffered( r#" FUNCTION_BLOCK parent @@ -155,11 +128,17 @@ fn this_cannot_be_assigned_to() { │ 10 │ this := ADR(parent); // this is not allowed │ ^^^^ Expression this is not assignable. + + error[E050]: Expression this is not assignable. + ┌─ :12:9 + │ + 12 │ this := p; + │ ^^^^ Expression this is not assignable. "); } #[test] -fn basic_use() { +fn this_in_method_and_body_in_function_block_is_ok() { let diagnostics = parse_and_validate_buffered( r#" FUNCTION_BLOCK FB_Test @@ -174,7 +153,7 @@ fn basic_use() { END_FUNCTION_BLOCK "#, ); - assert_snapshot!(diagnostics, @r#""#); + assert!(diagnostics.is_empty()); } #[test] @@ -184,32 +163,23 @@ fn pass_this_to_method_is_ok() { let diagnostics = parse_and_validate_buffered( r#" FUNCTION_BLOCK FB_Test - METHOD increment_from_other + METHOD foo VAR - test : FB_Test; + test : FB_Test2; END_VAR - test.method2(THIS); - + test.bar(THIS^); END_METHOD END_FUNCTION_BLOCK FUNCTION_BLOCK FB_Test2 - METHOD method1 - VAR - test : FB_Test; - END_VAR - test.method2(THIS); - END_METHOD - METHOD method2 - VAR + VAR_INPUT test : FB_Test; END_VAR - test := THIS; - END_METHOD + METHOD bar: INT + END_METHOD END_FUNCTION_BLOCK "#, ); - assert_snapshot!(diagnostics, @r#""#); - todo!(); + assert!(diagnostics.is_empty()); } #[test] @@ -272,30 +242,7 @@ fn nested_fbs_and_this_passing() { } #[test] -fn this_as_method_argument_is_ok() { - let diagnostics = parse_and_validate_buffered( - r#" - FUNCTION_BLOCK FB_Test - VAR - helper : FB_Helper; - END_VAR - METHOD CallHelper - helper.DoSomething(THIS^); - END_METHOD - END_FUNCTION_BLOCK - - FUNCTION_BLOCK FB_Helper - METHOD DoSomething - VAR_INPUT input : FB_Test; END_VAR - END_METHOD - END_FUNCTION_BLOCK - "#, - ); - assert_snapshot!(diagnostics, @r#""#); -} - -#[test] -fn this_in_recursive_method() { +fn this_in_recursive_method_is_ok() { let diagnostics = parse_and_validate_buffered( r#" FUNCTION_BLOCK FB_Test @@ -311,37 +258,7 @@ fn this_in_recursive_method() { END_FUNCTION_BLOCK "#, ); - assert_snapshot!(diagnostics, @r#""#); -} - -#[test] -fn this_is_read_only() { - let diagnostics = parse_and_validate_buffered( - r#" - FUNCTION_BLOCK FB_Test - END_FUNCTION_BLOCK - FUNCTION_BLOCK FB_Test2 - VAR - test : FB_Test; - END_VAR - this := ADR(test); // this is not allowed - this^ := test; - END_FUNCTION_BLOCK - "#, - ); - assert_snapshot!(diagnostics, @r" - error[E050]: Expression this is not assignable. - ┌─ :8:13 - │ - 8 │ this := ADR(test); // this is not allowed - │ ^^^^ Expression this is not assignable. - - error[E037]: Invalid assignment: cannot assign 'FB_Test' to 'FB_Test2' - ┌─ :9:13 - │ - 9 │ this^ := test; - │ ^^^^^^^^^^^^^ Invalid assignment: cannot assign 'FB_Test' to 'FB_Test2' - "); + assert!(diagnostics.is_empty()); } #[test] @@ -374,56 +291,21 @@ fn this_chained_with_super_is_not_ok() { "); } -#[test] -fn this_in_properties_is_ok() { - let diagnostics = parse_and_validate_buffered( - r#" - FUNCTION_BLOCK FB_Test - VAR - prop_var : INT; - END_VAR - PROPERTY GetProp : INT - GET - GetProp := THIS^.prop; - END_GET - END_PROPERTY - PROPERTY SetProp : INT - SET - THIS^.prop = SetProp; - END_SET - END_PROPERTY - END_FUNCTION_BLOCK - "#, - ); - assert_snapshot!(diagnostics, @r#""#); -} - #[test] fn this_calling_function_and_passing_this_is_ok() { let diagnostics = parse_and_validate_buffered( - // TODO: #THIS check with Michael r#" FUNCTION_BLOCK FB_Test - VAR - x : INT; - END_VAR - METHOD return_x : INT - VAR_INPUT - fb_from_foo : FB_Test; - END_VAR - return_x := fb_from_foo.this^.x; - END_METHOD foo(this); END_FUNCTION_BLOCK FUNCTION foo : INT VAR_INPUT pfb: REF_TO FB_TEST; END_VAR - foo := pfb^.return_x(pfb); END_FUNCTION "#, ); - assert_snapshot!(diagnostics, @r#""#); + assert!(diagnostics.is_empty()); } #[test] @@ -443,20 +325,23 @@ fn this_in_property_calling_method_is_ok() { GET Value := THIS^.DoubleX(); END_GET + SET + this^.x := Value; + END_SET END_PROPERTY END_FUNCTION_BLOCK "#, ); - assert_snapshot!(diagnostics, @r#""#); + assert!(diagnostics.is_empty()); } #[test] -fn this_with_self_pointer() { +fn this_with_self_pointer_is_ok() { let diagnostics = parse_and_validate_buffered( r#" FUNCTION_BLOCK FB_Test VAR - refToSelf : POINTER TO FB_Test; + refToSelf : REF_TO FB_Test; END_VAR METHOD InitRef @@ -466,8 +351,7 @@ fn this_with_self_pointer() { END_FUNCTION_BLOCK "#, ); - assert_snapshot!(diagnostics, @r#""#); - todo!(); + assert!(diagnostics.is_empty()); } #[test] @@ -484,7 +368,7 @@ fn this_in_variable_initialization_is_ok() { END_FUNCTION_BLOCK "#, ); - assert_snapshot!(diagnostics, @r#""#); + assert!(diagnostics.is_empty()); } // TODO: test with incompatible types (refToSelf gets assigned something of different type) @@ -494,6 +378,85 @@ fn this_in_variable_initialization_is_ok() { // TODO: lit tests // TODO: resolver tests (parenthesized expressions, nested binary expressions ...) // TODO: this in variable initializers +#[test] +fn this_in_unsupported_pous_is_not_allowed() { + let diagnostics = parse_and_validate_buffered( + r#" + PROGRAM foo + VAR + x : INT; + END_VAR + + METHOD bar : INT + bar := THIS^.x; // this in method in program is not allowed + END_METHOD + THIS^; + END_PROGRAM + + ACTION foo.act + THIS^; + END_ACTION + + FUNCTION baz : INT + THIS^; + END_FUNCTION + "#, + ); + assert_snapshot!(diagnostics, @r" + error[E120]: Invalid use of `THIS`. Usage is only allowed within POU of type `FUNCTION_BLOCK` + ┌─ :8:24 + │ + 8 │ bar := THIS^.x; // this in method in program is not allowed + │ ^^^^ Invalid use of `THIS`. Usage is only allowed within POU of type `FUNCTION_BLOCK` + + error[E120]: Invalid use of `THIS`. Usage is only allowed within POU of type `FUNCTION_BLOCK` + ┌─ :10:13 + │ + 10 │ THIS^; + │ ^^^^ Invalid use of `THIS`. Usage is only allowed within POU of type `FUNCTION_BLOCK` + + error[E120]: Invalid use of `THIS`. Usage is only allowed within POU of type `FUNCTION_BLOCK` + ┌─ :14:13 + │ + 14 │ THIS^; + │ ^^^^ Invalid use of `THIS`. Usage is only allowed within POU of type `FUNCTION_BLOCK` + + error[E120]: Invalid use of `THIS`. Usage is only allowed within POU of type `FUNCTION_BLOCK` + ┌─ :18:13 + │ + 18 │ THIS^; + │ ^^^^ Invalid use of `THIS`. Usage is only allowed within POU of type `FUNCTION_BLOCK` + "); +} + +#[test] +fn this_in_action_in_functionblock_is_ok() { + let diagnostics = parse_and_validate_buffered( + r#" + FUNCTION_BLOCK fb + END_FUNCTION_BLOCK + + ACTION fb.foo + THIS^; + END_ACTION + "#, + ); + assert!(diagnostics.is_empty()); +} + +#[test] +fn this_calling_functionblock_body_from_method_is_ok() { + let diagnostics = parse_and_validate_buffered( + r#" + FUNCTION_BLOCK fb + METHOD foo : INT + THIS^(); + END_METHOD + END_FUNCTION_BLOCK + "#, + ); + assert!(diagnostics.is_empty()); +} #[test] fn dummy() { From b12a460ef70ae4cb0a49b56f940a07ac8e7957c3 Mon Sep 17 00:00:00 2001 From: abroooo <61380142+abroooo@users.noreply.github.com> Date: Tue, 29 Apr 2025 13:30:19 +0200 Subject: [PATCH 33/48] more codegen tests --- src/codegen/tests/oop_tests.rs | 606 ++++++++++++++++++++- src/codegen/tests/oop_tests/debug_tests.rs | 44 +- src/codegen/tests/oop_tests/super_tests.rs | 98 ++++ 3 files changed, 731 insertions(+), 17 deletions(-) diff --git a/src/codegen/tests/oop_tests.rs b/src/codegen/tests/oop_tests.rs index 499fde4068..2643de2413 100644 --- a/src/codegen/tests/oop_tests.rs +++ b/src/codegen/tests/oop_tests.rs @@ -19,7 +19,7 @@ fn members_from_base_class_are_available_in_subclasses() { END_FUNCTION_BLOCK "#, ); - insta::assert_snapshot!(result, @r###" + insta::assert_snapshot!(result, @r#" ; ModuleID = '' source_filename = "" @@ -32,6 +32,8 @@ fn members_from_base_class_are_available_in_subclasses() { define void @foo(%foo* %0) { entry: + %this = alloca %foo*, align 8 + store %foo* %0, %foo** %this, align 8 %a = getelementptr inbounds %foo, %foo* %0, i32 0, i32 0 %b = getelementptr inbounds %foo, %foo* %0, i32 0, i32 1 %c = getelementptr inbounds %foo, %foo* %0, i32 0, i32 2 @@ -40,6 +42,8 @@ fn members_from_base_class_are_available_in_subclasses() { define void @bar(%bar* %0) { entry: + %this = alloca %bar*, align 8 + store %bar* %0, %bar** %this, align 8 %__foo = getelementptr inbounds %bar, %bar* %0, i32 0, i32 0 ret void } @@ -65,7 +69,7 @@ fn members_from_base_class_are_available_in_subclasses() { entry: ret void } - "###); + "#); } #[test] @@ -91,7 +95,7 @@ fn write_to_parent_variable_qualified_access() { ", ); - insta::assert_snapshot!(res, @r###" + insta::assert_snapshot!(res, @r#" ; ModuleID = '' source_filename = "" @@ -106,6 +110,8 @@ fn write_to_parent_variable_qualified_access() { define void @fb(%fb* %0) { entry: + %this = alloca %fb*, align 8 + store %fb* %0, %fb** %this, align 8 %x = getelementptr inbounds %fb, %fb* %0, i32 0, i32 0 %y = getelementptr inbounds %fb, %fb* %0, i32 0, i32 1 ret void @@ -113,12 +119,16 @@ fn write_to_parent_variable_qualified_access() { define void @fb2(%fb2* %0) { entry: + %this = alloca %fb2*, align 8 + store %fb2* %0, %fb2** %this, align 8 %__fb = getelementptr inbounds %fb2, %fb2* %0, i32 0, i32 0 ret void } define void @foo(%foo* %0) { entry: + %this = alloca %foo*, align 8 + store %foo* %0, %foo** %this, align 8 %myFb = getelementptr inbounds %foo, %foo* %0, i32 0, i32 0 %__fb = getelementptr inbounds %fb2, %fb2* %myFb, i32 0, i32 0 %x = getelementptr inbounds %fb, %fb* %__fb, i32 0, i32 0 @@ -157,7 +167,7 @@ fn write_to_parent_variable_qualified_access() { entry: ret void } - "###); + "#); } #[test] @@ -202,12 +212,16 @@ fn write_to_parent_variable_in_instance() { define void @foo(%foo* %0) { entry: + %this = alloca %foo*, align 8 + store %foo* %0, %foo** %this, align 8 %s = getelementptr inbounds %foo, %foo* %0, i32 0, i32 0 ret void } define void @foo_baz(%foo* %0) { entry: + %this = alloca %foo*, align 8 + store %foo* %0, %foo** %this, align 8 %s = getelementptr inbounds %foo, %foo* %0, i32 0, i32 0 %1 = bitcast [81 x i8]* %s to i8* call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 1 %1, i8* align 1 getelementptr inbounds ([6 x i8], [6 x i8]* @utf08_literal_0, i32 0, i32 0), i32 6, i1 false) @@ -216,6 +230,8 @@ fn write_to_parent_variable_in_instance() { define void @bar(%bar* %0) { entry: + %this = alloca %bar*, align 8 + store %bar* %0, %bar** %this, align 8 %__foo = getelementptr inbounds %bar, %bar* %0, i32 0, i32 0 %s = getelementptr inbounds %foo, %foo* %__foo, i32 0, i32 0 %1 = bitcast [81 x i8]* %s to i8* @@ -310,7 +326,7 @@ fn array_in_parent_generated() { END_FUNCTION "#, ); - insta::assert_snapshot!(result, @r###" + insta::assert_snapshot!(result, @r#" ; ModuleID = '' source_filename = "" @@ -325,6 +341,8 @@ fn array_in_parent_generated() { define void @grandparent(%grandparent* %0) { entry: + %this = alloca %grandparent*, align 8 + store %grandparent* %0, %grandparent** %this, align 8 %y = getelementptr inbounds %grandparent, %grandparent* %0, i32 0, i32 0 %a = getelementptr inbounds %grandparent, %grandparent* %0, i32 0, i32 1 ret void @@ -332,6 +350,8 @@ fn array_in_parent_generated() { define void @parent(%parent* %0) { entry: + %this = alloca %parent*, align 8 + store %parent* %0, %parent** %this, align 8 %__grandparent = getelementptr inbounds %parent, %parent* %0, i32 0, i32 0 %x = getelementptr inbounds %parent, %parent* %0, i32 0, i32 1 %b = getelementptr inbounds %parent, %parent* %0, i32 0, i32 2 @@ -340,6 +360,8 @@ fn array_in_parent_generated() { define void @child(%child* %0) { entry: + %this = alloca %child*, align 8 + store %child* %0, %child** %this, align 8 %__parent = getelementptr inbounds %child, %child* %0, i32 0, i32 0 %z = getelementptr inbounds %child, %child* %0, i32 0, i32 1 ret void @@ -413,7 +435,7 @@ fn array_in_parent_generated() { } attributes #0 = { argmemonly nofree nounwind willreturn writeonly } - "###); + "#); } #[test] @@ -443,7 +465,7 @@ fn complex_array_access_generated() { "#, ); - insta::assert_snapshot!(result, @r###" + insta::assert_snapshot!(result, @r#" ; ModuleID = '' source_filename = "" @@ -458,6 +480,8 @@ fn complex_array_access_generated() { define void @grandparent(%grandparent* %0) { entry: + %this = alloca %grandparent*, align 8 + store %grandparent* %0, %grandparent** %this, align 8 %y = getelementptr inbounds %grandparent, %grandparent* %0, i32 0, i32 0 %a = getelementptr inbounds %grandparent, %grandparent* %0, i32 0, i32 1 ret void @@ -465,6 +489,8 @@ fn complex_array_access_generated() { define void @parent(%parent* %0) { entry: + %this = alloca %parent*, align 8 + store %parent* %0, %parent** %this, align 8 %__grandparent = getelementptr inbounds %parent, %parent* %0, i32 0, i32 0 %x = getelementptr inbounds %parent, %parent* %0, i32 0, i32 1 %b = getelementptr inbounds %parent, %parent* %0, i32 0, i32 2 @@ -473,6 +499,8 @@ fn complex_array_access_generated() { define void @child(%child* %0) { entry: + %this = alloca %child*, align 8 + store %child* %0, %child** %this, align 8 %__parent = getelementptr inbounds %child, %child* %0, i32 0, i32 0 %z = getelementptr inbounds %child, %child* %0, i32 0, i32 1 %__grandparent = getelementptr inbounds %parent, %parent* %__parent, i32 0, i32 0 @@ -533,7 +561,7 @@ fn complex_array_access_generated() { entry: ret void } - "###); + "#); } #[test] @@ -586,16 +614,564 @@ fn properties_are_methods() { assert_eq!(property, method); } + #[test] -fn this() { - let property = codegen( - " - FUNCTION_BLOCK fb +fn this_in_method_call_chain() { + let code = codegen( + r#" + FUNCTION_BLOCK FB_Test VAR - localPrivateVariable : DINT; + counter : INT := 0; END_VAR - this^.localPrivateVariable := 5; + + METHOD Step + THIS^.Increment(); + END_METHOD + + METHOD Increment + counter := counter + 1; + END_METHOD END_FUNCTION_BLOCK - ", + "#, ); + insta::assert_snapshot!(code, @r#" + ; ModuleID = '' + source_filename = "" + + %FB_Test = type { i16 } + + @__FB_Test__init = constant %FB_Test zeroinitializer + @llvm.global_ctors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 0, void ()* @__init___Test, i8* null }] + + define void @FB_Test(%FB_Test* %0) { + entry: + %this = alloca %FB_Test*, align 8 + store %FB_Test* %0, %FB_Test** %this, align 8 + %counter = getelementptr inbounds %FB_Test, %FB_Test* %0, i32 0, i32 0 + ret void + } + + define void @FB_Test_Step(%FB_Test* %0) { + entry: + %this = alloca %FB_Test*, align 8 + store %FB_Test* %0, %FB_Test** %this, align 8 + %counter = getelementptr inbounds %FB_Test, %FB_Test* %0, i32 0, i32 0 + %deref = load %FB_Test*, %FB_Test** %this, align 8 + call void @FB_Test_Increment(%FB_Test* %deref) + ret void + } + + define void @FB_Test_Increment(%FB_Test* %0) { + entry: + %this = alloca %FB_Test*, align 8 + store %FB_Test* %0, %FB_Test** %this, align 8 + %counter = getelementptr inbounds %FB_Test, %FB_Test* %0, i32 0, i32 0 + %load_counter = load i16, i16* %counter, align 2 + %1 = sext i16 %load_counter to i32 + %tmpVar = add i32 %1, 1 + %2 = trunc i32 %tmpVar to i16 + store i16 %2, i16* %counter, align 2 + ret void + } + + define void @__init_fb_test(%FB_Test* %0) { + entry: + %self = alloca %FB_Test*, align 8 + store %FB_Test* %0, %FB_Test** %self, align 8 + ret void + } + + define void @__init___Test() { + entry: + ret void + } + "#); +} + +#[test] +fn this_in_method_and_body_in_function_block() { + let code = codegen( + r#" + FUNCTION_BLOCK FB_Test + VAR + val : INT := 5; + END_VAR + + METHOD GetVal : INT + GetVal := THIS^.val; + END_METHOD + val := this^.val; + this^.val := val; + END_FUNCTION_BLOCK + "#, + ); + insta::assert_snapshot!(code, @r#" + ; ModuleID = '' + source_filename = "" + + %FB_Test = type { i16 } + + @__FB_Test__init = constant %FB_Test { i16 5 } + @llvm.global_ctors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 0, void ()* @__init___Test, i8* null }] + + define void @FB_Test(%FB_Test* %0) { + entry: + %this = alloca %FB_Test*, align 8 + store %FB_Test* %0, %FB_Test** %this, align 8 + %val = getelementptr inbounds %FB_Test, %FB_Test* %0, i32 0, i32 0 + %deref = load %FB_Test*, %FB_Test** %this, align 8 + %val1 = getelementptr inbounds %FB_Test, %FB_Test* %deref, i32 0, i32 0 + %load_val = load i16, i16* %val1, align 2 + store i16 %load_val, i16* %val, align 2 + %deref2 = load %FB_Test*, %FB_Test** %this, align 8 + %val3 = getelementptr inbounds %FB_Test, %FB_Test* %deref2, i32 0, i32 0 + %load_val4 = load i16, i16* %val, align 2 + store i16 %load_val4, i16* %val3, align 2 + ret void + } + + define i16 @FB_Test_GetVal(%FB_Test* %0) { + entry: + %this = alloca %FB_Test*, align 8 + store %FB_Test* %0, %FB_Test** %this, align 8 + %val = getelementptr inbounds %FB_Test, %FB_Test* %0, i32 0, i32 0 + %FB_Test.GetVal = alloca i16, align 2 + store i16 0, i16* %FB_Test.GetVal, align 2 + %deref = load %FB_Test*, %FB_Test** %this, align 8 + %val1 = getelementptr inbounds %FB_Test, %FB_Test* %deref, i32 0, i32 0 + %load_val = load i16, i16* %val1, align 2 + store i16 %load_val, i16* %FB_Test.GetVal, align 2 + %FB_Test_GetVal_ret = load i16, i16* %FB_Test.GetVal, align 2 + ret i16 %FB_Test_GetVal_ret + } + + define void @__init_fb_test(%FB_Test* %0) { + entry: + %self = alloca %FB_Test*, align 8 + store %FB_Test* %0, %FB_Test** %self, align 8 + ret void + } + + define void @__init___Test() { + entry: + ret void + } + "#); +} + +#[test] +fn pass_this_to_method() { + // pass `this` pointer of FB1 to a method of another fb called FB2 which calls a method of FB1 + // and changes a value of the passed `this` pointer + let code = codegen( + r#" + FUNCTION_BLOCK FB_Test + METHOD foo + VAR + test : FB_Test2; + x : INT; + END_VAR + test.bar(THIS); + END_METHOD + END_FUNCTION_BLOCK + FUNCTION_BLOCK FB_Test2 + METHOD bar: INT + VAR_INPUT + test : REF_TO FB_Test; + END_VAR + bar := test^.x; + END_METHOD + END_FUNCTION_BLOCK + "#, + ); + insta::assert_snapshot!(code, @r#""#); +} + +#[test] +fn shadowing_is_working() { + let code = codegen( + r#" + FUNCTION_BLOCK FB_Test + VAR + val : INT := 5; + END_VAR + METHOD shadow_val + VAR + val : INT := 10; + local_val: INT; + shadow_val : INT; + END_VAR + local_val := THIS^.val; + shadow_val := val; + END_METHOD + END_FUNCTION_BLOCK + "#, + ); + insta::assert_snapshot!(code, @r#" + ; ModuleID = '' + source_filename = "" + + %FB_Test = type { i16 } + + @__FB_Test__init = constant %FB_Test { i16 5 } + @llvm.global_ctors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 0, void ()* @__init___Test, i8* null }] + + define void @FB_Test(%FB_Test* %0) { + entry: + %this = alloca %FB_Test*, align 8 + store %FB_Test* %0, %FB_Test** %this, align 8 + %val = getelementptr inbounds %FB_Test, %FB_Test* %0, i32 0, i32 0 + ret void + } + + define void @FB_Test_shadow_val(%FB_Test* %0) { + entry: + %this = alloca %FB_Test*, align 8 + store %FB_Test* %0, %FB_Test** %this, align 8 + %val = getelementptr inbounds %FB_Test, %FB_Test* %0, i32 0, i32 0 + %val1 = alloca i16, align 2 + %local_val = alloca i16, align 2 + %shadow_val = alloca i16, align 2 + store i16 10, i16* %val1, align 2 + store i16 0, i16* %local_val, align 2 + store i16 0, i16* %shadow_val, align 2 + %deref = load %FB_Test*, %FB_Test** %this, align 8 + %val2 = getelementptr inbounds %FB_Test, %FB_Test* %deref, i32 0, i32 0 + %load_val = load i16, i16* %val2, align 2 + store i16 %load_val, i16* %local_val, align 2 + %load_val3 = load i16, i16* %val1, align 2 + store i16 %load_val3, i16* %shadow_val, align 2 + ret void + } + + define void @__init_fb_test(%FB_Test* %0) { + entry: + %self = alloca %FB_Test*, align 8 + store %FB_Test* %0, %FB_Test** %self, align 8 + ret void + } + + define void @__init___Test() { + entry: + ret void + } + "#); +} + +#[test] +fn this_calling_function_and_passing_this() { + let code = codegen( + r#" + FUNCTION_BLOCK FB_Test + VAR + x : INT; + END_VAR + foo(this); + END_FUNCTION_BLOCK + FUNCTION foo : INT + VAR_INPUT + pfb: REF_TO FB_TEST; + END_VAR + foo := pfb^.x; + END_FUNCTION + "#, + ); + insta::assert_snapshot!(code, @r#" + ; ModuleID = '' + source_filename = "" + + %FB_Test = type { i16 } + + @__FB_Test__init = constant %FB_Test zeroinitializer + @llvm.global_ctors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 0, void ()* @__init___Test, i8* null }] + + define void @FB_Test(%FB_Test* %0) { + entry: + %this = alloca %FB_Test*, align 8 + store %FB_Test* %0, %FB_Test** %this, align 8 + %x = getelementptr inbounds %FB_Test, %FB_Test* %0, i32 0, i32 0 + %1 = load %FB_Test*, %FB_Test** %this, align 8 + %call = call i16 @foo(%FB_Test* %1) + ret void + } + + define i16 @foo(%FB_Test* %0) { + entry: + %foo = alloca i16, align 2 + %pfb = alloca %FB_Test*, align 8 + store %FB_Test* %0, %FB_Test** %pfb, align 8 + store i16 0, i16* %foo, align 2 + %deref = load %FB_Test*, %FB_Test** %pfb, align 8 + %x = getelementptr inbounds %FB_Test, %FB_Test* %deref, i32 0, i32 0 + %load_x = load i16, i16* %x, align 2 + store i16 %load_x, i16* %foo, align 2 + %foo_ret = load i16, i16* %foo, align 2 + ret i16 %foo_ret + } + + define void @__init_fb_test(%FB_Test* %0) { + entry: + %self = alloca %FB_Test*, align 8 + store %FB_Test* %0, %FB_Test** %self, align 8 + ret void + } + + define void @__init___Test() { + entry: + ret void + } + "#); +} + +#[test] +fn this_in_property_calling_method() { + let code = codegen( + r#" + FUNCTION_BLOCK FB_Test + VAR + x : INT; + END_VAR + + METHOD DoubleX : INT + DoubleX := 2 * THIS^.x; + END_METHOD + + PROPERTY Value : INT + GET + Value := THIS^.DoubleX(); + END_GET + SET + this^.x := Value; + END_SET + END_PROPERTY + END_FUNCTION_BLOCK + "#, + ); + insta::assert_snapshot!(code, @r#" + ; ModuleID = '' + source_filename = "" + + %FB_Test = type { i16 } + + @__FB_Test__init = constant %FB_Test zeroinitializer + @llvm.global_ctors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 0, void ()* @__init___Test, i8* null }] + + define void @FB_Test(%FB_Test* %0) { + entry: + %this = alloca %FB_Test*, align 8 + store %FB_Test* %0, %FB_Test** %this, align 8 + %x = getelementptr inbounds %FB_Test, %FB_Test* %0, i32 0, i32 0 + ret void + } + + define i16 @FB_Test_DoubleX(%FB_Test* %0) { + entry: + %this = alloca %FB_Test*, align 8 + store %FB_Test* %0, %FB_Test** %this, align 8 + %x = getelementptr inbounds %FB_Test, %FB_Test* %0, i32 0, i32 0 + %FB_Test.DoubleX = alloca i16, align 2 + store i16 0, i16* %FB_Test.DoubleX, align 2 + %deref = load %FB_Test*, %FB_Test** %this, align 8 + %x1 = getelementptr inbounds %FB_Test, %FB_Test* %deref, i32 0, i32 0 + %load_x = load i16, i16* %x1, align 2 + %1 = sext i16 %load_x to i32 + %tmpVar = mul i32 2, %1 + %2 = trunc i32 %tmpVar to i16 + store i16 %2, i16* %FB_Test.DoubleX, align 2 + %FB_Test_DoubleX_ret = load i16, i16* %FB_Test.DoubleX, align 2 + ret i16 %FB_Test_DoubleX_ret + } + + define i16 @FB_Test___get_Value(%FB_Test* %0) { + entry: + %this = alloca %FB_Test*, align 8 + store %FB_Test* %0, %FB_Test** %this, align 8 + %x = getelementptr inbounds %FB_Test, %FB_Test* %0, i32 0, i32 0 + %FB_Test.__get_Value = alloca i16, align 2 + %Value = alloca i16, align 2 + store i16 0, i16* %Value, align 2 + store i16 0, i16* %FB_Test.__get_Value, align 2 + %deref = load %FB_Test*, %FB_Test** %this, align 8 + %call = call i16 @FB_Test_DoubleX(%FB_Test* %deref) + store i16 %call, i16* %Value, align 2 + %load_Value = load i16, i16* %Value, align 2 + store i16 %load_Value, i16* %FB_Test.__get_Value, align 2 + %FB_Test___get_Value_ret = load i16, i16* %FB_Test.__get_Value, align 2 + ret i16 %FB_Test___get_Value_ret + } + + define void @FB_Test___set_Value(%FB_Test* %0, i16 %1) { + entry: + %this = alloca %FB_Test*, align 8 + store %FB_Test* %0, %FB_Test** %this, align 8 + %x = getelementptr inbounds %FB_Test, %FB_Test* %0, i32 0, i32 0 + %Value = alloca i16, align 2 + store i16 %1, i16* %Value, align 2 + %deref = load %FB_Test*, %FB_Test** %this, align 8 + %x1 = getelementptr inbounds %FB_Test, %FB_Test* %deref, i32 0, i32 0 + %load_Value = load i16, i16* %Value, align 2 + store i16 %load_Value, i16* %x1, align 2 + ret void + } + + define void @__init_fb_test(%FB_Test* %0) { + entry: + %self = alloca %FB_Test*, align 8 + store %FB_Test* %0, %FB_Test** %self, align 8 + ret void + } + + define void @__init___Test() { + entry: + ret void + } + "#); +} + +#[test] +fn this_with_self_pointer() { + let code = codegen( + r#" + FUNCTION_BLOCK FB_Test + VAR + refToSelf : REF_TO FB_Test; + END_VAR + + METHOD InitRef + refToSelf := ADR(THIS^); + refToSelf := REF(THIS^); + refToSelf := THIS; + END_METHOD + END_FUNCTION_BLOCK + "#, + ); + insta::assert_snapshot!(code, @r#" + ; ModuleID = '' + source_filename = "" + + %FB_Test = type { %FB_Test* } + + @__FB_Test__init = constant %FB_Test zeroinitializer + @llvm.global_ctors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 0, void ()* @__init___Test, i8* null }] + + define void @FB_Test(%FB_Test* %0) { + entry: + %this = alloca %FB_Test*, align 8 + store %FB_Test* %0, %FB_Test** %this, align 8 + %refToSelf = getelementptr inbounds %FB_Test, %FB_Test* %0, i32 0, i32 0 + ret void + } + + define void @FB_Test_InitRef(%FB_Test* %0) { + entry: + %this = alloca %FB_Test*, align 8 + store %FB_Test* %0, %FB_Test** %this, align 8 + %refToSelf = getelementptr inbounds %FB_Test, %FB_Test* %0, i32 0, i32 0 + %deref = load %FB_Test*, %FB_Test** %this, align 8 + store %FB_Test* %deref, %FB_Test** %refToSelf, align 8 + %deref1 = load %FB_Test*, %FB_Test** %this, align 8 + store %FB_Test* %deref1, %FB_Test** %refToSelf, align 8 + %1 = load %FB_Test*, %FB_Test** %this, align 8 + store %FB_Test* %1, %FB_Test** %refToSelf, align 8 + ret void + } + + define void @__init_fb_test(%FB_Test* %0) { + entry: + %self = alloca %FB_Test*, align 8 + store %FB_Test* %0, %FB_Test** %self, align 8 + ret void + } + + define void @__init___Test() { + entry: + ret void + } + "#); +} + +#[test] +fn this_in_variable_initialization() { + let code = codegen( + r#" + FUNCTION_BLOCK FB + VAR CONSTANT + x : INT; + END_VAR + VAR + self : REF_TO FB := THIS; + y : INT := THIS^.x; + END_VAR + END_FUNCTION_BLOCK + "#, + ); + insta::assert_snapshot!(code, @r#""#); +} + +#[test] +fn this_in_action_in_functionblock() { + let code = codegen( + r#" + FUNCTION_BLOCK fb + END_FUNCTION_BLOCK + + ACTION fb.foo + THIS^(); + END_ACTION + "#, + ); + insta::assert_snapshot!(code, @r#""#); +} + +#[test] +fn this_calling_functionblock_body_from_method() { + let code = codegen( + r#" + FUNCTION_BLOCK fb + METHOD foo : INT + THIS^(); + END_METHOD + END_FUNCTION_BLOCK + "#, + ); + insta::assert_snapshot!(code, @r#" + ; ModuleID = '' + source_filename = "" + + %fb = type {} + + @__fb__init = constant %fb zeroinitializer + @llvm.global_ctors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 0, void ()* @__init___Test, i8* null }] + + define void @fb(%fb* %0) { + entry: + %this = alloca %fb*, align 8 + store %fb* %0, %fb** %this, align 8 + ret void + } + + define i16 @fb_foo(%fb* %0) { + entry: + %this = alloca %fb*, align 8 + store %fb* %0, %fb** %this, align 8 + %fb.foo = alloca i16, align 2 + store i16 0, i16* %fb.foo, align 2 + %deref = load %fb*, %fb** %this, align 8 + call void @fb(%fb* %deref) + %fb_foo_ret = load i16, i16* %fb.foo, align 2 + ret i16 %fb_foo_ret + } + + define void @__init_fb(%fb* %0) { + entry: + %self = alloca %fb*, align 8 + store %fb* %0, %fb** %self, align 8 + ret void + } + + define void @__init___Test() { + entry: + ret void + } + "#); } diff --git a/src/codegen/tests/oop_tests/debug_tests.rs b/src/codegen/tests/oop_tests/debug_tests.rs index fb23452fd1..b2bbac53fc 100644 --- a/src/codegen/tests/oop_tests/debug_tests.rs +++ b/src/codegen/tests/oop_tests/debug_tests.rs @@ -30,6 +30,8 @@ fn members_from_base_class_are_available_in_subclasses() { define void @foo(%foo* %0) !dbg !25 { entry: call void @llvm.dbg.declare(metadata %foo* %0, metadata !29, metadata !DIExpression()), !dbg !30 + %this = alloca %foo*, align 8 + store %foo* %0, %foo** %this, align 8 %a = getelementptr inbounds %foo, %foo* %0, i32 0, i32 0 %b = getelementptr inbounds %foo, %foo* %0, i32 0, i32 1 %c = getelementptr inbounds %foo, %foo* %0, i32 0, i32 2 @@ -39,6 +41,8 @@ fn members_from_base_class_are_available_in_subclasses() { define void @bar(%bar* %0) !dbg !31 { entry: call void @llvm.dbg.declare(metadata %bar* %0, metadata !34, metadata !DIExpression()), !dbg !35 + %this = alloca %bar*, align 8 + store %bar* %0, %bar** %this, align 8 %__foo = getelementptr inbounds %bar, %bar* %0, i32 0, i32 0 ret void, !dbg !35 } @@ -151,6 +155,8 @@ fn write_to_parent_variable_qualified_access() { define void @fb(%fb* %0) !dbg !22 { entry: call void @llvm.dbg.declare(metadata %fb* %0, metadata !26, metadata !DIExpression()), !dbg !27 + %this = alloca %fb*, align 8 + store %fb* %0, %fb** %this, align 8 %x = getelementptr inbounds %fb, %fb* %0, i32 0, i32 0 %y = getelementptr inbounds %fb, %fb* %0, i32 0, i32 1 ret void, !dbg !27 @@ -159,6 +165,8 @@ fn write_to_parent_variable_qualified_access() { define void @fb2(%fb2* %0) !dbg !28 { entry: call void @llvm.dbg.declare(metadata %fb2* %0, metadata !31, metadata !DIExpression()), !dbg !32 + %this = alloca %fb2*, align 8 + store %fb2* %0, %fb2** %this, align 8 %__fb = getelementptr inbounds %fb2, %fb2* %0, i32 0, i32 0 ret void, !dbg !32 } @@ -166,6 +174,8 @@ fn write_to_parent_variable_qualified_access() { define void @foo(%foo* %0) !dbg !33 { entry: call void @llvm.dbg.declare(metadata %foo* %0, metadata !36, metadata !DIExpression()), !dbg !37 + %this = alloca %foo*, align 8 + store %foo* %0, %foo** %this, align 8 %myFb = getelementptr inbounds %foo, %foo* %0, i32 0, i32 0 %__fb = getelementptr inbounds %fb2, %fb2* %myFb, i32 0, i32 0, !dbg !37 %x = getelementptr inbounds %fb, %fb* %__fb, i32 0, i32 0, !dbg !37 @@ -298,6 +308,8 @@ fn write_to_parent_variable_in_instance() { define void @foo(%foo* %0) !dbg !19 { entry: call void @llvm.dbg.declare(metadata %foo* %0, metadata !23, metadata !DIExpression()), !dbg !24 + %this = alloca %foo*, align 8 + store %foo* %0, %foo** %this, align 8 %s = getelementptr inbounds %foo, %foo* %0, i32 0, i32 0 ret void, !dbg !24 } @@ -305,6 +317,8 @@ fn write_to_parent_variable_in_instance() { define void @foo_baz(%foo* %0) !dbg !25 { entry: call void @llvm.dbg.declare(metadata %foo* %0, metadata !26, metadata !DIExpression()), !dbg !27 + %this = alloca %foo*, align 8 + store %foo* %0, %foo** %this, align 8 %s = getelementptr inbounds %foo, %foo* %0, i32 0, i32 0 %1 = bitcast [81 x i8]* %s to i8*, !dbg !27 call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 1 %1, i8* align 1 getelementptr inbounds ([6 x i8], [6 x i8]* @utf08_literal_0, i32 0, i32 0), i32 6, i1 false), !dbg !27 @@ -314,6 +328,8 @@ fn write_to_parent_variable_in_instance() { define void @bar(%bar* %0) !dbg !29 { entry: call void @llvm.dbg.declare(metadata %bar* %0, metadata !32, metadata !DIExpression()), !dbg !33 + %this = alloca %bar*, align 8 + store %bar* %0, %bar** %this, align 8 %__foo = getelementptr inbounds %bar, %bar* %0, i32 0, i32 0 %s = getelementptr inbounds %foo, %foo* %__foo, i32 0, i32 0, !dbg !33 %1 = bitcast [81 x i8]* %s to i8*, !dbg !33 @@ -480,6 +496,8 @@ fn array_in_parent_generated() { define void @grandparent(%grandparent* %0) !dbg !31 { entry: call void @llvm.dbg.declare(metadata %grandparent* %0, metadata !35, metadata !DIExpression()), !dbg !36 + %this = alloca %grandparent*, align 8 + store %grandparent* %0, %grandparent** %this, align 8 %y = getelementptr inbounds %grandparent, %grandparent* %0, i32 0, i32 0 %a = getelementptr inbounds %grandparent, %grandparent* %0, i32 0, i32 1 ret void, !dbg !36 @@ -488,6 +506,8 @@ fn array_in_parent_generated() { define void @parent(%parent* %0) !dbg !37 { entry: call void @llvm.dbg.declare(metadata %parent* %0, metadata !40, metadata !DIExpression()), !dbg !41 + %this = alloca %parent*, align 8 + store %parent* %0, %parent** %this, align 8 %__grandparent = getelementptr inbounds %parent, %parent* %0, i32 0, i32 0 %x = getelementptr inbounds %parent, %parent* %0, i32 0, i32 1 %b = getelementptr inbounds %parent, %parent* %0, i32 0, i32 2 @@ -497,6 +517,8 @@ fn array_in_parent_generated() { define void @child(%child* %0) !dbg !42 { entry: call void @llvm.dbg.declare(metadata %child* %0, metadata !45, metadata !DIExpression()), !dbg !46 + %this = alloca %child*, align 8 + store %child* %0, %child** %this, align 8 %__parent = getelementptr inbounds %child, %child* %0, i32 0, i32 0 %z = getelementptr inbounds %child, %child* %0, i32 0, i32 1 ret void, !dbg !46 @@ -684,6 +706,8 @@ fn complex_array_access_generated() { define void @grandparent(%grandparent* %0) !dbg !31 { entry: call void @llvm.dbg.declare(metadata %grandparent* %0, metadata !35, metadata !DIExpression()), !dbg !36 + %this = alloca %grandparent*, align 8 + store %grandparent* %0, %grandparent** %this, align 8 %y = getelementptr inbounds %grandparent, %grandparent* %0, i32 0, i32 0 %a = getelementptr inbounds %grandparent, %grandparent* %0, i32 0, i32 1 ret void, !dbg !36 @@ -692,6 +716,8 @@ fn complex_array_access_generated() { define void @parent(%parent* %0) !dbg !37 { entry: call void @llvm.dbg.declare(metadata %parent* %0, metadata !40, metadata !DIExpression()), !dbg !41 + %this = alloca %parent*, align 8 + store %parent* %0, %parent** %this, align 8 %__grandparent = getelementptr inbounds %parent, %parent* %0, i32 0, i32 0 %x = getelementptr inbounds %parent, %parent* %0, i32 0, i32 1 %b = getelementptr inbounds %parent, %parent* %0, i32 0, i32 2 @@ -701,6 +727,8 @@ fn complex_array_access_generated() { define void @child(%child* %0) !dbg !42 { entry: call void @llvm.dbg.declare(metadata %child* %0, metadata !45, metadata !DIExpression()), !dbg !46 + %this = alloca %child*, align 8 + store %child* %0, %child** %this, align 8 %__parent = getelementptr inbounds %child, %child* %0, i32 0, i32 0 %z = getelementptr inbounds %child, %child* %0, i32 0, i32 1 %__grandparent = getelementptr inbounds %parent, %parent* %__parent, i32 0, i32 0, !dbg !46 @@ -848,18 +876,24 @@ fn function_block_method_debug_info() { define void @foo(%foo* %0) !dbg !14 { entry: call void @llvm.dbg.declare(metadata %foo* %0, metadata !17, metadata !DIExpression()), !dbg !18 + %this = alloca %foo*, align 8 + store %foo* %0, %foo** %this, align 8 ret void, !dbg !18 } define void @foo_baz(%foo* %0) !dbg !19 { entry: call void @llvm.dbg.declare(metadata %foo* %0, metadata !20, metadata !DIExpression()), !dbg !21 + %this = alloca %foo*, align 8 + store %foo* %0, %foo** %this, align 8 ret void, !dbg !21 } define void @bar(%bar* %0) !dbg !22 { entry: call void @llvm.dbg.declare(metadata %bar* %0, metadata !25, metadata !DIExpression()), !dbg !26 + %this = alloca %bar*, align 8 + store %bar* %0, %bar** %this, align 8 %__foo = getelementptr inbounds %bar, %bar* %0, i32 0, i32 0 ret void, !dbg !26 } @@ -986,7 +1020,7 @@ END_FUNCTION ", ); - insta::assert_snapshot!(result, @r###" + insta::assert_snapshot!(result, @r#" ; ModuleID = '' source_filename = "" @@ -1002,6 +1036,8 @@ END_FUNCTION define void @parent(%parent* %0) !dbg !23 { entry: call void @llvm.dbg.declare(metadata %parent* %0, metadata !27, metadata !DIExpression()), !dbg !28 + %this = alloca %parent*, align 8 + store %parent* %0, %parent** %this, align 8 %a = getelementptr inbounds %parent, %parent* %0, i32 0, i32 0 ret void, !dbg !28 } @@ -1009,6 +1045,8 @@ END_FUNCTION define void @child(%child* %0) !dbg !29 { entry: call void @llvm.dbg.declare(metadata %child* %0, metadata !32, metadata !DIExpression()), !dbg !33 + %this = alloca %child*, align 8 + store %child* %0, %child** %this, align 8 %__parent = getelementptr inbounds %child, %child* %0, i32 0, i32 0 %b = getelementptr inbounds %child, %child* %0, i32 0, i32 1 ret void, !dbg !33 @@ -1017,6 +1055,8 @@ END_FUNCTION define void @grandchild(%grandchild* %0) !dbg !34 { entry: call void @llvm.dbg.declare(metadata %grandchild* %0, metadata !37, metadata !DIExpression()), !dbg !38 + %this = alloca %grandchild*, align 8 + store %grandchild* %0, %grandchild** %this, align 8 %__child = getelementptr inbounds %grandchild, %grandchild* %0, i32 0, i32 0 %c = getelementptr inbounds %grandchild, %grandchild* %0, i32 0, i32 1 ret void, !dbg !38 @@ -1275,5 +1315,5 @@ END_FUNCTION !84 = !DILocation(line: 53, column: 4, scope: !39) !85 = !DILocation(line: 54, column: 4, scope: !39) !86 = !DILocation(line: 56, scope: !39) - "###); + "#); } diff --git a/src/codegen/tests/oop_tests/super_tests.rs b/src/codegen/tests/oop_tests/super_tests.rs index b13ab2a4ee..9133e371e8 100644 --- a/src/codegen/tests/oop_tests/super_tests.rs +++ b/src/codegen/tests/oop_tests/super_tests.rs @@ -29,12 +29,16 @@ fn super_keyword_basic_access() { define void @parent(%parent* %0) { entry: + %this = alloca %parent*, align 8 + store %parent* %0, %parent** %this, align 8 %x = getelementptr inbounds %parent, %parent* %0, i32 0, i32 0 ret void } define void @child(%child* %0) { entry: + %this = alloca %child*, align 8 + store %child* %0, %child** %this, align 8 %__parent = getelementptr inbounds %child, %child* %0, i32 0, i32 0 %x = getelementptr inbounds %parent, %parent* %__parent, i32 0, i32 0 store i16 20, i16* %x, align 2 @@ -97,12 +101,16 @@ fn super_without_deref() { define void @parent(%parent* %0) { entry: + %this = alloca %parent*, align 8 + store %parent* %0, %parent** %this, align 8 %x = getelementptr inbounds %parent, %parent* %0, i32 0, i32 0 ret void } define void @child(%child* %0) { entry: + %this = alloca %child*, align 8 + store %child* %0, %child** %this, align 8 %__parent = getelementptr inbounds %child, %child* %0, i32 0, i32 0 %p = getelementptr inbounds %child, %child* %0, i32 0, i32 1 store %parent* %__parent, %parent** %p, align 8 @@ -172,12 +180,16 @@ fn super_in_method_calls() { define void @parent(%parent* %0) { entry: + %this = alloca %parent*, align 8 + store %parent* %0, %parent** %this, align 8 %value = getelementptr inbounds %parent, %parent* %0, i32 0, i32 0 ret void } define i16 @parent_process(%parent* %0) { entry: + %this = alloca %parent*, align 8 + store %parent* %0, %parent** %this, align 8 %value = getelementptr inbounds %parent, %parent* %0, i32 0, i32 0 %parent.process = alloca i16, align 2 store i16 0, i16* %parent.process, align 2 @@ -192,12 +204,16 @@ fn super_in_method_calls() { define void @child(%child* %0) { entry: + %this = alloca %child*, align 8 + store %child* %0, %child** %this, align 8 %__parent = getelementptr inbounds %child, %child* %0, i32 0, i32 0 ret void } define i16 @child_process(%child* %0) { entry: + %this = alloca %child*, align 8 + store %child* %0, %child** %this, align 8 %__parent = getelementptr inbounds %child, %child* %0, i32 0, i32 0 %child.process = alloca i16, align 2 store i16 0, i16* %child.process, align 2 @@ -213,6 +229,8 @@ fn super_in_method_calls() { define i16 @child_test(%child* %0) { entry: + %this = alloca %child*, align 8 + store %child* %0, %child** %this, align 8 %__parent = getelementptr inbounds %child, %child* %0, i32 0, i32 0 %child.test = alloca i16, align 2 store i16 0, i16* %child.test, align 2 @@ -279,6 +297,8 @@ fn super_in_complex_expressions() { define void @parent(%parent* %0) { entry: + %this = alloca %parent*, align 8 + store %parent* %0, %parent** %this, align 8 %x = getelementptr inbounds %parent, %parent* %0, i32 0, i32 0 %y = getelementptr inbounds %parent, %parent* %0, i32 0, i32 1 ret void @@ -286,6 +306,8 @@ fn super_in_complex_expressions() { define void @child(%child* %0) { entry: + %this = alloca %child*, align 8 + store %child* %0, %child** %this, align 8 %__parent = getelementptr inbounds %child, %child* %0, i32 0, i32 0 %z = getelementptr inbounds %child, %child* %0, i32 0, i32 1 %x = getelementptr inbounds %parent, %parent* %__parent, i32 0, i32 0 @@ -358,12 +380,16 @@ fn super_with_array_access() { define void @parent(%parent* %0) { entry: + %this = alloca %parent*, align 8 + store %parent* %0, %parent** %this, align 8 %arr = getelementptr inbounds %parent, %parent* %0, i32 0, i32 0 ret void } define void @child(%child* %0) { entry: + %this = alloca %child*, align 8 + store %child* %0, %child** %this, align 8 %__parent = getelementptr inbounds %child, %child* %0, i32 0, i32 0 %index = getelementptr inbounds %child, %child* %0, i32 0, i32 1 %arr = getelementptr inbounds %parent, %parent* %__parent, i32 0, i32 0 @@ -451,12 +477,16 @@ fn super_in_multi_level_inheritance() { define void @grandparent(%grandparent* %0) { entry: + %this = alloca %grandparent*, align 8 + store %grandparent* %0, %grandparent** %this, align 8 %g_val = getelementptr inbounds %grandparent, %grandparent* %0, i32 0, i32 0 ret void } define i16 @grandparent_gp_method(%grandparent* %0) { entry: + %this = alloca %grandparent*, align 8 + store %grandparent* %0, %grandparent** %this, align 8 %g_val = getelementptr inbounds %grandparent, %grandparent* %0, i32 0, i32 0 %grandparent.gp_method = alloca i16, align 2 store i16 0, i16* %grandparent.gp_method, align 2 @@ -468,6 +498,8 @@ fn super_in_multi_level_inheritance() { define void @parent(%parent* %0) { entry: + %this = alloca %parent*, align 8 + store %parent* %0, %parent** %this, align 8 %__grandparent = getelementptr inbounds %parent, %parent* %0, i32 0, i32 0 %p_val = getelementptr inbounds %parent, %parent* %0, i32 0, i32 1 ret void @@ -475,6 +507,8 @@ fn super_in_multi_level_inheritance() { define i16 @parent_p_method(%parent* %0) { entry: + %this = alloca %parent*, align 8 + store %parent* %0, %parent** %this, align 8 %__grandparent = getelementptr inbounds %parent, %parent* %0, i32 0, i32 0 %p_val = getelementptr inbounds %parent, %parent* %0, i32 0, i32 1 %parent.p_method = alloca i16, align 2 @@ -492,6 +526,8 @@ fn super_in_multi_level_inheritance() { define void @child(%child* %0) { entry: + %this = alloca %child*, align 8 + store %child* %0, %child** %this, align 8 %__parent = getelementptr inbounds %child, %child* %0, i32 0, i32 0 %c_val = getelementptr inbounds %child, %child* %0, i32 0, i32 1 ret void @@ -499,6 +535,8 @@ fn super_in_multi_level_inheritance() { define i16 @child_test(%child* %0) { entry: + %this = alloca %child*, align 8 + store %child* %0, %child** %this, align 8 %__parent = getelementptr inbounds %child, %child* %0, i32 0, i32 0 %c_val = getelementptr inbounds %child, %child* %0, i32 0, i32 1 %child.test = alloca i16, align 2 @@ -575,6 +613,8 @@ fn super_with_pointer_operations() { define void @parent(%parent* %0) { entry: + %this = alloca %parent*, align 8 + store %parent* %0, %parent** %this, align 8 %val = getelementptr inbounds %parent, %parent* %0, i32 0, i32 0 %ptr = getelementptr inbounds %parent, %parent* %0, i32 0, i32 1 ret void @@ -582,6 +622,8 @@ fn super_with_pointer_operations() { define void @child(%child* %0) { entry: + %this = alloca %child*, align 8 + store %child* %0, %child** %this, align 8 %__parent = getelementptr inbounds %child, %child* %0, i32 0, i32 0 %ptr = getelementptr inbounds %parent, %parent* %__parent, i32 0, i32 1 %val = getelementptr inbounds %parent, %parent* %__parent, i32 0, i32 0 @@ -663,6 +705,8 @@ fn super_in_conditionals() { define void @parent(%parent* %0) { entry: + %this = alloca %parent*, align 8 + store %parent* %0, %parent** %this, align 8 %threshold = getelementptr inbounds %parent, %parent* %0, i32 0, i32 0 %value = getelementptr inbounds %parent, %parent* %0, i32 0, i32 1 ret void @@ -670,12 +714,16 @@ fn super_in_conditionals() { define void @child(%child* %0) { entry: + %this = alloca %child*, align 8 + store %child* %0, %child** %this, align 8 %__parent = getelementptr inbounds %child, %child* %0, i32 0, i32 0 ret void } define void @child_test(%child* %0) { entry: + %this = alloca %child*, align 8 + store %child* %0, %child** %this, align 8 %__parent = getelementptr inbounds %child, %child* %0, i32 0, i32 0 %value = getelementptr inbounds %parent, %parent* %__parent, i32 0, i32 1 %load_value = load i16, i16* %value, align 2 @@ -779,6 +827,8 @@ fn super_with_const_variables() { define void @parent(%parent* %0) { entry: + %this = alloca %parent*, align 8 + store %parent* %0, %parent** %this, align 8 %MAX_VALUE = getelementptr inbounds %parent, %parent* %0, i32 0, i32 0 %current = getelementptr inbounds %parent, %parent* %0, i32 0, i32 1 ret void @@ -786,6 +836,8 @@ fn super_with_const_variables() { define void @child(%child* %0) { entry: + %this = alloca %child*, align 8 + store %child* %0, %child** %this, align 8 %__parent = getelementptr inbounds %child, %child* %0, i32 0, i32 0 %current = getelementptr inbounds %parent, %parent* %__parent, i32 0, i32 1 store i16 50, i16* %current, align 2 @@ -862,18 +914,24 @@ fn super_as_function_parameter() { define void @parent(%parent* %0) { entry: + %this = alloca %parent*, align 8 + store %parent* %0, %parent** %this, align 8 %val = getelementptr inbounds %parent, %parent* %0, i32 0, i32 0 ret void } define void @child(%child* %0) { entry: + %this = alloca %child*, align 8 + store %child* %0, %child** %this, align 8 %__parent = getelementptr inbounds %child, %child* %0, i32 0, i32 0 ret void } define void @child_test(%child* %0) { entry: + %this = alloca %child*, align 8 + store %child* %0, %child** %this, align 8 %__parent = getelementptr inbounds %child, %child* %0, i32 0, i32 0 %call = call i16 @process_ref(%parent* %__parent) %call1 = call i16 @process_val(%parent* %__parent) @@ -973,6 +1031,8 @@ fn super_with_deeply_nested_expressions() { define void @parent(%parent* %0) { entry: + %this = alloca %parent*, align 8 + store %parent* %0, %parent** %this, align 8 %a = getelementptr inbounds %parent, %parent* %0, i32 0, i32 0 %b = getelementptr inbounds %parent, %parent* %0, i32 0, i32 1 %c = getelementptr inbounds %parent, %parent* %0, i32 0, i32 2 @@ -981,6 +1041,8 @@ fn super_with_deeply_nested_expressions() { define i16 @parent_calc(%parent* %0) { entry: + %this = alloca %parent*, align 8 + store %parent* %0, %parent** %this, align 8 %a = getelementptr inbounds %parent, %parent* %0, i32 0, i32 0 %b = getelementptr inbounds %parent, %parent* %0, i32 0, i32 1 %c = getelementptr inbounds %parent, %parent* %0, i32 0, i32 2 @@ -1002,12 +1064,16 @@ fn super_with_deeply_nested_expressions() { define void @child(%child* %0) { entry: + %this = alloca %child*, align 8 + store %child* %0, %child** %this, align 8 %__parent = getelementptr inbounds %child, %child* %0, i32 0, i32 0 ret void } define i16 @child_test(%child* %0) { entry: + %this = alloca %child*, align 8 + store %child* %0, %child** %this, align 8 %__parent = getelementptr inbounds %child, %child* %0, i32 0, i32 0 %child.test = alloca i16, align 2 store i16 0, i16* %child.test, align 2 @@ -1116,6 +1182,8 @@ fn super_in_loop_constructs() { define void @parent(%parent* %0) { entry: + %this = alloca %parent*, align 8 + store %parent* %0, %parent** %this, align 8 %counter = getelementptr inbounds %parent, %parent* %0, i32 0, i32 0 %arr = getelementptr inbounds %parent, %parent* %0, i32 0, i32 1 ret void @@ -1123,6 +1191,8 @@ fn super_in_loop_constructs() { define void @parent_increment(%parent* %0) { entry: + %this = alloca %parent*, align 8 + store %parent* %0, %parent** %this, align 8 %counter = getelementptr inbounds %parent, %parent* %0, i32 0, i32 0 %arr = getelementptr inbounds %parent, %parent* %0, i32 0, i32 1 %load_counter = load i16, i16* %counter, align 2 @@ -1135,12 +1205,16 @@ fn super_in_loop_constructs() { define void @child(%child* %0) { entry: + %this = alloca %child*, align 8 + store %child* %0, %child** %this, align 8 %__parent = getelementptr inbounds %child, %child* %0, i32 0, i32 0 ret void } define void @child_process(%child* %0) { entry: + %this = alloca %child*, align 8 + store %child* %0, %child** %this, align 8 %__parent = getelementptr inbounds %child, %child* %0, i32 0, i32 0 %i = alloca i16, align 2 %sum = alloca i16, align 2 @@ -1311,11 +1385,15 @@ fn super_with_method_overrides_in_three_levels() { define void @grandparent(%grandparent* %0) { entry: + %this = alloca %grandparent*, align 8 + store %grandparent* %0, %grandparent** %this, align 8 ret void } define i16 @grandparent_calculate(%grandparent* %0) { entry: + %this = alloca %grandparent*, align 8 + store %grandparent* %0, %grandparent** %this, align 8 %grandparent.calculate = alloca i16, align 2 store i16 0, i16* %grandparent.calculate, align 2 store i16 100, i16* %grandparent.calculate, align 2 @@ -1325,12 +1403,16 @@ fn super_with_method_overrides_in_three_levels() { define void @parent(%parent* %0) { entry: + %this = alloca %parent*, align 8 + store %parent* %0, %parent** %this, align 8 %__grandparent = getelementptr inbounds %parent, %parent* %0, i32 0, i32 0 ret void } define i16 @parent_calculate(%parent* %0) { entry: + %this = alloca %parent*, align 8 + store %parent* %0, %parent** %this, align 8 %__grandparent = getelementptr inbounds %parent, %parent* %0, i32 0, i32 0 %parent.calculate = alloca i16, align 2 store i16 0, i16* %parent.calculate, align 2 @@ -1345,12 +1427,16 @@ fn super_with_method_overrides_in_three_levels() { define void @child(%child* %0) { entry: + %this = alloca %child*, align 8 + store %child* %0, %child** %this, align 8 %__parent = getelementptr inbounds %child, %child* %0, i32 0, i32 0 ret void } define i16 @child_calculate(%child* %0) { entry: + %this = alloca %child*, align 8 + store %child* %0, %child** %this, align 8 %__parent = getelementptr inbounds %child, %child* %0, i32 0, i32 0 %child.calculate = alloca i16, align 2 store i16 0, i16* %child.calculate, align 2 @@ -1493,6 +1579,8 @@ fn super_with_structured_types() { define void @parent(%parent* %0) { entry: + %this = alloca %parent*, align 8 + store %parent* %0, %parent** %this, align 8 %data = getelementptr inbounds %parent, %parent* %0, i32 0, i32 0 %arr_data = getelementptr inbounds %parent, %parent* %0, i32 0, i32 1 ret void @@ -1500,12 +1588,16 @@ fn super_with_structured_types() { define void @child(%child* %0) { entry: + %this = alloca %child*, align 8 + store %child* %0, %child** %this, align 8 %__parent = getelementptr inbounds %child, %child* %0, i32 0, i32 0 ret void } define void @child_test(%child* %0) { entry: + %this = alloca %child*, align 8 + store %child* %0, %child** %this, align 8 %__parent = getelementptr inbounds %child, %child* %0, i32 0, i32 0 %local_data = alloca %Complex_Type, align 8 %1 = bitcast %Complex_Type* %local_data to i8* @@ -1620,12 +1712,16 @@ fn super_in_action_blocks() { define void @parent(%parent* %0) { entry: + %this = alloca %parent*, align 8 + store %parent* %0, %parent** %this, align 8 %value = getelementptr inbounds %parent, %parent* %0, i32 0, i32 0 ret void } define void @parent_increment(%parent* %0) { entry: + %this = alloca %parent*, align 8 + store %parent* %0, %parent** %this, align 8 %value = getelementptr inbounds %parent, %parent* %0, i32 0, i32 0 %load_value = load i16, i16* %value, align 2 %1 = sext i16 %load_value to i32 @@ -1637,6 +1733,8 @@ fn super_in_action_blocks() { define void @child(%child* %0) { entry: + %this = alloca %child*, align 8 + store %child* %0, %child** %this, align 8 %__parent = getelementptr inbounds %child, %child* %0, i32 0, i32 0 ret void } From bdecceea0c638789dd716de2a4ee56dc8517a770 Mon Sep 17 00:00:00 2001 From: abroooo <61380142+abroooo@users.noreply.github.com> Date: Wed, 30 Apr 2025 09:44:46 +0200 Subject: [PATCH 34/48] add lit tests --- tests/lit/single/oop/fb_method_shadowing.st | 4 +- tests/lit/single/oop/this_basic_access.st | 40 +++++++++++ .../single/oop/this_in_control_structures.st | 67 ++++++++++++++++++ .../this_with_parenthesized_expressions.st | 69 +++++++++++++++++++ tests/lit/single/oop/this_without_deref.st | 32 +++++++++ 5 files changed, 210 insertions(+), 2 deletions(-) create mode 100644 tests/lit/single/oop/this_basic_access.st create mode 100644 tests/lit/single/oop/this_in_control_structures.st create mode 100644 tests/lit/single/oop/this_with_parenthesized_expressions.st create mode 100644 tests/lit/single/oop/this_without_deref.st diff --git a/tests/lit/single/oop/fb_method_shadowing.st b/tests/lit/single/oop/fb_method_shadowing.st index 74eebeb43c..06aacc5f44 100644 --- a/tests/lit/single/oop/fb_method_shadowing.st +++ b/tests/lit/single/oop/fb_method_shadowing.st @@ -21,7 +21,7 @@ END_VAR bar: DINT := 17; END_VAR this^.bar := in + bar; - addToBar := this^.bar; + addToLocalBar := this^.bar; END_METHOD addToBar(3); @@ -37,5 +37,5 @@ END_VAR x := fb.addToBar(3); printf('%d$N', x); // CHECK: 20 x := fb.addToLocalBar(46); - printf('%d$N', x); // CHECK: 88 + printf('%d$N', x); // CHECK: 63 END_FUNCTION diff --git a/tests/lit/single/oop/this_basic_access.st b/tests/lit/single/oop/this_basic_access.st new file mode 100644 index 0000000000..f3df8264d2 --- /dev/null +++ b/tests/lit/single/oop/this_basic_access.st @@ -0,0 +1,40 @@ +// RUN: (%COMPILE %s && %RUN) | %CHECK %s +FUNCTION_BLOCK fb +VAR + x : INT := 10; + y : INT := 20; +END_VAR + +METHOD get_sum : INT + get_sum := x + y; +END_METHOD +METHOD get_values : INT + VAR + x : INT := 100; // Shadows fb's x + y : INT := 200; // Shadows fb's y + END_VAR + VAR_INPUT + use_function_block : BOOL; + END_VAR + + IF use_function_block THEN + get_values := THIS^.x + y; // Use fb's x but local y + ELSE + get_values := x + y; // Use shadowed x and y + END_IF; +END_METHOD + + printf('x = %d$N', x); // CHECK: x = 10 + printf('THIS^.x = %d$N', THIS^.x); // CHECK: THIS^.x = 10 + printf('y = %d$N', y); // CHECK: y = 20 + printf('get_values(TRUE) = %d$N', get_values(TRUE)); // CHECK: get_values(TRUE) = 210 + printf('get_values(FALSE) = %d$N', get_values(FALSE)); // CHECK: get_values(FALSE) = 300 + printf('THIS^.get_sum() = %d$N', THIS^.get_sum()); // CHECK: THIS^.get_sum() = 30 +END_FUNCTION_BLOCK + +FUNCTION main : INT +VAR + inst : fb; +END_VAR + inst(); +END_FUNCTION diff --git a/tests/lit/single/oop/this_in_control_structures.st b/tests/lit/single/oop/this_in_control_structures.st new file mode 100644 index 0000000000..6b3a606e82 --- /dev/null +++ b/tests/lit/single/oop/this_in_control_structures.st @@ -0,0 +1,67 @@ +// RUN: (%COMPILE %s && %RUN) | %CHECK %s +FUNCTION_BLOCK child +VAR + counter : INT := 0; + threshold : INT := 5; +END_VAR + +METHOD increment : INT + counter := counter + 1; + increment := counter; +END_METHOD + +METHOD reset + counter := 0; +END_METHOD +METHOD test + VAR + i : INT; + result : INT; + END_VAR + + // Reset parent's counter + THIS^.reset(); + + // Use THIS^ in IF statement + // CHECK: Initial counter: 0 + IF THIS^.counter < THIS^.threshold THEN + printf('Initial counter: %d$N', THIS^.counter); + END_IF; + + // Use THIS^ in FOR loop + // CHECK: Loop increment: 1 + // CHECK: Loop increment: 2 + // CHECK: Loop increment: 3 + FOR i := 1 TO 3 BY 1 DO + result := THIS^.increment(); + printf('Loop increment: %d$N', result); + END_FOR; + + // Use THIS^ in CASE statement + CASE THIS^.counter OF + // CHECK: Counter is three + 3: printf('Counter is three$N'); + 4: printf('Counter is four$N'); + END_CASE; + + // Use THIS^ in WHILE loop + // CHECK: While increment: 4 + // CHECK: While increment: 5 + WHILE THIS^.counter < 5 DO + result := THIS^.increment(); + printf('While increment: %d$N', result); + END_WHILE; +END_METHOD + + test(); + + // CHECK: Parent counter: 5 + printf('Parent counter: %d$N', THIS^.counter); +END_FUNCTION_BLOCK + +FUNCTION main : INT +VAR + inst : child; +END_VAR + inst(); +END_FUNCTION diff --git a/tests/lit/single/oop/this_with_parenthesized_expressions.st b/tests/lit/single/oop/this_with_parenthesized_expressions.st new file mode 100644 index 0000000000..18af504f3e --- /dev/null +++ b/tests/lit/single/oop/this_with_parenthesized_expressions.st @@ -0,0 +1,69 @@ +// RUN: (%COMPILE %s && %RUN) | %CHECK %s +FUNCTION_BLOCK fb +VAR + multiplier: INT := 3; + x: INT := 10; + y: INT := 20; + z: INT := 30; +END_VAR + +METHOD calculate: INT + VAR_INPUT + a: INT; + b: INT; + END_VAR + calculate := a * b; +END_METHOD + +METHOD get_value: INT + get_value := x + y + z; +END_METHOD + +METHOD test + VAR + result: INT; + END_VAR + + // Basic parenthesized expression with THIS + result := (THIS^.x + THIS^.y); + // CHECK: Basic parentheses: 30 + printf('Basic parentheses: %d$N', result); + + // Nested parentheses with THIS + result := ((THIS^.x + THIS^.y) * (THIS^.z)); + // CHECK: Nested parentheses: 900 + printf('Nested parentheses: %d$N', result); + + // Parenthesized expression with method call + result := (THIS^.x + THIS^.y + (THIS^.calculate(THIS^.x, THIS^.y))); + // CHECK: Expression with method: 230 + printf('Expression with method: %d$N', result); + + // Complex calculation with mixed precedence and parentheses + result := ((THIS^.x * multiplier) + (THIS^.y * 2) + THIS^.z); + // CHECK: Complex calculation: 100 + printf('Complex calculation: %d$N', result); + + // Conditional expression with parentheses + IF ((THIS^.x + THIS^.y) > THIS^.z) THEN + result := 42; + ELSE + result := 24; + END_IF; + // CHECK: Conditional with parentheses: 24 + printf('Conditional with parentheses: %d$N', result); + + // Combined THIS references across parentheses + result := (THIS^.get_value()) + (THIS^.x * THIS^.y / 2); + // CHECK: Combined THIS references: 160 + printf('Combined THIS references: %d$N', result); +END_METHOD + +END_FUNCTION_BLOCK + +FUNCTION main: INT +VAR + inst: fb; +END_VAR + inst.test(); +END_FUNCTION diff --git a/tests/lit/single/oop/this_without_deref.st b/tests/lit/single/oop/this_without_deref.st new file mode 100644 index 0000000000..a46bf8ad89 --- /dev/null +++ b/tests/lit/single/oop/this_without_deref.st @@ -0,0 +1,32 @@ +// RUN: (%COMPILE %s && %RUN) | %CHECK %s +FUNCTION_BLOCK fb +VAR + p_ref : REF_TO fb; + value : INT := 42; +END_VAR + +METHOD get_value : INT +get_value := value; +END_METHOD + + // THIS without deref creates a reference + p_ref := THIS; + + // Accessing through reference + printf('p_ref^.value = %d$N', p_ref^.value); // CHECK: p_ref^.value = 42 + printf('p_ref^.get_value() = %d$N', p_ref^.get_value()); // CHECK: p_ref^.get_value() = 42 + + // Modifying through reference + p_ref^.value := 100; + printf('THIS^.value after modification = %d$N', THIS^.value); // CHECK: THIS^.value after modification = 100 + + // Compare direct access vs reference access + printf('Are they equal? %d$N', THIS^.value = p_ref^.value); // CHECK: Are they equal? 1 +END_FUNCTION_BLOCK + +FUNCTION main : INT +VAR + inst : fb; +END_VAR + inst(); +END_FUNCTION From e5bb42f1e1cb4d2941644403d17fb1866cda5cb8 Mon Sep 17 00:00:00 2001 From: abroooo <61380142+abroooo@users.noreply.github.com> Date: Wed, 30 Apr 2025 09:17:54 +0000 Subject: [PATCH 35/48] fix snapshots --- src/codegen/tests/code_gen_tests.rs | 10 +++ src/codegen/tests/debug_tests.rs | 4 ++ ...g__implementation_added_as_subroutine.snap | 3 +- ...on_function_pous_have_struct_as_param.snap | 3 +- ...temp_variables_in_pous_added_as_local.snap | 3 +- src/codegen/tests/directaccess_test.rs | 16 +++-- .../complex_initializers.rs | 64 +++++++++++++++++++ ...lues_in_global_variables_out_of_order.snap | 3 +- ..._block_struct_initialized_in_function.snap | 3 +- ..._initial_values_in_function_block_pou.snap | 3 +- ...lizers__initial_values_in_fb_variable.snap | 3 +- src/codegen/tests/oop_tests.rs | 49 ++++++++++++++ src/codegen/tests/parameters_tests.rs | 8 ++- ...ion_in_function_blocks_are_propagated.snap | 3 +- ...n_tests__fb_method_called_as_function.snap | 5 +- ...sts__code_gen_tests__fb_method_in_pou.snap | 5 +- ..._gen_tests__fb_method_with_var_in_out.snap | 5 +- ...ts__fb_method_with_var_input_defaults.snap | 5 +- ...n_tests__function_block_instance_call.snap | 3 +- ...unction_block_qualified_instance_call.snap | 5 +- ...method_codegen_with_initialized_input.snap | 5 +- ...s__method_codegen_with_multiple_input.snap | 5 +- ...ts__method_with_aggregate_return_type.snap | 5 +- ...s__code_gen_tests__methods_var_output.snap | 5 +- ...fied_action_from_fb_called_in_program.snap | 3 +- ...e_gen_tests__reference_qualified_name.snap | 3 +- ...ts__returning_early_in_function_block.snap | 3 +- ...n_tests__enum_referenced_in_fb_nested.snap | 6 ++ ...ts__function_defined_in_external_file.snap | 2 + ...ccepts_empty_statement_as_input_param.snap | 3 +- ...cepts_empty_statement_as_output_param.snap | 3 +- ...unction_block_pointer_are_assigned_to.snap | 3 +- ...var_in_out_params_can_be_out_of_order.snap | 3 +- .../parse_error_containers_tests.rs | 8 +-- .../tests/resolve_expressions_tests.rs | 2 +- .../tests/super_keyword_validation_tests.rs | 16 ++++- 36 files changed, 240 insertions(+), 38 deletions(-) diff --git a/src/codegen/tests/code_gen_tests.rs b/src/codegen/tests/code_gen_tests.rs index 26f29a137a..c6a0cf036e 100644 --- a/src/codegen/tests/code_gen_tests.rs +++ b/src/codegen/tests/code_gen_tests.rs @@ -1114,6 +1114,8 @@ fn fb_method_called_locally() { define void @foo(%foo* %0) { entry: + %this = alloca %foo*, align 8 + store %foo* %0, %foo** %this, align 8 %bar = getelementptr inbounds %foo, %foo* %0, i32 0, i32 0 %call = call i32 @foo_addToBar(%foo* %0, i16 42) ret void @@ -1121,6 +1123,8 @@ fn fb_method_called_locally() { define i32 @foo_addToBar(%foo* %0, i16 %1) { entry: + %this = alloca %foo*, align 8 + store %foo* %0, %foo** %this, align 8 %bar = getelementptr inbounds %foo, %foo* %0, i32 0, i32 0 %foo.addToBar = alloca i32, align 4 %in = alloca i16, align 2 @@ -1197,6 +1201,8 @@ fn fb_local_method_var_shadows_parent_var() { define void @foo(%foo* %0) { entry: + %this = alloca %foo*, align 8 + store %foo* %0, %foo** %this, align 8 %bar = getelementptr inbounds %foo, %foo* %0, i32 0, i32 0 %call = call i32 @foo_addToBar(%foo* %0, i16 42) ret void @@ -1204,6 +1210,8 @@ fn fb_local_method_var_shadows_parent_var() { define i32 @foo_addToBar(%foo* %0, i16 %1) { entry: + %this = alloca %foo*, align 8 + store %foo* %0, %foo** %this, align 8 %bar = getelementptr inbounds %foo, %foo* %0, i32 0, i32 0 %foo.addToBar = alloca i32, align 4 %in = alloca i16, align 2 @@ -3969,6 +3977,8 @@ fn variables_in_var_external_block_are_not_generated() { define void @bar(%bar* %0) { entry: + %this = alloca %bar*, align 8 + store %bar* %0, %bar** %this, align 8 ret void } diff --git a/src/codegen/tests/debug_tests.rs b/src/codegen/tests/debug_tests.rs index 1539cbb55a..0e50509065 100644 --- a/src/codegen/tests/debug_tests.rs +++ b/src/codegen/tests/debug_tests.rs @@ -361,12 +361,16 @@ fn dbg_declare_has_valid_metadata_references_for_methods() { define void @fb(%fb* %0) !dbg !9 { entry: call void @llvm.dbg.declare(metadata %fb* %0, metadata !12, metadata !DIExpression()), !dbg !13 + %this = alloca %fb*, align 8 + store %fb* %0, %fb** %this, align 8 ret void, !dbg !13 } define void @fb_foo(%fb* %0) !dbg !14 { entry: call void @llvm.dbg.declare(metadata %fb* %0, metadata !15, metadata !DIExpression()), !dbg !16 + %this = alloca %fb*, align 8 + store %fb* %0, %fb** %this, align 8 ret void, !dbg !16 } diff --git a/src/codegen/tests/debug_tests/snapshots/rusty__codegen__tests__debug_tests__expression_debugging__implementation_added_as_subroutine.snap b/src/codegen/tests/debug_tests/snapshots/rusty__codegen__tests__debug_tests__expression_debugging__implementation_added_as_subroutine.snap index 3a4e58573d..910b5fb0e8 100644 --- a/src/codegen/tests/debug_tests/snapshots/rusty__codegen__tests__debug_tests__expression_debugging__implementation_added_as_subroutine.snap +++ b/src/codegen/tests/debug_tests/snapshots/rusty__codegen__tests__debug_tests__expression_debugging__implementation_added_as_subroutine.snap @@ -1,7 +1,6 @@ --- source: src/codegen/tests/debug_tests/expression_debugging.rs expression: result -snapshot_kind: text --- ; ModuleID = '' source_filename = "" @@ -30,6 +29,8 @@ entry: define void @myFb(%myFb* %0) !dbg !25 { entry: call void @llvm.dbg.declare(metadata %myFb* %0, metadata !28, metadata !DIExpression()), !dbg !29 + %this = alloca %myFb*, align 8 + store %myFb* %0, %myFb** %this, align 8 ret void, !dbg !29 } diff --git a/src/codegen/tests/debug_tests/snapshots/rusty__codegen__tests__debug_tests__expression_debugging__non_function_pous_have_struct_as_param.snap b/src/codegen/tests/debug_tests/snapshots/rusty__codegen__tests__debug_tests__expression_debugging__non_function_pous_have_struct_as_param.snap index bd92a495a1..5595711878 100644 --- a/src/codegen/tests/debug_tests/snapshots/rusty__codegen__tests__debug_tests__expression_debugging__non_function_pous_have_struct_as_param.snap +++ b/src/codegen/tests/debug_tests/snapshots/rusty__codegen__tests__debug_tests__expression_debugging__non_function_pous_have_struct_as_param.snap @@ -1,7 +1,6 @@ --- source: src/codegen/tests/debug_tests/expression_debugging.rs expression: result -snapshot_kind: text --- ; ModuleID = '' source_filename = "" @@ -25,6 +24,8 @@ entry: define void @fb(%fb* %0) !dbg !24 { entry: call void @llvm.dbg.declare(metadata %fb* %0, metadata !27, metadata !DIExpression()), !dbg !28 + %this = alloca %fb*, align 8 + store %fb* %0, %fb** %this, align 8 %x = getelementptr inbounds %fb, %fb* %0, i32 0, i32 0 %load_x = load i32, i32* %x, align 4, !dbg !28 %tmpVar = add i32 %load_x, 2, !dbg !28 diff --git a/src/codegen/tests/debug_tests/snapshots/rusty__codegen__tests__debug_tests__expression_debugging__var_and_vartemp_variables_in_pous_added_as_local.snap b/src/codegen/tests/debug_tests/snapshots/rusty__codegen__tests__debug_tests__expression_debugging__var_and_vartemp_variables_in_pous_added_as_local.snap index 2e6d2419c4..25077e3be0 100644 --- a/src/codegen/tests/debug_tests/snapshots/rusty__codegen__tests__debug_tests__expression_debugging__var_and_vartemp_variables_in_pous_added_as_local.snap +++ b/src/codegen/tests/debug_tests/snapshots/rusty__codegen__tests__debug_tests__expression_debugging__var_and_vartemp_variables_in_pous_added_as_local.snap @@ -1,7 +1,6 @@ --- source: src/codegen/tests/debug_tests/expression_debugging.rs expression: result -snapshot_kind: text --- ; ModuleID = '' source_filename = "" @@ -48,6 +47,8 @@ entry: define void @myFb(%myFb* %0) !dbg !37 { entry: call void @llvm.dbg.declare(metadata %myFb* %0, metadata !40, metadata !DIExpression()), !dbg !41 + %this = alloca %myFb*, align 8 + store %myFb* %0, %myFb** %this, align 8 %a = alloca i32, align 4 %b = alloca i32, align 4 %c = alloca i32, align 4 diff --git a/src/codegen/tests/directaccess_test.rs b/src/codegen/tests/directaccess_test.rs index 4a8251fc21..b92ca94c3d 100644 --- a/src/codegen/tests/directaccess_test.rs +++ b/src/codegen/tests/directaccess_test.rs @@ -141,7 +141,7 @@ fn direct_acess_in_output_assignment_implicit_explicit_and_mixed() { ", ); - assert_snapshot!(ir, @r###" + assert_snapshot!(ir, @r#" ; ModuleID = '' source_filename = "" @@ -151,6 +151,8 @@ fn direct_acess_in_output_assignment_implicit_explicit_and_mixed() { define void @FOO(%FOO* %0) { entry: + %this = alloca %FOO*, align 8 + store %FOO* %0, %FOO** %this, align 8 %X = getelementptr inbounds %FOO, %FOO* %0, i32 0, i32 0 %Y = getelementptr inbounds %FOO, %FOO* %0, i32 0, i32 1 ret void @@ -218,7 +220,7 @@ fn direct_acess_in_output_assignment_implicit_explicit_and_mixed() { 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 } - "###); + "#); } #[test] @@ -252,6 +254,8 @@ fn direct_acess_in_output_assignment_with_simple_expression() { define void @FOO(%FOO* %0) { entry: + %this = alloca %FOO*, align 8 + store %FOO* %0, %FOO** %this, align 8 %Q = getelementptr inbounds %FOO, %FOO* %0, i32 0, i32 0 ret void } @@ -305,7 +309,7 @@ fn direct_acess_in_output_assignment_with_simple_expression_implicit() { ", ); - assert_snapshot!(ir, @r###" + assert_snapshot!(ir, @r#" ; ModuleID = '' source_filename = "" @@ -315,6 +319,8 @@ fn direct_acess_in_output_assignment_with_simple_expression_implicit() { define void @FOO(%FOO* %0) { entry: + %this = alloca %FOO*, align 8 + store %FOO* %0, %FOO** %this, align 8 %Q = getelementptr inbounds %FOO, %FOO* %0, i32 0, i32 0 ret void } @@ -344,7 +350,7 @@ fn direct_acess_in_output_assignment_with_simple_expression_implicit() { 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 } - "###); + "#); } #[test] @@ -391,6 +397,8 @@ fn direct_acess_in_output_assignment_with_complexe_expression() { define void @QUUX(%QUUX* %0) { entry: + %this = alloca %QUUX*, align 8 + store %QUUX* %0, %QUUX** %this, align 8 %Q = getelementptr inbounds %QUUX, %QUUX* %0, i32 0, i32 0 ret void } diff --git a/src/codegen/tests/initialization_test/complex_initializers.rs b/src/codegen/tests/initialization_test/complex_initializers.rs index 857e3c94bf..0145db3e31 100644 --- a/src/codegen/tests/initialization_test/complex_initializers.rs +++ b/src/codegen/tests/initialization_test/complex_initializers.rs @@ -273,6 +273,8 @@ fn init_functions_generated_for_function_blocks() { define void @foo(%foo* %0) { entry: + %this = alloca %foo*, align 8 + store %foo* %0, %foo** %this, align 8 %to_init = getelementptr inbounds %foo, %foo* %0, i32 0, i32 0 ret void } @@ -386,6 +388,8 @@ fn nested_initializer_pous() { define void @foo(%foo* %0) { entry: + %this = alloca %foo*, align 8 + store %foo* %0, %foo** %this, align 8 %str_ref = getelementptr inbounds %foo, %foo* %0, i32 0, i32 0 %b = getelementptr inbounds %foo, %foo* %0, i32 0, i32 1 call void @bar_print(%bar* %b) @@ -395,6 +399,8 @@ fn nested_initializer_pous() { define void @bar(%bar* %0) { entry: + %this = alloca %bar*, align 8 + store %bar* %0, %bar** %this, align 8 %b = getelementptr inbounds %bar, %bar* %0, i32 0, i32 0 call void @baz_print(%baz* %b) ret void @@ -402,6 +408,8 @@ fn nested_initializer_pous() { define void @baz(%baz* %0) { entry: + %this = alloca %baz*, align 8 + store %baz* %0, %baz** %this, align 8 %str_ref = getelementptr inbounds %baz, %baz* %0, i32 0, i32 0 ret void } @@ -831,11 +839,15 @@ fn stateful_pous_methods_and_structs_get_init_functions() { define void @foo(%foo* %0) { entry: + %this = alloca %foo*, align 8 + store %foo* %0, %foo** %this, align 8 ret void } define void @foo_m(%foo* %0) { entry: + %this = alloca %foo*, align 8 + store %foo* %0, %foo** %this, align 8 ret void } @@ -952,6 +964,8 @@ fn global_instance() { define void @foo(%foo* %0) { entry: + %this = alloca %foo*, align 8 + store %foo* %0, %foo** %this, align 8 %s = getelementptr inbounds %foo, %foo* %0, i32 0, i32 0 ret void } @@ -1049,6 +1063,8 @@ fn aliased_types() { define void @foo(%foo* %0) { entry: + %this = alloca %foo*, align 8 + store %foo* %0, %foo** %this, align 8 %s = getelementptr inbounds %foo, %foo* %0, i32 0, i32 0 ret void } @@ -1218,6 +1234,8 @@ fn var_config_aliased_variables_initialized() { define void @FB(%FB* %0) { entry: + %this = alloca %FB*, align 8 + store %FB* %0, %FB** %this, align 8 %foo = getelementptr inbounds %FB, %FB* %0, i32 0, i32 0 ret void } @@ -1325,6 +1343,8 @@ fn var_external_blocks_are_ignored_in_init_functions() { define void @foo(%foo* %0) { entry: + %this = alloca %foo*, align 8 + store %foo* %0, %foo** %this, align 8 ret void } @@ -1384,6 +1404,8 @@ fn ref_to_local_member() { define void @foo(%foo* %0) { entry: + %this = alloca %foo*, align 8 + store %foo* %0, %foo** %this, align 8 %s = getelementptr inbounds %foo, %foo* %0, i32 0, i32 0 %ptr = getelementptr inbounds %foo, %foo* %0, i32 0, i32 1 %alias = getelementptr inbounds %foo, %foo* %0, i32 0, i32 2 @@ -1461,6 +1483,8 @@ fn ref_to_local_member_shadows_global() { define void @foo(%foo* %0) { entry: + %this = alloca %foo*, align 8 + store %foo* %0, %foo** %this, align 8 %s = getelementptr inbounds %foo, %foo* %0, i32 0, i32 0 %ptr = getelementptr inbounds %foo, %foo* %0, i32 0, i32 1 %alias = getelementptr inbounds %foo, %foo* %0, i32 0, i32 2 @@ -1535,6 +1559,8 @@ fn temporary_variable_ref_to_local_member() { define void @foo(%foo* %0) { entry: + %this = alloca %foo*, align 8 + store %foo* %0, %foo** %this, align 8 %s = getelementptr inbounds %foo, %foo* %0, i32 0, i32 0 %ptr = alloca [81 x i8]*, align 8 %alias = alloca [81 x i8]*, align 8 @@ -1654,11 +1680,15 @@ fn initializing_method_variables_with_refs() { define void @foo(%foo* %0) { entry: + %this = alloca %foo*, align 8 + store %foo* %0, %foo** %this, align 8 ret void } define void @foo_bar(%foo* %0) { entry: + %this = alloca %foo*, align 8 + store %foo* %0, %foo** %this, align 8 %x = alloca i32, align 4 %px = alloca i32*, align 8 store i32 10, i32* %x, align 4 @@ -1720,12 +1750,16 @@ fn initializing_method_variables_with_refs_referencing_parent_pou_variable() { define void @foo(%foo* %0) { entry: + %this = alloca %foo*, align 8 + store %foo* %0, %foo** %this, align 8 %x = getelementptr inbounds %foo, %foo* %0, i32 0, i32 0 ret void } define void @foo_bar(%foo* %0) { entry: + %this = alloca %foo*, align 8 + store %foo* %0, %foo** %this, align 8 %x = getelementptr inbounds %foo, %foo* %0, i32 0, i32 0 %px = alloca i32*, align 8 store i32* %x, i32** %px, align 8 @@ -1787,11 +1821,15 @@ fn initializing_method_variables_with_refs_referencing_global_variable() { define void @foo(%foo* %0) { entry: + %this = alloca %foo*, align 8 + store %foo* %0, %foo** %this, align 8 ret void } define void @foo_bar(%foo* %0) { entry: + %this = alloca %foo*, align 8 + store %foo* %0, %foo** %this, align 8 %px = alloca i32*, align 8 store i32* @x, i32** %px, align 8 store i32* @x, i32** %px, align 8 @@ -1853,11 +1891,15 @@ fn initializing_method_variables_with_refs_shadowing() { define void @foo(%foo* %0) { entry: + %this = alloca %foo*, align 8 + store %foo* %0, %foo** %this, align 8 ret void } define void @foo_bar(%foo* %0) { entry: + %this = alloca %foo*, align 8 + store %foo* %0, %foo** %this, align 8 %x = alloca i32, align 4 %px = alloca i32*, align 8 store i32 0, i32* %x, align 4 @@ -1916,11 +1958,15 @@ fn initializing_method_variables_with_alias() { define void @foo(%foo* %0) { entry: + %this = alloca %foo*, align 8 + store %foo* %0, %foo** %this, align 8 ret void } define void @foo_bar(%foo* %0) { entry: + %this = alloca %foo*, align 8 + store %foo* %0, %foo** %this, align 8 %x = alloca i32, align 4 %px = alloca i32*, align 8 store i32 0, i32* %x, align 4 @@ -1979,11 +2025,15 @@ fn initializing_method_variables_with_reference_to() { define void @foo(%foo* %0) { entry: + %this = alloca %foo*, align 8 + store %foo* %0, %foo** %this, align 8 ret void } define void @foo_bar(%foo* %0) { entry: + %this = alloca %foo*, align 8 + store %foo* %0, %foo** %this, align 8 %x = alloca i32, align 4 %px = alloca i32*, align 8 store i32 0, i32* %x, align 4 @@ -2051,6 +2101,8 @@ fn methods_call_init_functions_for_their_members() { define void @foo(%foo* %0) { entry: + %this = alloca %foo*, align 8 + store %foo* %0, %foo** %this, align 8 %x = getelementptr inbounds %foo, %foo* %0, i32 0, i32 0 %y = getelementptr inbounds %foo, %foo* %0, i32 0, i32 1 ret void @@ -2058,11 +2110,15 @@ fn methods_call_init_functions_for_their_members() { define void @bar(%bar* %0) { entry: + %this = alloca %bar*, align 8 + store %bar* %0, %bar** %this, align 8 ret void } define void @bar_baz(%bar* %0) { entry: + %this = alloca %bar*, align 8 + store %bar* %0, %bar** %this, align 8 %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) @@ -2157,6 +2213,8 @@ fn user_fb_init_is_added_and_called_if_it_exists() { define void @foo(%foo* %0) { entry: + %this = alloca %foo*, align 8 + store %foo* %0, %foo** %this, align 8 %x = getelementptr inbounds %foo, %foo* %0, i32 0, i32 0 %y = getelementptr inbounds %foo, %foo* %0, i32 0, i32 1 ret void @@ -2164,6 +2222,8 @@ fn user_fb_init_is_added_and_called_if_it_exists() { define void @foo_FB_INIT(%foo* %0) { entry: + %this = alloca %foo*, align 8 + store %foo* %0, %foo** %this, align 8 %x = getelementptr inbounds %foo, %foo* %0, i32 0, i32 0 %y = getelementptr inbounds %foo, %foo* %0, i32 0, i32 1 store i16 1, i16* %x, align 2 @@ -2277,6 +2337,8 @@ fn user_fb_init_in_global_struct() { define void @foo(%foo* %0) { entry: + %this = alloca %foo*, align 8 + store %foo* %0, %foo** %this, align 8 %x = getelementptr inbounds %foo, %foo* %0, i32 0, i32 0 %y = getelementptr inbounds %foo, %foo* %0, i32 0, i32 1 ret void @@ -2284,6 +2346,8 @@ fn user_fb_init_in_global_struct() { define void @foo_FB_INIT(%foo* %0) { entry: + %this = alloca %foo*, align 8 + store %foo* %0, %foo** %this, align 8 %x = getelementptr inbounds %foo, %foo* %0, i32 0, i32 0 %y = getelementptr inbounds %foo, %foo* %0, i32 0, i32 1 store i16 1, i16* %x, align 2 diff --git a/src/codegen/tests/initialization_test/snapshots/rusty__codegen__tests__initialization_test__global_initializers__initial_values_in_global_variables_out_of_order.snap b/src/codegen/tests/initialization_test/snapshots/rusty__codegen__tests__initialization_test__global_initializers__initial_values_in_global_variables_out_of_order.snap index 2c317ed080..61bab9db20 100644 --- a/src/codegen/tests/initialization_test/snapshots/rusty__codegen__tests__initialization_test__global_initializers__initial_values_in_global_variables_out_of_order.snap +++ b/src/codegen/tests/initialization_test/snapshots/rusty__codegen__tests__initialization_test__global_initializers__initial_values_in_global_variables_out_of_order.snap @@ -1,7 +1,6 @@ --- source: src/codegen/tests/initialization_test/global_initializers.rs expression: result -snapshot_kind: text --- ; ModuleID = '' source_filename = "" @@ -15,6 +14,8 @@ source_filename = "" define void @MyFB(%MyFB* %0) { entry: + %this = alloca %MyFB*, align 8 + store %MyFB* %0, %MyFB** %this, align 8 %x = getelementptr inbounds %MyFB, %MyFB* %0, i32 0, i32 0 ret void } diff --git a/src/codegen/tests/initialization_test/snapshots/rusty__codegen__tests__initialization_test__pou_initializers__function_block_struct_initialized_in_function.snap b/src/codegen/tests/initialization_test/snapshots/rusty__codegen__tests__initialization_test__pou_initializers__function_block_struct_initialized_in_function.snap index 37c3a55280..e0796fb22d 100644 --- a/src/codegen/tests/initialization_test/snapshots/rusty__codegen__tests__initialization_test__pou_initializers__function_block_struct_initialized_in_function.snap +++ b/src/codegen/tests/initialization_test/snapshots/rusty__codegen__tests__initialization_test__pou_initializers__function_block_struct_initialized_in_function.snap @@ -1,7 +1,6 @@ --- source: src/codegen/tests/initialization_test/pou_initializers.rs expression: function -snapshot_kind: text --- ; ModuleID = '' source_filename = "" @@ -14,6 +13,8 @@ source_filename = "" define void @fb(%fb* %0) { entry: + %this = alloca %fb*, align 8 + store %fb* %0, %fb** %this, align 8 ret void } diff --git a/src/codegen/tests/initialization_test/snapshots/rusty__codegen__tests__initialization_test__pou_initializers__initial_values_in_function_block_pou.snap b/src/codegen/tests/initialization_test/snapshots/rusty__codegen__tests__initialization_test__pou_initializers__initial_values_in_function_block_pou.snap index 8c8f16e8f2..b8c7f3a011 100644 --- a/src/codegen/tests/initialization_test/snapshots/rusty__codegen__tests__initialization_test__pou_initializers__initial_values_in_function_block_pou.snap +++ b/src/codegen/tests/initialization_test/snapshots/rusty__codegen__tests__initialization_test__pou_initializers__initial_values_in_function_block_pou.snap @@ -1,7 +1,6 @@ --- source: src/codegen/tests/initialization_test/pou_initializers.rs expression: result -snapshot_kind: text --- ; ModuleID = '' source_filename = "" @@ -14,6 +13,8 @@ source_filename = "" define void @FB(%FB* %0) { entry: + %this = alloca %FB*, align 8 + store %FB* %0, %FB** %this, align 8 %x = getelementptr inbounds %FB, %FB* %0, i32 0, i32 0 %xx = getelementptr inbounds %FB, %FB* %0, i32 0, i32 1 %y = getelementptr inbounds %FB, %FB* %0, i32 0, i32 2 diff --git a/src/codegen/tests/initialization_test/snapshots/rusty__codegen__tests__initialization_test__type_initializers__initial_values_in_fb_variable.snap b/src/codegen/tests/initialization_test/snapshots/rusty__codegen__tests__initialization_test__type_initializers__initial_values_in_fb_variable.snap index 4157072b14..9de39e45d3 100644 --- a/src/codegen/tests/initialization_test/snapshots/rusty__codegen__tests__initialization_test__type_initializers__initial_values_in_fb_variable.snap +++ b/src/codegen/tests/initialization_test/snapshots/rusty__codegen__tests__initialization_test__type_initializers__initial_values_in_fb_variable.snap @@ -1,7 +1,6 @@ --- source: src/codegen/tests/initialization_test/type_initializers.rs expression: result -snapshot_kind: text --- ; ModuleID = '' source_filename = "" @@ -16,6 +15,8 @@ source_filename = "" define void @TON(%TON* %0) { entry: + %this = alloca %TON*, align 8 + store %TON* %0, %TON** %this, align 8 %a = getelementptr inbounds %TON, %TON* %0, i32 0, i32 0 %b = getelementptr inbounds %TON, %TON* %0, i32 0, i32 1 ret void diff --git a/src/codegen/tests/oop_tests.rs b/src/codegen/tests/oop_tests.rs index 42e079ead7..9c35b089ac 100644 --- a/src/codegen/tests/oop_tests.rs +++ b/src/codegen/tests/oop_tests.rs @@ -797,6 +797,13 @@ fn this_in_method_call_chain() { ret void } + define void @__user_init_FB_Test(%FB_Test* %0) { + entry: + %self = alloca %FB_Test*, align 8 + store %FB_Test* %0, %FB_Test** %self, align 8 + ret void + } + define void @__init___Test() { entry: ret void @@ -868,6 +875,13 @@ fn this_in_method_and_body_in_function_block() { ret void } + define void @__user_init_FB_Test(%FB_Test* %0) { + entry: + %self = alloca %FB_Test*, align 8 + store %FB_Test* %0, %FB_Test** %self, align 8 + ret void + } + define void @__init___Test() { entry: ret void @@ -967,6 +981,13 @@ fn shadowing_is_working() { ret void } + define void @__user_init_FB_Test(%FB_Test* %0) { + entry: + %self = alloca %FB_Test*, align 8 + store %FB_Test* %0, %FB_Test** %self, align 8 + ret void + } + define void @__init___Test() { entry: ret void @@ -1032,6 +1053,13 @@ fn this_calling_function_and_passing_this() { ret void } + define void @__user_init_FB_Test(%FB_Test* %0) { + entry: + %self = alloca %FB_Test*, align 8 + store %FB_Test* %0, %FB_Test** %self, align 8 + ret void + } + define void @__init___Test() { entry: ret void @@ -1137,6 +1165,13 @@ fn this_in_property_calling_method() { ret void } + define void @__user_init_FB_Test(%FB_Test* %0) { + entry: + %self = alloca %FB_Test*, align 8 + store %FB_Test* %0, %FB_Test** %self, align 8 + ret void + } + define void @__init___Test() { entry: ret void @@ -1199,6 +1234,13 @@ fn this_with_self_pointer() { ret void } + define void @__user_init_FB_Test(%FB_Test* %0) { + entry: + %self = alloca %FB_Test*, align 8 + store %FB_Test* %0, %FB_Test** %self, align 8 + ret void + } + define void @__init___Test() { entry: ret void @@ -1285,6 +1327,13 @@ fn this_calling_functionblock_body_from_method() { ret void } + define void @__user_init_fb(%fb* %0) { + entry: + %self = alloca %fb*, align 8 + store %fb* %0, %fb** %self, align 8 + ret void + } + define void @__init___Test() { entry: ret void diff --git a/src/codegen/tests/parameters_tests.rs b/src/codegen/tests/parameters_tests.rs index 4de027ceed..de1a2b83c9 100644 --- a/src/codegen/tests/parameters_tests.rs +++ b/src/codegen/tests/parameters_tests.rs @@ -1025,7 +1025,7 @@ fn by_value_fb_arg_aggregates_are_memcopied() { "#, ); - assert_snapshot!(result, @r###" + assert_snapshot!(result, @r#" ; ModuleID = '' source_filename = "" @@ -1061,6 +1061,8 @@ fn by_value_fb_arg_aggregates_are_memcopied() { define void @FOO(%FOO* %0) { entry: + %this = alloca %FOO*, align 8 + store %FOO* %0, %FOO** %this, align 8 %val = getelementptr inbounds %FOO, %FOO* %0, i32 0, i32 0 %field = getelementptr inbounds %FOO, %FOO* %0, i32 0, i32 1 ret void @@ -1077,7 +1079,7 @@ fn by_value_fb_arg_aggregates_are_memcopied() { attributes #0 = { argmemonly nofree nounwind willreturn writeonly } attributes #1 = { argmemonly nofree nounwind willreturn } - "###); + "#); } #[test] @@ -1127,6 +1129,8 @@ fn var_output_aggregate_types_are_memcopied() { define void @FB(%FB* %0) { entry: + %this = alloca %FB*, align 8 + store %FB* %0, %FB** %this, align 8 %output = getelementptr inbounds %FB, %FB* %0, i32 0, i32 0 %output2 = getelementptr inbounds %FB, %FB* %0, i32 0, i32 1 %output3 = getelementptr inbounds %FB, %FB* %0, i32 0, i32 2 diff --git a/src/codegen/tests/snapshots/rusty__codegen__tests__code_gen_tests__constant_expression_in_function_blocks_are_propagated.snap b/src/codegen/tests/snapshots/rusty__codegen__tests__code_gen_tests__constant_expression_in_function_blocks_are_propagated.snap index e3c8747783..ce1b44f578 100644 --- a/src/codegen/tests/snapshots/rusty__codegen__tests__code_gen_tests__constant_expression_in_function_blocks_are_propagated.snap +++ b/src/codegen/tests/snapshots/rusty__codegen__tests__code_gen_tests__constant_expression_in_function_blocks_are_propagated.snap @@ -1,7 +1,6 @@ --- source: src/codegen/tests/code_gen_tests.rs expression: result -snapshot_kind: text --- ; ModuleID = '' source_filename = "" @@ -12,6 +11,8 @@ source_filename = "" define void @fbWithConstant(%fbWithConstant* %0) { entry: + %this = alloca %fbWithConstant*, align 8 + store %fbWithConstant* %0, %fbWithConstant** %this, align 8 %x = getelementptr inbounds %fbWithConstant, %fbWithConstant* %0, i32 0, i32 0 %const = getelementptr inbounds %fbWithConstant, %fbWithConstant* %0, i32 0, i32 1 store i16 2, i16* %x, align 2 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 index 2630bd0c34..8016d6492f 100644 --- 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 @@ -1,7 +1,6 @@ --- source: src/codegen/tests/code_gen_tests.rs expression: prg -snapshot_kind: text --- ; ModuleID = '' source_filename = "" @@ -12,6 +11,8 @@ source_filename = "" define void @MyClass(%MyClass* %0) { entry: + %this = alloca %MyClass*, align 8 + store %MyClass* %0, %MyClass** %this, align 8 %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) @@ -21,6 +22,8 @@ entry: define i16 @MyClass_testMethod(%MyClass* %0, i16 %1) { entry: + %this = alloca %MyClass*, align 8 + store %MyClass* %0, %MyClass** %this, align 8 %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 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 50357bb56b..a9ae726998 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,7 +1,6 @@ --- source: src/codegen/tests/code_gen_tests.rs expression: result -snapshot_kind: text --- ; ModuleID = '' source_filename = "" @@ -14,6 +13,8 @@ source_filename = "" define void @MyClass(%MyClass* %0) { entry: + %this = alloca %MyClass*, align 8 + store %MyClass* %0, %MyClass** %this, align 8 %x = getelementptr inbounds %MyClass, %MyClass* %0, i32 0, i32 0 %y = getelementptr inbounds %MyClass, %MyClass* %0, i32 0, i32 1 ret void @@ -21,6 +22,8 @@ entry: define void @MyClass_testMethod(%MyClass* %0, i16 %1) { entry: + %this = alloca %MyClass*, align 8 + store %MyClass* %0, %MyClass** %this, align 8 %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 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 index 3b49aa33b9..684c98dace 100644 --- 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 @@ -1,7 +1,6 @@ --- source: src/codegen/tests/code_gen_tests.rs expression: prg -snapshot_kind: text --- ; ModuleID = '' source_filename = "" @@ -14,6 +13,8 @@ source_filename = "" define void @MyClass(%MyClass* %0) { entry: + %this = alloca %MyClass*, align 8 + store %MyClass* %0, %MyClass** %this, align 8 %x = getelementptr inbounds %MyClass, %MyClass* %0, i32 0, i32 0 %y = getelementptr inbounds %MyClass, %MyClass* %0, i32 0, i32 1 ret void @@ -21,6 +22,8 @@ entry: define void @MyClass_testMethod(%MyClass* %0, i16* %1) { entry: + %this = alloca %MyClass*, align 8 + store %MyClass* %0, %MyClass** %this, align 8 %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 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 index c4fa686240..fbf72751cd 100644 --- 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 @@ -1,7 +1,6 @@ --- source: src/codegen/tests/code_gen_tests.rs expression: prg -snapshot_kind: text --- ; ModuleID = '' source_filename = "" @@ -14,6 +13,8 @@ source_filename = "" define void @MyClass(%MyClass* %0) { entry: + %this = alloca %MyClass*, align 8 + store %MyClass* %0, %MyClass** %this, align 8 %x = getelementptr inbounds %MyClass, %MyClass* %0, i32 0, i32 0 %y = getelementptr inbounds %MyClass, %MyClass* %0, i32 0, i32 1 ret void @@ -21,6 +22,8 @@ entry: define void @MyClass_testMethod(%MyClass* %0, i16 %1) { entry: + %this = alloca %MyClass*, align 8 + store %MyClass* %0, %MyClass** %this, align 8 %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 diff --git a/src/codegen/tests/snapshots/rusty__codegen__tests__code_gen_tests__function_block_instance_call.snap b/src/codegen/tests/snapshots/rusty__codegen__tests__code_gen_tests__function_block_instance_call.snap index ed20f609a7..a23df99fb1 100644 --- a/src/codegen/tests/snapshots/rusty__codegen__tests__code_gen_tests__function_block_instance_call.snap +++ b/src/codegen/tests/snapshots/rusty__codegen__tests__code_gen_tests__function_block_instance_call.snap @@ -1,7 +1,6 @@ --- source: src/codegen/tests/code_gen_tests.rs expression: result -snapshot_kind: text --- ; ModuleID = '' source_filename = "" @@ -14,6 +13,8 @@ source_filename = "" define void @foo(%foo* %0) { entry: + %this = alloca %foo*, align 8 + store %foo* %0, %foo** %this, align 8 %x = getelementptr inbounds %foo, %foo* %0, i32 0, i32 0 %y = getelementptr inbounds %foo, %foo* %0, i32 0, i32 1 ret void diff --git a/src/codegen/tests/snapshots/rusty__codegen__tests__code_gen_tests__function_block_qualified_instance_call.snap b/src/codegen/tests/snapshots/rusty__codegen__tests__code_gen_tests__function_block_qualified_instance_call.snap index 43ebad6206..0518ca30ee 100644 --- a/src/codegen/tests/snapshots/rusty__codegen__tests__code_gen_tests__function_block_qualified_instance_call.snap +++ b/src/codegen/tests/snapshots/rusty__codegen__tests__code_gen_tests__function_block_qualified_instance_call.snap @@ -1,7 +1,6 @@ --- source: src/codegen/tests/code_gen_tests.rs expression: result -snapshot_kind: text --- ; ModuleID = '' source_filename = "" @@ -16,12 +15,16 @@ source_filename = "" define void @foo(%foo* %0) { entry: + %this = alloca %foo*, align 8 + store %foo* %0, %foo** %this, align 8 %bar_inst = getelementptr inbounds %foo, %foo* %0, i32 0, i32 0 ret void } define void @bar(%bar* %0) { entry: + %this = alloca %bar*, align 8 + store %bar* %0, %bar** %this, align 8 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 index 84bec9d108..d2a8d4637b 100644 --- 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 @@ -1,7 +1,6 @@ --- source: src/codegen/tests/code_gen_tests.rs expression: prg -snapshot_kind: text --- ; ModuleID = '' source_filename = "" @@ -12,6 +11,8 @@ source_filename = "" define void @fb(%fb* %0) { entry: + %this = alloca %fb*, align 8 + store %fb* %0, %fb** %this, align 8 %call = call i32 @fb_meth(%fb* %0, i32 5) %call1 = call i32 @fb_meth(%fb* %0, i32 4) ret void @@ -19,6 +20,8 @@ entry: define i32 @fb_meth(%fb* %0, i32 %1) { entry: + %this = alloca %fb*, align 8 + store %fb* %0, %fb** %this, align 8 %fb.meth = alloca i32, align 4 %a = alloca i32, align 4 store i32 %1, i32* %a, align 4 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 index c1b33f8462..9d01000b6b 100644 --- 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 @@ -1,7 +1,6 @@ --- source: src/codegen/tests/code_gen_tests.rs expression: prg -snapshot_kind: text --- ; ModuleID = '' source_filename = "" @@ -12,6 +11,8 @@ source_filename = "" define void @fb(%fb* %0) { entry: + %this = alloca %fb*, align 8 + store %fb* %0, %fb** %this, align 8 %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) @@ -21,6 +22,8 @@ entry: define i32 @fb_meth(%fb* %0, i32 %1, i32 %2, i32 %3) { entry: + %this = alloca %fb*, align 8 + store %fb* %0, %fb** %this, align 8 %fb.meth = alloca i32, align 4 %a = alloca i32, align 4 store i32 %1, i32* %a, align 4 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 index cb1b14c815..d1e5a7a28b 100644 --- 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 @@ -1,7 +1,6 @@ --- source: src/codegen/tests/code_gen_tests.rs expression: res -snapshot_kind: text --- ; ModuleID = '' source_filename = "" @@ -13,6 +12,8 @@ source_filename = "" define void @fb_with_method(%fb_with_method* %0) { entry: + %this = alloca %fb_with_method*, align 8 + store %fb_with_method* %0, %fb_with_method** %this, align 8 %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) @@ -29,6 +30,8 @@ entry: define void @fb_with_method_method_with_aggregagte_return(%fb_with_method* %0, i8* %1, i8* %2) { entry: + %this = alloca %fb_with_method*, align 8 + store %fb_with_method* %0, %fb_with_method** %this, align 8 %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 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 index 3e54d86274..db2cc18cb0 100644 --- 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 @@ -1,7 +1,6 @@ --- source: src/codegen/tests/code_gen_tests.rs expression: res -snapshot_kind: text --- ; ModuleID = '' source_filename = "" @@ -13,11 +12,15 @@ source_filename = "" define void @foo(%foo* %0) { entry: + %this = alloca %foo*, align 8 + store %foo* %0, %foo** %this, align 8 ret void } define void @foo_baz(%foo* %0, [81 x i8]* %1) { entry: + %this = alloca %foo*, align 8 + store %foo* %0, %foo** %this, align 8 %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 diff --git a/src/codegen/tests/snapshots/rusty__codegen__tests__code_gen_tests__qualified_action_from_fb_called_in_program.snap b/src/codegen/tests/snapshots/rusty__codegen__tests__code_gen_tests__qualified_action_from_fb_called_in_program.snap index 4577ec2a96..0a788bb320 100644 --- a/src/codegen/tests/snapshots/rusty__codegen__tests__code_gen_tests__qualified_action_from_fb_called_in_program.snap +++ b/src/codegen/tests/snapshots/rusty__codegen__tests__code_gen_tests__qualified_action_from_fb_called_in_program.snap @@ -1,7 +1,6 @@ --- source: src/codegen/tests/code_gen_tests.rs expression: result -snapshot_kind: text --- ; ModuleID = '' source_filename = "" @@ -21,6 +20,8 @@ entry: define void @fb(%fb* %0) { entry: + %this = alloca %fb*, align 8 + store %fb* %0, %fb** %this, align 8 %x = getelementptr inbounds %fb, %fb* %0, i32 0, i32 0 ret void } diff --git a/src/codegen/tests/snapshots/rusty__codegen__tests__code_gen_tests__reference_qualified_name.snap b/src/codegen/tests/snapshots/rusty__codegen__tests__code_gen_tests__reference_qualified_name.snap index ed6219267e..9756406b39 100644 --- a/src/codegen/tests/snapshots/rusty__codegen__tests__code_gen_tests__reference_qualified_name.snap +++ b/src/codegen/tests/snapshots/rusty__codegen__tests__code_gen_tests__reference_qualified_name.snap @@ -1,7 +1,6 @@ --- source: src/codegen/tests/code_gen_tests.rs expression: result -snapshot_kind: text --- ; ModuleID = '' source_filename = "" @@ -16,6 +15,8 @@ source_filename = "" define void @fb(%fb* %0) { entry: + %this = alloca %fb*, align 8 + store %fb* %0, %fb** %this, align 8 %x = getelementptr inbounds %fb, %fb* %0, i32 0, i32 0 ret void } diff --git a/src/codegen/tests/snapshots/rusty__codegen__tests__code_gen_tests__returning_early_in_function_block.snap b/src/codegen/tests/snapshots/rusty__codegen__tests__code_gen_tests__returning_early_in_function_block.snap index b16efec1d0..30cb74073f 100644 --- a/src/codegen/tests/snapshots/rusty__codegen__tests__code_gen_tests__returning_early_in_function_block.snap +++ b/src/codegen/tests/snapshots/rusty__codegen__tests__code_gen_tests__returning_early_in_function_block.snap @@ -1,7 +1,6 @@ --- source: src/codegen/tests/code_gen_tests.rs expression: result -snapshot_kind: text --- ; ModuleID = '' source_filename = "" @@ -12,6 +11,8 @@ source_filename = "" define void @abcdef(%abcdef* %0) { entry: + %this = alloca %abcdef*, align 8 + store %abcdef* %0, %abcdef** %this, align 8 %n = getelementptr inbounds %abcdef, %abcdef* %0, i32 0, i32 0 %load_n = load i8, i8* %n, align 1 %1 = sext i8 %load_n to i32 diff --git a/src/codegen/tests/snapshots/rusty__codegen__tests__multifile_codegen_tests__enum_referenced_in_fb_nested.snap b/src/codegen/tests/snapshots/rusty__codegen__tests__multifile_codegen_tests__enum_referenced_in_fb_nested.snap index 7410deda6c..70eb5739c9 100644 --- a/src/codegen/tests/snapshots/rusty__codegen__tests__multifile_codegen_tests__enum_referenced_in_fb_nested.snap +++ b/src/codegen/tests/snapshots/rusty__codegen__tests__multifile_codegen_tests__enum_referenced_in_fb_nested.snap @@ -18,6 +18,8 @@ source_filename = "fb.st" define void @fb(%fb* %0) { entry: + %this = alloca %fb*, align 8 + store %fb* %0, %fb** %this, align 8 %x = getelementptr inbounds %fb, %fb* %0, i32 0, i32 0 ret void } @@ -46,6 +48,8 @@ source_filename = "fb2.st" define void @fb2(%fb2* %0) { entry: + %this = alloca %fb2*, align 8 + store %fb2* %0, %fb2** %this, align 8 %x = getelementptr inbounds %fb2, %fb2* %0, i32 0, i32 0 ret void } @@ -61,5 +65,7 @@ source_filename = "fb3.st" define void @fb3(%fb3* %0) { entry: + %this = alloca %fb3*, align 8 + store %fb3* %0, %fb3** %this, align 8 ret void } diff --git a/src/codegen/tests/snapshots/rusty__codegen__tests__multifile_codegen_tests__function_defined_in_external_file.snap b/src/codegen/tests/snapshots/rusty__codegen__tests__multifile_codegen_tests__function_defined_in_external_file.snap index 1d9bc46be5..074341c44d 100644 --- a/src/codegen/tests/snapshots/rusty__codegen__tests__multifile_codegen_tests__function_defined_in_external_file.snap +++ b/src/codegen/tests/snapshots/rusty__codegen__tests__multifile_codegen_tests__function_defined_in_external_file.snap @@ -22,6 +22,8 @@ source_filename = "fb.st" define void @fb(%fb* %0) { entry: + %this = alloca %fb*, align 8 + store %fb* %0, %fb** %this, align 8 ret void } diff --git a/src/codegen/tests/snapshots/rusty__codegen__tests__parameters_tests__fb_accepts_empty_statement_as_input_param.snap b/src/codegen/tests/snapshots/rusty__codegen__tests__parameters_tests__fb_accepts_empty_statement_as_input_param.snap index 7b96f4217f..1996f280b4 100644 --- a/src/codegen/tests/snapshots/rusty__codegen__tests__parameters_tests__fb_accepts_empty_statement_as_input_param.snap +++ b/src/codegen/tests/snapshots/rusty__codegen__tests__parameters_tests__fb_accepts_empty_statement_as_input_param.snap @@ -1,7 +1,6 @@ --- source: src/codegen/tests/parameters_tests.rs expression: result -snapshot_kind: text --- ; ModuleID = '' source_filename = "" @@ -14,6 +13,8 @@ source_filename = "" define void @fb_t(%fb_t* %0) { entry: + %this = alloca %fb_t*, align 8 + store %fb_t* %0, %fb_t** %this, align 8 %in1 = getelementptr inbounds %fb_t, %fb_t* %0, i32 0, i32 0 %in2 = getelementptr inbounds %fb_t, %fb_t* %0, i32 0, i32 1 ret void diff --git a/src/codegen/tests/snapshots/rusty__codegen__tests__parameters_tests__fb_accepts_empty_statement_as_output_param.snap b/src/codegen/tests/snapshots/rusty__codegen__tests__parameters_tests__fb_accepts_empty_statement_as_output_param.snap index 6e88b211f7..0d5356539c 100644 --- a/src/codegen/tests/snapshots/rusty__codegen__tests__parameters_tests__fb_accepts_empty_statement_as_output_param.snap +++ b/src/codegen/tests/snapshots/rusty__codegen__tests__parameters_tests__fb_accepts_empty_statement_as_output_param.snap @@ -1,7 +1,6 @@ --- source: src/codegen/tests/parameters_tests.rs expression: result -snapshot_kind: text --- ; ModuleID = '' source_filename = "" @@ -14,6 +13,8 @@ source_filename = "" define void @fb_t(%fb_t* %0) { entry: + %this = alloca %fb_t*, align 8 + store %fb_t* %0, %fb_t** %this, align 8 %out1 = getelementptr inbounds %fb_t, %fb_t* %0, i32 0, i32 0 %out2 = getelementptr inbounds %fb_t, %fb_t* %0, i32 0, i32 1 ret void diff --git a/src/codegen/tests/snapshots/rusty__codegen__tests__parameters_tests__parameters_behind_function_block_pointer_are_assigned_to.snap b/src/codegen/tests/snapshots/rusty__codegen__tests__parameters_tests__parameters_behind_function_block_pointer_are_assigned_to.snap index 14e636e858..db833be27e 100644 --- a/src/codegen/tests/snapshots/rusty__codegen__tests__parameters_tests__parameters_behind_function_block_pointer_are_assigned_to.snap +++ b/src/codegen/tests/snapshots/rusty__codegen__tests__parameters_tests__parameters_behind_function_block_pointer_are_assigned_to.snap @@ -1,7 +1,6 @@ --- source: src/codegen/tests/parameters_tests.rs expression: result -snapshot_kind: text --- ; ModuleID = '' source_filename = "" @@ -26,6 +25,8 @@ entry: define void @file_t(%file_t* %0) { entry: + %this = alloca %file_t*, align 8 + store %file_t* %0, %file_t** %this, align 8 %var1 = getelementptr inbounds %file_t, %file_t* %0, i32 0, i32 0 %var2 = getelementptr inbounds %file_t, %file_t* %0, i32 0, i32 1 ret void diff --git a/src/codegen/tests/snapshots/rusty__codegen__tests__parameters_tests__var_in_out_params_can_be_out_of_order.snap b/src/codegen/tests/snapshots/rusty__codegen__tests__parameters_tests__var_in_out_params_can_be_out_of_order.snap index 78c37314a1..41ab0d5781 100644 --- a/src/codegen/tests/snapshots/rusty__codegen__tests__parameters_tests__var_in_out_params_can_be_out_of_order.snap +++ b/src/codegen/tests/snapshots/rusty__codegen__tests__parameters_tests__var_in_out_params_can_be_out_of_order.snap @@ -1,7 +1,6 @@ --- source: src/codegen/tests/parameters_tests.rs expression: res -snapshot_kind: text --- ; ModuleID = '' source_filename = "" @@ -38,6 +37,8 @@ entry: define void @fb_t(%fb_t* %0) { entry: + %this = alloca %fb_t*, align 8 + store %fb_t* %0, %fb_t** %this, align 8 %myVar = getelementptr inbounds %fb_t, %fb_t* %0, i32 0, i32 0 %myInput = getelementptr inbounds %fb_t, %fb_t* %0, i32 0, i32 1 %myInOut = getelementptr inbounds %fb_t, %fb_t* %0, i32 0, i32 2 diff --git a/src/parser/tests/parse_errors/parse_error_containers_tests.rs b/src/parser/tests/parse_errors/parse_error_containers_tests.rs index bea34c8e31..1516feb07d 100644 --- a/src/parser/tests/parse_errors/parse_error_containers_tests.rs +++ b/src/parser/tests/parse_errors/parse_error_containers_tests.rs @@ -223,7 +223,7 @@ fn super_is_a_reserved_keyword() { error[E007]: Unexpected token: expected KeywordSemicolon but found 'VAR super' ┌─ :4:9 - │ + │ 4 │ ╭ VAR 5 │ │ super : INT; │ ╰─────────────────^ Unexpected token: expected KeywordSemicolon but found 'VAR @@ -238,7 +238,7 @@ fn super_is_a_reserved_keyword() { error[E007]: Unexpected token: expected KeywordSemicolon but found 'END_VAR METHOD super END_METHOD' ┌─ :6:9 - │ + │ 6 │ ╭ END_VAR 7 │ │ METHOD super END_METHOD │ ╰───────────────────────────────^ Unexpected token: expected KeywordSemicolon but found 'END_VAR @@ -346,10 +346,10 @@ fn this_is_a_reserved_keyword() { 3 │ PROGRAM this │ ^^^^ Case condition used outside of case statement! Did you mean to use ';'? - error[E120]: Invalid use of `THIS`. Usage is only allowed within POU of type `FUNCTION_BLOCK` or type `METHOD` + error[E120]: Invalid use of `THIS`. Usage is only allowed within POU of type `FUNCTION_BLOCK` ┌─ :3:13 │ 3 │ PROGRAM this - │ ^^^^ Invalid use of `THIS`. Usage is only allowed within POU of type `FUNCTION_BLOCK` or type `METHOD` + │ ^^^^ Invalid use of `THIS`. Usage is only allowed within POU of type `FUNCTION_BLOCK` "); } diff --git a/src/resolver/tests/resolve_expressions_tests.rs b/src/resolver/tests/resolve_expressions_tests.rs index b2b027ec4a..944f0046fa 100644 --- a/src/resolver/tests/resolve_expressions_tests.rs +++ b/src/resolver/tests/resolve_expressions_tests.rs @@ -6322,7 +6322,7 @@ fn just_this() { } #[test] -#[ignore = "Types on builtin types are not correctly annotated"] +#[ignore = "TODO: #THIS: fix this test"] fn this_assignment() { let id_provider = IdProvider::default(); let (unit, mut index) = index_with_ids( diff --git a/src/validation/tests/super_keyword_validation_tests.rs b/src/validation/tests/super_keyword_validation_tests.rs index 834fe9c72b..009b23b25a 100644 --- a/src/validation/tests/super_keyword_validation_tests.rs +++ b/src/validation/tests/super_keyword_validation_tests.rs @@ -245,10 +245,22 @@ fn super_keyword_is_not_assignable() { 10 │ SUPER := super_ptr; │ ^^^^^ Expression SUPER is not assignable. + error[E050]: Expression super is not assignable. + ┌─ :11:13 + │ + 11 │ super^ := 5; + │ ^^^^^ Expression super is not assignable. + + error[E037]: Invalid assignment: cannot assign 'DINT' to 'parent' + ┌─ :11:13 + │ + 11 │ super^ := 5; + │ ^^^^^^^^^^^ Invalid assignment: cannot assign 'DINT' to 'parent' + error[E050]: Expression (SUPER) is not assignable. - ┌─ :13:13 + ┌─ :14:13 │ - 13 │ (SUPER) := super_ptr; + 14 │ (SUPER) := super_ptr; │ ^^^^^^^ Expression (SUPER) is not assignable. "); } From 00c1244aec884f4ab782df7c5b4d8bba3df8c341 Mon Sep 17 00:00:00 2001 From: abroooo <61380142+abroooo@users.noreply.github.com> Date: Wed, 30 Apr 2025 12:59:35 +0200 Subject: [PATCH 36/48] some cleanups --- .../src/.inheritance.rs.pending-snap | 273 ++++++++++++++++++ compiler/plc_lowering/src/inheritance.rs | 42 --- .../tests/resolve_expressions_tests.rs | 73 +++-- 3 files changed, 321 insertions(+), 67 deletions(-) create mode 100644 compiler/plc_lowering/src/.inheritance.rs.pending-snap diff --git a/compiler/plc_lowering/src/.inheritance.rs.pending-snap b/compiler/plc_lowering/src/.inheritance.rs.pending-snap new file mode 100644 index 0000000000..44efd684c8 --- /dev/null +++ b/compiler/plc_lowering/src/.inheritance.rs.pending-snap @@ -0,0 +1,273 @@ +{"run_id":"1743593170-383049833","line":4410,"new":{"module_name":"plc_lowering__inheritance__super_tests","snapshot_name":"valid_super_deref_in_paren_expression_edge_case","metadata":{"source":"compiler/plc_lowering/src/inheritance.rs","assertion_line":4410,"expression":"statements"},"snapshot":"[\n Assignment {\n left: ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"z\",\n },\n ),\n base: None,\n },\n right: BinaryExpression {\n operator: Plus,\n left: ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"x\",\n },\n ),\n base: Some(\n ReferenceExpr {\n kind: Deref,\n base: Some(\n ParenExpression {\n expression: CallStatement {\n operator: ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"REF\",\n },\n ),\n base: None,\n },\n parameters: Some(\n ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"__parent\",\n },\n ),\n base: None,\n },\n ),\n },\n },\n ),\n },\n ),\n },\n right: ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"y\",\n },\n ),\n base: Some(\n ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"__parent\",\n },\n ),\n base: None,\n },\n ),\n },\n },\n },\n Assignment {\n left: ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"local\",\n },\n ),\n base: None,\n },\n right: ReferenceExpr {\n kind: Deref,\n base: Some(\n ParenExpression {\n expression: CallStatement {\n operator: ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"REF\",\n },\n ),\n base: None,\n },\n parameters: Some(\n ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"__parent\",\n },\n ),\n base: None,\n },\n ),\n },\n },\n ),\n },\n },\n]"},"old":{"module_name":"plc_lowering__inheritance__super_tests","metadata":{},"snapshot":"[\n Assignment {\n left: ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"z\",\n },\n ),\n base: None,\n },\n right: BinaryExpression {\n operator: Plus,\n left: ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"x\",\n },\n ),\n base: Some(\n ReferenceExpr {\n kind: Deref,\n base: Some(\n ParenExpression {\n expression: CallStatement {\n operator: ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"REF\",\n },\n ),\n base: None,\n },\n parameters: Some(\n ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"__parent\",\n },\n ),\n base: None,\n },\n ),\n },\n },\n ),\n },\n ),\n },\n right: ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"y\",\n },\n ),\n base: Some(\n ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"__parent\",\n },\n ),\n base: None,\n },\n ),\n },\n },\n },\n]"}} +{"run_id":"1743664173-131625004","line":2523,"new":null,"old":null} +{"run_id":"1743664173-131625004","line":4504,"new":null,"old":null} +{"run_id":"1743664173-131625004","line":4089,"new":null,"old":null} +{"run_id":"1743664173-131625004","line":3827,"new":null,"old":null} +{"run_id":"1743664173-131625004","line":3109,"new":null,"old":null} +{"run_id":"1743664173-131625004","line":4032,"new":null,"old":null} +{"run_id":"1743664173-131625004","line":2902,"new":null,"old":null} +{"run_id":"1743664173-131625004","line":2606,"new":null,"old":null} +{"run_id":"1743664173-131625004","line":3732,"new":null,"old":null} +{"run_id":"1743664173-131625004","line":4138,"new":null,"old":null} +{"run_id":"1743664173-131625004","line":4300,"new":null,"old":null} +{"run_id":"1743664173-131625004","line":2539,"new":null,"old":null} +{"run_id":"1743664173-131625004","line":2973,"new":null,"old":null} +{"run_id":"1743664173-131625004","line":2553,"new":null,"old":null} +{"run_id":"1743664173-131625004","line":2567,"new":null,"old":null} +{"run_id":"1743664173-131625004","line":3207,"new":null,"old":null} +{"run_id":"1743664173-131625004","line":3369,"new":null,"old":null} +{"run_id":"1743664173-131625004","line":2652,"new":null,"old":null} +{"run_id":"1743664173-131625004","line":2711,"new":null,"old":null} +{"run_id":"1743664173-131625004","line":492,"new":null,"old":null} +{"run_id":"1743664173-131625004","line":2854,"new":null,"old":null} +{"run_id":"1743664173-131625004","line":3061,"new":null,"old":null} +{"run_id":"1743664173-131625004","line":4237,"new":null,"old":null} +{"run_id":"1743664173-131625004","line":2385,"new":null,"old":null} +{"run_id":"1743664173-131625004","line":3925,"new":null,"old":null} +{"run_id":"1743664173-131625004","line":2790,"new":null,"old":null} +{"run_id":"1743664173-131625004","line":4188,"new":null,"old":null} +{"run_id":"1743664173-131625004","line":2311,"new":null,"old":null} +{"run_id":"1743664173-131625004","line":2223,"new":null,"old":null} +{"run_id":"1743664173-131625004","line":3557,"new":null,"old":null} +{"run_id":"1743664173-131625004","line":3661,"new":null,"old":null} +{"run_id":"1743664173-131625004","line":2008,"new":null,"old":null} +{"run_id":"1743664173-131625004","line":1461,"new":null,"old":null} +{"run_id":"1743664173-131625004","line":772,"new":null,"old":null} +{"run_id":"1743664173-131625004","line":1788,"new":null,"old":null} +{"run_id":"1743664173-131625004","line":541,"new":null,"old":null} +{"run_id":"1743664173-131625004","line":665,"new":null,"old":null} +{"run_id":"1743664173-131625004","line":899,"new":null,"old":null} +{"run_id":"1743664173-131625004","line":1320,"new":null,"old":null} +{"run_id":"1743664173-131625004","line":4410,"new":{"module_name":"plc_lowering__inheritance__super_tests","snapshot_name":"valid_super_deref_in_paren_expression_edge_case","metadata":{"source":"compiler/plc_lowering/src/inheritance.rs","assertion_line":4410,"expression":"statements"},"snapshot":"[\n Assignment {\n left: ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"z\",\n },\n ),\n base: None,\n },\n right: BinaryExpression {\n operator: Plus,\n left: ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"x\",\n },\n ),\n base: Some(\n ReferenceExpr {\n kind: Deref,\n base: Some(\n ParenExpression {\n expression: CallStatement {\n operator: ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"REF\",\n },\n ),\n base: None,\n },\n parameters: Some(\n ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"__parent\",\n },\n ),\n base: None,\n },\n ),\n },\n },\n ),\n },\n ),\n },\n right: ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"y\",\n },\n ),\n base: Some(\n ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"__parent\",\n },\n ),\n base: None,\n },\n ),\n },\n },\n },\n Assignment {\n left: ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"local\",\n },\n ),\n base: None,\n },\n right: ReferenceExpr {\n kind: Deref,\n base: Some(\n ParenExpression {\n expression: CallStatement {\n operator: ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"REF\",\n },\n ),\n base: None,\n },\n parameters: Some(\n ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"__parent\",\n },\n ),\n base: None,\n },\n ),\n },\n },\n ),\n },\n },\n]"},"old":{"module_name":"plc_lowering__inheritance__super_tests","metadata":{},"snapshot":"[\n Assignment {\n left: ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"z\",\n },\n ),\n base: None,\n },\n right: BinaryExpression {\n operator: Plus,\n left: ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"x\",\n },\n ),\n base: Some(\n ReferenceExpr {\n kind: Deref,\n base: Some(\n ParenExpression {\n expression: CallStatement {\n operator: ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"REF\",\n },\n ),\n base: None,\n },\n parameters: Some(\n ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"__parent\",\n },\n ),\n base: None,\n },\n ),\n },\n },\n ),\n },\n ),\n },\n right: ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"y\",\n },\n ),\n base: Some(\n ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"__parent\",\n },\n ),\n base: None,\n },\n ),\n },\n },\n },\n]"}} +{"run_id":"1743675955-17891460","line":2606,"new":null,"old":null} +{"run_id":"1743675955-17891460","line":2523,"new":null,"old":null} +{"run_id":"1743675955-17891460","line":4089,"new":null,"old":null} +{"run_id":"1743675955-17891460","line":4138,"new":null,"old":null} +{"run_id":"1743675955-17891460","line":2652,"new":null,"old":null} +{"run_id":"1743675955-17891460","line":2711,"new":null,"old":null} +{"run_id":"1743675955-17891460","line":4504,"new":null,"old":null} +{"run_id":"1743675955-17891460","line":2539,"new":null,"old":null} +{"run_id":"1743675955-17891460","line":4032,"new":null,"old":null} +{"run_id":"1743675955-17891460","line":2553,"new":null,"old":null} +{"run_id":"1743675955-17891460","line":2973,"new":null,"old":null} +{"run_id":"1743675955-17891460","line":2567,"new":null,"old":null} +{"run_id":"1743675955-17891460","line":2902,"new":null,"old":null} +{"run_id":"1743675955-17891460","line":4300,"new":null,"old":null} +{"run_id":"1743675955-17891460","line":3732,"new":null,"old":null} +{"run_id":"1743675955-17891460","line":3207,"new":null,"old":null} +{"run_id":"1743675955-17891460","line":3109,"new":null,"old":null} +{"run_id":"1743675955-17891460","line":3369,"new":null,"old":null} +{"run_id":"1743675955-17891460","line":3827,"new":null,"old":null} +{"run_id":"1743675955-17891460","line":3061,"new":null,"old":null} +{"run_id":"1743675955-17891460","line":2790,"new":null,"old":null} +{"run_id":"1743675955-17891460","line":3557,"new":null,"old":null} +{"run_id":"1743675955-17891460","line":4188,"new":null,"old":null} +{"run_id":"1743675955-17891460","line":3925,"new":null,"old":null} +{"run_id":"1743675955-17891460","line":2008,"new":null,"old":null} +{"run_id":"1743675955-17891460","line":492,"new":null,"old":null} +{"run_id":"1743675955-17891460","line":3661,"new":null,"old":null} +{"run_id":"1743675955-17891460","line":2223,"new":null,"old":null} +{"run_id":"1743675955-17891460","line":2385,"new":null,"old":null} +{"run_id":"1743675955-17891460","line":2311,"new":null,"old":null} +{"run_id":"1743675955-17891460","line":2854,"new":null,"old":null} +{"run_id":"1743675955-17891460","line":4237,"new":null,"old":null} +{"run_id":"1743675955-17891460","line":899,"new":null,"old":null} +{"run_id":"1743675955-17891460","line":1461,"new":null,"old":null} +{"run_id":"1743675955-17891460","line":1320,"new":null,"old":null} +{"run_id":"1743675955-17891460","line":665,"new":null,"old":null} +{"run_id":"1743675955-17891460","line":1788,"new":null,"old":null} +{"run_id":"1743675955-17891460","line":541,"new":null,"old":null} +{"run_id":"1743675955-17891460","line":772,"new":null,"old":null} +{"run_id":"1743675955-17891460","line":4410,"new":{"module_name":"plc_lowering__inheritance__super_tests","snapshot_name":"valid_super_deref_in_paren_expression_edge_case","metadata":{"source":"compiler/plc_lowering/src/inheritance.rs","assertion_line":4410,"expression":"statements"},"snapshot":"[\n Assignment {\n left: ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"z\",\n },\n ),\n base: None,\n },\n right: BinaryExpression {\n operator: Plus,\n left: ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"x\",\n },\n ),\n base: Some(\n ReferenceExpr {\n kind: Deref,\n base: Some(\n ParenExpression {\n expression: CallStatement {\n operator: ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"REF\",\n },\n ),\n base: None,\n },\n parameters: Some(\n ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"__parent\",\n },\n ),\n base: None,\n },\n ),\n },\n },\n ),\n },\n ),\n },\n right: ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"y\",\n },\n ),\n base: Some(\n ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"__parent\",\n },\n ),\n base: None,\n },\n ),\n },\n },\n },\n Assignment {\n left: ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"local\",\n },\n ),\n base: None,\n },\n right: ReferenceExpr {\n kind: Deref,\n base: Some(\n ParenExpression {\n expression: CallStatement {\n operator: ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"REF\",\n },\n ),\n base: None,\n },\n parameters: Some(\n ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"__parent\",\n },\n ),\n base: None,\n },\n ),\n },\n },\n ),\n },\n },\n]"},"old":{"module_name":"plc_lowering__inheritance__super_tests","metadata":{},"snapshot":"[\n Assignment {\n left: ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"z\",\n },\n ),\n base: None,\n },\n right: BinaryExpression {\n operator: Plus,\n left: ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"x\",\n },\n ),\n base: Some(\n ReferenceExpr {\n kind: Deref,\n base: Some(\n ParenExpression {\n expression: CallStatement {\n operator: ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"REF\",\n },\n ),\n base: None,\n },\n parameters: Some(\n ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"__parent\",\n },\n ),\n base: None,\n },\n ),\n },\n },\n ),\n },\n ),\n },\n right: ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"y\",\n },\n ),\n base: Some(\n ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"__parent\",\n },\n ),\n base: None,\n },\n ),\n },\n },\n },\n]"}} +{"run_id":"1743676161-849633236","line":2606,"new":null,"old":null} +{"run_id":"1743676161-849633236","line":4138,"new":null,"old":null} +{"run_id":"1743676161-849633236","line":4032,"new":null,"old":null} +{"run_id":"1743676161-849633236","line":2523,"new":null,"old":null} +{"run_id":"1743676161-849633236","line":2973,"new":null,"old":null} +{"run_id":"1743676161-849633236","line":4504,"new":null,"old":null} +{"run_id":"1743676161-849633236","line":2711,"new":null,"old":null} +{"run_id":"1743676161-849633236","line":3827,"new":null,"old":null} +{"run_id":"1743676161-849633236","line":3732,"new":null,"old":null} +{"run_id":"1743676161-849633236","line":2539,"new":null,"old":null} +{"run_id":"1743676161-849633236","line":2902,"new":null,"old":null} +{"run_id":"1743676161-849633236","line":3207,"new":null,"old":null} +{"run_id":"1743676161-849633236","line":2652,"new":null,"old":null} +{"run_id":"1743676161-849633236","line":4300,"new":null,"old":null} +{"run_id":"1743676161-849633236","line":2553,"new":null,"old":null} +{"run_id":"1743676161-849633236","line":4089,"new":null,"old":null} +{"run_id":"1743676161-849633236","line":2567,"new":null,"old":null} +{"run_id":"1743676161-849633236","line":3109,"new":null,"old":null} +{"run_id":"1743676161-849633236","line":3369,"new":null,"old":null} +{"run_id":"1743676161-849633236","line":4188,"new":null,"old":null} +{"run_id":"1743676161-849633236","line":2385,"new":null,"old":null} +{"run_id":"1743676161-849633236","line":3661,"new":null,"old":null} +{"run_id":"1743676161-849633236","line":3061,"new":null,"old":null} +{"run_id":"1743676161-849633236","line":2854,"new":null,"old":null} +{"run_id":"1743676161-849633236","line":2311,"new":null,"old":null} +{"run_id":"1743676161-849633236","line":2790,"new":null,"old":null} +{"run_id":"1743676161-849633236","line":1461,"new":null,"old":null} +{"run_id":"1743676161-849633236","line":3557,"new":null,"old":null} +{"run_id":"1743676161-849633236","line":4237,"new":null,"old":null} +{"run_id":"1743676161-849633236","line":492,"new":null,"old":null} +{"run_id":"1743676161-849633236","line":3925,"new":null,"old":null} +{"run_id":"1743676161-849633236","line":2008,"new":null,"old":null} +{"run_id":"1743676161-849633236","line":2223,"new":null,"old":null} +{"run_id":"1743676161-849633236","line":899,"new":null,"old":null} +{"run_id":"1743676161-849633236","line":665,"new":null,"old":null} +{"run_id":"1743676161-849633236","line":1320,"new":null,"old":null} +{"run_id":"1743676161-849633236","line":541,"new":null,"old":null} +{"run_id":"1743676161-849633236","line":772,"new":null,"old":null} +{"run_id":"1743676161-849633236","line":1788,"new":null,"old":null} +{"run_id":"1743676161-849633236","line":4410,"new":{"module_name":"plc_lowering__inheritance__super_tests","snapshot_name":"valid_super_deref_in_paren_expression_edge_case","metadata":{"source":"compiler/plc_lowering/src/inheritance.rs","assertion_line":4410,"expression":"statements"},"snapshot":"[\n Assignment {\n left: ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"z\",\n },\n ),\n base: None,\n },\n right: BinaryExpression {\n operator: Plus,\n left: ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"x\",\n },\n ),\n base: Some(\n ReferenceExpr {\n kind: Deref,\n base: Some(\n ParenExpression {\n expression: CallStatement {\n operator: ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"REF\",\n },\n ),\n base: None,\n },\n parameters: Some(\n ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"__parent\",\n },\n ),\n base: None,\n },\n ),\n },\n },\n ),\n },\n ),\n },\n right: ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"y\",\n },\n ),\n base: Some(\n ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"__parent\",\n },\n ),\n base: None,\n },\n ),\n },\n },\n },\n Assignment {\n left: ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"local\",\n },\n ),\n base: None,\n },\n right: ReferenceExpr {\n kind: Deref,\n base: Some(\n ParenExpression {\n expression: CallStatement {\n operator: ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"REF\",\n },\n ),\n base: None,\n },\n parameters: Some(\n ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"__parent\",\n },\n ),\n base: None,\n },\n ),\n },\n },\n ),\n },\n },\n]"},"old":{"module_name":"plc_lowering__inheritance__super_tests","metadata":{},"snapshot":"[\n Assignment {\n left: ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"z\",\n },\n ),\n base: None,\n },\n right: BinaryExpression {\n operator: Plus,\n left: ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"x\",\n },\n ),\n base: Some(\n ReferenceExpr {\n kind: Deref,\n base: Some(\n ParenExpression {\n expression: CallStatement {\n operator: ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"REF\",\n },\n ),\n base: None,\n },\n parameters: Some(\n ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"__parent\",\n },\n ),\n base: None,\n },\n ),\n },\n },\n ),\n },\n ),\n },\n right: ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"y\",\n },\n ),\n base: Some(\n ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"__parent\",\n },\n ),\n base: None,\n },\n ),\n },\n },\n },\n]"}} +{"run_id":"1743678517-19413121","line":2393,"new":null,"old":null} +{"run_id":"1743678517-19413121","line":279,"new":null,"old":null} +{"run_id":"1743678517-19413121","line":2310,"new":null,"old":null} +{"run_id":"1743678517-19413121","line":2439,"new":null,"old":null} +{"run_id":"1743678517-19413121","line":2498,"new":null,"old":null} +{"run_id":"1743678517-19413121","line":2326,"new":null,"old":null} +{"run_id":"1743678517-19413121","line":2010,"new":null,"old":null} +{"run_id":"1743678517-19413121","line":2340,"new":null,"old":null} +{"run_id":"1743678517-19413121","line":452,"new":null,"old":null} +{"run_id":"1743678517-19413121","line":2098,"new":null,"old":null} +{"run_id":"1743678517-19413121","line":2354,"new":null,"old":null} +{"run_id":"1743678517-19413121","line":559,"new":null,"old":null} +{"run_id":"1743678517-19413121","line":1107,"new":null,"old":null} +{"run_id":"1743678517-19413121","line":2172,"new":null,"old":null} +{"run_id":"1743678517-19413121","line":328,"new":null,"old":null} +{"run_id":"1743678517-19413121","line":1575,"new":null,"old":null} +{"run_id":"1743678517-19413121","line":1795,"new":null,"old":null} +{"run_id":"1743678517-19413121","line":1248,"new":null,"old":null} +{"run_id":"1743678517-19413121","line":686,"new":null,"old":null} +{"run_id":"1743684516-801695239","line":279,"new":null,"old":null} +{"run_id":"1743684516-801695239","line":2439,"new":null,"old":null} +{"run_id":"1743684516-801695239","line":2393,"new":null,"old":null} +{"run_id":"1743684516-801695239","line":2498,"new":null,"old":null} +{"run_id":"1743684516-801695239","line":2098,"new":null,"old":null} +{"run_id":"1743684516-801695239","line":2010,"new":null,"old":null} +{"run_id":"1743684516-801695239","line":2172,"new":null,"old":null} +{"run_id":"1743684516-801695239","line":2310,"new":null,"old":null} +{"run_id":"1743684516-801695239","line":2326,"new":null,"old":null} +{"run_id":"1743684516-801695239","line":452,"new":null,"old":null} +{"run_id":"1743684516-801695239","line":2340,"new":null,"old":null} +{"run_id":"1743684516-801695239","line":559,"new":null,"old":null} +{"run_id":"1743684516-801695239","line":1795,"new":null,"old":null} +{"run_id":"1743684516-801695239","line":2354,"new":null,"old":null} +{"run_id":"1743684516-801695239","line":328,"new":null,"old":null} +{"run_id":"1743684516-801695239","line":1107,"new":null,"old":null} +{"run_id":"1743684516-801695239","line":1575,"new":null,"old":null} +{"run_id":"1743684516-801695239","line":1248,"new":null,"old":null} +{"run_id":"1743684516-801695239","line":686,"new":null,"old":null} +{"run_id":"1743687248-609667611","line":2393,"new":null,"old":null} +{"run_id":"1743687248-609667611","line":279,"new":null,"old":null} +{"run_id":"1743687248-609667611","line":2439,"new":null,"old":null} +{"run_id":"1743687248-609667611","line":2498,"new":null,"old":null} +{"run_id":"1743687248-609667611","line":2310,"new":null,"old":null} +{"run_id":"1743687248-609667611","line":2010,"new":null,"old":null} +{"run_id":"1743687248-609667611","line":559,"new":null,"old":null} +{"run_id":"1743687248-609667611","line":2172,"new":null,"old":null} +{"run_id":"1743687248-609667611","line":452,"new":null,"old":null} +{"run_id":"1743687248-609667611","line":2098,"new":null,"old":null} +{"run_id":"1743687248-609667611","line":2326,"new":null,"old":null} +{"run_id":"1743687248-609667611","line":2340,"new":null,"old":null} +{"run_id":"1743687248-609667611","line":1575,"new":null,"old":null} +{"run_id":"1743687248-609667611","line":1795,"new":null,"old":null} +{"run_id":"1743687248-609667611","line":2354,"new":null,"old":null} +{"run_id":"1743687248-609667611","line":1107,"new":null,"old":null} +{"run_id":"1743687248-609667611","line":328,"new":null,"old":null} +{"run_id":"1743687248-609667611","line":1248,"new":null,"old":null} +{"run_id":"1743687248-609667611","line":686,"new":null,"old":null} +{"run_id":"1743687443-512040318","line":2393,"new":null,"old":null} +{"run_id":"1743687443-512040318","line":2439,"new":null,"old":null} +{"run_id":"1743687443-512040318","line":279,"new":null,"old":null} +{"run_id":"1743687443-512040318","line":2310,"new":null,"old":null} +{"run_id":"1743687443-512040318","line":2172,"new":null,"old":null} +{"run_id":"1743687443-512040318","line":452,"new":null,"old":null} +{"run_id":"1743687443-512040318","line":2326,"new":null,"old":null} +{"run_id":"1743687443-512040318","line":2098,"new":null,"old":null} +{"run_id":"1743687443-512040318","line":2010,"new":null,"old":null} +{"run_id":"1743687443-512040318","line":2340,"new":null,"old":null} +{"run_id":"1743687443-512040318","line":2498,"new":null,"old":null} +{"run_id":"1743687443-512040318","line":1107,"new":null,"old":null} +{"run_id":"1743687443-512040318","line":559,"new":null,"old":null} +{"run_id":"1743687443-512040318","line":2354,"new":null,"old":null} +{"run_id":"1743687443-512040318","line":1795,"new":null,"old":null} +{"run_id":"1743687443-512040318","line":328,"new":null,"old":null} +{"run_id":"1743687443-512040318","line":1248,"new":null,"old":null} +{"run_id":"1743687443-512040318","line":1575,"new":null,"old":null} +{"run_id":"1743687443-512040318","line":686,"new":null,"old":null} +{"run_id":"1743763095-690977869","line":279,"new":null,"old":null} +{"run_id":"1743763095-690977869","line":2393,"new":null,"old":null} +{"run_id":"1743763095-690977869","line":2439,"new":null,"old":null} +{"run_id":"1743763095-690977869","line":2010,"new":null,"old":null} +{"run_id":"1743763095-690977869","line":2172,"new":null,"old":null} +{"run_id":"1743763095-690977869","line":2498,"new":null,"old":null} +{"run_id":"1743763095-690977869","line":452,"new":null,"old":null} +{"run_id":"1743763095-690977869","line":2310,"new":null,"old":null} +{"run_id":"1743763095-690977869","line":1107,"new":null,"old":null} +{"run_id":"1743763095-690977869","line":2098,"new":null,"old":null} +{"run_id":"1743763095-690977869","line":559,"new":null,"old":null} +{"run_id":"1743763095-690977869","line":686,"new":null,"old":null} +{"run_id":"1743763095-690977869","line":328,"new":null,"old":null} +{"run_id":"1743763095-690977869","line":2326,"new":null,"old":null} +{"run_id":"1743763095-690977869","line":1248,"new":null,"old":null} +{"run_id":"1743763095-690977869","line":2340,"new":null,"old":null} +{"run_id":"1743763095-690977869","line":2354,"new":null,"old":null} +{"run_id":"1743763095-690977869","line":1795,"new":null,"old":null} +{"run_id":"1743763095-690977869","line":1575,"new":null,"old":null} +{"run_id":"1743763184-106471777","line":2439,"new":null,"old":null} +{"run_id":"1743763184-106471777","line":2393,"new":null,"old":null} +{"run_id":"1743763184-106471777","line":2310,"new":null,"old":null} +{"run_id":"1743763184-106471777","line":279,"new":null,"old":null} +{"run_id":"1743763184-106471777","line":2098,"new":null,"old":null} +{"run_id":"1743763184-106471777","line":452,"new":null,"old":null} +{"run_id":"1743763184-106471777","line":2498,"new":null,"old":null} +{"run_id":"1743763184-106471777","line":2326,"new":null,"old":null} +{"run_id":"1743763184-106471777","line":2010,"new":null,"old":null} +{"run_id":"1743763184-106471777","line":2172,"new":null,"old":null} +{"run_id":"1743763184-106471777","line":2340,"new":null,"old":null} +{"run_id":"1743763184-106471777","line":2354,"new":null,"old":null} +{"run_id":"1743763184-106471777","line":559,"new":null,"old":null} +{"run_id":"1743763184-106471777","line":328,"new":null,"old":null} +{"run_id":"1743763184-106471777","line":1107,"new":null,"old":null} +{"run_id":"1743763184-106471777","line":1795,"new":null,"old":null} +{"run_id":"1743763184-106471777","line":1575,"new":null,"old":null} +{"run_id":"1743763184-106471777","line":1248,"new":null,"old":null} +{"run_id":"1743763184-106471777","line":686,"new":null,"old":null} +{"run_id":"1743763305-876434218","line":2393,"new":null,"old":null} +{"run_id":"1743763305-876434218","line":279,"new":null,"old":null} +{"run_id":"1743763305-876434218","line":2439,"new":null,"old":null} +{"run_id":"1743763305-876434218","line":2310,"new":null,"old":null} +{"run_id":"1743763305-876434218","line":2010,"new":null,"old":null} +{"run_id":"1743763305-876434218","line":2326,"new":null,"old":null} +{"run_id":"1743763305-876434218","line":2172,"new":null,"old":null} +{"run_id":"1743763305-876434218","line":2098,"new":null,"old":null} +{"run_id":"1743763305-876434218","line":2498,"new":null,"old":null} +{"run_id":"1743763305-876434218","line":452,"new":null,"old":null} +{"run_id":"1743763305-876434218","line":2340,"new":null,"old":null} +{"run_id":"1743763305-876434218","line":559,"new":null,"old":null} +{"run_id":"1743763305-876434218","line":1107,"new":null,"old":null} +{"run_id":"1743763305-876434218","line":2354,"new":null,"old":null} +{"run_id":"1743763305-876434218","line":328,"new":null,"old":null} +{"run_id":"1743763305-876434218","line":1575,"new":null,"old":null} +{"run_id":"1743763305-876434218","line":1795,"new":null,"old":null} +{"run_id":"1743763305-876434218","line":1248,"new":null,"old":null} +{"run_id":"1743763305-876434218","line":686,"new":null,"old":null} +{"run_id":"1743782405-273296447","line":2393,"new":null,"old":null} +{"run_id":"1743782405-273296447","line":279,"new":null,"old":null} +{"run_id":"1743782405-273296447","line":2310,"new":null,"old":null} +{"run_id":"1743782405-273296447","line":2172,"new":null,"old":null} +{"run_id":"1743782405-273296447","line":2098,"new":null,"old":null} +{"run_id":"1743782405-273296447","line":2326,"new":null,"old":null} +{"run_id":"1743782405-273296447","line":452,"new":null,"old":null} +{"run_id":"1743782405-273296447","line":2498,"new":null,"old":null} +{"run_id":"1743782405-273296447","line":2010,"new":null,"old":null} +{"run_id":"1743782405-273296447","line":2340,"new":null,"old":null} +{"run_id":"1743782405-273296447","line":559,"new":null,"old":null} +{"run_id":"1743782405-273296447","line":2354,"new":null,"old":null} +{"run_id":"1743782405-273296447","line":2439,"new":null,"old":null} +{"run_id":"1743782405-273296447","line":328,"new":null,"old":null} +{"run_id":"1743782405-273296447","line":1107,"new":null,"old":null} +{"run_id":"1743782405-273296447","line":1795,"new":null,"old":null} +{"run_id":"1743782405-273296447","line":1575,"new":null,"old":null} +{"run_id":"1743782405-273296447","line":686,"new":null,"old":null} +{"run_id":"1743782405-273296447","line":1248,"new":null,"old":null} diff --git a/compiler/plc_lowering/src/inheritance.rs b/compiler/plc_lowering/src/inheritance.rs index c197e868f8..421ed391d0 100644 --- a/compiler/plc_lowering/src/inheritance.rs +++ b/compiler/plc_lowering/src/inheritance.rs @@ -447,46 +447,4 @@ impl AstVisitorMut for SuperKeywordLowerer<'_> { .with_pou(self.ctx.pou.as_deref().unwrap_or_default()); self.annotations.as_mut().unwrap().import(resolver.resolve_statement(node)); } - // TODO: this test is completly in the wrong place => move! - #[test] - fn simple_this_deref() { - let src: SourceCode = " - FUNCTION_BLOCK foo - VAR - x : INT; - y : INT; - END_VAR - y := this^.x; - END_FUNCTION_BLOCK - - " - .into(); - - let (_, project) = parse_and_annotate("test", vec![src]).unwrap(); - let statements = &project.units[0].get_unit().implementations[1].statements; - assert_debug_snapshot!(statements, @r#" - [ - CallStatement { - operator: ReferenceExpr { - kind: Member( - Identifier { - name: "REF", - }, - ), - base: None, - }, - parameters: Some( - ReferenceExpr { - kind: Member( - Identifier { - name: "__foo", - }, - ), - base: None, - }, - ), - }, - ] - "#); - } } diff --git a/src/resolver/tests/resolve_expressions_tests.rs b/src/resolver/tests/resolve_expressions_tests.rs index 944f0046fa..5eefe9afb5 100644 --- a/src/resolver/tests/resolve_expressions_tests.rs +++ b/src/resolver/tests/resolve_expressions_tests.rs @@ -6118,7 +6118,7 @@ fn program_call_declared_as_variable_is_annotated() { unreachable!() }; - insta::assert_debug_snapshot!(annotations.get(&operator), @r###" + insta::assert_debug_snapshot!(annotations.get(operator), @r###" Some( Variable { resulting_type: "ridiculous_chaining", @@ -6317,37 +6317,62 @@ fn just_this() { let annotations = annotate_with_ids(&unit, &mut index, id_provider); let statement = &unit.implementations[0].statements[0]; - assert!(index.find_type("fb.__THIS").is_some()); + dbg!(annotations.get_type_hint(statement, &index)); // => none + dbg!(annotations.get_type(statement, &index)); // => none + dbg!(&index.get_type("fb.__THIS")); + assert!(index.find_type("b.__THIS").is_some()); assert_type_and_hint!(&annotations, &index, statement, "fb.__THIS", None); } #[test] -#[ignore = "TODO: #THIS: fix this test"] -fn this_assignment() { - let id_provider = IdProvider::default(); - let (unit, mut index) = index_with_ids( - " - FUNCTION_BLOCK fb - VAR - x : REF_TO fb; - END_VAR - x := this; - END_FUNCTION_BLOCK - ", - id_provider.clone(), - ); - - let annotations = annotate_with_ids(&unit, &mut index, id_provider); - let statement = &unit.implementations[0].statements[0]; - dbg!(&statement); - assert!(index.find_type("fb.__THIS").is_some()); - assert_type_and_hint!(&annotations, &index, statement, "fb.__THIS", None); +#[ignore = "TODO: #THIS fixme"] +fn this_assignment_is_ok() { + todo!(); + // let id_provider = IdProvider::default(); + // let (unit, mut index) = index_with_ids( + // " + // FUNCTION_BLOCK fb + // VAR + // x : REF_TO fb; + // END_VAR + // x := this; + // END_FUNCTION_BLOCK + // ", + // id_provider.clone(), + // ); + + // let annotations = annotate_with_ids(&unit, &mut index, id_provider); + // let statement = &unit.implementations[0].statements[0]; + // dbg!(&statement); + // assert!(index.find_type("fb.__THIS").is_some()); + // // $crate::resolver::AnnotationMap::get_type_hint($annotations, $stmt, $index), + // dbg!(annotations.get_type_hint(statement, &index)); // => none + // dbg!(annotations.get_type(statement, &index)); // => none + // dbg!(&index.get_type("this")); + // // DataType { + // // name: "fb.__THIS", + // // initial_value: None, + // // information: Pointer { + // // name: "fb.__THIS", + // // inner_type_name: "fb", + // // auto_deref: None, + // // }, + // // nature: Any, + // // location: SourceLocation { + // // span: None, + // // }, + // // }, + // let AstStatement::Assignment(Assignment { left, right }) = statement.get_stmt() else { todo!() }; + // dbg!(&right); + // dbg!(annotations.get_type_hint(right, &index)); // => none + // dbg!(annotations.get_type(right, &index)); // => none + // assert_type_and_hint!(&annotations, &index, right, DINT_TYPE, None); } #[test] fn this_call() { let id_provider = IdProvider::default(); - let (unit, mut index) = index_with_ids( + let (unit, index) = index_with_ids( " FUNCTION_BLOCK fb VAR @@ -6358,7 +6383,6 @@ fn this_call() { id_provider.clone(), ); - let annotations = annotate_with_ids(&unit, &mut index, id_provider); let statement = &unit.implementations[0].statements[0]; dbg!(&statement); assert!(index.find_type("fb.__THIS").is_some()); @@ -6408,7 +6432,6 @@ fn this_in_conditionals() { ); let annotations = annotate_with_ids(&unit, &mut index, id_provider); - let statement = &unit.implementations[0].statements[0]; let AstStatement::ControlStatement(AstControlStatement::If(IfStatement { blocks, .. })) = unit.implementations[0].statements[0].get_stmt() else { From e38b70da9474ff955d38049e4fcf63416113f300 Mon Sep 17 00:00:00 2001 From: abroooo <61380142+abroooo@users.noreply.github.com> Date: Tue, 6 May 2025 12:07:19 +0200 Subject: [PATCH 37/48] snapshots --- src/codegen/tests/oop_tests.rs | 104 ++++++++++++++++++++- src/tests/adr/initializer_functions_adr.rs | 20 ++++ src/tests/adr/pou_adr.rs | 4 + 3 files changed, 126 insertions(+), 2 deletions(-) diff --git a/src/codegen/tests/oop_tests.rs b/src/codegen/tests/oop_tests.rs index 9c35b089ac..f84ac20af0 100644 --- a/src/codegen/tests/oop_tests.rs +++ b/src/codegen/tests/oop_tests.rs @@ -896,6 +896,9 @@ fn pass_this_to_method() { let code = codegen( r#" FUNCTION_BLOCK FB_Test + VAR + x : INT := 5; + END_VAR METHOD foo VAR test : FB_Test2; @@ -914,7 +917,103 @@ fn pass_this_to_method() { END_FUNCTION_BLOCK "#, ); - insta::assert_snapshot!(code, @r#""#); + insta::assert_snapshot!(code, @r#" + ; ModuleID = '' + source_filename = "" + + %FB_Test = type { i16 } + %FB_Test2 = type {} + + @__FB_Test__init = constant %FB_Test { i16 5 } + @__FB_Test2__init = constant %FB_Test2 zeroinitializer + @llvm.global_ctors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 0, void ()* @__init___Test, i8* null }] + + define void @FB_Test(%FB_Test* %0) { + entry: + %this = alloca %FB_Test*, align 8 + store %FB_Test* %0, %FB_Test** %this, align 8 + %x = getelementptr inbounds %FB_Test, %FB_Test* %0, i32 0, i32 0 + ret void + } + + define void @FB_Test_foo(%FB_Test* %0) { + entry: + %this = alloca %FB_Test*, align 8 + store %FB_Test* %0, %FB_Test** %this, align 8 + %x = getelementptr inbounds %FB_Test, %FB_Test* %0, i32 0, i32 0 + %test = alloca %FB_Test2, align 8 + %x1 = alloca i16, align 2 + %1 = bitcast %FB_Test2* %test to i8* + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 1 %1, i8* align 1 bitcast (%FB_Test2* @__FB_Test2__init to i8*), i64 ptrtoint (%FB_Test2* getelementptr (%FB_Test2, %FB_Test2* null, i32 1) to i64), i1 false) + store i16 0, i16* %x1, align 2 + call void @__init_fb_test2(%FB_Test2* %test) + call void @__user_init_FB_Test2(%FB_Test2* %test) + %2 = load %FB_Test*, %FB_Test** %this, align 8 + %call = call i16 @FB_Test2_bar(%FB_Test2* %test, %FB_Test* %2) + ret void + } + + define void @FB_Test2(%FB_Test2* %0) { + entry: + %this = alloca %FB_Test2*, align 8 + store %FB_Test2* %0, %FB_Test2** %this, align 8 + ret void + } + + define i16 @FB_Test2_bar(%FB_Test2* %0, %FB_Test* %1) { + entry: + %this = alloca %FB_Test2*, align 8 + store %FB_Test2* %0, %FB_Test2** %this, align 8 + %FB_Test2.bar = alloca i16, align 2 + %test = alloca %FB_Test*, align 8 + store %FB_Test* %1, %FB_Test** %test, align 8 + store i16 0, i16* %FB_Test2.bar, align 2 + %deref = load %FB_Test*, %FB_Test** %test, align 8 + %x = getelementptr inbounds %FB_Test, %FB_Test* %deref, i32 0, i32 0 + %load_x = load i16, i16* %x, align 2 + store i16 %load_x, i16* %FB_Test2.bar, align 2 + %FB_Test2_bar_ret = load i16, i16* %FB_Test2.bar, align 2 + ret i16 %FB_Test2_bar_ret + } + + ; 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 + + define void @__init_fb_test(%FB_Test* %0) { + entry: + %self = alloca %FB_Test*, align 8 + store %FB_Test* %0, %FB_Test** %self, align 8 + ret void + } + + define void @__init_fb_test2(%FB_Test2* %0) { + entry: + %self = alloca %FB_Test2*, align 8 + store %FB_Test2* %0, %FB_Test2** %self, align 8 + ret void + } + + define void @__user_init_FB_Test(%FB_Test* %0) { + entry: + %self = alloca %FB_Test*, align 8 + store %FB_Test* %0, %FB_Test** %self, align 8 + ret void + } + + define void @__user_init_FB_Test2(%FB_Test2* %0) { + entry: + %self = alloca %FB_Test2*, align 8 + store %FB_Test2* %0, %FB_Test2** %self, align 8 + ret void + } + + define void @__init___Test() { + entry: + ret void + } + + attributes #0 = { argmemonly nofree nounwind willreturn } + "#); } #[test] @@ -1249,11 +1348,12 @@ fn this_with_self_pointer() { } #[test] +#[ignore] fn this_in_variable_initialization() { let code = codegen( r#" FUNCTION_BLOCK FB - VAR CONSTANT + VAR x : INT; END_VAR VAR diff --git a/src/tests/adr/initializer_functions_adr.rs b/src/tests/adr/initializer_functions_adr.rs index 0dc08c1cc2..4ba6f4249e 100644 --- a/src/tests/adr/initializer_functions_adr.rs +++ b/src/tests/adr/initializer_functions_adr.rs @@ -579,12 +579,16 @@ fn generating_init_functions() { define void @foo(%foo* %0) { entry: + %this = alloca %foo*, align 8 + store %foo* %0, %foo** %this, align 8 %ps = getelementptr inbounds %foo, %foo* %0, i32 0, i32 0 ret void } define void @bar(%bar* %0) { entry: + %this = alloca %bar*, align 8 + store %bar* %0, %bar** %this, align 8 %fb = getelementptr inbounds %bar, %bar* %0, i32 0, i32 0 ret void } @@ -723,6 +727,8 @@ fn intializing_temporary_variables() { define void @foo(%foo* %0) { entry: + %this = alloca %foo*, align 8 + store %foo* %0, %foo** %this, align 8 %s = getelementptr inbounds %foo, %foo* %0, i32 0, i32 0 %s2 = alloca [81 x i8]*, align 8 store [81 x i8]* @ps2, [81 x i8]** %s2, align 8 @@ -810,11 +816,15 @@ fn initializing_method_variables() { define void @foo(%foo* %0) { entry: + %this = alloca %foo*, align 8 + store %foo* %0, %foo** %this, align 8 ret void } define void @foo_bar(%foo* %0) { entry: + %this = alloca %foo*, align 8 + store %foo* %0, %foo** %this, align 8 %x = alloca i32, align 4 %px = alloca i32*, align 8 store i32 10, i32* %x, align 4 @@ -882,12 +892,16 @@ fn initializing_method_variables() { define void @foo(%foo* %0) { entry: + %this = alloca %foo*, align 8 + store %foo* %0, %foo** %this, align 8 %x = getelementptr inbounds %foo, %foo* %0, i32 0, i32 0 ret void } define void @foo_bar(%foo* %0) { entry: + %this = alloca %foo*, align 8 + store %foo* %0, %foo** %this, align 8 %x = getelementptr inbounds %foo, %foo* %0, i32 0, i32 0 %px = alloca i32*, align 8 store i32* %x, i32** %px, align 8 @@ -897,6 +911,8 @@ fn initializing_method_variables() { define void @foo_baz(%foo* %0) { entry: + %this = alloca %foo*, align 8 + store %foo* %0, %foo** %this, align 8 %x = getelementptr inbounds %foo, %foo* %0, i32 0, i32 0 %px = alloca i32*, align 8 store i32* @y, i32** %px, align 8 @@ -952,12 +968,16 @@ fn initializing_method_variables() { define void @foo(%foo* %0) { entry: + %this = alloca %foo*, align 8 + store %foo* %0, %foo** %this, align 8 %x = getelementptr inbounds %foo, %foo* %0, i32 0, i32 0 ret void } define void @foo_bar(%foo* %0) { entry: + %this = alloca %foo*, align 8 + store %foo* %0, %foo** %this, align 8 %x = getelementptr inbounds %foo, %foo* %0, i32 0, i32 0 %x1 = alloca i32, align 4 %px = alloca i32*, align 8 diff --git a/src/tests/adr/pou_adr.rs b/src/tests/adr/pou_adr.rs index 2f41fbf046..5e9c62155e 100644 --- a/src/tests/adr/pou_adr.rs +++ b/src/tests/adr/pou_adr.rs @@ -300,6 +300,8 @@ fn function_blocks_get_a_method_with_a_self_parameter() { define void @main_fb(%main_fb* %0) { entry: + %this = alloca %main_fb*, align 8 + store %main_fb* %0, %main_fb** %this, align 8 %i = getelementptr inbounds %main_fb, %main_fb* %0, i32 0, i32 0 %io = getelementptr inbounds %main_fb, %main_fb* %0, i32 0, i32 1 %o = getelementptr inbounds %main_fb, %main_fb* %0, i32 0, i32 2 @@ -357,6 +359,8 @@ fn calling_a_function_block() { define void @main_fb(%main_fb* %0) { entry: + %this = alloca %main_fb*, align 8 + store %main_fb* %0, %main_fb** %this, align 8 %i = getelementptr inbounds %main_fb, %main_fb* %0, i32 0, i32 0 %io = getelementptr inbounds %main_fb, %main_fb* %0, i32 0, i32 1 %o = getelementptr inbounds %main_fb, %main_fb* %0, i32 0, i32 2 From a7e9d2b0819377784593142581de3c15e1de758b Mon Sep 17 00:00:00 2001 From: abroooo <61380142+abroooo@users.noreply.github.com> Date: Wed, 7 May 2025 11:32:37 +0200 Subject: [PATCH 38/48] cleanups --- .../src/diagnostics/error_codes/E120.md | 2 +- .../src/diagnostics/error_codes/E121.md | 2 +- .../generators/expression_generator.rs | 6 +- src/codegen/tests/oop_tests.rs | 66 ++++++++++----- .../tests/resolve_expressions_tests.rs | 84 ++++++++----------- .../tests/this_keyword_validation_tests.rs | 2 +- ...egration__cfc__ir__conditional_return.snap | 3 +- ...r__conditional_return_evaluating_true.snap | 3 +- ...tional_return_evaluating_true_negated.snap | 3 +- 9 files changed, 92 insertions(+), 79 deletions(-) diff --git a/compiler/plc_diagnostics/src/diagnostics/error_codes/E120.md b/compiler/plc_diagnostics/src/diagnostics/error_codes/E120.md index c2e8e04e4f..3f39624146 100644 --- a/compiler/plc_diagnostics/src/diagnostics/error_codes/E120.md +++ b/compiler/plc_diagnostics/src/diagnostics/error_codes/E120.md @@ -1,6 +1,6 @@ # `THIS` keyword is only allowed in `FUNCTION_BLOCK` -`THIS` is a keyword in IEC 61131-3 that refers to the instance of a `FUNCTION_BLOCK`. It is used to access the instance variables and methods of the `FUNCTION_BLOCK` from within its own methods. +`THIS` is a keyword in IEC 61131-3 that refers to the instance of a `FUNCTION_BLOCK`. It is used to access the instance variables and methods of the `FUNCTION_BLOCK` from within its own methods or body. Therefore it cannot be used in `FUNCTION`s or `PROGRAM`s, as these do not have an instance context like `FUNCTION_BLOCK`s do. Errouneus code example: diff --git a/compiler/plc_diagnostics/src/diagnostics/error_codes/E121.md b/compiler/plc_diagnostics/src/diagnostics/error_codes/E121.md index d595cd73cc..cfffa3cd82 100644 --- a/compiler/plc_diagnostics/src/diagnostics/error_codes/E121.md +++ b/compiler/plc_diagnostics/src/diagnostics/error_codes/E121.md @@ -1,6 +1,6 @@ # `THIS` is read-only -`THIS` is a keyword in IEC 61131-3 that refers to the instance of a `FUNCTION_BLOCK`. It is a pointer to the instance itself which doesn't change. Therefore `THIS` cannot be assigned. +`THIS` is a keyword in IEC 61131-3 that refers to the instance of a `FUNCTION_BLOCK`. It is a pointer to the instance itself which doesn't change. Therefore `THIS` cannot be assigned to. Errouneus code example: ```iec61131 diff --git a/src/codegen/generators/expression_generator.rs b/src/codegen/generators/expression_generator.rs index 93f125c531..c7fabd907f 100644 --- a/src/codegen/generators/expression_generator.rs +++ b/src/codegen/generators/expression_generator.rs @@ -218,13 +218,13 @@ impl<'ink, 'b> ExpressionCodeGenerator<'ink, 'b> { // generate the expression match expression.get_stmt() { AstStatement::This => { - // TODO: #THIS meaningful errors - // [ ] add tests + // TODO: #THIS how to test the errors here? self.function_context.ok_or_else(|| { Diagnostic::codegen_error("Cannot use 'this' without context", expression) })?; let Some(this_name) = self.annotations.get_call_name(expression) else { - todo!("error handling") + // TODO: is this correct? + unreachable!("this should have a name"); }; let this_value = self.llvm_index.find_loaded_associated_variable_value(this_name).ok_or_else(|| { diff --git a/src/codegen/tests/oop_tests.rs b/src/codegen/tests/oop_tests.rs index f84ac20af0..23607e2858 100644 --- a/src/codegen/tests/oop_tests.rs +++ b/src/codegen/tests/oop_tests.rs @@ -736,16 +736,11 @@ fn this_in_method_call_chain() { let code = codegen( r#" FUNCTION_BLOCK FB_Test - VAR - counter : INT := 0; - END_VAR - METHOD Step THIS^.Increment(); END_METHOD METHOD Increment - counter := counter + 1; END_METHOD END_FUNCTION_BLOCK "#, @@ -754,7 +749,7 @@ fn this_in_method_call_chain() { ; ModuleID = '' source_filename = "" - %FB_Test = type { i16 } + %FB_Test = type {} @__FB_Test__init = constant %FB_Test zeroinitializer @llvm.global_ctors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 0, void ()* @__init___Test, i8* null }] @@ -763,7 +758,6 @@ fn this_in_method_call_chain() { entry: %this = alloca %FB_Test*, align 8 store %FB_Test* %0, %FB_Test** %this, align 8 - %counter = getelementptr inbounds %FB_Test, %FB_Test* %0, i32 0, i32 0 ret void } @@ -771,7 +765,6 @@ fn this_in_method_call_chain() { entry: %this = alloca %FB_Test*, align 8 store %FB_Test* %0, %FB_Test** %this, align 8 - %counter = getelementptr inbounds %FB_Test, %FB_Test* %0, i32 0, i32 0 %deref = load %FB_Test*, %FB_Test** %this, align 8 call void @FB_Test_Increment(%FB_Test* %deref) ret void @@ -781,12 +774,6 @@ fn this_in_method_call_chain() { entry: %this = alloca %FB_Test*, align 8 store %FB_Test* %0, %FB_Test** %this, align 8 - %counter = getelementptr inbounds %FB_Test, %FB_Test* %0, i32 0, i32 0 - %load_counter = load i16, i16* %counter, align 2 - %1 = sext i16 %load_counter to i32 - %tmpVar = add i32 %1, 1 - %2 = trunc i32 %tmpVar to i16 - store i16 %2, i16* %counter, align 2 ret void } @@ -1017,7 +1004,7 @@ fn pass_this_to_method() { } #[test] -fn shadowing_is_working() { +fn this_with_shadowed_variable() { let code = codegen( r#" FUNCTION_BLOCK FB_Test @@ -1167,7 +1154,7 @@ fn this_calling_function_and_passing_this() { } #[test] -fn this_in_property_calling_method() { +fn this_in_property_and_calling_method() { let code = codegen( r#" FUNCTION_BLOCK FB_Test @@ -1348,25 +1335,62 @@ fn this_with_self_pointer() { } #[test] -#[ignore] fn this_in_variable_initialization() { let code = codegen( r#" FUNCTION_BLOCK FB - VAR - x : INT; + VAR CONSTANT + x : INT := 5; END_VAR VAR - self : REF_TO FB := THIS; + self : REF_TO FB; y : INT := THIS^.x; END_VAR END_FUNCTION_BLOCK "#, ); - insta::assert_snapshot!(code, @r#""#); + insta::assert_snapshot!(code, @r#" + ; ModuleID = '' + source_filename = "" + + %FB = type { i16, %FB*, i16 } + + @__FB__init = constant %FB { i16 5, %FB* null, i16 5 } + @llvm.global_ctors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 0, void ()* @__init___Test, i8* null }] + + define void @FB(%FB* %0) { + entry: + %this = alloca %FB*, align 8 + store %FB* %0, %FB** %this, align 8 + %x = getelementptr inbounds %FB, %FB* %0, i32 0, i32 0 + %self = getelementptr inbounds %FB, %FB* %0, i32 0, i32 1 + %y = getelementptr inbounds %FB, %FB* %0, i32 0, i32 2 + ret void + } + + define void @__init_fb(%FB* %0) { + entry: + %self = alloca %FB*, align 8 + store %FB* %0, %FB** %self, align 8 + ret void + } + + define void @__user_init_FB(%FB* %0) { + entry: + %self = alloca %FB*, align 8 + store %FB* %0, %FB** %self, align 8 + ret void + } + + define void @__init___Test() { + entry: + ret void + } + "#); } #[test] +#[ignore = "not working"] fn this_in_action_in_functionblock() { let code = codegen( r#" diff --git a/src/resolver/tests/resolve_expressions_tests.rs b/src/resolver/tests/resolve_expressions_tests.rs index 5eefe9afb5..f359a8c4d8 100644 --- a/src/resolver/tests/resolve_expressions_tests.rs +++ b/src/resolver/tests/resolve_expressions_tests.rs @@ -6245,8 +6245,6 @@ fn this_should_not_be_in_programs() { }; assert!(index.find_type("myProg.__THIS").is_none()); - // TODO: #THIS: should we still have the annotations? - // Yes, this is the correct since it's the AST which is the result of the parser. let AstStatement::ReferenceExpr(ReferenceExpr { base: Some(deref), .. }) = statement_1.left.get_stmt() else { unreachable!(); @@ -6320,71 +6318,59 @@ fn just_this() { dbg!(annotations.get_type_hint(statement, &index)); // => none dbg!(annotations.get_type(statement, &index)); // => none dbg!(&index.get_type("fb.__THIS")); - assert!(index.find_type("b.__THIS").is_some()); + assert!(index.find_type("fb.__THIS").is_some()); assert_type_and_hint!(&annotations, &index, statement, "fb.__THIS", None); } #[test] -#[ignore = "TODO: #THIS fixme"] -fn this_assignment_is_ok() { - todo!(); - // let id_provider = IdProvider::default(); - // let (unit, mut index) = index_with_ids( - // " - // FUNCTION_BLOCK fb - // VAR - // x : REF_TO fb; - // END_VAR - // x := this; - // END_FUNCTION_BLOCK - // ", - // id_provider.clone(), - // ); - - // let annotations = annotate_with_ids(&unit, &mut index, id_provider); - // let statement = &unit.implementations[0].statements[0]; - // dbg!(&statement); - // assert!(index.find_type("fb.__THIS").is_some()); - // // $crate::resolver::AnnotationMap::get_type_hint($annotations, $stmt, $index), - // dbg!(annotations.get_type_hint(statement, &index)); // => none - // dbg!(annotations.get_type(statement, &index)); // => none - // dbg!(&index.get_type("this")); - // // DataType { - // // name: "fb.__THIS", - // // initial_value: None, - // // information: Pointer { - // // name: "fb.__THIS", - // // inner_type_name: "fb", - // // auto_deref: None, - // // }, - // // nature: Any, - // // location: SourceLocation { - // // span: None, - // // }, - // // }, - // let AstStatement::Assignment(Assignment { left, right }) = statement.get_stmt() else { todo!() }; - // dbg!(&right); - // dbg!(annotations.get_type_hint(right, &index)); // => none - // dbg!(annotations.get_type(right, &index)); // => none - // assert_type_and_hint!(&annotations, &index, right, DINT_TYPE, None); +fn this_assignment() { + let id_provider = IdProvider::default(); + let (unit, mut index) = index_with_ids( + " + FUNCTION_BLOCK fb + VAR + x : REF_TO fb; + END_VAR + x := this; + END_FUNCTION_BLOCK + ", + id_provider.clone(), + ); + + annotate_with_ids(&unit, &mut index, id_provider); + let statement = &unit.implementations[0].statements[0]; + assert!(index.find_type("fb.__THIS").is_some()); + assert_debug_snapshot!(statement, @r#" + Assignment { + left: ReferenceExpr { + kind: Member( + Identifier { + name: "x", + }, + ), + base: None, + }, + right: This, + } + "#); } #[test] fn this_call() { let id_provider = IdProvider::default(); - let (unit, index) = index_with_ids( + let (unit, mut index) = index_with_ids( " FUNCTION_BLOCK fb VAR +x:int; END_VAR - this^(); + x :=this^.x; END_FUNCTION_BLOCK ", id_provider.clone(), ); - let statement = &unit.implementations[0].statements[0]; - dbg!(&statement); + annotate_with_ids(&unit, &mut index, id_provider); assert!(index.find_type("fb.__THIS").is_some()); } diff --git a/src/validation/tests/this_keyword_validation_tests.rs b/src/validation/tests/this_keyword_validation_tests.rs index a2c4cdbe20..9549cb2be2 100644 --- a/src/validation/tests/this_keyword_validation_tests.rs +++ b/src/validation/tests/this_keyword_validation_tests.rs @@ -264,7 +264,6 @@ fn this_in_recursive_method_is_ok() { #[test] fn this_chained_with_super_is_not_ok() { let diagnostics = parse_and_validate_buffered( - // TODO: #THIS check with Michael (nested) r#" FUNCTION_BLOCK parent METHOD DoSomething : DINT @@ -347,6 +346,7 @@ fn this_with_self_pointer_is_ok() { METHOD InitRef refToSelf := ADR(THIS^); refToSelf := REF(THIS^); + refToSelf := THIS; END_METHOD END_FUNCTION_BLOCK "#, diff --git a/tests/integration/snapshots/tests__integration__cfc__ir__conditional_return.snap b/tests/integration/snapshots/tests__integration__cfc__ir__conditional_return.snap index 2938a5a281..2e6081e93f 100644 --- a/tests/integration/snapshots/tests__integration__cfc__ir__conditional_return.snap +++ b/tests/integration/snapshots/tests__integration__cfc__ir__conditional_return.snap @@ -1,7 +1,6 @@ --- source: tests/integration/cfc.rs expression: output_file_content_without_headers -snapshot_kind: text --- %conditional_return = type { i32 } @@ -10,6 +9,8 @@ snapshot_kind: text define void @conditional_return(%conditional_return* %0) { entry: + %this = alloca %conditional_return*, align 8 + store %conditional_return* %0, %conditional_return** %this, align 8 %val = getelementptr inbounds %conditional_return, %conditional_return* %0, i32 0, i32 0 %load_val = load i32, i32* %val, align 4 %tmpVar = icmp eq i32 %load_val, 5 diff --git a/tests/integration/snapshots/tests__integration__cfc__ir__conditional_return_evaluating_true.snap b/tests/integration/snapshots/tests__integration__cfc__ir__conditional_return_evaluating_true.snap index 740b1fac16..f376228187 100644 --- a/tests/integration/snapshots/tests__integration__cfc__ir__conditional_return_evaluating_true.snap +++ b/tests/integration/snapshots/tests__integration__cfc__ir__conditional_return_evaluating_true.snap @@ -1,7 +1,6 @@ --- source: tests/integration/cfc.rs expression: output_file_content_without_headers -snapshot_kind: text --- %conditional_return = type { i32 } @@ -35,6 +34,8 @@ declare void @llvm.memcpy.p0i8.p0i8.i64(i8* noalias nocapture writeonly, i8* noa define void @conditional_return(%conditional_return* %0) { entry: + %this = alloca %conditional_return*, align 8 + store %conditional_return* %0, %conditional_return** %this, align 8 %val = getelementptr inbounds %conditional_return, %conditional_return* %0, i32 0, i32 0 %load_val = load i32, i32* %val, align 4 %tmpVar = icmp eq i32 %load_val, 5 diff --git a/tests/integration/snapshots/tests__integration__cfc__ir__conditional_return_evaluating_true_negated.snap b/tests/integration/snapshots/tests__integration__cfc__ir__conditional_return_evaluating_true_negated.snap index 9555be0e52..c058f21860 100644 --- a/tests/integration/snapshots/tests__integration__cfc__ir__conditional_return_evaluating_true_negated.snap +++ b/tests/integration/snapshots/tests__integration__cfc__ir__conditional_return_evaluating_true_negated.snap @@ -1,7 +1,6 @@ --- source: tests/integration/cfc.rs expression: output_file_content_without_headers -snapshot_kind: text --- %conditional_return = type { i32 } @@ -35,6 +34,8 @@ declare void @llvm.memcpy.p0i8.p0i8.i64(i8* noalias nocapture writeonly, i8* noa define void @conditional_return(%conditional_return* %0) { entry: + %this = alloca %conditional_return*, align 8 + store %conditional_return* %0, %conditional_return** %this, align 8 %val = getelementptr inbounds %conditional_return, %conditional_return* %0, i32 0, i32 0 %load_val = load i32, i32* %val, align 4 %tmpVar = icmp eq i32 %load_val, 5 From ef593a1c6a9ec4574df052fde33e467c9e55e576 Mon Sep 17 00:00:00 2001 From: abroooo <61380142+abroooo@users.noreply.github.com> Date: Thu, 8 May 2025 12:54:22 +0000 Subject: [PATCH 39/48] fix snapshots --- .../tests/parse_errors/parse_error_containers_tests.rs | 2 +- src/validation/statement.rs | 2 +- src/validation/tests/this_keyword_validation_tests.rs | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/parser/tests/parse_errors/parse_error_containers_tests.rs b/src/parser/tests/parse_errors/parse_error_containers_tests.rs index 1516feb07d..2ddce97777 100644 --- a/src/parser/tests/parse_errors/parse_error_containers_tests.rs +++ b/src/parser/tests/parse_errors/parse_error_containers_tests.rs @@ -346,7 +346,7 @@ fn this_is_a_reserved_keyword() { 3 │ PROGRAM this │ ^^^^ Case condition used outside of case statement! Did you mean to use ';'? - error[E120]: Invalid use of `THIS`. Usage is only allowed within POU of type `FUNCTION_BLOCK` + note[E121]: Invalid use of `THIS`. Usage is only allowed within POU of type `FUNCTION_BLOCK` ┌─ :3:13 │ 3 │ PROGRAM this diff --git a/src/validation/statement.rs b/src/validation/statement.rs index 533efa93f6..502b03cbfe 100644 --- a/src/validation/statement.rs +++ b/src/validation/statement.rs @@ -169,7 +169,7 @@ pub fn visit_statement( Diagnostic::new( "Invalid use of `THIS`. Usage is only allowed within POU of type `FUNCTION_BLOCK`", ) - .with_error_code("E120") + .with_error_code("E121") .with_location(statement), ); } diff --git a/src/validation/tests/this_keyword_validation_tests.rs b/src/validation/tests/this_keyword_validation_tests.rs index 9549cb2be2..98c0abf0c3 100644 --- a/src/validation/tests/this_keyword_validation_tests.rs +++ b/src/validation/tests/this_keyword_validation_tests.rs @@ -403,25 +403,25 @@ fn this_in_unsupported_pous_is_not_allowed() { "#, ); assert_snapshot!(diagnostics, @r" - error[E120]: Invalid use of `THIS`. Usage is only allowed within POU of type `FUNCTION_BLOCK` + note[E121]: Invalid use of `THIS`. Usage is only allowed within POU of type `FUNCTION_BLOCK` ┌─ :8:24 │ 8 │ bar := THIS^.x; // this in method in program is not allowed │ ^^^^ Invalid use of `THIS`. Usage is only allowed within POU of type `FUNCTION_BLOCK` - error[E120]: Invalid use of `THIS`. Usage is only allowed within POU of type `FUNCTION_BLOCK` + note[E121]: Invalid use of `THIS`. Usage is only allowed within POU of type `FUNCTION_BLOCK` ┌─ :10:13 │ 10 │ THIS^; │ ^^^^ Invalid use of `THIS`. Usage is only allowed within POU of type `FUNCTION_BLOCK` - error[E120]: Invalid use of `THIS`. Usage is only allowed within POU of type `FUNCTION_BLOCK` + note[E121]: Invalid use of `THIS`. Usage is only allowed within POU of type `FUNCTION_BLOCK` ┌─ :14:13 │ 14 │ THIS^; │ ^^^^ Invalid use of `THIS`. Usage is only allowed within POU of type `FUNCTION_BLOCK` - error[E120]: Invalid use of `THIS`. Usage is only allowed within POU of type `FUNCTION_BLOCK` + note[E121]: Invalid use of `THIS`. Usage is only allowed within POU of type `FUNCTION_BLOCK` ┌─ :18:13 │ 18 │ THIS^; From 8d8dcd226fbf320d6840df15e8eadf872a1a99b6 Mon Sep 17 00:00:00 2001 From: abroooo <61380142+abroooo@users.noreply.github.com> Date: Thu, 8 May 2025 15:03:54 +0200 Subject: [PATCH 40/48] Update src/validation/tests/this_keyword_validation_tests.rs Co-authored-by: Volkan --- src/validation/tests/this_keyword_validation_tests.rs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/validation/tests/this_keyword_validation_tests.rs b/src/validation/tests/this_keyword_validation_tests.rs index 98c0abf0c3..a5db98246f 100644 --- a/src/validation/tests/this_keyword_validation_tests.rs +++ b/src/validation/tests/this_keyword_validation_tests.rs @@ -458,11 +458,3 @@ fn this_calling_functionblock_body_from_method_is_ok() { assert!(diagnostics.is_empty()); } -#[test] -fn dummy() { - let diagnostics = parse_and_validate_buffered( - r#" - "#, - ); - assert_snapshot!(diagnostics, @r#""#); -} From fcaab225882b278509ac18db66f423f61589d024 Mon Sep 17 00:00:00 2001 From: abroooo <61380142+abroooo@users.noreply.github.com> Date: Thu, 8 May 2025 15:29:36 +0200 Subject: [PATCH 41/48] Update compiler/plc_diagnostics/src/diagnostics/diagnostics_registry.rs Co-authored-by: Volkan --- .../plc_diagnostics/src/diagnostics/diagnostics_registry.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/plc_diagnostics/src/diagnostics/diagnostics_registry.rs b/compiler/plc_diagnostics/src/diagnostics/diagnostics_registry.rs index 521a139afb..f7e73e38ec 100644 --- a/compiler/plc_diagnostics/src/diagnostics/diagnostics_registry.rs +++ b/compiler/plc_diagnostics/src/diagnostics/diagnostics_registry.rs @@ -221,7 +221,7 @@ lazy_static! { E117, Error, include_str!("./error_codes/E117.md"), // Property with invalid number of GET and/or SET blocks E118, Info, include_str!("./error_codes/E118.md"), // Follow-up error to 112 E119, Error, include_str!("./error_codes/E119.md"), // Invalid use of `SUPER` keyword - E120, Error, include_str!("./error_codes/E120.md"), // Invalid use of `THIS` keyword + E120, Error, include_str!("./error_codes/E120.md"), // Invalid use of `THIS` keyword E121, Info, include_str!("./error_codes/E121.md"), // `THIS` is read-only ); } From e034bb59bc89ce31ce8172117be5e670c0079984 Mon Sep 17 00:00:00 2001 From: abroooo <61380142+abroooo@users.noreply.github.com> Date: Thu, 8 May 2025 15:30:32 +0200 Subject: [PATCH 42/48] Update compiler/plc_ast/src/mut_visitor.rs Co-authored-by: Volkan --- compiler/plc_ast/src/mut_visitor.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/plc_ast/src/mut_visitor.rs b/compiler/plc_ast/src/mut_visitor.rs index 49a9a30cc1..d2b571a19a 100644 --- a/compiler/plc_ast/src/mut_visitor.rs +++ b/compiler/plc_ast/src/mut_visitor.rs @@ -409,7 +409,7 @@ impl WalkerMut for AstNode { AstStatement::LabelStatement(_) => visitor.visit_label_statement(self), AstStatement::AllocationStatement(_) => visitor.visit_allocation(self), AstStatement::Super(_) => visitor.visit_super(self), - AstStatement::This => {} + AstStatement::This => visitor.visit_this(self), } } } From 5fb7eb9c4b24cec79f06136d7f879726c537393d Mon Sep 17 00:00:00 2001 From: abroooo <61380142+abroooo@users.noreply.github.com> Date: Thu, 8 May 2025 16:39:36 +0200 Subject: [PATCH 43/48] implement feedback --- .../generators/expression_generator.rs | 2 +- src/codegen/tests/oop_tests.rs | 2 +- src/parser/tests/expressions_parser_tests.rs | 3 --- .../parse_error_containers_tests.rs | 4 +-- src/validation/statement.rs | 17 +++++++------ .../tests/this_keyword_validation_tests.rs | 25 +++++++------------ 6 files changed, 22 insertions(+), 31 deletions(-) diff --git a/src/codegen/generators/expression_generator.rs b/src/codegen/generators/expression_generator.rs index c7fabd907f..51346b7cc0 100644 --- a/src/codegen/generators/expression_generator.rs +++ b/src/codegen/generators/expression_generator.rs @@ -218,7 +218,7 @@ impl<'ink, 'b> ExpressionCodeGenerator<'ink, 'b> { // generate the expression match expression.get_stmt() { AstStatement::This => { - // TODO: #THIS how to test the errors here? + // TODO: #THIS can we expect to have context here? self.function_context.ok_or_else(|| { Diagnostic::codegen_error("Cannot use 'this' without context", expression) })?; diff --git a/src/codegen/tests/oop_tests.rs b/src/codegen/tests/oop_tests.rs index 23607e2858..4750f37fd0 100644 --- a/src/codegen/tests/oop_tests.rs +++ b/src/codegen/tests/oop_tests.rs @@ -1390,7 +1390,7 @@ fn this_in_variable_initialization() { } #[test] -#[ignore = "not working"] +#[ignore = "actions are not supported for now"] fn this_in_action_in_functionblock() { let code = codegen( r#" diff --git a/src/parser/tests/expressions_parser_tests.rs b/src/parser/tests/expressions_parser_tests.rs index b6fd377756..6db44ced77 100644 --- a/src/parser/tests/expressions_parser_tests.rs +++ b/src/parser/tests/expressions_parser_tests.rs @@ -1888,7 +1888,6 @@ fn super_keyword_can_be_parsed_in_expressions() { #[test] fn this_keyword_can_be_parsed_in_expressions() { - // TODO: `this()` is not valid let src = " FUNCTION_BLOCK fb this.x; @@ -2009,7 +2008,6 @@ fn this_keyword_can_be_parsed_in_expressions() { #[test] fn this_keyword_can_be_mixed_with_super() { - // TODO: `this()` is not valid let src = " FUNCTION_BLOCK fb this^.super^.foo(this^.x + this^.y); @@ -2084,7 +2082,6 @@ fn this_keyword_can_be_mixed_with_super() { #[test] fn this_keyword_can_be_parsed_in_method() { - // TODO: `this()` is not valid let src = " FUNCTION_BLOCK fb METHOD doSomething : INT diff --git a/src/parser/tests/parse_errors/parse_error_containers_tests.rs b/src/parser/tests/parse_errors/parse_error_containers_tests.rs index 2ddce97777..2ff0ca0a1f 100644 --- a/src/parser/tests/parse_errors/parse_error_containers_tests.rs +++ b/src/parser/tests/parse_errors/parse_error_containers_tests.rs @@ -346,10 +346,10 @@ fn this_is_a_reserved_keyword() { 3 │ PROGRAM this │ ^^^^ Case condition used outside of case statement! Did you mean to use ';'? - note[E121]: Invalid use of `THIS`. Usage is only allowed within POU of type `FUNCTION_BLOCK` + error[E120]: Invalid use of `THIS`. Usage is only allowed within `FUNCTION_BLOCK` and its `METHOD`s ┌─ :3:13 │ 3 │ PROGRAM this - │ ^^^^ Invalid use of `THIS`. Usage is only allowed within POU of type `FUNCTION_BLOCK` + │ ^^^^ Invalid use of `THIS`. Usage is only allowed within `FUNCTION_BLOCK` and its `METHOD`s "); } diff --git a/src/validation/statement.rs b/src/validation/statement.rs index 502b03cbfe..5545503795 100644 --- a/src/validation/statement.rs +++ b/src/validation/statement.rs @@ -156,20 +156,18 @@ pub fn visit_statement( context .index .find_pou(it) - .and_then(|it| { - if it.is_function_block() { - Some(it) - } else { - context.index.find_pou(it.get_parent_pou_name().unwrap_or_default()) - } + .and_then(|it| match it { + PouIndexEntry::FunctionBlock { .. } => Some(it), + PouIndexEntry::Method { parent_name, .. } => context.index.find_pou(parent_name), + _ => None, }) .is_some_and(|it| it.is_function_block()) }) { validator.push_diagnostic( Diagnostic::new( - "Invalid use of `THIS`. Usage is only allowed within POU of type `FUNCTION_BLOCK`", + "Invalid use of `THIS`. Usage is only allowed within `FUNCTION_BLOCK` and its `METHOD`s", ) - .with_error_code("E121") + .with_error_code("E120") .with_location(statement), ); } @@ -1081,6 +1079,9 @@ fn validate_assignment( if !left.can_be_assigned_to() { let expression = validator.context.slice(&left.get_location()); validator.push_diagnostic( + // TODO: would be nice to have a more specific error message. For instance `THIS` + // might not assignable because its use is only allowed in FBs and their methods. + // Same goes for `SUPER`. Diagnostic::new(format!("Expression {expression} is not assignable.")) .with_error_code("E050") .with_location(left), diff --git a/src/validation/tests/this_keyword_validation_tests.rs b/src/validation/tests/this_keyword_validation_tests.rs index a5db98246f..172cc16e47 100644 --- a/src/validation/tests/this_keyword_validation_tests.rs +++ b/src/validation/tests/this_keyword_validation_tests.rs @@ -371,13 +371,6 @@ fn this_in_variable_initialization_is_ok() { assert!(diagnostics.is_empty()); } -// TODO: test with incompatible types (refToSelf gets assigned something of different type) -// TODO: global namespaces operator tests -// TODO: .this^ tests -// TODO: codegen tests -// TODO: lit tests -// TODO: resolver tests (parenthesized expressions, nested binary expressions ...) -// TODO: this in variable initializers #[test] fn this_in_unsupported_pous_is_not_allowed() { let diagnostics = parse_and_validate_buffered( @@ -403,33 +396,34 @@ fn this_in_unsupported_pous_is_not_allowed() { "#, ); assert_snapshot!(diagnostics, @r" - note[E121]: Invalid use of `THIS`. Usage is only allowed within POU of type `FUNCTION_BLOCK` + error[E120]: Invalid use of `THIS`. Usage is only allowed within `FUNCTION_BLOCK` and its `METHOD`s ┌─ :8:24 │ 8 │ bar := THIS^.x; // this in method in program is not allowed - │ ^^^^ Invalid use of `THIS`. Usage is only allowed within POU of type `FUNCTION_BLOCK` + │ ^^^^ Invalid use of `THIS`. Usage is only allowed within `FUNCTION_BLOCK` and its `METHOD`s - note[E121]: Invalid use of `THIS`. Usage is only allowed within POU of type `FUNCTION_BLOCK` + error[E120]: Invalid use of `THIS`. Usage is only allowed within `FUNCTION_BLOCK` and its `METHOD`s ┌─ :10:13 │ 10 │ THIS^; - │ ^^^^ Invalid use of `THIS`. Usage is only allowed within POU of type `FUNCTION_BLOCK` + │ ^^^^ Invalid use of `THIS`. Usage is only allowed within `FUNCTION_BLOCK` and its `METHOD`s - note[E121]: Invalid use of `THIS`. Usage is only allowed within POU of type `FUNCTION_BLOCK` + error[E120]: Invalid use of `THIS`. Usage is only allowed within `FUNCTION_BLOCK` and its `METHOD`s ┌─ :14:13 │ 14 │ THIS^; - │ ^^^^ Invalid use of `THIS`. Usage is only allowed within POU of type `FUNCTION_BLOCK` + │ ^^^^ Invalid use of `THIS`. Usage is only allowed within `FUNCTION_BLOCK` and its `METHOD`s - note[E121]: Invalid use of `THIS`. Usage is only allowed within POU of type `FUNCTION_BLOCK` + error[E120]: Invalid use of `THIS`. Usage is only allowed within `FUNCTION_BLOCK` and its `METHOD`s ┌─ :18:13 │ 18 │ THIS^; - │ ^^^^ Invalid use of `THIS`. Usage is only allowed within POU of type `FUNCTION_BLOCK` + │ ^^^^ Invalid use of `THIS`. Usage is only allowed within `FUNCTION_BLOCK` and its `METHOD`s "); } #[test] +#[ignore = "actions are not supported for now"] fn this_in_action_in_functionblock_is_ok() { let diagnostics = parse_and_validate_buffered( r#" @@ -457,4 +451,3 @@ fn this_calling_functionblock_body_from_method_is_ok() { ); assert!(diagnostics.is_empty()); } - From 64f23fa90d148c406fddfc3d1b447085774bf711 Mon Sep 17 00:00:00 2001 From: abroooo <61380142+abroooo@users.noreply.github.com> Date: Thu, 8 May 2025 16:40:48 +0200 Subject: [PATCH 44/48] implement feedback 2 --- .../src/diagnostics/diagnostics_registry.rs | 1 - .../src/diagnostics/error_codes/E121.md | 15 - .../src/.inheritance.rs.pending-snap | 273 ------------------ 3 files changed, 289 deletions(-) delete mode 100644 compiler/plc_diagnostics/src/diagnostics/error_codes/E121.md delete mode 100644 compiler/plc_lowering/src/.inheritance.rs.pending-snap diff --git a/compiler/plc_diagnostics/src/diagnostics/diagnostics_registry.rs b/compiler/plc_diagnostics/src/diagnostics/diagnostics_registry.rs index f7e73e38ec..e923cb3375 100644 --- a/compiler/plc_diagnostics/src/diagnostics/diagnostics_registry.rs +++ b/compiler/plc_diagnostics/src/diagnostics/diagnostics_registry.rs @@ -222,7 +222,6 @@ lazy_static! { E118, Info, include_str!("./error_codes/E118.md"), // Follow-up error to 112 E119, Error, include_str!("./error_codes/E119.md"), // Invalid use of `SUPER` keyword E120, Error, include_str!("./error_codes/E120.md"), // Invalid use of `THIS` keyword - E121, Info, include_str!("./error_codes/E121.md"), // `THIS` is read-only ); } diff --git a/compiler/plc_diagnostics/src/diagnostics/error_codes/E121.md b/compiler/plc_diagnostics/src/diagnostics/error_codes/E121.md deleted file mode 100644 index cfffa3cd82..0000000000 --- a/compiler/plc_diagnostics/src/diagnostics/error_codes/E121.md +++ /dev/null @@ -1,15 +0,0 @@ -# `THIS` is read-only - -`THIS` is a keyword in IEC 61131-3 that refers to the instance of a `FUNCTION_BLOCK`. It is a pointer to the instance itself which doesn't change. Therefore `THIS` cannot be assigned to. -Errouneus code example: - -```iec61131 - FUNCTION_BLOCK foo - END_FUNCTION_BLOCK - FUNCTION_BLOCK bar - VAR - fb : foo; - END_VAR - this := REF(foo); // not allowed - END_FUNCTION_BLOCK -``` diff --git a/compiler/plc_lowering/src/.inheritance.rs.pending-snap b/compiler/plc_lowering/src/.inheritance.rs.pending-snap deleted file mode 100644 index 44efd684c8..0000000000 --- a/compiler/plc_lowering/src/.inheritance.rs.pending-snap +++ /dev/null @@ -1,273 +0,0 @@ -{"run_id":"1743593170-383049833","line":4410,"new":{"module_name":"plc_lowering__inheritance__super_tests","snapshot_name":"valid_super_deref_in_paren_expression_edge_case","metadata":{"source":"compiler/plc_lowering/src/inheritance.rs","assertion_line":4410,"expression":"statements"},"snapshot":"[\n Assignment {\n left: ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"z\",\n },\n ),\n base: None,\n },\n right: BinaryExpression {\n operator: Plus,\n left: ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"x\",\n },\n ),\n base: Some(\n ReferenceExpr {\n kind: Deref,\n base: Some(\n ParenExpression {\n expression: CallStatement {\n operator: ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"REF\",\n },\n ),\n base: None,\n },\n parameters: Some(\n ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"__parent\",\n },\n ),\n base: None,\n },\n ),\n },\n },\n ),\n },\n ),\n },\n right: ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"y\",\n },\n ),\n base: Some(\n ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"__parent\",\n },\n ),\n base: None,\n },\n ),\n },\n },\n },\n Assignment {\n left: ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"local\",\n },\n ),\n base: None,\n },\n right: ReferenceExpr {\n kind: Deref,\n base: Some(\n ParenExpression {\n expression: CallStatement {\n operator: ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"REF\",\n },\n ),\n base: None,\n },\n parameters: Some(\n ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"__parent\",\n },\n ),\n base: None,\n },\n ),\n },\n },\n ),\n },\n },\n]"},"old":{"module_name":"plc_lowering__inheritance__super_tests","metadata":{},"snapshot":"[\n Assignment {\n left: ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"z\",\n },\n ),\n base: None,\n },\n right: BinaryExpression {\n operator: Plus,\n left: ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"x\",\n },\n ),\n base: Some(\n ReferenceExpr {\n kind: Deref,\n base: Some(\n ParenExpression {\n expression: CallStatement {\n operator: ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"REF\",\n },\n ),\n base: None,\n },\n parameters: Some(\n ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"__parent\",\n },\n ),\n base: None,\n },\n ),\n },\n },\n ),\n },\n ),\n },\n right: ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"y\",\n },\n ),\n base: Some(\n ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"__parent\",\n },\n ),\n base: None,\n },\n ),\n },\n },\n },\n]"}} -{"run_id":"1743664173-131625004","line":2523,"new":null,"old":null} -{"run_id":"1743664173-131625004","line":4504,"new":null,"old":null} -{"run_id":"1743664173-131625004","line":4089,"new":null,"old":null} -{"run_id":"1743664173-131625004","line":3827,"new":null,"old":null} -{"run_id":"1743664173-131625004","line":3109,"new":null,"old":null} -{"run_id":"1743664173-131625004","line":4032,"new":null,"old":null} -{"run_id":"1743664173-131625004","line":2902,"new":null,"old":null} -{"run_id":"1743664173-131625004","line":2606,"new":null,"old":null} -{"run_id":"1743664173-131625004","line":3732,"new":null,"old":null} -{"run_id":"1743664173-131625004","line":4138,"new":null,"old":null} -{"run_id":"1743664173-131625004","line":4300,"new":null,"old":null} -{"run_id":"1743664173-131625004","line":2539,"new":null,"old":null} -{"run_id":"1743664173-131625004","line":2973,"new":null,"old":null} -{"run_id":"1743664173-131625004","line":2553,"new":null,"old":null} -{"run_id":"1743664173-131625004","line":2567,"new":null,"old":null} -{"run_id":"1743664173-131625004","line":3207,"new":null,"old":null} -{"run_id":"1743664173-131625004","line":3369,"new":null,"old":null} -{"run_id":"1743664173-131625004","line":2652,"new":null,"old":null} -{"run_id":"1743664173-131625004","line":2711,"new":null,"old":null} -{"run_id":"1743664173-131625004","line":492,"new":null,"old":null} -{"run_id":"1743664173-131625004","line":2854,"new":null,"old":null} -{"run_id":"1743664173-131625004","line":3061,"new":null,"old":null} -{"run_id":"1743664173-131625004","line":4237,"new":null,"old":null} -{"run_id":"1743664173-131625004","line":2385,"new":null,"old":null} -{"run_id":"1743664173-131625004","line":3925,"new":null,"old":null} -{"run_id":"1743664173-131625004","line":2790,"new":null,"old":null} -{"run_id":"1743664173-131625004","line":4188,"new":null,"old":null} -{"run_id":"1743664173-131625004","line":2311,"new":null,"old":null} -{"run_id":"1743664173-131625004","line":2223,"new":null,"old":null} -{"run_id":"1743664173-131625004","line":3557,"new":null,"old":null} -{"run_id":"1743664173-131625004","line":3661,"new":null,"old":null} -{"run_id":"1743664173-131625004","line":2008,"new":null,"old":null} -{"run_id":"1743664173-131625004","line":1461,"new":null,"old":null} -{"run_id":"1743664173-131625004","line":772,"new":null,"old":null} -{"run_id":"1743664173-131625004","line":1788,"new":null,"old":null} -{"run_id":"1743664173-131625004","line":541,"new":null,"old":null} -{"run_id":"1743664173-131625004","line":665,"new":null,"old":null} -{"run_id":"1743664173-131625004","line":899,"new":null,"old":null} -{"run_id":"1743664173-131625004","line":1320,"new":null,"old":null} -{"run_id":"1743664173-131625004","line":4410,"new":{"module_name":"plc_lowering__inheritance__super_tests","snapshot_name":"valid_super_deref_in_paren_expression_edge_case","metadata":{"source":"compiler/plc_lowering/src/inheritance.rs","assertion_line":4410,"expression":"statements"},"snapshot":"[\n Assignment {\n left: ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"z\",\n },\n ),\n base: None,\n },\n right: BinaryExpression {\n operator: Plus,\n left: ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"x\",\n },\n ),\n base: Some(\n ReferenceExpr {\n kind: Deref,\n base: Some(\n ParenExpression {\n expression: CallStatement {\n operator: ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"REF\",\n },\n ),\n base: None,\n },\n parameters: Some(\n ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"__parent\",\n },\n ),\n base: None,\n },\n ),\n },\n },\n ),\n },\n ),\n },\n right: ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"y\",\n },\n ),\n base: Some(\n ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"__parent\",\n },\n ),\n base: None,\n },\n ),\n },\n },\n },\n Assignment {\n left: ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"local\",\n },\n ),\n base: None,\n },\n right: ReferenceExpr {\n kind: Deref,\n base: Some(\n ParenExpression {\n expression: CallStatement {\n operator: ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"REF\",\n },\n ),\n base: None,\n },\n parameters: Some(\n ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"__parent\",\n },\n ),\n base: None,\n },\n ),\n },\n },\n ),\n },\n },\n]"},"old":{"module_name":"plc_lowering__inheritance__super_tests","metadata":{},"snapshot":"[\n Assignment {\n left: ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"z\",\n },\n ),\n base: None,\n },\n right: BinaryExpression {\n operator: Plus,\n left: ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"x\",\n },\n ),\n base: Some(\n ReferenceExpr {\n kind: Deref,\n base: Some(\n ParenExpression {\n expression: CallStatement {\n operator: ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"REF\",\n },\n ),\n base: None,\n },\n parameters: Some(\n ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"__parent\",\n },\n ),\n base: None,\n },\n ),\n },\n },\n ),\n },\n ),\n },\n right: ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"y\",\n },\n ),\n base: Some(\n ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"__parent\",\n },\n ),\n base: None,\n },\n ),\n },\n },\n },\n]"}} -{"run_id":"1743675955-17891460","line":2606,"new":null,"old":null} -{"run_id":"1743675955-17891460","line":2523,"new":null,"old":null} -{"run_id":"1743675955-17891460","line":4089,"new":null,"old":null} -{"run_id":"1743675955-17891460","line":4138,"new":null,"old":null} -{"run_id":"1743675955-17891460","line":2652,"new":null,"old":null} -{"run_id":"1743675955-17891460","line":2711,"new":null,"old":null} -{"run_id":"1743675955-17891460","line":4504,"new":null,"old":null} -{"run_id":"1743675955-17891460","line":2539,"new":null,"old":null} -{"run_id":"1743675955-17891460","line":4032,"new":null,"old":null} -{"run_id":"1743675955-17891460","line":2553,"new":null,"old":null} -{"run_id":"1743675955-17891460","line":2973,"new":null,"old":null} -{"run_id":"1743675955-17891460","line":2567,"new":null,"old":null} -{"run_id":"1743675955-17891460","line":2902,"new":null,"old":null} -{"run_id":"1743675955-17891460","line":4300,"new":null,"old":null} -{"run_id":"1743675955-17891460","line":3732,"new":null,"old":null} -{"run_id":"1743675955-17891460","line":3207,"new":null,"old":null} -{"run_id":"1743675955-17891460","line":3109,"new":null,"old":null} -{"run_id":"1743675955-17891460","line":3369,"new":null,"old":null} -{"run_id":"1743675955-17891460","line":3827,"new":null,"old":null} -{"run_id":"1743675955-17891460","line":3061,"new":null,"old":null} -{"run_id":"1743675955-17891460","line":2790,"new":null,"old":null} -{"run_id":"1743675955-17891460","line":3557,"new":null,"old":null} -{"run_id":"1743675955-17891460","line":4188,"new":null,"old":null} -{"run_id":"1743675955-17891460","line":3925,"new":null,"old":null} -{"run_id":"1743675955-17891460","line":2008,"new":null,"old":null} -{"run_id":"1743675955-17891460","line":492,"new":null,"old":null} -{"run_id":"1743675955-17891460","line":3661,"new":null,"old":null} -{"run_id":"1743675955-17891460","line":2223,"new":null,"old":null} -{"run_id":"1743675955-17891460","line":2385,"new":null,"old":null} -{"run_id":"1743675955-17891460","line":2311,"new":null,"old":null} -{"run_id":"1743675955-17891460","line":2854,"new":null,"old":null} -{"run_id":"1743675955-17891460","line":4237,"new":null,"old":null} -{"run_id":"1743675955-17891460","line":899,"new":null,"old":null} -{"run_id":"1743675955-17891460","line":1461,"new":null,"old":null} -{"run_id":"1743675955-17891460","line":1320,"new":null,"old":null} -{"run_id":"1743675955-17891460","line":665,"new":null,"old":null} -{"run_id":"1743675955-17891460","line":1788,"new":null,"old":null} -{"run_id":"1743675955-17891460","line":541,"new":null,"old":null} -{"run_id":"1743675955-17891460","line":772,"new":null,"old":null} -{"run_id":"1743675955-17891460","line":4410,"new":{"module_name":"plc_lowering__inheritance__super_tests","snapshot_name":"valid_super_deref_in_paren_expression_edge_case","metadata":{"source":"compiler/plc_lowering/src/inheritance.rs","assertion_line":4410,"expression":"statements"},"snapshot":"[\n Assignment {\n left: ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"z\",\n },\n ),\n base: None,\n },\n right: BinaryExpression {\n operator: Plus,\n left: ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"x\",\n },\n ),\n base: Some(\n ReferenceExpr {\n kind: Deref,\n base: Some(\n ParenExpression {\n expression: CallStatement {\n operator: ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"REF\",\n },\n ),\n base: None,\n },\n parameters: Some(\n ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"__parent\",\n },\n ),\n base: None,\n },\n ),\n },\n },\n ),\n },\n ),\n },\n right: ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"y\",\n },\n ),\n base: Some(\n ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"__parent\",\n },\n ),\n base: None,\n },\n ),\n },\n },\n },\n Assignment {\n left: ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"local\",\n },\n ),\n base: None,\n },\n right: ReferenceExpr {\n kind: Deref,\n base: Some(\n ParenExpression {\n expression: CallStatement {\n operator: ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"REF\",\n },\n ),\n base: None,\n },\n parameters: Some(\n ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"__parent\",\n },\n ),\n base: None,\n },\n ),\n },\n },\n ),\n },\n },\n]"},"old":{"module_name":"plc_lowering__inheritance__super_tests","metadata":{},"snapshot":"[\n Assignment {\n left: ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"z\",\n },\n ),\n base: None,\n },\n right: BinaryExpression {\n operator: Plus,\n left: ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"x\",\n },\n ),\n base: Some(\n ReferenceExpr {\n kind: Deref,\n base: Some(\n ParenExpression {\n expression: CallStatement {\n operator: ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"REF\",\n },\n ),\n base: None,\n },\n parameters: Some(\n ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"__parent\",\n },\n ),\n base: None,\n },\n ),\n },\n },\n ),\n },\n ),\n },\n right: ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"y\",\n },\n ),\n base: Some(\n ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"__parent\",\n },\n ),\n base: None,\n },\n ),\n },\n },\n },\n]"}} -{"run_id":"1743676161-849633236","line":2606,"new":null,"old":null} -{"run_id":"1743676161-849633236","line":4138,"new":null,"old":null} -{"run_id":"1743676161-849633236","line":4032,"new":null,"old":null} -{"run_id":"1743676161-849633236","line":2523,"new":null,"old":null} -{"run_id":"1743676161-849633236","line":2973,"new":null,"old":null} -{"run_id":"1743676161-849633236","line":4504,"new":null,"old":null} -{"run_id":"1743676161-849633236","line":2711,"new":null,"old":null} -{"run_id":"1743676161-849633236","line":3827,"new":null,"old":null} -{"run_id":"1743676161-849633236","line":3732,"new":null,"old":null} -{"run_id":"1743676161-849633236","line":2539,"new":null,"old":null} -{"run_id":"1743676161-849633236","line":2902,"new":null,"old":null} -{"run_id":"1743676161-849633236","line":3207,"new":null,"old":null} -{"run_id":"1743676161-849633236","line":2652,"new":null,"old":null} -{"run_id":"1743676161-849633236","line":4300,"new":null,"old":null} -{"run_id":"1743676161-849633236","line":2553,"new":null,"old":null} -{"run_id":"1743676161-849633236","line":4089,"new":null,"old":null} -{"run_id":"1743676161-849633236","line":2567,"new":null,"old":null} -{"run_id":"1743676161-849633236","line":3109,"new":null,"old":null} -{"run_id":"1743676161-849633236","line":3369,"new":null,"old":null} -{"run_id":"1743676161-849633236","line":4188,"new":null,"old":null} -{"run_id":"1743676161-849633236","line":2385,"new":null,"old":null} -{"run_id":"1743676161-849633236","line":3661,"new":null,"old":null} -{"run_id":"1743676161-849633236","line":3061,"new":null,"old":null} -{"run_id":"1743676161-849633236","line":2854,"new":null,"old":null} -{"run_id":"1743676161-849633236","line":2311,"new":null,"old":null} -{"run_id":"1743676161-849633236","line":2790,"new":null,"old":null} -{"run_id":"1743676161-849633236","line":1461,"new":null,"old":null} -{"run_id":"1743676161-849633236","line":3557,"new":null,"old":null} -{"run_id":"1743676161-849633236","line":4237,"new":null,"old":null} -{"run_id":"1743676161-849633236","line":492,"new":null,"old":null} -{"run_id":"1743676161-849633236","line":3925,"new":null,"old":null} -{"run_id":"1743676161-849633236","line":2008,"new":null,"old":null} -{"run_id":"1743676161-849633236","line":2223,"new":null,"old":null} -{"run_id":"1743676161-849633236","line":899,"new":null,"old":null} -{"run_id":"1743676161-849633236","line":665,"new":null,"old":null} -{"run_id":"1743676161-849633236","line":1320,"new":null,"old":null} -{"run_id":"1743676161-849633236","line":541,"new":null,"old":null} -{"run_id":"1743676161-849633236","line":772,"new":null,"old":null} -{"run_id":"1743676161-849633236","line":1788,"new":null,"old":null} -{"run_id":"1743676161-849633236","line":4410,"new":{"module_name":"plc_lowering__inheritance__super_tests","snapshot_name":"valid_super_deref_in_paren_expression_edge_case","metadata":{"source":"compiler/plc_lowering/src/inheritance.rs","assertion_line":4410,"expression":"statements"},"snapshot":"[\n Assignment {\n left: ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"z\",\n },\n ),\n base: None,\n },\n right: BinaryExpression {\n operator: Plus,\n left: ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"x\",\n },\n ),\n base: Some(\n ReferenceExpr {\n kind: Deref,\n base: Some(\n ParenExpression {\n expression: CallStatement {\n operator: ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"REF\",\n },\n ),\n base: None,\n },\n parameters: Some(\n ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"__parent\",\n },\n ),\n base: None,\n },\n ),\n },\n },\n ),\n },\n ),\n },\n right: ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"y\",\n },\n ),\n base: Some(\n ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"__parent\",\n },\n ),\n base: None,\n },\n ),\n },\n },\n },\n Assignment {\n left: ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"local\",\n },\n ),\n base: None,\n },\n right: ReferenceExpr {\n kind: Deref,\n base: Some(\n ParenExpression {\n expression: CallStatement {\n operator: ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"REF\",\n },\n ),\n base: None,\n },\n parameters: Some(\n ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"__parent\",\n },\n ),\n base: None,\n },\n ),\n },\n },\n ),\n },\n },\n]"},"old":{"module_name":"plc_lowering__inheritance__super_tests","metadata":{},"snapshot":"[\n Assignment {\n left: ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"z\",\n },\n ),\n base: None,\n },\n right: BinaryExpression {\n operator: Plus,\n left: ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"x\",\n },\n ),\n base: Some(\n ReferenceExpr {\n kind: Deref,\n base: Some(\n ParenExpression {\n expression: CallStatement {\n operator: ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"REF\",\n },\n ),\n base: None,\n },\n parameters: Some(\n ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"__parent\",\n },\n ),\n base: None,\n },\n ),\n },\n },\n ),\n },\n ),\n },\n right: ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"y\",\n },\n ),\n base: Some(\n ReferenceExpr {\n kind: Member(\n Identifier {\n name: \"__parent\",\n },\n ),\n base: None,\n },\n ),\n },\n },\n },\n]"}} -{"run_id":"1743678517-19413121","line":2393,"new":null,"old":null} -{"run_id":"1743678517-19413121","line":279,"new":null,"old":null} -{"run_id":"1743678517-19413121","line":2310,"new":null,"old":null} -{"run_id":"1743678517-19413121","line":2439,"new":null,"old":null} -{"run_id":"1743678517-19413121","line":2498,"new":null,"old":null} -{"run_id":"1743678517-19413121","line":2326,"new":null,"old":null} -{"run_id":"1743678517-19413121","line":2010,"new":null,"old":null} -{"run_id":"1743678517-19413121","line":2340,"new":null,"old":null} -{"run_id":"1743678517-19413121","line":452,"new":null,"old":null} -{"run_id":"1743678517-19413121","line":2098,"new":null,"old":null} -{"run_id":"1743678517-19413121","line":2354,"new":null,"old":null} -{"run_id":"1743678517-19413121","line":559,"new":null,"old":null} -{"run_id":"1743678517-19413121","line":1107,"new":null,"old":null} -{"run_id":"1743678517-19413121","line":2172,"new":null,"old":null} -{"run_id":"1743678517-19413121","line":328,"new":null,"old":null} -{"run_id":"1743678517-19413121","line":1575,"new":null,"old":null} -{"run_id":"1743678517-19413121","line":1795,"new":null,"old":null} -{"run_id":"1743678517-19413121","line":1248,"new":null,"old":null} -{"run_id":"1743678517-19413121","line":686,"new":null,"old":null} -{"run_id":"1743684516-801695239","line":279,"new":null,"old":null} -{"run_id":"1743684516-801695239","line":2439,"new":null,"old":null} -{"run_id":"1743684516-801695239","line":2393,"new":null,"old":null} -{"run_id":"1743684516-801695239","line":2498,"new":null,"old":null} -{"run_id":"1743684516-801695239","line":2098,"new":null,"old":null} -{"run_id":"1743684516-801695239","line":2010,"new":null,"old":null} -{"run_id":"1743684516-801695239","line":2172,"new":null,"old":null} -{"run_id":"1743684516-801695239","line":2310,"new":null,"old":null} -{"run_id":"1743684516-801695239","line":2326,"new":null,"old":null} -{"run_id":"1743684516-801695239","line":452,"new":null,"old":null} -{"run_id":"1743684516-801695239","line":2340,"new":null,"old":null} -{"run_id":"1743684516-801695239","line":559,"new":null,"old":null} -{"run_id":"1743684516-801695239","line":1795,"new":null,"old":null} -{"run_id":"1743684516-801695239","line":2354,"new":null,"old":null} -{"run_id":"1743684516-801695239","line":328,"new":null,"old":null} -{"run_id":"1743684516-801695239","line":1107,"new":null,"old":null} -{"run_id":"1743684516-801695239","line":1575,"new":null,"old":null} -{"run_id":"1743684516-801695239","line":1248,"new":null,"old":null} -{"run_id":"1743684516-801695239","line":686,"new":null,"old":null} -{"run_id":"1743687248-609667611","line":2393,"new":null,"old":null} -{"run_id":"1743687248-609667611","line":279,"new":null,"old":null} -{"run_id":"1743687248-609667611","line":2439,"new":null,"old":null} -{"run_id":"1743687248-609667611","line":2498,"new":null,"old":null} -{"run_id":"1743687248-609667611","line":2310,"new":null,"old":null} -{"run_id":"1743687248-609667611","line":2010,"new":null,"old":null} -{"run_id":"1743687248-609667611","line":559,"new":null,"old":null} -{"run_id":"1743687248-609667611","line":2172,"new":null,"old":null} -{"run_id":"1743687248-609667611","line":452,"new":null,"old":null} -{"run_id":"1743687248-609667611","line":2098,"new":null,"old":null} -{"run_id":"1743687248-609667611","line":2326,"new":null,"old":null} -{"run_id":"1743687248-609667611","line":2340,"new":null,"old":null} -{"run_id":"1743687248-609667611","line":1575,"new":null,"old":null} -{"run_id":"1743687248-609667611","line":1795,"new":null,"old":null} -{"run_id":"1743687248-609667611","line":2354,"new":null,"old":null} -{"run_id":"1743687248-609667611","line":1107,"new":null,"old":null} -{"run_id":"1743687248-609667611","line":328,"new":null,"old":null} -{"run_id":"1743687248-609667611","line":1248,"new":null,"old":null} -{"run_id":"1743687248-609667611","line":686,"new":null,"old":null} -{"run_id":"1743687443-512040318","line":2393,"new":null,"old":null} -{"run_id":"1743687443-512040318","line":2439,"new":null,"old":null} -{"run_id":"1743687443-512040318","line":279,"new":null,"old":null} -{"run_id":"1743687443-512040318","line":2310,"new":null,"old":null} -{"run_id":"1743687443-512040318","line":2172,"new":null,"old":null} -{"run_id":"1743687443-512040318","line":452,"new":null,"old":null} -{"run_id":"1743687443-512040318","line":2326,"new":null,"old":null} -{"run_id":"1743687443-512040318","line":2098,"new":null,"old":null} -{"run_id":"1743687443-512040318","line":2010,"new":null,"old":null} -{"run_id":"1743687443-512040318","line":2340,"new":null,"old":null} -{"run_id":"1743687443-512040318","line":2498,"new":null,"old":null} -{"run_id":"1743687443-512040318","line":1107,"new":null,"old":null} -{"run_id":"1743687443-512040318","line":559,"new":null,"old":null} -{"run_id":"1743687443-512040318","line":2354,"new":null,"old":null} -{"run_id":"1743687443-512040318","line":1795,"new":null,"old":null} -{"run_id":"1743687443-512040318","line":328,"new":null,"old":null} -{"run_id":"1743687443-512040318","line":1248,"new":null,"old":null} -{"run_id":"1743687443-512040318","line":1575,"new":null,"old":null} -{"run_id":"1743687443-512040318","line":686,"new":null,"old":null} -{"run_id":"1743763095-690977869","line":279,"new":null,"old":null} -{"run_id":"1743763095-690977869","line":2393,"new":null,"old":null} -{"run_id":"1743763095-690977869","line":2439,"new":null,"old":null} -{"run_id":"1743763095-690977869","line":2010,"new":null,"old":null} -{"run_id":"1743763095-690977869","line":2172,"new":null,"old":null} -{"run_id":"1743763095-690977869","line":2498,"new":null,"old":null} -{"run_id":"1743763095-690977869","line":452,"new":null,"old":null} -{"run_id":"1743763095-690977869","line":2310,"new":null,"old":null} -{"run_id":"1743763095-690977869","line":1107,"new":null,"old":null} -{"run_id":"1743763095-690977869","line":2098,"new":null,"old":null} -{"run_id":"1743763095-690977869","line":559,"new":null,"old":null} -{"run_id":"1743763095-690977869","line":686,"new":null,"old":null} -{"run_id":"1743763095-690977869","line":328,"new":null,"old":null} -{"run_id":"1743763095-690977869","line":2326,"new":null,"old":null} -{"run_id":"1743763095-690977869","line":1248,"new":null,"old":null} -{"run_id":"1743763095-690977869","line":2340,"new":null,"old":null} -{"run_id":"1743763095-690977869","line":2354,"new":null,"old":null} -{"run_id":"1743763095-690977869","line":1795,"new":null,"old":null} -{"run_id":"1743763095-690977869","line":1575,"new":null,"old":null} -{"run_id":"1743763184-106471777","line":2439,"new":null,"old":null} -{"run_id":"1743763184-106471777","line":2393,"new":null,"old":null} -{"run_id":"1743763184-106471777","line":2310,"new":null,"old":null} -{"run_id":"1743763184-106471777","line":279,"new":null,"old":null} -{"run_id":"1743763184-106471777","line":2098,"new":null,"old":null} -{"run_id":"1743763184-106471777","line":452,"new":null,"old":null} -{"run_id":"1743763184-106471777","line":2498,"new":null,"old":null} -{"run_id":"1743763184-106471777","line":2326,"new":null,"old":null} -{"run_id":"1743763184-106471777","line":2010,"new":null,"old":null} -{"run_id":"1743763184-106471777","line":2172,"new":null,"old":null} -{"run_id":"1743763184-106471777","line":2340,"new":null,"old":null} -{"run_id":"1743763184-106471777","line":2354,"new":null,"old":null} -{"run_id":"1743763184-106471777","line":559,"new":null,"old":null} -{"run_id":"1743763184-106471777","line":328,"new":null,"old":null} -{"run_id":"1743763184-106471777","line":1107,"new":null,"old":null} -{"run_id":"1743763184-106471777","line":1795,"new":null,"old":null} -{"run_id":"1743763184-106471777","line":1575,"new":null,"old":null} -{"run_id":"1743763184-106471777","line":1248,"new":null,"old":null} -{"run_id":"1743763184-106471777","line":686,"new":null,"old":null} -{"run_id":"1743763305-876434218","line":2393,"new":null,"old":null} -{"run_id":"1743763305-876434218","line":279,"new":null,"old":null} -{"run_id":"1743763305-876434218","line":2439,"new":null,"old":null} -{"run_id":"1743763305-876434218","line":2310,"new":null,"old":null} -{"run_id":"1743763305-876434218","line":2010,"new":null,"old":null} -{"run_id":"1743763305-876434218","line":2326,"new":null,"old":null} -{"run_id":"1743763305-876434218","line":2172,"new":null,"old":null} -{"run_id":"1743763305-876434218","line":2098,"new":null,"old":null} -{"run_id":"1743763305-876434218","line":2498,"new":null,"old":null} -{"run_id":"1743763305-876434218","line":452,"new":null,"old":null} -{"run_id":"1743763305-876434218","line":2340,"new":null,"old":null} -{"run_id":"1743763305-876434218","line":559,"new":null,"old":null} -{"run_id":"1743763305-876434218","line":1107,"new":null,"old":null} -{"run_id":"1743763305-876434218","line":2354,"new":null,"old":null} -{"run_id":"1743763305-876434218","line":328,"new":null,"old":null} -{"run_id":"1743763305-876434218","line":1575,"new":null,"old":null} -{"run_id":"1743763305-876434218","line":1795,"new":null,"old":null} -{"run_id":"1743763305-876434218","line":1248,"new":null,"old":null} -{"run_id":"1743763305-876434218","line":686,"new":null,"old":null} -{"run_id":"1743782405-273296447","line":2393,"new":null,"old":null} -{"run_id":"1743782405-273296447","line":279,"new":null,"old":null} -{"run_id":"1743782405-273296447","line":2310,"new":null,"old":null} -{"run_id":"1743782405-273296447","line":2172,"new":null,"old":null} -{"run_id":"1743782405-273296447","line":2098,"new":null,"old":null} -{"run_id":"1743782405-273296447","line":2326,"new":null,"old":null} -{"run_id":"1743782405-273296447","line":452,"new":null,"old":null} -{"run_id":"1743782405-273296447","line":2498,"new":null,"old":null} -{"run_id":"1743782405-273296447","line":2010,"new":null,"old":null} -{"run_id":"1743782405-273296447","line":2340,"new":null,"old":null} -{"run_id":"1743782405-273296447","line":559,"new":null,"old":null} -{"run_id":"1743782405-273296447","line":2354,"new":null,"old":null} -{"run_id":"1743782405-273296447","line":2439,"new":null,"old":null} -{"run_id":"1743782405-273296447","line":328,"new":null,"old":null} -{"run_id":"1743782405-273296447","line":1107,"new":null,"old":null} -{"run_id":"1743782405-273296447","line":1795,"new":null,"old":null} -{"run_id":"1743782405-273296447","line":1575,"new":null,"old":null} -{"run_id":"1743782405-273296447","line":686,"new":null,"old":null} -{"run_id":"1743782405-273296447","line":1248,"new":null,"old":null} From 1884fd2d9aad18780304247bcd98986543833609 Mon Sep 17 00:00:00 2001 From: abroooo <61380142+abroooo@users.noreply.github.com> Date: Thu, 8 May 2025 15:40:30 +0000 Subject: [PATCH 45/48] support actions --- src/codegen/generators/pou_generator.rs | 11 ++++------- .../tests/initialization_test/complex_initializers.rs | 8 ++++++++ src/codegen/tests/oop_tests.rs | 1 - src/codegen/tests/oop_tests/super_tests.rs | 2 ++ ...s__qualified_action_from_fb_called_in_program.snap | 2 ++ ..._tests__var_in_out_params_can_be_out_of_order.snap | 2 ++ src/index.rs | 4 ++++ src/resolver.rs | 9 ++++----- src/validation/statement.rs | 3 ++- src/validation/tests/this_keyword_validation_tests.rs | 1 - 10 files changed, 28 insertions(+), 15 deletions(-) diff --git a/src/codegen/generators/pou_generator.rs b/src/codegen/generators/pou_generator.rs index 40c3dc1ae9..41c6037ebf 100644 --- a/src/codegen/generators/pou_generator.rs +++ b/src/codegen/generators/pou_generator.rs @@ -700,18 +700,15 @@ impl<'ink, 'cg> PouGenerator<'ink, 'cg> { ); } - if function_context.linking_context.is_method() - && self.index.find_pou(type_name).is_some_and(|it| it.is_function_block()) + if ((function_context.linking_context.is_method() || function_context.linking_context.is_action()) + && self.index.find_pou(type_name).is_some_and(|it| it.is_function_block())) + || function_context.linking_context.get_implementation_type() + == &ImplementationType::FunctionBlock { let alloca = self.llvm.builder.build_alloca(param_pointer.get_type(), "this"); self.llvm.builder.build_store(alloca, param_pointer); index.associate_loaded_local_variable(type_name, "__THIS", alloca)?; } - if function_context.linking_context.get_implementation_type() == &ImplementationType::FunctionBlock { - let alloca = self.llvm.builder.build_alloca(param_pointer.get_type(), "this"); - self.llvm.builder.build_store(alloca, param_pointer); - index.associate_loaded_local_variable(type_name, "__THIS", alloca)?; - } //Generate reference to parameter // cannot use index from members because return and temp variables may not be considered for index in build_struct_gep diff --git a/src/codegen/tests/initialization_test/complex_initializers.rs b/src/codegen/tests/initialization_test/complex_initializers.rs index 0145db3e31..35552963aa 100644 --- a/src/codegen/tests/initialization_test/complex_initializers.rs +++ b/src/codegen/tests/initialization_test/complex_initializers.rs @@ -432,12 +432,16 @@ fn nested_initializer_pous() { define void @bar_print(%bar* %0) { entry: + %this = alloca %bar*, align 8 + store %bar* %0, %bar** %this, align 8 %b = getelementptr inbounds %bar, %bar* %0, i32 0, i32 0 ret void } define void @foo_print(%foo* %0) { entry: + %this = alloca %foo*, align 8 + store %foo* %0, %foo** %this, align 8 %str_ref = getelementptr inbounds %foo, %foo* %0, i32 0, i32 0 %b = getelementptr inbounds %foo, %foo* %0, i32 0, i32 1 ret void @@ -445,6 +449,8 @@ fn nested_initializer_pous() { define void @baz_print(%baz* %0) { entry: + %this = alloca %baz*, align 8 + store %baz* %0, %baz** %this, align 8 %str_ref = getelementptr inbounds %baz, %baz* %0, i32 0, i32 0 ret void } @@ -863,6 +869,8 @@ fn stateful_pous_methods_and_structs_get_init_functions() { define void @foo_act(%foo* %0) { entry: + %this = alloca %foo*, align 8 + store %foo* %0, %foo** %this, align 8 ret void } diff --git a/src/codegen/tests/oop_tests.rs b/src/codegen/tests/oop_tests.rs index 4750f37fd0..9412dcfd2d 100644 --- a/src/codegen/tests/oop_tests.rs +++ b/src/codegen/tests/oop_tests.rs @@ -1390,7 +1390,6 @@ fn this_in_variable_initialization() { } #[test] -#[ignore = "actions are not supported for now"] fn this_in_action_in_functionblock() { let code = codegen( r#" diff --git a/src/codegen/tests/oop_tests/super_tests.rs b/src/codegen/tests/oop_tests/super_tests.rs index 9cd113fcd3..3c595da8eb 100644 --- a/src/codegen/tests/oop_tests/super_tests.rs +++ b/src/codegen/tests/oop_tests/super_tests.rs @@ -2010,6 +2010,8 @@ fn super_in_action_blocks() { define void @child_increase(%child* %0) { entry: + %this = alloca %child*, align 8 + store %child* %0, %child** %this, align 8 %__parent = getelementptr inbounds %child, %child* %0, i32 0, i32 0 %value = getelementptr inbounds %parent, %parent* %__parent, i32 0, i32 0 %value1 = getelementptr inbounds %parent, %parent* %__parent, i32 0, i32 0 diff --git a/src/codegen/tests/snapshots/rusty__codegen__tests__code_gen_tests__qualified_action_from_fb_called_in_program.snap b/src/codegen/tests/snapshots/rusty__codegen__tests__code_gen_tests__qualified_action_from_fb_called_in_program.snap index 0a788bb320..07bd6ee4bb 100644 --- a/src/codegen/tests/snapshots/rusty__codegen__tests__code_gen_tests__qualified_action_from_fb_called_in_program.snap +++ b/src/codegen/tests/snapshots/rusty__codegen__tests__code_gen_tests__qualified_action_from_fb_called_in_program.snap @@ -28,6 +28,8 @@ entry: define void @fb_foo(%fb* %0) { entry: + %this = alloca %fb*, align 8 + store %fb* %0, %fb** %this, align 8 %x = getelementptr inbounds %fb, %fb* %0, i32 0, i32 0 store i32 2, i32* %x, align 4 ret void diff --git a/src/codegen/tests/snapshots/rusty__codegen__tests__parameters_tests__var_in_out_params_can_be_out_of_order.snap b/src/codegen/tests/snapshots/rusty__codegen__tests__parameters_tests__var_in_out_params_can_be_out_of_order.snap index 41ab0d5781..245d61e695 100644 --- a/src/codegen/tests/snapshots/rusty__codegen__tests__parameters_tests__var_in_out_params_can_be_out_of_order.snap +++ b/src/codegen/tests/snapshots/rusty__codegen__tests__parameters_tests__var_in_out_params_can_be_out_of_order.snap @@ -49,6 +49,8 @@ entry: define void @fb_t_foo(%fb_t* %0) { entry: + %this = alloca %fb_t*, align 8 + store %fb_t* %0, %fb_t** %this, align 8 %myVar = getelementptr inbounds %fb_t, %fb_t* %0, i32 0, i32 0 %myInput = getelementptr inbounds %fb_t, %fb_t* %0, i32 0, i32 1 %myInOut = getelementptr inbounds %fb_t, %fb_t* %0, i32 0, i32 2 diff --git a/src/index.rs b/src/index.rs index 0925bd89cb..3548cad022 100644 --- a/src/index.rs +++ b/src/index.rs @@ -468,6 +468,10 @@ impl ImplementationIndexEntry { pub fn is_method(&self) -> bool { matches!(self.get_implementation_type(), ImplementationType::Method) } + + pub fn is_action(&self) -> bool { + matches!(self.get_implementation_type(), ImplementationType::Action) + } } impl From<&PouType> for ImplementationType { diff --git a/src/resolver.rs b/src/resolver.rs index 2f393c6b99..fecf85c084 100644 --- a/src/resolver.rs +++ b/src/resolver.rs @@ -1684,11 +1684,10 @@ impl<'i> TypeAnnotator<'i> { AstStatement::This => { let name = match ctx.pou.and_then(|name| self.index.find_pou(name)) { Some(PouIndexEntry::FunctionBlock { name, .. }) => name, - Some(PouIndexEntry::Method { parent_name: name, .. }) - if self.index.find_pou(name).is_some_and(|it| it.is_function_block()) => - { - name - } + Some( + PouIndexEntry::Method { parent_name: name, .. } + | PouIndexEntry::Action { parent_name: name, .. }, + ) if self.index.find_pou(name).is_some_and(|it| it.is_function_block()) => name, _ => return, }; let ptr_name = format!("{}.__THIS", name); diff --git a/src/validation/statement.rs b/src/validation/statement.rs index 5545503795..dd78ea07fe 100644 --- a/src/validation/statement.rs +++ b/src/validation/statement.rs @@ -158,7 +158,8 @@ pub fn visit_statement( .find_pou(it) .and_then(|it| match it { PouIndexEntry::FunctionBlock { .. } => Some(it), - PouIndexEntry::Method { parent_name, .. } => context.index.find_pou(parent_name), + PouIndexEntry::Method { parent_name, .. } + | PouIndexEntry::Action { parent_name, .. } => context.index.find_pou(parent_name), _ => None, }) .is_some_and(|it| it.is_function_block()) diff --git a/src/validation/tests/this_keyword_validation_tests.rs b/src/validation/tests/this_keyword_validation_tests.rs index 172cc16e47..4366ecb401 100644 --- a/src/validation/tests/this_keyword_validation_tests.rs +++ b/src/validation/tests/this_keyword_validation_tests.rs @@ -423,7 +423,6 @@ fn this_in_unsupported_pous_is_not_allowed() { } #[test] -#[ignore = "actions are not supported for now"] fn this_in_action_in_functionblock_is_ok() { let diagnostics = parse_and_validate_buffered( r#" From c536d67ff0395847418d4cb3b369434a007cb990 Mon Sep 17 00:00:00 2001 From: abroooo <61380142+abroooo@users.noreply.github.com> Date: Thu, 8 May 2025 17:43:11 +0200 Subject: [PATCH 46/48] fix snapshot --- src/codegen/tests/oop_tests.rs | 45 +++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/src/codegen/tests/oop_tests.rs b/src/codegen/tests/oop_tests.rs index 9412dcfd2d..0867a35027 100644 --- a/src/codegen/tests/oop_tests.rs +++ b/src/codegen/tests/oop_tests.rs @@ -1401,7 +1401,50 @@ fn this_in_action_in_functionblock() { END_ACTION "#, ); - insta::assert_snapshot!(code, @r#""#); + insta::assert_snapshot!(code, @r#" + ; ModuleID = '' + source_filename = "" + + %fb = type {} + + @__fb__init = constant %fb zeroinitializer + @llvm.global_ctors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 0, void ()* @__init___Test, i8* null }] + + define void @fb(%fb* %0) { + entry: + %this = alloca %fb*, align 8 + store %fb* %0, %fb** %this, align 8 + ret void + } + + define void @fb_foo(%fb* %0) { + entry: + %this = alloca %fb*, align 8 + store %fb* %0, %fb** %this, align 8 + %deref = load %fb*, %fb** %this, align 8 + call void @fb(%fb* %deref) + ret void + } + + define void @__init_fb(%fb* %0) { + entry: + %self = alloca %fb*, align 8 + store %fb* %0, %fb** %self, align 8 + ret void + } + + define void @__user_init_fb(%fb* %0) { + entry: + %self = alloca %fb*, align 8 + store %fb* %0, %fb** %self, align 8 + ret void + } + + define void @__init___Test() { + entry: + ret void + } + "#); } #[test] From 6ed60b775951d1af65166e017ad42663fa6c26d5 Mon Sep 17 00:00:00 2001 From: abroooo <61380142+abroooo@users.noreply.github.com> Date: Thu, 8 May 2025 22:42:00 +0200 Subject: [PATCH 47/48] improve error doc --- .../src/diagnostics/error_codes/E120.md | 92 +++++++++++++++++-- .../parse_error_containers_tests.rs | 4 +- src/validation/statement.rs | 2 +- .../tests/this_keyword_validation_tests.rs | 16 ++-- 4 files changed, 93 insertions(+), 21 deletions(-) diff --git a/compiler/plc_diagnostics/src/diagnostics/error_codes/E120.md b/compiler/plc_diagnostics/src/diagnostics/error_codes/E120.md index 3f39624146..5d7c3bb0fd 100644 --- a/compiler/plc_diagnostics/src/diagnostics/error_codes/E120.md +++ b/compiler/plc_diagnostics/src/diagnostics/error_codes/E120.md @@ -1,17 +1,89 @@ -# `THIS` keyword is only allowed in `FUNCTION_BLOCK` +# Invalid use of the `THIS` keyword -`THIS` is a keyword in IEC 61131-3 that refers to the instance of a `FUNCTION_BLOCK`. It is used to access the instance variables and methods of the `FUNCTION_BLOCK` from within its own methods or body. -Therefore it cannot be used in `FUNCTION`s or `PROGRAM`s, as these do not have an instance context like `FUNCTION_BLOCK`s do. +The `THIS` keyword provides access to the current instance of a `FUNCTION_BLOCK`. However, there are several rules governing its proper use: -Errouneus code example: +## Common errors -```iec61131 -FUNCTION foo +1. **Using `THIS` outside of a `FUNCTION_BLOCK` context**: + The `THIS` keyword can only be used inside a `FUNCTION_BLOCK` or its `METHOD`s or `ACTION`s. It cannot be used in `FUNCTION`s, `PROGRAM`s, or . + +2. **Not dereferencing `THIS` to access members**: + When accessing members using `THIS`, it must be dereferenced using the `^` operator: `THIS^.member`. + +3. **Member access position**: + `THIS` cannot be accessed as a member of another object. Expressions like `x.THIS^` are invalid. + +4. **Global access position**: + `THIS` cannot be used with the global access operator (`.THIS^.member`). + +5. **Using `THIS` with type cast operators**: + The type cast operator (`#`) cannot be used with `THIS`. + +## Examples of invalid use + +```iecst +// Error: Using THIS outside `FUNCTION_BLOCK` context +FUNCTION func + THIS^.x := 2; // Error: Invalid use of `THIS` +END_FUNCTION + +// Error: Not dereferencing THIS when accessing members +FUNCTION_BLOCK fb + THIS.x := 20; // Error: `THIS` must be dereferenced to access its members +END_FUNCTION_BLOCK + +// Error: THIS in member access position +FUNCTION_BLOCK fb + x.THIS^.y := 20; // Error: `THIS` is not allowed in member-access position +END_FUNCTION_BLOCK + +// Error: Global access position +FUNCTION_BLOCK fb + .THIS^.x := 20; // Error: `THIS` is not allowed in global-access position +END_FUNCTION_BLOCK + +// Error: Using THIS with type cast +FUNCTION_BLOCK fb + p := fb#THIS; // Error: The `#` operator cannot be used with `THIS` +END_FUNCTION_BLOCK +``` + +## Examples of valid use + +```iecst +FUNCTION_BLOCK Counter VAR - someVar : STRING := 'this is the instance var'; + count : INT; + enabled : BOOL; END_VAR - // use of `THIS` is not allowed here - printf('%s', REF(THIS^.someVar)); -END_FUNCTION + // Valid: Direct use in FUNCTION_BLOCK implementation + METHOD increment + IF THIS^.enabled THEN + THIS^.count := THIS^.count + 1; + END_IF + END_METHOD + + // Valid: Using THIS to pass the instance to another FB + METHOD send_to_logger : BOOL + VAR_IN_OUT + logger : Logger; + END_VAR + logger.log_counter(THIS); + END_METHOD + + // Valid: Using THIS to compare with another instance + METHOD equals : BOOL + VAR_IN_OUT + other : Counter; + END_VAR + equals := THIS^.count = other.count; + END_METHOD + + ACTION my_action + IF THIS^.enabled THEN + THIS^.count := THIS^.count + 1; + END_IF + END_ACTION +END_FUNCTION_BLOCK ``` diff --git a/src/parser/tests/parse_errors/parse_error_containers_tests.rs b/src/parser/tests/parse_errors/parse_error_containers_tests.rs index 2ff0ca0a1f..ce7f93aa24 100644 --- a/src/parser/tests/parse_errors/parse_error_containers_tests.rs +++ b/src/parser/tests/parse_errors/parse_error_containers_tests.rs @@ -346,10 +346,10 @@ fn this_is_a_reserved_keyword() { 3 │ PROGRAM this │ ^^^^ Case condition used outside of case statement! Did you mean to use ';'? - error[E120]: Invalid use of `THIS`. Usage is only allowed within `FUNCTION_BLOCK` and its `METHOD`s + error[E120]: Invalid use of `THIS`. Usage is only allowed within `FUNCTION_BLOCK` and its `METHOD`s and `ACTION`s. ┌─ :3:13 │ 3 │ PROGRAM this - │ ^^^^ Invalid use of `THIS`. Usage is only allowed within `FUNCTION_BLOCK` and its `METHOD`s + │ ^^^^ Invalid use of `THIS`. Usage is only allowed within `FUNCTION_BLOCK` and its `METHOD`s and `ACTION`s. "); } diff --git a/src/validation/statement.rs b/src/validation/statement.rs index dd78ea07fe..05b43b1670 100644 --- a/src/validation/statement.rs +++ b/src/validation/statement.rs @@ -166,7 +166,7 @@ pub fn visit_statement( }) { validator.push_diagnostic( Diagnostic::new( - "Invalid use of `THIS`. Usage is only allowed within `FUNCTION_BLOCK` and its `METHOD`s", + "Invalid use of `THIS`. Usage is only allowed within `FUNCTION_BLOCK` and its `METHOD`s and `ACTION`s.", ) .with_error_code("E120") .with_location(statement), diff --git a/src/validation/tests/this_keyword_validation_tests.rs b/src/validation/tests/this_keyword_validation_tests.rs index 4366ecb401..54b890481c 100644 --- a/src/validation/tests/this_keyword_validation_tests.rs +++ b/src/validation/tests/this_keyword_validation_tests.rs @@ -396,29 +396,29 @@ fn this_in_unsupported_pous_is_not_allowed() { "#, ); assert_snapshot!(diagnostics, @r" - error[E120]: Invalid use of `THIS`. Usage is only allowed within `FUNCTION_BLOCK` and its `METHOD`s + error[E120]: Invalid use of `THIS`. Usage is only allowed within `FUNCTION_BLOCK` and its `METHOD`s and `ACTION`s. ┌─ :8:24 │ 8 │ bar := THIS^.x; // this in method in program is not allowed - │ ^^^^ Invalid use of `THIS`. Usage is only allowed within `FUNCTION_BLOCK` and its `METHOD`s + │ ^^^^ Invalid use of `THIS`. Usage is only allowed within `FUNCTION_BLOCK` and its `METHOD`s and `ACTION`s. - error[E120]: Invalid use of `THIS`. Usage is only allowed within `FUNCTION_BLOCK` and its `METHOD`s + error[E120]: Invalid use of `THIS`. Usage is only allowed within `FUNCTION_BLOCK` and its `METHOD`s and `ACTION`s. ┌─ :10:13 │ 10 │ THIS^; - │ ^^^^ Invalid use of `THIS`. Usage is only allowed within `FUNCTION_BLOCK` and its `METHOD`s + │ ^^^^ Invalid use of `THIS`. Usage is only allowed within `FUNCTION_BLOCK` and its `METHOD`s and `ACTION`s. - error[E120]: Invalid use of `THIS`. Usage is only allowed within `FUNCTION_BLOCK` and its `METHOD`s + error[E120]: Invalid use of `THIS`. Usage is only allowed within `FUNCTION_BLOCK` and its `METHOD`s and `ACTION`s. ┌─ :14:13 │ 14 │ THIS^; - │ ^^^^ Invalid use of `THIS`. Usage is only allowed within `FUNCTION_BLOCK` and its `METHOD`s + │ ^^^^ Invalid use of `THIS`. Usage is only allowed within `FUNCTION_BLOCK` and its `METHOD`s and `ACTION`s. - error[E120]: Invalid use of `THIS`. Usage is only allowed within `FUNCTION_BLOCK` and its `METHOD`s + error[E120]: Invalid use of `THIS`. Usage is only allowed within `FUNCTION_BLOCK` and its `METHOD`s and `ACTION`s. ┌─ :18:13 │ 18 │ THIS^; - │ ^^^^ Invalid use of `THIS`. Usage is only allowed within `FUNCTION_BLOCK` and its `METHOD`s + │ ^^^^ Invalid use of `THIS`. Usage is only allowed within `FUNCTION_BLOCK` and its `METHOD`s and `ACTION`s. "); } From 6fbdf25b2823aa319731e6959e0ff6910f6e0426 Mon Sep 17 00:00:00 2001 From: abroooo <61380142+abroooo@users.noreply.github.com> Date: Thu, 8 May 2025 22:45:41 +0200 Subject: [PATCH 48/48] cleanup --- src/codegen/generators/expression_generator.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/codegen/generators/expression_generator.rs b/src/codegen/generators/expression_generator.rs index 51346b7cc0..ad0bb8d3dc 100644 --- a/src/codegen/generators/expression_generator.rs +++ b/src/codegen/generators/expression_generator.rs @@ -218,12 +218,10 @@ impl<'ink, 'b> ExpressionCodeGenerator<'ink, 'b> { // generate the expression match expression.get_stmt() { AstStatement::This => { - // TODO: #THIS can we expect to have context here? self.function_context.ok_or_else(|| { Diagnostic::codegen_error("Cannot use 'this' without context", expression) })?; let Some(this_name) = self.annotations.get_call_name(expression) else { - // TODO: is this correct? unreachable!("this should have a name"); }; let this_value =