diff options
Diffstat (limited to 'src/dotty/tools')
-rw-r--r-- | src/dotty/tools/dotc/Main.scala | 4 | ||||
-rw-r--r-- | src/dotty/tools/dotc/ast/Desugar.scala | 12 | ||||
-rw-r--r-- | src/dotty/tools/dotc/ast/untpd.scala | 45 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/SymDenotations.scala | 45 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/TypeApplications.scala | 4 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/TypeComparer.scala | 31 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/TypeOps.scala | 4 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/Types.scala | 26 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/pickling/UnPickler.scala | 2 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/transform/Erasure.scala | 2 | ||||
-rw-r--r-- | src/dotty/tools/dotc/parsing/Parsers.scala | 39 | ||||
-rw-r--r-- | src/dotty/tools/dotc/typer/Applications.scala | 2 | ||||
-rw-r--r-- | src/dotty/tools/dotc/typer/Inferencing.scala | 90 | ||||
-rw-r--r-- | src/dotty/tools/dotc/typer/Namer.scala | 28 | ||||
-rw-r--r-- | src/dotty/tools/dotc/typer/Typer.scala | 27 |
15 files changed, 214 insertions, 147 deletions
diff --git a/src/dotty/tools/dotc/Main.scala b/src/dotty/tools/dotc/Main.scala index 955304dda..72f29fe6e 100644 --- a/src/dotty/tools/dotc/Main.scala +++ b/src/dotty/tools/dotc/Main.scala @@ -9,9 +9,7 @@ import core.Contexts.Context import reporting.Reporter /* To do: - * - simplify hk types - * - Don't open package objects from class files if they are present in source - * - Revise the way classes are inherited - when not followed by [...] or (...), +s * - Revise the way classes are inherited - when not followed by [...] or (...), * assume the unparameterized type and forward type parameters as we do now for the synthetic head class. */ object Main extends Driver { diff --git a/src/dotty/tools/dotc/ast/Desugar.scala b/src/dotty/tools/dotc/ast/Desugar.scala index 0f1d1010a..027c3238d 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,15 @@ 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 } * + * If the parent is missing, Object is assumed. * 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) + val parent = if (tree.tpt.isEmpty) TypeTree(defn.ObjectType) else tree.tpt + val impl = Template(emptyConstructor, parent :: Nil, EmptyValDef, tree.refinements) + 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/ast/untpd.scala b/src/dotty/tools/dotc/ast/untpd.scala index 879393a0a..4aff9da5d 100644 --- a/src/dotty/tools/dotc/ast/untpd.scala +++ b/src/dotty/tools/dotc/ast/untpd.scala @@ -13,8 +13,16 @@ object untpd extends Trees.Instance[Untyped] with TreeInfo[Untyped] { // ----- Tree cases that exist in untyped form only ------------------ + trait OpTree extends Tree { + def op: Name + override def isTerm = op.isTermName + override def isType = op.isTypeName + } + /** A typed subtree of an untyped tree needs to be wrapped in a TypedSlice */ - case class TypedSplice(tree: tpd.Tree) extends Tree + case class TypedSplice(tree: tpd.Tree) extends ProxyTree { + def forwardTo = tree + } /** mods object name impl */ case class ModuleDef(mods: Modifiers, name: TermName, impl: Template) @@ -25,12 +33,20 @@ object untpd extends Trees.Instance[Untyped] with TreeInfo[Untyped] { case class SymbolLit(str: String) extends TermTree case class InterpolatedString(id: TermName, strings: List[Literal], elems: List[Tree]) extends TermTree - case class Function(args: List[Tree], body: Tree) extends Tree - case class InfixOp(left: Tree, op: Name, right: Tree) extends Tree - case class PostfixOp(od: Tree, op: Name) extends Tree - case class PrefixOp(op: Name, od: Tree) extends Tree - case class Parens(t: Tree) extends Tree - case class Tuple(trees: List[Tree]) extends Tree + case class Function(args: List[Tree], body: Tree) extends Tree { + override def isTerm = body.isTerm + override def isType = body.isType + } + case class InfixOp(left: Tree, op: Name, right: Tree) extends OpTree + case class PostfixOp(od: Tree, op: Name) extends OpTree + case class PrefixOp(op: Name, od: Tree) extends OpTree + case class Parens(t: Tree) extends ProxyTree { + def forwardTo = t + } + case class Tuple(trees: List[Tree]) extends Tree { + override def isTerm = trees.isEmpty || trees.head.isTerm + override def isType = !isTerm + } case class WhileDo(cond: Tree, body: Tree) extends TermTree case class DoWhile(body: Tree, cond: Tree) extends TermTree case class ForYield(enums: List[Tree], expr: Tree) extends TermTree @@ -101,10 +117,19 @@ object untpd extends Trees.Instance[Untyped] with TreeInfo[Untyped] { * ==> * (new pre.C).<init>[Ts](args1)...(args_n) */ - def New(tpt: Tree, argss: List[List[Tree]]): Tree = { + def New(tpt: Tree, argss: List[List[Tree]])(implicit ctx: Context): Tree = { val (tycon, targs) = tpt match { - case AppliedTypeTree(tycon, targs) => (tycon, targs) - case _ => (tpt, Nil) + case AppliedTypeTree(tycon, targs) => + (tycon, targs) + case TypedSplice(AppliedTypeTree(tycon, targs)) => + (TypedSplice(tycon), targs map TypedSplice) + case TypedSplice(tpt1: Tree) => + val argTypes = tpt1.tpe.typeArgs + val tycon = tpt1.tpe.withoutArgs(argTypes) + def wrap(tpe: Type) = TypeTree(tpe) withPos tpt.pos + (wrap(tycon), argTypes map wrap) + case _ => + (tpt, Nil) } var prefix: Tree = Select(New(tycon), nme.CONSTRUCTOR) if (targs.nonEmpty) prefix = TypeApply(prefix, targs) diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala index a867253b7..ebe52d522 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] @@ -446,7 +449,7 @@ object SymDenotations { | ${owner.showLocated} where target is defined""".stripMargin) else if ( !( isType // allow accesses to types from arbitrary subclasses fixes #4737 - || pre.baseType(cls).exists + || pre.baseTypeRef(cls).exists // ??? why not use derivesFrom ??? || isConstructor || (owner is ModuleClass) // don't perform this check for static members )) @@ -847,12 +850,12 @@ object SymDenotations { private[this] var myBaseClasses: List[ClassSymbol] = null private[this] var mySuperClassBits: BitSet = null - /** Invalidate baseTypeCache and superClassBits on new run */ + /** Invalidate baseTypeRefCache and superClassBits on new run */ private def checkBasesUpToDate()(implicit ctx: Context) = - if (baseTypeValid != ctx.runId) { - baseTypeCache = new java.util.HashMap[CachedType, Type] + if (baseTypeRefValid != ctx.runId) { + baseTypeRefCache = new java.util.HashMap[CachedType, Type] mySuperClassBits = null - baseTypeValid = ctx.runId + baseTypeRefValid = ctx.runId } private def computeBases(implicit ctx: Context): Unit = { @@ -1062,18 +1065,18 @@ object SymDenotations { raw.filterExcluded(excluded).asSeenFrom(pre).toDenot(pre) } - private[this] var baseTypeCache: java.util.HashMap[CachedType, Type] = null - private[this] var baseTypeValid: RunId = NoRunId + private[this] var baseTypeRefCache: java.util.HashMap[CachedType, Type] = null + private[this] var baseTypeRefValid: RunId = NoRunId - /** Compute tp.baseType(this) */ - final def baseTypeOf(tp: Type)(implicit ctx: Context): Type = { + /** Compute tp.baseTypeRef(this) */ + final def baseTypeRefOf(tp: Type)(implicit ctx: Context): Type = { def foldGlb(bt: Type, ps: List[Type]): Type = ps match { - case p :: ps1 => foldGlb(bt & baseTypeOf(p), ps1) + case p :: ps1 => foldGlb(bt & baseTypeRefOf(p), ps1) case _ => bt } - def computeBaseTypeOf(tp: Type): Type = { + def computeBaseTypeRefOf(tp: Type): Type = { Stats.record("computeBaseTypeOf") if (symbol.isStatic && tp.derivesFrom(symbol)) symbol.typeRef @@ -1087,34 +1090,34 @@ object SymDenotations { if (cdenot.superClassBits contains symbol.superId) foldGlb(NoType, tp.parents) else NoType case _ => - baseTypeOf(tp.underlying) + baseTypeRefOf(tp.underlying) } case tp: TypeProxy => - baseTypeOf(tp.underlying) + baseTypeRefOf(tp.underlying) case AndType(tp1, tp2) => - baseTypeOf(tp1) & baseTypeOf(tp2) + baseTypeRefOf(tp1) & baseTypeRefOf(tp2) case OrType(tp1, tp2) => - baseTypeOf(tp1) | baseTypeOf(tp2) + baseTypeRefOf(tp1) | baseTypeRefOf(tp2) case _ => NoType } } - /*>|>*/ ctx.debugTraceIndented(s"$tp.baseType($this)") /*<|<*/ { + /*>|>*/ ctx.debugTraceIndented(s"$tp.baseTypeRef($this)") /*<|<*/ { tp match { case tp: CachedType => checkBasesUpToDate() - var basetp = baseTypeCache get tp + var basetp = baseTypeRefCache get tp if (basetp == null) { - baseTypeCache.put(tp, NoPrefix) - basetp = computeBaseTypeOf(tp) - baseTypeCache.put(tp, basetp) + baseTypeRefCache.put(tp, NoPrefix) + basetp = computeBaseTypeRefOf(tp) + baseTypeRefCache.put(tp, basetp) } else if (basetp == NoPrefix) { throw new CyclicReference(this) } basetp case _ => - computeBaseTypeOf(tp) + computeBaseTypeRefOf(tp) } } } diff --git a/src/dotty/tools/dotc/core/TypeApplications.scala b/src/dotty/tools/dotc/core/TypeApplications.scala index 9f742fcc2..e854e672f 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.baseTypeRef(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/core/TypeComparer.scala b/src/dotty/tools/dotc/core/TypeComparer.scala index a714cdf35..0af6ebf97 100644 --- a/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/src/dotty/tools/dotc/core/TypeComparer.scala @@ -405,7 +405,7 @@ class TypeComparer(initctx: Context) extends DotClass { case _ => val cls2 = tp2.symbol if (cls2.isClass) { - val base = tp1.baseType(cls2) + val base = tp1.baseTypeRef(cls2) if (base.exists && (base ne tp1)) return isSubType(base, tp2) if ( cls2 == defn.SingletonClass && tp1.isStable || cls2 == defn.NotNullClass && tp1.isNotNull @@ -720,7 +720,7 @@ class TypeComparer(initctx: Context) extends DotClass { else isSubType(tp1, tp2) && isSubType(tp2, tp1) /** The greatest lower bound of two types */ - def glb(tp1: Type, tp2: Type): Type = + def glb(tp1: Type, tp2: Type): Type = /*>|>*/ ctx.traceIndented(s"glb(${tp1.show}, ${tp2.show})", typr, show = true) /*<|<*/ { if (tp1 eq tp2) tp1 else if (!tp1.exists) tp2 else if (!tp2.exists) tp1 @@ -743,6 +743,7 @@ class TypeComparer(initctx: Context) extends DotClass { } } } + } /** The greatest lower bound of a list types */ final def glb(tps: List[Type]): Type = @@ -870,17 +871,10 @@ class TypeComparer(initctx: Context) extends DotClass { /** Try to distribute `&` inside type, detect and handle conflicts */ private def distributeAnd(tp1: Type, tp2: Type): Type = tp1 match { - case tp1 @ TypeBounds(lo1, hi1) => + case tp1: TypeBounds => tp2 match { - case tp2 @ TypeBounds(lo2, hi2) => - if ((lo1 eq hi1) && (lo2 eq hi2)) { - val v = tp1 commonVariance tp2 - if (v > 0) return TypeAlias(hi1 & hi2, v) - if (v < 0) return TypeAlias(lo1 | lo2, v) - } - TypeBounds(lo1 | lo2, hi1 & hi2) - case _ => - andConflict(tp1, tp2) + case tp2: TypeBounds => tp1 & tp2 + case _ => andConflict(tp1, tp2) } case tp1: ClassInfo => tp2 match { @@ -944,17 +938,10 @@ class TypeComparer(initctx: Context) extends DotClass { /** Try to distribute `|` inside type, detect and handle conflicts */ private def distributeOr(tp1: Type, tp2: Type): Type = tp1 match { - case tp1 @ TypeBounds(lo1, hi1) => + case tp1: TypeBounds => tp2 match { - case tp2 @ TypeBounds(lo2, hi2) => - if ((lo1 eq hi1) && (lo2 eq hi2)) { - val v = tp1 commonVariance tp2 - if (v > 0) return TypeAlias(hi1 | hi2, v) - if (v < 0) return TypeAlias(lo1 & lo2, v) - } - TypeBounds(lo1 & lo2, hi1 | hi2) - case _ => - orConflict(tp1, tp2) + case tp2: TypeBounds => tp1 | tp2 + case _ => orConflict(tp1, tp2) } case tp1: ClassInfo => tp2 match { diff --git a/src/dotty/tools/dotc/core/TypeOps.scala b/src/dotty/tools/dotc/core/TypeOps.scala index 272e93dd3..ca69ab615 100644 --- a/src/dotty/tools/dotc/core/TypeOps.scala +++ b/src/dotty/tools/dotc/core/TypeOps.scala @@ -12,13 +12,13 @@ trait TypeOps { this: Context => def toPrefix(pre: Type, cls: Symbol, thiscls: ClassSymbol): Type = /*>|>*/ ctx.debugTraceIndented(s"toPrefix($pre, $cls, $thiscls)") /*<|<*/ { if ((pre eq NoType) || (pre eq NoPrefix) || (cls is PackageClass)) tp - else if (thiscls.derivesFrom(cls) && pre.baseType(thiscls).exists) + else if (thiscls.derivesFrom(cls) && pre.baseTypeRef(thiscls).exists) pre match { case SuperType(thispre, _) => thispre case _ => pre } else - toPrefix(pre.baseType(cls).normalizedPrefix, cls.owner, thiscls) + toPrefix(pre.baseTypeRef(cls).normalizedPrefix, cls.owner, thiscls) } /*>|>*/ ctx.conditionalTraceIndented(TypeOps.track , s"asSeen ${tp.show} from (${pre.show}, ${cls.show})", show = true) /*<|<*/ { // !!! DEBUG diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index 6973af726..bc8d7dfd2 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -495,10 +495,12 @@ object Types { this, that, alwaysMatchSimple = !ctx.phase.erasedTypes) } - /** The basetype of this type with given class symbol */ - final def baseType(base: Symbol)(implicit ctx: Context): Type = /*ctx.traceIndented(s"$this baseType $base")*/ /*>|>*/ track("baseType") /*<|<*/ { + /** The basetype TypeRef of this type with given class symbol, + * but without including any type arguments + */ + final def baseTypeRef(base: Symbol)(implicit ctx: Context): Type = /*ctx.traceIndented(s"$this baseTypeRef $base")*/ /*>|>*/ track("baseTypeRef") /*<|<*/ { base.denot match { - case classd: ClassDenotation => classd.baseTypeOf(this)//widen.dealias) + case classd: ClassDenotation => classd.baseTypeRefOf(this)//widen.dealias) case _ => NoType } } @@ -1883,11 +1885,21 @@ object Types { def contains(tp: Type)(implicit ctx: Context) = lo <:< tp && tp <:< hi - def & (that: TypeBounds)(implicit ctx: Context): TypeBounds = - derivedTypeBounds(this.lo | that.lo, this.hi & that.hi, this commonVariance that) + def & (that: TypeBounds)(implicit ctx: Context): TypeBounds = { + val v = this commonVariance that + if (v != 0 && (this.lo eq this.hi) && (that.lo eq that.hi)) + if (v > 0) derivedTypeAlias(this.hi & that.hi, v) + else derivedTypeAlias(this.lo | that.lo, v) + else derivedTypeBounds(this.lo | that.lo, this.hi & that.hi, v) + } - def | (that: TypeBounds)(implicit ctx: Context): TypeBounds = - derivedTypeBounds(this.lo & that.lo, this.hi | that.hi, this commonVariance that) + def | (that: TypeBounds)(implicit ctx: Context): TypeBounds = { + val v = this commonVariance that + if (v == 0 && (this.lo eq this.hi) && (that.lo eq that.hi)) + if (v > 0) derivedTypeAlias(this.hi | that.hi, v) + else derivedTypeAlias(this.lo & that.lo, v) + else derivedTypeBounds(this.lo & that.lo, this.hi | that.hi, v) + } override def & (that: Type)(implicit ctx: Context) = that match { case that: TypeBounds => this & that diff --git a/src/dotty/tools/dotc/core/pickling/UnPickler.scala b/src/dotty/tools/dotc/core/pickling/UnPickler.scala index 84b300a2a..c48e71052 100644 --- a/src/dotty/tools/dotc/core/pickling/UnPickler.scala +++ b/src/dotty/tools/dotc/core/pickling/UnPickler.scala @@ -631,7 +631,7 @@ class UnPickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClassRoot: if (sym.owner != cls) { val overriding = cls.decls.lookup(sym.name) if (overriding.exists && overriding != sym) { - val base = pre.baseType(sym.owner) + val base = pre.baseTypeWithArgs(sym.owner) assert(base.exists) pre = SuperType(pre, base) } diff --git a/src/dotty/tools/dotc/core/transform/Erasure.scala b/src/dotty/tools/dotc/core/transform/Erasure.scala index 34f339d9a..eaeb3c8e7 100644 --- a/src/dotty/tools/dotc/core/transform/Erasure.scala +++ b/src/dotty/tools/dotc/core/transform/Erasure.scala @@ -51,7 +51,7 @@ object Erasure { case AndType(tp1, tp2) => erasure(tp1) case OrType(tp1, tp2) => - erasure(tp.baseType(lubClass(tp1, tp2))) + erasure(tp.baseTypeRef(lubClass(tp1, tp2))) case tp: MethodType => tp.derivedMethodType( tp.paramNames, tp.paramTypes.mapConserve(erasure), resultErasure(tp.resultType)) 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/Applications.scala b/src/dotty/tools/dotc/typer/Applications.scala index 15dd0e9ad..4b43aa8b7 100644 --- a/src/dotty/tools/dotc/typer/Applications.scala +++ b/src/dotty/tools/dotc/typer/Applications.scala @@ -222,7 +222,7 @@ trait Applications extends Compatibility { self: Typer => val pre = if (meth.isClassConstructor) { // default getters for class constructors are found in the companion object - mpre.baseType(cls) match { + mpre.baseTypeRef(cls) match { case tp: TypeRef => ref(tp.prefix, cls.companionModule) case _ => NoType } diff --git a/src/dotty/tools/dotc/typer/Inferencing.scala b/src/dotty/tools/dotc/typer/Inferencing.scala index 986ddf570..ea3109afa 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,59 @@ 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) + typr.println(i"ensure first is class $parents%, % --> ${parents map (_ baseTypeWithArgs pcls)}%, %") + 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. + /** 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 first :: _ = ensureFirstIsClass(parents.tpes) + TypeTree(checkFeasible(first, pos, i"\n in inferred parent $first")).withPos(pos) :: parents + } + + /** Check that any top-level type arguments in this type are feasible, i.e. that + * their lower bound conforms to their upper cound. If a type argument is + * infeasible, issue and error and continue with upper bound. */ - 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 _ => - } - } + def checkFeasible(tp: Type, pos: Position, where: => String = "")(implicit ctx: Context): Type = tp match { + case tp: RefinedType => + tp.derivedRefinedType(tp.parent, tp.refinedName, checkFeasible(tp.refinedInfo, pos, where)) + case tp @ TypeBounds(lo, hi) if !(lo <:< hi) => + ctx.error(i"no type exists between low bound $lo and high bound $hi$where", pos) + tp.derivedTypeAlias(hi) + case _ => + tp } /** 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..400a1407a 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) } @@ -798,7 +798,7 @@ class Typer extends Namer with Applications with Implicits { } def typedRefinedTypeTree(tree: untpd.RefinedTypeTree)(implicit ctx: Context): RefinedTypeTree = track("typedRefinedTypeTree") { - val tpt1 = typedAheadType(tree.tpt) + val tpt1 = if (tree.tpt.isEmpty) TypeTree(defn.ObjectType) else typedAheadType(tree.tpt) val refineClsDef = desugar.refinedTypeToClass(tree) val refineCls = createSymbol(refineClsDef).asClass val TypeDef(_, _, Template(_, _, _, refinements1)) = typed(refineClsDef) @@ -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)) |