diff --git a/src/diagnosticMessages.json b/src/diagnosticMessages.json index 6b98f6fd34..016ba4ec3f 100644 --- a/src/diagnosticMessages.json +++ b/src/diagnosticMessages.json @@ -53,6 +53,7 @@ "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, + "Cannot override generic method '{0}' with a non-generic method or vice versa.": 242, "Importing the table disables some indirect call optimizations.": 901, "Exporting the table disables some indirect call optimizations.": 902, diff --git a/src/program.ts b/src/program.ts index e5e27ebc59..3522465ba0 100644 --- a/src/program.ts +++ b/src/program.ts @@ -1557,6 +1557,16 @@ export class Program extends DiagnosticEmitter { ) { let thisMethod = thisMember; let baseMethod = baseMember; + let thisIsGeneric = thisMethod.is(CommonFlags.Generic); + let baseIsGeneric = baseMethod.is(CommonFlags.Generic); + if (thisIsGeneric != baseIsGeneric) { + this.errorRelated( + DiagnosticCode.Cannot_override_generic_method_0_with_a_non_generic_method_or_vice_versa, + thisMethod.identifierNode.range, baseMethod.identifierNode.range, + thisMethod.name + ); + return; + } if (!thisMethod.visibilityEquals(baseMethod)) { this.errorRelated( DiagnosticCode.Overload_signatures_must_all_be_public_private_or_protected, diff --git a/src/resolver.ts b/src/resolver.ts index 2a4df6c3e7..56ee1c7ccb 100644 --- a/src/resolver.ts +++ b/src/resolver.ts @@ -2933,11 +2933,24 @@ export class Resolver extends DiagnosticEmitter { incompatibleOverride = false; } else { if (baseMember.kind == ElementKind.FunctionPrototype) { - // Possibly generic. Resolve with same type arguments to obtain the correct one. let basePrototype = baseMember; - let baseFunction = this.resolveFunction(basePrototype, typeArguments, new Map(), ReportMode.Swallow); - if (baseFunction && instance.signature.isAssignableTo(baseFunction.signature, true)) { - incompatibleOverride = false; + let baseTypeParameterNodes = basePrototype.typeParameterNodes; + let baseIsGeneric = baseTypeParameterNodes != null && baseTypeParameterNodes.length > 0; + let instanceIsGeneric = typeArguments != null && typeArguments.length > 0; + if (baseIsGeneric != instanceIsGeneric) { + // Cannot mix generic and non-generic functions in an override chain + this.errorRelated( + DiagnosticCode.Cannot_override_generic_method_0_with_a_non_generic_method_or_vice_versa, + instance.identifierAndSignatureRange, baseMember.identifierAndSignatureRange, + methodOrPropertyName + ); + incompatibleOverride = false; // already reported + } else { + // Possibly generic. Resolve with same type arguments to obtain the correct one. + let baseFunction = this.resolveFunction(basePrototype, typeArguments, new Map(), ReportMode.Swallow); + if (baseFunction && instance.signature.isAssignableTo(baseFunction.signature, true)) { + incompatibleOverride = false; + } } } } @@ -3069,11 +3082,8 @@ export class Resolver extends DiagnosticEmitter { // 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)) { + // - non-generic child → generic base: skip; mismatched generic-ness. + if (boundFuncPrototype.is(CommonFlags.Generic) == instance.is(CommonFlags.Generic)) { overrideInstance = this.resolveFunction(boundFuncPrototype, instance.typeArguments); } } diff --git a/tests/compiler/override-generic-mismatch.json b/tests/compiler/override-generic-mismatch.json new file mode 100644 index 0000000000..d719ccfd28 --- /dev/null +++ b/tests/compiler/override-generic-mismatch.json @@ -0,0 +1,8 @@ +{ + "asc_flags": [], + "stderr": [ + "AS242: Cannot override generic method 'foo' with a non-generic method or vice versa.", + "AS242: Cannot override generic method 'bar' with a non-generic method or vice versa.", + "EOF" + ] +} diff --git a/tests/compiler/override-generic-mismatch.ts b/tests/compiler/override-generic-mismatch.ts new file mode 100644 index 0000000000..1848d804b8 --- /dev/null +++ b/tests/compiler/override-generic-mismatch.ts @@ -0,0 +1,25 @@ +// Non-generic method overriding generic method +class A { + foo(x: T): void {} +} + +class B extends A { + foo(x: i32): void {} +} + +let a:A = new B(); +a.foo(1); + +// Generic method overriding non-generic method +class C { + bar(x: i32): void {} +} + +class D extends C { + bar(x: T): void {} +} + +let c:C = new D(); +c.bar(1); + +ERROR("EOF");