Skip to content

Commit

Permalink
Compiler: make sure to hold the invariant to never shrink types. Fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
asterite committed Aug 7, 2016
1 parent a3df4d8 commit 671ecb3
Show file tree
Hide file tree
Showing 21 changed files with 173 additions and 284 deletions.
37 changes: 37 additions & 0 deletions spec/compiler/codegen/if_spec.cr
Expand Up @@ -230,4 +230,41 @@ describe "Code gen: if" do
1
)).to_i.should eq(1)
end

it "restricts with || always falsey" do
run(%(
t = 1
if t.is_a?(String) || t.is_a?(String)
t
else
2
end
)).to_i.should eq(2)
end

it "considers or truthy/falsey right" do
run(%(
t = 1 || 'a'
if t.is_a?(Char) || t.is_a?(Char)
1
else
2
end
)).to_i.should eq(2)
end

it "codegens #3104" do
codegen(%(
def foo
yield
end
x = typeof(nil && 1)
foo do
if x
end
end
x
))
end
end
4 changes: 2 additions & 2 deletions spec/compiler/codegen/macro_spec.cr
Expand Up @@ -1024,8 +1024,8 @@ describe "Code gen: macro" do
nil
end
me
)).to_i.should eq(123)
me || 0
), inject_primitives: false).to_i.should eq(123)
end

it "passes #826" do
Expand Down
15 changes: 15 additions & 0 deletions spec/compiler/codegen/nilable_cast_spec.cr
Expand Up @@ -70,4 +70,19 @@ describe "Code gen: nilable cast" do
x ? 10 : 20
)).to_i.should eq(20)
end

it "codegens with NoReturn" do
codegen(%(
lib LibC
fun exit : NoReturn
end
def foo
LibC.exit.as?(Int32)
10
end
foo
))
end
end
3 changes: 2 additions & 1 deletion spec/compiler/codegen/proc_spec.cr
Expand Up @@ -679,12 +679,13 @@ describe "Code gen: proc" do
it "doesn't crash on #2196" do
run(%(
x = 42
if x.is_a?(Int32)
z = if x.is_a?(Int32)
x
else
y = x
->{ y }
end
z.is_a?(Int32) ? z : 0
)).to_i.should eq(42)
end

Expand Down
50 changes: 0 additions & 50 deletions spec/compiler/semantic/cleanup_spec.cr
@@ -1,56 +1,6 @@
require "../../spec_helper"

describe "cleanup" do
it "keeps else of if with nil type" do
assert_after_cleanup "a = nil; if a; 1; else; 2; end",
"a = nil\na\n2"
end

it "keeps then of if with true literal" do
assert_after_cleanup "if true; 1; else; 2; end",
"1"
end

it "keeps else of if with false literal" do
assert_after_cleanup "if false; 1; else; 2; end",
"2"
end

it "keeps then of if with true assignment" do
assert_after_cleanup "if a = true; 1; else; 2; end",
"a = true\n1"
end

it "keeps else of if with false assignment" do
assert_after_cleanup "if a = false; 1; else; 2; end",
"a = false\n2"
end

it "keeps else of if with is_a? that can never hold" do
assert_after_cleanup "a = 1; if a.is_a?(Bool); 2; else 3; end",
"a = 1\n3"
end

it "keeps else of if with responds_to? that can never hold" do
assert_after_cleanup "a = 1; if a.responds_to?(:foo); 2; else 3; end",
"a = 1\n3"
end

it "keeps then of if with is_a? that is always true" do
assert_after_cleanup "a = 1; if a.is_a?(Int32); 2; end",
"a = 1\n2"
end

it "keeps then of if with is_a? that is always true" do
assert_after_cleanup "a = 1 || 1.5; if a.is_a?(Number); 2; end",
"a = if __temp_1 = 1\n __temp_1\nelse\n 1.5\nend\n2"
end

it "keeps then of if with responds_to? that is always true" do
assert_after_cleanup "struct Int; def +(other); end; end; a = 1; if a.responds_to?(:\"+\"); 2; end",
"struct Int\n def +(other)\n end\nend\na = 1\n2"
end

it "errors if assigning var to itself" do
assert_error "a = 1; a = a", "expression has no effect"
end
Expand Down
2 changes: 1 addition & 1 deletion spec/compiler/semantic/doc_spec.cr
Expand Up @@ -286,7 +286,7 @@ describe "Semantic: doc" do
# Hello
foo
), wants_doc: true
), wants_doc: true, inject_primitives: false
program = result.program
foo = program.types["Foo"]
foo.doc.should eq("Hello")
Expand Down
8 changes: 4 additions & 4 deletions spec/compiler/semantic/exception_spec.cr
Expand Up @@ -215,9 +215,9 @@ describe "Semantic: exception" do
p n
end
n
)) { no_return }
)) { int32 }
mod = result.program
eh = result.node.as(Expressions).expressions[-1]
eh = result.node.as(Expressions).expressions[-2]
call_p_n = eh.as(ExceptionHandler).ensure.not_nil!.as(Call)
call_p_n.args.first.type.should eq(mod.nilable(mod.int32))
end
Expand All @@ -233,9 +233,9 @@ describe "Semantic: exception" do
p n
end
n
)) { no_return }
)) { int32 }
mod = result.program
eh = result.node.as(Expressions).expressions[-1]
eh = result.node.as(Expressions).expressions[-2]
call_p_n = eh.as(ExceptionHandler).ensure.not_nil!.as(Call)
call_p_n.args.first.type.should eq(mod.nilable(mod.int32))
end
Expand Down
4 changes: 2 additions & 2 deletions spec/compiler/semantic/if_spec.cr
Expand Up @@ -78,7 +78,7 @@ describe "Semantic: if" do
n
end
n
)) { int32 }
)) { union_of(int32, float64) }
end

it "restricts the type of the right hand side of an || when using is_a? (#1728)" do
Expand Down Expand Up @@ -198,6 +198,6 @@ describe "Semantic: if" do
else
2
end
)) { int32 }
)) { union_of(bool, int32) }
end
end
2 changes: 1 addition & 1 deletion spec/compiler/semantic/initialize_spec.cr
Expand Up @@ -465,7 +465,7 @@ describe "Semantic: initialize" do
foo = Foo.new
foo.x
)) { no_return }
)) { int32 }
end

it "doesn't type instance var as nilable if not used in method call" do
Expand Down
2 changes: 1 addition & 1 deletion spec/compiler/semantic/macro_spec.cr
Expand Up @@ -466,7 +466,7 @@ describe "Semantic: macro" do
end
me
)) { symbol }
)) { nilable symbol }
end

it "errors if declares macro inside if" do
Expand Down
2 changes: 1 addition & 1 deletion spec/compiler/semantic/nilable_cast_spec.cr
Expand Up @@ -4,7 +4,7 @@ describe "Semantic: nilable cast" do
it "types as?" do
assert_type(%(
1.as?(Float64)
)) { nil_type }
)) { nilable float64 }
end

it "types as? with union" do
Expand Down
2 changes: 1 addition & 1 deletion spec/compiler/semantic/no_return_spec.cr
Expand Up @@ -317,6 +317,6 @@ describe "Semantic: NoReturn" do
end
baz
)) { no_return }
)) { int32 }
end
end
8 changes: 4 additions & 4 deletions spec/compiler/semantic/return_spec.cr
Expand Up @@ -6,7 +6,7 @@ describe "Semantic: return" do
end

it "infers return type with many returns (1)" do
assert_type("def foo; if true; return 1; end; 'a'; end; foo") { int32 }
assert_type("def foo; if true; return 1; end; 'a'; end; foo") { union_of(int32, char) }
end

it "infers return type with many returns (2)" do
Expand All @@ -26,7 +26,7 @@ describe "Semantic: return" do
end
bar
)) { nil_type }
)) { nilable int32 }
end

it "can use type var as return type (#1226)" do
Expand Down Expand Up @@ -130,7 +130,7 @@ describe "Semantic: return" do
foo = Foo.new
foo.bar
)) { no_return }
)) { int32 }
end

it "types bug (#1823)" do
Expand Down Expand Up @@ -166,7 +166,7 @@ describe "Semantic: return" do
end
test
)) { types["Bar"] }
)) { nilable types["Bar"] }
end

it "can use free var in return type (#2492)" do
Expand Down
4 changes: 2 additions & 2 deletions spec/spec_helper.cr
Expand Up @@ -150,8 +150,8 @@ def assert_type(str, flags = nil, inject_primitives = true)
result
end

def semantic(code : String, wants_doc = false)
code = inject_primitives(code)
def semantic(code : String, wants_doc = false, inject_primitives = true)
code = inject_primitives(code) if inject_primitives
semantic parse(code, wants_doc: wants_doc), wants_doc: wants_doc
end

Expand Down
6 changes: 6 additions & 0 deletions src/compiler/crystal/codegen/cast.cr
Expand Up @@ -70,6 +70,8 @@ require "./codegen"

class Crystal::CodeGenVisitor
def assign(target_pointer, target_type, value_type, value)
return if @builder.end

target_type = target_type.remove_indirection
value_type = value_type.remove_indirection

Expand Down Expand Up @@ -271,6 +273,8 @@ class Crystal::CodeGenVisitor
end

def downcast(value, to_type, from_type : Type, already_loaded)
return llvm_nil if @builder.end

from_type = from_type.remove_indirection
to_type = to_type.remove_indirection

Expand Down Expand Up @@ -397,6 +401,8 @@ class Crystal::CodeGenVisitor
end

def upcast(value, to_type, from_type)
return llvm_nil if @builder.end

from_type = from_type.remove_indirection
to_type = to_type.remove_indirection

Expand Down
30 changes: 29 additions & 1 deletion src/compiler/crystal/codegen/codegen.cr
Expand Up @@ -262,6 +262,10 @@ module Crystal
node.accept visitor
end

def visit_any(node)
!@builder.end
end

def type
context.type.not_nil!
end
Expand Down Expand Up @@ -564,6 +568,8 @@ module Crystal
end

def codegen_return(type : Type)
return if @builder.end

method_type = context.return_type.not_nil!
if method_type.void?
ret
Expand Down Expand Up @@ -659,6 +665,24 @@ module Crystal
end

def visit(node : If)
if node.truthy?
node.cond.accept self
node.then.accept self
if (node_type = node.type?) && (then_type = node.then.type?)
@last = upcast(@last, node_type, then_type)
end
return false
end

if node.falsey?
node.cond.accept self
node.else.accept self
if (node_type = node.type?) && (else_type = node.else.type?)
@last = upcast(@last, node_type, else_type)
end
return false
end

then_block, else_block = new_blocks "then", "else"

request_value do
Expand Down Expand Up @@ -1114,7 +1138,11 @@ module Crystal
resulting_type = node.type
non_nilable_type = node.non_nilable_type

if node.upcast?
filtered_type = obj_type.filter_by(to_type)

if !filtered_type
@last = upcast llvm_nil, resulting_type, @program.nil
elsif node.upcast?
@last = upcast last_value, non_nilable_type, obj_type
@last = upcast @last, resulting_type, non_nilable_type
elsif obj_type != non_nilable_type
Expand Down
4 changes: 4 additions & 0 deletions src/compiler/crystal/codegen/phi.cr
Expand Up @@ -51,6 +51,10 @@ class Crystal::CodeGenVisitor
return
end

if @codegen.builder.end
return
end

if @needs_value
unless @node_type.try(&.void?) || @node_type.try(&.nil_type?)
value = @codegen.upcast value, @node_type.not_nil!, type
Expand Down
6 changes: 6 additions & 0 deletions src/compiler/crystal/semantic/ast.cr
Expand Up @@ -338,6 +338,12 @@ module Crystal
# This is set to `true` for an `If` that was created from an `||` expression.
property? or = false

# This is set to `true` when the compiler is sure that the condition is truthy
property? truthy = false

# This is set to `true` when the compiler is sure that the condition is falsey
property? falsey = false

def clone_without_location
a_if = previous_def
a_if.and = and?
Expand Down

0 comments on commit 671ecb3

Please sign in to comment.