Skip to content

Commit

Permalink
Merge pull request #7475 from asterite/bug/typeof-and-union
Browse files Browse the repository at this point in the history
Compiler: fix a couple of issues with `as`, `as?` and unions
  • Loading branch information
asterite authored Feb 22, 2019
2 parents 2ae0c29 + 67cf6ce commit 202c7fd
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 22 deletions.
11 changes: 11 additions & 0 deletions spec/compiler/semantic/cast_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -308,4 +308,15 @@ describe "Semantic: cast" do
1.as(Int)
)) { int32 }
end

it "doesn't crash with typeof no-type (#7441)" do
assert_type(%(
a = 1
if a.is_a?(Char)
1.as(typeof(a))
else
""
end
)) { string }
end
end
11 changes: 11 additions & 0 deletions spec/compiler/semantic/nilable_cast_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,15 @@ describe "Semantic: nilable cast" do
Bar.new.as?(Foo)
)) { nilable types["Foo"].virtual_type! }
end

it "doesn't crash with typeof no-type (#7441)" do
assert_type(%(
a = 1
if a.is_a?(Char)
1.as?(typeof(a))
else
""
end
)) { string }
end
end
14 changes: 14 additions & 0 deletions spec/compiler/semantic/union_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -165,4 +165,18 @@ describe "Semantic: union" do
{foo(1), foo("hi")}
)) { tuple_of([int32, string]) }
end

it "doesn't crash with union of no-types (#5805)" do
assert_type(%(
class Gen(T)
end
foo = 42
if foo.is_a?(String)
Gen(typeof(foo) | Int32)
else
'a'
end
)) { union_of char, generic_class("Gen", int32).metaclass }
end
end
34 changes: 32 additions & 2 deletions src/compiler/crystal/semantic/bindings.cr
Original file line number Diff line number Diff line change
Expand Up @@ -296,12 +296,40 @@ module Crystal
end
end

class Union
property? inside_is_a = false

def update(from = nil)
computed_types = types.compact_map do |subtype|
instance_type = subtype.type?
next unless instance_type

unless instance_type.allowed_in_generics?
subtype.raise "can't use #{instance_type} in unions yet, use a more specific type"
end
instance_type.virtual_type
end

return if computed_types.empty?

program = computed_types.first.program

if inside_is_a?
self.type = program.type_merge_union_of(computed_types)
else
self.type = program.type_merge(computed_types)
end
end
end

class Cast
property? upcast = false

def update(from = nil)
to_type = to.type?
return unless to_type

obj_type = obj.type?
to_type = to.type

@upcast = false

Expand Down Expand Up @@ -333,8 +361,10 @@ module Crystal
getter! non_nilable_type : Type

def update(from = nil)
to_type = to.type?
return unless to_type

obj_type = obj.type?
to_type = to.type

@upcast = false

Expand Down
25 changes: 5 additions & 20 deletions src/compiler/crystal/semantic/main_visitor.cr
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ module Crystal
))
@found_self_in_initialize_call = nil
@used_ivars_in_calls_in_initialize = nil
@in_is_a = false
@inside_is_a = false

# We initialize meta_vars from vars given in the constructor.
# We store those meta vars either in the typed def or in the program
Expand Down Expand Up @@ -323,23 +323,8 @@ module Crystal
node.types.each &.accept self
@in_type_args -= 1

old_in_is_a, @in_is_a = @in_is_a, false

types = node.types.map do |subtype|
instance_type = subtype.type
unless instance_type.allowed_in_generics?
subtype.raise "can't use #{instance_type} in unions yet, use a more specific type"
end
instance_type.virtual_type
end

@in_is_a = old_in_is_a

if @in_is_a
node.type = @program.type_merge_union_of(types)
else
node.type = @program.type_merge(types)
end
node.inside_is_a = @inside_is_a
node.update

false
end
Expand Down Expand Up @@ -1770,9 +1755,9 @@ module Crystal
node.obj.accept self

@in_type_args += 1
@in_is_a = true
@inside_is_a = true
node.const.accept self
@in_is_a = false
@inside_is_a = false
@in_type_args -= 1

node.type = program.bool
Expand Down

0 comments on commit 202c7fd

Please sign in to comment.