Skip to content

Commit

Permalink
[dart2wasm] Use TFA-inferred types also for static fields
Browse files Browse the repository at this point in the history
Currently the type of a static field is only based on the dart type, not
inferred types. This can mean that the type of such field is e.g.
`dynamic`.

Though the return type of the initializer function used the unboxing
information.

=> There was an invariant that if we use `null` to signal uninitialized
in the global, then calling the initializer will also result in a
non-nullable reference type.

=> This was invariant was broken by recent changes to dart2wasm that
take advantage of TFA-inferred unboxing information.

=> We fix this inconsistency by ensuring the wasm global's type is the
same as the initializer function's return type.

Change-Id: I9c27c0b28d2f81f888b4c9afc59dad42af2229ab
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/365825
Reviewed-by: Slava Egorov <vegorov@google.com>
Commit-Queue: Martin Kustermann <kustermann@google.com>
  • Loading branch information
mkustermann authored and Commit Queue committed May 14, 2024
1 parent 31eb496 commit 3dfee27
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 22 deletions.
18 changes: 9 additions & 9 deletions pkg/dart2wasm/lib/globals.dart
Original file line number Diff line number Diff line change
Expand Up @@ -138,16 +138,16 @@ class Globals {

/// Return (and if needed create) the Wasm global corresponding to a static
/// field.
w.Global getGlobal(Field variable) {
assert(!variable.isLate);
return _globals.putIfAbsent(variable, () {
w.ValueType type = translator.translateType(variable.type);
Constant? init = _getConstantInitializer(variable);
w.Global getGlobal(Field field) {
assert(!field.isLate);
return _globals.putIfAbsent(field, () {
final Constant? init = _getConstantInitializer(field);
w.ValueType type = translator.translateTypeOfField(field);
if (init != null &&
!(translator.constants.ensureConstant(init)?.isLazy ?? false)) {
// Initialized to a constant
final global =
m.globals.define(w.GlobalType(type, mutable: !variable.isFinal));
m.globals.define(w.GlobalType(type, mutable: !field.isFinal));
translator.constants
.instantiateConstant(null, global.initializer, init, type);
global.initializer.end();
Expand All @@ -161,15 +161,15 @@ class Globals {
final flag = m.globals.define(w.GlobalType(w.NumType.i32));
flag.initializer.i32_const(0);
flag.initializer.end();
_globalInitializedFlag[variable] = flag;
_globalInitializedFlag[field] = flag;
}

final global = m.globals.define(w.GlobalType(type));
instantiateDummyValue(global.initializer, type);
global.initializer.end();

_globalInitializers[variable] =
translator.functions.getFunction(variable.fieldReference);
_globalInitializers[field] =
translator.functions.getFunction(field.fieldReference);
return global;
}
});
Expand Down
40 changes: 27 additions & 13 deletions pkg/dart2wasm/lib/translator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -995,18 +995,18 @@ class Translator with KernelNodes {
}

DartType typeOfReturnValue(Member member) {
final unboxingInfo = unboxingInfoMetadata[member];
if (unboxingInfo != null) {
final returnInfo = unboxingInfo.returnInfo;
if (returnInfo.kind == UnboxingKind.int) {
return coreTypes.intRawType(Nullability.nonNullable);
}
if (returnInfo.kind == UnboxingKind.double) {
return coreTypes.doubleRawType(Nullability.nonNullable);
}
}
if (member is Field) return member.type;
return member.function!.returnType;
if (member is Field) return typeOfField(member);

// TODO(http://dartbug.com/55668): Once TFA annotates members with inferred
// return types we should make use of them here.
return _inferredUnboxedReturnType(member) ?? member.function!.returnType;
}

DartType typeOfField(Field node) {
assert(!node.isLate);
return _inferredUnboxedReturnType(node) ??
_inferredTypeOfField(node) ??
node.type;
}

w.ValueType translateTypeOfParameter(
Expand All @@ -1019,7 +1019,7 @@ class Translator with KernelNodes {
}

w.ValueType translateTypeOfField(Field node) {
return translateType(_inferredTypeOfField(node) ?? node.type);
return translateType(typeOfField(node));
}

w.ValueType translateTypeOfLocalVariable(VariableDeclaration node) {
Expand Down Expand Up @@ -1080,6 +1080,20 @@ class Translator with KernelNodes {
return InterfaceType(concreteClass, nullability, typeArguments);
}

DartType? _inferredUnboxedReturnType(Member node) {
final unboxingInfo = unboxingInfoMetadata[node];
if (unboxingInfo == null) return null;

final returnInfo = unboxingInfo.returnInfo;
if (returnInfo.kind == UnboxingKind.int) {
return coreTypes.intRawType(Nullability.nonNullable);
}
if (returnInfo.kind == UnboxingKind.double) {
return coreTypes.doubleRawType(Nullability.nonNullable);
}
return null;
}

bool shouldInline(Reference target) {
if (!options.inlining) return false;
Member member = target.asMember;
Expand Down
24 changes: 24 additions & 0 deletions tests/web/wasm/lazy_static_number_initializer_regression_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'dart:math';

import 'package:expect/expect.dart';

final num maxInt = 1 is double ? pow(2, 52) : 1.0e300.floor();

dynamic round(dynamic number) {
if (number is num) {
if (number.isInfinite) {
return maxInt;
}
return number.round();
}
return number;
}

main() {
Expect.equals(round(1.0), 1);
Expect.equals(round(1), 1);
}

0 comments on commit 3dfee27

Please sign in to comment.