-
Notifications
You must be signed in to change notification settings - Fork 1.7k
CFE is not inferring generic type for const default parameters #32415
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
@stereotype441 @leafpetersen @lrhn - Paul says this is a bug in analyzer. Here's the actual repro with collection:
It throws with CFE:
|
Paul is correct. You cannot write (We actually do not consider parameter default values as an implicit const context where you can omit |
In the package:collection example, the way the analyzer bug manifests is that the constructor for ListEquality, which looks like this: class ListEquality<E> implements Equality<List<E>> {
const ListEquality([Equality<E> elementEquality = const DefaultEquality()]) ...;
} gets inferred as: class ListEquality<E> implements Equality<List<E>> {
const ListEquality([Equality<E> elementEquality = const DefaultEquality<E>()]) ...;
} That's not legal, since class ListEquality<E> implements Equality<List<E>> {
const ListEquality([Equality<E> elementEquality = const DefaultEquality<Null>()]) ...;
} Unfortunately, that causes a (correct) strong mode runtime error, because int hash(E e); Since One possible solution would be to change the signature of
|
The suggested solution (use |
That seems like it would work - a class that implements We could even make It's annoying that we have to make this workaround to dodge the type system, but luckily it's fairly localized. |
It does seem, intuitively, like inferring |
There are multiple issues that prevent this.
All of these prevents the default value from both depending on |
Sort of. There is a notion of a potentially const expression to handle this at the term level, right? So why not at the type level? Wouldn't that work here? |
Moved milestone since the workaround has been committed. |
The notion of potentially constant only applies to initializer lists and it only allows string/int/bool computations. Object creations are always actually constant (aka. "compile-time constant" rather than "potentially constant"). This is an object creation expression in a default value position. We expect default values to be actually constant so each invocation gets the same default value. Since it's a const constructor, it would still require an expression that can be evaluated at compile time to create a different non-int/bool/String object each time, which is not something we have. So, potentially constant type expressions are possible, but they won't apply to constant object creation expressions anyway. That's why I didn't introduce them - they would only be good for |
Assigning to myself to retriage. What do we need to resolve by the next milestone? |
I found a similar issue (in a flutter application) involving generic functions used as default values. In that case, CFE does appear to instantiate the generic function, although Dart VM doesn't seem to do that correctly at runtime. Here's the pattern it used: typedef F<T>(T t);
_defaultCallback<T>(T t) => t;
class C<T> {
final F<T> callback;
// the behavior is the same with or without `const` here
// the flutter app did not have `const`
const C({this.callback = _defaultCallback});
}
main() {
var c = new C<int>();
print(c.callback(42));
} The Kernel IR does contain instantiate (and the program works in DDK):
whereas Dart with preview-dart-2 yields:
Note the uninstantiated generic function type. ... I'd file this as a VM bug but I'm not sure the intended behavior of the language? Should CFE reject my example at compile time? In the meantime I worked around the problem by using a constructor initializer: typedef F<T>(T t);
_defaultCallback<T>(T t) => t;
class C<T> {
final F<T> callback;
const C({F<T> callback}) : callback = callback ?? _defaultCallback;
}
main() {
var c = new C<int>();
print(c.callback(42));
} |
Yes. You can reduce the example to: dynamic _defaultCallback<T>(T t) => t;
class C<T> {
final dynamic Function(T) callback;
const C({this.callback = _defaultCallback});
} The expression It can't be The resulting type must be assignable to In any case, using a private generic function, which isn't used for anything else, for the default value is unnecessary - the default value is a single value, one instantiation, so you should just write that non-generic function directly, As I see it, generic method instantiation should be treated like inference where we infer a type argument, like With that in mind, the rewrite shouldn't work any better, and it's an accident that it works. The accident here is that we haven't actually mentioned the generic-method-tearoff case, with its invisible type argument, in the constants section, and that you don't actually invoke the constructor with |
@lrhn -- does any of that change if the constructor is not marked In the real flutter app code, the constructor was not marked const, but VM still had a problem with the default argument value. I presume it's valid to reference |
It changes nothing for the default value expression. Default value expressions have to be constant no matter where they occur, whether it's a plain method, a const constructor or a non-const constructor. It does change what the initializer expression is allowed to do, and in a non-const constrcutor it can do a per-invocation instantiation/tear-off that uses |
@lrhn @leafpetersen - what's actionable here? Is there a bug in analyzer, CFE, or both? |
Seems like it's a bug in both, regardless :) If these should be errors, both Analyzer/CFE are missing that logic. If these should be inferred, it sounds like they'll have to be inferred as something that doesn't depend on |
@leafpetersen will turn this into analyzer and CFE bugs. |
@leafpetersen - have you had a chance to look at this? Do we need for Beta4? |
Filed separate issues as below. CFE specific issues:
Analyzer specific issues: |
Closing this since the individual issues have been filed. |
DDK asserts on the following. DDC does not. In the constructor of
C
, the CFE is instantiatingB
withNull
(causing the error). The analyzer withT
(which works).The result with DDK is:
@a-siva is hitting this in
package:collection
.@kmillikin
The text was updated successfully, but these errors were encountered: