Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: crystal-lang/crystal
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: a7aa93629e74
Choose a base ref
...
head repository: crystal-lang/crystal
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: dd1cd6095a93
Choose a head ref
  • 4 commits
  • 5 files changed
  • 1 contributor

Commits on Aug 5, 2016

  1. Compiler: reverted some bindings logic

    Ary Borenszweig committed Aug 5, 2016
    Copy the full SHA
    c441a36 View commit details
  2. Fixed bug related to if and multiple &&

    Ary Borenszweig committed Aug 5, 2016
    Copy the full SHA
    33c3eff View commit details
  3. Compiler: || now does type filtering. Fixes #2464

    Ary Borenszweig committed Aug 5, 2016
    Copy the full SHA
    88ae2f4 View commit details
  4. Compiler: some simplifications in filters

    Ary Borenszweig committed Aug 5, 2016
    Copy the full SHA
    dd1cd60 View commit details
99 changes: 99 additions & 0 deletions spec/compiler/semantic/if_spec.cr
Original file line number Diff line number Diff line change
@@ -101,4 +101,103 @@ describe "Semantic: if" do
!a.is_a?(Int32) || a + 2
)) { union_of bool, int32 }
end

it "restricts with || (#2464)" do
assert_type(%(
struct Int32
def foo
1
end
end
struct Char
def foo
1
end
end
a = 1 || "" || 'a'
if a.is_a?(Int32) || a.is_a?(Char)
a.foo
else
1
end
)) { int32 }
end

it "doesn't restrict with || on different vars" do
assert_error %(
struct Int32
def foo
1
end
end
struct Char
def bar
1
end
end
a = 1 || "" || 'a'
b = a
if a.is_a?(Int32) || b.is_a?(Char)
a.foo + b.bar
end
),
"undefined method"
end

it "doesn't restrict with || on var and non-restricting condition" do
assert_error %(
struct Int32
def foo
1
end
end
a = 1 || "" || 'a'
if a.is_a?(Int32) || 1 == 2
a.foo
end
),
"undefined method"
end

it "restricts with || but doesn't unify types to base class" do
assert_type(%(
class Foo
end
class Bar < Foo
def foo
1
end
end
class Baz < Foo
def foo
'a'
end
end
a = Bar.new.as(Foo)
if a.is_a?(Bar) || a.is_a?(Baz)
a.foo
else
nil
end
)) { union_of(nil_type, int32, char) }
end

it "restricts with && always falsey" do
assert_type(%(
x = 1
if (x.is_a?(String) && x.is_a?(String)) && x.is_a?(String)
true
else
2
end
)) { int32 }
end
end
69 changes: 37 additions & 32 deletions src/compiler/crystal/semantic/bindings.cr
Original file line number Diff line number Diff line change
@@ -104,7 +104,17 @@ module Crystal

node = yield dependencies

update(from)
if dependencies.size == 1
new_type = node.type?
else
new_type = Type.merge dependencies
end

return if @type.same? new_type
return unless new_type

set_type_from(map_type(new_type), from)
@dirty = true
propagate
end

@@ -182,7 +192,7 @@ module Crystal

new_type = Type.merge dependencies
if new_type
set_type_from(new_type, from)
set_type_from(map_type(new_type), from)
else
unless @type
@propagating_after_cleanup = false
@@ -201,7 +211,7 @@ module Crystal
end

if new_type
set_type_from(new_type, from)
set_type_from(map_type(new_type), from)
else
return unless @type

@@ -218,6 +228,10 @@ module Crystal
end
end

def map_type(type)
type
end

def find_owner_trace(owner)
owner_trace = [] of ASTNode
node = self
@@ -241,34 +255,27 @@ module Crystal
end

class Def
def update(from = nil)
if freeze_type.try &.nil_type?
# When we have Nil forced as a return type, NoReturn still
# wins, so we must account for this case.
# Otherwise we simply keep having the Nil type.
computed_type = Type.merge(dependencies)
if computed_type.try &.no_return?
super
end
def map_type(type)
# When we have Nil forced as a return type, NoReturn still
# wins, so we must account for this case.
# Otherwise we simply keep having the Nil type.
if freeze_type.try &.nil_type? && !type.no_return?
freeze_type
else
super
type
end
end
end

class PointerOf
def update(from = nil)
type = self.dependencies.first.type?
return unless type

def map_type(type)
old_type = self.type?
new_type = type.program.pointer_of(type)

new_type = type.try &.program.pointer_of(type)
if old_type && grew?(old_type, new_type)
raise "recursive pointerof expansion: #{old_type}, #{new_type}, ..."
end

self.type = new_type
new_type
end

def grew?(old_type, new_type)
@@ -294,24 +301,22 @@ module Crystal
class TypeOf
property? in_type_args = false

def update(from = nil)
type = Type.merge expressions
return unless type

type = type.metaclass unless @in_type_args

self.type = type
def map_type(type)
@in_type_args ? type : type.metaclass
end

def update(from = nil)
super
propagate
end
end

class ExceptionHandler
def update(from = nil)
def map_type(type)
if (ensure_type = @ensure.try &.type?).try &.is_a?(NoReturnType)
self.type = ensure_type
ensure_type
else
super
type
end
end
end
@@ -408,13 +413,13 @@ module Crystal
class ProcPointer
property! call : Call

def update(from = nil)
def map_type(type)
return nil unless call.type?

arg_types = call.args.map &.type
arg_types.push call.type

self.type = call.type.program.proc_of(arg_types)
call.type.program.proc_of(arg_types)
end
end

55 changes: 46 additions & 9 deletions src/compiler/crystal/semantic/cleanup_transformer.cr
Original file line number Diff line number Diff line change
@@ -166,9 +166,14 @@ module Crystal
exps << nop
end

node.expressions = exps
rebind_node node, exps.last
node
if simple = simplify_exps(exps)
rebind_node node, simple
simple
else
node.expressions = exps
rebind_node node, exps.last
node
end
end

def flatten_collect(exp, exps)
@@ -577,14 +582,46 @@ module Crystal
exp_nodes = [node.cond] of ASTNode
exp_nodes << branch

exp = Expressions.new(exp_nodes)
if branch
exp.bind_to branch
rebind_node node, branch
if simple = simplify_exps(exp_nodes)
rebind_node node, simple
simple
else
exp = Expressions.new(exp_nodes)
if branch
exp.bind_to branch
rebind_node node, branch
else
exp.bind_to @program.nil_var
end

exp
end
end

# Check if it's something like:
#
# ```
# __temp = value
# __temp
# ```
#
# In that case we simply use `value` as the result.
def simplify_exps(exps)
return unless exps.size == 2

first, second = exps

if first.is_a?(Assign) && (target = first.target).is_a?(Var) && second.is_a?(Var) &&
target.name == second.name && target.name.starts_with?("__")
value = first.value
if (value.is_a?(Expressions)) && (simple = simplify_exps(value.expressions))
simple
else
value
end
else
exp.bind_to @program.nil_var
nil
end
exp
end

def transform(node : IsA)
Loading