Skip to content

Commit

Permalink
Compiler and interpreter: fix is_a? from virtual metaclass to generic…
Browse files Browse the repository at this point in the history
… metaclass (#12306)

* Compiler: fix `is_a?` from virtual metaclass to generic metaclass

* Interpreter: add missing casts between virtual metaclass and generic metaclass
  • Loading branch information
asterite authored Jul 22, 2022
1 parent b5317ac commit cd2b7d6
Show file tree
Hide file tree
Showing 5 changed files with 44 additions and 9 deletions.
19 changes: 19 additions & 0 deletions spec/compiler/interpreter/types_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,25 @@ describe Crystal::Repl::Interpreter do
CODE
end

it "does class method on virtual metaclass casted to generic metaclass (#12302)" do
interpret(<<-CODE).should eq(42)
class A
def self.foo
1
end
end
class B(T) < A
def self.foo
42
end
end
b = B(String).new.as(A)
b.class.foo
CODE
end

it "discards class for virtual_type type" do
interpret(<<-CODE).should eq(2)
class Foo; end
Expand Down
18 changes: 18 additions & 0 deletions spec/compiler/semantic/is_a_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -240,4 +240,22 @@ describe "Semantic: is_a?" do
end
))
end

it "does is_a? from virtual metaclass to generic metaclass (#12302)" do
assert_type(%(
class A
end
class B(T) < A
end
x = B(String).new.as(A).class
if x.is_a?(B(String).class)
x
else
nil
end
), inject_primitives: true) { nilable generic_class("B", string).metaclass }
end
end
6 changes: 1 addition & 5 deletions src/compiler/crystal/interpreter/cast.cr
Original file line number Diff line number Diff line change
Expand Up @@ -371,11 +371,7 @@ class Crystal::Repl::Compiler
# Nothing to do
end

private def downcast_distinct(node : ASTNode, from : VirtualMetaclassType, to : MetaclassType)
# Nothing to do
end

private def downcast_distinct(node : ASTNode, from : VirtualMetaclassType, to : VirtualMetaclassType)
private def downcast_distinct(node : ASTNode, from : VirtualMetaclassType, to : MetaclassType | VirtualMetaclassType | GenericClassInstanceMetaclassType | GenericModuleInstanceMetaclassType)
# Nothing to do
end

Expand Down
4 changes: 1 addition & 3 deletions src/compiler/crystal/interpreter/compiler.cr
Original file line number Diff line number Diff line change
Expand Up @@ -1734,9 +1734,7 @@ class Crystal::Repl::Compiler < Crystal::Visitor
end
when VirtualMetaclassType
case filtered_type
when MetaclassType
metaclass_is_a(type_id(filtered_type), node: node)
when VirtualMetaclassType
when MetaclassType, VirtualMetaclassType, GenericClassInstanceMetaclassType, GenericModuleInstanceMetaclassType
metaclass_is_a(type_id(filtered_type), node: node)
else
node.raise "BUG: missing filter type from #{type} to #{filtered_type} (#{type.class} to #{filtered_type.class})"
Expand Down
6 changes: 5 additions & 1 deletion src/compiler/crystal/semantic/type_intersect.cr
Original file line number Diff line number Diff line change
Expand Up @@ -97,13 +97,17 @@ module Crystal
restricted.try(&.metaclass)
end

def self.common_descendent(type1 : GenericClassInstanceMetaclassType | GenericModuleInstanceMetaclassType, type2 : MetaclassType)
def self.common_descendent(type1 : GenericClassInstanceMetaclassType | GenericModuleInstanceMetaclassType, type2 : MetaclassType | VirtualMetaclassType)
return type1 if type1.instance_type.generic_type.metaclass == type2

restricted = common_descendent(type1.instance_type, type2.instance_type)
restricted ? type1 : nil
end

def self.common_descendent(type1 : MetaclassType | VirtualMetaclassType, type2 : GenericClassInstanceMetaclassType | GenericModuleInstanceMetaclassType)
common_descendent(type2, type1)
end

def self.common_descendent(type1 : UnionType, type2 : Type)
types = type1.union_types.compact_map do |union_type|
common_descendent(union_type, type2)
Expand Down

0 comments on commit cd2b7d6

Please sign in to comment.