Skip to content

Commit d113e86

Browse files
committed
Generalize syncing between parent trees and parent types
Namer will augment the parent types of a class with possibly other types. Currently the only such change is a possibly leading class type, but with #7613 we wll also generate trait parents that take context parameters. The new method `parentTrees` will reflect any such changes in the parent trees in Typer.
1 parent 0195dff commit d113e86

File tree

4 files changed

+66
-39
lines changed

4 files changed

+66
-39
lines changed

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

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1237,12 +1237,43 @@ class Namer { typer: Typer =>
12371237
}
12381238
}
12391239

1240+
/** Ensure that the first type in a list of parent types Ps points to a non-trait class.
1241+
* If that's not already the case, add one. The added class type CT is determined as follows.
1242+
* First, let C be the unique class such that
1243+
* - there is a parent P_i such that P_i derives from C, and
1244+
* - for every class D: If some parent P_j, j <= i derives from D, then C derives from D.
1245+
* Then, let CT be the smallest type which
1246+
* - has C as its class symbol, and
1247+
* - for all parents P_i: If P_i derives from C then P_i <:< CT.
1248+
*/
1249+
def ensureFirstIsClass(parents: List[Type])(using Context): List[Type] =
1250+
1251+
def realClassParent(sym: Symbol): ClassSymbol =
1252+
if !sym.isClass then defn.ObjectClass
1253+
else if !sym.is(Trait) then sym.asClass
1254+
else sym.info.parents match
1255+
case parentRef :: _ => realClassParent(parentRef.typeSymbol)
1256+
case nil => defn.ObjectClass
1257+
1258+
def improve(candidate: ClassSymbol, parent: Type): ClassSymbol =
1259+
val pcls = realClassParent(parent.classSymbol)
1260+
if (pcls derivesFrom candidate) pcls else candidate
1261+
1262+
parents match
1263+
case p :: _ if p.classSymbol.isRealClass => parents
1264+
case _ =>
1265+
val pcls = parents.foldLeft(defn.ObjectClass)(improve)
1266+
typr.println(i"ensure first is class $parents%, % --> ${parents map (_ baseType pcls)}%, %")
1267+
val first = TypeComparer.glb(defn.ObjectType :: parents.map(_.baseType(pcls)))
1268+
checkFeasibleParent(first, cls.srcPos, em" in inferred superclass $first") :: parents
1269+
end ensureFirstIsClass
1270+
12401271
completeConstructor(denot)
12411272
denot.info = tempInfo
12421273

12431274
val parentTypes = defn.adjustForTuple(cls, cls.typeParams,
12441275
defn.adjustForBoxedUnit(cls,
1245-
ensureFirstIsClass(parents.map(checkedParentType(_)), cls.span)
1276+
ensureFirstIsClass(parents.map(checkedParentType(_)))
12461277
)
12471278
)
12481279
typr.println(i"completing $denot, parents = $parents%, %, parentTypes = $parentTypes%, %")

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

Lines changed: 20 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -2278,8 +2278,11 @@ class Typer extends Namer
22782278

22792279
completeAnnotations(cdef, cls)
22802280
val constr1 = typed(constr).asInstanceOf[DefDef]
2281-
val parentsWithClass = ensureFirstTreeIsClass(parents.mapconserve(typedParent).filterConserve(!_.isEmpty), cdef.nameSpan)
2282-
val parents1 = ensureConstrCall(cls, parentsWithClass)(using superCtx)
2281+
val parents0 = parentTrees(
2282+
cls.classInfo.declaredParents,
2283+
parents.mapconserve(typedParent).filterConserve(!_.isEmpty),
2284+
cdef.nameSpan)
2285+
val parents1 = ensureConstrCall(cls, parents0)(using superCtx)
22832286
val firstParentTpe = parents1.head.tpe.dealias
22842287
val firstParent = firstParentTpe.typeSymbol
22852288

@@ -2348,42 +2351,22 @@ class Typer extends Namer
23482351
protected def addAccessorDefs(cls: Symbol, body: List[Tree])(using Context): List[Tree] =
23492352
ctx.compilationUnit.inlineAccessors.addAccessorDefs(cls, body)
23502353

2351-
/** Ensure that the first type in a list of parent types Ps points to a non-trait class.
2352-
* If that's not already the case, add one. The added class type CT is determined as follows.
2353-
* First, let C be the unique class such that
2354-
* - there is a parent P_i such that P_i derives from C, and
2355-
* - for every class D: If some parent P_j, j <= i derives from D, then C derives from D.
2356-
* Then, let CT be the smallest type which
2357-
* - has C as its class symbol, and
2358-
* - for all parents P_i: If P_i derives from C then P_i <:< CT.
2354+
/** Augment `ptrees` to have the same class symbols as `parents`. Generate TypeTrees
2355+
* to fill in any parents for wich no tree exists yet.
23592356
*/
2360-
def ensureFirstIsClass(parents: List[Type], span: Span)(using Context): List[Type] = {
2361-
def realClassParent(cls: Symbol): ClassSymbol =
2362-
if (!cls.isClass) defn.ObjectClass
2363-
else if (!cls.is(Trait)) cls.asClass
2364-
else cls.info.parents match {
2365-
case parentRef :: _ => realClassParent(parentRef.typeSymbol)
2366-
case nil => defn.ObjectClass
2367-
}
2368-
def improve(candidate: ClassSymbol, parent: Type): ClassSymbol = {
2369-
val pcls = realClassParent(parent.classSymbol)
2370-
if (pcls derivesFrom candidate) pcls else candidate
2371-
}
2372-
parents match {
2373-
case p :: _ if p.classSymbol.isRealClass => parents
2374-
case _ =>
2375-
val pcls = parents.foldLeft(defn.ObjectClass)(improve)
2376-
typr.println(i"ensure first is class $parents%, % --> ${parents map (_ baseType pcls)}%, %")
2377-
val first = TypeComparer.glb(defn.ObjectType :: parents.map(_.baseType(pcls)))
2378-
checkFeasibleParent(first, ctx.source.atSpan(span), em" in inferred superclass $first") :: parents
2379-
}
2380-
}
2381-
2382-
/** Ensure that first parent tree refers to a real class. */
2383-
def ensureFirstTreeIsClass(parents: List[Tree], span: Span)(using Context): List[Tree] = parents match {
2384-
case p :: ps if p.tpe.classSymbol.isRealClass => parents
2385-
case _ => TypeTree(ensureFirstIsClass(parents.tpes, span).head).withSpan(span.focus) :: parents
2386-
}
2357+
def parentTrees(parents: List[Type], ptrees: List[Tree], span: Span)(using Context): List[Tree] = parents match
2358+
case parent :: parents1 =>
2359+
val psym = parent.classSymbol
2360+
def hasSameParent(ptree: Tree) = ptree.tpe.classSymbol == psym
2361+
ptrees match
2362+
case ptree :: ptrees1 if hasSameParent(ptree) =>
2363+
ptree :: parentTrees(parents1, ptrees1, span)
2364+
case ptree :: ptrees1 if ptrees1.exists(hasSameParent) =>
2365+
ptree :: parentTrees(parents, ptrees1, span)
2366+
case _ =>
2367+
TypeTree(parent).withSpan(span.focus) :: parentTrees(parents1, ptrees, span)
2368+
case _ =>
2369+
ptrees
23872370

23882371
/** If this is a real class, make sure its first parent is a
23892372
* constructor call. Cannot simply use a type. Overridden in ReTyper.

tests/neg/i6060.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
class I1(i2: Int) {
22
def apply(i3: Int) = 1
3-
new I1(1)(2) {} // error: too many arguments in parent constructor
3+
new I1(1)(2) {} // error: too many arguments in parent constructor // error
44
}
55

66
class I0(i1: Int) {

tests/pos/i7613.scala

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
trait Foo[A]
2+
trait Bar[A] extends Foo[A]
3+
trait Baz[A] extends Bar[A]
4+
5+
trait FooLaws[A](using Foo[A])
6+
trait BarLaws[A](using Bar[A]) extends FooLaws[A]
7+
trait BazLaws[A](using Baz[A]) extends BarLaws[A]
8+
9+
def instance1[A](using Baz[A]): BazLaws[A] =
10+
new FooLaws[A] with BarLaws[A] with BazLaws[A] {}
11+
12+
def instance2[A](using Baz[A]): BazLaws[A] =
13+
new BazLaws[A] {}

0 commit comments

Comments
 (0)