Remove the old constraint variable based nullability migration logic.

Change-Id: I4fe2836160acf5adfd805a327616880f857e9b7a
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/102387
Reviewed-by: Dan Rubel <danrubel@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
diff --git a/pkg/analysis_server/lib/src/nullability/constraint_gatherer.dart b/pkg/analysis_server/lib/src/nullability/constraint_gatherer.dart
index e2f24d9..5a56ba8 100644
--- a/pkg/analysis_server/lib/src/nullability/constraint_gatherer.dart
+++ b/pkg/analysis_server/lib/src/nullability/constraint_gatherer.dart
@@ -9,7 +9,6 @@
 import 'package:analysis_server/src/nullability/nullability_graph.dart';
 import 'package:analysis_server/src/nullability/nullability_node.dart';
 import 'package:analysis_server/src/nullability/transitional_api.dart';
-import 'package:analysis_server/src/nullability/unit_propagation.dart';
 import 'package:analyzer/dart/ast/ast.dart';
 import 'package:analyzer/dart/ast/token.dart';
 import 'package:analyzer/dart/ast/visitor.dart';
@@ -36,9 +35,6 @@
 
   final NullabilityMigrationAssumptions assumptions;
 
-  /// Constraints gathered by the visitor are stored here.
-  final Constraints _constraints;
-
   final NullabilityGraph _graph;
 
   /// The file being analyzed.
@@ -81,14 +77,8 @@
   /// or expression.
   bool _inConditionalControlFlow = false;
 
-  ConstraintGatherer(
-      TypeProvider typeProvider,
-      this._variables,
-      this._constraints,
-      this._graph,
-      this._source,
-      this._permissive,
-      this.assumptions)
+  ConstraintGatherer(TypeProvider typeProvider, this._variables, this._graph,
+      this._source, this._permissive, this.assumptions)
       : _notNullType = DecoratedType(
             typeProvider.objectType, NullabilityNode.never, _graph),
         _nonNullableBoolType =
@@ -132,8 +122,7 @@
       } else {
         throw element.runtimeType; // TODO(paulberry)
       }
-      return decoratedBaseType.substitute(
-          _constraints, _graph, substitution, elementType);
+      return decoratedBaseType.substitute(_graph, substitution, elementType);
     } else {
       return decoratedBaseType;
     }
@@ -146,7 +135,7 @@
       if (!_inConditionalControlFlow &&
           _conditionInfo.trueDemonstratesNonNullIntent != null) {
         _conditionInfo.trueDemonstratesNonNullIntent
-            ?.recordNonNullIntent(_constraints, _guards, _graph);
+            ?.recordNonNullIntent(_guards, _graph);
       }
     }
     node.message?.accept(this);
@@ -215,8 +204,7 @@
     assert(_isSimple(elseType)); // TODO(paulberry)
     var overallType = DecoratedType(
         node.staticType,
-        NullabilityNode.forLUB(
-            node, thenType.node, elseType.node, _graph, _joinNullabilities),
+        NullabilityNode.forLUB(node, thenType.node, elseType.node, _graph),
         _graph);
     _variables.recordDecoratedExpressionType(node, overallType);
     return overallType;
@@ -235,9 +223,7 @@
         NullabilityNode.recordAssignment(
             NullabilityNode.always,
             getOrComputeElementType(node.declaredElement).node,
-            null,
             _guards,
-            _constraints,
             _graph,
             false);
       } else {
@@ -360,8 +346,7 @@
     // Any parameters not supplied must be optional.
     for (var entry in calleeType.namedParameters.entries) {
       if (suppliedNamedParameters.contains(entry.key)) continue;
-      entry.value.node
-          .recordNamedParameterNotSupplied(_constraints, _guards, _graph);
+      entry.value.node.recordNamedParameterNotSupplied(_guards, _graph);
     }
     return calleeType.returnType;
   }
@@ -440,9 +425,7 @@
   /// where a nullable source is assigned to a non-nullable destination.
   void _checkAssignment(DecoratedType destinationType, DecoratedType sourceType,
       Expression expression) {
-    CheckExpression checkNotNull;
     if (expression != null) {
-      checkNotNull = CheckExpression(expression);
       _variables.recordExpressionChecks(
           _source,
           expression,
@@ -450,7 +433,7 @@
               expression.end, sourceType.node, destinationType.node, _guards));
     }
     NullabilityNode.recordAssignment(sourceType.node, destinationType.node,
-        checkNotNull, _guards, _constraints, _graph, _inConditionalControlFlow);
+        _guards, _graph, _inConditionalControlFlow);
     // TODO(paulberry): it's a cheat to pass in expression=null for the
     // recursive checks.  Really we want to unify all the checks in a single
     // ExpressionChecks object.
@@ -510,23 +493,6 @@
     if ((type.type as InterfaceType).typeParameters.isNotEmpty) return false;
     return true;
   }
-
-  /// Creates a constraint variable (if necessary) representing the nullability
-  /// of [node], which is the disjunction of the nullabilities [a] and [b].
-  ConstraintVariable _joinNullabilities(
-      Expression node, ConstraintVariable a, ConstraintVariable b) {
-    if (a == null) return b;
-    if (b == null) return a;
-    if (identical(a, ConstraintVariable.always) ||
-        identical(b, ConstraintVariable.always)) {
-      return ConstraintVariable.always;
-    }
-    var result = TypeIsNullable(node.offset);
-    _constraints.record([a], result);
-    _constraints.record([b], result);
-    _constraints.record([result], ConstraintVariable.or(_constraints, a, b));
-    return result;
-  }
 }
 
 /// Information about a binary expression whose boolean value could possibly
diff --git a/pkg/analysis_server/lib/src/nullability/constraint_variable_gatherer.dart b/pkg/analysis_server/lib/src/nullability/constraint_variable_gatherer.dart
index c7d0d5f..1db17fa 100644
--- a/pkg/analysis_server/lib/src/nullability/constraint_variable_gatherer.dart
+++ b/pkg/analysis_server/lib/src/nullability/constraint_variable_gatherer.dart
@@ -8,7 +8,6 @@
 import 'package:analysis_server/src/nullability/nullability_graph.dart';
 import 'package:analysis_server/src/nullability/nullability_node.dart';
 import 'package:analysis_server/src/nullability/transitional_api.dart';
-import 'package:analysis_server/src/nullability/unit_propagation.dart';
 import 'package:analyzer/dart/ast/ast.dart';
 import 'package:analyzer/dart/ast/visitor.dart';
 import 'package:analyzer/dart/element/element.dart';
@@ -57,7 +56,7 @@
         ? new DecoratedType(
             DynamicTypeImpl.instance,
             NullabilityNode.forInferredDynamicType(
-                _graph, _variables.constraints, enclosingNode.offset),
+                _graph, enclosingNode.offset),
             _graph)
         : type.accept(this);
   }
@@ -116,7 +115,6 @@
   DecoratedType visitSimpleFormalParameter(SimpleFormalParameter node) {
     var type = decorateType(node.type, node);
     var declaredElement = node.declaredElement;
-    type.node.trackNonNullIntent(node.offset);
     _variables.recordDecoratedElementType(declaredElement, type);
     if (declaredElement.isNamed) {
       _currentFunctionType.namedParameters[declaredElement.name] = type;
@@ -193,10 +191,6 @@
 /// ([ConstraintVariableGatherer], which finds all the variables that need to be
 /// constrained).
 abstract class VariableRecorder {
-  /// Gets the [Constraints] object currently in use.  Note: this will go away
-  /// when we stop using constraint variables for migration.
-  Constraints get constraints;
-
   /// Associates decorated type information with the given [element].
   void recordDecoratedElementType(Element element, DecoratedType type);
 
diff --git a/pkg/analysis_server/lib/src/nullability/decorated_type.dart b/pkg/analysis_server/lib/src/nullability/decorated_type.dart
index a926ba0..4ffa024 100644
--- a/pkg/analysis_server/lib/src/nullability/decorated_type.dart
+++ b/pkg/analysis_server/lib/src/nullability/decorated_type.dart
@@ -5,7 +5,6 @@
 import 'package:analysis_server/src/nullability/nullability_graph.dart';
 import 'package:analysis_server/src/nullability/nullability_node.dart';
 import 'package:analysis_server/src/nullability/transitional_api.dart';
-import 'package:analysis_server/src/nullability/unit_propagation.dart';
 import 'package:analyzer/dart/element/element.dart';
 import 'package:analyzer/dart/element/type.dart';
 import 'package:analyzer/src/dart/element/type.dart';
@@ -86,12 +85,11 @@
   /// [undecoratedResult] is the result of the substitution, as determined by
   /// the normal type system.
   DecoratedType substitute(
-      Constraints constraints,
       NullabilityGraph graph,
       Map<TypeParameterElement, DecoratedType> substitution,
       DartType undecoratedResult) {
     if (substitution.isEmpty) return this;
-    return _substitute(constraints, graph, substitution, undecoratedResult);
+    return _substitute(graph, substitution, undecoratedResult);
   }
 
   @override
@@ -122,7 +120,6 @@
 
   /// Internal implementation of [_substitute], used as a recursion target.
   DecoratedType _substitute(
-      Constraints constraints,
       NullabilityGraph graph,
       Map<TypeParameterElement, DecoratedType> substitution,
       DartType undecoratedResult) {
@@ -137,19 +134,17 @@
             ? undecoratedResult.normalParameterTypes[i]
             : undecoratedResult
                 .optionalParameterTypes[i - numRequiredParameters];
-        newPositionalParameters.add(positionalParameters[i]._substitute(
-            constraints, graph, substitution, undecoratedParameterType));
+        newPositionalParameters.add(positionalParameters[i]
+            ._substitute(graph, substitution, undecoratedParameterType));
       }
       return DecoratedType(undecoratedResult, node, graph,
           returnType: returnType._substitute(
-              constraints, graph, substitution, undecoratedResult.returnType),
+              graph, substitution, undecoratedResult.returnType),
           positionalParameters: newPositionalParameters);
     } else if (type is TypeParameterType) {
       var inner = substitution[type.element];
-      return DecoratedType(
-          undecoratedResult,
-          NullabilityNode.forSubstitution(constraints, inner?.node, node),
-          graph);
+      return DecoratedType(undecoratedResult,
+          NullabilityNode.forSubstitution(inner?.node, node), graph);
     } else if (type is VoidType) {
       return this;
     }
@@ -178,25 +173,3 @@
   Iterable<SourceEdit> get modifications =>
       isEmpty ? [] : [SourceEdit(_offset, 0, '?')];
 }
-
-/// Type of a [ConstraintVariable] representing the fact that a type is intended
-/// to be non-null.
-class NonNullIntent extends ConstraintVariable {
-  final int _offset;
-
-  NonNullIntent(this._offset);
-
-  @override
-  toString() => 'nonNullIntent($_offset)';
-}
-
-/// Type of a [ConstraintVariable] representing the fact that a type is
-/// nullable.
-class TypeIsNullable extends ConstraintVariable {
-  final int _offset;
-
-  TypeIsNullable(this._offset);
-
-  @override
-  toString() => 'nullable($_offset)';
-}
diff --git a/pkg/analysis_server/lib/src/nullability/nullability_graph.dart b/pkg/analysis_server/lib/src/nullability/nullability_graph.dart
index 0541ed0..fa7d16c 100644
--- a/pkg/analysis_server/lib/src/nullability/nullability_graph.dart
+++ b/pkg/analysis_server/lib/src/nullability/nullability_graph.dart
@@ -29,22 +29,6 @@
 
   final _nonNullIntentNodes = Set<NullabilityNode>.identity();
 
-  /// Verifies that the conclusions reached by [propagate] match the conclusions
-  /// reached by the old nullability propagation algorithm (using constraint
-  /// variables).
-  void check() {
-    try {
-      var allNodes = _downstream.keys.toSet();
-      allNodes.addAll(_upstream.keys);
-      for (var node in allNodes) {
-        node.check();
-      }
-    } catch (_) {
-      debugDump();
-      rethrow;
-    }
-  }
-
   /// Records that [sourceNode] is immediately upstream from [destinationNode].
   void connect(NullabilityNode sourceNode, NullabilityNode destinationNode,
       {bool unconditional: false, List<NullabilityNode> guards: const []}) {
diff --git a/pkg/analysis_server/lib/src/nullability/nullability_node.dart b/pkg/analysis_server/lib/src/nullability/nullability_node.dart
index a489dd4..70c0c89 100644
--- a/pkg/analysis_server/lib/src/nullability/nullability_node.dart
+++ b/pkg/analysis_server/lib/src/nullability/nullability_node.dart
@@ -2,10 +2,7 @@
 // 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 'package:analysis_server/src/nullability/decorated_type.dart';
 import 'package:analysis_server/src/nullability/nullability_graph.dart';
-import 'package:analysis_server/src/nullability/transitional_api.dart';
-import 'package:analysis_server/src/nullability/unit_propagation.dart';
 import 'package:analyzer/dart/ast/ast.dart';
 import 'package:meta/meta.dart';
 
@@ -19,24 +16,15 @@
   /// [NullabilityNode] used for types that are known a priori to be nullable
   /// (e.g. the type of the `null` literal).
   static final NullabilityNode always =
-      _NullabilityNodeImmutable(ConstraintVariable.always, 'always', true);
+      _NullabilityNodeImmutable('always', true);
 
   /// [NullabilityNode] used for types that are known a priori to be
   /// non-nullable (e.g. the type of an integer literal).
   static final NullabilityNode never =
-      _NullabilityNodeImmutable(null, 'never', false);
+      _NullabilityNodeImmutable('never', false);
 
   static final _debugNamesInUse = Set<String>();
 
-  /// [ConstraintVariable] whose value will be set to `true` if this type needs
-  /// to be nullable.
-  ///
-  /// If `null`, that means that an external constraint (outside the code being
-  /// migrated) forces this type to be non-nullable.
-  final ConstraintVariable _nullable;
-
-  ConstraintVariable _nonNullIntent;
-
   bool _isPossiblyOptional = false;
 
   String _debugName;
@@ -49,10 +37,8 @@
   /// TODO(paulberry): this should go away; we should decorate the actual
   /// inferred type rather than assuming `dynamic`.
   factory NullabilityNode.forInferredDynamicType(
-      NullabilityGraph graph, Constraints constraints, int offset) {
-    var node = _NullabilityNodeSimple(
-        TypeIsNullable(null), 'inferredDynamic($offset)');
-    constraints.record([], node._nullable);
+      NullabilityGraph graph, int offset) {
+    var node = _NullabilityNodeSimple('inferredDynamic($offset)');
     graph.connect(NullabilityNode.always, node);
     return node;
   }
@@ -68,10 +54,7 @@
       Expression conditionalExpression,
       NullabilityNode a,
       NullabilityNode b,
-      NullabilityGraph graph,
-      ConstraintVariable Function(
-              Expression, ConstraintVariable, ConstraintVariable)
-          joinNullabilities) = NullabilityNodeForLUB._;
+      NullabilityGraph graph) = NullabilityNodeForLUB._;
 
   /// Creates a [NullabilityNode] representing the nullability of a type
   /// substitution where [outerNode] is the nullability node for the type
@@ -85,19 +68,16 @@
   /// TODO(paulberry): this should become unnecessary once constraint solving is
   /// performed directly using [NullabilityNode] objects.
   factory NullabilityNode.forSubstitution(
-      Constraints constraints,
-      NullabilityNode innerNode,
-      NullabilityNode outerNode) = NullabilityNodeForSubstitution._;
+          NullabilityNode innerNode, NullabilityNode outerNode) =
+      NullabilityNodeForSubstitution._;
 
   /// Creates a [NullabilityNode] representing the nullability of a type
   /// annotation appearing explicitly in the user's program.
   factory NullabilityNode.forTypeAnnotation(int endOffset,
           {@required bool always}) =>
-      _NullabilityNodeSimple(
-          always ? ConstraintVariable.always : TypeIsNullable(endOffset),
-          'type($endOffset)');
+      _NullabilityNodeSimple('type($endOffset)');
 
-  NullabilityNode._(this._nullable, {bool initiallyNullable: false})
+  NullabilityNode._({bool initiallyNullable: false})
       : _isNullable = initiallyNullable;
 
   /// Gets a string that can be appended to a type name during debugging to help
@@ -113,18 +93,8 @@
   /// nullability migration needs to decide whether it is optional or required.
   bool get isPossiblyOptional => _isPossiblyOptional;
 
-  /// [ConstraintVariable] whose value will be set to `true` if the usage of
-  /// this type suggests that it is intended to be non-null (because of the
-  /// presence of a statement or expression that would unconditionally lead to
-  /// an exception being thrown in the case of a `null` value at runtime).
-  ConstraintVariable get nonNullIntent => _nonNullIntent;
-
   String get _debugPrefix;
 
-  /// After constraint solving, this getter can be used to query whether the
-  /// type associated with this node should be considered nullable.
-  bool get _oldIsNullable => _nullable == null ? false : _nullable.value;
-
   /// During constraint solving, this method marks the type as nullable, or does
   /// nothing if the type was already nullable.
   ///
@@ -135,29 +105,18 @@
     return true;
   }
 
-  /// Verifies that the old and new nullability propagation algorithms match.
-  void check() {
-    if (_oldIsNullable != this.isNullable) {
-      throw new StateError(
-          'For $this, new algorithm gives nullability $isNullable; '
-          'old algorithm gives $_oldIsNullable');
-    }
-  }
-
   /// Records the fact that an invocation was made to a function with named
   /// parameters, and the named parameter associated with this node was not
   /// supplied.
-  void recordNamedParameterNotSupplied(Constraints constraints,
+  void recordNamedParameterNotSupplied(
       List<NullabilityNode> guards, NullabilityGraph graph) {
     if (isPossiblyOptional) {
-      _recordConstraints(constraints, guards, const [], _nullable);
       graph.connect(NullabilityNode.always, this, guards: guards);
     }
   }
 
-  void recordNonNullIntent(Constraints constraints,
+  void recordNonNullIntent(
       List<NullabilityNode> guards, NullabilityGraph graph) {
-    _recordConstraints(constraints, guards, const [], nonNullIntent);
     graph.connect(this, NullabilityNode.never, unconditional: true);
   }
 
@@ -179,17 +138,6 @@
     return _debugName;
   }
 
-  /// Tracks that the possibility that this nullability node might demonstrate
-  /// non-null intent, based on the fact that it corresponds to a formal
-  /// parameter declaration at location [offset].
-  ///
-  /// TODO(paulberry): consider eliminating this method altogether, and simply
-  /// allowing all nullability nodes to track non-null intent if necessary.
-  void trackNonNullIntent(int offset) {
-    assert(_nonNullIntent == null);
-    _nonNullIntent = NonNullIntent(offset);
-  }
-
   /// Tracks the possibility that this node is associated with a named parameter
   /// for which nullability migration needs to decide whether it is optional or
   /// required.
@@ -210,58 +158,11 @@
   static void recordAssignment(
       NullabilityNode sourceNode,
       NullabilityNode destinationNode,
-      CheckExpression checkNotNull,
       List<NullabilityNode> guards,
-      Constraints constraints,
       NullabilityGraph graph,
       bool inConditionalControlFlow) {
-    var additionalConditions = <ConstraintVariable>[];
     graph.connect(sourceNode, destinationNode,
         guards: guards, unconditional: !inConditionalControlFlow);
-    if (sourceNode != NullabilityNode.never) {
-      additionalConditions.add(sourceNode._nullable);
-      var destinationNonNullIntent = destinationNode.nonNullIntent;
-      // nullable_src => nullable_dst | check_expr
-      _recordConstraints(
-          constraints,
-          guards,
-          additionalConditions,
-          ConstraintVariable.or(
-              constraints, destinationNode._nullable, checkNotNull));
-      if (checkNotNull != null) {
-        // nullable_src & nonNullIntent_dst => check_expr
-        if (destinationNonNullIntent != null) {
-          additionalConditions.add(destinationNonNullIntent);
-          _recordConstraints(
-              constraints, guards, additionalConditions, checkNotNull);
-        }
-      }
-      additionalConditions.clear();
-      var sourceNonNullIntent = sourceNode.nonNullIntent;
-      if (!inConditionalControlFlow && sourceNonNullIntent != null) {
-        if (destinationNode._nullable == null) {
-          // The destination type can never be nullable so this demonstrates
-          // non-null intent.
-          _recordConstraints(
-              constraints, guards, additionalConditions, sourceNonNullIntent);
-        } else if (destinationNonNullIntent != null) {
-          // Propagate non-null intent from the destination to the source.
-          additionalConditions.add(destinationNonNullIntent);
-          _recordConstraints(
-              constraints, guards, additionalConditions, sourceNonNullIntent);
-        }
-      }
-    }
-  }
-
-  static void _recordConstraints(
-      Constraints constraints,
-      List<NullabilityNode> guards,
-      List<ConstraintVariable> additionalConditions,
-      ConstraintVariable consequence) {
-    var conditions = guards.map((node) => node._nullable).toList();
-    conditions.addAll(additionalConditions);
-    constraints.record(conditions, consequence);
   }
 }
 
@@ -273,15 +174,8 @@
   final NullabilityNode right;
 
   NullabilityNodeForLUB._(
-      Expression expression,
-      this.left,
-      this.right,
-      NullabilityGraph graph,
-      ConstraintVariable Function(
-              ConditionalExpression, ConstraintVariable, ConstraintVariable)
-          joinNullabilities)
-      : super._(
-            joinNullabilities(expression, left._nullable, right._nullable)) {
+      Expression expression, this.left, this.right, NullabilityGraph graph)
+      : super._() {
     graph.connect(left, this);
     graph.connect(right, this);
   }
@@ -307,19 +201,15 @@
   /// `*` in `T*`.
   final NullabilityNode outerNode;
 
-  NullabilityNodeForSubstitution._(
-      Constraints constraints, this.innerNode, this.outerNode)
-      : super._(ConstraintVariable.or(
-            constraints, innerNode?._nullable, outerNode._nullable));
+  NullabilityNodeForSubstitution._(this.innerNode, this.outerNode) : super._();
 
   @override
   String get _debugPrefix => 'Substituted($innerNode, $outerNode)';
 }
 
 class _NullabilityNodeImmutable extends _NullabilityNodeSimple {
-  _NullabilityNodeImmutable(
-      ConstraintVariable nullable, String debugPrefix, bool isNullable)
-      : super(nullable, debugPrefix, initiallyNullable: isNullable);
+  _NullabilityNodeImmutable(String debugPrefix, bool isNullable)
+      : super(debugPrefix, initiallyNullable: isNullable);
 
   @override
   bool becomeNullable() {
@@ -332,7 +222,6 @@
   @override
   final String _debugPrefix;
 
-  _NullabilityNodeSimple(ConstraintVariable nullable, this._debugPrefix,
-      {bool initiallyNullable: false})
-      : super._(nullable, initiallyNullable: initiallyNullable);
+  _NullabilityNodeSimple(this._debugPrefix, {bool initiallyNullable: false})
+      : super._(initiallyNullable: initiallyNullable);
 }
diff --git a/pkg/analysis_server/lib/src/nullability/transitional_api.dart b/pkg/analysis_server/lib/src/nullability/transitional_api.dart
index ab436bd..261a03d 100644
--- a/pkg/analysis_server/lib/src/nullability/transitional_api.dart
+++ b/pkg/analysis_server/lib/src/nullability/transitional_api.dart
@@ -9,23 +9,12 @@
 import 'package:analysis_server/src/nullability/expression_checks.dart';
 import 'package:analysis_server/src/nullability/nullability_graph.dart';
 import 'package:analysis_server/src/nullability/nullability_node.dart';
-import 'package:analysis_server/src/nullability/unit_propagation.dart';
 import 'package:analyzer/dart/ast/ast.dart';
 import 'package:analyzer/dart/element/element.dart';
 import 'package:analyzer/src/generated/resolver.dart';
 import 'package:analyzer/src/generated/source.dart';
 import 'package:analyzer_plugin/protocol/protocol_common.dart' show SourceEdit;
 
-/// Type of a [ConstraintVariable] representing the addition of a null check.
-class CheckExpression extends ConstraintVariable {
-  final int offset;
-
-  CheckExpression(Expression expression) : offset = expression.end;
-
-  @override
-  toString() => 'checkNotNull($offset)';
-}
-
 /// Records information about how a conditional expression or statement might
 /// need to be modified.
 class ConditionalModification extends PotentialModification {
@@ -148,8 +137,6 @@
 
   final Variables _variables;
 
-  final Solver _constraints;
-
   final NullabilityGraph _graph;
 
   /// Prepares to perform nullability migration.
@@ -162,16 +149,13 @@
       {bool permissive: false,
       NullabilityMigrationAssumptions assumptions:
           const NullabilityMigrationAssumptions()})
-      : this._(permissive, assumptions, NullabilityGraph(), Solver());
+      : this._(permissive, assumptions, NullabilityGraph());
 
-  NullabilityMigration._(
-      this._permissive, this.assumptions, this._graph, this._constraints)
-      : _variables = Variables(_graph, _constraints);
+  NullabilityMigration._(this._permissive, this.assumptions, this._graph)
+      : _variables = Variables(_graph);
 
   Map<Source, List<PotentialModification>> finish() {
-    _constraints.applyHeuristics();
     _graph.propagate();
-    _graph.check();
     return _variables.getPotentialModifications();
   }
 
@@ -181,8 +165,8 @@
   }
 
   void processInput(CompilationUnit unit, TypeProvider typeProvider) {
-    unit.accept(ConstraintGatherer(typeProvider, _variables, _constraints,
-        _graph, unit.declaredElement.source, _permissive, assumptions));
+    unit.accept(ConstraintGatherer(typeProvider, _variables, _graph,
+        unit.declaredElement.source, _permissive, assumptions));
   }
 }
 
@@ -278,10 +262,7 @@
 
   final NullabilityGraph _graph;
 
-  @override
-  final Constraints constraints;
-
-  Variables(this._graph, this.constraints);
+  Variables(this._graph);
 
   @override
   DecoratedType decoratedElementType(Element element, {bool create: false}) =>
diff --git a/pkg/analysis_server/lib/src/nullability/unit_propagation.dart b/pkg/analysis_server/lib/src/nullability/unit_propagation.dart
deleted file mode 100644
index b587e33..0000000
--- a/pkg/analysis_server/lib/src/nullability/unit_propagation.dart
+++ /dev/null
@@ -1,187 +0,0 @@
-// Copyright (c) 2019, 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.
-
-/// Repository of constraints corresponding to the code being migrated.
-///
-/// This data structure carries information from the second pass of migration
-/// ([ConstraintGatherer]) to the third (which creates individual code
-/// modifications from each constraint).
-abstract class Constraints {
-  /// Records a new constraint equation.
-  void record(
-      Iterable<ConstraintVariable> conditions, ConstraintVariable consequence);
-}
-
-/// Representation of a single boolean variable in the constraint solver.
-class ConstraintVariable {
-  /// A special boolean variable whose value is known to be `true`.
-  static final ConstraintVariable always = _Always();
-
-  /// A list of all constraints containing this variable on their left hand side
-  /// that may not have been satisfied yet.
-  final _dependencies = <_Clause>[];
-
-  /// The value assigned to this constraint variable by the solution currently
-  /// being computed.
-  bool _value = false;
-
-  /// If this variable represents a disjunction ("or") of several other
-  /// variables, the variables in the disjunction.  Otherwise a singleton list
-  /// containing `this`.
-  final List<ConstraintVariable> _disjunctionParts;
-
-  ConstraintVariable() : _disjunctionParts = List.filled(1, null) {
-    _disjunctionParts[0] = this;
-  }
-
-  /// Creates a [ConstraintVariable] representing a disjunction ("or") of
-  /// several other variables.
-  ///
-  /// Additional constraints will be recorded in [constraints] to ensure that
-  /// the solution will be consistent.
-  factory ConstraintVariable.or(
-      Constraints constraints, ConstraintVariable a, ConstraintVariable b) {
-    if (a == null) return b;
-    if (b == null) return a;
-    var parts = a.disjunctionParts.toList();
-    parts.addAll(b.disjunctionParts);
-    assert(parts.length > 1);
-    var result = ConstraintVariable._(parts);
-    constraints.record([a], result);
-    constraints.record([b], result);
-    return result;
-  }
-
-  ConstraintVariable._(this._disjunctionParts);
-
-  /// If this variable represents a disjunction ("or") of several other
-  /// variables, the variables in the disjunction.  Otherwise a singleton list
-  /// containing `this`.
-  Iterable<ConstraintVariable> get disjunctionParts => _disjunctionParts;
-
-  /// Indicates whether this variable represents a disjunction ("or") of several
-  /// other variables.
-  bool get isDisjunction => _disjunctionParts.length > 1;
-
-  /// The value assigned to this constraint variable by the solution currently
-  /// being computed.
-  bool get value => _value;
-
-  @override
-  String toString() =>
-      isDisjunction ? '(${_disjunctionParts.join(' | ')})' : super.toString();
-}
-
-/// The core of the migration tool's constraint solver.  This class implements
-/// unit propagation (see https://en.wikipedia.org/wiki/Unit_propagation),
-/// extended to support disjunctions.
-///
-/// The extension works approximately as follows: first we perform ordinary unit
-/// propagation, accumulating a list of any disjunction variables that need to
-/// be assigned a value of `true`.  Once this finishes, we heuristically choose
-/// one of these disjunction variables and ensure that it is assigned a value of
-/// `true` by setting one of its constituent variables to `true` and propagating
-/// again.  Once all disjunctions have been resolved, we have a final solution.
-class Solver extends Constraints {
-  /// Clauses that should be evaluated as part of ordinary unit propagation.
-  final _pending = <_Clause>[];
-
-  /// Disjunction variables that have been determined by unit propagation to be
-  /// `true`, but for which we have not yet propagated the `true` value to one
-  /// of the constituent variables.
-  final _pendingDisjunctions = <ConstraintVariable>[];
-
-  /// Heuristically resolves any pending disjunctions.
-  void applyHeuristics() {
-    while (_pendingDisjunctions.isNotEmpty) {
-      var disjunction = _pendingDisjunctions.removeLast();
-      if (disjunction.disjunctionParts.any((v) => v.value)) continue;
-      // TODO(paulberry): smarter heuristics
-      var choice = disjunction.disjunctionParts.first;
-      record([], choice);
-    }
-  }
-
-  @override
-  void record(Iterable<ConstraintVariable> conditions,
-      covariant ConstraintVariable consequence) {
-    var _conditions = List<ConstraintVariable>.from(conditions);
-    var clause = _Clause(_conditions, consequence);
-    int i = 0;
-    while (i < _conditions.length) {
-      ConstraintVariable variable = _conditions[i];
-      if (variable._value) {
-        int j = _conditions.length - 1;
-        _conditions[i] = _conditions[j];
-        _conditions.removeLast();
-        continue;
-      }
-      variable._dependencies.add(clause);
-      i++;
-    }
-    if (i == 0) {
-      if (!consequence._value) {
-        consequence._value = true;
-        if (consequence.isDisjunction) {
-          _pendingDisjunctions.add(consequence);
-        }
-        _pending.addAll(consequence._dependencies);
-        _propagate();
-      }
-    } else {
-      consequence._dependencies.add(clause);
-    }
-  }
-
-  /// Performs ordinary unit propagation, recording any disjunctions encountered
-  /// in [_pendingDisjunctions].
-  void _propagate() {
-    while (_pending.isNotEmpty) {
-      var clause = _pending.removeLast();
-      var conditions = clause.conditions;
-      int i = 0;
-      while (i < conditions.length) {
-        ConstraintVariable variable = conditions[i];
-        if (variable._value) {
-          int j = conditions.length - 1;
-          conditions[i] = conditions[j];
-          conditions.removeLast();
-          continue;
-        }
-        i++;
-      }
-      if (i == 0) {
-        var consequence = clause.consequence;
-        if (!consequence._value) {
-          consequence._value = true;
-          if (consequence.isDisjunction) {
-            _pendingDisjunctions.add(consequence);
-          }
-          _pending.addAll(consequence._dependencies);
-        }
-      }
-    }
-  }
-}
-
-/// The special singleton [ConstraintVariable] whose value is always `true`.
-class _Always extends ConstraintVariable {
-  _Always() {
-    _value = true;
-  }
-
-  @override
-  String toString() => 'always';
-}
-
-/// A single equation in a system of constraints.
-class _Clause {
-  /// The conditions on the left hand side of the equation.
-  final List<ConstraintVariable> conditions;
-
-  /// The single variable on the right hand side of the equation.
-  final ConstraintVariable consequence;
-
-  _Clause(this.conditions, this.consequence);
-}
diff --git a/pkg/analysis_server/test/src/nullability/migration_visitor_test.dart b/pkg/analysis_server/test/src/nullability/migration_visitor_test.dart
index 51ae0e6..1255b40 100644
--- a/pkg/analysis_server/test/src/nullability/migration_visitor_test.dart
+++ b/pkg/analysis_server/test/src/nullability/migration_visitor_test.dart
@@ -10,7 +10,6 @@
 import 'package:analysis_server/src/nullability/nullability_graph.dart';
 import 'package:analysis_server/src/nullability/nullability_node.dart';
 import 'package:analysis_server/src/nullability/transitional_api.dart';
-import 'package:analysis_server/src/nullability/unit_propagation.dart';
 import 'package:analyzer/dart/ast/ast.dart';
 import 'package:analyzer/src/generated/resolver.dart';
 import 'package:analyzer/src/generated/source.dart';
@@ -42,32 +41,13 @@
     expect(graph.getDownstreamNodes(sourceNode), contains(destinationNode));
   }
 
-  /// Checks that a constraint was recorded with a left hand side of
-  /// [conditions] and a right hand side of [consequence].
-  void assertConstraint(
-      Iterable<ConstraintVariable> conditions, ConstraintVariable consequence) {
-    expect(constraints._clauses,
-        contains(_Clause(conditions.toSet(), consequence)));
-  }
-
-  /// Checks that no constraint was recorded with a right hand side of
-  /// [consequence].
-  void assertNoConstraints(ConstraintVariable consequence) {
-    expect(
-        constraints._clauses,
-        isNot(contains(
-            predicate((_Clause clause) => clause.consequence == consequence))));
-  }
-
   void assertNonNullIntent(NullabilityNode node, bool expected) {
     if (expected) {
       expect(graph.getUnconditionalUpstreamNodes(NullabilityNode.never),
           contains(node));
-      assertConstraint([], node.nonNullIntent);
     } else {
       expect(graph.getUnconditionalUpstreamNodes(NullabilityNode.never),
           isNot(contains(node)));
-      assertNoConstraints(node.nonNullIntent);
     }
   }
 
@@ -122,10 +102,7 @@
   test_always() async {
     await analyze('');
 
-    // No clause is needed for `always`; it is assigned the value `true` before
-    // solving begins.
-    assertNoConstraints(ConstraintVariable.always);
-    assert(ConstraintVariable.always.value, isTrue);
+    expect(NullabilityNode.always.isNullable, isTrue);
   }
 
   test_assert_demonstrates_non_null_intent() async {
@@ -445,7 +422,8 @@
     var int_2 = decoratedTypeAnnotation('int/*2*/');
     var i_3 = checkExpression('i/*3*/');
     assertNullCheck(i_3, int_2.node, contextNode: int_1.node);
-    assertConstraint([int_1.node.nonNullIntent], int_2.node.nonNullIntent);
+    expect(
+        graph.getUnconditionalUpstreamNodes(int_1.node), contains(int_2.node));
   }
 
   test_functionInvocation_parameter_named() async {
@@ -720,6 +698,12 @@
     assertNonNullIntent(decoratedTypeAnnotation('C c').node, true);
   }
 
+  test_never() async {
+    await analyze('');
+
+    expect(NullabilityNode.never.isNullable, isFalse);
+  }
+
   test_parenthesizedExpression() async {
     await analyze('''
 int f() {
@@ -801,8 +785,8 @@
       {NullabilityMigrationAssumptions assumptions:
           const NullabilityMigrationAssumptions()}) async {
     var unit = await super.analyze(code);
-    unit.accept(ConstraintGatherer(typeProvider, _variables, constraints, graph,
-        testSource, false, assumptions));
+    unit.accept(ConstraintGatherer(
+        typeProvider, _variables, graph, testSource, false, assumptions));
     return unit;
   }
 }
@@ -903,7 +887,6 @@
         same(decoratedType));
     expect(decoratedType.node, isNotNull);
     expect(decoratedType.node, isNot(NullabilityNode.never));
-    expect(decoratedType.node.nonNullIntent, isNotNull);
   }
 
   test_topLevelFunction_returnType_implicit_dynamic() async {
@@ -934,12 +917,9 @@
 
   final NullabilityGraph graph;
 
-  final _Constraints constraints;
+  MigrationVisitorTestBase() : this._(NullabilityGraph());
 
-  MigrationVisitorTestBase() : this._(NullabilityGraph(), _Constraints());
-
-  MigrationVisitorTestBase._(this.graph, this.constraints)
-      : _variables = _Variables(graph, constraints);
+  MigrationVisitorTestBase._(this.graph) : _variables = _Variables(graph);
 
   TypeProvider get typeProvider => testAnalysisResult.typeProvider;
 
@@ -971,52 +951,6 @@
   }
 }
 
-/// Mock representation of a constraint equation that is not connected to a
-/// constraint solver.  We use this to confirm that analysis produces the
-/// correct constraint equations.
-///
-/// [hashCode] and equality are implemented using [toString] for simplicity.
-class _Clause {
-  final Set<ConstraintVariable> conditions;
-  final ConstraintVariable consequence;
-
-  _Clause(this.conditions, this.consequence);
-
-  @override
-  int get hashCode => toString().hashCode;
-
-  @override
-  bool operator ==(Object other) =>
-      other is _Clause && toString() == other.toString();
-
-  @override
-  String toString() {
-    String lhs;
-    if (conditions.isNotEmpty) {
-      var sortedConditionStrings = conditions.map((v) => v.toString()).toList()
-        ..sort();
-      lhs = sortedConditionStrings.join(' & ') + ' => ';
-    } else {
-      lhs = '';
-    }
-    String rhs = consequence.toString();
-    return lhs + rhs;
-  }
-}
-
-/// Mock representation of a constraint solver that does not actually do any
-/// solving.  We use this to confirm that analysis produced the correct
-/// constraint equations.
-class _Constraints extends Constraints {
-  final _clauses = <_Clause>[];
-
-  @override
-  void record(
-      Iterable<ConstraintVariable> conditions, ConstraintVariable consequence) {
-    _clauses.add(_Clause(conditions.toSet(), consequence));
-  }
-}
-
 /// Mock representation of constraint variables.
 class _Variables extends Variables {
   final _conditionalDiscard = <AstNode, ConditionalDiscard>{};
@@ -1029,8 +963,7 @@
 
   final _possiblyOptional = <DefaultFormalParameter, NullabilityNode>{};
 
-  _Variables(NullabilityGraph graph, Constraints constraints)
-      : super(graph, constraints);
+  _Variables(NullabilityGraph graph) : super(graph);
 
   /// Gets the [ExpressionChecks] associated with the given [expression].
   ExpressionChecks checkExpression(Expression expression) =>
diff --git a/pkg/analysis_server/test/src/nullability/test_all.dart b/pkg/analysis_server/test/src/nullability/test_all.dart
index 77b5187..c43a34f 100644
--- a/pkg/analysis_server/test/src/nullability/test_all.dart
+++ b/pkg/analysis_server/test/src/nullability/test_all.dart
@@ -6,12 +6,10 @@
 
 import 'migration_visitor_test.dart' as migration_visitor_test;
 import 'provisional_api_test.dart' as provisional_api_test;
-import 'unit_propagation_test.dart' as unit_propagation_test;
 
 main() {
   defineReflectiveSuite(() {
     migration_visitor_test.main();
     provisional_api_test.main();
-    unit_propagation_test.main();
   });
 }
diff --git a/pkg/analysis_server/test/src/nullability/unit_propagation_test.dart b/pkg/analysis_server/test/src/nullability/unit_propagation_test.dart
deleted file mode 100644
index d2fcf89..0000000
--- a/pkg/analysis_server/test/src/nullability/unit_propagation_test.dart
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright (c) 2019, 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 'package:analysis_server/src/nullability/unit_propagation.dart';
-import 'package:test/test.dart';
-import 'package:test_reflective_loader/test_reflective_loader.dart';
-
-main() {
-  defineReflectiveSuite(() {
-    defineReflectiveTests(UnitPropagationTest);
-  });
-}
-
-/// TODO(paulberry): write more tests
-@reflectiveTest
-class UnitPropagationTest {
-  var solver = Solver();
-
-  ConstraintVariable newVar(String name) => _NamedConstraintVariable(name);
-
-  test_record_copies_conditions() {
-    var a = newVar('a');
-    var b = newVar('b');
-    var conditions = [a];
-    solver.record(conditions, b);
-    conditions.removeLast();
-    expect(a.value, false);
-    expect(b.value, false);
-    solver.record([], a);
-    expect(a.value, true);
-    expect(b.value, true);
-  }
-
-  test_record_propagates_true_variables_immediately() {
-    var a = newVar('a');
-    expect(a.value, false);
-    solver.record([], a);
-    expect(a.value, true);
-    var b = newVar('b');
-    expect(b.value, false);
-    solver.record([a], b);
-    expect(b.value, true);
-  }
-}
-
-/// Representation of a constraint variable with a specified name.
-///
-/// This makes test failures easier to comprehend.
-class _NamedConstraintVariable extends ConstraintVariable {
-  final String _name;
-
-  _NamedConstraintVariable(this._name);
-
-  @override
-  String toString() => _name;
-}