-
-
Notifications
You must be signed in to change notification settings - Fork 670
Fix compiler crash when certain operators used on types #2 #2293
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
Conversation
@@ -9237,13 +9237,10 @@ export class Compiler extends DiagnosticEmitter { | |||
// make a getter for the expression (also obtains the type) | |||
var getValue = this.compileExpression( // reports | |||
expression.operand, | |||
contextualType.exceptVoid, | |||
contextualType, |
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.
Iirc, .exceptVoid
makes sure that if contextualType
is somehow void
(could be a result of a previous error), that compileExpression
here doesn't produce a drop
, an expression that is never, say, assignable to a local, which would lead to validation errors in Binaryen. A way to test whether this leads to problems with the change could be a test for ++someFunctionReturningVoid()
.
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.
ERROR TS2357: The operand of an increment or decrement operator must be a variable or a property access.
++foo();
~~~~~
in increment-error.ts(7,3)
FAILURE 1 parse error(s)
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.
Ok, that's already good. Then I wonder if there is still a way to produce a void
type using a variable or property access, perhaps via a previous error. The general idea is that we don't want Binaryen to blow up but to diagnose every possible error ourselves. Do you know why exactly this is blowing up with .exceptVoid
in place?
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.
Yes, because there is a missing check. I am adding a new commit right now.
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.
Taking a step back, am I assuming correctly that the use of .exceptVoid
prevents a fix in makeAssignment
? If so, what is target.kind
in makeAssignment
with .exceptVoid
in place and does commenting out the assert
being hit still produce the expected diagnostic?
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.
Hmm, what about removing the last assert
in makeAssignment
and add a default case?
+ default: {
+ this.error(
+ DiagnosticCode.The_target_of_an_assignment_must_be_a_variable_or_a_property_access,
+ valueExpression.range
+ );
+ }
}
- assert(false);
return module.unreachable();
ERROR AS234: Expression does not compile to a value at runtime.
++String;
~~~~~~
ERROR TS2541: The target of an assignment must be a variable or a property access.
++String;
~~~~~~
Essentially I am not sure about the .exceptVoid
change as it seems unrelated. If something goes wrong when compiling with .exceptVoid
, this would already produce an error before the resulting compiled expression is returned (here the case: AS234, just not diagnosed because an assert
is hit before diagnostics are printed), with the compiled expression not really mattering anymore, except that it cannot be a drop
, which .exceptVoid
prevents. The alternative is ofc to add a bunch of late checks, but that seems good to avoid.
Unfortunately, though, my modification only works for ++String
from the initial example, but not ++i32
, but the latter is probably a separate bug related to i32(...)
being a builtin function.
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 doesn't Foo += 1
use .exceptVoid
? My changes directly reflect the former's implementation; they are, in essence, the same thing. Do all of those compound operators need changing?
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.
My concern is mostly that .exceptVoid
seems unrelated in this PR, so that the fix can be simpler (no additional late checks). It might be the case that .exceptVoid
can be removed, or it might not be the case, but I'm hesitant to make this call 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.
Do you insist on this asymmetry? If Foo
can be void, then what happens when there is Foo += 1
instead of Foo++
? I lean towards either removing .exceptVoid
or changing the implementation of all compound operators. I'm fine with either of the aforementioned.
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 be fine to thoroughly investigate the asymmetry separately I think. If Foo +=1
does not use .exceptVoid
, it seems that it should to be sure. Other than that it doesn't seem necessary to remove .exceptVoid
as it serves a purpose, even if the issue it prevents is untypical.
Are the errors in this test satisfactory? The primary aim of this PR is to unite the behaviors of |
The issue
When the prefix compound unary operators (
++x
and--x
) are used on types, the compiler crashesExample
Solution
Purely removing
.exceptVoid
seems to work, though I'm not sure if it's the right way to go. I haven't been able to completely parse what.exceptVoid
even is, but it seems to have no place in the functionality of an operator that cannot work on thevoid
type anyway.