-
-
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
Nil assertion failed when accessing $~ #4776
Comments
The documentation says:
So |
@straight-shoota I understand, but this behavior makes impossible to use $~ whithin a condition expression after trying to match, for example: https://carc.in/#/r/2gma "a" =~ /b/ || $~ |
Hmm. I think that works if you use an explicit if "a" =~ /b/
$~ # or whatever
end This might be a situation where crystal could be smarter about the one-liner case. |
@drosehn I ran up into this incompatibility when trying to port this Ruby code: def auto_linked?(left, right)
(left =~ AUTO_LINK_CRE[0] and right =~ AUTO_LINK_CRE[1]) or
(left.rindex(AUTO_LINK_CRE[2]) and $' !~ AUTO_LINK_CRE[3])
end I found a workaround, but the same happens for IMO |
I'm just another crystal user here, not a developer of the compiler. This seems to me like a situation where the compiler could do a better job, but I don't have the experience or time to investigate/fix the compiler itself. I was just suggesting one tactic you could try to work around it. As something of an aside: Note that crystal never promises to be completely compatible with ruby. In some situations it absolutely cannot be (because the goal is to compile into very fast code). In other situations the people who are doing all the work to write crystal happen to have their own opinions on the best way for the language to work. They might agree completely with my earlier comment, or they might not. However I know that I don't have the time to write my own compiler (not even if I spent the rest of my life trying to do it!). Cheerio. |
@hugoabonizio But I actually agree with the part of your opinion: "the error message should be more accurate at least." |
@drosehn I understand the Crystal goals, just pointed this situation where - agreeing with you - "the compiler could do a better job". Thank you for your help! 😃 @makenowjust I understand, probably just fixing this inconsistency with other regex related methods could fix the problem. For now I found a workaround and moved on, but a better error message implemented by someone with more knoledge than me on the compiler world be nice! |
It would be sweet if the compiler would somehow force you to check for a match instead of blowing up later with an NPE...since crystal in other places is "nil safe" as it were. If possible... |
Given "foo" =~ /bar/
puts $~ raises this is at least a documentation bug, though I would prefer the documented behavior too. I wonder if we can't rewrite something like puts $~ if "foo" =~ /bar/ to something like: "foo" =~ /bar/
__temp123 = assign_match
puts __temp123 if __temp123 to preserve the "no nil check inside if" behavior of |
That might actually be a valid option. The comment in https://github.com/crystal-lang/crystal/blob/master/src/compiler/crystal/semantic/main_visitor.cr#L576-L581 states that it adds
So I suppose we should remove that |
Maybe could introduce |
No, that defeats the purpose. |
This can be one reason to remove the dollar sign notation (#6969). |
Sorry, could someone write a "tl;dr" summary of the problem and proposed solution?
if "foo" =~ /bar/
# Because the `if` check guarantees that $~ will be set, this will never fail
puts $~
end Or: case "foo"
when /bar/
puts $1
end which is essentially the same. But note that That's why the compiler inserts case "foo"
when /bar/
# Ugh, why am I checking for not nil if I know this is not nil?
puts $~.not_nil!
end So what are the proposals to improve this? (or why is this a problem in the first place? the code from OP seems like ficticious code, I never had |
Note it can also be reproduced with all dollar variants foo =~ /bar/
puts $~ Reversing the order, by mistake, returns an error. Another issue is this variable get leaked: if "foo" =~ /foo/
puts $~
end
puts $~ Logically, the variable should be scoped, and not available before using a regex match. |
You're right, these global variables have some special use cases and one could consider them usually guarded by a regex match. I think I somehow mixed up The issue discussed here is that when not used in a guarded branch, As long as you only use In case it goes wrong, the user should be presented with a meaningful error message. As mentioned in #4776 (comment) the documentation currently states that So it would be an option to actually change the implementation to what is documented. But given the special nature of these global variables, I suppose this is not a good idea as per @asterite's comment. In any case, the documentation should reflect the actual behaviour. |
Improving the error messages is a good idea 👍 |
I never had any issue. I always access $ variables after verifying that there was a match. I'd be very annoyed if suddenly I had nilable $ match variables. Now, the injected not nil check message could be more explicit about having no match (unexpected). Similar to how [] accessors raise an explicit message / exception. |
At least, |
@j8r |
You know it's about a regex syntax @straight-shoota , compared to nothing at all - it helps. |
It might help a bit, but it's not enough. If we're changing it, we should change it to a good error message, not a slightly less shitty one. |
Honestly, I wouldn't be suuuper opposed to just dropping the whole affair (including if match = foo.match(/bar/)
puts match[1]
end Yes these are nice for little one of scripts, but I don't see that as Crystal's domain anyway. For more serious software the above trumps in clarity. |
I'd also support removing that feature entirely an alternative option. This was discussed before in #6969 and decided to keep them (though not a strong decision). When matching a regex as a |
I wouldn't mind dropping them and having to write it like in #6969 (comment) : case
when match = line_to_parse.match(/host=(.*)/)
hostname = match[1]
when match = line_to_parse.match(/credentials=(.*?):(.*)/)
user = match[1]
password = match[2]
end vs. case line_to_parse
when /host=(.*)/
hostname = $1
when /credentials=(.*?):(.*)/
user = $1
password = $2
end it's a bit more verbose but: no magic, simpler language, simpler compiler. But it almost reduces the main use case of |
@asterite It's going to be even more verbose when the
Oh yes. It might not be a bad move if we could remove that as well... It's always difficult to grasp the difference between But if we want to continue in this direction, we should have a dedicated discussion about that, and especially look closer at the consequences such a change would have. |
I really tried to find an user friendly alternative, there is this option. |
I think ruby uses |
Sorry, I meant |
Can someone actually detail what the issue is and code to reproduce it here? Is it to do with the compiler flow checking regex matches and replacing Either way the error message is bad, but i'm not sure if it's occurring when it should or not? And if it's occuring in sane code? |
Yes, it's the implicit A simple reproducible sample already mentioned above is |
@straight-shoota is |
AFAIK the special variable |
what the fuck |
Haha, yeah, there's a bit of magic going on there. The But I don't think it matters how it's implemented as long as it behaves correctly. I do think we can and should improve the error message. I started working on this but stopped because I have limited time, but once I have more time I can fix it. |
To drive this forward, I think we can make a simple improvement to the error message. This should give the user more context information which helps locate the error.
For implementing both of this, it would be useful to have a parameter on |
A little wired when code only one line. puts $0
No any clue point out what happen. Although, add one line before it, will point out the correct line in backtrace. x = 100
puts $0
|
I think that might be a bug with the stacktrace decoder. |
I would probably use a different exception than |
Yes, probably. It's definitely unrelated to this problem (although I see how it increases confusion; but it's unlikely to have |
When a regex match fails, accessing $~ raises an exception: https://carc.in/#/r/2gks
However, I think the Ruby's behavior would be more useful in this case which is return nil: https://carc.in/#/r/2gkt
Maybe related: #3978 #3648
The text was updated successfully, but these errors were encountered: