Skip to content
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

"this is Foo ? this : null" => "Type check failed: ... is not of type Foo" #25260

Closed
Hixie opened this issue Dec 15, 2015 · 9 comments
Closed
Labels
area-language Dart language related items (some items might be better tracked at github.com/dart-lang/language).

Comments

@Hixie
Copy link
Contributor

Hixie commented Dec 15, 2015

Analyze:

abstract class A { }
abstract class B { A asA() => this is A ? this : null; }
void main() { }

Gives these two errors (or only the second when you turn off strong mode):

[error] Type check failed: this is A ? this : null (B) is not of type A (test.dart, line 2, col 31)
[warning] The return type 'B' is not a 'A', as defined by the method 'asA' (test.dart, line 2, col 31)

...but you can statically determine that this code is type-safe.

This kind of code lets you do stuff like:

class C extends B with A { } // asA() works
class D extends B implements A { } // asA() works
class E extends B { } // asA() safely returns null
@lrhn
Copy link
Member

lrhn commented Dec 15, 2015

This is a case where Dart's internal type promotion fails. The this is A test doesn't promote this to be A in the following branch, for two reasons.
One reason is that promotion only works for variables, and this is not a variable. That's fixable.
The other reason is that the current type of this is B, and A is not a subtype of B, and promotion only allows a variable to have one type at a time - it can't be both B and A, and neither subsumes the other, so it gives up on promoting the type, so even if this was a variable, it's static type is still B. The static type of the entire conditional operator is then the least upper bound of B and null (bottom), which is B, hence the warning that you are returning B where A is expected.

In Dart, you can't cast an A to a B without going through dynamic (or a warning) when there is no connection between A and B.
Solution:

A asA() { 
  var result = null;
  if (this is A) result = this;
  return result;
}

@sethladd
Copy link
Contributor

That's fixable.

What would that take? language fix?

@Hixie
Copy link
Contributor Author

Hixie commented Dec 15, 2015

@lrhn The solution I went with was A asA() => this is A ? this as dynamic : null;, but yeah, same idea.

It'd be interesting to allow promotion to tag variables with multiple disjoint types. (In general it would be great to have that in the language, too, i.e. union types. Presumably if we had union types it would become easier to have promotion promote to union types too!) I don't know how often we hit this exact case, but I can imagine it happening more in the future, certainly. It's a useful pattern.

Anyway, thanks for the explanation!

@lrhn
Copy link
Member

lrhn commented Dec 16, 2015

That's fixable.

What would that take? language fix?

Yes. We only do type propagation for variables. We could treat this as a variable in that respect, but we currently don't.

@lrhn
Copy link
Member

lrhn commented Dec 16, 2015

It'd be interesting to allow promotion to tag variables with multiple disjoint types. (In general it would be great to have that in the language, too, i.e. union types.

Technically (obXkcd) that would be intersection types, which is the dual of union types. A union type is a type that allows either of the types. Here we want to know that the actual type is all of the types - it's both A and B. In the perfect world, your programming language will have both.

@floitschG floitschG added the area-language Dart language related items (some items might be better tracked at github.com/dart-lang/language). label Dec 16, 2015
@askeksa-google
Copy link

The question in this issue seems to have been adequately answered.

@Hixie
Copy link
Contributor Author

Hixie commented Jun 22, 2018

@askeksa-google how so? is it fixed?

@askeksa-google
Copy link

askeksa-google commented Jun 25, 2018

I understand the discussion here as describing two reasons why the example does not work with the Dart type promotion. It is not clear whether this is a feature request, and if it is, whether it is about type promotion on this or allowing is checks on unrelated types, or both.

If you think it is still relevant to track either or both of these feature requests, please re-open the issue or file new ones. In any case the two independent issues should be clearly distinguished between.

@Hixie
Copy link
Contributor Author

Hixie commented Jun 25, 2018

The issue is still real. Whether it's relevant to track is up to you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-language Dart language related items (some items might be better tracked at github.com/dart-lang/language).
Projects
None yet
Development

No branches or pull requests

5 participants