Skip to content

Commit 4fed15e

Browse files
committed
Skip bot type in typecheck to fix false errors on return/next
When a method body ends with a `return` (or a branch contains one), the implicit return value of the body includes `bot` (bottom type). Because `bot` is a subtype of all types, typecheck should not reject it. Previously each typecheck implementation iterated over every type in the vertex including `bot`, which caused spurious diagnostics like "expected: bool; actual: bot". Add `next if ty.is_a?(Type::Bot)` to all leaf typecheck methods in sig_type.rb so that `bot` is uniformly skipped during type checking. Fixes #377
1 parent 5e8dac2 commit 4fed15e

4 files changed

Lines changed: 136 additions & 0 deletions

File tree

lib/typeprof/core/ast/sig_type.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ def self.typecheck_for_module(genv, changes, f_mod, f_args, a_vtx, subst)
44
changes.add_edge(genv, a_vtx, changes.target)
55
found_any = false
66
a_vtx.each_type do |ty|
7+
next if ty.is_a?(Type::Bot)
78
found_any = true
89
ty = ty.base_type(genv)
910
while ty
@@ -199,6 +200,7 @@ def contravariant_vertex0(genv, changes, vtx, subst)
199200
def typecheck(genv, changes, vtx, subst)
200201
changes.add_edge(genv, vtx, changes.target)
201202
vtx.each_type do |ty|
203+
next if ty.is_a?(Type::Bot)
202204
return false unless ty == genv.true_type || ty == genv.false_type
203205
end
204206
true
@@ -221,6 +223,7 @@ def contravariant_vertex0(genv, changes, vtx, subst)
221223
def typecheck(genv, changes, vtx, subst)
222224
changes.add_edge(genv, vtx, changes.target)
223225
vtx.each_type do |ty|
226+
next if ty.is_a?(Type::Bot)
224227
return false unless ty == genv.nil_type
225228
end
226229
true
@@ -597,6 +600,7 @@ def typecheck(genv, changes, vtx, subst)
597600
changes.add_edge(genv, vtx, changes.target)
598601
found_any = false
599602
vtx.each_type do |ty|
603+
next if ty.is_a?(Type::Bot)
600604
found_any = true
601605
case ty
602606
when Type::Singleton
@@ -735,6 +739,7 @@ def typecheck(genv, changes, vtx, subst)
735739
changes.add_edge(genv, vtx, changes.target)
736740
found_any = false
737741
vtx.each_type do |ty|
742+
next if ty.is_a?(Type::Bot)
738743
found_any = true
739744
case ty
740745
when Type::Array
@@ -807,6 +812,7 @@ def typecheck(genv, changes, vtx, subst)
807812
changes.add_edge(genv, vtx, changes.target)
808813
found_any = false
809814
vtx.each_type do |ty|
815+
next if ty.is_a?(Type::Bot)
810816
found_any = true
811817
case ty
812818
when Type::Hash
@@ -925,6 +931,7 @@ def typecheck(genv, changes, vtx, subst)
925931
changes.add_edge(genv, vtx, changes.target)
926932
found_any = false
927933
vtx.each_type do |ty|
934+
next if ty.is_a?(Type::Bot)
928935
found_any = true
929936
case ty
930937
when Type::Symbol
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
## update
2+
# Instance type (typecheck_for_module): pure bot
3+
#: { () -> String } -> void
4+
def yield_instance
5+
yield
6+
end
7+
8+
yield_instance do
9+
next "hello"
10+
end
11+
12+
# Bool type (SigTyBaseBoolNode): pure bot
13+
#: { () -> bool } -> void
14+
def yield_bool
15+
yield
16+
end
17+
18+
yield_bool do
19+
next false
20+
end
21+
22+
# Nil type (SigTyBaseNilNode): pure bot
23+
#: { () -> nil } -> void
24+
def yield_nil
25+
yield
26+
end
27+
28+
yield_nil do
29+
next nil
30+
end
31+
32+
## diagnostics
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
## update
2+
# Instance type (typecheck_for_module): pure bot
3+
#: -> String
4+
def test_instance
5+
return "hello"
6+
end
7+
8+
# Instance type (typecheck_for_module): mixed bot
9+
#: (bool) -> String
10+
def test_instance_mixed(a)
11+
if a
12+
return "hello"
13+
end
14+
"world"
15+
end
16+
17+
# Bool type (SigTyBaseBoolNode): pure bot
18+
#: -> bool
19+
def test_bool
20+
return false
21+
end
22+
23+
# Bool type (SigTyBaseBoolNode): mixed bot
24+
#: (bool) -> bool
25+
def test_bool_mixed(a)
26+
if a
27+
return true
28+
end
29+
false
30+
end
31+
32+
# Nil type (SigTyBaseNilNode): pure bot
33+
#: -> nil
34+
def test_nil
35+
return nil
36+
end
37+
38+
# Nil type (SigTyBaseNilNode): mixed bot
39+
#: (bool) -> nil
40+
def test_nil_mixed(a)
41+
if a
42+
return nil
43+
end
44+
nil
45+
end
46+
47+
# Tuple type (SigTyTupleNode): pure bot
48+
#: -> [Integer, String]
49+
def test_tuple
50+
return [1, "hello"]
51+
end
52+
53+
# Tuple type (SigTyTupleNode): mixed bot
54+
#: (bool) -> [Integer, String]
55+
def test_tuple_mixed(a)
56+
if a
57+
return [1, "hello"]
58+
end
59+
[2, "world"]
60+
end
61+
62+
# Literal symbol type (SigTyLiteralNode): pure bot
63+
#: -> :foo
64+
def test_symbol
65+
return :foo
66+
end
67+
68+
# Literal symbol type (SigTyLiteralNode): mixed bot
69+
#: (bool) -> :foo
70+
def test_symbol_mixed(a)
71+
if a
72+
return :foo
73+
end
74+
:foo
75+
end
76+
77+
## diagnostics
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
## update
2+
class Foo
3+
end
4+
5+
# Singleton type (SigTySingletonNode): pure bot
6+
#: -> singleton(Foo)
7+
def test_singleton
8+
return Foo
9+
end
10+
11+
# Singleton type (SigTySingletonNode): mixed bot
12+
#: (bool) -> singleton(Foo)
13+
def test_singleton_mixed(a)
14+
if a
15+
return Foo
16+
end
17+
Foo
18+
end
19+
20+
## diagnostics

0 commit comments

Comments
 (0)