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

Analyzer is satisfied as soon as one sub-expression of a ternary-if yields the right type #22750

Closed
DartBot opened this issue Mar 10, 2015 · 5 comments
Assignees
Labels
area-analyzer Use area-analyzer for Dart analyzer issues, including the analysis server and code completion. closed-as-intended Closed as the reported issue is expected behavior

Comments

@DartBot
Copy link

DartBot commented Mar 10, 2015

This issue was originally filed by googlegroups...@kaioa.com


Example #­1:

    double rd1(bool a) => a ? 1 : 0;
    double rd2(bool a) => a ? 1.0 : 0;
    double rd3(bool a) => a ? 0 : 0.0;

"rd1" is correctly flagged. "rd2" and "rd3", however, are not. In checked mode, it bombs when you hit the wrong branch.

Example #­2:

    var a = true;
    int b = a ? 1.2 : 3;
    int c = a ? 1 : 3.4;
    int d = a ? 1.2 : 3.4;

Only the last line is flagged.

The analyzer should ensure that both sub-expressions of a ternary-if yield the specified type.

@floitschG
Copy link
Contributor

Thanks.
I could reproduce on bleeding edge.


Added Area-Analyzer, Triaged labels.

@bwilkerson
Copy link
Member

While I agree that this is confusing, I believe that it is the behavior required by the Dart Language Specification.

You should get a warning if the static type of the initialization expression is not assignment compatible with the declared type. The static type of a ternary expression is the least upper bound of the types of the second (then) and third (else) operands. The least upper bound of 'int' and 'double' is 'num'. But in Dart, one type T1 is assignable to another T2 if either T1 is a subtype of T2 or if T2 is a subtype of T1. That means that 'num' is assignable to both 'int' and 'double'.

The reason you get the warning you expect in the first case (rd1) is because the type of the expression is 'int' and 'int' is not assignable to 'double'. The inverse is true for the second case (d) because the type of the expression is 'double' and 'double' is not assignable to 'int'.


cc @gbracha.
cc @kasperl.
Set owner to @bwilkerson.
Removed Priority-Unassigned label.
Added Priority-Medium, AsDesigned labels.

@DartBot
Copy link
Author

DartBot commented Mar 11, 2015

This comment was originally written by googlegroups...@kaioa.com


The analyzer thinks this is okay:

    main(){
      var a = true;
      int b = a ? 1.2 : 3;
    }

Checked mode disagrees (and so do I):

    Breaking on exception: object of type _TypeError:
    type 'double' is not a subtype of type 'int' of 'b'.

If an "if" is used instead:

    main(){
      var a = true;
      int b;
      if (a) {
        b = 1.2;
      } else {
        b = 3;
      }
    }

The analyzer will spot the mistake:

    A value of type 'double' cannot be assigned to a variable of type 'int'

I really think the analyzer should flag something like "int b = a ? 1.2 : 3", because it's obviously wrong.

When I ported some code yesterday, I had a handful of these mistakes. I had to "abuse" checked mode, because the analyzer failed to spot them.

Also note that the types don't have to be compatible. If one matches, the other one is completely ignored. The analyzer also thinks that this one is okay:

    main(){
      var a = true;
      int b = a ? 'not an int' : 3;
    }

@bwilkerson
Copy link
Member

The reason it doesn't produce a warning is because the least upper bound of 'String' and 'int' is 'Object', and 'Object' is assignable to 'int'.

The warnings that are specified are not intended to cover all possible runtime errors, they are intended to catch cases that can never be right. There is intentionally no warning in these cases because the code might be written in such a way that it could work without error. But, yes, that does mean that sometimes helpful warnings are not produced, such as in the cases you've given or even in the worse case of:

f(bool b) {
  int i = b ? '0' : 1.0;
}

which can never be valid in checked mode (although it will execute fine in production mode).

@DartBot
Copy link
Author

DartBot commented Mar 11, 2015

This comment was originally written by googlegroups...@kaioa.com


How is this any different from if-else? If the wrong branch isn't executed it will also work "without error".

Is there any use case where this behavior is desirable?

If I put the type annotation there, I do apparently intend/assume that both sub-expressions return the specified type.

Not flagging this means that you need two unit tests for each "?:". You need to execute both branches in checked mode to identify the problem.

This isn't desirable nor reasonable.

For what it's worth, TypeScript does this right:

    var a:boolean = true;
    var b:number = a ? 'not a number' : 4;

The second line is flagged with:

    Type 'string | number' is not assignable to type 'number'.
    Type 'string' is not assignable to type 'number'.

@DartBot DartBot added Type-Defect area-analyzer Use area-analyzer for Dart analyzer issues, including the analysis server and code completion. closed-as-intended Closed as the reported issue is expected behavior labels Mar 11, 2015
This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-analyzer Use area-analyzer for Dart analyzer issues, including the analysis server and code completion. closed-as-intended Closed as the reported issue is expected behavior
Projects
None yet
Development

No branches or pull requests

3 participants