require "../../spec_helper" private struct ProperSubtypeExpectation def initialize(@expected_value : Crystal::Type?) end def match(actual_value) subtype?(actual_value, @expected_value) && !subtype?(@expected_value, actual_value) end private def subtype?(t, u) if t.is_a?(Crystal::NoReturnType?) true elsif u.is_a?(Crystal::NoReturnType?) false else t.implements?(u) end end def failure_message(actual_value) actual_str = type_to_s(actual_value) expected_str = type_to_s(@expected_value) String.build do |io| io << "Expected: #{actual_str} to be a proper subtype of #{expected_str}, but " if !subtype?(actual_value, @expected_value) io << "#{actual_str} <= #{expected_str} is false" else # subtype?(@expected_value, actual_value) io << "#{expected_str} <= #{actual_str} is true" end end end def negative_failure_message(actual_value) "Expected: #{type_to_s(actual_value)} not to be a proper subtype of #{type_to_s(@expected_value)}" end private def type_to_s(type) case type when Nil "NoReturn" when Crystal::GenericInstanceType, Crystal::GenericClassInstanceMetaclassType, Crystal::GenericModuleInstanceMetaclassType type.to_s(generic_args: true) else type.to_s(generic_args: false) end end end private def be_a_proper_subtype_of(supertype) ProperSubtypeExpectation.new(supertype) end describe "Semantic: metaclass" do it "types Object class" do assert_type("Object") { program.object.metaclass } end it "types Class class" do assert_type("Class") { class_type } end it "types Object and Class metaclasses" do assert_type("Object.class", inject_primitives: true) { class_type } assert_type("Class.class", inject_primitives: true) { class_type } end it "types Reference metaclass" do assert_type("Reference") { program.reference.metaclass } assert_type("Reference.class", inject_primitives: true) { class_type } end it "types generic class metaclass" do assert_type("Pointer") { pointer.metaclass } assert_type("Pointer.class", inject_primitives: true) { class_type } assert_type("Pointer(Int32)") { pointer_of(int32).metaclass } assert_type("Pointer(Int32).class", inject_primitives: true) { class_type } end it "types generic module metaclass" do assert_type("module Foo(T); end; Foo") { types["Foo"].metaclass } assert_type("module Foo(T); end; Foo.class", inject_primitives: true) { class_type } assert_type("module Foo(T); end; Foo(Int32)") { generic_module("Foo", int32).metaclass } assert_type("module Foo(T); end; Foo(Int32).class", inject_primitives: true) { class_type } end it "types metaclass superclass" do mod = semantic(<<-CRYSTAL).program class Foo; end class Bar < Foo; end CRYSTAL bar_class = mod.types["Bar"].metaclass.as(MetaclassType) bar_class.superclass.should eq(mod.types["Foo"].metaclass) end it "types generic metaclass superclass" do mod = semantic(<<-CRYSTAL).program class Foo(T); end class Bar(T) < Foo(T); end CRYSTAL foo_class = mod.types["Foo"].metaclass.as(MetaclassType) foo_class.superclass.should eq(mod.program.reference.metaclass) bar = mod.types["Bar"].as(GenericClassType) bar_class = bar.metaclass.as(MetaclassType) bar_class.superclass.should eq(mod.generic_class("Foo", bar.type_parameter("T")).metaclass) end it "types generic instance metaclass superclass" do mod = semantic(<<-CRYSTAL).program class Foo(T); end class Bar(T) < Foo(T); end CRYSTAL foo_class = mod.generic_class("Foo", mod.int32).metaclass.as(GenericClassInstanceMetaclassType) foo_class.superclass.should eq(mod.program.reference.metaclass) bar_class = mod.generic_class("Bar", mod.int32).metaclass.as(GenericClassInstanceMetaclassType) bar_class.superclass.should eq(foo_class) end describe "subtyping relations between metaclasses" do it "non-generic classes" do mod = semantic(<<-CRYSTAL).program class Foo; end class Bar < Foo; end CRYSTAL foo_class = mod.types["Foo"].metaclass bar_class = mod.types["Bar"].metaclass bar_class.should be_a_proper_subtype_of(foo_class) end it "virtual metaclass type with virtual type (#12628)" do mod = semantic(<<-CRYSTAL).program class Base; end class Impl < Base; end CRYSTAL base = mod.types["Base"] base.virtual_type!.metaclass.implements?(base).should be_false base.virtual_type!.metaclass.implements?(base.metaclass).should be_true base.virtual_type!.metaclass.implements?(base.metaclass.virtual_type!).should be_true end it "generic classes (1)" do mod = semantic(<<-CRYSTAL).program class Foo(T); end class Bar < Foo(Int32); end CRYSTAL foo_class = mod.types["Foo"].metaclass foo_int32_class = mod.generic_class("Foo", mod.int32).metaclass bar_class = mod.types["Bar"].metaclass bar_class.should be_a_proper_subtype_of(foo_int32_class) bar_class.should be_a_proper_subtype_of(foo_class) foo_int32_class.should be_a_proper_subtype_of(foo_class) end it "generic classes (2)" do mod = semantic(<<-CRYSTAL).program class Foo; end class Bar(T) < Foo; end CRYSTAL foo_class = mod.types["Foo"].metaclass bar_class = mod.types["Bar"].metaclass bar_int32_class = mod.generic_class("Bar", mod.int32).metaclass bar_int32_class.should be_a_proper_subtype_of(bar_class) bar_int32_class.should be_a_proper_subtype_of(foo_class) bar_class.should be_a_proper_subtype_of(foo_class) end it "generic classes (3)" do mod = semantic(<<-CRYSTAL).program class Foo(T); end class Bar(T) < Foo(T); end CRYSTAL foo_class = mod.types["Foo"].metaclass bar_class = mod.types["Bar"].metaclass foo_int32_class = mod.generic_class("Foo", mod.int32).metaclass bar_int32_class = mod.generic_class("Bar", mod.int32).metaclass bar_int32_class.should be_a_proper_subtype_of(bar_class) bar_int32_class.should be_a_proper_subtype_of(foo_int32_class) bar_class.should be_a_proper_subtype_of(foo_class) foo_int32_class.should be_a_proper_subtype_of(foo_class) bar_int32_class.should be_a_proper_subtype_of(foo_class) end it "non-generic modules" do mod = semantic(<<-CRYSTAL).program module Foo; end module Bar; include Foo; end CRYSTAL foo_class = mod.types["Foo"].metaclass bar_class = mod.types["Bar"].metaclass bar_class.implements?(foo_class).should be_false end it "generic modules (1)" do mod = semantic(<<-CRYSTAL).program module Foo(T); end module Bar; include Foo(Int32); end CRYSTAL foo_class = mod.types["Foo"].metaclass foo_int32_class = mod.generic_module("Foo", mod.int32).metaclass bar_class = mod.types["Bar"].metaclass # only instantiations of generic module metaclasses are subtypes of the # uninstantiated metaclasses foo_int32_class.should be_a_proper_subtype_of(foo_class) bar_class.implements?(foo_int32_class).should be_false bar_class.implements?(foo_class).should be_false end it "generic modules (2)" do mod = semantic(<<-CRYSTAL).program module Foo; end module Bar(T); include Foo; end CRYSTAL foo_class = mod.types["Foo"].metaclass bar_class = mod.types["Bar"].metaclass bar_int32_class = mod.generic_module("Bar", mod.int32).metaclass bar_int32_class.should be_a_proper_subtype_of(bar_class) bar_int32_class.implements?(foo_class).should be_false bar_class.implements?(foo_class).should be_false end it "generic modules (3)" do mod = semantic(<<-CRYSTAL).program module Foo(T); end module Bar(T); include Foo(T); end CRYSTAL foo_class = mod.types["Foo"].metaclass bar_class = mod.types["Bar"].metaclass foo_int32_class = mod.generic_module("Foo", mod.int32).metaclass bar_int32_class = mod.generic_module("Bar", mod.int32).metaclass bar_int32_class.should be_a_proper_subtype_of(bar_class) foo_int32_class.should be_a_proper_subtype_of(foo_class) bar_int32_class.implements?(foo_int32_class).should be_false bar_int32_class.implements?(foo_class).should be_false bar_class.implements?(foo_class).should be_false end end it "can't reopen as struct" do assert_error <<-CRYSTAL, "Bar is not a struct, it's a metaclass" class Foo end alias Bar = Foo.class struct Bar end CRYSTAL end it "can't reopen as module" do assert_error <<-CRYSTAL, "Bar is not a module, it's a metaclass" class Foo end alias Bar = Foo.class module Bar end CRYSTAL end end