Skip to content

Commit

Permalink
Showing 4 changed files with 51 additions and 38 deletions.
16 changes: 16 additions & 0 deletions spec/compiler/codegen/cast_spec.cr
Original file line number Diff line number Diff line change
@@ -255,4 +255,20 @@ describe "Code gen: cast" do
123
)).to_i.should eq(123)
end

it "can cast from Void* to virtual type (#3014)" do
run(%(
abstract class A
abstract def hi
end
class B < A
def hi
42
end
end
B.new.as(Void*).as(A).hi
)).to_i.should eq(42)
end
end
12 changes: 12 additions & 0 deletions spec/compiler/type_inference/cast_spec.cr
Original file line number Diff line number Diff line change
@@ -278,4 +278,16 @@ describe "Type inference: cast" do
),
"can't cast to Class yet"
end

it "can cast from Void* to virtual type (#3014)" do
assert_type(%(
abstract class A
end
class B < A
end
B.new.as(Void*).as(A)
)) { types["A"].virtual_type! }
end
end
2 changes: 1 addition & 1 deletion src/compiler/crystal/codegen/codegen.cr
Original file line number Diff line number Diff line change
@@ -1085,7 +1085,7 @@ module Crystal
last_value = @last

obj_type = node.obj.type
to_type = node.to.type
to_type = node.to.type.virtual_type

if to_type.pointer?
if obj_type.nil_type?
59 changes: 22 additions & 37 deletions src/compiler/crystal/semantic/ast.cr
Original file line number Diff line number Diff line change
@@ -567,19 +567,10 @@ module Crystal
property? upcast = false

def update(from = nil)
to_type = to.type

obj_type = obj.type?
to_type = to.type

# If we don't know what type we are casting from, leave it as the to_type
unless obj_type
self.type = to_type.virtual_type
return
end

if obj_type.pointer? || to_type.pointer?
self.type = to_type
else
if obj_type && !(obj_type.pointer? || to_type.pointer?)
filtered_type = obj_type.filter_by(to_type)

# If the filtered type didn't change it means that an
@@ -588,16 +579,16 @@ module Crystal
# 1 as Int32 | Float64
# Bar.new as Foo # where Bar < Foo
if obj_type == filtered_type && obj_type != to_type && !to_type.is_a?(GenericClassType)
filtered_type = to_type.virtual_type
filtered_type = to_type
@upcast = true
end
end

# If we don't have a matching type, leave it as the to_type:
# later (in after type inference) we will check again.
filtered_type ||= to_type.virtual_type
# If we don't have a matching type, leave it as the to_type:
# later (in after type inference) we will check again.
filtered_type ||= to_type

self.type = filtered_type
end
self.type = filtered_type.virtual_type
end
end

@@ -606,33 +597,27 @@ module Crystal
getter! non_nilable_type : Type

def update(from = nil)
to_type = to.type

obj_type = obj.type?
to_type = to.type

# If we don't know what type we are casting from, leave it as nilable to_type
unless obj_type
@non_nilable_type = non_nilable_type = to_type.virtual_type

self.type = to_type.program.nilable(non_nilable_type)
return
end

filtered_type = obj_type.filter_by(to_type)
if obj_type
filtered_type = obj_type.filter_by(to_type)

# If the filtered type didn't change it means that an
# upcast is being made, for example:
#
# 1 as Int32 | Float64
# Bar.new as Foo # where Bar < Foo
if obj_type == filtered_type && obj_type != to_type && !to_type.is_a?(GenericClassType)
filtered_type = to_type.virtual_type
@upcast = true
# If the filtered type didn't change it means that an
# upcast is being made, for example:
#
# 1 as Int32 | Float64
# Bar.new as Foo # where Bar < Foo
if obj_type == filtered_type && obj_type != to_type && !to_type.is_a?(GenericClassType)
filtered_type = to_type.virtual_type
@upcast = true
end
end

# If we don't have a matching type, leave it as the to_type:
# later (in after type inference) we will check again.
filtered_type ||= to_type.virtual_type
filtered_type ||= to_type
filtered_type = filtered_type.virtual_type

@non_nilable_type = filtered_type

0 comments on commit d777ce2

Please sign in to comment.