fix: report error when generic method attempts to implement non-generic interface method#3007
Conversation
…ic interface method
6463821 to
c1f84f8
Compare
|
The only thing that could be improved is to add a new error specific to AS. Such errors: isn’t very informative, since the method is actually defined and has the correct signature. The only issue is incorrect parametric polymorphism. Perhaps may sense to add new AS241 error for this |
src/resolver.ts
Outdated
| if (numOverrideTypeParams == numBaseTypeArgs) { | ||
| overrideInstance = this.resolveFunction(boundFuncPrototype, baseTypeArgs); | ||
| } |
There was a problem hiding this comment.
should we allow this implement when the generic number are the same? I think it will crash also if the typing mismatched.
IMO, maybe we should forbidden all generic function in interface?
There was a problem hiding this comment.
Hmm, I see only one edge case in this scenario:
interface I {
add<T, U>(x: T, y: U): U;
}
class C2 implements I {
add<U, T>(x: T, y: U): U {
return x;
}
}There was a problem hiding this comment.
Good point. If we follow C++ design:
- template function aka. generic function can't be member in interface
- template function also can't override virtual function in interface.
Shall we do the same?
I would prefer to follow C++ design because generic function in interface is a confused behaviour
But it may break some existing code on user side.
What do maintainers prefer?
There was a problem hiding this comment.
Dear maintainers,
I have pushed a new commit to forbidden interface function and abstract function to be generic.
Could you check this design concept.
By the way, I just awared that if base class and child class one is generic, the other one is non-generic, then there is another crash. But I would prefer to fix it in another PR. I just left a fix me in current PR.
var called: string = "";
class A {
foo<T>(x: T): void {
called = "A";
}
}
class B extends A {
foo(x: i32): void { // non-generic overrides generic
called = "B";
}
}
// Static dispatch: B.foo called directly
var b = new B();
b.foo(1);
assert(called == "B");
called = "";
// Virtual dispatch through A reference: which gets called?
var a: A = new B();
a.foo<i32>(1);
assert(called == "B"); // if vtable works, "B"; if fallback to base, "A"
There I just referred the hehavior of clang. In clang, a generic(template) function isn't regarded as overriding of virtual interface function, so the interface function is regarded as "unimplemented" |
HerrCai0907
left a comment
There was a problem hiding this comment.
should we mark it as break changing?
|
I think it doesn't need to be marked as break change, because calling generic function interface is not assemblyscript designed to be. Even if it worked in some cases, it's just working by accident. I would regard generic function in interface as a bug which isn't detected in the past, not a feature break change. |
Fixes #3006 .
Changes proposed in this pull request:
TypeScript allows this because it uses type erasure — generics disappear at runtime, so
foo<T>()andfoo()are the same function. AssemblyScript cannot do the same because it uses monomorphization — each type argument produces a distinct function, and the vtable needs a single fixed entry. This is the same reason C++ forbids template functions from overriding virtual functions.The fix treats a generic method as not satisfying a non-generic interface method, matching C++ semantics.