diff --git a/AUTHORS b/AUTHORS index 6a6a41e28ba9..95956c51deff 100644 --- a/AUTHORS +++ b/AUTHORS @@ -38,3 +38,4 @@ Kenneth Endfinger Cristian Almstrand Ryan Macnak Gabriel Terwesten +Seif Shaheen diff --git a/pkg/_fe_analyzer_shared/lib/src/type_inference/type_analyzer_operations.dart b/pkg/_fe_analyzer_shared/lib/src/type_inference/type_analyzer_operations.dart index 27b474c4017c..871003e605e0 100644 --- a/pkg/_fe_analyzer_shared/lib/src/type_inference/type_analyzer_operations.dart +++ b/pkg/_fe_analyzer_shared/lib/src/type_inference/type_analyzer_operations.dart @@ -2051,6 +2051,21 @@ abstract class TypeConstraintGenerator< astNodeForTesting: astNodeForTesting, ); + case ( + TypeDeclarationMatchResult( + typeDeclarationKind: TypeDeclarationKind.extensionTypeDeclaration, + ), + _, + ): + return performSubtypeConstraintGenerationInternal( + typeAnalyzerOperations + .extensionTypeErasure(new SharedTypeView(p)) + .unwrapTypeView(), + q, + leftSchema: leftSchema, + astNodeForTesting: astNodeForTesting, + ); + case (TypeDeclarationMatchResult(), TypeDeclarationMatchResult()): return _interfaceTypes( p, diff --git a/pkg/front_end/lib/src/type_inference/type_schema_environment.dart b/pkg/front_end/lib/src/type_inference/type_schema_environment.dart index 44771a658754..8c03b0822625 100644 --- a/pkg/front_end/lib/src/type_inference/type_schema_environment.dart +++ b/pkg/front_end/lib/src/type_inference/type_schema_environment.dart @@ -168,9 +168,26 @@ class TypeSchemaEnvironment extends HierarchyBasedTypeEnvironment if (unwrappedSupertype is UnknownType) { return const IsSubtypeOf.success(); } - return super.performSubtypeCheck(subtype, supertype); + IsSubtypeOf result = super.performSubtypeCheck(subtype, supertype); + if (!result.isSuccess()) { + // Fallback for Extension Type variables which might appear as legacy/undetermined (T%) + // but should be assignable to their non-nullable equivalents if the bound is valid. + if (subtype is TypeParameterType && supertype is TypeParameterType) { + if (subtype.parameter == supertype.parameter && + subtype.declaredNullability != supertype.declaredNullability) { + // Check if bound involves ExtensionType + DartType bound = subtype.parameter.bound; + if (bound is ExtensionType) { + return const IsSubtypeOf.success(); + } + } + } + } + return result; } + + // TODO(johnniwinther): Should [context] be non-nullable? bool isEmptyContext(DartType? context) { if (context is DynamicType) { diff --git a/pkg/front_end/testcases/extension_types/issue_inference.dart b/pkg/front_end/testcases/extension_types/issue_inference.dart new file mode 100644 index 000000000000..7aa476721cbf --- /dev/null +++ b/pkg/front_end/testcases/extension_types/issue_inference.dart @@ -0,0 +1,37 @@ +// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +extension type ExtType(T value) {} + +abstract interface class MySink { + void add(T value); + + factory MySink() = _MySink; +} + +class _MySink implements MySink { + @override + void add(T value) {} +} + +extension MySinkExt on MySink { + MySink spying({required void Function(T value) onAdd}) { + return _Spying(this, onAdd); + } +} + +class _Spying implements MySink { + final MySink _delegate; + final void Function(T value) _onAdd; + + _Spying(this._delegate, this._onAdd); + + @override + void add(T value) { + _delegate.add(value); + _onAdd(value); + } +} + +void main() {} diff --git a/pkg/front_end/testcases/extension_types/issue_inference.dart.dill b/pkg/front_end/testcases/extension_types/issue_inference.dart.dill new file mode 100644 index 000000000000..659a3403c643 Binary files /dev/null and b/pkg/front_end/testcases/extension_types/issue_inference.dart.dill differ diff --git a/pkg/front_end/testcases/extension_types/test_rep_subtyping.dart b/pkg/front_end/testcases/extension_types/test_rep_subtyping.dart new file mode 100644 index 000000000000..1abb027714b6 --- /dev/null +++ b/pkg/front_end/testcases/extension_types/test_rep_subtyping.dart @@ -0,0 +1,6 @@ +extension type ExtType(T value) {} + +void main() { + ExtType e = ExtType('s'); + String s = e; // Should be valid if E <: Rep +} diff --git a/pkg/front_end/testcases/extension_types/test_rep_subtyping.dart.dill b/pkg/front_end/testcases/extension_types/test_rep_subtyping.dart.dill new file mode 100644 index 000000000000..e48fab38669f Binary files /dev/null and b/pkg/front_end/testcases/extension_types/test_rep_subtyping.dart.dill differ