Skip to content
Closed
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
264 changes: 264 additions & 0 deletions spec/compiler/semantic/simple_rescues_spec.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,264 @@
require "../../spec_helper"

private def assert_transform(from, to, flags = nil)
program = Program.new
program.flags = flags if flags
from_nodes = Parser.parse(from)
to_nodes = from_nodes.transform SimpleRescues.new
to_nodes.to_s.strip.should eq(to.strip)
end

describe "Semantic: SimpleRescues" do
it "can do noop" do
assert_transform(
<<-CR
a = 1
CR
,
<<-CR
a = 1
CR
)
end

it "ensures variables on rescue blocks" do
assert_transform(
<<-CR
begin
a = 1
rescue
a = 2
end
CR
,
<<-CR
begin
a = 1
rescue ___e1
a = 2
end
CR
)
end

it "ensures translate single type restrictions to conditionals" do
assert_transform(
<<-CR
begin
a = 1
rescue e : A
e.foo
a = 2
end
CR
,
<<-CR
begin
a = 1
rescue e
if e.is_a?(A)
e.foo
a = 2
else
::raise(e)
end
end
CR
)
end

it "ensures translate multiple type restrictions to conditionals" do
assert_transform(
<<-CR
begin
a = 1
rescue e : A | B
e.foo
a = 2
end
CR
,
<<-CR
begin
a = 1
rescue e
if e.is_a?(A | B)
e.foo
a = 2
else
::raise(e)
end
end
CR
)
end

it "ensures translate multiple rescues with type restrictions" do
assert_transform(
<<-CR
begin
lorem
rescue a : A
a.foo
rescue b : B
b.bar
end
CR
,
<<-CR
begin
lorem
rescue ___e1
if (a = ___e1) && (a.is_a?(A))
a.foo
else
if (b = ___e1) && (b.is_a?(B))
b.bar
else
::raise(___e1)
end
end
end
CR
)
end

it "ensures translate multiple rescues with type some restrictions" do
assert_transform(
<<-CR
begin
lorem
rescue a : A
a.foo
rescue b
b.bar
end
CR
,
<<-CR
begin
lorem
rescue ___e1
if (a = ___e1) && (a.is_a?(A))
a.foo
else
b = ___e1
b.bar
end
end
CR
)
end

it "translate ensure with a wrapping rescue" do
assert_transform(
<<-CR
begin
lorem
rescue
foo
else
bar
ensure
qux
end
CR
,
<<-CR
(begin
begin
lorem
rescue ___e2
foo
else
bar
end
rescue ___e1
qux
::raise(___e1)
end).tap do
qux
end
CR
)
end

it "translate ensure without rescue with a simlre rescue" do
assert_transform(
<<-CR
begin
lorem
ensure
qux
end
CR
,
<<-CR
(begin
lorem
rescue ___e1
qux
::raise(___e1)
end).tap do
qux
end
CR
)
end

it "ensures nested transforms in body" do
assert_transform(
<<-CR
begin
begin
a = 1
rescue
a = 2
end
rescue
a = 3
end
CR
,
<<-CR
begin
begin
a = 1
rescue ___e2
a = 2
end
rescue ___e1
a = 3
end
CR
)
end

it "ensures nested transforms in rescue" do
assert_transform(
<<-CR
begin
a = 0
rescue
begin
a = 1
rescue
a = 2
end
end
CR
,
<<-CR
begin
a = 0
rescue ___e1
begin
a = 1
rescue ___e2
a = 2
end
end
CR
)
end
end
10 changes: 8 additions & 2 deletions src/compiler/crystal/codegen/call.cr
Original file line number Diff line number Diff line change
Expand Up @@ -457,12 +457,18 @@ class Crystal::CodeGenVisitor
def codegen_call_or_invoke(node, target_def, self_type, func, call_args, raises, type, is_closure = false, fun_type = nil)
set_current_debug_location node if @debug.line_numbers?

funclet = if (catch_pad = @catch_pad)
builder.build_operand_bundle_def("funclet", [catch_pad])
else
LLVM::OperandBundleDef.null
end

if raises && (rescue_block = @rescue_block)
invoke_out_block = new_block "invoke_out"
@last = builder.invoke func, call_args, invoke_out_block, rescue_block
@last = builder.invoke func, call_args, invoke_out_block, rescue_block, bundle: funclet
position_at_end invoke_out_block
else
@last = call func, call_args
@last = call func, call_args, bundle: funclet
end

if target_def.is_a?(External) && (call_convention = target_def.call_convention)
Expand Down
15 changes: 13 additions & 2 deletions src/compiler/crystal/codegen/codegen.cr
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ module Crystal
MALLOC_NAME = "__crystal_malloc64"
MALLOC_ATOMIC_NAME = "__crystal_malloc_atomic64"
REALLOC_NAME = "__crystal_realloc64"
PERSONALITY_NAME = "__crystal_personality"
GET_EXCEPTION_NAME = "__crystal_get_exception"

class Program
Expand Down Expand Up @@ -97,6 +96,7 @@ module Crystal
getter llvm_typer : LLVMTyper
getter alloca_block : LLVM::BasicBlock
getter entry_block : LLVM::BasicBlock
getter personality_name : String
property last : LLVM::Value

class LLVMVar
Expand Down Expand Up @@ -129,6 +129,7 @@ module Crystal
@argv : LLVM::Value
@empty_md_list : LLVM::Value
@rescue_block : LLVM::BasicBlock?
@catch_pad : LLVM::Value?
@malloc_fun : LLVM::Function?
@malloc_atomic_fun : LLVM::Function?
@c_malloc_fun : LLVM::Function?
Expand Down Expand Up @@ -156,6 +157,16 @@ module Crystal
ret_type = @llvm_typer.llvm_return_type(@main_ret_type)
@main = @llvm_mod.functions.add(MAIN_NAME, [llvm_context.int32, llvm_context.void_pointer.pointer], ret_type)

@personality_name = if @program.has_flag?("windows")
"__CxxFrameHandler3"
else
"__crystal_personality"
end

if @program.has_flag?("windows")
@main.personality_function = @llvm_mod.functions.add(@personality_name, ([] of LLVM::Type), llvm_context.int32, true)
end

emit_main_def_debug_metadata(@main, "??") unless @debug.none?

@context = Context.new @main, @program
Expand Down Expand Up @@ -280,7 +291,7 @@ module Crystal

def visit(node : FunDef)
case node.name
when MALLOC_NAME, MALLOC_ATOMIC_NAME, REALLOC_NAME, RAISE_NAME, PERSONALITY_NAME, GET_EXCEPTION_NAME
when MALLOC_NAME, MALLOC_ATOMIC_NAME, REALLOC_NAME, RAISE_NAME, @codegen.personality_name, GET_EXCEPTION_NAME
@codegen.accept node
end
false
Expand Down
4 changes: 4 additions & 0 deletions src/compiler/crystal/codegen/crystal_llvm_builder.cr
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ module Crystal
@builder.insert_block
end

def build_operand_bundle_def(name, values : Array(LLVM::Value))
@builder.build_operand_bundle_def(name, values)
end

def to_unsafe
@builder.to_unsafe
end
Expand Down
Loading