-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Add __crystal_raise_cast_failed
for non-interpreted code
#15708
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
straight-shoota
merged 1 commit into
crystal-lang:master
from
HertzDevil:refactor/codegen-type-cast-error
Apr 27, 2025
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,15 +7,16 @@ require "./llvm_builder_helper" | |
require "./abi/*" | ||
|
||
module Crystal | ||
MAIN_NAME = "__crystal_main" | ||
RAISE_NAME = "__crystal_raise" | ||
RAISE_OVERFLOW_NAME = "__crystal_raise_overflow" | ||
MALLOC_NAME = "__crystal_malloc64" | ||
MALLOC_ATOMIC_NAME = "__crystal_malloc_atomic64" | ||
REALLOC_NAME = "__crystal_realloc64" | ||
GET_EXCEPTION_NAME = "__crystal_get_exception" | ||
ONCE_INIT = "__crystal_once_init" | ||
ONCE = "__crystal_once" | ||
MAIN_NAME = "__crystal_main" | ||
RAISE_NAME = "__crystal_raise" | ||
RAISE_OVERFLOW_NAME = "__crystal_raise_overflow" | ||
RAISE_CAST_FAILED_NAME = "__crystal_raise_cast_failed" | ||
MALLOC_NAME = "__crystal_malloc64" | ||
MALLOC_ATOMIC_NAME = "__crystal_malloc_atomic64" | ||
REALLOC_NAME = "__crystal_realloc64" | ||
GET_EXCEPTION_NAME = "__crystal_get_exception" | ||
ONCE_INIT = "__crystal_once_init" | ||
ONCE = "__crystal_once" | ||
|
||
class Program | ||
def run(code, filename : String? = nil, debug = Debug::Default) | ||
|
@@ -265,6 +266,7 @@ module Crystal | |
@malloc_atomic_fun : LLVMTypedFunction? | ||
@realloc_fun : LLVMTypedFunction? | ||
@raise_overflow_fun : LLVMTypedFunction? | ||
@raise_cast_failed_fun : LLVMTypedFunction? | ||
@c_malloc_fun : LLVMTypedFunction? | ||
@c_realloc_fun : LLVMTypedFunction? | ||
|
||
|
@@ -470,7 +472,7 @@ module Crystal | |
case node.name | ||
when MALLOC_NAME, MALLOC_ATOMIC_NAME, REALLOC_NAME, RAISE_NAME, | ||
@codegen.personality_name, GET_EXCEPTION_NAME, RAISE_OVERFLOW_NAME, | ||
ONCE_INIT, ONCE | ||
RAISE_CAST_FAILED_NAME, ONCE_INIT, ONCE | ||
@codegen.accept node | ||
end | ||
|
||
|
@@ -1488,11 +1490,7 @@ module Crystal | |
cond cmp, matches_block, doesnt_match_block | ||
|
||
position_at_end doesnt_match_block | ||
|
||
temp_var_name = @program.new_temp_var_name | ||
context.vars[temp_var_name] = LLVMVar.new(last_value, obj_type, already_loaded: true) | ||
accept type_cast_exception_call(obj_type, to_type, node, temp_var_name) | ||
context.vars.delete temp_var_name | ||
codegen_raise_cast_failed(type_id, to_type, node) | ||
|
||
position_at_end matches_block | ||
@last = downcast last_value, resulting_type, obj_type, true | ||
|
@@ -1548,28 +1546,6 @@ module Crystal | |
false | ||
end | ||
|
||
def type_cast_exception_call(from_type, to_type, node, var_name) | ||
pieces = [ | ||
StringLiteral.new("Cast from ").at(node), | ||
Call.new(Var.new(var_name).at(node), "class").at(node), | ||
StringLiteral.new(" to #{to_type} failed").at(node), | ||
] of ASTNode | ||
|
||
if location = node.location | ||
pieces << StringLiteral.new(", at #{location.expanded_location}:#{location.line_number}").at(node) | ||
end | ||
|
||
ex = Call.new(Path.global("TypeCastError").at(node), "new", StringInterpolation.new(pieces).at(node)).at(node) | ||
call = Call.global("raise", ex).at(node) | ||
call = @program.normalize(call) | ||
|
||
meta_vars = MetaVars.new | ||
meta_vars[var_name] = MetaVar.new(var_name, type: from_type) | ||
visitor = MainVisitor.new(@program, meta_vars) | ||
@program.visit_main call, visitor: visitor | ||
call | ||
end | ||
|
||
def cant_pass_closure_to_c_exception_call | ||
@cant_pass_closure_to_c_exception_call ||= begin | ||
call = Call.global("raise", StringLiteral.new("passing a closure to C is not allowed")).at(UNKNOWN_LOCATION) | ||
|
@@ -1579,6 +1555,63 @@ module Crystal | |
end | ||
end | ||
|
||
def codegen_raise_cast_failed(type_id, to_type, node) | ||
location = node.location | ||
set_current_debug_location(location) if location && @debug.line_numbers? | ||
|
||
func = crystal_raise_cast_failed_fun | ||
call_args = [ | ||
cast_to_void_pointer(type_id_to_class_name(type_id)), | ||
cast_to_void_pointer(build_string_constant(to_type.to_s)), | ||
location ? cast_to_void_pointer(build_string_constant(location.expanded_location.to_s)) : llvm_context.void_pointer.null, | ||
] of LLVM::Value | ||
|
||
if (rescue_block = @rescue_block) | ||
invoke_out_block = new_block "invoke_out" | ||
invoke func, call_args, invoke_out_block, rescue_block | ||
position_at_end invoke_out_block | ||
else | ||
call func, call_args | ||
end | ||
|
||
unreachable | ||
end | ||
|
||
def type_id_to_class_name(type_id) | ||
fun_name = "~type_id_to_class_name" | ||
func = typed_fun?(@main_mod, fun_name) || create_type_id_to_class_name_fun(fun_name) | ||
func = check_main_fun fun_name, func | ||
call func, type_id | ||
end | ||
|
||
# See also: `#create_metaclass_fun` | ||
def create_type_id_to_class_name_fun(name) | ||
in_main do | ||
define_main_function(name, [llvm_context.int32], llvm_type(@program.string)) do |func| | ||
set_internal_fun_debug_location(func, name) | ||
|
||
arg = func.params.first | ||
|
||
current_block = insert_block | ||
|
||
cases = {} of LLVM::Value => LLVM::BasicBlock | ||
@[email protected] do |type, (_, type_id)| | ||
block = new_block "type_#{type_id}" | ||
cases[int32(type_id)] = block | ||
position_at_end block | ||
ret build_string_constant(type.to_s) | ||
end | ||
|
||
otherwise = new_block "otherwise" | ||
position_at_end otherwise | ||
unreachable | ||
|
||
position_at_end current_block | ||
@builder.switch arg, otherwise, cases | ||
end | ||
end | ||
end | ||
|
||
def visit(node : IsA) | ||
codegen_type_filter node, &.filter_by(node.const.type) | ||
end | ||
|
@@ -2315,6 +2348,15 @@ module Crystal | |
end | ||
end | ||
|
||
def crystal_raise_cast_failed_fun | ||
@raise_cast_failed_fun ||= typed_fun?(@main_mod, RAISE_CAST_FAILED_NAME) | ||
if raise_cast_failed_fun = @raise_cast_failed_fun | ||
check_main_fun RAISE_CAST_FAILED_NAME, raise_cast_failed_fun | ||
else | ||
raise Error.new("Missing __crystal_raise_cast_failed function, either use std-lib's prelude or define it") | ||
end | ||
end | ||
|
||
# Fallbacks to libc malloc and realloc when the expected __crystal_* | ||
# functions aren't defined (e.g. empty prelude). We only use them in tests | ||
# that don't require the prelude, so they don't require the GC. | ||
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.