Skip to content
Open
Show file tree
Hide file tree
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
1 change: 1 addition & 0 deletions src/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
"Initializer, definitive assignment or nullable type expected.": 238,
"Definitive assignment has no effect on local variables.": 239,
"Ambiguous operator overload '{0}' (conflicting overloads '{1}' and '{2}').": 240,
"An interface or abstract method '{0}' cannot have type parameters.": 241,

"Importing the table disables some indirect call optimizations.": 901,
"Exporting the table disables some indirect call optimizations.": 902,
Expand Down
14 changes: 14 additions & 0 deletions src/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2079,6 +2079,13 @@ export class Program extends DiagnosticEmitter {
}
case NodeKind.MethodDeclaration: {
let methodDeclaration = <MethodDeclaration>memberDeclaration;
if (methodDeclaration.is(CommonFlags.Abstract) && methodDeclaration.is(CommonFlags.Generic)) {
this.error(
DiagnosticCode.An_interface_or_abstract_method_0_cannot_have_type_parameters,
methodDeclaration.name.range,
methodDeclaration.name.text
);
}
if (memberDeclaration.isAny(CommonFlags.Get | CommonFlags.Set)) {
this.initializeProperty(methodDeclaration, element);
} else {
Expand Down Expand Up @@ -2653,6 +2660,13 @@ export class Program extends DiagnosticEmitter {
}
case NodeKind.MethodDeclaration: {
let methodDeclaration = <MethodDeclaration>memberDeclaration;
if (methodDeclaration.is(CommonFlags.Generic)) {
this.error(
DiagnosticCode.An_interface_or_abstract_method_0_cannot_have_type_parameters,
methodDeclaration.name.range,
methodDeclaration.name.text
);
}
if (memberDeclaration.isAny(CommonFlags.Get | CommonFlags.Set)) {
this.initializeProperty(methodDeclaration, element);
} else {
Expand Down
19 changes: 17 additions & 2 deletions src/resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3063,7 +3063,19 @@ export class Resolver extends DiagnosticEmitter {
let boundPrototype = classInstance.getMember(unboundOverridePrototype.name);
if (boundPrototype) { // might have errored earlier and wasn't added
assert(boundPrototype.kind == ElementKind.FunctionPrototype);
overrideInstance = this.resolveFunction(<FunctionPrototype>boundPrototype, instance.typeArguments);
let boundFuncPrototype = <FunctionPrototype>boundPrototype;
// Only resolve the override when the generic-ness matches the base method.
// - generic child → non-generic base: skip; vtable dispatch site has no type
// arguments to forward to the monomorphized child.
// - generic child → generic base: OK; type args come from the base call site.
// - non-generic child → non-generic base: OK; plain vtable override.
// FIXME: non-generic child → generic base is also mismatched (resolveFunction
// would assert on typeArguments/typeParameterNodes length mismatch) but that
// case is not yet guarded here. The correct fix is to replace this condition
// with `boundFuncPrototype.is(Generic) == instance.is(Generic)`.
if (!boundFuncPrototype.is(CommonFlags.Generic) || instance.is(CommonFlags.Generic)) {
overrideInstance = this.resolveFunction(boundFuncPrototype, instance.typeArguments);
}
}
}
if (overrideInstance) overrides.add(overrideInstance);
Expand Down Expand Up @@ -3438,7 +3450,10 @@ export class Resolver extends DiagnosticEmitter {
}
default: assert(false);
}
if (!member.is(CommonFlags.Abstract)) {
if (!member.is(CommonFlags.Abstract) && !member.is(CommonFlags.Generic)) {
// A generic method cannot satisfy a non-generic interface/abstract
// requirement: interface methods cannot be generic (AS241), and
// virtual dispatch cannot supply type arguments for monomorphization.
unimplemented.delete(memberName);
}
}
Expand Down
13 changes: 13 additions & 0 deletions tests/compiler/override-typeparam-mismatch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"asc_flags": [],
"stderr": [
"AS241: An interface or abstract method 'foo' cannot have type parameters.",
"AS241: An interface or abstract method 'baz' cannot have type parameters.",
"TS2515: Non-abstract class 'override-typeparam-mismatch/CC' does not implement inherited abstract member 'foo' from 'override-typeparam-mismatch/I'.",
"TS2515: Non-abstract class 'override-typeparam-mismatch/DD' does not implement inherited abstract member 'bar' from 'override-typeparam-mismatch/J'.",
"TS2515: Non-abstract class 'override-typeparam-mismatch/C2' does not implement inherited abstract member 'foo' from 'override-typeparam-mismatch/I2'.",
"TS2515: Non-abstract class 'override-typeparam-mismatch/FF' does not implement inherited abstract member 'baz' from 'override-typeparam-mismatch/A1'.",
"TS2515: Non-abstract class 'override-typeparam-mismatch/GG' does not implement inherited abstract member 'qux' from 'override-typeparam-mismatch/A2'.",
"EOF"
]
}
66 changes: 66 additions & 0 deletions tests/compiler/override-typeparam-mismatch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
interface I {
foo(x: i32): i32;
}

class CC implements I {
foo<T>(x: i32): i32 {
return x;
}
}

let c:I = new CC();
c.foo(1);

interface J {
bar(x: i32): i32;
}

class DD implements J {
bar<T>(x: i32): i32 {
return x;
}
}

let dd:DD = new DD();
dd.bar<i32>(1);

interface I2 {
foo<T, U>(x: i32): i32;
}

class C2 implements I2 {
foo<T>(x: i32): i32 {
return x;
}
}

new C2().foo<i32>(1);

// abstract method cannot be generic (AS241)
abstract class A1 {
abstract baz<T>(x: i32): i32;
}

class FF extends A1 {
baz<T>(x: i32): i32 {
return x;
}
}

new FF().baz<i32>(1);

// generic method cannot implement non-generic abstract method (TS2515)
abstract class A2 {
abstract qux(x: i32): i32;
}

class GG extends A2 {
qux<T>(x: i32): i32 {
return x;
}
}

let a: A2 = new GG();
a.qux(1);

ERROR("EOF");
Loading