Open eernstg opened 2 years ago
The discrepancy actually still exists for this example, which is a bit simpler:
abstract class C<X extends C<X>> {}
class D1 extends C<D1> {}
X f<X extends C<X>>(X x) => x;
void main() => f(D1());
The issue seems to be in the return type void
of main
. It's treated as a not-unknown upper constraint on the type variable being inferred, which results in the top-down inference deciding the type. In this case that's the conjunction of the constraints by void
and C<Object?>
.
If void
is replaced by dynamic
, which the CFE treats as UnknownType
when it solely plays the type context for the expression being inferred, the top-down part of the inference leaves the type undecided, and the bottom-up part yields D1
, as it happens in the case of the Analyzer.
The case of Object?
is similar to that of void
: it forces the CFE to use the top-down inferences to provide the type, and the Analyzer yields no error.
My current hypothesis is that the Analyzer treats all top types as empty context in inference. I'll try to see if we can do the same in the CFE and if it breaks anything.
That sounds like a tricky decision (like everything in type inference ;-). There is one situation where I'd think it creates a problem if every top type is considered to be the empty context during inference, and that is during computation of the return type of a function literal.
void main() {
void Function() f = () {
return 3; // Error.
};
}
In this example, we should get the error, because the return 3;
statement is treated like the same return statement would be treated in a regular (named) function or method declaration. If we consider the return type of the context type of the function literal to be the empty context then the function literal will just get the return type int
. The function literal will then have type int Function()
, and that's perfectly assignable to void Function()
, so the desired error message is gone.
It would probably be useful to get some input from @scheglov, in order to make sure the CFE and the analyzer handle the situation in ways that don't differ in any essential manner.
Not sure what is the question. The analyzer does report a compile-time error for this example with a local function. We get the context type void Function()
from the variable declaration, and set the return type void
as the imposed type for the body.
IIRC, for =>
, i.e. expression function body, we have an exception, that it is OK to return a value into void
.
For
abstract class C<X extends C<X>> {}
class D1 extends C<D1> {}
X f<X extends C<X>>(X x) => x;
void main() => f(D1());
the analyzer infers D1
for X
, using constraints:
0 = {map entry} [TypeParameterElementImpl] -> [_GrowableList]
key = {TypeParameterElementImpl} X extends C<X>
value = {_GrowableList} size = 3
0 = {_TypeConstraint} 'X extends C<X>' must extend 'void'
1 = {_TypeConstraint} 'D1' must extend 'X extends C<X>'
2 = {_TypeConstraint} 'X extends C<X>' must extend 'C<D1>'
Not sure what is the question
I just noticed that the CFE and the analyzer disagree, so the overall question would be "What's the correct behavior for the following example?"
abstract class C<X extends C<X>> {}
class D1 extends C<D1> {}
X f<X extends C<X>>(X x) => x;
void main() => f(D1());
But looking at the constraints produced by the analyzer, I'm wondering whether the CFE omits the creation of this constraint:
2 = {_TypeConstraint} 'X extends C<X>' must extend 'C<D1>'
@stereotype441, @chloestefantsova, @scheglov, it looks like the approaches taken by the analyzer and the CFE aren't quite identical. Is there a place where the behavior of this part of type inference is specified?
I don't know of a place where this particular rule is specified. Generally type inference is not very well specified, and even when it is, a lot of the specs for it were written without consulting the analyzer and CFE implementations, so I generally don't consider the specs to be authoritative. That said, I believe @leafpetersen has been working on writing up some specs for type inference, so he may know where to look for a spec of this particular behavior.
Consider the following program:
This program is accepted by the analyzer (from ) with no issues, but the CFE reports an inference failure:
The inference failure disappears if
main
callsf
directly (that is, the returned result isn't printed).Is this a bug in CFE type inference? In any case, it would be expected that the CFE and the analyzer agree on the outcome, so I've used the label 'type-bug'.