[cfe] Introduce ExtensionType into isSubtypeOf

Change-Id: Iea88e7355d7eee9d82c0c41247407fe1178ee049
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/195068
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Commit-Queue: Dmitry Stefantsov <dmitryas@google.com>
diff --git a/pkg/front_end/lib/src/fasta/type_inference/type_schema_environment.dart b/pkg/front_end/lib/src/fasta/type_inference/type_schema_environment.dart
index bc3f745..bdf54c8 100644
--- a/pkg/front_end/lib/src/fasta/type_inference/type_schema_environment.dart
+++ b/pkg/front_end/lib/src/fasta/type_inference/type_schema_environment.dart
@@ -364,51 +364,12 @@
       DartType subtype, DartType supertype) {
     if (subtype is UnknownType) return const IsSubtypeOf.always();
 
-    // For now, extension types are only related to themselves, top types, and
-    // bottom types.
-    // TODO(dmitryas): Implement subtyping rules for extension types.
-    if (subtype is ExtensionType) {
-      if (coreTypes.isTop(supertype)) {
-        return const IsSubtypeOf.always();
-      } else if (supertype is ExtensionType &&
-          subtype.extension == supertype.extension) {
-        assert(subtype.typeArguments.length == supertype.typeArguments.length);
-        IsSubtypeOf result = const IsSubtypeOf.always();
-        for (int i = 0; i < subtype.typeArguments.length; ++i) {
-          result.and(performNullabilityAwareMutualSubtypesCheck(
-              subtype.typeArguments[i], supertype.typeArguments[i]));
-        }
-        return result;
-      } else {
-        return const IsSubtypeOf.never();
-      }
-    }
-
     DartType unwrappedSupertype = supertype;
     while (unwrappedSupertype is FutureOrType) {
       unwrappedSupertype = (unwrappedSupertype as FutureOrType).typeArgument;
     }
     if (unwrappedSupertype is UnknownType) {
       return const IsSubtypeOf.always();
-    } else if (unwrappedSupertype is ExtensionType) {
-      // For now, extension types are only related to themselves, top types, and
-      // bottom types.
-      // TODO(dmitryas): Implement subtyping rules for extension types.
-      if (coreTypes.isBottom(subtype)) {
-        return const IsSubtypeOf.always();
-      } else if (subtype is ExtensionType &&
-          subtype.extension == unwrappedSupertype.extension) {
-        assert(subtype.typeArguments.length ==
-            unwrappedSupertype.typeArguments.length);
-        IsSubtypeOf result = const IsSubtypeOf.always();
-        for (int i = 0; i < subtype.typeArguments.length; ++i) {
-          result.and(performNullabilityAwareMutualSubtypesCheck(
-              subtype.typeArguments[i], unwrappedSupertype.typeArguments[i]));
-        }
-        return result;
-      } else {
-        return const IsSubtypeOf.never();
-      }
     }
     return super.performNullabilityAwareSubtypeCheck(subtype, supertype);
   }
diff --git a/pkg/kernel/lib/src/types.dart b/pkg/kernel/lib/src/types.dart
index 357af4c..0c4abbc 100644
--- a/pkg/kernel/lib/src/types.dart
+++ b/pkg/kernel/lib/src/types.dart
@@ -2,25 +2,7 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-import '../ast.dart'
-    show
-        Class,
-        DartType,
-        DynamicType,
-        FunctionType,
-        FutureOrType,
-        InterfaceType,
-        InvalidType,
-        Library,
-        NamedType,
-        NeverType,
-        NullType,
-        Nullability,
-        TypeParameter,
-        TypeParameterType,
-        TypedefType,
-        Variance,
-        VoidType;
+import '../ast.dart';
 
 import '../class_hierarchy.dart' show ClassHierarchyBase;
 
@@ -350,6 +332,8 @@
   IsSubtypeOf isTypeParameterRelated(TypeParameterType s, T t, Types types);
 
   IsSubtypeOf isTypedefRelated(TypedefType s, T t, Types types);
+
+  IsSubtypeOf isExtensionRelated(ExtensionType s, T t, Types types);
 }
 
 class IsInterfaceSubtypeOf extends TypeRelation<InterfaceType> {
@@ -421,6 +405,13 @@
   IsSubtypeOf isVoidRelated(VoidType s, InterfaceType t, Types types) {
     return const IsSubtypeOf.never();
   }
+
+  @override
+  IsSubtypeOf isExtensionRelated(
+      ExtensionType s, InterfaceType t, Types types) {
+    // TODO(dmitryas): Use with the actual subtyping rules for extension types.
+    return const IsSubtypeOf.never();
+  }
 }
 
 class IsFunctionSubtypeOf extends TypeRelation<FunctionType> {
@@ -596,6 +587,12 @@
   IsSubtypeOf isVoidRelated(VoidType s, FunctionType t, Types types) {
     return const IsSubtypeOf.never();
   }
+
+  @override
+  IsSubtypeOf isExtensionRelated(ExtensionType s, FunctionType t, Types types) {
+    // TODO(dmitryas): Use with the actual subtyping rules for extension types.
+    return const IsSubtypeOf.never();
+  }
 }
 
 class IsTypeParameterSubtypeOf extends TypeRelation<TypeParameterType> {
@@ -676,6 +673,13 @@
   IsSubtypeOf isVoidRelated(VoidType s, TypeParameterType t, Types types) {
     return const IsSubtypeOf.never();
   }
+
+  @override
+  IsSubtypeOf isExtensionRelated(
+      ExtensionType s, TypeParameterType t, Types types) {
+    // TODO(dmitryas): Use with the actual subtyping rules for extension types.
+    return const IsSubtypeOf.never();
+  }
 }
 
 class IsTypedefSubtypeOf extends TypeRelation<TypedefType> {
@@ -722,6 +726,12 @@
   IsSubtypeOf isVoidRelated(VoidType s, TypedefType t, Types types) {
     return types.performNullabilityAwareSubtypeCheck(s, t.unalias);
   }
+
+  @override
+  IsSubtypeOf isExtensionRelated(ExtensionType s, TypedefType t, Types types) {
+    // TODO(dmitryas): Use with the actual subtyping rules for extension types.
+    return const IsSubtypeOf.never();
+  }
 }
 
 class IsFutureOrSubtypeOf extends TypeRelation<FutureOrType> {
@@ -849,6 +859,12 @@
   IsSubtypeOf isTypedefRelated(TypedefType s, FutureOrType t, Types types) {
     return types.performNullabilityAwareSubtypeCheck(s.unalias, t);
   }
+
+  @override
+  IsSubtypeOf isExtensionRelated(ExtensionType s, FutureOrType t, Types types) {
+    // TODO(dmitryas): Use with the actual subtyping rules for extension types.
+    return const IsSubtypeOf.never();
+  }
 }
 
 class IsIntersectionSubtypeOf extends TypeRelation<TypeParameterType> {
@@ -908,37 +924,51 @@
       VoidType s, TypeParameterType intersection, Types types) {
     return const IsSubtypeOf.never();
   }
+
+  @override
+  IsSubtypeOf isExtensionRelated(
+      ExtensionType s, TypeParameterType t, Types types) {
+    // TODO(dmitryas): Use with the actual subtyping rules for extension types.
+    return const IsSubtypeOf.never();
+  }
 }
 
 class IsNullTypeSubtypeOf implements TypeRelation<NullType> {
   const IsNullTypeSubtypeOf();
 
+  @override
   IsSubtypeOf isDynamicRelated(DynamicType s, NullType t, Types types) {
     return const IsSubtypeOf.never();
   }
 
+  @override
   IsSubtypeOf isVoidRelated(VoidType s, NullType t, Types types) {
     return const IsSubtypeOf.never();
   }
 
+  @override
   IsSubtypeOf isInterfaceRelated(InterfaceType s, NullType t, Types types) {
     return const IsSubtypeOf.never();
   }
 
+  @override
   IsSubtypeOf isIntersectionRelated(
       TypeParameterType intersection, NullType t, Types types) {
     return types.performNullabilityAwareMutualSubtypesCheck(
         intersection.promotedBound!, t);
   }
 
+  @override
   IsSubtypeOf isFunctionRelated(FunctionType s, NullType t, Types types) {
     return const IsSubtypeOf.never();
   }
 
+  @override
   IsSubtypeOf isFutureOrRelated(FutureOrType s, NullType t, Types types) {
     return const IsSubtypeOf.never();
   }
 
+  @override
   IsSubtypeOf isTypeParameterRelated(
       TypeParameterType s, NullType t, Types types) {
     // We don't need to combine the check of the bound against [t] with the
@@ -947,40 +977,54 @@
     return types.performNullabilityAwareSubtypeCheck(s.bound, t);
   }
 
+  @override
   IsSubtypeOf isTypedefRelated(TypedefType s, NullType t, Types types) {
     return types.performNullabilityAwareSubtypeCheck(s.unalias, t);
   }
+
+  @override
+  IsSubtypeOf isExtensionRelated(ExtensionType s, NullType t, Types types) {
+    // TODO(dmitryas): Use with the actual subtyping rules for extension types.
+    return const IsSubtypeOf.never();
+  }
 }
 
 class IsNeverTypeSubtypeOf implements TypeRelation<NeverType> {
   const IsNeverTypeSubtypeOf();
 
+  @override
   IsSubtypeOf isDynamicRelated(DynamicType s, NeverType t, Types types) {
     return const IsSubtypeOf.never();
   }
 
+  @override
   IsSubtypeOf isVoidRelated(VoidType s, NeverType t, Types types) {
     return const IsSubtypeOf.never();
   }
 
+  @override
   IsSubtypeOf isInterfaceRelated(InterfaceType s, NeverType t, Types types) {
     return const IsSubtypeOf.never();
   }
 
+  @override
   IsSubtypeOf isIntersectionRelated(
       TypeParameterType intersection, NeverType t, Types types) {
     return types.performNullabilityAwareSubtypeCheck(
         intersection.promotedBound!, t);
   }
 
+  @override
   IsSubtypeOf isFunctionRelated(FunctionType s, NeverType t, Types types) {
     return const IsSubtypeOf.never();
   }
 
+  @override
   IsSubtypeOf isFutureOrRelated(FutureOrType s, NeverType t, Types types) {
     return const IsSubtypeOf.never();
   }
 
+  @override
   IsSubtypeOf isTypeParameterRelated(
       TypeParameterType s, NeverType t, Types types) {
     return types
@@ -988,7 +1032,82 @@
         .and(new IsSubtypeOf.basedSolelyOnNullabilities(s, t));
   }
 
+  @override
   IsSubtypeOf isTypedefRelated(TypedefType s, NeverType t, Types types) {
     return types.performNullabilityAwareSubtypeCheck(s.unalias, t);
   }
+
+  @override
+  IsSubtypeOf isExtensionRelated(ExtensionType s, NeverType t, Types types) {
+    // TODO(dmitryas): Use with the actual subtyping rules for extension types.
+    return const IsSubtypeOf.never();
+  }
+}
+
+class IsExtensionTypeSubtypeOf implements TypeRelation<ExtensionType> {
+  const IsExtensionTypeSubtypeOf();
+
+  @override
+  IsSubtypeOf isDynamicRelated(DynamicType s, ExtensionType t, Types types) {
+    // TODO(dmitryas): Use with the actual subtyping rules for extension types.
+    return const IsSubtypeOf.never();
+  }
+
+  @override
+  IsSubtypeOf isVoidRelated(VoidType s, ExtensionType t, Types types) {
+    // TODO(dmitryas): Use with the actual subtyping rules for extension types.
+    return const IsSubtypeOf.never();
+  }
+
+  @override
+  IsSubtypeOf isInterfaceRelated(
+      InterfaceType s, ExtensionType t, Types types) {
+    // TODO(dmitryas): Use with the actual subtyping rules for extension types.
+    return const IsSubtypeOf.never();
+  }
+
+  @override
+  IsSubtypeOf isIntersectionRelated(
+      TypeParameterType intersection, ExtensionType t, Types types) {
+    // TODO(dmitryas): Use with the actual subtyping rules for extension types.
+    return const IsSubtypeOf.never();
+  }
+
+  @override
+  IsSubtypeOf isFunctionRelated(FunctionType s, ExtensionType t, Types types) {
+    // TODO(dmitryas): Use with the actual subtyping rules for extension types.
+    return const IsSubtypeOf.never();
+  }
+
+  @override
+  IsSubtypeOf isFutureOrRelated(FutureOrType s, ExtensionType t, Types types) {
+    // TODO(dmitryas): Use with the actual subtyping rules for extension types.
+    return const IsSubtypeOf.never();
+  }
+
+  @override
+  IsSubtypeOf isTypeParameterRelated(
+      TypeParameterType s, ExtensionType t, Types types) {
+    // TODO(dmitryas): Use with the actual subtyping rules for extension types.
+    return const IsSubtypeOf.never();
+  }
+
+  @override
+  IsSubtypeOf isTypedefRelated(TypedefType s, ExtensionType t, Types types) {
+    // TODO(dmitryas): Use with the actual subtyping rules for extension types.
+    return const IsSubtypeOf.never();
+  }
+
+  @override
+  IsSubtypeOf isExtensionRelated(
+      ExtensionType s, ExtensionType t, Types types) {
+    // TODO(dmitryas): Use with the actual subtyping rules for extension types.
+    if (s.extension != t.extension) {
+      return const IsSubtypeOf.never();
+    }
+    return types
+        .areTypeArgumentsOfSubtypeKernel(
+            s.typeArguments, t.typeArguments, t.extension.typeParameters)
+        .and(new IsSubtypeOf.basedSolelyOnNullabilities(s, t));
+  }
 }