From 4c69ece84a25b968519cdb227ef4f2a0ac0b5b37 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 22 Feb 2019 11:47:49 -0300 Subject: [PATCH 1/3] Compiler: fix as casting when target doesn't have a type yet --- spec/compiler/semantic/cast_spec.cr | 11 +++++++++++ src/compiler/crystal/semantic/bindings.cr | 4 +++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/spec/compiler/semantic/cast_spec.cr b/spec/compiler/semantic/cast_spec.cr index 4885092f75fb..a75663fde296 100644 --- a/spec/compiler/semantic/cast_spec.cr +++ b/spec/compiler/semantic/cast_spec.cr @@ -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 diff --git a/src/compiler/crystal/semantic/bindings.cr b/src/compiler/crystal/semantic/bindings.cr index 0fc90681dffa..d80e6c0daaf8 100644 --- a/src/compiler/crystal/semantic/bindings.cr +++ b/src/compiler/crystal/semantic/bindings.cr @@ -300,8 +300,10 @@ module Crystal 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 From eccdb1a2230f8838ddd13970f26707d9d58282ad Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 22 Feb 2019 11:48:06 -0300 Subject: [PATCH 2/3] Compiler: fix as? casting when target doesn't have a type yet --- spec/compiler/semantic/nilable_cast_spec.cr | 11 +++++++++++ src/compiler/crystal/semantic/bindings.cr | 4 +++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/spec/compiler/semantic/nilable_cast_spec.cr b/spec/compiler/semantic/nilable_cast_spec.cr index 5099d2b26c14..c2ddac878cd0 100644 --- a/spec/compiler/semantic/nilable_cast_spec.cr +++ b/spec/compiler/semantic/nilable_cast_spec.cr @@ -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 diff --git a/src/compiler/crystal/semantic/bindings.cr b/src/compiler/crystal/semantic/bindings.cr index d80e6c0daaf8..21d37551540f 100644 --- a/src/compiler/crystal/semantic/bindings.cr +++ b/src/compiler/crystal/semantic/bindings.cr @@ -335,8 +335,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 From 67cf6ce3a6023587bf4addb6843d26468236fc1c Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Fri, 22 Feb 2019 11:48:30 -0300 Subject: [PATCH 3/3] Compiler: reactively compute a union's type, and check for missing types --- spec/compiler/semantic/union_spec.cr | 14 ++++++++++ src/compiler/crystal/semantic/bindings.cr | 26 +++++++++++++++++++ src/compiler/crystal/semantic/main_visitor.cr | 25 ++++-------------- 3 files changed, 45 insertions(+), 20 deletions(-) diff --git a/spec/compiler/semantic/union_spec.cr b/spec/compiler/semantic/union_spec.cr index 5035f7da4260..e9164e4f51fa 100644 --- a/spec/compiler/semantic/union_spec.cr +++ b/spec/compiler/semantic/union_spec.cr @@ -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 diff --git a/src/compiler/crystal/semantic/bindings.cr b/src/compiler/crystal/semantic/bindings.cr index 21d37551540f..f234b8f9dfb2 100644 --- a/src/compiler/crystal/semantic/bindings.cr +++ b/src/compiler/crystal/semantic/bindings.cr @@ -296,6 +296,32 @@ 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 diff --git a/src/compiler/crystal/semantic/main_visitor.cr b/src/compiler/crystal/semantic/main_visitor.cr index 075fb7f0a8fb..59e32b035c1b 100644 --- a/src/compiler/crystal/semantic/main_visitor.cr +++ b/src/compiler/crystal/semantic/main_visitor.cr @@ -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 @@ -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 @@ -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