Skip to content
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ static ?= ## Enable static linking
O := .build
SOURCES := $(shell find src -name '*.cr')
SPEC_SOURCES := $(shell find spec -name '*.cr')
override FLAGS += $(if $(release),--release )$(if $(stats),--stats )$(if $(progress),--progress )$(if $(threads),--threads $(threads) )$(if $(debug),-d )$(if $(static),--static )$(if $(LDFLAGS),--link-flags="$(LDFLAGS)" )$(if $(target),--cross-compile --target $(target) )
override FLAGS += -D preview_multi_assign $(if $(release),--release )$(if $(stats),--stats )$(if $(progress),--progress )$(if $(threads),--threads $(threads) )$(if $(debug),-d )$(if $(static),--static )$(if $(LDFLAGS),--link-flags="$(LDFLAGS)" )$(if $(target),--cross-compile --target $(target) )
SPEC_WARNINGS_OFF := --exclude-warnings spec/std --exclude-warnings spec/compiler --exclude-warnings spec/primitives
SPEC_FLAGS := $(if $(verbose),-v )$(if $(junit_output),--junit_output $(junit_output) )
CRYSTAL_CONFIG_LIBRARY_PATH := '$$ORIGIN/../lib/crystal'
Expand Down
32 changes: 32 additions & 0 deletions spec/compiler/codegen/multi_assign_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,38 @@ describe "Code gen: multi assign" do
CR
end

context "without strict_multi_assign" do
it "doesn't raise if value size in 1 to n assignment doesn't match target count" do
run(<<-CR).to_i.should eq(4)
require "prelude"

begin
a, b = [1, 2, 3]
4
rescue ex : Exception
raise ex unless ex.message == "Multiple assignment count mismatch"
5
end
CR
end
end

context "strict_multi_assign" do
it "raises if value size in 1 to n assignment doesn't match target count" do
run(<<-CR, flags: %w(strict_multi_assign)).to_i.should eq(5)
require "prelude"

begin
a, b = [1, 2, 3]
4
rescue ex : Exception
raise ex unless ex.message == "Multiple assignment count mismatch"
5
end
CR
end
end

it "supports m to n assignment, with splat on left-hand side (1)" do
run(<<-CR).to_i.should eq(12345)
#{tuple_new}
Expand Down
84 changes: 61 additions & 23 deletions spec/compiler/normalize/multi_assign_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,6 @@ describe "Normalize: multi assign" do
CR
end

it "normalizes 1 to n" do
assert_expand_second "d = 1; a, b, c = d", <<-CR
__temp_1 = d
a = __temp_1[0]
b = __temp_1[1]
c = __temp_1[2]
CR
end

it "normalizes n to n with []" do
assert_expand_third "a = 1; b = 2; a[0], b[1] = 2, 3", <<-CR
__temp_1 = 2
Expand All @@ -30,14 +21,6 @@ describe "Normalize: multi assign" do
CR
end

it "normalizes 1 to n with []" do
assert_expand_third "a = 1; b = 2; a[0], b[1] = 2", <<-CR
__temp_1 = 2
a[0] = __temp_1[0]
b[1] = __temp_1[1]
CR
end

it "normalizes n to n with call" do
assert_expand_third "a = 1; b = 2; a.foo, b.bar = 2, 3", <<-CR
__temp_1 = 2
Expand All @@ -47,12 +30,67 @@ describe "Normalize: multi assign" do
CR
end

it "normalizes 1 to n with call" do
assert_expand_third "a = 1; b = 2; a.foo, b.bar = 2", <<-CR
__temp_1 = 2
a.foo = __temp_1[0]
b.bar = __temp_1[1]
CR
context "without strict_multi_assign" do
it "normalizes 1 to n" do
assert_expand_second "d = 1; a, b, c = d", <<-CR
__temp_1 = d
a = __temp_1[0]
b = __temp_1[1]
c = __temp_1[2]
CR
end

it "normalizes 1 to n with []" do
assert_expand_third "a = 1; b = 2; a[0], b[1] = 2", <<-CR
__temp_1 = 2
a[0] = __temp_1[0]
b[1] = __temp_1[1]
CR
end

it "normalizes 1 to n with call" do
assert_expand_third "a = 1; b = 2; a.foo, b.bar = 2", <<-CR
__temp_1 = 2
a.foo = __temp_1[0]
b.bar = __temp_1[1]
CR
end
end

context "strict_multi_assign" do
it "normalizes 1 to n" do
assert_expand_second "d = 1; a, b, c = d", <<-CR, flags: "strict_multi_assign"
__temp_1 = d
if __temp_1.size != 3
::raise(::IndexError.new("Multiple assignment count mismatch"))
end
a = __temp_1[0]
b = __temp_1[1]
c = __temp_1[2]
CR
end

it "normalizes 1 to n with []" do
assert_expand_third "a = 1; b = 2; a[0], b[1] = 2", <<-CR, flags: "strict_multi_assign"
__temp_1 = 2
if __temp_1.size != 2
::raise(::IndexError.new("Multiple assignment count mismatch"))
end
a[0] = __temp_1[0]
b[1] = __temp_1[1]
CR
end

it "normalizes 1 to n with call" do
assert_expand_third "a = 1; b = 2; a.foo, b.bar = 2", <<-CR, flags: "strict_multi_assign"
__temp_1 = 2
if __temp_1.size != 2
::raise(::IndexError.new("Multiple assignment count mismatch"))
end
a.foo = __temp_1[0]
b.bar = __temp_1[1]
CR
end
end

it "normalizes m to n, with splat on left-hand side, splat is empty" do
Expand Down
64 changes: 64 additions & 0 deletions spec/compiler/semantic/multi_assign_spec.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
require "../../spec_helper"

describe "Semantic: multi assign" do
context "without strict_multi_assign" do
it "doesn't error if assigning tuple to fewer targets" do
assert_type(%(
require "prelude"

x = {1, 2, ""}
a, b = x
{a, b}
)) { tuple_of [int32, int32] }
end
end

context "strict_multi_assign" do
it "errors if assigning tuple to fewer targets" do
assert_error %(
require "prelude"

x = {1, 2, ""}
a, b = x
), "cannot assign Tuple(Int32, Int32, String) to 2 targets", flags: "strict_multi_assign"
end

pending "errors if assigning tuple to more targets" do
assert_error %(
require "prelude"

x = {1}
a, b = x
), "cannot assign Tuple(Int32) to 2 targets", flags: "strict_multi_assign"
end

it "errors if assigning union of tuples to fewer targets" do
assert_error %(
require "prelude"

x = true ? {1, 2, 3} : {4, 5, 6, 7}
a, b = x
), "cannot assign (Tuple(Int32, Int32, Int32) | Tuple(Int32, Int32, Int32, Int32)) to 2 targets", flags: "strict_multi_assign"
end

it "doesn't error if some type in union matches target count" do
assert_type(%(
require "prelude"

x = true ? {1, "", 3} : {4, 5}
a, b = x
{a, b}
), flags: "strict_multi_assign") { tuple_of [int32, union_of(int32, string)] }
end

it "doesn't error if some type in union has no constant size" do
assert_type(%(
require "prelude"

x = true ? {1, "", 3} : [4, 5]
a, b = x
{a, b}
), flags: "strict_multi_assign") { tuple_of [int32, union_of(int32, string)] }
end
end
end
20 changes: 11 additions & 9 deletions spec/spec_helper.cr
Original file line number Diff line number Diff line change
Expand Up @@ -85,26 +85,28 @@ def assert_normalize(from, to, flags = nil, *, file = __FILE__, line = __LINE__)
to_nodes
end

def assert_expand(from : String, to, *, file = __FILE__, line = __LINE__)
assert_expand Parser.parse(from), to, file: file, line: line
def assert_expand(from : String, to, *, flags = nil, file = __FILE__, line = __LINE__)
assert_expand Parser.parse(from), to, flags: flags, file: file, line: line
end

def assert_expand(from_nodes : ASTNode, to, *, file = __FILE__, line = __LINE__)
to_nodes = LiteralExpander.new(new_program).expand(from_nodes)
def assert_expand(from_nodes : ASTNode, to, *, flags = nil, file = __FILE__, line = __LINE__)
program = new_program
program.flags.concat(flags.split) if flags
to_nodes = LiteralExpander.new(program).expand(from_nodes)
to_nodes.to_s.strip.should eq(to.strip), file: file, line: line
end

def assert_expand_second(from : String, to, *, file = __FILE__, line = __LINE__)
def assert_expand_second(from : String, to, *, flags = nil, file = __FILE__, line = __LINE__)
node = (Parser.parse(from).as(Expressions))[1]
assert_expand node, to, file: file, line: line
assert_expand node, to, flags: flags, file: file, line: line
end

def assert_expand_third(from : String, to, *, file = __FILE__, line = __LINE__)
def assert_expand_third(from : String, to, *, flags = nil, file = __FILE__, line = __LINE__)
node = (Parser.parse(from).as(Expressions))[2]
assert_expand node, to, file: file, line: line
assert_expand node, to, flags: flags, file: file, line: line
end

def assert_error(str, message = nil, *, inject_primitives = false, file = __FILE__, line = __LINE__, flags = nil)
def assert_error(str, message = nil, *, inject_primitives = false, flags = nil, file = __FILE__, line = __LINE__)
expect_raises TypeException, message, file, line do
semantic str, inject_primitives: inject_primitives, flags: flags
end
Expand Down
18 changes: 9 additions & 9 deletions spec/std/kernel_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,20 @@ require "./spec_helper"

describe "exit" do
it "exits normally with status 0" do
status, _ = compile_and_run_source "exit"
status, _, _ = compile_and_run_source "exit"
status.success?.should be_true
end

it "exits with given error code" do
status, _ = compile_and_run_source "exit 42"
status, _, _ = compile_and_run_source "exit 42"
status.success?.should be_false
status.exit_code.should eq(42)
end
end

describe "at_exit" do
it "runs handlers on normal program ending" do
status, output = compile_and_run_source <<-CODE
status, output, _ = compile_and_run_source <<-CODE
at_exit do
puts "handler code"
end
Expand All @@ -27,7 +27,7 @@ describe "at_exit" do
end

it "runs handlers on explicit program ending" do
status, output = compile_and_run_source <<-'CODE'
status, output, _ = compile_and_run_source <<-'CODE'
at_exit do |exit_code|
puts "handler code, exit code: #{exit_code}"
end
Expand All @@ -40,7 +40,7 @@ describe "at_exit" do
end

it "runs handlers in reverse order" do
status, output = compile_and_run_source <<-CODE
status, output, _ = compile_and_run_source <<-CODE
at_exit do
puts "first handler code"
end
Expand All @@ -59,7 +59,7 @@ describe "at_exit" do
end

it "runs all handlers maximum once" do
status, output = compile_and_run_source <<-CODE
status, output, _ = compile_and_run_source <<-CODE
at_exit do
puts "first handler code"
end
Expand All @@ -86,7 +86,7 @@ describe "at_exit" do
end

it "allows handlers to change the exit code with explicit `exit` call" do
status, output = compile_and_run_source <<-'CODE'
status, output, _ = compile_and_run_source <<-'CODE'
at_exit do |exit_code|
puts "first handler code, exit code: #{exit_code}"
end
Expand Down Expand Up @@ -114,7 +114,7 @@ describe "at_exit" do
end

it "allows handlers to change the exit code with explicit `exit` call (2)" do
status, output = compile_and_run_source <<-'CODE'
status, output, _ = compile_and_run_source <<-'CODE'
at_exit do |exit_code|
puts "first handler code, exit code: #{exit_code}"
end
Expand Down Expand Up @@ -210,7 +210,7 @@ describe "at_exit" do
end

it "allows at_exit inside at_exit" do
status, output = compile_and_run_source <<-CODE
status, output, _ = compile_and_run_source <<-CODE
at_exit do
puts "1"
at_exit do
Expand Down
2 changes: 1 addition & 1 deletion spec/std/process_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ describe Process do
end

pending_win32 "chroot raises when unprivileged" do
status, output = compile_and_run_source <<-'CODE'
status, output, _ = compile_and_run_source <<-'CODE'
begin
Process.chroot("/usr")
puts "FAIL"
Expand Down
12 changes: 6 additions & 6 deletions src/compiler/crystal/codegen/primitives.cr
Original file line number Diff line number Diff line change
Expand Up @@ -1136,8 +1136,8 @@ class Crystal::CodeGenVisitor
success_ordering = atomic_ordering_from_symbol_literal(call.args[-2])
failure_ordering = atomic_ordering_from_symbol_literal(call.args[-1])

pointer, cmp, new = call_args
value = builder.cmpxchg(pointer, cmp, new, success_ordering, failure_ordering)
ptr, cmp, new, _, _ = call_args
value = builder.cmpxchg(ptr, cmp, new, success_ordering, failure_ordering)
value_ptr = alloca llvm_type(node.type)
store extract_value(value, 0), gep(value_ptr, 0, 0)
store extract_value(value, 1), gep(value_ptr, 0, 1)
Expand All @@ -1150,8 +1150,8 @@ class Crystal::CodeGenVisitor
ordering = atomic_ordering_from_symbol_literal(call.args[-2])
singlethread = bool_from_bool_literal(call.args[-1])

_, pointer, val = call_args
builder.atomicrmw(op, pointer, val, ordering, singlethread)
_, ptr, val, _, _ = call_args
builder.atomicrmw(op, ptr, val, ordering, singlethread)
end

def codegen_primitive_fence(call, node, target_def, call_args)
Expand All @@ -1168,7 +1168,7 @@ class Crystal::CodeGenVisitor
ordering = atomic_ordering_from_symbol_literal(call.args[-2])
volatile = bool_from_bool_literal(call.args[-1])

ptr = call_args.first
ptr, _, _ = call_args

inst = builder.load(ptr)
inst.ordering = ordering
Expand All @@ -1182,7 +1182,7 @@ class Crystal::CodeGenVisitor
ordering = atomic_ordering_from_symbol_literal(call.args[-2])
volatile = bool_from_bool_literal(call.args[-1])

ptr, value = call_args
ptr, value, _, _ = call_args

inst = builder.store(value, ptr)
inst.ordering = ordering
Expand Down
Loading