-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Forwarding stubs should not forward to other forwarding stubs #31580
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
Comments
The reason for this is that the semantics of forwarding stubs are much more complicated if they can forward to other forwarding stubs. |
I'm not sure I would expect this program to compile. I'd expect the class The type of When we check whether a class correctly implements its interface, we check whether the member implementation's type would be a valid override of the interface member type. You can't write an override like that, because covariance is inherited, but this example inherits a non-covariant implementation and tries to make it match a (covariant) type that it doesn't match. So, instead of forwarding anything, I'd just reject the classes You could instead choose to provide a satisfying implementation: class D extends C implements I1 {
void f(covariant A x, B y) => super.f(x as B, y); // and tell me *why* x is a B!
} The fact that you have a surprising and unsafe-looking cast here suggests that the just implicitly calling Are there any good reasons for allowing classes like Come to think of it, I'm not we have fully considered the is-a-valid-implementation rules for covariant parameter methods. |
I think the strongest argument in favor of requiring an explicit declaration of So, based on the fact that the forwarder in I think the way to say this is that it is a compile-time error if the implementation available to a class (of a method blah-blah) has a non-covariant parameter, and the corresponding parameter (in blah-blah) in the interface is covariant. Checking, it is clear (and requires no amendments to the notion of covariant parameters) that |
@lrhn I think you make a very convincing case that class @leafpetersen do you agree with this? If so, I'm happy to take that as gospel from the language team, and I'll adjust the front end behavior accordingly. |
@lrhn regarding your followup comment, that the classes As I understand it, the current analyzer behavior is: when checking whether an implementation method satisfies the interface of a class, if the implementation method contains any parameters that are marked class A {
f(covariant num x) {}
}
class B extends A {
f(covariant Object x) {}
}
class C extends B {
f(covariant int x) {}
}
class D extends B {
f(covariant String x) {} // Incompatible with transitively inherited interface from A
} What rule do you have in mind, and how would it work with this example? |
@eernstg I think the rule you have in mind would reject a lot of programs that DDC currently accepts, for example: class C {
void f(String x) {}
}
abstract class I<T> {
void f(T x);
}
class D extends C implements I<String> {} This would be rejected because the parameter of I'm not certain, but I suspect that the rule you have in mind would actually reject all programs in which DDC currently inserts forwarding stubs. I suspect that probably makes it a bigger breaking change than we can feasibly entertain right now. (Edit: changed |
Regarding the original subject of this bug (that the front end shouldn't create forwarding stubs that forward to other forwarding stubs), I think that issue is still of concern, if we consider a slightly different example: class A {}
class B extends A {}
class C {
void f(B x, B y) {}
}
abstract class I1<T> {
void f(T x, B y);
}
class D extends C implements I1<B> {}
abstract class I2<T> {
void f(B x, T y);
}
class E extends D implements I2<B> {} In this example the covariance arises from covariant generics rather than the use of the |
From Lasse:
I didn't see this, but it wouldn't be the case with the approach that I considered: The first positional parameter of From Paul:
class C {
void f(String x) {}
}
abstract class I<T> {
void f(T x);
}
class D extends C implements I<String> {} That's true, the rule that I proposed would cause this to be rejected (and the developer could fix it by writing an explicit forwarder in But if you ask your IDE (if that's possible, but it could certainly be possible) to take you to the implementation of (Ah, here's a natural scenario: You are actually running the torn-off method, so you can directly see during single-stepping that you're running the implementation declared in Does this really affect "lots of programs"? Isn't it reasonable to tell developers that something spooky is going on with |
I think I was perhaps a bit too strict there. It's correct that the covariant method from The generic-introduced case is harder to reject. I want to, but I can't really find a good argument (except a pseudo-rule of "a non-covariant method is not a valid implementation of a covariant signature", which isn't a rule we have introduced ... yet?) |
Yes, I'm happy with this. For this example: class C {
void f(String x) {}
}
abstract class I<T> {
void f(T x);
}
class D extends C implements I {} Did you mean Assuming so, I'm hesitant to reject this. I see where @lrhn is coming from, but on the other hand, if |
@leafpetersen Thanks! I'll fix the front end accordingly next week. (And yes, you are correct that I meant |
Consider the following code:
During compilation of class
D
, the front end discovers that it needs to insert a forwarding stub forf
to ensure thatx
is type checked. This forwarding stub forwards toC::f
. During compilation of classE
, the front end discovers that it needs to insert a forwarding stub forf
to ensure thaty
is also type checked. This forwarding stub should forward directly toC::f
--it shouldn't forward toD::f
.Note that the front end currently does this correctly provided that
D
andE
are being compiled at the same time. But in a modular compilation scenario whereE
is being compiled andD
comes from a .dill file, the front end tries to forwardE::f
toD::f
.(Borrowing terminology from #31562 (comment), the target in question here is the "super target")
CC: @sjindel-google
The text was updated successfully, but these errors were encountered: