aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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