-
Notifications
You must be signed in to change notification settings - Fork 1.7k
CFE/Analyzer inconsistent behavior of instantiate to bounds #32483
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
Note: bug title is about DDC/K but in reality, this is more of an inconsistency between analyzer and front_end |
Right, the "default default" (that is, the top type chosen by instantiate-to-bound as the type argument when there is no bound) is For For If the CFE fills in It would be nice if the CFE could simply fill in missing type variable bounds with It looks like the CFE would need to somehow remember that these |
Edited the subject, as this sounds like a CFE bug. |
Yes, this is definitely a CFE bug. I just ran into a more obvious failure of the same thing: foo<T extends num>(T bar, int p) {
return bar + p;
}
confuse(x) {
return x;
return x;
}
main() {
dynamic r = 4;
r = confuse(r);
print(foo(r, 5));
print(foo<double>(2.0, 4));
} Then if you dump the produced IR, you get:
Instead, that first invocation of
because num is the lower bound in this case. adding @kmillikin so he can assign to the proper person to fix this. |
Yep, sorry for that. I'm working on a fix in CL 53212. Also, the following program that is currently rejected by the CFE should be compiled correctly after the fix: class A<T extends num> {
final T t;
A(this.t);
}
dynamic foo(dynamic x) => x;
main() {
var a = A(foo(42));
} |
Ok, so I think there are two issues that are being discussed here:
Now, I want to write how (1) could be resolved after #31581 is fixed. The idea is to give // The bound is omitted, so `defaultType` is `dynamic`.
void f<T extends Object = dynamic>() => print(T);
// The bound is specified explicitly, so `defaultType` equals to that.
void g<T extends dynamic = dynamic>() => print(T);
// The bound is specified explicitly, so `defaultType` equals to that.
void h<T extends Object = Object>() => print(T);
main() {
print('Runtime behavior:');
// `f` is a tear-off here, so no type arguments are provided by the CFE.
(f as dynamic)();
// Same as above: no type arguments are provided by the CFE to the tear-off.
(g as dynamic)();
// Same as above: no type arguments are provided by the CFE to the tear-off.
(h as dynamic)();
print('\nStatic behavior:');
// Filled in by the CFE with the contents of `defaultType`, that is, `dynamic`.
f<dynamic>();
// Filled in by the CFE with the contents of `defaultType`, that is, `dynamic`.
g<dynamic>();
// Filled in by the CFE with the contents of `defaultType`, that is, `Object`.
h<Object>();
} Here the In order to achieve the desired run-time behavior, the back ends will need to fetch the contents of The reason why currently the raw types with type variables that have explicit |
Yes, both of those examples should be inference failure errors (as the analyzer reports) since T foo<T extends num>([T x]) => x;
class A<T extends num> {
final T t;
A([this.t]);
}
dynamic bar(dynamic x) => x;
void main() {
dynamic r;
var a = foo(r); // Failed inference
var b = new A(bar(42)); // Failed inference
var c = foo(); // Inferred as foo<num>
var d = A(); // Inferred as A<num>
} |
Small status update. #31581 is resolved, and the programs are now compiled into the form that allows backends to retrieve the default types (that is, the results from instantiate-to-bound) from type parameters and use them in place of the missing type arguments in dynamic invocations of generic functions. The example script from the original message by @jmesserly is now compiled into the following representation in kernel:
In the listing above, $ out/ReleaseX64/dart --preview-dart-2 example.dart
Runtime behavior:
dynamic
dynamic
Object
Static behavior:
dynamic
dynamic
dynamic Note that the static invocation still receives |
Ok, the static part of the fix has just landed: 2c9d892. With that patch in place, the original example is compiled into the following in strong mode:
And here is the output from running the script with the VM in Dart 2 mode: $ out/ReleaseX64/dart --preview-dart-2 example.dart
Runtime behavior:
dynamic
dynamic
Object
Static behavior:
dynamic
dynamic
Object The remaining part of the issue is to produce a compile-time error for the code from Emily's comment as suggested by Leaf. |
Ok, I guess when you consider the title of this issue and the remaining issues discussed in the thread, you may see that they are slightly different. The issue in the code from Emily's comment is actually about checking the type arguments against their bounds after type inference is done, and is not about instantiate-to-bound. So, I'm closing this issue and will be tracking the progress on the remaining in a new one: #33308. |
Uh oh!
There was an error while loading. Please reload this page.
Instantiate to bounds works differently if a bound is omitted in DDC depending on the front-end. Analyzer fills in
dynamic
, CFE/Kernel fills inObject
. Inference behavior is different as well:DDC and DDK both use the same runtime. The reason it behaves differently is the compile time bound is recorded differently. CFE/Kernel fills it in like this:
Statically, I'm not totally sure what's going on, in particular, why front_end chooses
dynamic
forT
inh()
. It appears the inference algorithms are slightly different in how they treat the bound.I like that DDC/Analyzer's behavior is consistent: both runtime and compile time give the same results. But regardless we need the two implementations to be more consistent.
Thoughts? @leafpetersen @lrhn @eernstg
My reading of https://github.com/dart-lang/sdk/blob/master/docs/language/informal/instantiate-to-bound.md suggests that
dynamic
is the correct answer for omitted bounds, but that informal spec covers generic classes rather than generic methods. (The Dart 1 spec said that omitted bounds were Object, but that had little practical effect, as raw types were instantiated with dynamic, and they're both top types for the purposes of checking whether the type argument is legal w.r.t. the bound.)The text was updated successfully, but these errors were encountered: