Skip to content

Commit

Permalink
fixes #52 by raising an inference error when types arent compat
Browse files Browse the repository at this point in the history
note: I ran into an issue where deferred inference assumes that you are inferring as an expression,
which isn't always true for a rescue statement

so I put in a ivar to cache the initial expression value--I feel like this should be done in a more general way, but my main goal was to fix the bug.

I also noticed that there is a lot of variation in how the infer(typer, exp) methods determined whether they are cached or not. It' be nice to clean that up. I might do some of that next weekend.
  • Loading branch information
baroquebobcat committed Oct 24, 2011
1 parent 81be877 commit 677e4aa
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 13 deletions.
38 changes: 26 additions & 12 deletions lib/mirah/ast/flow.rb
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ def infer(typer, expression)
if expression
have_body_type = body.nil? || then_type
have_else_type = self.else.nil? || else_type

if have_body_type && have_else_type
if then_type && else_type
# both then and else inferred, ensure they're compatible
Expand Down Expand Up @@ -323,24 +324,37 @@ def initialize(parent, position, &block)

def infer(typer, expression)
unless resolved?
types = []
body_type = typer.infer(body, else_node.nil?) if body
else_type = typer.infer(else_node, true) if else_node
if else_node
types << else_type
# TODO: generalize this s.t.
# the problem with deferred inference
# assuming expression == true is dealt with
@expression = expression if @expression == nil
expression = @expression

primary_type = if else_node
typer.infer(body, false) if body
typer.infer(else_node, expression)
elsif body
types << body_type
typer.infer(body, expression)
end
types += clauses.map {|c| typer.infer(c, true)}

clause_types = clauses.map {|c| typer.infer(c, expression)}
types = []
types << primary_type if primary_type
types += clause_types
if types.any? {|t| t.nil?}
typer.defer(self)
else
# TODO check types for compatibility (maybe only if an expression)
resolved!
types.each do |type|
@inferred_type ||= type unless type.unreachable?
if !expression || clause_types.all?{ |t| primary_type.compatible? t}
resolved!
types.each do |type|
@inferred_type ||= type unless type.unreachable?
end
@inferred_type ||= primary_type
else
clause, clause_type = clauses.zip(clause_types).find{ |clause, t| !primary_type.compatible? t }

raise Mirah::Typer::InferenceError.new("rescue statement with incompatible result types #{primary_type} and #{clause_type}", clause)
end
@inferred_type ||= types[0]
end
end
@inferred_type
Expand Down
11 changes: 11 additions & 0 deletions test/core/test_typer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,17 @@ def test_if

assert_equal(typer.float_type, ast2.body[0].inferred_type)
end

def test_rescue_returning_different_type_raises_inference_error
ast = AST.parse("begin true; 1.0; rescue; ''; end").body[0]
typer = Typer::Simple.new("bar")

# raise when it is an expression
assert_raise(Typer::InferenceError) {ast.infer(typer, true)}
# don't raise when it was not an expression
assert_nothing_raised {ast.infer(typer, false)}
end


def test_class
ast = AST.parse("class Foo; def foo; 1; end; def baz; foo; end; end")
Expand Down
1 change: 0 additions & 1 deletion test/jvm/test_rescue.rb
Original file line number Diff line number Diff line change
Expand Up @@ -149,5 +149,4 @@ def test_empty_rescues
nil
EOF
end

end

0 comments on commit 677e4aa

Please sign in to comment.