Skip to content

Commit a084779

Browse files
Adapt context-bound-companion logic to use AndType of AppliedTypes
Instead of AppliedType of OrTypes, which relied on the distribution of AndTypes when inheriting multiple context bound companions of the same name
1 parent 0441af0 commit a084779

File tree

6 files changed

+64
-34
lines changed

6 files changed

+64
-34
lines changed

compiler/src/dotty/tools/dotc/core/Definitions.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -460,7 +460,7 @@ class Definitions {
460460
@tu lazy val CBCompanion: TypeSymbol = // type `<context-bound-companion>`[-Refs]
461461
enterPermanentSymbol(tpnme.CBCompanion,
462462
TypeBounds(NothingType,
463-
HKTypeLambda(tpnme.syntheticTypeParamName(0) :: Nil, Contravariant :: Nil)(
463+
HKTypeLambda(tpnme.syntheticTypeParamName(0) :: Nil)(
464464
tl => TypeBounds.empty :: Nil,
465465
tl => AnyType))).asType
466466

compiler/src/dotty/tools/dotc/core/NamerOps.scala

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,7 @@ object NamerOps:
307307
* The context-bound companion has as name the name of `tsym` translated to
308308
* a term name. We create a synthetic val of the form
309309
*
310-
* val A: `<context-bound-companion>`[witnessRef1 | ... | witnessRefN]
310+
* val A: `<context-bound-companion>`[witnessRef1] & ... & `<context-bound-companion>`[witnessRefN]
311311
*
312312
* where
313313
*
@@ -325,8 +325,7 @@ object NamerOps:
325325
prefix.select(params.find(_.name == witnessName).get)
326326
else
327327
witnessNames.map(TermRef(prefix, _))
328-
val cbtype = defn.CBCompanion.typeRef.appliedTo:
329-
witnessRefs.reduce[Type](OrType(_, _, soft = false))
328+
val cbtype = witnessRefs.map(defn.CBCompanion.typeRef.appliedTo).reduce(AndType.apply)
330329
val cbc = newSymbol(
331330
ctx.owner, companionName,
332331
(tsym.flagsUNSAFE & (AccessFlags)).toTermFlags | Synthetic,

compiler/src/dotty/tools/dotc/core/SymUtils.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ class SymUtils:
8888
}
8989

9090
def isContextBoundCompanion(using Context): Boolean =
91-
self.is(Synthetic) && self.infoOrCompleter.typeSymbol == defn.CBCompanion
91+
self.is(Synthetic) && self.infoOrCompleter.isContextBoundCompanion
9292

9393
def isDummyCaptureParam(using Context): Boolean =
9494
self.isAllOf(CaptureParam) && !(self.isClass || self.is(Method))

compiler/src/dotty/tools/dotc/core/Types.scala

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,14 @@ object Types extends TypeUtils {
466466
case AppliedType(tycon: TypeRef, arg :: Nil) => defn.isInto(tycon.symbol)
467467
case _ => false
468468

469+
/** Is this type of the form `<context-bound-companion>[Ref1] & ... & <context-bound-companion>[RefN]`?
470+
* Where the intersection may be introduced by `NamerOps.addContextBoundCompanionFor`
471+
* or by inheriting multiple context bound companions for the same name.
472+
*/
473+
def isContextBoundCompanion(using Context): Boolean = this.widen match
474+
case AndType(tp1, tp2) => tp1.isContextBoundCompanion.ensuring(_ == tp2.isContextBoundCompanion)
475+
case tp => tp.typeSymbol == defn.CBCompanion
476+
469477
/** Is this type a legal target type for an implicit conversion, so that
470478
* no `implicitConversions` language import is necessary?
471479
*/

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

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -928,7 +928,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
928928
// Otherwise, if the qualifier is a context bound companion, handle
929929
// by selecting a witness in typedCBSelect
930930
def tryCBCompanion() =
931-
if qual.tpe.typeSymbol == defn.CBCompanion then
931+
if qual.tpe.isContextBoundCompanion then
932932
typedCBSelect(tree0, pt, qual)
933933
else EmptyTree
934934

@@ -997,13 +997,13 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
997997
* alternatives referred to by `witnesses`.
998998
* @param prevs a list of (ref tree, typer state, term ref) tripls that
999999
* represents previously identified alternatives
1000-
* @param witnesses a type of the form ref_1 | ... | ref_n containing references
1000+
* @param witnesses a type of the form `isContextBoundCompanion` containing references
10011001
* still to be considered.
10021002
*/
1003-
def tryAlts(prevs: Alts, witnesses: Type): Alts = witnesses match
1004-
case OrType(wit1, wit2) =>
1003+
def tryAlts(prevs: Alts, witnesses: Type): Alts = witnesses.widen match
1004+
case AndType(wit1, wit2) =>
10051005
tryAlts(tryAlts(prevs, wit1), wit2)
1006-
case witness: TermRef =>
1006+
case AppliedType(_, List(witness: TermRef)) =>
10071007
val altQual = tpd.ref(witness).withSpan(qual.span)
10081008
val altCtx = ctx.fresh.setNewTyperState()
10091009
val alt = typedSelectWithAdapt(tree, pt, altQual)(using altCtx)
@@ -1015,19 +1015,17 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
10151015
if comparisons.exists(_ == 1) then prevs
10161016
else current :: prevs.zip(comparisons).collect{ case (prev, cmp) if cmp != -1 => prev }
10171017

1018-
qual.tpe.widen match
1019-
case AppliedType(_, arg :: Nil) =>
1020-
tryAlts(Nil, arg) match
1021-
case Nil => EmptyTree
1022-
case (best @ (bestTree, bestState, _)) :: Nil =>
1023-
bestState.commit()
1024-
bestTree
1025-
case multiAlts =>
1026-
report.error(
1027-
em"""Ambiguous witness reference. None of the following alternatives is more specific than the other:
1028-
|${multiAlts.map((alt, _, witness) => i"\n $witness.${tree.name}: ${alt.tpe.widen}")}""",
1029-
tree.srcPos)
1030-
EmptyTree
1018+
tryAlts(Nil, qual.tpe) match
1019+
case Nil => EmptyTree
1020+
case (best @ (bestTree, bestState, _)) :: Nil =>
1021+
bestState.commit()
1022+
bestTree
1023+
case multiAlts =>
1024+
report.error(
1025+
em"""Ambiguous witness reference. None of the following alternatives is more specific than the other:
1026+
|${multiAlts.map((alt, _, witness) => i"\n $witness.${tree.name}: ${alt.tpe.widen}")}""",
1027+
tree.srcPos)
1028+
EmptyTree
10311029
end typedCBSelect
10321030

10331031
def typedSelect(tree: untpd.Select, pt: Type)(using Context): Tree = {

tests/pos/cb-companion-joins.scala

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,39 @@ trait M[Self]:
77
trait Num[Self]:
88
def zero: Self
99

10-
trait A extends M[A]
11-
trait B extends M[A]
12-
13-
trait AA:
14-
type X: M
15-
trait BB:
16-
type X: Num
17-
class CC[X1: {M, Num}] extends AA, BB:
18-
type X = X1
19-
X.zero
20-
X.unit
10+
object Test1:
11+
trait X extends M[X]
12+
trait Y extends M[Y]
13+
14+
object Test2:
15+
trait A[X: Num]:
16+
X.zero
17+
trait B[X: {M, Num}]:
18+
X.unit
19+
X.zero
20+
21+
object Test3:
22+
23+
trait A:
24+
type X: M
25+
X.unit
26+
27+
trait B:
28+
type X: Num
29+
X.zero
30+
31+
trait C extends A, B:
32+
X.zero
33+
X.unit
34+
35+
class AA[Y: M] extends A:
36+
type X = Y
37+
X.unit
38+
Y.unit
39+
40+
class CC[Y: {M, Num}] extends C:
41+
type X = Y
42+
X.zero
43+
X.unit
44+
Y.zero
45+
Y.unit

0 commit comments

Comments
 (0)