-
Notifications
You must be signed in to change notification settings - Fork 207
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
Incorrect extension method resolution with generics #1182
Comments
I agree. I recently filed dart-lang/sdk#43177 which is the same issue. It is a breaking change to fix it, though. I fixed it in https://dart-review.googlesource.com/c/sdk/+/155324 but had to revert in https://dart-review.googlesource.com/c/sdk/+/156195 because of failures in code matching the patterns in dart-lang/sdk#43177. |
I agree that it's a bug. The static type of Changing it to var tmp = x.test;
S Function(S) f = tmp; makes no difference (reasonably, a receiver has no type context). If you instead (or additionally) write S Function(S) g = Test(x).test;
S Function(S) h = Test<S>(x).test; there is no error in either of those, but that's exactly what @johnniwinther dart-lang/sdk#43177 is slightly different because it's about promoted type variables. You can't represent |
I think it's surprising that both the CFE and the analyzer treat the example in this way. It is specified (in the feature spec as well as in the language-specification-integrated text) that type inference for the extension application class Test<T> {
final T _this;
Test(this._this);
} @johnniwinther, @scheglov it seems like that approach is incompatible with the steps actually taken by the implementation? |
Johnni explained the likely reason for the bug: You need to use the bound/intersection type in order to check whether |
@lrhn #43177 is not only about promoted type variables - it's about bounded type variables - this can be from declaration (as here) or from promotion (as in examples 1 and 3 in #43177). |
Ok, it sounds like this is something we'll have to manage as a breaking bug fix. @johnniwinther @scheglov any thoughts on rolling this out as part of null safety, vs as a flat out breaking change? Sounds like @johnniwinther encountered actual breakage from an attempted fix? |
Yes, the analyzer has the same issue - we resolve the type to its bounds to search for an interface member, and then continue using the resolved type to infer type arguments extensions. I will try to fix it in the analyzer tomorrow, run shared tests and google3. |
CL for the analyzer: https://dart-review.googlesource.com/c/sdk/+/160883 |
It almost works in google3, mostly have to add Just one target is broken, like this: // @dart = 2.8
class A {}
class B extends A {}
extension E<T> on T {
T get test => this;
}
void g(B _) {}
void f<U extends A>(U u) {
g(u.test);
} What used to work because of implicit downcast from @leafpetersen The change to resolve type parameters to bounds in the analyzer is fairly limited, and could be done depending on whether the library being analyzer in Null Safe or not. |
Yeah, I suspected this is why we hadn't noticed this yet. I'm inclined to roll this out under the null safety flag if it's easy to do, just to avoid the overhead of the breaking change. @johnniwinther is that easy enough to do on your end? If not it's fine, I'll just file a breaking change request, I don't anticipate any concerns given the limited breakage. |
It can easily be under null safety in the CFE. |
Sure, I'll take a look. Proposed test: https://dart-review.googlesource.com/c/sdk/+/165022. |
Tests landed in dart-lang/sdk@cd280a3. |
I see that tests expect that we erase / demote type parameters. Could you point me at the place where the spec describes this? Currently in the analyzer we don't. class A<T> {
A(T t);
T get foo => throw 0;
}
void f<S>(S s) {
if (s is int) {
s.isEven;
var bar = A(s).foo;
bar.isEven;
}
} |
Edit: The applicable rule in inference.md is
so X f<X>(X x) => "Hello!" as X;
int g<S>(S s) {
if (s is int) {
return f(s);
} else {
return 1;
}
}
void main() {
int y = g<Object>(42);
y.isEven;
} |
@scheglov I think the issue you are raising is that we don't specify when intersection types get erased. @eernstg and I spent a bit of time discussing this, but have a few more things to explore before I follow up with a spec update. In the meantime, I noted that the analyzer behaves differently with your example depending on whether NNBD is on or off. In the former case it infers @stefantsov do you know where the CFE does the erasure of intersections? |
The CFE erasure intersection through the demoteTypeInLibrary function. This is called on an inferred type before store it as local function return types, local function parameter types, local variables types, field types, and type argument. I.e. in (hopefully) all cases where an inferred type is reified. |
With null safety the type of With legacy the type of In the analyzer do demotion only when infer the type of a variable. Should we do more? |
https://dart-review.googlesource.com/c/sdk/+/165683 will also demote inferred type arguments, for constructor or function invocations. |
Bug: dart-lang/language#1182 (comment) Change-Id: I72d30f51aaf1d2651c1568cd68f37e5c7c03b582 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/165683 Reviewed-by: Brian Wilkerson <[email protected]> Commit-Queue: Konstantin Shcheglov <[email protected]>
Bug: #43590 Bug: dart-lang/language#1182 Change-Id: I80366ad4f777eec299143a002bbdc9d92ba61a5b Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/160883 Commit-Queue: Konstantin Shcheglov <[email protected]> Reviewed-by: Brian Wilkerson <[email protected]>
This has been fixed. |
Filing this as a language issue first to ensure that I'm not missing something. The analyzer and the CFE both consistently resolve extensions applied to things of generic type in a way that appears completely incorrect to me. Specifically, given the program below, both implementations instantiate
T
to benum
rather thanS
.As best I can see, this is completely inconsistent with both the specification and my expectation. This seems to be the released behavior however, on both platforms. Am I confused, or missing something?
cc @eernstg @lrhn @johnniwinther @stereotype441 @scheglov
The text was updated successfully, but these errors were encountered: