blob: 991fa4ecb16bbe87c315fc6a25aebe52f72e9605 [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.
library fasta.class_hierarchy_builder;
import 'package:kernel/ast.dart'
show
Class,
DartType,
Field,
FunctionNode,
InterfaceType,
InvalidType,
Member,
Name,
Procedure,
ProcedureKind,
Supertype,
TypeParameter,
TypeParameterType,
VariableDeclaration;
import 'package:kernel/class_hierarchy.dart' show ClassHierarchy;
import 'package:kernel/core_types.dart' show CoreTypes;
import 'package:kernel/type_algebra.dart' show Substitution;
import '../dill/dill_member_builder.dart' show DillMemberBuilder;
import '../loader.dart' show Loader;
import '../messages.dart'
show
LocatedMessage,
Message,
messageDeclaredMemberConflictsWithInheritedMember,
messageDeclaredMemberConflictsWithInheritedMemberCause,
messageInheritedMembersConflict,
messageInheritedMembersConflictCause1,
messageInheritedMembersConflictCause2,
messageStaticAndInstanceConflict,
messageStaticAndInstanceConflictCause,
templateCantInferReturnTypeDueToInconsistentOverrides,
templateCantInferTypeDueToInconsistentOverrides,
templateCombinedMemberSignatureFailed,
templateDuplicatedDeclaration,
templateDuplicatedDeclarationCause,
templateDuplicatedDeclarationUse,
templateMissingImplementationCause,
templateMissingImplementationNotAbstract;
import '../names.dart' show noSuchMethodName;
import '../problems.dart' show unhandled;
import '../scope.dart' show Scope;
import '../source/source_library_builder.dart' show SourceLibraryBuilder;
import '../source/source_loader.dart' show SourceLoader;
import '../type_inference/standard_bounds.dart' show StandardBounds;
import '../type_inference/type_constraint_gatherer.dart'
show TypeConstraintGatherer;
import '../type_inference/type_inferrer.dart' show MixinInferrer;
import '../type_inference/type_schema.dart' show UnknownType;
import '../type_inference/type_schema_environment.dart' show TypeConstraint;
import 'forwarding_node.dart' show ForwardingNode;
import 'kernel_builder.dart'
show
Builder,
FormalParameterBuilder,
ImplicitFieldType,
ClassBuilder,
FieldBuilder,
NamedTypeBuilder,
ProcedureBuilder,
LibraryBuilder,
MemberBuilder,
NullabilityBuilder,
TypeBuilder,
TypeVariableBuilder;
import 'types.dart' show Types;
const DebugLogger debug =
const bool.fromEnvironment("debug.hierarchy") ? const DebugLogger() : null;
class DebugLogger {
const DebugLogger();
void log(Object message) => print(message);
}
int compareDeclarations(Builder a, Builder b) {
return ClassHierarchy.compareMembers(a.target, b.target);
}
ProcedureKind memberKind(Member member) {
return member is Procedure ? member.kind : null;
}
bool isNameVisibleIn(Name name, LibraryBuilder libraryBuilder) {
return !name.isPrivate || name.library == libraryBuilder.library;
}
/// Returns true if [a] is a class member conflict with [b]. [a] is assumed to
/// be declared in the class, [b] is assumed to be inherited.
///
/// See the section named "Class Member Conflicts" in [Dart Programming
/// Language Specification](
/// ../../../../../../docs/language/dartLangSpec.tex#classMemberConflicts).
bool isInheritanceConflict(Builder a, Builder b) {
if (a.isStatic) return true;
if (memberKind(a.target) == memberKind(b.target)) return false;
if (a.isField) return !(b.isField || b.isGetter || b.isSetter);
if (b.isField) return !(a.isField || a.isGetter || a.isSetter);
if (a.isSetter) return !(b.isGetter || b.isSetter);
if (b.isSetter) return !(a.isGetter || a.isSetter);
if (a is InterfaceConflict || b is InterfaceConflict) return false;
return true;
}
bool impliesSetter(Builder declaration) {
return declaration.isField && !(declaration.isFinal || declaration.isConst);
}
bool hasSameSignature(FunctionNode a, FunctionNode b) {
List<TypeParameter> aTypeParameters = a.typeParameters;
List<TypeParameter> bTypeParameters = b.typeParameters;
int typeParameterCount = aTypeParameters.length;
if (typeParameterCount != bTypeParameters.length) return false;
Substitution substitution;
if (typeParameterCount != 0) {
List<DartType> types = new List<DartType>(typeParameterCount);
for (int i = 0; i < typeParameterCount; i++) {
types[i] = new TypeParameterType(aTypeParameters[i]);
}
substitution = Substitution.fromPairs(bTypeParameters, types);
for (int i = 0; i < typeParameterCount; i++) {
DartType aBound = aTypeParameters[i].bound;
DartType bBound = substitution.substituteType(bTypeParameters[i].bound);
if (aBound != bBound) return false;
}
}
if (a.requiredParameterCount != b.requiredParameterCount) return false;
List<VariableDeclaration> aPositionalParameters = a.positionalParameters;
List<VariableDeclaration> bPositionalParameters = b.positionalParameters;
if (aPositionalParameters.length != bPositionalParameters.length) {
return false;
}
for (int i = 0; i < aPositionalParameters.length; i++) {
VariableDeclaration aParameter = aPositionalParameters[i];
VariableDeclaration bParameter = bPositionalParameters[i];
if (aParameter.isCovariant != bParameter.isCovariant) return false;
DartType aType = aParameter.type;
DartType bType = bParameter.type;
if (substitution != null) {
bType = substitution.substituteType(bType);
}
if (aType != bType) return false;
}
List<VariableDeclaration> aNamedParameters = a.namedParameters;
List<VariableDeclaration> bNamedParameters = b.namedParameters;
if (aNamedParameters.length != bNamedParameters.length) return false;
for (int i = 0; i < aNamedParameters.length; i++) {
VariableDeclaration aParameter = aNamedParameters[i];
VariableDeclaration bParameter = bNamedParameters[i];
if (aParameter.isCovariant != bParameter.isCovariant) return false;
if (aParameter.name != bParameter.name) return false;
DartType aType = aParameter.type;
DartType bType = bParameter.type;
if (substitution != null) {
bType = substitution.substituteType(bType);
}
if (aType != bType) return false;
}
DartType aReturnType = a.returnType;
DartType bReturnType = b.returnType;
if (substitution != null) {
bReturnType = substitution.substituteType(bReturnType);
}
return aReturnType == bReturnType;
}
class ClassHierarchyBuilder {
final Map<Class, ClassHierarchyNode> nodes = <Class, ClassHierarchyNode>{};
final ClassBuilder objectClassBuilder;
final Loader loader;
final Class objectClass;
final Class futureClass;
final Class futureOrClass;
final Class functionClass;
final Class nullClass;
final List<DelayedOverrideCheck> overrideChecks = <DelayedOverrideCheck>[];
final List<DelayedMember> delayedMemberChecks = <DelayedMember>[];
// TODO(dmitryas): Consider removing this.
final CoreTypes coreTypes;
Types types;
ClassHierarchyBuilder(this.objectClassBuilder, this.loader, this.coreTypes)
: objectClass = objectClassBuilder.cls,
futureClass = coreTypes.futureClass,
futureOrClass = coreTypes.futureOrClass,
functionClass = coreTypes.functionClass,
nullClass = coreTypes.nullClass {
types = new Types(this);
}
ClassHierarchyNode getNodeFromClass(ClassBuilder classBuilder) {
return nodes[classBuilder.cls] ??=
new ClassHierarchyNodeBuilder(this, classBuilder).build();
}
ClassHierarchyNode getNodeFromType(TypeBuilder type) {
ClassBuilder cls = getClass(type);
return cls == null ? null : getNodeFromClass(cls);
}
ClassHierarchyNode getNodeFromKernelClass(Class cls) {
return nodes[cls] ??
getNodeFromClass(loader.computeClassBuilderFromTargetClass(cls));
}
TypeBuilder asSupertypeOf(Class cls, Class supertype) {
ClassHierarchyNode clsNode = getNodeFromKernelClass(cls);
if (cls == supertype) {
return new NamedTypeBuilder(
clsNode.classBuilder.name, const NullabilityBuilder.omitted(), null)
..bind(clsNode.classBuilder);
}
ClassHierarchyNode supertypeNode = getNodeFromKernelClass(supertype);
List<TypeBuilder> supertypes = clsNode.superclasses;
int depth = supertypeNode.depth;
Builder supertypeDeclaration = supertypeNode.classBuilder;
if (depth < supertypes.length) {
TypeBuilder asSupertypeOf = supertypes[depth];
if (asSupertypeOf.declaration == supertypeDeclaration) {
return asSupertypeOf;
}
}
supertypes = clsNode.interfaces;
for (int i = 0; i < supertypes.length; i++) {
TypeBuilder type = supertypes[i];
if (type.declaration == supertypeDeclaration) return type;
}
return null;
}
InterfaceType getKernelTypeAsInstanceOf(
InterfaceType type, Class superclass) {
Class kernelClass = type.classNode;
if (kernelClass == superclass) return type;
if (kernelClass == nullClass) {
if (superclass.typeParameters.isEmpty) {
return coreTypes.legacyRawType(superclass);
} else {
// This is a safe fall-back for dealing with `Null`. It will likely be
// faster to check for `Null` before calling this method.
return new InterfaceType(
superclass,
new List<DartType>.filled(
superclass.typeParameters.length, coreTypes.nullType));
}
}
NamedTypeBuilder supertype = asSupertypeOf(kernelClass, superclass);
if (supertype == null) return null;
if (supertype.arguments == null && superclass.typeParameters.isEmpty) {
return coreTypes.legacyRawType(superclass);
}
return Substitution.fromInterfaceType(type)
.substituteType(supertype.build(null));
}
InterfaceType getKernelLegacyLeastUpperBound(
InterfaceType type1, InterfaceType type2) {
if (type1 == type2) return type1;
ClassHierarchyNode node1 = getNodeFromKernelClass(type1.classNode);
ClassHierarchyNode node2 = getNodeFromKernelClass(type2.classNode);
Set<ClassHierarchyNode> nodes1 = node1.computeAllSuperNodes(this).toSet();
List<ClassHierarchyNode> nodes2 = node2.computeAllSuperNodes(this);
List<ClassHierarchyNode> common = <ClassHierarchyNode>[];
for (int i = 0; i < nodes2.length; i++) {
ClassHierarchyNode node = nodes2[i];
if (node == null) continue;
if (nodes1.contains(node)) {
DartType candidate1 =
getKernelTypeAsInstanceOf(type1, node.classBuilder.cls);
DartType candidate2 =
getKernelTypeAsInstanceOf(type2, node.classBuilder.cls);
if (candidate1 == candidate2) {
common.add(node);
}
}
}
if (common.length == 1) return coreTypes.objectLegacyRawType;
common.sort(ClassHierarchyNode.compareMaxInheritancePath);
for (int i = 0; i < common.length - 1; i++) {
ClassHierarchyNode node = common[i];
if (node.maxInheritancePath != common[i + 1].maxInheritancePath) {
return getKernelTypeAsInstanceOf(type1, node.classBuilder.cls);
} else {
do {
i++;
} while (node.maxInheritancePath == common[i + 1].maxInheritancePath);
}
}
return coreTypes.objectLegacyRawType;
}
Member getInterfaceMemberKernel(Class cls, Name name, bool isSetter) {
return getNodeFromKernelClass(cls)
.getInterfaceMember(name, isSetter)
?.target;
}
Member getDispatchTargetKernel(Class cls, Name name, bool isSetter) {
return getNodeFromKernelClass(cls)
.getDispatchTarget(name, isSetter)
?.target;
}
Member getCombinedMemberSignatureKernel(Class cls, Name name, bool isSetter,
int charOffset, SourceLibraryBuilder library) {
Builder declaration =
getNodeFromKernelClass(cls).getInterfaceMember(name, isSetter);
if (declaration?.isStatic ?? true) return null;
if (declaration.next != null) {
library?.addProblem(
templateDuplicatedDeclarationUse.withArguments(name.name),
charOffset,
name.name.length,
library.fileUri);
return null;
}
if (declaration is DelayedMember) {
return declaration.check(this);
} else {
return declaration.target;
}
}
static ClassHierarchyBuilder build(ClassBuilder objectClass,
List<ClassBuilder> classes, SourceLoader loader, CoreTypes coreTypes) {
ClassHierarchyBuilder hierarchy =
new ClassHierarchyBuilder(objectClass, loader, coreTypes);
for (int i = 0; i < classes.length; i++) {
ClassBuilder classBuilder = classes[i];
if (!classBuilder.isPatch) {
hierarchy.nodes[classBuilder.cls] =
new ClassHierarchyNodeBuilder(hierarchy, classBuilder).build();
} else {
// TODO(ahe): Merge the injected members of patch into the hierarchy
// node of `cls.origin`.
}
}
return hierarchy;
}
}
class ClassHierarchyNodeBuilder {
final ClassHierarchyBuilder hierarchy;
final ClassBuilder classBuilder;
bool hasNoSuchMethod = false;
List<Builder> abstractMembers = null;
ClassHierarchyNodeBuilder(this.hierarchy, this.classBuilder);
ClassBuilder get objectClass => hierarchy.objectClassBuilder;
final Map<Class, Substitution> substitutions = <Class, Substitution>{};
/// When merging `aList` and `bList`, [a] (from `aList`) and [b] (from
/// `bList`) each have the same name.
///
/// If [mergeKind] is `MergeKind.superclass`, [a] should override [b].
///
/// If [mergeKind] is `MergeKind.interfaces`, we need to record them and
/// solve the conflict later.
///
/// If [mergeKind] is `MergeKind.supertypes`, [a] should implement [b], and
/// [b] is implicitly abstract.
Builder handleMergeConflict(Builder a, Builder b, MergeKind mergeKind) {
debug?.log(
"handleMergeConflict: ${fullName(a)} ${fullName(b)} ${mergeKind}");
// TODO(ahe): Enable this optimization, but be careful about abstract
// methods overriding concrete methods.
// if (cls is DillClassBuilder) return a;
if (a == b) return a;
if (a.next != null || b.next != null) {
// Don't check overrides involving duplicated members.
return a;
}
Builder result = checkInheritanceConflict(a, b);
if (result != null) return result;
result = a;
switch (mergeKind) {
case MergeKind.superclassMembers:
case MergeKind.superclassSetters:
// [a] is a method declared in [cls]. This means it defines the
// interface of this class regardless if its abstract.
debug?.log("superclass: checkValidOverride("
"${classBuilder.fullNameForErrors}, "
"${fullName(a)}, ${fullName(b)})");
checkValidOverride(
a, AbstractMemberOverridingImplementation.selectAbstract(b));
if (isAbstract(a)) {
if (isAbstract(b)) {
recordAbstractMember(a);
} else {
if (!classBuilder.isAbstract) {
// The interface of this class is [a]. But the implementation is
// [b]. So [b] must implement [a], unless [cls] is abstract.
checkValidOverride(b, a);
}
result = new AbstractMemberOverridingImplementation(
classBuilder,
a,
AbstractMemberOverridingImplementation.selectConcrete(b),
mergeKind == MergeKind.superclassSetters,
classBuilder.library.loader == hierarchy.loader);
hierarchy.delayedMemberChecks.add(result);
}
} else if (classBuilder.isMixinApplication &&
a.parent != classBuilder) {
result = InheritedImplementationInterfaceConflict.combined(
classBuilder,
a,
b,
mergeKind == MergeKind.superclassSetters,
classBuilder.library.loader == hierarchy.loader,
isInheritableConflict: false);
if (result is DelayedMember) {
hierarchy.delayedMemberChecks.add(result);
}
}
Member target = result.target;
if (target.enclosingClass != objectClass.cls &&
target.name == noSuchMethodName) {
hasNoSuchMethod = true;
}
break;
case MergeKind.membersWithSetters:
case MergeKind.settersWithMembers:
if (a.parent == classBuilder && b.parent != classBuilder) {
if (a is FieldBuilder) {
if (a.isFinal && b.isSetter) {
hierarchy.overrideChecks
.add(new DelayedOverrideCheck(classBuilder, a, b));
} else {
if (!inferFieldTypes(a, b)) {
hierarchy.overrideChecks
.add(new DelayedOverrideCheck(classBuilder, a, b));
}
}
} else if (a is ProcedureBuilder) {
if (!inferMethodTypes(a, b)) {
hierarchy.overrideChecks
.add(new DelayedOverrideCheck(classBuilder, a, b));
}
}
}
break;
case MergeKind.interfacesMembers:
result = InterfaceConflict.combined(classBuilder, a, b, false,
classBuilder.library.loader == hierarchy.loader);
break;
case MergeKind.interfacesSetters:
result = InterfaceConflict.combined(classBuilder, a, b, true,
classBuilder.library.loader == hierarchy.loader);
break;
case MergeKind.supertypesMembers:
case MergeKind.supertypesSetters:
// [b] is inherited from an interface so it is implicitly abstract.
a = AbstractMemberOverridingImplementation.selectAbstract(a);
b = AbstractMemberOverridingImplementation.selectAbstract(b);
// If [a] is declared in this class, it defines the interface.
if (a.parent == classBuilder) {
debug?.log("supertypes: checkValidOverride("
"${classBuilder.fullNameForErrors}, "
"${fullName(a)}, ${fullName(b)})");
checkValidOverride(a, b);
if (a is DelayedMember && !a.isInheritableConflict) {
if (b is DelayedMember) {
b.addAllDeclarationsTo(a.declarations);
} else {
addDeclarationIfDifferent(b, a.declarations);
}
}
} else {
if (isAbstract(a)) {
result = InterfaceConflict.combined(
classBuilder,
a,
b,
mergeKind == MergeKind.supertypesSetters,
classBuilder.library.loader == hierarchy.loader);
} else {
result = InheritedImplementationInterfaceConflict.combined(
classBuilder,
a,
b,
mergeKind == MergeKind.supertypesSetters,
classBuilder.library.loader == hierarchy.loader);
}
debug?.log("supertypes: ${result}");
if (result is DelayedMember) {
hierarchy.delayedMemberChecks.add(result);
}
}
break;
}
return result;
}
Builder checkInheritanceConflict(Builder a, Builder b) {
if (a is DelayedMember) {
Builder result;
for (int i = 0; i < a.declarations.length; i++) {
Builder d = checkInheritanceConflict(a.declarations[i], b);
result ??= d;
}
return result;
}
if (b is DelayedMember) {
Builder result;
for (int i = 0; i < b.declarations.length; i++) {
Builder d = checkInheritanceConflict(a, b.declarations[i]);
result ??= d;
}
return result;
}
if (isInheritanceConflict(a, b)) {
reportInheritanceConflict(a, b);
return a;
}
return null;
}
bool inferMethodTypes(ProcedureBuilder a, Builder b) {
debug?.log(
"Trying to infer types for ${fullName(a)} based on ${fullName(b)}");
if (b is DelayedMember) {
bool hasSameSignature = true;
List<Builder> declarations = b.declarations;
for (int i = 0; i < declarations.length; i++) {
if (!inferMethodTypes(a, declarations[i])) {
hasSameSignature = false;
}
}
return hasSameSignature;
}
if (a.isGetter) {
return inferGetterType(a, b);
} else if (a.isSetter) {
return inferSetterType(a, b);
}
bool hadTypesInferred = a.hadTypesInferred;
ClassBuilder aClassBuilder = a.parent;
Substitution aSubstitution;
if (classBuilder != aClassBuilder) {
assert(
substitutions.containsKey(aClassBuilder.cls),
"${classBuilder.fullNameForErrors} "
"${aClassBuilder.fullNameForErrors}");
aSubstitution = substitutions[aClassBuilder.cls];
debug?.log("${classBuilder.fullNameForErrors} -> "
"${aClassBuilder.fullNameForErrors} $aSubstitution");
}
ClassBuilder bClassBuilder = b.parent;
Substitution bSubstitution;
if (classBuilder != bClassBuilder) {
assert(
substitutions.containsKey(bClassBuilder.cls),
"${classBuilder.fullNameForErrors} "
"${bClassBuilder.fullNameForErrors}");
bSubstitution = substitutions[bClassBuilder.cls];
debug?.log("${classBuilder.fullNameForErrors} -> "
"${bClassBuilder.fullNameForErrors} $bSubstitution");
}
Procedure aProcedure = a.procedure;
if (b.target is! Procedure) {
debug?.log("Giving up 1");
return false;
}
Procedure bProcedure = b.target;
FunctionNode aFunction = aProcedure.function;
FunctionNode bFunction = bProcedure.function;
List<TypeParameter> aTypeParameters = aFunction.typeParameters;
List<TypeParameter> bTypeParameters = bFunction.typeParameters;
int typeParameterCount = aTypeParameters.length;
if (typeParameterCount != bTypeParameters.length) {
debug?.log("Giving up 2");
return false;
}
Substitution substitution;
if (typeParameterCount != 0) {
for (int i = 0; i < typeParameterCount; i++) {
copyTypeParameterCovariance(
a.parent, aTypeParameters[i], bTypeParameters[i]);
}
List<DartType> types = new List<DartType>(typeParameterCount);
for (int i = 0; i < typeParameterCount; i++) {
types[i] = new TypeParameterType(aTypeParameters[i]);
}
substitution = Substitution.fromPairs(bTypeParameters, types);
for (int i = 0; i < typeParameterCount; i++) {
DartType aBound = aTypeParameters[i].bound;
DartType bBound = substitution.substituteType(bTypeParameters[i].bound);
if (aBound != bBound) {
debug?.log("Giving up 3");
return false;
}
}
}
DartType aReturnType = aFunction.returnType;
if (aSubstitution != null) {
aReturnType = aSubstitution.substituteType(aReturnType);
}
DartType bReturnType = bFunction.returnType;
if (bSubstitution != null) {
bReturnType = bSubstitution.substituteType(bReturnType);
}
if (substitution != null) {
bReturnType = substitution.substituteType(bReturnType);
}
bool result = true;
if (aFunction.requiredParameterCount > bFunction.requiredParameterCount) {
debug?.log("Giving up 4");
return false;
}
List<VariableDeclaration> aPositional = aFunction.positionalParameters;
List<VariableDeclaration> bPositional = bFunction.positionalParameters;
if (aPositional.length < bPositional.length) {
debug?.log("Giving up 5");
return false;
}
if (aReturnType != bReturnType) {
if (a.parent == classBuilder && a.returnType == null) {
result = inferReturnType(
classBuilder, a, bReturnType, hadTypesInferred, hierarchy);
} else {
debug?.log("Giving up 6");
result = false;
}
}
for (int i = 0; i < bPositional.length; i++) {
VariableDeclaration aParameter = aPositional[i];
VariableDeclaration bParameter = bPositional[i];
copyParameterCovariance(a.parent, aParameter, bParameter);
DartType aType = aParameter.type;
if (aSubstitution != null) {
aType = aSubstitution.substituteType(aType);
}
DartType bType = bParameter.type;
if (bSubstitution != null) {
bType = bSubstitution.substituteType(bType);
}
if (substitution != null) {
bType = substitution.substituteType(bType);
}
if (aType != bType) {
if (a.parent == classBuilder && a.formals[i].type == null) {
result = inferParameterType(classBuilder, a, a.formals[i], bType,
hadTypesInferred, hierarchy);
} else {
debug?.log("Giving up 8");
result = false;
}
}
}
List<VariableDeclaration> aNamed = aFunction.namedParameters;
List<VariableDeclaration> bNamed = bFunction.namedParameters;
named:
if (aNamed.isNotEmpty || bNamed.isNotEmpty) {
if (aPositional.length != bPositional.length) {
debug?.log("Giving up 9");
result = false;
break named;
}
if (aFunction.requiredParameterCount !=
bFunction.requiredParameterCount) {
debug?.log("Giving up 10");
result = false;
break named;
}
aNamed = aNamed.toList()..sort(compareNamedParameters);
bNamed = bNamed.toList()..sort(compareNamedParameters);
int aCount = 0;
for (int bCount = 0; bCount < bNamed.length; bCount++) {
String name = bNamed[bCount].name;
for (; aCount < aNamed.length; aCount++) {
if (aNamed[aCount].name == name) break;
}
if (aCount == aNamed.length) {
debug?.log("Giving up 11");
result = false;
break named;
}
VariableDeclaration aParameter = aNamed[aCount];
VariableDeclaration bParameter = bNamed[bCount];
copyParameterCovariance(a.parent, aParameter, bParameter);
DartType aType = aParameter.type;
if (aSubstitution != null) {
aType = aSubstitution.substituteType(aType);
}
DartType bType = bParameter.type;
if (bSubstitution != null) {
bType = bSubstitution.substituteType(bType);
}
if (substitution != null) {
bType = substitution.substituteType(bType);
}
if (aType != bType) {
FormalParameterBuilder parameter;
for (int i = aPositional.length; i < a.formals.length; ++i) {
if (a.formals[i].name == name) {
parameter = a.formals[i];
break;
}
}
if (a.parent == classBuilder && parameter.type == null) {
result = inferParameterType(
classBuilder, a, parameter, bType, hadTypesInferred, hierarchy);
} else {
debug?.log("Giving up 12");
result = false;
}
}
}
}
debug?.log("Inferring types for ${fullName(a)} based on ${fullName(b)} " +
(result ? "succeeded." : "failed."));
return result;
}
bool inferGetterType(ProcedureBuilder a, Builder b) {
debug?.log(
"Inferring getter types for ${fullName(a)} based on ${fullName(b)}");
Member bTarget = b.target;
DartType bType;
if (bTarget is Field) {
bType = bTarget.type;
} else if (bTarget is Procedure) {
if (b.isSetter) {
VariableDeclaration bParameter =
bTarget.function.positionalParameters.single;
bType = bParameter.type;
if (!hasExplicitlyTypedFormalParameter(b, 0)) {
debug?.log("Giving up (type may be inferred)");
return false;
}
} else if (b.isGetter) {
bType = bTarget.function.returnType;
if (!hasExplicitReturnType(b)) {
debug?.log("Giving up (return type may be inferred)");
return false;
}
} else {
debug?.log("Giving up (not accessor: ${bTarget.kind})");
return false;
}
} else {
debug?.log("Giving up (not field/procedure: ${bTarget.runtimeType})");
return false;
}
return a.procedure.function.returnType == bType;
}
bool inferSetterType(ProcedureBuilder a, Builder b) {
debug?.log(
"Inferring setter types for ${fullName(a)} based on ${fullName(b)}");
Member bTarget = b.target;
Procedure aProcedure = a.procedure;
VariableDeclaration aParameter =
aProcedure.function.positionalParameters.single;
DartType bType;
if (bTarget is Field) {
bType = bTarget.type;
copyParameterCovarianceFromField(a.parent, aParameter, bTarget);
}
if (bTarget is Procedure) {
if (b.isSetter) {
VariableDeclaration bParameter =
bTarget.function.positionalParameters.single;
bType = bParameter.type;
copyParameterCovariance(a.parent, aParameter, bParameter);
if (!hasExplicitlyTypedFormalParameter(b, 0) ||
!hasExplicitlyTypedFormalParameter(a, 0)) {
debug?.log("Giving up (type may be inferred)");
return false;
}
} else if (b.isGetter) {
bType = bTarget.function.returnType;
if (!hasExplicitReturnType(b)) {
debug?.log("Giving up (return type may be inferred)");
return false;
}
} else {
debug?.log("Giving up (not accessor: ${bTarget.kind})");
return false;
}
} else {
debug?.log("Giving up (not field/procedure: ${bTarget.runtimeType})");
return false;
}
return aParameter.type == bType;
}
void checkValidOverride(Builder a, Builder b) {
debug?.log(
"checkValidOverride(${fullName(a)}, ${fullName(b)}) ${a.runtimeType}");
if (a is ProcedureBuilder) {
if (inferMethodTypes(a, b)) return;
} else if (a.isField) {
if (inferFieldTypes(a, b)) return;
}
Member aTarget = a.target;
Member bTarget = b.target;
if (aTarget is Procedure && !aTarget.isAccessor && bTarget is Procedure) {
if (hasSameSignature(aTarget.function, bTarget.function)) return;
}
if (b is DelayedMember) {
for (int i = 0; i < b.declarations.length; i++) {
hierarchy.overrideChecks
.add(new DelayedOverrideCheck(classBuilder, a, b.declarations[i]));
}
} else {
hierarchy.overrideChecks
.add(new DelayedOverrideCheck(classBuilder, a, b));
}
}
bool inferFieldTypes(MemberBuilder a, Builder b) {
debug?.log("Trying to infer field types for ${fullName(a)} "
"based on ${fullName(b)}");
if (b is DelayedMember) {
bool hasSameSignature = true;
List<Builder> declarations = b.declarations;
for (int i = 0; i < declarations.length; i++) {
if (!inferFieldTypes(a, declarations[i])) {
hasSameSignature = false;
}
}
return hasSameSignature;
}
Member bTarget = b.target;
DartType inheritedType;
if (bTarget is Procedure) {
if (bTarget.isSetter) {
VariableDeclaration parameter =
bTarget.function.positionalParameters.single;
// inheritedType = parameter.type;
copyFieldCovarianceFromParameter(a.parent, a.member, parameter);
if (!hasExplicitlyTypedFormalParameter(b, 0)) {
debug?.log("Giving up (type may be inferred)");
return false;
}
} else if (bTarget.isGetter) {
if (!hasExplicitReturnType(b)) return false;
inheritedType = bTarget.function.returnType;
}
} else if (bTarget is Field) {
copyFieldCovariance(a.parent, a.member, bTarget);
inheritedType = bTarget.type;
}
if (inheritedType == null) {
debug?.log("Giving up (inheritedType == null)\n${StackTrace.current}");
return false;
}
ClassBuilder aClassBuilder = a.parent;
Substitution aSubstitution;
if (classBuilder != aClassBuilder) {
assert(
substitutions.containsKey(aClassBuilder.cls),
"${classBuilder.fullNameForErrors} "
"${aClassBuilder.fullNameForErrors}");
aSubstitution = substitutions[aClassBuilder.cls];
debug?.log("${classBuilder.fullNameForErrors} -> "
"${aClassBuilder.fullNameForErrors} $aSubstitution");
}
ClassBuilder bClassBuilder = b.parent;
Substitution bSubstitution;
if (classBuilder != bClassBuilder) {
assert(
substitutions.containsKey(bClassBuilder.cls),
"${classBuilder.fullNameForErrors} "
"${bClassBuilder.fullNameForErrors}");
bSubstitution = substitutions[bClassBuilder.cls];
debug?.log("${classBuilder.fullNameForErrors} -> "
"${bClassBuilder.fullNameForErrors} $bSubstitution");
}
if (bSubstitution != null && inheritedType is! ImplicitFieldType) {
inheritedType = bSubstitution.substituteType(inheritedType);
}
Field aField = a.member;
DartType declaredType = aField.type;
if (aSubstitution != null) {
declaredType = aSubstitution.substituteType(declaredType);
}
if (declaredType == inheritedType) return true;
bool result = false;
if (a is FieldBuilder) {
if (a.parent == classBuilder && a.type == null) {
if (a.hadTypesInferred) {
reportCantInferFieldType(classBuilder, a);
inheritedType = const InvalidType();
} else {
result = true;
a.hadTypesInferred = true;
}
if (inheritedType is ImplicitFieldType) {
SourceLibraryBuilder library = classBuilder.library;
(library.implicitlyTypedFields ??= <FieldBuilder>[]).add(a);
}
a.field.type = inheritedType;
}
}
return result;
}
void copyParameterCovariance(Builder parent, VariableDeclaration aParameter,
VariableDeclaration bParameter) {
if (parent == classBuilder) {
if (bParameter.isCovariant) {
aParameter.isCovariant = true;
}
if (bParameter.isGenericCovariantImpl) {
aParameter.isGenericCovariantImpl = true;
}
}
}
void copyParameterCovarianceFromField(
Builder parent, VariableDeclaration aParameter, Field bField) {
if (parent == classBuilder) {
if (bField.isCovariant) {
aParameter.isCovariant = true;
}
if (bField.isGenericCovariantImpl) {
aParameter.isGenericCovariantImpl = true;
}
}
}
void copyFieldCovariance(Builder parent, Field aField, Field bField) {
if (parent == classBuilder) {
if (bField.isCovariant) {
aField.isCovariant = true;
}
if (bField.isGenericCovariantImpl) {
aField.isGenericCovariantImpl = true;
}
}
}
void copyFieldCovarianceFromParameter(
Builder parent, Field aField, VariableDeclaration bParameter) {
if (parent == classBuilder) {
if (bParameter.isCovariant) {
aField.isCovariant = true;
}
if (bParameter.isGenericCovariantImpl) {
aField.isGenericCovariantImpl = true;
}
}
}
void copyTypeParameterCovariance(
Builder parent, TypeParameter aParameter, TypeParameter bParameter) {
if (parent == classBuilder) {
if (bParameter.isGenericCovariantImpl) {
aParameter.isGenericCovariantImpl = true;
}
}
}
void reportInheritanceConflict(Builder a, Builder b) {
String name = a.fullNameForErrors;
if (a.parent != b.parent) {
if (a.parent == classBuilder) {
classBuilder.addProblem(
messageDeclaredMemberConflictsWithInheritedMember,
a.charOffset,
name.length,
context: <LocatedMessage>[
messageDeclaredMemberConflictsWithInheritedMemberCause
.withLocation(b.fileUri, b.charOffset, name.length)
]);
} else {
classBuilder.addProblem(messageInheritedMembersConflict,
classBuilder.charOffset, classBuilder.fullNameForErrors.length,
context: inheritedConflictContext(a, b));
}
} else if (a.isStatic != b.isStatic) {
Builder staticMember;
Builder instanceMember;
if (a.isStatic) {
staticMember = a;
instanceMember = b;
} else {
staticMember = b;
instanceMember = a;
}
classBuilder.library.addProblem(messageStaticAndInstanceConflict,
staticMember.charOffset, name.length, staticMember.fileUri,
context: <LocatedMessage>[
messageStaticAndInstanceConflictCause.withLocation(
instanceMember.fileUri, instanceMember.charOffset, name.length)
]);
} else {
// This message can be reported twice (when merging localMembers with
// classSetters, or localSetters with classMembers). By ensuring that
// we always report the one with higher charOffset as the duplicate,
// the message duplication logic ensures that we only report this
// problem once.
Builder existing;
Builder duplicate;
assert(a.fileUri == b.fileUri);
if (a.charOffset < b.charOffset) {
existing = a;
duplicate = b;
} else {
existing = b;
duplicate = a;
}
classBuilder.library.addProblem(
templateDuplicatedDeclaration.withArguments(name),
duplicate.charOffset,
name.length,
duplicate.fileUri,
context: <LocatedMessage>[
templateDuplicatedDeclarationCause.withArguments(name).withLocation(
existing.fileUri, existing.charOffset, name.length)
]);
}
}
/// When merging `aList` and `bList`, [member] was only found in `aList`.
///
/// If [mergeKind] is `MergeKind.superclass` [member] is declared in current
/// class, and isn't overriding a method from the superclass.
///
/// If [mergeKind] is `MergeKind.interfaces`, [member] is ignored for now.
///
/// If [mergeKind] is `MergeKind.supertypes`, [member] isn't
/// implementing/overriding anything.
void handleOnlyA(Builder member, MergeKind mergeKind) {
if (mergeKind == MergeKind.interfacesMembers ||
mergeKind == MergeKind.interfacesSetters) {
return;
}
// TODO(ahe): Enable this optimization:
// if (cls is DillClassBuilder) return;
// assert(mergeKind == MergeKind.interfaces ||
// member is! InterfaceConflict);
if ((mergeKind == MergeKind.superclassMembers ||
mergeKind == MergeKind.superclassSetters) &&
isAbstract(member)) {
recordAbstractMember(member);
}
}
/// When merging `aList` and `bList`, [member] was only found in `bList`.
///
/// If [mergeKind] is `MergeKind.superclass` [member] is being inherited from
/// a superclass.
///
/// If [mergeKind] is `MergeKind.interfaces`, [member] is ignored for now.
///
/// If [mergeKind] is `MergeKind.supertypes`, [member] is implicitly
/// abstract, and not implemented.
Builder handleOnlyB(Builder member, MergeKind mergeKind) {
if (mergeKind == MergeKind.interfacesMembers ||
mergeKind == MergeKind.interfacesSetters) {
return member;
}
// TODO(ahe): Enable this optimization:
// if (cls is DillClassBuilder) return member;
Member target = member.target;
if ((mergeKind == MergeKind.supertypesMembers ||
mergeKind == MergeKind.supertypesSetters) ||
((mergeKind == MergeKind.superclassMembers ||
mergeKind == MergeKind.superclassSetters) &&
target.isAbstract)) {
if (isNameVisibleIn(target.name, classBuilder.library)) {
recordAbstractMember(member);
}
}
if (mergeKind == MergeKind.superclassMembers &&
target.enclosingClass != objectClass.cls &&
target.name == noSuchMethodName) {
hasNoSuchMethod = true;
}
if (mergeKind != MergeKind.membersWithSetters &&
mergeKind != MergeKind.settersWithMembers &&
member is DelayedMember &&
member.isInheritableConflict) {
hierarchy.delayedMemberChecks.add(member.withParent(classBuilder));
}
return member;
}
void recordAbstractMember(Builder member) {
abstractMembers ??= <Builder>[];
if (member is DelayedMember) {
abstractMembers.addAll(member.declarations);
} else {
abstractMembers.add(member);
}
}
ClassHierarchyNode build() {
assert(!classBuilder.isPatch);
ClassHierarchyNode supernode;
if (objectClass != classBuilder.origin) {
supernode = hierarchy.getNodeFromType(classBuilder.supertype);
if (supernode == null) {
supernode = hierarchy.getNodeFromClass(objectClass);
}
assert(supernode != null);
}
Scope scope = classBuilder.scope;
if (classBuilder.isMixinApplication) {
Builder mixin = classBuilder.mixedInType.declaration;
inferMixinApplication();
// recordSupertype(cls.mixedInType);
while (mixin.isNamedMixinApplication) {
ClassBuilder named = mixin;
// recordSupertype(named.mixedInType);
mixin = named.mixedInType.declaration;
}
if (mixin is ClassBuilder) {
scope = mixin.scope.computeMixinScope();
}
}
/// Members (excluding setters) declared in [cls].
List<Builder> localMembers = new List<Builder>.from(scope.local.values)
..sort(compareDeclarations);
/// Setters declared in [cls].
List<Builder> localSetters = new List<Builder>.from(scope.setters.values)
..sort(compareDeclarations);
// Add implied setters from fields in [localMembers].
localSetters = mergeAccessors(localMembers, localSetters);
/// Members (excluding setters) declared in [cls] or its superclasses. This
/// includes static methods of [cls], but not its superclasses.
List<Builder> classMembers;
/// Setters declared in [cls] or its superclasses. This includes static
/// setters of [cls], but not its superclasses.
List<Builder> classSetters;
/// Members (excluding setters) inherited from interfaces. This contains no
/// static members. Is null if no interfaces are implemented by this class
/// or its superclasses.
List<Builder> interfaceMembers;
/// Setters inherited from interfaces. This contains no static setters. Is
/// null if no interfaces are implemented by this class or its
/// superclasses.
List<Builder> interfaceSetters;
List<TypeBuilder> superclasses;
List<TypeBuilder> interfaces;
int maxInheritancePath;
if (supernode == null) {
// This should be Object.
classMembers = localMembers;
classSetters = localSetters;
superclasses = new List<TypeBuilder>(0);
interfaces = new List<TypeBuilder>(0);
maxInheritancePath = 0;
} else {
maxInheritancePath = supernode.maxInheritancePath + 1;
superclasses = new List<TypeBuilder>(supernode.superclasses.length + 1);
superclasses.setRange(0, superclasses.length - 1,
substSupertypes(classBuilder.supertype, supernode.superclasses));
superclasses[superclasses.length - 1] =
recordSupertype(classBuilder.supertype);
List<TypeBuilder> directInterfaces =
ignoreFunction(classBuilder.interfaces);
if (classBuilder.isMixinApplication) {
if (directInterfaces == null) {
directInterfaces = <TypeBuilder>[classBuilder.mixedInType];
} else {
directInterfaces = <TypeBuilder>[classBuilder.mixedInType]
..addAll(directInterfaces);
}
}
if (directInterfaces != null) {
for (int i = 0; i < directInterfaces.length; i++) {
recordSupertype(directInterfaces[i]);
}
}
List<TypeBuilder> superclassInterfaces = supernode.interfaces;
if (superclassInterfaces != null) {
superclassInterfaces =
substSupertypes(classBuilder.supertype, superclassInterfaces);
}
classMembers = merge(
localMembers, supernode.classMembers, MergeKind.superclassMembers);
classSetters = merge(
localSetters, supernode.classSetters, MergeKind.superclassSetters);
if (directInterfaces != null) {
MergeResult result = mergeInterfaces(supernode, directInterfaces);
interfaceMembers = result.mergedMembers;
interfaceSetters = result.mergedSetters;
interfaces = <TypeBuilder>[];
if (superclassInterfaces != null) {
for (int i = 0; i < superclassInterfaces.length; i++) {
addInterface(interfaces, superclasses, superclassInterfaces[i]);
}
}
for (int i = 0; i < directInterfaces.length; i++) {
TypeBuilder directInterface = directInterfaces[i];
addInterface(interfaces, superclasses, directInterface);
ClassHierarchyNode interfaceNode =
hierarchy.getNodeFromType(directInterface);
if (interfaceNode != null) {
if (maxInheritancePath < interfaceNode.maxInheritancePath + 1) {
maxInheritancePath = interfaceNode.maxInheritancePath + 1;
}
List<TypeBuilder> types =
substSupertypes(directInterface, interfaceNode.superclasses);
for (int i = 0; i < types.length; i++) {
addInterface(interfaces, superclasses, types[i]);
}
if (interfaceNode.interfaces != null) {
List<TypeBuilder> types =
substSupertypes(directInterface, interfaceNode.interfaces);
for (int i = 0; i < types.length; i++) {
addInterface(interfaces, superclasses, types[i]);
}
}
}
}
} else {
interfaceMembers = supernode.interfaceMembers;
interfaceSetters = supernode.interfaceSetters;
interfaces = superclassInterfaces;
}
// Check if local members conflict with inherited setters. This check has
// already been performed in the superclass, so we only need to check the
// local members. These checks have to occur late to enable inferring
// types between setters and getters, or from a setter to a final field.
merge(localMembers, classSetters, MergeKind.membersWithSetters);
// Check if local setters conflict with inherited members. As above, we
// only need to check the local setters.
merge(localSetters, classMembers, MergeKind.settersWithMembers);
if (interfaceMembers != null) {
interfaceMembers =
merge(classMembers, interfaceMembers, MergeKind.supertypesMembers);
// Check if class setters conflict with members inherited from
// interfaces.
merge(classSetters, interfaceMembers, MergeKind.settersWithMembers);
}
if (interfaceSetters != null) {
interfaceSetters =
merge(classSetters, interfaceSetters, MergeKind.supertypesSetters);
// Check if class members conflict with setters inherited from
// interfaces.
merge(classMembers, interfaceSetters, MergeKind.membersWithSetters);
}
}
if (abstractMembers != null && !classBuilder.isAbstract) {
if (!hasNoSuchMethod) {
reportMissingMembers();
} else {
installNsmHandlers();
}
}
return new ClassHierarchyNode(
classBuilder,
classMembers,
classSetters,
interfaceMembers,
interfaceSetters,
superclasses,
interfaces,
maxInheritancePath,
hasNoSuchMethod,
);
}
TypeBuilder recordSupertype(TypeBuilder supertype) {
if (supertype is NamedTypeBuilder) {
debug?.log("In ${this.classBuilder.fullNameForErrors} "
"recordSupertype(${supertype.fullNameForErrors})");
Builder declaration = supertype.declaration;
if (declaration is! ClassBuilder) return supertype;
ClassBuilder classBuilder = declaration;
if (classBuilder.isMixinApplication) {
recordSupertype(classBuilder.mixedInType);
}
List<TypeVariableBuilder> typeVariableBuilders =
classBuilder.typeVariables;
if (typeVariableBuilders == null) {
substitutions[classBuilder.cls] = Substitution.empty;
assert(classBuilder.cls.typeParameters.isEmpty);
} else {
List<TypeBuilder> arguments =
supertype.arguments ?? computeDefaultTypeArguments(supertype);
if (arguments.length != typeVariableBuilders.length) {
arguments = computeDefaultTypeArguments(supertype);
}
List<DartType> typeArguments = new List<DartType>(arguments.length);
List<TypeParameter> typeParameters =
new List<TypeParameter>(arguments.length);
for (int i = 0; i < arguments.length; i++) {
typeParameters[i] = typeVariableBuilders[i].parameter;
typeArguments[i] = arguments[i].build(this.classBuilder.parent);
}
substitutions[classBuilder.cls] =
Substitution.fromPairs(typeParameters, typeArguments);
}
}
return supertype;
}
List<TypeBuilder> substSupertypes(
NamedTypeBuilder supertype, List<TypeBuilder> supertypes) {
Builder declaration = supertype.declaration;
if (declaration is! ClassBuilder) return supertypes;
ClassBuilder cls = declaration;
List<TypeVariableBuilder> typeVariables = cls.typeVariables;
if (typeVariables == null) {
debug?.log("In ${this.classBuilder.fullNameForErrors} "
"$supertypes aren't substed");
for (int i = 0; i < supertypes.length; i++) {
recordSupertype(supertypes[i]);
}
return supertypes;
}
Map<TypeVariableBuilder, TypeBuilder> substitution =
<TypeVariableBuilder, TypeBuilder>{};
List<TypeBuilder> arguments =
supertype.arguments ?? computeDefaultTypeArguments(supertype);
for (int i = 0; i < typeVariables.length; i++) {
substitution[typeVariables[i]] = arguments[i];
}
List<TypeBuilder> result;
for (int i = 0; i < supertypes.length; i++) {
TypeBuilder supertype = supertypes[i];
TypeBuilder substed = recordSupertype(supertype.subst(substitution));
if (supertype != substed) {
debug?.log(
"In ${this.classBuilder.fullNameForErrors} $supertype -> $substed");
result ??= supertypes.toList();
result[i] = substed;
} else {
debug?.log("In ${this.classBuilder.fullNameForErrors} "
"$supertype isn't substed");
}
}
return result ?? supertypes;
}
List<TypeBuilder> computeDefaultTypeArguments(TypeBuilder type) {
ClassBuilder cls = type.declaration;
List<TypeBuilder> result = new List<TypeBuilder>(cls.typeVariables.length);
for (int i = 0; i < result.length; ++i) {
TypeVariableBuilder tv = cls.typeVariables[i];
result[i] = tv.defaultType ??
cls.library.loader.computeTypeBuilder(tv.parameter.defaultType);
}
return result;
}
TypeBuilder addInterface(List<TypeBuilder> interfaces,
List<TypeBuilder> superclasses, TypeBuilder type) {
ClassHierarchyNode node = hierarchy.getNodeFromType(type);
if (node == null) return null;
int depth = node.depth;
int myDepth = superclasses.length;
if (depth < myDepth &&
superclasses[depth].declaration == node.classBuilder) {
// This is a potential conflict.
return superclasses[depth];
} else {
for (int i = 0; i < interfaces.length; i++) {
// This is a quadratic algorithm, but normally, the number of
// interfaces is really small.
if (interfaces[i].declaration == type.declaration) {
// This is a potential conflict.
return interfaces[i];
}
}
}
interfaces.add(type);
return null;
}
MergeResult mergeInterfaces(
ClassHierarchyNode supernode, List<TypeBuilder> interfaces) {
debug?.log("mergeInterfaces($classBuilder (${this.classBuilder}) "
"${supernode.interfaces} ${interfaces}");
List<List<Builder>> memberLists =
new List<List<Builder>>(interfaces.length + 1);
List<List<Builder>> setterLists =
new List<List<Builder>>(interfaces.length + 1);
memberLists[0] = supernode.interfaceMembers;
setterLists[0] = supernode.interfaceSetters;
for (int i = 0; i < interfaces.length; i++) {
ClassHierarchyNode interfaceNode =
hierarchy.getNodeFromType(interfaces[i]);
if (interfaceNode == null) {
memberLists[i + 1] = null;
setterLists[i + 1] = null;
} else {
memberLists[i + 1] =
interfaceNode.interfaceMembers ?? interfaceNode.classMembers;
setterLists[i + 1] =
interfaceNode.interfaceSetters ?? interfaceNode.classSetters;
}
}
return new MergeResult(mergeLists(memberLists, MergeKind.interfacesMembers),
mergeLists(setterLists, MergeKind.interfacesSetters));
}
List<Builder> mergeLists(List<List<Builder>> input, MergeKind mergeKind) {
// This is a k-way merge sort (where k is `input.length + 1`). We merge the
// lists pairwise, which reduces the number of lists to merge by half on
// each iteration. Consequently, we perform O(log k) merges.
while (input.length > 1) {
List<List<Builder>> output = <List<Builder>>[];
for (int i = 0; i < input.length - 1; i += 2) {
List<Builder> first = input[i];
List<Builder> second = input[i + 1];
if (first == null) {
output.add(second);
} else if (second == null) {
output.add(first);
} else {
output.add(merge(first, second, mergeKind));
}
}
if (input.length.isOdd) {
output.add(input.last);
}
input = output;
}
return input.single;
}
/// Merge [and check] accessors. This entails copying mutable fields to
/// setters to simulate implied setters, and checking that setters don't
/// override regular methods.
List<Builder> mergeAccessors(List<Builder> members, List<Builder> setters) {
final List<Builder> mergedSetters = new List<Builder>.filled(
members.length + setters.length, null,
growable: true);
int storeIndex = 0;
int i = 0;
int j = 0;
while (i < members.length && j < setters.length) {
final Builder member = members[i];
final Builder setter = setters[j];
final int compare = compareDeclarations(member, setter);
if (compare == 0) {
mergedSetters[storeIndex++] = setter;
i++;
j++;
} else if (compare < 0) {
if (impliesSetter(member)) {
mergedSetters[storeIndex++] = member;
}
i++;
} else {
mergedSetters[storeIndex++] = setters[j];
j++;
}
}
while (i < members.length) {
final Builder member = members[i];
if (impliesSetter(member)) {
mergedSetters[storeIndex++] = member;
}
i++;
}
while (j < setters.length) {
mergedSetters[storeIndex++] = setters[j];
j++;
}
if (storeIndex == j) {
return setters;
} else {
return mergedSetters..length = storeIndex;
}
}
void reportMissingMembers() {
Map<String, LocatedMessage> contextMap = <String, LocatedMessage>{};
for (int i = 0; i < abstractMembers.length; i++) {
Builder declaration = abstractMembers[i];
Member target = declaration.target;
if (isNameVisibleIn(target.name, classBuilder.library)) {
String name = declaration.fullNameForErrors;
String parentName = declaration.parent.fullNameForErrors;
String displayName =
declaration.isSetter ? "$parentName.$name=" : "$parentName.$name";
contextMap[displayName] = templateMissingImplementationCause
.withArguments(displayName)
.withLocation(
declaration.fileUri, declaration.charOffset, name.length);
}
}
if (contextMap.isEmpty) return;
List<String> names = new List<String>.from(contextMap.keys)..sort();
List<LocatedMessage> context = <LocatedMessage>[];
for (int i = 0; i < names.length; i++) {
context.add(contextMap[names[i]]);
}
classBuilder.addProblem(
templateMissingImplementationNotAbstract.withArguments(
classBuilder.fullNameForErrors, names),
classBuilder.charOffset,
classBuilder.fullNameForErrors.length,
context: context);
}
void installNsmHandlers() {
// TODO(ahe): Implement this.
}
List<Builder> merge(
List<Builder> aList, List<Builder> bList, MergeKind mergeKind) {
final List<Builder> result = new List<Builder>.filled(
aList.length + bList.length, null,
growable: true);
int storeIndex = 0;
int i = 0;
int j = 0;
while (i < aList.length && j < bList.length) {
final Builder a = aList[i];
final Builder b = bList[j];
if ((mergeKind == MergeKind.interfacesMembers ||
mergeKind == MergeKind.interfacesSetters) &&
a.isStatic) {
i++;
continue;
}
if (b.isStatic) {
j++;
continue;
}
final int compare = compareDeclarations(a, b);
if (compare == 0) {
result[storeIndex++] = handleMergeConflict(a, b, mergeKind);
i++;
j++;
} else if (compare < 0) {
handleOnlyA(a, mergeKind);
result[storeIndex++] = a;
i++;
} else {
result[storeIndex++] = handleOnlyB(b, mergeKind);
j++;
}
}
while (i < aList.length) {
final Builder a = aList[i];
if (!(mergeKind == MergeKind.interfacesMembers ||
mergeKind == MergeKind.interfacesSetters) ||
!a.isStatic) {
handleOnlyA(a, mergeKind);
result[storeIndex++] = a;
}
i++;
}
while (j < bList.length) {
final Builder b = bList[j];
if (!b.isStatic) {
result[storeIndex++] = handleOnlyB(b, mergeKind);
}
j++;
}
if (aList.isEmpty && storeIndex == bList.length) return bList;
if (bList.isEmpty && storeIndex == aList.length) return aList;
return result..length = storeIndex;
}
void inferMixinApplication() {
Class cls = classBuilder.cls;
Supertype mixedInType = cls.mixedInType;
if (mixedInType == null) return;
List<DartType> typeArguments = mixedInType.typeArguments;
if (typeArguments.isEmpty || typeArguments.first is! UnknownType) return;
new BuilderMixinInferrer(
classBuilder,
hierarchy.coreTypes,
new TypeBuilderConstraintGatherer(
hierarchy, mixedInType.classNode.typeParameters))
.infer(cls);
List<TypeBuilder> inferredArguments =
new List<TypeBuilder>(typeArguments.length);
for (int i = 0; i < typeArguments.length; i++) {
inferredArguments[i] =
hierarchy.loader.computeTypeBuilder(typeArguments[i]);
}
NamedTypeBuilder mixedInTypeBuilder = classBuilder.mixedInType;
mixedInTypeBuilder.arguments = inferredArguments;
}
/// The class Function from dart:core is supposed to be ignored when used as
/// an interface.
List<TypeBuilder> ignoreFunction(List<TypeBuilder> interfaces) {
if (interfaces == null) return null;
for (int i = 0; i < interfaces.length; i++) {
ClassBuilder classBuilder = getClass(interfaces[i]);
if (classBuilder != null && classBuilder.cls == hierarchy.functionClass) {
if (interfaces.length == 1) {
return null;
} else {
interfaces = interfaces.toList();
interfaces.removeAt(i);
return ignoreFunction(interfaces);
}
}
}
return interfaces;
}
}
class ClassHierarchyNode {
/// The class corresponding to this hierarchy node.
final ClassBuilder classBuilder;
/// All the members of this class including [classMembers] of its
/// superclasses. The members are sorted by [compareDeclarations].
final List<Builder> classMembers;
/// Similar to [classMembers] but for setters.
final List<Builder> classSetters;
/// All the interface members of this class including [interfaceMembers] of
/// its supertypes. The members are sorted by [compareDeclarations].
///
/// In addition to the members of [classMembers] this also contains members
/// from interfaces.
///
/// This may be null, in which case [classMembers] is the interface members.
final List<Builder> interfaceMembers;
/// Similar to [interfaceMembers] but for setters.
///
/// This may be null, in which case [classSetters] is the interface setters.
final List<Builder> interfaceSetters;
/// All superclasses of [classBuilder] excluding itself. The classes are
/// sorted by depth from the root (Object) in ascending order.
final List<TypeBuilder> superclasses;
/// The list of all classes implemented by [classBuilder] and its supertypes
/// excluding any classes from [superclasses].
final List<TypeBuilder> interfaces;
/// The longest inheritance path from [classBuilder] to `Object`.
final int maxInheritancePath;
int get depth => superclasses.length;
final bool hasNoSuchMethod;
ClassHierarchyNode(
this.classBuilder,
this.classMembers,
this.classSetters,
this.interfaceMembers,
this.interfaceSetters,
this.superclasses,
this.interfaces,
this.maxInheritancePath,
this.hasNoSuchMethod);
/// Returns a list of all supertypes of [classBuilder], including this node.
List<ClassHierarchyNode> computeAllSuperNodes(
ClassHierarchyBuilder hierarchy) {
List<ClassHierarchyNode> result = new List<ClassHierarchyNode>(
1 + superclasses.length + interfaces.length);
for (int i = 0; i < superclasses.length; i++) {
Builder declaration = superclasses[i].declaration;
if (declaration is ClassBuilder) {
result[i] = hierarchy.getNodeFromClass(declaration);
}
}
for (int i = 0; i < interfaces.length; i++) {
Builder declaration = interfaces[i].declaration;
if (declaration is ClassBuilder) {
result[i + superclasses.length] =
hierarchy.getNodeFromClass(declaration);
}
}
return result..last = this;
}
String toString([StringBuffer sb]) {
sb ??= new StringBuffer();
sb
..write(classBuilder.fullNameForErrors)
..writeln(":");
if (maxInheritancePath != this.depth) {
sb
..write(" Longest path to Object: ")
..writeln(maxInheritancePath);
}
sb..writeln(" superclasses:");
int depth = 0;
for (TypeBuilder superclass in superclasses) {
sb.write(" " * (depth + 2));
if (depth != 0) sb.write("-> ");
superclass.printOn(sb);
sb.writeln();
depth++;
}
if (interfaces != null) {
sb.write(" interfaces:");
bool first = true;
for (TypeBuilder i in interfaces) {
if (!first) sb.write(",");
sb.write(" ");
i.printOn(sb);
first = false;
}
sb.writeln();
}
printMembers(classMembers, sb, "classMembers");
printMembers(classSetters, sb, "classSetters");
if (interfaceMembers != null) {
printMembers(interfaceMembers, sb, "interfaceMembers");
}
if (interfaceSetters != null) {
printMembers(interfaceSetters, sb, "interfaceSetters");
}
return "$sb";
}
void printMembers(List<Builder> members, StringBuffer sb, String heading) {
sb.write(" ");
sb.write(heading);
sb.writeln(":");
for (Builder member in members) {
sb
..write(" ")
..write(member.parent.fullNameForErrors)
..write(".")
..write(member.fullNameForErrors)
..writeln();
}
}
Builder getInterfaceMember(Name name, bool isSetter) {
return findMember(
name,
isSetter
? interfaceSetters ?? classSetters
: interfaceMembers ?? classMembers);
}
Builder findMember(Name name, List<Builder> declarations) {
// TODO(ahe): Consider creating a map or scope. The obvious choice would be
// to use scopes, but they don't handle private names correctly.
// This is a copy of `ClassHierarchy.findMemberByName`.
int low = 0, high = declarations.length - 1;
while (low <= high) {
int mid = low + ((high - low) >> 1);
Builder pivot = declarations[mid];
int comparison = ClassHierarchy.compareNames(name, pivot.target.name);
if (comparison < 0) {
high = mid - 1;
} else if (comparison > 0) {
low = mid + 1;
} else if (high != mid) {
// Ensure we find the first element of the given name.
high = mid;
} else {
return pivot;
}
}
return null;
}
Builder getDispatchTarget(Name name, bool isSetter) {
return findMember(name, isSetter ? classSetters : classMembers);
}
static int compareMaxInheritancePath(
ClassHierarchyNode a, ClassHierarchyNode b) {
return b.maxInheritancePath.compareTo(a.maxInheritancePath);
}
}
class MergeResult {
final List<Builder> mergedMembers;
final List<Builder> mergedSetters;
MergeResult(this.mergedMembers, this.mergedSetters);
}
enum MergeKind {
/// Merging superclass members with the current class.
superclassMembers,
/// Merging superclass setters with the current class.
superclassSetters,
/// Merging members of two interfaces.
interfacesMembers,
/// Merging setters of two interfaces.
interfacesSetters,
/// Merging class members with interface members.
supertypesMembers,
/// Merging class setters with interface setters.
supertypesSetters,
/// Merging members with inherited setters.
membersWithSetters,
/// Merging setters with inherited members.
settersWithMembers,
}
List<LocatedMessage> inheritedConflictContext(Builder a, Builder b) {
return inheritedConflictContextKernel(
a.target, b.target, a.fullNameForErrors.length);
}
List<LocatedMessage> inheritedConflictContextKernel(
Member a, Member b, int length) {
// TODO(ahe): Delete this method when it isn't used by [InterfaceResolver].
int compare = "${a.fileUri}".compareTo("${b.fileUri}");
if (compare == 0) {
compare = a.fileOffset.compareTo(b.fileOffset);
}
Member first;
Member second;
if (compare < 0) {
first = a;
second = b;
} else {
first = b;
second = a;
}
return <LocatedMessage>[
messageInheritedMembersConflictCause1.withLocation(
first.fileUri, first.fileOffset, length),
messageInheritedMembersConflictCause2.withLocation(
second.fileUri, second.fileOffset, length),
];
}
class BuilderMixinInferrer extends MixinInferrer {
final ClassBuilder cls;
BuilderMixinInferrer(
this.cls, CoreTypes coreTypes, TypeBuilderConstraintGatherer gatherer)
: super(coreTypes, gatherer);
Supertype asInstantiationOf(Supertype type, Class superclass) {
InterfaceType interfaceType =
gatherer.getTypeAsInstanceOf(type.asInterfaceType, superclass);
if (interfaceType == null) return null;
return new Supertype(interfaceType.classNode, interfaceType.typeArguments);
}
void reportProblem(Message message, Class kernelClass) {
int length = cls.isMixinApplication ? 1 : cls.fullNameForErrors.length;
cls.addProblem(message, cls.charOffset, length);
}
}
class TypeBuilderConstraintGatherer extends TypeConstraintGatherer
with StandardBounds {
final ClassHierarchyBuilder hierarchy;
TypeBuilderConstraintGatherer(
this.hierarchy, Iterable<TypeParameter> typeParameters)
: super.subclassing(typeParameters);
@override
Class get objectClass => hierarchy.objectClass;
@override
Class get functionClass => hierarchy.functionClass;
@override
Class get futureClass => hierarchy.futureClass;
@override
Class get futureOrClass => hierarchy.futureOrClass;
@override
Class get nullClass => hierarchy.nullClass;
@override
InterfaceType get nullType => hierarchy.coreTypes.nullType;
@override
InterfaceType get objectLegacyRawType =>
hierarchy.coreTypes.objectLegacyRawType;
@override
InterfaceType get functionLegacyRawType =>
hierarchy.coreTypes.functionLegacyRawType;
@override
void addLowerBound(TypeConstraint constraint, DartType lower) {
constraint.lower = getStandardUpperBound(constraint.lower, lower);
}
@override
void addUpperBound(TypeConstraint constraint, DartType upper) {
constraint.upper = getStandardLowerBound(constraint.upper, upper);
}
@override
Member getInterfaceMember(Class class_, Name name, {bool setter: false}) {
return null;
}
@override
InterfaceType getTypeAsInstanceOf(InterfaceType type, Class superclass) {
return hierarchy.getKernelTypeAsInstanceOf(type, superclass);
}
@override
InterfaceType futureType(DartType type) {
return new InterfaceType(hierarchy.futureClass, <DartType>[type]);
}
@override
bool isSubtypeOf(DartType subtype, DartType supertype) {
return hierarchy.types.isSubtypeOfKernel(subtype, supertype);
}
@override
InterfaceType getLegacyLeastUpperBound(
InterfaceType type1, InterfaceType type2) {
return hierarchy.getKernelLegacyLeastUpperBound(type1, type2);
}
}
class DelayedOverrideCheck {
final ClassBuilder classBuilder;
final Builder a;
final Builder b;
const DelayedOverrideCheck(this.classBuilder, this.a, this.b);
void check(ClassHierarchyBuilder hierarchy) {
void callback(
Member declaredMember, Member interfaceMember, bool isSetter) {
classBuilder.checkOverride(
hierarchy.types, declaredMember, interfaceMember, isSetter, callback,
isInterfaceCheck: !classBuilder.isMixinApplication);
}
Builder a = this.a;
debug?.log("Delayed override check of ${fullName(a)} "
"${fullName(b)} wrt. ${classBuilder.fullNameForErrors}");
if (classBuilder == a.parent) {
if (a is ProcedureBuilder) {
if (a.isGetter && !hasExplicitReturnType(a)) {
DartType type;
if (b.isGetter) {
Procedure bTarget = b.target;
type = bTarget.function.returnType;
} else if (b.isSetter) {
Procedure bTarget = b.target;
type = bTarget.function.positionalParameters.single.type;
} else if (b.isField) {
Field bTarget = b.target;
type = bTarget.type;
}
if (type != null) {
type = Substitution.fromInterfaceType(
hierarchy.getKernelTypeAsInstanceOf(
classBuilder.cls.thisType, b.target.enclosingClass))
.substituteType(type);
if (!a.hadTypesInferred || !b.isSetter) {
inferReturnType(
classBuilder, a, type, a.hadTypesInferred, hierarchy);
}
}
} else if (a.isSetter && !hasExplicitlyTypedFormalParameter(a, 0)) {
DartType type;
if (b.isGetter) {
Procedure bTarget = b.target;
type = bTarget.function.returnType;
} else if (b.isSetter) {
Procedure bTarget = b.target;
type = bTarget.function.positionalParameters.single.type;
} else if (b.isField) {
Field bTarget = b.target;
type = bTarget.type;
}
if (type != null) {
type = Substitution.fromInterfaceType(
hierarchy.getKernelTypeAsInstanceOf(
classBuilder.cls.thisType, b.target.enclosingClass))
.substituteType(type);
if (!a.hadTypesInferred || !b.isGetter) {
inferParameterType(classBuilder, a, a.formals.single, type,
a.hadTypesInferred, hierarchy);
}
}
}
a.hadTypesInferred = true;
} else if (a is FieldBuilder && a.type == null) {
DartType type;
if (b.isGetter) {
Procedure bTarget = b.target;
type = bTarget.function.returnType;
} else if (b.isSetter) {
Procedure bTarget = b.target;
type = bTarget.function.positionalParameters.single.type;
} else if (b.isField) {
Field bTarget = b.target;
type = bTarget.type;
}
if (type != null) {
type = Substitution.fromInterfaceType(
hierarchy.getKernelTypeAsInstanceOf(
classBuilder.cls.thisType, b.target.enclosingClass))
.substituteType(type);
if (type != a.field.type) {
if (a.hadTypesInferred) {
if (b.isSetter &&
(!impliesSetter(a) ||
hierarchy.types.isSubtypeOfKernel(type, a.field.type))) {
type = a.field.type;
} else {
reportCantInferFieldType(classBuilder, a);
type = const InvalidType();
}
}
debug?.log("Inferred type ${type} for ${fullName(a)}");
a.field.type = type;
}
}
a.hadTypesInferred = true;
}
}
callback(a.target, b.target, a.isSetter);
}
}
abstract class DelayedMember extends Builder {
/// The class which has inherited [declarations].
@override
final ClassBuilder parent;
/// Conflicting declarations.
final List<Builder> declarations;
final bool isSetter;
final bool modifyKernel;
DelayedMember(
this.parent, this.declarations, this.isSetter, this.modifyKernel);
void addAllDeclarationsTo(List<Builder> declarations) {
for (int i = 0; i < this.declarations.length; i++) {
addDeclarationIfDifferent(this.declarations[i], declarations);
}
assert(declarations.toSet().length == declarations.length);
}
Member check(ClassHierarchyBuilder hierarchy);
DelayedMember withParent(ClassBuilder parent);
@override
Uri get fileUri => parent.fileUri;
@override
int get charOffset => parent.charOffset;
@override
String get fullNameForErrors => declarations.map(fullName).join("%");
bool get isInheritableConflict => true;
@override
Member get target => declarations.first.target;
}
/// This represents a concrete implementation inherited from a superclass that
/// has conflicts with methods inherited from an interface. The concrete
/// implementation is the first element of [declarations].
class InheritedImplementationInterfaceConflict extends DelayedMember {
Member combinedMemberSignatureResult;
@override
final bool isInheritableConflict;
InheritedImplementationInterfaceConflict(ClassBuilder parent,
List<Builder> declarations, bool isSetter, bool modifyKernel,
{this.isInheritableConflict = true})
: super(parent, declarations, isSetter, modifyKernel);
@override
String toString() {
return "InheritedImplementationInterfaceConflict("
"${parent.fullNameForErrors}, "
"[${declarations.map(fullName).join(', ')}])";
}
@override
Member check(ClassHierarchyBuilder hierarchy) {
if (combinedMemberSignatureResult != null) {
return combinedMemberSignatureResult;
}
if (!parent.isAbstract) {
Builder concreteImplementation = declarations.first;
for (int i = 1; i < declarations.length; i++) {
new DelayedOverrideCheck(
parent, concreteImplementation, declarations[i])
.check(hierarchy);
}
}
return combinedMemberSignatureResult =
new InterfaceConflict(parent, declarations, isSetter, modifyKernel)
.check(hierarchy);
}
@override
DelayedMember withParent(ClassBuilder parent) {
return parent == this.parent
? this
: new InheritedImplementationInterfaceConflict(
parent, declarations, isSetter, modifyKernel);
}
static Builder combined(ClassBuilder parent, Builder concreteImplementation,
Builder other, bool isSetter, bool createForwarders,
{bool isInheritableConflict = true}) {
List<Builder> declarations = <Builder>[];
if (concreteImplementation is DelayedMember) {
concreteImplementation.addAllDeclarationsTo(declarations);
} else {
declarations.add(concreteImplementation);
}
if (other is DelayedMember) {
other.addAllDeclarationsTo(declarations);
} else {
addDeclarationIfDifferent(other, declarations);
}
if (declarations.length == 1) {
return declarations.single;
} else {
return new InheritedImplementationInterfaceConflict(
parent, declarations, isSetter, createForwarders,
isInheritableConflict: isInheritableConflict);
}
}
}
class InterfaceConflict extends DelayedMember {
InterfaceConflict(ClassBuilder parent, List<Builder> declarations,
bool isSetter, bool modifyKernel)
: super(parent, declarations, isSetter, modifyKernel);
Member combinedMemberSignatureResult;
@override
String toString() {
return "InterfaceConflict(${parent.fullNameForErrors}, "
"[${declarations.map(fullName).join(', ')}])";
}
DartType computeMemberType(
ClassHierarchyBuilder hierarchy, DartType thisType, Member member) {
DartType type;
if (member is Procedure) {
if (member.isGetter) {
type = member.getterType;
} else if (member.isSetter) {
type = member.setterType;
} else {
type = member.function.functionType;
}
} else if (member is Field) {
type = member.type;
} else {
unhandled("${member.runtimeType}", "$member", parent.charOffset,
parent.fileUri);
}
return Substitution.fromInterfaceType(hierarchy.getKernelTypeAsInstanceOf(
thisType, member.enclosingClass))
.substituteType(type);
}
bool isMoreSpecific(ClassHierarchyBuilder hierarchy, DartType a, DartType b) {
if (isSetter) {
return hierarchy.types.isSubtypeOfKernel(b, a);
} else {
return hierarchy.types.isSubtypeOfKernel(a, b);
}
}
@override
Member check(ClassHierarchyBuilder hierarchy) {
if (combinedMemberSignatureResult != null) {
return combinedMemberSignatureResult;
}
if (parent.library is! SourceLibraryBuilder) {
return combinedMemberSignatureResult = declarations.first.target;
}
DartType thisType = parent.cls.thisType;
Builder bestSoFar;
DartType bestTypeSoFar;
for (int i = declarations.length - 1; i >= 0; i--) {
Builder candidate = declarations[i];
Member target = candidate.target;
DartType candidateType = computeMemberType(hierarchy, thisType, target);
if (bestSoFar == null) {
bestSoFar = candidate;
bestTypeSoFar = candidateType;
} else {
if (isMoreSpecific(hierarchy, candidateType, bestTypeSoFar)) {
debug?.log("Combined Member Signature: ${fullName(candidate)} "
"${candidateType} <: ${fullName(bestSoFar)} ${bestTypeSoFar}");
bestSoFar = candidate;
bestTypeSoFar = candidateType;
} else {
debug?.log("Combined Member Signature: "
"${fullName(candidate)} !<: ${fullName(bestSoFar)}");
}
}
}
if (bestSoFar != null) {
debug?.log("Combined Member Signature bestSoFar: ${fullName(bestSoFar)}");
for (int i = 0; i < declarations.length; i++) {
Builder candidate = declarations[i];
Member target = candidate.target;
DartType candidateType = computeMemberType(hierarchy, thisType, target);
if (!isMoreSpecific(hierarchy, bestTypeSoFar, candidateType)) {
debug?.log("Combined Member Signature: "
"${fullName(bestSoFar)} !<: ${fullName(candidate)}");
String uri = '${parent.library.uri}';
if (uri == 'dart:js' &&
parent.fileUri.pathSegments.last == 'js_dart2js.dart' ||
uri == 'dart:_interceptors' &&
parent.fileUri.pathSegments.last == 'js_number.dart') {
// TODO(johnniwinther): Fix the dart2js libraries and remove the
// above URIs.
} else {
bestSoFar = null;
bestTypeSoFar = null;
}
break;
}
}
}
if (bestSoFar == null) {
String name = parent.fullNameForErrors;
int length = parent.isAnonymousMixinApplication ? 1 : name.length;
List<LocatedMessage> context = declarations.map((Builder d) {
return messageDeclaredMemberConflictsWithInheritedMemberCause
.withLocation(d.fileUri, d.charOffset, d.fullNameForErrors.length);
}).toList();
parent.addProblem(
templateCombinedMemberSignatureFailed.withArguments(
parent.fullNameForErrors, declarations.first.fullNameForErrors),
parent.charOffset,
length,
context: context);
return null;
}
debug?.log("Combined Member Signature of ${fullNameForErrors}: "
"${fullName(bestSoFar)}");
ProcedureKind kind = ProcedureKind.Method;
if (bestSoFar.isField || bestSoFar.isSetter || bestSoFar.isGetter) {
kind = isSetter ? ProcedureKind.Setter : ProcedureKind.Getter;
} else if (bestSoFar.target is Procedure &&
bestSoFar.target.kind == ProcedureKind.Operator) {
kind = ProcedureKind.Operator;
}
if (modifyKernel) {
debug?.log("Combined Member Signature of ${fullNameForErrors}: "
"new ForwardingNode($parent, $bestSoFar, $declarations, $kind)");
Member stub =
new ForwardingNode(hierarchy, parent, bestSoFar, declarations, kind)
.finalize();
if (parent.cls == stub.enclosingClass) {
parent.cls.addMember(stub);
SourceLibraryBuilder library = parent.library;
if (bestSoFar.target is Procedure) {
library.forwardersOrigins..add(stub)..add(bestSoFar.target);
}
debug?.log("Combined Member Signature of ${fullNameForErrors}: "
"added stub $stub");
if (parent.isMixinApplication) {
return combinedMemberSignatureResult = bestSoFar.target;
} else {
return combinedMemberSignatureResult = stub;
}
}
}
debug?.log(
"Combined Member Signature of ${fullNameForErrors}: picked bestSoFar");
return combinedMemberSignatureResult = bestSoFar.target;
}
@override
DelayedMember withParent(ClassBuilder parent) {
return parent == this.parent
? this
: new InterfaceConflict(parent, declarations, isSetter, modifyKernel);
}
static Builder combined(ClassBuilder parent, Builder a, Builder b,
bool isSetter, bool createForwarders) {
List<Builder> declarations = <Builder>[];
if (a is DelayedMember) {
a.addAllDeclarationsTo(declarations);
} else {
declarations.add(a);
}
if (b is DelayedMember) {
b.addAllDeclarationsTo(declarations);
} else {
addDeclarationIfDifferent(b, declarations);
}
if (declarations.length == 1) {
return declarations.single;
} else {
return new InterfaceConflict(
parent, declarations, isSetter, createForwarders);
}
}
}
class AbstractMemberOverridingImplementation extends DelayedMember {
AbstractMemberOverridingImplementation(
ClassBuilder parent,
Builder abstractMember,
Builder concreteImplementation,
bool isSetter,
bool modifyKernel)
: super(parent, <Builder>[concreteImplementation, abstractMember],
isSetter, modifyKernel);
Builder get concreteImplementation => declarations[0];
Builder get abstractMember => declarations[1];
Member check(ClassHierarchyBuilder hierarchy) {
if (!parent.isAbstract && !hierarchy.nodes[parent.cls].hasNoSuchMethod) {
new DelayedOverrideCheck(parent, concreteImplementation, abstractMember)
.check(hierarchy);
}
ProcedureKind kind = ProcedureKind.Method;
if (abstractMember.isSetter || abstractMember.isGetter) {
kind = isSetter ? ProcedureKind.Setter : ProcedureKind.Getter;
}
if (modifyKernel) {
// This call will add a body to the abstract method if needed for
// isGenericCovariantImpl checks.
new ForwardingNode(hierarchy, parent, abstractMember, declarations, kind)
.finalize();
}
return abstractMember.target;
}
@override
DelayedMember withParent(ClassBuilder parent) {
return parent == this.parent
? this
: new AbstractMemberOverridingImplementation(parent, abstractMember,
concreteImplementation, isSetter, modifyKernel);
}
static Builder selectAbstract(Builder declaration) {
if (declaration is AbstractMemberOverridingImplementation) {
return declaration.abstractMember;
} else {
return declaration;
}
}
static Builder selectConcrete(Builder declaration) {
if (declaration is AbstractMemberOverridingImplementation) {
return declaration.concreteImplementation;
} else {
return declaration;
}
}
}
void addDeclarationIfDifferent(
Builder declaration, List<Builder> declarations) {
Member target = declaration.target;
if (target is Procedure) {
FunctionNode function = target.function;
for (int i = 0; i < declarations.length; i++) {
Member other = declarations[i].target;
if (other is Procedure) {
if (hasSameSignature(function, other.function)) return;
}
}
} else {
for (int i = 0; i < declarations.length; i++) {
if (declaration == declarations[i]) return;
}
}
declarations.add(declaration);
}
String fullName(Builder declaration) {
String suffix = declaration.isSetter ? "=" : "";
if (declaration is DelayedMember) {
return "${declaration.fullNameForErrors}$suffix";
}
Builder parent = declaration.parent;
return parent == null
? "${declaration.fullNameForErrors}$suffix"
: "${parent.fullNameForErrors}.${declaration.fullNameForErrors}$suffix";
}
int compareNamedParameters(VariableDeclaration a, VariableDeclaration b) {
return a.name.compareTo(b.name);
}
bool isAbstract(Builder declaration) {
return declaration.target.isAbstract || declaration is InterfaceConflict;
}
bool inferParameterType(
ClassBuilder cls,
ProcedureBuilder member,
FormalParameterBuilder parameter,
DartType type,
bool hadTypesInferred,
ClassHierarchyBuilder hierarchy) {
debug?.log("Inferred type ${type} for ${parameter}");
if (type == parameter.target.type) return true;
bool result = true;
if (hadTypesInferred) {
reportCantInferParameterType(cls, member, parameter, hierarchy);
type = const InvalidType();
result = false;
}
parameter.target.type = type;
member.hadTypesInferred = true;
return result;
}
void reportCantInferParameterType(ClassBuilder cls, MemberBuilder member,
FormalParameterBuilder parameter, ClassHierarchyBuilder hierarchy) {
String name = parameter.name;
cls.addProblem(
templateCantInferTypeDueToInconsistentOverrides.withArguments(name),
parameter.charOffset,
name.length,
wasHandled: true);
}
bool inferReturnType(ClassBuilder cls, ProcedureBuilder procedureBuilder,
DartType type, bool hadTypesInferred, ClassHierarchyBuilder hierarchy) {
if (type == procedureBuilder.procedure.function.returnType) return true;
bool result = true;
if (hadTypesInferred) {
reportCantInferReturnType(cls, procedureBuilder, hierarchy);
type = const InvalidType();
result = false;
} else {
procedureBuilder.hadTypesInferred = true;
}
procedureBuilder.procedure.function.returnType = type;
return result;
}
void reportCantInferReturnType(
ClassBuilder cls, MemberBuilder member, ClassHierarchyBuilder hierarchy) {
String name = member.fullNameForErrors;
List<LocatedMessage> context;
// // TODO(ahe): The following is for debugging, but could be cleaned up and
// // used to improve this error message in general.
//
// context = <LocatedMessage>[];
// ClassHierarchyNode supernode = hierarchy.getNodeFromType(cls.supertype);
// // TODO(ahe): Wrong template.
// Template<Message Function(String)> template =
// templateMissingImplementationCause;
// if (supernode != null) {
// Declaration superMember =
// supernode.getInterfaceMember(new Name(name), false);
// if (superMember != null) {
// context.add(template
// .withArguments(name)
// .withLocation(
// superMember.fileUri, superMember.charOffset, name.length));
// }
// superMember = supernode.getInterfaceMember(new Name(name), true);
// if (superMember != null) {
// context.add(template
// .withArguments(name)
// .withLocation(
// superMember.fileUri, superMember.charOffset, name.length));
// }
// }
// List<TypeBuilder> directInterfaces = cls.interfaces;
// for (int i = 0; i < directInterfaces.length; i++) {
// ClassHierarchyNode supernode =
// hierarchy.getNodeFromType(directInterfaces[i]);
// if (supernode != null) {
// Declaration superMember =
// supernode.getInterfaceMember(new Name(name), false);
// if (superMember != null) {
// context.add(template
// .withArguments(name)
// .withLocation(
// superMember.fileUri, superMember.charOffset, name.length));
// }
// superMember = supernode.getInterfaceMember(new Name(name), true);
// if (superMember != null) {
// context.add(template
// .withArguments(name)
// .withLocation(
// superMember.fileUri, superMember.charOffset, name.length));
// }
// }
// }
cls.addProblem(
templateCantInferReturnTypeDueToInconsistentOverrides.withArguments(name),
member.charOffset,
name.length,
wasHandled: true,
context: context);
}
void reportCantInferFieldType(ClassBuilder cls, FieldBuilder member) {
String name = member.fullNameForErrors;
cls.addProblem(
templateCantInferTypeDueToInconsistentOverrides.withArguments(name),
member.charOffset,
name.length,
wasHandled: true);
}
ClassBuilder getClass(TypeBuilder type) {
Builder declaration = type.declaration;
return declaration is ClassBuilder ? declaration : null;
}
bool hasExplicitReturnType(Builder declaration) {
assert(declaration is ProcedureBuilder || declaration is DillMemberBuilder,
"${declaration.runtimeType}");
return declaration is ProcedureBuilder
? declaration.returnType != null
: true;
}
bool hasExplicitlyTypedFormalParameter(Builder declaration, int index) {
assert(declaration is ProcedureBuilder || declaration is DillMemberBuilder,
"${declaration.runtimeType}");
return declaration is ProcedureBuilder
? declaration.formals[index].type != null
: true;
}