-
-
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
Collision between module and abstract class breaks the code generator, with null reference in LLVM #11673
Comments
Related: #10979 |
@beta-ziliani Please prioritize, this has been in the compiler for at least 6 months, according to #10979, and results in no comprehensible error message, so is really difficult for the user to isolate. |
@BrucePerens you saved me a lot of time binary searching my own source code for this. Thank you. |
@robocarp You're welcome. It did indeed take quite a while. And @beta-ziliani, here's someone else who hit that bug. |
This appears to be pretty much the same issue as #10979, it appears when merging modules or modules and classes. The reproduction in the OP also works with a concrete class, so abstract is not a condition for this error. It's clear that this error should be fixed, but I'm not entirely sure about what would be the expected behaviour. Is it inevitably an error? Or can the compiler accept ivar redefinitions from different hierarchies? In the example, the ivars' types are identical, so they could be trivially unified. This works in linear hierarchies as well. The only trouble is figuring out precedence of default values, but I presume that should just follow the overload ordering of methods (i.e. Incompatible types must certainly be an error, like in linear hierarchies. Does that sound good? |
I think the example in this issue should compile fine. The main problem is figuring out how to fix this. |
A readable error would certainly be helpful in moving forward, but Bruce's example is certainly not something that seems broken to me. Resolving default values by method overloading paradigms makes sense at first glance. |
It appears the case of different types is already handled correctly. So it seems to be only a matter of unifying the instance variable when the types are compatible. module M
@x = 'a'
end
abstract class Parent
@x = 1 # Error: instance variable '@x' of Parent must be Int32, not (Char | Int32)
end
class Child < Parent
include M
end The error location isn't accurate, it should probably be on |
I looked into this briefly the last week to see if I could quickly fix it, but I failed. To understand this problem better, you have to know that:
So for instance if we have: class A
@a = 1
end
class B < A
@b = 1
end We have that Let's say we have this: ```crystal
class A
@a = 1
end
class B < A
@a = 3
@b = 1
end In this case there's also To be able to do this the compiler will analyze type hierarchies starting from root types and going downwards. This... kind of falls apart with modules! What Crystal does right now is to analyze modules first, before classes and structs. If a module mentions an instance variable, that instance variable is propagated to inherited types. So in a code like this: module Moo
@a = 1
end
class Foo
@b = 2
end
The end result is that
So far, so good. But with this example: module M
@a = 1
end
abstract class A
@a = 1
end
class B < A
include M
@b = 1
end
B.new
Do you see the problem? The definition of How to fix this? Hard! I have no idea, hehe... One idea is, when in point 2 we learn that Well, maybe that could work but I didn't have time to try it... (not I'll have time) Another alternative could be to first process type hierarchies and then modules. If we do that, then:
This I tried, but type initialization breaks. It seems the logic to determine instance variables is tied with determining which ones are initialized in So... maybe now you understand why this issue has been sitting here for a long time. It's hard! The core team can prioritize it but that doesn't mean it will get fixed soon. It's like asking to prioritize solving the halting problem :-) |
I sympathize with the desire to have the compiler actually process this construct correctly. But since this is a difficult thing to do, could we have it detect the issue and emit a message, in the short term? |
Thanks @asterite, this was very insightful. I took the liberty to export your explanations to the wiki (https://github.com/crystal-lang/crystal/wiki/Compiler-Internals:-Instance-Variables) to make it discoverable. |
This program:
Results in this error inside of LLVM:
With the bleeding-edge compiler and LLVM 13:
I get a better report:
The text was updated successfully, but these errors were encountered: