blob: b453f065a2178783f640a5c91140fb76123ac58c [file] [log] [blame]
// Copyright (c) 2018, 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.
import '../ast.dart'
show
BottomType,
DartType,
DynamicType,
FunctionType,
InterfaceType,
NamedType,
TypeParameter,
TypedefType,
VoidType;
import '../type_algebra.dart' show substitute;
import '../type_environment.dart' show TypeEnvironment;
class TypeArgumentIssue {
// The type argument that violated the bound.
final DartType argument;
// The type parameter with the bound that was violated.
final TypeParameter typeParameter;
// The enclosing type of the issue, that is, the one with [typeParameter].
final DartType enclosingType;
TypeArgumentIssue(this.argument, this.typeParameter, this.enclosingType);
}
// TODO(dmitryas): Remove [typedefInstantiations] when type arguments passed to
// typedefs are preserved in the Kernel output.
List<TypeArgumentIssue> findTypeArgumentIssues(
DartType type, TypeEnvironment typeEnvironment,
{bool allowSuperBounded = false}) {
List<TypeParameter> variables;
List<DartType> arguments;
List<TypeArgumentIssue> typedefRhsResult;
if (type is FunctionType && type.typedefType != null) {
// [type] is a function type that is an application of a parametrized
// typedef. We need to check both the l.h.s. and the r.h.s. of the
// definition in that case. For details, see [link]
// (https://github.com/dart-lang/sdk/blob/master/docs/language/informal/super-bounded-types.md).
FunctionType functionType = type;
FunctionType cloned = new FunctionType(
functionType.positionalParameters, functionType.returnType,
namedParameters: functionType.namedParameters,
typeParameters: functionType.typeParameters,
requiredParameterCount: functionType.requiredParameterCount,
typedefType: null);
typedefRhsResult = findTypeArgumentIssues(cloned, typeEnvironment,
allowSuperBounded: true);
type = functionType.typedefType;
}
if (type is InterfaceType) {
variables = type.classNode.typeParameters;
arguments = type.typeArguments;
} else if (type is TypedefType) {
variables = type.typedefNode.typeParameters;
arguments = type.typeArguments;
} else if (type is FunctionType) {
List<TypeArgumentIssue> result = <TypeArgumentIssue>[];
for (TypeParameter parameter in type.typeParameters) {
result.addAll(findTypeArgumentIssues(parameter.bound, typeEnvironment,
allowSuperBounded: true) ??
const <TypeArgumentIssue>[]);
}
for (DartType formal in type.positionalParameters) {
result.addAll(findTypeArgumentIssues(formal, typeEnvironment,
allowSuperBounded: true) ??
const <TypeArgumentIssue>[]);
}
for (NamedType named in type.namedParameters) {
result.addAll(findTypeArgumentIssues(named.type, typeEnvironment,
allowSuperBounded: true) ??
const <TypeArgumentIssue>[]);
}
result.addAll(findTypeArgumentIssues(type.returnType, typeEnvironment,
allowSuperBounded: true) ??
const <TypeArgumentIssue>[]);
return result.isEmpty ? null : result;
} else {
return null;
}
if (variables == null) return null;
List<TypeArgumentIssue> result;
List<TypeArgumentIssue> argumentsResult;
Map<TypeParameter, DartType> substitutionMap =
new Map<TypeParameter, DartType>.fromIterables(variables, arguments);
for (int i = 0; i < arguments.length; ++i) {
DartType argument = arguments[i];
if (argument is FunctionType && argument.typeParameters.length > 0) {
// Generic function types aren't allowed as type arguments either.
result ??= <TypeArgumentIssue>[];
result.add(new TypeArgumentIssue(argument, variables[i], type));
} else if (!typeEnvironment.isSubtypeOf(
argument, substitute(variables[i].bound, substitutionMap))) {
result ??= <TypeArgumentIssue>[];
result.add(new TypeArgumentIssue(argument, variables[i], type));
}
List<TypeArgumentIssue> issues = findTypeArgumentIssues(
argument, typeEnvironment,
allowSuperBounded: true);
if (issues != null) {
argumentsResult ??= <TypeArgumentIssue>[];
argumentsResult.addAll(issues);
}
}
if (argumentsResult != null) {
result ??= <TypeArgumentIssue>[];
result.addAll(argumentsResult);
}
if (typedefRhsResult != null) {
result ??= <TypeArgumentIssue>[];
result.addAll(typedefRhsResult);
}
// [type] is regular-bounded.
if (result == null) return null;
if (!allowSuperBounded) return result;
result = null;
type = convertSuperBoundedToRegularBounded(typeEnvironment, type);
List<DartType> argumentsToReport = arguments.toList();
if (type is InterfaceType) {
variables = type.classNode.typeParameters;
arguments = type.typeArguments;
} else if (type is TypedefType) {
variables = type.typedefNode.typeParameters;
arguments = type.typeArguments;
}
substitutionMap =
new Map<TypeParameter, DartType>.fromIterables(variables, arguments);
for (int i = 0; i < arguments.length; ++i) {
DartType argument = arguments[i];
if (argument is FunctionType && argument.typeParameters.length > 0) {
// Generic function types aren't allowed as type arguments either.
result ??= <TypeArgumentIssue>[];
result
.add(new TypeArgumentIssue(argumentsToReport[i], variables[i], type));
} else if (!typeEnvironment.isSubtypeOf(
argument, substitute(variables[i].bound, substitutionMap))) {
result ??= <TypeArgumentIssue>[];
result
.add(new TypeArgumentIssue(argumentsToReport[i], variables[i], type));
}
}
if (argumentsResult != null) {
result ??= <TypeArgumentIssue>[];
result.addAll(argumentsResult);
}
if (typedefRhsResult != null) {
result ??= <TypeArgumentIssue>[];
result.addAll(typedefRhsResult);
}
return result;
}
// TODO(dmitryas): Remove [typedefInstantiations] when type arguments passed to
// typedefs are preserved in the Kernel output.
List<TypeArgumentIssue> findTypeArgumentIssuesForInvocation(
List<TypeParameter> parameters,
List<DartType> arguments,
TypeEnvironment typeEnvironment,
{Map<FunctionType, List<DartType>> typedefInstantiations}) {
assert(arguments.length == parameters.length);
List<TypeArgumentIssue> result;
var substitutionMap = <TypeParameter, DartType>{};
for (int i = 0; i < arguments.length; ++i) {
substitutionMap[parameters[i]] = arguments[i];
}
for (int i = 0; i < arguments.length; ++i) {
DartType argument = arguments[i];
if (argument is FunctionType && argument.typeParameters.length > 0) {
// Generic function types aren't allowed as type arguments either.
result ??= <TypeArgumentIssue>[];
result.add(new TypeArgumentIssue(argument, parameters[i], null));
} else if (!typeEnvironment.isSubtypeOf(
argument, substitute(parameters[i].bound, substitutionMap))) {
result ??= <TypeArgumentIssue>[];
result.add(new TypeArgumentIssue(argument, parameters[i], null));
}
List<TypeArgumentIssue> issues = findTypeArgumentIssues(
argument, typeEnvironment,
allowSuperBounded: true);
if (issues != null) {
result ??= <TypeArgumentIssue>[];
result.addAll(issues);
}
}
return result;
}
String getGenericTypeName(DartType type) {
if (type is InterfaceType) {
return type.classNode.name;
} else if (type is TypedefType) {
return type.typedefNode.name;
}
return type.toString();
}
/// Replaces all covariant occurrences of `dynamic`, `Object`, and `void` with
/// [BottomType] and all contravariant occurrences of `Null` and [BottomType]
/// with `Object`.
DartType convertSuperBoundedToRegularBounded(
TypeEnvironment typeEnvironment, DartType type,
{bool isCovariant = true}) {
if ((type is DynamicType ||
type is VoidType ||
isObject(typeEnvironment, type)) &&
isCovariant) {
return const BottomType();
} else if ((type is BottomType || isNull(typeEnvironment, type)) &&
!isCovariant) {
return typeEnvironment.objectType;
} else if (type is InterfaceType && type.classNode.typeParameters != null) {
List<DartType> replacedTypeArguments =
new List<DartType>(type.typeArguments.length);
for (int i = 0; i < replacedTypeArguments.length; i++) {
replacedTypeArguments[i] = convertSuperBoundedToRegularBounded(
typeEnvironment, type.typeArguments[i],
isCovariant: isCovariant);
}
return new InterfaceType(type.classNode, replacedTypeArguments);
} else if (type is TypedefType && type.typedefNode.typeParameters != null) {
List<DartType> replacedTypeArguments =
new List<DartType>(type.typeArguments.length);
for (int i = 0; i < replacedTypeArguments.length; i++) {
replacedTypeArguments[i] = convertSuperBoundedToRegularBounded(
typeEnvironment, type.typeArguments[i],
isCovariant: isCovariant);
}
return new TypedefType(type.typedefNode, replacedTypeArguments);
} else if (type is FunctionType) {
var replacedReturnType = convertSuperBoundedToRegularBounded(
typeEnvironment, type.returnType,
isCovariant: isCovariant);
var replacedPositionalParameters =
new List<DartType>(type.positionalParameters.length);
for (int i = 0; i < replacedPositionalParameters.length; i++) {
replacedPositionalParameters[i] = convertSuperBoundedToRegularBounded(
typeEnvironment, type.positionalParameters[i],
isCovariant: !isCovariant);
}
var replacedNamedParameters =
new List<NamedType>(type.namedParameters.length);
for (int i = 0; i < replacedNamedParameters.length; i++) {
replacedNamedParameters[i] = new NamedType(
type.namedParameters[i].name,
convertSuperBoundedToRegularBounded(
typeEnvironment, type.namedParameters[i].type,
isCovariant: !isCovariant));
}
return new FunctionType(replacedPositionalParameters, replacedReturnType,
namedParameters: replacedNamedParameters,
typeParameters: type.typeParameters,
requiredParameterCount: type.requiredParameterCount,
typedefType: type.typedefType);
}
return type;
}
bool isObject(TypeEnvironment typeEnvironment, DartType type) {
return type is InterfaceType &&
type.classNode == typeEnvironment.objectType.classNode;
}
bool isNull(TypeEnvironment typeEnvironment, DartType type) {
return type is InterfaceType &&
type.classNode == typeEnvironment.nullType.classNode;
}