[dart2wasm] Consolidate workarounds for type mismatches
In a various places in the compiler, we have had situations with
type mismatches that prompted workarounds.
These fell into a number of cases:
- Calls of functions with return type `Null` or `Never`. These were
treated like `void` and given no output types. This is now fixed, so
`Null` or `Never` in return types are just translated to their Wasm
representations, currently `(ref null #Top)`, later (when we add the
Wasm bottom types) `(ref null none)`.
- No-value `return` statements in `sync*` functions. Since we don't
support `sync*`, it's fine that these are broken.
- Constants passed directly to imported functions, or returned from
exported functions, such that the expected type is `externref`.
A special case is added for this.
- Constants produced by the TFA constant propagation in places where
they are incompatible with the context type (e.g. `null` in non-
nullable context). This is now handled generally in constant
instantiation and assumed to be always unreachable.
Change-Id: I43915db1b0c91ce0933a16bf36d6108a9cf390f4
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/268001
Reviewed-by: Ömer Ağacan <omersa@google.com>
Commit-Queue: Aske Simon Christensen <askesc@google.com>
diff --git a/pkg/dart2wasm/lib/constants.dart b/pkg/dart2wasm/lib/constants.dart
index 0526532..9f6f75a2 100644
--- a/pkg/dart2wasm/lib/constants.dart
+++ b/pkg/dart2wasm/lib/constants.dart
@@ -235,8 +235,17 @@
void instantiate(Constant constant) {
w.ValueType resultType = constant.accept(this);
- assert(!translator.needsConversion(resultType, expectedType),
- "For $constant: expected $expectedType, got $resultType");
+ if (translator.needsConversion(resultType, expectedType)) {
+ if (expectedType == const w.RefType.extern(nullable: true)) {
+ assert(resultType.isSubtypeOf(w.RefType.any(nullable: true)));
+ b.extern_externalize();
+ } else {
+ // This only happens in invalid but unreachable code produced by the
+ // TFA dead-code elimination.
+ b.comment("Constant in incompatible context");
+ b.unreachable();
+ }
+ }
}
@override
@@ -273,22 +282,11 @@
@override
w.ValueType visitNullConstant(NullConstant node) {
- w.ValueType? expectedType = this.expectedType;
- if (expectedType != translator.voidMarker) {
- if (expectedType.nullable) {
- w.HeapType heapType =
- expectedType is w.RefType ? expectedType.heapType : w.HeapType.data;
- b.ref_null(heapType);
- } else {
- // This only happens in invalid but unreachable code produced by the
- // TFA dead-code elimination.
- b.comment("Non-nullable null constant");
- b.block(const [], [expectedType]);
- b.unreachable();
- b.end();
- }
- }
- return expectedType;
+ w.ValueType expectedType = this.expectedType;
+ w.HeapType heapType =
+ expectedType is w.RefType ? expectedType.heapType : w.HeapType.data;
+ b.ref_null(heapType);
+ return w.RefType(heapType, nullable: true);
}
w.ValueType _maybeBox(w.ValueType wasmType, void Function() pushValue) {
diff --git a/pkg/dart2wasm/lib/functions.dart b/pkg/dart2wasm/lib/functions.dart
index 7e73b87..a72829d 100644
--- a/pkg/dart2wasm/lib/functions.dart
+++ b/pkg/dart2wasm/lib/functions.dart
@@ -239,9 +239,7 @@
inputs.addAll(
params.map((t) => adjustExternalType(translator.translateType(t))));
- List<w.ValueType> outputs = returnType is VoidType ||
- returnType is NeverType ||
- returnType is NullType
+ List<w.ValueType> outputs = returnType is VoidType
? member.function?.asyncMarker == AsyncMarker.Async
? [adjustExternalType(translator.topInfo.nullableType)]
: const []
diff --git a/pkg/dart2wasm/lib/translator.dart b/pkg/dart2wasm/lib/translator.dart
index 648e772..358796f 100644
--- a/pkg/dart2wasm/lib/translator.dart
+++ b/pkg/dart2wasm/lib/translator.dart
@@ -851,17 +851,11 @@
return;
}
if (to != voidMarker) {
- if (to is w.RefType && to.nullable) {
- // This can happen when a void method has its return type overridden
- // to return a value, in which case the selector signature will have a
- // non-void return type to encompass all possible return values.
- b.ref_null(to.heapType);
- } else {
- // This only happens in invalid but unreachable code produced by the
- // TFA dead-code elimination.
- b.comment("Non-nullable void conversion");
- b.unreachable();
- }
+ assert(to is w.RefType && to.nullable);
+ // This can happen when a void method has its return type overridden
+ // to return a value, in which case the selector signature will have a
+ // non-void return type to encompass all possible return values.
+ b.ref_null((to as w.RefType).heapType);
return;
}
}