-
Notifications
You must be signed in to change notification settings - Fork 5.8k
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
AsmParser: parse source comment using scanner instead of regex #15209
Conversation
ff37008
to
f3b731e
Compare
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 some nitpicks and small suggestions.
Also, just to confirm/point to, I see that we have some tests in |
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.
This needs a changelog entry for the bugfix.
Also note that github is a bit picky about the fixes
/closes
annotation. fixes issue #15207
did not work, The PR is not connected to the issue and won't close it automatically. You need Fixes #15207
, i.e. without anything in between the word and the issue number.
Also, just to confirm/point to, I see that we have some tests in
test/libyul/Parser.cpp
which seems to cover parsing. Not sure if we need or would benefit from adding something more there.
The repro is a bit large and otherwise trivial, so I think it's ok to skip it.
I think we're missing some coverage for whitespace and newline handling behavior, because the PR passes all tests even though the way it handles whitespace seems to have changed.
7cf8e50
to
b3fd10e
Compare
b3fd10e
to
5bca035
Compare
I guess you could now also remove the |
5bca035
to
a1fb95f
Compare
Looks like it worked. Which is actually odd, because this PR doesn't fix #13496, does it? I mean, if it does then great, maybe we should close it too, but it's also possible that it got somehow fixed on |
It does segfault for me in the issue you linked above on develop and not on this branch when, e.g., invoking Invoking the same with |
yep exactly! its because of the debug comments. I ran into this when I was working on the debug attribute parsing stuff. I would say we should merge this soon so I can rework my PR. |
In general it is a bug in |
a1fb95f
to
64ab6be
Compare
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 think we still need some some adjustments in the scanner and a few more tests. Other than that it looks pretty good now.
liblangutil/Scanner.cpp
Outdated
if (c == '\\') | ||
scanEscape(); |
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.
If I understand this code correctly, this does not quite match the behavior of the original regex. The code here will just skip invalid escape sequences and stray \
at the end of input. If we want to match the original regex, we'd have to instead:
- not interpret unrecognized escapes, just keep them as is,
- report an unterminated string if we reach
\
at the end of input (well, I guess reporting an illegal escape instead would be fine too, as long as it's still an error).
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 it's true, it wasn't reflecting the RegEx behavior - although snippets are really just skipped during parsing, the important bit is the correct detection of the tail. That being said, it absolutely makes sense to interpret the 'special comment' literal string sensibly. Now everything that is escaped and interpretable is being interpreted as such and the rest is appended verbatim. I have added a couple tests to the scanner for that.
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.
By the way, the most sensible behavior for such escapes would actually be to report an error, but that's breaking. They should never happen in Yul coming from the codegen and are only possible in user-supplied Yul, so maybe it's acceptable, buy I wouldn't do it in this PR. Maybe this is something that we should consider later though. Really, neither skipping them, nor keeping them "as is" is ideal.
Ah, right. I focused on parsing the long hex string (which should not be affected by this PR) but overlooked the fact that such a string will also end up in a snippet as a part of debug info. In that case, yeah, this PR does fix it. |
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.
This generally looks good so I'm approving already.
I still have some final suggestions, mostly regarding wording and comments. The only bigger one would to change how unterminated escapes are handled, though that's not incorrect, just leaves some unexpected artifacts in the (invalid) literal.
liblangutil/Scanner.cpp
Outdated
if (c == '\\') | ||
scanEscape(); |
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.
It would be more consistent with the other branch to treat unterminated \
as IllegalEscapeSequence
. Currently you continue, run scanEscape()
and then error with IllegalStringEndQuote
right after the loop.
It does work, but it's still weird to let scanEscape()
run when we're at the end of input and m_char
is 0
, which I guess is assumed to never actually be read. The function will actually take that 0
and put into the literal and that literal is still accessible via currentLiteral()
even when we error out.
Also, you should either handle the return value from scanEscape()
or assert that it's not supposed to fail:
if (c == '\\') | |
scanEscape(); | |
if (c == '\\') | |
{ | |
if (isSourcePastEndOfInput()) | |
return setError(ScannerError::IllegalEscapeSequence); | |
bool validEscape = scanEscape(); | |
solAssert(validEscape); | |
} |
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.
Right! Done that, tests now reflect a case where there's a dangling backslash R"("test\)"
and a missing string end quote R"("test)"
.
liblangutil/Scanner.cpp
Outdated
{ | ||
if (isSourcePastEndOfInput()) | ||
return setError(ScannerError::IllegalEscapeSequence); | ||
bool const validEscape = scanEscape(false /* _rejectInvalidEscapes */); |
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.
So just that I understand correctly: the only thing you need to do here is to check for escaped quotes, right? Couldn't you do that here directly instead of the entire refactoring of scanEscape
? We don't actually do anything with the source quote in the snippets other than discarding them, so not sure why we'd go out of our way to deal with all sorts of escape sequences here...
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.
The change was based off of this comment. I am completely fine with just checking for escaped quotes as well - all depends on the intended use of the scanner mode beyond this pr I guess.
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.
scanEscape(false)
is generally rather weird, though, isn't it? Either you want to properly handle all kinds of escape sequences or you don't, but handling some properly and ignoring others is strange, isn't it :-)?
I think the simpler solution to just only handle escaped quotes here matches more directly what we need now, and keeps the PR simpler - and if we ever want to use this for anything else we can revisit this then...
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.
We don't even need to check for escaped quotes, this should be enough for the purpose of just skipping over it
if (c == '\\')
{
if (isSourcePastEndOfInput())
return setError(ScannerError::IllegalEscapeSequence);
advance();
}
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, and that's exactly what the regex did, right? Let's just do that then.
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.
Was a code snippet runinng to EOF here actually even an error before the change :-)?
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.
It would have reported an error because of missing end quote (eg "literal\
- the \
is no longer part of the match so the regex would have found "literal
). Or did you mean in the current develop implementation? There as well
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.
Well, when scanning string literals hitting EOF is an error. But I meant, whether it was an error if a source snippet of a comment in Yul, parsed with the regex, it was an error if the quote didn't terminate :-).
Technically, it it wasn't an error then, making it an error now is breaking.
There is error 1544_error even before this PR, but will this ever be hit in this case, i.e. will the regex even match that? Ah, the terminating quote there is "?
right? So we do get that error in that case also on develop? Then it's fine.
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, the previous condition for error 1544 is rather convoluted, but looks like that's what it did.
96c162f
to
7086538
Compare
/// @src 1 : 111: | ||
/// 222 " | ||
/// abc\"def | ||
/// | ||
/// " @src 0:333:444 |
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.
Where and how does this actually happen now - and how does it prevent
/// @src 1:1:1 "
let x := 42
/// "
from swallowing the non-comment?
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.
in the example it'll just skip all escaped stuff, so the snippet amounts to abcdef
. the non-comment is not swallowed because it will never reach that part of the asmparser, there's an outer loop taking care of that, so only 1:1:1 "
would arrive in parseSrcComment
and error out with unterminated quote
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.
so more precisely the input to parseSrcComment
in the test you referenced up there is (with escapes)
1\t: 111:\n222 "\nabc\"def\n\n" @src 0:333:444
for which (1,111,222)
is identified as components and abcdef
as snippet (the rest is now skipped by scanner as discussed); everything else goes into the tail
(being @src 0:333:444
) which is returned to an outer loop.
The outer loop will match it as source location comment and parseSrcComment
is called again, this time with 0:333:444
as input.
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.
Ah, the point is that Scanner::scanSingleLineDocComment()
just merges the contents of all consecutive lines starting with ///
(but not more than that) - alright!
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'd say I'm reasonably convinced by this now and Kamil approved it earlier, so I'm fine with merging after squashing the commits a bit!
7086538
to
8b0bb61
Compare
Just to leave it here: the segmentation fault was related to a bug in GCC's |
Fixes #15207
Fixes #13496