Skip to content

Commit

Permalink
Issue 34699. Use type parameter variance when instantiate to bounds.
Browse files Browse the repository at this point in the history
Bug: #34699
Bug: #43524
Change-Id: I8fe14c59fff17f3e4ebee62fbd7be2b45172a595
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/164860
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
  • Loading branch information
scheglov authored and commit-bot@chromium.org committed Sep 28, 2020
1 parent c4b5644 commit 499bae0
Show file tree
Hide file tree
Showing 4 changed files with 391 additions and 28 deletions.
2 changes: 1 addition & 1 deletion pkg/analyzer/lib/src/dart/analysis/driver.dart
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ typedef WorkToWaitAfterComputingResult = Future<void> Function(String path);
/// TODO(scheglov) Clean up the list of implicitly analyzed files.
class AnalysisDriver implements AnalysisDriverGeneric {
/// The version of data format, should be incremented on every format change.
static const int DATA_VERSION = 111;
static const int DATA_VERSION = 112;

/// The length of the list returned by [_computeDeclaredVariablesSignature].
static const int _declaredVariablesSignatureLength = 4;
Expand Down
190 changes: 182 additions & 8 deletions pkg/analyzer/lib/src/dart/element/replacement_visitor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,17 @@ import 'package:analyzer/src/dart/element/type_algebra.dart';
import 'package:analyzer/src/dart/element/type_schema.dart';
import 'package:analyzer/src/dart/element/type_visitor.dart';
import 'package:analyzer/src/generated/utilities_dart.dart';
import 'package:analyzer/src/summary2/function_type_builder.dart';
import 'package:analyzer/src/summary2/named_type_builder.dart';
import 'package:meta/meta.dart';

/// Helper visitor that clones a type if a nested type is replaced, and
/// otherwise returns `null`.
class ReplacementVisitor
implements TypeVisitor<DartType>, InferenceTypeVisitor<DartType> {
implements
TypeVisitor<DartType>,
InferenceTypeVisitor<DartType>,
LinkingTypeVisitor<DartType> {
const ReplacementVisitor();

void changeVariance() {}
Expand Down Expand Up @@ -47,6 +52,27 @@ class ReplacementVisitor
);
}

DartType createFunctionTypeBuilder({
@required FunctionTypeBuilder type,
@required List<TypeParameterElement> newTypeParameters,
@required List<ParameterElement> newParameters,
@required DartType newReturnType,
@required NullabilitySuffix newNullability,
}) {
if (newNullability == null &&
newReturnType == null &&
newParameters == null) {
return null;
}

return FunctionTypeBuilder(
newTypeParameters ?? type.typeFormals,
newParameters ?? type.parameters,
newReturnType ?? type.returnType,
newNullability ?? type.nullabilitySuffix,
);
}

DartType createInterfaceType({
@required InterfaceType type,
@required List<DartType> newTypeArguments,
Expand All @@ -63,6 +89,23 @@ class ReplacementVisitor
);
}

NamedTypeBuilder createNamedTypeBuilder({
@required NamedTypeBuilder type,
@required List<DartType> newTypeArguments,
@required NullabilitySuffix newNullability,
}) {
if (newTypeArguments == null && newNullability == null) {
return null;
}

return NamedTypeBuilder(
type.typeSystem,
type.element,
newTypeArguments ?? type.arguments,
newNullability ?? type.nullabilitySuffix,
);
}

DartType createNeverType({
@required NeverTypeImpl type,
@required NullabilitySuffix newNullability,
Expand Down Expand Up @@ -207,25 +250,127 @@ class ReplacementVisitor
}

@override
DartType visitInterfaceType(InterfaceType type) {
var newNullability = visitNullability(type);
DartType visitFunctionTypeBuilder(FunctionTypeBuilder node) {
var newNullability = visitNullability(node);

List<DartType> newTypeArguments;
for (var i = 0; i < type.typeArguments.length; i++) {
var substitution = type.typeArguments[i].accept(this);
List<TypeParameterElement> newTypeParameters;
for (var i = 0; i < node.typeFormals.length; i++) {
var typeParameter = node.typeFormals[i];
var bound = typeParameter.bound;
if (bound != null) {
var newBound = bound.accept(this);
if (newBound != null) {
newTypeParameters ??= node.typeFormals.toList(growable: false);
newTypeParameters[i] = TypeParameterElementImpl.synthetic(
typeParameter.name,
)..bound = newBound;
}
}
}

Substitution substitution;
if (newTypeParameters != null) {
var map = <TypeParameterElement, DartType>{};
for (var i = 0; i < newTypeParameters.length; ++i) {
var typeParameter = node.typeFormals[i];
var newTypeParameter = newTypeParameters[i];
map[typeParameter] = newTypeParameter.instantiate(
nullabilitySuffix: NullabilitySuffix.none,
);
}

substitution = Substitution.fromMap(map);

for (var i = 0; i < newTypeParameters.length; i++) {
var newTypeParameter = newTypeParameters[i];
var bound = newTypeParameter.bound;
if (bound != null) {
var newBound = substitution.substituteType(bound);
(newTypeParameter as TypeParameterElementImpl).bound = newBound;
}
}
}

DartType visitType(DartType type) {
if (type == null) return null;
var result = type.accept(this);
if (substitution != null) {
newTypeArguments ??= type.typeArguments.toList(growable: false);
newTypeArguments[i] = substitution;
result = substitution.substituteType(result ?? type);
}
return result;
}

var newReturnType = visitType(node.returnType);

changeVariance();

List<ParameterElement> newParameters;
for (var i = 0; i < node.parameters.length; i++) {
var parameter = node.parameters[i];

var type = parameter.type;
var newType = visitType(type);

// ignore: deprecated_member_use_from_same_package
var kind = parameter.parameterKind;
var newKind = visitParameterKind(kind);

if (newType != null || newKind != null) {
newParameters ??= node.parameters.toList(growable: false);
newParameters[i] = parameter.copyWith(
type: newType,
kind: newKind,
);
}
}

changeVariance();

return createFunctionTypeBuilder(
type: node,
newTypeParameters: newTypeParameters,
newParameters: newParameters,
newReturnType: newReturnType,
newNullability: newNullability,
);
}

@override
DartType visitInterfaceType(InterfaceType type) {
var newNullability = visitNullability(type);

var newTypeArguments = _typeArguments(
type.element.typeParameters,
type.typeArguments,
);

return createInterfaceType(
type: type,
newTypeArguments: newTypeArguments,
newNullability: newNullability,
);
}

@override
DartType visitNamedTypeBuilder(NamedTypeBuilder type) {
var newNullability = visitNullability(type);

var parameters = const <TypeParameterElement>[];
var element = type.element;
if (element is ClassElement) {
parameters = element.typeParameters;
} else if (element is FunctionTypeAliasElement) {
parameters = element.typeParameters;
}

var newArguments = _typeArguments(parameters, type.arguments);
return createNamedTypeBuilder(
type: type,
newTypeArguments: newArguments,
newNullability: newNullability,
);
}

@override
DartType visitNeverType(NeverType type) {
var newNullability = visitNullability(type);
Expand All @@ -244,6 +389,11 @@ class ReplacementVisitor
return null;
}

DartType visitTypeArgument(
TypeParameterElement parameter, DartType argument) {
return argument.accept(this);
}

@override
DartType visitTypeParameterType(TypeParameterType type) {
var newNullability = visitNullability(type);
Expand Down Expand Up @@ -273,4 +423,28 @@ class ReplacementVisitor
DartType visitVoidType(VoidType type) {
return null;
}

List<DartType> _typeArguments(
List<TypeParameterElement> parameters,
List<DartType> arguments,
) {
if (arguments == null) {
return null;
}

if (arguments.length != parameters.length) {
return null;
}

List<DartType> newArguments;
for (var i = 0; i < arguments.length; i++) {
var substitution = visitTypeArgument(parameters[i], arguments[i]);
if (substitution != null) {
newArguments ??= arguments.toList(growable: false);
newArguments[i] = substitution;
}
}

return newArguments;
}
}
92 changes: 76 additions & 16 deletions pkg/analyzer/lib/src/summary2/default_types_builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@ import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/dart/element/replacement_visitor.dart';
import 'package:analyzer/src/dart/element/type.dart';
import 'package:analyzer/src/dart/element/type_algebra.dart';
import 'package:analyzer/src/dart/resolver/variance.dart';
import 'package:analyzer/src/summary2/function_type_builder.dart';
import 'package:analyzer/src/summary2/lazy_ast.dart';
import 'package:analyzer/src/summary2/named_type_builder.dart';
import 'package:analyzer/src/summary2/type_builder.dart';
import 'package:analyzer/src/util/graph.dart';
import 'package:meta/meta.dart';

class DefaultTypesBuilder {
void build(List<AstNode> nodes) {
Expand Down Expand Up @@ -152,12 +154,14 @@ class DefaultTypesBuilder {
if (parameterList == null) return;

var typeProvider = declarationElement.library.typeProvider;
var dynamicType = typeProvider.dynamicType;
var nullType = typeProvider.nullType;
var dynamicType = DynamicTypeImpl.instance;
var bottomType = declarationElement.library.isNonNullableByDefault
? NeverTypeImpl.instance
: typeProvider.nullType;

var nodes = parameterList.typeParameters;
var length = nodes.length;
var elements = List<TypeParameterElement>(length);
var elements = List<TypeParameterElementImpl>(length);
var bounds = List<DartType>(length);
for (int i = 0; i < length; i++) {
var node = nodes[i];
Expand All @@ -173,15 +177,17 @@ class DefaultTypesBuilder {
for (var i in component) {
var element = elements[i];
dynamicSubstitution[element] = dynamicType;
nullSubstitution[element] = nullType;
nullSubstitution[element] = bottomType;
}

var substitution = Substitution.fromUpperAndLowerBounds(
dynamicSubstitution,
nullSubstitution,
);
for (var i in component) {
bounds[i] = substitution.substituteType(bounds[i]);
var variable = elements[i];
var visitor = _UpperLowerReplacementVisitor(
upper: dynamicSubstitution,
lower: nullSubstitution,
variance: variable.variance,
);
bounds[i] = visitor.run(bounds[i]);
}
}

Expand All @@ -190,14 +196,16 @@ class DefaultTypesBuilder {
var nullSubstitution = <TypeParameterElement, DartType>{};
var element = elements[i];
thisSubstitution[element] = bounds[i];
nullSubstitution[element] = nullType;
nullSubstitution[element] = bottomType;

var substitution = Substitution.fromUpperAndLowerBounds(
thisSubstitution,
nullSubstitution,
);
for (var j = 0; j < length; j++) {
bounds[j] = substitution.substituteType(bounds[j]);
var variable = elements[j];
var visitor = _UpperLowerReplacementVisitor(
upper: thisSubstitution,
lower: nullSubstitution,
variance: variable.variance,
);
bounds[j] = visitor.run(bounds[j]);
}
}

Expand Down Expand Up @@ -355,3 +363,55 @@ class _TypeParametersGraph implements Graph<int> {
}
}
}

class _UpperLowerReplacementVisitor extends ReplacementVisitor {
final Map<TypeParameterElement, DartType> _upper;
final Map<TypeParameterElement, DartType> _lower;
Variance _variance;

_UpperLowerReplacementVisitor({
@required Map<TypeParameterElement, DartType> upper,
@required Map<TypeParameterElement, DartType> lower,
@required Variance variance,
}) : _upper = upper,
_lower = lower,
_variance = variance;

@override
void changeVariance() {
if (_variance == Variance.covariant) {
_variance = Variance.contravariant;
} else if (_variance == Variance.contravariant) {
_variance = Variance.covariant;
}
}

DartType run(DartType type) {
return type.accept(this) ?? type;
}

@override
DartType visitTypeArgument(
TypeParameterElement parameter,
DartType argument,
) {
var savedVariance = _variance;
try {
_variance = _variance.combine(
(parameter as TypeParameterElementImpl).variance,
);
return super.visitTypeArgument(parameter, argument);
} finally {
_variance = savedVariance;
}
}

@override
DartType visitTypeParameterType(TypeParameterType type) {
if (_variance == Variance.contravariant) {
return _lower[type.element];
} else {
return _upper[type.element];
}
}
}
Loading

0 comments on commit 499bae0

Please sign in to comment.