diff --git a/.github/workflows/scaladoc.yaml b/.github/workflows/scaladoc.yaml index 8429066fc472..366d9bee5f05 100644 --- a/.github/workflows/scaladoc.yaml +++ b/.github/workflows/scaladoc.yaml @@ -75,13 +75,14 @@ jobs: stdlib-sourcelinks-test: runs-on: ubuntu-latest - if: "( github.event_name == 'pull_request' + # if false - disable flaky test + if: "false && (( github.event_name == 'pull_request' && !contains(github.event.pull_request.body, '[skip ci]') && !contains(github.event.pull_request.body, '[skip docs]') ) || contains(github.event.ref, 'scaladoc') || contains(github.event.ref, 'scala3doc') - || contains(github.event.ref, 'master')" + || contains(github.event.ref, 'master'))" steps: - name: Git Checkout diff --git a/compiler/src/dotty/tools/dotc/core/MatchTypeTrace.scala b/compiler/src/dotty/tools/dotc/core/MatchTypeTrace.scala index f711f8d1ab67..64a91dfb17e6 100644 --- a/compiler/src/dotty/tools/dotc/core/MatchTypeTrace.scala +++ b/compiler/src/dotty/tools/dotc/core/MatchTypeTrace.scala @@ -102,12 +102,22 @@ object MatchTypeTrace: i""" failed since selector $scrut | is uninhabited (there are no values of that type).""" case Stuck(scrut, stuckCase, otherCases) => - i""" failed since selector $scrut - | does not match ${caseText(stuckCase)} - | and cannot be shown to be disjoint from it either. - | Therefore, reduction cannot advance to the remaining case${if otherCases.length == 1 then "" else "s"} - | - | ${casesText(otherCases)}""" + val msg = + i""" failed since selector $scrut + | does not match ${caseText(stuckCase)} + | and cannot be shown to be disjoint from it either.""" + if otherCases.length == 0 then msg + else + val s = if otherCases.length == 1 then "" else "s" + i"""$msg + | Therefore, reduction cannot advance to the remaining case$s + | + | ${casesText(otherCases)}""" -end MatchTypeTrace + def noMatchesText(scrut: Type, cases: List[Type])(using Context): String = + i"""failed since selector $scrut + |matches none of the cases + | + | ${casesText(cases)}""" +end MatchTypeTrace diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index 45d02ca9538e..479f6c8e07c8 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -2542,7 +2542,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling x && { t match { case tp: TypeRef if tp.symbol.isAbstractOrParamType => false - case _: SkolemType | _: TypeVar => false + case _: SkolemType | _: TypeVar | _: TypeParamRef => false case _ => foldOver(x, t) } } @@ -2585,17 +2585,17 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling def covariantDisjoint(tp1: Type, tp2: Type, tparam: TypeParamInfo): Boolean = provablyDisjoint(tp1, tp2) && typeparamCorrespondsToField(tycon1, tparam) - // In the invariant case, we used a weaker version of disjointness: - // we consider types not equal with respect to =:= to be disjoint + // In the invariant case, we also use a stronger notion of disjointness: + // we consider fully instantiated types not equal wrt =:= to be disjoint // (under any context). This is fine because it matches the runtime // semantics of pattern matching. To implement a pattern such as // `case Inv[T] => ...`, one needs a type tag for `T` and the compiler // is used at runtime to check it the scrutinee's type is =:= to `T`. - // Note that this is currently a theoretical concern since we Dotty + // Note that this is currently a theoretical concern since Dotty // doesn't have type tags, meaning that users cannot write patterns // that do type tests on higher kinded types. def invariantDisjoint(tp1: Type, tp2: Type, tparam: TypeParamInfo): Boolean = - covariantDisjoint(tp1, tp2, tparam) || + provablyDisjoint(tp1, tp2) || !isSameType(tp1, tp2) && fullyInstantiated(tp1) && // We can only trust a "no" from `isSameType` when fullyInstantiated(tp2) // both `tp1` and `tp2` are fully instantiated. @@ -2924,14 +2924,13 @@ class TrackingTypeComparer(initctx: Context) extends TypeComparer(initctx) { case None => recur(remaining1) case Some(NoType) => - if remaining1.isEmpty then MatchTypeTrace.noMatches(scrut, cases) - else MatchTypeTrace.stuck(scrut, cas, remaining1) + MatchTypeTrace.stuck(scrut, cas, remaining1) NoType case Some(tp) => tp case Nil => - MatchTypeTrace.noMatches(scrut, cases) - NoType + val casesText = MatchTypeTrace.noMatchesText(scrut, cases) + throw new TypeError(s"Match type reduction $casesText") inFrozenConstraint { // Empty types break the basic assumption that if a scrutinee and a diff --git a/compiler/src/dotty/tools/dotc/core/TypeOps.scala b/compiler/src/dotty/tools/dotc/core/TypeOps.scala index 75a5816c3164..565ed8cf2c58 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeOps.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeOps.scala @@ -137,6 +137,8 @@ object TypeOps: tp2 case tp1 => tp1 } + case defn.MatchCase(pat, body) => + defn.MatchCase(simplify(pat, theMap), body) case tp: AppliedType => tp.tycon match case tycon: TypeRef if tycon.info.isInstanceOf[MatchAlias] => @@ -485,7 +487,7 @@ object TypeOps: tp else tryWiden(tp, tp.prefix).orElse { if (tp.isTerm && variance > 0 && !pre.isSingleton) - apply(tp.info.widenExpr) + apply(tp.info.widenExpr) else if (upper(pre).member(tp.name).exists) super.derivedSelect(tp, pre) else diff --git a/compiler/src/dotty/tools/dotc/typer/VarianceChecker.scala b/compiler/src/dotty/tools/dotc/typer/VarianceChecker.scala index f0ad18ee18c5..8c796cfb6fa9 100644 --- a/compiler/src/dotty/tools/dotc/typer/VarianceChecker.scala +++ b/compiler/src/dotty/tools/dotc/typer/VarianceChecker.scala @@ -133,7 +133,7 @@ class VarianceChecker(using Context) { def apply(status: Option[VarianceError], tp: Type): Option[VarianceError] = trace(s"variance checking $tp of $base at $variance", variances) { try if (status.isDefined) status - else tp.normalized match { + else tp match { case tp: TypeRef => val sym = tp.symbol if (sym.isOneOf(VarianceFlags) && base.isContainedIn(sym.owner)) checkVarianceOfSymbol(sym) diff --git a/tests/neg-macros/toexproftuple.scala b/tests/neg-macros/toexproftuple.scala index f33bfd5f6dfb..20ae2f08ff8d 100644 --- a/tests/neg-macros/toexproftuple.scala +++ b/tests/neg-macros/toexproftuple.scala @@ -1,10 +1,63 @@ -import scala.quoted._, scala.deriving.* +import scala.quoted._, scala.deriving.* // error +// ^ +// Match type reduction failed since selector ((2 : Int), quoted.Expr[(3 : Int)]) +// matches none of the cases +// +// case quoted.Expr[x] *: t => x *: scala.Tuple.InverseMap[t, quoted.Expr] +// case EmptyTuple => EmptyTuple + +inline def mcr: Any = ${mcrImpl} // error +// ^ +// Match type reduction failed since selector ((2 : Int), quoted.Expr[(3 : Int)]) +// matches none of the cases +// +// case quoted.Expr[x] *: t => x *: scala.Tuple.InverseMap[t, quoted.Expr] +// case EmptyTuple => EmptyTuple + +def mcrImpl(using ctx: Quotes): Expr[Any] = { // error // error + //^ + // Match type reduction failed since selector ((2 : Int), quoted.Expr[(3 : Int)]) + // matches none of the cases + // + // case quoted.Expr[x] *: t => x *: scala.Tuple.InverseMap[t, quoted.Expr] + // case EmptyTuple => EmptyTuple + + // ^ + // Match type reduction failed since selector ((2 : Int), quoted.Expr[(3 : Int)]) + // matches none of the cases + // + // case quoted.Expr[x] *: t => x *: scala.Tuple.InverseMap[t, quoted.Expr] + // case EmptyTuple => EmptyTuple -inline def mcr: Any = ${mcrImpl} -def mcrImpl(using ctx: Quotes): Expr[Any] = { val tpl: (Expr[1], Expr[2], Expr[3]) = ('{1}, '{2}, '{3}) '{val res: (1, 3, 3) = ${Expr.ofTuple(tpl)}; res} // error + // ^^^^^^^^^^^^^^^^^ + // Found: quoted.Expr[(1 : Int) *: (2 : Int) *: (3 : Int) *: EmptyTuple] + // Required: quoted.Expr[((1 : Int), (3 : Int), (3 : Int))] val tpl2: (Expr[1], 2, Expr[3]) = ('{1}, 2, '{3}) - '{val res = ${Expr.ofTuple(tpl2)}; res} // error + '{val res = ${Expr.ofTuple(tpl2)}; res} // error // error // error // error + // ^ + // Cannot prove that (quoted.Expr[(1 : Int)], (2 : Int), quoted.Expr[(3 : Int)]) =:= scala.Tuple.Map[ + // scala.Tuple.InverseMap[ + // (quoted.Expr[(1 : Int)], (2 : Int), quoted.Expr[(3 : Int)]) + // , quoted.Expr] + // , quoted.Expr]. + + // ^ + // Match type reduction failed since selector ((2 : Int), quoted.Expr[(3 : Int)]) + // matches none of the cases + // + // case quoted.Expr[x] *: t => x *: scala.Tuple.InverseMap[t, quoted.Expr] + // case EmptyTuple => EmptyTuple + + // ^ + // Cyclic reference involving val res + + // ^ + // Match type reduction failed since selector ((2 : Int), quoted.Expr[(3 : Int)]) + // matches none of the cases + // + // case quoted.Expr[x] *: t => x *: scala.Tuple.InverseMap[t, quoted.Expr] + // case EmptyTuple => EmptyTuple } diff --git a/tests/neg/10747.scala b/tests/neg/10747.scala new file mode 100644 index 000000000000..a299f2a6590c --- /dev/null +++ b/tests/neg/10747.scala @@ -0,0 +1,5 @@ +type Foo[A] = A match { + case Int => String +} + +type B = Foo[Boolean] // error diff --git a/tests/neg/12974.scala b/tests/neg/12974.scala new file mode 100644 index 000000000000..90edcc916471 --- /dev/null +++ b/tests/neg/12974.scala @@ -0,0 +1,35 @@ +package example + +object RecMap { + + object Record { + // use this scope to bound who can see inside the opaque type + opaque type Rec[A <: Tuple] = Map[String, Any] + + object Rec { + type HasKey[A <: Tuple, K] = + A match + case (K, t) *: _ => t + case _ *: t => HasKey[t, K] + + val empty: Rec[EmptyTuple] = Map.empty + + extension [A <: Tuple](toMap: Rec[A]) + def fetch[K <: String & Singleton](key: K): HasKey[A, K] = + toMap(key).asInstanceOf[HasKey[A, K]] + } + } + + def main(args: Array[String]) = + import Record._ + + val foo: Any = Rec.empty.fetch("foo") // error + // ^ + // Match type reduction failed since selector EmptyTuple.type + // matches none of the cases + // + // case (("foo" : String), t) *: _ => t + // case _ *: t => example.RecMap.Record.Rec.HasKey[t, ("foo" : String)] + + end main +} diff --git a/tests/neg/6697.check b/tests/neg/6697.check index b413e75e0d60..b2aeaa94d859 100644 --- a/tests/neg/6697.check +++ b/tests/neg/6697.check @@ -7,8 +7,7 @@ | | trying to reduce Test.Sub[O] | failed since selector O - | matches none of the cases - | - | case Test.Of[sup, sub] => sub + | does not match case Test.Of[sup, sub] => sub + | and cannot be shown to be disjoint from it either. longer explanation available when compiling with `-explain` diff --git a/tests/neg/i12049.check b/tests/neg/i12049.check index adcccf51a4dc..0efc803477a8 100644 --- a/tests/neg/i12049.check +++ b/tests/neg/i12049.check @@ -15,39 +15,22 @@ | case B => String longer explanation available when compiling with `-explain` --- [E007] Type Mismatch Error: tests/neg/i12049.scala:14:17 ------------------------------------------------------------ +-- Error: tests/neg/i12049.scala:14:23 --------------------------------------------------------------------------------- 14 |val y3: String = ??? : Last[Int *: Int *: Boolean *: String *: EmptyTuple] // error - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | Found: Last[EmptyTuple.type] - | Required: String + | ^ + | Match type reduction failed since selector EmptyTuple.type + | matches none of the cases | - | Note: a match type could not be fully reduced: - | - | trying to reduce Last[EmptyTuple.type] - | failed since selector EmptyTuple.type - | matches none of the cases - | - | case _ *: _ *: t => Last[t] - | case t *: EmptyTuple => t - -longer explanation available when compiling with `-explain` --- [E007] Type Mismatch Error: tests/neg/i12049.scala:22:20 ------------------------------------------------------------ + | case _ *: _ *: t => Last[t] + | case t *: EmptyTuple => t +-- Error: tests/neg/i12049.scala:22:26 --------------------------------------------------------------------------------- 22 |val z3: (A, B, A) = ??? : Reverse[(A, B, A)] // error - | ^^^^^^^^^^^^^^^^^^^^^^^^ - | Found: Tuple.Concat[Reverse[A *: EmptyTuple.type], (B, A)] - | Required: (A, B, A) - | - | Note: a match type could not be fully reduced: - | - | trying to reduce Tuple.Concat[Reverse[A *: EmptyTuple.type], (B, A)] - | trying to reduce Reverse[A *: EmptyTuple.type] - | failed since selector A *: EmptyTuple.type - | matches none of the cases + | ^ + | Match type reduction failed since selector A *: EmptyTuple.type + | matches none of the cases | - | case t1 *: t2 *: ts => Tuple.Concat[Reverse[ts], (t2, t1)] - | case EmptyTuple => EmptyTuple - -longer explanation available when compiling with `-explain` + | case t1 *: t2 *: ts => Tuple.Concat[Reverse[ts], (t2, t1)] + | case EmptyTuple => EmptyTuple -- Error: tests/neg/i12049.scala:24:20 --------------------------------------------------------------------------------- 24 |val _ = summon[M[B]] // error | ^ @@ -62,33 +45,22 @@ longer explanation available when compiling with `-explain` | Therefore, reduction cannot advance to the remaining case | | case B => String --- Error: tests/neg/i12049.scala:25:78 --------------------------------------------------------------------------------- +-- Error: tests/neg/i12049.scala:25:26 --------------------------------------------------------------------------------- 25 |val _ = summon[String =:= Last[Int *: Int *: Boolean *: String *: EmptyTuple]] // error - | ^ - | Cannot prove that String =:= Last[EmptyTuple.type]. - | - | Note: a match type could not be fully reduced: + | ^ + | Match type reduction failed since selector EmptyTuple.type + | matches none of the cases | - | trying to reduce Last[EmptyTuple.type] - | failed since selector EmptyTuple.type - | matches none of the cases - | - | case _ *: _ *: t => Last[t] - | case t *: EmptyTuple => t --- Error: tests/neg/i12049.scala:26:48 --------------------------------------------------------------------------------- + | case _ *: _ *: t => Last[t] + | case t *: EmptyTuple => t +-- Error: tests/neg/i12049.scala:26:29 --------------------------------------------------------------------------------- 26 |val _ = summon[(A, B, A) =:= Reverse[(A, B, A)]] // error - | ^ - | Cannot prove that (A, B, A) =:= Tuple.Concat[Reverse[A *: EmptyTuple.type], (B, A)]. - | - | Note: a match type could not be fully reduced: - | - | trying to reduce Tuple.Concat[Reverse[A *: EmptyTuple.type], (B, A)] - | trying to reduce Reverse[A *: EmptyTuple.type] - | failed since selector A *: EmptyTuple.type - | matches none of the cases + | ^ + | Match type reduction failed since selector A *: EmptyTuple.type + | matches none of the cases | - | case t1 *: t2 *: ts => Tuple.Concat[Reverse[ts], (t2, t1)] - | case EmptyTuple => EmptyTuple + | case t1 *: t2 *: ts => Tuple.Concat[Reverse[ts], (t2, t1)] + | case EmptyTuple => EmptyTuple -- [E008] Not Found Error: tests/neg/i12049.scala:28:21 ---------------------------------------------------------------- 28 |val _ = (??? : M[B]).length // error | ^^^^^^^^^^^^^^^^^^^ diff --git a/tests/neg/matchtype-seq.check b/tests/neg/matchtype-seq.check index c2207fa1aea3..07db895b9006 100644 --- a/tests/neg/matchtype-seq.check +++ b/tests/neg/matchtype-seq.check @@ -1,35 +1,19 @@ --- [E007] Type Mismatch Error: tests/neg/matchtype-seq.scala:9:18 ------------------------------------------------------ +-- Error: tests/neg/matchtype-seq.scala:9:11 --------------------------------------------------------------------------- 9 | identity[T1[3]]("") // error - | ^^ - | Found: ("" : String) - | Required: Test.T1[(3 : Int)] + | ^ + | Match type reduction failed since selector (3 : Int) + | matches none of the cases | - | Note: a match type could not be fully reduced: - | - | trying to reduce Test.T1[(3 : Int)] - | failed since selector (3 : Int) - | matches none of the cases - | - | case (1 : Int) => Int - | case (2 : Int) => String - -longer explanation available when compiling with `-explain` --- [E007] Type Mismatch Error: tests/neg/matchtype-seq.scala:10:18 ----------------------------------------------------- + | case (1 : Int) => Int + | case (2 : Int) => String +-- Error: tests/neg/matchtype-seq.scala:10:11 -------------------------------------------------------------------------- 10 | identity[T1[3]](1) // error - | ^ - | Found: (1 : Int) - | Required: Test.T1[(3 : Int)] + | ^ + | Match type reduction failed since selector (3 : Int) + | matches none of the cases | - | Note: a match type could not be fully reduced: - | - | trying to reduce Test.T1[(3 : Int)] - | failed since selector (3 : Int) - | matches none of the cases - | - | case (1 : Int) => Int - | case (2 : Int) => String - -longer explanation available when compiling with `-explain` + | case (1 : Int) => Int + | case (2 : Int) => String -- [E007] Type Mismatch Error: tests/neg/matchtype-seq.scala:11:20 ----------------------------------------------------- 11 | identity[T1[Int]]("") // error | ^^ diff --git a/tests/pos/10747-onnxmin.scala b/tests/pos/10747-onnxmin.scala new file mode 100644 index 000000000000..99d7e0a96d79 --- /dev/null +++ b/tests/pos/10747-onnxmin.scala @@ -0,0 +1,45 @@ +import scala.compiletime.ops.string.+ +import scala.compiletime.ops.int +import scala.compiletime.ops.int.{S, +, <, <=, *} +import scala.compiletime.ops.boolean.&& + +object OnnxMin { + type Index = Int & Singleton + + sealed trait Indices + final case class :::[+H <: Index, +T <: Indices](head: H, tail: T) extends Indices + + sealed trait INil extends Indices + case object INil extends INil + + + type Dimension = Int & Singleton + + sealed trait Shape extends Product with Serializable + + final case class #:[+H <: Dimension, +T <: Shape](head: H, tail: T) extends Shape + + sealed trait SNil extends Shape + case object SNil extends SNil + + type CContains[Haystack <: Indices, Needle <: Index] <: Boolean = Haystack match { + case head ::: tail => head match { + case Needle => true + case _ => CContains[tail, Needle] + } + case INil => false + } + + type AddGivenAxisSizeLoop[First <: Shape, Second <: Shape, AxisIndex <: Indices, I <: Index] <: Shape = First match { + case head #: tail => CContains[AxisIndex, I] match { + case true => Second match { + case secondHead #: secondTail => head #: tail + case SNil => AxisIndex match{ + case INil => SNil + } + } + } + } + + def ConcatV13: AddGivenAxisSizeLoop[Dimension #: Shape, Dimension #: Shape, Index ::: INil, 0] = ??? +} diff --git a/tests/pos/10747-shapeless-min.scala b/tests/pos/10747-shapeless-min.scala new file mode 100644 index 000000000000..fbb8012fc9f2 --- /dev/null +++ b/tests/pos/10747-shapeless-min.scala @@ -0,0 +1,12 @@ +trait Monoidal { + type to[_] <: Tuple +} + +object eithers extends Monoidal { + class Wrap[T] + + type to[t] <: Tuple = Wrap[t] match { + case Wrap[Either[hd, tl]] => hd *: to[tl] + case Wrap[Nothing] => EmptyTuple + } +} diff --git a/tests/run-macros/tasty-simplified.check b/tests/run-macros/tasty-simplified.check index 55e725e10f17..41ae9120aa03 100644 --- a/tests/run-macros/tasty-simplified.check +++ b/tests/run-macros/tasty-simplified.check @@ -1,4 +1,4 @@ -Functor[Const[scala.collection.immutable.List[Dummy]]] -Functor[Const[scala.Int]] -Functor[Const[Dummy]] -Functor[Const[scala.Option[Dummy]]] +Functor[[A >: scala.Nothing <: scala.Any] => scala.collection.immutable.List[A]] +Unapply[[F >: scala.Nothing <: [_$9 >: scala.Nothing <: scala.Any] => scala.Any] => Functor[F], Wrap[scala.Int]] +Unapply[[F >: scala.Nothing <: [_$9 >: scala.Nothing <: scala.Any] => scala.Any] => Functor[F], Wrap[Dummy]] +Functor[[A >: scala.Nothing <: scala.Any] => scala.Option[A]]