Skip to content

Commit e5315cc

Browse files
oderskysmarter
authored andcommitted
Fix #1444: Add implicit arguments to supertraits
If a super trait is given as a type (i.e. no argument list), implicit args were not passed. This is fixed now. Also, we now check for parameterized traits lacking type arguments in Typer instead of in Mixin. Fixes #1444.
1 parent 5a5f9d7 commit e5315cc

File tree

7 files changed

+95
-15
lines changed

7 files changed

+95
-15
lines changed

src/dotty/tools/dotc/transform/Mixin.scala

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -189,16 +189,17 @@ class Mixin extends MiniPhaseTransform with SymTransformer { thisTransform =>
189189
var argNum = 0
190190
def nextArgument() = initArgs.get(mixin) match {
191191
case Some(arguments) =>
192-
try arguments(argNum) finally argNum += 1
192+
val result = arguments(argNum)
193+
argNum += 1
194+
result
193195
case None =>
194-
val (msg, pos) = impl.parents.find(_.tpe.typeSymbol == mixin) match {
195-
case Some(parent) => ("lacks argument list", parent.pos)
196-
case None =>
197-
("""is indirectly implemented,
198-
|needs to be implemented directly so that arguments can be passed""".stripMargin,
199-
cls.pos)
200-
}
201-
ctx.error(i"parameterized $mixin $msg", pos)
196+
assert(
197+
impl.parents.forall(_.tpe.typeSymbol != mixin),
198+
i"missing parameters for $mixin from $impl should have been caught in typer")
199+
ctx.error(
200+
em"""parameterized $mixin is indirectly implemented,
201+
|needs to be implemented directly so that arguments can be passed""",
202+
cls.pos)
202203
EmptyTree
203204
}
204205

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

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1132,8 +1132,43 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
11321132
def typedClassDef(cdef: untpd.TypeDef, cls: ClassSymbol)(implicit ctx: Context) = track("typedClassDef") {
11331133
val TypeDef(name, impl @ Template(constr, parents, self, _)) = cdef
11341134
val superCtx = ctx.superCallContext
1135+
1136+
/** If `ref` is an implicitly parameterized trait, pass an implicit argument list.
1137+
* Otherwise, if `ref` is a parameterized trait, error.
1138+
* Note: Traits and classes currently always have at least an empty parameter list ()
1139+
* before the implicit parameters (this is inserted if not given in source).
1140+
* We skip this parameter list when deciding whether a trait is parameterless or not.
1141+
* @param ref The tree referring to the (parent) trait
1142+
* @param psym Its type symbol
1143+
* @param cinfo The info of its constructor
1144+
*/
1145+
def maybeCall(ref: Tree, psym: Symbol, cinfo: Type): Tree = cinfo match {
1146+
case cinfo: PolyType =>
1147+
maybeCall(ref, psym, cinfo.resultType)
1148+
case cinfo @ MethodType(Nil, _) if cinfo.resultType.isInstanceOf[ImplicitMethodType] =>
1149+
val icall = New(ref).select(nme.CONSTRUCTOR).appliedToNone
1150+
typedExpr(untpd.TypedSplice(icall))(superCtx)
1151+
case cinfo @ MethodType(Nil, _) if !cinfo.resultType.isInstanceOf[MethodType] =>
1152+
ref
1153+
case cinfo: MethodType =>
1154+
if (!ctx.erasedTypes) { // after constructors arguments are passed in super call.
1155+
typr.println(i"constr type: $cinfo")
1156+
ctx.error(em"parameterized $psym lacks argument list", ref.pos)
1157+
}
1158+
ref
1159+
case _ =>
1160+
ref
1161+
}
1162+
11351163
def typedParent(tree: untpd.Tree): Tree =
1136-
if (tree.isType) typedType(tree)(superCtx)
1164+
if (tree.isType) {
1165+
val result = typedType(tree)(superCtx)
1166+
val psym = result.tpe.typeSymbol
1167+
if (psym.is(Trait) && !cls.is(Trait) && !cls.superClass.isSubClass(psym))
1168+
maybeCall(result, psym, psym.primaryConstructor.info)
1169+
else
1170+
result
1171+
}
11371172
else {
11381173
val result = typedExpr(tree)(superCtx)
11391174
checkParentCall(result, cls)

tests/neg/i1263.scala

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
object Test {
2+
trait Foo(val s: String)
3+
4+
val foo1 = new Foo("bar") {}
5+
val foo2 = new Foo { override val s = "bar" } // error: parameterized trait lacks argument list
6+
def main(args: Array[String]): Unit = {
7+
assert(foo1.s == "bar")
8+
assert(foo2.s == "bar")
9+
}
10+
}
11+
object Test1 {
12+
trait Foo(private val s0: String) {
13+
def s = s0
14+
}
15+
16+
val foo1 = new Foo("bar") {}
17+
def main(args: Array[String]): Unit = {
18+
assert(foo1.s == "bar")
19+
}
20+
}
21+
object Test2 {
22+
trait Foo(protected val s: String)
23+
24+
val foo1 = new Foo("bar") {}
25+
}
26+
object Test3 {
27+
trait Foo(final val s: String)
28+
29+
val foo1 = new Foo("bar") {}
30+
def main(args: Array[String]): Unit = {
31+
assert(foo1.s == "bar")
32+
}
33+
}

tests/neg/traitParamsMixin.scala

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@ trait T(x: Int) {
22
def f = x
33
}
44

5-
class C extends T // error
6-
75
trait U extends T
86

97
class D extends U { // error

tests/neg/traitParamsTyper.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ trait T(x: Int) {
22
def f = x
33
}
44

5+
class C extends T // error
6+
57
class C(x: Int) extends T() // error
68

79
trait U extends C with T

tests/pos/i1444.scala

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
object Test {
2+
3+
class Cls(implicit x:X)
4+
class ClsImpl extends Cls //this works
5+
6+
trait Tr1(implicit x:X)
7+
class TrtImpl extends Tr1 //Compiler: Error: parameterized trait Tr1 lacks argument list
8+
9+
trait Tr2()(implicit x:X)
10+
class Tr2Impl extends Tr2() //this works
11+
12+
trait X
13+
implicit object AnX extends X
14+
}

tests/run/i1263.scala

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,8 @@ object Test {
22
trait Foo(val s: String)
33

44
val foo1 = new Foo("bar") {}
5-
val foo2 = new Foo { override val s = "bar" }
65
def main(args: Array[String]): Unit = {
76
assert(foo1.s == "bar")
8-
assert(foo2.s == "bar")
97
}
108
}
119
object Test1 {
@@ -22,7 +20,6 @@ object Test2 {
2220
trait Foo(protected val s: String)
2321

2422
val foo1 = new Foo("bar") {}
25-
val foo2 = new Foo { override val s = "bar" }
2623
}
2724
object Test3 {
2825
trait Foo(final val s: String)

0 commit comments

Comments
 (0)