diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 5e9575903895..51e468153d1f 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -1385,6 +1385,8 @@ trait Implicits: def disambiguate(alt1: SearchResult, alt2: SearchSuccess) = alt1 match case alt1: SearchSuccess => var diff = compareAlternatives(alt1, alt2, disambiguate = true) + assert(diff <= 0 || isWarnPriorityChangeVersion) + // diff > 0 candidates should already have been eliminated in `rank` if diff == 0 && alt1.ref =:= alt2.ref then diff = 1 // See i12951 for a test where this happens else if diff == 0 && alt2.isExtension then @@ -1636,21 +1638,7 @@ trait Implicits: validateOrdering(ord) throw ex - val sorted = sort(eligible) - val res = sorted match - case first :: rest => - val firstIsImplicit = first.ref.symbol.is(Implicit) - if rest.exists(_.ref.symbol.is(Implicit) != firstIsImplicit) then - // Mixture of implicits and givens - // Rank implicits first, then, if there is a given that it better than the best implicit(s) - // switch over to givens. - val (sortedImplicits, sortedGivens) = sorted.partition(_.ref.symbol.is(Implicit)) - val implicitResult = rank(sortedImplicits, NoMatchingImplicitsFailure, Nil) - rank(sortedGivens, implicitResult, Nil) - else - rank(sorted, NoMatchingImplicitsFailure, Nil) - case _ => - NoMatchingImplicitsFailure + val res = rank(sort(eligible), NoMatchingImplicitsFailure, Nil) // Issue all priority change warnings that can affect the result val shownWarnings = priorityChangeWarnings.toList.collect: diff --git a/tests/pos/i21320.scala b/tests/pos/i21320a.scala similarity index 100% rename from tests/pos/i21320.scala rename to tests/pos/i21320a.scala diff --git a/tests/pos/i21320b.scala b/tests/pos/i21320b.scala new file mode 100644 index 000000000000..6711d3d9d952 --- /dev/null +++ b/tests/pos/i21320b.scala @@ -0,0 +1,73 @@ +import scala.deriving.* +import scala.compiletime.* + +trait ConfigMonoid[T]: + def zero: T + def orElse(main: T, defaults: T): T + +object ConfigMonoid: + given option[T]: ConfigMonoid[Option[T]] = ??? + + inline def zeroTuple[C <: Tuple]: Tuple = + inline erasedValue[C] match + case _: EmptyTuple => EmptyTuple + case _: (t *: ts) => + summonInline[ConfigMonoid[t]].zero *: zeroTuple[ts] + + inline def valueTuple[C <: Tuple, T](index: Int, main: T, defaults: T): Tuple = + inline erasedValue[C] match + case _: EmptyTuple => EmptyTuple + case _: (t *: ts) => + def get(v: T) = v.asInstanceOf[Product].productElement(index).asInstanceOf[t] + summonInline[ConfigMonoid[t]].orElse(get(main), get(defaults)) *: valueTuple[ts, T]( + index + 1, + main, + defaults + ) + + inline given derive[T](using m: Mirror.ProductOf[T]): ConfigMonoid[T] = + new ConfigMonoid[T]: + def zero: T = m.fromProduct(zeroTuple[m.MirroredElemTypes]) + def orElse(main: T, defaults: T): T = m.fromProduct(valueTuple[m.MirroredElemTypes, T](0, main, defaults)) + + + +final case class PublishOptions( + v1: Option[String] = None, + v2: Option[String] = None, + v3: Option[String] = None, + v4: Option[String] = None, + v5: Option[String] = None, + v6: Option[String] = None, + v7: Option[String] = None, + v8: Option[String] = None, + v9: Option[String] = None, + ci: PublishContextualOptions = PublishContextualOptions(), +) +object PublishOptions: + implicit val monoid: ConfigMonoid[PublishOptions] = ConfigMonoid.derive + +final case class PublishContextualOptions( + v1: Option[String] = None, + v2: Option[String] = None, + v3: Option[String] = None, + v4: Option[String] = None, + v5: Option[String] = None, + v6: Option[String] = None, + v7: Option[String] = None, + v8: Option[String] = None, + v9: Option[String] = None, + v10: Option[String] = None, + v11: Option[String] = None, + v12: Option[String] = None, + v13: Option[String] = None, + v14: Option[String] = None, + v15: Option[String] = None, + v16: Option[String] = None, + v17: Option[String] = None, + v18: Option[String] = None, + v19: Option[String] = None, + v20: Option[String] = None +) +object PublishContextualOptions: + implicit val monoid: ConfigMonoid[PublishContextualOptions] = ConfigMonoid.derive // was crash diff --git a/tests/pos/i21352a/schema.scala b/tests/pos/i21352a/schema.scala new file mode 100644 index 000000000000..486e1bb1ea34 --- /dev/null +++ b/tests/pos/i21352a/schema.scala @@ -0,0 +1,19 @@ +//> using options -source:3.5 + +case class Schema[T](format: String): + def asOption: Schema[Option[T]] = ??? + def name(name: Option[SName]): Schema[T] = ??? + def format(f: String): Schema[T] = ??? + +object Schema extends SchemaCompanionMacros: + implicit def schemaForOption[T: Schema]: Schema[Option[T]] = + implicitly[Schema[T]] + ??? + +trait SchemaCompanionMacros extends SchemaDerivation: + given derivedStringBasedUnionEnumeration[S](using IsUnionOf[String, S]): Schema[S] = + val x: Schema[S] = ??? + x.name(None) + +@main def Test = + case class Foo(x: Int) derives Schema diff --git a/tests/pos/i21352a/schemaDerivation.scala b/tests/pos/i21352a/schemaDerivation.scala new file mode 100644 index 000000000000..d34a59c51a61 --- /dev/null +++ b/tests/pos/i21352a/schemaDerivation.scala @@ -0,0 +1,26 @@ +//> using options -source:3.5 + +import scala.deriving.* +import scala.quoted.* + +trait SName +abstract class CaseClass[Typeclass[_], Type]: + def param: CaseClass.Param[Typeclass, Type] + +object CaseClass: + trait Param[Typeclass[_], Type]: + type PType + def typeclass: Typeclass[PType] + + +sealed trait IsUnionOf[T, A] +object IsUnionOf: + transparent inline given derived[T, A]: IsUnionOf[T, A] = ${ deriveImpl[T, A] } + private def deriveImpl[T, A](using quotes: Quotes): Expr[IsUnionOf[T, A]] = ??? + +trait SchemaDerivation: + inline implicit def derived[T](implicit m: Mirror.Of[T]): Schema[T] = + val ctx: CaseClass[Schema, T] = ??? + val valueSchema = ctx.param.typeclass + val format = valueSchema.format + ??? diff --git a/tests/pos/i21352b.scala b/tests/pos/i21352b.scala new file mode 100644 index 000000000000..6e1dfbd18e3c --- /dev/null +++ b/tests/pos/i21352b.scala @@ -0,0 +1,33 @@ + +object serializer: + trait Reader[T] + trait Writer[T] + // Needs to be implicit val + implicit val UnitReader: Reader[Unit] = ??? + implicit val StringReader: Reader[String] = ??? + // A way to derive instances needs to be available + inline given superTypeReader[T: scala.reflect.ClassTag]: Reader[T] = ??? +import serializer.Reader + +trait Codec[T] +trait Channel[F[_]]: + def notificationStub[In: Codec](): In => F[Unit] +trait Monadic[F[_]] + +sealed abstract class LSPNotification(): + type In + given inputReader: Reader[In] + +class PreparedNotification[X <: LSPNotification](val x: X, val in: x.In): + type In = x.In + +trait Communicate[F[_]]: + def notification[X <: LSPNotification](notif: X, in: notif.In): F[Unit] + +object Communicate: + given codec[T: Reader]: Codec[T] = ??? + + def channel[F[_]: Monadic](channel: Channel[F]) = + new Communicate[F]: + override def notification[X <: LSPNotification](notif: X, in: notif.In): F[Unit] = + channel.notificationStub().apply(in) // was error diff --git a/tests/pos/i21352c.scala b/tests/pos/i21352c.scala new file mode 100644 index 000000000000..22169f3560a8 --- /dev/null +++ b/tests/pos/i21352c.scala @@ -0,0 +1,17 @@ + +trait Text[T] +trait Read[A] +object Read extends ReadImplicits: + implicit val unit: Read[Unit] = ??? +trait ReadImplicits: + import scala.deriving.* + given roe: Read[Option[EmptyTuple]] = ??? + given rou: Read[Option[Unit]] = ??? + given cons1[H, T <: Tuple](using Read[Option[H]], Read[Option[T]]): Read[Option[H *: T]] = ??? + +trait Fragment: + def query[B: Read]: String = ??? + +@main def Test = + val f: Fragment = ??? + f.query // was error