-
-
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: remove duplicate instance vars once we know them all #11995
Conversation
errr... should we, like, discuss this before? 😅 IIUC here you're proposing that ivars with compatible types are OK to merge, and those with incompatible types aren't. What's confusing in the example: module M
@a = 'a'
end
class A
@a = 1
end
class B < A
include M
end is that the error says that (but this might just be nitpicking while doing reverse engineering of your proposal) |
@beta-ziliani Makes sense. With this PR I'm making it work when it should work (compatible types.) When it doesn't work, the error isn't the ideal, but that's how it was before this issue was approached (even before #11768) The error message can definitely be improved! But I feel that that can happen in 1.5, or maybe as a patch to 1.4.1. This PR is about not introducing a breaking change in 1.4, while also doing a correct fix for the problem :-) |
I'll push a commit to improve the error message, it's pretty easy :-) |
Done! There are still a couple of specs that could have an improved error message, but that's related to guessed types and I'd rather not touch those for a minor release. |
type.instance_vars.reject! do |name, ivar| | ||
supervar = type.superclass.try &.lookup_instance_var?(name) | ||
if supervar && supervar.type != ivar.type | ||
message = "instance variable '#{name}' of #{supervar.owner}, with #{type} < #{supervar.owner}, is already declared as #{supervar.type} (trying to re-declare it in #{type} as #{ivar.type})" |
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.
I believe adding the reference to re-declaring in type
is confusing. type
is the including class. But from a user's perspective, that's not where you find the variable. It's actually declared in a module.
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.
But the conflict is that the variable was redeclared in the class because if the module inclusion. In any case are you fine with continuing this PR?
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.
Yeah, I think this is a gread solution. It restores backwards compatibility and fixes the original issue.
We were discussing the benefits of more strict behaviour, but that would be something for later (if at all).
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.
Just a quick comment: I think that If we want a stricter behavior, that should be discussed now: by enabling this behavior (which wasn't really working before) we are making it part of the language. Removing it later is a major breaking change. This said, I think we agree we want this, and not having this might be a discussion worth having only for 2.0.
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.
Why I think this is the correct thing to do: instance variables only live on classes, because you can't instantiate modules. If an instance variable is "declared" in a module that's later included in the middle of a type hierarchy, and the type matches, I don't see why it would be a problem. It was a problem before only because a bug existed around this. But if the bug didn't exist, I can't think of another semantic for this. Why would it not compile?
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.
Just the 'ol saying: with great power comes great responsibility
@@ -272,20 +274,14 @@ struct Crystal::TypeDeclarationProcessor | |||
if supervar && supervar.owner != owner | |||
# Redeclaring a variable with the same type is OK | |||
unless supervar.type.same?(type_decl.type) | |||
raise TypeException.new("instance variable '#{name}' of #{supervar.owner}, with #{owner} < #{supervar.owner}, is already declared as #{supervar.type} (trying to re-declare as #{type_decl.type})", type_decl.location) | |||
raise TypeException.new("instance variable '#{name}' of #{supervar.owner}, with #{owner} < #{supervar.owner}, is already declared as #{supervar.type} (trying to re-declare it in #{owner} as #{type_decl.type})", type_decl.location) |
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.
I'm not very happy about the duplication of owner
here. Type names can be quite a mouthful, so it would be nice to reduce their usage to the minimum.
Maybe we can rephrase this error message to be more concise?
raise TypeException.new("instance variable '#{name}' of #{supervar.owner}, with #{owner} < #{supervar.owner}, is already declared as #{supervar.type} (trying to re-declare it in #{owner} as #{type_decl.type})", type_decl.location) | |
raise TypeException.new("instance variable '#{name}' is already declared as #{supervar.type} in #{supervar.owner} (trying to re-declare it as #{type_decl.type} in #{owner})", type_decl.location) |
A similar rephrasing could be applied to the other message.
After thinking more about it, I'm now convinced that this behavior is the most reasonable one given how other parts of the language work, so here you have my stamp 👌 in case you need it 😄 I'm not in conditions to review it rn, but let's make it happen 🚀 |
Yeah, sorry about not discussing it first. Sometimes I have ideas about how to solve things so I try them and if they kind of work locally I send a PR to see if full CI passes too. In this case I tried two different approaches, the first one didn't work but this second one did. Given that this is mainly a bug fix, I didn't feel the need to open a discussion about what the fix should be (like, what should compile or not compile) |
I think this is totally fine. A discussion was already started and you have a clear understanding of the problem. Opening a draft PR helps to communicate the intended changes very clearly and thus helps the discussion. |
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.
Just comments 🚀
type.instance_vars.reject! do |name, ivar| | ||
supervar = type.superclass.try &.lookup_instance_var?(name) | ||
if supervar && supervar.type != ivar.type | ||
message = "instance variable '#{name}' of #{supervar.owner}, with #{type} < #{supervar.owner}, is already declared as #{supervar.type} (trying to re-declare it in #{type} as #{ivar.type})" |
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.
Just the 'ol saying: with great power comes great responsibility
remove_duplicate_instance_vars_declarations(@program, duplicates_checked) | ||
end | ||
|
||
private def remove_duplicate_instance_vars_declarations(type : Type, duplicates_checked : Set(Type)) |
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.
Out of curiosity, wouldn't it make sense just to use defaults and avoid the other definition?
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.
I don't think using defaults makes sense. It's not like I'd like to call this with one of the arguments and not the other. But... if you feel like it, feel free to adapt this PR to your taste :-)
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.
No, this is fine, I was just curious. And I just saw a resolved previous comment about this same thing, sorry for the nagging!
I added it to the milestone. We can improve the error messages later. |
@asterite I had segfaults with XML in Crystal, and it turns out they were in |
Fixes #11673
Fixes #11994
I'll let CI run the specs because there's an XML spec that on my machine always segfaults and I still don't know why 🤷