diff --git a/spec/compiler/interpreter/types_spec.cr b/spec/compiler/interpreter/types_spec.cr index 385de7ce0258..02bae1be40a1 100644 --- a/spec/compiler/interpreter/types_spec.cr +++ b/spec/compiler/interpreter/types_spec.cr @@ -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 diff --git a/spec/compiler/semantic/is_a_spec.cr b/spec/compiler/semantic/is_a_spec.cr index 4898daec4f84..031d43011484 100644 --- a/spec/compiler/semantic/is_a_spec.cr +++ b/spec/compiler/semantic/is_a_spec.cr @@ -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 diff --git a/src/compiler/crystal/interpreter/cast.cr b/src/compiler/crystal/interpreter/cast.cr index 36fc3f98771a..e9dafef9dc3f 100644 --- a/src/compiler/crystal/interpreter/cast.cr +++ b/src/compiler/crystal/interpreter/cast.cr @@ -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 diff --git a/src/compiler/crystal/interpreter/compiler.cr b/src/compiler/crystal/interpreter/compiler.cr index 61a6c06f5426..2b00659c1b50 100644 --- a/src/compiler/crystal/interpreter/compiler.cr +++ b/src/compiler/crystal/interpreter/compiler.cr @@ -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})" diff --git a/src/compiler/crystal/semantic/type_intersect.cr b/src/compiler/crystal/semantic/type_intersect.cr index 5bf0e24f046a..8e15f9186cc7 100644 --- a/src/compiler/crystal/semantic/type_intersect.cr +++ b/src/compiler/crystal/semantic/type_intersect.cr @@ -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)