From d36767f83c4dc6b6e4759362dc84ab55e9a93596 Mon Sep 17 00:00:00 2001 From: Hamza Remmal Date: Tue, 11 Nov 2025 14:10:29 +0100 Subject: [PATCH] push distributivity rule to source future --- .../tools/dotc/config/SourceVersion.scala | 2 +- tests/neg/i23435-min.scala | 2 +- tests/neg/i23435.scala | 2 +- tests/neg/i24096.check | 20 ++++---- tests/neg/i24096.scala | 2 + tests/neg/i3989e.scala | 2 +- tests/neg/singletonInterval.scala | 2 +- tests/pos/cb-companion-joins.scala | 2 +- tests/pos/i10256.scala | 2 +- tests/pos/i11064.scala | 8 ++-- tests/pos/i24094.scala | 46 +++++++++++++++++++ tests/pos/i24097-a.scala | 36 +++++++++++++++ tests/pos/i24097-b.scala | 25 ++++++++++ 13 files changed, 130 insertions(+), 21 deletions(-) create mode 100644 tests/pos/i24094.scala create mode 100644 tests/pos/i24097-a.scala create mode 100644 tests/pos/i24097-b.scala diff --git a/compiler/src/dotty/tools/dotc/config/SourceVersion.scala b/compiler/src/dotty/tools/dotc/config/SourceVersion.scala index 59b958762042..d445da625afa 100644 --- a/compiler/src/dotty/tools/dotc/config/SourceVersion.scala +++ b/compiler/src/dotty/tools/dotc/config/SourceVersion.scala @@ -46,7 +46,7 @@ enum SourceVersion: def enablesNamedTuples = isAtLeast(`3.7`) def enablesBetterFors(using Context) = isAtLeast(`3.8`) || (isAtLeast(`3.7`) && isPreviewEnabled) /** See PR #23441 and tests/neg/i23435-min */ - def enablesDistributeAnd = !isAtLeast(`3.8`) + def enablesDistributeAnd = !isAtLeast(`future`) def requiresNewSyntax = isAtLeast(future) diff --git a/tests/neg/i23435-min.scala b/tests/neg/i23435-min.scala index b68425f5404c..e038c1e4a4d4 100644 --- a/tests/neg/i23435-min.scala +++ b/tests/neg/i23435-min.scala @@ -1,4 +1,4 @@ -//> using options -source:3.8 +//> using options -source:future type Or[+A, +B] = A | B diff --git a/tests/neg/i23435.scala b/tests/neg/i23435.scala index 17e83d15c6d6..1334f15c5d60 100644 --- a/tests/neg/i23435.scala +++ b/tests/neg/i23435.scala @@ -1,4 +1,4 @@ -//> using options -source:3.8 +//> using options -source:future trait L[+A]{val a:A} trait R[+B]{val b: B} diff --git a/tests/neg/i24096.check b/tests/neg/i24096.check index 0272747fd00e..8349298f716b 100644 --- a/tests/neg/i24096.check +++ b/tests/neg/i24096.check @@ -1,10 +1,10 @@ --- [E007] Type Mismatch Error: tests/neg/i24096.scala:8:31 ------------------------------------------------------------- -8 | case r: Terminal[?] => r // error - | ^ - | Found: (r : Pull.Terminal[_] & Pull[F, O, Unit]) - | Required: Pull[F2, O2, Unit] - | - | where: F is a type in class StreamPullOps with bounds <: [_²] =>> Any - | F2 is a type in method flatMapOutput with bounds >: [x] =>> F[x] and <: [x] =>> Any - | - | longer explanation available when compiling with `-explain` +-- [E007] Type Mismatch Error: tests/neg/i24096.scala:10:31 ------------------------------------------------------------ +10 | case r: Terminal[?] => r // error + | ^ + | Found: (r : Pull.Terminal[_] & Pull[F, O, Unit]) + | Required: Pull[F2, O2, Unit] + | + | where: F is a type in class StreamPullOps with bounds <: [_²] =>> Any + | F2 is a type in method flatMapOutput with bounds >: [x] =>> F[x] and <: [x] =>> Any + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg/i24096.scala b/tests/neg/i24096.scala index e7ae73d9f170..3eaeaf77411a 100644 --- a/tests/neg/i24096.scala +++ b/tests/neg/i24096.scala @@ -1,3 +1,5 @@ +//> using options -source:future + abstract class Pull[+F[_], +O, +R] object Pull: abstract class Terminal[+R] extends Pull[Nothing, Nothing, R] diff --git a/tests/neg/i3989e.scala b/tests/neg/i3989e.scala index f1dae2717506..ed1eddcaae9e 100644 --- a/tests/neg/i3989e.scala +++ b/tests/neg/i3989e.scala @@ -1,4 +1,4 @@ -//> using options -source:3.8 +//> using options -source:future object Test extends App { trait A[+X](val x: X) diff --git a/tests/neg/singletonInterval.scala b/tests/neg/singletonInterval.scala index 39a42253da42..895dcfedad25 100644 --- a/tests/neg/singletonInterval.scala +++ b/tests/neg/singletonInterval.scala @@ -1,4 +1,4 @@ -//> using options -source:3.8 +//> using options -source:future /** Why the singletonInterval logic cannot be applied for lubArgs and glbArgs in TypeComparer. */ diff --git a/tests/pos/cb-companion-joins.scala b/tests/pos/cb-companion-joins.scala index 1f445c07ddff..aa976a3fb10f 100644 --- a/tests/pos/cb-companion-joins.scala +++ b/tests/pos/cb-companion-joins.scala @@ -1,4 +1,4 @@ -//> using options -source:3.8 +//> using options -source:future import language.experimental.modularity trait M[Self]: diff --git a/tests/pos/i10256.scala b/tests/pos/i10256.scala index bd37206731eb..f0ac890df33c 100644 --- a/tests/pos/i10256.scala +++ b/tests/pos/i10256.scala @@ -1,4 +1,4 @@ -//> using options -source:3.8 +//> using options -source:future trait Foo[T <: Foo[T]] { type I <: Foo[I] diff --git a/tests/pos/i11064.scala b/tests/pos/i11064.scala index e9999f75e352..646f1da9fcea 100644 --- a/tests/pos/i11064.scala +++ b/tests/pos/i11064.scala @@ -1,12 +1,12 @@ -//> using options -source:3.8 +//> using options -source:future trait TypedArray[T, Repr] -trait Ops[T <: TypedArray[_, T]] { +trait Ops[T <: TypedArray[?, T]] { def typedArray(): T } object Test { - def test1(ops: Ops[_ <: TypedArray[_, _]]) = ops.typedArray() - def test2(ops: Ops[_ <: TypedArray[_ <: AnyRef, _]]) = ops.typedArray() // ok, was error: Recursion limit exceeded. + def test1(ops: Ops[? <: TypedArray[?, ?]]) = ops.typedArray() + def test2(ops: Ops[? <: TypedArray[? <: AnyRef, ?]]) = ops.typedArray() // ok, was error: Recursion limit exceeded. } \ No newline at end of file diff --git a/tests/pos/i24094.scala b/tests/pos/i24094.scala new file mode 100644 index 000000000000..1ccb4e5fbf82 --- /dev/null +++ b/tests/pos/i24094.scala @@ -0,0 +1,46 @@ +class TyreCompiler[IN <: Tuple, R](val context: Context[R *: IN]): + import context.* + + private class Loop[IS <: Tuple, T]( + val context: Context[T *: IS], + innerAutomaton: context.Automaton[IS] + ) { + private lazy val fixableStates: List[RefinedInitNonAcceptingState[T, IS]] = + innerAutomaton.initStates.map { case is: context.InitNonAcceptingState[?] => + new RefinedInitNonAcceptingState[T, IS] { + type Tail = is.OS + type OS = List[T] *: Tail + lazy val state = new NonAcceptingState: + val next: List[Transition[OS]] = + is.state.next.flatMap(fixTransition[is.OS](fixableStates, _)) + } + } + + private def fixTransition[S <: Tuple]( + initStates: List[RefinedInitNonAcceptingState[T, IS]], + transition: context.Transition[S] + ): List[Transition[List[T] *: S]] = ??? + + private trait RefinedInitNonAcceptingState[T, IS <: Tuple] extends InitNonAcceptingState[IS]: + type Tail <: Tuple + type OS = List[T] *: Tail + lazy val state: NonAcceptingState[OS] + } + +private class Context[R <: Tuple]: + sealed trait State[S <: Tuple]: + val next: List[Transition[S]] + trait NonAcceptingState[S <: Tuple] extends State[S] + + sealed trait Transition[IS <: Tuple]: + def state: State[?] + + sealed trait InitState[-IS <: Tuple]: + type OS <: Tuple + def state: State[?] + + trait InitNonAcceptingState[-IS <: Tuple] extends InitState[IS]: + lazy val state: NonAcceptingState[OS] + + trait Automaton[-IS <: Tuple]: + val initStates: List[InitState[IS]] diff --git a/tests/pos/i24097-a.scala b/tests/pos/i24097-a.scala new file mode 100644 index 000000000000..7d7a1ca803a3 --- /dev/null +++ b/tests/pos/i24097-a.scala @@ -0,0 +1,36 @@ +trait <[+A, -S] +object `<`: + extension [A, S](v: A < S) def map[B, S2](f: A => B < S2): B < (S & S2) = ??? + +def lift[A, S](v: A): A < S = ??? + +abstract class ArrowEffect[-Input[_], +Output[_]] +object ArrowEffect: + inline def handleCatching[I[_], O[_], E <: ArrowEffect[I, O], A, B, S, S2, S3]( + v: A < (E & S) + ): B < (S & S2 & S3) = ??? + +trait Result[+E, +A] +object Result: + def succeed[E, A](value: A): Result[E, A] = ??? + +abstract class Error[+E] + +type Const[A] = [B] =>> A +sealed trait Abort[-E] extends ArrowEffect[Const[Error[E]], Const[Unit]] +object Abort: + inline def runWith[E, A, S, ER, B, S2](v: A < (Abort[E | ER] & S)) = + ArrowEffect.handleCatching[ + Const[Error[E]], + Const[Unit], + Abort[E], + Result[E, A], + B, + Abort[ER] & S, + Abort[ER] & S, + S2 + ]( + v.map: + value => lift: + Result.succeed[E, A](value) + ) diff --git a/tests/pos/i24097-b.scala b/tests/pos/i24097-b.scala new file mode 100644 index 000000000000..0a73fc8c0121 --- /dev/null +++ b/tests/pos/i24097-b.scala @@ -0,0 +1,25 @@ +trait <[+A, -S] + +trait TypeMap[+A]: + def union[B](that: TypeMap[B]): TypeMap[A & B] = ??? + +trait Tag[E] +trait ContextEffect[+A] +object ContextEffect: + def handle[A, E <: ContextEffect[A], B, S](effectTag: Tag[E], ifDefined: A => A)( + v: B < (E & S) + ): B < S = ??? + +sealed trait Env[+R] extends ContextEffect[TypeMap[R]] +object Env: + def runAll[R >: Nothing, A, S, VR]( + env: TypeMap[R], + tag: Tag[Env[R]] + )( + v: A < (Env[R & VR] & S) + ) = + ContextEffect.handle( + tag, + _.union(env) + )(v): A < (Env[VR] & S) + ???