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 has trouble analyzing function-typed type parameters. #27392

Closed
lrhn opened this issue Sep 19, 2016 · 6 comments
Closed

Analyzer has trouble analyzing function-typed type parameters. #27392

lrhn opened this issue Sep 19, 2016 · 6 comments
Assignees
Labels
area-analyzer Use area-analyzer for Dart analyzer issues, including the analysis server and code completion. P2 A bug or feature request we're likely to work on type-bug Incorrect behavior (everything from a crash to more subtle misbehavior)

Comments

@lrhn
Copy link
Member

lrhn commented Sep 19, 2016

Tested in dartpad.
See https://dartpad.dartlang.org/f983c5dac60898576edcbd38cadfe2cd for runnable example.

In the code:

typedef void Action<T>(T x);

class C<T, U extends Action<T>> {
  T value;
  U action;
  C(this.value, [this.action]);
  void act() { action(value); }  // <- warning here
}

the analyzer claims that "action is not a method". If I change action(value) to (action)(value) the error changes to "cannot invoke a non-function". It doesn't realize that because U extends a function type, action is a function.

In the following code:

fnum(num x) { print("num: $x"); }
fobj(Object x) { print("obj: $x"); }

main() {
  C<num, Action<num>> c = new C<int, Action<Object>>(42, fobj);  // <- warning here
  c.act();
  c.action = fnum;
  c.value = 1.0;
  c.act();
}

strong mode has no problem, but non-strong-mode says that:

A value of type 'C<int, Action>' cannot be assigned to a variable of type 'C<num, Action>'

It's missing the type parameters on Action, and even then it's still wrong.

@lrhn lrhn added area-analyzer Use area-analyzer for Dart analyzer issues, including the analysis server and code completion. type-bug Incorrect behavior (everything from a crash to more subtle misbehavior) labels Sep 19, 2016
@bwilkerson bwilkerson added the P2 A bug or feature request we're likely to work on label Sep 19, 2016
@scheglov scheglov self-assigned this Sep 22, 2016
@scheglov
Copy link
Contributor

scheglov added a commit that referenced this issue Sep 22, 2016
@scheglov
Copy link
Contributor

First part is fixed in 65d2a2c.

@scheglov
Copy link
Contributor

The assignment warning looks valid to me.

In 19.4 Interface Types the specification says that An interface type T may be assigned to a type S, written T ⇐⇒ S, iff either T <: S or S <: T. Here int <: num but not Action<Object> <: Action<int>; and in opposite direction also Action<int> <: Action<Object> but not num <: int.

@lrhn
Copy link
Member Author

lrhn commented Sep 23, 2016

I think the warning is wrong (and not just because it omits the type parameters on Action, but that makes it more confusing).

The assignment to C<num, Action<num>> of C<int, Action<Object>> should be valid because int <: num and Action<Object> <: Action<num>, so C<int,Action<Object>> <: C<num, Action<num>> (covariance of generic interfaces).

The Action<Object> <: Action<num> <: Action<int> relations are not covariant because Action is not a generic interface, it's a generic function type declaration, so you have to look at the type structurally, Action<Object> == Object->void <: num->void == Action<num> from the rules of function subtyping.

(Also, Action<Object> <: Action<int> so the class instantiation is valid wrt. the bound on U).

There should be a runtime error on the c.action = fnum; assignment in strong mode, but that's not something the analyzer is required to notice.

@lrhn lrhn reopened this Sep 23, 2016
@scheglov
Copy link
Contributor

I'm looking at 19.5 Function Types.

A function type (T1, . . . Tk, [Tk+1 . . . , Tn+m]) → T is more specific than
the function type (S1, . . . , Sk+j , [Sk+j+1 . . . , Sn]) → S, if all of the following
conditions are met:

1. Either
• S is void, Or
• T << S.
2. ∀i ∈ 1..n, Ti << Si.

So, Action<Object> << Action<num> is false, because Object << num is false.

@lrhn
Copy link
Member Author

lrhn commented Sep 24, 2016

Ack, you are correct. The subtyping relation on interfaces is based on the "more specific" relation of the type parameters, not the subtype relation. I had missed that.
The "more specific" relation of functions is covariant in the parameter type, as you quote, and the subtype relation would have been bi-variant.

@lrhn lrhn closed this as completed Sep 24, 2016
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. P2 A bug or feature request we're likely to work on type-bug Incorrect behavior (everything from a crash to more subtle misbehavior)
Projects
None yet
Development

No branches or pull requests

3 participants