-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Compiler: fix new/initialize lookup regarding modules #7818
Compiler: fix new/initialize lookup regarding modules #7818
Conversation
Does that mean that this won't work intentionally? module Base1
def initialize(x)
end
end
module Base2
def initialize(x, y)
end
end
class Foo
include Base1
include Base2
end
Foo.new 1
Foo.new 1, 2 (not sure which one would fail based on the order of parents, the one in Base1 or Base2?) |
@bew Yes, that's correct. You can't have |
8b43946
to
3f03066
Compare
Maybe later we can refine this rule, but for now I think this is what makes most sense. I really wouldn't advice using |
Put another way, I coded the compiler to always check the first level of |
You can always do: module Base1
def initialize(x)
end
end
module Base2
def initialize(x, y)
end
end
class Foo
include Base1
include Base2
def initialize(x)
super
end
end
Foo.new 1
Foo.new 1, 2 (in fact |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To clarify: this PR disallow the default initialize/new if any of the included modules define an initialize method.
User is able to declare multiple initialize across multiple modules and include them.
Similar to when a custom #initialize
is defined, if the default arg-less is wanted, it needs to be defined explicitly.
Did I miss any other aspect/scenario?
This is not like that. If a type defines an initialize method (any arity) then initialize in super types (parent classes or included modules) are not looked up. Think of Java or C#: constructors are never inherited. The exception in Crystal is that if a type doesn't define a constructor, parent constructors are looked up. |
I didn't mention inheritance, but I should have. Ok with that semantics. I was trying to state what will happen after this PR with: module Base1
def initialize(x)
end
end
module Base2
def initialize(x, y)
end
end
class Foo
include Base1
include Base2
end Foo will have both initialize or only one? |
@bcardiff Only the one from Maybe we can change that... but if we do that we should formally define how we want |
Ok, the last context for now then. I see how this can be understood as consistent with not inheriting initializer from the parent from some point of view. Let's merge this as is for now. It's not 100% neat that |
@asterite @bcardiff I'm assuming it's known that this code would no longer work come next release? Seems undesired IMO. require "json"
abstract class Parent
@name : String
def initialize(@name : String); end
end
class Child < Parent
include JSON::Serializable
end
Child.new "Fred"
|
Yes, constructors are not (and shouldn't) be inherited. That said, I always wanted JSON serialization to be decoupled from constructors, but nobody else liked that idea, so... |
@asterite Why shouldn't constructors be inherited from abstract parent classes/structs? Currently, anything that has constructors shared in a parent abstract class/struct that includes a serializable modules in a child class, just doesn't work. |
Because a parent constructor has no guarantee of initializing instance variables of a subclass. |
Fixes #7007
It turned out the issue was that even this compiled:
The problem was that
new
/initialize
lookup looked past modules that defined aninitialize
method and this was wrong.