Skip to content

Commit 62acaf1

Browse files
committed
A tweak of a tweak about owner scope disambiguation
1 parent 3d9ce8e commit 62acaf1

File tree

5 files changed

+98
-14
lines changed

5 files changed

+98
-14
lines changed

compiler/src/dotty/tools/dotc/typer/Applications.scala

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1963,8 +1963,11 @@ trait Applications extends Compatibility {
19631963
else if winsPrefix1 then 1
19641964
else -1
19651965

1966+
val ownerScore = compareOwner(alt1.symbol.maybeOwner, alt2.symbol.maybeOwner)
1967+
val implicitPair = alt1.symbol.is(Implicit) && alt2.symbol.is(Implicit)
1968+
val newGivenRules = preferGeneral && !ctx.mode.is(Mode.OldImplicitResolution)
1969+
19661970
def compareWithTypes(tp1: Type, tp2: Type) =
1967-
val ownerScore = compareOwner(alt1.symbol.maybeOwner, alt2.symbol.maybeOwner)
19681971
val winsType1 = isAsGood(alt1, tp1, alt2, tp2)
19691972
val winsType2 = isAsGood(alt2, tp2, alt1, tp1)
19701973

@@ -1976,13 +1979,14 @@ trait Applications extends Compatibility {
19761979
// (prefer the one that is not a method, but that's arbitrary).
19771980
if alt1.widenExpr =:= alt2 then -1 else 1
19781981
else
1979-
// For implicit resolution, take ownerscore as more significant than type resolution
1982+
// For new implicit resolution, take ownerscore as more significant than type resolution
19801983
// Reason: People use owner hierarchies to explicitly prioritize, we should not
19811984
// break that by changing implicit priority of types.
1985+
// But don't do that for implicit/implicit pairs. It's better to leave
1986+
// them ambiguous so that the logic in Implicits/compareAlternatives kicks in
1987+
// which resolves them with the old rules.
19821988
def drawOrOwner =
1983-
if preferGeneral && !ctx.mode.is(Mode.OldImplicitResolution)
1984-
then ownerScore
1985-
else 0
1989+
if newGivenRules && !implicitPair then ownerScore else 0
19861990
ownerScore match
19871991
case 1 => if winsType1 || !winsType2 then 1 else drawOrOwner
19881992
case -1 => if winsType2 || !winsType1 then -1 else drawOrOwner
@@ -2002,6 +2006,13 @@ trait Applications extends Compatibility {
20022006

20032007
var result = compareWithTypes(strippedType1, strippedType2)
20042008
if result != 0 then result
2009+
else if ownerScore != 0 && newGivenRules && implicitPair then
2010+
0 // for implicit/implicit pairs fail fast
2011+
// so that we retry in compareAlternatives with old rules.
2012+
// Note: This is problematic since it fails the transitity
2013+
// requirement, i.e compareAlternatives is not a partial order
2014+
// anymore. But it might be needed to keep the number of failing
2015+
// projects smaller
20052016
else if strippedType1 eq fullType1 then
20062017
if strippedType2 eq fullType2 then 0 // no implicits either side: its' a draw
20072018
else 1 // prefer 1st alternative with no implicits

tests/neg/scala-uri.check

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@
66
|
77
| QueryKeyValue.tuple2QueryKeyValue[String, None.type](QueryKey.stringQueryKey,
88
| QueryValue.optionQueryValue[A](
9-
| /* ambiguous: both value stringQueryValue in trait QueryValueInstances1 and value noneQueryValue in trait QueryValueInstances1 match type QueryValue[A] */
9+
| /* ambiguous: both given instance stringQueryValue in trait QueryValueInstances1 and given instance noneQueryValue in trait QueryValueInstances1 match type QueryValue[A] */
1010
| summon[QueryValue[A]]
1111
| )
1212
| )
1313
|
14-
|But both value stringQueryValue in trait QueryValueInstances1 and value noneQueryValue in trait QueryValueInstances1 match type QueryValue[A].
14+
|But both given instance stringQueryValue in trait QueryValueInstances1 and given instance noneQueryValue in trait QueryValueInstances1 match type QueryValue[A].

tests/neg/scala-uri.scala

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@ import scala.language.implicitConversions
33
trait QueryKey[A]
44
object QueryKey extends QueryKeyInstances
55
sealed trait QueryKeyInstances:
6-
implicit val stringQueryKey: QueryKey[String] = ???
6+
given stringQueryKey: QueryKey[String] = ???
77

88
trait QueryValue[-A]
99
object QueryValue extends QueryValueInstances
1010
sealed trait QueryValueInstances1:
11-
implicit final val stringQueryValue: QueryValue[String] = ???
12-
implicit final val noneQueryValue: QueryValue[None.type] = ???
11+
given stringQueryValue: QueryValue[String] = ???
12+
given noneQueryValue: QueryValue[None.type] = ???
1313
// The noneQueryValue makes no sense at this priority. Since QueryValue
1414
// is contravariant, QueryValue[None.type] is always better than QueryValue[Option[A]]
1515
// no matter whether it's old or new resolution. So taking both owner and type
@@ -20,11 +20,11 @@ sealed trait QueryValueInstances1:
2020
// same trait as QueryValue[Option[A]], as is shown in pos/scala-uri.scala.
2121

2222
sealed trait QueryValueInstances extends QueryValueInstances1:
23-
implicit final def optionQueryValue[A: QueryValue]: QueryValue[Option[A]] = ???
23+
given optionQueryValue[A: QueryValue]: QueryValue[Option[A]] = ???
2424

2525
trait QueryKeyValue[A]
2626
object QueryKeyValue:
27-
implicit def tuple2QueryKeyValue[K: QueryKey, V: QueryValue]: QueryKeyValue[(K, V)] = ???
27+
given tuple2QueryKeyValue[K: QueryKey, V: QueryValue]: QueryKeyValue[(K, V)] = ???
2828

2929

3030
@main def Test = summon[QueryKeyValue[(String, None.type)]] // error

tests/pos/i21320.scala

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import scala.deriving.*
2+
import scala.compiletime.*
3+
4+
trait ConfigMonoid[T]:
5+
def zero: T
6+
def orElse(main: T, defaults: T): T
7+
8+
object ConfigMonoid:
9+
given option[T]: ConfigMonoid[Option[T]] = ???
10+
11+
inline def zeroTuple[C <: Tuple]: Tuple =
12+
inline erasedValue[C] match
13+
case _: EmptyTuple => EmptyTuple
14+
case _: (t *: ts) =>
15+
summonInline[ConfigMonoid[t]].zero *: zeroTuple[ts]
16+
17+
inline def valueTuple[C <: Tuple, T](index: Int, main: T, defaults: T): Tuple =
18+
inline erasedValue[C] match
19+
case _: EmptyTuple => EmptyTuple
20+
case _: (t *: ts) =>
21+
def get(v: T) = v.asInstanceOf[Product].productElement(index).asInstanceOf[t]
22+
summonInline[ConfigMonoid[t]].orElse(get(main), get(defaults)) *: valueTuple[ts, T](
23+
index + 1,
24+
main,
25+
defaults
26+
)
27+
28+
inline given derive[T](using m: Mirror.ProductOf[T]): ConfigMonoid[T] =
29+
new ConfigMonoid[T]:
30+
def zero: T = m.fromProduct(zeroTuple[m.MirroredElemTypes])
31+
def orElse(main: T, defaults: T): T = m.fromProduct(valueTuple[m.MirroredElemTypes, T](0, main, defaults))
32+
33+
34+
35+
final case class PublishOptions(
36+
v1: Option[String] = None,
37+
v2: Option[String] = None,
38+
v3: Option[String] = None,
39+
v4: Option[String] = None,
40+
v5: Option[String] = None,
41+
v6: Option[String] = None,
42+
v7: Option[String] = None,
43+
v8: Option[String] = None,
44+
v9: Option[String] = None,
45+
ci: PublishContextualOptions = PublishContextualOptions(),
46+
)
47+
object PublishOptions:
48+
implicit val monoid: ConfigMonoid[PublishOptions] = ConfigMonoid.derive
49+
50+
final case class PublishContextualOptions(
51+
v1: Option[String] = None,
52+
v2: Option[String] = None,
53+
v3: Option[String] = None,
54+
v4: Option[String] = None,
55+
v5: Option[String] = None,
56+
v6: Option[String] = None,
57+
v7: Option[String] = None,
58+
v8: Option[String] = None,
59+
v9: Option[String] = None,
60+
v10: Option[String] = None,
61+
v11: Option[String] = None,
62+
v12: Option[String] = None,
63+
v13: Option[String] = None,
64+
v14: Option[String] = None,
65+
v15: Option[String] = None,
66+
v16: Option[String] = None,
67+
v17: Option[String] = None,
68+
v18: Option[String] = None,
69+
v19: Option[String] = None,
70+
v20: Option[String] = None
71+
)
72+
object PublishContextualOptions:
73+
implicit val monoid: ConfigMonoid[PublishContextualOptions] = ConfigMonoid.derive

tests/pos/scala-uri.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
// This works for implicit/implicit pairs but not for givens, see neg version.
12
import scala.language.implicitConversions
23

34
trait QueryKey[A]
@@ -9,14 +10,13 @@ trait QueryValue[-A]
910
object QueryValue extends QueryValueInstances
1011
sealed trait QueryValueInstances1:
1112
implicit final val stringQueryValue: QueryValue[String] = ???
13+
implicit final val noneQueryValue: QueryValue[None.type] = ???
1214

1315
sealed trait QueryValueInstances extends QueryValueInstances1:
1416
implicit final def optionQueryValue[A: QueryValue]: QueryValue[Option[A]] = ???
15-
implicit final val noneQueryValue: QueryValue[None.type] = ???
1617

1718
trait QueryKeyValue[A]
1819
object QueryKeyValue:
1920
implicit def tuple2QueryKeyValue[K: QueryKey, V: QueryValue]: QueryKeyValue[(K, V)] = ???
2021

21-
2222
@main def Test = summon[QueryKeyValue[(String, None.type)]]

0 commit comments

Comments
 (0)