Don't leak variable name when assigning to ivar/cvar in method signature#6007
Don't leak variable name when assigning to ivar/cvar in method signature#6007
Conversation
| name = "__arg#{@temp_arg_count}" | ||
| @temp_arg_count += 1 | ||
| name | ||
| end |
There was a problem hiding this comment.
Can/Should this check if the name already exists in the current scope and discard it? Say I do
def foo(__arg0, @foo)
endThere was a problem hiding this comment.
The convention is that variables that start with __ are reserved for the compiler, so they shouldn't clash with user variables. If a user choose to name their variables __argN, they are warned that that's problematic (at least in my mind :-P).
I don't want to make things more complex than they should, at this point.
There was a problem hiding this comment.
Mmh, not asking to do this here, but do you think it would actually be possible to make trying to use these a parser error then? I don't think it's too uncommon for somebody to try to use such variable names, especially when dealing with macros.
There was a problem hiding this comment.
I'm not sure it can be done. Sometimes methods are passed to macros, and these are to_s'd, and then reparsed again... and that would error when it shouldn't.
Maybe it's something to consider in the future.
Note that right now the __arg or __temp hack is used all over the place in the compiler (like, when you do if a() || b(), this isn't something new. And so far it didn't cause any problem, so I guess it's fine.
|
I don't like the sound of this.. How do you resolve this then: #3413 |
src/compiler/crystal/types.cr
Outdated
|
|
||
| def initialize(program, namespace, name, @superclass, add_subclass = true) | ||
| super(program, namespace, name) | ||
| superclass = @superclass |
There was a problem hiding this comment.
It might be a style issue, but I think it makes more sense the other way around: @superclass = superclass (and superclass as arg). That's how it worked implicitly before and prevents surprises caused by mismatching types between argument and ivar.
| @reached_end = false | ||
|
|
||
| def initialize(@range : Range(B, E), @current = range.begin, @reached_end = false) | ||
| def initialize(@range : Range(B, E)) |
There was a problem hiding this comment.
Here you're also changing the ctor signature, we can no more setup current & reached_end. Is that intentional?
There was a problem hiding this comment.
It's a private class. And the removed arguments don't seem to be used anywhere. Wouldn't make much sense anyway.
There was a problem hiding this comment.
The constructor was used internally, so no problem. Same in other places.
|
|
||
| def initialize(@address : String, @port : Int32) | ||
| def initialize(address : String, @port : Int32) | ||
| @address = address |
There was a problem hiding this comment.
Why this change?
Ditto a few times below, where inline ivar init is ok.
There was a problem hiding this comment.
address is used in the next line and it should be type String. @address would be String?.
There was a problem hiding this comment.
Because @address is String? and the methods/conditions bellow won't work. But address is String and that works.
There was a problem hiding this comment.
Oh wow, that's weird to read ^^ but makes sense, ty
|
@bew I don't know the fix to #3413 but I don't know how much it has to do with this PR. The fix is probably disallowing instance variables after |
|
I see it related by the suggested solution #3413 (comment) class A
getter :a, :b
def initialize(@a : Int32, @b : Int32 = a)
end
end
a = A.new a: 1
puts a.b # => 1That won't work anymore due to this PR. To me this solution was beautifully clean, but I can understand why having an implicit local var can be unexpected.. Maybe that other issue could be solved (in a later PR) by changing |
|
@bew True that workaround won't work but I think that's a good thing. Shadowing ivar arguments as local vars can be unexpected. In this example it would seem that |
Right, you'll have to do: class A
getter :a, :b
def initialize(a : Int32, b : Int32 = a)
@a = a
@b = b
end
endAaaaand... that's why I'd like to remove the |
|
@straight-shoota And in fact it will now refer to the getter, and give you 0 (uninitialized memory). That's a separate issue, though... but yeah, with this PR it's likely than a lot of code right now will compile just fine and behave in a weird way (segfaults, wrong memory, etc.) |
|
Meh, I don't know why I'm even trying... my conclusion, again, as usual, is that nothing can be changed/fixed at the moment because there are so many broken/missing things that changing one thing will break another thing. This whole thing needs to be redone (and first rethought) from scratch. |
|
Are you sure about those memory issues? I assume that'd only be a problem with constructors, right? And even there... what can break that's currently working and won't raise a compiler error with this PR? There shouldn't be an issue with uninitialized memory in the example- With the PR it's essentially going to be equal to this: class A
def a_method
@a
end
def initialize(@a : Int32, @b : Int32 = a_method)
end
end |
|
To me the old solution was beautifully clean, but I can understand why having an implicit local var can be unexpected.. What's wrong here? |
|
@straight-shoota Right, and the problem is that See how that feature is not intuitive at all? |
|
Essentially, it gets rewritten as: class A
def a_method
@a
end
def initialize(a : Int32)
initialize(a, a_method) # oops!
end
def initialize(a : Int32, b : Int32)
@a = a
@b = b
end
endBut maybe someone can find a better way to implement that... |
|
Maybe we should always fully expand the body with named arguments... a lot of code duplication, and it will slow down compilation, but it's probably for the best. |
|
I like this PR, it shouldn't be closed. Yes, working on the compiler is frustrating but that's not a reason not to do it. It seems we're just missing a few rules about the scopes that default arguments can access. |
|
@asterite My code works fine right now. It gives a nice compiler error. I don't see a reason why this PR would change that. If there was really an issue, a simple fix would just be to disallow instance method calls as default arguments in constructors. |
|
Reopened. Do we really want to go forward with this change, or just documenting that: def foo(@var)
endexpands to: def foo(var)
@var = var
endis good? I don't mind the second option. It's how the language has been working so far. And if you have a local method with that same name, just use What do you think? |
|
I don't understand why it only fails in Travis :-( |
49b239d to
2b3c696
Compare
|
This deserves more ❤️ |
|
I think this should be closed. The current behaviour is fine. |
Fixes #5997
Fixes #4332
The idea is that this:
expands to this:
instead of:
so you don't have access to that "implicit" local variable anymore.
I think this is good. It's a breaking change, but it's more intuitive. If you want it assigned to a local variable, explicitly assign it to a local variable.