Skip to content
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

Interpreter: fix 12351 #12374

Merged
merged 3 commits into from
Aug 11, 2022
Merged
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
20 changes: 20 additions & 0 deletions spec/compiler/interpreter/classes_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -159,4 +159,24 @@ describe Crystal::Repl::Interpreter do
expression.value.foo
CODE
end

it "downcasts virtual type to its only type (#12351)" do
interpret(<<-CODE).should eq(1)
abstract class A
end

class B < A
def x
1
end
end

def foo(b : B)
b = 1
end

b = B.new.as(A)
foo(b)
CODE
end
end
13 changes: 13 additions & 0 deletions spec/compiler/interpreter/multidispatch_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -422,5 +422,18 @@ describe Crystal::Repl::Interpreter do
a_value - b_value
CODE
end

it "casts multidispatch argument to the def's arg type" do
interpret(<<-CODE)
def foo(a : String) forall T
end

def foo(a)
a
end

foo("b" || nil)
CODE
end
end
end
12 changes: 8 additions & 4 deletions src/compiler/crystal/interpreter/compiler.cr
Original file line number Diff line number Diff line change
Expand Up @@ -2244,7 +2244,7 @@ class Crystal::Repl::Compiler < Crystal::Visitor
target_def_arg = target_def_args[i]
target_def_var_type = target_def.vars.not_nil![target_def_arg.name].type

compile_call_arg(arg, arg_type, target_def_var_type)
compile_call_arg(arg, arg_type, target_def_arg.type, target_def_var_type)

i += 1
end
Expand Down Expand Up @@ -2291,7 +2291,7 @@ class Crystal::Repl::Compiler < Crystal::Visitor
end
end

private def compile_call_arg(arg, arg_type, target_def_var_type)
private def compile_call_arg(arg, arg_type, target_def_arg_type, target_def_var_type)
# Check autocasting from symbol to enum
if arg.is_a?(SymbolLiteral) && target_def_var_type.is_a?(EnumType)
symbol_name = arg.value.underscore
Expand All @@ -2318,7 +2318,11 @@ class Crystal::Repl::Compiler < Crystal::Visitor

request_value(arg)

# We need to cast the argument to the target_def variable
# We first cast the argument to the def's arg type,
# which is the external methods' type.
downcast arg, arg_type, target_def_arg_type

# Then we need to cast the argument to the target_def variable
# corresponding to the argument. If for example we have this:
#
# ```
Expand All @@ -2331,7 +2335,7 @@ class Crystal::Repl::Compiler < Crystal::Visitor
#
# Then the actual type of `x` inside `foo` is (Int32 | Nil),
# and we must cast `1` to it.
upcast arg, arg_type, target_def_var_type
upcast arg, target_def_arg_type, target_def_var_type
end

private def compile_pointerof_node(obj : Var, owner : Type) : Nil
Expand Down
24 changes: 21 additions & 3 deletions src/compiler/crystal/interpreter/multidispatch.cr
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ module Crystal::Repl::Multidispatch

blocks = [] of Block

target_defs.each do |target_def|
target_defs.each_with_index do |target_def, target_def_index|
i = 0

condition = nil
Expand All @@ -168,8 +168,26 @@ module Crystal::Repl::Multidispatch
call_args = [] of ASTNode

i = 0
node.args.each do
call_args << Var.new("arg#{i}")
node.args.each_with_index do |arg, arg_index|
var = Var.new("arg#{i}")

# If the argument was autocasted it will always match in a multidispatch
if autocast_types.try &.[arg_index]?
call_args << var
next
end

# Make sure to cast the argument to the target def arg's type
# in the last case, where the argument's type is not restricted by an if is_a?
if target_def_index == target_defs.size - 1
target_def_arg = target_def.args[i]

cast = Cast.new(var, TypeNode.new(target_def_arg.type))
call_args << cast
else
call_args << var
end

i += 1
end

Expand Down