mirror of
https://github.com/dart-lang/sdk
synced 2024-09-16 02:07:06 +00:00
cb3a9b0043
TEST=existing Change-Id: I05ee649ecfa7945e2e0f5e5d09441d8916a9c46e Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/208185 Commit-Queue: Johnni Winther <johnniwinther@google.com> Reviewed-by: Dmitry Stefantsov <dmitryas@google.com>
315 lines
11 KiB
Dart
315 lines
11 KiB
Dart
// Copyright (c) 2017, 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 'class_hierarchy.dart';
|
|
import 'core_types.dart';
|
|
import 'kernel.dart';
|
|
import 'type_checker.dart' as type_checker;
|
|
import 'type_algebra.dart';
|
|
import 'type_environment.dart';
|
|
|
|
abstract class FailureListener {
|
|
void reportFailure(TreeNode node, String message);
|
|
void reportNotAssignable(TreeNode node, DartType first, DartType second);
|
|
void reportInvalidOverride(Member member, Member inherited, String message);
|
|
}
|
|
|
|
class NaiveTypeChecker extends type_checker.TypeChecker {
|
|
final FailureListener failures;
|
|
|
|
factory NaiveTypeChecker(FailureListener failures, Component component,
|
|
{bool ignoreSdk: false}) {
|
|
CoreTypes coreTypes = new CoreTypes(component);
|
|
return new NaiveTypeChecker._(
|
|
failures,
|
|
coreTypes,
|
|
new ClassHierarchy(component, coreTypes,
|
|
onAmbiguousSupertypes: (Class cls, Supertype s0, Supertype s1) {
|
|
failures.reportFailure(cls, "$cls can't implement both $s1 and $s1");
|
|
}),
|
|
ignoreSdk);
|
|
}
|
|
|
|
NaiveTypeChecker._(this.failures, CoreTypes coreTypes,
|
|
ClassHierarchy hierarchy, bool ignoreSdk)
|
|
: super(coreTypes, hierarchy, ignoreSdk: ignoreSdk);
|
|
|
|
// TODO(vegorov) this only gets called for immediate overrides which leads
|
|
// to less strict checking that Dart 2.0 specification demands for covariant
|
|
// parameters.
|
|
@override
|
|
void checkOverride(
|
|
Class host, Member ownMember, Member superMember, bool isSetter) {
|
|
final bool ownMemberIsFieldOrAccessor =
|
|
ownMember is Field || (ownMember as Procedure).isAccessor;
|
|
final bool superMemberIsFieldOrAccessor =
|
|
superMember is Field || (superMember as Procedure).isAccessor;
|
|
|
|
// TODO: move to error reporting code
|
|
String _memberKind(Member m) {
|
|
if (m is Field) {
|
|
return 'field';
|
|
} else {
|
|
final Procedure p = m as Procedure;
|
|
if (p.isGetter) {
|
|
return 'getter';
|
|
} else if (p.isSetter) {
|
|
return 'setter';
|
|
} else {
|
|
return 'method';
|
|
}
|
|
}
|
|
}
|
|
|
|
// First check if we are overriding field/accessor with a normal method
|
|
// or other way around.
|
|
if (ownMemberIsFieldOrAccessor != superMemberIsFieldOrAccessor) {
|
|
return failures.reportInvalidOverride(ownMember, superMember, '''
|
|
${ownMember} is a ${_memberKind(ownMember)}
|
|
${superMember} is a ${_memberKind(superMember)}
|
|
''');
|
|
}
|
|
|
|
if (ownMemberIsFieldOrAccessor) {
|
|
if (isSetter) {
|
|
final DartType ownType = setterType(host, ownMember);
|
|
final DartType superType = setterType(host, superMember);
|
|
final bool isCovariant = ownMember is Field
|
|
? ownMember.isCovariant
|
|
: ownMember.function!.positionalParameters[0].isCovariant;
|
|
if (!_isValidParameterOverride(isCovariant, ownType, superType)) {
|
|
if (isCovariant) {
|
|
return failures.reportInvalidOverride(ownMember, superMember, '''
|
|
${ownType} is neither a subtype nor supertype of ${superType}
|
|
''');
|
|
} else {
|
|
return failures.reportInvalidOverride(ownMember, superMember, '''
|
|
${ownType} is not a subtype of ${superType}
|
|
''');
|
|
}
|
|
}
|
|
} else {
|
|
final DartType ownType = getterType(host, ownMember);
|
|
final DartType superType = getterType(host, superMember);
|
|
if (!_isSubtypeOf(ownType, superType)) {
|
|
return failures.reportInvalidOverride(ownMember, superMember, '''
|
|
${ownType} is not a subtype of ${superType}
|
|
''');
|
|
}
|
|
}
|
|
} else {
|
|
final String? msg =
|
|
_checkFunctionOverride(host, ownMember, superMember as Procedure);
|
|
if (msg != null) {
|
|
return failures.reportInvalidOverride(ownMember, superMember, msg);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Check if [subtype] is subtype of [supertype] after applying
|
|
/// type parameter [substitution].
|
|
bool _isSubtypeOf(DartType subtype, DartType supertype) {
|
|
// TODO(dmitryas): Remove this when ExtensionType is in ast.dart.
|
|
if (!_isKnownDartTypeImplementation(subtype) ||
|
|
!_isKnownDartTypeImplementation(supertype)) {
|
|
return true;
|
|
}
|
|
|
|
if (subtype is InvalidType || supertype is InvalidType) {
|
|
return true;
|
|
}
|
|
// TODO(dmitryas): Find a way to tell the weak mode from strong mode to use
|
|
// [SubtypeCheckMode.withNullabilities] where necessary.
|
|
return environment.isSubtypeOf(
|
|
subtype, supertype, SubtypeCheckMode.ignoringNullabilities);
|
|
}
|
|
|
|
Substitution _makeSubstitutionForMember(Class host, Member member) {
|
|
final Supertype hostType =
|
|
hierarchy.getClassAsInstanceOf(host, member.enclosingClass!)!;
|
|
return Substitution.fromSupertype(hostType);
|
|
}
|
|
|
|
/// Check if function node [ownMember] is a valid override for [superMember].
|
|
/// Returns [null] if override is valid or an error message.
|
|
///
|
|
/// Note: this function is a copy of [SubtypeTester._isFunctionSubtypeOf]
|
|
/// but it additionally accounts for parameter covariance.
|
|
String? _checkFunctionOverride(
|
|
Class host, Procedure ownMember, Procedure superMember) {
|
|
if (ownMember.isMemberSignature ||
|
|
(ownMember.isForwardingStub && !ownMember.isForwardingSemiStub)) {
|
|
// Synthesized members are not obligated to override super members.
|
|
return null;
|
|
}
|
|
|
|
final FunctionNode ownFunction = ownMember.function;
|
|
final FunctionNode superFunction = superMember.function;
|
|
Substitution ownSubstitution = _makeSubstitutionForMember(host, ownMember);
|
|
final Substitution superSubstitution =
|
|
_makeSubstitutionForMember(host, superMember);
|
|
|
|
if (ownFunction.requiredParameterCount >
|
|
superFunction.requiredParameterCount) {
|
|
return 'override has more required parameters';
|
|
}
|
|
if (ownFunction.positionalParameters.length <
|
|
superFunction.positionalParameters.length) {
|
|
return 'super method has more positional parameters';
|
|
}
|
|
if (ownFunction.typeParameters.length !=
|
|
superFunction.typeParameters.length) {
|
|
return 'methods have different type parameters counts';
|
|
}
|
|
|
|
if (ownFunction.typeParameters.isNotEmpty) {
|
|
final Map<TypeParameter, DartType> typeParameterMap =
|
|
<TypeParameter, DartType>{};
|
|
for (int i = 0; i < ownFunction.typeParameters.length; ++i) {
|
|
TypeParameter subParameter = ownFunction.typeParameters[i];
|
|
TypeParameter superParameter = superFunction.typeParameters[i];
|
|
typeParameterMap[subParameter] = new TypeParameterType.forAlphaRenaming(
|
|
subParameter, superParameter);
|
|
}
|
|
|
|
ownSubstitution = Substitution.combine(
|
|
ownSubstitution, Substitution.fromMap(typeParameterMap));
|
|
for (int i = 0; i < ownFunction.typeParameters.length; ++i) {
|
|
TypeParameter subParameter = ownFunction.typeParameters[i];
|
|
TypeParameter superParameter = superFunction.typeParameters[i];
|
|
DartType subBound = ownSubstitution.substituteType(subParameter.bound);
|
|
if (!_isSubtypeOf(
|
|
superSubstitution.substituteType(superParameter.bound), subBound)) {
|
|
return 'type parameters have incompatible bounds';
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!_isSubtypeOf(ownSubstitution.substituteType(ownFunction.returnType),
|
|
superSubstitution.substituteType(superFunction.returnType))) {
|
|
return 'return type of override ${ownFunction.returnType} is not a'
|
|
' subtype of ${superFunction.returnType}';
|
|
}
|
|
|
|
for (int i = 0; i < superFunction.positionalParameters.length; ++i) {
|
|
final VariableDeclaration ownParameter =
|
|
ownFunction.positionalParameters[i];
|
|
final VariableDeclaration superParameter =
|
|
superFunction.positionalParameters[i];
|
|
if (!_isValidParameterOverride(
|
|
ownParameter.isCovariant,
|
|
ownSubstitution.substituteType(ownParameter.type),
|
|
superSubstitution.substituteType(superParameter.type))) {
|
|
return '''
|
|
type of parameter ${ownParameter.name} is incompatible
|
|
override declares ${ownParameter.type}
|
|
super method declares ${superParameter.type}
|
|
''';
|
|
}
|
|
}
|
|
|
|
if (superFunction.namedParameters.isEmpty) {
|
|
return null;
|
|
}
|
|
|
|
// Note: FunctionNode.namedParameters are not sorted so we convert them
|
|
// to map to make lookup faster.
|
|
final Map<String, VariableDeclaration> ownParameters =
|
|
new Map<String, VariableDeclaration>.fromIterable(
|
|
ownFunction.namedParameters,
|
|
key: (v) => v.name);
|
|
for (VariableDeclaration superParameter in superFunction.namedParameters) {
|
|
final VariableDeclaration? ownParameter =
|
|
ownParameters[superParameter.name];
|
|
if (ownParameter == null) {
|
|
return 'override is missing ${superParameter.name} parameter';
|
|
}
|
|
|
|
if (!_isValidParameterOverride(
|
|
ownParameter.isCovariant,
|
|
ownSubstitution.substituteType(ownParameter.type),
|
|
superSubstitution.substituteType(superParameter.type))) {
|
|
return '''
|
|
type of parameter ${ownParameter.name} is incompatible
|
|
override declares ${ownParameter.type}
|
|
super method declares ${superParameter.type}
|
|
''';
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/// Checks whether parameter with [ownParameterType] type is a valid override
|
|
/// for parameter with [superParameterType] type taking into account its
|
|
/// covariance and applying type parameter [substitution] if necessary.
|
|
bool _isValidParameterOverride(bool isCovariant, DartType ownParameterType,
|
|
DartType superParameterType) {
|
|
if (_isSubtypeOf(superParameterType, ownParameterType)) {
|
|
return true;
|
|
} else if (isCovariant &&
|
|
_isSubtypeOf(ownParameterType, superParameterType)) {
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
@override
|
|
void checkAssignable(TreeNode where, DartType from, DartType to) {
|
|
// Note: we permit implicit downcasts.
|
|
if (from != to && !_isSubtypeOf(from, to) && !_isSubtypeOf(to, from)) {
|
|
failures.reportNotAssignable(where, from, to);
|
|
}
|
|
}
|
|
|
|
@override
|
|
void checkUnresolvedInvocation(DartType receiver, TreeNode where) {
|
|
while (receiver is TypeParameterType) {
|
|
TypeParameterType typeParameterType = receiver;
|
|
receiver = typeParameterType.bound;
|
|
}
|
|
|
|
if (receiver is DynamicType) {
|
|
return;
|
|
}
|
|
if (receiver is InvalidType) {
|
|
return;
|
|
}
|
|
if (receiver is NeverType &&
|
|
receiver.nullability == Nullability.nonNullable) {
|
|
return;
|
|
}
|
|
|
|
// Permit any invocation or tear-off of `call` on Function type.
|
|
if ((receiver == environment.coreTypes.functionLegacyRawType ||
|
|
receiver == environment.coreTypes.functionNonNullableRawType ||
|
|
receiver is FunctionType) &&
|
|
(where is InvocationExpression && where.name.text == 'call') ||
|
|
where is FunctionTearOff) {
|
|
return;
|
|
}
|
|
|
|
fail(where, 'Unresolved method invocation on ${receiver}');
|
|
}
|
|
|
|
@override
|
|
void fail(TreeNode where, String message) {
|
|
failures.reportFailure(where, message);
|
|
}
|
|
}
|
|
|
|
bool _isKnownDartTypeImplementation(DartType type) {
|
|
return type is DynamicType ||
|
|
type is FunctionType ||
|
|
type is FutureOrType ||
|
|
type is InterfaceType ||
|
|
type is InvalidType ||
|
|
type is NeverType ||
|
|
type is NullType ||
|
|
type is TypeParameterType ||
|
|
type is TypedefType ||
|
|
type is VoidType;
|
|
}
|