Skip to content

Wrong inferred argument bounds violation #35518

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
passsy opened this issue Dec 29, 2018 · 9 comments
Closed

Wrong inferred argument bounds violation #35518

passsy opened this issue Dec 29, 2018 · 9 comments
Assignees
Labels
legacy-area-front-end Legacy: Use area-dart-model instead.

Comments

@passsy
Copy link

passsy commented Dec 29, 2018

I run into a generic bounds bug. I tried to create a minimal sample but wasn't able to. It seems it has to do with the number of levels of inheritance. That's why I stripped all irrelevant code from my project and pushed it. Minimal sample project

Here's a gist of what I'm doing:

abstract class KIterable<T> implements KIterableExtension<T> {}
abstract class KIterableExtension<T> { 
  C toCollection<C extends KMutableCollection<T>>(C destination);
}
void main() {
  test("copy to other list", () {
    KMutableList<String> other = DartMutableList<String>();
    KList<String> list = DartList(["1", "2", "3"]);

    // this line errors
    final result = list.toCollection(other);
  });
}
  Unable to spawn isolate: test/collection/iterable_extensions_test.dart:9:25: 
Error: Inferred type argument '#lib1::DartMutableList<dart.core::String>' violates the
corresponding type variable bound of 'DartList<dart.core::String>::toCollection'.
  Try specifying type arguments explicitly so that they conform to the bounds.
      final result = list.toCollection(other);
                          ^

I don't get what's the violation here.

Also the kollection package contains similar methods, which have similar errors. i.e. filterIndexedTo

  final beatles = setOf(["John", "Paul", "George", "Ringo"]);
  final holder1 = mutableListOf<String>();
  beatles.filterIndexedTo(holder1, (i, it) => true);

Which errors with:

example/main.dart:72:11: Error: Inferred type argument 'KMutableList<String>' doesn't conform to the bound 'KMutableCollection<T>' of the type variable 'C' on 'KSet<String>.filterIndexedTo'.
 - 'KMutableList' is from 'package:dart_kollection/src/k_list_mutable.dart' ('lib/src/k_list_mutable.dart').
 - 'KMutableCollection' is from 'package:dart_kollection/src/k_collection_mutable.dart' ('lib/src/k_collection_mutable.dart').
 - 'KSet' is from 'package:dart_kollection/src/k_set.dart' ('lib/src/k_set.dart').
Try specifying type arguments explicitly so that they conform to the bounds.
  beatles.filterIndexedTo(holder1, (i, it) => true);
          ^

Signature:

abstract class KIterableExtension<T> { 
  C filterIndexedTo<C extends KMutableCollection<T>>(
      C destination, bool Function(int index, T) predicate);
}

Here' a workaroud I found:

// non type-safe signature, works
abstract class KIterableExtension<T> { 
  C filterIndexedTo<X, C extends KMutableCollection<X>>(
      C destination, bool Function(int index, T) predicate);
}

It seems like the type T of the owning class doesn't get passed into the extends Something<T> part leaving the type as extends Something<dynamic>.


Dart VM version: 2.1.0 (Tue Nov 13 18:22:02 2018 +0100) on "macos_x64"

@passsy
Copy link
Author

passsy commented Dec 30, 2018

The weirdness goes on. The function filterIndexedTo is not completely broken. It is used by filterIndexed (in the same class) and works just fine.

  // works
  @override
  KList<T> filterIndexed(bool Function(int index, T) predicate) {
    final list = filterIndexedTo(mutableListOf<T>(), predicate);
    return list;
  }

One might assume this method could be simlified, but there comes the weirdness:

  // throws
  @override
  KList<T> filterIndexed(bool Function(int index, T) predicate) {
    return filterIndexedTo(mutableListOf<T>(), predicate);
  }

Instantly returning filterIndexedTo throws

package:dart_kollection/src/extension/iterable_extension_mixin.dart 284:28  _DartMutableSet&Object&KIterableExtensionsMixin.filterIndexed
test/collection/iterable_extensions_test.dart 491:20                        testIterable.<fn>.<fn>

type 'DartMutableList<String>' is not a subtype of type 'Null'

passsy added a commit to passsy/kt.dart that referenced this issue Dec 30, 2018
Bug dart-lang/sdk#35518 prevents calls from external classes to those methods to not pass the generic bound type correctly resulting in a wrong inferred argument bounds violation
@passsy
Copy link
Author

passsy commented Jan 2, 2019

Although this error exists, I made progress by using dynamic as type. It's even better because it allows me to pass contravariant as parameters.

final KIterable<int> iterable = listOf([4, 25, -12, 10]);
final result = mutableListOf<num>(); // contravariant!
final filtered = iterable.filterIndexedTo(result, (i, it) => it < 10);
expect(identical(result, filtered), isTrue);
expect(result, listOf([4, -12]));

filterIndexedTo is now not statically typed and can only be checked at runtime. It makes the API better in terms of usage. The downsides: Not statically typed, requires allocation of an object to check the type.

  C filterIndexedTo<C extends KMutableCollection<dynamic>>(
      C destination, bool Function(int index, T) predicate) {
    assert(() {
      if (destination == null) throw ArgumentError("destination can't be null");
      if (predicate == null) throw ArgumentError("predicate can't be null");
      if (mutableListOf<T>() is! C)
        throw ArgumentError(
            "filterIndexedTo destination has wrong type parameters."
            "\nExpected: KMutableCollection<$T>, Actual: ${destination.runtimeType}"
            "\ndestination (${destination.runtimeType}) entries aren't subtype of "
            "map ($runtimeType) entries. Entries can't be copied to destination."
            "\n\n$kBug35518GenericTypeError");
      return true;
    }());
    // impl ...
  }

Kotlin uses the in and out keyword for such cases, allowing contravariant and covariants (https://kotlinlang.org/docs/reference/generics.html). Is something similar possible in Dart?

@passsy
Copy link
Author

passsy commented Jan 2, 2019

Most likely related to #35068

@vsmenon
Copy link
Member

vsmenon commented Jan 4, 2019

@leafpetersen @lrhn @eernstg - can one of you take a look? Is this a CFE bug or working as intended?

@leafpetersen
Copy link
Member

This looks like a CFE bug to me. Minimal repro which (correctly) passes the analyzer but fails with a static error in the CFE enclosed below. I think this is related to mixins, since hand inlining toCollection resolves the issue.

cc @kmillikin

abstract class KMutableCollection<T> {}

abstract class KIterableExtensionMixin<T> { 
  C toCollection<C extends KMutableCollection<T>>(C destination) => null;
}

class DartMutableList<T>
    with KIterableExtensionMixin<T> implements KMutableCollection<T> {}
  
void main() {
  var other = DartMutableList<String>();
  var list = DartMutableList<String>();

  // Error in CFE both when explicitly and implicitly instantiated
  final r1 = list.toCollection<DartMutableList<String>>(other);
  final r2 = list.toCollection(other);
}

@leafpetersen leafpetersen added the legacy-area-front-end Legacy: Use area-dart-model instead. label Jan 9, 2019
@passsy
Copy link
Author

passsy commented Mar 24, 2021

Does still exist with dart 2.12.2. Here the nullsafe example

abstract class KMutableCollection<T> {}

mixin KIterableExtensionMixin<T> { 
  C toCollection<C extends KMutableCollection<T>>(C destination) {
    return destination;
  }
}

class DartMutableList<T>
    with KIterableExtensionMixin<T> implements KMutableCollection<T> {}
  
void main() {
  var other = DartMutableList<String>();
  var list = DartMutableList<String>();

  // Error in CFE both when explicitly and implicitly instantiated
  final r1 = list.toCollection<DartMutableList<String>>(other);
  final r2 = list.toCollection(other);
}

Error

lib/main.dart:17:19:
Error: Type argument 'DartMutableList<String>' doesn't conform to the bound 'KMutableCollection<T>' of the type variable 'C' on 'DartMutableList<String>.toCollection'.
 - 'DartMutableList' is from 'package:dartpad_sample/main.dart' ('lib/main.dart').
 - 'KMutableCollection' is from 'package:dartpad_sample/main.dart' ('lib/main.dart').
  final r1 = list.toCollection<DartMutableList<String>>(other);
                  ^
lib/main.dart:18:19:
Error: Inferred type argument 'DartMutableList<String>' doesn't conform to the bound 'KMutableCollection<T>' of the type variable 'C' on 'DartMutableList<String>.toCollection'.
 - 'DartMutableList' is from 'package:dartpad_sample/main.dart' ('lib/main.dart').
 - 'KMutableCollection' is from 'package:dartpad_sample/main.dart' ('lib/main.dart').
  final r2 = list.toCollection(other);
                  ^
Error: Compilation failed.

@leafpetersen
Copy link
Member

cc @johnniwinther

@chloestefantsova
Copy link
Contributor

This appears to be fixed now. Please reopen if needed.

@passsy
Copy link
Author

passsy commented Apr 28, 2022

Confirmed, this bug is fixed in Dart SDK 2.17.0-266.5.beta

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
legacy-area-front-end Legacy: Use area-dart-model instead.
Projects
None yet
Development

No branches or pull requests

4 participants