Skip to content

feat: THIS keyword #1454

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 47 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
a8b4e05
parse this keyword
abroooo Apr 1, 2025
83a78ff
add some tests
abroooo Apr 3, 2025
add58e0
add error codes
abroooo Apr 6, 2025
e41bd09
add validation tests
abroooo Apr 6, 2025
c9c3a53
extend shadowing lit test with `this` test
abroooo Apr 6, 2025
5b100f2
add expression parser test
abroooo Apr 6, 2025
39825c1
prepare codegen extension
abroooo Apr 6, 2025
ab7cd7f
prepare visitor extension
abroooo Apr 6, 2025
02c41b1
add test
abroooo Apr 6, 2025
1e22a63
resolve `this`
abroooo Apr 6, 2025
d6fb560
add another parser test
abroooo Apr 7, 2025
58ff739
this doesnt need ptr_type
abroooo Apr 7, 2025
7821333
generate expression value
abroooo Apr 8, 2025
8b0172f
resolve this works
abroooo Apr 9, 2025
3fb8f5c
cheat validation
abroooo Apr 9, 2025
de0c84c
do not forget the todo
abroooo Apr 9, 2025
daa2601
a bit of error handling and a bit of progress in resolving
abroooo Apr 9, 2025
7f13735
`this` works for functionblocks and methods
abroooo Apr 10, 2025
c2ad79d
cleanup
abroooo Apr 10, 2025
0ca734a
add pointer to new index
abroooo Apr 10, 2025
f3c58a2
add resolve test
abroooo Apr 10, 2025
3dcb582
fix resolver and work on tests
abroooo Apr 11, 2025
6968405
start validation
abroooo Apr 11, 2025
821ef07
validation
abroooo Apr 11, 2025
d7c1aa0
this only is only allowed in methods of fbs
abroooo Apr 14, 2025
81145c1
refactor and more resolver tests
abroooo Apr 14, 2025
46e30da
more resolver tests
abroooo Apr 15, 2025
7d1ab66
validation and tests
abroooo Apr 17, 2025
7529063
more validation
abroooo Apr 17, 2025
7177ca9
still more validation
abroooo Apr 17, 2025
7fa0fca
believe it or not there is still validation left to do
abroooo Apr 28, 2025
25b5afb
more tests, more validation, some cleanups
abroooo Apr 29, 2025
b12a460
more codegen tests
abroooo Apr 29, 2025
bdeccee
add lit tests
abroooo Apr 30, 2025
c655b06
Merge branch 'master' into this_in_parser
abroooo Apr 30, 2025
e5bb42f
fix snapshots
abroooo Apr 30, 2025
00c1244
some cleanups
abroooo Apr 30, 2025
e38b70d
snapshots
abroooo May 6, 2025
a7e9d2b
cleanups
abroooo May 7, 2025
ef593a1
fix snapshots
abroooo May 8, 2025
8d8dcd2
Update src/validation/tests/this_keyword_validation_tests.rs
abroooo May 8, 2025
fcaab22
Update compiler/plc_diagnostics/src/diagnostics/diagnostics_registry.rs
abroooo May 8, 2025
e034bb5
Update compiler/plc_ast/src/mut_visitor.rs
abroooo May 8, 2025
5fb7eb9
implement feedback
abroooo May 8, 2025
64f23fa
implement feedback 2
abroooo May 8, 2025
1884fd2
support actions
abroooo May 8, 2025
c536d67
fix snapshot
abroooo May 8, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions compiler/plc_ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -861,6 +861,7 @@ pub enum AstStatement {
ReferenceExpr(ReferenceExpr),
Identifier(String),
Super(Option<DerefMarker>),
This,
DirectAccess(DirectAccess),
HardwareAccess(HardwareAccess),
BinaryExpression(BinaryExpression),
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -1236,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 { .. })
}
Expand Down Expand Up @@ -1282,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()
Expand Down Expand Up @@ -1640,6 +1649,13 @@ impl AstFactory {
AstNode::new(AstStatement::Super(deref), id, location.into())
}

pub fn create_this_reference<T>(location: T, id: AstId) -> AstNode
where
T: Into<SourceLocation>,
{
AstNode::new(AstStatement::This, id, location.into())
}

pub fn create_unary_expression(
operator: Operator,
value: AstNode,
Expand Down
4 changes: 4 additions & 0 deletions compiler/plc_ast/src/mut_visitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -406,6 +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 => visitor.visit_this(self),
}
}
}
Expand Down
1 change: 1 addition & 0 deletions compiler/plc_ast/src/visitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 => {}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +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
);
}

Expand Down
17 changes: 17 additions & 0 deletions compiler/plc_diagnostics/src/diagnostics/error_codes/E120.md
Original file line number Diff line number Diff line change
@@ -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 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:

```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
```
16 changes: 16 additions & 0 deletions src/codegen/generators/expression_generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,22 @@ 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 =
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)
})?;
Ok(ExpressionValue::LValue(this_value))
}
AstStatement::ReferenceExpr(data) => {
let res =
self.generate_reference_expression(&data.access, data.base.as_deref(), expression)?;
Expand Down
11 changes: 11 additions & 0 deletions src/codegen/generators/pou_generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -699,6 +699,17 @@ impl<'ink, 'cg> PouGenerator<'ink, 'cg> {
location.get_column(),
);
}

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)?;
}

//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;
Expand Down
10 changes: 10 additions & 0 deletions src/codegen/tests/code_gen_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1114,13 +1114,17 @@ 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
}

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
Expand Down Expand Up @@ -1197,13 +1201,17 @@ 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
}

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
Expand Down Expand Up @@ -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
}

Expand Down
4 changes: 4 additions & 0 deletions src/codegen/tests/debug_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
---
source: src/codegen/tests/debug_tests/expression_debugging.rs
expression: result
snapshot_kind: text
---
; ModuleID = '<internal>'
source_filename = "<internal>"
Expand Down Expand Up @@ -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
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
---
source: src/codegen/tests/debug_tests/expression_debugging.rs
expression: result
snapshot_kind: text
---
; ModuleID = '<internal>'
source_filename = "<internal>"
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
---
source: src/codegen/tests/debug_tests/expression_debugging.rs
expression: result
snapshot_kind: text
---
; ModuleID = '<internal>'
source_filename = "<internal>"
Expand Down Expand Up @@ -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
Expand Down
16 changes: 12 additions & 4 deletions src/codegen/tests/directaccess_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ fn direct_acess_in_output_assignment_implicit_explicit_and_mixed() {
",
);

assert_snapshot!(ir, @r###"
assert_snapshot!(ir, @r#"
; ModuleID = '<internal>'
source_filename = "<internal>"

Expand All @@ -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
Expand Down Expand Up @@ -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]
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -305,7 +309,7 @@ fn direct_acess_in_output_assignment_with_simple_expression_implicit() {
",
);

assert_snapshot!(ir, @r###"
assert_snapshot!(ir, @r#"
; ModuleID = '<internal>'
source_filename = "<internal>"

Expand All @@ -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
}
Expand Down Expand Up @@ -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]
Expand Down Expand Up @@ -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
}
Expand Down
Loading