Skip to content

Commit 4d4c93d

Browse files
committed
Demonstrate more potential unsoundness with a CCE reproducer.
This additional test demonstrates that relaxing the `isConcrete` in a particular way around `AndType`s would also cause unsoundness. This justifies new failures in the Open Community Build.
1 parent bf1ea23 commit 4d4c93d

File tree

3 files changed

+111
-0
lines changed

3 files changed

+111
-0
lines changed

compiler/src/dotty/tools/dotc/core/TypeComparer.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3353,6 +3353,9 @@ class TrackingTypeComparer(initctx: Context) extends TypeComparer(initctx) {
33533353
* upper bound of an abstract type.
33543354
*
33553355
* See notably neg/wildcard-match.scala for examples of this.
3356+
*
3357+
* See neg/i13780.scala and neg/i13780-1.scala for ClassCastException
3358+
* reproducers if we disable this check.
33563359
*/
33573360

33583361
def followEverythingConcrete(tp: Type): Type =

tests/neg/i13780-1.check

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
-- [E007] Type Mismatch Error: tests/neg/i13780-1.scala:38:24 ----------------------------------------------------------
2+
38 | case x: (h *: t) => x.head // error
3+
| ^^^^^^
4+
| Found: Tuple.Head[VS & h *: t]
5+
| Required: h
6+
|
7+
| where: VS is a type in method foo with bounds <: Tuple
8+
| h is a type in method foo with bounds
9+
| t is a type in method foo with bounds <: Tuple
10+
|
11+
|
12+
| Note: a match type could not be fully reduced:
13+
|
14+
| trying to reduce Tuple.Head[VS & h *: t]
15+
| failed since selector VS & h *: t
16+
| does not uniquely determine parameter x in
17+
| case x *: _ => x
18+
| The computed bounds for the parameter are:
19+
| x <: h
20+
| Note that implicit conversions were not tried because the result of an implicit conversion
21+
| must be more specific than h
22+
|
23+
| longer explanation available when compiling with `-explain`
24+
-- [E007] Type Mismatch Error: tests/neg/i13780-1.scala:52:31 ----------------------------------------------------------
25+
52 | def unpair: SelectH[Y & W] = "" // error
26+
| ^^
27+
| Found: ("" : String)
28+
| Required: SelectH[A.this.Y & A.this.W]
29+
|
30+
| Note: a match type could not be fully reduced:
31+
|
32+
| trying to reduce SelectH[A.this.Y & A.this.W]
33+
| failed since selector A.this.Y & A.this.W
34+
| does not uniquely determine parameter h in
35+
| case h *: _ => h
36+
| The computed bounds for the parameter are:
37+
| h
38+
|
39+
| longer explanation available when compiling with `-explain`

tests/neg/i13780-1.scala

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
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

Comments
 (0)