Skip to content

Commit 93043be

Browse files
committed
Refactor handling of class parents in Typer
1 parent a503b7a commit 93043be

File tree

9 files changed

+81
-104
lines changed

9 files changed

+81
-104
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ class ReTyper(nestingLevel: Int = 0) extends Typer(nestingLevel) with ReChecking
108108

109109
override def completeAnnotations(mdef: untpd.MemberDef, sym: Symbol)(using Context): Unit = ()
110110

111-
override def ensureConstrCall(cls: ClassSymbol, parent: Tree)(using Context): Tree =
111+
override def ensureConstrCall(cls: ClassSymbol, parent: Tree, psym: Symbol)(using Context): Tree =
112112
parent
113113

114114
override def handleUnexpectedFunType(tree: untpd.Apply, fun: Tree)(using Context): Tree = fun.tpe match {

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

Lines changed: 54 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -2426,65 +2426,29 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
24262426
val TypeDef(name, impl @ Template(constr, _, self, _)) = cdef: @unchecked
24272427
val parents = impl.parents
24282428
val superCtx = ctx.superCallContext
2429-
2430-
/** If `ref` is an implicitly parameterized trait, pass an implicit argument list.
2431-
* Otherwise, if `ref` is a parameterized trait, error.
2432-
* Note: Traits and classes have sometimes a synthesized empty parameter list ()
2433-
* in front or after the implicit parameter(s). See NamerOps.normalizeIfConstructor.
2434-
* We synthesize a () argument at the correct place in this case.
2435-
* @param ref The tree referring to the (parent) trait
2436-
* @param psym Its type symbol
2437-
*/
2438-
def maybeCall(ref: Tree, psym: Symbol): Tree =
2439-
def appliedRef =
2440-
typedExpr(untpd.New(untpd.TypedSplice(ref)(using superCtx), Nil))(using superCtx)
2441-
def dropContextual(tp: Type): Type = tp.stripPoly match
2442-
case mt: MethodType if mt.isContextualMethod => dropContextual(mt.resType)
2443-
case _ => tp
2444-
psym.primaryConstructor.info.stripPoly match
2445-
case cinfo @ MethodType(Nil)
2446-
if cinfo.resultType.isImplicitMethod && !cinfo.resultType.isContextualMethod =>
2447-
appliedRef
2448-
case cinfo =>
2449-
val cinfo1 = dropContextual(cinfo)
2450-
cinfo1 match
2451-
case cinfo1 @ MethodType(Nil) if !cinfo1.resultType.isInstanceOf[MethodType] =>
2452-
if cinfo1 ne cinfo then appliedRef else ref
2453-
case cinfo1: MethodType if !ctx.erasedTypes =>
2454-
report.error(ParameterizedTypeLacksArguments(psym), ref.srcPos)
2455-
ref
2456-
case _ =>
2457-
ref
2458-
24592429
val seenParents = mutable.Set[Symbol]()
24602430

2461-
def typedParent(tree: untpd.Tree): Tree = {
2462-
def isTreeType(t: untpd.Tree): Boolean = t match {
2463-
case _: untpd.Apply => false
2464-
case _ => true
2465-
}
2466-
var result =
2467-
if isTreeType(tree) then typedType(tree)(using superCtx)
2468-
else typedExpr(tree)(using superCtx)
2469-
val psym = result.tpe.dealias.typeSymbol
2470-
if (seenParents.contains(psym) && !cls.isRefinementClass) {
2471-
// Desugaring can adds parents to classes, but we don't want to emit an
2431+
def typedParent(tree: untpd.Tree): Tree =
2432+
val parent = tree match
2433+
case _: untpd.Apply => typedExpr(tree)(using superCtx)
2434+
case _ => typedType(tree)(using superCtx)
2435+
val psym = parent.tpe.dealias.typeSymbol
2436+
if seenParents.contains(psym) && !cls.isRefinementClass then
2437+
// Desugaring can add parents to classes, but we don't want to emit an
24722438
// error if the same parent was explicitly added in user code.
2473-
if (!tree.span.isSourceDerived)
2439+
if !tree.span.isSourceDerived then
24742440
return EmptyTree
2475-
2476-
if (!ctx.isAfterTyper) report.error(i"$psym is extended twice", tree.srcPos)
2477-
}
2478-
else seenParents += psym
2479-
if (tree.isType) {
2441+
if !ctx.isAfterTyper then report.error(i"$psym is extended twice", tree.srcPos)
2442+
else
2443+
seenParents += psym
2444+
val result = ensureConstrCall(cls, parent, psym)(using superCtx)
2445+
if result.isType then
24802446
checkSimpleKinded(result) // Not needed for constructor calls, as type arguments will be inferred.
2481-
if (psym.is(Trait) && !cls.is(Trait) && !cls.superClass.isSubClass(psym))
2482-
result = maybeCall(result, psym)
2483-
}
2484-
else checkParentCall(result, cls)
2485-
if (cls is Case) checkCaseInheritance(psym, cls, tree.srcPos)
2447+
else
2448+
checkParentCall(result, cls)
2449+
if cls is Case then
2450+
checkCaseInheritance(psym, cls, tree.srcPos)
24862451
result
2487-
}
24882452

24892453
def ensureCorrectSuperClass(): Unit =
24902454
val parents0 = cls.classInfo.declaredParents
@@ -2499,23 +2463,27 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
24992463
/** Augment `ptrees` to have the same class symbols as `parents`. Generate TypeTrees
25002464
* or New trees to fill in any parents for which no tree exists yet.
25012465
*/
2502-
def parentTrees(parents: List[Type], ptrees: List[Tree]): List[Tree] = parents match
2503-
case parent :: parents1 =>
2504-
val psym = parent.classSymbol
2505-
def hasSameParent(ptree: Tree) = ptree.tpe.classSymbol == psym
2506-
ptrees match
2507-
case ptree :: ptrees1 if hasSameParent(ptree) =>
2508-
ptree :: parentTrees(parents1, ptrees1)
2509-
case ptree :: ptrees1 if ptrees1.exists(hasSameParent) =>
2510-
ptree :: parentTrees(parents, ptrees1)
2511-
case _ =>
2512-
var added: Tree = TypeTree(parent).withSpan(cdef.nameSpan.focus)
2513-
if psym.is(Trait) && psym.primaryConstructor.info.takesImplicitParams then
2514-
// classes get a constructor separately using a different context
2515-
added = ensureConstrCall(cls, added)(using superCtx)
2516-
added :: parentTrees(parents1, ptrees)
2517-
case _ =>
2518-
ptrees
2466+
def parentTrees(parents: List[Type], ptrees: List[Tree]): List[Tree] =
2467+
if ptrees.exists(_.tpe.isError) then ptrees
2468+
else parents match
2469+
case parent :: parents1 =>
2470+
val psym = parent.classSymbol
2471+
def hasSameParent(ptree: Tree) =
2472+
psym == (
2473+
if ptree.symbol.isConstructor then ptree.symbol.owner
2474+
else ptree.tpe.classSymbol
2475+
)
2476+
ptrees match
2477+
case ptree :: ptrees1 if hasSameParent(ptree) =>
2478+
ptree :: parentTrees(parents1, ptrees1)
2479+
case ptree :: ptrees1 if ptrees1.exists(hasSameParent) =>
2480+
ptree :: parentTrees(parents, ptrees1)
2481+
case _ =>
2482+
val added: Tree = ensureConstrCall(
2483+
cls, TypeTree(parent).withSpan(cdef.nameSpan.focus), psym)(using superCtx)
2484+
added :: parentTrees(parents1, ptrees)
2485+
case _ =>
2486+
ptrees
25192487

25202488
/** Checks if one of the decls is a type with the same name as class type member in selfType */
25212489
def classExistsOnSelf(decls: Scope, self: tpd.ValDef): Boolean = {
@@ -2538,10 +2506,9 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
25382506
ensureCorrectSuperClass()
25392507
completeAnnotations(cdef, cls)
25402508
val constr1 = typed(constr).asInstanceOf[DefDef]
2541-
val parents0 = parentTrees(
2509+
val parents1 = parentTrees(
25422510
cls.classInfo.declaredParents,
25432511
parents.mapconserve(typedParent).filterConserve(!_.isEmpty))
2544-
val parents1 = ensureConstrCall(cls, parents0)(using superCtx)
25452512
val firstParentTpe = parents1.head.tpe.dealias
25462513
val firstParent = firstParentTpe.typeSymbol
25472514

@@ -2610,23 +2577,22 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
26102577
protected def addAccessorDefs(cls: Symbol, body: List[Tree])(using Context): List[Tree] =
26112578
PrepareInlineable.addAccessorDefs(cls, body)
26122579

2613-
/** If this is a real class, make sure its first parent is a
2614-
* constructor call. Cannot simply use a type. Overridden in ReTyper.
2615-
*/
2616-
def ensureConstrCall(cls: ClassSymbol, parents: List[Tree])(using Context): List[Tree] = parents match
2617-
case parents @ (first :: others) =>
2618-
parents.derivedCons(ensureConstrCall(cls, first), others)
2619-
case parents =>
2620-
parents
2621-
2622-
/** If this is a real class, make sure its first parent is a
2623-
* constructor call. Cannot simply use a type. Overridden in ReTyper.
2580+
/** Turn a parent type into a constructor call where needed. This is the case where
2581+
* - we are in a Scala class or module (not a Java class, nor a trait), and
2582+
* - the parent symbol is a non-trait class, or
2583+
* - the parent symbol is a trait that takes at least one (explicit or implicit) parameter
2584+
* and the parent symbol is directly extended by the current class (i.e. not
2585+
* extended by the superclass).
26242586
*/
2625-
def ensureConstrCall(cls: ClassSymbol, parent: Tree)(using Context): Tree =
2626-
if (parent.isType && !cls.is(Trait) && !cls.is(JavaDefined))
2627-
typed(untpd.New(untpd.TypedSplice(parent), Nil))
2628-
else
2629-
parent
2587+
def ensureConstrCall(cls: ClassSymbol, parent: Tree, psym: Symbol)(using Context): Tree =
2588+
def takesParams(info: Type): Boolean = info.stripPoly match
2589+
case mt @ MethodType(pnames) => pnames.nonEmpty || takesParams(mt.resType)
2590+
case _ => false
2591+
if parent.isType && !cls.is(Trait) && !cls.is(JavaDefined) && psym.isClass
2592+
&& (!psym.is(Trait)
2593+
|| takesParams(psym.primaryConstructor.info) && !cls.superClass.isSubClass(psym))
2594+
then typed(untpd.New(untpd.TypedSplice(parent), Nil))
2595+
else parent
26302596

26312597
def localDummy(cls: ClassSymbol, impl: untpd.Template)(using Context): Symbol =
26322598
newLocalDummy(cls, impl.span)

tests/neg/i14432c.check

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
11
-- Error: tests/neg/i14432c.scala:12:18 --------------------------------------------------------------------------------
2-
12 |class Bar extends example.Foo(23) { // error // error: cant access private[example] ctor
2+
12 |class Bar extends example.Foo(23) { // error: cant access private[example] ctor
33
| ^^^^^^^^^^^
44
| constructor Foo cannot be accessed as a member of example.Foo from class Bar.
5-
-- Error: tests/neg/i14432c.scala:12:6 ---------------------------------------------------------------------------------
6-
12 |class Bar extends example.Foo(23) { // error // error: cant access private[example] ctor
7-
| ^
8-
| constructor Foo cannot be accessed as a member of example.Foo from class Bar.
95
-- Error: tests/neg/i14432c.scala:16:43 --------------------------------------------------------------------------------
106
16 | val mFoo = summon[Mirror.Of[example.Foo]] // error: no mirror
117
| ^

tests/neg/i14432c.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ package example {
99

1010
}
1111

12-
class Bar extends example.Foo(23) { // error // error: cant access private[example] ctor
12+
class Bar extends example.Foo(23) { // error: cant access private[example] ctor
1313

1414
// however we can not provide an anonymous mirror
1515
// at this call site because the constructor is not accessible.

tests/neg/i6778.check

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
-- [E104] Syntax Error: tests/neg/i6778.scala:3:27 ---------------------------------------------------------------------
2-
3 |class Bar extends Foo with A(10) // error
2+
3 |class Bar extends Foo with A(10) // error // error
33
| ^^^^^
44
| class A is not a trait
55
|
66
| longer explanation available when compiling with `-explain`
7+
-- Error: tests/neg/i6778.scala:3:6 ------------------------------------------------------------------------------------
8+
3 |class Bar extends Foo with A(10) // error // error
9+
| ^
10+
| missing argument for parameter x of constructor A in class A: (x: Int): A

tests/neg/i6778.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
trait Foo
22
class A(x: Int)
3-
class Bar extends Foo with A(10) // error
3+
class Bar extends Foo with A(10) // error // error

tests/neg/i7613.check

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,4 @@
11
-- Error: tests/neg/i7613.scala:10:16 ----------------------------------------------------------------------------------
2-
10 | new BazLaws[A] {} // error // error
2+
10 | new BazLaws[A] {} // error
33
| ^
44
| No given instance of type Baz[A] was found for parameter x$1 of constructor BazLaws in trait BazLaws
5-
-- Error: tests/neg/i7613.scala:10:2 -----------------------------------------------------------------------------------
6-
10 | new BazLaws[A] {} // error // error
7-
| ^
8-
| No given instance of type Bar[A] was found for parameter x$1 of constructor BarLaws in trait BarLaws

tests/neg/i7613.scala

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,4 @@ trait BarLaws[A](using Bar[A]) extends FooLaws[A]
77
trait BazLaws[A](using Baz[A]) extends BarLaws[A]
88

99
def instance[A](using Foo[A]): BazLaws[A] =
10-
new BazLaws[A] {} // error // error
11-
10+
new BazLaws[A] {} // error

tests/pos/i15976.scala

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
final abstract class ForcedRecompilationToken[T]
2+
object ForcedRecompilationToken {
3+
implicit def default: ForcedRecompilationToken["abc"] = null
4+
}
5+
6+
class BadNoParens[T](implicit ev: ForcedRecompilationToken[T])
7+
8+
// error
9+
object X extends BadNoParens
10+
11+
// ok
12+
object Y extends BadNoParens()
13+
14+
object App extends App {
15+
println("compiled")
16+
}

0 commit comments

Comments
 (0)