Skip to content

Commit

Permalink
Merge branch 'master' into volsa/rust182
Browse files Browse the repository at this point in the history
  • Loading branch information
ghaith authored Oct 30, 2024
2 parents 50bca54 + 8a07316 commit 810664c
Show file tree
Hide file tree
Showing 11 changed files with 628 additions and 54 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ lazy_static! {
E106, Warning, include_str!("./error_codes/E106.md"), // VAR_EXTERNAL have no effect
E107, Error, include_str!("./error_codes/E107.md"), // Missing configuration for template variable
E108, Error, include_str!("./error_codes/E108.md"), // Template variable is configured multiple times
E109, Error, include_str!("./error_codes/E109.md"), // Stateful pointer variable initialized with temporary value
);
}

Expand Down
16 changes: 16 additions & 0 deletions compiler/plc_diagnostics/src/diagnostics/error_codes/E109.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Stateful member variable initialized with temporary reference

Stack-local variables do not yet exist at the time of initialization. Additionally, pointing to a temporary variable will lead to a dangling pointer
as soon as it goes out of scope - potential use after free.

Erroneous code example:
```
FUNCTION_BLOCK foo
VAR
a : REF_TO BOOL := REF(b);
END_VAR
VAR_TEMP
b : BOOL;
END_VAR
END_FUNCTION_BLOCK
```
14 changes: 14 additions & 0 deletions src/codegen/generators/pou_generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -772,6 +772,20 @@ impl<'ink, 'cg> PouGenerator<'ink, 'cg> {
variable.source_location.get_column(),
);
}

if self
.index
.find_effective_type_by_name(variable.get_type_name())
.map(|it| it.get_type_information())
.is_some_and(|it| it.is_reference_to() || it.is_alias())
{
// aliases and reference to variables have special handling for initialization. initialize with a nullpointer
self.llvm.builder.build_store(
left,
left.get_type().get_element_type().into_pointer_type().const_null(),
);
continue;
};
let right_stmt =
self.index.get_const_expressions().maybe_get_constant_statement(&variable.initial_value);
self.generate_variable_initializer(variable, left, right_stmt, &exp_gen)?;
Expand Down
272 changes: 272 additions & 0 deletions src/codegen/tests/initialization_test/complex_initializers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1368,3 +1368,275 @@ fn var_external_blocks_are_ignored_in_init_functions() {
}
"###)
}

#[test]
fn ref_to_local_member() {
let res = codegen(
r"
FUNCTION_BLOCK foo
VAR
s : STRING;
ptr : REF_TO STRING := REF(s);
alias AT s : STRING;
reference_to : REFERENCE TO STRING REF= s;
END_VAR
END_FUNCTION_BLOCK
",
);

insta::assert_snapshot!(res, @r#"
; ModuleID = '<internal>'
source_filename = "<internal>"
%foo = type { [81 x i8], [81 x i8]*, [81 x i8]*, [81 x i8]* }
@__foo__init = unnamed_addr constant %foo zeroinitializer
define void @foo(%foo* %0) {
entry:
%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
%reference_to = getelementptr inbounds %foo, %foo* %0, i32 0, i32 3
ret void
}
; ModuleID = '__initializers'
source_filename = "__initializers"
%foo = type { [81 x i8], [81 x i8]*, [81 x i8]*, [81 x i8]* }
@__foo__init = external global %foo
define void @__init_foo(%foo* %0) {
entry:
%self = alloca %foo*, align 8
store %foo* %0, %foo** %self, align 8
%deref = load %foo*, %foo** %self, align 8
%ptr = getelementptr inbounds %foo, %foo* %deref, i32 0, i32 1
%deref1 = load %foo*, %foo** %self, align 8
%s = getelementptr inbounds %foo, %foo* %deref1, i32 0, i32 0
store [81 x i8]* %s, [81 x i8]** %ptr, align 8
%deref2 = load %foo*, %foo** %self, align 8
%alias = getelementptr inbounds %foo, %foo* %deref2, i32 0, i32 2
%deref3 = load %foo*, %foo** %self, align 8
%s4 = getelementptr inbounds %foo, %foo* %deref3, i32 0, i32 0
store [81 x i8]* %s4, [81 x i8]** %alias, align 8
%deref5 = load %foo*, %foo** %self, align 8
%reference_to = getelementptr inbounds %foo, %foo* %deref5, i32 0, i32 3
%deref6 = load %foo*, %foo** %self, align 8
%s7 = getelementptr inbounds %foo, %foo* %deref6, i32 0, i32 0
store [81 x i8]* %s7, [81 x i8]** %reference_to, align 8
ret void
}
declare void @foo(%foo*)
; ModuleID = '__init___testproject'
source_filename = "__init___testproject"
@llvm.global_ctors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 0, void ()* @__init___testproject, i8* null }]
define void @__init___testproject() {
entry:
ret void
}
"#)
}

#[test]
fn ref_to_local_member_shadows_global() {
let res = codegen(
r"
VAR_GLOBAL
s : STRING;
END_VAR
FUNCTION_BLOCK foo
VAR
s : STRING;
ptr : REF_TO STRING := REF(s);
alias AT s : STRING;
reference_to : REFERENCE TO STRING REF= s;
END_VAR
END_FUNCTION_BLOCK
",
);

insta::assert_snapshot!(res, @r#"
; ModuleID = '<internal>'
source_filename = "<internal>"
%foo = type { [81 x i8], [81 x i8]*, [81 x i8]*, [81 x i8]* }
@s = global [81 x i8] zeroinitializer
@__foo__init = unnamed_addr constant %foo zeroinitializer
define void @foo(%foo* %0) {
entry:
%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
%reference_to = getelementptr inbounds %foo, %foo* %0, i32 0, i32 3
ret void
}
; ModuleID = '__initializers'
source_filename = "__initializers"
%foo = type { [81 x i8], [81 x i8]*, [81 x i8]*, [81 x i8]* }
@__foo__init = external global %foo
define void @__init_foo(%foo* %0) {
entry:
%self = alloca %foo*, align 8
store %foo* %0, %foo** %self, align 8
%deref = load %foo*, %foo** %self, align 8
%ptr = getelementptr inbounds %foo, %foo* %deref, i32 0, i32 1
%deref1 = load %foo*, %foo** %self, align 8
%s = getelementptr inbounds %foo, %foo* %deref1, i32 0, i32 0
store [81 x i8]* %s, [81 x i8]** %ptr, align 8
%deref2 = load %foo*, %foo** %self, align 8
%alias = getelementptr inbounds %foo, %foo* %deref2, i32 0, i32 2
%deref3 = load %foo*, %foo** %self, align 8
%s4 = getelementptr inbounds %foo, %foo* %deref3, i32 0, i32 0
store [81 x i8]* %s4, [81 x i8]** %alias, align 8
%deref5 = load %foo*, %foo** %self, align 8
%reference_to = getelementptr inbounds %foo, %foo* %deref5, i32 0, i32 3
%deref6 = load %foo*, %foo** %self, align 8
%s7 = getelementptr inbounds %foo, %foo* %deref6, i32 0, i32 0
store [81 x i8]* %s7, [81 x i8]** %reference_to, align 8
ret void
}
declare void @foo(%foo*)
; ModuleID = '__init___testproject'
source_filename = "__init___testproject"
@llvm.global_ctors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 0, void ()* @__init___testproject, i8* null }]
define void @__init___testproject() {
entry:
ret void
}
"#)
}

#[test]
fn temporary_variable_ref_to_local_member() {
let res = codegen(
r"
FUNCTION_BLOCK foo
VAR
s : STRING;
END_VAR
VAR_TEMP
ptr : REF_TO STRING := REF(s);
alias AT s : STRING;
reference_to : REFERENCE TO STRING REF= s;
END_VAR
END_FUNCTION_BLOCK
",
);

insta::assert_snapshot!(res, @r#"
; ModuleID = '<internal>'
source_filename = "<internal>"
%foo = type { [81 x i8] }
@__foo__init = unnamed_addr constant %foo zeroinitializer
define void @foo(%foo* %0) {
entry:
%s = getelementptr inbounds %foo, %foo* %0, i32 0, i32 0
%ptr = alloca [81 x i8]*, align 8
%alias = alloca [81 x i8]*, align 8
%reference_to = alloca [81 x i8]*, align 8
store [81 x i8]* %s, [81 x i8]** %ptr, align 8
store [81 x i8]* null, [81 x i8]** %alias, align 8
store [81 x i8]* null, [81 x i8]** %reference_to, align 8
store [81 x i8]* %s, [81 x i8]** %ptr, align 8
store [81 x i8]* %s, [81 x i8]** %alias, align 8
store [81 x i8]* %s, [81 x i8]** %reference_to, align 8
ret void
}
; ModuleID = '__initializers'
source_filename = "__initializers"
%foo = type { [81 x i8] }
@__foo__init = external global %foo
define void @__init_foo(%foo* %0) {
entry:
%self = alloca %foo*, align 8
store %foo* %0, %foo** %self, align 8
ret void
}
declare void @foo(%foo*)
; ModuleID = '__init___testproject'
source_filename = "__init___testproject"
@llvm.global_ctors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 0, void ()* @__init___testproject, i8* null }]
define void @__init___testproject() {
entry:
ret void
}
"#)
}

#[test]
fn temporary_variable_ref_to_temporary_variable() {
let res = codegen(
r"
FUNCTION foo
VAR
ptr : REF_TO STRING := REF(s);
alias AT s : STRING;
END_VAR
VAR_TEMP
s : STRING;
reference_to : REFERENCE TO STRING REF= alias;
END_VAR
FUNCTION
",
);

insta::assert_snapshot!(res, @r##"
; ModuleID = '<internal>'
source_filename = "<internal>"
define void @foo() {
entry:
%ptr = alloca [81 x i8]*, align 8
%alias = alloca [81 x i8]*, align 8
%s = alloca [81 x i8], align 1
%reference_to = alloca [81 x i8]*, align 8
store [81 x i8]* %s, [81 x i8]** %ptr, align 8
store [81 x i8]* null, [81 x i8]** %alias, align 8
%0 = bitcast [81 x i8]* %s to i8*
call void @llvm.memset.p0i8.i64(i8* align 1 %0, i8 0, i64 ptrtoint ([81 x i8]* getelementptr ([81 x i8], [81 x i8]* null, i32 1) to i64), i1 false)
store [81 x i8]* null, [81 x i8]** %reference_to, align 8
store [81 x i8]* %s, [81 x i8]** %ptr, align 8
store [81 x i8]* %s, [81 x i8]** %alias, align 8
%deref = load [81 x i8]*, [81 x i8]** %alias, align 8
store [81 x i8]* %deref, [81 x i8]** %reference_to, align 8
ret void
}
; Function Attrs: argmemonly nofree nounwind willreturn writeonly
declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i1 immarg) #0
attributes #0 = { argmemonly nofree nounwind willreturn writeonly }
; ModuleID = '__init___testproject'
source_filename = "__init___testproject"
@llvm.global_ctors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 0, void ()* @__init___testproject, i8* null }]
define void @__init___testproject() {
entry:
ret void
}
"##)
}
15 changes: 6 additions & 9 deletions src/codegen/tests/statement_codegen_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,6 @@ fn reference_to_string_assignment() {
VAR
a : REF_TO STRING;
END_VAR
a^ := 'hello';
END_FUNCTION
"#,
Expand Down Expand Up @@ -351,16 +350,15 @@ fn local_alias() {
"#,
);

assert_snapshot!(content, @r###"
assert_snapshot!(content, @r#"
; ModuleID = '<internal>'
source_filename = "<internal>"
define void @main() {
entry:
%foo = alloca i32*, align 8
%bar = alloca i32, align 4
%load_bar = load i32, i32* %bar, align 4
store i32 %load_bar, i32** %foo, align 4
store i32* null, i32** %foo, align 8
store i32 0, i32* %bar, align 4
store i32* %bar, i32** %foo, align 8
ret void
Expand All @@ -374,7 +372,7 @@ fn local_alias() {
entry:
ret void
}
"###);
"#);
}

#[test]
Expand All @@ -390,16 +388,15 @@ fn local_string_alias() {
"#,
);

assert_snapshot!(content, @r###"
assert_snapshot!(content, @r##"
; ModuleID = '<internal>'
source_filename = "<internal>"
define void @main() {
entry:
%foo = alloca [81 x i8]*, align 8
%bar = alloca [81 x i8], align 1
%load_bar = load [81 x i8], [81 x i8]* %bar, align 1
store [81 x i8] %load_bar, [81 x i8]** %foo, align 1
store [81 x i8]* null, [81 x i8]** %foo, align 8
%0 = bitcast [81 x i8]* %bar to i8*
call void @llvm.memset.p0i8.i64(i8* align 1 %0, i8 0, i64 ptrtoint ([81 x i8]* getelementptr ([81 x i8], [81 x i8]* null, i32 1) to i64), i1 false)
store [81 x i8]* %bar, [81 x i8]** %foo, align 8
Expand All @@ -419,7 +416,7 @@ fn local_string_alias() {
entry:
ret void
}
"###);
"##);
}

#[test]
Expand Down
Loading

0 comments on commit 810664c

Please sign in to comment.