diff --git a/pkg/front_end/lib/src/fasta/kernel/hierarchy/members_node.dart b/pkg/front_end/lib/src/fasta/kernel/hierarchy/members_node.dart index f40bc8c91e6a..04e6c910fd02 100644 --- a/pkg/front_end/lib/src/fasta/kernel/hierarchy/members_node.dart +++ b/pkg/front_end/lib/src/fasta/kernel/hierarchy/members_node.dart @@ -474,6 +474,30 @@ class ClassMembersNodeBuilder { } } + List _inheritedConflictContext(ClassMember a, ClassMember b) { + int length = a.fullNameForErrors.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.charOffset.compareTo(b.charOffset); + } + ClassMember first; + ClassMember second; + if (compare < 0) { + first = a; + second = b; + } else { + first = b; + second = a; + } + return [ + messageInheritedMembersConflictCause1.withLocation( + first.fileUri, first.charOffset, length), + messageInheritedMembersConflictCause2.withLocation( + second.fileUri, second.charOffset, length), + ]; + } + void reportInheritanceConflict(ClassMember a, ClassMember b) { String name = a.fullNameForErrors; while (a.hasDeclarations) { @@ -580,6 +604,89 @@ class ClassMembersNodeBuilder { } } + /// Returns `true` if the current class is from an opt-out library and + /// [classMember] is from an opt-in library. + /// + /// In this case a member signature needs to be inserted to show the + /// legacy erased type of the interface member. For instance: + /// + /// // Opt-in library: + /// class Super { + /// int? method(int i) {} + /// } + /// // Opt-out library: + /// class Class extends Super { + /// // A member signature is inserted: + /// // int* method(int* i); + /// } + /// + bool needsMemberSignatureFor(ClassMember classMember) { + return !classBuilder.libraryBuilder.isNonNullableByDefault && + classMember.classBuilder.libraryBuilder.isNonNullableByDefault; + } + + /// Registers that the current class has an interface member without a + /// corresponding class member. + /// + /// This is used to report missing implementation or, in the case the class + /// has a user defined concrete noSuchMethod, to insert noSuchMethod + /// forwarders. (Currently, insertion of forwarders is handled elsewhere.) + /// + /// For instance: + /// + /// abstract class Interface { + /// method(); + /// } + /// class Class1 implements Interface { + /// // Missing implementation for `Interface.method`. + /// } + /// class Class2 implements Interface { + /// noSuchMethod(_) {} + /// // A noSuchMethod forwarder is added for `Interface.method`. + /// } + /// + void registerAbstractMember( + List abstractMembers, ClassMember abstractMember) { + if (!abstractMember.isInternalImplementation) { + /// If `isInternalImplementation` is `true`, the member is synthesized + /// implementation that does not require implementation in other + /// classes. + /// + /// This is for instance used for late lowering where + /// + /// class Interface { + /// late int? field; + /// } + /// class Class implements Interface { + /// int? field; + /// } + /// + /// is encoded as + /// + /// class Interface { + /// bool _#field#isSet = false; + /// int? _#field = null; + /// int? get field => _#field#isSet ? _#field : throw ...; + /// void set field(int? value) { ... } + /// } + /// class Class implements Interface { + /// int? field; + /// } + /// + /// and `Class` should not be required to implement + /// `Interface._#field#isSet` and `Interface._#field`. + abstractMembers.add(abstractMember); + } + } + + /// Set to `true` during [build] if the class needs interfaces, that is, if it + /// has any members where the interface member is different from its + /// corresponding class members. + /// + /// This is an optimization to avoid unnecessary computation of interface + /// members. + bool _hasInterfaces = false; + ClassMembersNode build() { ClassMembersNode? supernode = _hierarchyNode.directSuperClassNode != null ? _membersBuilder.getNodeFromClassBuilder( @@ -588,14 +695,6 @@ class ClassMembersNodeBuilder { List? interfaceNodes = _hierarchyNode.directInterfaceNodes; - /// Set to `true` if the class needs interfaces, that is, if it has any - /// members where the interface member is different from its corresponding - /// class members. - /// - /// This is an optimization to avoid unnecessary computation of interface - /// members. - bool hasInterfaces = false; - /// Concrete user defined `noSuchMethod` member, declared or inherited. I.e. /// concrete `noSuchMethod` class member that is _not_ /// `Object.noSuchMethod`. @@ -610,7 +709,7 @@ class ClassMembersNodeBuilder { for (ClassMember classMember in memberBuilder.localMembers) { Name name = classMember.name; if (classMember.isAbstract) { - hasInterfaces = true; + _hasInterfaces = true; } Tuple? tuple = memberMap[name]; if (tuple == null) { @@ -627,7 +726,7 @@ class ClassMembersNodeBuilder { for (ClassMember classMember in memberBuilder.localSetters) { Name name = classMember.name; if (classMember.isAbstract) { - hasInterfaces = true; + _hasInterfaces = true; } Tuple? tuple = memberMap[name]; if (tuple == null) { @@ -665,7 +764,7 @@ class ClassMembersNodeBuilder { for (ClassMember classMember in memberBuilder.localMembers) { Name name = classMember.name; if (classMember.isAbstract || classMember.isNoSuchMethodForwarder) { - hasInterfaces = true; + _hasInterfaces = true; } Tuple? tuple = memberMap[name]; if (tuple == null) { @@ -682,7 +781,7 @@ class ClassMembersNodeBuilder { for (ClassMember classMember in memberBuilder.localSetters) { Name name = classMember.name; if (classMember.isAbstract || classMember.isNoSuchMethodForwarder) { - hasInterfaces = true; + _hasInterfaces = true; } Tuple? tuple = memberMap[name]; if (tuple == null) { @@ -751,10 +850,10 @@ class ClassMembersNodeBuilder { if (supernode.interfaceMemberMap != null || supernode.interfaceSetterMap != null) { - hasInterfaces = true; + _hasInterfaces = true; } - if (hasInterfaces) { + if (_hasInterfaces) { implement(supernode.interfaceMemberMap ?? supernode.classMemberMap); implement(supernode.interfaceSetterMap ?? supernode.classSetterMap); } @@ -763,7 +862,7 @@ class ClassMembersNodeBuilder { for (int i = 0; i < interfaceNodes.length; i++) { ClassMembersNode? interfaceNode = _membersBuilder .getNodeFromClassBuilder(interfaceNodes[i].classBuilder); - hasInterfaces = true; + _hasInterfaces = true; implement( interfaceNode.interfaceMemberMap ?? interfaceNode.classMemberMap); @@ -826,1548 +925,148 @@ class ClassMembersNodeBuilder { inheritedImplementsMap); } - /// Registers that the current class has an interface member without a - /// corresponding class member. - /// - /// This is used to report missing implementation or, in the case the class - /// has a user defined concrete noSuchMethod, to insert noSuchMethod - /// forwarders. (Currently, insertion of forwarders is handled elsewhere.) - /// - /// For instance: - /// - /// abstract class Interface { - /// method(); - /// } - /// class Class1 implements Interface { - /// // Missing implementation for `Interface.method`. - /// } - /// class Class2 implements Interface { - /// noSuchMethod(_) {} - /// // A noSuchMethod forwarder is added for `Interface.method`. - /// } - /// - void registerAbstractMember(ClassMember abstractMember) { - if (!abstractMember.isInternalImplementation) { - /// If `isInternalImplementation` is `true`, the member is synthesized - /// implementation that does not require implementation in other - /// classes. - /// - /// This is for instance used for late lowering where - /// - /// class Interface { - /// late int? field; - /// } - /// class Class implements Interface { - /// int? field; - /// } - /// - /// is encoded as - /// - /// class Interface { - /// bool _#field#isSet = false; - /// int? _#field = null; - /// int? get field => _#field#isSet ? _#field : throw ...; - /// void set field(int? value) { ... } - /// } - /// class Class implements Interface { - /// int? field; - /// } - /// - /// and `Class` should not be required to implement - /// `Interface._#field#isSet` and `Interface._#field`. - abstractMembers.add(abstractMember); - } - } - - /// Registers that [inheritedMember] should be checked to validly override - /// [overrides]. - /// - /// This is needed in the case where a concrete member is inherited into - /// a concrete subclass. For instance: - /// - /// class Super { - /// void method() {} - /// } - /// abstract class Interface { - /// void method(); - /// } - /// class Class extends Super implements Interface {} - /// - /// Here `Super.method` must be checked to be a valid implementation for - /// `Interface.method` by being a valid override of it. - void registerInheritedImplements( - ClassMember inheritedMember, Set overrides, - {required ClassMember aliasForTesting}) { - if (classBuilder is SourceClassBuilder) { - assert( - inheritedMember.classBuilder != classBuilder, - "Only inherited members can implement by inheritance: " - "${inheritedMember}"); - inheritedImplementsMap[inheritedMember] = overrides; - if (dataForTesting != null) { - dataForTesting.aliasMap[aliasForTesting] = inheritedMember; - } - } - } - - /// Returns `true` if the current class is from an opt-out library and - /// [classMember] is from an opt-in library. - /// - /// In this case a member signature needs to be inserted to show the - /// legacy erased type of the interface member. For instance: - /// - /// // Opt-in library: - /// class Super { - /// int? method(int i) {} - /// } - /// // Opt-out library: - /// class Class extends Super { - /// // A member signature is inserted: - /// // int* method(int* i); - /// } - /// - bool needsMemberSignatureFor(ClassMember classMember) { - return !classBuilder.libraryBuilder.isNonNullableByDefault && - classMember.classBuilder.libraryBuilder.isNonNullableByDefault; - } - void computeClassInterfaceMember(Name name, Tuple tuple) { /// The computation starts by sanitizing the members. Conflicts between /// methods and properties (getters/setters) or between static and /// instance members are reported. Conflicting members and members /// overridden by duplicates are removed. /// - /// For this [definingGetable] and [definingSetable] hold the first member - /// of its kind found among declared, mixed in, extended and implemented - /// members. - /// - /// Conflicts between [definingGetable] and [definingSetable] are reported - /// afterwards. + /// Conflicts between the getable and setable are reported afterwards. + var (_SanitizedMember? getable, _SanitizedMember? setable) = + tuple.sanitize(this); - ClassMember? definingGetable; - ClassMember? definingSetable; - - ClassMember? declaredGetable = tuple.declaredMember; - if (declaredGetable != null) { - /// class Class { - /// method() {} - /// } - definingGetable = declaredGetable; - } - ClassMember? declaredSetable = tuple.declaredSetter; - if (declaredSetable != null) { - /// class Class { - /// set setter(value) {} - /// } - definingSetable = declaredSetable; - } + _Overrides overrides = new _Overrides( + classBuilder: classBuilder, + inheritedImplementsMap: inheritedImplementsMap, + dataForTesting: dataForTesting); - ClassMember? mixedInGetable; - ClassMember? tupleMixedInMember = tuple.mixedInMember; - if (tupleMixedInMember != null && - !tupleMixedInMember.isStatic && - !tupleMixedInMember.isDuplicate && - !tupleMixedInMember.isSynthesized) { - /// We treat - /// - /// opt-in: - /// class Interface { - /// method3() {} - /// } - /// opt-out: - /// class Mixin implements Interface { - /// static method1() {} - /// method2() {} - /// method2() {} - /// /*member-signature*/ method3() {} - /// } - /// class Class with Mixin {} - /// - /// as - /// - /// class Mixin {} - /// class Class with Mixin {} - /// - /// Note that skipped synthetic getable 'method3' is still included - /// in the implemented getables, but its type will not define the type - /// when mixed in. For instance - /// - /// opt-in: - /// abstract class Interface { - /// num get getter; - /// } - /// opt-out: - /// abstract class Super { - /// int get getter; - /// } - /// abstract class Mixin implements Interface { - /// /*member-signature*/ num get getter; - /// } - /// abstract class Class extends Super with Mixin {} - /// - /// Here the type of `Class.getter` should not be defined from the - /// synthetic member signature `Mixin.getter` but as a combined member - /// signature of `Super.getter` and `Mixin.getter`, resulting in type - /// `int` instead of `num`. - if (definingGetable == null) { - /// class Mixin { - /// method() {} - /// } - /// class Class with Mixin {} - definingGetable = mixedInGetable = tupleMixedInMember; - } else if (!definingGetable.isDuplicate) { - // This case is currently unreachable from source code since classes - // cannot both declare and mix in members. From dill, this can occur - // but should not conflicting members. - // - // The case is handled for consistency. - if (definingGetable.isStatic || - definingGetable.isProperty != tupleMixedInMember.isProperty) { - reportInheritanceConflict(definingGetable, tupleMixedInMember); - } else { - mixedInGetable = tupleMixedInMember; - } - } - } - ClassMember? mixedInSetable; - ClassMember? tupleMixedInSetter = tuple.mixedInSetter; - if (tupleMixedInSetter != null && - !tupleMixedInSetter.isStatic && - !tupleMixedInSetter.isDuplicate && - !tupleMixedInSetter.isSynthesized) { - /// We treat - /// - /// class Mixin { - /// static set setter1(value) {} - /// set setter2(value) {} - /// set setter2(value) {} - /// /*member-signature*/ setter3() {} - /// } - /// class Class with Mixin {} - /// - /// as - /// - /// class Mixin {} - /// class Class with Mixin {} - /// - /// Note that skipped synthetic setable 'setter3' is still included - /// in the implemented setables, but its type will not define the type - /// when mixed in. For instance - /// - /// opt-in: - /// abstract class Interface { - /// void set setter(int value); - /// } - /// opt-out: - /// abstract class Super { - /// void set setter(num value); - /// } - /// abstract class Mixin implements Interface { - /// /*member-signature*/ num get getter; - /// } - /// abstract class Class extends Super with Mixin {} - /// - /// Here the type of `Class.setter` should not be defined from the - /// synthetic member signature `Mixin.setter` but as a combined member - /// signature of `Super.setter` and `Mixin.setter`, resulting in type - /// `num` instead of `int`. - if (definingSetable == null) { - /// class Mixin { - /// set setter(value) {} - /// } - /// class Class with Mixin {} - definingSetable = mixedInSetable = tupleMixedInSetter; - } else if (!definingSetable.isDuplicate) { - if (definingSetable.isStatic || - definingSetable.isProperty != tupleMixedInSetter.isProperty) { - reportInheritanceConflict(definingSetable, tupleMixedInSetter); - } else { - mixedInSetable = tupleMixedInSetter; - } - } + ClassMember? interfaceGetable; + if (getable != null) { + interfaceGetable = getable.computeMembers(this, overrides, + noSuchMethodMember: noSuchMethodMember, + userNoSuchMethodMember: userNoSuchMethodMember, + abstractMembers: abstractMembers, + classMemberMap: classMemberMap, + interfaceMemberMap: interfaceMemberMap, + dataForTesting: dataForTesting); } - - ClassMember? extendedGetable; - ClassMember? tupleExtendedMember = tuple.extendedMember; - if (tupleExtendedMember != null && - !tupleExtendedMember.isStatic && - !tupleExtendedMember.isDuplicate) { - /// We treat - /// - /// class Super { - /// static method1() {} - /// method2() {} - /// method2() {} - /// } - /// class Class extends Super {} - /// - /// as - /// - /// class Super {} - /// class Class extends Super {} - /// - if (definingGetable == null) { - /// class Super { - /// method() {} - /// } - /// class Class extends Super {} - definingGetable = extendedGetable = tupleExtendedMember; - } else if (!definingGetable.isDuplicate) { - if (definingGetable.isStatic || - definingGetable.isProperty != tupleExtendedMember.isProperty) { - /// class Super { - /// method() {} - /// } - /// class Class extends Super { - /// static method() {} - /// } - /// - /// or - /// - /// class Super { - /// method() {} - /// } - /// class Class extends Super { - /// get getter => 0; - /// } - reportInheritanceConflict(definingGetable, tupleExtendedMember); - } else { - extendedGetable = tupleExtendedMember; - } - } + ClassMember? interfaceSetable; + if (setable != null) { + interfaceSetable = setable.computeMembers(this, overrides, + noSuchMethodMember: noSuchMethodMember, + userNoSuchMethodMember: userNoSuchMethodMember, + abstractMembers: abstractMembers, + classMemberMap: classSetterMap, + interfaceMemberMap: interfaceSetterMap, + dataForTesting: dataForTesting); } - ClassMember? extendedSetable; - ClassMember? tupleExtendedSetter = tuple.extendedSetter; - if (tupleExtendedSetter != null && - !tupleExtendedSetter.isStatic && - !tupleExtendedSetter.isDuplicate) { - /// We treat - /// - /// class Super { - /// static set setter1(value) {} - /// set setter2(value) {} - /// set setter2(value) {} - /// } - /// class Class extends Super {} - /// - /// as - /// - /// class Super {} - /// class Class extends Super {} - /// - if (definingSetable == null) { - /// class Super { - /// set setter(value) {} - /// } - /// class Class extends Super {} - definingSetable = extendedSetable = tupleExtendedSetter; - } else if (!definingSetable.isDuplicate) { - if (definingSetable.isStatic || - definingSetable.isProperty != tupleExtendedSetter.isProperty) { - reportInheritanceConflict(definingSetable, tupleExtendedSetter); - } else { - extendedSetable = tupleExtendedSetter; - } + if (classBuilder is SourceClassBuilder) { + if (interfaceGetable != null && + interfaceSetable != null && + interfaceGetable.isProperty && + interfaceSetable.isProperty && + interfaceGetable.isStatic == interfaceSetable.isStatic && + !interfaceGetable.isSameDeclaration(interfaceSetable)) { + /// We need to check that the getter type is a subtype of the setter + /// type. For instance + /// + /// class Super { + /// int get property1 => null; + /// num get property2 => null; + /// } + /// class Mixin { + /// void set property1(num value) {} + /// void set property2(int value) {} + /// } + /// class Class = Super with Mixin; + /// + /// Here `Super.property1` and `Mixin.property1` form a valid getter/ + /// setter pair in `Class` because the type of the getter + /// `Super.property1` is a subtype of the setter `Mixin.property1`. + /// + /// In contrast the pair `Super.property2` and `Mixin.property2` is + /// not a valid getter/setter in `Class` because the type of the getter + /// `Super.property2` is _not_ a subtype of the setter + /// `Mixin.property1`. + _membersBuilder.registerGetterSetterCheck( + classBuilder as SourceClassBuilder, + interfaceGetable, + interfaceSetable); } } + overrides.collectOverrides( + getable: getable, + setable: setable, + mixinApplicationOverridesMap: mixinApplicationOverridesMap, + declaredOverridesMap: declaredOverridesMap); + } - // TODO(johnniwinther): Remove extended and mixed in members/setters - // from implemented members/setters. Mixin applications always implement - // the mixin class leading to unnecessary interface members. - List? implementedGetables; - List? tupleImplementedMembers = tuple.implementedMembers; - if (tupleImplementedMembers != null && - // Skip implemented members if we already have a duplicate. - !(definingGetable != null && definingGetable.isDuplicate)) { - for (int i = 0; i < tupleImplementedMembers.length; i++) { - ClassMember? implementedGetable = tupleImplementedMembers[i]; - if (implementedGetable.isStatic || implementedGetable.isDuplicate) { - /// We treat - /// - /// class Interface { - /// static method1() {} - /// method2() {} - /// method2() {} - /// } - /// class Class implements Interface {} - /// - /// as - /// - /// class Interface {} - /// class Class implements Interface {} - /// - implementedGetable = null; - } else { - if (definingGetable == null) { - /// class Interface { - /// method() {} - /// } - /// class Class implements Interface {} - definingGetable = implementedGetable; - } else if (definingGetable.isStatic || - definingGetable.isProperty != implementedGetable.isProperty) { - /// class Interface { - /// method() {} - /// } - /// class Class implements Interface { - /// static method() {} - /// } - /// - /// or - /// - /// class Interface { - /// method() {} - /// } - /// class Class implements Interface { - /// get getter => 0; - /// } - reportInheritanceConflict(definingGetable, implementedGetable); - implementedGetable = null; - } - } - if (implementedGetable == null) { - // On the first skipped member we add all previous. - implementedGetables ??= tupleImplementedMembers.take(i).toList(); - } else if (implementedGetables != null) { - // If already skipping members we add [implementedGetable] - // explicitly. - implementedGetables.add(implementedGetable); - } - } - if (implementedGetables == null) { - // No members were skipped so we use the full list. - implementedGetables = tupleImplementedMembers; - } else if (implementedGetables.isEmpty) { - // No members were included. - implementedGetables = null; - } - } + // Compute the 'noSuchMethod' member first so we know the target for + // noSuchMethod forwarders. + Tuple? noSuchMethod = memberMap.remove(noSuchMethodName); + if (noSuchMethod != null) { + // The noSuchMethod is always available - unless Object is not valid. + // See for instance pkg/front_end/test/fasta/object_supertype_test.dart + computeClassInterfaceMember(noSuchMethodName, noSuchMethod); + } + noSuchMethodMember = interfaceMemberMap[noSuchMethodName] ?? + classMemberMap[noSuchMethodName]; - List? implementedSetables; - List? tupleImplementedSetters = tuple.implementedSetters; - if (tupleImplementedSetters != null && - // Skip implemented setters if we already have a duplicate. - !(definingSetable != null && definingSetable.isDuplicate)) { - for (int i = 0; i < tupleImplementedSetters.length; i++) { - ClassMember? implementedSetable = tupleImplementedSetters[i]; - if (implementedSetable.isStatic || implementedSetable.isDuplicate) { - /// We treat - /// - /// class Interface { - /// static set setter1(value) {} - /// set setter2(value) {} - /// set setter2(value) {} - /// } - /// class Class implements Interface {} - /// - /// as - /// - /// class Interface {} - /// class Class implements Interface {} - /// - implementedSetable = null; - } else { - if (definingSetable == null) { - /// class Interface { - /// set setter(value) {} - /// } - /// class Class implements Interface {} - definingSetable = implementedSetable; - } else if (definingSetable.isStatic || - definingSetable.isProperty != implementedSetable.isProperty) { - /// class Interface { - /// set setter(value) {} - /// } - /// class Class implements Interface { - /// static set setter(value) {} - /// } - reportInheritanceConflict(definingSetable, implementedSetable); - implementedSetable = null; - } - } - if (implementedSetable == null) { - // On the first skipped setter we add all previous. - implementedSetables ??= tupleImplementedSetters.take(i).toList(); - } else if (implementedSetables != null) { - // If already skipping setters we add [implementedSetable] - // explicitly. - implementedSetables.add(implementedSetable); - } - } - if (implementedSetables == null) { - // No setters were skipped so we use the full list. - implementedSetables = tupleImplementedSetters; - } else if (implementedSetables.isEmpty) { - // No setters were included. - implementedSetables = null; - } - } + memberMap.forEach(computeClassInterfaceMember); - if (definingGetable != null && definingSetable != null) { - if (definingGetable.isStatic != definingSetable.isStatic || - definingGetable.isProperty != definingSetable.isProperty) { - reportInheritanceConflict(definingGetable, definingSetable); - // TODO(johnniwinther): Should we remove [definingSetable]? If we - // leave it in this conflict will also be reported in subclasses. If - // we remove it, any write to the setable will be unresolved. - } - } + if (classBuilder is SourceClassBuilder) { + // TODO(johnniwinther): Avoid duplicate override check computations + // between [declaredOverridesMap], [mixinApplicationOverridesMap] and + // [inheritedImplementsMap]. - // TODO(johnniwinther): Handle declared members together with mixed in - // members. This should only occur from .dill, though. - if (mixedInGetable != null) { - declaredGetable = null; - } - if (mixedInSetable != null) { - declaredSetable = null; - } + // TODO(johnniwinther): Ensure that a class member is only checked to + // validly override another member once. Currently it can happen multiple + // times as an inherited implementation. - /// Set to `true` if declared members have been registered in - /// [registerDeclaredOverride] or [registerMixedInOverride]. - bool hasDeclaredMembers = false; + declaredOverridesMap.forEach( + (ClassMember classMember, Set overriddenMembers) { + /// A declared member can inherit its type from the overridden members. + /// + /// We register this with the class member itself so the it can force + /// computation of type on the overridden members before determining its + /// own type. + /// + /// Member types can be queried at arbitrary points during top level + /// inference so we need to ensure that types are computed in dependency + /// order. + classMember.registerOverrideDependency(overriddenMembers); - /// Declared methods, getters and setters registered in - /// [registerDeclaredOverride]. - ClassMember? declaredMethod; - List? declaredProperties; + /// Not all member type are queried during top level inference so we + /// register delayed computation to ensure that all types have been + /// computed before override checks are performed. + DelayedTypeComputation computation = + new DelayedTypeComputation(this, classMember, overriddenMembers); + _membersBuilder.registerDelayedTypeComputation(computation); - /// Declared methods, getters and setters registered in - /// [registerDeclaredOverride]. - ClassMember? mixedInMethod; - List? mixedInProperties; + /// Declared members must be checked to validly override the + /// overridden members. + _membersBuilder.registerOverrideCheck( + classBuilder as SourceClassBuilder, classMember, overriddenMembers); + }); - /// Registers that [declaredMember] overrides extended and implemented - /// members. - /// - /// Getters and setters share overridden members so the registration - /// of override relations is performed after the interface members have - /// been computed. - /// - /// Declared members must be checked for valid override of the overridden - /// members _and_ must register an override dependency with the overridden - /// members so that override inference can propagate inferred types - /// correctly. For instance: - /// - /// class Super { - /// int get property => 42; - /// } - /// class Class extends Super { - /// void set property(value) {} - /// } - /// - /// Here the parameter type of the setter `Class.property` must be - /// inferred from the type of the getter `Super.property`. - void registerDeclaredOverride(ClassMember declaredMember, - {ClassMember? aliasForTesting}) { - if (classBuilder is SourceClassBuilder && !declaredMember.isStatic) { - assert( - declaredMember.isSourceDeclaration && - declaredMember.classBuilder.origin == classBuilder, - "Only declared members can override: ${declaredMember}"); - hasDeclaredMembers = true; - if (declaredMember.isProperty) { - declaredProperties ??= []; - declaredProperties!.add(declaredMember); - } else { - assert( - declaredMethod == null, - "Multiple methods unexpectedly declared: " - "${declaredMethod} and ${declaredMember}."); - declaredMethod = declaredMember; - } - if (dataForTesting != null && aliasForTesting != null) { - dataForTesting.aliasMap[aliasForTesting] = declaredMember; - } - } - } + mixinApplicationOverridesMap.forEach( + (ClassMember classMember, Set overriddenMembers) { + /// Declared mixed in members must be checked to validly override the + /// overridden members. + _membersBuilder.registerOverrideCheck( + classBuilder as SourceClassBuilder, classMember, overriddenMembers); + }); - /// Registers that [mixedMember] overrides extended and implemented - /// members through application. - /// - /// Getters and setters share overridden members so the registration - /// of override relations in performed after the interface members have - /// been computed. - /// - /// Declared mixed in members must be checked for valid override of the - /// overridden members but _not_ register an override dependency with the - /// overridden members. This is in contrast to declared members. For - /// instance: - /// - /// class Super { - /// int get property => 42; - /// } - /// class Mixin { - /// void set property(value) {} - /// } - /// class Class = Super with Mixin; - /// - /// Here the parameter type of the setter `Mixin.property` must _not_ be - /// inferred from the type of the getter `Super.property`, but should - /// instead default to `dynamic`. - void registerMixedInOverride(ClassMember mixedInMember, - {ClassMember? aliasForTesting}) { - assert(mixedInMember.classBuilder != classBuilder, - "Only mixin members can override by application: ${mixedInMember}"); - if (classBuilder is SourceClassBuilder) { - hasDeclaredMembers = true; - if (mixedInMember.isProperty) { - mixedInProperties ??= []; - mixedInProperties!.add(mixedInMember); - } else { - assert( - mixedInMethod == null, - "Multiple methods unexpectedly declared in mixin: " - "${mixedInMethod} and ${mixedInMember}."); - mixedInMethod = mixedInMember; - } - if (dataForTesting != null && aliasForTesting != null) { - dataForTesting.aliasMap[aliasForTesting] = mixedInMember; - } - } - } + inheritedImplementsMap.forEach( + (ClassMember classMember, Set overriddenMembers) { + /// Concrete members must be checked to validly override the overridden + /// members in concrete classes. + _membersBuilder.registerOverrideCheck( + classBuilder as SourceClassBuilder, classMember, overriddenMembers); + }); + } - /// Computes the class and interface members for a method, getter, or - /// setter in the current [tuple]. - /// - /// [definingMember] is the member which defines whether the computation - /// is for a method, a getter or a setter. - /// [declaredMember] is the member declared in the current class, if any. - /// [mixedInMember] is the member declared in a mixin that is mixed into - /// the current class, if any. - /// [extendedMember] is the member inherited from the super class. - /// [implementedMembers] are the members inherited from the super - /// interfaces, if none this is `null`. - /// - /// The computed class and interface members are added to [classMemberMap] - /// and [interfaceMemberMap], respectively. - ClassMember? computeMembers( - {required ClassMember definingMember, - required ClassMember? declaredMember, - required ClassMember? mixedInMember, - required ClassMember? extendedMember, - required List? implementedMembers, - required Map classMemberMap, - required Map? interfaceMemberMap}) { - ClassMember? classMember; - ClassMember? interfaceMember; - - /// A noSuchMethodForwarder can be inserted in non-abstract class - /// if a user defined noSuchMethod implementation is available or - /// if the member is not accessible from this library; - bool canHaveNoSuchMethodForwarder = !classBuilder.isAbstract && - (userNoSuchMethodMember != null || - !isNameVisibleIn(name, classBuilder.libraryBuilder)); - - if (mixedInMember != null) { - if (mixedInMember.isAbstract || - mixedInMember.isNoSuchMethodForwarder) { - /// class Mixin { - /// method(); - /// } - /// class Class = Object with Mixin; - - /// Interface members from the extended, mixed in, and implemented - /// members define the combined member signature. - Set interfaceMembers = {}; - - if (extendedMember != null) { - /// class Super { - /// method() {} - /// } - /// class Mixin { - /// method(); - /// } - /// class Class = Super with Mixin; - interfaceMembers.add(extendedMember.interfaceMember); - } - - interfaceMembers.add(mixedInMember); - - if (implementedMembers != null) { - /// class Interface { - /// method() {} - /// } - /// class Mixin { - /// method(); - /// } - /// class Class = Object with Mixin implements Interface; - interfaceMembers.addAll(implementedMembers); - } - - ClassMember? noSuchMethodTarget; - if (canHaveNoSuchMethodForwarder && - (extendedMember == null || - extendedMember.isNoSuchMethodForwarder)) { - /// class Super { - /// noSuchMethod(_) => null; - /// extendedMember(); - /// } - /// abstract class Mixin { - /// mixinMethod(); - /// extendedMember(); - /// } - /// class Class = Super with Mixin /* - /// mixinMethod() => ...; // noSuchMethod forwarder created - /// */; - noSuchMethodTarget = noSuchMethodMember; - } - - /// We always create a synthesized interface member, even in the - /// case of [interfaceMembers] being a singleton, to insert the - /// abstract mixin stub. - interfaceMember = new SynthesizedInterfaceMember( - classBuilder, name, interfaceMembers.toList(), - superClassMember: extendedMember, - // [definingMember] and [mixedInMember] are always the same - // here. Use the latter here and the former below to show the - // the member is canonical _because_ its the mixed in member and - // it defines the isProperty/forSetter properties _because_ it - // is the defining member. - canonicalMember: mixedInMember, - mixedInMember: mixedInMember, - noSuchMethodTarget: noSuchMethodTarget, - isProperty: definingMember.isProperty, - forSetter: definingMember.forSetter, - shouldModifyKernel: shouldModifyKernel); - _membersBuilder.registerMemberComputation(interfaceMember); - - if (extendedMember != null) { - /// class Super { - /// method() {} - /// } - /// class Mixin { - /// method(); - /// } - /// class Class = Super with Mixin; - /// - /// The concrete extended member is the class member but might - /// be overwritten by a concrete forwarding stub: - /// - /// class Super { - /// method(int i) {} - /// } - /// class Interface { - /// method(covariant int i) {} - /// } - /// class Mixin { - /// method(int i); - /// } - /// // A concrete forwarding stub - /// // method(covariant int i) => super.method(i); - /// // will be inserted. - /// class Class = Super with Mixin implements Interface; - /// - classMember = new InheritedClassMemberImplementsInterface( - classBuilder, name, - inheritedClassMember: extendedMember, - implementedInterfaceMember: interfaceMember, - forSetter: definingMember.forSetter, - isProperty: definingMember.isProperty); - _membersBuilder.registerMemberComputation(classMember); - if (!classBuilder.isAbstract) { - registerInheritedImplements(extendedMember, {interfaceMember}, - aliasForTesting: classMember); - } - } else if (noSuchMethodTarget != null) { - classMember = interfaceMember; - } else if (!classBuilder.isAbstract) { - assert(!canHaveNoSuchMethodForwarder); - - /// class Mixin { - /// method(); // Missing implementation. - /// } - /// class Class = Object with Mixin; - registerAbstractMember(interfaceMember); - } - - assert(!mixedInMember.isSynthesized); - if (!mixedInMember.isSynthesized) { - /// Members declared in the mixin must override extended and - /// implemented members. - /// - /// When loading from .dill the mixed in member might be - /// synthesized, for instance a member signature or forwarding - /// stub, and this should not be checked to override the extended - /// and implemented members: - /// - /// // Opt-out library, from source: - /// class Mixin {} - /// // Opt-out library, from .dill: - /// class Mixin { - /// ... - /// String* toString(); // member signature - /// } - /// // Opt-out library, from source: - /// class Class = Object with Mixin; - /// // Mixin.toString should not be checked to override - /// // Object.toString. - /// - registerMixedInOverride(mixedInMember, - aliasForTesting: interfaceMember); - } - } else { - assert(!mixedInMember.isAbstract); - - /// class Mixin { - /// method() {} - /// } - /// class Class = Object with Mixin; - /// - - /// Interface members from the extended, mixed in, and implemented - /// members define the combined member signature. - Set interfaceMembers = {}; - - if (extendedMember != null) { - /// class Super { - /// method() {} - /// } - /// class Mixin { - /// method() {} - /// } - /// class Class = Super with Mixin; - interfaceMembers.add(extendedMember.interfaceMember); - } - - interfaceMembers.add(mixedInMember); - - if (implementedMembers != null) { - /// class Interface { - /// method() {} - /// } - /// class Mixin { - /// method() {} - /// } - /// class Class = Object with Mixin implements Interface; - interfaceMembers.addAll(implementedMembers); - } - - /// We always create a synthesized interface member, even in the - /// case of [interfaceMembers] being a singleton, to insert the - /// concrete mixin stub. - interfaceMember = new SynthesizedInterfaceMember( - classBuilder, name, interfaceMembers.toList(), - superClassMember: mixedInMember, - // [definingMember] and [mixedInMember] are always the same - // here. Use the latter here and the former below to show the - // the member is canonical _because_ its the mixed in member and - // it defines the isProperty/forSetter properties _because_ it - // is the defining member. - canonicalMember: mixedInMember, - mixedInMember: mixedInMember, - isProperty: definingMember.isProperty, - forSetter: definingMember.forSetter, - shouldModifyKernel: shouldModifyKernel); - _membersBuilder.registerMemberComputation(interfaceMember); - - /// The concrete mixed in member is the class member but will - /// be overwritten by a concrete mixin stub: - /// - /// class Mixin { - /// method() {} - /// } - /// // A concrete mixin stub - /// // method() => super.method(); - /// // will be inserted. - /// class Class = Object with Mixin; - /// - classMember = new InheritedClassMemberImplementsInterface( - classBuilder, name, - inheritedClassMember: mixedInMember, - implementedInterfaceMember: interfaceMember, - forSetter: definingMember.forSetter, - isProperty: definingMember.isProperty); - _membersBuilder.registerMemberComputation(classMember); - - if (!classBuilder.isAbstract) { - /// class Interface { - /// method() {} - /// } - /// class Mixin { - /// method() {} - /// } - /// class Class = Object with Mixin; - /// - /// [mixinMember] must implemented interface member. - registerInheritedImplements(mixedInMember, {interfaceMember}, - aliasForTesting: classMember); - } - assert(!mixedInMember.isSynthesized); - if (!mixedInMember.isSynthesized) { - /// Members declared in the mixin must override extended and - /// implemented members. - /// - /// When loading from .dill the mixed in member might be - /// synthesized, for instance a member signature or forwarding - /// stub, and this should not be checked to override the extended - /// and implemented members. - /// - /// These synthesized mixed in members should always be abstract - /// and therefore not be handled here, but we handled them here - /// for consistency. - registerMixedInOverride(mixedInMember); - } - } - } else if (declaredMember != null) { - if (declaredMember.isAbstract) { - /// class Class { - /// method(); - /// } - interfaceMember = declaredMember; - - /// Interface members from the declared, extended, and implemented - /// members define the combined member signature. - Set interfaceMembers = {}; - - if (extendedMember != null) { - /// class Super { - /// method() {} - /// } - /// class Class extends Super { - /// method(); - /// } - interfaceMembers.add(extendedMember); - } - - interfaceMembers.add(declaredMember); - - if (implementedMembers != null) { - /// class Interface { - /// method() {} - /// } - /// class Class implements Interface { - /// method(); - /// } - interfaceMembers.addAll(implementedMembers); - } - - ClassMember? noSuchMethodTarget; - if (canHaveNoSuchMethodForwarder && - (extendedMember == null || - extendedMember.isNoSuchMethodForwarder)) { - /// class Super { - /// noSuchMethod(_) => null; - /// extendedMember(); - /// } - /// class Class extends Super { - /// declaredMethod(); // noSuchMethod forwarder created - /// extendedMember(); - /// } - noSuchMethodTarget = noSuchMethodMember; - } - - /// If only one member defines the interface member there is no - /// need for a synthesized interface member, since its result will - /// simply be that one member. - if (interfaceMembers.length > 1 || noSuchMethodTarget != null) { - /// class Super { - /// method() {} - /// } - /// class Interface { - /// method() {} - /// } - /// class Class extends Super implements Interface { - /// method(); - /// } - interfaceMember = new SynthesizedInterfaceMember( - classBuilder, name, interfaceMembers.toList(), - superClassMember: extendedMember, - // [definingMember] and [declaredMember] are always the same - // here. Use the latter here and the former below to show the - // the member is canonical _because_ its the declared member - // and it defines the isProperty/forSetter properties - // _because_ it is the defining member. - canonicalMember: declaredMember, - noSuchMethodTarget: noSuchMethodTarget, - isProperty: definingMember.isProperty, - forSetter: definingMember.forSetter, - shouldModifyKernel: shouldModifyKernel); - _membersBuilder.registerMemberComputation(interfaceMember); - } - - if (extendedMember != null) { - /// class Super { - /// method() {} - /// } - /// class Class extends Super { - /// method(); - /// } - /// - /// The concrete extended member is the class member but might - /// be overwritten by a concrete forwarding stub: - /// - /// class Super { - /// method(int i) {} - /// } - /// class Interface { - /// method(covariant int i) {} - /// } - /// class Class extends Super implements Interface { - /// // This will be turned into the concrete forwarding stub - /// // method(covariant int i) => super.method(i); - /// method(int i); - /// } - /// - classMember = new InheritedClassMemberImplementsInterface( - classBuilder, name, - inheritedClassMember: extendedMember, - implementedInterfaceMember: interfaceMember, - forSetter: definingMember.forSetter, - isProperty: definingMember.isProperty); - _membersBuilder.registerMemberComputation(classMember); - - if (!classBuilder.isAbstract && noSuchMethodTarget == null) { - /// class Super { - /// method() {} - /// } - /// class Class extends Super { - /// method(); - /// } - /// - /// [extendedMember] must implemented interface member. - registerInheritedImplements(extendedMember, {interfaceMember}, - aliasForTesting: classMember); - } - } else if (noSuchMethodTarget != null) { - classMember = interfaceMember; - } else if (!classBuilder.isAbstract) { - assert(!canHaveNoSuchMethodForwarder); - - /// class Class { - /// method(); // Missing implementation. - /// } - registerAbstractMember(declaredMember); - } - - /// The declared member must override extended and implemented - /// members. - registerDeclaredOverride(declaredMember, - aliasForTesting: interfaceMember); - } else { - assert(!declaredMember.isAbstract); - - /// class Class { - /// method() {} - /// } - classMember = declaredMember; - - /// The declared member must override extended and implemented - /// members. - registerDeclaredOverride(declaredMember); - } - } else if (extendedMember != null) { - /// class Super { - /// method() {} - /// } - /// class Class extends Super {} - assert(!extendedMember.isAbstract, - "Abstract extended member: ${extendedMember}"); - - classMember = extendedMember; - - if (implementedMembers != null) { - /// class Super { - /// method() {} - /// } - /// class Interface { - /// method() {} - /// } - /// class Class extends Super implements Interface {} - ClassMember extendedInterfaceMember = - extendedMember.interfaceMember; - - /// Interface members from the extended and implemented - /// members define the combined member signature. - Set interfaceMembers = {extendedInterfaceMember}; - - // TODO(johnniwinther): The extended member might be included in - // a synthesized implemented member. For instance: - // - // class Super { - // void method() {} - // } - // class Interface { - // void method() {} - // } - // abstract class Class extends Super implements Interface { - // // Synthesized interface member of - // // {Super.method, Interface.method} - // } - // class Sub extends Class { - // // Super.method implements Class.method = - // // {Super.method, Interface.method} - // // Synthesized interface member of - // // {Super.method, Class.method} - // } - // - // Maybe we should recognize this. - interfaceMembers.addAll(implementedMembers); - - ClassMember? noSuchMethodTarget; - if (extendedMember.isNoSuchMethodForwarder && - !classBuilder.isAbstract && - (userNoSuchMethodMember != null || - !isNameVisibleIn(name, classBuilder.libraryBuilder))) { - noSuchMethodTarget = noSuchMethodMember; - } - - /// Normally, if only one member defines the interface member there - /// is no need for a synthesized interface member, since its result - /// will simply be that one member, but if the extended member is - /// from an opt-in library and the current class is from an opt-out - /// library we need to create a member signature: - /// - /// // Opt-in: - /// class Super { - /// int? method() => null; - /// } - /// class Interface implements Super {} - /// // Opt-out: - /// class Class extends Super implements Interface { - /// // Member signature added: - /// int* method(); - /// } - /// - if (interfaceMembers.length == 1 && - !needsMemberSignatureFor(extendedInterfaceMember) && - noSuchMethodTarget == null) { - /// class Super { - /// method() {} - /// } - /// class Interface implements Super {} - /// class Class extends Super implements Interface {} - interfaceMember = interfaceMembers.first; - } else { - /// class Super { - /// method() {} - /// } - /// class Interface { - /// method() {} - /// } - /// class Class extends Super implements Interface {} - interfaceMember = new SynthesizedInterfaceMember( - classBuilder, name, interfaceMembers.toList(), - superClassMember: extendedMember, - noSuchMethodTarget: noSuchMethodTarget, - isProperty: definingMember.isProperty, - forSetter: definingMember.forSetter, - shouldModifyKernel: shouldModifyKernel); - _membersBuilder.registerMemberComputation(interfaceMember); - } - if (interfaceMember == classMember) { - /// class Super { - /// method() {} - /// } - /// class Interface implements Super {} - /// class Class extends Super implements Interface {} - /// - /// We keep track of whether a class needs interfaces, that is, - /// whether is has any members that have an interface member - /// different from its corresponding class member, so we set - /// [interfaceMember] to `null` so show that the interface member - /// is not needed. - interfaceMember = null; - } else { - /// class Super { - /// method() {} - /// } - /// class Interface { - /// method() {} - /// } - /// class Class extends Super implements Interface {} - /// - /// The concrete extended member is the class member but might - /// be overwritten by a concrete forwarding stub: - /// - /// class Super { - /// method(int i) {} - /// } - /// class Interface { - /// method(covariant int i) {} - /// } - /// class Class extends Super implements Interface { - /// // A concrete forwarding stub will be created: - /// // method(covariant int i) => super.method(i); - /// } - /// - classMember = new InheritedClassMemberImplementsInterface( - classBuilder, name, - inheritedClassMember: extendedMember, - implementedInterfaceMember: interfaceMember, - isProperty: definingMember.isProperty, - forSetter: definingMember.forSetter); - _membersBuilder.registerMemberComputation(classMember); - if (!classBuilder.isAbstract && noSuchMethodTarget == null) { - /// class Super { - /// method() {} - /// } - /// class Interface { - /// method() {} - /// } - /// class Class extends Super implements Interface {} - registerInheritedImplements(extendedMember, {interfaceMember}, - aliasForTesting: classMember); - } - } - } else if (needsMemberSignatureFor(extendedMember)) { - /// // Opt-in library: - /// class Super { - /// method() {} - /// } - /// // opt-out library: - /// class Class extends Super {} - interfaceMember = new SynthesizedInterfaceMember( - classBuilder, name, [extendedMember], - superClassMember: extendedMember, - isProperty: definingMember.isProperty, - forSetter: definingMember.forSetter, - shouldModifyKernel: shouldModifyKernel); - _membersBuilder.registerMemberComputation(interfaceMember); - - /// The concrete extended member is the class member and should - /// be able to be overwritten by a synthesized concrete member here, - /// but we handle the case for consistency. - classMember = new InheritedClassMemberImplementsInterface( - classBuilder, name, - inheritedClassMember: extendedMember, - implementedInterfaceMember: interfaceMember, - isProperty: definingMember.isProperty, - forSetter: definingMember.forSetter); - _membersBuilder.registerMemberComputation(classMember); - } - } else if (implementedMembers != null) { - /// class Interface { - /// method() {} - /// } - /// class Class implements Interface {} - Set interfaceMembers = implementedMembers.toSet(); - if (interfaceMembers.isNotEmpty) { - ClassMember? noSuchMethodTarget; - if (canHaveNoSuchMethodForwarder) { - /// abstract class Interface { - /// implementedMember(); - /// } - /// class Class implements Interface { - /// noSuchMethod(_) => null; - /// implementedMember(); // noSuchMethod forwarder created - /// } - noSuchMethodTarget = noSuchMethodMember; - } - - /// Normally, if only one member defines the interface member there - /// is no need for a synthesized interface member, since its result - /// will simply be that one member, but if the implemented member is - /// from an opt-in library and the current class is from an opt-out - /// library we need to create a member signature: - /// - /// // Opt-in: - /// class Interface { - /// int? method() => null; - /// } - /// // Opt-out: - /// class Class implements Interface { - /// // Member signature added: - /// int* method(); - /// } - /// - if (interfaceMembers.length == 1 && - !needsMemberSignatureFor(interfaceMembers.first) && - noSuchMethodTarget == null) { - /// class Interface { - /// method() {} - /// } - /// class Class implements Interface {} - interfaceMember = interfaceMembers.first; - } else { - /// class Interface1 { - /// method() {} - /// } - /// class Interface2 { - /// method() {} - /// } - /// class Class implements Interface1, Interface2 {} - interfaceMember = new SynthesizedInterfaceMember( - classBuilder, name, interfaceMembers.toList(), - noSuchMethodTarget: noSuchMethodTarget, - isProperty: definingMember.isProperty, - forSetter: definingMember.forSetter, - shouldModifyKernel: shouldModifyKernel); - _membersBuilder.registerMemberComputation(interfaceMember); - } - if (noSuchMethodTarget != null) { - classMember = interfaceMember; - } else if (!classBuilder.isAbstract) { - assert(!canHaveNoSuchMethodForwarder); - - /// class Interface { - /// method() {} - /// } - /// class Class implements Interface {} - registerAbstractMember(interfaceMember); - } - } - } - - if (interfaceMember != null) { - // We have an explicit interface. - hasInterfaces = true; - } - if (classMember != null) { - classMemberMap[name] = classMember; - interfaceMember ??= classMember.interfaceMember; - } - if (interfaceMember != null) { - interfaceMemberMap![name] = interfaceMember; - } - return interfaceMember; - } - - ClassMember? interfaceGetable; - if (definingGetable != null) { - interfaceGetable = computeMembers( - definingMember: definingGetable, - declaredMember: declaredGetable, - mixedInMember: mixedInGetable, - extendedMember: extendedGetable, - implementedMembers: implementedGetables, - classMemberMap: classMemberMap, - interfaceMemberMap: interfaceMemberMap); - } - ClassMember? interfaceSetable; - if (definingSetable != null) { - interfaceSetable = computeMembers( - definingMember: definingSetable, - declaredMember: declaredSetable, - mixedInMember: mixedInSetable, - extendedMember: extendedSetable, - implementedMembers: implementedSetables, - classMemberMap: classSetterMap, - interfaceMemberMap: interfaceSetterMap); - } - if (classBuilder is SourceClassBuilder) { - if (interfaceGetable != null && - interfaceSetable != null && - interfaceGetable.isProperty && - interfaceSetable.isProperty && - interfaceGetable.isStatic == interfaceSetable.isStatic && - !interfaceGetable.isSameDeclaration(interfaceSetable)) { - /// We need to check that the getter type is a subtype of the setter - /// type. For instance - /// - /// class Super { - /// int get property1 => null; - /// num get property2 => null; - /// } - /// class Mixin { - /// void set property1(num value) {} - /// void set property2(int value) {} - /// } - /// class Class = Super with Mixin; - /// - /// Here `Super.property1` and `Mixin.property1` form a valid getter/ - /// setter pair in `Class` because the type of the getter - /// `Super.property1` is a subtype of the setter `Mixin.property1`. - /// - /// In contrast the pair `Super.property2` and `Mixin.property2` is - /// not a valid getter/setter in `Class` because the type of the getter - /// `Super.property2` is _not_ a subtype of the setter - /// `Mixin.property1`. - _membersBuilder.registerGetterSetterCheck( - classBuilder as SourceClassBuilder, - interfaceGetable, - interfaceSetable); - } - } - if (hasDeclaredMembers) { - Set getableOverrides = {}; - Set setableOverrides = {}; - if (extendedGetable != null) { - /// (abstract) class Super { - /// method() {} - /// int get property => 0; - /// } - /// (abstract) class Class extends Super { - /// method() {} - /// set property(int value) {} - /// } - getableOverrides.add(extendedGetable.interfaceMember); - } - if (extendedSetable != null) { - /// (abstract) class Super { - /// set setter(int value) {} - /// set property(int value) {} - /// } - /// (abstract) class Class extends Super { - /// set setter(int value) {} - /// int get property => 0; - /// } - setableOverrides.add(extendedSetable.interfaceMember); - } - if (implementedGetables != null) { - /// (abstract) class Interface { - /// method() {} - /// int get property => 0; - /// } - /// (abstract) class Class implements Interface { - /// method() {} - /// set property(int value) {} - /// } - getableOverrides.addAll(implementedGetables); - } - if (implementedSetables != null) { - /// (abstract) class Interface { - /// set setter(int value) {} - /// set property(int value) {} - /// } - /// (abstract) class Class implements Interface { - /// set setter(int value) {} - /// int get property => 0; - /// } - setableOverrides.addAll(implementedSetables); - } - if (getableOverrides.isNotEmpty || setableOverrides.isNotEmpty) { - if (declaredMethod != null && getableOverrides.isNotEmpty) { - /// class Super { - /// method() {} - /// } - /// class Class extends Super { - /// method() {} - /// } - declaredOverridesMap[declaredMethod!] = getableOverrides; - } - if (declaredProperties != null) { - Set overrides; - if (declaredMethod != null) { - /// class Super { - /// set setter() {} - /// } - /// class Class extends Super { - /// method() {} - /// } - overrides = setableOverrides; - } else { - /// class Super { - /// get property => null - /// void set property(value) {} - /// } - /// class Class extends Super { - /// get property => null - /// void set property(value) {} - /// } - overrides = {...getableOverrides, ...setableOverrides}; - } - if (overrides.isNotEmpty) { - for (ClassMember declaredMember in declaredProperties!) { - declaredOverridesMap[declaredMember] = overrides; - } - } - } - if (mixedInMethod != null && getableOverrides.isNotEmpty) { - /// class Super { - /// method() {} - /// } - /// class Mixin { - /// method() {} - /// } - /// class Class = Super with Mixin; - mixinApplicationOverridesMap[mixedInMethod!] = getableOverrides; - } - if (mixedInProperties != null) { - Set overrides; - if (mixedInMethod != null) { - /// class Super { - /// set setter() {} - /// } - /// class Mixin { - /// method() {} - /// } - /// class Class = Super with Mixin; - overrides = setableOverrides; - } else { - /// class Super { - /// method() {} - /// } - /// class Mixin extends Super { - /// method() {} - /// } - overrides = {...getableOverrides, ...setableOverrides}; - } - if (overrides.isNotEmpty) { - for (ClassMember mixedInMember in mixedInProperties!) { - mixinApplicationOverridesMap[mixedInMember] = overrides; - } - } - } - } - } - } - - // Compute the 'noSuchMethod' member first so we know the target for - // noSuchMethod forwarders. - Tuple? noSuchMethod = memberMap.remove(noSuchMethodName); - if (noSuchMethod != null) { - // The noSuchMethod is always available - unless Object is not valid. - // See for instance pkg/front_end/test/fasta/object_supertype_test.dart - computeClassInterfaceMember(noSuchMethodName, noSuchMethod); - } - noSuchMethodMember = interfaceMemberMap[noSuchMethodName] ?? - classMemberMap[noSuchMethodName]; - - memberMap.forEach(computeClassInterfaceMember); - - if (classBuilder is SourceClassBuilder) { - // TODO(johnniwinther): Avoid duplicate override check computations - // between [declaredOverridesMap], [mixinApplicationOverridesMap] and - // [inheritedImplementsMap]. - - // TODO(johnniwinther): Ensure that a class member is only checked to - // validly override another member once. Currently it can happen multiple - // times as an inherited implementation. - - declaredOverridesMap.forEach( - (ClassMember classMember, Set overriddenMembers) { - /// A declared member can inherit its type from the overridden members. - /// - /// We register this with the class member itself so the it can force - /// computation of type on the overridden members before determining its - /// own type. - /// - /// Member types can be queried at arbitrary points during top level - /// inference so we need to ensure that types are computed in dependency - /// order. - classMember.registerOverrideDependency(overriddenMembers); - - /// Not all member type are queried during top level inference so we - /// register delayed computation to ensure that all types have been - /// computed before override checks are performed. - DelayedTypeComputation computation = - new DelayedTypeComputation(this, classMember, overriddenMembers); - _membersBuilder.registerDelayedTypeComputation(computation); - - /// Declared members must be checked to validly override the - /// overridden members. - _membersBuilder.registerOverrideCheck( - classBuilder as SourceClassBuilder, classMember, overriddenMembers); - }); - - mixinApplicationOverridesMap.forEach( - (ClassMember classMember, Set overriddenMembers) { - /// Declared mixed in members must be checked to validly override the - /// overridden members. - _membersBuilder.registerOverrideCheck( - classBuilder as SourceClassBuilder, classMember, overriddenMembers); - }); - - inheritedImplementsMap.forEach( - (ClassMember classMember, Set overriddenMembers) { - /// Concrete members must be checked to validly override the overridden - /// members in concrete classes. - _membersBuilder.registerOverrideCheck( - classBuilder as SourceClassBuilder, classMember, overriddenMembers); - }); - } - - if (!hasInterfaces) { + if (!_hasInterfaces) { /// All interface members also class members to we don't need to store /// the interface members separately. assert( @@ -2445,14 +1144,14 @@ class ClassMembersNode { final ClassMembersNode? supernode; /// All the members of this class including [classMembers] of its - /// superclasses. The members are sorted by [compareDeclarations]. + /// superclasses. final Map classMemberMap; /// Similar to [classMembers] but for setters. final Map classSetterMap; /// All the interface members of this class including [interfaceMembers] of - /// its supertypes. The members are sorted by [compareDeclarations]. + /// its supertypes. /// /// In addition to the members of [classMembers] this also contains members /// from interfaces. @@ -2516,7 +1215,10 @@ class ClassMembersNode { void printMemberMap( Map memberMap, StringBuffer sb, String heading) { List members = memberMap.values.toList(); - members.sort(compareDeclarations); + members.sort((ClassMember a, ClassMember b) { + if (a == b) return 0; + return ClassHierarchy.compareNames(a.name, b.name); + }); printMembers(members, sb, heading); } @@ -2585,135 +1287,135 @@ class ClassHierarchyNodeDataForTesting { class Tuple { final Name name; - ClassMember? _declaredMember; - ClassMember? _declaredSetter; - ClassMember? _mixedInMember; - ClassMember? _mixedInSetter; - ClassMember? _extendedMember; - ClassMember? _extendedSetter; - List? _implementedMembers; - List? _implementedSetters; + ClassMember? _declaredGetable; + ClassMember? _declaredSetable; + ClassMember? _mixedInGetable; + ClassMember? _mixedInSetable; + ClassMember? _extendedGetable; + ClassMember? _extendedSetable; + List? _implementedGetables; + List? _implementedSetables; Tuple.declareMember(ClassMember declaredMember) : assert(!declaredMember.forSetter), - this._declaredMember = declaredMember, + this._declaredGetable = declaredMember, this.name = declaredMember.name; Tuple.mixInMember(ClassMember mixedInMember) : assert(!mixedInMember.forSetter), - this._mixedInMember = mixedInMember, + this._mixedInGetable = mixedInMember, this.name = mixedInMember.name; Tuple.extendMember(ClassMember extendedMember) : assert(!extendedMember.forSetter), - this._extendedMember = extendedMember, + this._extendedGetable = extendedMember, this.name = extendedMember.name; Tuple.implementMember(ClassMember implementedMember) : assert(!implementedMember.forSetter), this.name = implementedMember.name, - _implementedMembers = [implementedMember]; + _implementedGetables = [implementedMember]; Tuple.declareSetter(ClassMember declaredSetter) : assert(declaredSetter.forSetter), - this._declaredSetter = declaredSetter, + this._declaredSetable = declaredSetter, this.name = declaredSetter.name; Tuple.mixInSetter(ClassMember mixedInSetter) : assert(mixedInSetter.forSetter), - this._mixedInSetter = mixedInSetter, + this._mixedInSetable = mixedInSetter, this.name = mixedInSetter.name; Tuple.extendSetter(ClassMember extendedSetter) : assert(extendedSetter.forSetter), - this._extendedSetter = extendedSetter, + this._extendedSetable = extendedSetter, this.name = extendedSetter.name; Tuple.implementSetter(ClassMember implementedSetter) : assert(implementedSetter.forSetter), this.name = implementedSetter.name, - _implementedSetters = [implementedSetter]; + _implementedSetables = [implementedSetter]; - ClassMember? get declaredMember => _declaredMember; + ClassMember? get declaredMember => _declaredGetable; void set declaredMember(ClassMember? value) { assert(!value!.forSetter); assert( - _declaredMember == null, - "Declared member already set to $_declaredMember, " + _declaredGetable == null, + "Declared member already set to $_declaredGetable, " "trying to set it to $value."); - _declaredMember = value; + _declaredGetable = value; } - ClassMember? get declaredSetter => _declaredSetter; + ClassMember? get declaredSetter => _declaredSetable; void set declaredSetter(ClassMember? value) { assert(value!.forSetter); assert( - _declaredSetter == null, - "Declared setter already set to $_declaredSetter, " + _declaredSetable == null, + "Declared setter already set to $_declaredSetable, " "trying to set it to $value."); - _declaredSetter = value; + _declaredSetable = value; } - ClassMember? get extendedMember => _extendedMember; + ClassMember? get extendedMember => _extendedGetable; void set extendedMember(ClassMember? value) { assert(!value!.forSetter); assert( - _extendedMember == null, - "Extended member already set to $_extendedMember, " + _extendedGetable == null, + "Extended member already set to $_extendedGetable, " "trying to set it to $value."); - _extendedMember = value; + _extendedGetable = value; } - ClassMember? get extendedSetter => _extendedSetter; + ClassMember? get extendedSetter => _extendedSetable; void set extendedSetter(ClassMember? value) { assert(value!.forSetter); assert( - _extendedSetter == null, - "Extended setter already set to $_extendedSetter, " + _extendedSetable == null, + "Extended setter already set to $_extendedSetable, " "trying to set it to $value."); - _extendedSetter = value; + _extendedSetable = value; } - ClassMember? get mixedInMember => _mixedInMember; + ClassMember? get mixedInMember => _mixedInGetable; void set mixedInMember(ClassMember? value) { assert(!value!.forSetter); assert( - _mixedInMember == null, - "Mixed in member already set to $_mixedInMember, " + _mixedInGetable == null, + "Mixed in member already set to $_mixedInGetable, " "trying to set it to $value."); - _mixedInMember = value; + _mixedInGetable = value; } - ClassMember? get mixedInSetter => _mixedInSetter; + ClassMember? get mixedInSetter => _mixedInSetable; void set mixedInSetter(ClassMember? value) { assert(value!.forSetter); assert( - _mixedInSetter == null, - "Mixed in setter already set to $_mixedInSetter, " + _mixedInSetable == null, + "Mixed in setter already set to $_mixedInSetable, " "trying to set it to $value."); - _mixedInSetter = value; + _mixedInSetable = value; } - List? get implementedMembers => _implementedMembers; + List? get implementedMembers => _implementedGetables; void addImplementedMember(ClassMember value) { assert(!value.forSetter); - _implementedMembers ??= []; - _implementedMembers!.add(value); + _implementedGetables ??= []; + _implementedGetables!.add(value); } - List? get implementedSetters => _implementedSetters; + List? get implementedSetters => _implementedSetables; void addImplementedSetter(ClassMember value) { assert(value.forSetter); - _implementedSetters ??= []; - _implementedSetters!.add(value); + _implementedSetables ??= []; + _implementedSetables!.add(value); } @override @@ -2721,57 +1423,1485 @@ class Tuple { StringBuffer sb = new StringBuffer(); String comma = ''; sb.write('Tuple('); - if (_declaredMember != null) { + if (_declaredGetable != null) { sb.write(comma); sb.write('declaredMember='); - sb.write(_declaredMember); + sb.write(_declaredGetable); comma = ','; } - if (_declaredSetter != null) { + if (_declaredSetable != null) { sb.write(comma); sb.write('declaredSetter='); - sb.write(_declaredSetter); + sb.write(_declaredSetable); comma = ','; } - if (_mixedInMember != null) { + if (_mixedInGetable != null) { sb.write(comma); sb.write('mixedInMember='); - sb.write(_mixedInMember); + sb.write(_mixedInGetable); comma = ','; } - if (_mixedInSetter != null) { + if (_mixedInSetable != null) { sb.write(comma); sb.write('mixedInSetter='); - sb.write(_mixedInSetter); + sb.write(_mixedInSetable); comma = ','; } - if (_extendedMember != null) { + if (_extendedGetable != null) { sb.write(comma); sb.write('extendedMember='); - sb.write(_extendedMember); + sb.write(_extendedGetable); comma = ','; } - if (_extendedSetter != null) { + if (_extendedSetable != null) { sb.write(comma); sb.write('extendedSetter='); - sb.write(_extendedSetter); + sb.write(_extendedSetable); comma = ','; } - if (_implementedMembers != null) { + if (_implementedGetables != null) { sb.write(comma); sb.write('implementedMembers='); - sb.write(_implementedMembers); + sb.write(_implementedGetables); comma = ','; } - if (_implementedSetters != null) { + if (_implementedSetables != null) { sb.write(comma); sb.write('implementedSetters='); - sb.write(_implementedSetters); + sb.write(_implementedSetables); comma = ','; } sb.write(')'); return sb.toString(); } + + /// Sanitizing the members of this tuple. + /// + /// Conflicts between methods and properties (getters/setters) or between + /// static and instance members are reported. Conflicting members and members + /// overridden by duplicates are removed. + /// + /// For this [definingGetable] and [definingSetable] hold the first member + /// of its kind found among declared, mixed in, extended and implemented + /// members. + /// + /// Conflicts between [definingGetable] and [definingSetable] are reported + /// afterwards. + (_SanitizedMember?, _SanitizedMember?) sanitize( + ClassMembersNodeBuilder builder) { + ClassMember? definingGetable; + ClassMember? definingSetable; + + ClassMember? declaredGetable = this.declaredMember; + if (declaredGetable != null) { + /// class Class { + /// method() {} + /// } + definingGetable = declaredGetable; + } + ClassMember? declaredSetable = this.declaredSetter; + if (declaredSetable != null) { + /// class Class { + /// set setter(value) {} + /// } + definingSetable = declaredSetable; + } + + ClassMember? mixedInGetable; + ClassMember? tupleMixedInMember = this.mixedInMember; + if (tupleMixedInMember != null && + !tupleMixedInMember.isStatic && + !tupleMixedInMember.isDuplicate && + !tupleMixedInMember.isSynthesized) { + /// We treat + /// + /// opt-in: + /// class Interface { + /// method3() {} + /// } + /// opt-out: + /// class Mixin implements Interface { + /// static method1() {} + /// method2() {} + /// method2() {} + /// /*member-signature*/ method3() {} + /// } + /// class Class with Mixin {} + /// + /// as + /// + /// class Mixin {} + /// class Class with Mixin {} + /// + /// Note that skipped synthetic getable 'method3' is still included + /// in the implemented getables, but its type will not define the type + /// when mixed in. For instance + /// + /// opt-in: + /// abstract class Interface { + /// num get getter; + /// } + /// opt-out: + /// abstract class Super { + /// int get getter; + /// } + /// abstract class Mixin implements Interface { + /// /*member-signature*/ num get getter; + /// } + /// abstract class Class extends Super with Mixin {} + /// + /// Here the type of `Class.getter` should not be defined from the + /// synthetic member signature `Mixin.getter` but as a combined member + /// signature of `Super.getter` and `Mixin.getter`, resulting in type + /// `int` instead of `num`. + if (definingGetable == null) { + /// class Mixin { + /// method() {} + /// } + /// class Class with Mixin {} + definingGetable = mixedInGetable = tupleMixedInMember; + } else if (!definingGetable.isDuplicate) { + // This case is currently unreachable from source code since classes + // cannot both declare and mix in members. From dill, this can occur + // but should not conflicting members. + // + // The case is handled for consistency. + if (definingGetable.isStatic || + definingGetable.isProperty != tupleMixedInMember.isProperty) { + builder.reportInheritanceConflict( + definingGetable, tupleMixedInMember); + } else { + mixedInGetable = tupleMixedInMember; + } + } + } + ClassMember? mixedInSetable; + ClassMember? tupleMixedInSetter = this.mixedInSetter; + if (tupleMixedInSetter != null && + !tupleMixedInSetter.isStatic && + !tupleMixedInSetter.isDuplicate && + !tupleMixedInSetter.isSynthesized) { + /// We treat + /// + /// class Mixin { + /// static set setter1(value) {} + /// set setter2(value) {} + /// set setter2(value) {} + /// /*member-signature*/ setter3() {} + /// } + /// class Class with Mixin {} + /// + /// as + /// + /// class Mixin {} + /// class Class with Mixin {} + /// + /// Note that skipped synthetic setable 'setter3' is still included + /// in the implemented setables, but its type will not define the type + /// when mixed in. For instance + /// + /// opt-in: + /// abstract class Interface { + /// void set setter(int value); + /// } + /// opt-out: + /// abstract class Super { + /// void set setter(num value); + /// } + /// abstract class Mixin implements Interface { + /// /*member-signature*/ num get getter; + /// } + /// abstract class Class extends Super with Mixin {} + /// + /// Here the type of `Class.setter` should not be defined from the + /// synthetic member signature `Mixin.setter` but as a combined member + /// signature of `Super.setter` and `Mixin.setter`, resulting in type + /// `num` instead of `int`. + if (definingSetable == null) { + /// class Mixin { + /// set setter(value) {} + /// } + /// class Class with Mixin {} + definingSetable = mixedInSetable = tupleMixedInSetter; + } else if (!definingSetable.isDuplicate) { + if (definingSetable.isStatic || + definingSetable.isProperty != tupleMixedInSetter.isProperty) { + builder.reportInheritanceConflict( + definingSetable, tupleMixedInSetter); + } else { + mixedInSetable = tupleMixedInSetter; + } + } + } + + ClassMember? extendedGetable; + ClassMember? tupleExtendedMember = this.extendedMember; + if (tupleExtendedMember != null && + !tupleExtendedMember.isStatic && + !tupleExtendedMember.isDuplicate) { + /// We treat + /// + /// class Super { + /// static method1() {} + /// method2() {} + /// method2() {} + /// } + /// class Class extends Super {} + /// + /// as + /// + /// class Super {} + /// class Class extends Super {} + /// + if (definingGetable == null) { + /// class Super { + /// method() {} + /// } + /// class Class extends Super {} + definingGetable = extendedGetable = tupleExtendedMember; + } else if (!definingGetable.isDuplicate) { + if (definingGetable.isStatic || + definingGetable.isProperty != tupleExtendedMember.isProperty) { + /// class Super { + /// method() {} + /// } + /// class Class extends Super { + /// static method() {} + /// } + /// + /// or + /// + /// class Super { + /// method() {} + /// } + /// class Class extends Super { + /// get getter => 0; + /// } + builder.reportInheritanceConflict( + definingGetable, tupleExtendedMember); + } else { + extendedGetable = tupleExtendedMember; + } + } + } + ClassMember? extendedSetable; + ClassMember? tupleExtendedSetter = this.extendedSetter; + if (tupleExtendedSetter != null && + !tupleExtendedSetter.isStatic && + !tupleExtendedSetter.isDuplicate) { + /// We treat + /// + /// class Super { + /// static set setter1(value) {} + /// set setter2(value) {} + /// set setter2(value) {} + /// } + /// class Class extends Super {} + /// + /// as + /// + /// class Super {} + /// class Class extends Super {} + /// + if (definingSetable == null) { + /// class Super { + /// set setter(value) {} + /// } + /// class Class extends Super {} + definingSetable = extendedSetable = tupleExtendedSetter; + } else if (!definingSetable.isDuplicate) { + if (definingSetable.isStatic || + definingSetable.isProperty != tupleExtendedSetter.isProperty) { + builder.reportInheritanceConflict( + definingSetable, tupleExtendedSetter); + } else { + extendedSetable = tupleExtendedSetter; + } + } + } + + // TODO(johnniwinther): Remove extended and mixed in members/setters + // from implemented members/setters. Mixin applications always implement + // the mixin class leading to unnecessary interface members. + List? implementedGetables; + List? tupleImplementedMembers = this.implementedMembers; + if (tupleImplementedMembers != null && + // Skip implemented members if we already have a duplicate. + !(definingGetable != null && definingGetable.isDuplicate)) { + for (int i = 0; i < tupleImplementedMembers.length; i++) { + ClassMember? implementedGetable = tupleImplementedMembers[i]; + if (implementedGetable.isStatic || implementedGetable.isDuplicate) { + /// We treat + /// + /// class Interface { + /// static method1() {} + /// method2() {} + /// method2() {} + /// } + /// class Class implements Interface {} + /// + /// as + /// + /// class Interface {} + /// class Class implements Interface {} + /// + implementedGetable = null; + } else { + if (definingGetable == null) { + /// class Interface { + /// method() {} + /// } + /// class Class implements Interface {} + definingGetable = implementedGetable; + } else if (definingGetable.isStatic || + definingGetable.isProperty != implementedGetable.isProperty) { + /// class Interface { + /// method() {} + /// } + /// class Class implements Interface { + /// static method() {} + /// } + /// + /// or + /// + /// class Interface { + /// method() {} + /// } + /// class Class implements Interface { + /// get getter => 0; + /// } + builder.reportInheritanceConflict( + definingGetable, implementedGetable); + implementedGetable = null; + } + } + if (implementedGetable == null) { + // On the first skipped member we add all previous. + implementedGetables ??= tupleImplementedMembers.take(i).toList(); + } else if (implementedGetables != null) { + // If already skipping members we add [implementedGetable] + // explicitly. + implementedGetables.add(implementedGetable); + } + } + if (implementedGetables == null) { + // No members were skipped so we use the full list. + implementedGetables = tupleImplementedMembers; + } else if (implementedGetables.isEmpty) { + // No members were included. + implementedGetables = null; + } + } + + List? implementedSetables; + List? tupleImplementedSetters = this.implementedSetters; + if (tupleImplementedSetters != null && + // Skip implemented setters if we already have a duplicate. + !(definingSetable != null && definingSetable.isDuplicate)) { + for (int i = 0; i < tupleImplementedSetters.length; i++) { + ClassMember? implementedSetable = tupleImplementedSetters[i]; + if (implementedSetable.isStatic || implementedSetable.isDuplicate) { + /// We treat + /// + /// class Interface { + /// static set setter1(value) {} + /// set setter2(value) {} + /// set setter2(value) {} + /// } + /// class Class implements Interface {} + /// + /// as + /// + /// class Interface {} + /// class Class implements Interface {} + /// + implementedSetable = null; + } else { + if (definingSetable == null) { + /// class Interface { + /// set setter(value) {} + /// } + /// class Class implements Interface {} + definingSetable = implementedSetable; + } else if (definingSetable.isStatic || + definingSetable.isProperty != implementedSetable.isProperty) { + /// class Interface { + /// set setter(value) {} + /// } + /// class Class implements Interface { + /// static set setter(value) {} + /// } + builder.reportInheritanceConflict( + definingSetable, implementedSetable); + implementedSetable = null; + } + } + if (implementedSetable == null) { + // On the first skipped setter we add all previous. + implementedSetables ??= tupleImplementedSetters.take(i).toList(); + } else if (implementedSetables != null) { + // If already skipping setters we add [implementedSetable] + // explicitly. + implementedSetables.add(implementedSetable); + } + } + if (implementedSetables == null) { + // No setters were skipped so we use the full list. + implementedSetables = tupleImplementedSetters; + } else if (implementedSetables.isEmpty) { + // No setters were included. + implementedSetables = null; + } + } + + if (definingGetable != null && definingSetable != null) { + if (definingGetable.isStatic != definingSetable.isStatic || + definingGetable.isProperty != definingSetable.isProperty) { + builder.reportInheritanceConflict(definingGetable, definingSetable); + // TODO(johnniwinther): Should we remove [definingSetable]? If we + // leave it in this conflict will also be reported in subclasses. If + // we remove it, any write to the setable will be unresolved. + } + } + + // TODO(johnniwinther): Handle declared members together with mixed in + // members. This should only occur from .dill, though. + if (mixedInGetable != null) { + declaredGetable = null; + } + if (mixedInSetable != null) { + declaredSetable = null; + } + return ( + definingGetable != null + ? new _SanitizedMember(name, definingGetable, declaredGetable, + mixedInGetable, extendedGetable, implementedGetables) + : null, + definingSetable != null + ? new _SanitizedMember(name, definingSetable, declaredSetable, + mixedInSetable, extendedSetable, implementedSetables) + : null + ); + } +} + +/// The [ClassMember]s involved in defined the [name] getable or setable of +/// a class. +/// +/// The values are sanitized to avoid duplicates and conflicting members. +/// +/// The [_definingMember] hold the first member found among declared, mixed in, +/// extended and implemented members. +/// +/// This is computed by [Tuple.sanitize]. +class _SanitizedMember { + final Name name; + + /// [_definingMember] is the member which defines whether the computation + /// is for a method, a getter or a setter. + final ClassMember _definingMember; + + /// [_declaredMember] is the member declared in the current class, if any. + final ClassMember? _declaredMember; + + /// [_mixedInMember] is the member declared in a mixin that is mixed into + /// the current class, if any. + final ClassMember? _mixedInMember; + + /// [_extendedMember] is the member inherited from the super class. + final ClassMember? _extendedMember; + + /// [_implementedMembers] are the members inherited from the super + /// interfaces, if none this is `null`. + final List? _implementedMembers; + + _SanitizedMember(this.name, this._definingMember, this._declaredMember, + this._mixedInMember, this._extendedMember, this._implementedMembers); + + /// Computes the class and interface members for this [_SanitizedMember]. + /// + /// The computed class and interface members are added to [classMemberMap] + /// and [interfaceMemberMap], respectively. + /// + /// [ + ClassMember? computeMembers( + ClassMembersNodeBuilder builder, _Overrides overrides, + {required ClassMember? noSuchMethodMember, + required ClassMember? userNoSuchMethodMember, + required List abstractMembers, + required Map classMemberMap, + required Map? interfaceMemberMap, + required ClassHierarchyNodeDataForTesting? dataForTesting}) { + ClassBuilder classBuilder = builder.classBuilder; + + ClassMember? classMember; + ClassMember? interfaceMember; + + /// A noSuchMethodForwarder can be inserted in non-abstract class + /// if a user defined noSuchMethod implementation is available or + /// if the member is not accessible from this library; + bool canHaveNoSuchMethodForwarder = !classBuilder.isAbstract && + (userNoSuchMethodMember != null || + !isNameVisibleIn(name, classBuilder.libraryBuilder)); + + if (_mixedInMember != null) { + if (_mixedInMember.isAbstract || _mixedInMember.isNoSuchMethodForwarder) { + /// class Mixin { + /// method(); + /// } + /// class Class = Object with Mixin; + + /// Interface members from the extended, mixed in, and implemented + /// members define the combined member signature. + Set interfaceMembers = {}; + + if (_extendedMember != null) { + /// class Super { + /// method() {} + /// } + /// class Mixin { + /// method(); + /// } + /// class Class = Super with Mixin; + interfaceMembers.add(_extendedMember.interfaceMember); + } + + interfaceMembers.add(_mixedInMember); + + if (_implementedMembers != null) { + /// class Interface { + /// method() {} + /// } + /// class Mixin { + /// method(); + /// } + /// class Class = Object with Mixin implements Interface; + interfaceMembers.addAll(_implementedMembers); + } + + ClassMember? noSuchMethodTarget; + if (canHaveNoSuchMethodForwarder && + (_extendedMember == null || + _extendedMember.isNoSuchMethodForwarder)) { + /// class Super { + /// noSuchMethod(_) => null; + /// _extendedMember(); + /// } + /// abstract class Mixin { + /// mixinMethod(); + /// _extendedMember(); + /// } + /// class Class = Super with Mixin /* + /// mixinMethod() => ...; // noSuchMethod forwarder created + /// */; + noSuchMethodTarget = noSuchMethodMember; + } + + /// We always create a synthesized interface member, even in the + /// case of [interfaceMembers] being a singleton, to insert the + /// abstract mixin stub. + interfaceMember = new SynthesizedInterfaceMember( + classBuilder, name, interfaceMembers.toList(), + superClassMember: _extendedMember, + // [definingMember] and [mixedInMember] are always the same + // here. Use the latter here and the former below to show the + // the member is canonical _because_ its the mixed in member and + // it defines the isProperty/forSetter properties _because_ it + // is the defining member. + canonicalMember: _mixedInMember, + mixedInMember: _mixedInMember, + noSuchMethodTarget: noSuchMethodTarget, + isProperty: _definingMember.isProperty, + forSetter: _definingMember.forSetter, + shouldModifyKernel: builder.shouldModifyKernel); + builder._membersBuilder.registerMemberComputation(interfaceMember); + + if (_extendedMember != null) { + /// class Super { + /// method() {} + /// } + /// class Mixin { + /// method(); + /// } + /// class Class = Super with Mixin; + /// + /// The concrete extended member is the class member but might + /// be overwritten by a concrete forwarding stub: + /// + /// class Super { + /// method(int i) {} + /// } + /// class Interface { + /// method(covariant int i) {} + /// } + /// class Mixin { + /// method(int i); + /// } + /// // A concrete forwarding stub + /// // method(covariant int i) => super.method(i); + /// // will be inserted. + /// class Class = Super with Mixin implements Interface; + /// + classMember = new InheritedClassMemberImplementsInterface( + classBuilder, name, + inheritedClassMember: _extendedMember, + implementedInterfaceMember: interfaceMember, + forSetter: _definingMember.forSetter, + isProperty: _definingMember.isProperty); + builder._membersBuilder.registerMemberComputation(classMember); + if (!classBuilder.isAbstract) { + overrides.registerInheritedImplements( + _extendedMember, {interfaceMember}, + aliasForTesting: classMember); + } + } else if (noSuchMethodTarget != null) { + classMember = interfaceMember; + } else if (!classBuilder.isAbstract) { + assert(!canHaveNoSuchMethodForwarder); + + /// class Mixin { + /// method(); // Missing implementation. + /// } + /// class Class = Object with Mixin; + builder.registerAbstractMember(abstractMembers, interfaceMember); + } + + assert(!_mixedInMember.isSynthesized); + if (!_mixedInMember.isSynthesized) { + /// Members declared in the mixin must override extended and + /// implemented members. + /// + /// When loading from .dill the mixed in member might be + /// synthesized, for instance a member signature or forwarding + /// stub, and this should not be checked to override the extended + /// and implemented members: + /// + /// // Opt-out library, from source: + /// class Mixin {} + /// // Opt-out library, from .dill: + /// class Mixin { + /// ... + /// String* toString(); // member signature + /// } + /// // Opt-out library, from source: + /// class Class = Object with Mixin; + /// // Mixin.toString should not be checked to override + /// // Object.toString. + /// + overrides.registerMixedInOverride(_mixedInMember, + aliasForTesting: interfaceMember); + } + } else { + assert(!_mixedInMember.isAbstract); + + /// class Mixin { + /// method() {} + /// } + /// class Class = Object with Mixin; + /// + + /// Interface members from the extended, mixed in, and implemented + /// members define the combined member signature. + Set interfaceMembers = {}; + + if (_extendedMember != null) { + /// class Super { + /// method() {} + /// } + /// class Mixin { + /// method() {} + /// } + /// class Class = Super with Mixin; + interfaceMembers.add(_extendedMember.interfaceMember); + } + + interfaceMembers.add(_mixedInMember); + + if (_implementedMembers != null) { + /// class Interface { + /// method() {} + /// } + /// class Mixin { + /// method() {} + /// } + /// class Class = Object with Mixin implements Interface; + interfaceMembers.addAll(_implementedMembers); + } + + /// We always create a synthesized interface member, even in the + /// case of [interfaceMembers] being a singleton, to insert the + /// concrete mixin stub. + interfaceMember = new SynthesizedInterfaceMember( + classBuilder, name, interfaceMembers.toList(), + superClassMember: _mixedInMember, + // [definingMember] and [mixedInMember] are always the same + // here. Use the latter here and the former below to show the + // the member is canonical _because_ its the mixed in member and + // it defines the isProperty/forSetter properties _because_ it + // is the defining member. + canonicalMember: _mixedInMember, + mixedInMember: _mixedInMember, + isProperty: _definingMember.isProperty, + forSetter: _definingMember.forSetter, + shouldModifyKernel: builder.shouldModifyKernel); + builder._membersBuilder.registerMemberComputation(interfaceMember); + + /// The concrete mixed in member is the class member but will + /// be overwritten by a concrete mixin stub: + /// + /// class Mixin { + /// method() {} + /// } + /// // A concrete mixin stub + /// // method() => super.method(); + /// // will be inserted. + /// class Class = Object with Mixin; + /// + classMember = new InheritedClassMemberImplementsInterface( + classBuilder, name, + inheritedClassMember: _mixedInMember, + implementedInterfaceMember: interfaceMember, + forSetter: _definingMember.forSetter, + isProperty: _definingMember.isProperty); + builder._membersBuilder.registerMemberComputation(classMember); + + if (!classBuilder.isAbstract) { + /// class Interface { + /// method() {} + /// } + /// class Mixin { + /// method() {} + /// } + /// class Class = Object with Mixin; + /// + /// [mixinMember] must implemented interface member. + overrides.registerInheritedImplements( + _mixedInMember, {interfaceMember}, + aliasForTesting: classMember); + } + assert(!_mixedInMember.isSynthesized); + if (!_mixedInMember.isSynthesized) { + /// Members declared in the mixin must override extended and + /// implemented members. + /// + /// When loading from .dill the mixed in member might be + /// synthesized, for instance a member signature or forwarding + /// stub, and this should not be checked to override the extended + /// and implemented members. + /// + /// These synthesized mixed in members should always be abstract + /// and therefore not be handled here, but we handled them here + /// for consistency. + overrides.registerMixedInOverride(_mixedInMember); + } + } + } else if (_declaredMember != null) { + if (_declaredMember.isAbstract) { + /// class Class { + /// method(); + /// } + interfaceMember = _declaredMember; + + /// Interface members from the declared, extended, and implemented + /// members define the combined member signature. + Set interfaceMembers = {}; + + if (_extendedMember != null) { + /// class Super { + /// method() {} + /// } + /// class Class extends Super { + /// method(); + /// } + interfaceMembers.add(_extendedMember); + } + + interfaceMembers.add(_declaredMember); + + if (_implementedMembers != null) { + /// class Interface { + /// method() {} + /// } + /// class Class implements Interface { + /// method(); + /// } + interfaceMembers.addAll(_implementedMembers); + } + + ClassMember? noSuchMethodTarget; + if (canHaveNoSuchMethodForwarder && + (_extendedMember == null || + _extendedMember.isNoSuchMethodForwarder)) { + /// class Super { + /// noSuchMethod(_) => null; + /// _extendedMember(); + /// } + /// class Class extends Super { + /// declaredMethod(); // noSuchMethod forwarder created + /// _extendedMember(); + /// } + noSuchMethodTarget = noSuchMethodMember; + } + + /// If only one member defines the interface member there is no + /// need for a synthesized interface member, since its result will + /// simply be that one member. + if (interfaceMembers.length > 1 || noSuchMethodTarget != null) { + /// class Super { + /// method() {} + /// } + /// class Interface { + /// method() {} + /// } + /// class Class extends Super implements Interface { + /// method(); + /// } + interfaceMember = new SynthesizedInterfaceMember( + classBuilder, name, interfaceMembers.toList(), + superClassMember: _extendedMember, + // [definingMember] and [declaredMember] are always the same + // here. Use the latter here and the former below to show the + // the member is canonical _because_ its the declared member + // and it defines the isProperty/forSetter properties + // _because_ it is the defining member. + canonicalMember: _declaredMember, + noSuchMethodTarget: noSuchMethodTarget, + isProperty: _definingMember.isProperty, + forSetter: _definingMember.forSetter, + shouldModifyKernel: builder.shouldModifyKernel); + builder._membersBuilder.registerMemberComputation(interfaceMember); + } + + if (_extendedMember != null) { + /// class Super { + /// method() {} + /// } + /// class Class extends Super { + /// method(); + /// } + /// + /// The concrete extended member is the class member but might + /// be overwritten by a concrete forwarding stub: + /// + /// class Super { + /// method(int i) {} + /// } + /// class Interface { + /// method(covariant int i) {} + /// } + /// class Class extends Super implements Interface { + /// // This will be turned into the concrete forwarding stub + /// // method(covariant int i) => super.method(i); + /// method(int i); + /// } + /// + classMember = new InheritedClassMemberImplementsInterface( + classBuilder, name, + inheritedClassMember: _extendedMember, + implementedInterfaceMember: interfaceMember, + forSetter: _definingMember.forSetter, + isProperty: _definingMember.isProperty); + builder._membersBuilder.registerMemberComputation(classMember); + + if (!classBuilder.isAbstract && noSuchMethodTarget == null) { + /// class Super { + /// method() {} + /// } + /// class Class extends Super { + /// method(); + /// } + /// + /// [_extendedMember] must implemented interface member. + overrides.registerInheritedImplements( + _extendedMember, {interfaceMember}, + aliasForTesting: classMember); + } + } else if (noSuchMethodTarget != null) { + classMember = interfaceMember; + } else if (!classBuilder.isAbstract) { + assert(!canHaveNoSuchMethodForwarder); + + /// class Class { + /// method(); // Missing implementation. + /// } + builder.registerAbstractMember(abstractMembers, _declaredMember); + } + + /// The declared member must override extended and implemented + /// members. + overrides.registerDeclaredOverride(_declaredMember, + aliasForTesting: interfaceMember); + } else { + assert(!_declaredMember.isAbstract); + + /// class Class { + /// method() {} + /// } + classMember = _declaredMember; + + /// The declared member must override extended and implemented + /// members. + overrides.registerDeclaredOverride(_declaredMember); + } + } else if (_extendedMember != null) { + /// class Super { + /// method() {} + /// } + /// class Class extends Super {} + assert(!_extendedMember.isAbstract, + "Abstract extended member: ${_extendedMember}"); + + classMember = _extendedMember; + + if (_implementedMembers != null) { + /// class Super { + /// method() {} + /// } + /// class Interface { + /// method() {} + /// } + /// class Class extends Super implements Interface {} + ClassMember extendedInterfaceMember = _extendedMember.interfaceMember; + + /// Interface members from the extended and implemented + /// members define the combined member signature. + Set interfaceMembers = {extendedInterfaceMember}; + + // TODO(johnniwinther): The extended member might be included in + // a synthesized implemented member. For instance: + // + // class Super { + // void method() {} + // } + // class Interface { + // void method() {} + // } + // abstract class Class extends Super implements Interface { + // // Synthesized interface member of + // // {Super.method, Interface.method} + // } + // class Sub extends Class { + // // Super.method implements Class.method = + // // {Super.method, Interface.method} + // // Synthesized interface member of + // // {Super.method, Class.method} + // } + // + // Maybe we should recognize this. + interfaceMembers.addAll(_implementedMembers); + + ClassMember? noSuchMethodTarget; + if (_extendedMember.isNoSuchMethodForwarder && + !classBuilder.isAbstract && + (userNoSuchMethodMember != null || + !isNameVisibleIn(name, classBuilder.libraryBuilder))) { + noSuchMethodTarget = noSuchMethodMember; + } + + /// Normally, if only one member defines the interface member there + /// is no need for a synthesized interface member, since its result + /// will simply be that one member, but if the extended member is + /// from an opt-in library and the current class is from an opt-out + /// library we need to create a member signature: + /// + /// // Opt-in: + /// class Super { + /// int? method() => null; + /// } + /// class Interface implements Super {} + /// // Opt-out: + /// class Class extends Super implements Interface { + /// // Member signature added: + /// int* method(); + /// } + /// + if (interfaceMembers.length == 1 && + !builder.needsMemberSignatureFor(extendedInterfaceMember) && + noSuchMethodTarget == null) { + /// class Super { + /// method() {} + /// } + /// class Interface implements Super {} + /// class Class extends Super implements Interface {} + interfaceMember = interfaceMembers.first; + } else { + /// class Super { + /// method() {} + /// } + /// class Interface { + /// method() {} + /// } + /// class Class extends Super implements Interface {} + interfaceMember = new SynthesizedInterfaceMember( + classBuilder, name, interfaceMembers.toList(), + superClassMember: _extendedMember, + noSuchMethodTarget: noSuchMethodTarget, + isProperty: _definingMember.isProperty, + forSetter: _definingMember.forSetter, + shouldModifyKernel: builder.shouldModifyKernel); + builder._membersBuilder.registerMemberComputation(interfaceMember); + } + if (interfaceMember == classMember) { + /// class Super { + /// method() {} + /// } + /// class Interface implements Super {} + /// class Class extends Super implements Interface {} + /// + /// We keep track of whether a class needs interfaces, that is, + /// whether is has any members that have an interface member + /// different from its corresponding class member, so we set + /// [interfaceMember] to `null` so show that the interface member + /// is not needed. + interfaceMember = null; + } else { + /// class Super { + /// method() {} + /// } + /// class Interface { + /// method() {} + /// } + /// class Class extends Super implements Interface {} + /// + /// The concrete extended member is the class member but might + /// be overwritten by a concrete forwarding stub: + /// + /// class Super { + /// method(int i) {} + /// } + /// class Interface { + /// method(covariant int i) {} + /// } + /// class Class extends Super implements Interface { + /// // A concrete forwarding stub will be created: + /// // method(covariant int i) => super.method(i); + /// } + /// + classMember = new InheritedClassMemberImplementsInterface( + classBuilder, name, + inheritedClassMember: _extendedMember, + implementedInterfaceMember: interfaceMember, + isProperty: _definingMember.isProperty, + forSetter: _definingMember.forSetter); + builder._membersBuilder.registerMemberComputation(classMember); + if (!classBuilder.isAbstract && noSuchMethodTarget == null) { + /// class Super { + /// method() {} + /// } + /// class Interface { + /// method() {} + /// } + /// class Class extends Super implements Interface {} + overrides.registerInheritedImplements( + _extendedMember, {interfaceMember}, + aliasForTesting: classMember); + } + } + } else if (builder.needsMemberSignatureFor(_extendedMember)) { + /// // Opt-in library: + /// class Super { + /// method() {} + /// } + /// // opt-out library: + /// class Class extends Super {} + interfaceMember = new SynthesizedInterfaceMember( + classBuilder, name, [_extendedMember], + superClassMember: _extendedMember, + isProperty: _definingMember.isProperty, + forSetter: _definingMember.forSetter, + shouldModifyKernel: builder.shouldModifyKernel); + builder._membersBuilder.registerMemberComputation(interfaceMember); + + /// The concrete extended member is the class member and should + /// be able to be overwritten by a synthesized concrete member here, + /// but we handle the case for consistency. + classMember = new InheritedClassMemberImplementsInterface( + classBuilder, name, + inheritedClassMember: _extendedMember, + implementedInterfaceMember: interfaceMember, + isProperty: _definingMember.isProperty, + forSetter: _definingMember.forSetter); + builder._membersBuilder.registerMemberComputation(classMember); + } + } else if (_implementedMembers != null) { + /// class Interface { + /// method() {} + /// } + /// class Class implements Interface {} + Set interfaceMembers = _implementedMembers.toSet(); + if (interfaceMembers.isNotEmpty) { + ClassMember? noSuchMethodTarget; + if (canHaveNoSuchMethodForwarder) { + /// abstract class Interface { + /// implementedMember(); + /// } + /// class Class implements Interface { + /// noSuchMethod(_) => null; + /// implementedMember(); // noSuchMethod forwarder created + /// } + noSuchMethodTarget = noSuchMethodMember; + } + + /// Normally, if only one member defines the interface member there + /// is no need for a synthesized interface member, since its result + /// will simply be that one member, but if the implemented member is + /// from an opt-in library and the current class is from an opt-out + /// library we need to create a member signature: + /// + /// // Opt-in: + /// class Interface { + /// int? method() => null; + /// } + /// // Opt-out: + /// class Class implements Interface { + /// // Member signature added: + /// int* method(); + /// } + /// + if (interfaceMembers.length == 1 && + !builder.needsMemberSignatureFor(interfaceMembers.first) && + noSuchMethodTarget == null) { + /// class Interface { + /// method() {} + /// } + /// class Class implements Interface {} + interfaceMember = interfaceMembers.first; + } else { + /// class Interface1 { + /// method() {} + /// } + /// class Interface2 { + /// method() {} + /// } + /// class Class implements Interface1, Interface2 {} + interfaceMember = new SynthesizedInterfaceMember( + classBuilder, name, interfaceMembers.toList(), + noSuchMethodTarget: noSuchMethodTarget, + isProperty: _definingMember.isProperty, + forSetter: _definingMember.forSetter, + shouldModifyKernel: builder.shouldModifyKernel); + builder._membersBuilder.registerMemberComputation(interfaceMember); + } + if (noSuchMethodTarget != null) { + classMember = interfaceMember; + } else if (!classBuilder.isAbstract) { + assert(!canHaveNoSuchMethodForwarder); + + /// class Interface { + /// method() {} + /// } + /// class Class implements Interface {} + builder.registerAbstractMember(abstractMembers, interfaceMember); + } + } + } + + if (interfaceMember != null) { + // We have an explicit interface. + builder._hasInterfaces = true; + } + if (classMember != null) { + classMemberMap[name] = classMember; + interfaceMember ??= classMember.interfaceMember; + } + if (interfaceMember != null) { + interfaceMemberMap![name] = interfaceMember; + } + return interfaceMember; + } + + Set computeOverrides() { + Set set = {}; + if (_extendedMember != null) { + /// (abstract) class Super { + /// method() {} + /// int get property => 0; + /// } + /// (abstract) class Class extends Super { + /// method() {} + /// set property(int value) {} + /// } + /// + /// or + /// + /// (abstract) class Super { + /// set setter(int value) {} + /// set property(int value) {} + /// } + /// (abstract) class Class extends Super { + /// set setter(int value) {} + /// int get property => 0; + /// } + set.add(_extendedMember.interfaceMember); + } + if (_implementedMembers != null) { + /// (abstract) class Interface { + /// method() {} + /// int get property => 0; + /// } + /// (abstract) class Class implements Interface { + /// method() {} + /// set property(int value) {} + /// } + /// + /// or + /// + /// (abstract) class Interface { + /// set setter(int value) {} + /// set property(int value) {} + /// } + /// (abstract) class Class implements Interface { + /// set setter(int value) {} + /// int get property => 0; + /// } + set.addAll(_implementedMembers); + } + return set; + } +} + +/// Object that collects data of overrides found during +/// [_SanitizedMember.computeMembers]. +class _Overrides { + final ClassBuilder _classBuilder; + + /// In case this class is concrete, this maps concrete members that are + /// inherited into this class to the members they should override to validly + /// implement the interface of this class. + final Map> _inheritedImplementsMap; + + final ClassHierarchyNodeDataForTesting? _dataForTesting; + + /// Set to `true` if declared members have been registered in + /// [registerDeclaredOverride] or [registerMixedInOverride]. + bool hasDeclaredMembers = false; + + /// Declared methods, getters and setters registered in + /// [registerDeclaredOverride]. + ClassMember? declaredMethod; + List? declaredProperties; + + /// Declared methods, getters and setters registered in + /// [registerDeclaredOverride]. + ClassMember? mixedInMethod; + List? mixedInProperties; + + _Overrides( + {required ClassBuilder classBuilder, + required Map> inheritedImplementsMap, + required ClassHierarchyNodeDataForTesting? dataForTesting}) + : _classBuilder = classBuilder, + _inheritedImplementsMap = inheritedImplementsMap, + _dataForTesting = dataForTesting; + + /// Registers that [declaredMember] overrides extended and implemented + /// members. + /// + /// Getters and setters share overridden members so the registration + /// of override relations is performed after the interface members have + /// been computed. + /// + /// Declared members must be checked for valid override of the overridden + /// members _and_ must register an override dependency with the overridden + /// members so that override inference can propagate inferred types + /// correctly. For instance: + /// + /// class Super { + /// int get property => 42; + /// } + /// class Class extends Super { + /// void set property(value) {} + /// } + /// + /// Here the parameter type of the setter `Class.property` must be + /// inferred from the type of the getter `Super.property`. + void registerDeclaredOverride(ClassMember declaredMember, + {ClassMember? aliasForTesting}) { + if (_classBuilder is SourceClassBuilder && !declaredMember.isStatic) { + assert( + declaredMember.isSourceDeclaration && + declaredMember.classBuilder.origin == _classBuilder, + "Only declared members can override: ${declaredMember}"); + hasDeclaredMembers = true; + if (declaredMember.isProperty) { + declaredProperties ??= []; + declaredProperties!.add(declaredMember); + } else { + assert( + declaredMethod == null, + "Multiple methods unexpectedly declared: " + "${declaredMethod} and ${declaredMember}."); + declaredMethod = declaredMember; + } + if (_dataForTesting != null && aliasForTesting != null) { + _dataForTesting.aliasMap[aliasForTesting] = declaredMember; + } + } + } + + /// Registers that [mixedMember] overrides extended and implemented + /// members through application. + /// + /// Getters and setters share overridden members so the registration + /// of override relations in performed after the interface members have + /// been computed. + /// + /// Declared mixed in members must be checked for valid override of the + /// overridden members but _not_ register an override dependency with the + /// overridden members. This is in contrast to declared members. For + /// instance: + /// + /// class Super { + /// int get property => 42; + /// } + /// class Mixin { + /// void set property(value) {} + /// } + /// class Class = Super with Mixin; + /// + /// Here the parameter type of the setter `Mixin.property` must _not_ be + /// inferred from the type of the getter `Super.property`, but should + /// instead default to `dynamic`. + void registerMixedInOverride(ClassMember mixedInMember, + {ClassMember? aliasForTesting}) { + assert(mixedInMember.classBuilder != _classBuilder, + "Only mixin members can override by application: ${mixedInMember}"); + if (_classBuilder is SourceClassBuilder) { + hasDeclaredMembers = true; + if (mixedInMember.isProperty) { + mixedInProperties ??= []; + mixedInProperties!.add(mixedInMember); + } else { + assert( + mixedInMethod == null, + "Multiple methods unexpectedly declared in mixin: " + "${mixedInMethod} and ${mixedInMember}."); + mixedInMethod = mixedInMember; + } + if (_dataForTesting != null && aliasForTesting != null) { + _dataForTesting.aliasMap[aliasForTesting] = mixedInMember; + } + } + } + + /// Registers that [inheritedMember] should be checked to validly override + /// [overrides]. + /// + /// This is needed in the case where a concrete member is inherited into + /// a concrete subclass. For instance: + /// + /// class Super { + /// void method() {} + /// } + /// abstract class Interface { + /// void method(); + /// } + /// class Class extends Super implements Interface {} + /// + /// Here `Super.method` must be checked to be a valid implementation for + /// `Interface.method` by being a valid override of it. + void registerInheritedImplements( + ClassMember inheritedMember, Set overrides, + {required ClassMember aliasForTesting}) { + if (_classBuilder is SourceClassBuilder) { + assert( + inheritedMember.classBuilder != _classBuilder, + "Only inherited members can implement by inheritance: " + "${inheritedMember}"); + _inheritedImplementsMap[inheritedMember] = overrides; + if (_dataForTesting != null) { + _dataForTesting.aliasMap[aliasForTesting] = inheritedMember; + } + } + } + + /// Collects overrides of [getable] and [setable] in [declaredOverridesMap] + /// and [mixinApplicationOverridesMap] to set up need override checks. + void collectOverrides( + {required _SanitizedMember? getable, + required _SanitizedMember? setable, + required Map> declaredOverridesMap, + required Map> + mixinApplicationOverridesMap}) { + if (hasDeclaredMembers) { + Set getableOverrides = getable?.computeOverrides() ?? {}; + Set setableOverrides = setable?.computeOverrides() ?? {}; + if (getableOverrides.isNotEmpty || setableOverrides.isNotEmpty) { + if (declaredMethod != null && getableOverrides.isNotEmpty) { + /// class Super { + /// method() {} + /// } + /// class Class extends Super { + /// method() {} + /// } + declaredOverridesMap[declaredMethod!] = getableOverrides; + } + if (declaredProperties != null) { + Set overrides; + if (declaredMethod != null) { + /// class Super { + /// set setter() {} + /// } + /// class Class extends Super { + /// method() {} + /// } + overrides = setableOverrides; + } else { + /// class Super { + /// get property => null + /// void set property(value) {} + /// } + /// class Class extends Super { + /// get property => null + /// void set property(value) {} + /// } + overrides = {...getableOverrides, ...setableOverrides}; + } + if (overrides.isNotEmpty) { + for (ClassMember declaredMember in declaredProperties!) { + declaredOverridesMap[declaredMember] = overrides; + } + } + } + if (mixedInMethod != null && getableOverrides.isNotEmpty) { + /// class Super { + /// method() {} + /// } + /// class Mixin { + /// method() {} + /// } + /// class Class = Super with Mixin; + mixinApplicationOverridesMap[mixedInMethod!] = getableOverrides; + } + if (mixedInProperties != null) { + Set overrides; + if (mixedInMethod != null) { + /// class Super { + /// set setter() {} + /// } + /// class Mixin { + /// method() {} + /// } + /// class Class = Super with Mixin; + overrides = setableOverrides; + } else { + /// class Super { + /// method() {} + /// } + /// class Mixin extends Super { + /// method() {} + /// } + overrides = {...getableOverrides, ...setableOverrides}; + } + if (overrides.isNotEmpty) { + for (ClassMember mixedInMember in mixedInProperties!) { + mixinApplicationOverridesMap[mixedInMember] = overrides; + } + } + } + } + } + } } Set toSet( @@ -2923,39 +3053,10 @@ void reportCantInferFieldType(ClassBuilder cls, SourceFieldBuilder member, context: context); } -List _inheritedConflictContext(ClassMember a, ClassMember b) { - int length = a.fullNameForErrors.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.charOffset.compareTo(b.charOffset); - } - ClassMember first; - ClassMember second; - if (compare < 0) { - first = a; - second = b; - } else { - first = b; - second = a; - } - return [ - messageInheritedMembersConflictCause1.withLocation( - first.fileUri, first.charOffset, length), - messageInheritedMembersConflictCause2.withLocation( - second.fileUri, second.charOffset, length), - ]; -} - bool isNameVisibleIn(Name name, LibraryBuilder libraryBuilder) { return !name.isPrivate || name.library == libraryBuilder.library; } -int compareDeclarations(ClassMember a, ClassMember b) { - if (a == b) return 0; - return ClassHierarchy.compareNames(a.name, b.name); -} - Set unfoldDeclarations(Iterable members) { Set result = {}; _unfoldDeclarations(members, result); diff --git a/pkg/front_end/test/spell_checking_list_code.txt b/pkg/front_end/test/spell_checking_list_code.txt index 3dfb6ec3c059..9bd71215a28f 100644 --- a/pkg/front_end/test/spell_checking_list_code.txt +++ b/pkg/front_end/test/spell_checking_list_code.txt @@ -1391,6 +1391,8 @@ s safer sampled sandboxed +sanitize +sanitized sanitizing saw say