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
Ancestors of module metaclasses are inconsistent #11110
Comments
The class Crystal::MetaclassType
def replace_type_parameters(instance)
instance_type.replace_type_parameters(instance).metaclass
end
end
class Crystal::GenericModuleInstanceMetaclassType
def parents
instance_type.generic_type.metaclass.parents.try &.map do |parent|
parent.replace_type_parameters(instance_type)
end
end
end The direct parents of Metaclasses of generic class instances are immune because they must have a superclass ( |
The assessment sounds good and the proposed solution seems convincing. However, I fear this might be too big a breaking change in regards to how class methods on As I understand it, I personally don't like the idea of making such methods instance methods of Could it be an option to implicitly define class methods of |
The method in question is actually also related to #5697, so if we succeed in debloating |
Another problem I just discovered is that generic module instances implicitly inherit module Foo(T)
end
puts Foo(Int32).allocate # can't execute `obj.to_s(self)` at /usr/lib/crystal/io.cr:174:5: `obj.to_s(self)` has no type module Foo(T)
end
class Bar
include Foo(Int32)
@x = 1
end
puts Foo(Int32).allocate # => #<Bar:0x7f308c5a1ea0> module Foo(T)
end
class Bar
include Foo(Int32)
end
class Baz
include Foo(Int32)
end
Foo(Int32).allocate # BUG: called llvm_struct_type for (Bar | Baz)
module Foo(T)
end
Foo.allocate # Error: undefined method 'allocate' for Foo(T):Module (modules cannot be instantiated) |
Modules that don't include any other types have no ancestors at all, not even
Object
:It follows that
Class
should be the sole direct parent of those modules' metaclasses. Instead, the metaclasses of generic module instances containObject.class
in their hierarchy:This is unexpected, because
B(Int32)
still isn't a class. This hierarchy directly affects class method lookup. The standard library seems to rely on this in at least one place:crystal/spec/std/json/serialization_spec.cr
Lines 280 to 283 in b35c8c9
Where
from_json
is defined as:A
String
is not aJSON::PullParser
, so the overload inEnum::ValueConverter.class
fails to match and the one inObject.class
is chosen. Now suppose that we specializeEnum::ValueConverter(JSONSpecEnum)
ourself. The following no longer compiles (as it shouldn't):We should remove
Object.class
from the ancestors of those generic module instance metaclasses. Even in that case, the same behaviour can be achieved with either of the following:The text was updated successfully, but these errors were encountered: