blob: 2d921c82014652963b37bcdc3d3aeef0dfd2c6ac [file] [log] [blame]
// Copyright (c) 2022, 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.md file.
import 'package:kernel/ast.dart';
import 'package:kernel/src/legacy_erasure.dart';
import 'package:kernel/type_algebra.dart';
import 'package:kernel/type_environment.dart' show SubtypeCheckMode;
import '../builder/member_builder.dart';
import '../problems.dart' show unexpected;
import 'inference_visitor_base.dart';
import 'type_schema_environment.dart';
enum ObjectAccessTargetKind {
/// A valid access to a statically known instance member on a non-nullable
/// receiver.
instanceMember,
/// A potentially nullable access to a statically known instance member. This
/// is an erroneous case and a compile-time error is reported.
nullableInstanceMember,
/// A valid access to a statically known instance Object member on a
/// potentially nullable receiver.
objectMember,
/// A valid access to a statically known super member.
superMember,
/// A (non-nullable) access to the `.call` method of a function. This is used
/// for access on `Function` and on function types.
callFunction,
/// A potentially nullable access to the `.call` method of a function. This is
/// an erroneous case and a compile-time error is reported.
nullableCallFunction,
/// A valid access to an extension member.
extensionMember,
/// A potentially nullable access to an extension member on an extension of
/// a non-nullable type. This is an erroneous case and a compile-time error is
/// reported.
nullableExtensionMember,
/// An access on a receiver of type `dynamic`.
dynamic,
/// An access on a receiver of type `Never`.
never,
/// An access on a receiver of an invalid type. This case is the result of
/// a previously report error and no error is report this case.
invalid,
/// An access to a statically unknown instance member. This is an erroneous
/// case and a compile-time error is reported.
missing,
/// An access to multiple extension members, none of which are most specific.
/// This is an erroneous case and a compile-time error is reported.
ambiguous,
}
/// Result for performing an access on an object, like `o.foo`, `o.foo()` and
/// `o.foo = ...`.
abstract class ObjectAccessTarget {
final ObjectAccessTargetKind kind;
const ObjectAccessTarget.internal(this.kind);
/// Creates an access to the instance [member].
factory ObjectAccessTarget.interfaceMember(
DartType receiverType, Member member,
{required bool isPotentiallyNullable}) {
// ignore: unnecessary_null_comparison
assert(member != null);
// ignore: unnecessary_null_comparison
assert(isPotentiallyNullable != null);
return isPotentiallyNullable
? new InstanceAccessTarget.nullable(receiverType, member)
: new InstanceAccessTarget.nonNullable(receiverType, member);
}
/// Creates an access to the super [member].
factory ObjectAccessTarget.superMember(DartType receiverType, Member member) =
InstanceAccessTarget.superMember;
/// Creates an access to the Object [member].
factory ObjectAccessTarget.objectMember(
DartType receiverType, Member member) = InstanceAccessTarget.object;
/// Creates an access to the extension [member].
factory ObjectAccessTarget.extensionMember(
DartType receiverType,
Member member,
Member? tearoffTarget,
ProcedureKind kind,
List<DartType> inferredTypeArguments,
{bool isPotentiallyNullable}) = ExtensionAccessTarget;
/// Creates an access to a 'call' method on a function, i.e. a function
/// invocation.
factory ObjectAccessTarget.callFunction(DartType receiverType) =
FunctionAccessTarget.nonNullable;
/// Creates an access to a 'call' method on a potentially nullable function,
/// i.e. a function invocation.
factory ObjectAccessTarget.nullableCallFunction(DartType receiverType) =
FunctionAccessTarget.nullable;
/// Creates an access on a dynamic receiver type with no known target.
const factory ObjectAccessTarget.dynamic() = DynamicAccessTarget.dynamic;
/// Creates an access on a receiver of type Never with no known target.
const factory ObjectAccessTarget.never() = DynamicAccessTarget.never;
/// Creates an access with no target due to an invalid receiver type.
///
/// This is not in itself an error but a consequence of another error.
const factory ObjectAccessTarget.invalid() = DynamicAccessTarget.invalid;
/// Creates an access with no target.
///
/// This is an error case.
const factory ObjectAccessTarget.missing() = DynamicAccessTarget.missing;
DartType? get receiverType;
Member? get member;
/// Returns `true` if this is an access to an instance member.
bool get isInstanceMember => kind == ObjectAccessTargetKind.instanceMember;
/// Returns `true` if this is an access to a super member.
bool get isSuperMember => kind == ObjectAccessTargetKind.superMember;
/// Returns `true` if this is an access to an Object member.
bool get isObjectMember => kind == ObjectAccessTargetKind.objectMember;
/// Returns `true` if this is an access to an extension member.
bool get isExtensionMember => kind == ObjectAccessTargetKind.extensionMember;
/// Returns `true` if this is an access to the 'call' method on a function.
bool get isCallFunction => kind == ObjectAccessTargetKind.callFunction;
/// Returns `true` if this is an access to the 'call' method on a potentially
/// nullable function.
bool get isNullableCallFunction =>
kind == ObjectAccessTargetKind.nullableCallFunction;
/// Returns `true` if this is an access on a `dynamic` receiver type.
bool get isDynamic => kind == ObjectAccessTargetKind.dynamic;
/// Returns `true` if this is an access on a `Never` receiver type.
bool get isNever => kind == ObjectAccessTargetKind.never;
/// Returns `true` if this is an access on an invalid receiver type.
bool get isInvalid => kind == ObjectAccessTargetKind.invalid;
/// Returns `true` if this is an access with no target.
bool get isMissing => kind == ObjectAccessTargetKind.missing;
/// Returns `true` if this is an access with no unambiguous target. This
/// occurs when an implicit extension access is ambiguous.
bool get isAmbiguous => kind == ObjectAccessTargetKind.ambiguous;
/// Returns `true` if this is an access to an instance member on a potentially
/// nullable receiver.
bool get isNullableInstanceMember =>
kind == ObjectAccessTargetKind.nullableInstanceMember;
/// Returns `true` if this is an access to an instance member on a potentially
/// nullable receiver.
bool get isNullableExtensionMember =>
kind == ObjectAccessTargetKind.nullableExtensionMember;
/// Returns `true` if this is an access to an instance member on a potentially
/// nullable receiver.
bool get isNullable =>
isNullableInstanceMember ||
isNullableCallFunction ||
isNullableExtensionMember;
/// Returns the candidates for an ambiguous extension access.
List<ExtensionAccessCandidate> get candidates =>
throw new UnsupportedError('ObjectAccessTarget.candidates');
/// Returns the original procedure kind, if this is an extension method
/// target.
///
/// This is need because getters, setters, and methods are converted into
/// top level methods, but access and invocation should still be treated as
/// if they are the original procedure kind.
ProcedureKind get extensionMethodKind =>
throw new UnsupportedError('ObjectAccessTarget.extensionMethodKind');
/// Returns inferred type arguments for the type parameters of an extension
/// method that comes from the extension declaration.
List<DartType> get inferredExtensionTypeArguments =>
throw new UnsupportedError(
'ObjectAccessTarget.inferredExtensionTypeArguments');
/// Returns the member to use for a tearoff.
///
/// This is currently used for extension methods.
// TODO(johnniwinther): Normalize use by having `readTarget` and
// `invokeTarget`?
Member? get tearoffTarget =>
throw new UnsupportedError('ObjectAccessTarget.tearoffTarget');
FunctionType _getFunctionType(
InferenceVisitorBase base, DartType calleeType) {
calleeType = base.resolveTypeParameter(calleeType);
if (calleeType is FunctionType) {
if (!base.isNonNullableByDefault) {
calleeType = legacyErasure(calleeType);
}
return calleeType as FunctionType;
}
return base.unknownFunction;
}
/// Returns the type of this target when accessed as an invocation on
/// [receiverType].
///
/// If the target is known not to be invokable
/// [InferenceVisitorBase.unknownFunction] is returned.
///
/// For instance
///
/// abstract class Class<T> {
/// T method();
/// T Function() get getter1;
/// T get getter2;
/// }
///
/// Class<int> c = ...
/// c.method; // The getter type is `int Function()`.
/// c.getter1; // The getter type is `int Function()`.
/// c.getter2; // The getter type is [unknownFunction].
///
FunctionType getFunctionType(InferenceVisitorBase base);
/// Returns the type of this target when accessed as a getter on
/// [receiverType].
///
/// For instance
///
/// abstract class Class<T> {
/// T method();
/// T get getter;
/// }
///
/// Class<int> c = ...
/// c.method; // The getter type is `int Function()`.
/// c.getter; // The getter type is `int`.
///
DartType getGetterType(InferenceVisitorBase base);
/// Returns the type of this target when accessed as a setter on
/// [receiverType].
///
/// For instance
///
/// class Class<T> {
/// void set setter(T value) {}
/// }
///
/// Class<int> c = ...
/// c.setter = 42; // The setter type is `int`.
///
DartType getSetterType(InferenceVisitorBase base);
/// Returns `true` if this target is binary operator, whose return type is
/// specialized to take the operand type into account.
///
/// This is for instance the case for `int.+` which returns `int` when the
/// operand is of type `int` and `num` otherwise.
bool isSpecialCasedBinaryOperator(InferenceVisitorBase base) => false;
/// Returns `true` if this target is ternary operator, whose return type is
/// specialized to take the operand type into account.
///
/// This is for instance the case for `int.clamp` which returns `int` when the
/// operands are of type `int` and `num` otherwise.
bool isSpecialCasedTernaryOperator(InferenceVisitorBase base) => false;
/// Returns the type of the 'key' parameter in an [] or []= implementation.
///
/// For instance
///
/// class Class<K, V> {
/// V operator [](K key) => throw '';
/// void operator []=(K key, V value) {}
/// }
///
/// extension Extension<K, V> on Class<K, V> {
/// V operator [](K key) => throw '';
/// void operator []=(K key, V value) {}
/// }
///
/// new Class<int, String>()[0]; // The key type is `int`.
/// new Class<int, String>()[0] = 'foo'; // The key type is `int`.
/// Extension<int, String>(null)[0]; // The key type is `int`.
/// Extension<int, String>(null)[0] = 'foo'; // The key type is `int`.
///
DartType getIndexKeyType(InferenceVisitorBase base);
/// Returns the type of the 'value' parameter in an []= implementation.
///
/// For instance
///
/// class Class<K, V> {
/// void operator []=(K key, V value) {}
/// }
///
/// extension Extension<K, V> on Class<K, V> {
/// void operator []=(K key, V value) {}
/// }
///
/// new Class<int, String>()[0] = 'foo'; // The value type is `String`.
/// Extension<int, String>(null)[0] = 'foo'; // The value type is `String`.
///
DartType getIndexSetValueType(InferenceVisitorBase base);
/// Returns the return type of the invocation of this target on
/// [receiverType].
///
/// If the target is known not to be invokable `dynamic` is returned.
///
/// For instance
///
/// abstract class Class<T> {
/// T method();
/// T Function() get getter1;
/// T get getter2;
/// }
///
/// Class<int> c = ...
/// c.method(); // The return type is `int`.
/// c.getter1(); // The return type is `int`.
/// c.getter2(); // The return type is `dynamic`.
///
// TODO(johnniwinther): Cleanup [getFunctionType], [getReturnType],
// [getIndexKeyType] and [getIndexSetValueType]. We shouldn't need that many.
DartType getReturnType(InferenceVisitorBase base);
/// Returns the operand type of this target accessed as a binary operation
/// on [receiverType].
///
/// For instance
///
/// abstract class Class<T> {
/// T operator +(T t);
/// T operator - (List<T> t);
/// }
///
/// Class<int> c = ...
/// c + 0; // The operand type is `int`.
/// c - [0]; // The operand type is `List<int>`.
///
DartType getBinaryOperandType(InferenceVisitorBase base);
@override
String toString() => 'ObjectAccessTarget($kind,$member)';
}
class InstanceAccessTarget extends ObjectAccessTarget {
@override
final DartType receiverType;
@override
final Member member;
/// Creates an access to the instance [member].
InstanceAccessTarget.nonNullable(this.receiverType, this.member)
: super.internal(ObjectAccessTargetKind.instanceMember);
/// Creates an access to the instance [member].
InstanceAccessTarget.nullable(this.receiverType, this.member)
: super.internal(ObjectAccessTargetKind.nullableInstanceMember);
/// Creates an access to the super [member].
InstanceAccessTarget.superMember(this.receiverType, this.member)
: super.internal(ObjectAccessTargetKind.superMember);
/// Creates an access to the Object [member].
InstanceAccessTarget.object(this.receiverType, this.member)
: super.internal(ObjectAccessTargetKind.objectMember);
@override
FunctionType getFunctionType(InferenceVisitorBase base) {
return _getFunctionType(
base,
base.getGetterTypeForMemberTarget(member, receiverType,
isSuper: isSuperMember));
}
@override
DartType getGetterType(InferenceVisitorBase base) {
return base.getGetterTypeForMemberTarget(member, receiverType,
isSuper: isSuperMember);
}
@override
DartType getSetterType(InferenceVisitorBase base) {
Member interfaceMember = member;
Class memberClass = interfaceMember.enclosingClass!;
assert(
interfaceMember is Field && interfaceMember.hasSetter ||
interfaceMember is Procedure && interfaceMember.isSetter,
"Unexpected setter target $interfaceMember");
DartType setterType = isSuperMember
? interfaceMember.superSetterType
: interfaceMember.setterType;
if (memberClass.typeParameters.isNotEmpty) {
DartType resolvedReceiverType = base.resolveTypeParameter(receiverType);
if (resolvedReceiverType is InterfaceType) {
setterType = Substitution.fromPairs(
memberClass.typeParameters,
base.classHierarchy.getTypeArgumentsAsInstanceOf(
resolvedReceiverType, memberClass)!)
.substituteType(setterType);
}
}
if (!base.isNonNullableByDefault) {
setterType = legacyErasure(setterType);
}
return setterType;
}
@override
bool isSpecialCasedBinaryOperator(InferenceVisitorBase base) {
return member is Procedure &&
base.typeSchemaEnvironment.isSpecialCasesBinaryForReceiverType(
member as Procedure, receiverType,
isNonNullableByDefault: base.isNonNullableByDefault);
}
@override
bool isSpecialCasedTernaryOperator(InferenceVisitorBase base) {
return member is Procedure &&
base.typeSchemaEnvironment.isSpecialCasedTernaryOperator(
member as Procedure,
isNonNullableByDefault: base.isNonNullableByDefault);
}
@override
DartType getIndexKeyType(InferenceVisitorBase base) {
FunctionType functionType = _getFunctionType(
base,
base.getGetterTypeForMemberTarget(member, receiverType,
isSuper: isSuperMember));
if (functionType.positionalParameters.length >= 1) {
return functionType.positionalParameters[0];
}
return const DynamicType();
}
@override
DartType getIndexSetValueType(InferenceVisitorBase base) {
FunctionType functionType = _getFunctionType(
base,
base.getGetterTypeForMemberTarget(member, receiverType,
isSuper: isSuperMember));
if (functionType.positionalParameters.length >= 2) {
return functionType.positionalParameters[1];
}
return const DynamicType();
}
@override
DartType getReturnType(InferenceVisitorBase base) {
FunctionType functionType = _getFunctionType(
base,
base.getGetterTypeForMemberTarget(member, receiverType,
isSuper: isSuperMember));
return functionType.returnType;
}
@override
DartType getBinaryOperandType(InferenceVisitorBase base) {
FunctionType functionType = _getFunctionType(
base,
base.getGetterTypeForMemberTarget(member, receiverType,
isSuper: isSuperMember));
if (functionType.positionalParameters.isNotEmpty) {
return functionType.positionalParameters.first;
}
return const DynamicType();
}
}
class FunctionAccessTarget extends ObjectAccessTarget {
@override
final DartType receiverType;
/// Creates an access to a 'call' method on a function, i.e. a function
/// invocation.
FunctionAccessTarget.nonNullable(this.receiverType)
: super.internal(ObjectAccessTargetKind.callFunction);
/// Creates an access to a 'call' method on a potentially nullable function,
/// i.e. a function invocation.
FunctionAccessTarget.nullable(this.receiverType)
: super.internal(ObjectAccessTargetKind.nullableCallFunction);
@override
Member? get member => null;
@override
FunctionType getFunctionType(InferenceVisitorBase base) {
return _getFunctionType(base, receiverType);
}
@override
DartType getGetterType(InferenceVisitorBase base) {
return receiverType;
}
@override
DartType getSetterType(InferenceVisitorBase base) {
throw unexpected(runtimeType.toString(), 'getSetterType', -1, null);
}
@override
DartType getIndexKeyType(InferenceVisitorBase base) {
return const DynamicType();
}
@override
DartType getIndexSetValueType(InferenceVisitorBase base) {
return const DynamicType();
}
@override
DartType getReturnType(InferenceVisitorBase base) {
return getFunctionType(base).returnType;
}
@override
DartType getBinaryOperandType(InferenceVisitorBase base) {
return const DynamicType();
}
}
class DynamicAccessTarget extends ObjectAccessTarget {
/// Creates an access on a dynamic receiver type with no known target.
const DynamicAccessTarget.dynamic()
: super.internal(ObjectAccessTargetKind.dynamic);
/// Creates an access on a receiver of type Never with no known target.
const DynamicAccessTarget.never()
: super.internal(ObjectAccessTargetKind.never);
/// Creates an access with no target due to an invalid receiver type.
///
/// This is not in itself an error but a consequence of another error.
const DynamicAccessTarget.invalid()
: super.internal(ObjectAccessTargetKind.invalid);
/// Creates an access with no target.
///
/// This is an error case.
const DynamicAccessTarget.missing()
: super.internal(ObjectAccessTargetKind.missing);
@override
DartType? get receiverType => null;
@override
Member? get member => null;
@override
FunctionType getFunctionType(InferenceVisitorBase base) {
return base.unknownFunction;
}
@override
DartType getGetterType(InferenceVisitorBase base) {
return isInvalid
? const InvalidType()
: (isNever ? const NeverType.nonNullable() : const DynamicType());
}
@override
DartType getSetterType(InferenceVisitorBase base) {
return isInvalid ? const InvalidType() : const DynamicType();
}
@override
DartType getIndexKeyType(InferenceVisitorBase base) {
return isInvalid ? const InvalidType() : const DynamicType();
}
@override
DartType getIndexSetValueType(InferenceVisitorBase base) {
return isInvalid ? const InvalidType() : const DynamicType();
}
@override
DartType getReturnType(InferenceVisitorBase base) {
return isInvalid
? const InvalidType()
: (isNever ? const NeverType.nonNullable() : const DynamicType());
}
@override
DartType getBinaryOperandType(InferenceVisitorBase base) {
return isInvalid ? const InvalidType() : const DynamicType();
}
}
class ExtensionAccessTarget extends ObjectAccessTarget {
@override
final DartType receiverType;
@override
final Member member;
@override
final Member? tearoffTarget;
@override
final ProcedureKind extensionMethodKind;
@override
final List<DartType> inferredExtensionTypeArguments;
ExtensionAccessTarget(this.receiverType, this.member, this.tearoffTarget,
this.extensionMethodKind, this.inferredExtensionTypeArguments,
{bool isPotentiallyNullable: false})
: super.internal(isPotentiallyNullable
? ObjectAccessTargetKind.nullableExtensionMember
: ObjectAccessTargetKind.extensionMember);
@override
FunctionType getFunctionType(InferenceVisitorBase base) {
switch (extensionMethodKind) {
case ProcedureKind.Method:
case ProcedureKind.Operator:
FunctionType functionType = member.function!
.computeFunctionType(base.libraryBuilder.nonNullable);
if (!base.isNonNullableByDefault) {
functionType = legacyErasure(functionType) as FunctionType;
}
return functionType;
case ProcedureKind.Getter:
// TODO(johnniwinther): Handle implicit .call on extension getter.
return _getFunctionType(base, member.function!.returnType);
case ProcedureKind.Setter:
case ProcedureKind.Factory:
throw unexpected('$this', 'getFunctionType', -1, null);
}
}
@override
DartType getGetterType(InferenceVisitorBase base) {
switch (extensionMethodKind) {
case ProcedureKind.Method:
case ProcedureKind.Operator:
FunctionType functionType = member.function!
.computeFunctionType(base.libraryBuilder.nonNullable);
List<TypeParameter> extensionTypeParameters = functionType
.typeParameters
.take(inferredExtensionTypeArguments.length)
.toList();
Substitution substitution = Substitution.fromPairs(
extensionTypeParameters, inferredExtensionTypeArguments);
DartType resultType = substitution.substituteType(new FunctionType(
functionType.positionalParameters.skip(1).toList(),
functionType.returnType,
base.libraryBuilder.nonNullable,
namedParameters: functionType.namedParameters,
typeParameters: functionType.typeParameters
.skip(inferredExtensionTypeArguments.length)
.toList(),
requiredParameterCount: functionType.requiredParameterCount - 1));
if (!base.isNonNullableByDefault) {
resultType = legacyErasure(resultType);
}
return resultType;
case ProcedureKind.Getter:
FunctionType functionType = member.function!
.computeFunctionType(base.libraryBuilder.nonNullable);
List<TypeParameter> extensionTypeParameters = functionType
.typeParameters
.take(inferredExtensionTypeArguments.length)
.toList();
Substitution substitution = Substitution.fromPairs(
extensionTypeParameters, inferredExtensionTypeArguments);
DartType resultType =
substitution.substituteType(functionType.returnType);
if (!base.isNonNullableByDefault) {
resultType = legacyErasure(resultType);
}
return resultType;
case ProcedureKind.Setter:
case ProcedureKind.Factory:
throw unexpected('$this', 'getGetterType', -1, null);
}
}
@override
DartType getSetterType(InferenceVisitorBase base) {
switch (extensionMethodKind) {
case ProcedureKind.Setter:
FunctionType functionType = member.function!
.computeFunctionType(base.libraryBuilder.nonNullable);
List<TypeParameter> extensionTypeParameters = functionType
.typeParameters
.take(inferredExtensionTypeArguments.length)
.toList();
Substitution substitution = Substitution.fromPairs(
extensionTypeParameters, inferredExtensionTypeArguments);
DartType setterType =
substitution.substituteType(functionType.positionalParameters[1]);
if (!base.isNonNullableByDefault) {
setterType = legacyErasure(setterType);
}
return setterType;
case ProcedureKind.Method:
case ProcedureKind.Getter:
case ProcedureKind.Operator:
case ProcedureKind.Factory:
throw unexpected('$this', 'getSetterType', -1, null);
}
}
@override
DartType getIndexKeyType(InferenceVisitorBase base) {
switch (extensionMethodKind) {
case ProcedureKind.Operator:
FunctionType functionType = member.function!
.computeFunctionType(base.libraryBuilder.nonNullable);
if (functionType.positionalParameters.length >= 2) {
DartType keyType = functionType.positionalParameters[1];
if (functionType.typeParameters.isNotEmpty) {
Substitution substitution = Substitution.fromPairs(
functionType.typeParameters, inferredExtensionTypeArguments);
keyType = substitution.substituteType(keyType);
}
if (!base.isNonNullableByDefault) {
keyType = legacyErasure(keyType);
}
return keyType;
}
return const InvalidType();
case ProcedureKind.Method:
case ProcedureKind.Getter:
case ProcedureKind.Setter:
case ProcedureKind.Factory:
throw unexpected('$this', 'getIndexKeyType', -1, null);
}
}
@override
DartType getIndexSetValueType(InferenceVisitorBase base) {
switch (extensionMethodKind) {
case ProcedureKind.Operator:
FunctionType functionType = member.function!
.computeFunctionType(base.libraryBuilder.nonNullable);
if (functionType.positionalParameters.length >= 3) {
DartType indexType = functionType.positionalParameters[2];
if (functionType.typeParameters.isNotEmpty) {
Substitution substitution = Substitution.fromPairs(
functionType.typeParameters, inferredExtensionTypeArguments);
indexType = substitution.substituteType(indexType);
}
if (!base.isNonNullableByDefault) {
indexType = legacyErasure(indexType);
}
return indexType;
}
return const InvalidType();
case ProcedureKind.Method:
case ProcedureKind.Getter:
case ProcedureKind.Setter:
case ProcedureKind.Factory:
throw unexpected('$this', 'getIndexSetValueType', -1, null);
}
}
@override
DartType getReturnType(InferenceVisitorBase base) {
switch (extensionMethodKind) {
case ProcedureKind.Operator:
case ProcedureKind.Method:
case ProcedureKind.Getter:
FunctionType functionType = member.function!
.computeFunctionType(base.libraryBuilder.nonNullable);
DartType returnType = functionType.returnType;
if (functionType.typeParameters.isNotEmpty) {
Substitution substitution = Substitution.fromPairs(
functionType.typeParameters, inferredExtensionTypeArguments);
returnType = substitution.substituteType(returnType);
}
if (!base.isNonNullableByDefault) {
returnType = legacyErasure(returnType);
}
return returnType;
case ProcedureKind.Setter:
return const VoidType();
case ProcedureKind.Factory:
throw unexpected('$this', 'getReturnType', -1, null);
}
}
@override
DartType getBinaryOperandType(InferenceVisitorBase base) {
switch (extensionMethodKind) {
case ProcedureKind.Operator:
FunctionType functionType = member.function!
.computeFunctionType(base.libraryBuilder.nonNullable);
if (functionType.positionalParameters.length > 1) {
DartType keyType = functionType.positionalParameters[1];
if (functionType.typeParameters.isNotEmpty) {
Substitution substitution = Substitution.fromPairs(
functionType.typeParameters, inferredExtensionTypeArguments);
keyType = substitution.substituteType(keyType);
}
if (!base.isNonNullableByDefault) {
keyType = legacyErasure(keyType);
}
return keyType;
}
return const InvalidType();
case ProcedureKind.Method:
case ProcedureKind.Getter:
case ProcedureKind.Setter:
return const InvalidType();
case ProcedureKind.Factory:
throw unexpected('$this', 'getBinaryOperandType', -1, null);
}
}
@override
String toString() =>
'ExtensionAccessTarget($kind,$member,$extensionMethodKind,'
'$inferredExtensionTypeArguments)';
}
class AmbiguousExtensionAccessTarget extends ObjectAccessTarget {
@override
final DartType receiverType;
@override
final List<ExtensionAccessCandidate> candidates;
AmbiguousExtensionAccessTarget(this.receiverType, this.candidates)
: super.internal(ObjectAccessTargetKind.ambiguous);
@override
Member? get member => null;
@override
FunctionType getFunctionType(InferenceVisitorBase base) {
return base.unknownFunction;
}
@override
DartType getGetterType(InferenceVisitorBase base) {
return const InvalidType();
}
@override
DartType getSetterType(InferenceVisitorBase base) {
return const InvalidType();
}
@override
DartType getIndexKeyType(InferenceVisitorBase base) {
return const InvalidType();
}
@override
DartType getIndexSetValueType(InferenceVisitorBase base) {
return const InvalidType();
}
@override
DartType getReturnType(InferenceVisitorBase base) {
return const InvalidType();
}
@override
DartType getBinaryOperandType(InferenceVisitorBase base) {
return const InvalidType();
}
@override
String toString() => 'AmbiguousExtensionAccessTarget($kind,$candidates)';
}
class ExtensionAccessCandidate {
final MemberBuilder memberBuilder;
final bool isPlatform;
final DartType onType;
final DartType onTypeInstantiateToBounds;
final ObjectAccessTarget target;
ExtensionAccessCandidate(this.memberBuilder, this.onType,
this.onTypeInstantiateToBounds, this.target,
{required this.isPlatform})
// ignore: unnecessary_null_comparison
: assert(isPlatform != null);
bool? isMoreSpecificThan(TypeSchemaEnvironment typeSchemaEnvironment,
ExtensionAccessCandidate other) {
if (this.isPlatform == other.isPlatform) {
// Both are platform or not platform.
bool thisIsSubtype = typeSchemaEnvironment.isSubtypeOf(
this.onType, other.onType, SubtypeCheckMode.withNullabilities);
bool thisIsSupertype = typeSchemaEnvironment.isSubtypeOf(
other.onType, this.onType, SubtypeCheckMode.withNullabilities);
if (thisIsSubtype && !thisIsSupertype) {
// This is subtype of other and not vice-versa.
return true;
} else if (thisIsSupertype && !thisIsSubtype) {
// [other] is subtype of this and not vice-versa.
return false;
} else if (thisIsSubtype || thisIsSupertype) {
thisIsSubtype = typeSchemaEnvironment.isSubtypeOf(
this.onTypeInstantiateToBounds,
other.onTypeInstantiateToBounds,
SubtypeCheckMode.withNullabilities);
thisIsSupertype = typeSchemaEnvironment.isSubtypeOf(
other.onTypeInstantiateToBounds,
this.onTypeInstantiateToBounds,
SubtypeCheckMode.withNullabilities);
if (thisIsSubtype && !thisIsSupertype) {
// This is subtype of other and not vice-versa.
return true;
} else if (thisIsSupertype && !thisIsSubtype) {
// [other] is subtype of this and not vice-versa.
return false;
}
}
} else if (other.isPlatform) {
// This is not platform, [other] is: this is more specific.
return true;
} else {
// This is platform, [other] is not: other is more specific.
return false;
}
// Neither is more specific than the other.
return null;
}
}