| // 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 isCovariantByDeclaration = ownMember is Field |
| ? ownMember.isCovariantByDeclaration |
| : ownMember |
| .function!.positionalParameters[0].isCovariantByDeclaration; |
| if (!_isValidParameterOverride( |
| isCovariantByDeclaration, ownType, superType)) { |
| if (isCovariantByDeclaration) { |
| 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(cstefantsova): 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(cstefantsova): 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.isCovariantByDeclaration, |
| 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.isCovariantByDeclaration, |
| 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 isCovariantByDeclaration, |
| DartType ownParameterType, DartType superParameterType) { |
| if (_isSubtypeOf(superParameterType, ownParameterType)) { |
| return true; |
| } else if (isCovariantByDeclaration && |
| _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; |
| } |