diff options
Diffstat (limited to 'compiler')
14 files changed, 109 insertions, 55 deletions
diff --git a/compiler/src/dotty/tools/dotc/Compiler.scala b/compiler/src/dotty/tools/dotc/Compiler.scala index f413fc902..efde897cd 100644 --- a/compiler/src/dotty/tools/dotc/Compiler.scala +++ b/compiler/src/dotty/tools/dotc/Compiler.scala @@ -92,12 +92,12 @@ class Compiler { new ElimStaticThis, // Replace `this` references to static objects by global identifiers new Flatten, // Lift all inner classes to package scope new RestoreScopes), // Repair scopes rendered invalid by moving definitions in prior phases of the group - List(new ExpandPrivate, // Widen private definitions accessed from nested classes + List(new MoveStatics, // Move static methods to companion classes + new ExpandPrivate, // Widen private definitions accessed from nested classes new SelectStatic, // get rid of selects that would be compiled into GetStatic new CollectEntryPoints, // Find classes with main methods new CollectSuperCalls, // Find classes that are called with super new DropInlined, // Drop Inlined nodes, since backend has no use for them - new MoveStatics, // Move static methods to companion classes new LabelDefs), // Converts calls to labels to jumps List(new GenBCode) // Generate JVM bytecode ) diff --git a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala index 40e2b083b..318295751 100644 --- a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -64,6 +64,7 @@ class ScalaSettings extends Settings.SettingGroup { val Ycheck = PhasesSetting("-Ycheck", "Check the tree at the end of") val YcheckMods = BooleanSetting("-Ycheck-mods", "Check that symbols and their defining trees have modifiers in sync") val debug = BooleanSetting("-Ydebug", "Increase the quantity of debugging output.") + val debugAlias = BooleanSetting("-Ydebug-alias", "Never follow alias when printing types") val debugNames = BooleanSetting("-YdebugNames", "Show name-space indicators when printing names") val debugTrace = BooleanSetting("-Ydebug-trace", "Trace core operations") val debugFlags = BooleanSetting("-Ydebug-flags", "Print all flags of definitions") diff --git a/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala b/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala index 2a1f4ee6e..a12936c58 100644 --- a/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala +++ b/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala @@ -35,15 +35,6 @@ trait ConstraintHandling { /** If the constraint is frozen we cannot add new bounds to the constraint. */ protected var frozenConstraint = false - protected var alwaysFluid = false - - /** Perform `op` in a mode where all attempts to set `frozen` to true are ignored */ - def fluidly[T](op: => T): T = { - val saved = alwaysFluid - alwaysFluid = true - try op finally alwaysFluid = saved - } - /** If set, align arguments `S1`, `S2`when taking the glb * `T1 { X = S1 } & T2 { X = S2 }` of a constraint upper bound for some type parameter. * Aligning means computing `S1 =:= S2` which may change the current constraint. @@ -156,16 +147,24 @@ trait ConstraintHandling { up.forall(addOneBound(_, lo, isUpper = false)) } + + protected def isSubType(tp1: Type, tp2: Type, whenFrozen: Boolean): Boolean = { + if (whenFrozen) + isSubTypeWhenFrozen(tp1, tp2) + else + isSubType(tp1, tp2) + } + final def isSubTypeWhenFrozen(tp1: Type, tp2: Type): Boolean = { val saved = frozenConstraint - frozenConstraint = !alwaysFluid + frozenConstraint = true try isSubType(tp1, tp2) finally frozenConstraint = saved } final def isSameTypeWhenFrozen(tp1: Type, tp2: Type): Boolean = { val saved = frozenConstraint - frozenConstraint = !alwaysFluid + frozenConstraint = true try isSameType(tp1, tp2) finally frozenConstraint = saved } diff --git a/compiler/src/dotty/tools/dotc/core/Periods.scala b/compiler/src/dotty/tools/dotc/core/Periods.scala index 29d9d208f..892a1f59a 100644 --- a/compiler/src/dotty/tools/dotc/core/Periods.scala +++ b/compiler/src/dotty/tools/dotc/core/Periods.scala @@ -46,9 +46,9 @@ object Periods { * It is coded as follows: * * sign, always 0 1 bit - * runid 19 bits - * last phase id: 6 bits - * #phases before last: 6 bits + * runid 17 bits + * last phase id: 7 bits + * #phases before last: 7 bits * * // Dmitry: sign == 0 isn't actually always true, in some cases phaseId == -1 is used for shifts, that easily creates code < 0 */ @@ -108,6 +108,7 @@ object Periods { else Nowhere + /** The smallest period containing two periods */ def | (that: Period): Period = Period(this.runId, this.firstPhaseId min that.firstPhaseId, diff --git a/compiler/src/dotty/tools/dotc/core/TypeApplications.scala b/compiler/src/dotty/tools/dotc/core/TypeApplications.scala index c713cd542..ba3e6a461 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeApplications.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeApplications.scala @@ -1,4 +1,5 @@ -package dotty.tools.dotc +package dotty.tools +package dotc package core import Types._ @@ -412,8 +413,8 @@ class TypeApplications(val self: Type) extends AnyVal { val followAlias = Config.simplifyApplications && { dealiased.resType match { case AppliedType(tyconBody, _) => - variancesConform(typParams, tyconBody.typeParams) - // Reducing is safe for type inference, as kind of type constructor does not change + sameLength(dealiased.typeParams, tyconBody.typeParams) + // Reducing is safe for type inference, as kind arity of type constructor does not change case _ => false } } diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index 21a12dbb7..57dde3288 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -384,7 +384,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { // So if the constraint is not yet frozen, we do the same comparison again // with a frozen constraint, which means that we get a chance to do the // widening in `fourthTry` before adding to the constraint. - if (frozenConstraint || alwaysFluid) isSubType(tp1, bounds(tp2).lo) + if (frozenConstraint) isSubType(tp1, bounds(tp2).lo) else isSubTypeWhenFrozen(tp1, tp2) alwaysTrue || { if (canConstrain(tp2)) addConstraint(tp2, tp1.widenExpr, fromBelow = true) @@ -1143,19 +1143,20 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { ((defn.AnyType: Type) /: tps)(glb) /** The least upper bound of two types + * @param canConstrain If true, new constraints might be added to simplify the lub. * @note We do not admit singleton types in or-types as lubs. */ - def lub(tp1: Type, tp2: Type): Type = /*>|>*/ ctx.traceIndented(s"lub(${tp1.show}, ${tp2.show})", subtyping, show = true) /*<|<*/ { + def lub(tp1: Type, tp2: Type, canConstrain: Boolean = false): Type = /*>|>*/ ctx.traceIndented(s"lub(${tp1.show}, ${tp2.show}, canConstrain=$canConstrain)", subtyping, show = true) /*<|<*/ { if (tp1 eq tp2) tp1 else if (!tp1.exists) tp1 else if (!tp2.exists) tp2 else if ((tp1 isRef AnyClass) || (tp2 isRef NothingClass)) tp1 else if ((tp2 isRef AnyClass) || (tp1 isRef NothingClass)) tp2 else { - val t1 = mergeIfSuper(tp1, tp2) + val t1 = mergeIfSuper(tp1, tp2, canConstrain) if (t1.exists) t1 else { - val t2 = mergeIfSuper(tp2, tp1) + val t2 = mergeIfSuper(tp2, tp1, canConstrain) if (t2.exists) t2 else { val tp1w = tp1.widen @@ -1169,7 +1170,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { /** The least upper bound of a list of types */ final def lub(tps: List[Type]): Type = - ((defn.NothingType: Type) /: tps)(lub) + ((defn.NothingType: Type) /: tps)(lub(_,_, canConstrain = false)) /** Merge `t1` into `tp2` if t1 is a subtype of some &-summand of tp2. */ @@ -1192,17 +1193,18 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { } /** Merge `tp1` into `tp2` if tp1 is a supertype of some |-summand of tp2. + * @param canConstrain If true, new constraints might be added to make the merge possible. */ - private def mergeIfSuper(tp1: Type, tp2: Type): Type = - if (isSubTypeWhenFrozen(tp2, tp1)) - if (isSubTypeWhenFrozen(tp1, tp2)) tp2 else tp1 // keep existing type if possible + private def mergeIfSuper(tp1: Type, tp2: Type, canConstrain: Boolean): Type = + if (isSubType(tp2, tp1, whenFrozen = !canConstrain)) + if (isSubType(tp1, tp2, whenFrozen = !canConstrain)) tp2 else tp1 // keep existing type if possible else tp2 match { case tp2 @ OrType(tp21, tp22) => - val higher1 = mergeIfSuper(tp1, tp21) + val higher1 = mergeIfSuper(tp1, tp21, canConstrain) if (higher1 eq tp21) tp2 else if (higher1.exists) higher1 | tp22 else { - val higher2 = mergeIfSuper(tp1, tp22) + val higher2 = mergeIfSuper(tp1, tp22, canConstrain) if (higher2 eq tp22) tp2 else if (higher2.exists) tp21 | higher2 else NoType @@ -1491,9 +1493,9 @@ class ExplainingTypeComparer(initctx: Context) extends TypeComparer(initctx) { super.hasMatchingMember(name, tp1, tp2) } - override def lub(tp1: Type, tp2: Type) = - traceIndented(s"lub(${show(tp1)}, ${show(tp2)})") { - super.lub(tp1, tp2) + override def lub(tp1: Type, tp2: Type, canConstrain: Boolean = false) = + traceIndented(s"lub(${show(tp1)}, ${show(tp2)}, canConstrain=$canConstrain)") { + super.lub(tp1, tp2, canConstrain) } override def glb(tp1: Type, tp2: Type) = diff --git a/compiler/src/dotty/tools/dotc/core/TypeOps.scala b/compiler/src/dotty/tools/dotc/core/TypeOps.scala index 6c40794e3..3d2906320 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeOps.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeOps.scala @@ -282,7 +282,7 @@ trait TypeOps { this: Context => // TODO: Make standalone object. */ def harmonizeUnion(tp: Type): Type = tp match { case tp: OrType => - joinIfScala2(typeComparer.fluidly(tp.tp1 | tp.tp2)) + joinIfScala2(ctx.typeComparer.lub(harmonizeUnion(tp.tp1), harmonizeUnion(tp.tp2), canConstrain = true)) case tp @ AndType(tp1, tp2) => tp derived_& (harmonizeUnion(tp1), harmonizeUnion(tp2)) case tp: RefinedType => diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 3d952f425..8a33472b8 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -130,7 +130,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { if (defn.isTupleClass(cls)) return toTextTuple(args) return (toTextLocal(tycon) ~ "[" ~ Text(args map argText, ", ") ~ "]").close case tp: TypeRef => - val hideType = tp.symbol is AliasPreferred + val hideType = !ctx.settings.debugAlias.value && (tp.symbol is AliasPreferred) if (hideType && !ctx.phase.erasedTypes && !tp.symbol.isCompleting) { tp.info match { case TypeAlias(alias) => return toText(alias) diff --git a/compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.java b/compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.java index 7b56c8ed4..6ea168b99 100644 --- a/compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.java +++ b/compiler/src/dotty/tools/dotc/reporting/diagnostic/ErrorMessageID.java @@ -55,6 +55,7 @@ public enum ErrorMessageID { RecursiveValueNeedsResultTypeID, CyclicReferenceInvolvingID, CyclicReferenceInvolvingImplicitID, + SuperQualMustBeParentID, ; public int errorNumber() { diff --git a/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala index 4c53fa496..6fa056646 100644 --- a/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala @@ -1192,4 +1192,21 @@ object messages { |""".stripMargin } + case class SuperQualMustBeParent(qual: untpd.Ident, cls: Symbols.ClassSymbol)(implicit ctx: Context) + extends Message(SuperQualMustBeParentID) { + + val msg = hl"""|$qual does not name a parent of $cls""" + + val kind = "Reference" + + private val parents: Seq[String] = (cls.info.parents map (_.name.show)).sorted + + val explanation = + hl"""|When a qualifier ${"T"} is used in a ${"super"} prefix of the form ${"C.super[T]"}, + |${"T"} must be a parent type of ${"C"}. + | + |In this case, the parents of $cls are: + |${parents.mkString(" - ", "\n - ", "")} + |""".stripMargin + } } diff --git a/compiler/src/dotty/tools/dotc/transform/ExpandPrivate.scala b/compiler/src/dotty/tools/dotc/transform/ExpandPrivate.scala index 83cd395ff..8f99ef1d0 100644 --- a/compiler/src/dotty/tools/dotc/transform/ExpandPrivate.scala +++ b/compiler/src/dotty/tools/dotc/transform/ExpandPrivate.scala @@ -3,7 +3,7 @@ package transform import core._ import dotty.tools.dotc.ast.tpd -import dotty.tools.dotc.core.DenotTransformers.{SymTransformer, IdentityDenotTransformer} +import dotty.tools.dotc.core.DenotTransformers.{IdentityDenotTransformer, SymTransformer} import Contexts.Context import Symbols._ import Scopes._ @@ -11,13 +11,16 @@ import Flags._ import StdNames._ import SymDenotations._ import Types._ + import collection.mutable import TreeTransforms._ import Decorators._ import ast.Trees._ import TreeTransforms._ import java.io.File.separatorChar + import ValueClasses._ +import dotty.tools.dotc.core.Phases.Phase /** Make private term members that are accessed from another class * non-private by resetting the Private flag and expanding their name. @@ -38,6 +41,9 @@ class ExpandPrivate extends MiniPhaseTransform with IdentityDenotTransformer { t override def phaseName: String = "expandPrivate" + // This phase moves methods around (in infotransform) so it may need to make other methods public + override def runsAfter: Set[Class[_ <: Phase]] = Set(classOf[MoveStatics]) + override def checkPostCondition(tree: Tree)(implicit ctx: Context): Unit = { tree match { case t: DefDef => diff --git a/compiler/src/dotty/tools/dotc/transform/MoveStatics.scala b/compiler/src/dotty/tools/dotc/transform/MoveStatics.scala index 5c2cd3145..b0ee0930d 100644 --- a/compiler/src/dotty/tools/dotc/transform/MoveStatics.scala +++ b/compiler/src/dotty/tools/dotc/transform/MoveStatics.scala @@ -9,6 +9,7 @@ import dotty.tools.dotc.core.Decorators._ import dotty.tools.dotc.core.NameOps._ import dotty.tools.dotc.core.{Flags, Names} import dotty.tools.dotc.core.Names.Name +import dotty.tools.dotc.core.Phases.Phase import dotty.tools.dotc.core.Symbols._ import dotty.tools.dotc.core.Types.MethodType import dotty.tools.dotc.transform.TreeTransforms.{MiniPhaseTransform, TransformerInfo} @@ -19,7 +20,6 @@ class MoveStatics extends MiniPhaseTransform with SymTransformer { thisTransform import tpd._ override def phaseName = "moveStatic" - def transformSym(sym: SymDenotation)(implicit ctx: Context): SymDenotation = { if (sym.hasAnnotation(defn.ScalaStaticAnnot) && sym.owner.is(Flags.Module) && sym.owner.companionClass.exists) { sym.owner.asClass.delete(sym.symbol) diff --git a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala index 6e774e38e..6d0fc08f9 100644 --- a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -291,26 +291,28 @@ trait TypeAssigner { def assignType(tree: untpd.Super, qual: Tree, inConstrCall: Boolean, mixinClass: Symbol = NoSymbol)(implicit ctx: Context) = { val mix = tree.mix - val qtype @ ThisType(_) = qual.tpe - val cls = qtype.cls - - def findMixinSuper(site: Type): Type = site.parents filter (_.name == mix.name) match { - case p :: Nil => - p - case Nil => - errorType(em"$mix does not name a parent class of $cls", tree.pos) - case p :: q :: _ => - errorType("ambiguous parent class qualifier", tree.pos) + qual.tpe match { + case err: ErrorType => untpd.cpy.Super(tree)(qual, mix).withType(err) + case qtype @ ThisType(_) => + val cls = qtype.cls + def findMixinSuper(site: Type): Type = site.parents filter (_.name == mix.name) match { + case p :: Nil => + p + case Nil => + errorType(SuperQualMustBeParent(mix, cls), tree.pos) + case p :: q :: _ => + errorType("ambiguous parent class qualifier", tree.pos) + } + val owntype = + if (mixinClass.exists) mixinClass.typeRef + else if (!mix.isEmpty) findMixinSuper(cls.info) + else if (inConstrCall || ctx.erasedTypes) cls.info.firstParent + else { + val ps = cls.classInfo.parentsWithArgs + if (ps.isEmpty) defn.AnyType else ps.reduceLeft((x: Type, y: Type) => x & y) + } + tree.withType(SuperType(cls.thisType, owntype)) } - val owntype = - if (mixinClass.exists) mixinClass.typeRef - else if (!mix.isEmpty) findMixinSuper(cls.info) - else if (inConstrCall || ctx.erasedTypes) cls.info.firstParent - else { - val ps = cls.classInfo.parentsWithArgs - if (ps.isEmpty) defn.AnyType else ps.reduceLeft((x: Type, y: Type) => x & y) - } - tree.withType(SuperType(cls.thisType, owntype)) } def assignType(tree: untpd.Apply, fn: Tree, args: List[Tree])(implicit ctx: Context) = { diff --git a/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala b/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala index f11c6dd96..be160ac7f 100644 --- a/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala +++ b/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala @@ -304,5 +304,29 @@ class ErrorMessagesTests extends ErrorMessagesTest { assertEquals("x", tree.name.show) } + @Test def superQualMustBeParent = + checkMessagesAfter("frontend") { + """ + |class A { + | def foo(): Unit = () + |} + | + |class B { + |} + | + |class C extends A { + | super[B].foo + |} + """.stripMargin + } + .expect { (ictx, messages) => + implicit val ctx: Context = ictx + val defn = ictx.definitions + assertMessageCount(1, messages) + val SuperQualMustBeParent(qual, cls) :: Nil = messages + + assertEquals("B", qual.show) + assertEquals("class C", cls.show) + } } |