[cfe] Check superbounded extension types
Closes #53139
Change-Id: I2f13d77454e2a7bac70d19afa1fb321815aac3bc
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/318641
Reviewed-by: Chloe Stefantsova <cstefantsova@google.com>
Commit-Queue: Johnni Winther <johnniwinther@google.com>
diff --git a/pkg/front_end/lib/src/fasta/builder/extension_type_declaration_builder.dart b/pkg/front_end/lib/src/fasta/builder/extension_type_declaration_builder.dart
index 24ad3085..6fca442 100644
--- a/pkg/front_end/lib/src/fasta/builder/extension_type_declaration_builder.dart
+++ b/pkg/front_end/lib/src/fasta/builder/extension_type_declaration_builder.dart
@@ -5,6 +5,7 @@
import 'package:kernel/ast.dart';
import '../scope.dart';
+import '../source/source_library_builder.dart';
import 'builder.dart';
import 'builder_mixins.dart';
import 'declaration_builder.dart';
@@ -76,7 +77,13 @@
Uri fileUri,
int charOffset,
{required bool hasExplicitTypeArguments}) {
- return new ExtensionType(extensionTypeDeclaration, nullability, arguments);
+ ExtensionType type =
+ new ExtensionType(extensionTypeDeclaration, nullability, arguments);
+ if (typeVariablesCount != 0 && library is SourceLibraryBuilder) {
+ library.registerBoundsCheck(type, fileUri, charOffset, typeUse,
+ inferred: !hasExplicitTypeArguments);
+ }
+ return type;
}
@override
diff --git a/pkg/front_end/testcases/inline_class/extension_types/issue53123.dart b/pkg/front_end/testcases/inline_class/extension_types/issue53123.dart
new file mode 100644
index 0000000..75a84f6
--- /dev/null
+++ b/pkg/front_end/testcases/inline_class/extension_types/issue53123.dart
@@ -0,0 +1,9 @@
+// Copyright (c) 2023, 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 V<T extends V<T>>(T id) {}
+
+test(V v) {
+ List<V> l = [v];
+}
\ No newline at end of file
diff --git a/pkg/front_end/testcases/inline_class/extension_types/issue53123.dart.strong.expect b/pkg/front_end/testcases/inline_class/extension_types/issue53123.dart.strong.expect
new file mode 100644
index 0000000..20f288d
--- /dev/null
+++ b/pkg/front_end/testcases/inline_class/extension_types/issue53123.dart.strong.expect
@@ -0,0 +1,36 @@
+library;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/inline_class/extension_types/issue53123.dart:7:6: Error: Inferred type argument 'V<dynamic>' doesn't conform to the bound 'V<T>' of the type variable 'T' on 'ExtensionType(V<V<dynamic>>)'.
+// Try specifying type arguments explicitly so that they conform to the bounds.
+// test(V v) {
+// ^
+// pkg/front_end/testcases/inline_class/extension_types/issue53123.dart:5:18: Context: This is the type variable whose bound isn't conformed to.
+// extension type V<T extends V<T>>(T id) {}
+// ^
+//
+// pkg/front_end/testcases/inline_class/extension_types/issue53123.dart:8:8: Error: Inferred type argument 'V<dynamic>' doesn't conform to the bound 'V<T>' of the type variable 'T' on 'ExtensionType(V<V<dynamic>>)'.
+// Try specifying type arguments explicitly so that they conform to the bounds.
+// List<V> l = [v];
+// ^
+// pkg/front_end/testcases/inline_class/extension_types/issue53123.dart:5:18: Context: This is the type variable whose bound isn't conformed to.
+// extension type V<T extends V<T>>(T id) {}
+// ^
+//
+import self as self;
+import "dart:core" as core;
+
+inline class V<T extends self::V<T> = self::V<dynamic>> /* declaredRepresentationType = T */ {
+ constructor • = self::V|;
+ tearoff • = self::V|_#new#tearOff;
+}
+static inline-class-member method V|<T extends self::V<self::V|::T> = dynamic>(dynamic id) → self::V<self::V|::T> {
+ lowered final self::V<self::V|::T> #this = id;
+ return #this;
+}
+static inline-class-member method V|_#new#tearOff<T extends self::V<self::V|_#new#tearOff::T> = dynamic>(dynamic id) → self::V<self::V|_#new#tearOff::T>
+ return self::V|<self::V|_#new#tearOff::T>(id);
+static method test(self::V<self::V<dynamic>> v) → dynamic {
+ core::List<self::V<self::V<dynamic>>> l = <self::V<self::V<dynamic>>>[v];
+}
diff --git a/pkg/front_end/testcases/inline_class/extension_types/issue53123.dart.strong.transformed.expect b/pkg/front_end/testcases/inline_class/extension_types/issue53123.dart.strong.transformed.expect
new file mode 100644
index 0000000..1dd36f8
--- /dev/null
+++ b/pkg/front_end/testcases/inline_class/extension_types/issue53123.dart.strong.transformed.expect
@@ -0,0 +1,36 @@
+library;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/inline_class/extension_types/issue53123.dart:7:6: Error: Inferred type argument 'V<dynamic>' doesn't conform to the bound 'V<T>' of the type variable 'T' on 'ExtensionType(V<V<dynamic>>)'.
+// Try specifying type arguments explicitly so that they conform to the bounds.
+// test(V v) {
+// ^
+// pkg/front_end/testcases/inline_class/extension_types/issue53123.dart:5:18: Context: This is the type variable whose bound isn't conformed to.
+// extension type V<T extends V<T>>(T id) {}
+// ^
+//
+// pkg/front_end/testcases/inline_class/extension_types/issue53123.dart:8:8: Error: Inferred type argument 'V<dynamic>' doesn't conform to the bound 'V<T>' of the type variable 'T' on 'ExtensionType(V<V<dynamic>>)'.
+// Try specifying type arguments explicitly so that they conform to the bounds.
+// List<V> l = [v];
+// ^
+// pkg/front_end/testcases/inline_class/extension_types/issue53123.dart:5:18: Context: This is the type variable whose bound isn't conformed to.
+// extension type V<T extends V<T>>(T id) {}
+// ^
+//
+import self as self;
+import "dart:core" as core;
+
+inline class V<T extends self::V<T> = self::V<dynamic>> /* declaredRepresentationType = T */ {
+ constructor • = self::V|;
+ tearoff • = self::V|_#new#tearOff;
+}
+static inline-class-member method V|<T extends self::V<self::V|::T> = dynamic>(dynamic id) → self::V<self::V|::T> {
+ lowered final self::V<self::V|::T> #this = id;
+ return #this;
+}
+static inline-class-member method V|_#new#tearOff<T extends self::V<self::V|_#new#tearOff::T> = dynamic>(dynamic id) → self::V<self::V|_#new#tearOff::T>
+ return self::V|<self::V|_#new#tearOff::T>(id);
+static method test(self::V<self::V<dynamic>> v) → dynamic {
+ core::List<self::V<self::V<dynamic>>> l = core::_GrowableList::_literal1<self::V<self::V<dynamic>>>(v);
+}
diff --git a/pkg/front_end/testcases/inline_class/extension_types/issue53123.dart.textual_outline.expect b/pkg/front_end/testcases/inline_class/extension_types/issue53123.dart.textual_outline.expect
new file mode 100644
index 0000000..11f4b74
--- /dev/null
+++ b/pkg/front_end/testcases/inline_class/extension_types/issue53123.dart.textual_outline.expect
@@ -0,0 +1,2 @@
+extension type V<T extends V<T>>(T id) {}
+test(V v) {}
diff --git a/pkg/front_end/testcases/inline_class/extension_types/issue53123.dart.weak.expect b/pkg/front_end/testcases/inline_class/extension_types/issue53123.dart.weak.expect
new file mode 100644
index 0000000..20f288d
--- /dev/null
+++ b/pkg/front_end/testcases/inline_class/extension_types/issue53123.dart.weak.expect
@@ -0,0 +1,36 @@
+library;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/inline_class/extension_types/issue53123.dart:7:6: Error: Inferred type argument 'V<dynamic>' doesn't conform to the bound 'V<T>' of the type variable 'T' on 'ExtensionType(V<V<dynamic>>)'.
+// Try specifying type arguments explicitly so that they conform to the bounds.
+// test(V v) {
+// ^
+// pkg/front_end/testcases/inline_class/extension_types/issue53123.dart:5:18: Context: This is the type variable whose bound isn't conformed to.
+// extension type V<T extends V<T>>(T id) {}
+// ^
+//
+// pkg/front_end/testcases/inline_class/extension_types/issue53123.dart:8:8: Error: Inferred type argument 'V<dynamic>' doesn't conform to the bound 'V<T>' of the type variable 'T' on 'ExtensionType(V<V<dynamic>>)'.
+// Try specifying type arguments explicitly so that they conform to the bounds.
+// List<V> l = [v];
+// ^
+// pkg/front_end/testcases/inline_class/extension_types/issue53123.dart:5:18: Context: This is the type variable whose bound isn't conformed to.
+// extension type V<T extends V<T>>(T id) {}
+// ^
+//
+import self as self;
+import "dart:core" as core;
+
+inline class V<T extends self::V<T> = self::V<dynamic>> /* declaredRepresentationType = T */ {
+ constructor • = self::V|;
+ tearoff • = self::V|_#new#tearOff;
+}
+static inline-class-member method V|<T extends self::V<self::V|::T> = dynamic>(dynamic id) → self::V<self::V|::T> {
+ lowered final self::V<self::V|::T> #this = id;
+ return #this;
+}
+static inline-class-member method V|_#new#tearOff<T extends self::V<self::V|_#new#tearOff::T> = dynamic>(dynamic id) → self::V<self::V|_#new#tearOff::T>
+ return self::V|<self::V|_#new#tearOff::T>(id);
+static method test(self::V<self::V<dynamic>> v) → dynamic {
+ core::List<self::V<self::V<dynamic>>> l = <self::V<self::V<dynamic>>>[v];
+}
diff --git a/pkg/front_end/testcases/inline_class/extension_types/issue53123.dart.weak.modular.expect b/pkg/front_end/testcases/inline_class/extension_types/issue53123.dart.weak.modular.expect
new file mode 100644
index 0000000..20f288d
--- /dev/null
+++ b/pkg/front_end/testcases/inline_class/extension_types/issue53123.dart.weak.modular.expect
@@ -0,0 +1,36 @@
+library;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/inline_class/extension_types/issue53123.dart:7:6: Error: Inferred type argument 'V<dynamic>' doesn't conform to the bound 'V<T>' of the type variable 'T' on 'ExtensionType(V<V<dynamic>>)'.
+// Try specifying type arguments explicitly so that they conform to the bounds.
+// test(V v) {
+// ^
+// pkg/front_end/testcases/inline_class/extension_types/issue53123.dart:5:18: Context: This is the type variable whose bound isn't conformed to.
+// extension type V<T extends V<T>>(T id) {}
+// ^
+//
+// pkg/front_end/testcases/inline_class/extension_types/issue53123.dart:8:8: Error: Inferred type argument 'V<dynamic>' doesn't conform to the bound 'V<T>' of the type variable 'T' on 'ExtensionType(V<V<dynamic>>)'.
+// Try specifying type arguments explicitly so that they conform to the bounds.
+// List<V> l = [v];
+// ^
+// pkg/front_end/testcases/inline_class/extension_types/issue53123.dart:5:18: Context: This is the type variable whose bound isn't conformed to.
+// extension type V<T extends V<T>>(T id) {}
+// ^
+//
+import self as self;
+import "dart:core" as core;
+
+inline class V<T extends self::V<T> = self::V<dynamic>> /* declaredRepresentationType = T */ {
+ constructor • = self::V|;
+ tearoff • = self::V|_#new#tearOff;
+}
+static inline-class-member method V|<T extends self::V<self::V|::T> = dynamic>(dynamic id) → self::V<self::V|::T> {
+ lowered final self::V<self::V|::T> #this = id;
+ return #this;
+}
+static inline-class-member method V|_#new#tearOff<T extends self::V<self::V|_#new#tearOff::T> = dynamic>(dynamic id) → self::V<self::V|_#new#tearOff::T>
+ return self::V|<self::V|_#new#tearOff::T>(id);
+static method test(self::V<self::V<dynamic>> v) → dynamic {
+ core::List<self::V<self::V<dynamic>>> l = <self::V<self::V<dynamic>>>[v];
+}
diff --git a/pkg/front_end/testcases/inline_class/extension_types/issue53123.dart.weak.outline.expect b/pkg/front_end/testcases/inline_class/extension_types/issue53123.dart.weak.outline.expect
new file mode 100644
index 0000000..d200706
--- /dev/null
+++ b/pkg/front_end/testcases/inline_class/extension_types/issue53123.dart.weak.outline.expect
@@ -0,0 +1,24 @@
+library;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/inline_class/extension_types/issue53123.dart:7:6: Error: Inferred type argument 'V<dynamic>' doesn't conform to the bound 'V<T>' of the type variable 'T' on 'ExtensionType(V<V<dynamic>>)'.
+// Try specifying type arguments explicitly so that they conform to the bounds.
+// test(V v) {
+// ^
+// pkg/front_end/testcases/inline_class/extension_types/issue53123.dart:5:18: Context: This is the type variable whose bound isn't conformed to.
+// extension type V<T extends V<T>>(T id) {}
+// ^
+//
+import self as self;
+
+inline class V<T extends self::V<T> = self::V<dynamic>> /* declaredRepresentationType = T */ {
+ constructor • = self::V|;
+ tearoff • = self::V|_#new#tearOff;
+}
+static inline-class-member method V|<T extends self::V<self::V|::T> = dynamic>(dynamic id) → self::V<self::V|::T>
+ ;
+static inline-class-member method V|_#new#tearOff<T extends self::V<self::V|_#new#tearOff::T> = dynamic>(dynamic id) → self::V<self::V|_#new#tearOff::T>
+ return self::V|<self::V|_#new#tearOff::T>(id);
+static method test(self::V<self::V<dynamic>> v) → dynamic
+ ;
diff --git a/pkg/front_end/testcases/inline_class/extension_types/issue53123.dart.weak.transformed.expect b/pkg/front_end/testcases/inline_class/extension_types/issue53123.dart.weak.transformed.expect
new file mode 100644
index 0000000..1dd36f8
--- /dev/null
+++ b/pkg/front_end/testcases/inline_class/extension_types/issue53123.dart.weak.transformed.expect
@@ -0,0 +1,36 @@
+library;
+//
+// Problems in library:
+//
+// pkg/front_end/testcases/inline_class/extension_types/issue53123.dart:7:6: Error: Inferred type argument 'V<dynamic>' doesn't conform to the bound 'V<T>' of the type variable 'T' on 'ExtensionType(V<V<dynamic>>)'.
+// Try specifying type arguments explicitly so that they conform to the bounds.
+// test(V v) {
+// ^
+// pkg/front_end/testcases/inline_class/extension_types/issue53123.dart:5:18: Context: This is the type variable whose bound isn't conformed to.
+// extension type V<T extends V<T>>(T id) {}
+// ^
+//
+// pkg/front_end/testcases/inline_class/extension_types/issue53123.dart:8:8: Error: Inferred type argument 'V<dynamic>' doesn't conform to the bound 'V<T>' of the type variable 'T' on 'ExtensionType(V<V<dynamic>>)'.
+// Try specifying type arguments explicitly so that they conform to the bounds.
+// List<V> l = [v];
+// ^
+// pkg/front_end/testcases/inline_class/extension_types/issue53123.dart:5:18: Context: This is the type variable whose bound isn't conformed to.
+// extension type V<T extends V<T>>(T id) {}
+// ^
+//
+import self as self;
+import "dart:core" as core;
+
+inline class V<T extends self::V<T> = self::V<dynamic>> /* declaredRepresentationType = T */ {
+ constructor • = self::V|;
+ tearoff • = self::V|_#new#tearOff;
+}
+static inline-class-member method V|<T extends self::V<self::V|::T> = dynamic>(dynamic id) → self::V<self::V|::T> {
+ lowered final self::V<self::V|::T> #this = id;
+ return #this;
+}
+static inline-class-member method V|_#new#tearOff<T extends self::V<self::V|_#new#tearOff::T> = dynamic>(dynamic id) → self::V<self::V|_#new#tearOff::T>
+ return self::V|<self::V|_#new#tearOff::T>(id);
+static method test(self::V<self::V<dynamic>> v) → dynamic {
+ core::List<self::V<self::V<dynamic>>> l = core::_GrowableList::_literal1<self::V<self::V<dynamic>>>(v);
+}
diff --git a/pkg/front_end/testcases/textual_outline.status b/pkg/front_end/testcases/textual_outline.status
index 696d87e..06ab9a6 100644
--- a/pkg/front_end/testcases/textual_outline.status
+++ b/pkg/front_end/testcases/textual_outline.status
@@ -148,6 +148,7 @@
inline_class/extension_types/issue52284: FormatterCrash
inline_class/extension_types/issue52525: FormatterCrash
inline_class/extension_types/issue52667: FormatterCrash
+inline_class/extension_types/issue53123: FormatterCrash
inline_class/extension_types/member_not_found: FormatterCrash
inline_class/extension_types/method_access: FormatterCrash
inline_class/extension_types/non_covariant_type_parameter: FormatterCrash
diff --git a/pkg/kernel/lib/src/bounds_checks.dart b/pkg/kernel/lib/src/bounds_checks.dart
index eec83b3..0d508c4 100644
--- a/pkg/kernel/lib/src/bounds_checks.dart
+++ b/pkg/kernel/lib/src/bounds_checks.dart
@@ -342,6 +342,10 @@
} else if (type is TypedefType) {
variables = type.typedefNode.typeParameters;
arguments = type.typeArguments;
+ } else if (type is ExtensionType) {
+ variables = type.extensionTypeDeclaration.typeParameters;
+ arguments = type.typeArguments;
+ allowSuperBounded = false;
} else if (type is FunctionType) {
List<TypeArgumentIssue> result = <TypeArgumentIssue>[];
@@ -372,6 +376,12 @@
variables = typeEnvironment.coreTypes.futureClass.typeParameters;
arguments = <DartType>[type.typeArgument];
} else {
+ assert(type is DynamicType ||
+ type is VoidType ||
+ type is IntersectionType ||
+ type is TypeParameterType ||
+ type is NeverType ||
+ type is NullType);
return const <TypeArgumentIssue>[];
}