diff --git a/community-build/community-projects/munit b/community-build/community-projects/munit index 5c77d7316fc6..c5d6f474fa0d 160000 --- a/community-build/community-projects/munit +++ b/community-build/community-projects/munit @@ -1 +1 @@ -Subproject commit 5c77d7316fc66adaed64e9532ee0a45a668b01ec +Subproject commit c5d6f474fa0d481e2c29f15d6a67d10ef2099e78 diff --git a/community-build/community-projects/specs2 b/community-build/community-projects/specs2 index a618330aa808..005c5847ecf9 160000 --- a/community-build/community-projects/specs2 +++ b/community-build/community-projects/specs2 @@ -1 +1 @@ -Subproject commit a618330aa80833787859dae805d02e45d4304c42 +Subproject commit 005c5847ecf9439691505f0628d318b0fed9d341 diff --git a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala index ecf1da30cac1..5909cda8c428 100644 --- a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala +++ b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala @@ -11,8 +11,9 @@ import Constants.* import util.{Stats, SimpleIdentityMap, SimpleIdentitySet} import Decorators.* import Uniques.* -import Flags.Method +import Flags.{Method, Transparent} import inlines.Inlines +import config.{Feature, SourceVersion} import config.Printers.typr import Inferencing.* import ErrorReporting.* @@ -108,7 +109,7 @@ object ProtoTypes { res /** Constrain result with two special cases: - * 1. If `meth` is an inlineable method in an inlineable context, + * 1. If `meth` is a transparent inlineable method in an inlineable context, * we should always succeed and not constrain type parameters in the expected type, * because the actual return type can be a subtype of the currently known return type. * However, we should constrain parameters of the declared return type. This distinction is @@ -128,11 +129,30 @@ object ProtoTypes { case _ => false - if Inlines.isInlineable(meth) then - constrainResult(mt, wildApprox(pt)) - true - else - constFoldException(pt) || constrainResult(mt, pt) + constFoldException(pt) || { + if Inlines.isInlineable(meth) then + // Stricter behavisour in 3.4+: do not apply `wildApprox` to non-transparent inlines + // unless their return type is a MatchType. In this case there's no reason + // not to constrain type variables in the expected type. For transparent inlines + // we do not want to constrain type variables in the expected type since the + // actual return type might be smaller after instantiation. For inlines returning + // MatchTypes we do not want to constrain because the MatchType might be more + // specific after instantiation. TODO: Should we also use Wildcards for non-inline + // methods returning MatchTypes? + if Feature.sourceVersion.isAtLeast(SourceVersion.`3.4`) then + if meth.is(Transparent) || mt.resultType.isMatchAlias then + constrainResult(mt, wildApprox(pt)) + // do not constrain the result type of transparent inline methods + true + else + constrainResult(mt, pt) + else + // Best-effort to fix https://github.com/scala/scala3/issues/9685 in the 3.3.x series + // while preserving source compatibility as much as possible + constrainResult(mt, wildApprox(pt)) || meth.is(Transparent) + else constrainResult(mt, pt) + } + end constrainResult end Compatibility diff --git a/tests/neg/i18123.check b/tests/neg/i18123.check new file mode 100644 index 000000000000..d784c4d12673 --- /dev/null +++ b/tests/neg/i18123.check @@ -0,0 +1,12 @@ +-- [E172] Type Error: tests/neg/i18123.scala:25:33 --------------------------------------------------------------------- +25 | (charClassIntersection.rep() | classItem.rep()) // error + | ^^^^^^^^^^^^^^^ + |No given instance of type pkg.Implicits.Repeater[pkg.RegexTree, V] was found. + |I found: + | + | pkg.Implicits.Repeater.GenericRepeaterImplicit[T] + | + |But method GenericRepeaterImplicit in object Repeater does not match type pkg.Implicits.Repeater[pkg.RegexTree, V] + | + |where: V is a type variable with constraint <: Seq[pkg.CharClassIntersection] + |. diff --git a/tests/neg/i18123.scala b/tests/neg/i18123.scala new file mode 100644 index 000000000000..bb220dc78e93 --- /dev/null +++ b/tests/neg/i18123.scala @@ -0,0 +1,25 @@ +// may not compile anymore in Scala 3.4+ +package pkg + +trait P[+T] + +extension [T](inline parse0: P[T]) + inline def | [V >: T](inline other: P[V]): P[V] = ??? + +extension [T](inline parse0: => P[T]) + inline def rep[V](inline min: Int = 0)(using repeater: Implicits.Repeater[T, V]): P[V] = ??? + +object Implicits: + trait Repeater[-T, R] + object Repeater: + implicit def GenericRepeaterImplicit[T]: Repeater[T, Seq[T]] = ??? + +sealed trait RegexTree +abstract class Node extends RegexTree +class CharClassIntersection() extends Node + +def classItem: P[RegexTree] = ??? +def charClassIntersection: P[CharClassIntersection] = ??? + +def x = + (charClassIntersection.rep() | classItem.rep()) // error diff --git a/tests/pos/i18123.scala b/tests/pos/i18123.scala index 714850004d2c..2b18b3fc73c3 100644 --- a/tests/pos/i18123.scala +++ b/tests/pos/i18123.scala @@ -7,7 +7,8 @@ extension [T](inline parse0: P[T]) inline def | [V >: T](inline other: P[V]): P[V] = ??? extension [T](inline parse0: => P[T]) - inline def rep[V](inline min: Int = 0)(using repeater: Implicits.Repeater[T, V]): P[V] = ??? + // transparent needed to make this compile in 3.4+ + transparent inline def rep[V](inline min: Int = 0)(using repeater: Implicits.Repeater[T, V]): P[V] = ??? object Implicits: trait Repeater[-T, R] diff --git a/tests/pos/i19415.scala b/tests/pos/i19415.scala new file mode 100644 index 000000000000..3d9c40127cb0 --- /dev/null +++ b/tests/pos/i19415.scala @@ -0,0 +1,24 @@ +def Test = { + val left: Parser[String] = ??? + val right: Parser[Int] = ??? + val both = left && right + + val works = both.map(Ior.Both.apply) + val fails = (left && right).map(Ior.Both.apply) +} + +trait Parser[T]: + final def &&[T2](other: Parser[T2])(implicit zip: Zip[T, T2]): Parser[zip.Out] = ??? + final def map[T2](f: T => T2): Parser[T2] = ??? + +infix trait Ior[+A, +B] +object Ior: + final case class Both[+A, +B](a: A, b: B) extends (A Ior B) + +trait Zip[In1, In2]: + type Out + +object Zip { + type Out[In1, In2, O] = Zip[In1, In2] { type Out = O } + implicit def zip2[_1, _2]: Zip.Out[_1, _2, (_1, _2)] = ??? +} \ No newline at end of file diff --git a/tests/pos/i19479.scala b/tests/pos/i19479.scala new file mode 100644 index 000000000000..a12bd378a490 --- /dev/null +++ b/tests/pos/i19479.scala @@ -0,0 +1,54 @@ +case class Person(id: Int) + +class GeodeContinuousSourceSpec { + summon[PdxEncoder[Person]] +} + +trait PdxEncoder[A] { + def encode(a: A): Boolean +} + +object PdxEncoder extends ObjectEncoder { + implicit def intEncoder: PdxEncoder[Int] = ??? +} + +trait ObjectEncoder { + given emptyTupleEncoder: PdxEncoder[EmptyTuple] = ??? + + given tupleEncoder[K <: String, H, T <: Tuple](using + m: ValueOf[K], + hEncoder: PdxEncoder[H], + tEncoder: PdxEncoder[T] + ): PdxEncoder[FieldType[K, H] *: T] = ??? + + given objectEncoder[A, Repr <: Tuple](using + gen: LabelledGeneric.Aux[A, Repr], + tupleEncoder: PdxEncoder[Repr] + ): PdxEncoder[A] = ??? +} + +import scala.deriving.Mirror + +private type FieldType[K, +V] = V & KeyTag[K, V] +private type KeyTag[K, +V] +private type ZipWith[T1 <: Tuple, T2 <: Tuple, F[_, _]] <: Tuple = (T1, T2) match { + case (h1 *: t1, h2 *: t2) => F[h1, h2] *: ZipWith[t1, t2, F] + case (EmptyTuple, ?) => EmptyTuple + case (?, EmptyTuple) => EmptyTuple + case _ => Tuple +} + +private trait LabelledGeneric[A] { + type Repr +} + +private object LabelledGeneric { + type Aux[A, R] = LabelledGeneric[A] { type Repr = R } + + transparent inline given productInst[A <: Product](using + m: Mirror.ProductOf[A] + ): LabelledGeneric.Aux[A, ZipWith[m.MirroredElemLabels, m.MirroredElemTypes, FieldType]] = + new LabelledGeneric[A] { + type Repr = Tuple & ZipWith[m.MirroredElemLabels, m.MirroredElemTypes, FieldType] + } +} \ No newline at end of file diff --git a/tests/pos/i20297.scala b/tests/pos/i20297.scala new file mode 100644 index 000000000000..ee7ee57045ae --- /dev/null +++ b/tests/pos/i20297.scala @@ -0,0 +1,20 @@ +sealed abstract class Kyo[+T, -S] +opaque type <[+T, -S] >: T = T | Kyo[T, S] + +extension [T, S](v: T < S) + inline def map[U, S2](inline f: T => U < S2): U < (S & S2) = ??? + +class Streams[V] +object Streams: + def emitValue[V](v: V): Unit < Streams[V] = ??? + +opaque type Stream[+T, V, -S] = T < (Streams[V] & S) +object Stream: + extension [T, V, S](s: Stream[T, V, S]) + def reemit[S2, V2](f: V => Unit < (Streams[V2] & S2)): Stream[T, V2, S & S2] = ??? + def filter[S2](f: V => Boolean < S2): Stream[T, V, S & S2] = reemit { v => + f(v).map { + case false => () + case true => Streams.emitValue(v) + } + } diff --git a/tests/pos/i20342.scala b/tests/pos/i20342.scala new file mode 100644 index 000000000000..250839680174 --- /dev/null +++ b/tests/pos/i20342.scala @@ -0,0 +1,15 @@ +class Repo[EC, E](using defaults: RepoDefaults[EC, E]) +trait RepoDefaults[EC, E] +object RepoDefaults: + inline given genImmutableRepo[E: DbCodec]: RepoDefaults[E, E] = ??? + inline given genRepo[EC: DbCodec, E: DbCodec]: RepoDefaults[EC, E] = ??? + +trait DbCodec[E] + +case class PersonCreator(name: String) +case class Person(id: Long) +given DbCodec[Person] = ??? +given DbCodec[PersonCreator] = ??? + +@main def Test = + val personRepo = Repo[PersonCreator, Person]