Skip to content

Commit 439b79f

Browse files
chloestefantsovaCommit Queue
authored and
Commit Queue
committed
[cfe] Report contravariant type variable use in extension types
Closes #53803 Part of #49731 Change-Id: I348f6c84aa61eddae306faa96a560b4960724462 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/331242 Reviewed-by: Johnni Winther <[email protected]> Commit-Queue: Chloe Stefantsova <[email protected]>
1 parent f458158 commit 439b79f

19 files changed

+1333
-80
lines changed

pkg/front_end/lib/src/fasta/fasta_codes_cfe_generated.dart

+41
Original file line numberDiff line numberDiff line change
@@ -6120,3 +6120,44 @@ Message _withArgumentsUndefinedSetter(
61206120
"""Try correcting the name to the name of an existing setter, or defining a setter or field named '${name}'.""",
61216121
arguments: {'name': name, 'type': _type});
61226122
}
6123+
6124+
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
6125+
const Template<
6126+
Message Function(
6127+
String name, DartType _type, bool isNonNullableByDefault)>
6128+
templateWrongTypeParameterVarianceInSuperinterface =
6129+
const Template<
6130+
Message Function(String name, DartType _type,
6131+
bool isNonNullableByDefault)>(
6132+
"WrongTypeParameterVarianceInSuperinterface",
6133+
problemMessageTemplate:
6134+
r"""'#name' can't be used contravariantly or invariantly in '#type'.""",
6135+
withArguments:
6136+
_withArgumentsWrongTypeParameterVarianceInSuperinterface);
6137+
6138+
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
6139+
const Code<
6140+
Message Function(
6141+
String name, DartType _type, bool isNonNullableByDefault)>
6142+
codeWrongTypeParameterVarianceInSuperinterface = const Code<
6143+
Message Function(
6144+
String name, DartType _type, bool isNonNullableByDefault)>(
6145+
"WrongTypeParameterVarianceInSuperinterface",
6146+
analyzerCodes: <String>[
6147+
"WRONG_TYPE_PARAMETER_VARIANCE_IN_SUPERINTERFACE"
6148+
]);
6149+
6150+
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
6151+
Message _withArgumentsWrongTypeParameterVarianceInSuperinterface(
6152+
String name, DartType _type, bool isNonNullableByDefault) {
6153+
if (name.isEmpty) throw 'No name provided';
6154+
name = demangleMixinApplicationName(name);
6155+
TypeLabeler labeler = new TypeLabeler(isNonNullableByDefault);
6156+
List<Object> typeParts = labeler.labelType(_type);
6157+
String type = typeParts.join();
6158+
return new Message(codeWrongTypeParameterVarianceInSuperinterface,
6159+
problemMessage:
6160+
"""'${name}' can't be used contravariantly or invariantly in '${type}'.""" +
6161+
labeler.originMessages,
6162+
arguments: {'name': name, 'type': _type});
6163+
}

pkg/front_end/lib/src/fasta/kernel/type_algorithms.dart

+81-74
Original file line numberDiff line numberDiff line change
@@ -58,84 +58,91 @@ int computeTypeVariableBuilderVariance(NominalVariableBuilder variable,
5858
typeArguments: List<TypeBuilder>? arguments
5959
):
6060
assert(declaration != null);
61-
if (declaration is NominalVariableBuilder) {
62-
if (declaration == variable) {
63-
return Variance.covariant;
64-
} else {
65-
return Variance.unrelated;
66-
}
67-
} else {
68-
switch (declaration) {
69-
case ClassBuilder():
70-
int result = Variance.unrelated;
71-
if (arguments != null) {
72-
for (int i = 0; i < arguments.length; ++i) {
73-
result = Variance.meet(
74-
result,
75-
Variance.combine(
76-
declaration.cls.typeParameters[i].variance,
77-
computeTypeVariableBuilderVariance(
78-
variable, arguments[i], libraryBuilder)));
79-
}
61+
switch (declaration) {
62+
case ClassBuilder():
63+
int result = Variance.unrelated;
64+
if (arguments != null) {
65+
for (int i = 0; i < arguments.length; ++i) {
66+
result = Variance.meet(
67+
result,
68+
Variance.combine(
69+
declaration.cls.typeParameters[i].variance,
70+
computeTypeVariableBuilderVariance(
71+
variable, arguments[i], libraryBuilder)));
8072
}
81-
return result;
82-
case TypeAliasBuilder():
83-
int result = Variance.unrelated;
84-
85-
if (type.typeArguments != null) {
86-
for (int i = 0; i < type.typeArguments!.length; ++i) {
87-
const int visitMarker = -2;
88-
89-
int declarationTypeVariableVariance = declaration.varianceAt(i);
90-
if (declarationTypeVariableVariance == pendingVariance) {
91-
assert(!declaration.fromDill);
92-
NominalVariableBuilder declarationTypeVariable =
93-
declaration.typeVariables![i];
94-
declarationTypeVariable.variance = visitMarker;
95-
int computedVariance = computeTypeVariableBuilderVariance(
96-
declarationTypeVariable,
97-
declaration.type,
98-
libraryBuilder);
99-
declarationTypeVariableVariance =
100-
declarationTypeVariable.variance = computedVariance;
101-
} else if (declarationTypeVariableVariance == visitMarker) {
102-
assert(!declaration.fromDill);
103-
NominalVariableBuilder declarationTypeVariable =
104-
declaration.typeVariables![i];
105-
libraryBuilder.addProblem(
106-
templateCyclicTypedef.withArguments(declaration.name),
107-
declaration.charOffset,
108-
declaration.name.length,
109-
declaration.fileUri);
110-
// Use [Variance.unrelated] for recovery. The type with the
111-
// cyclic dependency will be replaced with an [InvalidType]
112-
// elsewhere.
113-
declarationTypeVariableVariance =
114-
declarationTypeVariable.variance = Variance.unrelated;
115-
}
116-
117-
result = Variance.meet(
118-
result,
119-
Variance.combine(
120-
computeTypeVariableBuilderVariance(
121-
variable, type.typeArguments![i], libraryBuilder),
122-
declarationTypeVariableVariance));
73+
}
74+
return result;
75+
case TypeAliasBuilder():
76+
int result = Variance.unrelated;
77+
78+
if (type.typeArguments != null) {
79+
for (int i = 0; i < type.typeArguments!.length; ++i) {
80+
const int visitMarker = -2;
81+
82+
int declarationTypeVariableVariance = declaration.varianceAt(i);
83+
if (declarationTypeVariableVariance == pendingVariance) {
84+
assert(!declaration.fromDill);
85+
NominalVariableBuilder declarationTypeVariable =
86+
declaration.typeVariables![i];
87+
declarationTypeVariable.variance = visitMarker;
88+
int computedVariance = computeTypeVariableBuilderVariance(
89+
declarationTypeVariable, declaration.type, libraryBuilder);
90+
declarationTypeVariableVariance =
91+
declarationTypeVariable.variance = computedVariance;
92+
} else if (declarationTypeVariableVariance == visitMarker) {
93+
assert(!declaration.fromDill);
94+
NominalVariableBuilder declarationTypeVariable =
95+
declaration.typeVariables![i];
96+
libraryBuilder.addProblem(
97+
templateCyclicTypedef.withArguments(declaration.name),
98+
declaration.charOffset,
99+
declaration.name.length,
100+
declaration.fileUri);
101+
// Use [Variance.unrelated] for recovery. The type with the
102+
// cyclic dependency will be replaced with an [InvalidType]
103+
// elsewhere.
104+
declarationTypeVariableVariance =
105+
declarationTypeVariable.variance = Variance.unrelated;
123106
}
107+
108+
result = Variance.meet(
109+
result,
110+
Variance.combine(
111+
computeTypeVariableBuilderVariance(
112+
variable, type.typeArguments![i], libraryBuilder),
113+
declarationTypeVariableVariance));
124114
}
125-
return result;
126-
case ExtensionTypeDeclarationBuilder():
127-
// TODO(johnniwinther): Handle this case.
128-
case NominalVariableBuilder():
129-
case StructuralVariableBuilder():
130-
case ExtensionBuilder():
131-
case InvalidTypeDeclarationBuilder():
132-
case BuiltinTypeDeclarationBuilder():
133-
// TODO(johnniwinther): How should we handle this case?
134-
case OmittedTypeDeclarationBuilder():
135-
case null:
136-
}
137-
return Variance.unrelated;
115+
}
116+
return result;
117+
case ExtensionTypeDeclarationBuilder():
118+
int result = Variance.unrelated;
119+
if (arguments != null) {
120+
for (int i = 0; i < arguments.length; ++i) {
121+
result = Variance.meet(
122+
result,
123+
Variance.combine(
124+
declaration
125+
.extensionTypeDeclaration.typeParameters[i].variance,
126+
computeTypeVariableBuilderVariance(
127+
variable, arguments[i], libraryBuilder)));
128+
}
129+
}
130+
return result;
131+
case NominalVariableBuilder():
132+
if (declaration == variable) {
133+
return Variance.covariant;
134+
} else {
135+
return Variance.unrelated;
136+
}
137+
case StructuralVariableBuilder():
138+
case ExtensionBuilder():
139+
case InvalidTypeDeclarationBuilder():
140+
case BuiltinTypeDeclarationBuilder():
141+
// TODO(johnniwinther): How should we handle this case?
142+
case OmittedTypeDeclarationBuilder():
143+
case null:
138144
}
145+
return Variance.unrelated;
139146
case FunctionTypeBuilder(
140147
:List<StructuralVariableBuilder>? typeVariables,
141148
:List<ParameterBuilder>? formals,

pkg/front_end/lib/src/fasta/source/source_extension_type_declaration_builder.dart

+31
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import '../builder/name_iterator.dart';
2222
import '../builder/type_builder.dart';
2323
import '../kernel/hierarchy/hierarchy_builder.dart';
2424
import '../kernel/kernel_helper.dart';
25+
import '../kernel/type_algorithms.dart';
2526
import '../messages.dart';
2627
import '../problems.dart';
2728
import '../scope.dart';
@@ -136,6 +137,36 @@ class SourceExtensionTypeDeclarationBuilder
136137
typeBuilder.build(libraryBuilder, TypeUse.superType);
137138
Message? errorMessage;
138139
List<LocatedMessage>? errorContext;
140+
141+
if (typeParameters?.isNotEmpty ?? false) {
142+
for (NominalVariableBuilder variable in typeParameters!) {
143+
int variance = computeTypeVariableBuilderVariance(
144+
variable, typeBuilder, libraryBuilder);
145+
if (!Variance.greaterThanOrEqual(variance, variable.variance)) {
146+
if (variable.parameter.isLegacyCovariant) {
147+
errorMessage =
148+
templateWrongTypeParameterVarianceInSuperinterface
149+
.withArguments(variable.name, interface,
150+
libraryBuilder.isNonNullableByDefault);
151+
} else {
152+
errorMessage =
153+
templateInvalidTypeVariableInSupertypeWithVariance
154+
.withArguments(
155+
Variance.keywordString(variable.variance),
156+
variable.name,
157+
Variance.keywordString(variance),
158+
typeBuilder.typeName!.name);
159+
}
160+
}
161+
}
162+
if (errorMessage != null) {
163+
libraryBuilder.addProblem(errorMessage, typeBuilder.charOffset!,
164+
noLength, typeBuilder.fileUri,
165+
context: errorContext);
166+
errorMessage = null;
167+
}
168+
}
169+
139170
if (interface is ExtensionType) {
140171
if (interface.nullability == Nullability.nullable) {
141172
errorMessage =

pkg/front_end/messages.yaml

+7
Original file line numberDiff line numberDiff line change
@@ -7454,3 +7454,10 @@ RepresentationFieldTrailingComma:
74547454
script: |
74557455
extension type E(int foo,) {}
74567456
analyzerCode: REPRESENTATION_FIELD_TRAILING_COMMA
7457+
7458+
WrongTypeParameterVarianceInSuperinterface:
7459+
problemMessage: "'#name' can't be used contravariantly or invariantly in '#type'."
7460+
experiments: inline-class
7461+
script: |
7462+
extension type E<X>(List<Function(Object?)> foo) implements List<Function(X)> {}
7463+
analyzerCode: WRONG_TYPE_PARAMETER_VARIANCE_IN_SUPERINTERFACE

pkg/front_end/testcases/extension_types/cyclic_representation_type.dart.strong.expect

+1-1
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ import self as self;
195195
import "dart:core" as core;
196196

197197
typedef Alias9 = self::E9 /* = invalid-type */;
198-
typedef Alias10<unrelated T extends core::Object? = dynamic> = self::E10<T%> /* = invalid-type */;
198+
typedef Alias10<T extends core::Object? = dynamic> = self::E10<T%> /* = invalid-type */;
199199
typedef Alias11 = () → self::E11 /* = invalid-type */;
200200
typedef Alias12 = (self::E12 /* = invalid-type */) → void;
201201
typedef Alias13 = <T extends self::E13 /* = invalid-type */ = dynamic>() → void;

pkg/front_end/testcases/extension_types/cyclic_representation_type.dart.strong.transformed.expect

+1-1
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ import self as self;
195195
import "dart:core" as core;
196196

197197
typedef Alias9 = self::E9 /* = invalid-type */;
198-
typedef Alias10<unrelated T extends core::Object? = dynamic> = self::E10<T%> /* = invalid-type */;
198+
typedef Alias10<T extends core::Object? = dynamic> = self::E10<T%> /* = invalid-type */;
199199
typedef Alias11 = () → self::E11 /* = invalid-type */;
200200
typedef Alias12 = (self::E12 /* = invalid-type */) → void;
201201
typedef Alias13 = <T extends self::E13 /* = invalid-type */ = dynamic>() → void;

pkg/front_end/testcases/extension_types/cyclic_representation_type.dart.weak.expect

+1-1
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ import self as self;
195195
import "dart:core" as core;
196196

197197
typedef Alias9 = self::E9 /* = invalid-type */;
198-
typedef Alias10<unrelated T extends core::Object? = dynamic> = self::E10<T%> /* = invalid-type */;
198+
typedef Alias10<T extends core::Object? = dynamic> = self::E10<T%> /* = invalid-type */;
199199
typedef Alias11 = () → self::E11 /* = invalid-type */;
200200
typedef Alias12 = (self::E12 /* = invalid-type */) → void;
201201
typedef Alias13 = <T extends self::E13 /* = invalid-type */ = dynamic>() → void;

pkg/front_end/testcases/extension_types/cyclic_representation_type.dart.weak.modular.expect

+1-1
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ import self as self;
195195
import "dart:core" as core;
196196

197197
typedef Alias9 = self::E9 /* = invalid-type */;
198-
typedef Alias10<unrelated T extends core::Object? = dynamic> = self::E10<T%> /* = invalid-type */;
198+
typedef Alias10<T extends core::Object? = dynamic> = self::E10<T%> /* = invalid-type */;
199199
typedef Alias11 = () → self::E11 /* = invalid-type */;
200200
typedef Alias12 = (self::E12 /* = invalid-type */) → void;
201201
typedef Alias13 = <T extends self::E13 /* = invalid-type */ = dynamic>() → void;

pkg/front_end/testcases/extension_types/cyclic_representation_type.dart.weak.outline.expect

+1-1
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ import self as self;
195195
import "dart:core" as core;
196196

197197
typedef Alias9 = self::E9 /* = invalid-type */;
198-
typedef Alias10<unrelated T extends core::Object? = dynamic> = self::E10<T%> /* = invalid-type */;
198+
typedef Alias10<T extends core::Object? = dynamic> = self::E10<T%> /* = invalid-type */;
199199
typedef Alias11 = () → self::E11 /* = invalid-type */;
200200
typedef Alias12 = (self::E12 /* = invalid-type */) → void;
201201
typedef Alias13 = <T extends self::E13 /* = invalid-type */ = dynamic>() → void;

pkg/front_end/testcases/extension_types/cyclic_representation_type.dart.weak.transformed.expect

+1-1
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ import self as self;
195195
import "dart:core" as core;
196196

197197
typedef Alias9 = self::E9 /* = invalid-type */;
198-
typedef Alias10<unrelated T extends core::Object? = dynamic> = self::E10<T%> /* = invalid-type */;
198+
typedef Alias10<T extends core::Object? = dynamic> = self::E10<T%> /* = invalid-type */;
199199
typedef Alias11 = () → self::E11 /* = invalid-type */;
200200
typedef Alias12 = (self::E12 /* = invalid-type */) → void;
201201
typedef Alias13 = <T extends self::E13 /* = invalid-type */ = dynamic>() → void;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by b
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
class Class<X> {}
6+
extension type ExtensionType<X>(Object? foo) {}
7+
8+
extension type E1<X>(Class<Never> foo) implements Class<Function(X)> {} // Error.
9+
extension type E2<X>(Class<Never> foo) implements Class<Function(Function(X))> {} // Ok.
10+
extension type E3<X>(Class<Never> foo) implements Class<Function(Function(Function(X)))> {} // Error.
11+
extension type E4<X>(Class<Never> foo) implements Class<X Function(X)> {} // Error.
12+
extension type E5<X>(Class<Never> foo) implements Class<X Function(Function(X))> {} // Ok.
13+
extension type E6<X>(Class<Never> foo) implements Class<X Function(Function(Function(X)))> {} // Error.
14+
15+
extension type E7<X>(Object? foo) implements ExtensionType<Function(X)> {} // Error.
16+
extension type E8<X>(Object? foo) implements ExtensionType<Function(Function(X))> {} // Ok.
17+
extension type E9<X>(Object? foo) implements ExtensionType<Function(Function(Function(X)))> {} // Error.
18+
extension type E10<X>(Object? foo) implements ExtensionType<X Function(X)> {} // Error.
19+
extension type E11<X>(Object? foo) implements ExtensionType<X Function(Function(X))> {} // Ok.
20+
extension type E12<X>(Object? foo) implements ExtensionType<X Function(Function(Function(X)))> {} // Error.

0 commit comments

Comments
 (0)