Skip to content

Commit

Permalink
Compiler: fix incorrect casting between different union types (#10333)
Browse files Browse the repository at this point in the history
* Compiler: fix incorrect casting between different union types

* Add regression tests

Co-authored-by: Brian J. Cardiff <bcardiff@gmail.com>
  • Loading branch information
asterite and bcardiff committed Jan 30, 2021
1 parent 55a5e2b commit 3a910f1
Show file tree
Hide file tree
Showing 4 changed files with 38 additions and 3 deletions.
13 changes: 13 additions & 0 deletions spec/llvm-ir/assign-unions.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
class Foo
@v : Int32 | Bool | UInt8[12]

def initialize
@v = 1 || true

# X64: [[DEST:%.*]] = bitcast %"(Bool | Int32 | StaticArray(UInt8, 12))"* {{.*}} to %"(Bool | Int32)"*
# X64-NEXT: [[SRC:%.*]] = load %"(Bool | Int32)", %"(Bool | Int32)"* {{.*}}
# X64-NEXT: store %"(Bool | Int32)" {{.*}}[[SRC]], %"(Bool | Int32)"* {{.*}}[[DEST]]
end
end

Foo.new
5 changes: 5 additions & 0 deletions spec/llvm-ir/cast-unions.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
(1 || true).as(Int32 | Bool | UInt8[12])

# X64: [[DEST:%.*]] = bitcast %"(Bool | Int32 | StaticArray(UInt8, 12))"* {{.*}} to %"(Bool | Int32)"*
# X64-NEXT: [[SRC:%.*]] = load %"(Bool | Int32)", %"(Bool | Int32)"* {{.*}}
# X64-NEXT: store %"(Bool | Int32)" {{.*}}[[SRC]], %"(Bool | Int32)"* {{.*}}[[DEST]]
3 changes: 3 additions & 0 deletions spec/llvm-ir/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,8 @@ test memset.cr "--cross-compile --target x86_64-unknown-linux-gnu --prelude=empt
test memcpy.cr "--cross-compile --target x86_64-apple-darwin --prelude=empty" X64
test memcpy.cr "--cross-compile --target x86_64-unknown-linux-gnu --prelude=empty" X64

test cast-unions.cr "--cross-compile --target x86_64-apple-darwin --prelude=empty" X64
test assign-unions.cr "--cross-compile --target x86_64-apple-darwin --prelude=empty" X64

popd >/dev/null

20 changes: 17 additions & 3 deletions src/compiler/crystal/codegen/unions.cr
Original file line number Diff line number Diff line change
Expand Up @@ -103,16 +103,30 @@ module Crystal
end

def assign_distinct_union_types(target_pointer, target_type, value_type, value)
casted_value = cast_to_pointer value, target_type
store load(casted_value), target_pointer
# If we have:
# - target_pointer: Pointer(A | B | C)
# - target_type: A | B | C
# - value_type: A | B
# - value: Pointer(A | B)
#
# Then we:
# - load the value, we get A | B
# - cast the target pointer to Pointer(A | B)
# - store the A | B from the first pointer into the casted target pointer
casted_target_pointer = cast_to_pointer target_pointer, value_type
store load(value), casted_target_pointer
end

def downcast_distinct_union_types(value, to_type : MixedUnionType, from_type : MixedUnionType)
cast_to_pointer value, to_type
end

def upcast_distinct_union_types(value, to_type : MixedUnionType, from_type : MixedUnionType)
cast_to_pointer value, to_type
# Because we are casting a union to a bigger union, we need new space
# for that, hence the alloca. Then we simply reuse `assign_distinct_union_types`.
target_pointer = alloca llvm_type(to_type)
assign_distinct_union_types target_pointer, to_type, from_type, value
target_pointer
end

private def type_id_impl(value, type : MixedUnionType)
Expand Down

0 comments on commit 3a910f1

Please sign in to comment.