[cfe] Separate out IntersectionType from TypeParameterType

TEST=Covered by existing tests

Change-Id: Ie7b99b1c109edff5198cfbf5d22e1cfb1dc130d2
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/253665
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Reviewed-by: Mayank Patke <fishythefish@google.com>
Reviewed-by: Alexander Markov <alexmarkov@google.com>
Reviewed-by: Nicholas Shahan <nshahan@google.com>
Commit-Queue: Chloe Stefantsova <cstefantsova@google.com>
diff --git a/pkg/_fe_analyzer_shared/test/flow_analysis/type_promotion/data/type_parameter.dart b/pkg/_fe_analyzer_shared/test/flow_analysis/type_promotion/data/type_parameter.dart
index 5b091df..b51fc45 100644
--- a/pkg/_fe_analyzer_shared/test/flow_analysis/type_promotion/data/type_parameter.dart
+++ b/pkg/_fe_analyzer_shared/test/flow_analysis/type_promotion/data/type_parameter.dart
@@ -12,7 +12,7 @@
   void promoteNullable(T? t) {
     T? s;
     if (t is int) {
-      s = /*T & int*/ t;
+      s = /*cfe.T? & int*/ /*analyzer.T & int*/ t;
     }
   }
 
diff --git a/pkg/compiler/lib/src/ir/scope_visitor.dart b/pkg/compiler/lib/src/ir/scope_visitor.dart
index 113387f..8b57cf3 100644
--- a/pkg/compiler/lib/src/ir/scope_visitor.dart
+++ b/pkg/compiler/lib/src/ir/scope_visitor.dart
@@ -755,6 +755,12 @@
     return const EvaluationComplexity.lazy();
   }
 
+  @override
+  EvaluationComplexity visitIntersectionType(ir.IntersectionType node) {
+    _analyzeTypeVariable(node.left, _currentTypeUsage!);
+    return const EvaluationComplexity.lazy();
+  }
+
   EvaluationComplexity visitInContext(ir.Node node, VariableUse use) {
     VariableUse? oldCurrentTypeUsage = _currentTypeUsage;
     _currentTypeUsage = use;
diff --git a/pkg/compiler/lib/src/ir/util.dart b/pkg/compiler/lib/src/ir/util.dart
index d96ad3d..b955b0c 100644
--- a/pkg/compiler/lib/src/ir/util.dart
+++ b/pkg/compiler/lib/src/ir/util.dart
@@ -195,6 +195,11 @@
   }
 
   @override
+  bool visitIntersectionType(ir.IntersectionType node) {
+    return true;
+  }
+
+  @override
   bool visitFunctionType(ir.FunctionType node) {
     if (visit(node.returnType)) return true;
     if (visitList(node.positionalParameters)) return true;
diff --git a/pkg/compiler/lib/src/ir/visitors.dart b/pkg/compiler/lib/src/ir/visitors.dart
index c752997..b84523f 100644
--- a/pkg/compiler/lib/src/ir/visitors.dart
+++ b/pkg/compiler/lib/src/ir/visitors.dart
@@ -113,6 +113,11 @@
   }
 
   @override
+  DartType visitIntersectionType(ir.IntersectionType node) {
+    return node.left.accept(this);
+  }
+
+  @override
   DartType visitFunctionType(ir.FunctionType node) {
     int index = 0;
     List<FunctionTypeVariable>? typeVariables;
diff --git a/pkg/compiler/lib/src/serialization/helpers.dart b/pkg/compiler/lib/src/serialization/helpers.dart
index a2e2a26..1659004 100644
--- a/pkg/compiler/lib/src/serialization/helpers.dart
+++ b/pkg/compiler/lib/src/serialization/helpers.dart
@@ -142,14 +142,31 @@
       _sink.writeEnum(DartTypeNodeKind.functionTypeVariable);
       _sink.writeInt(index);
       _sink.writeEnum(node.declaredNullability);
-      _sink._writeDartTypeNode(node.promotedBound, functionTypeVariables,
-          allowNull: true);
+      _sink._writeDartTypeNode(null, functionTypeVariables, allowNull: true);
     } else {
       _sink.writeEnum(DartTypeNodeKind.typeParameterType);
       _sink.writeTypeParameterNode(node.parameter);
       _sink.writeEnum(node.declaredNullability);
-      _sink._writeDartTypeNode(node.promotedBound, functionTypeVariables,
-          allowNull: true);
+      _sink._writeDartTypeNode(null, functionTypeVariables, allowNull: true);
+    }
+  }
+
+  @override
+  void visitIntersectionType(
+      ir.IntersectionType node, List<ir.TypeParameter> functionTypeVariables) {
+    int index = functionTypeVariables.indexOf(node.left.parameter);
+    if (index != -1) {
+      _sink.writeEnum(DartTypeNodeKind.functionTypeVariable);
+      _sink.writeInt(index);
+      _sink.writeEnum(node.declaredNullability);
+      _sink._writeDartTypeNode(node.right, functionTypeVariables,
+          allowNull: false);
+    } else {
+      _sink.writeEnum(DartTypeNodeKind.typeParameterType);
+      _sink.writeTypeParameterNode(node.left.parameter);
+      _sink.writeEnum(node.declaredNullability);
+      _sink._writeDartTypeNode(node.right, functionTypeVariables,
+          allowNull: false);
     }
   }
 
diff --git a/pkg/compiler/lib/src/serialization/source.dart b/pkg/compiler/lib/src/serialization/source.dart
index b3cc2c9..1bdab33 100644
--- a/pkg/compiler/lib/src/serialization/source.dart
+++ b/pkg/compiler/lib/src/serialization/source.dart
@@ -826,16 +826,26 @@
         ir.Nullability typeParameterTypeNullability =
             readEnum(ir.Nullability.values);
         ir.DartType? promotedBound = _readDartTypeNode(functionTypeVariables);
-        return ir.TypeParameterType(
-            typeParameter, typeParameterTypeNullability, promotedBound);
+        ir.TypeParameterType typeParameterType =
+            ir.TypeParameterType(typeParameter, typeParameterTypeNullability);
+        if (promotedBound == null) {
+          return typeParameterType;
+        } else {
+          return ir.IntersectionType(typeParameterType, promotedBound);
+        }
       case DartTypeNodeKind.functionTypeVariable:
         int index = readInt();
         assert(0 <= index && index < functionTypeVariables.length);
         ir.Nullability typeParameterTypeNullability =
             readEnum(ir.Nullability.values);
         ir.DartType? promotedBound = _readDartTypeNode(functionTypeVariables);
-        return ir.TypeParameterType(functionTypeVariables[index],
-            typeParameterTypeNullability, promotedBound);
+        ir.TypeParameterType typeParameterType = ir.TypeParameterType(
+            functionTypeVariables[index], typeParameterTypeNullability);
+        if (promotedBound == null) {
+          return typeParameterType;
+        } else {
+          return ir.IntersectionType(typeParameterType, promotedBound);
+        }
       case DartTypeNodeKind.functionType:
         begin(functionTypeNodeTag);
         int typeParameterCount = readInt();
diff --git a/pkg/compiler/test/helpers/ir_types.dart b/pkg/compiler/test/helpers/ir_types.dart
index b9745e5..e1ff1f7 100644
--- a/pkg/compiler/test/helpers/ir_types.dart
+++ b/pkg/compiler/test/helpers/ir_types.dart
@@ -48,6 +48,11 @@
   }
 
   @override
+  void visitIntersectionType(ir.IntersectionType node, StringBuffer sb) {
+    sb.write(node.left.parameter.name);
+  }
+
+  @override
   void visitFunctionType(ir.FunctionType node, StringBuffer sb) {
     writeType(node.returnType, sb);
     sb.write(' Function');
diff --git a/pkg/dev_compiler/lib/src/kernel/compiler.dart b/pkg/dev_compiler/lib/src/kernel/compiler.dart
index 110dc18..22ce35e 100644
--- a/pkg/dev_compiler/lib/src/kernel/compiler.dart
+++ b/pkg/dev_compiler/lib/src/kernel/compiler.dart
@@ -3223,6 +3223,10 @@
   js_ast.Expression visitTypeParameterType(TypeParameterType type) =>
       _emitTypeParameterType(type);
 
+  @override
+  js_ast.Expression visitIntersectionType(IntersectionType type) =>
+      _emitTypeParameterType(type.left);
+
   js_ast.Expression _emitTypeParameterType(TypeParameterType type,
       {bool emitNullability = true}) {
     var typeParam = _emitTypeParameter(type.parameter);
diff --git a/pkg/front_end/lib/src/fasta/kernel/invalid_type.dart b/pkg/front_end/lib/src/fasta/kernel/invalid_type.dart
index c2759d1..132bda6 100644
--- a/pkg/front_end/lib/src/fasta/kernel/invalid_type.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/invalid_type.dart
@@ -110,10 +110,12 @@
     // node.parameter.bound is not checked because such a bound doesn't
     // automatically means that the potential errors related to the occurrences
     // of the type-parameter type itself are reported.
-    if (node.promotedBound != null &&
-        node.promotedBound!.accept1(this, visitedTypedefs)) {
-      return true;
-    }
     return false;
   }
+
+  @override
+  bool visitIntersectionType(
+      IntersectionType node, Set<TypedefType> visitedTypedefs) {
+    return node.right.accept1(this, visitedTypedefs);
+  }
 }
diff --git a/pkg/front_end/lib/src/fasta/kernel/type_algorithms.dart b/pkg/front_end/lib/src/fasta/kernel/type_algorithms.dart
index 13f1592..25d9701 100644
--- a/pkg/front_end/lib/src/fasta/kernel/type_algorithms.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/type_algorithms.dart
@@ -1149,6 +1149,15 @@
   bool visitTypeParameterType(TypeParameterType node) => true;
 
   @override
+  bool visitIntersectionType(IntersectionType node) {
+    // The left-hand side of an [IntersectionType] is always a
+    // [TypeParameterType].
+    // ignore: unnecessary_type_check
+    assert(node.left is TypeParameterType);
+    return true;
+  }
+
+  @override
   bool visitTypedefType(TypedefType node) {
     return anyTypeVariables(node.typeArguments);
   }
diff --git a/pkg/front_end/lib/src/fasta/kernel/type_builder_computer.dart b/pkg/front_end/lib/src/fasta/kernel/type_builder_computer.dart
index d21a6c7..d248946 100644
--- a/pkg/front_end/lib/src/fasta/kernel/type_builder_computer.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/type_builder_computer.dart
@@ -178,6 +178,11 @@
   }
 
   @override
+  TypeBuilder visitIntersectionType(IntersectionType node) {
+    throw "Not implemented";
+  }
+
+  @override
   TypeBuilder visitTypedefType(TypedefType node) {
     throw "Not implemented";
   }
diff --git a/pkg/front_end/lib/src/fasta/kernel/type_labeler.dart b/pkg/front_end/lib/src/fasta/kernel/type_labeler.dart
index ec26f2a..5537820 100644
--- a/pkg/front_end/lib/src/fasta/kernel/type_labeler.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/type_labeler.dart
@@ -175,6 +175,11 @@
   }
 
   @override
+  void visitIntersectionType(IntersectionType node) {
+    return node.left.accept(this);
+  }
+
+  @override
   void visitFunctionType(FunctionType node) {
     node.returnType.accept(this);
     result.add(" Function");
diff --git a/pkg/front_end/lib/src/fasta/type_inference/inference_visitor_base.dart b/pkg/front_end/lib/src/fasta/type_inference/inference_visitor_base.dart
index 25e39b9..d9de536 100644
--- a/pkg/front_end/lib/src/fasta/type_inference/inference_visitor_base.dart
+++ b/pkg/front_end/lib/src/fasta/type_inference/inference_visitor_base.dart
@@ -3615,6 +3615,8 @@
     DartType? resolveOneStep(DartType type) {
       if (type is TypeParameterType) {
         return type.bound;
+      } else if (type is IntersectionType) {
+        return type.right;
       } else {
         return null;
       }
diff --git a/pkg/front_end/lib/src/fasta/type_inference/type_demotion.dart b/pkg/front_end/lib/src/fasta/type_inference/type_demotion.dart
index 48a0929..9e0f44b 100644
--- a/pkg/front_end/lib/src/fasta/type_inference/type_demotion.dart
+++ b/pkg/front_end/lib/src/fasta/type_inference/type_demotion.dart
@@ -46,9 +46,10 @@
   }
 
   @override
-  bool visitTypeParameterType(TypeParameterType node) {
-    return node.promotedBound != null;
-  }
+  bool visitTypeParameterType(TypeParameterType node) => false;
+
+  @override
+  bool visitIntersectionType(IntersectionType node) => true;
 }
 
 /// Returns [type] in which all promoted type variables have been replace with
@@ -125,10 +126,16 @@
   @override
   DartType? visitTypeParameterType(TypeParameterType node, int variance) {
     Nullability? newNullability = visitNullability(node);
-    if (demoteTypeVariables && node.promotedBound != null) {
-      return new TypeParameterType(
-          node.parameter, newNullability ?? node.declaredNullability);
-    }
     return createTypeParameterType(node, newNullability);
   }
+
+  @override
+  DartType? visitIntersectionType(IntersectionType node, int variance) {
+    Nullability? newNullability = visitNullability(node);
+    if (demoteTypeVariables) {
+      return new TypeParameterType(
+          node.left.parameter, newNullability ?? node.left.nullability);
+    }
+    return createTypeParameterType(node.left, newNullability);
+  }
 }
diff --git a/pkg/front_end/lib/src/fasta/type_inference/type_inference_engine.dart b/pkg/front_end/lib/src/fasta/type_inference/type_inference_engine.dart
index a817c94..93b0299 100644
--- a/pkg/front_end/lib/src/fasta/type_inference/type_inference_engine.dart
+++ b/pkg/front_end/lib/src/fasta/type_inference/type_inference_engine.dart
@@ -26,7 +26,7 @@
 
 /// Visitor to check whether a given type mentions any of a class's type
 /// parameters in a non-covariant fashion.
-class IncludesTypeParametersNonCovariantly extends DartTypeVisitor<bool> {
+class IncludesTypeParametersNonCovariantly implements DartTypeVisitor<bool> {
   int _variance;
 
   final List<TypeParameter> _typeParametersToSearchFor;
@@ -36,7 +36,28 @@
       : _variance = initialVariance;
 
   @override
-  bool defaultDartType(DartType node) => false;
+  bool defaultDartType(DartType node) {
+    throw new UnsupportedError(
+        "IncludesTypeParametersNonCovariantly.defaultDartType");
+  }
+
+  @override
+  bool visitDynamicType(DynamicType node) => false;
+
+  @override
+  bool visitExtensionType(ExtensionType node) => false;
+
+  @override
+  bool visitNeverType(NeverType node) => false;
+
+  @override
+  bool visitInvalidType(InvalidType node) => false;
+
+  @override
+  bool visitNullType(NullType node) => false;
+
+  @override
+  bool visitVoidType(VoidType node) => false;
 
   @override
   bool visitFunctionType(FunctionType node) {
@@ -84,6 +105,11 @@
     return !Variance.greaterThanOrEqual(_variance, node.parameter.variance) &&
         _typeParametersToSearchFor.contains(node.parameter);
   }
+
+  @override
+  bool visitIntersectionType(IntersectionType node) {
+    return node.left.accept(this);
+  }
 }
 
 /// Keeps track of the global state for the type inference that occurs outside
@@ -321,7 +347,9 @@
   }
 
   @override
-  bool isTypeParameterType(DartType type) => type is TypeParameterType;
+  bool isTypeParameterType(DartType type) {
+    return type is TypeParameterType || type is IntersectionType;
+  }
 
   @override
   DartType tryPromoteToType(DartType to, DartType from) {
@@ -329,9 +357,13 @@
       return to;
     }
     if (from is TypeParameterType) {
-      if (isSubtypeOf(to, from.promotedBound ?? from.bound)) {
-        return new TypeParameterType.intersection(
-            from.parameter, from.nullability, to);
+      if (isSubtypeOf(to, from.bound)) {
+        return new IntersectionType(from, to);
+      }
+    }
+    if (from is IntersectionType) {
+      if (isSubtypeOf(to, from.right)) {
+        return new IntersectionType(from.left, to);
       }
     }
     return from;
diff --git a/pkg/front_end/lib/src/testing/id_testing_utils.dart b/pkg/front_end/lib/src/testing/id_testing_utils.dart
index a764f12..7d5ddbf 100644
--- a/pkg/front_end/lib/src/testing/id_testing_utils.dart
+++ b/pkg/front_end/lib/src/testing/id_testing_utils.dart
@@ -641,10 +641,13 @@
   void visitTypeParameterType(TypeParameterType node) {
     sb.write(node.parameter.name);
     sb.write(nullabilityToText(node.nullability, typeRepresentation));
-    if (node.promotedBound != null) {
-      sb.write(' & ');
-      visit(node.promotedBound!);
-    }
+  }
+
+  @override
+  void visitIntersectionType(IntersectionType node) {
+    visit(node.left);
+    sb.write(' & ');
+    visit(node.right);
   }
 
   @override
diff --git a/pkg/front_end/test/static_types/data/promoted_access.dart b/pkg/front_end/test/static_types/data/promoted_access.dart
index 82085cb..d3ea4ff 100644
--- a/pkg/front_end/test/static_types/data/promoted_access.dart
+++ b/pkg/front_end/test/static_types/data/promoted_access.dart
@@ -11,11 +11,11 @@
   method(T o) {
     if (/*cfe.T*/ /*cfe:nnbd.T%*/ o is Class) {
       /*cfe.T & Class<dynamic>*/
-      /*cfe:nnbd.T! & Class<dynamic>!*/
+      /*cfe:nnbd.T% & Class<dynamic>!*/
       o. /*invoke: dynamic*/ method(/*Null*/ null);
-      /*cfe.T & Class<dynamic>|dynamic*/ /*cfe:nnbd.T! & Class<dynamic>!|dynamic*/ o
+      /*cfe.T & Class<dynamic>|dynamic*/ /*cfe:nnbd.T% & Class<dynamic>!|dynamic*/ o
           ?. /*invoke: dynamic*/ method(/*Null*/ null);
-      /*cfe.T & Class<dynamic>|dynamic*/ /*cfe:nnbd.T! & Class<dynamic>!|dynamic*/ o
+      /*cfe.T & Class<dynamic>|dynamic*/ /*cfe:nnbd.T% & Class<dynamic>!|dynamic*/ o
           ?. /*dynamic*/ property;
     }
   }
@@ -24,11 +24,11 @@
 method<T>(T o) {
   if (/*cfe.T*/ /*cfe:nnbd.T%*/ o is Class) {
     /*cfe.T & Class<dynamic>*/
-    /*cfe:nnbd.T! & Class<dynamic>!*/
+    /*cfe:nnbd.T% & Class<dynamic>!*/
     o. /*invoke: dynamic*/ method(/*Null*/ null);
-    /*cfe.T & Class<dynamic>|dynamic*/ /*cfe:nnbd.T! & Class<dynamic>!|dynamic*/ o
+    /*cfe.T & Class<dynamic>|dynamic*/ /*cfe:nnbd.T% & Class<dynamic>!|dynamic*/ o
         ?. /*invoke: dynamic*/ method(/*Null*/ null);
-    /*cfe.T & Class<dynamic>|dynamic*/ /*cfe:nnbd.T! & Class<dynamic>!|dynamic*/ o
+    /*cfe.T & Class<dynamic>|dynamic*/ /*cfe:nnbd.T% & Class<dynamic>!|dynamic*/ o
         ?. /*dynamic*/ property;
   }
 }
diff --git a/pkg/front_end/testcases/nnbd/intersection_types.dart.strong.expect b/pkg/front_end/testcases/nnbd/intersection_types.dart.strong.expect
index 9723009..5717e61 100644
--- a/pkg/front_end/testcases/nnbd/intersection_types.dart.strong.expect
+++ b/pkg/front_end/testcases/nnbd/intersection_types.dart.strong.expect
@@ -33,7 +33,7 @@
     if(t is{ForNonNullableByDefault} self::B) {
       self::Foo::T% bar = t{self::Foo::T% & self::B /* '%' & '!' = '!' */};
       if(t{self::Foo::T% & self::B /* '%' & '!' = '!' */} is{ForNonNullableByDefault} self::C) {
-        self::Foo::T baz = t{self::Foo::T & self::C /* '!' & '!' = '!' */};
+        self::Foo::T% baz = t{self::Foo::T% & self::C /* '%' & '!' = '!' */};
       }
     }
   }
diff --git a/pkg/front_end/testcases/nnbd/intersection_types.dart.strong.transformed.expect b/pkg/front_end/testcases/nnbd/intersection_types.dart.strong.transformed.expect
index 9723009..5717e61 100644
--- a/pkg/front_end/testcases/nnbd/intersection_types.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/nnbd/intersection_types.dart.strong.transformed.expect
@@ -33,7 +33,7 @@
     if(t is{ForNonNullableByDefault} self::B) {
       self::Foo::T% bar = t{self::Foo::T% & self::B /* '%' & '!' = '!' */};
       if(t{self::Foo::T% & self::B /* '%' & '!' = '!' */} is{ForNonNullableByDefault} self::C) {
-        self::Foo::T baz = t{self::Foo::T & self::C /* '!' & '!' = '!' */};
+        self::Foo::T% baz = t{self::Foo::T% & self::C /* '%' & '!' = '!' */};
       }
     }
   }
diff --git a/pkg/front_end/testcases/nnbd/intersection_types.dart.weak.expect b/pkg/front_end/testcases/nnbd/intersection_types.dart.weak.expect
index 9723009..5717e61 100644
--- a/pkg/front_end/testcases/nnbd/intersection_types.dart.weak.expect
+++ b/pkg/front_end/testcases/nnbd/intersection_types.dart.weak.expect
@@ -33,7 +33,7 @@
     if(t is{ForNonNullableByDefault} self::B) {
       self::Foo::T% bar = t{self::Foo::T% & self::B /* '%' & '!' = '!' */};
       if(t{self::Foo::T% & self::B /* '%' & '!' = '!' */} is{ForNonNullableByDefault} self::C) {
-        self::Foo::T baz = t{self::Foo::T & self::C /* '!' & '!' = '!' */};
+        self::Foo::T% baz = t{self::Foo::T% & self::C /* '%' & '!' = '!' */};
       }
     }
   }
diff --git a/pkg/front_end/testcases/nnbd/intersection_types.dart.weak.modular.expect b/pkg/front_end/testcases/nnbd/intersection_types.dart.weak.modular.expect
index 9723009..5717e61 100644
--- a/pkg/front_end/testcases/nnbd/intersection_types.dart.weak.modular.expect
+++ b/pkg/front_end/testcases/nnbd/intersection_types.dart.weak.modular.expect
@@ -33,7 +33,7 @@
     if(t is{ForNonNullableByDefault} self::B) {
       self::Foo::T% bar = t{self::Foo::T% & self::B /* '%' & '!' = '!' */};
       if(t{self::Foo::T% & self::B /* '%' & '!' = '!' */} is{ForNonNullableByDefault} self::C) {
-        self::Foo::T baz = t{self::Foo::T & self::C /* '!' & '!' = '!' */};
+        self::Foo::T% baz = t{self::Foo::T% & self::C /* '%' & '!' = '!' */};
       }
     }
   }
diff --git a/pkg/front_end/testcases/nnbd/intersection_types.dart.weak.transformed.expect b/pkg/front_end/testcases/nnbd/intersection_types.dart.weak.transformed.expect
index 9723009..5717e61 100644
--- a/pkg/front_end/testcases/nnbd/intersection_types.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/nnbd/intersection_types.dart.weak.transformed.expect
@@ -33,7 +33,7 @@
     if(t is{ForNonNullableByDefault} self::B) {
       self::Foo::T% bar = t{self::Foo::T% & self::B /* '%' & '!' = '!' */};
       if(t{self::Foo::T% & self::B /* '%' & '!' = '!' */} is{ForNonNullableByDefault} self::C) {
-        self::Foo::T baz = t{self::Foo::T & self::C /* '!' & '!' = '!' */};
+        self::Foo::T% baz = t{self::Foo::T% & self::C /* '%' & '!' = '!' */};
       }
     }
   }
diff --git a/pkg/front_end/tool/_fasta/bench_maker.dart b/pkg/front_end/tool/_fasta/bench_maker.dart
index 58c45c97..4cc1ed5 100644
--- a/pkg/front_end/tool/_fasta/bench_maker.dart
+++ b/pkg/front_end/tool/_fasta/bench_maker.dart
@@ -334,10 +334,13 @@
     String name = computeName(node.parameter);
     usedTypeParameters.add(node.parameter);
     sb.write(name);
-    if (node.promotedBound != null) {
-      sb.write(" & ");
-      node.promotedBound!.accept1(this, sb);
-    }
+  }
+
+  @override
+  void visitIntersectionType(IntersectionType node, StringBuffer sb) {
+    node.left.accept1(this, sb);
+    sb.write(" & ");
+    node.right.accept1(this, sb);
   }
 
   @override
diff --git a/pkg/kernel/binary.md b/pkg/kernel/binary.md
index db46616..4149740 100644
--- a/pkg/kernel/binary.md
+++ b/pkg/kernel/binary.md
@@ -147,7 +147,7 @@
 
 type ComponentFile {
   UInt32 magic = 0x90ABCDEF;
-  UInt32 formatVersion = 83;
+  UInt32 formatVersion = 84;
   Byte[10] shortSdkHash;
   List<String> problemsAsJson; // Described in problems.md.
   Library[] libraries;
@@ -1529,7 +1529,12 @@
   // the class type parameters in a constructor refer to those declared on the
   // class.
   UInt index;
-  Option<DartType> bound;
+}
+
+type IntersectionType extends DartType {
+  Byte tag = 99;
+  TypeParameterType left;
+  DartType right;
 }
 
 type TypedefType {
diff --git a/pkg/kernel/lib/ast.dart b/pkg/kernel/lib/ast.dart
index 74fe943..8e40415 100644
--- a/pkg/kernel/lib/ast.dart
+++ b/pkg/kernel/lib/ast.dart
@@ -12078,6 +12078,335 @@
   }
 }
 
+class IntersectionType extends DartType {
+  final TypeParameterType left;
+  final DartType right;
+
+  IntersectionType(this.left, this.right) {
+    // TODO(cstefantsova): Also assert that [rhs] is a subtype of [lhs.bound].
+
+    Nullability leftNullability = left.nullability;
+    Nullability rightNullability = right.nullability;
+    assert(
+        (leftNullability == Nullability.nonNullable &&
+                rightNullability == Nullability.nonNullable) ||
+            (leftNullability == Nullability.nonNullable &&
+                rightNullability == Nullability.undetermined) ||
+            (leftNullability == Nullability.legacy &&
+                rightNullability == Nullability.legacy) ||
+            (leftNullability == Nullability.undetermined &&
+                rightNullability == Nullability.nonNullable) ||
+            (leftNullability == Nullability.undetermined &&
+                rightNullability == Nullability.nullable) ||
+            (leftNullability == Nullability.undetermined &&
+                rightNullability == Nullability.undetermined)
+            // These are observed in real situations:
+            ||
+            // pkg/front_end/test/id_tests/type_promotion_test
+            // replicated in nnbd_mixed/type_parameter_nullability
+            (leftNullability == Nullability.nullable &&
+                rightNullability == Nullability.nonNullable) ||
+            // pkg/front_end/test/fasta/types/kernel_type_parser_test
+            // pkg/front_end/test/fasta/incremental_hello_test
+            // pkg/front_end/test/fasta/types/fasta_types_test
+            // pkg/front_end/test/explicit_creation_test
+            // pkg/front_end/tool/fasta_perf_test
+            // nnbd/issue42089
+            // replicated in nnbd_mixed/type_parameter_nullability
+            (leftNullability == Nullability.nullable &&
+                rightNullability == Nullability.nullable) ||
+            // pkg/front_end/test/explicit_creation_test
+            // pkg/front_end/test/dill_round_trip_test
+            // pkg/front_end/test/compile_dart2js_with_no_sdk_test
+            // pkg/front_end/test/fasta/types/large_app_benchmark_test
+            // pkg/front_end/test/incremental_dart2js_test
+            // pkg/front_end/test/read_dill_from_binary_md_test
+            // pkg/front_end/test/static_types/static_type_test
+            // pkg/front_end/test/split_dill_test
+            // pkg/front_end/tool/incremental_perf_test
+            // pkg/vm/test/kernel_front_end_test
+            // general/promoted_null_aware_access
+            // inference/constructors_infer_from_arguments_factory
+            // inference/infer_types_on_loop_indices_for_each_loop
+            // inference/infer_types_on_loop_indices_for_each_loop_async
+            // replicated in nnbd_mixed/type_parameter_nullability
+            (leftNullability == Nullability.legacy &&
+                rightNullability == Nullability.nonNullable) ||
+            // pkg/front_end/test/fasta/incremental_hello_test
+            // pkg/front_end/test/explicit_creation_test
+            // pkg/front_end/tool/fasta_perf_test
+            // replicated in nnbd_mixed/type_parameter_nullability
+            (leftNullability == Nullability.nullable &&
+                rightNullability == Nullability.undetermined) ||
+            // These are only observed in tests and might be artifacts of the
+            // tests rather than real situations:
+            //
+            // pkg/front_end/test/fasta/types/kernel_type_parser_test
+            // pkg/front_end/test/fasta/types/fasta_types_test
+            (leftNullability == Nullability.legacy &&
+                rightNullability == Nullability.nullable) ||
+            // pkg/front_end/test/fasta/types/kernel_type_parser_test
+            // pkg/front_end/test/fasta/types/fasta_types_test
+            (leftNullability == Nullability.nonNullable &&
+                rightNullability == Nullability.nullable) ||
+            // pkg/front_end/test/fasta/types/kernel_type_parser_test
+            // pkg/front_end/test/fasta/types/fasta_types_test
+            (leftNullability == Nullability.undetermined &&
+                rightNullability == Nullability.legacy) ||
+            // pkg/kernel/test/clone_test
+            // The legacy nullability is due to RHS being InvalidType.
+            (leftNullability == Nullability.nonNullable &&
+                rightNullability == Nullability.legacy),
+        "Unexpected nullabilities for ${left} & ${right}: "
+        "leftNullability = ${leftNullability}, "
+        "rightNullability = ${rightNullability}.");
+  }
+
+  @override
+  R accept<R>(DartTypeVisitor<R> v) => v.visitIntersectionType(this);
+
+  @override
+  R accept1<R, A>(DartTypeVisitor1<R, A> v, A arg) =>
+      v.visitIntersectionType(this, arg);
+
+  @override
+  void visitChildren(Visitor v) {
+    left.accept(v);
+    right.accept(v);
+  }
+
+  @override
+  bool operator ==(Object other) => equals(other, null);
+
+  @override
+  bool equals(Object other, Assumptions? assumptions) {
+    if (identical(this, other)) {
+      return true;
+    } else if (other is IntersectionType) {
+      return left.equals(other.left, assumptions) &&
+          right.equals(other.right, assumptions);
+    } else {
+      return false;
+    }
+  }
+
+  @override
+  int get hashCode {
+    int nullabilityHash = (0x33333333 >> nullability.index) ^ 0x33333333;
+    int hash = nullabilityHash;
+    hash = 0x3fffffff & (hash * 31 + (hash ^ left.hashCode));
+    hash = 0x3fffffff & (hash * 31 + (hash ^ right.hashCode));
+    return hash;
+  }
+
+  /// Computes the nullability of [IntersectionType] from its parts.
+  ///
+  /// [nullability] is calculated from [left.nullability] and
+  /// [right.nullability].
+  ///
+  /// In the following program the nullability of `x` is
+  /// [Nullability.undetermined] because it's copied from that of `bar`. The
+  /// nullability of `y` is [Nullability.nonNullable] because its type is an
+  /// intersection type where the LHS is `T` and the RHS is the promoted type
+  /// `int`. The nullability of the type of `y` is computed from the
+  /// nullabilities of those two types.
+  ///
+  ///     class A<T extends Object?> {
+  ///       foo(T bar) {
+  ///         var x = bar;
+  ///         if (bar is int) {
+  ///           var y = bar;
+  ///         }
+  ///       }
+  ///     }
+  ///
+  /// The method combines the nullabilities of [left] and [right] to yield the
+  /// nullability of the intersection type.
+  @override
+  Nullability get nullability {
+    // Note that RHS is always a subtype of the bound of the type parameter.
+
+    // The code below implements the rule for the nullability of an
+    // intersection type as per the following table:
+    //
+    // | LHS \ RHS |  !  |  ?  |  *  |  %  |
+    // |-----------|-----|-----|-----|-----|
+    // |     !     |  !  |  +  | N/A |  !  |
+    // |     ?     | (!) | (?) | N/A | (%) |
+    // |     *     | (*) |  +  |  *  | N/A |
+    // |     %     |  !  |  %  |  +  |  %  |
+    //
+    // In the table, LHS corresponds to [lhsNullability] in the code below; RHS
+    // corresponds to [rhsNullability]; !, ?, *, and % correspond to
+    // nonNullable, nullable, legacy, and undetermined values of the
+    // Nullability enum.
+
+    Nullability lhsNullability = left.nullability;
+    Nullability rhsNullability = right.nullability;
+    assert(
+        (lhsNullability == Nullability.nonNullable &&
+                rhsNullability == Nullability.nonNullable) ||
+            (lhsNullability == Nullability.nonNullable &&
+                rhsNullability == Nullability.undetermined) ||
+            (lhsNullability == Nullability.legacy &&
+                rhsNullability == Nullability.legacy) ||
+            (lhsNullability == Nullability.undetermined &&
+                rhsNullability == Nullability.nonNullable) ||
+            (lhsNullability == Nullability.undetermined &&
+                rhsNullability == Nullability.nullable) ||
+            (lhsNullability == Nullability.undetermined &&
+                rhsNullability == Nullability.undetermined)
+            // Apparently these happens as well:
+            ||
+            // pkg/front_end/test/id_tests/type_promotion_test
+            (lhsNullability == Nullability.nullable &&
+                rhsNullability == Nullability.nonNullable) ||
+            // pkg/front_end/test/fasta/types/kernel_type_parser_test
+            // pkg/front_end/test/fasta/incremental_hello_test
+            // pkg/front_end/test/fasta/types/fasta_types_test
+            // pkg/front_end/test/explicit_creation_test
+            // pkg/front_end/tool/fasta_perf_test
+            // nnbd/issue42089
+            (lhsNullability == Nullability.nullable &&
+                rhsNullability == Nullability.nullable) ||
+            // pkg/front_end/test/explicit_creation_test
+            // pkg/front_end/test/dill_round_trip_test
+            // pkg/front_end/test/compile_dart2js_with_no_sdk_test
+            // pkg/front_end/test/fasta/types/large_app_benchmark_test
+            // pkg/front_end/test/incremental_dart2js_test
+            // pkg/front_end/test/read_dill_from_binary_md_test
+            // pkg/front_end/test/static_types/static_type_test
+            // pkg/front_end/test/split_dill_test
+            // pkg/front_end/tool/incremental_perf_test
+            // pkg/vm/test/kernel_front_end_test
+            // general/promoted_null_aware_access
+            // inference/constructors_infer_from_arguments_factory
+            // inference/infer_types_on_loop_indices_for_each_loop
+            // inference/infer_types_on_loop_indices_for_each_loop_async
+            (lhsNullability == Nullability.legacy &&
+                rhsNullability == Nullability.nonNullable) ||
+            // pkg/front_end/test/fasta/incremental_hello_test
+            // pkg/front_end/test/explicit_creation_test
+            // pkg/front_end/tool/fasta_perf_test
+            // pkg/front_end/test/fasta/incremental_hello_test
+            (lhsNullability == Nullability.nullable &&
+                rhsNullability == Nullability.undetermined) ||
+
+            // This is created but never observed.
+            // (lhsNullability == Nullability.legacy &&
+            //     rhsNullability == Nullability.nullable) ||
+
+            // pkg/front_end/test/fasta/types/kernel_type_parser_test
+            // pkg/front_end/test/fasta/types/fasta_types_test
+            (lhsNullability == Nullability.undetermined &&
+                rhsNullability == Nullability.legacy) ||
+            // pkg/front_end/test/fasta/types/kernel_type_parser_test
+            // pkg/front_end/test/fasta/types/fasta_types_test
+            (lhsNullability == Nullability.nonNullable &&
+                rhsNullability == Nullability.nullable),
+        "Unexpected nullabilities for: LHS nullability = $lhsNullability, "
+        "RHS nullability = ${rhsNullability}.");
+
+    // Whenever there's N/A in the table, it means that the corresponding
+    // combination of the LHS and RHS nullability is not possible when
+    // compiling from Dart source files, so we can define it to be whatever is
+    // faster and more convenient to implement.  The verifier should check that
+    // the cases marked as N/A never occur in the output of the CFE.
+    //
+    // The code below uses the following extension of the table function:
+    //
+    // | LHS \ RHS |  !  |  ?  |  *  |  %  |
+    // |-----------|-----|-----|-----|-----|
+    // |     !     |  !  |  !  |  !  |  !  |
+    // |     ?     | (!) | (?) |  *  | (%) |
+    // |     *     | (*) |  *  |  *  |  %  |
+    // |     %     |  !  |  %  |  %  |  %  |
+
+    if (lhsNullability == Nullability.nullable &&
+        rhsNullability == Nullability.nonNullable) {
+      return Nullability.nonNullable;
+    }
+
+    if (lhsNullability == Nullability.nullable &&
+        rhsNullability == Nullability.nullable) {
+      return Nullability.nullable;
+    }
+
+    if (lhsNullability == Nullability.legacy &&
+        rhsNullability == Nullability.nonNullable) {
+      return Nullability.legacy;
+    }
+
+    if (lhsNullability == Nullability.nullable &&
+        rhsNullability == Nullability.undetermined) {
+      return Nullability.undetermined;
+    }
+
+    // Intersection with a non-nullable type always yields a non-nullable type,
+    // as it's the most restrictive kind of types.
+    if (lhsNullability == Nullability.nonNullable ||
+        rhsNullability == Nullability.nonNullable) {
+      return Nullability.nonNullable;
+    }
+
+    // If the nullability of LHS is 'undetermined', the nullability of the
+    // intersection is also 'undetermined' if RHS is 'undetermined' or
+    // nullable.
+    //
+    // Consider the following example:
+    //
+    //     class A<X extends Object?, Y extends X> {
+    //       foo(X x) {
+    //         if (x is Y) {
+    //           x = null;     // Compile-time error.  Consider X = Y = int.
+    //           Object a = x; // Compile-time error.  Consider X = Y = int?.
+    //         }
+    //         if (x is int?) {
+    //           x = null;     // Compile-time error.  Consider X = int.
+    //           Object b = x; // Compile-time error.  Consider X = int?.
+    //         }
+    //       }
+    //     }
+    if (lhsNullability == Nullability.undetermined ||
+        rhsNullability == Nullability.undetermined) {
+      return Nullability.undetermined;
+    }
+
+    return Nullability.legacy;
+  }
+
+  @override
+  Nullability get declaredNullability => nullability;
+
+  @override
+  IntersectionType withDeclaredNullability(Nullability declaredNullability) {
+    if (left.declaredNullability == this.declaredNullability) {
+      return this;
+    }
+    TypeParameterType newLeft =
+        left.withDeclaredNullability(declaredNullability);
+    if (identical(newLeft, left)) {
+      return this;
+    }
+    return new IntersectionType(newLeft, right);
+  }
+
+  @override
+  String toString() {
+    return "IntersectionType(${toStringInternal()})";
+  }
+
+  @override
+  void toTextInternal(AstPrinter printer) {
+    printer.write('(');
+    printer.writeType(left);
+    printer.write(" & ");
+    printer.writeType(right);
+    printer.write(')');
+    printer.write(nullabilityToString(nullability));
+  }
+}
+
 /// Reference to a type variable.
 ///
 /// A type variable has an optional bound because type promotion can change the
@@ -12096,95 +12425,7 @@
 
   TypeParameter parameter;
 
-  /// An optional promoted bound on the type parameter.
-  ///
-  /// 'null' indicates that the type parameter's bound has not been promoted and
-  /// is therefore the same as the bound of [parameter].
-  DartType? promotedBound;
-
-  TypeParameterType.internal(
-      this.parameter, this.declaredNullability, DartType? promotedBound)
-      : this.promotedBound = promotedBound {
-    assert(
-        promotedBound == null ||
-            (declaredNullability == Nullability.nonNullable &&
-                promotedBound.nullability == Nullability.nonNullable) ||
-            (declaredNullability == Nullability.nonNullable &&
-                promotedBound.nullability == Nullability.undetermined) ||
-            (declaredNullability == Nullability.legacy &&
-                promotedBound.nullability == Nullability.legacy) ||
-            (declaredNullability == Nullability.undetermined &&
-                promotedBound.nullability == Nullability.nonNullable) ||
-            (declaredNullability == Nullability.undetermined &&
-                promotedBound.nullability == Nullability.nullable) ||
-            (declaredNullability == Nullability.undetermined &&
-                promotedBound.nullability == Nullability.undetermined)
-            // These are observed in real situations:
-            ||
-            // pkg/front_end/test/id_tests/type_promotion_test
-            // replicated in nnbd_mixed/type_parameter_nullability
-            (declaredNullability == Nullability.nullable &&
-                promotedBound.nullability == Nullability.nonNullable) ||
-            // pkg/front_end/test/fasta/types/kernel_type_parser_test
-            // pkg/front_end/test/fasta/incremental_hello_test
-            // pkg/front_end/test/fasta/types/fasta_types_test
-            // pkg/front_end/test/explicit_creation_test
-            // pkg/front_end/tool/fasta_perf_test
-            // nnbd/issue42089
-            // replicated in nnbd_mixed/type_parameter_nullability
-            (declaredNullability == Nullability.nullable &&
-                promotedBound.nullability == Nullability.nullable) ||
-            // pkg/front_end/test/explicit_creation_test
-            // pkg/front_end/test/dill_round_trip_test
-            // pkg/front_end/test/compile_dart2js_with_no_sdk_test
-            // pkg/front_end/test/fasta/types/large_app_benchmark_test
-            // pkg/front_end/test/incremental_dart2js_test
-            // pkg/front_end/test/read_dill_from_binary_md_test
-            // pkg/front_end/test/static_types/static_type_test
-            // pkg/front_end/test/split_dill_test
-            // pkg/front_end/tool/incremental_perf_test
-            // pkg/vm/test/kernel_front_end_test
-            // general/promoted_null_aware_access
-            // inference/constructors_infer_from_arguments_factory
-            // inference/infer_types_on_loop_indices_for_each_loop
-            // inference/infer_types_on_loop_indices_for_each_loop_async
-            // replicated in nnbd_mixed/type_parameter_nullability
-            (declaredNullability == Nullability.legacy &&
-                promotedBound.nullability == Nullability.nonNullable) ||
-            // pkg/front_end/test/fasta/incremental_hello_test
-            // pkg/front_end/test/explicit_creation_test
-            // pkg/front_end/tool/fasta_perf_test
-            // replicated in nnbd_mixed/type_parameter_nullability
-            (declaredNullability == Nullability.nullable &&
-                promotedBound.nullability == Nullability.undetermined) ||
-            // These are only observed in tests and might be artifacts of the
-            // tests rather than real situations:
-            //
-            // pkg/front_end/test/fasta/types/kernel_type_parser_test
-            // pkg/front_end/test/fasta/types/fasta_types_test
-            (declaredNullability == Nullability.legacy &&
-                promotedBound.nullability == Nullability.nullable) ||
-            // pkg/front_end/test/fasta/types/kernel_type_parser_test
-            // pkg/front_end/test/fasta/types/fasta_types_test
-            (declaredNullability == Nullability.nonNullable &&
-                promotedBound.nullability == Nullability.nullable) ||
-            // pkg/front_end/test/fasta/types/kernel_type_parser_test
-            // pkg/front_end/test/fasta/types/fasta_types_test
-            (declaredNullability == Nullability.undetermined &&
-                promotedBound.nullability == Nullability.legacy),
-        "Unexpected nullabilities for $parameter & $promotedBound: "
-        "declaredNullability = $declaredNullability, "
-        "promoted bound nullability = ${promotedBound.nullability}.");
-  }
-
-  TypeParameterType(TypeParameter parameter, Nullability declaredNullability,
-      [DartType? promotedBound])
-      : this.internal(parameter, declaredNullability, promotedBound);
-
-  /// Creates an intersection type between a type parameter and [promotedBound].
-  TypeParameterType.intersection(TypeParameter parameter,
-      Nullability declaredNullability, DartType promotedBound)
-      : this.internal(parameter, declaredNullability, promotedBound);
+  TypeParameterType(this.parameter, this.declaredNullability);
 
   /// Creates a type-parameter type to be used in alpha-renaming.
   ///
@@ -12218,9 +12459,7 @@
       v.visitTypeParameterType(this, arg);
 
   @override
-  void visitChildren(Visitor v) {
-    promotedBound?.accept(v);
-  }
+  void visitChildren(Visitor v) {}
 
   @override
   bool operator ==(Object other) => equals(other, null);
@@ -12244,14 +12483,6 @@
           return false;
         }
       }
-      if (promotedBound != null) {
-        if (other.promotedBound == null) return false;
-        if (!promotedBound!.equals(other.promotedBound!, assumptions)) {
-          return false;
-        }
-      } else if (other.promotedBound != null) {
-        return false;
-      }
       return true;
     } else {
       return false;
@@ -12266,36 +12497,14 @@
     int hash = parameter.isFunctionTypeTypeParameter ? 0 : parameter.hashCode;
     int nullabilityHash = (0x33333333 >> nullability.index) ^ 0x33333333;
     hash = 0x3fffffff & (hash * 31 + (hash ^ nullabilityHash));
-    hash = 0x3fffffff & (hash * 31 + (hash ^ promotedBound.hashCode));
     return hash;
   }
 
-  /// Returns the bound of the type parameter, accounting for promotions.
-  DartType get bound => promotedBound ?? parameter.bound;
+  /// A quick access to the bound of the parameter.
+  DartType get bound => parameter.bound;
 
-  /// Nullability of the type, calculated from its parts.
-  ///
-  /// [nullability] is calculated from [typeParameterTypeNullability] and the
-  /// nullability of [promotedBound] if it's present.
-  ///
-  /// For example, in the following program [typeParameterTypeNullability] of
-  /// both `x` and `y` is [Nullability.undetermined], because it's copied from
-  /// that of `bar` and T has a nullable type as its bound.  However, despite
-  /// [nullability] of `x` is [Nullability.undetermined], [nullability] of `y`
-  /// is [Nullability.nonNullable] because of its [promotedBound].
-  ///
-  ///     class A<T extends Object?> {
-  ///       foo(T bar) {
-  ///         var x = bar;
-  ///         if (bar is int) {
-  ///           var y = bar;
-  ///         }
-  ///       }
-  ///     }
   @override
-  Nullability get nullability {
-    return getNullability(declaredNullability, promotedBound);
-  }
+  Nullability get nullability => declaredNullability;
 
   /// Gets a new [TypeParameterType] with given [typeParameterTypeNullability].
   ///
@@ -12308,7 +12517,7 @@
     if (declaredNullability == this.declaredNullability) {
       return this;
     }
-    return new TypeParameterType(parameter, declaredNullability, promotedBound);
+    return new TypeParameterType(parameter, declaredNullability);
   }
 
   /// Gets the nullability of a type-parameter type based on the bound.
@@ -12337,9 +12546,6 @@
         type = type.typeArgument;
       }
       if (type is TypeParameterType && type.parameter == typeParameter) {
-        // Intersection types can't appear in the bound.
-        assert(type.promotedBound == null);
-
         nullabilityDependsOnItself = true;
       }
     }
@@ -12355,173 +12561,6 @@
         : boundNullability;
   }
 
-  /// Gets nullability of [TypeParameterType] from arguments to its constructor.
-  ///
-  /// The method combines [typeParameterTypeNullability] and the nullability of
-  /// [promotedBound] to yield the nullability of the intersection type.  If the
-  /// right-hand side of the intersection is absent (that is, if [promotedBound]
-  /// is null), the nullability of the intersection type is simply
-  /// [typeParameterTypeNullability].
-  static Nullability getNullability(
-      Nullability typeParameterTypeNullability, DartType? promotedBound) {
-    // If promotedBound is null, getNullability simply returns the nullability
-    // of the type parameter type.
-    Nullability lhsNullability = typeParameterTypeNullability;
-    if (promotedBound == null) {
-      return lhsNullability;
-    }
-
-    // If promotedBound isn't null, getNullability returns the nullability of an
-    // intersection of the left-hand side (referred to as LHS below) and the
-    // right-hand side (referred to as RHS below).  Note that RHS is always a
-    // subtype of the bound of the type parameter.
-
-    // The code below implements the rule for the nullability of an intersection
-    // type as per the following table:
-    //
-    // | LHS \ RHS |  !  |  ?  |  *  |  %  |
-    // |-----------|-----|-----|-----|-----|
-    // |     !     |  !  |  +  | N/A |  !  |
-    // |     ?     | (!) | (?) | N/A | (%) |
-    // |     *     | (*) |  +  |  *  | N/A |
-    // |     %     |  !  |  %  |  +  |  %  |
-    //
-    // In the table, LHS corresponds to lhsNullability in the code below; RHS
-    // corresponds to promotedBound.nullability; !, ?, *, and % correspond to
-    // nonNullable, nullable, legacy, and undetermined values of the Nullability
-    // enum.
-
-    assert(
-        (lhsNullability == Nullability.nonNullable &&
-                promotedBound.nullability == Nullability.nonNullable) ||
-            (lhsNullability == Nullability.nonNullable &&
-                promotedBound.nullability == Nullability.undetermined) ||
-            (lhsNullability == Nullability.legacy &&
-                promotedBound.nullability == Nullability.legacy) ||
-            (lhsNullability == Nullability.undetermined &&
-                promotedBound.nullability == Nullability.nonNullable) ||
-            (lhsNullability == Nullability.undetermined &&
-                promotedBound.nullability == Nullability.nullable) ||
-            (lhsNullability == Nullability.undetermined &&
-                promotedBound.nullability == Nullability.undetermined)
-            // Apparently these happens as well:
-            ||
-            // pkg/front_end/test/id_tests/type_promotion_test
-            (lhsNullability == Nullability.nullable &&
-                promotedBound.nullability == Nullability.nonNullable) ||
-            // pkg/front_end/test/fasta/types/kernel_type_parser_test
-            // pkg/front_end/test/fasta/incremental_hello_test
-            // pkg/front_end/test/fasta/types/fasta_types_test
-            // pkg/front_end/test/explicit_creation_test
-            // pkg/front_end/tool/fasta_perf_test
-            // nnbd/issue42089
-            (lhsNullability == Nullability.nullable &&
-                promotedBound.nullability == Nullability.nullable) ||
-            // pkg/front_end/test/explicit_creation_test
-            // pkg/front_end/test/dill_round_trip_test
-            // pkg/front_end/test/compile_dart2js_with_no_sdk_test
-            // pkg/front_end/test/fasta/types/large_app_benchmark_test
-            // pkg/front_end/test/incremental_dart2js_test
-            // pkg/front_end/test/read_dill_from_binary_md_test
-            // pkg/front_end/test/static_types/static_type_test
-            // pkg/front_end/test/split_dill_test
-            // pkg/front_end/tool/incremental_perf_test
-            // pkg/vm/test/kernel_front_end_test
-            // general/promoted_null_aware_access
-            // inference/constructors_infer_from_arguments_factory
-            // inference/infer_types_on_loop_indices_for_each_loop
-            // inference/infer_types_on_loop_indices_for_each_loop_async
-            (lhsNullability == Nullability.legacy &&
-                promotedBound.nullability == Nullability.nonNullable) ||
-            // pkg/front_end/test/fasta/incremental_hello_test
-            // pkg/front_end/test/explicit_creation_test
-            // pkg/front_end/tool/fasta_perf_test
-            // pkg/front_end/test/fasta/incremental_hello_test
-            (lhsNullability == Nullability.nullable &&
-                promotedBound.nullability == Nullability.undetermined) ||
-
-            // This is created but never observed.
-            // (lhsNullability == Nullability.legacy &&
-            //     promotedBound.nullability == Nullability.nullable) ||
-
-            // pkg/front_end/test/fasta/types/kernel_type_parser_test
-            // pkg/front_end/test/fasta/types/fasta_types_test
-            (lhsNullability == Nullability.undetermined &&
-                promotedBound.nullability == Nullability.legacy) ||
-            // pkg/front_end/test/fasta/types/kernel_type_parser_test
-            // pkg/front_end/test/fasta/types/fasta_types_test
-            (lhsNullability == Nullability.nonNullable &&
-                promotedBound.nullability == Nullability.nullable),
-        "Unexpected nullabilities for: LHS nullability = $lhsNullability, "
-        "RHS nullability = ${promotedBound.nullability}.");
-
-    // Whenever there's N/A in the table, it means that the corresponding
-    // combination of the LHS and RHS nullability is not possible when compiling
-    // from Dart source files, so we can define it to be whatever is faster and
-    // more convenient to implement.  The verifier should check that the cases
-    // marked as N/A never occur in the output of the CFE.
-    //
-    // The code below uses the following extension of the table function:
-    //
-    // | LHS \ RHS |  !  |  ?  |  *  |  %  |
-    // |-----------|-----|-----|-----|-----|
-    // |     !     |  !  |  !  |  !  |  !  |
-    // |     ?     | (!) | (?) |  *  | (%) |
-    // |     *     | (*) |  *  |  *  |  %  |
-    // |     %     |  !  |  %  |  %  |  %  |
-
-    if (lhsNullability == Nullability.nullable &&
-        promotedBound.nullability == Nullability.nonNullable) {
-      return Nullability.nonNullable;
-    }
-
-    if (lhsNullability == Nullability.nullable &&
-        promotedBound.nullability == Nullability.nullable) {
-      return Nullability.nullable;
-    }
-
-    if (lhsNullability == Nullability.legacy &&
-        promotedBound.nullability == Nullability.nonNullable) {
-      return Nullability.legacy;
-    }
-
-    if (lhsNullability == Nullability.nullable &&
-        promotedBound.nullability == Nullability.undetermined) {
-      return Nullability.undetermined;
-    }
-
-    // Intersection with a non-nullable type always yields a non-nullable type,
-    // as it's the most restrictive kind of types.
-    if (lhsNullability == Nullability.nonNullable ||
-        promotedBound.nullability == Nullability.nonNullable) {
-      return Nullability.nonNullable;
-    }
-
-    // If the nullability of LHS is 'undetermined', the nullability of the
-    // intersection is also 'undetermined' if RHS is 'undetermined' or nullable.
-    //
-    // Consider the following example:
-    //
-    //     class A<X extends Object?, Y extends X> {
-    //       foo(X x) {
-    //         if (x is Y) {
-    //           x = null;     // Compile-time error.  Consider X = Y = int.
-    //           Object a = x; // Compile-time error.  Consider X = Y = int?.
-    //         }
-    //         if (x is int?) {
-    //           x = null;     // Compile-time error.  Consider X = int.
-    //           Object b = x; // Compile-time error.  Consider X = int?.
-    //         }
-    //       }
-    //     }
-    if (lhsNullability == Nullability.undetermined ||
-        promotedBound.nullability == Nullability.undetermined) {
-      return Nullability.undetermined;
-    }
-
-    return Nullability.legacy;
-  }
-
   @override
   String toString() {
     return "TypeParameterType(${toStringInternal()})";
@@ -12529,18 +12568,8 @@
 
   @override
   void toTextInternal(AstPrinter printer) {
-    if (promotedBound != null) {
-      printer.write('(');
-      printer.writeTypeParameterName(parameter);
-      printer.write(nullabilityToString(declaredNullability));
-      printer.write(" & ");
-      printer.writeType(promotedBound!);
-      printer.write(')');
-      printer.write(nullabilityToString(nullability));
-    } else {
-      printer.writeTypeParameterName(parameter);
-      printer.write(nullabilityToString(declaredNullability));
-    }
+    printer.writeTypeParameterName(parameter);
+    printer.write(nullabilityToString(declaredNullability));
   }
 }
 
diff --git a/pkg/kernel/lib/binary/ast_from_binary.dart b/pkg/kernel/lib/binary/ast_from_binary.dart
index f37d04d..5feffea 100644
--- a/pkg/kernel/lib/binary/ast_from_binary.dart
+++ b/pkg/kernel/lib/binary/ast_from_binary.dart
@@ -3053,6 +3053,8 @@
         return _readSimpleFunctionType();
       case Tag.TypeParameterType:
         return _readTypeParameterType();
+      case Tag.IntersectionType:
+        return _readIntersectionType();
       default:
         throw fail('unexpected dart type tag: $tag');
     }
@@ -3142,9 +3144,14 @@
   DartType _readTypeParameterType() {
     int declaredNullabilityIndex = readByte();
     int index = readUInt30();
-    DartType? bound = readDartTypeOption();
     return new TypeParameterType(typeParameterStack[index],
-        Nullability.values[declaredNullabilityIndex], bound);
+        Nullability.values[declaredNullabilityIndex]);
+  }
+
+  DartType _readIntersectionType() {
+    TypeParameterType left = readDartType() as TypeParameterType;
+    DartType right = readDartType();
+    return new IntersectionType(left, right);
   }
 
   List<TypeParameter> readAndPushTypeParameterList(
diff --git a/pkg/kernel/lib/binary/ast_to_binary.dart b/pkg/kernel/lib/binary/ast_to_binary.dart
index 6b7c969..8b33a8b 100644
--- a/pkg/kernel/lib/binary/ast_to_binary.dart
+++ b/pkg/kernel/lib/binary/ast_to_binary.dart
@@ -2501,7 +2501,13 @@
     writeByte(Tag.TypeParameterType);
     writeByte(node.declaredNullability.index);
     writeUInt30(_typeParameterIndexer[node.parameter]);
-    writeOptionalNode(node.promotedBound);
+  }
+
+  @override
+  void visitIntersectionType(IntersectionType node) {
+    writeByte(Tag.IntersectionType);
+    writeDartType(node.left);
+    writeDartType(node.right);
   }
 
   @override
diff --git a/pkg/kernel/lib/binary/tag.dart b/pkg/kernel/lib/binary/tag.dart
index 2e861b6..985273d 100644
--- a/pkg/kernel/lib/binary/tag.dart
+++ b/pkg/kernel/lib/binary/tag.dart
@@ -140,6 +140,7 @@
   static const int SimpleInterfaceType = 96;
   static const int SimpleFunctionType = 97;
   static const int NeverType = 98;
+  static const int IntersectionType = 99;
 
   static const int ConstantExpression = 106;
 
@@ -179,7 +180,7 @@
   /// Internal version of kernel binary format.
   /// Bump it when making incompatible changes in kernel binaries.
   /// Keep in sync with runtime/vm/kernel_binary.h, pkg/kernel/binary.md.
-  static const int BinaryFormatVersion = 83;
+  static const int BinaryFormatVersion = 84;
 }
 
 abstract class ConstantTag {
diff --git a/pkg/kernel/lib/core_types.dart b/pkg/kernel/lib/core_types.dart
index 1734950..ec38516 100644
--- a/pkg/kernel/lib/core_types.dart
+++ b/pkg/kernel/lib/core_types.dart
@@ -1142,15 +1142,12 @@
     }
 
     // BOTTOM(X&T) is true iff BOTTOM(T).
-    if (type is TypeParameterType &&
-        type.promotedBound != null &&
-        type.isPotentiallyNonNullable) {
-      return isBottom(type.promotedBound!);
+    if (type is IntersectionType && type.isPotentiallyNonNullable) {
+      return isBottom(type.right);
     }
 
     // BOTTOM(X extends T) is true iff BOTTOM(T).
     if (type is TypeParameterType && type.isPotentiallyNonNullable) {
-      assert(type.promotedBound == null);
       return isBottom(type.parameter.bound);
     }
 
diff --git a/pkg/kernel/lib/src/bounds_checks.dart b/pkg/kernel/lib/src/bounds_checks.dart
index 884fe9d..ccb9e59 100644
--- a/pkg/kernel/lib/src/bounds_checks.dart
+++ b/pkg/kernel/lib/src/bounds_checks.dart
@@ -467,7 +467,7 @@
   }
   for (int i = 0; i < arguments.length; ++i) {
     DartType argument = arguments[i];
-    if (argument is TypeParameterType && argument.promotedBound != null) {
+    if (argument is IntersectionType) {
       // TODO(cstefantsova): Consider recognizing this case with a flag on the
       // issue object.
       result.add(new TypeArgumentIssue(i, argument, parameters[i], null));
@@ -642,6 +642,16 @@
     }
   }
 
+  @override
+  DartType? visitIntersectionType(IntersectionType node, int variance) {
+    // Types such as X & Never are bottom types.
+    if (isBottom(node) && flipBottom(variance)) {
+      return topType;
+    } else {
+      return null;
+    }
+  }
+
   // TypedefTypes receive special treatment because the variance of their
   // arguments' positions depend on the opt-in status of the library.
   @override
@@ -715,6 +725,13 @@
   }
 
   @override
+  int visitIntersectionType(IntersectionType node,
+      Map<TypeParameter, Map<DartType, int>> computedVariances) {
+    if (node.left.parameter == typeParameter) return Variance.covariant;
+    return Variance.unrelated;
+  }
+
+  @override
   int visitInterfaceType(InterfaceType node,
       Map<TypeParameter, Map<DartType, int>> computedVariances) {
     int result = Variance.unrelated;
diff --git a/pkg/kernel/lib/src/coverage.dart b/pkg/kernel/lib/src/coverage.dart
index 553e22f..6625a63 100644
--- a/pkg/kernel/lib/src/coverage.dart
+++ b/pkg/kernel/lib/src/coverage.dart
@@ -744,6 +744,12 @@
   }
 
   @override
+  void visitIntersectionType(IntersectionType node) {
+    visited.add(DartTypeKind.IntersectionType);
+    node.visitChildren(this);
+  }
+
+  @override
   void visitTypeParameterType(TypeParameterType node) {
     visited.add(DartTypeKind.TypeParameterType);
     node.visitChildren(this);
@@ -1121,6 +1127,7 @@
   FunctionType,
   FutureOrType,
   InterfaceType,
+  IntersectionType,
   InvalidType,
   NeverType,
   NullType,
diff --git a/pkg/kernel/lib/src/dart_type_equivalence.dart b/pkg/kernel/lib/src/dart_type_equivalence.dart
index 18a2081..60dc0e1 100644
--- a/pkg/kernel/lib/src/dart_type_equivalence.dart
+++ b/pkg/kernel/lib/src/dart_type_equivalence.dart
@@ -194,11 +194,6 @@
   @override
   bool visitTypeParameterType(TypeParameterType node, DartType other) {
     if (other is TypeParameterType) {
-      bool nodeIsIntersection = node.promotedBound != null;
-      bool otherIsIntersection = other.promotedBound != null;
-      if (nodeIsIntersection != otherIsIntersection) {
-        return false;
-      }
       if (!_checkAndRegisterNullabilities(
           node.declaredNullability, other.declaredNullability)) {
         return false;
@@ -206,9 +201,16 @@
       if (!identical(_lookup(node.parameter), other.parameter)) {
         return false;
       }
-      return nodeIsIntersection
-          ? node.promotedBound!.accept1(this, other.promotedBound)
-          : true;
+      return true;
+    }
+    return false;
+  }
+
+  @override
+  bool visitIntersectionType(IntersectionType node, DartType other) {
+    if (other is IntersectionType) {
+      return node.left.accept1(this, other.left) &&
+          node.right.accept1(this, other.right);
     }
     return false;
   }
diff --git a/pkg/kernel/lib/src/equivalence.dart b/pkg/kernel/lib/src/equivalence.dart
index 365311d..9ee89a7 100644
--- a/pkg/kernel/lib/src/equivalence.dart
+++ b/pkg/kernel/lib/src/equivalence.dart
@@ -666,6 +666,11 @@
   }
 
   @override
+  bool visitIntersectionType(IntersectionType node, Node other) {
+    return strategy.checkIntersectionType(this, node, other);
+  }
+
+  @override
   bool visitTypeParameterType(TypeParameterType node, Node other) {
     return strategy.checkTypeParameterType(this, node, other);
   }
@@ -4245,6 +4250,23 @@
     return result;
   }
 
+  bool checkIntersectionType(
+      EquivalenceVisitor visitor, IntersectionType? node, Object? other) {
+    if (identical(node, other)) return true;
+    if (node is! IntersectionType) return false;
+    if (other is! IntersectionType) return false;
+    visitor.pushNodeState(node, other);
+    bool result = true;
+    if (!checkIntersectionType_left(visitor, node, other)) {
+      result = visitor.resultOnInequivalence;
+    }
+    if (!checkIntersectionType_right(visitor, node, other)) {
+      result = visitor.resultOnInequivalence;
+    }
+    visitor.popState();
+    return result;
+  }
+
   bool checkTypeParameterType(
       EquivalenceVisitor visitor, TypeParameterType? node, Object? other) {
     if (identical(node, other)) return true;
@@ -4258,9 +4280,6 @@
     if (!checkTypeParameterType_parameter(visitor, node, other)) {
       result = visitor.resultOnInequivalence;
     }
-    if (!checkTypeParameterType_promotedBound(visitor, node, other)) {
-      result = visitor.resultOnInequivalence;
-    }
     visitor.popState();
     return result;
   }
@@ -7351,6 +7370,16 @@
     return visitor.checkNodes(node.onType, other.onType, 'onType');
   }
 
+  bool checkIntersectionType_left(EquivalenceVisitor visitor,
+      IntersectionType node, IntersectionType other) {
+    return visitor.checkNodes(node.left, other.left, 'left');
+  }
+
+  bool checkIntersectionType_right(EquivalenceVisitor visitor,
+      IntersectionType node, IntersectionType other) {
+    return visitor.checkNodes(node.right, other.right, 'right');
+  }
+
   bool checkTypeParameterType_declaredNullability(EquivalenceVisitor visitor,
       TypeParameterType node, TypeParameterType other) {
     return visitor.checkValues(node.declaredNullability,
@@ -7363,12 +7392,6 @@
         node.parameter, other.parameter, 'parameter');
   }
 
-  bool checkTypeParameterType_promotedBound(EquivalenceVisitor visitor,
-      TypeParameterType node, TypeParameterType other) {
-    return visitor.checkNodes(
-        node.promotedBound, other.promotedBound, 'promotedBound');
-  }
-
   bool checkNamedType_name(
       EquivalenceVisitor visitor, NamedType node, NamedType other) {
     return visitor.checkValues(node.name, other.name, 'name');
diff --git a/pkg/kernel/lib/src/future_value_type.dart b/pkg/kernel/lib/src/future_value_type.dart
index 773c8e7..b5c2e08 100644
--- a/pkg/kernel/lib/src/future_value_type.dart
+++ b/pkg/kernel/lib/src/future_value_type.dart
@@ -88,6 +88,12 @@
   }
 
   @override
+  DartType visitIntersectionType(DartType node, CoreTypes coreTypes) {
+    // Otherwise, for all S, futureValueType(S) = Object?.
+    return coreTypes.objectNullableRawType;
+  }
+
+  @override
   DartType visitTypedefType(DartType node, CoreTypes coreTypes) {
     // Otherwise, for all S, futureValueType(S) = Object?.
     return coreTypes.objectNullableRawType;
diff --git a/pkg/kernel/lib/src/merge_visitor.dart b/pkg/kernel/lib/src/merge_visitor.dart
index 58e18fa..da2040d 100644
--- a/pkg/kernel/lib/src/merge_visitor.dart
+++ b/pkg/kernel/lib/src/merge_visitor.dart
@@ -281,11 +281,7 @@
       if (nullability == null) {
         return null;
       }
-      if (a.promotedBound != null && b.promotedBound != null) {
-        return mergePromotedTypeParameterTypes(a, b, nullability);
-      } else if (a.promotedBound == null && b.promotedBound == null) {
-        return mergeTypeParameterTypes(a, b, nullability);
-      }
+      return mergeTypeParameterTypes(a, b, nullability);
     }
     if (b is InvalidType) {
       return b;
@@ -296,22 +292,30 @@
   DartType mergeTypeParameterTypes(
       TypeParameterType a, TypeParameterType b, Nullability nullability) {
     assert(a.parameter == b.parameter);
-    assert(a.promotedBound == null);
-    assert(b.promotedBound == null);
     return new TypeParameterType(a.parameter, nullability);
   }
 
-  DartType? mergePromotedTypeParameterTypes(
-      TypeParameterType a, TypeParameterType b, Nullability nullability) {
-    assert(a.parameter == b.parameter);
-    assert(a.promotedBound != null);
-    assert(b.promotedBound != null);
-    DartType? newPromotedBound =
-        a.promotedBound!.accept1(this, b.promotedBound);
-    if (newPromotedBound == null) {
+  @override
+  DartType? visitIntersectionType(IntersectionType a, DartType b) {
+    if (b is IntersectionType) {
+      return mergeIntersectionTypes(a, b);
+    }
+    if (b is InvalidType) {
+      return b;
+    }
+    return null;
+  }
+
+  DartType? mergeIntersectionTypes(IntersectionType a, IntersectionType b) {
+    DartType? newLeft = a.left.accept1(this, b.left);
+    if (newLeft == null) {
       return null;
     }
-    return new TypeParameterType(a.parameter, nullability, newPromotedBound);
+    DartType? newRight = a.right.accept1(this, b.right);
+    if (newRight == null) {
+      return null;
+    }
+    return new IntersectionType(newLeft as TypeParameterType, newRight);
   }
 
   @override
diff --git a/pkg/kernel/lib/src/node_creator.dart b/pkg/kernel/lib/src/node_creator.dart
index 51fc5d4..eb1d1cf 100644
--- a/pkg/kernel/lib/src/node_creator.dart
+++ b/pkg/kernel/lib/src/node_creator.dart
@@ -1134,8 +1134,12 @@
         return _createOneOf(_pendingDartTypes, kind, index, [
           () =>
               TypeParameterType(_needTypeParameter(), Nullability.nonNullable),
-          () => TypeParameterType(
-              _needTypeParameter(), Nullability.nonNullable, _createDartType()),
+        ]);
+      case DartTypeKind.IntersectionType:
+        return _createOneOf(_pendingDartTypes, kind, index, [
+          () => IntersectionType(
+              TypeParameterType(_needTypeParameter(), Nullability.nonNullable),
+              _createDartType()),
         ]);
       case DartTypeKind.TypedefType:
         return _createOneOf(_pendingDartTypes, kind, index, [
diff --git a/pkg/kernel/lib/src/non_null.dart b/pkg/kernel/lib/src/non_null.dart
index b0e7cd2..bcdd0b9 100644
--- a/pkg/kernel/lib/src/non_null.dart
+++ b/pkg/kernel/lib/src/non_null.dart
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE.md file.
 
 import '../ast.dart';
+import '../type_algebra.dart';
 
 /// Returns the type defined as `NonNull(type)` in the nnbd specification.
 DartType computeNonNull(DartType type) {
@@ -134,65 +135,70 @@
   DartType? visitTypeParameterType(TypeParameterType node) {
     // NonNull(X) = X & NonNull(B), where B is the bound of X.
     //
-    // NonNull(X & T) = X & NonNull(T)
-    //
     // NonNull(T?) = NonNull(T)
     //
     // NonNull(T*) = NonNull(T)
     if (node.nullability == Nullability.nonNullable) {
       return null;
     }
-    if (node.promotedBound != null) {
-      // NonNull(X & T) = X & NonNull(T)
-
-      if (node.promotedBound!.nullability == Nullability.nonNullable) {
-        // The promoted bound is already non-nullable so we set the declared
-        // nullability to non-nullable.
-        return node.withDeclaredNullability(Nullability.nonNullable);
+    // NonNull(X) = X & NonNull(B), where B is the bound of X.
+    if (node.bound.nullability == Nullability.nonNullable) {
+      // The bound is already non-nullable so we set the declared nullability
+      // to non-nullable.
+      return node.withDeclaredNullability(Nullability.nonNullable);
+    }
+    DartType? bound = node.bound.accept(this);
+    if (bound == null) {
+      // The bound could not be made non-nullable so we set the declared
+      // nullability to undetermined.
+      if (node.declaredNullability == Nullability.undetermined) {
+        return null;
       }
-      DartType? promotedBound = node.promotedBound!.accept(this);
-      if (promotedBound == null) {
-        // The promoted bound could not be made non-nullable so we set the
-        // declared nullability to undetermined.
-        if (node.declaredNullability == Nullability.undetermined) {
-          return null;
-        }
-        return new TypeParameterType.intersection(
-            node.parameter, Nullability.undetermined, node.promotedBound!);
-      } else if (promotedBound.nullability == Nullability.nonNullable) {
-        // The bound could be made non-nullable so we use it as the promoted
-        // bound.
-        return new TypeParameterType.intersection(
-            node.parameter, Nullability.nonNullable, promotedBound);
-      } else {
-        // The bound could not be made non-nullable so we use it as the promoted
-        // bound with undetermined nullability.
-        return new TypeParameterType.intersection(
-            node.parameter, Nullability.undetermined, promotedBound);
-      }
+      return node.withDeclaredNullability(Nullability.undetermined);
     } else {
-      // NonNull(X) = X & NonNull(B), where B is the bound of X.
-      if (node.bound.nullability == Nullability.nonNullable) {
-        // The bound is already non-nullable so we set the declared nullability
-        // to non-nullable.
-        return node.withDeclaredNullability(Nullability.nonNullable);
+      // The nullability is fully determined by the bound so we pass the
+      // default nullability for the declared nullability.
+      return new IntersectionType(
+          new TypeParameterType(node.parameter,
+              TypeParameterType.computeNullabilityFromBound(node.parameter)),
+          bound);
+    }
+  }
+
+  @override
+  DartType? visitIntersectionType(IntersectionType node) {
+    // NonNull(X & T) = X & NonNull(T)
+    if (node.nullability == Nullability.nonNullable) {
+      return null;
+    }
+
+    if (node.right.nullability == Nullability.nonNullable) {
+      // The RHS is already non-nullable so nothing should be changed.
+      return node.withDeclaredNullability(Nullability.nonNullable);
+    }
+    DartType? right = node.right.accept(this);
+    if (right == null) {
+      // The RHS could not be made non-nullable so we set the
+      // declared nullability to undetermined.
+      if (node.left.declaredNullability == Nullability.undetermined) {
+        return null;
       }
-      DartType? bound = node.bound.accept(this);
-      if (bound == null) {
-        // The bound could not be made non-nullable so we set the declared
-        // nullability to undetermined.
-        if (node.declaredNullability == Nullability.undetermined) {
-          return null;
-        }
-        return node.withDeclaredNullability(Nullability.undetermined);
-      } else {
-        // The nullability is fully determined by the bound so we pass the
-        // default nullability for the declared nullability.
-        return new TypeParameterType.intersection(
-            node.parameter,
-            TypeParameterType.computeNullabilityFromBound(node.parameter),
-            bound);
-      }
+      return new IntersectionType(
+          new TypeParameterType(node.left.parameter, Nullability.undetermined),
+          node.right);
+    } else if (right.nullability == Nullability.nonNullable) {
+      // The bound could be made non-nullable so we use it as the promoted
+      // bound.
+      return new IntersectionType(
+          computeTypeWithoutNullabilityMarker(node.left,
+              isNonNullableByDefault: true) as TypeParameterType,
+          right);
+    } else {
+      // The bound could not be made non-nullable so we use it as the promoted
+      // bound with undetermined nullability.
+      return new IntersectionType(
+          new TypeParameterType(node.left.parameter, Nullability.undetermined),
+          right);
     }
   }
 
diff --git a/pkg/kernel/lib/src/norm.dart b/pkg/kernel/lib/src/norm.dart
index 0cc5d54..e961f27 100644
--- a/pkg/kernel/lib/src/norm.dart
+++ b/pkg/kernel/lib/src/norm.dart
@@ -92,44 +92,43 @@
 
   @override
   DartType? visitTypeParameterType(TypeParameterType node, int variance) {
-    if (node.promotedBound == null) {
-      DartType bound = node.parameter.bound;
-      if (normalizesToNever(bound)) {
-        DartType result = NeverType.fromNullability(node.nullability);
-        return result.accept1(this, variance) ?? result;
-      }
-      assert(!coreTypes.isBottom(bound));
-      // If the bound isn't Never, the type is already normalized.
-      return null;
-    } else {
-      DartType bound = node.promotedBound!;
-      bound = bound.accept1(this, variance) ?? bound;
-      if (bound is NeverType && bound.nullability == Nullability.nonNullable) {
-        return bound;
-      } else if (coreTypes.isTop(bound)) {
-        assert(!coreTypes.isBottom(bound));
-        assert(bound.nullability == Nullability.nullable);
-        return new TypeParameterType(node.parameter, node.declaredNullability);
-      } else if (bound is TypeParameterType &&
-          bound.parameter == node.parameter &&
-          bound.declaredNullability == node.declaredNullability &&
-          bound.promotedBound == null) {
-        assert(!coreTypes.isBottom(bound));
-        assert(!coreTypes.isTop(bound));
-        return new TypeParameterType(node.parameter, node.declaredNullability);
-      } else if (bound == coreTypes.objectNonNullableRawType &&
-          norm(coreTypes, node.parameter.bound) ==
-              coreTypes.objectNonNullableRawType) {
-        return new TypeParameterType(node.parameter, node.declaredNullability);
-      } else if (identical(bound, node.promotedBound)) {
-        // If [bound] is identical to [node.promotedBound], then the NORM
-        // algorithms didn't change the promoted bound, so the [node] is
-        // unchanged as well, and we return null to indicate that.
-        return null;
-      }
-      return new TypeParameterType(
-          node.parameter, node.declaredNullability, bound);
+    DartType bound = node.parameter.bound;
+    if (normalizesToNever(bound)) {
+      DartType result = NeverType.fromNullability(node.nullability);
+      return result.accept1(this, variance) ?? result;
     }
+    assert(!coreTypes.isBottom(bound));
+    // If the bound isn't Never, the type is already normalized.
+    return null;
+  }
+
+  @override
+  DartType? visitIntersectionType(IntersectionType node, int variance) {
+    DartType right = node.right;
+    right = right.accept1(this, variance) ?? right;
+    if (right is NeverType && right.nullability == Nullability.nonNullable) {
+      return right;
+    } else if (coreTypes.isTop(right)) {
+      assert(!coreTypes.isBottom(right));
+      assert(right.nullability == Nullability.nullable);
+      return node.left;
+    } else if (right is TypeParameterType &&
+        right.parameter == node.left.parameter &&
+        right.declaredNullability == node.left.declaredNullability) {
+      assert(!coreTypes.isBottom(right));
+      assert(!coreTypes.isTop(right));
+      return node.left;
+    } else if (right == coreTypes.objectNonNullableRawType &&
+        norm(coreTypes, node.left.parameter.bound) ==
+            coreTypes.objectNonNullableRawType) {
+      return node.left;
+    } else if (identical(right, node.right)) {
+      // If [bound] is identical to [node.right], then the NORM
+      // algorithms didn't change the promoted bound, so the [node] is
+      // unchanged as well, and we return null to indicate that.
+      return null;
+    }
+    return new IntersectionType(node.left, right);
   }
 
   @override
@@ -142,11 +141,9 @@
     if (type is NeverType && type.nullability == Nullability.nonNullable) {
       return true;
     } else if (type is TypeParameterType) {
-      if (type.promotedBound == null) {
-        return normalizesToNever(type.parameter.bound);
-      } else {
-        return normalizesToNever(type.promotedBound!);
-      }
+      return normalizesToNever(type.parameter.bound);
+    } else if (type is IntersectionType) {
+      return normalizesToNever(type.right);
     }
     return false;
   }
diff --git a/pkg/kernel/lib/src/replacement_visitor.dart b/pkg/kernel/lib/src/replacement_visitor.dart
index 5a60355..3fea941 100644
--- a/pkg/kernel/lib/src/replacement_visitor.dart
+++ b/pkg/kernel/lib/src/replacement_visitor.dart
@@ -213,14 +213,17 @@
   @override
   DartType? visitTypeParameterType(TypeParameterType node, int variance) {
     Nullability? newNullability = visitNullability(node);
-    if (node.promotedBound != null) {
-      DartType? newPromotedBound = node.promotedBound!.accept1(this, variance);
-      return createPromotedTypeParameterType(
-          node, newNullability, newPromotedBound);
-    }
     return createTypeParameterType(node, newNullability);
   }
 
+  @override
+  DartType? visitIntersectionType(IntersectionType node, int variance) {
+    DartType? newLeft = node.left.accept1(this, variance);
+    DartType? newRight = node.right.accept1(this, variance);
+    return createIntersectionType(
+        node, newLeft as TypeParameterType?, newRight);
+  }
+
   DartType? createTypeParameterType(
       TypeParameterType node, Nullability? newNullability) {
     if (newNullability == null) {
@@ -231,16 +234,12 @@
     }
   }
 
-  DartType? createPromotedTypeParameterType(TypeParameterType node,
-      Nullability? newNullability, DartType? newPromotedBound) {
-    if (newNullability == null && newPromotedBound == null) {
-      // No nullability or bound needed to be substituted.
+  DartType? createIntersectionType(
+      IntersectionType node, TypeParameterType? left, DartType? right) {
+    if (left == null && right == null) {
       return null;
     } else {
-      return new TypeParameterType(
-          node.parameter,
-          newNullability ?? node.declaredNullability,
-          newPromotedBound ?? node.promotedBound);
+      return new IntersectionType(left ?? node.left, right ?? node.right);
     }
   }
 
diff --git a/pkg/kernel/lib/src/standard_bounds.dart b/pkg/kernel/lib/src/standard_bounds.dart
index 4263359..9e3b8a7 100644
--- a/pkg/kernel/lib/src/standard_bounds.dart
+++ b/pkg/kernel/lib/src/standard_bounds.dart
@@ -211,27 +211,22 @@
     }
 
     // MOREBOTTOM(X&S, Y&T) = MOREBOTTOM(S, T).
-    if (s is TypeParameterType &&
-        s.promotedBound != null &&
-        t is TypeParameterType &&
-        t.promotedBound != null) {
-      return morebottom(s.promotedBound!, t.promotedBound!);
+    if (s is IntersectionType && t is IntersectionType) {
+      return morebottom(s.right, t.right);
     }
 
     // MOREBOTTOM(X&S, T) = true.
-    if (s is TypeParameterType && s.promotedBound != null) {
+    if (s is IntersectionType) {
       return true;
     }
 
     // MOREBOTTOM(S, X&T) = false.
-    if (t is TypeParameterType && t.promotedBound != null) {
+    if (t is IntersectionType) {
       return false;
     }
 
     // MOREBOTTOM(X extends S, Y extends T) = MOREBOTTOM(S, T).
     if (s is TypeParameterType && t is TypeParameterType) {
-      assert(s.promotedBound == null);
-      assert(t.promotedBound == null);
       return morebottom(s.parameter.bound, t.parameter.bound);
     }
 
@@ -741,11 +736,21 @@
           type1, type2, clientLibrary);
     }
 
+    if (type1 is IntersectionType) {
+      return _getNullabilityAwareIntersectionStandardUpperBound(
+          type1, type2, clientLibrary);
+    }
+
     if (type2 is TypeParameterType) {
       return _getNullabilityAwareTypeParameterStandardUpperBound(
           type2, type1, clientLibrary);
     }
 
+    if (type2 is IntersectionType) {
+      return _getNullabilityAwareIntersectionStandardUpperBound(
+          type2, type1, clientLibrary);
+    }
+
     if (type1 is FunctionType) {
       if (type2 is FunctionType) {
         return _getNullabilityAwareFunctionStandardUpperBound(
@@ -1216,67 +1221,65 @@
 
   DartType _getNullabilityAwareTypeParameterStandardUpperBound(
       TypeParameterType type1, DartType type2, Library clientLibrary) {
-    if (type1.promotedBound == null) {
-      // UP(X1 extends B1, T2) =
-      //   T2 if X1 <: T2
-      //   otherwise X1 if T2 <: X1
-      //   otherwise UP(B1a, T2)
-      //     where B1a is the greatest closure of B1 with respect to X1,
-      //     as defined in [inference.md].
-      if (isSubtypeOf(type1, type2, SubtypeCheckMode.withNullabilities)) {
-        return type2.withDeclaredNullability(
-            uniteNullabilities(type1.declaredNullability, type2.nullability));
-      }
-      if (isSubtypeOf(type2, type1, SubtypeCheckMode.withNullabilities)) {
-        return type1.withDeclaredNullability(
-            uniteNullabilities(type1.declaredNullability, type2.nullability));
-      }
-      NullabilityAwareTypeVariableEliminator eliminator =
-          new NullabilityAwareTypeVariableEliminator(
-              eliminationTargets: <TypeParameter>{type1.parameter},
-              bottomType: const NeverType.nonNullable(),
-              topType: coreTypes.objectNullableRawType,
-              topFunctionType: coreTypes.functionNonNullableRawType,
-              unhandledTypeHandler: (type, recursor) => false);
-      return _getNullabilityAwareStandardUpperBound(
-              eliminator.eliminateToGreatest(type1.parameter.bound),
-              type2,
-              clientLibrary)
-          .withDeclaredNullability(uniteNullabilities(
-              type1.declaredNullability,
-              uniteNullabilities(type1.parameter.bound.declaredNullability,
-                  type2.nullability)));
-    } else {
-      // UP(X1 & B1, T2) =
-      //   T2 if X1 <: T2
-      //   otherwise X1 if T2 <: X1
-      //   otherwise UP(B1a, T2)
-      //     where B1a is the greatest closure of B1 with respect to X1,
-      //     as defined in [inference.md].
-      DartType demoted =
-          new TypeParameterType(type1.parameter, type1.declaredNullability);
-      if (isSubtypeOf(demoted, type2, SubtypeCheckMode.withNullabilities)) {
-        return type2.withDeclaredNullability(uniteNullabilities(
-            type1.declaredNullability, type2.declaredNullability));
-      }
-      if (isSubtypeOf(type2, demoted, SubtypeCheckMode.withNullabilities)) {
-        return demoted.withDeclaredNullability(uniteNullabilities(
-            type1.declaredNullability, type2.declaredNullability));
-      }
-      NullabilityAwareTypeVariableEliminator eliminator =
-          new NullabilityAwareTypeVariableEliminator(
-              eliminationTargets: <TypeParameter>{type1.parameter},
-              bottomType: const NeverType.nonNullable(),
-              topType: coreTypes.objectNullableRawType,
-              topFunctionType: coreTypes.functionNonNullableRawType,
-              unhandledTypeHandler: (type, recursor) => false);
-      return _getNullabilityAwareStandardUpperBound(
-              eliminator.eliminateToGreatest(type1.promotedBound!),
-              type2,
-              clientLibrary)
-          .withDeclaredNullability(uniteNullabilities(
-              type1.promotedBound!.declaredNullability, type2.nullability));
+    // UP(X1 extends B1, T2) =
+    //   T2 if X1 <: T2
+    //   otherwise X1 if T2 <: X1
+    //   otherwise UP(B1a, T2)
+    //     where B1a is the greatest closure of B1 with respect to X1,
+    //     as defined in [inference.md].
+    if (isSubtypeOf(type1, type2, SubtypeCheckMode.withNullabilities)) {
+      return type2.withDeclaredNullability(
+          uniteNullabilities(type1.declaredNullability, type2.nullability));
     }
+    if (isSubtypeOf(type2, type1, SubtypeCheckMode.withNullabilities)) {
+      return type1.withDeclaredNullability(
+          uniteNullabilities(type1.declaredNullability, type2.nullability));
+    }
+    NullabilityAwareTypeVariableEliminator eliminator =
+        new NullabilityAwareTypeVariableEliminator(
+            eliminationTargets: <TypeParameter>{type1.parameter},
+            bottomType: const NeverType.nonNullable(),
+            topType: coreTypes.objectNullableRawType,
+            topFunctionType: coreTypes.functionNonNullableRawType,
+            unhandledTypeHandler: (type, recursor) => false);
+    return _getNullabilityAwareStandardUpperBound(
+            eliminator.eliminateToGreatest(type1.parameter.bound),
+            type2,
+            clientLibrary)
+        .withDeclaredNullability(uniteNullabilities(
+            type1.declaredNullability,
+            uniteNullabilities(
+                type1.parameter.bound.declaredNullability, type2.nullability)));
+  }
+
+  DartType _getNullabilityAwareIntersectionStandardUpperBound(
+      IntersectionType type1, DartType type2, Library clientLibrary) {
+    // UP(X1 & B1, T2) =
+    //   T2 if X1 <: T2
+    //   otherwise X1 if T2 <: X1
+    //   otherwise UP(B1a, T2)
+    //     where B1a is the greatest closure of B1 with respect to X1,
+    //     as defined in [inference.md].
+    DartType demoted = type1.left;
+    if (isSubtypeOf(demoted, type2, SubtypeCheckMode.withNullabilities)) {
+      return type2.withDeclaredNullability(uniteNullabilities(
+          type1.declaredNullability, type2.declaredNullability));
+    }
+    if (isSubtypeOf(type2, demoted, SubtypeCheckMode.withNullabilities)) {
+      return demoted.withDeclaredNullability(uniteNullabilities(
+          type1.declaredNullability, type2.declaredNullability));
+    }
+    NullabilityAwareTypeVariableEliminator eliminator =
+        new NullabilityAwareTypeVariableEliminator(
+            eliminationTargets: <TypeParameter>{type1.left.parameter},
+            bottomType: const NeverType.nonNullable(),
+            topType: coreTypes.objectNullableRawType,
+            topFunctionType: coreTypes.functionNonNullableRawType,
+            unhandledTypeHandler: (type, recursor) => false);
+    return _getNullabilityAwareStandardUpperBound(
+            eliminator.eliminateToGreatest(type1.right), type2, clientLibrary)
+        .withDeclaredNullability(uniteNullabilities(
+            type1.right.declaredNullability, type2.nullability));
   }
 
   DartType _getNullabilityObliviousStandardUpperBound(
diff --git a/pkg/kernel/lib/src/types.dart b/pkg/kernel/lib/src/types.dart
index 4cfbb33..ba77a54 100644
--- a/pkg/kernel/lib/src/types.dart
+++ b/pkg/kernel/lib/src/types.dart
@@ -106,9 +106,9 @@
       } else if (s is FunctionType) {
         return relation.isFunctionRelated(s, t, this);
       } else if (s is TypeParameterType) {
-        return s.promotedBound == null
-            ? relation.isTypeParameterRelated(s, t, this)
-            : relation.isIntersectionRelated(s, t, this);
+        return relation.isTypeParameterRelated(s, t, this);
+      } else if (s is IntersectionType) {
+        return relation.isIntersectionRelated(s, t, this);
       } else if (s is TypedefType) {
         return relation.isTypedefRelated(s, t, this);
       } else if (s is FutureOrType) {
@@ -127,9 +127,9 @@
       } else if (s is FunctionType) {
         return relation.isFunctionRelated(s, t, this);
       } else if (s is TypeParameterType) {
-        return s.promotedBound == null
-            ? relation.isTypeParameterRelated(s, t, this)
-            : relation.isIntersectionRelated(s, t, this);
+        return relation.isTypeParameterRelated(s, t, this);
+      } else if (s is IntersectionType) {
+        return relation.isIntersectionRelated(s, t, this);
       } else if (s is TypedefType) {
         return relation.isTypedefRelated(s, t, this);
       } else if (s is FutureOrType) {
@@ -138,50 +138,47 @@
         return relation.isExtensionRelated(s, t, this);
       }
     } else if (t is TypeParameterType) {
-      if (t.promotedBound == null) {
-        const IsTypeParameterSubtypeOf relation =
-            const IsTypeParameterSubtypeOf();
-        if (s is DynamicType) {
-          return relation.isDynamicRelated(s, t, this);
-        } else if (s is VoidType) {
-          return relation.isVoidRelated(s, t, this);
-        } else if (s is InterfaceType) {
-          return relation.isInterfaceRelated(s, t, this);
-        } else if (s is FunctionType) {
-          return relation.isFunctionRelated(s, t, this);
-        } else if (s is TypeParameterType) {
-          return s.promotedBound == null
-              ? relation.isTypeParameterRelated(s, t, this)
-              : relation.isIntersectionRelated(s, t, this);
-        } else if (s is TypedefType) {
-          return relation.isTypedefRelated(s, t, this);
-        } else if (s is FutureOrType) {
-          return relation.isFutureOrRelated(s, t, this);
-        } else if (s is ExtensionType) {
-          return relation.isExtensionRelated(s, t, this);
-        }
-      } else {
-        const IsIntersectionSubtypeOf relation =
-            const IsIntersectionSubtypeOf();
-        if (s is DynamicType) {
-          return relation.isDynamicRelated(s, t, this);
-        } else if (s is VoidType) {
-          return relation.isVoidRelated(s, t, this);
-        } else if (s is InterfaceType) {
-          return relation.isInterfaceRelated(s, t, this);
-        } else if (s is FunctionType) {
-          return relation.isFunctionRelated(s, t, this);
-        } else if (s is TypeParameterType) {
-          return s.promotedBound == null
-              ? relation.isTypeParameterRelated(s, t, this)
-              : relation.isIntersectionRelated(s, t, this);
-        } else if (s is TypedefType) {
-          return relation.isTypedefRelated(s, t, this);
-        } else if (s is FutureOrType) {
-          return relation.isFutureOrRelated(s, t, this);
-        } else if (s is ExtensionType) {
-          return relation.isExtensionRelated(s, t, this);
-        }
+      const IsTypeParameterSubtypeOf relation =
+          const IsTypeParameterSubtypeOf();
+      if (s is DynamicType) {
+        return relation.isDynamicRelated(s, t, this);
+      } else if (s is VoidType) {
+        return relation.isVoidRelated(s, t, this);
+      } else if (s is InterfaceType) {
+        return relation.isInterfaceRelated(s, t, this);
+      } else if (s is FunctionType) {
+        return relation.isFunctionRelated(s, t, this);
+      } else if (s is TypeParameterType) {
+        return relation.isTypeParameterRelated(s, t, this);
+      } else if (s is IntersectionType) {
+        return relation.isIntersectionRelated(s, t, this);
+      } else if (s is TypedefType) {
+        return relation.isTypedefRelated(s, t, this);
+      } else if (s is FutureOrType) {
+        return relation.isFutureOrRelated(s, t, this);
+      } else if (s is ExtensionType) {
+        return relation.isExtensionRelated(s, t, this);
+      }
+    } else if (t is IntersectionType) {
+      const IsIntersectionSubtypeOf relation = const IsIntersectionSubtypeOf();
+      if (s is DynamicType) {
+        return relation.isDynamicRelated(s, t, this);
+      } else if (s is VoidType) {
+        return relation.isVoidRelated(s, t, this);
+      } else if (s is InterfaceType) {
+        return relation.isInterfaceRelated(s, t, this);
+      } else if (s is FunctionType) {
+        return relation.isFunctionRelated(s, t, this);
+      } else if (s is TypeParameterType) {
+        return relation.isTypeParameterRelated(s, t, this);
+      } else if (s is IntersectionType) {
+        return relation.isIntersectionRelated(s, t, this);
+      } else if (s is TypedefType) {
+        return relation.isTypedefRelated(s, t, this);
+      } else if (s is FutureOrType) {
+        return relation.isFutureOrRelated(s, t, this);
+      } else if (s is ExtensionType) {
+        return relation.isExtensionRelated(s, t, this);
       }
     } else if (t is TypedefType) {
       const IsTypedefSubtypeOf relation = const IsTypedefSubtypeOf();
@@ -194,9 +191,9 @@
       } else if (s is FunctionType) {
         return relation.isFunctionRelated(s, t, this);
       } else if (s is TypeParameterType) {
-        return s.promotedBound == null
-            ? relation.isTypeParameterRelated(s, t, this)
-            : relation.isIntersectionRelated(s, t, this);
+        return relation.isTypeParameterRelated(s, t, this);
+      } else if (s is IntersectionType) {
+        return relation.isIntersectionRelated(s, t, this);
       } else if (s is TypedefType) {
         return relation.isTypedefRelated(s, t, this);
       } else if (s is FutureOrType) {
@@ -215,9 +212,9 @@
       } else if (s is FunctionType) {
         return relation.isFunctionRelated(s, t, this);
       } else if (s is TypeParameterType) {
-        return s.promotedBound == null
-            ? relation.isTypeParameterRelated(s, t, this)
-            : relation.isIntersectionRelated(s, t, this);
+        return relation.isTypeParameterRelated(s, t, this);
+      } else if (s is IntersectionType) {
+        return relation.isIntersectionRelated(s, t, this);
       } else if (s is TypedefType) {
         return relation.isTypedefRelated(s, t, this);
       } else if (s is FutureOrType) {
@@ -236,9 +233,9 @@
       } else if (s is FunctionType) {
         return relation.isFunctionRelated(s, t, this);
       } else if (s is TypeParameterType) {
-        return s.promotedBound == null
-            ? relation.isTypeParameterRelated(s, t, this)
-            : relation.isIntersectionRelated(s, t, this);
+        return relation.isTypeParameterRelated(s, t, this);
+      } else if (s is IntersectionType) {
+        return relation.isIntersectionRelated(s, t, this);
       } else if (s is TypedefType) {
         return relation.isTypedefRelated(s, t, this);
       } else if (s is FutureOrType) {
@@ -257,9 +254,9 @@
       } else if (s is FunctionType) {
         return relation.isFunctionRelated(s, t, this);
       } else if (s is TypeParameterType) {
-        return s.promotedBound == null
-            ? relation.isTypeParameterRelated(s, t, this)
-            : relation.isIntersectionRelated(s, t, this);
+        return relation.isTypeParameterRelated(s, t, this);
+      } else if (s is IntersectionType) {
+        return relation.isIntersectionRelated(s, t, this);
       } else if (s is TypedefType) {
         return relation.isTypedefRelated(s, t, this);
       } else if (s is FutureOrType) {
@@ -279,9 +276,10 @@
       } else if (s is FunctionType) {
         return relation.isFunctionRelated(s, t, this);
       } else if (s is TypeParameterType) {
-        return s.promotedBound == null
-            ? relation.isTypeParameterRelated(s, t, this)
-            : relation.isIntersectionRelated(s, t, this);
+        return relation.isTypeParameterRelated(s, t, this);
+      } else if (s is IntersectionType) {
+        return relation.isIntersectionRelated(s, t, this);
+      } else if (s is IntersectionType) {
       } else if (s is TypedefType) {
         return relation.isTypedefRelated(s, t, this);
       } else if (s is FutureOrType) {
@@ -362,7 +360,7 @@
   IsSubtypeOf isInterfaceRelated(InterfaceType s, T t, Types types);
 
   IsSubtypeOf isIntersectionRelated(
-      TypeParameterType intersection, T t, Types types);
+      IntersectionType intersection, T t, Types types);
 
   IsSubtypeOf isFunctionRelated(FunctionType s, T t, Types types);
 
@@ -414,9 +412,9 @@
 
   @override
   IsSubtypeOf isIntersectionRelated(
-      TypeParameterType intersection, InterfaceType t, Types types) {
+      IntersectionType intersection, InterfaceType t, Types types) {
     return types.performNullabilityAwareSubtypeCheck(
-        intersection.promotedBound!, t); // Rule 12.
+        intersection.right, t); // Rule 12.
   }
 
   @override
@@ -599,10 +597,9 @@
 
   @override
   IsSubtypeOf isIntersectionRelated(
-      TypeParameterType intersection, FunctionType t, Types types) {
+      IntersectionType intersection, FunctionType t, Types types) {
     // Rule 12.
-    return types.performNullabilityAwareSubtypeCheck(
-        intersection.promotedBound!, t);
+    return types.performNullabilityAwareSubtypeCheck(intersection.right, t);
   }
 
   @override
@@ -652,14 +649,14 @@
 
   @override
   IsSubtypeOf isIntersectionRelated(
-      TypeParameterType intersection, TypeParameterType t, Types types) {
+      IntersectionType intersection, TypeParameterType t, Types types) {
     // Nullable types aren't promoted to intersection types.
     // TODO(cstefantsova): Uncomment the following when the inference is
     // updated.
     //assert(intersection.typeParameterTypeNullability != Nullability.nullable);
 
     // Rule 8.
-    if (intersection.parameter == t.parameter) {
+    if (intersection.left.parameter == t.parameter) {
       if (intersection.nullability == Nullability.undetermined &&
           t.nullability == Nullability.undetermined) {
         // The two nullabilities are undetermined, but are connected via
@@ -671,8 +668,7 @@
 
     // Rule 12.
     return types.performNullabilityAwareSubtypeCheck(
-        intersection.promotedBound!
-            .withDeclaredNullability(intersection.nullability),
+        intersection.right.withDeclaredNullability(intersection.nullability),
         t);
   }
 
@@ -743,7 +739,7 @@
 
   @override
   IsSubtypeOf isIntersectionRelated(
-      TypeParameterType intersection, TypedefType t, Types types) {
+      IntersectionType intersection, TypedefType t, Types types) {
     return types.performNullabilityAwareSubtypeCheck(intersection, t.unalias);
   }
 
@@ -885,9 +881,9 @@
 
   @override
   IsSubtypeOf isIntersectionRelated(
-      TypeParameterType intersection, FutureOrType t, Types types) {
-    return isTypeParameterRelated(intersection, t, types) // Rule 8.
-        .orSubtypeCheckFor(intersection.promotedBound!, t, types); // Rule 12.
+      IntersectionType intersection, FutureOrType t, Types types) {
+    return isTypeParameterRelated(intersection.left, t, types) // Rule 8.
+        .orSubtypeCheckFor(intersection.right, t, types); // Rule 12.
   }
 
   @override
@@ -903,67 +899,67 @@
   }
 }
 
-class IsIntersectionSubtypeOf extends TypeRelation<TypeParameterType> {
+class IsIntersectionSubtypeOf extends TypeRelation<IntersectionType> {
   const IsIntersectionSubtypeOf();
 
   @override
-  IsSubtypeOf isIntersectionRelated(TypeParameterType sIntersection,
-      TypeParameterType tIntersection, Types types) {
+  IsSubtypeOf isIntersectionRelated(IntersectionType sIntersection,
+      IntersectionType tIntersection, Types types) {
     // Rule 9.
     return const IsTypeParameterSubtypeOf()
-        .isIntersectionRelated(sIntersection, tIntersection, types)
-        .andSubtypeCheckFor(sIntersection, tIntersection.promotedBound!, types);
+        .isIntersectionRelated(sIntersection, tIntersection.left, types)
+        .andSubtypeCheckFor(sIntersection, tIntersection.right, types);
   }
 
   @override
   IsSubtypeOf isTypeParameterRelated(
-      TypeParameterType s, TypeParameterType intersection, Types types) {
+      TypeParameterType s, IntersectionType intersection, Types types) {
     // Rule 9.
     return const IsTypeParameterSubtypeOf()
-        .isTypeParameterRelated(s, intersection, types)
-        .andSubtypeCheckFor(s, intersection.promotedBound!, types);
+        .isTypeParameterRelated(s, intersection.left, types)
+        .andSubtypeCheckFor(s, intersection.right, types);
   }
 
   @override
   IsSubtypeOf isInterfaceRelated(
-      InterfaceType s, TypeParameterType intersection, Types types) {
+      InterfaceType s, IntersectionType intersection, Types types) {
     return const IsSubtypeOf.never();
   }
 
   @override
   IsSubtypeOf isDynamicRelated(
-      DynamicType s, TypeParameterType intersection, Types types) {
+      DynamicType s, IntersectionType intersection, Types types) {
     return const IsSubtypeOf.never();
   }
 
   @override
   IsSubtypeOf isFunctionRelated(
-      FunctionType s, TypeParameterType intersection, Types types) {
+      FunctionType s, IntersectionType intersection, Types types) {
     return const IsSubtypeOf.never();
   }
 
   @override
   IsSubtypeOf isFutureOrRelated(
-      FutureOrType s, TypeParameterType intersection, Types types) {
+      FutureOrType s, IntersectionType intersection, Types types) {
     return const IsSubtypeOf.never();
   }
 
   @override
   IsSubtypeOf isTypedefRelated(
-      TypedefType s, TypeParameterType intersection, Types types) {
+      TypedefType s, IntersectionType intersection, Types types) {
     // Rule 5.
     return types.performNullabilityAwareSubtypeCheck(s.unalias, intersection);
   }
 
   @override
   IsSubtypeOf isVoidRelated(
-      VoidType s, TypeParameterType intersection, Types types) {
+      VoidType s, IntersectionType intersection, Types types) {
     return const IsSubtypeOf.never();
   }
 
   @override
   IsSubtypeOf isExtensionRelated(
-      ExtensionType s, TypeParameterType t, Types types) {
+      ExtensionType s, IntersectionType t, Types types) {
     return const IsSubtypeOf.never();
   }
 }
@@ -988,9 +984,9 @@
 
   @override
   IsSubtypeOf isIntersectionRelated(
-      TypeParameterType intersection, NullType t, Types types) {
+      IntersectionType intersection, NullType t, Types types) {
     return types.performNullabilityAwareMutualSubtypesCheck(
-        intersection.promotedBound!, t);
+        intersection.right, t);
   }
 
   @override
@@ -1043,9 +1039,8 @@
 
   @override
   IsSubtypeOf isIntersectionRelated(
-      TypeParameterType intersection, NeverType t, Types types) {
-    return types.performNullabilityAwareSubtypeCheck(
-        intersection.promotedBound!, t);
+      IntersectionType intersection, NeverType t, Types types) {
+    return types.performNullabilityAwareSubtypeCheck(intersection.right, t);
   }
 
   @override
@@ -1098,7 +1093,7 @@
 
   @override
   IsSubtypeOf isIntersectionRelated(
-      TypeParameterType intersection, ExtensionType t, Types types) {
+      IntersectionType intersection, ExtensionType t, Types types) {
     return types.performNullabilityAwareSubtypeCheck(intersection, t.onType);
   }
 
diff --git a/pkg/kernel/lib/testing/type_parser_environment.dart b/pkg/kernel/lib/testing/type_parser_environment.dart
index c550c02..9e33bfe 100644
--- a/pkg/kernel/lib/testing/type_parser_environment.dart
+++ b/pkg/kernel/lib/testing/type_parser_environment.dart
@@ -464,13 +464,12 @@
   }
 
   @override
-  TypeParameterType visitIntersectionType(
+  IntersectionType visitIntersectionType(
       ParsedIntersectionType node, TypeParserEnvironment environment) {
     TypeParameterType type =
         _parseType(node.a, environment) as TypeParameterType;
     DartType bound = _parseType(node.b, environment);
-    return new TypeParameterType.intersection(
-        type.parameter, type.nullability, bound);
+    return new IntersectionType(type, bound);
   }
 
   Supertype toSupertype(InterfaceType type) {
diff --git a/pkg/kernel/lib/text/ast_to_text.dart b/pkg/kernel/lib/text/ast_to_text.dart
index 9b97a47..0e523dd 100644
--- a/pkg/kernel/lib/text/ast_to_text.dart
+++ b/pkg/kernel/lib/text/ast_to_text.dart
@@ -2661,19 +2661,21 @@
   void visitTypeParameterType(TypeParameterType node) {
     writeTypeParameterReference(node.parameter);
     writeNullability(node.declaredNullability);
-    DartType? promotedBound = node.promotedBound;
-    if (promotedBound != null) {
-      writeSpaced('&');
-      writeType(promotedBound);
+  }
 
-      writeWord("/* '");
-      writeNullability(node.declaredNullability, inComment: true);
-      writeWord("' & '");
-      writeDartTypeNullability(promotedBound, inComment: true);
-      writeWord("' = '");
-      writeNullability(node.nullability, inComment: true);
-      writeWord("' */");
-    }
+  @override
+  void visitIntersectionType(IntersectionType node) {
+    writeType(node.left);
+    writeSpaced('&');
+    writeType(node.right);
+    writeWord("/* '");
+
+    writeDartTypeNullability(node.left, inComment: true);
+    writeWord("' & '");
+    writeDartTypeNullability(node.right, inComment: true);
+    writeWord("' = '");
+    writeNullability(node.nullability, inComment: true);
+    writeWord("' */");
   }
 
   @override
diff --git a/pkg/kernel/lib/type_algebra.dart b/pkg/kernel/lib/type_algebra.dart
index aa91ae6..35f89770 100644
--- a/pkg/kernel/lib/type_algebra.dart
+++ b/pkg/kernel/lib/type_algebra.dart
@@ -375,6 +375,12 @@
       freeTypeVariables.add(node.parameter);
     }
   }
+
+  @override
+  void visitIntersectionType(IntersectionType node) {
+    node.left.accept(this);
+    node.right.accept(this);
+  }
 }
 
 class _NullSubstitution extends Substitution {
@@ -806,6 +812,11 @@
     }
     return node;
   }
+
+  @override
+  DartType visitIntersectionType(IntersectionType node) {
+    return node.left.accept(this);
+  }
 }
 
 class _DeepTypeSubstitutor extends _InnerTypeSubstitutor {
@@ -916,6 +927,11 @@
     return variables.contains(node.parameter);
   }
 
+  @override
+  bool visitIntersectionType(IntersectionType node) {
+    return visit(node.left) || visit(node.right);
+  }
+
   bool handleTypeParameter(TypeParameter node) {
     assert(!variables.contains(node));
     if (node.bound.accept(this)) return true;
@@ -988,6 +1004,11 @@
     return node.parameter.parent == null && !variables.contains(node.parameter);
   }
 
+  @override
+  bool visitIntersectionType(IntersectionType node) {
+    return visit(node.left) || visit(node.right);
+  }
+
   bool handleTypeParameter(TypeParameter node) {
     assert(variables.contains(node));
     if (node.bound.accept(this)) return true;
@@ -1061,6 +1082,11 @@
     return !boundVariables.contains(node.parameter);
   }
 
+  @override
+  bool visitIntersectionType(IntersectionType node) {
+    return visit(node.left) && visit(node.right);
+  }
+
   bool handleTypeParameter(TypeParameter node) {
     assert(boundVariables.contains(node));
     if (node.bound.accept(this)) return true;
@@ -1156,9 +1182,10 @@
   bool visitNullType(NullType node) => true;
 
   @override
-  bool visitTypeParameterType(TypeParameterType node) {
-    return node.promotedBound == null;
-  }
+  bool visitTypeParameterType(TypeParameterType node) => true;
+
+  @override
+  bool visitIntersectionType(IntersectionType node) => false;
 
   @override
   bool visitTypedefType(TypedefType node) {
@@ -1230,13 +1257,14 @@
 
   @override
   DartType visitTypeParameterType(TypeParameterType node, CoreTypes coreTypes) {
-    if (node.promotedBound != null) {
-      // Intersection types don't have their own nullabilities.
-      return node;
-    } else {
-      return node.withDeclaredNullability(
-          TypeParameterType.computeNullabilityFromBound(node.parameter));
-    }
+    return node.withDeclaredNullability(
+        TypeParameterType.computeNullabilityFromBound(node.parameter));
+  }
+
+  @override
+  DartType visitIntersectionType(IntersectionType node, CoreTypes coreTypes) {
+    // Intersection types don't have their own nullabilities.
+    return node;
   }
 
   @override
@@ -1441,18 +1469,16 @@
   assert(isNonNullableByDefault != null);
 
   if (type is TypeParameterType) {
-    if (type.promotedBound == null) {
-      // The default nullability for library is used when there are no
-      // nullability markers on the type.
-      return new TypeParameterType(
-          type.parameter,
-          _defaultNullabilityForTypeParameterType(type.parameter,
-              isNonNullableByDefault: isNonNullableByDefault));
-    } else {
-      // Intersection types can't be arguments to the nullable and the legacy
-      // type constructors, so nothing can be peeled off.
-      return type;
-    }
+    // The default nullability for library is used when there are no
+    // nullability markers on the type.
+    return new TypeParameterType(
+        type.parameter,
+        _defaultNullabilityForTypeParameterType(type.parameter,
+            isNonNullableByDefault: isNonNullableByDefault));
+  } else if (type is IntersectionType) {
+    // Intersection types can't be arguments to the nullable and the legacy
+    // type constructors, so nothing can be peeled off.
+    return type;
   } else if (type is NullType) {
     return type;
   } else {
@@ -1475,10 +1501,9 @@
 
   // The default nullability for library is used when there are no nullability
   // markers on the type.
-  return type.promotedBound == null &&
-      type.declaredNullability ==
-          _defaultNullabilityForTypeParameterType(type.parameter,
-              isNonNullableByDefault: isNonNullableByDefault);
+  return type.declaredNullability ==
+      _defaultNullabilityForTypeParameterType(type.parameter,
+          isNonNullableByDefault: isNonNullableByDefault);
 }
 
 bool isTypeWithoutNullabilityMarker(DartType type,
@@ -1552,6 +1577,9 @@
   }
 
   @override
+  bool visitIntersectionType(IntersectionType node) => false;
+
+  @override
   bool visitTypedefType(TypedefType node) {
     assert(node.declaredNullability != Nullability.undetermined);
     return node.declaredNullability == Nullability.nullable ||
@@ -1570,7 +1598,7 @@
 /// and Null are nullable, but aren't considered applications of the nullable
 /// type constructor.
 bool isNullableTypeConstructorApplication(DartType type) {
-  if (type is TypeParameterType && type.promotedBound != null) {
+  if (type is IntersectionType) {
     // Promoted types are never considered applications of ?.
     return false;
   }
diff --git a/pkg/kernel/lib/type_checker.dart b/pkg/kernel/lib/type_checker.dart
index 6f2a341..6116e0e 100644
--- a/pkg/kernel/lib/type_checker.dart
+++ b/pkg/kernel/lib/type_checker.dart
@@ -259,8 +259,16 @@
     if (superclass.supertype == null) {
       return Substitution.empty; // Members on Object are always accessible.
     }
-    while (type is TypeParameterType) {
-      type = type.bound;
+    // TODO(cstefantsova): Implement the procedure for resolving type parameter
+    // types and intersection types.
+    while (true) {
+      if (type is TypeParameterType) {
+        type = type.bound;
+      } else if (type is IntersectionType) {
+        type = type.right;
+      } else {
+        break;
+      }
     }
     if (type is NeverType || type is NullType || type is InvalidType) {
       // The bottom type is a subtype of all types, so it should be allowed.
diff --git a/pkg/kernel/lib/type_environment.dart b/pkg/kernel/lib/type_environment.dart
index a67f7ea..aa06199 100644
--- a/pkg/kernel/lib/type_environment.dart
+++ b/pkg/kernel/lib/type_environment.dart
@@ -115,9 +115,14 @@
 
   /// Returns the non-type parameter type bound of [type].
   DartType _resolveTypeParameterType(DartType type) {
-    while (type is TypeParameterType) {
-      TypeParameterType typeParameterType = type;
-      type = typeParameterType.bound;
+    while (true) {
+      if (type is TypeParameterType) {
+        type = type.bound;
+      } else if (type is IntersectionType) {
+        type = type.right;
+      } else {
+        break;
+      }
     }
     return type;
   }
diff --git a/pkg/kernel/lib/visitor.dart b/pkg/kernel/lib/visitor.dart
index 64f3acc..5ab39a6 100644
--- a/pkg/kernel/lib/visitor.dart
+++ b/pkg/kernel/lib/visitor.dart
@@ -748,6 +748,7 @@
   R visitNeverType(NeverType node) => defaultDartType(node);
   R visitNullType(NullType node) => defaultDartType(node);
   R visitExtensionType(ExtensionType node) => defaultDartType(node);
+  R visitIntersectionType(IntersectionType node) => defaultDartType(node);
 }
 
 abstract class DartTypeVisitor1<R, T> {
@@ -767,6 +768,8 @@
   R visitNeverType(NeverType node, T arg) => defaultDartType(node, arg);
   R visitNullType(NullType node, T arg) => defaultDartType(node, arg);
   R visitExtensionType(ExtensionType node, T arg) => defaultDartType(node, arg);
+  R visitIntersectionType(IntersectionType node, T arg) =>
+      defaultDartType(node, arg);
 }
 
 /// Visitor for [Constant] nodes.
@@ -1143,6 +1146,8 @@
   R visitNullType(NullType node) => defaultDartType(node);
   @override
   R visitExtensionType(ExtensionType node) => defaultDartType(node);
+  @override
+  R visitIntersectionType(IntersectionType node) => defaultDartType(node);
 
   // Constants
   @override
diff --git a/pkg/kernel/test/non_null_test.dart b/pkg/kernel/test/non_null_test.dart
index 7ec2800..67d2b6f 100644
--- a/pkg/kernel/test/non_null_test.dart
+++ b/pkg/kernel/test/non_null_test.dart
@@ -66,7 +66,7 @@
   'X & Object': 'X & Object',
   'X? & Object?': 'X & Object',
   'X? & dynamic': 'X & dynamic',
-  'X? & Object': 'X & Object',
+  'X? & Object': 'X? & Object',
   'Y': 'Y & X & Object',
   'Y?': 'Y & X & Object',
   'Y_extends_dynamic': 'Y_extends_dynamic',
diff --git a/pkg/kernel/test/type_parser.dart b/pkg/kernel/test/type_parser.dart
index 52401b9..a9a998d 100644
--- a/pkg/kernel/test/type_parser.dart
+++ b/pkg/kernel/test/type_parser.dart
@@ -184,11 +184,13 @@
             default:
               break;
           }
-          return new TypeParameterType(
+          TypeParameterType typeParameterType = new TypeParameterType(
               target,
               nullability ??
-                  TypeParameterType.computeNullabilityFromBound(target),
-              promotedBound);
+                  TypeParameterType.computeNullabilityFromBound(target));
+          return promotedBound == null
+              ? typeParameterType
+              : new IntersectionType(typeParameterType, promotedBound);
         }
         return fail("Unexpected lookup result for $name: $target");
 
diff --git a/pkg/vm/lib/transformations/type_flow/types.dart b/pkg/vm/lib/transformations/type_flow/types.dart
index 6db75a7..ecffc6b 100644
--- a/pkg/vm/lib/transformations/type_flow/types.dart
+++ b/pkg/vm/lib/transformations/type_flow/types.dart
@@ -99,6 +99,13 @@
       } else {
         result = fromStaticType(bound, canBeNull);
       }
+    } else if (type is IntersectionType) {
+      final bound = type.right;
+      if (bound is TypeParameterType) {
+        result = const AnyType();
+      } else {
+        result = fromStaticType(bound, canBeNull);
+      }
     } else {
       throw 'Unexpected type ${type.runtimeType} $type';
     }
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/regress_47878.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/regress_47878.dart.expect
index d52c496..e967752 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/regress_47878.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/regress_47878.dart.expect
@@ -14,7 +14,7 @@
     self::DataStream::T? lastValue = block {
       #C1!;
     } =>throw "Attempt to execute code removed by Dart AOT compiler (TFA)";
-    if(!(throw "Attempt to execute code removed by Dart AOT compiler (TFA)") && false && !([@vm.inferred-type.metadata=dart.core::bool (skip check) (receiver not int)] lastValue{self::DataStream::T & self::Disposable /* '!' & '!' = '!' */} =={core::Object::==}{(core::Object) → core::bool} #C1)) {
+    if(!(throw "Attempt to execute code removed by Dart AOT compiler (TFA)") && false && !([@vm.inferred-type.metadata=dart.core::bool (skip check) (receiver not int)] lastValue{self::DataStream::T% & self::Disposable /* '%' & '!' = '!' */} =={core::Object::==}{(core::Object) → core::bool} #C1)) {
     }
   }
 }
diff --git a/runtime/vm/compiler/frontend/kernel_fingerprints.cc b/runtime/vm/compiler/frontend/kernel_fingerprints.cc
index 6be0574..3896a91 100644
--- a/runtime/vm/compiler/frontend/kernel_fingerprints.cc
+++ b/runtime/vm/compiler/frontend/kernel_fingerprints.cc
@@ -251,9 +251,12 @@
       Nullability nullability = ReadNullability();
       BuildHash(static_cast<uint32_t>(nullability));
       ReadUInt();                              // read index for parameter.
-      CalculateOptionalDartTypeFingerprint();  // read bound bound.
       break;
     }
+    case kIntersectionType:
+      CalculateDartTypeFingerprint();  // read left;
+      CalculateDartTypeFingerprint();  // read right;
+      break;
     default:
       ReportUnexpectedTag("type", tag);
       UNREACHABLE();
diff --git a/runtime/vm/compiler/frontend/kernel_translation_helper.cc b/runtime/vm/compiler/frontend/kernel_translation_helper.cc
index b84479f..ced4d94 100644
--- a/runtime/vm/compiler/frontend/kernel_translation_helper.cc
+++ b/runtime/vm/compiler/frontend/kernel_translation_helper.cc
@@ -2221,7 +2221,10 @@
     case kTypeParameterType:
       ReadNullability();       // read nullability.
       ReadUInt();              // read index for parameter.
-      SkipOptionalDartType();  // read bound bound.
+      return;
+    case kIntersectionType:
+      SkipDartType();  // read left.
+      SkipDartType();  // read right.
       return;
     default:
       ReportUnexpectedTag("type", tag);
@@ -3152,6 +3155,9 @@
         refers_to_derived_type_param_ = true;
       }
       break;
+    case kIntersectionType:
+      BuildIntersectionType();
+      break;
     default:
       helper_->ReportUnexpectedTag("type", tag);
       UNREACHABLE();
@@ -3300,7 +3306,6 @@
   }
 
   intptr_t parameter_index = helper_->ReadUInt();  // read parameter index.
-  helper_->SkipOptionalDartType();                 // read bound.
 
   // If the type is from a constant, the parameter index isn't offset by the
   // enclosing context.
@@ -3398,6 +3403,11 @@
       active_class_->ToCString());
 }
 
+void TypeTranslator::BuildIntersectionType() {
+  BuildTypeInternal();      // read left.
+  helper_->SkipDartType();  // read right.
+}
+
 const TypeArguments& TypeTranslator::BuildTypeArguments(intptr_t length) {
   bool only_dynamic = true;
   intptr_t offset = helper_->ReaderOffset();
diff --git a/runtime/vm/compiler/frontend/kernel_translation_helper.h b/runtime/vm/compiler/frontend/kernel_translation_helper.h
index 314cab9..badf719 100644
--- a/runtime/vm/compiler/frontend/kernel_translation_helper.h
+++ b/runtime/vm/compiler/frontend/kernel_translation_helper.h
@@ -1540,6 +1540,7 @@
   void BuildInterfaceType(bool simple);
   void BuildFunctionType(bool simple);
   void BuildTypeParameterType();
+  void BuildIntersectionType();
 
   class TypeParameterScope {
    public:
diff --git a/runtime/vm/compiler/frontend/scope_builder.cc b/runtime/vm/compiler/frontend/scope_builder.cc
index b6e849e..037d9ae 100644
--- a/runtime/vm/compiler/frontend/scope_builder.cc
+++ b/runtime/vm/compiler/frontend/scope_builder.cc
@@ -1358,6 +1358,9 @@
     case kTypeParameterType:
       VisitTypeParameterType();
       return;
+    case kIntersectionType:
+      VisitIntersectionType();
+      return;
     default:
       ReportUnexpectedTag("type", tag);
       UNREACHABLE();
@@ -1447,8 +1450,11 @@
       }
     }
   }
+}
 
-  helper_.SkipOptionalDartType();  // read bound bound.
+void ScopeBuilder::VisitIntersectionType() {
+  VisitDartType();         // read left.
+  helper_.SkipDartType();  // read right.
 }
 
 void ScopeBuilder::HandleLocalFunction(intptr_t parent_kernel_offset) {
diff --git a/runtime/vm/compiler/frontend/scope_builder.h b/runtime/vm/compiler/frontend/scope_builder.h
index ef1a985..619a686 100644
--- a/runtime/vm/compiler/frontend/scope_builder.h
+++ b/runtime/vm/compiler/frontend/scope_builder.h
@@ -47,6 +47,7 @@
   void VisitInterfaceType(bool simple);
   void VisitFunctionType(bool simple);
   void VisitTypeParameterType();
+  void VisitIntersectionType();
   void HandleLocalFunction(intptr_t parent_kernel_offset);
 
   AbstractType& BuildAndVisitVariableType();
diff --git a/runtime/vm/compiler/recognized_methods_list.h b/runtime/vm/compiler/recognized_methods_list.h
index 19ed9a6..6f83801 100644
--- a/runtime/vm/compiler/recognized_methods_list.h
+++ b/runtime/vm/compiler/recognized_methods_list.h
@@ -15,11 +15,11 @@
   V(::, identical, ObjectIdentical, 0x04168315)                                \
   V(ClassID, getID, ClassIDgetID, 0xdc8b888a)                                  \
   V(Object, Object., ObjectConstructor, 0xab6d6cfa)                            \
-  V(List, ., ListFactory, 0xbc820cf9)                                          \
-  V(_List, ., ObjectArrayAllocate, 0xd693eee6)                                 \
-  V(_List, []=, ObjectArraySetIndexed, 0xd7b48abc)                             \
-  V(_GrowableList, ._withData, GrowableArrayAllocateWithData, 0xa32d060b)      \
-  V(_GrowableList, []=, GrowableArraySetIndexed, 0xd7b48abc)                   \
+  V(List, ., ListFactory, 0x1892cc51)                                          \
+  V(_List, ., ObjectArrayAllocate, 0x4c9d39e2)                                 \
+  V(_List, []=, ObjectArraySetIndexed, 0x050cd2ba)                             \
+  V(_GrowableList, ._withData, GrowableArrayAllocateWithData, 0x1947d8a1)      \
+  V(_GrowableList, []=, GrowableArraySetIndexed, 0x050cd2ba)                   \
   V(_TypedList, _getInt8, ByteArrayBaseGetInt8, 0x1623dc34)                    \
   V(_TypedList, _getUint8, ByteArrayBaseGetUint8, 0x177ffe2a)                  \
   V(_TypedList, _getInt16, ByteArrayBaseGetInt16, 0x2e40964f)                  \
@@ -144,8 +144,8 @@
   V(_Double, roundToDouble, DoubleRoundToDouble, 0x5649ca00)                   \
   V(_Double, toInt, DoubleToInteger, 0x676f20a9)                               \
   V(_Double, truncateToDouble, DoubleTruncateToDouble, 0x62d48659)             \
-  V(::, min, MathMin, 0xc2021a5b)                                              \
-  V(::, max, MathMax, 0xe45b2596)                                              \
+  V(::, min, MathMin, 0xd0ef27f3)                                              \
+  V(::, max, MathMax, 0xbbfa2f8c)                                              \
   V(::, _doublePow, MathDoublePow, 0x989f3334)                                 \
   V(::, _intPow, MathIntPow, 0xb9afc09a)                                       \
   V(::, _sin, MathSin, 0x17daca03)                                             \
@@ -169,8 +169,8 @@
     0x70f53b2b)                                                                \
   V(FinalizerBase, set:_isolateFinalizers, FinalizerBase_setIsolateFinalizers, \
     0xb3e66928)                                                                \
-  V(_FinalizerImpl, get:_callback, Finalizer_getCallback, 0x6f3d56bc)          \
-  V(_FinalizerImpl, set:_callback, Finalizer_setCallback, 0xc6aa96f9)          \
+  V(_FinalizerImpl, get:_callback, Finalizer_getCallback, 0x185ebcf8)          \
+  V(_FinalizerImpl, set:_callback, Finalizer_setCallback, 0xad0b5e35)          \
   V(_NativeFinalizer, get:_callback, NativeFinalizer_getCallback, 0x5cb374f5)  \
   V(_NativeFinalizer, set:_callback, NativeFinalizer_setCallback, 0xb12268f2)  \
   V(FinalizerEntry, allocate, FinalizerEntry_allocate, 0xe0bad878)             \
@@ -259,11 +259,11 @@
   V(_WeakProperty, set:key, WeakProperty_setKey, 0x963a095f)                   \
   V(_WeakProperty, get:value, WeakProperty_getValue, 0xd2f28aae)               \
   V(_WeakProperty, set:value, WeakProperty_setValue, 0x8b2bafab)               \
-  V(_WeakReferenceImpl, get:target, WeakReference_getTarget, 0x632d6ca8)       \
-  V(_WeakReferenceImpl, set:_target, WeakReference_setTarget, 0x6edc7518)      \
+  V(_WeakReferenceImpl, get:target, WeakReference_getTarget, 0xc990118a)       \
+  V(_WeakReferenceImpl, set:_target, WeakReference_setTarget, 0xc729697a)      \
   V(::, _classRangeCheck, ClassRangeCheck, 0x09f5fc7a)                         \
   V(::, _abi, FfiAbi, 0x7c4ab3b4)                                              \
-  V(::, _asFunctionInternal, FfiAsFunctionInternal, 0x92ae104f)                \
+  V(::, _asFunctionInternal, FfiAsFunctionInternal, 0x631b1071)                \
   V(::, _nativeCallbackFunction, FfiNativeCallbackFunction, 0x3ff5ae9c)        \
   V(::, _nativeEffect, NativeEffect, 0x537dce91)                               \
   V(::, _loadAbiSpecificInt, FfiLoadAbiSpecificInt, 0x7807e872)                \
@@ -280,7 +280,7 @@
   V(::, _loadFloatUnaligned, FfiLoadFloatUnaligned, 0xc8c8dfff)                \
   V(::, _loadDouble, FfiLoadDouble, 0xf70cc619)                                \
   V(::, _loadDoubleUnaligned, FfiLoadDoubleUnaligned, 0xc99ebd39)              \
-  V(::, _loadPointer, FfiLoadPointer, 0x4e79d0fc)                              \
+  V(::, _loadPointer, FfiLoadPointer, 0x9a0810c4)                              \
   V(::, _storeAbiSpecificInt, FfiStoreAbiSpecificInt, 0xc70954c0)              \
   V(::, _storeAbiSpecificIntAtIndex, FfiStoreAbiSpecificIntAtIndex, 0xc64efe4b)\
   V(::, _storeInt8, FfiStoreInt8, 0xdf50b2cd)                                  \
@@ -295,8 +295,8 @@
   V(::, _storeFloatUnaligned, FfiStoreFloatUnaligned, 0x600a9203)              \
   V(::, _storeDouble, FfiStoreDouble, 0x42998c64)                              \
   V(::, _storeDoubleUnaligned, FfiStoreDoubleUnaligned, 0x3dced75b)            \
-  V(::, _storePointer, FfiStorePointer, 0xea6b7751)                            \
-  V(::, _fromAddress, FfiFromAddress, 0xfd8cb1cc)                              \
+  V(::, _storePointer, FfiStorePointer, 0x8b68e519)                            \
+  V(::, _fromAddress, FfiFromAddress, 0x811e2220)                              \
   V(Pointer, get:address, FfiGetAddress, 0x7cde87be)                           \
   V(::, _asExternalTypedDataInt8, FfiAsExternalTypedDataInt8, 0x768a0698)      \
   V(::, _asExternalTypedDataInt16, FfiAsExternalTypedDataInt16, 0xd09cf9c6)    \
@@ -311,10 +311,10 @@
   V(::, _getNativeField, GetNativeField, 0xa0139b85)                           \
   V(::, reachabilityFence, ReachabilityFence, 0x730f2b7f)                      \
   V(_Utf8Decoder, _scan, Utf8DecoderScan, 0xf296c901)                          \
-  V(_Future, timeout, FutureTimeout, 0xa7cb3294)                               \
-  V(Future, wait, FutureWait, 0xb0b596bd)                                      \
-  V(_RootZone, runUnary, RootZoneRunUnary, 0xb607f8bf)                         \
-  V(_FutureListener, handleValue, FutureListenerHandleValue, 0x438115a8)       \
+  V(_Future, timeout, FutureTimeout, 0xbc736ef8)                               \
+  V(Future, wait, FutureWait, 0x764434e5)                                      \
+  V(_RootZone, runUnary, RootZoneRunUnary, 0x7168b20b)                         \
+  V(_FutureListener, handleValue, FutureListenerHandleValue, 0x25b39832)       \
   V(::, has63BitSmis, Has63BitSmis, 0xf61b56f1)                                \
   V(::, get:extensionStreamHasListener, ExtensionStreamHasListener, 0xfab46343)\
 
@@ -430,14 +430,14 @@
 
 #define GRAPH_CORE_INTRINSICS_LIST(V)                                          \
   V(_Array, get:length, ObjectArrayLength, 0x5850f06b)                         \
-  V(_Array, [], ObjectArrayGetIndexed, 0x57b029cf)                             \
-  V(_List, _setIndexed, ObjectArraySetIndexedUnchecked, 0x02f293ae)            \
+  V(_Array, [], ObjectArrayGetIndexed, 0x78f4f491)                             \
+  V(_List, _setIndexed, ObjectArraySetIndexedUnchecked, 0xe62fb5f0)            \
   V(_GrowableList, get:length, GrowableArrayLength, 0x5850f06b)                \
   V(_GrowableList, get:_capacity, GrowableArrayCapacity, 0x7d9f9bf2)           \
   V(_GrowableList, _setData, GrowableArraySetData, 0xbdda401b)                 \
   V(_GrowableList, _setLength, GrowableArraySetLength, 0xcc1bf9b6)             \
-  V(_GrowableList, [], GrowableArrayGetIndexed, 0x57b029cf)                    \
-  V(_GrowableList, _setIndexed, GrowableArraySetIndexedUnchecked, 0xfb40ee4f)  \
+  V(_GrowableList, [], GrowableArrayGetIndexed, 0x78f4f491)                    \
+  V(_GrowableList, _setIndexed, GrowableArraySetIndexedUnchecked, 0x514b032f)  \
   V(_StringBase, get:length, StringBaseLength, 0x5850f06b)                     \
   V(_OneByteString, codeUnitAt, OneByteStringCodeUnitAt, 0x17f90910)           \
   V(_TwoByteString, codeUnitAt, TwoByteStringCodeUnitAt, 0x17f90910)           \
@@ -522,17 +522,17 @@
 // (factory-name-symbol, class-name-string, constructor-name-string,
 //  result-cid, fingerprint).
 #define RECOGNIZED_LIST_FACTORY_LIST(V)                                        \
-  V(_ListFactory, _List, ., kArrayCid, 0xd693eee6)                             \
-  V(_ListFilledFactory, _List, .filled, kArrayCid, 0x7ffc3415)                 \
-  V(_ListGenerateFactory, _List, .generate, kArrayCid, 0xc85f10b8)             \
+  V(_ListFactory, _List, ., kArrayCid, 0x4c9d39e2)                             \
+  V(_ListFilledFactory, _List, .filled, kArrayCid, 0xe23ae9b1)                 \
+  V(_ListGenerateFactory, _List, .generate, kArrayCid, 0xa7c3f2ee)             \
   V(_GrowableListFactory, _GrowableList, ., kGrowableObjectArrayCid,           \
-    0x3bff5c79)                                                                \
+    0xf210216d)                                                                \
   V(_GrowableListFilledFactory, _GrowableList, .filled,                        \
-    kGrowableObjectArrayCid, 0x38a40a6d)                                       \
+    kGrowableObjectArrayCid, 0x3aa70b31)                                       \
   V(_GrowableListGenerateFactory, _GrowableList, .generate,                    \
-    kGrowableObjectArrayCid, 0x85567510)                                       \
+    kGrowableObjectArrayCid, 0xe123f46e)                                       \
   V(_GrowableListWithData, _GrowableList, ._withData, kGrowableObjectArrayCid, \
-    0xa32d060b)                                                                \
+    0x1947d8a1)                                                                \
   V(_Int8ArrayFactory, Int8List, ., kTypedDataInt8ArrayCid, 0x660dd888)        \
   V(_Uint8ArrayFactory, Uint8List, ., kTypedDataUint8ArrayCid, 0xede3f64f)     \
   V(_Uint8ClampedArrayFactory, Uint8ClampedList, .,                            \
diff --git a/runtime/vm/kernel_binary.h b/runtime/vm/kernel_binary.h
index b0658b3..6465f9a2 100644
--- a/runtime/vm/kernel_binary.h
+++ b/runtime/vm/kernel_binary.h
@@ -20,8 +20,8 @@
 static const uint32_t kMagicProgramFile = 0x90ABCDEFu;
 
 // Both version numbers are inclusive.
-static const uint32_t kMinSupportedKernelFormatVersion = 83;
-static const uint32_t kMaxSupportedKernelFormatVersion = 83;
+static const uint32_t kMinSupportedKernelFormatVersion = 84;
+static const uint32_t kMaxSupportedKernelFormatVersion = 84;
 
 // Keep in sync with package:kernel/lib/binary/tag.dart
 #define KERNEL_TAG_LIST(V)                                                     \
@@ -122,6 +122,7 @@
   V(AssertBlock, 81)                                                           \
   V(TypedefType, 87)                                                           \
   V(NeverType, 98)                                                             \
+  V(IntersectionType, 99)                                                      \
   V(InvalidType, 90)                                                           \
   V(DynamicType, 91)                                                           \
   V(VoidType, 92)                                                              \