diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/dotty/tools/dotc/ast/Desugar.scala | 8 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/SymDenotations.scala | 3 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/TypeApplications.scala | 4 | ||||
-rw-r--r-- | src/dotty/tools/dotc/parsing/Parsers.scala | 39 | ||||
-rw-r--r-- | src/dotty/tools/dotc/typer/Inferencing.scala | 78 | ||||
-rw-r--r-- | src/dotty/tools/dotc/typer/Namer.scala | 28 | ||||
-rw-r--r-- | src/dotty/tools/dotc/typer/Typer.scala | 25 |
7 files changed, 107 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)) |