Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix unreachability test #842

Merged
merged 4 commits into from
Jul 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 23 additions & 19 deletions lib/steep/type_construction.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1398,8 +1398,11 @@ def synthesize(node, hint: nil, condition: false)
when :true, :false
ty = node.type == :true ? AST::Types::Literal.new(value: true) : AST::Types::Literal.new(value: false)

if hint && check_relation(sub_type: ty, super_type: hint).success? && !hint.is_a?(AST::Types::Any) && !hint.is_a?(AST::Types::Top)
case
when hint && check_relation(sub_type: ty, super_type: hint).success? && !hint.is_a?(AST::Types::Any) && !hint.is_a?(AST::Types::Top)
add_typing(node, type: hint)
when condition
add_typing(node, type: ty)
else
add_typing(node, type: AST::Types::Boolean.new)
end
Expand Down Expand Up @@ -2016,9 +2019,6 @@ def synthesize(node, hint: nil, condition: false)
branch_results = [] #: Array[Pair]

condition_constr = constr
clause_constr = constr

next_branch_reachable = true

whens.each do |when_clause|
when_clause_constr = condition_constr
Expand All @@ -2029,7 +2029,6 @@ def synthesize(node, hint: nil, condition: false)
*tests, body = when_clause.children

branch_reachable = false
false_branch_reachable = false

tests.each do |test|
test_type, condition_constr = condition_constr.synthesize(test, condition: true)
Expand All @@ -2040,12 +2039,9 @@ def synthesize(node, hint: nil, condition: false)
condition_constr = condition_constr.update_type_env { falsy_env }
body_envs << truthy_env

branch_reachable ||= next_branch_reachable && !truthy.unreachable
false_branch_reachable ||= !falsy.unreachable
branch_reachable ||= !truthy.unreachable
end

next_branch_reachable &&= false_branch_reachable

if body
branch_results <<
when_clause_constr
Expand Down Expand Up @@ -4476,19 +4472,27 @@ def union_type(*types)
end

def union_type_unify(*types)
types.inject do |type1, type2|
next type1 if type1.is_a?(AST::Types::Any)
next type2 if type2.is_a?(AST::Types::Any)
types = types.reject {|t| t.is_a?(AST::Types::Bot) }

unless no_subtyping?(sub_type: type1, super_type: type2)
next type2
end
if types.empty?
AST::Types::Bot.new
else
types.inject do |type1, type2|
next type2 if type1.is_a?(AST::Types::Any)
next type1 if type2.is_a?(AST::Types::Any)

unless no_subtyping?(sub_type: type2, super_type: type1)
next type1
end
unless no_subtyping?(sub_type: type1, super_type: type2)
# type1 <: type2
next type2
end

union_type(type1, type2)
unless no_subtyping?(sub_type: type2, super_type: type1)
# type2 <: type1
next type1
end

union_type(type1, type2)
end
end
end

Expand Down
4 changes: 4 additions & 0 deletions lib/steep/type_inference/logic_type_interpreter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,10 @@ def literal_var_type_case_select(value_node, arg_type)
end

[truthy_types, falsy_types]
when AST::Types::Boolean
[[arg_type], [arg_type]]
when AST::Types::Top, AST::Types::Any
[[arg_type], [arg_type]]
else
types = [arg_type]

Expand Down
146 changes: 136 additions & 10 deletions test/type_check_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -428,16 +428,6 @@ def test_case_unreachable_2
severity: ERROR
message: Type `::String` does not have method `is_a_string`
code: Ruby::NoMethod
- range:
start:
line: 7
character: 0
end:
line: 7
character: 19
severity: ERROR
message: The branch is unreachable
code: Ruby::UnreachableBranch
YAML
)
end
Expand Down Expand Up @@ -887,6 +877,142 @@ def test_type_case__returns_nil_untyped_union
a.is_untyped
RUBY
},
expectations: <<~YAML
---
- file: a.rb
diagnostics:
- range:
start:
line: 13
character: 2
end:
line: 13
character: 12
severity: ERROR
message: Type `nil` does not have method `is_untyped`
code: Ruby::NoMethod
YAML
)
end

def test_case_when__no_subject__reachability
run_type_check_test(
signatures: {
},
code: {
"a.rb" => <<~RUBY
case
when false
:a
when nil
:b
when "".is_a?(NilClass)
:c
end
RUBY
},
expectations: <<~YAML
---
- file: a.rb
diagnostics:
- range:
start:
line: 3
character: 2
end:
line: 3
character: 4
severity: ERROR
message: The branch is unreachable
code: Ruby::UnreachableBranch
- range:
start:
line: 5
character: 2
end:
line: 5
character: 4
severity: ERROR
message: The branch is unreachable
code: Ruby::UnreachableBranch
- range:
start:
line: 7
character: 2
end:
line: 7
character: 4
severity: ERROR
message: The branch is unreachable
code: Ruby::UnreachableBranch
YAML
)
end

def test_case_when__no_subject__reachability_no_continue
run_type_check_test(
signatures: {
},
code: {
"a.rb" => <<~RUBY
case
when true
:a
when 1
:b
else
:c
end
RUBY
},
expectations: <<~YAML
---
- file: a.rb
diagnostics: []
YAML
)
end

def test_case_when__untyped_value
run_type_check_test(
signatures: {
},
code: {
"a.rb" => <<~RUBY
foo = true #: untyped

case foo
when nil
1
when true
2
end
RUBY
},
expectations: <<~YAML
---
- file: a.rb
diagnostics: []
YAML
)
end

def test_case_when__bool_value
run_type_check_test(
signatures: {
},
code: {
"a.rb" => <<~RUBY
foo = true #: bool

case foo
when false
1
when true
2
end
RUBY
},
expectations: <<~YAML
---
- file: a.rb
Expand Down
4 changes: 2 additions & 2 deletions test/type_construction_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3804,8 +3804,8 @@ def test_and_or

assert_empty typing.errors

assert_equal parse_type("bool"), pair.context.type_env[:a]
assert_equal parse_type("bool"), pair.context.type_env[:b]
assert_equal parse_type("false"), pair.context.type_env[:a]
assert_equal parse_type("true"), pair.context.type_env[:b]
end
end
end
Expand Down