aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/dotty/tools/dotc/transform/Mixin.scala19
-rw-r--r--src/dotty/tools/dotc/typer/Typer.scala37
-rw-r--r--tests/neg/i1263.scala33
-rw-r--r--tests/neg/traitParamsMixin.scala2
-rw-r--r--tests/neg/traitParamsTyper.scala2
-rw-r--r--tests/pos/i1444.scala14
-rw-r--r--tests/run/i1263.scala3
7 files changed, 95 insertions, 15 deletions
diff --git a/src/dotty/tools/dotc/transform/Mixin.scala b/src/dotty/tools/dotc/transform/Mixin.scala
index 8cdc82f7a..27cfc835a 100644
--- a/src/dotty/tools/dotc/transform/Mixin.scala
+++ b/src/dotty/tools/dotc/transform/Mixin.scala
@@ -189,16 +189,17 @@ class Mixin extends MiniPhaseTransform with SymTransformer { thisTransform =>
var argNum = 0
def nextArgument() = initArgs.get(mixin) match {
case Some(arguments) =>
- try arguments(argNum) finally argNum += 1
+ val result = arguments(argNum)
+ argNum += 1
+ result
case None =>
- val (msg, pos) = impl.parents.find(_.tpe.typeSymbol == mixin) match {
- case Some(parent) => ("lacks argument list", parent.pos)
- case None =>
- ("""is indirectly implemented,
- |needs to be implemented directly so that arguments can be passed""".stripMargin,
- cls.pos)
- }
- ctx.error(i"parameterized $mixin $msg", pos)
+ assert(
+ impl.parents.forall(_.tpe.typeSymbol != mixin),
+ i"missing parameters for $mixin from $impl should have been caught in typer")
+ ctx.error(
+ em"""parameterized $mixin is indirectly implemented,
+ |needs to be implemented directly so that arguments can be passed""",
+ cls.pos)
EmptyTree
}
diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala
index 2b690ef51..81e6e9430 100644
--- a/src/dotty/tools/dotc/typer/Typer.scala
+++ b/src/dotty/tools/dotc/typer/Typer.scala
@@ -1132,8 +1132,43 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
def typedClassDef(cdef: untpd.TypeDef, cls: ClassSymbol)(implicit ctx: Context) = track("typedClassDef") {
val TypeDef(name, impl @ Template(constr, parents, self, _)) = cdef
val superCtx = ctx.superCallContext
+
+ /** If `ref` is an implicitly parameterized trait, pass an implicit argument list.
+ * Otherwise, if `ref` is a parameterized trait, error.
+ * Note: Traits and classes currently always have at least an empty parameter list ()
+ * before the implicit parameters (this is inserted if not given in source).
+ * We skip this parameter list when deciding whether a trait is parameterless or not.
+ * @param ref The tree referring to the (parent) trait
+ * @param psym Its type symbol
+ * @param cinfo The info of its constructor
+ */
+ def maybeCall(ref: Tree, psym: Symbol, cinfo: Type): Tree = cinfo match {
+ case cinfo: PolyType =>
+ maybeCall(ref, psym, cinfo.resultType)
+ case cinfo @ MethodType(Nil, _) if cinfo.resultType.isInstanceOf[ImplicitMethodType] =>
+ val icall = New(ref).select(nme.CONSTRUCTOR).appliedToNone
+ typedExpr(untpd.TypedSplice(icall))(superCtx)
+ case cinfo @ MethodType(Nil, _) if !cinfo.resultType.isInstanceOf[MethodType] =>
+ ref
+ case cinfo: MethodType =>
+ if (!ctx.erasedTypes) { // after constructors arguments are passed in super call.
+ typr.println(i"constr type: $cinfo")
+ ctx.error(em"parameterized $psym lacks argument list", ref.pos)
+ }
+ ref
+ case _ =>
+ ref
+ }
+
def typedParent(tree: untpd.Tree): Tree =
- if (tree.isType) typedType(tree)(superCtx)
+ if (tree.isType) {
+ val result = typedType(tree)(superCtx)
+ val psym = result.tpe.typeSymbol
+ if (psym.is(Trait) && !cls.is(Trait) && !cls.superClass.isSubClass(psym))
+ maybeCall(result, psym, psym.primaryConstructor.info)
+ else
+ result
+ }
else {
val result = typedExpr(tree)(superCtx)
checkParentCall(result, cls)
diff --git a/tests/neg/i1263.scala b/tests/neg/i1263.scala
new file mode 100644
index 000000000..e6d8c37b5
--- /dev/null
+++ b/tests/neg/i1263.scala
@@ -0,0 +1,33 @@
+object Test {
+ trait Foo(val s: String)
+
+ val foo1 = new Foo("bar") {}
+ val foo2 = new Foo { override val s = "bar" } // error: parameterized trait lacks argument list
+ def main(args: Array[String]): Unit = {
+ assert(foo1.s == "bar")
+ assert(foo2.s == "bar")
+ }
+}
+object Test1 {
+ trait Foo(private val s0: String) {
+ def s = s0
+ }
+
+ val foo1 = new Foo("bar") {}
+ def main(args: Array[String]): Unit = {
+ assert(foo1.s == "bar")
+ }
+}
+object Test2 {
+ trait Foo(protected val s: String)
+
+ val foo1 = new Foo("bar") {}
+}
+object Test3 {
+ trait Foo(final val s: String)
+
+ val foo1 = new Foo("bar") {}
+ def main(args: Array[String]): Unit = {
+ assert(foo1.s == "bar")
+ }
+}
diff --git a/tests/neg/traitParamsMixin.scala b/tests/neg/traitParamsMixin.scala
index dfb9fbe2f..aa91012d5 100644
--- a/tests/neg/traitParamsMixin.scala
+++ b/tests/neg/traitParamsMixin.scala
@@ -2,8 +2,6 @@ trait T(x: Int) {
def f = x
}
-class C extends T // error
-
trait U extends T
class D extends U { // error
diff --git a/tests/neg/traitParamsTyper.scala b/tests/neg/traitParamsTyper.scala
index e97906b50..c4b612033 100644
--- a/tests/neg/traitParamsTyper.scala
+++ b/tests/neg/traitParamsTyper.scala
@@ -2,6 +2,8 @@ trait T(x: Int) {
def f = x
}
+class C extends T // error
+
class C(x: Int) extends T() // error
trait U extends C with T
diff --git a/tests/pos/i1444.scala b/tests/pos/i1444.scala
new file mode 100644
index 000000000..da858d50f
--- /dev/null
+++ b/tests/pos/i1444.scala
@@ -0,0 +1,14 @@
+object Test {
+
+class Cls(implicit x:X)
+class ClsImpl extends Cls //this works
+
+trait Tr1(implicit x:X)
+class TrtImpl extends Tr1 //Compiler: Error: parameterized trait Tr1 lacks argument list
+
+trait Tr2()(implicit x:X)
+class Tr2Impl extends Tr2() //this works
+
+trait X
+implicit object AnX extends X
+}
diff --git a/tests/run/i1263.scala b/tests/run/i1263.scala
index 630e5758e..e97606ef6 100644
--- a/tests/run/i1263.scala
+++ b/tests/run/i1263.scala
@@ -2,10 +2,8 @@ object Test {
trait Foo(val s: String)
val foo1 = new Foo("bar") {}
- val foo2 = new Foo { override val s = "bar" }
def main(args: Array[String]): Unit = {
assert(foo1.s == "bar")
- assert(foo2.s == "bar")
}
}
object Test1 {
@@ -22,7 +20,6 @@ object Test2 {
trait Foo(protected val s: String)
val foo1 = new Foo("bar") {}
- val foo2 = new Foo { override val s = "bar" }
}
object Test3 {
trait Foo(final val s: String)