[dart2wasm] Remove synthesizing values from nothing

When there's no expression on the stack but we expect something on the
stack, then the code should be unreachable.

Though the current code would just synthesize a value that matches
the expected type (`convertType(voidMarker, <some type>)`). This
is problematic: If we ever used that synthesized value we may
have incorrect program behavior.

Now there were some valid uses where we synthesize values

* A function that has `void` return type but no explicit return
  => Here we should synthesize `null`
* Synthesize `null` in cases where we know it's not going to be used
  => e.g. for CFE desugaring of `a[i] = b` is roughly
     `let tmp = b in (let ignored = a.[]=(tmp) in b)`
     where we synthesize `null` as `a.[]=(tmp)` result,
     `ignored` isn't used.
* ...

With this CL we no longer allow synthesizing a value of a type
out of thin air, instead all the places where this occurs have
to do that explicitly.

There's some impurities around how setters and index setters
are handled today (and even after this CL). Those impurities
start all the way at CFE, which treats setters and index
setters very differently. See the CFE issue [0].

For those we have two choices:

* special case all call sites that require synthesizing
  null values
* special case all call sites that require dropping an
  auto synthesized null value

This CL now marks instance setter/index-setter methods as
requiring auto-synthesizeing null values on usage sites and
make code that doesn't need them explicitly drop them.

Somewhat related to this change is how we deal with `void`
on the Dart <-> Wasm Import / Wasm Export boundary: When we
call an imported wasm function that has `void` as return
type (meaning no return values) we have to synthesize a `null`
(as the caller may "use"/"observe" the `void`).

=> We now are more strict and instead use `WasmVoid` as type
   instead of allowing `void` as type on the import/export
   functions.

[0] https://github.com/dart-lang/sdk/issues/63360

Change-Id: Ie30df3bd68553724437607bab3163c98f5467efe
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/501061
Reviewed-by: Srujan Gaddam <srujzs@google.com>
Commit-Queue: Martin Kustermann <kustermann@google.com>
This commit is contained in:
Martin Kustermann
2026-05-13 00:22:52 -07:00
committed by dart-scoped@luci-project-accounts.iam.gserviceaccount.com
parent 96a4dd4c19
commit d0e0290702
12 changed files with 156 additions and 95 deletions
@@ -41,9 +41,10 @@ external void _invokeMainInternal(WasmExternRef jsArray);
/// Used to invoke the `main` function from JS, printing any exceptions that
/// escape.
@pragma("wasm:export", "\$invokeMain")
void _invokeMain(WasmExternRef jsArrayRef) {
WasmVoid _invokeMain(WasmExternRef jsArrayRef) {
try {
_invokeMainInternal(jsArrayRef);
return WasmVoid();
} catch (e, s) {
print(e);
print(s);
@@ -455,11 +455,12 @@ WasmI32 _wasmI8ArrayGet(WasmExternRef? ref, WasmI32 index) {
}
@pragma("wasm:weak-export", "\$wasmI8ArraySet")
void _wasmI8ArraySet(WasmExternRef? ref, WasmI32 index, WasmI32 value) {
WasmVoid _wasmI8ArraySet(WasmExternRef? ref, WasmI32 index, WasmI32 value) {
final array = unsafeCastOpaque<WasmArray<WasmI8>>(
unsafeCast<WasmExternRef>(ref).internalize(),
);
array.write(index.toIntUnsigned(), value.toIntUnsigned());
return WasmVoid();
}
@pragma("wasm:export", "\$wasmI16ArrayGet")
@@ -471,11 +472,12 @@ WasmI32 _wasmI16ArrayGet(WasmExternRef? ref, WasmI32 index) {
}
@pragma("wasm:export", "\$wasmI16ArraySet")
void _wasmI16ArraySet(WasmExternRef? ref, WasmI32 index, WasmI32 value) {
WasmVoid _wasmI16ArraySet(WasmExternRef? ref, WasmI32 index, WasmI32 value) {
final array = unsafeCastOpaque<WasmArray<WasmI16>>(
unsafeCast<WasmExternRef>(ref).internalize(),
);
array.write(index.toIntUnsigned(), value.toIntUnsigned());
return WasmVoid();
}
@pragma("wasm:weak-export", "\$wasmI32ArrayGet")
@@ -487,11 +489,12 @@ WasmI32 _wasmI32ArrayGet(WasmExternRef? ref, WasmI32 index) {
}
@pragma("wasm:weak-export", "\$wasmI32ArraySet")
void _wasmI32ArraySet(WasmExternRef? ref, WasmI32 index, WasmI32 value) {
WasmVoid _wasmI32ArraySet(WasmExternRef? ref, WasmI32 index, WasmI32 value) {
final array = unsafeCastOpaque<WasmArray<WasmI32>>(
unsafeCast<WasmExternRef>(ref).internalize(),
);
array.write(index.toIntUnsigned(), value.toIntUnsigned());
return WasmVoid();
}
@pragma("wasm:weak-export", "\$wasmF32ArrayGet")
@@ -503,11 +506,12 @@ WasmF32 _wasmF32ArrayGet(WasmExternRef? ref, WasmI32 index) {
}
@pragma("wasm:weak-export", "\$wasmF32ArraySet")
void _wasmF32ArraySet(WasmExternRef? ref, WasmI32 index, WasmF32 value) {
WasmVoid _wasmF32ArraySet(WasmExternRef? ref, WasmI32 index, WasmF32 value) {
final array = unsafeCastOpaque<WasmArray<WasmF32>>(
unsafeCast<WasmExternRef>(ref).internalize(),
);
array[index.toIntUnsigned()] = value;
return WasmVoid();
}
@pragma("wasm:weak-export", "\$wasmF64ArrayGet")
@@ -519,9 +523,10 @@ WasmF64 _wasmF64ArrayGet(WasmExternRef? ref, WasmI32 index) {
}
@pragma("wasm:weak-export", "\$wasmF64ArraySet")
void _wasmF64ArraySet(WasmExternRef? ref, WasmI32 index, WasmF64 value) {
WasmVoid _wasmF64ArraySet(WasmExternRef? ref, WasmI32 index, WasmF64 value) {
final array = unsafeCastOpaque<WasmArray<WasmF64>>(
unsafeCast<WasmExternRef>(ref).internalize(),
);
array[index.toIntUnsigned()] = value;
return WasmVoid();
}