[dart2wasm] Use TFA-inferred types also for static fields

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>
diff --git a/pkg/dart2wasm/lib/globals.dart b/pkg/dart2wasm/lib/globals.dart
index eae306b..4ce65fb 100644
--- a/pkg/dart2wasm/lib/globals.dart
+++ b/pkg/dart2wasm/lib/globals.dart
@@ -138,16 +138,16 @@
 
   /// 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();
@@ -161,15 +161,15 @@
           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;
       }
     });
diff --git a/pkg/dart2wasm/lib/translator.dart b/pkg/dart2wasm/lib/translator.dart
index 7d4f330..7c56c5a 100644
--- a/pkg/dart2wasm/lib/translator.dart
+++ b/pkg/dart2wasm/lib/translator.dart
@@ -995,18 +995,18 @@
   }
 
   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(
@@ -1019,7 +1019,7 @@
   }
 
   w.ValueType translateTypeOfField(Field node) {
-    return translateType(_inferredTypeOfField(node) ?? node.type);
+    return translateType(typeOfField(node));
   }
 
   w.ValueType translateTypeOfLocalVariable(VariableDeclaration node) {
@@ -1080,6 +1080,20 @@
     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;
diff --git a/tests/web/wasm/lazy_static_number_initializer_regression_test.dart b/tests/web/wasm/lazy_static_number_initializer_regression_test.dart
new file mode 100644
index 0000000..313d440
--- /dev/null
+++ b/tests/web/wasm/lazy_static_number_initializer_regression_test.dart
@@ -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);
+}