Skip to content

Implement missing warnings for generic methods #25200

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

Closed
6 tasks done
leafpetersen opened this issue Dec 9, 2015 · 15 comments
Closed
6 tasks done

Implement missing warnings for generic methods #25200

leafpetersen opened this issue Dec 9, 2015 · 15 comments
Labels
legacy-area-analyzer Use area-devexp instead. P1 A high priority bug; for example, a single project is unusable or has many test failures

Comments

@leafpetersen
Copy link
Member

leafpetersen commented Dec 9, 2015

Tracking bug for remaining missing warnings to be implemented in the analyzer for generic methods.

  • Arity matching on type instantiations
  • Check that type arguments satisfy the bounds
  • Check that implicit instantiations result in valid instantiations (strong mode only)
  • Check that overrides of a non-generic method with a generic one is valid (implemented but perhaps not correct, see comment below)
  • Check for unbound generic type variables
  • possible issue in _isFunctionSubtype, see comment (added by @jmesserly)

(edit by @jmesserly: Check that bounds (on both classes and generic methods) are not themselves generic function types -- moved to #25179)

@jmesserly
Copy link

I believe artiy warnings during instantiate has been fixed (ElementResolver._resolveGenericMethod). I don't think we check bounds, however.

@jmesserly
Copy link

Check that implicit instantiations result in valid instantiations (strong mode only)

Is this covered by StrongTypeSystemImpl.instantiateToBounds + our existing checks? I think we'll implicitly instantiate with the bounds, then later we'll do appropriate type checking against the parameters/return type as we normally would.

One case that is not covered is the "override a generic method with a non-generic method" case.

@jmesserly jmesserly self-assigned this Jan 13, 2016
@jmesserly
Copy link

Check for unbound generic type variables

I believe this check already exists in Analyzer:

abstract class Iter {
  List/*<S>*/ map(/*=S*/ f(x)) => <dynamic/*=T*/>[];
}
[warning] The name 'S' is not a type and cannot be used as a parameterized type 
[warning] Undefined class 'S'
[warning] The name 'T' is not a type and cannot be used as a parameterized type
3 warnings found.

@jmesserly
Copy link

Check that bounds (on both classes and generic methods) are not themselves generic function types

Just checking, I believe this can't be expressed right now, until we fix #25179? In other words you can't write:

typedef generic S F<S>(S x);
void foo<T extends F>(T t) {}

Something to think about when we implement 25179.

@leafpetersen
Copy link
Member Author

I think that's correct.

@jmesserly
Copy link

also can confirm overrides are being checked. _checkForAllInvalidOverrideErrorCodes handles checking for matching number of type params, as well as instantiation if generic overrides non-generic. (around the "// Handle generic function type parameters." comment)

EDIT: this code looks busted so I've unchecked it. In particular this looks wrong:

      if (overriddenFT.typeFormals.isEmpty) {
        overriddenFT = _typeSystem.instantiateToBounds(overriddenFT);
      }

If overriddenFT has no typeFormals, instantiateToBounds is a no-op... that doesn't look right. I think it meant to use overridingFT. Either way this is a bit worrying and needs further investigation.

@jmesserly
Copy link

Oh, just noticed: TYPE_ARGUMENT_NOT_MATCHING_BOUNDS is only a warning even in strong mode. I think we should bump it to an error? (easy to do, there's already an error for when it happens in constant evaluation)

@leafpetersen
Copy link
Member Author

Yes, I think that should be an error.

@jmesserly
Copy link

edit: this is fixed in f976407. My original message is below for context.


BTW @leafpetersen -- I was reading through the code in _isFunctionSubtype ... I think we may currently allow () -> void <: <T>() - > void. This may allow you to override a generic function with a non-generic one. Another subtype we may allow is (*) -> * <: <T>(T) -> T.

I suspect it's caused by this code:

    if (!f1.typeFormals.isEmpty) {
      if (f2.typeFormals.isEmpty) {
        f1 = instantiateToBounds(f1);
        return _isFunctionSubtypeOf(f1, f2);
      } else {
        return _isGenericFunctionSubtypeOf(f1, f2, fuzzyArrows: fuzzyArrows);
      }
    }

In particular, I don't see anything in the method that checks to ensure f2.typeFormals.isEmpty. I expected to find a case like:

} else if (f2.typeFormals.isNotEmpty) {
  // non-generic functions can't subtype generic ones.
  return false;
}

_isGenericFunctionSubtypeOf will always return return false if the parameter count is mismatched, but I don't think we'll get there.

@jmesserly jmesserly assigned jmesserly and unassigned jmesserly Feb 3, 2016
@jmesserly
Copy link

Another issue w/ universal types: the spec type system didn't know how to handle them. This led to breaking some invariants, like this assert in our DownCast.create:

    // toT <:_R fromT => to <: fromT
    // NB: classes with call methods are subtypes of function
    // types, but the function type is not assignable to the class
    assert(toT.isSubtypeOf(fromT) || fromT.isAssignableTo(toT));

Fix for both issues on the way.

jmesserly pushed a commit that referenced this issue Feb 11, 2016
A few other fixes:
* inference of generic function expressions now works. It was broken because they ignored type parameters.

* fixes subtype of generic functions in the "normal" type system. These types are only possible if generic methods support is enabled, which can be accessed via a flag. The generic methods DEP is designed to work with the normal Dart type system, too, so we try to keep these code paths working. ("normal" type equality for function types already handled this correctly.) This is also needed because strong mode delegates to normal type system for some cases, e.g. to pick warning vs error.

* also refactors structural comparison of two generic function types, along the lines of recent changes, so there is less code duplication. This was my original motivation, but reviewing the code led to the discovery of the other issues.

* refactors strong_test_helper. The existing way of matching up errors did not work with generic function expressions like: /*<T>*/(x) => x. Simplified it to just look for the right offset. This also exposed a bug in a test -- previously, it was possible to have an error that wasn't validated.

[email protected], [email protected]

Review URL: https://codereview.chromium.org/1678313002 .
@jmesserly jmesserly removed their assignment Feb 13, 2016
@munificent munificent mentioned this issue Feb 18, 2016
35 tasks
@munificent munificent added P1 A high priority bug; for example, a single project is unusable or has many test failures and removed Priority-Medium labels Feb 18, 2016
@bwilkerson
Copy link
Member

@leafpetersen Re:

Check that type arguments satisfy the bounds

Can you give me an example of a failing case? I tried to come up with one and failed (that is, analyzer correctly produces an error for this case), so I'm not sure what needs to be done for it:

class C<E extends num> {}
main() {
  C<String> c; // <-- ERROR: 'String' does not extend 'num'
}

@jmesserly
Copy link

I think it would happen for generic methods:

import 'dart:math';

Point/*<T>*/ f/*<T extends num>*/(num/*=T*/ x, num/*=T*/ y) {
  return new Point(x, y);
}

main() {
  print(f/*<String>*/('hello', 'world'));
}
$ dart test.dart
Point(hello, world)
$ dartanalyzer --strong test.dart
Analyzing [test.dart]...
No issues found
$ dart -c test.dart
Unhandled exception:
type 'String' is not a subtype of type 'num' of 'x' where
[...]

If I get rid of the <String> I get some warnings (I think DDC treats them as errors)

$ dartanalyzer --strong test.dart
Analyzing [test.dart]...
[warning] The argument type 'String' cannot be assigned to the parameter type 'num'. (/Users/jmesserly/tmp/test.dart, line 8, col 11)
[warning] The argument type 'String' cannot be assigned to the parameter type 'num'. (/Users/jmesserly/tmp/test.dart, line 8, col 20)
2 warnings found.

@bwilkerson
Copy link
Member

Of course! I'll look into producing the errors.

@bwilkerson
Copy link
Member

Some errors caught by https://codereview.chromium.org/1927103002/.

@jmesserly jmesserly self-assigned this Aug 9, 2016
@jmesserly jmesserly removed their assignment Aug 9, 2016
@jmesserly
Copy link

I think Brian fixed this. If we see any more issues we can file a new bug.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
legacy-area-analyzer Use area-devexp instead. P1 A high priority bug; for example, a single project is unusable or has many test failures
Projects
None yet
Development

No branches or pull requests

4 participants