diff options
-rw-r--r-- | src/dotty/tools/dotc/typer/Namer.scala | 56 | ||||
-rw-r--r-- | test/dotc/tests.scala | 1 | ||||
-rw-r--r-- | tests/neg/named-params.scala | 22 |
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 +} |