Skip to content

Commit c45ceba

Browse files
committed
allow assignments between different pointer types
casts between pointer types will generate a llvm bitcast statement e.g. x : INT; p : POINTER TO BYTE; p := &x; fixes #463
1 parent 7adbdd2 commit c45ceba

7 files changed

+192
-6
lines changed

src/codegen/llvm_typesystem.rs

+21-2
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,18 @@ fn create_llvm_extend_int_value<'a>(
111111
}
112112
}
113113

114+
///
115+
/// generates a cast from the given `value` to the given `target_type` if necessary and returns the casted value. It returns
116+
/// the original `value` if no cast is necessary
117+
///
118+
/// - `llvm` the llvm utilities to use for code-generation
119+
/// - `index` the current Index used for type-lookups
120+
/// - `llvm_type_index` the type index to lookup llvm generated types
121+
/// - `target_type` the expected target type of the value
122+
/// - `value` the value to (maybe) cast
123+
/// - `value_type` the current type of the given value
124+
/// - `statement` the original statement as a context (e.g. for error reporting)
125+
///
114126
pub fn cast_if_needed<'ctx>(
115127
llvm: &Llvm<'ctx>,
116128
index: &Index,
@@ -303,8 +315,15 @@ pub fn cast_if_needed<'ctx>(
303315
)
304316
.into()),
305317
DataTypeInformation::Pointer { .. } | DataTypeInformation::Void { .. } => {
306-
//this is ok, no cast required
307-
Ok(value)
318+
let target_ptr_type =
319+
llvm_type_index.get_associated_type(target_type.get_name())?;
320+
if value.get_type() != target_ptr_type {
321+
// bit-cast necessary
322+
Ok(builder.build_bitcast(value, target_ptr_type, ""))
323+
} else {
324+
//this is ok, no cast required
325+
Ok(value)
326+
}
308327
}
309328
_ => Err(Diagnostic::casting_error(
310329
value_type.get_name(),

src/codegen/tests/expression_tests.rs

+68-1
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,7 @@ fn cast_lword_to_pointer() {
290290
FUNCTION baz : INT
291291
VAR
292292
ptr_x : POINTER TO INT;
293-
y : LWORD;
293+
y : LWORD;
294294
END_VAR;
295295
296296
ptr_x := y;
@@ -302,6 +302,73 @@ fn cast_lword_to_pointer() {
302302
insta::assert_snapshot!(result);
303303
}
304304

305+
#[test]
306+
fn cast_between_pointer_types() {
307+
let result = codegen(
308+
r#"
309+
PROGRAM baz
310+
VAR
311+
ptr_x : POINTER TO BYTE;
312+
y : WORD;
313+
END_VAR;
314+
315+
ptr_x := &y;
316+
END_PROGRAM
317+
"#,
318+
);
319+
320+
//should result in bitcast conversion when assigning to ptr_x
321+
insta::assert_snapshot!(result);
322+
}
323+
324+
#[test]
325+
fn unnecessary_casts_between_pointer_types() {
326+
let result = codegen(
327+
r#"
328+
TYPE MyByte : BYTE; END_TYPE
329+
330+
PROGRAM baz
331+
VAR
332+
ptr : POINTER TO BYTE;
333+
b : BYTE;
334+
si : SINT;
335+
mb : MyByte;
336+
END_VAR;
337+
338+
ptr := &b; //no cast necessary
339+
ptr := &si; //no cast necessary
340+
ptr := &mb; //no cast necessary
341+
END_PROGRAM
342+
"#,
343+
);
344+
345+
//should not result in bitcast
346+
insta::assert_snapshot!(result);
347+
}
348+
349+
#[test]
350+
fn access_string_via_byte_array() {
351+
let result = codegen(
352+
r#"
353+
TYPE MyByte : BYTE; END_TYPE
354+
355+
PROGRAM baz
356+
VAR
357+
str: STRING[10];
358+
ptr : POINTER TO BYTE;
359+
bytes : POINTER TO ARRAY[0..9] OF BYTE;
360+
END_VAR;
361+
362+
ptr := &str; //bit-cast expected
363+
bytes := &str;
364+
END_PROGRAM
365+
"#,
366+
);
367+
368+
//should result in bitcasts
369+
insta::assert_snapshot!(result);
370+
}
371+
305372
#[test]
306373
fn pointer_arithmetics() {
307374
// codegen should be successful for binary expression for pointer<->int / int<->pointer / pointer<->pointer

src/codegen/tests/snapshots/rusty__codegen__tests__code_gen_tests__pointers_generated.snap

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
source: src/codegen/tests/code_gen_tests.rs
3-
assertion_line: 1718
3+
assertion_line: 1719
44
expression: result
55

66
---
@@ -16,8 +16,8 @@ entry:
1616
%X = getelementptr inbounds %prg_interface, %prg_interface* %0, i32 0, i32 0
1717
%pX = getelementptr inbounds %prg_interface, %prg_interface* %0, i32 0, i32 1
1818
%rX = getelementptr inbounds %prg_interface, %prg_interface* %0, i32 0, i32 2
19-
store i32* null, i8** %pX, align 8
20-
store i32* null, i8** %rX, align 8
19+
store i8* null, i8** %pX, align 8
20+
store i8* null, i8** %rX, align 8
2121
store i8* %X, i8** %pX, align 8
2222
store i8* %X, i8** %rX, align 8
2323
%deref = load i8*, i8** %pX, align 8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
---
2+
source: src/codegen/tests/expression_tests.rs
3+
assertion_line: 370
4+
expression: result
5+
6+
---
7+
; ModuleID = 'main'
8+
source_filename = "main"
9+
10+
%baz_interface = type { [11 x i8], i8*, [10 x i8]* }
11+
12+
@baz_instance = global %baz_interface zeroinitializer
13+
14+
define void @baz(%baz_interface* %0) {
15+
entry:
16+
%str = getelementptr inbounds %baz_interface, %baz_interface* %0, i32 0, i32 0
17+
%ptr = getelementptr inbounds %baz_interface, %baz_interface* %0, i32 0, i32 1
18+
%bytes = getelementptr inbounds %baz_interface, %baz_interface* %0, i32 0, i32 2
19+
%1 = bitcast [11 x i8]* %str to i8*
20+
store i8* %1, i8** %ptr, align 8
21+
%2 = bitcast [11 x i8]* %str to [10 x i8]*
22+
store [10 x i8]* %2, [10 x i8]** %bytes, align 8
23+
ret void
24+
}
25+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
---
2+
source: src/codegen/tests/expression_tests.rs
3+
assertion_line: 321
4+
expression: result
5+
6+
---
7+
; ModuleID = 'main'
8+
source_filename = "main"
9+
10+
%baz_interface = type { i8*, i16 }
11+
12+
@baz_instance = global %baz_interface zeroinitializer
13+
14+
define void @baz(%baz_interface* %0) {
15+
entry:
16+
%ptr_x = getelementptr inbounds %baz_interface, %baz_interface* %0, i32 0, i32 0
17+
%y = getelementptr inbounds %baz_interface, %baz_interface* %0, i32 0, i32 1
18+
%1 = bitcast i16* %y to i8*
19+
store i8* %1, i8** %ptr_x, align 8
20+
ret void
21+
}
22+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
---
2+
source: src/codegen/tests/expression_tests.rs
3+
assertion_line: 346
4+
expression: result
5+
6+
---
7+
; ModuleID = 'main'
8+
source_filename = "main"
9+
10+
%baz_interface = type { i8*, i8, i8, i8 }
11+
12+
@baz_instance = global %baz_interface zeroinitializer
13+
14+
define void @baz(%baz_interface* %0) {
15+
entry:
16+
%ptr = getelementptr inbounds %baz_interface, %baz_interface* %0, i32 0, i32 0
17+
%b = getelementptr inbounds %baz_interface, %baz_interface* %0, i32 0, i32 1
18+
%si = getelementptr inbounds %baz_interface, %baz_interface* %0, i32 0, i32 2
19+
%mb = getelementptr inbounds %baz_interface, %baz_interface* %0, i32 0, i32 3
20+
store i8* %b, i8** %ptr, align 8
21+
store i8* %si, i8** %ptr, align 8
22+
store i8* %mb, i8** %ptr, align 8
23+
ret void
24+
}
25+

src/resolver/tests/resolve_expressions_tests.rs

+28
Original file line numberDiff line numberDiff line change
@@ -698,6 +698,7 @@ fn pointer_expressions_resolve_types() {
698698

699699
assert_eq!(format!("{:?}", expected_types), format!("{:?}", type_names));
700700
}
701+
701702
#[test]
702703
fn array_expressions_resolve_types() {
703704
let (unit, index) = index(
@@ -2921,6 +2922,33 @@ fn adress_of_is_annotated_correctly() {
29212922
}
29222923
}
29232924

2925+
#[test]
2926+
fn pointer_assignment_with_incompatible_types_hints_correctly() {
2927+
let (unit, mut index) = index(
2928+
"PROGRAM PRG
2929+
VAR
2930+
x : INT;
2931+
pt : POINTER TO BYTE;
2932+
END_VAR
2933+
pt := &x;
2934+
END_PROGRAM",
2935+
);
2936+
2937+
let annotations = annotate(&unit, &mut index);
2938+
let assignment = &unit.implementations[0].statements[0];
2939+
2940+
if let AstStatement::Assignment { left, right, .. } = assignment {
2941+
assert_type_and_hint!(&annotations, &index, left, "__PRG_pt", None);
2942+
assert_type_and_hint!(
2943+
&annotations,
2944+
&index,
2945+
right,
2946+
"POINTER_TO_INT",
2947+
Some("__PRG_pt")
2948+
);
2949+
}
2950+
}
2951+
29242952
#[test]
29252953
fn call_on_function_block_array() {
29262954
//GIVEN

0 commit comments

Comments
 (0)