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 34 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 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
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 => {}
}
}
}
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,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, Error, include_str!("./error_codes/E120.md"), // Invalid use of `THIS` keyword
E121, Info, include_str!("./error_codes/E121.md"), // `THIS` is read-only
);
}

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.
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
```
15 changes: 15 additions & 0 deletions compiler/plc_diagnostics/src/diagnostics/error_codes/E121.md
Original file line number Diff line number Diff line change
@@ -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
```
42 changes: 42 additions & 0 deletions compiler/plc_lowering/src/inheritance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
},
),
},
]
"#);
}
}
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 meaningful errors
// [ ] add tests
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")
};
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
14 changes: 14 additions & 0 deletions src/codegen/generators/pou_generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -699,6 +699,20 @@ impl<'ink, 'cg> PouGenerator<'ink, 'cg> {
location.get_column(),
);
}

if function_context.linking_context.is_method()
&& self.index.find_pou(type_name).is_some_and(|it| it.is_function_block())
{
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
let mut var_count = 0;
Expand Down
Loading
Loading