Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: crystal-lang/crystal
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: fad767ceec83
Choose a base ref
...
head repository: crystal-lang/crystal
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 33111b2a7728
Choose a head ref
  • 2 commits
  • 5 files changed
  • 1 contributor

Commits on Sep 4, 2016

  1. Compiler: fixed macro hooks for generic types. Fixes #3250

    Ary Borenszweig committed Sep 4, 2016
    Copy the full SHA
    7b7380d View commit details
  2. Copy the full SHA
    33111b2 View commit details
9 changes: 9 additions & 0 deletions spec/compiler/codegen/named_tuple_spec.cr
Original file line number Diff line number Diff line change
@@ -288,4 +288,13 @@ describe "Code gen: named tuple" do
NamedTuple(x: Nil, y: Int32).foo
)).to_string.should eq("NamedTupleLiteral")
end

it "assigns two same-size named tuple types to a same var (#3132)" do
run(%(
t = {x: true}
t
t = {x: 2}
t[:x]
)).to_i.should eq(2)
end
end
27 changes: 27 additions & 0 deletions spec/compiler/codegen/tuple_spec.cr
Original file line number Diff line number Diff line change
@@ -324,4 +324,31 @@ describe "Code gen: tuple" do
bar
))
end

it "assigns two same-size tuple types to a same var (#3132)" do
run(%(
t = {true}
t
t = {2}
t[0]
)).to_i.should eq(2)
end

it "downcasts union to mixed tuple type" do
run(%(
t = {1} || 2 || {true}
t = {1}
t[0]
)).to_i.should eq(1)
end

it "downcasts union to mixed union with mixed tuple types" do
run(%(
require "prelude"
t = {1} || 2 || {true}
t = {1} || 2
t.as(Tuple)[0]
)).to_i.should eq(1)
end
end
35 changes: 35 additions & 0 deletions spec/compiler/semantic/hooks_spec.cr
Original file line number Diff line number Diff line change
@@ -147,4 +147,39 @@ describe "Semantic: hooks" do
),
"undefined macro method 'MacroId#unknown'"
end

it "does included macro for generic module" do
assert_type(%(
module Mod(T)
macro included
def self.method
1
end
end
end
class Klass
include Mod(Nil)
end
Klass.method
)) { int32 }
end

it "does inherited macro for generic class" do
assert_type(%(
class Foo(T)
macro inherited
def self.method
1
end
end
end
class Klass < Foo(Int32)
end
Klass.method
)) { int32 }
end
end
81 changes: 80 additions & 1 deletion src/compiler/crystal/codegen/cast.cr
Original file line number Diff line number Diff line change
@@ -371,7 +371,47 @@ class Crystal::CodeGenVisitor
end

def downcast_distinct(value, to_type : MixedUnionType, from_type : MixedUnionType)
cast_to_pointer value, to_type
# It might happen that some types inside the union `from_type` are not inside `to_type`,
# for example with named tuple of same keys with different order. In that case we need cast
# those value to the correct type before finally storing them in the target union.
needs_union_value_cast = from_type.union_types.any? do |vt|
needs_value_cast_inside_union?(vt, to_type)
end

if needs_union_value_cast
# Compute the values that need a cast
types_needing_cast = from_type.union_types.select do |vt|
needs_value_cast_inside_union?(vt, to_type)
end

# Fetch the value's type id
from_type_id = type_id(value, from_type)

Phi.open(self, to_type, @needs_value) do |phi|
types_needing_cast.each_with_index do |type_needing_cast, i|
# Find compatible type
compatible_type = to_type.union_types.find { |ut| ut.implements?(type_needing_cast) }.not_nil!

matches_label, doesnt_match_label = new_blocks "matches", "doesnt_match_label"
cmp_result = equal?(from_type_id, type_id(type_needing_cast))
cond cmp_result, matches_label, doesnt_match_label

position_at_end matches_label

casted_value = cast_to_pointer(union_value(value), type_needing_cast)
downcasted_value = downcast(casted_value, compatible_type, type_needing_cast, true)
final_value = upcast(downcasted_value, to_type, compatible_type)
phi.add final_value, to_type

position_at_end doesnt_match_label
end

final_value = cast_to_pointer value, to_type
phi.add final_value, to_type, last: true
end
else
cast_to_pointer value, to_type
end
end

def downcast_distinct(value, to_type : NilableType, from_type : MixedUnionType)
@@ -386,6 +426,18 @@ class Crystal::CodeGenVisitor
end

def downcast_distinct(value, to_type : Type, from_type : MixedUnionType)
# It might happen that to_type is not of the union but it's compatible with one of them.
# We need to first cast the value to the compatible type and to to_type
case to_type
when TupleInstanceType, NamedTupleInstanceType
unless from_type.union_types.any? &.==(to_type)
compatible_type = from_type.union_types.find { |ut| to_type.implements?(ut) }.not_nil!
value = downcast(value, compatible_type, from_type, true)
value = downcast(value, to_type, compatible_type, true)
return value
end
end

value_ptr = union_value(value)
value = cast_to_pointer(value_ptr, to_type)
to_lhs value, to_type
@@ -396,6 +448,33 @@ class Crystal::CodeGenVisitor
value
end

def downcast_distinct(value, to_type : TupleInstanceType, from_type : TupleInstanceType)
target_pointer = alloca(llvm_type(to_type))
index = 0
to_type.tuple_types.zip(from_type.tuple_types) do |target_tuple_type, value_tuple_type|
target_ptr = gep target_pointer, 0, index
value_ptr = gep value, 0, index
loaded_value = to_lhs(value_ptr, value_tuple_type)
downcasted_value = downcast(loaded_value, target_tuple_type, value_tuple_type, true)
store downcasted_value, target_ptr
index += 1
end
target_pointer
end

def downcast_distinct(value, to_type : NamedTupleInstanceType, from_type : NamedTupleInstanceType)
target_pointer = alloca(llvm_type(to_type))
from_type.entries.each_with_index do |entry, index|
value_ptr = aggregate_index(value, index)
value_at_index = to_lhs(value_ptr, entry.type)
target_index = to_type.name_index(entry.name).not_nil!
target_index_type = to_type.name_type(entry.name)
downcasted_value = downcast(value_at_index, target_index_type, entry.type, true)
store downcasted_value, aggregate_index(target_pointer, target_index)
end
target_pointer
end

def downcast_distinct(value, to_type : Type, from_type : Type)
raise "Bug: trying to downcast #{to_type} <- #{from_type}"
end
9 changes: 7 additions & 2 deletions src/compiler/crystal/semantic/top_level_visitor.cr
Original file line number Diff line number Diff line change
@@ -142,7 +142,7 @@ class Crystal::TopLevelVisitor < Crystal::SemanticVisitor
attach_doc type, node

pushing_type(type) do
run_hooks(superclass.metaclass, type, :inherited, node) if created_new_type
run_hooks(hook_type(superclass), type, :inherited, node) if created_new_type
node.body.accept self
end

@@ -712,7 +712,7 @@ class Crystal::TopLevelVisitor < Crystal::SemanticVisitor

begin
current_type.as(ModuleType).include type
run_hooks type.metaclass, current_type, kind, node
run_hooks hook_type(type), current_type, kind, node
rescue ex : TypeException
node.raise "at '#{kind}' hook", ex
end
@@ -738,6 +738,11 @@ class Crystal::TopLevelVisitor < Crystal::SemanticVisitor
end
end

private def hook_type(type)
type = type.generic_type if type.is_a?(GenericInstanceType)
type.metaclass
end

def check_call_convention_attributes(node)
attributes = @attributes
return unless attributes