Skip to content

Commit 28247a3

Browse files
author
John Messerly
committed
fix #25578, implement @covariant parameter overrides
[email protected] Review URL: https://codereview.chromium.org/2336503003 .
1 parent 1f10422 commit 28247a3

File tree

14 files changed

+285
-90
lines changed

14 files changed

+285
-90
lines changed

pkg/analyzer/lib/dart/element/element.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1612,6 +1612,12 @@ abstract class ParameterElement
16121612
*/
16131613
String get defaultValueCode;
16141614

1615+
/**
1616+
* Return `true` if this parameter is covariant, meaning it is allowed to have
1617+
* a narrower type in an override.
1618+
*/
1619+
bool get isCovariant;
1620+
16151621
/**
16161622
* Return `true` if this parameter is an initializing formal parameter.
16171623
*/

pkg/analyzer/lib/src/dart/element/element.dart

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2440,6 +2440,12 @@ class DynamicElementImpl extends ElementImpl implements TypeDefiningElement {
24402440
* A concrete implementation of an [ElementAnnotation].
24412441
*/
24422442
class ElementAnnotationImpl implements ElementAnnotation {
2443+
/**
2444+
* The name of the top-level variable used to mark a method parameter as
2445+
* covariant.
2446+
*/
2447+
static String _COVARIANT_VARIABLE_NAME = "checked";
2448+
24432449
/**
24442450
* The name of the class used to mark an element as being deprecated.
24452451
*/
@@ -2543,6 +2549,15 @@ class ElementAnnotationImpl implements ElementAnnotation {
25432549
@override
25442550
AnalysisContext get context => compilationUnit.library.context;
25452551

2552+
/**
2553+
* Return `true` if this annotation marks the associated parameter as being
2554+
* covariant, meaning it is allowed to have a narrower type in an override.
2555+
*/
2556+
bool get isCovariant =>
2557+
element is PropertyAccessorElement &&
2558+
element.name == _COVARIANT_VARIABLE_NAME &&
2559+
element.library?.name == _META_LIB_NAME;
2560+
25462561
@override
25472562
bool get isDeprecated {
25482563
if (element?.library?.isDartCore == true) {
@@ -6752,6 +6767,13 @@ class ParameterElementImpl extends VariableElementImpl
67526767
*/
67536768
int _visibleRangeLength = -1;
67546769

6770+
/**
6771+
* True if this parameter inherits from a covariant parameter. This happens
6772+
* when it overrides a method in a supertype that has a corresponding
6773+
* covariant parameter.
6774+
*/
6775+
bool inheritsCovariant = false;
6776+
67556777
/**
67566778
* Initialize a newly created parameter element to have the given [name] and
67576779
* [nameOffset].
@@ -6906,6 +6928,19 @@ class ParameterElementImpl extends VariableElementImpl
69066928
return super.isConst;
69076929
}
69086930

6931+
@override
6932+
bool get isCovariant {
6933+
if (inheritsCovariant) {
6934+
return true;
6935+
}
6936+
for (ElementAnnotationImpl annotation in metadata) {
6937+
if (annotation.isCovariant) {
6938+
return true;
6939+
}
6940+
}
6941+
return false;
6942+
}
6943+
69096944
@override
69106945
bool get isFinal {
69116946
if (_unlinkedParam != null) {

pkg/analyzer/lib/src/dart/element/handle.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -894,6 +894,9 @@ class ParameterElementHandle extends VariableElementHandle
894894
@override
895895
String get defaultValueCode => actualElement.defaultValueCode;
896896

897+
@override
898+
bool get isCovariant => actualElement.isCovariant;
899+
897900
@override
898901
bool get isInitializingFormal => actualElement.isInitializingFormal;
899902

pkg/analyzer/lib/src/dart/element/member.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,9 @@ class FieldFormalParameterMember extends ParameterMember
232232
[DartType type])
233233
: super(baseElement, definingType, type);
234234

235+
@override
236+
bool get isCovariant => baseElement.isCovariant;
237+
235238
@override
236239
FieldElement get field {
237240
FieldElement field = (baseElement as FieldFormalParameterElement).field;
@@ -674,6 +677,9 @@ class ParameterMember extends VariableMember
674677
@override
675678
int get hashCode => baseElement.hashCode;
676679

680+
@override
681+
bool get isCovariant => baseElement.isCovariant;
682+
677683
@override
678684
bool get isInitializingFormal => baseElement.isInitializingFormal;
679685

pkg/analyzer/lib/src/dart/element/type.dart

Lines changed: 53 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -797,14 +797,17 @@ class FunctionTypeImpl extends TypeImpl implements FunctionType {
797797
return relate(
798798
this,
799799
type,
800-
(DartType t, DartType s) =>
800+
(DartType t, DartType s, _, __) =>
801801
(t as TypeImpl).isMoreSpecificThan(s, withDynamic),
802802
new TypeSystemImpl().instantiateToBounds);
803803
}
804804

805805
@override
806806
bool isSubtypeOf(DartType type) {
807-
return relate(this, type, (DartType t, DartType s) => t.isAssignableTo(s),
807+
return relate(
808+
this,
809+
type,
810+
(DartType t, DartType s, _, __) => t.isAssignableTo(s),
808811
new TypeSystemImpl().instantiateToBounds);
809812
}
810813

@@ -966,17 +969,18 @@ class FunctionTypeImpl extends TypeImpl implements FunctionType {
966969
*
967970
* Used for the various relations on function types which have the same
968971
* structural rules for handling optional parameters and arity, but use their
969-
* own relation for comparing corresponding paramaters or return types.
972+
* own relation for comparing corresponding parameters or return types.
970973
*
971974
* If [returnRelation] is omitted, uses [parameterRelation] for both.
972975
*/
973976
static bool relate(
974977
FunctionType t,
975978
DartType other,
976-
bool parameterRelation(DartType t, DartType s),
979+
bool parameterRelation(
980+
DartType t, DartType s, bool tIsCovariant, bool sIsCovariant),
977981
DartType instantiateToBounds(DartType t),
978982
{bool returnRelation(DartType t, DartType s)}) {
979-
returnRelation ??= parameterRelation;
983+
returnRelation ??= (t, s) => parameterRelation(t, s, false, false);
980984

981985
// Trivial base cases.
982986
if (other == null) {
@@ -1014,12 +1018,39 @@ class FunctionTypeImpl extends TypeImpl implements FunctionType {
10141018
}
10151019

10161020
// Test the parameter types.
1017-
List<DartType> tRequired = t.normalParameterTypes;
1018-
List<DartType> sRequired = s.normalParameterTypes;
1019-
List<DartType> tOptional = t.optionalParameterTypes;
1020-
List<DartType> sOptional = s.optionalParameterTypes;
1021-
Map<String, DartType> tNamed = t.namedParameterTypes;
1022-
Map<String, DartType> sNamed = s.namedParameterTypes;
1021+
1022+
// TODO(jmesserly): this could be implemented with less allocation if we
1023+
// wanted, by taking advantage of the fact that positional arguments must
1024+
// appear before named ones.
1025+
var tRequired = <ParameterElement>[];
1026+
var tOptional = <ParameterElement>[];
1027+
var tNamed = <String, ParameterElement>{};
1028+
for (var p in t.parameters) {
1029+
var kind = p.parameterKind;
1030+
if (kind == ParameterKind.REQUIRED) {
1031+
tRequired.add(p);
1032+
} else if (kind == ParameterKind.POSITIONAL) {
1033+
tOptional.add(p);
1034+
} else {
1035+
assert(kind == ParameterKind.NAMED);
1036+
tNamed[p.name] = p;
1037+
}
1038+
}
1039+
1040+
var sRequired = <ParameterElement>[];
1041+
var sOptional = <ParameterElement>[];
1042+
var sNamed = <String, ParameterElement>{};
1043+
for (var p in s.parameters) {
1044+
var kind = p.parameterKind;
1045+
if (kind == ParameterKind.REQUIRED) {
1046+
sRequired.add(p);
1047+
} else if (kind == ParameterKind.POSITIONAL) {
1048+
sOptional.add(p);
1049+
} else {
1050+
assert(kind == ParameterKind.NAMED);
1051+
sNamed[p.name] = p;
1052+
}
1053+
}
10231054

10241055
// If one function has positional and the other has named parameters,
10251056
// they don't relate.
@@ -1037,19 +1068,21 @@ class FunctionTypeImpl extends TypeImpl implements FunctionType {
10371068
// For each named parameter in s, make sure we have a corresponding one
10381069
// that relates.
10391070
for (String key in sNamed.keys) {
1040-
var tParamType = tNamed[key];
1041-
if (tParamType == null) {
1071+
var tParam = tNamed[key];
1072+
if (tParam == null) {
10421073
return false;
10431074
}
1044-
if (!parameterRelation(tParamType, sNamed[key])) {
1075+
var sParam = sNamed[key];
1076+
if (!parameterRelation(
1077+
tParam.type, sParam.type, tParam.isCovariant, sParam.isCovariant)) {
10451078
return false;
10461079
}
10471080
}
10481081

10491082
// Make sure all of the positional parameters (both required and optional)
10501083
// relate to each other.
1051-
List<DartType> tPositional = tRequired;
1052-
List<DartType> sPositional = sRequired;
1084+
var tPositional = tRequired;
1085+
var sPositional = sRequired;
10531086

10541087
if (tOptional.isNotEmpty) {
10551088
tPositional = tPositional.toList()..addAll(tOptional);
@@ -1070,7 +1103,10 @@ class FunctionTypeImpl extends TypeImpl implements FunctionType {
10701103
}
10711104

10721105
for (int i = 0; i < sPositional.length; i++) {
1073-
if (!parameterRelation(tPositional[i], sPositional[i])) {
1106+
var tParam = tPositional[i];
1107+
var sParam = sPositional[i];
1108+
if (!parameterRelation(
1109+
tParam.type, sParam.type, tParam.isCovariant, sParam.isCovariant)) {
10741110
return false;
10751111
}
10761112
}

pkg/analyzer/lib/src/generated/element_resolver.dart

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -419,7 +419,7 @@ class ElementResolver extends SimpleAstVisitor<Object> {
419419

420420
@override
421421
Object visitFieldFormalParameter(FieldFormalParameter node) {
422-
_resolveMetadataForParameter(node.element, node);
422+
_resolveMetadataForParameter(node);
423423
return super.visitFieldFormalParameter(node);
424424
}
425425

@@ -463,7 +463,7 @@ class ElementResolver extends SimpleAstVisitor<Object> {
463463

464464
@override
465465
Object visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) {
466-
_resolveMetadataForParameter(node.element, node);
466+
_resolveMetadataForParameter(node);
467467
return null;
468468
}
469469

@@ -1055,7 +1055,7 @@ class ElementResolver extends SimpleAstVisitor<Object> {
10551055

10561056
@override
10571057
Object visitSimpleFormalParameter(SimpleFormalParameter node) {
1058-
_resolveMetadataForParameter(node.element, node);
1058+
_resolveMetadataForParameter(node);
10591059
return null;
10601060
}
10611061

@@ -2270,8 +2270,7 @@ class ElementResolver extends SimpleAstVisitor<Object> {
22702270
* Given a [node] that can have annotations associated with it, resolve the
22712271
* annotations in the element model representing annotations to the node.
22722272
*/
2273-
void _resolveMetadataForParameter(
2274-
Element element, NormalFormalParameter node) {
2273+
void _resolveMetadataForParameter(NormalFormalParameter node) {
22752274
_resolveAnnotations(node.metadata);
22762275
}
22772276

pkg/analyzer/lib/src/generated/error_verifier.dart

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2491,7 +2491,6 @@ class ErrorVerifier extends RecursiveAstVisitor<Object> {
24912491
DartType expectedStaticType,
24922492
ErrorCode errorCode) {
24932493
// TODO(leafp): Move the Downcast functionality here.
2494-
// TODO(leafp): Support strict downcasts
24952494
if (!_expressionIsAssignableAtType(
24962495
expression, actualStaticType, expectedStaticType)) {
24972496
_errorReporter.reportTypeErrorForNode(
@@ -5038,10 +5037,10 @@ class ErrorVerifier extends RecursiveAstVisitor<Object> {
50385037
FunctionType requiredMemberFT = _inheritanceManager
50395038
.substituteTypeArgumentsInMemberFromInheritance(
50405039
requiredMemberType, memberName, enclosingType);
5041-
foundConcreteFT =
5042-
_typeSystem.typeToConcreteType(_typeProvider, foundConcreteFT);
5043-
requiredMemberFT =
5044-
_typeSystem.typeToConcreteType(_typeProvider, requiredMemberFT);
5040+
foundConcreteFT = _typeSystem.functionTypeToConcreteType(
5041+
_typeProvider, foundConcreteFT);
5042+
requiredMemberFT = _typeSystem.functionTypeToConcreteType(
5043+
_typeProvider, requiredMemberFT);
50455044
if (_typeSystem.isSubtypeOf(foundConcreteFT, requiredMemberFT)) {
50465045
continue;
50475046
}
@@ -6229,11 +6228,10 @@ class ErrorVerifier extends RecursiveAstVisitor<Object> {
62296228
bool _expressionIsAssignableAtType(Expression expression,
62306229
DartType actualStaticType, DartType expectedStaticType) {
62316230
bool concrete = _options.strongMode && checker.isKnownFunction(expression);
6232-
if (concrete) {
6233-
actualStaticType =
6234-
_typeSystem.typeToConcreteType(_typeProvider, actualStaticType);
6231+
if (concrete && actualStaticType is FunctionType) {
6232+
actualStaticType = _typeSystem.functionTypeToConcreteType(
6233+
_typeProvider, actualStaticType);
62356234
// TODO(leafp): Move the Downcast functionality here.
6236-
// TODO(leafp): Support strict downcasts
62376235
}
62386236
return _typeSystem.isAssignableTo(actualStaticType, expectedStaticType);
62396237
}

pkg/analyzer/lib/src/generated/resolver.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7334,7 +7334,8 @@ class ResolverVisitor extends ScopedVisitor {
73347334
!FunctionTypeImpl.relate(
73357335
expectedClosureType,
73367336
staticClosureType,
7337-
(DartType t, DartType s) => (t as TypeImpl).isMoreSpecificThan(s),
7337+
(DartType t, DartType s, _, __) =>
7338+
(t as TypeImpl).isMoreSpecificThan(s),
73387339
new TypeSystemImpl().instantiateToBounds,
73397340
returnRelation: (s, t) => true)) {
73407341
return;

pkg/analyzer/lib/src/generated/type_system.dart

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -811,18 +811,30 @@ class StrongTypeSystemImpl extends TypeSystem {
811811
provider.dynamicType;
812812
}
813813

814-
/**
815-
* Check that [f1] is a subtype of [f2].
816-
*
817-
* This will always assume function types use fuzzy arrows, in other words
818-
* that dynamic parameters of f1 and f2 are treated as bottom.
819-
*/
814+
/// Check that [f1] is a subtype of [f2].
815+
///
816+
/// This will always assume function types use fuzzy arrows, in other words
817+
/// that dynamic parameters of f1 and f2 are treated as bottom.
820818
bool _isFunctionSubtypeOf(FunctionType f1, FunctionType f2) {
821819
return FunctionTypeImpl.relate(
822820
f1,
823821
f2,
824-
(DartType t1, DartType t2) =>
825-
_isSubtypeOf(t2, t1, null, dynamicIsBottom: true),
822+
(t1, t2, _, __) => _isSubtypeOf(t2, t1, null, dynamicIsBottom: true),
823+
instantiateToBounds,
824+
returnRelation: isSubtypeOf);
825+
}
826+
827+
/// Check that [f1] is a subtype of [f2] for an override.
828+
///
829+
/// This is different from the normal function subtyping in two ways:
830+
/// - we know the function types are strict arrows,
831+
/// - it allows opt-in covariant parameters.
832+
bool isOverrideSubtypeOf(FunctionType f1, FunctionType f2) {
833+
return FunctionTypeImpl.relate(
834+
f1,
835+
f2,
836+
(t1, t2, t1Covariant, _) =>
837+
isSubtypeOf(t2, t1) || t1Covariant && isSubtypeOf(t1, t2),
826838
instantiateToBounds,
827839
returnRelation: isSubtypeOf);
828840
}

pkg/analyzer/lib/src/summary/link.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3901,6 +3901,9 @@ class ParameterElementForLink implements ParameterElementImpl {
39013901
DartType _inferredType;
39023902
DartType _declaredType;
39033903

3904+
@override
3905+
bool inheritsCovariant = false;
3906+
39043907
ParameterElementForLink(this.enclosingElement, this._unlinkedParam,
39053908
this._typeParameterContext, this.compilationUnit, this._parameterIndex) {
39063909
if (_unlinkedParam.initializer?.bodyExpr != null) {
@@ -3915,6 +3918,9 @@ class ParameterElementForLink implements ParameterElementImpl {
39153918
bool get hasImplicitType =>
39163919
!_unlinkedParam.isFunctionTyped && _unlinkedParam.type == null;
39173920

3921+
@override
3922+
bool get isCovariant => false;
3923+
39183924
@override
39193925
String get name => _unlinkedParam.name;
39203926

0 commit comments

Comments
 (0)