Version 2.14.0-0.0.dev

Merge commit '4658b9ee6b3bd83a89aa9af8546ee7b9477c9105' into 'dev'
diff --git a/DEPS b/DEPS
index c5f55af..9408e60 100644
--- a/DEPS
+++ b/DEPS
@@ -44,7 +44,7 @@
   # co19 is a cipd package. Use update.sh in tests/co19[_2] to update these
   # hashes. It requires access to the dart-build-access group, which EngProd
   # has.
-  "co19_rev": "42fe3fd9d6eb66702604dc38f4deca66245ee974",
+  "co19_rev": "65d887eaa99f45e0bd5320ae7ef247eb8d4a264f",
   "co19_2_rev": "f7f583366396cb1457e58c9bfb6d6e53dc21d741",
 
   # The internal benchmarks to use. See go/dart-benchmarks-internal
diff --git a/pkg/analyzer/lib/src/dart/analysis/experiments.g.dart b/pkg/analyzer/lib/src/dart/analysis/experiments.g.dart
index 74cac9d..cc7a05d 100644
--- a/pkg/analyzer/lib/src/dart/analysis/experiments.g.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/experiments.g.dart
@@ -8,7 +8,7 @@
 
 /// The current version of the Dart language (or, for non-stable releases, the
 /// version of the language currently in the process of being developed).
-const _currentVersion = '2.13.0';
+const _currentVersion = '2.14.0';
 
 /// A map containing information about all known experimental flags.
 final _knownFeatures = <String, ExperimentalFeature>{
diff --git a/pkg/compiler/test/codegen/data/shift_right_unsigned.dart b/pkg/compiler/test/codegen/data/shift_right_unsigned.dart
index 23043cf..af2ef54 100644
--- a/pkg/compiler/test/codegen/data/shift_right_unsigned.dart
+++ b/pkg/compiler/test/codegen/data/shift_right_unsigned.dart
@@ -2,7 +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.
 
-//@dart=2.13
+//@dart=2.14
 
 /*member: main:ignore*/
 void main() {
diff --git a/pkg/compiler/test/inference/data/shift_right_unsigned.dart b/pkg/compiler/test/inference/data/shift_right_unsigned.dart
index 3518692..a229111 100644
--- a/pkg/compiler/test/inference/data/shift_right_unsigned.dart
+++ b/pkg/compiler/test/inference/data/shift_right_unsigned.dart
@@ -2,7 +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.
 
-// @dart = 2.13
+//@dart=2.14
 
 /*member: main:[null]*/
 main() {
diff --git a/pkg/front_end/lib/src/api_prototype/experimental_flags_generated.dart b/pkg/front_end/lib/src/api_prototype/experimental_flags_generated.dart
index f774c07..759b8fb 100644
--- a/pkg/front_end/lib/src/api_prototype/experimental_flags_generated.dart
+++ b/pkg/front_end/lib/src/api_prototype/experimental_flags_generated.dart
@@ -29,20 +29,20 @@
 }
 
 const Version enableAlternativeInvalidationStrategyVersion =
-    const Version(2, 13);
-const Version enableConstFunctionsVersion = const Version(2, 13);
+    const Version(2, 14);
+const Version enableConstFunctionsVersion = const Version(2, 14);
 const Version enableConstantUpdate2018Version = const Version(2, 0);
 const Version enableControlFlowCollectionsVersion = const Version(2, 0);
 const Version enableExtensionMethodsVersion = const Version(2, 6);
-const Version enableExtensionTypesVersion = const Version(2, 13);
-const Version enableGenericMetadataVersion = const Version(2, 13);
+const Version enableExtensionTypesVersion = const Version(2, 14);
+const Version enableGenericMetadataVersion = const Version(2, 14);
 const Version enableNonNullableVersion = const Version(2, 12);
 const Version enableNonfunctionTypeAliasesVersion = const Version(2, 13);
 const Version enableSetLiteralsVersion = const Version(2, 0);
 const Version enableSpreadCollectionsVersion = const Version(2, 0);
-const Version enableTripleShiftVersion = const Version(2, 13);
-const Version enableValueClassVersion = const Version(2, 13);
-const Version enableVarianceVersion = const Version(2, 13);
+const Version enableTripleShiftVersion = const Version(2, 14);
+const Version enableValueClassVersion = const Version(2, 14);
+const Version enableVarianceVersion = const Version(2, 14);
 
 ExperimentalFlag parseExperimentalFlag(String flag) {
   switch (flag) {
@@ -113,37 +113,37 @@
 };
 
 const Map<ExperimentalFlag, Version> experimentEnabledVersion = {
-  ExperimentalFlag.alternativeInvalidationStrategy: const Version(2, 13),
-  ExperimentalFlag.constFunctions: const Version(2, 13),
+  ExperimentalFlag.alternativeInvalidationStrategy: const Version(2, 14),
+  ExperimentalFlag.constFunctions: const Version(2, 14),
   ExperimentalFlag.constantUpdate2018: const Version(2, 0),
   ExperimentalFlag.controlFlowCollections: const Version(2, 0),
   ExperimentalFlag.extensionMethods: const Version(2, 6),
-  ExperimentalFlag.extensionTypes: const Version(2, 13),
-  ExperimentalFlag.genericMetadata: const Version(2, 13),
+  ExperimentalFlag.extensionTypes: const Version(2, 14),
+  ExperimentalFlag.genericMetadata: const Version(2, 14),
   ExperimentalFlag.nonNullable: const Version(2, 12),
   ExperimentalFlag.nonfunctionTypeAliases: const Version(2, 13),
   ExperimentalFlag.setLiterals: const Version(2, 0),
   ExperimentalFlag.spreadCollections: const Version(2, 0),
-  ExperimentalFlag.tripleShift: const Version(2, 13),
-  ExperimentalFlag.valueClass: const Version(2, 13),
-  ExperimentalFlag.variance: const Version(2, 13),
+  ExperimentalFlag.tripleShift: const Version(2, 14),
+  ExperimentalFlag.valueClass: const Version(2, 14),
+  ExperimentalFlag.variance: const Version(2, 14),
 };
 
 const Map<ExperimentalFlag, Version> experimentReleasedVersion = {
-  ExperimentalFlag.alternativeInvalidationStrategy: const Version(2, 13),
-  ExperimentalFlag.constFunctions: const Version(2, 13),
+  ExperimentalFlag.alternativeInvalidationStrategy: const Version(2, 14),
+  ExperimentalFlag.constFunctions: const Version(2, 14),
   ExperimentalFlag.constantUpdate2018: const Version(2, 0),
   ExperimentalFlag.controlFlowCollections: const Version(2, 0),
   ExperimentalFlag.extensionMethods: const Version(2, 6),
-  ExperimentalFlag.extensionTypes: const Version(2, 13),
-  ExperimentalFlag.genericMetadata: const Version(2, 13),
+  ExperimentalFlag.extensionTypes: const Version(2, 14),
+  ExperimentalFlag.genericMetadata: const Version(2, 14),
   ExperimentalFlag.nonNullable: const Version(2, 10),
   ExperimentalFlag.nonfunctionTypeAliases: const Version(2, 13),
   ExperimentalFlag.setLiterals: const Version(2, 0),
   ExperimentalFlag.spreadCollections: const Version(2, 0),
-  ExperimentalFlag.tripleShift: const Version(2, 13),
-  ExperimentalFlag.valueClass: const Version(2, 13),
-  ExperimentalFlag.variance: const Version(2, 13),
+  ExperimentalFlag.tripleShift: const Version(2, 14),
+  ExperimentalFlag.valueClass: const Version(2, 14),
+  ExperimentalFlag.variance: const Version(2, 14),
 };
 
 const AllowedExperimentalFlags defaultAllowedExperimentalFlags =
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/front_end/testcases/general/ffi_sample.dart.weak.expect b/pkg/front_end/testcases/general/ffi_sample.dart.weak.expect
index 92bc643..1ab807f 100644
--- a/pkg/front_end/testcases/general/ffi_sample.dart.weak.expect
+++ b/pkg/front_end/testcases/general/ffi_sample.dart.weak.expect
@@ -19,7 +19,7 @@
       #t1.{self::Coordinate::next} = next;
     } =>#t1;
   }
-  abstract member-signature get _typedDataBase() → core::Object*; -> ffi::Struct::_typedDataBase
+  abstract member-signature get _typedDataBase() → core::Object*; -> ffi::_Compound::_typedDataBase
   abstract member-signature get _identityHashCode() → core::int*; -> core::Object::_identityHashCode
   abstract member-signature method _instanceOf(dynamic instantiatorTypeArguments, dynamic functionTypeArguments, dynamic type) → core::bool*; -> core::Object::_instanceOf
   abstract member-signature method _simpleInstanceOf(dynamic type) → core::bool*; -> core::Object::_simpleInstanceOf
diff --git a/pkg/front_end/testcases/general/ffi_sample.dart.weak.outline.expect b/pkg/front_end/testcases/general/ffi_sample.dart.weak.outline.expect
index 6390099..d01ceca 100644
--- a/pkg/front_end/testcases/general/ffi_sample.dart.weak.outline.expect
+++ b/pkg/front_end/testcases/general/ffi_sample.dart.weak.outline.expect
@@ -14,7 +14,7 @@
   field ffi::Pointer<self::Coordinate*>* next;
   static factory allocate(ffi::Allocator* allocator, core::double* x, core::double* y, ffi::Pointer<self::Coordinate*>* next) → self::Coordinate*
     ;
-  abstract member-signature get _typedDataBase() → core::Object*; -> ffi::Struct::_typedDataBase
+  abstract member-signature get _typedDataBase() → core::Object*; -> ffi::_Compound::_typedDataBase
   abstract member-signature get _identityHashCode() → core::int*; -> core::Object::_identityHashCode
   abstract member-signature method _instanceOf(dynamic instantiatorTypeArguments, dynamic functionTypeArguments, dynamic type) → core::bool*; -> core::Object::_instanceOf
   abstract member-signature method _simpleInstanceOf(dynamic type) → core::bool*; -> core::Object::_simpleInstanceOf
diff --git a/pkg/front_end/testcases/general/ffi_sample.dart.weak.transformed.expect b/pkg/front_end/testcases/general/ffi_sample.dart.weak.transformed.expect
index fa07ce3..cad4e7a 100644
--- a/pkg/front_end/testcases/general/ffi_sample.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/general/ffi_sample.dart.weak.transformed.expect
@@ -19,7 +19,7 @@
       #t1.{self::Coordinate::next} = next;
     } =>#t1;
   }
-  abstract member-signature get _typedDataBase() → core::Object*; -> ffi::Struct::_typedDataBase
+  abstract member-signature get _typedDataBase() → core::Object*; -> ffi::_Compound::_typedDataBase
   abstract member-signature get _identityHashCode() → core::int*; -> core::Object::_identityHashCode
   abstract member-signature method _instanceOf(dynamic instantiatorTypeArguments, dynamic functionTypeArguments, dynamic type) → core::bool*; -> core::Object::_instanceOf
   abstract member-signature method _simpleInstanceOf(dynamic type) → core::bool*; -> core::Object::_simpleInstanceOf
@@ -31,17 +31,17 @@
   abstract member-signature method noSuchMethod(core::Invocation* invocation) → dynamic; -> core::Object::noSuchMethod
   abstract member-signature get runtimeType() → core::Type*; -> core::Object::runtimeType
   get x() → core::double*
-    return ffi::_loadDouble(this.{ffi::Struct::_typedDataBase}, (#C12).{core::List::[]}(ffi::_abi()));
+    return ffi::_loadDouble(this.{ffi::_Compound::_typedDataBase}, (#C12).{core::List::[]}(ffi::_abi()));
   set x(core::double* #v) → void
-    return ffi::_storeDouble(this.{ffi::Struct::_typedDataBase}, (#C12).{core::List::[]}(ffi::_abi()), #v);
+    return ffi::_storeDouble(this.{ffi::_Compound::_typedDataBase}, (#C12).{core::List::[]}(ffi::_abi()), #v);
   get y() → core::double*
-    return ffi::_loadDouble(this.{ffi::Struct::_typedDataBase}, (#C14).{core::List::[]}(ffi::_abi()));
+    return ffi::_loadDouble(this.{ffi::_Compound::_typedDataBase}, (#C14).{core::List::[]}(ffi::_abi()));
   set y(core::double* #v) → void
-    return ffi::_storeDouble(this.{ffi::Struct::_typedDataBase}, (#C14).{core::List::[]}(ffi::_abi()), #v);
+    return ffi::_storeDouble(this.{ffi::_Compound::_typedDataBase}, (#C14).{core::List::[]}(ffi::_abi()), #v);
   get next() → ffi::Pointer<self::Coordinate*>*
-    return ffi::_fromAddress<self::Coordinate*>(ffi::_loadIntPtr(this.{ffi::Struct::_typedDataBase}, (#C16).{core::List::[]}(ffi::_abi())));
+    return ffi::_fromAddress<self::Coordinate*>(ffi::_loadIntPtr(this.{ffi::_Compound::_typedDataBase}, (#C16).{core::List::[]}(ffi::_abi())));
   set next(ffi::Pointer<self::Coordinate*>* #v) → void
-    return ffi::_storeIntPtr(this.{ffi::Struct::_typedDataBase}, (#C16).{core::List::[]}(ffi::_abi()), #v.{ffi::Pointer::address});
+    return ffi::_storeIntPtr(this.{ffi::_Compound::_typedDataBase}, (#C16).{core::List::[]}(ffi::_abi()), #v.{ffi::Pointer::address});
 }
 static method main() → dynamic {}
 
diff --git a/pkg/front_end/testcases/incremental/ffi_01.yaml.world.1.expect b/pkg/front_end/testcases/incremental/ffi_01.yaml.world.1.expect
index 359b430..e983eec 100644
--- a/pkg/front_end/testcases/incremental/ffi_01.yaml.world.1.expect
+++ b/pkg/front_end/testcases/incremental/ffi_01.yaml.world.1.expect
@@ -12,7 +12,7 @@
     static factory allocate(dart.core::double* x, dart.core::double* y, dart.ffi::Pointer<lib::Coordinate*>* next) → lib::Coordinate* {
       return null;
     }
-    abstract member-signature get _typedDataBase() → dart.core::Object*; -> dart.ffi::Struct::_typedDataBase
+    abstract member-signature get _typedDataBase() → dart.core::Object*; -> dart.ffi::_Compound::_typedDataBase
     abstract member-signature get _identityHashCode() → dart.core::int*; -> dart.core::Object::_identityHashCode
     abstract member-signature method _instanceOf(dynamic instantiatorTypeArguments, dynamic functionTypeArguments, dynamic type) → dart.core::bool*; -> dart.core::Object::_instanceOf
     abstract member-signature method _simpleInstanceOf(dynamic type) → dart.core::bool*; -> dart.core::Object::_simpleInstanceOf
@@ -24,17 +24,17 @@
     abstract member-signature method noSuchMethod(dart.core::Invocation* invocation) → dynamic; -> dart.core::Object::noSuchMethod
     abstract member-signature get runtimeType() → dart.core::Type*; -> dart.core::Object::runtimeType
     get x() → dart.core::double*
-      return dart.ffi::_loadDouble(this.{dart.ffi::Struct::_typedDataBase}, (#C12).{dart.core::List::[]}(dart.ffi::_abi()));
+      return dart.ffi::_loadDouble(this.{dart.ffi::_Compound::_typedDataBase}, (#C12).{dart.core::List::[]}(dart.ffi::_abi()));
     set x(dart.core::double* #v) → void
-      return dart.ffi::_storeDouble(this.{dart.ffi::Struct::_typedDataBase}, (#C12).{dart.core::List::[]}(dart.ffi::_abi()), #v);
+      return dart.ffi::_storeDouble(this.{dart.ffi::_Compound::_typedDataBase}, (#C12).{dart.core::List::[]}(dart.ffi::_abi()), #v);
     get y() → dart.core::double*
-      return dart.ffi::_loadDouble(this.{dart.ffi::Struct::_typedDataBase}, (#C14).{dart.core::List::[]}(dart.ffi::_abi()));
+      return dart.ffi::_loadDouble(this.{dart.ffi::_Compound::_typedDataBase}, (#C14).{dart.core::List::[]}(dart.ffi::_abi()));
     set y(dart.core::double* #v) → void
-      return dart.ffi::_storeDouble(this.{dart.ffi::Struct::_typedDataBase}, (#C14).{dart.core::List::[]}(dart.ffi::_abi()), #v);
+      return dart.ffi::_storeDouble(this.{dart.ffi::_Compound::_typedDataBase}, (#C14).{dart.core::List::[]}(dart.ffi::_abi()), #v);
     get next() → dart.ffi::Pointer<lib::Coordinate*>*
-      return dart.ffi::_fromAddress<lib::Coordinate*>(dart.ffi::_loadIntPtr(this.{dart.ffi::Struct::_typedDataBase}, (#C16).{dart.core::List::[]}(dart.ffi::_abi())));
+      return dart.ffi::_fromAddress<lib::Coordinate*>(dart.ffi::_loadIntPtr(this.{dart.ffi::_Compound::_typedDataBase}, (#C16).{dart.core::List::[]}(dart.ffi::_abi())));
     set next(dart.ffi::Pointer<lib::Coordinate*>* #v) → void
-      return dart.ffi::_storeIntPtr(this.{dart.ffi::Struct::_typedDataBase}, (#C16).{dart.core::List::[]}(dart.ffi::_abi()), #v.{dart.ffi::Pointer::address});
+      return dart.ffi::_storeIntPtr(this.{dart.ffi::_Compound::_typedDataBase}, (#C16).{dart.core::List::[]}(dart.ffi::_abi()), #v.{dart.ffi::Pointer::address});
   }
 }
 library from "org-dartlang-test:///main.dart" as main {
diff --git a/pkg/front_end/testcases/incremental/ffi_01.yaml.world.2.expect b/pkg/front_end/testcases/incremental/ffi_01.yaml.world.2.expect
index 9e6554f..f61ae87 100644
--- a/pkg/front_end/testcases/incremental/ffi_01.yaml.world.2.expect
+++ b/pkg/front_end/testcases/incremental/ffi_01.yaml.world.2.expect
@@ -12,7 +12,7 @@
     static factory allocate(dart.core::double* x, dart.core::double* y, dart.ffi::Pointer<lib::Coordinate*>* next) → lib::Coordinate* {
       return null;
     }
-    abstract member-signature get _typedDataBase() → dart.core::Object*; -> dart.ffi::Struct::_typedDataBase
+    abstract member-signature get _typedDataBase() → dart.core::Object*; -> dart.ffi::_Compound::_typedDataBase
     abstract member-signature get _identityHashCode() → dart.core::int*; -> dart.core::Object::_identityHashCode
     abstract member-signature method _instanceOf(dynamic instantiatorTypeArguments, dynamic functionTypeArguments, dynamic type) → dart.core::bool*; -> dart.core::Object::_instanceOf
     abstract member-signature method _simpleInstanceOf(dynamic type) → dart.core::bool*; -> dart.core::Object::_simpleInstanceOf
@@ -24,17 +24,17 @@
     abstract member-signature method noSuchMethod(dart.core::Invocation* invocation) → dynamic; -> dart.core::Object::noSuchMethod
     abstract member-signature get runtimeType() → dart.core::Type*; -> dart.core::Object::runtimeType
     get x() → dart.core::double*
-      return dart.ffi::_loadDouble(this.{dart.ffi::Struct::_typedDataBase}, (#C12).{dart.core::List::[]}(dart.ffi::_abi()));
+      return dart.ffi::_loadDouble(this.{dart.ffi::_Compound::_typedDataBase}, (#C12).{dart.core::List::[]}(dart.ffi::_abi()));
     set x(dart.core::double* #v) → void
-      return dart.ffi::_storeDouble(this.{dart.ffi::Struct::_typedDataBase}, (#C12).{dart.core::List::[]}(dart.ffi::_abi()), #v);
+      return dart.ffi::_storeDouble(this.{dart.ffi::_Compound::_typedDataBase}, (#C12).{dart.core::List::[]}(dart.ffi::_abi()), #v);
     get y() → dart.core::double*
-      return dart.ffi::_loadDouble(this.{dart.ffi::Struct::_typedDataBase}, (#C14).{dart.core::List::[]}(dart.ffi::_abi()));
+      return dart.ffi::_loadDouble(this.{dart.ffi::_Compound::_typedDataBase}, (#C14).{dart.core::List::[]}(dart.ffi::_abi()));
     set y(dart.core::double* #v) → void
-      return dart.ffi::_storeDouble(this.{dart.ffi::Struct::_typedDataBase}, (#C14).{dart.core::List::[]}(dart.ffi::_abi()), #v);
+      return dart.ffi::_storeDouble(this.{dart.ffi::_Compound::_typedDataBase}, (#C14).{dart.core::List::[]}(dart.ffi::_abi()), #v);
     get next() → dart.ffi::Pointer<lib::Coordinate*>*
-      return dart.ffi::_fromAddress<lib::Coordinate*>(dart.ffi::_loadIntPtr(this.{dart.ffi::Struct::_typedDataBase}, (#C16).{dart.core::List::[]}(dart.ffi::_abi())));
+      return dart.ffi::_fromAddress<lib::Coordinate*>(dart.ffi::_loadIntPtr(this.{dart.ffi::_Compound::_typedDataBase}, (#C16).{dart.core::List::[]}(dart.ffi::_abi())));
     set next(dart.ffi::Pointer<lib::Coordinate*>* #v) → void
-      return dart.ffi::_storeIntPtr(this.{dart.ffi::Struct::_typedDataBase}, (#C16).{dart.core::List::[]}(dart.ffi::_abi()), #v.{dart.ffi::Pointer::address});
+      return dart.ffi::_storeIntPtr(this.{dart.ffi::_Compound::_typedDataBase}, (#C16).{dart.core::List::[]}(dart.ffi::_abi()), #v.{dart.ffi::Pointer::address});
   }
 }
 library from "org-dartlang-test:///main.dart" as main {
diff --git a/pkg/front_end/testcases/incremental/ffi_02.yaml.world.1.expect b/pkg/front_end/testcases/incremental/ffi_02.yaml.world.1.expect
index aa03bd6d..1979d4a 100644
--- a/pkg/front_end/testcases/incremental/ffi_02.yaml.world.1.expect
+++ b/pkg/front_end/testcases/incremental/ffi_02.yaml.world.1.expect
@@ -12,7 +12,7 @@
     static factory allocate(dart.core::double* x, dart.core::double* y, dart.ffi::Pointer<lib::Coordinate*>* next) → lib::Coordinate* {
       return null;
     }
-    abstract member-signature get _typedDataBase() → dart.core::Object*; -> dart.ffi::Struct::_typedDataBase
+    abstract member-signature get _typedDataBase() → dart.core::Object*; -> dart.ffi::_Compound::_typedDataBase
     abstract member-signature get _identityHashCode() → dart.core::int*; -> dart.core::Object::_identityHashCode
     abstract member-signature method _instanceOf(dynamic instantiatorTypeArguments, dynamic functionTypeArguments, dynamic type) → dart.core::bool*; -> dart.core::Object::_instanceOf
     abstract member-signature method _simpleInstanceOf(dynamic type) → dart.core::bool*; -> dart.core::Object::_simpleInstanceOf
@@ -24,17 +24,17 @@
     abstract member-signature method noSuchMethod(dart.core::Invocation* invocation) → dynamic; -> dart.core::Object::noSuchMethod
     abstract member-signature get runtimeType() → dart.core::Type*; -> dart.core::Object::runtimeType
     get x() → dart.core::double*
-      return dart.ffi::_loadDouble(this.{dart.ffi::Struct::_typedDataBase}, (#C12).{dart.core::List::[]}(dart.ffi::_abi()));
+      return dart.ffi::_loadDouble(this.{dart.ffi::_Compound::_typedDataBase}, (#C12).{dart.core::List::[]}(dart.ffi::_abi()));
     set x(dart.core::double* #v) → void
-      return dart.ffi::_storeDouble(this.{dart.ffi::Struct::_typedDataBase}, (#C12).{dart.core::List::[]}(dart.ffi::_abi()), #v);
+      return dart.ffi::_storeDouble(this.{dart.ffi::_Compound::_typedDataBase}, (#C12).{dart.core::List::[]}(dart.ffi::_abi()), #v);
     get y() → dart.core::double*
-      return dart.ffi::_loadDouble(this.{dart.ffi::Struct::_typedDataBase}, (#C14).{dart.core::List::[]}(dart.ffi::_abi()));
+      return dart.ffi::_loadDouble(this.{dart.ffi::_Compound::_typedDataBase}, (#C14).{dart.core::List::[]}(dart.ffi::_abi()));
     set y(dart.core::double* #v) → void
-      return dart.ffi::_storeDouble(this.{dart.ffi::Struct::_typedDataBase}, (#C14).{dart.core::List::[]}(dart.ffi::_abi()), #v);
+      return dart.ffi::_storeDouble(this.{dart.ffi::_Compound::_typedDataBase}, (#C14).{dart.core::List::[]}(dart.ffi::_abi()), #v);
     get next() → dart.ffi::Pointer<lib::Coordinate*>*
-      return dart.ffi::_fromAddress<lib::Coordinate*>(dart.ffi::_loadIntPtr(this.{dart.ffi::Struct::_typedDataBase}, (#C16).{dart.core::List::[]}(dart.ffi::_abi())));
+      return dart.ffi::_fromAddress<lib::Coordinate*>(dart.ffi::_loadIntPtr(this.{dart.ffi::_Compound::_typedDataBase}, (#C16).{dart.core::List::[]}(dart.ffi::_abi())));
     set next(dart.ffi::Pointer<lib::Coordinate*>* #v) → void
-      return dart.ffi::_storeIntPtr(this.{dart.ffi::Struct::_typedDataBase}, (#C16).{dart.core::List::[]}(dart.ffi::_abi()), #v.{dart.ffi::Pointer::address});
+      return dart.ffi::_storeIntPtr(this.{dart.ffi::_Compound::_typedDataBase}, (#C16).{dart.core::List::[]}(dart.ffi::_abi()), #v.{dart.ffi::Pointer::address});
   }
 }
 library from "org-dartlang-test:///main.dart" as main {
diff --git a/pkg/front_end/testcases/incremental/no_outline_change_35.yaml.world.1.expect b/pkg/front_end/testcases/incremental/no_outline_change_35.yaml.world.1.expect
index 359b430..e983eec 100644
--- a/pkg/front_end/testcases/incremental/no_outline_change_35.yaml.world.1.expect
+++ b/pkg/front_end/testcases/incremental/no_outline_change_35.yaml.world.1.expect
@@ -12,7 +12,7 @@
     static factory allocate(dart.core::double* x, dart.core::double* y, dart.ffi::Pointer<lib::Coordinate*>* next) → lib::Coordinate* {
       return null;
     }
-    abstract member-signature get _typedDataBase() → dart.core::Object*; -> dart.ffi::Struct::_typedDataBase
+    abstract member-signature get _typedDataBase() → dart.core::Object*; -> dart.ffi::_Compound::_typedDataBase
     abstract member-signature get _identityHashCode() → dart.core::int*; -> dart.core::Object::_identityHashCode
     abstract member-signature method _instanceOf(dynamic instantiatorTypeArguments, dynamic functionTypeArguments, dynamic type) → dart.core::bool*; -> dart.core::Object::_instanceOf
     abstract member-signature method _simpleInstanceOf(dynamic type) → dart.core::bool*; -> dart.core::Object::_simpleInstanceOf
@@ -24,17 +24,17 @@
     abstract member-signature method noSuchMethod(dart.core::Invocation* invocation) → dynamic; -> dart.core::Object::noSuchMethod
     abstract member-signature get runtimeType() → dart.core::Type*; -> dart.core::Object::runtimeType
     get x() → dart.core::double*
-      return dart.ffi::_loadDouble(this.{dart.ffi::Struct::_typedDataBase}, (#C12).{dart.core::List::[]}(dart.ffi::_abi()));
+      return dart.ffi::_loadDouble(this.{dart.ffi::_Compound::_typedDataBase}, (#C12).{dart.core::List::[]}(dart.ffi::_abi()));
     set x(dart.core::double* #v) → void
-      return dart.ffi::_storeDouble(this.{dart.ffi::Struct::_typedDataBase}, (#C12).{dart.core::List::[]}(dart.ffi::_abi()), #v);
+      return dart.ffi::_storeDouble(this.{dart.ffi::_Compound::_typedDataBase}, (#C12).{dart.core::List::[]}(dart.ffi::_abi()), #v);
     get y() → dart.core::double*
-      return dart.ffi::_loadDouble(this.{dart.ffi::Struct::_typedDataBase}, (#C14).{dart.core::List::[]}(dart.ffi::_abi()));
+      return dart.ffi::_loadDouble(this.{dart.ffi::_Compound::_typedDataBase}, (#C14).{dart.core::List::[]}(dart.ffi::_abi()));
     set y(dart.core::double* #v) → void
-      return dart.ffi::_storeDouble(this.{dart.ffi::Struct::_typedDataBase}, (#C14).{dart.core::List::[]}(dart.ffi::_abi()), #v);
+      return dart.ffi::_storeDouble(this.{dart.ffi::_Compound::_typedDataBase}, (#C14).{dart.core::List::[]}(dart.ffi::_abi()), #v);
     get next() → dart.ffi::Pointer<lib::Coordinate*>*
-      return dart.ffi::_fromAddress<lib::Coordinate*>(dart.ffi::_loadIntPtr(this.{dart.ffi::Struct::_typedDataBase}, (#C16).{dart.core::List::[]}(dart.ffi::_abi())));
+      return dart.ffi::_fromAddress<lib::Coordinate*>(dart.ffi::_loadIntPtr(this.{dart.ffi::_Compound::_typedDataBase}, (#C16).{dart.core::List::[]}(dart.ffi::_abi())));
     set next(dart.ffi::Pointer<lib::Coordinate*>* #v) → void
-      return dart.ffi::_storeIntPtr(this.{dart.ffi::Struct::_typedDataBase}, (#C16).{dart.core::List::[]}(dart.ffi::_abi()), #v.{dart.ffi::Pointer::address});
+      return dart.ffi::_storeIntPtr(this.{dart.ffi::_Compound::_typedDataBase}, (#C16).{dart.core::List::[]}(dart.ffi::_abi()), #v.{dart.ffi::Pointer::address});
   }
 }
 library from "org-dartlang-test:///main.dart" as main {
diff --git a/pkg/front_end/testcases/incremental/no_outline_change_35.yaml.world.2.expect b/pkg/front_end/testcases/incremental/no_outline_change_35.yaml.world.2.expect
index feb7dd1..aae1c15 100644
--- a/pkg/front_end/testcases/incremental/no_outline_change_35.yaml.world.2.expect
+++ b/pkg/front_end/testcases/incremental/no_outline_change_35.yaml.world.2.expect
@@ -12,7 +12,7 @@
     static factory allocate(dart.core::double* x, dart.core::double* y, dart.ffi::Pointer<lib::Coordinate*>* next) → lib::Coordinate* {
       return null;
     }
-    abstract member-signature get _typedDataBase() → dart.core::Object*; -> dart.ffi::Struct::_typedDataBase
+    abstract member-signature get _typedDataBase() → dart.core::Object*; -> dart.ffi::_Compound::_typedDataBase
     abstract member-signature get _identityHashCode() → dart.core::int*; -> dart.core::Object::_identityHashCode
     abstract member-signature method _instanceOf(dynamic instantiatorTypeArguments, dynamic functionTypeArguments, dynamic type) → dart.core::bool*; -> dart.core::Object::_instanceOf
     abstract member-signature method _simpleInstanceOf(dynamic type) → dart.core::bool*; -> dart.core::Object::_simpleInstanceOf
@@ -24,17 +24,17 @@
     abstract member-signature method noSuchMethod(dart.core::Invocation* invocation) → dynamic; -> dart.core::Object::noSuchMethod
     abstract member-signature get runtimeType() → dart.core::Type*; -> dart.core::Object::runtimeType
     get x() → dart.core::double*
-      return dart.ffi::_loadDouble(this.{dart.ffi::Struct::_typedDataBase}, (#C12).{dart.core::List::[]}(dart.ffi::_abi()));
+      return dart.ffi::_loadDouble(this.{dart.ffi::_Compound::_typedDataBase}, (#C12).{dart.core::List::[]}(dart.ffi::_abi()));
     set x(dart.core::double* #v) → void
-      return dart.ffi::_storeDouble(this.{dart.ffi::Struct::_typedDataBase}, (#C12).{dart.core::List::[]}(dart.ffi::_abi()), #v);
+      return dart.ffi::_storeDouble(this.{dart.ffi::_Compound::_typedDataBase}, (#C12).{dart.core::List::[]}(dart.ffi::_abi()), #v);
     get y() → dart.core::double*
-      return dart.ffi::_loadDouble(this.{dart.ffi::Struct::_typedDataBase}, (#C14).{dart.core::List::[]}(dart.ffi::_abi()));
+      return dart.ffi::_loadDouble(this.{dart.ffi::_Compound::_typedDataBase}, (#C14).{dart.core::List::[]}(dart.ffi::_abi()));
     set y(dart.core::double* #v) → void
-      return dart.ffi::_storeDouble(this.{dart.ffi::Struct::_typedDataBase}, (#C14).{dart.core::List::[]}(dart.ffi::_abi()), #v);
+      return dart.ffi::_storeDouble(this.{dart.ffi::_Compound::_typedDataBase}, (#C14).{dart.core::List::[]}(dart.ffi::_abi()), #v);
     get next() → dart.ffi::Pointer<lib::Coordinate*>*
-      return dart.ffi::_fromAddress<lib::Coordinate*>(dart.ffi::_loadIntPtr(this.{dart.ffi::Struct::_typedDataBase}, (#C16).{dart.core::List::[]}(dart.ffi::_abi())));
+      return dart.ffi::_fromAddress<lib::Coordinate*>(dart.ffi::_loadIntPtr(this.{dart.ffi::_Compound::_typedDataBase}, (#C16).{dart.core::List::[]}(dart.ffi::_abi())));
     set next(dart.ffi::Pointer<lib::Coordinate*>* #v) → void
-      return dart.ffi::_storeIntPtr(this.{dart.ffi::Struct::_typedDataBase}, (#C16).{dart.core::List::[]}(dart.ffi::_abi()), #v.{dart.ffi::Pointer::address});
+      return dart.ffi::_storeIntPtr(this.{dart.ffi::_Compound::_typedDataBase}, (#C16).{dart.core::List::[]}(dart.ffi::_abi()), #v.{dart.ffi::Pointer::address});
   }
 }
 library from "org-dartlang-test:///main.dart" as main {
diff --git a/pkg/front_end/testcases/incremental/no_outline_change_35.yaml.world.3.expect b/pkg/front_end/testcases/incremental/no_outline_change_35.yaml.world.3.expect
index 3b28c5c..dcd9515 100644
--- a/pkg/front_end/testcases/incremental/no_outline_change_35.yaml.world.3.expect
+++ b/pkg/front_end/testcases/incremental/no_outline_change_35.yaml.world.3.expect
@@ -13,7 +13,7 @@
       dart.core::print("hello");
       return null;
     }
-    abstract member-signature get _typedDataBase() → dart.core::Object*; -> dart.ffi::Struct::_typedDataBase
+    abstract member-signature get _typedDataBase() → dart.core::Object*; -> dart.ffi::_Compound::_typedDataBase
     abstract member-signature get _identityHashCode() → dart.core::int*; -> dart.core::Object::_identityHashCode
     abstract member-signature method _instanceOf(dynamic instantiatorTypeArguments, dynamic functionTypeArguments, dynamic type) → dart.core::bool*; -> dart.core::Object::_instanceOf
     abstract member-signature method _simpleInstanceOf(dynamic type) → dart.core::bool*; -> dart.core::Object::_simpleInstanceOf
@@ -25,17 +25,17 @@
     abstract member-signature method noSuchMethod(dart.core::Invocation* invocation) → dynamic; -> dart.core::Object::noSuchMethod
     abstract member-signature get runtimeType() → dart.core::Type*; -> dart.core::Object::runtimeType
     get x() → dart.core::double*
-      return dart.ffi::_loadDouble(this.{dart.ffi::Struct::_typedDataBase}, (#C12).{dart.core::List::[]}(dart.ffi::_abi()));
+      return dart.ffi::_loadDouble(this.{dart.ffi::_Compound::_typedDataBase}, (#C12).{dart.core::List::[]}(dart.ffi::_abi()));
     set x(dart.core::double* #v) → void
-      return dart.ffi::_storeDouble(this.{dart.ffi::Struct::_typedDataBase}, (#C12).{dart.core::List::[]}(dart.ffi::_abi()), #v);
+      return dart.ffi::_storeDouble(this.{dart.ffi::_Compound::_typedDataBase}, (#C12).{dart.core::List::[]}(dart.ffi::_abi()), #v);
     get y() → dart.core::double*
-      return dart.ffi::_loadDouble(this.{dart.ffi::Struct::_typedDataBase}, (#C14).{dart.core::List::[]}(dart.ffi::_abi()));
+      return dart.ffi::_loadDouble(this.{dart.ffi::_Compound::_typedDataBase}, (#C14).{dart.core::List::[]}(dart.ffi::_abi()));
     set y(dart.core::double* #v) → void
-      return dart.ffi::_storeDouble(this.{dart.ffi::Struct::_typedDataBase}, (#C14).{dart.core::List::[]}(dart.ffi::_abi()), #v);
+      return dart.ffi::_storeDouble(this.{dart.ffi::_Compound::_typedDataBase}, (#C14).{dart.core::List::[]}(dart.ffi::_abi()), #v);
     get next() → dart.ffi::Pointer<lib::Coordinate*>*
-      return dart.ffi::_fromAddress<lib::Coordinate*>(dart.ffi::_loadIntPtr(this.{dart.ffi::Struct::_typedDataBase}, (#C16).{dart.core::List::[]}(dart.ffi::_abi())));
+      return dart.ffi::_fromAddress<lib::Coordinate*>(dart.ffi::_loadIntPtr(this.{dart.ffi::_Compound::_typedDataBase}, (#C16).{dart.core::List::[]}(dart.ffi::_abi())));
     set next(dart.ffi::Pointer<lib::Coordinate*>* #v) → void
-      return dart.ffi::_storeIntPtr(this.{dart.ffi::Struct::_typedDataBase}, (#C16).{dart.core::List::[]}(dart.ffi::_abi()), #v.{dart.ffi::Pointer::address});
+      return dart.ffi::_storeIntPtr(this.{dart.ffi::_Compound::_typedDataBase}, (#C16).{dart.core::List::[]}(dart.ffi::_abi()), #v.{dart.ffi::Pointer::address});
   }
 }
 library from "org-dartlang-test:///main.dart" as main {
diff --git a/pkg/front_end/testcases/nnbd/ffi_sample.dart.strong.transformed.expect b/pkg/front_end/testcases/nnbd/ffi_sample.dart.strong.transformed.expect
index a7d1f8b..eee9d220 100644
--- a/pkg/front_end/testcases/nnbd/ffi_sample.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/nnbd/ffi_sample.dart.strong.transformed.expect
@@ -14,20 +14,20 @@
     ;
   @#C11
   get x() → core::double
-    return ffi::_loadDouble(this.{ffi::Struct::_typedDataBase}, (#C13).{core::List::[]}(ffi::_abi()));
+    return ffi::_loadDouble(this.{ffi::_Compound::_typedDataBase}, (#C13).{core::List::[]}(ffi::_abi()));
   @#C11
   set x(core::double #externalFieldValue) → void
-    return ffi::_storeDouble(this.{ffi::Struct::_typedDataBase}, (#C13).{core::List::[]}(ffi::_abi()), #externalFieldValue);
+    return ffi::_storeDouble(this.{ffi::_Compound::_typedDataBase}, (#C13).{core::List::[]}(ffi::_abi()), #externalFieldValue);
   @#C11
   get y() → core::double
-    return ffi::_loadDouble(this.{ffi::Struct::_typedDataBase}, (#C15).{core::List::[]}(ffi::_abi()));
+    return ffi::_loadDouble(this.{ffi::_Compound::_typedDataBase}, (#C15).{core::List::[]}(ffi::_abi()));
   @#C11
   set y(core::double #externalFieldValue) → void
-    return ffi::_storeDouble(this.{ffi::Struct::_typedDataBase}, (#C15).{core::List::[]}(ffi::_abi()), #externalFieldValue);
+    return ffi::_storeDouble(this.{ffi::_Compound::_typedDataBase}, (#C15).{core::List::[]}(ffi::_abi()), #externalFieldValue);
   get next() → ffi::Pointer<self::Coordinate>
-    return ffi::_fromAddress<self::Coordinate>(ffi::_loadIntPtr(this.{ffi::Struct::_typedDataBase}, (#C17).{core::List::[]}(ffi::_abi())));
+    return ffi::_fromAddress<self::Coordinate>(ffi::_loadIntPtr(this.{ffi::_Compound::_typedDataBase}, (#C17).{core::List::[]}(ffi::_abi())));
   set next(ffi::Pointer<self::Coordinate> #externalFieldValue) → void
-    return ffi::_storeIntPtr(this.{ffi::Struct::_typedDataBase}, (#C17).{core::List::[]}(ffi::_abi()), #externalFieldValue.{ffi::Pointer::address});
+    return ffi::_storeIntPtr(this.{ffi::_Compound::_typedDataBase}, (#C17).{core::List::[]}(ffi::_abi()), #externalFieldValue.{ffi::Pointer::address});
   static factory allocate(ffi::Allocator allocator, core::double x, core::double y, ffi::Pointer<self::Coordinate> next) → self::Coordinate {
     return let final self::Coordinate #t1 = new self::Coordinate::#fromTypedDataBase(allocator.{ffi::Allocator::allocate}<self::Coordinate>(self::Coordinate::#sizeOf)!) in block {
       #t1.{self::Coordinate::x} = x;
diff --git a/pkg/front_end/testcases/nnbd/ffi_sample.dart.weak.transformed.expect b/pkg/front_end/testcases/nnbd/ffi_sample.dart.weak.transformed.expect
index a7d1f8b..eee9d220 100644
--- a/pkg/front_end/testcases/nnbd/ffi_sample.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/nnbd/ffi_sample.dart.weak.transformed.expect
@@ -14,20 +14,20 @@
     ;
   @#C11
   get x() → core::double
-    return ffi::_loadDouble(this.{ffi::Struct::_typedDataBase}, (#C13).{core::List::[]}(ffi::_abi()));
+    return ffi::_loadDouble(this.{ffi::_Compound::_typedDataBase}, (#C13).{core::List::[]}(ffi::_abi()));
   @#C11
   set x(core::double #externalFieldValue) → void
-    return ffi::_storeDouble(this.{ffi::Struct::_typedDataBase}, (#C13).{core::List::[]}(ffi::_abi()), #externalFieldValue);
+    return ffi::_storeDouble(this.{ffi::_Compound::_typedDataBase}, (#C13).{core::List::[]}(ffi::_abi()), #externalFieldValue);
   @#C11
   get y() → core::double
-    return ffi::_loadDouble(this.{ffi::Struct::_typedDataBase}, (#C15).{core::List::[]}(ffi::_abi()));
+    return ffi::_loadDouble(this.{ffi::_Compound::_typedDataBase}, (#C15).{core::List::[]}(ffi::_abi()));
   @#C11
   set y(core::double #externalFieldValue) → void
-    return ffi::_storeDouble(this.{ffi::Struct::_typedDataBase}, (#C15).{core::List::[]}(ffi::_abi()), #externalFieldValue);
+    return ffi::_storeDouble(this.{ffi::_Compound::_typedDataBase}, (#C15).{core::List::[]}(ffi::_abi()), #externalFieldValue);
   get next() → ffi::Pointer<self::Coordinate>
-    return ffi::_fromAddress<self::Coordinate>(ffi::_loadIntPtr(this.{ffi::Struct::_typedDataBase}, (#C17).{core::List::[]}(ffi::_abi())));
+    return ffi::_fromAddress<self::Coordinate>(ffi::_loadIntPtr(this.{ffi::_Compound::_typedDataBase}, (#C17).{core::List::[]}(ffi::_abi())));
   set next(ffi::Pointer<self::Coordinate> #externalFieldValue) → void
-    return ffi::_storeIntPtr(this.{ffi::Struct::_typedDataBase}, (#C17).{core::List::[]}(ffi::_abi()), #externalFieldValue.{ffi::Pointer::address});
+    return ffi::_storeIntPtr(this.{ffi::_Compound::_typedDataBase}, (#C17).{core::List::[]}(ffi::_abi()), #externalFieldValue.{ffi::Pointer::address});
   static factory allocate(ffi::Allocator allocator, core::double x, core::double y, ffi::Pointer<self::Coordinate> next) → self::Coordinate {
     return let final self::Coordinate #t1 = new self::Coordinate::#fromTypedDataBase(allocator.{ffi::Allocator::allocate}<self::Coordinate>(self::Coordinate::#sizeOf)!) in block {
       #t1.{self::Coordinate::x} = x;
diff --git a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.strong.transformed.expect b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.strong.transformed.expect
index e7423e9c..49368b2 100644
--- a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.strong.transformed.expect
@@ -20,12 +20,12 @@
   @#C10
   get a0() → ffi::Array<ffi::Uint8>
     return new ffi::Array::_<ffi::Uint8>( block {
-      core::Object #typedDataBase = this.{ffi::Struct::_typedDataBase};
+      core::Object #typedDataBase = this.{ffi::_Compound::_typedDataBase};
       core::int #offset = (#C12).{core::List::[]}(ffi::_abi());
     } =>#typedDataBase is ffi::Pointer<dynamic> ?{core::Object} ffi::_fromAddress<ffi::Uint8>(#typedDataBase.{ffi::Pointer::address}.{core::num::+}(#offset)) : let typ::TypedData #typedData = _in::unsafeCast<typ::TypedData>(#typedDataBase) in #typedData.{typ::TypedData::buffer}.{typ::ByteBuffer::asUint8List}(#typedData.{typ::TypedData::offsetInBytes}.{core::num::+}(#offset), (#C9).{core::List::[]}(ffi::_abi())), #C3, #C13);
   @#C10
   set a0(ffi::Array<ffi::Uint8> #externalFieldValue) → void
-    return ffi::_memCopy(this.{ffi::Struct::_typedDataBase}, (#C12).{core::List::[]}(ffi::_abi()), #externalFieldValue.{ffi::Array::_typedDataBase}, #C11, (#C9).{core::List::[]}(ffi::_abi()));
+    return ffi::_memCopy(this.{ffi::_Compound::_typedDataBase}, (#C12).{core::List::[]}(ffi::_abi()), #externalFieldValue.{ffi::Array::_typedDataBase}, #C11, (#C9).{core::List::[]}(ffi::_abi()));
 }
 static method main() → dynamic {}
 
diff --git a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.weak.transformed.expect b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.weak.transformed.expect
index 73f80ad..0164237 100644
--- a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array.dart.weak.transformed.expect
@@ -20,12 +20,12 @@
   @#C10
   get a0() → ffi::Array<ffi::Uint8>
     return new ffi::Array::_<ffi::Uint8>( block {
-      core::Object #typedDataBase = this.{ffi::Struct::_typedDataBase};
+      core::Object #typedDataBase = this.{ffi::_Compound::_typedDataBase};
       core::int #offset = (#C12).{core::List::[]}(ffi::_abi());
     } =>#typedDataBase is ffi::Pointer<dynamic> ?{core::Object} ffi::_fromAddress<ffi::Uint8>(#typedDataBase.{ffi::Pointer::address}.{core::num::+}(#offset)) : let typ::TypedData #typedData = _in::unsafeCast<typ::TypedData>(#typedDataBase) in #typedData.{typ::TypedData::buffer}.{typ::ByteBuffer::asUint8List}(#typedData.{typ::TypedData::offsetInBytes}.{core::num::+}(#offset), (#C9).{core::List::[]}(ffi::_abi())), #C3, #C13);
   @#C10
   set a0(ffi::Array<ffi::Uint8> #externalFieldValue) → void
-    return ffi::_memCopy(this.{ffi::Struct::_typedDataBase}, (#C12).{core::List::[]}(ffi::_abi()), #externalFieldValue.{ffi::Array::_typedDataBase}, #C11, (#C9).{core::List::[]}(ffi::_abi()));
+    return ffi::_memCopy(this.{ffi::_Compound::_typedDataBase}, (#C12).{core::List::[]}(ffi::_abi()), #externalFieldValue.{ffi::Array::_typedDataBase}, #C11, (#C9).{core::List::[]}(ffi::_abi()));
 }
 static method main() → dynamic {}
 
diff --git a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.strong.transformed.expect b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.strong.transformed.expect
index e9e3431..6b9936f 100644
--- a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.strong.transformed.expect
+++ b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.strong.transformed.expect
@@ -20,12 +20,12 @@
   @#C11
   get a0() → ffi::Array<ffi::Array<ffi::Array<ffi::Uint8>>>
     return new ffi::Array::_<ffi::Array<ffi::Array<ffi::Uint8>>>( block {
-      core::Object #typedDataBase = this.{ffi::Struct::_typedDataBase};
+      core::Object #typedDataBase = this.{ffi::_Compound::_typedDataBase};
       core::int #offset = (#C13).{core::List::[]}(ffi::_abi());
     } =>#typedDataBase is ffi::Pointer<dynamic> ?{core::Object} ffi::_fromAddress<ffi::Array<ffi::Array<ffi::Uint8>>>(#typedDataBase.{ffi::Pointer::address}.{core::num::+}(#offset)) : let typ::TypedData #typedData = _in::unsafeCast<typ::TypedData>(#typedDataBase) in #typedData.{typ::TypedData::buffer}.{typ::ByteBuffer::asUint8List}(#typedData.{typ::TypedData::offsetInBytes}.{core::num::+}(#offset), (#C9).{core::List::[]}(ffi::_abi())), #C10, #C14);
   @#C11
   set a0(ffi::Array<ffi::Array<ffi::Array<ffi::Uint8>>> #externalFieldValue) → void
-    return ffi::_memCopy(this.{ffi::Struct::_typedDataBase}, (#C13).{core::List::[]}(ffi::_abi()), #externalFieldValue.{ffi::Array::_typedDataBase}, #C12, (#C9).{core::List::[]}(ffi::_abi()));
+    return ffi::_memCopy(this.{ffi::_Compound::_typedDataBase}, (#C13).{core::List::[]}(ffi::_abi()), #externalFieldValue.{ffi::Array::_typedDataBase}, #C12, (#C9).{core::List::[]}(ffi::_abi()));
 }
 static method main() → dynamic {
   final ffi::Pointer<self::StructInlineArrayMultiDimensional> pointer = (#C15).{ffi::Allocator::allocate}<self::StructInlineArrayMultiDimensional>(self::StructInlineArrayMultiDimensional::#sizeOf);
diff --git a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.weak.transformed.expect b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.weak.transformed.expect
index f6b5c52..cff35ac 100644
--- a/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.weak.transformed.expect
+++ b/pkg/front_end/testcases/nnbd/ffi_struct_inline_array_multi_dimensional.dart.weak.transformed.expect
@@ -20,12 +20,12 @@
   @#C11
   get a0() → ffi::Array<ffi::Array<ffi::Array<ffi::Uint8>>>
     return new ffi::Array::_<ffi::Array<ffi::Array<ffi::Uint8>>>( block {
-      core::Object #typedDataBase = this.{ffi::Struct::_typedDataBase};
+      core::Object #typedDataBase = this.{ffi::_Compound::_typedDataBase};
       core::int #offset = (#C13).{core::List::[]}(ffi::_abi());
     } =>#typedDataBase is ffi::Pointer<dynamic> ?{core::Object} ffi::_fromAddress<ffi::Array<ffi::Array<ffi::Uint8>>>(#typedDataBase.{ffi::Pointer::address}.{core::num::+}(#offset)) : let typ::TypedData #typedData = _in::unsafeCast<typ::TypedData>(#typedDataBase) in #typedData.{typ::TypedData::buffer}.{typ::ByteBuffer::asUint8List}(#typedData.{typ::TypedData::offsetInBytes}.{core::num::+}(#offset), (#C9).{core::List::[]}(ffi::_abi())), #C10, #C14);
   @#C11
   set a0(ffi::Array<ffi::Array<ffi::Array<ffi::Uint8>>> #externalFieldValue) → void
-    return ffi::_memCopy(this.{ffi::Struct::_typedDataBase}, (#C13).{core::List::[]}(ffi::_abi()), #externalFieldValue.{ffi::Array::_typedDataBase}, #C12, (#C9).{core::List::[]}(ffi::_abi()));
+    return ffi::_memCopy(this.{ffi::_Compound::_typedDataBase}, (#C13).{core::List::[]}(ffi::_abi()), #externalFieldValue.{ffi::Array::_typedDataBase}, #C12, (#C9).{core::List::[]}(ffi::_abi()));
 }
 static method main() → dynamic {
   final ffi::Pointer<self::StructInlineArrayMultiDimensional> pointer = (#C15).{ffi::Allocator::allocate}<self::StructInlineArrayMultiDimensional>(self::StructInlineArrayMultiDimensional::#sizeOf);
diff --git a/pkg/kernel/lib/default_language_version.dart b/pkg/kernel/lib/default_language_version.dart
index 6de3073..135db61 100644
--- a/pkg/kernel/lib/default_language_version.dart
+++ b/pkg/kernel/lib/default_language_version.dart
@@ -9,4 +9,4 @@
 
 import "ast.dart";
 
-Version defaultLanguageVersion = const Version(2, 13);
+Version defaultLanguageVersion = const Version(2, 14);
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));
+  }
 }
diff --git a/pkg/vm/lib/transformations/ffi.dart b/pkg/vm/lib/transformations/ffi.dart
index 551ee3a..bd70fef 100644
--- a/pkg/vm/lib/transformations/ffi.dart
+++ b/pkg/vm/lib/transformations/ffi.dart
@@ -229,6 +229,7 @@
   final Field arraySizeDimension5Field;
   final Field arraySizeDimensionsField;
   final Class pointerClass;
+  final Class compoundClass;
   final Class structClass;
   final Class ffiStructLayoutClass;
   final Field ffiStructLayoutTypesField;
@@ -254,7 +255,7 @@
   final Procedure sizeOfMethod;
   final Procedure lookupFunctionMethod;
   final Procedure fromFunctionMethod;
-  final Field structTypedDataBaseField;
+  final Field compoundTypedDataBaseField;
   final Field arrayTypedDataBaseField;
   final Field arraySizeField;
   final Field arrayNestedDimensionsField;
@@ -330,6 +331,7 @@
         arraySizeDimensionsField =
             index.getMember('dart:ffi', '_ArraySize', 'dimensions'),
         pointerClass = index.getClass('dart:ffi', 'Pointer'),
+        compoundClass = index.getClass('dart:ffi', '_Compound'),
         structClass = index.getClass('dart:ffi', 'Struct'),
         ffiStructLayoutClass = index.getClass('dart:ffi', '_FfiStructLayout'),
         ffiStructLayoutTypesField =
@@ -351,8 +353,8 @@
         offsetByMethod = index.getMember('dart:ffi', 'Pointer', '_offsetBy'),
         elementAtMethod = index.getMember('dart:ffi', 'Pointer', 'elementAt'),
         addressGetter = index.getMember('dart:ffi', 'Pointer', 'get:address'),
-        structTypedDataBaseField =
-            index.getMember('dart:ffi', 'Struct', '_typedDataBase'),
+        compoundTypedDataBaseField =
+            index.getMember('dart:ffi', '_Compound', '_typedDataBase'),
         arrayTypedDataBaseField =
             index.getMember('dart:ffi', 'Array', '_typedDataBase'),
         arraySizeField = index.getMember('dart:ffi', 'Array', '_size'),
@@ -459,8 +461,7 @@
   /// [NativeFunction]<T1 Function(T2, T3) -> S1 Function(S2, S3)
   ///    where DartRepresentationOf(Tn) -> Sn
   DartType convertNativeTypeToDartType(DartType nativeType,
-      {bool allowStructs = false,
-      bool allowStructItself = false,
+      {bool allowCompounds = false,
       bool allowHandle = false,
       bool allowInlineArray = false}) {
     if (nativeType is! InterfaceType) {
@@ -476,11 +477,11 @@
       }
       return nativeType;
     }
-    if (hierarchy.isSubclassOf(nativeClass, structClass)) {
-      if (structClass == nativeClass) {
-        return allowStructItself ? nativeType : null;
+    if (hierarchy.isSubclassOf(nativeClass, compoundClass)) {
+      if (nativeClass == structClass) {
+        return null;
       }
-      return allowStructs ? nativeType : null;
+      return allowCompounds ? nativeType : null;
     }
     if (nativeType_ == null) {
       return null;
@@ -514,11 +515,11 @@
     if (fun.typeParameters.length != 0) return null;
 
     final DartType returnType = convertNativeTypeToDartType(fun.returnType,
-        allowStructs: allowStructs, allowHandle: true);
+        allowCompounds: true, allowHandle: true);
     if (returnType == null) return null;
     final List<DartType> argumentTypes = fun.positionalParameters
         .map((t) => convertNativeTypeToDartType(t,
-            allowStructs: allowStructs, allowHandle: true))
+            allowCompounds: true, allowHandle: true))
         .toList();
     if (argumentTypes.contains(null)) return null;
     return FunctionType(argumentTypes, returnType, Nullability.legacy);
@@ -740,7 +741,7 @@
     return dimensions;
   }
 
-  bool isStructSubtype(DartType type) {
+  bool isCompoundSubtype(DartType type) {
     if (type is InvalidType) {
       return false;
     }
@@ -762,9 +763,9 @@
 class FfiTransformerData {
   final Map<Field, Procedure> replacedGetters;
   final Map<Field, Procedure> replacedSetters;
-  final Set<Class> emptyStructs;
+  final Set<Class> emptyCompounds;
   FfiTransformerData(
-      this.replacedGetters, this.replacedSetters, this.emptyStructs);
+      this.replacedGetters, this.replacedSetters, this.emptyCompounds);
 }
 
 /// Checks if any library depends on dart:ffi.
diff --git a/pkg/vm/lib/transformations/ffi_definitions.dart b/pkg/vm/lib/transformations/ffi_definitions.dart
index ccf8b9d..732a676 100644
--- a/pkg/vm/lib/transformations/ffi_definitions.dart
+++ b/pkg/vm/lib/transformations/ffi_definitions.dart
@@ -34,7 +34,7 @@
 
 import 'ffi.dart';
 
-/// Checks and elaborates the dart:ffi structs and fields.
+/// Checks and elaborates the dart:ffi compounds and their fields.
 ///
 /// Input:
 /// class Coord extends Struct {
@@ -87,30 +87,30 @@
   libraries.forEach(transformer.visitLibrary);
   transformer.manualVisitInTopologicalOrder();
   return FfiTransformerData(transformer.replacedGetters,
-      transformer.replacedSetters, transformer.emptyStructs);
+      transformer.replacedSetters, transformer.emptyCompounds);
 }
 
-class StructDependencyGraph<T> implements Graph<T> {
+class CompoundDependencyGraph<T> implements Graph<T> {
   final Map<T, Iterable<T>> map;
-  StructDependencyGraph(this.map);
+  CompoundDependencyGraph(this.map);
 
   Iterable<T> get vertices => map.keys;
   Iterable<T> neighborsOf(T vertex) => map[vertex];
 }
 
-/// Checks and elaborates the dart:ffi structs and fields.
+/// Checks and elaborates the dart:ffi compounds and their fields.
 class _FfiDefinitionTransformer extends FfiTransformer {
   final LibraryIndex index;
 
   // Data structures for topological navigation.
-  Map<Class, IndexedClass> indexedStructClasses = {};
-  Map<Class, Set<Class>> structClassDependencies = {};
+  Map<Class, IndexedClass> indexedCompoundClasses = {};
+  Map<Class, Set<Class>> compoundClassDependencies = {};
   Map<Class, bool> fieldsValid = {};
-  Map<Class, StructNativeTypeCfe> structCache = {};
+  Map<Class, CompoundNativeTypeCfe> compoundCache = {};
 
   Map<Field, Procedure> replacedGetters = {};
   Map<Field, Procedure> replacedSetters = {};
-  Set<Class> emptyStructs = {};
+  Set<Class> emptyCompounds = {};
 
   ChangedStructureNotifier changedStructureNotifier;
 
@@ -127,8 +127,8 @@
             referenceFromIndex) {}
 
   void manualVisitInTopologicalOrder() {
-    final connectedComponents =
-        computeStrongComponents(StructDependencyGraph(structClassDependencies));
+    final connectedComponents = computeStrongComponents(
+        CompoundDependencyGraph(compoundClassDependencies));
 
     connectedComponents.forEach((List<Class> component) {
       bool report = false;
@@ -137,7 +137,7 @@
         report = true;
       }
       if (component.length == 1) {
-        if (structClassDependencies[component.single]
+        if (compoundClassDependencies[component.single]
             .contains(component.single)) {
           // Direct cycle.
           report = true;
@@ -173,15 +173,17 @@
 
   @override
   visitClass(Class node) {
-    if (!hierarchy.isSubclassOf(node, structClass) || node == structClass) {
+    if (!hierarchy.isSubclassOf(node, compoundClass) ||
+        node == compoundClass ||
+        node == structClass) {
       return node;
     }
 
-    final packing = _checkStructClass(node);
+    final packing = _checkCompoundClass(node);
 
     final indexedClass = currentLibraryIndex?.lookupIndexedClass(node.name);
     _checkConstructors(node, indexedClass);
-    indexedStructClasses[node] = indexedClass;
+    indexedCompoundClasses[node] = indexedClass;
 
     fieldsValid[node] = _checkFieldAnnotations(node, packing);
 
@@ -189,16 +191,16 @@
   }
 
   void visitClassInTopologicalOrder(Class node) {
-    final indexedClass = indexedStructClasses[node];
+    final indexedClass = indexedCompoundClasses[node];
     if (fieldsValid[node]) {
-      final structSize = _replaceFields(node, indexedClass);
-      _replaceSizeOfMethod(node, structSize, indexedClass);
+      final compoundSize = _replaceFields(node, indexedClass);
+      _replaceSizeOfMethod(node, compoundSize, indexedClass);
       changedStructureNotifier?.registerClassMemberChange(node);
     }
   }
 
   /// Returns packing if any.
-  int _checkStructClass(Class node) {
+  int _checkCompoundClass(Class node) {
     if (node.typeParameters.length > 0) {
       diagnosticReporter.report(
           templateFfiStructGeneric.withArguments(node.name),
@@ -207,7 +209,7 @@
           node.location.file);
     }
 
-    if (node.supertype?.classNode != structClass) {
+    if (node.superclass != structClass) {
       // Not a struct, but extends a struct. The error will be emitted by
       // _FfiUseSiteTransformer.
       return null;
@@ -233,17 +235,18 @@
       }
       return packing;
     }
+
     return null;
   }
 
-  /// Returns members of [node] that correspond to struct fields.
+  /// Returns members of [node] that correspond to compound fields.
   ///
   /// Note that getters and setters that originate from an external field have
   /// the same `fileOffset`, we always returns getters first.
-  List<Member> _structFieldMembers(Class node) {
+  List<Member> _compoundFieldMembers(Class node) {
     final externalGetterSetters = [...node.procedures]
       ..retainWhere((p) => p.isExternal && (p.isGetter || p.isSetter));
-    final structMembers = [...node.fields, ...externalGetterSetters]
+    final compoundMembers = [...node.fields, ...externalGetterSetters]
       ..sort((m1, m2) {
         if (m1.fileOffset == m2.fileOffset) {
           // Getter and setter have same offset, getter comes first.
@@ -251,10 +254,10 @@
         }
         return m1.fileOffset - m2.fileOffset;
       });
-    return structMembers;
+    return compoundMembers;
   }
 
-  DartType _structFieldMemberType(Member member) {
+  DartType _compoundFieldMemberType(Member member) {
     if (member is Field) {
       return member.type;
     }
@@ -267,8 +270,8 @@
 
   bool _checkFieldAnnotations(Class node, int packing) {
     bool success = true;
-    structClassDependencies[node] = {};
-    final membersWithAnnotations = _structFieldMembers(node)
+    compoundClassDependencies[node] = {};
+    final membersWithAnnotations = _compoundFieldMembers(node)
       ..retainWhere((m) => (m is Field) || (m is Procedure && m.isGetter));
     for (final Member f in membersWithAnnotations) {
       if (f is Field) {
@@ -283,7 +286,7 @@
         }
       }
       final nativeTypeAnnos = _getNativeTypeAnnotations(f).toList();
-      final type = _structFieldMemberType(f);
+      final type = _compoundFieldMemberType(f);
       if (type is NullType) {
         diagnosticReporter.report(
             templateFfiFieldNull.withArguments(f.name.text),
@@ -293,7 +296,7 @@
         // This class is invalid, but continue reporting other errors on it.
         success = false;
       } else if (isPointerType(type) ||
-          isStructSubtype(type) ||
+          isCompoundSubtype(type) ||
           isArrayType(type)) {
         if (nativeTypeAnnos.length != 0) {
           diagnosticReporter.report(
@@ -304,17 +307,17 @@
           // This class is invalid, but continue reporting other errors on it.
           success = false;
         }
-        if (isStructSubtype(type)) {
+        if (isCompoundSubtype(type)) {
           final clazz = (type as InterfaceType).classNode;
-          structClassDependencies[node].add(clazz);
+          compoundClassDependencies[node].add(clazz);
           _checkPacking(node, packing, clazz, f);
         } else if (isArrayType(type)) {
           final sizeAnnotations = _getArraySizeAnnotations(f);
           if (sizeAnnotations.length == 1) {
             final singleElementType = arraySingleElementType(type);
-            if (isStructSubtype(singleElementType)) {
+            if (isCompoundSubtype(singleElementType)) {
               final clazz = (singleElementType as InterfaceType).classNode;
-              structClassDependencies[node].add(clazz);
+              compoundClassDependencies[node].add(clazz);
               _checkPacking(node, packing, clazz, f);
             }
             if (arrayDimensions(type) != sizeAnnotations.single.length) {
@@ -348,7 +351,7 @@
             Nullability.legacy);
         final DartType shouldBeDartType = convertNativeTypeToDartType(
             nativeType,
-            allowStructs: true,
+            allowCompounds: true,
             allowHandle: false);
         if (shouldBeDartType == null ||
             !env.isSubtypeOf(type, shouldBeDartType,
@@ -450,10 +453,10 @@
     node.addConstructor(ctor);
   }
 
-  /// Computes the field offsets (for all ABIs) in the struct and replaces the
-  /// fields with getters and setters using these offsets.
+  /// Computes the field offsets (for all ABIs) in the compound and replaces
+  /// the fields with getters and setters using these offsets.
   ///
-  /// Returns the total size of the struct (for all ABIs).
+  /// Returns the total size of the compound (for all ABIs).
   Map<Abi, int> _replaceFields(Class node, IndexedClass indexedClass) {
     final types = <NativeTypeCfe>[];
     final fields = <int, Field>{};
@@ -461,8 +464,8 @@
     final setters = <int, Procedure>{};
 
     int i = 0;
-    for (final Member m in _structFieldMembers(node)) {
-      final dartType = _structFieldMemberType(m);
+    for (final Member m in _compoundFieldMembers(node)) {
+      final dartType = _compoundFieldMemberType(m);
 
       NativeTypeCfe type;
       if (isArrayType(dartType)) {
@@ -470,10 +473,10 @@
         if (sizeAnnotations.length == 1) {
           final arrayDimensions = sizeAnnotations.single;
           type = NativeTypeCfe(this, dartType,
-              structCache: structCache, arrayDimensions: arrayDimensions);
+              compoundCache: compoundCache, arrayDimensions: arrayDimensions);
         }
-      } else if (isPointerType(dartType) || isStructSubtype(dartType)) {
-        type = NativeTypeCfe(this, dartType, structCache: structCache);
+      } else if (isPointerType(dartType) || isCompoundSubtype(dartType)) {
+        type = NativeTypeCfe(this, dartType, compoundCache: compoundCache);
       } else {
         // The C type is in the annotation, not the field type itself.
         final nativeTypeAnnos = _getNativeTypeAnnotations(m).toList();
@@ -506,21 +509,21 @@
     final packing =
         (!packingAnnotations.isEmpty) ? packingAnnotations.first : null;
 
-    _annoteStructWithFields(node, types, packing);
+    _annoteCompoundWithFields(node, types, packing);
     if (types.isEmpty) {
       diagnosticReporter.report(templateFfiEmptyStruct.withArguments(node.name),
           node.fileOffset, node.name.length, node.location.file);
-      emptyStructs.add(node);
+      emptyCompounds.add(node);
     }
 
-    final structType = StructNativeTypeCfe(node, types, packing: packing);
-    structCache[node] = structType;
-    final structLayout = structType.layout;
+    final compoundType = StructNativeTypeCfe(node, types, packing: packing);
+    compoundCache[node] = compoundType;
+    final compoundLayout = compoundType.layout;
 
     final unalignedAccess = packing != null;
     for (final i in fields.keys) {
-      final fieldOffsets = structLayout
-          .map((Abi abi, StructLayout v) => MapEntry(abi, v.offsets[i]));
+      final fieldOffsets = compoundLayout
+          .map((Abi abi, CompoundLayout v) => MapEntry(abi, v.offsets[i]));
       final methods = _generateMethodsForField(
           fields[i], types[i], fieldOffsets, unalignedAccess, indexedClass);
       methods.forEach((p) => node.addProcedure(p));
@@ -531,8 +534,8 @@
     }
 
     for (final i in getters.keys) {
-      final fieldOffsets = structLayout
-          .map((Abi abi, StructLayout v) => MapEntry(abi, v.offsets[i]));
+      final fieldOffsets = compoundLayout
+          .map((Abi abi, CompoundLayout v) => MapEntry(abi, v.offsets[i]));
       Procedure getter = getters[i];
       getter.function.body = types[i].generateGetterStatement(
           getter.function.returnType,
@@ -544,8 +547,8 @@
     }
 
     for (final i in setters.keys) {
-      final fieldOffsets = structLayout
-          .map((Abi abi, StructLayout v) => MapEntry(abi, v.offsets[i]));
+      final fieldOffsets = compoundLayout
+          .map((Abi abi, CompoundLayout v) => MapEntry(abi, v.offsets[i]));
       Procedure setter = setters[i];
       setter.function.body = types[i].generateSetterStatement(
           setter.function.positionalParameters.single.type,
@@ -557,11 +560,11 @@
       setter.isExternal = false;
     }
 
-    return structLayout.map((k, v) => MapEntry(k, v.size));
+    return compoundLayout.map((k, v) => MapEntry(k, v.size));
   }
 
   // packing is `int?`.
-  void _annoteStructWithFields(
+  void _annoteCompoundWithFields(
       Class node, List<NativeTypeCfe> types, int packing) {
     List<Constant> constants =
         types.map((t) => t.generateConstant(this)).toList();
@@ -630,7 +633,7 @@
   /// Sample output:
   /// int #sizeOf => [24,24,16][_abi()];
   void _replaceSizeOfMethod(
-      Class struct, Map<Abi, int> sizes, IndexedClass indexedClass) {
+      Class compound, Map<Abi, int> sizes, IndexedClass indexedClass) {
     var name = Name("#sizeOf");
     var getterReference = indexedClass?.lookupGetterReference(name);
     final Field sizeOf = Field.immutable(name,
@@ -638,10 +641,10 @@
         isFinal: true,
         initializer: runtimeBranchOnLayout(sizes),
         type: InterfaceType(intClass, Nullability.legacy),
-        fileUri: struct.fileUri,
+        fileUri: compound.fileUri,
         getterReference: getterReference)
-      ..fileOffset = struct.fileOffset;
-    struct.addField(sizeOf);
+      ..fileOffset = compound.fileOffset;
+    compound.addField(sizeOf);
   }
 
   NativeType _getFieldType(Class c) {
@@ -712,18 +715,20 @@
   }
 }
 
-/// The layout of a `Struct` in one [Abi].
-class StructLayout {
-  /// Size of the entire struct.
+/// The layout of a `Struct` or `Union` in one [Abi].
+class CompoundLayout {
+  /// Size of the entire struct or union.
   final int size;
 
-  /// Alignment of struct when nested in other struct.
+  /// Alignment of struct or union when nested in a struct.
   final int alignment;
 
   /// Offset in bytes for each field, indexed by field number.
+  ///
+  /// Always 0 for unions.
   final List<int> offsets;
 
-  StructLayout(this.size, this.alignment, this.offsets);
+  CompoundLayout(this.size, this.alignment, this.offsets);
 }
 
 /// AST node wrapper for native types.
@@ -733,7 +738,7 @@
 abstract class NativeTypeCfe {
   factory NativeTypeCfe(FfiTransformer transformer, DartType dartType,
       {List<int> arrayDimensions,
-      Map<Class, StructNativeTypeCfe> structCache = const {}}) {
+      Map<Class, CompoundNativeTypeCfe> compoundCache = const {}}) {
     if (transformer.isPrimitiveType(dartType)) {
       final clazz = (dartType as InterfaceType).classNode;
       final nativeType = transformer.getType(clazz);
@@ -742,12 +747,12 @@
     if (transformer.isPointerType(dartType)) {
       return PointerNativeTypeCfe();
     }
-    if (transformer.isStructSubtype(dartType)) {
+    if (transformer.isCompoundSubtype(dartType)) {
       final clazz = (dartType as InterfaceType).classNode;
-      if (structCache.containsKey(clazz)) {
-        return structCache[clazz];
+      if (compoundCache.containsKey(clazz)) {
+        return compoundCache[clazz];
       } else {
-        throw "$clazz not found in structCache";
+        throw "$clazz not found in compoundCache";
       }
     }
     if (transformer.isArrayType(dartType)) {
@@ -756,7 +761,7 @@
       }
       final elementType = transformer.arraySingleElementType(dartType);
       final elementCfeType =
-          NativeTypeCfe(transformer, elementType, structCache: structCache);
+          NativeTypeCfe(transformer, elementType, compoundCache: compoundCache);
       return ArrayNativeTypeCfe.multi(elementCfeType, arrayDimensions);
     }
     throw "Invalid type $dartType";
@@ -777,13 +782,13 @@
   /// See runtime/vm/compiler/ffi/native_type.cc:NativeType::FromAbstractType.
   Constant generateConstant(FfiTransformer transformer);
 
-  /// Generates the return statement for a struct field getter with this type.
+  /// Generates the return statement for a compound field getter with this type.
   ///
   /// Takes [transformer] to be able to lookup classes and methods.
   ReturnStatement generateGetterStatement(DartType dartType, int fileOffset,
       Map<Abi, int> offsets, bool unalignedAccess, FfiTransformer transformer);
 
-  /// Generates the return statement for a struct field setter with this type.
+  /// Generates the return statement for a compound field setter with this type.
   ///
   /// Takes [transformer] to be able to lookup classes and methods.
   ReturnStatement generateSetterStatement(
@@ -853,8 +858,8 @@
           Arguments([
             PropertyGet(
                 ThisExpression(),
-                transformer.structTypedDataBaseField.name,
-                transformer.structTypedDataBaseField)
+                transformer.compoundTypedDataBaseField.name,
+                transformer.compoundTypedDataBaseField)
               ..fileOffset = fileOffset,
             transformer.runtimeBranchOnLayout(offsets)
           ]))
@@ -880,8 +885,8 @@
           Arguments([
             PropertyGet(
                 ThisExpression(),
-                transformer.structTypedDataBaseField.name,
-                transformer.structTypedDataBaseField)
+                transformer.compoundTypedDataBaseField.name,
+                transformer.compoundTypedDataBaseField)
               ..fileOffset = fileOffset,
             transformer.runtimeBranchOnLayout(offsets),
             VariableGet(argument)
@@ -923,8 +928,8 @@
                 Arguments([
                   PropertyGet(
                       ThisExpression(),
-                      transformer.structTypedDataBaseField.name,
-                      transformer.structTypedDataBaseField)
+                      transformer.compoundTypedDataBaseField.name,
+                      transformer.compoundTypedDataBaseField)
                     ..fileOffset = fileOffset,
                   transformer.runtimeBranchOnLayout(offsets)
                 ]))
@@ -952,8 +957,8 @@
           Arguments([
             PropertyGet(
                 ThisExpression(),
-                transformer.structTypedDataBaseField.name,
-                transformer.structTypedDataBaseField)
+                transformer.compoundTypedDataBaseField.name,
+                transformer.compoundTypedDataBaseField)
               ..fileOffset = fileOffset,
             transformer.runtimeBranchOnLayout(offsets),
             PropertyGet(VariableGet(argument), transformer.addressGetter.name,
@@ -963,46 +968,14 @@
         ..fileOffset = fileOffset);
 }
 
-class StructNativeTypeCfe implements NativeTypeCfe {
+abstract class CompoundNativeTypeCfe implements NativeTypeCfe {
   final Class clazz;
 
   final List<NativeTypeCfe> members;
 
-  // Nullable int.
-  final int packing;
+  final Map<Abi, CompoundLayout> layout;
 
-  final Map<Abi, StructLayout> layout;
-
-  factory StructNativeTypeCfe(Class clazz, List<NativeTypeCfe> members,
-      {int packing}) {
-    final layout = Map.fromEntries(Abi.values
-        .map((abi) => MapEntry(abi, _calculateLayout(members, packing, abi))));
-    return StructNativeTypeCfe._(clazz, members, packing, layout);
-  }
-
-  // Keep consistent with runtime/vm/compiler/ffi/native_type.cc
-  // NativeCompoundType::FromNativeTypes.
-  static StructLayout _calculateLayout(
-      List<NativeTypeCfe> types, int packing, Abi abi) {
-    int offset = 0;
-    final offsets = <int>[];
-    int structAlignment = 1;
-    for (int i = 0; i < types.length; i++) {
-      final int size = types[i].size[abi];
-      int alignment = types[i].alignment[abi];
-      if (packing != null && packing < alignment) {
-        alignment = packing;
-      }
-      offset = _alignOffset(offset, alignment);
-      offsets.add(offset);
-      offset += size;
-      structAlignment = math.max(structAlignment, alignment);
-    }
-    final int size = _alignOffset(offset, structAlignment);
-    return StructLayout(size, structAlignment, offsets);
-  }
-
-  StructNativeTypeCfe._(this.clazz, this.members, this.packing, this.layout);
+  CompoundNativeTypeCfe._(this.clazz, this.members, this.layout);
 
   @override
   Map<Abi, int> get size =>
@@ -1035,8 +1008,8 @@
           transformer.typedDataBaseOffset(
               PropertyGet(
                   ThisExpression(),
-                  transformer.structTypedDataBaseField.name,
-                  transformer.structTypedDataBaseField)
+                  transformer.compoundTypedDataBaseField.name,
+                  transformer.compoundTypedDataBaseField)
                 ..fileOffset = fileOffset,
               transformer.runtimeBranchOnLayout(offsets),
               transformer.runtimeBranchOnLayout(size),
@@ -1064,14 +1037,14 @@
           Arguments([
             PropertyGet(
                 ThisExpression(),
-                transformer.structTypedDataBaseField.name,
-                transformer.structTypedDataBaseField)
+                transformer.compoundTypedDataBaseField.name,
+                transformer.compoundTypedDataBaseField)
               ..fileOffset = fileOffset,
             transformer.runtimeBranchOnLayout(offsets),
             PropertyGet(
                 VariableGet(argument),
-                transformer.structTypedDataBaseField.name,
-                transformer.structTypedDataBaseField)
+                transformer.compoundTypedDataBaseField.name,
+                transformer.compoundTypedDataBaseField)
               ..fileOffset = fileOffset,
             ConstantExpression(IntConstant(0)),
             transformer.runtimeBranchOnLayout(size),
@@ -1079,6 +1052,44 @@
         ..fileOffset = fileOffset);
 }
 
+class StructNativeTypeCfe extends CompoundNativeTypeCfe {
+  // Nullable int.
+  final int packing;
+
+  factory StructNativeTypeCfe(Class clazz, List<NativeTypeCfe> members,
+      {int packing}) {
+    final layout = Map.fromEntries(Abi.values
+        .map((abi) => MapEntry(abi, _calculateLayout(members, packing, abi))));
+    return StructNativeTypeCfe._(clazz, members, packing, layout);
+  }
+
+  StructNativeTypeCfe._(Class clazz, List<NativeTypeCfe> members, this.packing,
+      Map<Abi, CompoundLayout> layout)
+      : super._(clazz, members, layout);
+
+  // Keep consistent with runtime/vm/compiler/ffi/native_type.cc
+  // NativeStructType::FromNativeTypes.
+  static CompoundLayout _calculateLayout(
+      List<NativeTypeCfe> types, int packing, Abi abi) {
+    int offset = 0;
+    final offsets = <int>[];
+    int structAlignment = 1;
+    for (int i = 0; i < types.length; i++) {
+      final int size = types[i].size[abi];
+      int alignment = types[i].alignment[abi];
+      if (packing != null && packing < alignment) {
+        alignment = packing;
+      }
+      offset = _alignOffset(offset, alignment);
+      offsets.add(offset);
+      offset += size;
+      structAlignment = math.max(structAlignment, alignment);
+    }
+    final int size = _alignOffset(offset, structAlignment);
+    return CompoundLayout(size, structAlignment, offsets);
+  }
+}
+
 class ArrayNativeTypeCfe implements NativeTypeCfe {
   final NativeTypeCfe elementType;
   final int length;
@@ -1151,8 +1162,8 @@
           transformer.typedDataBaseOffset(
               PropertyGet(
                   ThisExpression(),
-                  transformer.structTypedDataBaseField.name,
-                  transformer.structTypedDataBaseField)
+                  transformer.compoundTypedDataBaseField.name,
+                  transformer.compoundTypedDataBaseField)
                 ..fileOffset = fileOffset,
               transformer.runtimeBranchOnLayout(offsets),
               transformer.runtimeBranchOnLayout(size),
@@ -1184,8 +1195,8 @@
           Arguments([
             PropertyGet(
                 ThisExpression(),
-                transformer.structTypedDataBaseField.name,
-                transformer.structTypedDataBaseField)
+                transformer.compoundTypedDataBaseField.name,
+                transformer.compoundTypedDataBaseField)
               ..fileOffset = fileOffset,
             transformer.runtimeBranchOnLayout(offsets),
             PropertyGet(
diff --git a/pkg/vm/lib/transformations/ffi_use_sites.dart b/pkg/vm/lib/transformations/ffi_use_sites.dart
index 20cfbe8..52075ce 100644
--- a/pkg/vm/lib/transformations/ffi_use_sites.dart
+++ b/pkg/vm/lib/transformations/ffi_use_sites.dart
@@ -35,7 +35,7 @@
         UNKNOWN,
         wordSize;
 
-/// Checks and replaces calls to dart:ffi struct fields and methods.
+/// Checks and replaces calls to dart:ffi compound fields and methods.
 void transformLibraries(
     Component component,
     CoreTypes coreTypes,
@@ -64,15 +64,15 @@
       referenceFromIndex,
       ffiTransformerData.replacedGetters,
       ffiTransformerData.replacedSetters,
-      ffiTransformerData.emptyStructs);
+      ffiTransformerData.emptyCompounds);
   libraries.forEach(transformer.visitLibrary);
 }
 
-/// Checks and replaces calls to dart:ffi struct fields and methods.
+/// Checks and replaces calls to dart:ffi compound fields and methods.
 class _FfiUseSiteTransformer extends FfiTransformer {
   final Map<Field, Procedure> replacedGetters;
   final Map<Field, Procedure> replacedSetters;
-  final Set<Class> emptyStructs;
+  final Set<Class> emptyCompounds;
   StaticTypeContext _staticTypeContext;
 
   bool get isFfiLibrary => currentLibrary == ffiLibrary;
@@ -89,7 +89,7 @@
       ReferenceFromIndex referenceFromIndex,
       this.replacedGetters,
       this.replacedSetters,
-      this.emptyStructs)
+      this.emptyCompounds)
       : super(index, coreTypes, hierarchy, diagnosticReporter,
             referenceFromIndex) {}
 
@@ -179,33 +179,33 @@
       if (target == structPointerRef || target == structPointerElemAt) {
         final DartType nativeType = node.arguments.types[0];
 
-        _ensureNativeTypeValid(nativeType, node, allowStructItself: false);
+        _ensureNativeTypeValid(nativeType, node, allowCompounds: true);
 
         return _replaceRef(node);
       } else if (target == structArrayElemAt) {
         final DartType nativeType = node.arguments.types[0];
 
-        _ensureNativeTypeValid(nativeType, node, allowStructItself: false);
+        _ensureNativeTypeValid(nativeType, node, allowCompounds: true);
 
         return _replaceRefArray(node);
       } else if (target == arrayArrayElemAt) {
         final DartType nativeType = node.arguments.types[0];
 
         _ensureNativeTypeValid(nativeType, node,
-            allowStructItself: false, allowInlineArray: true);
+            allowInlineArray: true, allowCompounds: true);
 
         return _replaceArrayArrayElemAt(node);
       } else if (target == arrayArrayAssignAt) {
         final DartType nativeType = node.arguments.types[0];
 
         _ensureNativeTypeValid(nativeType, node,
-            allowStructItself: false, allowInlineArray: true);
+            allowInlineArray: true, allowCompounds: true);
 
         return _replaceArrayArrayElemAt(node, setter: true);
       } else if (target == sizeOfMethod) {
         final DartType nativeType = node.arguments.types[0];
 
-        _ensureNativeTypeValid(nativeType, node);
+        _ensureNativeTypeValid(nativeType, node, allowCompounds: true);
 
         if (nativeType is InterfaceType) {
           Expression inlineSizeOf = _inlineSizeOf(nativeType);
@@ -220,7 +220,7 @@
 
         _ensureNativeTypeValid(nativeType, node);
         _ensureNativeTypeToDartType(nativeType, dartType, node);
-        _ensureNoEmptyStructs(dartType, node);
+        _ensureNoEmptyCompounds(dartType, node);
 
         final replacement = _replaceLookupFunction(node);
 
@@ -229,7 +229,7 @@
           if (returnType is InterfaceType) {
             final clazz = returnType.classNode;
             if (clazz.superclass == structClass) {
-              return _invokeStructConstructor(replacement, clazz);
+              return _invokeCompoundConstructor(replacement, clazz);
             }
           }
         }
@@ -241,7 +241,7 @@
 
         _ensureNativeTypeValid(nativeType, node);
         _ensureNativeTypeToDartType(nativeType, dartType, node);
-        _ensureNoEmptyStructs(dartType, node);
+        _ensureNoEmptyCompounds(dartType, node);
 
         final DartType nativeSignature =
             (nativeType as InterfaceType).typeArguments[0];
@@ -257,7 +257,7 @@
           if (returnType is InterfaceType) {
             final clazz = returnType.classNode;
             if (clazz.superclass == structClass) {
-              return _invokeStructConstructor(replacement, clazz);
+              return _invokeCompoundConstructor(replacement, clazz);
             }
           }
         }
@@ -272,7 +272,7 @@
 
         _ensureNativeTypeValid(nativeType, node);
         _ensureNativeTypeToDartType(nativeType, dartType, node);
-        _ensureNoEmptyStructs(dartType, node);
+        _ensureNoEmptyCompounds(dartType, node);
 
         final funcType = dartType as FunctionType;
 
@@ -348,16 +348,16 @@
 
         final replacement = _replaceFromFunction(node);
 
-        final structClasses = funcType.positionalParameters
+        final compoundClasses = funcType.positionalParameters
             .whereType<InterfaceType>()
             .map((t) => t.classNode)
             .where((c) => c.superclass == structClass)
             .toList();
-        return _invokeStructConstructors(replacement, structClasses);
+        return _invokeCompoundConstructors(replacement, compoundClasses);
       } else if (target == allocateMethod) {
         final DartType nativeType = node.arguments.types[0];
 
-        _ensureNativeTypeValid(nativeType, node);
+        _ensureNativeTypeValid(nativeType, node, allowCompounds: true);
 
         // Inline the body to get rid of a generic invocation of sizeOf.
         // TODO(http://dartbug.com/39964): Add `allignmentOf<T>()` call.
@@ -388,9 +388,9 @@
 
   /// Prevents the struct from being tree-shaken in TFA by invoking its
   /// constructor in a `_nativeEffect` expression.
-  Expression _invokeStructConstructor(
-      Expression nestedExpression, Class compositeClass) {
-    final constructor = compositeClass.constructors
+  Expression _invokeCompoundConstructor(
+      Expression nestedExpression, Class compoundClass) {
+    final constructor = compoundClass.constructors
         .firstWhere((c) => c.name == Name("#fromTypedDataBase"));
     return BlockExpression(
         Block([
@@ -414,15 +414,17 @@
       ..fileOffset = nestedExpression.fileOffset;
   }
 
-  Expression _invokeStructConstructors(
-          Expression nestedExpression, List<Class> structClasses) =>
-      structClasses.distinct().fold(nestedExpression, _invokeStructConstructor);
+  Expression _invokeCompoundConstructors(
+          Expression nestedExpression, List<Class> compoundClasses) =>
+      compoundClasses
+          .distinct()
+          .fold(nestedExpression, _invokeCompoundConstructor);
 
   Expression _inlineSizeOf(InterfaceType nativeType) {
     final Class nativeClass = nativeType.classNode;
     final NativeType nt = getType(nativeClass);
     if (nt == null) {
-      // User-defined structs.
+      // User-defined compounds.
       Field sizeOfField = nativeClass.fields.single;
       return StaticGet(sizeOfField);
     }
@@ -699,7 +701,7 @@
             node.receiver.getStaticType(_staticTypeContext);
         final DartType nativeType = _pointerTypeGetTypeArg(pointerType);
 
-        _ensureNativeTypeValid(nativeType, node);
+        _ensureNativeTypeValid(nativeType, node, allowCompounds: true);
 
         Expression inlineSizeOf = _inlineSizeOf(nativeType);
         if (inlineSizeOf != null) {
@@ -735,7 +737,7 @@
       {bool allowHandle: false}) {
     final DartType correspondingDartType = convertNativeTypeToDartType(
         nativeType,
-        allowStructs: true,
+        allowCompounds: true,
         allowHandle: allowHandle);
     if (dartType == correspondingDartType) return;
     if (env.isSubtypeOf(correspondingDartType, dartType,
@@ -753,11 +755,10 @@
 
   void _ensureNativeTypeValid(DartType nativeType, Expression node,
       {bool allowHandle: false,
-      bool allowStructItself = true,
+      bool allowCompounds: false,
       bool allowInlineArray = false}) {
     if (!_nativeTypeValid(nativeType,
-        allowStructs: true,
-        allowStructItself: allowStructItself,
+        allowCompounds: allowCompounds,
         allowHandle: allowHandle,
         allowInlineArray: allowInlineArray)) {
       diagnosticReporter.report(
@@ -770,12 +771,12 @@
     }
   }
 
-  void _ensureNoEmptyStructs(DartType nativeType, Expression node) {
+  void _ensureNoEmptyCompounds(DartType nativeType, Expression node) {
     // Error on structs with no fields.
     if (nativeType is InterfaceType) {
       final Class nativeClass = nativeType.classNode;
-      if (hierarchy.isSubclassOf(nativeClass, structClass)) {
-        if (emptyStructs.contains(nativeClass)) {
+      if (hierarchy.isSubclassOf(nativeClass, compoundClass)) {
+        if (emptyCompounds.contains(nativeClass)) {
           diagnosticReporter.report(
               templateFfiEmptyStruct.withArguments(nativeClass.name),
               node.fileOffset,
@@ -788,21 +789,19 @@
     // Recurse when seeing a function type.
     if (nativeType is FunctionType) {
       nativeType.positionalParameters
-          .forEach((e) => _ensureNoEmptyStructs(e, node));
-      _ensureNoEmptyStructs(nativeType.returnType, node);
+          .forEach((e) => _ensureNoEmptyCompounds(e, node));
+      _ensureNoEmptyCompounds(nativeType.returnType, node);
     }
   }
 
   /// The Dart type system does not enforce that NativeFunction return and
   /// parameter types are only NativeTypes, so we need to check this.
   bool _nativeTypeValid(DartType nativeType,
-      {bool allowStructs: false,
-      bool allowStructItself = false,
+      {bool allowCompounds: false,
       bool allowHandle = false,
       bool allowInlineArray = false}) {
     return convertNativeTypeToDartType(nativeType,
-            allowStructs: allowStructs,
-            allowStructItself: allowStructItself,
+            allowCompounds: allowCompounds,
             allowHandle: allowHandle,
             allowInlineArray: allowInlineArray) !=
         null;
@@ -822,14 +821,14 @@
   }
 
   Class _extendsOrImplementsSealedClass(Class klass) {
-    final Class superClass = klass.supertype?.classNode;
+    final Class superClass = klass.superclass;
 
     // The Opaque and Struct classes can be extended, but subclasses
     // cannot be (nor implemented).
     if (klass != opaqueClass &&
         klass != structClass &&
         (hierarchy.isSubtypeOf(klass, opaqueClass) ||
-            hierarchy.isSubtypeOf(klass, structClass))) {
+            hierarchy.isSubtypeOf(klass, compoundClass))) {
       return superClass != opaqueClass && superClass != structClass
           ? superClass
           : null;
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/ffi_struct_constructors.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/ffi_struct_constructors.dart.expect
index 3469672..367f663 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/ffi_struct_constructors.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/ffi_struct_constructors.dart.expect
@@ -41,7 +41,7 @@
     ;
 [@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,getterSelectorId:1]  get nested() → self::Struct12*
     return new self::Struct12::#fromTypedDataBase( block {
-      core::Object #typedDataBase = [@vm.direct-call.metadata=dart.ffi::Struct._typedDataBase] this.{ffi::Struct::_typedDataBase};
+      core::Object #typedDataBase = [@vm.direct-call.metadata=dart.ffi::_Compound._typedDataBase] this.{ffi::_Compound::_typedDataBase};
       core::int #offset = (#C12).{core::List::[]}(ffi::_abi());
     } =>#typedDataBase is ffi::Pointer<dynamic> ?{core::Object} [@vm.inferred-type.metadata=dart.ffi::Pointer?] ffi::_fromAddress<self::Struct12*>([@vm.direct-call.metadata=dart.core::_IntegerImplementation.+??] [@vm.inferred-type.metadata=int (skip check)] [@vm.direct-call.metadata=dart.ffi::Pointer.address] [@vm.inferred-type.metadata=int?] #typedDataBase.{ffi::Pointer::address}.{core::num::+}(#offset)) : let typ::TypedData #typedData = _in::unsafeCast<typ::TypedData>(#typedDataBase) in [@vm.direct-call.metadata=dart.typed_data::_ByteBuffer.asUint8List] [@vm.inferred-type.metadata=dart.typed_data::_Uint8ArrayView (skip check)] [@vm.inferred-type.metadata=dart.typed_data::_ByteBuffer] #typedData.{typ::TypedData::buffer}.{typ::ByteBuffer::asUint8List}([@vm.direct-call.metadata=dart.core::_IntegerImplementation.+] [@vm.inferred-type.metadata=int (skip check)] [@vm.inferred-type.metadata=dart.core::_Smi] #typedData.{typ::TypedData::offsetInBytes}.{core::num::+}(#offset), (#C15).{core::List::[]}(ffi::_abi())));
 }
diff --git a/runtime/vm/compiler/ffi/marshaller.cc b/runtime/vm/compiler/ffi/marshaller.cc
index 480f045..dbaff91 100644
--- a/runtime/vm/compiler/ffi/marshaller.cc
+++ b/runtime/vm/compiler/ffi/marshaller.cc
@@ -390,7 +390,7 @@
 }
 
 bool CallMarshaller::PassTypedData() const {
-  return IsStruct(compiler::ffi::kResultIndex);
+  return IsCompound(compiler::ffi::kResultIndex);
 }
 
 intptr_t CallMarshaller::TypedDataSizeInBytes() const {
diff --git a/runtime/vm/compiler/ffi/marshaller.h b/runtime/vm/compiler/ffi/marshaller.h
index 657817e..4fafbe1 100644
--- a/runtime/vm/compiler/ffi/marshaller.h
+++ b/runtime/vm/compiler/ffi/marshaller.h
@@ -110,7 +110,7 @@
            kFfiHandleCid;
   }
 
-  bool IsStruct(intptr_t arg_index) const {
+  bool IsCompound(intptr_t arg_index) const {
     const auto& type = AbstractType::Handle(zone_, CType(arg_index));
     const bool predefined = IsFfiTypeClassId(type.type_class_id());
     return !predefined;
diff --git a/runtime/vm/compiler/frontend/kernel_to_il.cc b/runtime/vm/compiler/frontend/kernel_to_il.cc
index ed3fba1..5d294d8 100644
--- a/runtime/vm/compiler/frontend/kernel_to_il.cc
+++ b/runtime/vm/compiler/frontend/kernel_to_il.cc
@@ -3776,44 +3776,45 @@
   return Fragment(instr);
 }
 
-Fragment FlowGraphBuilder::WrapTypedDataBaseInStruct(
-    const AbstractType& struct_type) {
-  const auto& struct_sub_class = Class::ZoneHandle(Z, struct_type.type_class());
-  struct_sub_class.EnsureIsFinalized(thread_);
+Fragment FlowGraphBuilder::WrapTypedDataBaseInCompound(
+    const AbstractType& compound_type) {
+  const auto& compound_sub_class =
+      Class::ZoneHandle(Z, compound_type.type_class());
+  compound_sub_class.EnsureIsFinalized(thread_);
   const auto& lib_ffi = Library::Handle(Z, Library::FfiLibrary());
-  const auto& struct_class =
-      Class::Handle(Z, lib_ffi.LookupClass(Symbols::Struct()));
-  const auto& struct_addressof = Field::ZoneHandle(
-      Z,
-      struct_class.LookupInstanceFieldAllowPrivate(Symbols::_typedDataBase()));
-  ASSERT(!struct_addressof.IsNull());
+  const auto& compound_class =
+      Class::Handle(Z, lib_ffi.LookupClassAllowPrivate(Symbols::Compound()));
+  const auto& compound_typed_data_base =
+      Field::ZoneHandle(Z, compound_class.LookupInstanceFieldAllowPrivate(
+                               Symbols::_typedDataBase()));
+  ASSERT(!compound_typed_data_base.IsNull());
 
   Fragment body;
   LocalVariable* typed_data = MakeTemporary("typed_data_base");
-  body += AllocateObject(TokenPosition::kNoSource, struct_sub_class, 0);
-  body += LoadLocal(MakeTemporary("struct"));  // Duplicate Struct.
+  body += AllocateObject(TokenPosition::kNoSource, compound_sub_class, 0);
+  body += LoadLocal(MakeTemporary("compound"));  // Duplicate Struct or Union.
   body += LoadLocal(typed_data);
-  body += StoreInstanceField(struct_addressof,
+  body += StoreInstanceField(compound_typed_data_base,
                              StoreInstanceFieldInstr::Kind::kInitializing);
   body += DropTempsPreserveTop(1);  // Drop TypedData.
   return body;
 }
 
-Fragment FlowGraphBuilder::LoadTypedDataBaseFromStruct() {
-  const Library& lib_ffi = Library::Handle(zone_, Library::FfiLibrary());
-  const Class& struct_class =
-      Class::Handle(zone_, lib_ffi.LookupClass(Symbols::Struct()));
-  const Field& struct_addressof = Field::ZoneHandle(
-      zone_,
-      struct_class.LookupInstanceFieldAllowPrivate(Symbols::_typedDataBase()));
-  ASSERT(!struct_addressof.IsNull());
+Fragment FlowGraphBuilder::LoadTypedDataBaseFromCompound() {
+  const auto& lib_ffi = Library::Handle(Z, Library::FfiLibrary());
+  const auto& compound_class =
+      Class::Handle(Z, lib_ffi.LookupClassAllowPrivate(Symbols::Compound()));
+  const auto& compound_typed_data_base =
+      Field::ZoneHandle(Z, compound_class.LookupInstanceFieldAllowPrivate(
+                               Symbols::_typedDataBase()));
+  ASSERT(!compound_typed_data_base.IsNull());
 
   Fragment body;
-  body += LoadField(struct_addressof, /*calls_initializer=*/false);
+  body += LoadField(compound_typed_data_base, /*calls_initializer=*/false);
   return body;
 }
 
-Fragment FlowGraphBuilder::CopyFromStructToStack(
+Fragment FlowGraphBuilder::CopyFromCompoundToStack(
     LocalVariable* variable,
     const GrowableArray<Representation>& representations) {
   Fragment body;
@@ -3821,7 +3822,7 @@
   int offset_in_bytes = 0;
   for (intptr_t i = 0; i < num_defs; i++) {
     body += LoadLocal(variable);
-    body += LoadTypedDataBaseFromStruct();
+    body += LoadTypedDataBaseFromCompound();
     body += LoadUntagged(compiler::target::Pointer::data_field_offset());
     body += IntConstant(offset_in_bytes);
     const Representation representation = representations[i];
@@ -3959,7 +3960,7 @@
   return body;
 }
 
-Fragment FlowGraphBuilder::FfiCallConvertStructArgumentToNative(
+Fragment FlowGraphBuilder::FfiCallConvertCompoundArgumentToNative(
     LocalVariable* variable,
     const compiler::ffi::BaseMarshaller& marshaller,
     intptr_t arg_index) {
@@ -3970,29 +3971,29 @@
     // separate definitions into the FFI call.
     GrowableArray<Representation> representations;
     marshaller.RepsInFfiCall(arg_index, &representations);
-    body += CopyFromStructToStack(variable, representations);
+    body += CopyFromCompoundToStack(variable, representations);
   } else {
     ASSERT(native_loc.IsPointerToMemory());
     // Only load the typed data, do copying in the FFI call machine code.
     body += LoadLocal(variable);  // User-defined struct.
-    body += LoadTypedDataBaseFromStruct();
+    body += LoadTypedDataBaseFromCompound();
   }
   return body;
 }
 
-Fragment FlowGraphBuilder::FfiCallConvertStructReturnToDart(
+Fragment FlowGraphBuilder::FfiCallConvertCompoundReturnToDart(
     const compiler::ffi::BaseMarshaller& marshaller,
     intptr_t arg_index) {
   Fragment body;
   // The typed data is allocated before the FFI call, and is populated in
   // machine code. So, here, it only has to be wrapped in the struct class.
-  const auto& struct_type =
+  const auto& compound_type =
       AbstractType::Handle(Z, marshaller.CType(arg_index));
-  body += WrapTypedDataBaseInStruct(struct_type);
+  body += WrapTypedDataBaseInCompound(compound_type);
   return body;
 }
 
-Fragment FlowGraphBuilder::FfiCallbackConvertStructArgumentToDart(
+Fragment FlowGraphBuilder::FfiCallbackConvertCompoundArgumentToDart(
     const compiler::ffi::BaseMarshaller& marshaller,
     intptr_t arg_index,
     ZoneGrowableArray<LocalVariable*>* definitions) {
@@ -4012,24 +4013,24 @@
   } else {
     ASSERT(marshaller.Location(arg_index).IsPointerToMemory());
     // Allocate a TypedData and copy contents pointed to by an address into it.
-    LocalVariable* address_of_struct = MakeTemporary("address_of_struct");
+    LocalVariable* address_of_compound = MakeTemporary("address_of_compound");
     body += IntConstant(length_in_bytes);
     body +=
         AllocateTypedData(TokenPosition::kNoSource, kTypedDataUint8ArrayCid);
     LocalVariable* typed_data_base = MakeTemporary("typed_data_base");
-    body += LoadLocal(address_of_struct);
+    body += LoadLocal(address_of_compound);
     body += LoadLocal(typed_data_base);
     body += CopyFromUnboxedAddressToTypedDataBase(length_in_bytes);
-    body += DropTempsPreserveTop(1);  // address_of_struct.
+    body += DropTempsPreserveTop(1);  // address_of_compound.
   }
-  // Wrap typed data in struct class.
-  const auto& struct_type =
+  // Wrap typed data in compound class.
+  const auto& compound_type =
       AbstractType::Handle(Z, marshaller.CType(arg_index));
-  body += WrapTypedDataBaseInStruct(struct_type);
+  body += WrapTypedDataBaseInCompound(compound_type);
   return body;
 }
 
-Fragment FlowGraphBuilder::FfiCallbackConvertStructReturnToNative(
+Fragment FlowGraphBuilder::FfiCallbackConvertCompoundReturnToNative(
     const compiler::ffi::CallbackMarshaller& marshaller,
     intptr_t arg_index) {
   Fragment body;
@@ -4037,14 +4038,14 @@
   if (native_loc.IsMultiple()) {
     // We pass in typed data to native return instruction, and do the copying
     // in machine code.
-    body += LoadTypedDataBaseFromStruct();
+    body += LoadTypedDataBaseFromCompound();
   } else {
     ASSERT(native_loc.IsPointerToMemory());
     // We copy the data into the right location in IL.
     const intptr_t length_in_bytes =
         marshaller.Location(arg_index).payload_type().SizeInBytes();
 
-    body += LoadTypedDataBaseFromStruct();
+    body += LoadTypedDataBaseFromCompound();
     LocalVariable* typed_data_base = MakeTemporary("typed_data_base");
 
     auto* pointer_to_return =
@@ -4065,7 +4066,7 @@
 Fragment FlowGraphBuilder::FfiConvertPrimitiveToDart(
     const compiler::ffi::BaseMarshaller& marshaller,
     intptr_t arg_index) {
-  ASSERT(!marshaller.IsStruct(arg_index));
+  ASSERT(!marshaller.IsCompound(arg_index));
 
   Fragment body;
   if (marshaller.IsPointer(arg_index)) {
@@ -4093,7 +4094,7 @@
     const compiler::ffi::BaseMarshaller& marshaller,
     intptr_t arg_index,
     LocalVariable* api_local_scope) {
-  ASSERT(!marshaller.IsStruct(arg_index));
+  ASSERT(!marshaller.IsCompound(arg_index));
 
   Fragment body;
   if (marshaller.IsPointer(arg_index)) {
@@ -4195,8 +4196,8 @@
 
   // Unbox and push the arguments.
   for (intptr_t i = 0; i < marshaller.num_args(); i++) {
-    if (marshaller.IsStruct(i)) {
-      body += FfiCallConvertStructArgumentToNative(
+    if (marshaller.IsCompound(i)) {
+      body += FfiCallConvertCompoundArgumentToNative(
           parsed_function_->ParameterVariable(kFirstArgumentParameterOffset +
                                               i),
           marshaller, i);
@@ -4246,9 +4247,9 @@
     body += Drop();
   }
 
-  if (marshaller.IsStruct(compiler::ffi::kResultIndex)) {
-    body += FfiCallConvertStructReturnToDart(marshaller,
-                                             compiler::ffi::kResultIndex);
+  if (marshaller.IsCompound(compiler::ffi::kResultIndex)) {
+    body += FfiCallConvertCompoundReturnToDart(marshaller,
+                                               compiler::ffi::kResultIndex);
   } else {
     body += FfiConvertPrimitiveToDart(marshaller, compiler::ffi::kResultIndex);
   }
@@ -4322,8 +4323,8 @@
       defs->Add(def);
     }
 
-    if (marshaller.IsStruct(i)) {
-      body += FfiCallbackConvertStructArgumentToDart(marshaller, i, defs);
+    if (marshaller.IsCompound(i)) {
+      body += FfiCallbackConvertCompoundArgumentToDart(marshaller, i, defs);
     } else {
       body += FfiConvertPrimitiveToDart(marshaller, i);
     }
@@ -4346,9 +4347,9 @@
                            String::ZoneHandle(Z, marshaller.function_name()));
   }
 
-  if (marshaller.IsStruct(compiler::ffi::kResultIndex)) {
-    body += FfiCallbackConvertStructReturnToNative(marshaller,
-                                                   compiler::ffi::kResultIndex);
+  if (marshaller.IsCompound(compiler::ffi::kResultIndex)) {
+    body += FfiCallbackConvertCompoundReturnToNative(
+        marshaller, compiler::ffi::kResultIndex);
   } else {
     body += FfiConvertPrimitiveToNative(marshaller, compiler::ffi::kResultIndex,
                                         /*api_local_scope=*/nullptr);
@@ -4379,7 +4380,7 @@
         FfiConvertPrimitiveToNative(marshaller, compiler::ffi::kResultIndex,
                                     /*api_local_scope=*/nullptr);
 
-  } else if (marshaller.IsStruct(compiler::ffi::kResultIndex)) {
+  } else if (marshaller.IsCompound(compiler::ffi::kResultIndex)) {
     ASSERT(function.FfiCallbackExceptionalReturn() == Object::null());
     // Manufacture empty result.
     const intptr_t size =
@@ -4390,9 +4391,9 @@
     catch_body += IntConstant(size);
     catch_body +=
         AllocateTypedData(TokenPosition::kNoSource, kTypedDataUint8ArrayCid);
-    catch_body += WrapTypedDataBaseInStruct(
+    catch_body += WrapTypedDataBaseInCompound(
         AbstractType::Handle(Z, marshaller.CType(compiler::ffi::kResultIndex)));
-    catch_body += FfiCallbackConvertStructReturnToNative(
+    catch_body += FfiCallbackConvertCompoundReturnToNative(
         marshaller, compiler::ffi::kResultIndex);
 
   } else {
diff --git a/runtime/vm/compiler/frontend/kernel_to_il.h b/runtime/vm/compiler/frontend/kernel_to_il.h
index 7e93009..ced5185 100644
--- a/runtime/vm/compiler/frontend/kernel_to_il.h
+++ b/runtime/vm/compiler/frontend/kernel_to_il.h
@@ -293,44 +293,46 @@
       intptr_t arg_index);
 
   // We pass in `variable` instead of on top of the stack so that we can have
-  // multiple consecutive calls that keep only struct parts on the stack with
-  // no struct parts in between.
-  Fragment FfiCallConvertStructArgumentToNative(
+  // multiple consecutive calls that keep only compound parts on the stack with
+  // no compound parts in between.
+  Fragment FfiCallConvertCompoundArgumentToNative(
       LocalVariable* variable,
       const compiler::ffi::BaseMarshaller& marshaller,
       intptr_t arg_index);
 
-  Fragment FfiCallConvertStructReturnToDart(
+  Fragment FfiCallConvertCompoundReturnToDart(
       const compiler::ffi::BaseMarshaller& marshaller,
       intptr_t arg_index);
 
   // We pass in multiple `definitions`, which are also expected to be the top
-  // of the stack. This eases storing each definition in the resulting struct.
-  Fragment FfiCallbackConvertStructArgumentToDart(
+  // of the stack. This eases storing each definition in the resulting struct
+  // or union.
+  Fragment FfiCallbackConvertCompoundArgumentToDart(
       const compiler::ffi::BaseMarshaller& marshaller,
       intptr_t arg_index,
       ZoneGrowableArray<LocalVariable*>* definitions);
 
-  Fragment FfiCallbackConvertStructReturnToNative(
+  Fragment FfiCallbackConvertCompoundReturnToNative(
       const compiler::ffi::CallbackMarshaller& marshaller,
       intptr_t arg_index);
 
-  // Wraps a TypedDataBase from the stack and wraps it in a subclass of Struct.
-  Fragment WrapTypedDataBaseInStruct(const AbstractType& struct_type);
+  // Wraps a TypedDataBase from the stack and wraps it in a subclass of
+  // _Compound.
+  Fragment WrapTypedDataBaseInCompound(const AbstractType& compound_type);
 
-  // Loads the _typedDataBase field from a subclass of Struct.
-  Fragment LoadTypedDataBaseFromStruct();
+  // Loads the _typedDataBase field from a subclass of _Compound.
+  Fragment LoadTypedDataBaseFromCompound();
 
-  // Breaks up a subclass of Struct in multiple definitions and puts them on
+  // Breaks up a subclass of _Compound in multiple definitions and puts them on
   // the stack.
   //
-  // Takes in the Struct as a local `variable` so that can be anywhere on the
-  // stack and this function can be called multiple times to leave only the
-  // results of this function on the stack without any Structs in between.
+  // Takes in the _Compound as a local `variable` so that can be anywhere on
+  // the stack and this function can be called multiple times to leave only the
+  // results of this function on the stack without any _Compounds in between.
   //
-  // The struct contents are heterogeneous, so pass in `representations` to
-  // know what representation to load.
-  Fragment CopyFromStructToStack(
+  // The compound contents are heterogeneous, so pass in
+  // `representations` to know what representation to load.
+  Fragment CopyFromCompoundToStack(
       LocalVariable* variable,
       const GrowableArray<Representation>& representations);
 
@@ -340,7 +342,7 @@
   //
   // Leaves TypedData on stack.
   //
-  // The struct contents are heterogeneous, so pass in `representations` to
+  // The compound contents are heterogeneous, so pass in `representations` to
   // know what representation to load.
   Fragment PopFromStackToTypedDataBase(
       ZoneGrowableArray<LocalVariable*>* definitions,
diff --git a/runtime/vm/experimental_features.cc b/runtime/vm/experimental_features.cc
index 57eb309..1c79061 100644
--- a/runtime/vm/experimental_features.cc
+++ b/runtime/vm/experimental_features.cc
@@ -6,7 +6,7 @@
 // Instead modify 'tools/experimental_features.yaml' and run
 // 'dart tools/generate_experimental_flags.dart' to update.
 //
-// Current version: 2.13.0
+// Current version: 2.14.0
 
 #include "vm/experimental_features.h"
 
diff --git a/runtime/vm/experimental_features.h b/runtime/vm/experimental_features.h
index d6816b3..035af24 100644
--- a/runtime/vm/experimental_features.h
+++ b/runtime/vm/experimental_features.h
@@ -6,7 +6,7 @@
 // Instead modify 'tools/experimental_features.yaml' and run
 // 'dart tools/generate_experimental_flags.dart' to update.
 //
-// Current version: 2.13.0
+// Current version: 2.14.0
 
 #ifndef RUNTIME_VM_EXPERIMENTAL_FEATURES_H_
 #define RUNTIME_VM_EXPERIMENTAL_FEATURES_H_
diff --git a/runtime/vm/kernel_loader.cc b/runtime/vm/kernel_loader.cc
index fdff2a6..e5220d1 100644
--- a/runtime/vm/kernel_loader.cc
+++ b/runtime/vm/kernel_loader.cc
@@ -1661,7 +1661,7 @@
     // optimized. We immediately set the guarded_cid_ to kDynamicCid, which
     // is effectively the same as calling this method first with Pointer and
     // subsequently with TypedData with field guards.
-    if (klass.Name() == Symbols::Struct().ptr() &&
+    if (klass.Name() == Symbols::Compound().ptr() &&
         Library::Handle(Z, klass.library()).url() == Symbols::DartFfi().ptr()) {
       ASSERT(fields_.length() == 1);
       ASSERT(String::Handle(Z, fields_[0]->name())
diff --git a/runtime/vm/symbols.h b/runtime/vm/symbols.h
index 4921334..fd790e9 100644
--- a/runtime/vm/symbols.h
+++ b/runtime/vm/symbols.h
@@ -55,6 +55,7 @@
   V(CompleterFuture, "future")                                                 \
   V(CompleterGetFuture, "get:future")                                          \
   V(CompleterSyncConstructor, "Completer.sync")                                \
+  V(Compound, "_Compound")                                                     \
   V(CompressedStackMaps, "CompressedStackMaps")                                \
   V(ConstructorStacktracePrefix, "new ")                                       \
   V(Context, "Context")                                                        \
diff --git a/sdk/lib/ffi/struct.dart b/sdk/lib/ffi/struct.dart
index e4fce17..de45d6d 100644
--- a/sdk/lib/ffi/struct.dart
+++ b/sdk/lib/ffi/struct.dart
@@ -4,6 +4,19 @@
 
 part of dart.ffi;
 
+/// The supertype of all FFI compound types.
+///
+/// FFI struct types should extend [Struct]. For more information see the
+/// documentation on this class.
+abstract class _Compound extends NativeType {
+  @pragma("vm:entry-point")
+  final Object _typedDataBase;
+
+  _Compound._() : _typedDataBase = nullptr;
+
+  _Compound._fromTypedDataBase(this._typedDataBase);
+}
+
 /// The supertype of all FFI struct types.
 ///
 /// FFI struct types should extend this class and declare fields corresponding
@@ -47,17 +60,15 @@
 /// by native memory or typed data. They may allocated via allocation or loaded
 /// from a [Pointer] or created by ffi calls or callbacks. They cannot be
 /// created by a generative constructor.
-abstract class Struct extends NativeType {
-  @pragma("vm:entry-point")
-  final Object _typedDataBase;
-
+abstract class Struct extends _Compound {
   /// Construct a reference to the [nullptr].
   ///
   /// Use [StructPointer]'s `.ref` to gain references to native memory backed
   /// structs.
-  Struct() : _typedDataBase = nullptr;
+  Struct() : super._();
 
-  Struct._fromTypedDataBase(this._typedDataBase);
+  Struct._fromTypedDataBase(Object typedDataBase)
+      : super._fromTypedDataBase(typedDataBase);
 }
 
 /// Annotation to specify on `Struct` subtypes to indicate that its members
diff --git a/tools/VERSION b/tools/VERSION
index 18aba60..b12f608 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -25,7 +25,7 @@
 #
 CHANNEL dev
 MAJOR 2
-MINOR 13
+MINOR 14
 PATCH 0
-PRERELEASE 236
+PRERELEASE 0
 PRERELEASE_PATCH 0
\ No newline at end of file
diff --git a/tools/experimental_features.yaml b/tools/experimental_features.yaml
index 336213d..55dfb4d 100644
--- a/tools/experimental_features.yaml
+++ b/tools/experimental_features.yaml
@@ -103,7 +103,7 @@
 # default 'language' "category" with code generated for both CFE and Analyzer,
 # while other categories can be tailored more specifically.
 
-current-version: '2.13.0'
+current-version: '2.14.0'
 
 features:
   triple-shift: