Skip to content

Fix: multi implements issue #2510

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Oct 6, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 32 additions & 3 deletions src/program.ts
Original file line number Diff line number Diff line change
@@ -3007,18 +3007,38 @@ export abstract class DeclaredElement extends Element {
isCompatibleOverride(base: DeclaredElement): bool {
var self: DeclaredElement = this; // TS
var kind = self.kind;
var checkCompatibleOverride = false;
if (kind == base.kind) {
switch (kind) {
case ElementKind.FUNCTION_PROTOTYPE : {
let selfFunction = this.program.resolver.resolveFunction(<FunctionPrototype>self, null);
if (!selfFunction) return false;
let baseFunction = this.program.resolver.resolveFunction(<FunctionPrototype>base, null);
if (!baseFunction) return false;
self = selfFunction;
base = baseFunction;
checkCompatibleOverride = true;
// fall-through
}
case ElementKind.FUNCTION: {
return (<Function>self).signature.isAssignableTo((<Function>base).signature);
return (<Function>self).signature.isAssignableTo((<Function>base).signature, checkCompatibleOverride);
}
case ElementKind.PROPERTY_PROTOTYPE: {
let selfProperty = this.program.resolver.resolveProperty(<PropertyPrototype>self);
if (!selfProperty) return false;
let baseProperty = this.program.resolver.resolveProperty(<PropertyPrototype>base);
if (!baseProperty) return false;
self = selfProperty;
base = baseProperty;
// fall-through
}
case ElementKind.PROPERTY: {
let selfProperty = <Property>self;
let baseProperty = <Property>base;
let selfGetter = selfProperty.getterInstance;
let baseGetter = baseProperty.getterInstance;
if (selfGetter) {
if (!baseGetter || !selfGetter.signature.isAssignableTo(baseGetter.signature)) {
if (!baseGetter || !selfGetter.signature.isAssignableTo(baseGetter.signature, true)) {
return false;
}
} else if (baseGetter) {
@@ -3027,14 +3047,18 @@ export abstract class DeclaredElement extends Element {
let selfSetter = selfProperty.setterInstance;
let baseSetter = baseProperty.setterInstance;
if (selfSetter) {
if (!baseSetter || !selfSetter.signature.isAssignableTo(baseSetter.signature)) {
if (!baseSetter || !selfSetter.signature.isAssignableTo(baseSetter.signature, true)) {
return false;
}
} else if (baseSetter) {
return false;
}
return true;
}
// TODO: Implement properties overriding fields and vice-versa. Challenge is that anything overridable requires
// a virtual stub, but fields aren't functions. Either all (such) fields should become property-like, with a
// getter and a setter that can participate as a virtual stub, or it's allowed one-way, with fields integrated
// into what can be a virtual stub as get=load and set=store, then not necessarily with own accessor functions.
}
}
return false;
@@ -4294,6 +4318,11 @@ export class Class extends TypedElement {
);
}

/** Tests if this is an interface. */
get isInterface(): bool {
return this.kind == ElementKind.INTERFACE;
}

/** Constructs a new class. */
constructor(
/** Name incl. type parameters, i.e. `Foo<i32>`. */
45 changes: 38 additions & 7 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -502,6 +502,27 @@ export class Type {
return this.kind == target.kind;
}

/** Tests if this type can extend or implement the given type. */
canExtendOrImplement(base: Type): bool {
// Both must be class types
var thisClass = this.getClass();
var baseClass = base.getClass();
if (!thisClass || !baseClass) return false;
// Both types must be either managed or unmanaged
if (this.isManaged != base.isManaged) return false;
// Both types must be either internal or external references
if (this.isInternalReference) {
if (!base.isInternalReference) return false;
} else if (this.isExternalReference) {
if (!base.isExternalReference) return false;
} else {
return false;
}
// Interfaces can only extend interfaces
if (thisClass.isInterface && !baseClass.isInterface) return false;
return true;
}

/** Determines the common denominator type of two types, if there is any. */
static commonDenominator(left: Type, right: Type, signednessIsImportant: bool): Type | null {
if (right.isAssignableTo(left, signednessIsImportant)) return left;
@@ -926,17 +947,27 @@ export class Signature {
}

/** Tests if a value of this function type is assignable to a target of the specified function type. */
isAssignableTo(target: Signature): bool {

// check `this` type
isAssignableTo(target: Signature, checkCompatibleOverride: bool = false): bool {
var thisThisType = this.thisType;
var targetThisType = target.thisType;
if (thisThisType) {
if (!targetThisType || !thisThisType.isAssignableTo(targetThisType)) {
if (!checkCompatibleOverride) {
// check exact `this` type
if (thisThisType) {
if (!targetThisType || !thisThisType.isAssignableTo(targetThisType)) {
return false;
}
} else if (targetThisType) {
return false;
}
} else {
// check kind of `this` type
if (thisThisType) {
if (!targetThisType || !thisThisType.canExtendOrImplement(targetThisType)) {
return false;
}
} else if (targetThisType) {
return false;
}
} else if (targetThisType) {
return false;
}

// check rest parameter
Loading