-
Notifications
You must be signed in to change notification settings - Fork 1.5k
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
Unexpected generic typing when using ternary operator and casting to base type #53663
Comments
It is all working as intended, but not quite as desired. ;-) You might wish to take a look at this topic: https://github.com/dart-lang/language/issues?q=is%3Aopen+is%3Aissue+label%3Aleast-upper-bound, where it is discussed and explained in great detail what is going on in situations like this one. In particular, you could take a look at this issue, and perhaps vote for it: dart-lang/language#1618. First, the underlying problem: An expression needs to have a static type, and in particular an expression of the form In order to make this sound we'll have to choose a type which is a supertype of the type of (OK, it's not a "least" upper bound, because we can't in general find the least one, but it is a "rather low" upper bound, so we call it the standard upper bound.) The proposal in dart-lang/language#1618 says that we should type check the So if we adopt dart-lang/language#1648 then your example just works, done! However, right now you can do what you already mentioned: // ignore_for_file: unused_local_variable
abstract class Base<T> {
factory Base(int val) => BaseImpl1(val == 0) as Base<T>;
T doSomething(T input);
}
class BaseImpl1 implements Base<bool> {
BaseImpl1(this.someVal);
bool someVal;
@override
bool doSomething(bool input) => someVal = input;
}
class BaseImpl2 implements Base<int> {
BaseImpl2();
@override
int doSomething(int input) => 0;
}
var someCondition = true;
void main() {
Base obj = someCondition
? (BaseImpl1(false) as dynamic) as Base<Object>
: BaseImpl2(); // this throws an error
late Base obj2;
if (someCondition) {
obj2 = BaseImpl1(false); // doesn't throw
} else {
obj2 = BaseImpl2(); // doesn't throw
}
} (I disabled a lint and moved the declaration of The point is that you stop asking for the standard upper bound of In short, we'd like to have a better standard upper bound computation and also to introduce the context as a source of information, but right now you should probably just be prepared to give the standard upper bound algorithm a bit of help now and then. ;-) |
Thanks for the detailed response as always @eernstg. |
Dart SDK version: 3.1.2 (stable) on "windows_x64"
I think I understand why the contrived example below (but I ran into it with my actual project) is throwing an error, but I'm having trouble understanding why the analyzer cannot determine the correct type. I've also run into some general oddities regarding this case that I'd love some clarification on.
My understanding is that since
BaseImpl1
implements the specific generic<bool>
andBaseImpl2
implements the specific generic<int>
, the two ternary conditions cannot be implicitly cast to return the genericBase<dynamic>
like I want. What doesn't make sense is why the same behavior doesn't happen with theif
statement (it doesn't throw). I would expect the ternary to be able to resolve the types the same way as the conditional flow.So... I guess this is happening because Dart doesn't allow casting generics (like
List<int>
->List<bool>
isn't allowed) but clearly there is some support since it is fine for the conditional statement. Is something else going on here that I'm not aware of? Is it okay with the conditional because it is casting todynamic
? but if so, why is it not the same with the ternary operator?Additionally, it is odd to me that replacing the ternary operator with:
no longer throws. This doesn't make sense to me because while I've explicitly cast
BaseImpl2()
, I haven't castBaseImpl1(false)
so I'd expect the analyzer to still complain about assigningObject
toBase
since it is trying to resolve typesBase<bool>
andBase<dynamic>
.Another (probably separate?) oddity I don't understand is that the analyzer doesn't show any problems with the following:
But if the
as
statement is wrapped in parenthesis, it says it is an unnecessary cast (which it isn't according to the analyzer, since it throws without it).And lastly, as a side note:
While this isn't critical since I can cast the objects myself in this case, I found I could solve the issue by changing implementation classes to implement the base class with the generic type
dynamic
and using covariant to allow override of the base class functionality. In my case where I ran into this, this actually doesn't work for me since I am actually calling a static function that returns a more specific implementation class and I don't want to lose the typing information :/ (so I just useas
to cast it as described above).The text was updated successfully, but these errors were encountered: