Skip to content
This repository has been archived by the owner on Oct 22, 2024. It is now read-only.

Fix cast and retype implementations. #88

Merged
merged 4 commits into from
Mar 20, 2018

Conversation

matanlurey
Copy link
Contributor

Closes dart-lang/core#590.

I had to be a little creative for QueueList. The others seem OK enough.

/// all elements stored in the returned are actually instance of [S],
/// then the returned set can be used as a `MapKeySet<T>`.
static MapKeySet<T> castFrom<S, T>(MapKeySet<S> source) {
return new MapKeySet(source._baseMap.cast<T, dynamic>());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Too creative here. A MapKeySet is just as Set, so use Set.castFrom and don't introduce a new castFrom for this implementation of Set.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

/// If all accessed elements of [source] are actually instances of [T] and if
/// all elements stored in the returned are actually instance of [S],
/// then the returned set can be used as a `MapKeySet<T>`.
static MapValueSet<K2, V2> castFrom<K, V, K2, V2>(MapValueSet<K, V> source) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, this class is just an implementation of Set, so just use Set.castFrom.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@@ -84,10 +102,10 @@ class QueueList<E> extends Object with ListMixin<E> implements Queue<E> {
if (this is QueueList<T>) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While you are here, please rewrite this as:

QueueList<Object> self = this;
return self is QueueList<T> ? self : this.retype<T>();

It generates better code for dart2js to not have an explicit cast. By up-casting first. we make sure that a successful test promotes the variable.

Ditto for the cast methods below.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

CHANGELOG.md Outdated

* Added `QueueList.castFrom<S, T>` (similar to `List.castFrom`, `Queue.castFrom`).

* Added `MapKeySet.castFrom<S, T>` (similar to `Set.castFrom`).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for the last two castFrom's.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

final Map<E, dynamic> _baseMap;

MapKeySet(Map<E, dynamic> base) : _baseMap = base;

Iterable<E> get _base => _baseMap.keys;

Set<T> cast<T>() => _baseMap.keys.toSet().cast<T>();
MapKeySet<T> cast<T>() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just return Set<T> and use Set.castFrom.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@@ -513,7 +533,7 @@ class MapKeySet<E> extends _DelegatingIterableBase<E>
E lookup(Object element) =>
throw new UnsupportedError("MapKeySet doesn't support lookup().");

Set<T> retype<T>() => _baseMap.keys.toSet().retype<T>();
Set<T> retype<T>() => MapKeySet.castFrom<E, T>(this);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MapKeySet -> Set

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@@ -230,3 +248,17 @@ class QueueList<E> extends Object with ListMixin<E> implements Queue<E> {
_head = 0;
}
}

class _CastQueueList<S, T> extends QueueList<T> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is creative. Tests, or it doesn't work. :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was hoping to extend ThingImAddingRetypeTo as well 😄

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Take a look - tests are basic.

expect(
() => numQueue.add(1),
throwsCastError,
skip: isDart2 ? false : 'In checked mode (VM), CastError is not thrown',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The real reason is that cast only works in Dart 2 because it depends on the type parameter of a generic method. It's not VM specific in any way.

(In checked mode, the add(1) would throw because you are adding an int to a QueueList<String>, but it's more likely to be a TypeError than a CastError).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. Done!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like the type thrown is CastErrorImplementation, which is not a TypeError.

}
return QueueList.castFrom<E, T>(this);
return retype<T>();
}

QueueList<T> retype<T>() => QueueList.castFrom<E, T>(this);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TBH, after some more consideration, I'd remove QueueList.castFrom again.
It's a very specific implementation class, not a general interface that others might implement, so you don't need to make the wrapping available to to others.
The reason for Iterable.castFrom is that it can be used to implement Iterable.cast for other classes that implement Iterable, but nobody should implement QueueList, they should just implement Queue and List if they want a class that is both.

So, consider making castFrom private or just inline it here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Made it private. Thanks!

@@ -258,6 +258,25 @@ void main() {
expect(queue.cast<Pattern>(), same(queue));
}, skip: isDart2 ? false : 'Requires a Dart2 runtime');

test("cast does not throw on mutation when the type is valid", () {
var patternQueue = new QueueList<Pattern>()..addAll(['a', 'b']);
QueueList<Object> untypedQueue = patternQueue;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this line? Changing the static type has no effect on cast, you can just write patternQueue.cast<String>() below.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

var patternQueue = new QueueList<Pattern>()..addAll(['a', 'b']);
QueueList<Object> untypedQueue = patternQueue;
var stringQueue = untypedQueue.cast<String>();
stringQueue.addAll(['c', 'd']);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you forgot to do the cast, the test would still succeed (either because it infers <Pattern>['c', 'd'], or because List<String> is-a List<Pattern>).

At least add expect(stringQueue is List<String>, true);, which shows that the cast actually did something (and skip in non-dart2 mode because there cast actually doesn't do anything).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

var stringQueue = untypedQueue.cast<String>();
stringQueue.addAll(['c', 'd']);

expect(stringQueue, ['a', 'b', 'c', 'd']);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check that patternQueue was also modified.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Copy link
Contributor

@lrhn lrhn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM if you fix the type that you check against.

stringQueue.addAll(['c', 'd']);
expect(
stringQueue,
const isInstanceOf<QueueList<Pattern>>(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check for QueueList<String> here. That's the type you are casting to, and a type that patternQueue doesn't have, so it checks that something did happen.
(You could throw in a check that stringQueue and patternQueue are not identical, but it's not important after checking that stringQueue has a type that patternQueue doesn't).

This is the expect that needs the skip parameter, because in Dart 1 mode, the type check won't be true since cast returns patternQueue unmodified. The two tests below will still succeed when stringQueue and patternQueue are the same object.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Thanks @lrhn!

@matanlurey matanlurey merged commit 07bb52e into dart-archive:master Mar 20, 2018
@matanlurey matanlurey deleted the fix-cast-retype branch March 20, 2018 15:49
mosuem pushed a commit to dart-lang/core that referenced this pull request Oct 18, 2024
* Fix cast and retype implementations.

* Fix and add a few tests.

* Address feedbakc.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Development

Successfully merging this pull request may close these issues.

QueueList's cast and retype do not adhere to the List methods' documentation
4 participants