aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2014-03-01 13:17:05 +0100
committerMartin Odersky <odersky@gmail.com>2014-03-01 13:41:00 +0100
commitd8356b6dc9221bfc38b1f167e5cfafcc9261f3d7 (patch)
tree3bb6fda0feb33680f68f61908a7076162159701e
parent5a8f4c822be82e23a0c230071673425423664442 (diff)
downloaddotty-d8356b6dc9221bfc38b1f167e5cfafcc9261f3d7.tar.gz
dotty-d8356b6dc9221bfc38b1f167e5cfafcc9261f3d7.tar.bz2
dotty-d8356b6dc9221bfc38b1f167e5cfafcc9261f3d7.zip
Reorganization of template parents.
Template parents always were constructor calls before. This is not correct because in a situation like the one elaborated in templateParents, the trait D has the class C as supertype, but it does not call its constructor (in fact, if we added a () parameter list to make it into a constructor this would be wrong because C takes parameters. Now parents can be either types or constructor calls. The logic in Namer and Typer that deals with parents is cleaned up. In particular, we now construct any synthetic class parent as a full type, before calling normalizeToClassRefs. This obviates the forwardRefs logic that needed to be done in a cleanup of Namers. Also added two more checks: (1) All parents except the first one must point to traits. (2) A trait may not call a parent class constructor.
-rw-r--r--src/dotty/tools/dotc/ast/Desugar.scala8
-rw-r--r--src/dotty/tools/dotc/core/SymDenotations.scala3
-rw-r--r--src/dotty/tools/dotc/core/TypeApplications.scala4
-rw-r--r--src/dotty/tools/dotc/parsing/Parsers.scala39
-rw-r--r--src/dotty/tools/dotc/typer/Inferencing.scala78
-rw-r--r--src/dotty/tools/dotc/typer/Namer.scala28
-rw-r--r--src/dotty/tools/dotc/typer/Typer.scala25
-rw-r--r--test/dotc/tests.scala3
-rw-r--r--tests/neg/privates.scala11
-rw-r--r--tests/neg/templateParents.scala9
-rw-r--r--tests/pos/templateParents.scala12
11 files changed, 142 insertions, 78 deletions
diff --git a/src/dotty/tools/dotc/ast/Desugar.scala b/src/dotty/tools/dotc/ast/Desugar.scala
index 0f1d1010a..473800cde 100644
--- a/src/dotty/tools/dotc/ast/Desugar.scala
+++ b/src/dotty/tools/dotc/ast/Desugar.scala
@@ -226,7 +226,7 @@ object desugar {
def productConstr(n: Int) = {
val tycon = ref(defn.ProductNClass(n).typeRef)
val targs = vparamss.head map (_.tpt)
- New(AppliedTypeTree(tycon, targs), Nil)
+ AppliedTypeTree(tycon, targs)
}
// Case classes get a ProductN parent
@@ -241,7 +241,7 @@ object desugar {
moduleDef(
ModuleDef(
Modifiers(Synthetic), name.toTermName,
- Template(emptyConstructor, New(parentTpt, Nil) :: Nil, EmptyValDef, defs))).toList
+ Template(emptyConstructor, parentTpt :: Nil, EmptyValDef, defs))).toList
// The companion object defifinitions, if a companion is needed, Nil otherwise.
// companion definitions include:
@@ -713,13 +713,13 @@ object desugar {
/** Create a class definition with the same info as this refined type.
* parent { refinements }
* ==>
- * class <refinement> extends parent { refinements }
+ * trait <refinement> extends parent { refinements }
*
* The result is used for validity checking, is thrown away afterwards.
*/
def refinedTypeToClass(tree: RefinedTypeTree)(implicit ctx: Context): TypeDef = {
val impl = Template(emptyConstructor, tree.tpt :: Nil, EmptyValDef, tree.refinements)
- TypeDef(Modifiers(), tpnme.REFINE_CLASS, impl)
+ TypeDef(Modifiers(Trait), tpnme.REFINE_CLASS, impl)
}
/** If tree is a variable pattern, return its name and type, otherwise return None.
diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala
index a867253b7..225fdbcc0 100644
--- a/src/dotty/tools/dotc/core/SymDenotations.scala
+++ b/src/dotty/tools/dotc/core/SymDenotations.scala
@@ -269,6 +269,9 @@ object SymDenotations {
/** Is this denotation a class? */
final def isClass: Boolean = isInstanceOf[ClassDenotation]
+ /** Is this denotation a non-trait class? */
+ final def isRealClass(implicit ctx: Context) = isClass && !is(Trait)
+
/** Cast to class denotation */
final def asClass: ClassDenotation = asInstanceOf[ClassDenotation]
diff --git a/src/dotty/tools/dotc/core/TypeApplications.scala b/src/dotty/tools/dotc/core/TypeApplications.scala
index 9f742fcc2..5659ec9c6 100644
--- a/src/dotty/tools/dotc/core/TypeApplications.scala
+++ b/src/dotty/tools/dotc/core/TypeApplications.scala
@@ -150,6 +150,10 @@ class TypeApplications(val self: Type) extends AnyVal {
NoType
}
+ /** The base type including all type arguments of this type */
+ final def baseTypeWithArgs(base: Symbol)(implicit ctx: Context): Type =
+ self.baseType(base).appliedTo(baseTypeArgs(base))
+
/** Translate a type of the form From[T] to To[T], keep other types as they are.
* `from` and `to` must be static classes, both with one type parameter, and the same variance.
*/
diff --git a/src/dotty/tools/dotc/parsing/Parsers.scala b/src/dotty/tools/dotc/parsing/Parsers.scala
index ceb4ebe42..37a0a3ce1 100644
--- a/src/dotty/tools/dotc/parsing/Parsers.scala
+++ b/src/dotty/tools/dotc/parsing/Parsers.scala
@@ -1050,9 +1050,12 @@ object Parsers {
case NEW =>
canApply = false
val start = in.skipToken()
- templateOrNew(emptyConstructor()) match {
- case impl: Template => atPos(start) { New(impl) }
- case nu => adjustStart(start) { nu }
+ val (impl, missingBody) = template(emptyConstructor())
+ impl.parents match {
+ case parent :: Nil if missingBody =>
+ if (parent.isType) ensureApplied(wrapNew(parent)) else parent
+ case _ =>
+ New(impl)
}
case _ =>
if (isLiteral) literal()
@@ -1825,38 +1828,36 @@ object Parsers {
/** ConstrApp ::= SimpleType {ParArgumentExprs}
*/
- val constrApp = () =>
- ensureApplied(parArgumentExprss(wrapNew(simpleType())))
+ val constrApp = () => {
+ val t = simpleType()
+ if (in.token == LPAREN) parArgumentExprss(wrapNew(t))
+ else t
+ }
/** Template ::= ConstrApps [TemplateBody] | TemplateBody
* ConstrApps ::= ConstrApp {`with' ConstrApp}
+ *
+ * @return a pair consisting of the template, and a boolean which indicates
+ * whether the template misses a body (i.e. no {...} part).
*/
- def template(constr: DefDef): Template = templateOrNew(constr) match {
- case impl: Template => impl
- case parent => Template(constr, parent :: Nil, EmptyValDef, Nil)
- }
-
- /** Same as template, but if {...} is missing and there's only one
- * parent return the parent instead of a template. Called from New.
- */
- def templateOrNew(constr: DefDef): Tree = {
+ def template(constr: DefDef): (Template, Boolean) = {
newLineOptWhenFollowedBy(LBRACE)
- if (in.token == LBRACE) templateBodyOpt(constr, Nil)
+ if (in.token == LBRACE) (templateBodyOpt(constr, Nil), false)
else {
val parents = tokenSeparated(WITH, constrApp)
newLineOptWhenFollowedBy(LBRACE)
- if (in.token != LBRACE && parents.length == 1) parents.head
- else templateBodyOpt(constr, parents)
+ val missingBody = in.token != LBRACE
+ (templateBodyOpt(constr, parents), missingBody)
}
}
/** TemplateOpt = [`extends' Template | TemplateBody]
*/
def templateOpt(constr: DefDef): Template =
- if (in.token == EXTENDS) { in.nextToken(); template(constr) }
+ if (in.token == EXTENDS) { in.nextToken(); template(constr)._1 }
else {
newLineOptWhenFollowedBy(LBRACE)
- if (in.token == LBRACE) template(constr)
+ if (in.token == LBRACE) template(constr)._1
else Template(constr, Nil, EmptyValDef, Nil).withPos(constr.pos.toSynthetic)
}
diff --git a/src/dotty/tools/dotc/typer/Inferencing.scala b/src/dotty/tools/dotc/typer/Inferencing.scala
index 986ddf570..680096d38 100644
--- a/src/dotty/tools/dotc/typer/Inferencing.scala
+++ b/src/dotty/tools/dotc/typer/Inferencing.scala
@@ -410,13 +410,15 @@ object Inferencing {
def checkStable(tp: Type, pos: Position)(implicit ctx: Context): Unit =
if (!tp.isStable) ctx.error(i"Prefix of type ${tp.widenIfUnstable} is not stable", pos)
- /** Check that `tp` is a class type with a stable prefix.
- * @return Underlying class type if type checks out OK, ObjectClass.typeRef if not.
+ /** Check that `tp` is a class type with a stable prefix. Also, if `isFirst` is
+ * false check that `tp` is a trait.
+ * @return `tp` itself if it is a class or trait ref, ObjectClass.typeRef if not.
*/
- def checkClassTypeWithStablePrefix(tp: Type, pos: Position)(implicit ctx: Context): TypeRef =
+ def checkClassTypeWithStablePrefix(tp: Type, pos: Position, traitReq: Boolean)(implicit ctx: Context): Type =
tp.underlyingClassRef match {
- case tp: TypeRef =>
- checkStable(tp.prefix, pos)
+ case tref: TypeRef =>
+ checkStable(tref.prefix, pos)
+ if (traitReq && !(tref.symbol is Trait)) ctx.error(i"$tref is not a trait", pos)
tp
case _ =>
ctx.error(i"$tp is not a class type", pos)
@@ -441,45 +443,45 @@ object Inferencing {
case _ =>
}
- /** Ensure that first typeref in a list of parents points to a non-trait class.
- * If that's not already the case, add one.
+ /** Ensure that the first type in a list of parent types Ps points to a non-trait class.
+ * If that's not already the case, add one. The added class type CT is determined as follows.
+ * First, let C be the unique class such that
+ * - there is a parent P_i such that P_i derives from C, and
+ * - for every class D: If some parent P_j, j <= i derives from D, then C derives from D.
+ * Then, let CT be the smallest type which
+ * - has C as its class symbol, and
+ * - for all parents P_i: If P_i derives from C then P_i <:< CT.
*/
- def ensureFirstIsClass(prefs: List[TypeRef])(implicit ctx: Context): List[TypeRef] = {
- def isRealClass(sym: Symbol) = sym.isClass && !(sym is Trait)
- def realClassParent(tref: TypeRef): TypeRef =
- if (isRealClass(tref.symbol)) tref
- else tref.info.parents match {
- case pref :: _ => if (isRealClass(pref.symbol)) pref else realClassParent(pref)
- case nil => defn.ObjectClass.typeRef
+ def ensureFirstIsClass(parents: List[Type])(implicit ctx: Context): List[Type] = {
+ def realClassParent(cls: Symbol): ClassSymbol =
+ if (!cls.isClass) defn.ObjectClass
+ else if (!(cls is Trait)) cls.asClass
+ else cls.asClass.classParents match {
+ case parentRef :: _ => realClassParent(parentRef.symbol)
+ case nil => defn.ObjectClass
}
- def improve(clsRef: TypeRef, parent: TypeRef): TypeRef = {
- val pclsRef = realClassParent(parent)
- if (pclsRef.symbol derivesFrom clsRef.symbol) pclsRef else clsRef
+ def improve(candidate: ClassSymbol, parent: Type): ClassSymbol = {
+ val pcls = realClassParent(parent.classSymbol)
+ if (pcls derivesFrom candidate) pcls else candidate
}
- prefs match {
- case pref :: _ if isRealClass(pref.symbol) => prefs
- case _ => (defn.ObjectClass.typeRef /: prefs)(improve) :: prefs
+ parents match {
+ case p :: _ if p.classSymbol.isRealClass => parents
+ case _ =>
+ val pcls = (defn.ObjectClass /: parents)(improve)
+ val ptype = ctx.typeComparer.glb(
+ defn.ObjectType :: (parents map (_ baseTypeWithArgs pcls)))
+ ptype :: parents
}
}
- /** Forward bindings of all type parameters of `pcls`. That is, if the type parameter
- * if instantiated in a parent class, include its type binding in the current class.
- */
- def forwardTypeParams(pcls: ClassSymbol, cls: ClassSymbol, decls: Scope)(implicit ctx: Context): Unit = {
- for (tparam <- pcls.typeParams) {
- val argSym: Symbol = cls.thisType.member(tparam.name).symbol
- argSym.info match {
- case TypeAlias(TypeRef(ThisType(_), name)) =>
- val from = cls.thisType.member(name).symbol
- from.info match {
- case bounds: TypeBounds =>
- typr.println(s"forward ref $argSym $from $bounds")
- ctx.forwardRef(argSym, from, bounds, cls, decls)
- case _ =>
- }
- case _ =>
- }
- }
+ /** Ensure that first parent tree refers to a real class. */
+ def ensureFirstIsClass(parents: List[Tree], pos: Position)(implicit ctx: Context): List[Tree] = parents match {
+ case p :: ps if p.tpe.classSymbol.isRealClass => parents
+ case _ =>
+ // add synthetic class type
+ val parentTypes = ensureFirstIsClass(parents.tpes)
+ assert(parentTypes.length > parents.length)
+ (TypeTree(parentTypes.head) withPos pos) :: parents
}
/** Check that class does not define */
diff --git a/src/dotty/tools/dotc/typer/Namer.scala b/src/dotty/tools/dotc/typer/Namer.scala
index 4f4e8300e..019432c61 100644
--- a/src/dotty/tools/dotc/typer/Namer.scala
+++ b/src/dotty/tools/dotc/typer/Namer.scala
@@ -409,11 +409,11 @@ class Namer { typer: Typer =>
/** The type of a parent constructor. Types constructor arguments
* only if parent type contains uninstantiated type parameters.
*/
- def parentType(constr: untpd.Tree)(implicit ctx: Context): Type =
- if (constr.isType) { // this case applies to desugared refined types
- typedAheadType(constr).tpe
+ def parentType(parent: untpd.Tree)(implicit ctx: Context): Type =
+ if (parent.isType) {
+ typedAheadType(parent).tpe
} else {
- val (core, targs) = stripApply(constr) match {
+ val (core, targs) = stripApply(parent) match {
case TypeApply(core, targs) => (core, targs)
case core => (core, Nil)
}
@@ -421,29 +421,27 @@ class Namer { typer: Typer =>
val targs1 = targs map (typedAheadType(_))
val ptype = typedAheadType(tpt).tpe appliedTo targs1.tpes
if (ptype.uninstantiatedTypeParams.isEmpty) ptype
- else typedAheadExpr(constr).tpe
+ else typedAheadExpr(parent).tpe
}
+ def checkedParentType(parent: untpd.Tree): Type = {
+ val ptype = parentType(parent)(ctx.fresh addMode Mode.InSuperCall)
+ checkClassTypeWithStablePrefix(ptype, parent.pos, traitReq = parent ne parents.head)
+ }
+
val selfInfo =
if (self.isEmpty) NoType
else if (cls is Module) cls.owner.thisType select sourceModule
else createSymbol(self)
// pre-set info, so that parent types can refer to type params
denot.info = ClassInfo(cls.owner.thisType, cls, Nil, decls, selfInfo)
- val parentTypes = parents map (parentType(_)(ctx.fresh addMode Mode.InSuperCall))
+ val parentTypes = ensureFirstIsClass(parents map checkedParentType)
val parentRefs = ctx.normalizeToClassRefs(parentTypes, cls, decls)
- val parentClsRefs =
- for ((parentRef, constr) <- parentRefs zip parents)
- yield checkClassTypeWithStablePrefix(parentRef, constr.pos)
- val normalizedParentClsRefs = ensureFirstIsClass(parentClsRefs)
+ typr.println(s"completing $denot, parents = $parents, parentTypes = $parentTypes, parentRefs = $parentRefs")
index(constr)
index(rest)(inClassContext(selfInfo))
- denot.info = ClassInfo(cls.owner.thisType, cls, normalizedParentClsRefs, decls, selfInfo)
- if (parentClsRefs ne normalizedParentClsRefs) {
- forwardTypeParams(normalizedParentClsRefs.head.symbol.asClass, cls, decls)
- typr.println(i"expanded parents of $denot: $normalizedParentClsRefs%, %")
- }
+ denot.info = ClassInfo(cls.owner.thisType, cls, parentRefs, decls, selfInfo)
}
}
diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala
index 7e44f6a76..ebb648ea5 100644
--- a/src/dotty/tools/dotc/typer/Typer.scala
+++ b/src/dotty/tools/dotc/typer/Typer.scala
@@ -431,7 +431,7 @@ class Typer extends Namer with Applications with Implicits {
typed(cpy.Block(tree, clsDef :: Nil, New(Ident(x), Nil)), pt)
case _ =>
val tpt1 = typedType(tree.tpt)
- val clsref = checkClassTypeWithStablePrefix(tpt1.tpe, tpt1.pos)
+ val clsref = checkClassTypeWithStablePrefix(tpt1.tpe, tpt1.pos, traitReq = false)
// todo in a later phase: checkInstantiatable(cls, tpt1.pos)
cpy.New(tree, tpt1).withType(tpt1.tpe)
}
@@ -904,10 +904,31 @@ class Typer extends Namer with Applications with Implicits {
}
def typedClassDef(cdef: untpd.TypeDef, cls: ClassSymbol)(implicit ctx: Context) = track("typedClassDef") {
+ val superCtx = ctx.fresh addMode Mode.InSuperCall
+ def typedParent(tree: untpd.Tree): Tree =
+ if (tree.isType) typedType(tree)(superCtx)
+ else {
+ val result = typedExpr(tree)(superCtx)
+ if ((cls is Trait) && result.tpe.classSymbol.isRealClass)
+ ctx.error(s"trait may not call constructor of ${result.tpe.classSymbol}", tree.pos)
+ result
+ }
+
+ /** If this is a real class, make sure its first parent is a
+ * constructor call. Cannot simply use a type.
+ */
+ def ensureConstrCall(parents: List[Tree]): List[Tree] = {
+ val firstParent :: otherParents = parents
+ if (firstParent.isType && !(cls is Trait))
+ typed(untpd.New(untpd.TypedSplice(firstParent), Nil))(superCtx) :: otherParents
+ else parents
+ }
+
val TypeDef(mods, name, impl @ Template(constr, parents, self, body)) = cdef
val mods1 = typedModifiers(mods)
val constr1 = typed(constr).asInstanceOf[DefDef]
- val parents1 = parents mapconserve (typed(_)(ctx.fresh addMode Mode.InSuperCall))
+ val parents1 = ensureConstrCall(ensureFirstIsClass(
+ parents mapconserve typedParent, cdef.pos.toSynthetic))
val self1 = typed(self).asInstanceOf[ValDef]
val localDummy = ctx.newLocalDummy(cls, impl.pos)
val body1 = typedStats(body, localDummy)(inClassContext(self1.symbol))
diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala
index 51982d0de..b1fb42d9e 100644
--- a/test/dotc/tests.scala
+++ b/test/dotc/tests.scala
@@ -38,13 +38,16 @@ class tests extends CompilerTest {
@Test def pos_assignments() = compileFile(posDir, "assignments")
@Test def pos_packageobject() = compileFile(posDir, "packageobject")
@Test def pos_overloaded() = compileFile(posDir, "overloaded")
+ @Test def pos_templateParents() = compileFile(posDir, "templateParents")
@Test def neg_blockescapes() = compileFile(negDir, "blockescapesNeg", xerrors = 1)
@Test def neg_typedapply() = compileFile(negDir, "typedapply", xerrors = 4)
@Test def neg_typedidents() = compileFile(negDir, "typedIdents", xerrors = 2)
@Test def neg_assignments() = compileFile(negDir, "assignments", xerrors = 3)
@Test def neg_typers() = compileFile(negDir, "typers", xerrors = 10)
+ @Test def neg_privates() = compileFile(negDir, "privates", xerrors = 2)
@Test def neg_rootImports = compileFile(negDir, "rootImplicits", xerrors = 2)
+ @Test def neg_templateParents() = compileFile(posDir, "templateParents", xerrors = 2)
@Test def dotc = compileDir(dotcDir + "tools/dotc")
@Test def dotc_ast = compileDir(dotcDir + "tools/dotc/ast")
diff --git a/tests/neg/privates.scala b/tests/neg/privates.scala
new file mode 100644
index 000000000..6cc1ab67f
--- /dev/null
+++ b/tests/neg/privates.scala
@@ -0,0 +1,11 @@
+trait T {
+ private def foo = 0;
+ private[this] def bar = 0
+
+}
+
+class C { self: T =>
+ foo
+ bar
+}
+
diff --git a/tests/neg/templateParents.scala b/tests/neg/templateParents.scala
new file mode 100644
index 000000000..637c6037a
--- /dev/null
+++ b/tests/neg/templateParents.scala
@@ -0,0 +1,9 @@
+object templateParentsNeg {
+
+ class C(x: String)
+ class C2
+ trait D extends C("a") // error: traits may not call class constructors
+
+ new C("b") with C2 // error: C2 is not a trait
+
+}
diff --git a/tests/pos/templateParents.scala b/tests/pos/templateParents.scala
new file mode 100644
index 000000000..530f8c148
--- /dev/null
+++ b/tests/pos/templateParents.scala
@@ -0,0 +1,12 @@
+object templateParents {
+
+// traits do not call a constructor
+ class C[+T](x: T)
+ trait D extends C[String]
+ trait E extends C[Int]
+ new C("abc") with D
+
+ //val x = new D with E
+
+ //val y: C = x
+} \ No newline at end of file