Skip to content
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

allow assignments between different pointer types #466

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
23 changes: 21 additions & 2 deletions src/codegen/llvm_typesystem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,18 @@ fn create_llvm_extend_int_value<'a>(
}
}

///
/// generates a cast from the given `value` to the given `target_type` if necessary and returns the casted value. It returns
/// the original `value` if no cast is necessary
///
/// - `llvm` the llvm utilities to use for code-generation
/// - `index` the current Index used for type-lookups
/// - `llvm_type_index` the type index to lookup llvm generated types
/// - `target_type` the expected target type of the value
/// - `value` the value to (maybe) cast
/// - `value_type` the current type of the given value
/// - `statement` the original statement as a context (e.g. for error reporting)
///
pub fn cast_if_needed<'ctx>(
llvm: &Llvm<'ctx>,
index: &Index,
Expand Down Expand Up @@ -303,8 +315,15 @@ pub fn cast_if_needed<'ctx>(
)
.into()),
DataTypeInformation::Pointer { .. } | DataTypeInformation::Void { .. } => {
//this is ok, no cast required
Ok(value)
let target_ptr_type =
llvm_type_index.get_associated_type(target_type.get_name())?;
if value.get_type() != target_ptr_type {
// bit-cast necessary
Ok(builder.build_bitcast(value, target_ptr_type, ""))
} else {
//this is ok, no cast required
Ok(value)
}
}
_ => Err(Diagnostic::casting_error(
value_type.get_name(),
Expand Down
69 changes: 68 additions & 1 deletion src/codegen/tests/expression_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ fn cast_lword_to_pointer() {
FUNCTION baz : INT
VAR
ptr_x : POINTER TO INT;
y : LWORD;
y : LWORD;
END_VAR;

ptr_x := y;
Expand All @@ -302,6 +302,73 @@ fn cast_lword_to_pointer() {
insta::assert_snapshot!(result);
}

#[test]
fn cast_between_pointer_types() {
let result = codegen(
r#"
PROGRAM baz
VAR
ptr_x : POINTER TO BYTE;
y : WORD;
END_VAR;

ptr_x := &y;
END_PROGRAM
"#,
);

//should result in bitcast conversion when assigning to ptr_x
insta::assert_snapshot!(result);
}

#[test]
fn unnecessary_casts_between_pointer_types() {
let result = codegen(
r#"
TYPE MyByte : BYTE; END_TYPE

PROGRAM baz
VAR
ptr : POINTER TO BYTE;
b : BYTE;
si : SINT;
mb : MyByte;
END_VAR;

ptr := &b; //no cast necessary
ptr := &si; //no cast necessary
ptr := &mb; //no cast necessary
END_PROGRAM
"#,
);

//should not result in bitcast
insta::assert_snapshot!(result);
}

#[test]
fn access_string_via_byte_array() {
let result = codegen(
r#"
TYPE MyByte : BYTE; END_TYPE

PROGRAM baz
VAR
str: STRING[10];
ptr : POINTER TO BYTE;
bytes : POINTER TO ARRAY[0..9] OF BYTE;
END_VAR;

ptr := &str; //bit-cast expected
bytes := &str;
END_PROGRAM
"#,
);

//should result in bitcasts
insta::assert_snapshot!(result);
}

#[test]
fn pointer_arithmetics() {
// codegen should be successful for binary expression for pointer<->int / int<->pointer / pointer<->pointer
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
source: src/codegen/tests/code_gen_tests.rs
assertion_line: 1718
assertion_line: 1719
expression: result

---
Expand All @@ -16,8 +16,8 @@ entry:
%X = getelementptr inbounds %prg_interface, %prg_interface* %0, i32 0, i32 0
%pX = getelementptr inbounds %prg_interface, %prg_interface* %0, i32 0, i32 1
%rX = getelementptr inbounds %prg_interface, %prg_interface* %0, i32 0, i32 2
store i32* null, i8** %pX, align 8
store i32* null, i8** %rX, align 8
store i8* null, i8** %pX, align 8
store i8* null, i8** %rX, align 8
store i8* %X, i8** %pX, align 8
store i8* %X, i8** %rX, align 8
%deref = load i8*, i8** %pX, align 8
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
---
source: src/codegen/tests/expression_tests.rs
assertion_line: 370
expression: result

---
; ModuleID = 'main'
source_filename = "main"

%baz_interface = type { [11 x i8], i8*, [10 x i8]* }

@baz_instance = global %baz_interface zeroinitializer

define void @baz(%baz_interface* %0) {
entry:
%str = getelementptr inbounds %baz_interface, %baz_interface* %0, i32 0, i32 0
%ptr = getelementptr inbounds %baz_interface, %baz_interface* %0, i32 0, i32 1
%bytes = getelementptr inbounds %baz_interface, %baz_interface* %0, i32 0, i32 2
%1 = bitcast [11 x i8]* %str to i8*
store i8* %1, i8** %ptr, align 8
%2 = bitcast [11 x i8]* %str to [10 x i8]*
store [10 x i8]* %2, [10 x i8]** %bytes, align 8
ret void
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
source: src/codegen/tests/expression_tests.rs
assertion_line: 321
expression: result

---
; ModuleID = 'main'
source_filename = "main"

%baz_interface = type { i8*, i16 }

@baz_instance = global %baz_interface zeroinitializer

define void @baz(%baz_interface* %0) {
entry:
%ptr_x = getelementptr inbounds %baz_interface, %baz_interface* %0, i32 0, i32 0
%y = getelementptr inbounds %baz_interface, %baz_interface* %0, i32 0, i32 1
%1 = bitcast i16* %y to i8*
store i8* %1, i8** %ptr_x, align 8
ret void
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
---
source: src/codegen/tests/expression_tests.rs
assertion_line: 346
expression: result

---
; ModuleID = 'main'
source_filename = "main"

%baz_interface = type { i8*, i8, i8, i8 }

@baz_instance = global %baz_interface zeroinitializer

define void @baz(%baz_interface* %0) {
entry:
%ptr = getelementptr inbounds %baz_interface, %baz_interface* %0, i32 0, i32 0
%b = getelementptr inbounds %baz_interface, %baz_interface* %0, i32 0, i32 1
%si = getelementptr inbounds %baz_interface, %baz_interface* %0, i32 0, i32 2
%mb = getelementptr inbounds %baz_interface, %baz_interface* %0, i32 0, i32 3
store i8* %b, i8** %ptr, align 8
store i8* %si, i8** %ptr, align 8
store i8* %mb, i8** %ptr, align 8
ret void
}

28 changes: 28 additions & 0 deletions src/resolver/tests/resolve_expressions_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -698,6 +698,7 @@ fn pointer_expressions_resolve_types() {

assert_eq!(format!("{:?}", expected_types), format!("{:?}", type_names));
}

#[test]
fn array_expressions_resolve_types() {
let (unit, index) = index(
Expand Down Expand Up @@ -2921,6 +2922,33 @@ fn adress_of_is_annotated_correctly() {
}
}

#[test]
fn pointer_assignment_with_incompatible_types_hints_correctly() {
let (unit, mut index) = index(
"PROGRAM PRG
VAR
x : INT;
pt : POINTER TO BYTE;
END_VAR
pt := &x;
END_PROGRAM",
);

let annotations = annotate(&unit, &mut index);
let assignment = &unit.implementations[0].statements[0];

if let AstStatement::Assignment { left, right, .. } = assignment {
assert_type_and_hint!(&annotations, &index, left, "__PRG_pt", None);
assert_type_and_hint!(
&annotations,
&index,
right,
"POINTER_TO_INT",
Some("__PRG_pt")
);
}
}

#[test]
fn call_on_function_block_array() {
//GIVEN
Expand Down