[dart2js] Add abstract value domain to distinguish uncomputed values.

Normal dart2js invocations will continue using the existing abstract
value domains with `emptyType` as the initial type for type graph nodes.

When an appropriate debug flag is passed, all abstract values will be
wrapped so that the underlying empty type is only used when a value is
known to be empty. Abstract values which have not yet been computed will
print as "[uncomputed]" in order to aid in debugging but will otherwise
behave like the empty type during type graph construction.

Change-Id: I1ec41e42e8b566a0a6bfe969c2ff96f4e53d5f4e
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/243844
Commit-Queue: Mayank Patke <fishythefish@google.com>
Reviewed-by: Stephen Adams <sra@google.com>
diff --git a/pkg/compiler/lib/src/compiler.dart b/pkg/compiler/lib/src/compiler.dart
index a568685..951a49e 100644
--- a/pkg/compiler/lib/src/compiler.dart
+++ b/pkg/compiler/lib/src/compiler.dart
@@ -38,8 +38,9 @@
 import 'enqueue.dart' show Enqueuer;
 import 'environment.dart';
 import 'inferrer/abstract_value_strategy.dart';
-import 'inferrer/trivial.dart' show TrivialAbstractValueStrategy;
+import 'inferrer/computable.dart' show ComputableAbstractValueStrategy;
 import 'inferrer/powersets/powersets.dart' show PowersetStrategy;
+import 'inferrer/trivial.dart' show TrivialAbstractValueStrategy;
 import 'inferrer/typemasks/masks.dart' show TypeMaskStrategy;
 import 'inferrer/types.dart'
     show GlobalTypeInferenceResults, GlobalTypeInferenceTask;
@@ -189,6 +190,10 @@
     } else if (options.experimentalPowersets) {
       abstractValueStrategy = PowersetStrategy(abstractValueStrategy);
     }
+    if (options.debugGlobalInference) {
+      abstractValueStrategy =
+          ComputableAbstractValueStrategy(abstractValueStrategy);
+    }
 
     CompilerTask kernelFrontEndTask;
     selfTask = GenericTask('self', measurer);
diff --git a/pkg/compiler/lib/src/inferrer/abstract_value_domain.dart b/pkg/compiler/lib/src/inferrer/abstract_value_domain.dart
index 0df88c8..d27e038 100644
--- a/pkg/compiler/lib/src/inferrer/abstract_value_domain.dart
+++ b/pkg/compiler/lib/src/inferrer/abstract_value_domain.dart
@@ -92,6 +92,12 @@
 
 /// A system that implements an abstraction over runtime values.
 abstract class AbstractValueDomain {
+  /// The [AbstractValue] that represents a type which has not yet been
+  /// computed. Type graph nodes may carry this type during construction of the
+  /// graph, but it should be replaced by a computed type by the time the graph
+  /// is queried.
+  AbstractValue get uncomputedType => emptyType;
+
   /// The [AbstractValue] that represents an unknown runtime value. This
   /// includes values internal to the implementation, such as late sentinels.
   AbstractValue get internalTopType;
diff --git a/pkg/compiler/lib/src/inferrer/computable.dart b/pkg/compiler/lib/src/inferrer/computable.dart
new file mode 100644
index 0000000..b9380de
--- /dev/null
+++ b/pkg/compiler/lib/src/inferrer/computable.dart
@@ -0,0 +1,673 @@
+// Copyright (c) 2022, 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 '../constants/values.dart' show ConstantValue, PrimitiveConstantValue;
+import '../elements/entities.dart';
+import '../elements/names.dart';
+import '../elements/types.dart' show DartType;
+import '../ir/class_relation.dart';
+import '../serialization/serialization.dart';
+import '../universe/selector.dart';
+import '../universe/world_builder.dart';
+import '../universe/use.dart';
+import '../world.dart';
+import 'abstract_value_domain.dart';
+import 'abstract_value_strategy.dart';
+
+class ComputableAbstractValue implements AbstractValue {
+  final AbstractValue? _wrappedValue;
+  const ComputableAbstractValue(this._wrappedValue);
+
+  bool get isComputed => _wrappedValue != null;
+  bool get isUncomputed => _wrappedValue == null;
+
+  AbstractValue _unwrapOrThrow() => isUncomputed
+      ? throw StateError("Uncomputed abstract value")
+      : _wrappedValue!;
+
+  AbstractValue _unwrapOrEmpty(AbstractValueDomain wrappedDomain) =>
+      isUncomputed ? wrappedDomain.emptyType : _wrappedValue!;
+
+  @override
+  bool operator ==(Object other) {
+    if (identical(this, other)) return true;
+    if (other is ComputableAbstractValue) {
+      return _wrappedValue == other._wrappedValue;
+    }
+    return false;
+  }
+
+  @override
+  int get hashCode => _wrappedValue.hashCode;
+
+  @override
+  String toString() =>
+      isUncomputed ? "[uncomputed]" : _wrappedValue!.toString();
+}
+
+class ComputableAbstractValueDomain with AbstractValueDomain {
+  final AbstractValueDomain _wrappedDomain;
+  const ComputableAbstractValueDomain(this._wrappedDomain);
+
+  AbstractValue _unwrap(ComputableAbstractValue value) =>
+      value._unwrapOrEmpty(_wrappedDomain);
+
+  AbstractValue? _unwrapOrNull(ComputableAbstractValue? value) =>
+      value?._unwrapOrEmpty(_wrappedDomain);
+
+  @override
+  AbstractValue get uncomputedType => const ComputableAbstractValue(null);
+
+  @override
+  AbstractValue get internalTopType =>
+      ComputableAbstractValue(_wrappedDomain.internalTopType);
+
+  @override
+  AbstractValue get dynamicType =>
+      ComputableAbstractValue(_wrappedDomain.dynamicType);
+
+  @override
+  AbstractValue get typeType =>
+      ComputableAbstractValue(_wrappedDomain.typeType);
+
+  @override
+  AbstractValue get functionType =>
+      ComputableAbstractValue(_wrappedDomain.functionType);
+
+  @override
+  AbstractValue get boolType =>
+      ComputableAbstractValue(_wrappedDomain.boolType);
+
+  @override
+  AbstractValue get intType => ComputableAbstractValue(_wrappedDomain.intType);
+
+  @override
+  AbstractValue get numNotIntType =>
+      ComputableAbstractValue(_wrappedDomain.numNotIntType);
+
+  @override
+  AbstractValue get numType => ComputableAbstractValue(_wrappedDomain.numType);
+
+  @override
+  AbstractValue get stringType =>
+      ComputableAbstractValue(_wrappedDomain.stringType);
+
+  @override
+  AbstractValue get listType =>
+      ComputableAbstractValue(_wrappedDomain.listType);
+
+  @override
+  AbstractValue get setType => ComputableAbstractValue(_wrappedDomain.setType);
+
+  @override
+  AbstractValue get mapType => ComputableAbstractValue(_wrappedDomain.mapType);
+
+  @override
+  AbstractValue get nonNullType =>
+      ComputableAbstractValue(_wrappedDomain.nonNullType);
+
+  @override
+  AbstractValue get nullType =>
+      ComputableAbstractValue(_wrappedDomain.nullType);
+
+  @override
+  AbstractValue get lateSentinelType =>
+      ComputableAbstractValue(_wrappedDomain.lateSentinelType);
+
+  @override
+  AbstractValue get growableListType =>
+      ComputableAbstractValue(_wrappedDomain.growableListType);
+
+  @override
+  AbstractValue get fixedListType =>
+      ComputableAbstractValue(_wrappedDomain.fixedListType);
+
+  @override
+  AbstractValue get mutableArrayType =>
+      ComputableAbstractValue(_wrappedDomain.mutableArrayType);
+
+  @override
+  AbstractValue get uint31Type =>
+      ComputableAbstractValue(_wrappedDomain.uint31Type);
+
+  @override
+  AbstractValue get uint32Type =>
+      ComputableAbstractValue(_wrappedDomain.uint32Type);
+
+  @override
+  AbstractValue get positiveIntType =>
+      ComputableAbstractValue(_wrappedDomain.positiveIntType);
+
+  @override
+  AbstractValue get constListType =>
+      ComputableAbstractValue(_wrappedDomain.constListType);
+
+  @override
+  AbstractValue get constSetType =>
+      ComputableAbstractValue(_wrappedDomain.constSetType);
+
+  @override
+  AbstractValue get constMapType =>
+      ComputableAbstractValue(_wrappedDomain.constMapType);
+
+  @override
+  AbstractValue get emptyType =>
+      ComputableAbstractValue(_wrappedDomain.emptyType);
+
+  @override
+  AbstractValue get syncStarIterableType =>
+      ComputableAbstractValue(_wrappedDomain.syncStarIterableType);
+
+  @override
+  AbstractValue get asyncFutureType =>
+      ComputableAbstractValue(_wrappedDomain.asyncFutureType);
+
+  @override
+  AbstractValue get asyncStarStreamType =>
+      ComputableAbstractValue(_wrappedDomain.asyncStarStreamType);
+
+  @override
+  AbstractValueWithPrecision createFromStaticType(DartType type,
+      {ClassRelation classRelation = ClassRelation.subtype,
+      required bool nullable}) {
+    final unwrapped = _wrappedDomain.createFromStaticType(type,
+        classRelation: classRelation, nullable: nullable);
+    return AbstractValueWithPrecision(
+        ComputableAbstractValue(unwrapped.abstractValue), unwrapped.isPrecise);
+  }
+
+  @override
+  AbstractValue createNonNullExact(ClassEntity cls) =>
+      ComputableAbstractValue(_wrappedDomain.createNonNullExact(cls));
+
+  @override
+  AbstractValue createNullableExact(ClassEntity cls) =>
+      ComputableAbstractValue(_wrappedDomain.createNullableExact(cls));
+
+  @override
+  AbstractValue createNonNullSubclass(ClassEntity cls) =>
+      ComputableAbstractValue(_wrappedDomain.createNonNullSubclass(cls));
+
+  @override
+  AbstractValue createNonNullSubtype(ClassEntity cls) =>
+      ComputableAbstractValue(_wrappedDomain.createNonNullSubtype(cls));
+
+  @override
+  AbstractValue createNullableSubtype(ClassEntity cls) =>
+      ComputableAbstractValue(_wrappedDomain.createNullableSubtype(cls));
+
+  @override
+  AbstractBool isTypedArray(covariant ComputableAbstractValue value) =>
+      _wrappedDomain.isTypedArray(_unwrap(value));
+
+  @override
+  AbstractBool couldBeTypedArray(covariant ComputableAbstractValue value) =>
+      _wrappedDomain.couldBeTypedArray(_unwrap(value));
+
+  @override
+  AbstractValue excludeNull(covariant ComputableAbstractValue value) =>
+      ComputableAbstractValue(_wrappedDomain.excludeNull(_unwrap(value)));
+
+  @override
+  AbstractValue includeNull(covariant ComputableAbstractValue value) =>
+      ComputableAbstractValue(_wrappedDomain.includeNull(_unwrap(value)));
+
+  @override
+  AbstractValue excludeLateSentinel(covariant ComputableAbstractValue value) =>
+      ComputableAbstractValue(
+          _wrappedDomain.excludeLateSentinel(_unwrap(value)));
+
+  @override
+  AbstractValue includeLateSentinel(covariant ComputableAbstractValue value) =>
+      ComputableAbstractValue(
+          _wrappedDomain.includeLateSentinel(_unwrap(value)));
+
+  @override
+  AbstractBool containsType(
+          covariant ComputableAbstractValue value, ClassEntity cls) =>
+      _wrappedDomain.containsType(_unwrap(value), cls);
+
+  @override
+  AbstractBool containsOnlyType(
+          covariant ComputableAbstractValue value, ClassEntity cls) =>
+      _wrappedDomain.containsOnlyType(_unwrap(value), cls);
+
+  @override
+  AbstractBool isInstanceOfOrNull(
+          covariant ComputableAbstractValue value, ClassEntity cls) =>
+      _wrappedDomain.isInstanceOfOrNull(_unwrap(value), cls);
+
+  @override
+  AbstractBool isInstanceOf(
+          covariant ComputableAbstractValue value, ClassEntity cls) =>
+      _wrappedDomain.isInstanceOf(_unwrap(value), cls);
+
+  @override
+  AbstractBool isEmpty(covariant ComputableAbstractValue value) =>
+      _wrappedDomain.isEmpty(_unwrap(value));
+
+  @override
+  AbstractBool isExact(covariant ComputableAbstractValue value) =>
+      _wrappedDomain.isExact(_unwrap(value));
+
+  @override
+  ClassEntity? getExactClass(covariant ComputableAbstractValue value) =>
+      _wrappedDomain.getExactClass(_unwrap(value));
+
+  @override
+  AbstractBool isNull(covariant ComputableAbstractValue value) =>
+      _wrappedDomain.isNull(_unwrap(value));
+
+  @override
+  AbstractBool isLateSentinel(covariant ComputableAbstractValue value) =>
+      _wrappedDomain.isLateSentinel(_unwrap(value));
+
+  @override
+  AbstractBool isPrimitive(covariant ComputableAbstractValue value) =>
+      _wrappedDomain.isPrimitive(_unwrap(value));
+
+  @override
+  AbstractBool isPrimitiveNumber(covariant ComputableAbstractValue value) =>
+      _wrappedDomain.isPrimitiveNumber(_unwrap(value));
+
+  @override
+  AbstractBool isPrimitiveBoolean(covariant ComputableAbstractValue value) =>
+      _wrappedDomain.isPrimitiveBoolean(_unwrap(value));
+
+  @override
+  AbstractBool isIndexablePrimitive(covariant ComputableAbstractValue value) =>
+      _wrappedDomain.isIndexablePrimitive(_unwrap(value));
+
+  @override
+  AbstractBool isFixedArray(covariant ComputableAbstractValue value) =>
+      _wrappedDomain.isFixedArray(_unwrap(value));
+
+  @override
+  AbstractBool isExtendableArray(covariant ComputableAbstractValue value) =>
+      _wrappedDomain.isExtendableArray(_unwrap(value));
+
+  @override
+  AbstractBool isMutableArray(covariant ComputableAbstractValue value) =>
+      _wrappedDomain.isMutableArray(_unwrap(value));
+
+  @override
+  AbstractBool isMutableIndexable(covariant ComputableAbstractValue value) =>
+      _wrappedDomain.isMutableIndexable(_unwrap(value));
+
+  @override
+  AbstractBool isArray(covariant ComputableAbstractValue value) =>
+      _wrappedDomain.isArray(_unwrap(value));
+
+  @override
+  AbstractBool isPrimitiveString(covariant ComputableAbstractValue value) =>
+      _wrappedDomain.isPrimitiveString(_unwrap(value));
+
+  @override
+  AbstractBool isInterceptor(covariant ComputableAbstractValue value) =>
+      _wrappedDomain.isInterceptor(_unwrap(value));
+
+  @override
+  AbstractBool isInteger(covariant ComputableAbstractValue value) =>
+      _wrappedDomain.isInteger(_unwrap(value));
+
+  @override
+  AbstractBool isUInt32(covariant ComputableAbstractValue value) =>
+      _wrappedDomain.isUInt32(_unwrap(value));
+
+  @override
+  AbstractBool isUInt31(covariant ComputableAbstractValue value) =>
+      _wrappedDomain.isUInt31(_unwrap(value));
+
+  @override
+  AbstractBool isPositiveInteger(covariant ComputableAbstractValue value) =>
+      _wrappedDomain.isPositiveInteger(_unwrap(value));
+
+  @override
+  AbstractBool isPositiveIntegerOrNull(
+          covariant ComputableAbstractValue value) =>
+      _wrappedDomain.isPositiveIntegerOrNull(_unwrap(value));
+
+  @override
+  AbstractBool isIntegerOrNull(covariant ComputableAbstractValue value) =>
+      _wrappedDomain.isIntegerOrNull(_unwrap(value));
+
+  @override
+  AbstractBool isNumber(covariant ComputableAbstractValue value) =>
+      _wrappedDomain.isNumber(_unwrap(value));
+
+  @override
+  AbstractBool isNumberOrNull(covariant ComputableAbstractValue value) =>
+      _wrappedDomain.isNumberOrNull(_unwrap(value));
+
+  @override
+  AbstractBool isBoolean(covariant ComputableAbstractValue value) =>
+      _wrappedDomain.isBoolean(_unwrap(value));
+
+  @override
+  AbstractBool isBooleanOrNull(covariant ComputableAbstractValue value) =>
+      _wrappedDomain.isBooleanOrNull(_unwrap(value));
+
+  @override
+  AbstractBool isString(covariant ComputableAbstractValue value) =>
+      _wrappedDomain.isString(_unwrap(value));
+
+  @override
+  AbstractBool isStringOrNull(covariant ComputableAbstractValue value) =>
+      _wrappedDomain.isStringOrNull(_unwrap(value));
+
+  @override
+  AbstractBool isPrimitiveOrNull(covariant ComputableAbstractValue value) =>
+      _wrappedDomain.isPrimitiveOrNull(_unwrap(value));
+
+  @override
+  AbstractBool isTruthy(covariant ComputableAbstractValue value) =>
+      _wrappedDomain.isTruthy(_unwrap(value));
+
+  @override
+  AbstractValue union(covariant ComputableAbstractValue a,
+          covariant ComputableAbstractValue b) =>
+      ComputableAbstractValue(_wrappedDomain.union(_unwrap(a), _unwrap(b)));
+
+  @override
+  AbstractValue unionOfMany(covariant Iterable<AbstractValue> values) =>
+      ComputableAbstractValue(_wrappedDomain.unionOfMany(values.map(
+          (AbstractValue value) => _unwrap(value as ComputableAbstractValue))));
+
+  @override
+  AbstractValue intersection(covariant ComputableAbstractValue a,
+          covariant ComputableAbstractValue b) =>
+      ComputableAbstractValue(
+          _wrappedDomain.intersection(_unwrap(a), _unwrap(b)));
+
+  @override
+  AbstractBool areDisjoint(covariant ComputableAbstractValue a,
+          covariant ComputableAbstractValue b) =>
+      _wrappedDomain.areDisjoint(_unwrap(a), _unwrap(b));
+
+  @override
+  AbstractBool containsAll(covariant ComputableAbstractValue a) =>
+      _wrappedDomain.containsAll(_unwrap(a));
+
+  @override
+  AbstractValue computeAbstractValueForConstant(
+          covariant ConstantValue value) =>
+      ComputableAbstractValue(
+          _wrappedDomain.computeAbstractValueForConstant(value));
+
+  @override
+  bool isContainer(covariant ComputableAbstractValue value) =>
+      _wrappedDomain.isContainer(_unwrap(value));
+
+  @override
+  AbstractValue createContainerValue(
+          covariant ComputableAbstractValue? originalValue,
+          Object? allocationNode,
+          MemberEntity? allocationElement,
+          covariant ComputableAbstractValue elementType,
+          int? length) =>
+      ComputableAbstractValue(_wrappedDomain.createContainerValue(
+          _unwrapOrNull(originalValue),
+          allocationNode,
+          allocationElement,
+          _unwrap(elementType),
+          length));
+
+  @override
+  AbstractValue getContainerElementType(
+          covariant ComputableAbstractValue value) =>
+      ComputableAbstractValue(
+          _wrappedDomain.getContainerElementType(_unwrap(value)));
+
+  @override
+  int? getContainerLength(covariant ComputableAbstractValue value) =>
+      _wrappedDomain.getContainerLength(_unwrap(value));
+
+  @override
+  bool isSet(covariant ComputableAbstractValue value) =>
+      _wrappedDomain.isSet(_unwrap(value));
+
+  @override
+  AbstractValue createSetValue(
+          covariant ComputableAbstractValue? originalValue,
+          Object? allocationNode,
+          MemberEntity? allocationElement,
+          covariant ComputableAbstractValue elementType) =>
+      ComputableAbstractValue(_wrappedDomain.createSetValue(
+          _unwrapOrNull(originalValue),
+          allocationNode,
+          allocationElement,
+          _unwrap(elementType)));
+
+  @override
+  AbstractValue getSetElementType(covariant ComputableAbstractValue value) =>
+      ComputableAbstractValue(_wrappedDomain.getSetElementType(_unwrap(value)));
+
+  @override
+  bool isMap(covariant ComputableAbstractValue value) =>
+      _wrappedDomain.isMap(_unwrap(value));
+
+  @override
+  AbstractValue createMapValue(
+          covariant ComputableAbstractValue? originalValue,
+          Object? allocationNode,
+          MemberEntity? allocationElement,
+          covariant ComputableAbstractValue key,
+          covariant ComputableAbstractValue value) =>
+      ComputableAbstractValue(_wrappedDomain.createMapValue(
+          _unwrapOrNull(originalValue),
+          allocationNode,
+          allocationElement,
+          _unwrap(key),
+          _unwrap(value)));
+
+  @override
+  AbstractValue getMapKeyType(covariant ComputableAbstractValue value) =>
+      ComputableAbstractValue(_wrappedDomain.getMapKeyType(_unwrap(value)));
+
+  @override
+  AbstractValue getMapValueType(covariant ComputableAbstractValue value) =>
+      ComputableAbstractValue(_wrappedDomain.getMapValueType(_unwrap(value)));
+
+  @override
+  bool isDictionary(covariant ComputableAbstractValue value) =>
+      _wrappedDomain.isDictionary(_unwrap(value));
+
+  @override
+  AbstractValue createDictionaryValue(
+          covariant ComputableAbstractValue? originalValue,
+          Object? allocationNode,
+          MemberEntity? allocationElement,
+          covariant ComputableAbstractValue key,
+          covariant ComputableAbstractValue value,
+          covariant Map<String, AbstractValue> mappings) =>
+      ComputableAbstractValue(_wrappedDomain.createDictionaryValue(
+          _unwrapOrNull(originalValue),
+          allocationNode,
+          allocationElement,
+          _unwrap(key),
+          _unwrap(value), {
+        for (final entry in mappings.entries)
+          entry.key: _unwrap(entry.value as ComputableAbstractValue)
+      }));
+
+  @override
+  bool containsDictionaryKey(
+          covariant ComputableAbstractValue value, String key) =>
+      value.isComputed &&
+      _wrappedDomain.containsDictionaryKey(value._wrappedValue!, key);
+
+  @override
+  AbstractValue getDictionaryValueForKey(
+          covariant ComputableAbstractValue value, String key) =>
+      ComputableAbstractValue(
+          _wrappedDomain.getDictionaryValueForKey(_unwrap(value), key));
+
+  @override
+  bool isSpecializationOf(covariant ComputableAbstractValue specialization,
+          covariant ComputableAbstractValue generalization) =>
+      _wrappedDomain.isSpecializationOf(
+          _unwrap(specialization), _unwrap(generalization));
+
+  @override
+  AbstractValue? getGeneralization(covariant ComputableAbstractValue? value) {
+    final generalization =
+        _wrappedDomain.getGeneralization(_unwrapOrNull(value));
+    if (generalization == null) return null;
+    return ComputableAbstractValue(generalization);
+  }
+
+  @override
+  Object? getAllocationNode(covariant ComputableAbstractValue value) =>
+      _wrappedDomain.getAllocationNode(_unwrap(value));
+
+  @override
+  MemberEntity? getAllocationElement(covariant ComputableAbstractValue value) =>
+      _wrappedDomain.getAllocationElement(_unwrap(value));
+
+  @override
+  bool isPrimitiveValue(covariant ComputableAbstractValue value) =>
+      _wrappedDomain.isPrimitiveValue(_unwrap(value));
+
+  @override
+  AbstractValue createPrimitiveValue(
+          covariant ComputableAbstractValue originalValue,
+          PrimitiveConstantValue value) =>
+      ComputableAbstractValue(
+          _wrappedDomain.createPrimitiveValue(_unwrap(originalValue), value));
+
+  @override
+  PrimitiveConstantValue? getPrimitiveValue(
+          covariant ComputableAbstractValue value) =>
+      _wrappedDomain.getPrimitiveValue(_unwrap(value));
+
+  @override
+  AbstractValue computeReceiver(Iterable<MemberEntity> members) =>
+      ComputableAbstractValue(_wrappedDomain.computeReceiver(members));
+
+  @override
+  AbstractBool isTargetingMember(covariant ComputableAbstractValue receiver,
+          MemberEntity member, Name name) =>
+      _wrappedDomain.isTargetingMember(_unwrap(receiver), member, name);
+
+  @override
+  AbstractBool needsNoSuchMethodHandling(
+          covariant ComputableAbstractValue receiver, Selector selector) =>
+      _wrappedDomain.needsNoSuchMethodHandling(_unwrap(receiver), selector);
+
+  @override
+  AbstractValue? getAbstractValueForNativeMethodParameterType(DartType type) {
+    final value =
+        _wrappedDomain.getAbstractValueForNativeMethodParameterType(type);
+    if (value == null) return null;
+    return ComputableAbstractValue(value);
+  }
+
+  @override
+  AbstractBool isIn(covariant ComputableAbstractValue subset,
+          covariant ComputableAbstractValue superset) =>
+      _wrappedDomain.isIn(_unwrap(subset), _unwrap(superset));
+
+  @override
+  MemberEntity? locateSingleMember(
+          covariant ComputableAbstractValue receiver, Selector selector) =>
+      _wrappedDomain.locateSingleMember(_unwrap(receiver), selector);
+
+  @override
+  AbstractBool isJsIndexable(covariant ComputableAbstractValue value) =>
+      _wrappedDomain.isJsIndexable(_unwrap(value));
+
+  @override
+  AbstractBool isJsIndexableAndIterable(
+          covariant ComputableAbstractValue value) =>
+      _wrappedDomain.isJsIndexableAndIterable(_unwrap(value));
+
+  @override
+  AbstractBool isFixedLengthJsIndexable(
+          covariant ComputableAbstractValue value) =>
+      _wrappedDomain.isFixedLengthJsIndexable(_unwrap(value));
+
+  @override
+  String getCompactText(covariant ComputableAbstractValue value) =>
+      _wrappedDomain.getCompactText(_unwrap(value));
+
+  @override
+  AbstractValue readAbstractValueFromDataSource(DataSourceReader source) =>
+      ComputableAbstractValue(
+          _wrappedDomain.readAbstractValueFromDataSource(source));
+
+  @override
+  void writeAbstractValueToDataSink(
+      DataSinkWriter sink, covariant ComputableAbstractValue value) {
+    _wrappedDomain.writeAbstractValueToDataSink(sink, _unwrap(value));
+  }
+}
+
+class ComputableAbstractValueStrategy implements AbstractValueStrategy {
+  final AbstractValueStrategy _wrappedStrategy;
+
+  const ComputableAbstractValueStrategy(this._wrappedStrategy);
+
+  @override
+  AbstractValueDomain createDomain(JClosedWorld closedWorld) =>
+      ComputableAbstractValueDomain(_wrappedStrategy.createDomain(closedWorld));
+
+  @override
+  SelectorConstraintsStrategy createSelectorStrategy() =>
+      ComputableSelectorStrategy(_wrappedStrategy.createSelectorStrategy());
+}
+
+class ComputableSelectorStrategy implements SelectorConstraintsStrategy {
+  final SelectorConstraintsStrategy _wrappedStrategy;
+
+  const ComputableSelectorStrategy(this._wrappedStrategy);
+
+  // There should be no uncomputed values at this point, so throw instead of
+  // requiring a domain.
+  AbstractValue? _unwrap(ComputableAbstractValue? value) =>
+      value?._unwrapOrThrow();
+
+  @override
+  UniverseSelectorConstraints createSelectorConstraints(
+          Selector selector, Object? initialConstraint) =>
+      ComputableUniverseSelectorConstraints(
+          _wrappedStrategy.createSelectorConstraints(
+              selector, _unwrap(initialConstraint as ComputableAbstractValue)));
+
+  @override
+  bool appliedUnnamed(DynamicUse dynamicUse, MemberEntity member,
+          covariant JClosedWorld world) =>
+      _wrappedStrategy.appliedUnnamed(
+          dynamicUse.withReceiverConstraint(_unwrap(
+              dynamicUse.receiverConstraint as ComputableAbstractValue)),
+          member,
+          world);
+}
+
+class ComputableUniverseSelectorConstraints
+    implements UniverseSelectorConstraints {
+  final UniverseSelectorConstraints _universeSelectorConstraints;
+
+  const ComputableUniverseSelectorConstraints(
+      this._universeSelectorConstraints);
+
+  // There should be no uncomputed values at this point, so throw instead of
+  // requiring a domain.
+  AbstractValue? _unwrap(ComputableAbstractValue? value) =>
+      value?._unwrapOrThrow();
+
+  @override
+  bool addReceiverConstraint(covariant ComputableAbstractValue constraint) =>
+      _universeSelectorConstraints.addReceiverConstraint(_unwrap(constraint));
+
+  @override
+  bool needsNoSuchMethodHandling(Selector selector, World world) =>
+      _universeSelectorConstraints.needsNoSuchMethodHandling(selector, world);
+
+  @override
+  bool canHit(MemberEntity element, Name name, World world) =>
+      _universeSelectorConstraints.canHit(element, name, world);
+
+  @override
+  String toString() => 'ComputableUniverseSelectorConstraints:$hashCode';
+}
diff --git a/pkg/compiler/lib/src/inferrer/powersets/powersets.dart b/pkg/compiler/lib/src/inferrer/powersets/powersets.dart
index 8b53761..0449df3 100644
--- a/pkg/compiler/lib/src/inferrer/powersets/powersets.dart
+++ b/pkg/compiler/lib/src/inferrer/powersets/powersets.dart
@@ -53,7 +53,7 @@
       : PowersetValue(abstractValue, powersetBits);
 }
 
-class PowersetDomain implements AbstractValueDomain {
+class PowersetDomain with AbstractValueDomain {
   final AbstractValueDomain _abstractValueDomain;
   final PowersetBitsDomain _powersetBitsDomain;
 
diff --git a/pkg/compiler/lib/src/inferrer/trivial.dart b/pkg/compiler/lib/src/inferrer/trivial.dart
index 61dbb61..2ee1e5b 100644
--- a/pkg/compiler/lib/src/inferrer/trivial.dart
+++ b/pkg/compiler/lib/src/inferrer/trivial.dart
@@ -22,7 +22,7 @@
   String toString() => '?';
 }
 
-class TrivialAbstractValueDomain implements AbstractValueDomain {
+class TrivialAbstractValueDomain with AbstractValueDomain {
   const TrivialAbstractValueDomain();
 
   @override
diff --git a/pkg/compiler/lib/src/inferrer/type_graph_nodes.dart b/pkg/compiler/lib/src/inferrer/type_graph_nodes.dart
index fa20fb2..a6cd6eb 100644
--- a/pkg/compiler/lib/src/inferrer/type_graph_nodes.dart
+++ b/pkg/compiler/lib/src/inferrer/type_graph_nodes.dart
@@ -177,7 +177,7 @@
 
   bool reset(InferrerEngine inferrer) {
     if (abandonInferencing) return false;
-    type = inferrer.abstractValueDomain.emptyType;
+    type = inferrer.abstractValueDomain.uncomputedType;
     refineCount = 0;
     return true;
   }
@@ -247,7 +247,7 @@
 class PlaceholderTypeInformation extends TypeInformation {
   PlaceholderTypeInformation(
       AbstractValueDomain abstractValueDomain, MemberTypeInformation context)
-      : super(abstractValueDomain.emptyType, context);
+      : super(abstractValueDomain.uncomputedType, context);
 
   @override
   void accept(TypeInformationVisitor visitor) {
@@ -380,10 +380,11 @@
 
   ElementTypeInformation._internal(
       AbstractValueDomain abstractValueDomain, MemberTypeInformation? context)
-      : super(abstractValueDomain.emptyType, context);
+      : super(abstractValueDomain.uncomputedType, context);
+
   ElementTypeInformation._withInputs(AbstractValueDomain abstractValueDomain,
       MemberTypeInformation? context, ParameterInputs inputs)
-      : super.withInputs(abstractValueDomain.emptyType, context, inputs);
+      : super.withInputs(abstractValueDomain.uncomputedType, context, inputs);
 
   String getInferredSignature(TypeSystem types);
 
@@ -967,9 +968,8 @@
       this.selector,
       this.arguments,
       this.inLoop)
-      : super.noInputs(abstractValueDomain.emptyType, context) {
-    assert(_call is ir.Node || (_call == null && selector?.name == '=='));
-  }
+      : assert(_call is ir.Node || (_call == null && selector?.name == '==')),
+        super.noInputs(abstractValueDomain.uncomputedType, context);
 
   @override
   String toString() => 'Call site $debugName $type';
@@ -1603,7 +1603,7 @@
 
   NarrowTypeInformation(AbstractValueDomain abstractValueDomain,
       TypeInformation narrowedType, this.typeAnnotation)
-      : super(abstractValueDomain.emptyType, narrowedType.context) {
+      : super(abstractValueDomain.uncomputedType, narrowedType.context) {
     addInput(narrowedType);
   }
 
@@ -1643,7 +1643,7 @@
 
   InferredTypeInformation(AbstractValueDomain abstractValueDomain,
       MemberTypeInformation? context, TypeInformation? parentType)
-      : super(abstractValueDomain.emptyType, context) {
+      : super(abstractValueDomain.uncomputedType, context) {
     if (parentType != null) addInput(parentType);
   }
 
@@ -2025,7 +2025,7 @@
   PhiElementTypeInformation(AbstractValueDomain abstractValueDomain,
       MemberTypeInformation? context, this.branchNode, this.variable,
       {required this.isTry})
-      : super(abstractValueDomain.emptyType, context);
+      : super(abstractValueDomain.uncomputedType, context);
 
   @override
   AbstractValue computeType(InferrerEngine inferrer) {
@@ -2062,7 +2062,7 @@
 
   ClosureTypeInformation(AbstractValueDomain abstractValueDomain,
       MemberTypeInformation? context, this._element)
-      : super(abstractValueDomain.emptyType, context);
+      : super(abstractValueDomain.uncomputedType, context);
 
   FunctionEntity get closure => _element;
 
@@ -2126,7 +2126,7 @@
 
   AwaitTypeInformation(AbstractValueDomain abstractValueDomain,
       MemberTypeInformation context, this._node)
-      : super(abstractValueDomain.emptyType, context);
+      : super(abstractValueDomain.uncomputedType, context);
 
   // TODO(22894): Compute a better type here.
   @override
@@ -2148,7 +2148,7 @@
 
   YieldTypeInformation(AbstractValueDomain abstractValueDomain,
       MemberTypeInformation context, this._node)
-      : super(abstractValueDomain.emptyType, context);
+      : super(abstractValueDomain.uncomputedType, context);
 
   @override
   AbstractValue computeType(InferrerEngine inferrer) => safeType(inferrer);
diff --git a/pkg/compiler/lib/src/inferrer/typemasks/masks.dart b/pkg/compiler/lib/src/inferrer/typemasks/masks.dart
index b12d9c3..bdad0fe 100644
--- a/pkg/compiler/lib/src/inferrer/typemasks/masks.dart
+++ b/pkg/compiler/lib/src/inferrer/typemasks/masks.dart
@@ -35,7 +35,7 @@
 part 'union_type_mask.dart';
 part 'value_type_mask.dart';
 
-class CommonMasks implements AbstractValueDomain {
+class CommonMasks with AbstractValueDomain {
   // TODO(sigmund): once we split out the backend common elements, depend
   // directly on those instead.
   final JClosedWorld _closedWorld;
diff --git a/pkg/compiler/lib/src/inferrer/wrapped.dart b/pkg/compiler/lib/src/inferrer/wrapped.dart
index 455c84f..dbc1005 100644
--- a/pkg/compiler/lib/src/inferrer/wrapped.dart
+++ b/pkg/compiler/lib/src/inferrer/wrapped.dart
@@ -43,7 +43,7 @@
   return abstractValue == null ? null : WrappedAbstractValue(abstractValue);
 }
 
-class WrappedAbstractValueDomain implements AbstractValueDomain {
+class WrappedAbstractValueDomain with AbstractValueDomain {
   final AbstractValueDomain _abstractValueDomain;
   const WrappedAbstractValueDomain(this._abstractValueDomain);
 
diff --git a/pkg/compiler/lib/src/inferrer_experimental/powersets/powersets.dart b/pkg/compiler/lib/src/inferrer_experimental/powersets/powersets.dart
index 86bad4e..0b56518 100644
--- a/pkg/compiler/lib/src/inferrer_experimental/powersets/powersets.dart
+++ b/pkg/compiler/lib/src/inferrer_experimental/powersets/powersets.dart
@@ -53,7 +53,7 @@
       : PowersetValue(abstractValue, powersetBits);
 }
 
-class PowersetDomain implements AbstractValueDomain {
+class PowersetDomain with AbstractValueDomain {
   final AbstractValueDomain _abstractValueDomain;
   final PowersetBitsDomain _powersetBitsDomain;
 
diff --git a/pkg/compiler/lib/src/inferrer_experimental/trivial.dart b/pkg/compiler/lib/src/inferrer_experimental/trivial.dart
index ba3e67e..56a01aa 100644
--- a/pkg/compiler/lib/src/inferrer_experimental/trivial.dart
+++ b/pkg/compiler/lib/src/inferrer_experimental/trivial.dart
@@ -22,7 +22,7 @@
   String toString() => '?';
 }
 
-class TrivialAbstractValueDomain implements AbstractValueDomain {
+class TrivialAbstractValueDomain with AbstractValueDomain {
   const TrivialAbstractValueDomain();
 
   @override
diff --git a/pkg/compiler/lib/src/inferrer_experimental/type_graph_nodes.dart b/pkg/compiler/lib/src/inferrer_experimental/type_graph_nodes.dart
index cfd56d8..15128e4 100644
--- a/pkg/compiler/lib/src/inferrer_experimental/type_graph_nodes.dart
+++ b/pkg/compiler/lib/src/inferrer_experimental/type_graph_nodes.dart
@@ -177,7 +177,7 @@
 
   bool reset(InferrerEngine inferrer) {
     if (abandonInferencing) return false;
-    type = inferrer.abstractValueDomain.emptyType;
+    type = inferrer.abstractValueDomain.uncomputedType;
     refineCount = 0;
     return true;
   }
@@ -247,7 +247,7 @@
 class PlaceholderTypeInformation extends TypeInformation {
   PlaceholderTypeInformation(
       AbstractValueDomain abstractValueDomain, MemberTypeInformation context)
-      : super(abstractValueDomain.emptyType, context);
+      : super(abstractValueDomain.uncomputedType, context);
 
   @override
   void accept(TypeInformationVisitor visitor) {
@@ -380,10 +380,11 @@
 
   ElementTypeInformation._internal(
       AbstractValueDomain abstractValueDomain, MemberTypeInformation? context)
-      : super(abstractValueDomain.emptyType, context);
+      : super(abstractValueDomain.uncomputedType, context);
+
   ElementTypeInformation._withInputs(AbstractValueDomain abstractValueDomain,
       MemberTypeInformation? context, ParameterInputs inputs)
-      : super.withInputs(abstractValueDomain.emptyType, context, inputs);
+      : super.withInputs(abstractValueDomain.uncomputedType, context, inputs);
 
   String getInferredSignature(TypeSystem types);
 
@@ -967,9 +968,8 @@
       this.selector,
       this.arguments,
       this.inLoop)
-      : super.noInputs(abstractValueDomain.emptyType, context) {
-    assert(_call is ir.Node || (_call == null && selector?.name == '=='));
-  }
+      : assert(_call is ir.Node || (_call == null && selector?.name == '==')),
+        super.noInputs(abstractValueDomain.uncomputedType, context);
 
   @override
   String toString() => 'Call site $debugName $type';
@@ -1596,7 +1596,7 @@
 
   NarrowTypeInformation(AbstractValueDomain abstractValueDomain,
       TypeInformation narrowedType, this.typeAnnotation)
-      : super(abstractValueDomain.emptyType, narrowedType.context) {
+      : super(abstractValueDomain.uncomputedType, narrowedType.context) {
     addInput(narrowedType);
   }
 
@@ -1636,7 +1636,7 @@
 
   InferredTypeInformation(AbstractValueDomain abstractValueDomain,
       MemberTypeInformation? context, TypeInformation? parentType)
-      : super(abstractValueDomain.emptyType, context) {
+      : super(abstractValueDomain.uncomputedType, context) {
     if (parentType != null) addInput(parentType);
   }
 
@@ -2018,7 +2018,7 @@
   PhiElementTypeInformation(AbstractValueDomain abstractValueDomain,
       MemberTypeInformation? context, this.branchNode, this.variable,
       {required this.isTry})
-      : super(abstractValueDomain.emptyType, context);
+      : super(abstractValueDomain.uncomputedType, context);
 
   @override
   AbstractValue computeType(InferrerEngine inferrer) {
@@ -2055,7 +2055,7 @@
 
   ClosureTypeInformation(AbstractValueDomain abstractValueDomain,
       MemberTypeInformation? context, this._element)
-      : super(abstractValueDomain.emptyType, context);
+      : super(abstractValueDomain.uncomputedType, context);
 
   FunctionEntity get closure => _element;
 
@@ -2119,7 +2119,7 @@
 
   AwaitTypeInformation(AbstractValueDomain abstractValueDomain,
       MemberTypeInformation context, this._node)
-      : super(abstractValueDomain.emptyType, context);
+      : super(abstractValueDomain.uncomputedType, context);
 
   // TODO(22894): Compute a better type here.
   @override
@@ -2141,7 +2141,7 @@
 
   YieldTypeInformation(AbstractValueDomain abstractValueDomain,
       MemberTypeInformation context, this._node)
-      : super(abstractValueDomain.emptyType, context);
+      : super(abstractValueDomain.uncomputedType, context);
 
   @override
   AbstractValue computeType(InferrerEngine inferrer) => safeType(inferrer);
diff --git a/pkg/compiler/lib/src/inferrer_experimental/typemasks/masks.dart b/pkg/compiler/lib/src/inferrer_experimental/typemasks/masks.dart
index 209aa06..271bd78 100644
--- a/pkg/compiler/lib/src/inferrer_experimental/typemasks/masks.dart
+++ b/pkg/compiler/lib/src/inferrer_experimental/typemasks/masks.dart
@@ -35,7 +35,7 @@
 part 'union_type_mask.dart';
 part 'value_type_mask.dart';
 
-class CommonMasks implements AbstractValueDomain {
+class CommonMasks with AbstractValueDomain {
   // TODO(sigmund): once we split out the backend common elements, depend
   // directly on those instead.
   final JClosedWorld _closedWorld;
diff --git a/pkg/compiler/lib/src/inferrer_experimental/wrapped.dart b/pkg/compiler/lib/src/inferrer_experimental/wrapped.dart
index 2f5f04d..31188fa 100644
--- a/pkg/compiler/lib/src/inferrer_experimental/wrapped.dart
+++ b/pkg/compiler/lib/src/inferrer_experimental/wrapped.dart
@@ -43,7 +43,7 @@
   return abstractValue == null ? null : WrappedAbstractValue(abstractValue);
 }
 
-class WrappedAbstractValueDomain implements AbstractValueDomain {
+class WrappedAbstractValueDomain with AbstractValueDomain {
   final AbstractValueDomain _abstractValueDomain;
   const WrappedAbstractValueDomain(this._abstractValueDomain);