aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/dotty/tools/dotc/typer/Namer.scala56
-rw-r--r--test/dotc/tests.scala1
-rw-r--r--tests/neg/named-params.scala22
3 files changed, 75 insertions, 4 deletions
diff --git a/src/dotty/tools/dotc/typer/Namer.scala b/src/dotty/tools/dotc/typer/Namer.scala
index c8d2b3418..13ed96249 100644
--- a/src/dotty/tools/dotc/typer/Namer.scala
+++ b/src/dotty/tools/dotc/typer/Namer.scala
@@ -576,8 +576,8 @@ class Namer { typer: Typer =>
/** The type signature of a ClassDef with given symbol */
override def completeInCreationContext(denot: SymDenotation): Unit = {
- /** The type of a parent constructor. Types constructor arguments
- * only if parent type contains uninstantiated type parameters.
+ /* The type of a parent constructor. Types constructor arguments
+ * only if parent type contains uninstantiated type parameters.
*/
def parentType(parent: untpd.Tree)(implicit ctx: Context): Type =
if (parent.isType) {
@@ -594,7 +594,12 @@ class Namer { typer: Typer =>
else typedAheadExpr(parent).tpe
}
- def checkedParentType(parent: untpd.Tree): Type = {
+ /* Check parent type tree `parent` for the following well-formedness conditions:
+ * (1) It must be a class type with a stable prefix (@see checkClassTypeWithStablePrefix)
+ * (2) If may not derive from itself
+ * (3) Overriding type parameters must be correctly forwarded. (@see checkTypeParamOverride)
+ */
+ def checkedParentType(parent: untpd.Tree, paramAccessors: List[Symbol]): Type = {
val ptype = parentType(parent)(ctx.superCallContext)
if (cls.isRefinementClass) ptype
else {
@@ -608,10 +613,52 @@ class Namer { typer: Typer =>
ctx.error(i"cyclic inheritance: $cls extends itself$addendum", parent.pos)
defn.ObjectType
}
+ else if (!paramAccessors.forall(checkTypeParamOverride(pt, _)))
+ defn.ObjectType
else pt
}
}
+ /* Check that every parameter with the same name as a visible named parameter in the parent
+ * class satisfies the following two conditions:
+ * (1) The overriding parameter is also named (i.e. not local/name mangled).
+ * (2) The overriding parameter is passed on directly to the parent parameter, or the
+ * parent parameter is not fully defined.
+ * @return true if conditions are satisfied, false otherwise.
+ */
+ def checkTypeParamOverride(parent: Type, paramAccessor: Symbol): Boolean = {
+ var ok = true
+ val pname = paramAccessor.name
+
+ def illegal(how: String): Unit = {
+ ctx.error(d"Illegal override of public type parameter $pname in $parent$how", paramAccessor.pos)
+ ok = false
+ }
+
+ def checkAlias(tp: Type): Unit = tp match {
+ case tp: RefinedType =>
+ if (tp.refinedName == pname)
+ tp.refinedInfo match {
+ case TypeAlias(alias) =>
+ alias match {
+ case TypeRef(pre, name1) if name1 == pname && (pre =:= cls.thisType) =>
+ // OK, parameter is passed on directly
+ case _ =>
+ illegal(d".\nParameter is both redeclared and instantiated with $alias.")
+ }
+ case _ => // OK, argument is not fully defined
+ }
+ else checkAlias(tp.parent)
+ case _ =>
+ }
+ if (parent.nonPrivateMember(paramAccessor.name).symbol.is(Param))
+ if (paramAccessor is Private)
+ illegal("\nwith private parameter. Parameter definition needs to be prefixed with `type'.")
+ else
+ checkAlias(parent)
+ ok
+ }
+
val selfInfo =
if (self.isEmpty) NoType
else if (cls.is(Module)) {
@@ -634,7 +681,8 @@ class Namer { typer: Typer =>
index(constr)
symbolOfTree(constr).ensureCompleted()
- val parentTypes = ensureFirstIsClass(parents map checkedParentType)
+ val tparamAccessors = decls.filter(_ is TypeParamAccessor).toList
+ val parentTypes = ensureFirstIsClass(parents.map(checkedParentType(_, tparamAccessors)))
val parentRefs = ctx.normalizeToClassRefs(parentTypes, cls, decls)
typr.println(s"completing $denot, parents = $parents, parentTypes = $parentTypes, parentRefs = $parentRefs")
diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala
index 421846ca2..1f4ff2da4 100644
--- a/test/dotc/tests.scala
+++ b/test/dotc/tests.scala
@@ -122,6 +122,7 @@ class tests extends CompilerTest {
@Test def neg_autoTupling = compileFile(posDir, "autoTuplingTest", args = "-language:noAutoTupling" :: Nil, xerrors = 3)
@Test def neg_autoTupling2 = compileFile(negDir, "autoTuplingTest", xerrors = 3)
@Test def neg_companions = compileFile(negDir, "companions", xerrors = 1)
+ @Test def namedParams = compileFile(negDir, "named-params", xerrors = 3)
@Test def neg_over = compileFile(negDir, "over", xerrors = 3)
@Test def neg_overrides = compileFile(negDir, "overrides", xerrors = 14)
@Test def neg_overrideClass = compileFile(negDir, "overrideClass", List("-language:Scala2"), xerrors = 1)
diff --git a/tests/neg/named-params.scala b/tests/neg/named-params.scala
new file mode 100644
index 000000000..c1f055e10
--- /dev/null
+++ b/tests/neg/named-params.scala
@@ -0,0 +1,22 @@
+package namedparams
+
+class C[type Elem, type Value](val elem: Elem) {
+ def toVal: Elem = ???
+}
+
+abstract class D[type Elem, V](elem: Elem) extends C[Elem, V](elem)
+abstract class D2[Elem, V](elem: Elem) extends C[Elem, V](elem) // error
+abstract class D3[type Elem, V](x: V) extends C[V, V](x) // error
+abstract class D4[type Elem](elem: Elem) extends C[Elem, Elem] // error
+object Test {
+ val c = new C[String, String]("A") {
+ override def toVal = elem
+ }
+ val x: c.Elem = c.elem
+
+ val c2: C { type Elem = String } = c
+
+ val c3 = new C[Elem = String, Value = Int]("B")
+ val c4 = new C[Elem = String]("C")
+ val x2: c2.Elem = c2.elem
+}