-
Notifications
You must be signed in to change notification settings - Fork 205
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
Should combined member signature computation be defined WRT the current library? #2263
Comments
I believe we do have a specified compile-time error for the member signature clashes. We have the notion of a 'combined interface', and the combined interface of a list of interfaces includes the combined member signature for all members, for all libraries where the given member is accessible, cf. this spec location:
So in the example here the computation of the combined interface of However, it looks like the language specification is vague when it comes to the computation of a combined interface: I don't think it's ever specified that we must compute the combined interface for |
Yes, I cannot find any place in the spec where that definition is used. The only uses of |
We wouldn't simply specify that the combined interface of all superinterfaces of every class/mixin is computed, and the compile-time error may then occur: We don't want spurious compile-time errors for a class based on public member signatures from superinterfaces that do not have a combined member interface. abstract class A { void m(int i, double d); }
abstract class B { void m(double d, int i); }
class C implements A, B {
void m(num n, num n) {}
} So we could drop the notion of 'combined interface' entirely and re-word the checks on classes and mixins such that they require a combined member signature to exist for every member of a direct superinterface (public or private to any library) that is not overridden in the class itself. I think we have almost all of that already, but the explicit reference to private methods seems to be mentioned with 'combined interface' only, so that but would be reorganized. |
Ok, after discussing this further with @lrhn and @eernstg I think we're all in agreement that the correct resolution is as @eernstg says above, i.e. "require a combined member signature to exist for every member of a direct superinterface (public or private to any library) that is not overridden in the class itself". More concretely, this means that we end up with the following as the proposed semantics for the errors and warnings around a given method
These restrictions enforce the following properties:
|
(Technically we require it to be a proper override of all signatures in all super-interfaces, not just direct super-interfaces.
It's possible to inherit a correct implementation. In that case, we do not need to introduce a Otherwise I agree. (I then want to introduce lints that warn you if your private members risk causing conflicts for people in other libraries, because those people usually can't fix the conflicts.) |
@lrhn wrote:
That isn't quite true, we have defined the interface of a class This is crucial for programs that include libraries both with and without null safety, and convenient in the simpler setting where everything has null safety. But we do need to double check that the special rule (which is outside the definition of what it means to be a 'correct override') about the relationship between the declared type of a parameter which is covariant by declaration, and the declared types of all corresponding parameters in all superinterfaces (direct or indirect) will also be performed for members that are private to other libraries than the current one.
I believe this would be covered by 'and
+100! |
@leafpetersen said:
Should this apply to the synthetic classes that are created as part of de-sugaring class A {
void f(int i) {}
}
abstract class I {
void f(String s);
}
mixin M implements I {}
class AwithM = A with M;
class B extends AwithM {
void f(Object o) {}
} because in the class If I'm right about that, then what does that mean about this "equivalent" code, in which the class class A {
void f(int i) {}
}
abstract class I {
void f(String s);
}
mixin M implements I {}
class B extends A with M {
void f(Object o) {}
} Note that currently the CFE rejects both of these examples, and the analyzer accepts both of them 😢 |
The mixin application classes introduced by |
@stereotype441, the rules about correct overrides (and similar checks, e.g., the one about class A {
void f(int i) {}
}
abstract class I {
void f(String s);
}
mixin M implements I {}
class AwithM = A with M;
class B extends AwithM {
void f(Object o) {}
} With the current rules we should get an error at language/specification/dartLangSpec.tex Line 2575 in 812f939
We do not specify the handling of dependent errors, which means that class class A {
void f(int i) {}
}
abstract class I {
void f(String s);
}
mixin M implements I {}
class B extends A with M {
void f(Object o) {}
} With this variant the implicitly induced superclass of abstract class S extends A implements M {} Class We could have a special exception saying that these implicitly induced classes don't have to have a complete interface, so they could have certain members whose signature is ambiguous as long as they're part of a declaration of a class (like However, it could just as well be argued that this kind of ambiguity in a mixin application used as a superclass should be detected and reported, because it's likely to be a mistake. The CFE already reports an error (whose explanatory text is compatible with the perspective that the error is caused by an ambiguity in member signatures). I'd recommend that we rely on this behavior (which makes it a change that isn't breaking in practice), and change the analyzer to report the error as well. |
We could make Not sure it buys us much. |
Cf. #2283. |
This issue is an attempt to split out one of several overlapping issues relative to this issue.
In discussion in the last language team meeting, I believe the claim was made that the following code is, per the spec, valid code which should be accepted:
I believe that the reason for this is that the definition of combined member signature (section 11.2.2) is done with respect to a library
L
, thereby filtering out inaccessible members from consideration when requiring that there exist a member signature in one or more of the super-interfaces which is a subtype of all of the others.@eernstg is this a correct summary of the position WRT to the current specification, or have I missed other relevant bits?
Note that none of our implementations currently accept this program: the definition of class
C
causes errors to be emitted for all three members.Note that there is no concrete member which can implement
_n
, nor_x
. For_m
there is a valid signature which implements both, but we do not specify any way of computing this, and neither declaration of_m
is a subtype of the other.It seems to me that allowing this program is very contrary to the notion of combined member signature. There is no valid combined signature which can be computed. Practically speaking, this becomes an issue for the question of generating a noSuchMethod forward for these members: what is the signature (what even is the calling convention?) for the generated forwarder?
I believe that we should specify this program to be an error, and impose the combined member signature requirements to all members, accessible or not. Thoughts on this?
cc @eernstg @munificent @lrhn @jakemac53 @natebosch @chloestefantsova @johnniwinther @scheglov
The text was updated successfully, but these errors were encountered: