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

ClassCastException during pattern matching #2403

Merged
merged 1 commit into from
Mar 31, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 26 additions & 5 deletions vavr/generator/Generator.scala
Original file line number Diff line number Diff line change
Expand Up @@ -960,7 +960,13 @@ def generateMainClasses(): Unit = {

@Override
public boolean isDefinedAt(T obj) {
return $Objects.equals(obj, prototype);
if (obj == prototype) {
return true;
} else if (prototype != null && prototype.getClass().isInstance(obj)) {
return $Objects.equals(obj, prototype);
} else {
return false;
}
}
};
}
Expand Down Expand Up @@ -1205,7 +1211,7 @@ def generateMainClasses(): Unit = {

@Override
public boolean isDefinedAt(T obj) {
return obj != null && type.isAssignableFrom(obj.getClass());
return type.isInstance(obj);
}
};
}
Expand Down Expand Up @@ -1241,12 +1247,12 @@ def generateMainClasses(): Unit = {
@SuppressWarnings("unchecked")
@Override
public boolean isDefinedAt(T obj) {
if (obj == null || !type.isAssignableFrom(obj.getClass())) {
return false;
} else {
if (type.isInstance(obj)) {
final $unapplyTupleType u = unapply.apply(obj);
return
${(1 to i).gen(j => s"((Pattern<U$j, ?>) p$j).isDefinedAt(u._$j)")(" &&\n")};
} else {
return false;
}
}
};
Expand Down Expand Up @@ -2606,6 +2612,8 @@ def generateTestClasses(): Unit = {
val API = im.getType("io.vavr.API")
val AssertionsExtensions = im.getType("io.vavr.AssertionsExtensions")
val ListType = im.getType("io.vavr.collection.List")
val StreamType = im.getType("io.vavr.collection.Stream")
val SeqType = im.getType("io.vavr.collection.Seq")
val MapType = im.getType("io.vavr.collection.Map")
val OptionType = im.getType("io.vavr.control.Option")
val FutureType = im.getType("io.vavr.concurrent.Future")
Expand Down Expand Up @@ -2971,6 +2979,19 @@ def generateTestClasses(): Unit = {
assertThat(Case($$(ignored -> false), 1).isDefinedAt(null)).isFalse();
}

@$test
public void shouldPassIssue2401() {
final $SeqType<String> empty = $StreamType.empty();
try {
final String matched = Match(empty).of(
Case($$($ListType.empty()), ignored -> "list")
);
fail("expected MatchError");
} catch (MatchError err) {
// ok!
}
}

// -- Match patterns

static class ClzMatch {}
Expand Down
58 changes: 32 additions & 26 deletions vavr/src-gen/main/java/io/vavr/API.java
Original file line number Diff line number Diff line change
Expand Up @@ -4988,7 +4988,13 @@ public T apply(T obj) {

@Override
public boolean isDefinedAt(T obj) {
return Objects.equals(obj, prototype);
if (obj == prototype) {
return true;
} else if (prototype != null && prototype.getClass().isInstance(obj)) {
return Objects.equals(obj, prototype);
} else {
return false;
}
}
};
}
Expand Down Expand Up @@ -5379,7 +5385,7 @@ public T apply(T obj) {

@Override
public boolean isDefinedAt(T obj) {
return obj != null && type.isAssignableFrom(obj.getClass());
return type.isInstance(obj);
}
};
}
Expand All @@ -5403,12 +5409,12 @@ public T1 apply(T obj) {
@SuppressWarnings("unchecked")
@Override
public boolean isDefinedAt(T obj) {
if (obj == null || !type.isAssignableFrom(obj.getClass())) {
return false;
} else {
if (type.isInstance(obj)) {
final Tuple1<U1> u = unapply.apply(obj);
return
((Pattern<U1, ?>) p1).isDefinedAt(u._1);
} else {
return false;
}
}
};
Expand All @@ -5433,13 +5439,13 @@ public Tuple2<T1, T2> apply(T obj) {
@SuppressWarnings("unchecked")
@Override
public boolean isDefinedAt(T obj) {
if (obj == null || !type.isAssignableFrom(obj.getClass())) {
return false;
} else {
if (type.isInstance(obj)) {
final Tuple2<U1, U2> u = unapply.apply(obj);
return
((Pattern<U1, ?>) p1).isDefinedAt(u._1) &&
((Pattern<U2, ?>) p2).isDefinedAt(u._2);
} else {
return false;
}
}
};
Expand All @@ -5464,14 +5470,14 @@ public Tuple3<T1, T2, T3> apply(T obj) {
@SuppressWarnings("unchecked")
@Override
public boolean isDefinedAt(T obj) {
if (obj == null || !type.isAssignableFrom(obj.getClass())) {
return false;
} else {
if (type.isInstance(obj)) {
final Tuple3<U1, U2, U3> u = unapply.apply(obj);
return
((Pattern<U1, ?>) p1).isDefinedAt(u._1) &&
((Pattern<U2, ?>) p2).isDefinedAt(u._2) &&
((Pattern<U3, ?>) p3).isDefinedAt(u._3);
} else {
return false;
}
}
};
Expand All @@ -5496,15 +5502,15 @@ public Tuple4<T1, T2, T3, T4> apply(T obj) {
@SuppressWarnings("unchecked")
@Override
public boolean isDefinedAt(T obj) {
if (obj == null || !type.isAssignableFrom(obj.getClass())) {
return false;
} else {
if (type.isInstance(obj)) {
final Tuple4<U1, U2, U3, U4> u = unapply.apply(obj);
return
((Pattern<U1, ?>) p1).isDefinedAt(u._1) &&
((Pattern<U2, ?>) p2).isDefinedAt(u._2) &&
((Pattern<U3, ?>) p3).isDefinedAt(u._3) &&
((Pattern<U4, ?>) p4).isDefinedAt(u._4);
} else {
return false;
}
}
};
Expand All @@ -5529,16 +5535,16 @@ public Tuple5<T1, T2, T3, T4, T5> apply(T obj) {
@SuppressWarnings("unchecked")
@Override
public boolean isDefinedAt(T obj) {
if (obj == null || !type.isAssignableFrom(obj.getClass())) {
return false;
} else {
if (type.isInstance(obj)) {
final Tuple5<U1, U2, U3, U4, U5> u = unapply.apply(obj);
return
((Pattern<U1, ?>) p1).isDefinedAt(u._1) &&
((Pattern<U2, ?>) p2).isDefinedAt(u._2) &&
((Pattern<U3, ?>) p3).isDefinedAt(u._3) &&
((Pattern<U4, ?>) p4).isDefinedAt(u._4) &&
((Pattern<U5, ?>) p5).isDefinedAt(u._5);
} else {
return false;
}
}
};
Expand All @@ -5563,9 +5569,7 @@ public Tuple6<T1, T2, T3, T4, T5, T6> apply(T obj) {
@SuppressWarnings("unchecked")
@Override
public boolean isDefinedAt(T obj) {
if (obj == null || !type.isAssignableFrom(obj.getClass())) {
return false;
} else {
if (type.isInstance(obj)) {
final Tuple6<U1, U2, U3, U4, U5, U6> u = unapply.apply(obj);
return
((Pattern<U1, ?>) p1).isDefinedAt(u._1) &&
Expand All @@ -5574,6 +5578,8 @@ public boolean isDefinedAt(T obj) {
((Pattern<U4, ?>) p4).isDefinedAt(u._4) &&
((Pattern<U5, ?>) p5).isDefinedAt(u._5) &&
((Pattern<U6, ?>) p6).isDefinedAt(u._6);
} else {
return false;
}
}
};
Expand All @@ -5598,9 +5604,7 @@ public Tuple7<T1, T2, T3, T4, T5, T6, T7> apply(T obj) {
@SuppressWarnings("unchecked")
@Override
public boolean isDefinedAt(T obj) {
if (obj == null || !type.isAssignableFrom(obj.getClass())) {
return false;
} else {
if (type.isInstance(obj)) {
final Tuple7<U1, U2, U3, U4, U5, U6, U7> u = unapply.apply(obj);
return
((Pattern<U1, ?>) p1).isDefinedAt(u._1) &&
Expand All @@ -5610,6 +5614,8 @@ public boolean isDefinedAt(T obj) {
((Pattern<U5, ?>) p5).isDefinedAt(u._5) &&
((Pattern<U6, ?>) p6).isDefinedAt(u._6) &&
((Pattern<U7, ?>) p7).isDefinedAt(u._7);
} else {
return false;
}
}
};
Expand All @@ -5634,9 +5640,7 @@ public Tuple8<T1, T2, T3, T4, T5, T6, T7, T8> apply(T obj) {
@SuppressWarnings("unchecked")
@Override
public boolean isDefinedAt(T obj) {
if (obj == null || !type.isAssignableFrom(obj.getClass())) {
return false;
} else {
if (type.isInstance(obj)) {
final Tuple8<U1, U2, U3, U4, U5, U6, U7, U8> u = unapply.apply(obj);
return
((Pattern<U1, ?>) p1).isDefinedAt(u._1) &&
Expand All @@ -5647,6 +5651,8 @@ public boolean isDefinedAt(T obj) {
((Pattern<U6, ?>) p6).isDefinedAt(u._6) &&
((Pattern<U7, ?>) p7).isDefinedAt(u._7) &&
((Pattern<U8, ?>) p8).isDefinedAt(u._8);
} else {
return false;
}
}
};
Expand Down
15 changes: 15 additions & 0 deletions vavr/src-gen/test/java/io/vavr/APITest.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@

import io.vavr.collection.List;
import io.vavr.collection.Map;
import io.vavr.collection.Seq;
import io.vavr.collection.Stream;
import io.vavr.concurrent.Future;
import io.vavr.control.Option;
import io.vavr.control.Try;
Expand Down Expand Up @@ -1406,6 +1408,19 @@ public void shouldReturnNoneWhenApplyingCaseGivenPredicateAndValue() {
assertThat(Case($(ignored -> false), 1).isDefinedAt(null)).isFalse();
}

@Test
public void shouldPassIssue2401() {
final Seq<String> empty = Stream.empty();
try {
final String matched = Match(empty).of(
Case($(List.empty()), ignored -> "list")
);
fail("expected MatchError");
} catch (MatchError err) {
// ok!
}
}

// -- Match patterns

static class ClzMatch {}
Expand Down