|
| 1 | +/* It is tempting to relax the `isConcrete` test of match types for `AndType` |
| 2 | + * in the following way: |
| 3 | + * |
| 4 | + * (isConcrete(tp.tp1) || !tp.tp1.derivesFrom(targetClass)) && (... same for tp.tp2) |
| 5 | + * |
| 6 | + * but the test in this file shows that this would be unsound. |
| 7 | + * |
| 8 | + * If we did relax the rule, it would help usages of match types applied to the |
| 9 | + * singleton type of term pattern match scrutinees. For example: |
| 10 | + * |
| 11 | + * def foo[VS <: Tuple](x: VS): SelectH[VS] = x match |
| 12 | + * case x: (h *: t) => x.head |
| 13 | + * |
| 14 | + * The type of `x` in the branch body is `(VS & (h *: t))`. The result of |
| 15 | + * `x.head` is therefore a `Tuple.Head[VS & (h *: t)]`, which does not reduce |
| 16 | + * according to the current rules, but would reduce to `h` with the relaxed |
| 17 | + * rule. |
| 18 | + * |
| 19 | + * Note that the code can be fixed with an explicit type argument to `.head`: |
| 20 | + * |
| 21 | + * def foo[VS <: Tuple](x: VS): SelectH[VS] = x match |
| 22 | + * case x: (h *: t) => x.head[h *: t] |
| 23 | + * |
| 24 | + * So it *seems* like it would be fine to relax the rule, based on the insight |
| 25 | + * that `VS` in `Tuple.Head[VS & (h *: t)]` does not contribute anything to the |
| 26 | + * computed type capture in `Tuple.Head`. |
| 27 | + * |
| 28 | + * The test is this file demonstrates that relaxing the rule can cause |
| 29 | + * unsoundness. So don't do it. |
| 30 | + */ |
| 31 | + |
| 32 | +type SelectH[VS <: Tuple] = VS match |
| 33 | + case h *: ? => h |
| 34 | + |
| 35 | +// The original example found in the fingo/spata library |
| 36 | +object ExampleFromSpata: |
| 37 | + def foo[VS <: Tuple](x: VS): SelectH[VS] = x match |
| 38 | + case x: (h *: t) => x.head // error |
| 39 | + |
| 40 | + def bar[VS <: Tuple](x: VS): SelectH[VS] = x match |
| 41 | + case x: (h *: t) => x.head[h *: t] // ok |
| 42 | +end ExampleFromSpata |
| 43 | + |
| 44 | +trait Z { |
| 45 | + type Y <: Tuple |
| 46 | + type W <: Tuple |
| 47 | + def unpair: SelectH[Y & W] |
| 48 | +} |
| 49 | + |
| 50 | +class A extends Z { |
| 51 | + type Y = Tuple2[Any, Any] |
| 52 | + def unpair: SelectH[Y & W] = "" // error |
| 53 | + def any: Any = unpair |
| 54 | +} |
| 55 | + |
| 56 | +class B extends A { this: Z => |
| 57 | + type W = Tuple2[Int, Int] |
| 58 | + def int: Int = unpair |
| 59 | +} |
| 60 | + |
| 61 | +class C extends A { this: Z => |
| 62 | + type W = Tuple2[String, String] |
| 63 | + def string: String = unpair |
| 64 | +} |
| 65 | + |
| 66 | +object Main { |
| 67 | + def main(args: Array[String]): Unit = |
| 68 | + println((new B).int + 1) // would give ClassCastException |
| 69 | +} |
0 commit comments