diff options
119 files changed, 2054 insertions, 1224 deletions
diff --git a/compiler/src/dotty/tools/backend/jvm/GenBCode.scala b/compiler/src/dotty/tools/backend/jvm/GenBCode.scala index 65dcb6c79..8fd6d1bc0 100644 --- a/compiler/src/dotty/tools/backend/jvm/GenBCode.scala +++ b/compiler/src/dotty/tools/backend/jvm/GenBCode.scala @@ -1,19 +1,18 @@ package dotty.tools.backend.jvm import dotty.tools.dotc.CompilationUnit -import dotty.tools.dotc.ast.Trees.{ValDef, PackageDef} +import dotty.tools.dotc.ast.Trees.{PackageDef, ValDef} import dotty.tools.dotc.ast.tpd import dotty.tools.dotc.core.Phases.Phase import dotty.tools.dotc.core.Names.TypeName import scala.collection.mutable -import scala.tools.asm.{CustomAttr, ClassVisitor, MethodVisitor, FieldVisitor} +import scala.tools.asm.{ClassVisitor, CustomAttr, FieldVisitor, MethodVisitor} import scala.tools.nsc.Settings import scala.tools.nsc.backend.jvm._ import dotty.tools.dotc import dotty.tools.dotc.backend.jvm.DottyPrimitives import dotty.tools.dotc.transform.Erasure - import dotty.tools.dotc.interfaces import java.util.Optional @@ -27,14 +26,15 @@ import Symbols._ import Denotations._ import Phases._ import java.lang.AssertionError -import java.io.{ File => JFile } +import java.io.{FileOutputStream, File => JFile} + import scala.tools.asm import scala.tools.asm.tree._ -import dotty.tools.dotc.util.{Positions, DotClass} +import dotty.tools.dotc.util.{DotClass, Positions} import tpd._ import StdNames._ -import scala.reflect.io.{Directory, PlainDirectory, AbstractFile} +import scala.reflect.io.{AbstractFile, Directory, PlainDirectory} import scala.tools.nsc.backend.jvm.opt.LocalOpt class GenBCode extends Phase { @@ -205,7 +205,15 @@ class GenBCodePipeline(val entryPoints: List[Symbol], val int: DottyBackendInter if (claszSymbol.isClass) // @DarkDimius is this test needed here? for (binary <- ctx.compilationUnit.pickled.get(claszSymbol.asClass)) { val dataAttr = new CustomAttr(nme.TASTYATTR.toString, binary) - (if (mirrorC ne null) mirrorC else plainC).visitAttribute(dataAttr) + val store = if (mirrorC ne null) mirrorC else plainC + store.visitAttribute(dataAttr) + if (ctx.settings.emitTasty.value) { + val outTastyFile = getFileForClassfile(outF, store.name, ".tasty").file + val fos = new FileOutputStream(outTastyFile, false) + fos.write(binary) + fos.close() + + } } // -------------- bean info class, if needed -------------- diff --git a/compiler/src/dotty/tools/backend/jvm/scalaPrimitives.scala b/compiler/src/dotty/tools/backend/jvm/scalaPrimitives.scala index 89831e56b..7beae90c4 100644 --- a/compiler/src/dotty/tools/backend/jvm/scalaPrimitives.scala +++ b/compiler/src/dotty/tools/backend/jvm/scalaPrimitives.scala @@ -142,7 +142,7 @@ class DottyPrimitives(ctx: Context) { ctx.error(s"Unknown primitive method $cls.$method") else alts foreach (s => addPrimitive(s, - s.info.paramTypess match { + s.info.paramInfoss match { case List(tp :: _) if code == ADD && tp =:= ctx.definitions.StringType => CONCAT case _ => code } diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index 75c7078a1..66fc6bf84 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -14,6 +14,7 @@ import reporting.diagnostic.messages._ object desugar { import untpd._ + import DesugarEnums._ /** Tags a .withFilter call generated by desugaring a for expression. * Such calls can alternatively be rewritten to use filter. @@ -153,8 +154,8 @@ object desugar { case ContextBounds(tbounds, cxbounds) => epbuf ++= makeImplicitParameters(cxbounds, isPrimaryConstructor) tbounds - case PolyTypeTree(tparams, body) => - cpy.PolyTypeTree(rhs)(tparams, desugarContextBounds(body)) + case LambdaTypeTree(tparams, body) => + cpy.LambdaTypeTree(rhs)(tparams, desugarContextBounds(body)) case _ => rhs } @@ -263,7 +264,9 @@ object desugar { val className = checkNotReservedName(cdef).asTypeName val impl @ Template(constr0, parents, self, _) = cdef.rhs val mods = cdef.mods - val companionMods = mods.withFlags((mods.flags & AccessFlags).toCommonFlags) + val companionMods = mods + .withFlags((mods.flags & AccessFlags).toCommonFlags) + .withMods(mods.mods.filter(!_.isInstanceOf[Mod.EnumCase])) val (constr1, defaultGetters) = defDef(constr0, isPrimaryConstructor = true) match { case meth: DefDef => (meth, Nil) @@ -288,17 +291,31 @@ object desugar { } val isCaseClass = mods.is(Case) && !mods.is(Module) + val isEnum = mods.hasMod[Mod.Enum] + val isEnumCase = isLegalEnumCase(cdef) val isValueClass = parents.nonEmpty && isAnyVal(parents.head) // This is not watertight, but `extends AnyVal` will be replaced by `inline` later. - val constrTparams = constr1.tparams map toDefParam + lazy val reconstitutedTypeParams = reconstitutedEnumTypeParams(cdef.pos.startPos) + + val originalTparams = + if (isEnumCase && parents.isEmpty) { + if (constr1.tparams.nonEmpty) { + if (reconstitutedTypeParams.nonEmpty) + ctx.error(em"case with type parameters needs extends clause", constr1.tparams.head.pos) + constr1.tparams + } + else reconstitutedTypeParams + } + else constr1.tparams + val originalVparamss = constr1.vparamss + val constrTparams = originalTparams.map(toDefParam) val constrVparamss = - if (constr1.vparamss.isEmpty) { // ensure parameter list is non-empty - if (isCaseClass) - ctx.error(CaseClassMissingParamList(cdef), cdef.namePos) + if (originalVparamss.isEmpty) { // ensure parameter list is non-empty + if (isCaseClass) ctx.error(CaseClassMissingParamList(cdef), cdef.namePos) ListOfNil } - else constr1.vparamss.nestedMap(toDefParam) + else originalVparamss.nestedMap(toDefParam) val constr = cpy.DefDef(constr1)(tparams = constrTparams, vparamss = constrVparamss) // Add constructor type parameters and evidence implicit parameters @@ -312,21 +329,24 @@ object desugar { stat } - val derivedTparams = constrTparams map derivedTypeParam + val derivedTparams = + if (isEnumCase) constrTparams else constrTparams map derivedTypeParam val derivedVparamss = constrVparamss nestedMap derivedTermParam val arity = constrVparamss.head.length - var classTycon: Tree = EmptyTree + val classTycon: Tree = new TypeRefTree // watching is set at end of method - // a reference to the class type, with all parameters given. - val classTypeRef/*: Tree*/ = { - // -language:keepUnions difference: classTypeRef needs type annotation, otherwise - // infers Ident | AppliedTypeTree, which - // renders the :\ in companions below untypable. - classTycon = (new TypeRefTree) withPos cdef.pos.startPos // watching is set at end of method - val tparams = impl.constr.tparams - if (tparams.isEmpty) classTycon else AppliedTypeTree(classTycon, tparams map refOfDef) - } + def appliedRef(tycon: Tree) = + (if (constrTparams.isEmpty) tycon + else AppliedTypeTree(tycon, constrTparams map refOfDef)) + .withPos(cdef.pos.startPos) + + // a reference to the class type bound by `cdef`, with type parameters coming from the constructor + val classTypeRef = appliedRef(classTycon) + // a reference to `enumClass`, with type parameters coming from the constructor + lazy val enumClassTypeRef = + if (reconstitutedTypeParams.isEmpty) enumClassRef + else appliedRef(enumClassRef) // new C[Ts](paramss) lazy val creatorExpr = New(classTypeRef, constrVparamss nestedMap refOfDef) @@ -374,7 +394,9 @@ object desugar { DefDef(nme.copy, derivedTparams, copyFirstParams :: copyRestParamss, TypeTree(), creatorExpr) .withMods(synthetic) :: Nil } - copyMeths ::: productElemMeths.toList + + val enumTagMeths = if (isEnumCase) enumTagMeth(CaseKind.Class)._1 :: Nil else Nil + copyMeths ::: enumTagMeths ::: productElemMeths.toList } else Nil @@ -387,8 +409,12 @@ object desugar { // Case classes and case objects get a ProductN parent var parents1 = parents + if (isEnumCase && parents.isEmpty) + parents1 = enumClassTypeRef :: Nil if (mods.is(Case) && arity <= Definitions.MaxTupleArity) - parents1 = parents1 :+ productConstr(arity) + parents1 = parents1 :+ productConstr(arity) // TODO: This also adds Product0 to caes objects. Do we want that? + if (isEnum) + parents1 = parents1 :+ ref(defn.EnumType) // The thicket which is the desugared version of the companion object // synthetic object C extends parentTpt { defs } @@ -410,17 +436,26 @@ object desugar { // For all other classes, the parent is AnyRef. val companions = if (isCaseClass) { + // The return type of the `apply` method + val applyResultTpt = + if (isEnumCase) + if (parents.isEmpty) enumClassTypeRef + else parents.reduceLeft(AndTypeTree) + else TypeTree() + val parent = if (constrTparams.nonEmpty || constrVparamss.length > 1 || mods.is(Abstract) || constr.mods.is(Private)) anyRef + else // todo: also use anyRef if constructor has a dependent method type (or rule that out)! - else (constrVparamss :\ classTypeRef) ((vparams, restpe) => Function(vparams map (_.tpt), restpe)) + (constrVparamss :\ (if (isEnumCase) applyResultTpt else classTypeRef)) ( + (vparams, restpe) => Function(vparams map (_.tpt), restpe)) val applyMeths = if (mods is Abstract) Nil else - DefDef(nme.apply, derivedTparams, derivedVparamss, TypeTree(), creatorExpr) + DefDef(nme.apply, derivedTparams, derivedVparamss, applyResultTpt, creatorExpr) .withFlags(Synthetic | (constr1.mods.flags & DefaultParameterized)) :: Nil val unapplyMeth = { val unapplyParam = makeSyntheticParameter(tpt = classTypeRef) @@ -464,15 +499,15 @@ object desugar { else cpy.ValDef(self)(tpt = selfType).withMods(self.mods | SelfName) } - val cdef1 = { - val originalTparams = constr1.tparams.toIterator - val originalVparams = constr1.vparamss.toIterator.flatten - val tparamAccessors = derivedTparams.map(_.withMods(originalTparams.next.mods)) + val cdef1 = addEnumFlags { + val originalTparamsIt = originalTparams.toIterator + val originalVparamsIt = originalVparamss.toIterator.flatten + val tparamAccessors = derivedTparams.map(_.withMods(originalTparamsIt.next.mods)) val caseAccessor = if (isCaseClass) CaseAccessor else EmptyFlags val vparamAccessors = derivedVparamss match { case first :: rest => - first.map(_.withMods(originalVparams.next.mods | caseAccessor)) ++ - rest.flatten.map(_.withMods(originalVparams.next.mods)) + first.map(_.withMods(originalVparamsIt.next.mods | caseAccessor)) ++ + rest.flatten.map(_.withMods(originalVparamsIt.next.mods)) case _ => Nil } @@ -503,23 +538,26 @@ object desugar { */ def moduleDef(mdef: ModuleDef)(implicit ctx: Context): Tree = { val moduleName = checkNotReservedName(mdef).asTermName - val tmpl = mdef.impl + val impl = mdef.impl val mods = mdef.mods + lazy val isEnumCase = isLegalEnumCase(mdef) if (mods is Package) - PackageDef(Ident(moduleName), cpy.ModuleDef(mdef)(nme.PACKAGE, tmpl).withMods(mods &~ Package) :: Nil) + PackageDef(Ident(moduleName), cpy.ModuleDef(mdef)(nme.PACKAGE, impl).withMods(mods &~ Package) :: Nil) + else if (isEnumCase) + expandEnumModule(moduleName, impl, mods, mdef.pos) else { val clsName = moduleName.moduleClassName val clsRef = Ident(clsName) val modul = ValDef(moduleName, clsRef, New(clsRef, Nil)) .withMods(mods | ModuleCreationFlags | mods.flags & AccessFlags) .withPos(mdef.pos) - val ValDef(selfName, selfTpt, _) = tmpl.self - val selfMods = tmpl.self.mods - if (!selfTpt.isEmpty) ctx.error(ObjectMayNotHaveSelfType(mdef), tmpl.self.pos) - val clsSelf = ValDef(selfName, SingletonTypeTree(Ident(moduleName)), tmpl.self.rhs) + val ValDef(selfName, selfTpt, _) = impl.self + val selfMods = impl.self.mods + if (!selfTpt.isEmpty) ctx.error(ObjectMayNotHaveSelfType(mdef), impl.self.pos) + val clsSelf = ValDef(selfName, SingletonTypeTree(Ident(moduleName)), impl.self.rhs) .withMods(selfMods) - .withPos(tmpl.self.pos orElse tmpl.pos.startPos) - val clsTmpl = cpy.Template(tmpl)(self = clsSelf, body = tmpl.body) + .withPos(impl.self.pos orElse impl.pos.startPos) + val clsTmpl = cpy.Template(impl)(self = clsSelf, body = impl.body) val cls = TypeDef(clsName, clsTmpl) .withMods(mods.toTypeFlags & RetainedModuleClassFlags | ModuleClassCreationFlags) Thicket(modul, classDef(cls).withPos(mdef.pos)) @@ -542,11 +580,23 @@ object desugar { /** val p1, ..., pN: T = E * ==> * makePatDef[[val p1: T1 = E]]; ...; makePatDef[[val pN: TN = E]] + * + * case e1, ..., eN + * ==> + * expandSimpleEnumCase([case e1]); ...; expandSimpleEnumCase([case eN]) */ - def patDef(pdef: PatDef)(implicit ctx: Context): Tree = { + def patDef(pdef: PatDef)(implicit ctx: Context): Tree = flatTree { val PatDef(mods, pats, tpt, rhs) = pdef - val pats1 = if (tpt.isEmpty) pats else pats map (Typed(_, tpt)) - flatTree(pats1 map (makePatDef(pdef, mods, _, rhs))) + if (mods.hasMod[Mod.EnumCase] && enumCaseIsLegal(pdef)) + pats map { + case id: Ident => + expandSimpleEnumCase(id.name.asTermName, mods, + Position(pdef.pos.start, id.pos.end, id.pos.start)) + } + else { + val pats1 = if (tpt.isEmpty) pats else pats map (Typed(_, tpt)) + pats1 map (makePatDef(pdef, mods, _, rhs)) + } } /** If `pat` is a variable pattern, @@ -923,7 +973,7 @@ object desugar { case (gen: GenFrom) :: (rest @ (GenFrom(_, _) :: _)) => val cont = makeFor(mapName, flatMapName, rest, body) Apply(rhsSelect(gen, flatMapName), makeLambda(gen.pat, cont)) - case (enum @ GenFrom(pat, rhs)) :: (rest @ GenAlias(_, _) :: _) => + case (GenFrom(pat, rhs)) :: (rest @ GenAlias(_, _) :: _) => val (valeqs, rest1) = rest.span(_.isInstanceOf[GenAlias]) val pats = valeqs map { case GenAlias(pat, _) => pat } val rhss = valeqs map { case GenAlias(_, rhs) => rhs } @@ -1024,7 +1074,6 @@ object desugar { List(CaseDef(Ident(nme.DEFAULT_EXCEPTION_NAME), EmptyTree, Apply(handler, Ident(nme.DEFAULT_EXCEPTION_NAME)))), finalizer) } - } }.withPos(tree.pos) diff --git a/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala b/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala new file mode 100644 index 000000000..43f915961 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala @@ -0,0 +1,200 @@ +package dotty.tools +package dotc +package ast + +import core._ +import util.Positions._, Types._, Contexts._, Constants._, Names._, NameOps._, Flags._ +import SymDenotations._, Symbols._, StdNames._, Annotations._, Trees._ +import Decorators._ +import collection.mutable.ListBuffer +import util.Property +import typer.ErrorReporting._ + +/** Helper methods to desugar enums */ +object DesugarEnums { + import untpd._ + import desugar.DerivedFromParamTree + + @sharable object CaseKind extends Enumeration { + val Simple, Object, Class = Value + } + + /** Attachment containing the number of enum cases and the smallest kind that was seen so far. */ + val EnumCaseCount = new Property.Key[(Int, CaseKind.Value)] + + /** the enumeration class that is a companion of the current object */ + def enumClass(implicit ctx: Context) = ctx.owner.linkedClass + + /** Is this an enum case that's situated in a companion object of an enum class? */ + def isLegalEnumCase(tree: MemberDef)(implicit ctx: Context): Boolean = + tree.mods.hasMod[Mod.EnumCase] && enumCaseIsLegal(tree) + + /** Is enum case `tree` situated in a companion object of an enum class? */ + def enumCaseIsLegal(tree: Tree)(implicit ctx: Context): Boolean = ( + ctx.owner.is(ModuleClass) && enumClass.derivesFrom(defn.EnumClass) + || { ctx.error(em"case not allowed here, since owner ${ctx.owner} is not an `enum' object", tree.pos) + false + } + ) + + /** Type parameters reconstituted from the constructor + * of the `enum' class corresponding to an enum case. + * The variance is the same as the corresponding type parameter of the enum class. + */ + def reconstitutedEnumTypeParams(pos: Position)(implicit ctx: Context) = { + val tparams = enumClass.primaryConstructor.info match { + case info: PolyType => + ctx.newTypeParams(ctx.newLocalDummy(enumClass), info.paramNames, EmptyFlags, info.instantiateBounds) + case _ => + Nil + } + (tparams, enumClass.typeParams).zipped.map { (tparam, ecTparam) => + val tbounds = new DerivedFromParamTree + tbounds.pushAttachment(OriginalSymbol, tparam) + TypeDef(tparam.name, tbounds) + .withFlags(Param | PrivateLocal | ecTparam.flags & VarianceFlags).withPos(pos) + } + } + + /** A reference to the enum class `E`, possibly followed by type arguments. + * Each covariant type parameter is approximated by its lower bound. + * Each contravariant type parameter is approximated by its upper bound. + * It is an error if a type parameter is non-variant, or if its approximation + * refers to pther type parameters. + */ + def interpolatedEnumParent(pos: Position)(implicit ctx: Context): Tree = { + val tparams = enumClass.typeParams + def isGround(tp: Type) = tp.subst(tparams, tparams.map(_ => NoType)) eq tp + val targs = tparams map { tparam => + if (tparam.variance > 0 && isGround(tparam.info.bounds.lo)) + tparam.info.bounds.lo + else if (tparam.variance < 0 && isGround(tparam.info.bounds.hi)) + tparam.info.bounds.hi + else { + def problem = + if (tparam.variance == 0) "is non variant" + else "has bounds that depend on a type parameter in the same parameter list" + errorType(i"""cannot determine type argument for enum parent $enumClass, + |type parameter $tparam $problem""", pos) + } + } + TypeTree(enumClass.typeRef.appliedTo(targs)).withPos(pos) + } + + /** A type tree referring to `enumClass` */ + def enumClassRef(implicit ctx: Context) = TypeTree(enumClass.typeRef) + + /** Add implied flags to an enum class or an enum case */ + def addEnumFlags(cdef: TypeDef)(implicit ctx: Context) = + if (cdef.mods.hasMod[Mod.Enum]) cdef.withFlags(cdef.mods.flags | Abstract | Sealed) + else if (isLegalEnumCase(cdef)) cdef.withFlags(cdef.mods.flags | Final) + else cdef + + private def valuesDot(name: String) = Select(Ident(nme.DOLLAR_VALUES), name.toTermName) + private def registerCall(implicit ctx: Context): List[Tree] = + if (enumClass.typeParams.nonEmpty) Nil + else Apply(valuesDot("register"), This(EmptyTypeIdent) :: Nil) :: Nil + + /** The following lists of definitions for an enum type E: + * + * private val $values = new EnumValues[E] + * def enumValue = $values.fromInt + * def enumValueNamed = $values.fromName + * def enumValues = $values.values + */ + private def enumScaffolding(implicit ctx: Context): List[Tree] = { + def enumDefDef(name: String, select: String) = + DefDef(name.toTermName, Nil, Nil, TypeTree(), valuesDot(select)) + val privateValuesDef = + ValDef(nme.DOLLAR_VALUES, TypeTree(), + New(TypeTree(defn.EnumValuesType.appliedTo(enumClass.typeRef :: Nil)), ListOfNil)) + .withFlags(Private) + val valueOfDef = enumDefDef("enumValue", "fromInt") + val withNameDef = enumDefDef("enumValueNamed", "fromName") + val valuesDef = enumDefDef("enumValues", "values") + List(privateValuesDef, valueOfDef, withNameDef, valuesDef) + } + + /** A creation method for a value of enum type `E`, which is defined as follows: + * + * private def $new(tag: Int, name: String) = new E { + * def enumTag = tag + * override def toString = name + * $values.register(this) + * } + */ + private def enumValueCreator(implicit ctx: Context) = { + def param(name: TermName, typ: Type) = + ValDef(name, TypeTree(typ), EmptyTree).withFlags(Param) + val enumTagDef = + DefDef(nme.enumTag, Nil, Nil, TypeTree(), Ident(nme.tag)) + val toStringDef = + DefDef(nme.toString_, Nil, Nil, TypeTree(), Ident(nme.name)) + .withFlags(Override) + def creator = New(Template(emptyConstructor, enumClassRef :: Nil, EmptyValDef, + List(enumTagDef, toStringDef) ++ registerCall)) + DefDef(nme.DOLLAR_NEW, Nil, + List(List(param(nme.tag, defn.IntType), param(nme.name, defn.StringType))), + TypeTree(), creator) + } + + /** A pair consisting of + * - the next enum tag + * - scaffolding containing the necessary definitions for singleton enum cases + * unless that scaffolding was already generated by a previous call to `nextEnumKind`. + */ + def nextEnumTag(kind: CaseKind.Value)(implicit ctx: Context): (Int, List[Tree]) = { + val (count, seenKind) = ctx.tree.removeAttachment(EnumCaseCount).getOrElse((0, CaseKind.Class)) + val minKind = if (kind < seenKind) kind else seenKind + ctx.tree.pushAttachment(EnumCaseCount, (count + 1, minKind)) + val scaffolding = + if (enumClass.typeParams.nonEmpty || kind >= seenKind) Nil + else if (kind == CaseKind.Object) enumScaffolding + else if (seenKind == CaseKind.Object) enumValueCreator :: Nil + else enumScaffolding :+ enumValueCreator + (count, scaffolding) + } + + /** A pair consisting of + * - a method returning the next enum tag + * - scaffolding as defined in `nextEnumTag` + */ + def enumTagMeth(kind: CaseKind.Value)(implicit ctx: Context): (DefDef, List[Tree]) = { + val (tag, scaffolding) = nextEnumTag(kind) + (DefDef(nme.enumTag, Nil, Nil, TypeTree(), Literal(Constant(tag))), scaffolding) + } + + /** Expand a module definition representing a parameterless enum case */ + def expandEnumModule(name: TermName, impl: Template, mods: Modifiers, pos: Position)(implicit ctx: Context): Tree = + if (impl.parents.isEmpty) + if (impl.body.isEmpty) + expandSimpleEnumCase(name, mods, pos) + else { + val parent = interpolatedEnumParent(pos) + expandEnumModule(name, cpy.Template(impl)(parents = parent :: Nil), mods, pos) + } + else { + def toStringMeth = + DefDef(nme.toString_, Nil, Nil, TypeTree(defn.StringType), Literal(Constant(name.toString))) + .withFlags(Override) + val (tagMeth, scaffolding) = enumTagMeth(CaseKind.Object) + val impl1 = cpy.Template(impl)(body = + impl.body ++ List(tagMeth, toStringMeth) ++ registerCall) + val vdef = ValDef(name, TypeTree(), New(impl1)).withMods(mods | Final).withPos(pos) + flatTree(scaffolding ::: vdef :: Nil).withPos(pos.startPos) + } + + /** Expand a simple enum case */ + def expandSimpleEnumCase(name: TermName, mods: Modifiers, pos: Position)(implicit ctx: Context): Tree = + if (enumClass.typeParams.nonEmpty) { + val parent = interpolatedEnumParent(pos) + val impl = Template(emptyConstructor, parent :: Nil, EmptyValDef, Nil) + expandEnumModule(name, impl, mods, pos) + } + else { + val (tag, scaffolding) = nextEnumTag(CaseKind.Simple) + val creator = Apply(Ident(nme.DOLLAR_NEW), List(Literal(Constant(tag)), Literal(Constant(name.toString)))) + val vdef = ValDef(name, enumClassRef, creator).withMods(mods | Final).withPos(pos) + flatTree(scaffolding ::: vdef :: Nil).withPos(pos.startPos) + } +} diff --git a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala index b00d7df71..037ab73af 100644 --- a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala +++ b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala @@ -262,7 +262,7 @@ trait UntypedTreeInfo extends TreeInfo[Untyped] { self: Trees.Instance[Untyped] case mdef: TypeDef => def isBounds(rhs: Tree): Boolean = rhs match { case _: TypeBoundsTree => true - case PolyTypeTree(_, body) => isBounds(body) + case LambdaTypeTree(_, body) => isBounds(body) case _ => false } mdef.rhs.isEmpty || isBounds(mdef.rhs) diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index 21cc6a6a3..20d7c1458 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -578,9 +578,9 @@ object Trees { } /** [typeparams] -> tpt */ - case class PolyTypeTree[-T >: Untyped] private[ast] (tparams: List[TypeDef[T]], body: Tree[T]) + case class LambdaTypeTree[-T >: Untyped] private[ast] (tparams: List[TypeDef[T]], body: Tree[T]) extends TypTree[T] { - type ThisTree[-T >: Untyped] = PolyTypeTree[T] + type ThisTree[-T >: Untyped] = LambdaTypeTree[T] } /** => T */ @@ -833,7 +833,7 @@ object Trees { type OrTypeTree = Trees.OrTypeTree[T] type RefinedTypeTree = Trees.RefinedTypeTree[T] type AppliedTypeTree = Trees.AppliedTypeTree[T] - type PolyTypeTree = Trees.PolyTypeTree[T] + type LambdaTypeTree = Trees.LambdaTypeTree[T] type ByNameTypeTree = Trees.ByNameTypeTree[T] type TypeBoundsTree = Trees.TypeBoundsTree[T] type Bind = Trees.Bind[T] @@ -998,9 +998,9 @@ object Trees { case tree: AppliedTypeTree if (tpt eq tree.tpt) && (args eq tree.args) => tree case _ => finalize(tree, untpd.AppliedTypeTree(tpt, args)) } - def PolyTypeTree(tree: Tree)(tparams: List[TypeDef], body: Tree): PolyTypeTree = tree match { - case tree: PolyTypeTree if (tparams eq tree.tparams) && (body eq tree.body) => tree - case _ => finalize(tree, untpd.PolyTypeTree(tparams, body)) + def LambdaTypeTree(tree: Tree)(tparams: List[TypeDef], body: Tree): LambdaTypeTree = tree match { + case tree: LambdaTypeTree if (tparams eq tree.tparams) && (body eq tree.body) => tree + case _ => finalize(tree, untpd.LambdaTypeTree(tparams, body)) } def ByNameTypeTree(tree: Tree)(result: Tree): ByNameTypeTree = tree match { case tree: ByNameTypeTree if result eq tree.result => tree @@ -1144,8 +1144,8 @@ object Trees { cpy.RefinedTypeTree(tree)(transform(tpt), transformSub(refinements)) case AppliedTypeTree(tpt, args) => cpy.AppliedTypeTree(tree)(transform(tpt), transform(args)) - case PolyTypeTree(tparams, body) => - cpy.PolyTypeTree(tree)(transformSub(tparams), transform(body)) + case LambdaTypeTree(tparams, body) => + cpy.LambdaTypeTree(tree)(transformSub(tparams), transform(body)) case ByNameTypeTree(result) => cpy.ByNameTypeTree(tree)(transform(result)) case TypeBoundsTree(lo, hi) => @@ -1248,7 +1248,7 @@ object Trees { this(this(x, tpt), refinements) case AppliedTypeTree(tpt, args) => this(this(x, tpt), args) - case PolyTypeTree(tparams, body) => + case LambdaTypeTree(tparams, body) => implicit val ctx = localCtx this(this(x, tparams), body) case ByNameTypeTree(result) => diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index cbb256dd0..ff66c8c8e 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -145,8 +145,8 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def ByNameTypeTree(result: Tree)(implicit ctx: Context): ByNameTypeTree = ta.assignType(untpd.ByNameTypeTree(result), result) - def PolyTypeTree(tparams: List[TypeDef], body: Tree)(implicit ctx: Context): PolyTypeTree = - ta.assignType(untpd.PolyTypeTree(tparams, body), tparams, body) + def LambdaTypeTree(tparams: List[TypeDef], body: Tree)(implicit ctx: Context): LambdaTypeTree = + ta.assignType(untpd.LambdaTypeTree(tparams, body), tparams, body) def TypeBoundsTree(lo: Tree, hi: Tree)(implicit ctx: Context): TypeBoundsTree = ta.assignType(untpd.TypeBoundsTree(lo, hi), lo, hi) @@ -190,7 +190,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { val maybeImplicit = if (tp.isInstanceOf[ImplicitMethodType]) Implicit else EmptyFlags ctx.newSymbol(sym, name, TermParam | maybeImplicit, info) } - val params = (tp.paramNames, tp.paramTypes).zipped.map(valueParam) + val params = (tp.paramNames, tp.paramInfos).zipped.map(valueParam) val (paramss, rtp) = valueParamss(tp.instantiate(params map (_.termRef))) (params :: paramss, rtp) case tp => (Nil, tp.widenExpr) @@ -221,7 +221,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { case ctpe: PolyType => isApplicable(ctpe.instantiate(firstParent.argTypes)) case ctpe: MethodType => - (superArgs corresponds ctpe.paramTypes)(_.tpe <:< _) + (superArgs corresponds ctpe.paramInfos)(_.tpe <:< _) case _ => false } @@ -836,7 +836,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { val selected = if (denot.isOverloaded) { def typeParamCount(tp: Type) = tp.widen match { - case tp: PolyType => tp.paramBounds.length + case tp: PolyType => tp.paramInfos.length case _ => 0 } var allAlts = denot.alternatives @@ -859,7 +859,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def adaptLastArg(lastParam: Tree, expectedType: Type) = { if (isAnnotConstructor && !(lastParam.tpe <:< expectedType)) { val defn = ctx.definitions - val prefix = args.take(selected.widen.paramTypess.head.size - 1) + val prefix = args.take(selected.widen.paramInfoss.head.size - 1) expectedType match { case defn.ArrayOf(el) => lastParam.tpe match { @@ -878,7 +878,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { } val callArgs: List[Tree] = if (args.isEmpty) Nil else { - val expectedType = selected.widen.paramTypess.head.last + val expectedType = selected.widen.paramInfoss.head.last val lastParam = args.last adaptLastArg(lastParam, expectedType) } diff --git a/compiler/src/dotty/tools/dotc/ast/untpd.scala b/compiler/src/dotty/tools/dotc/ast/untpd.scala index 1f37ad1c5..7a9f2fe66 100644 --- a/compiler/src/dotty/tools/dotc/ast/untpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala @@ -9,6 +9,7 @@ import Decorators._ import util.Property import language.higherKinds import collection.mutable.ListBuffer +import reflect.ClassTag object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { @@ -132,6 +133,10 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { case class Inline() extends Mod(Flags.Inline) case class Type() extends Mod(Flags.EmptyFlags) + + case class Enum() extends Mod(Flags.EmptyFlags) + + case class EnumCase() extends Mod(Flags.EmptyFlags) } /** Modifiers and annotations for definitions @@ -185,6 +190,10 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { def hasFlags = flags != EmptyFlags def hasAnnotations = annotations.nonEmpty def hasPrivateWithin = privateWithin != tpnme.EMPTY + def hasMod[T: ClassTag] = { + val cls = implicitly[ClassTag[T]].runtimeClass + mods.exists(mod => cls.isAssignableFrom(mod.getClass)) + } } @sharable val EmptyModifiers: Modifiers = new Modifiers() @@ -261,7 +270,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { def OrTypeTree(left: Tree, right: Tree): OrTypeTree = new OrTypeTree(left, right) def RefinedTypeTree(tpt: Tree, refinements: List[Tree]): RefinedTypeTree = new RefinedTypeTree(tpt, refinements) def AppliedTypeTree(tpt: Tree, args: List[Tree]): AppliedTypeTree = new AppliedTypeTree(tpt, args) - def PolyTypeTree(tparams: List[TypeDef], body: Tree): PolyTypeTree = new PolyTypeTree(tparams, body) + def LambdaTypeTree(tparams: List[TypeDef], body: Tree): LambdaTypeTree = new LambdaTypeTree(tparams, body) def ByNameTypeTree(result: Tree): ByNameTypeTree = new ByNameTypeTree(result) def TypeBoundsTree(lo: Tree, hi: Tree): TypeBoundsTree = new TypeBoundsTree(lo, hi) def Bind(name: Name, body: Tree): Bind = new Bind(name, body) @@ -352,7 +361,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { ValDef(nme.syntheticParamName(n), tpt, EmptyTree).withFlags(SyntheticTermParam) def lambdaAbstract(tparams: List[TypeDef], tpt: Tree)(implicit ctx: Context) = - if (tparams.isEmpty) tpt else PolyTypeTree(tparams, tpt) + if (tparams.isEmpty) tpt else LambdaTypeTree(tparams, tpt) /** A reference to given definition. If definition is a repeated * parameter, the reference will be a repeated argument. diff --git a/compiler/src/dotty/tools/dotc/config/Config.scala b/compiler/src/dotty/tools/dotc/config/Config.scala index 903efd794..46b1896f1 100644 --- a/compiler/src/dotty/tools/dotc/config/Config.scala +++ b/compiler/src/dotty/tools/dotc/config/Config.scala @@ -40,7 +40,7 @@ object Config { * accesses javac's settings.) * * It is recommended to turn this option on only when chasing down - * a PolyParam instantiation error. See comment in Types.TypeVar.instantiate. + * a TypeParamRef instantiation error. See comment in Types.TypeVar.instantiate. */ final val debugCheckConstraintsClosed = false diff --git a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala index 318295751..49b64d869 100644 --- a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -71,6 +71,7 @@ class ScalaSettings extends Settings.SettingGroup { val debugOwners = BooleanSetting("-Ydebug-owners", "Print all owners of definitions (requires -Yprint-syms)") val termConflict = ChoiceSetting("-Yresolve-term-conflict", "strategy", "Resolve term conflicts", List("package", "object", "error"), "error") val log = PhasesSetting("-Ylog", "Log operations during") + val emitTasty = BooleanSetting("-YemitTasty", "Generate tasty in separate *.tasty file.") val Ylogcp = BooleanSetting("-Ylog-classpath", "Output information about what classpath is being applied.") val YnoImports = BooleanSetting("-Yno-imports", "Compile without importing scala.*, java.lang.*, or Predef.") val YnoPredef = BooleanSetting("-Yno-predef", "Compile without importing Predef.") diff --git a/compiler/src/dotty/tools/dotc/core/Constants.scala b/compiler/src/dotty/tools/dotc/core/Constants.scala index 1892e4bdc..ed388b7ec 100644 --- a/compiler/src/dotty/tools/dotc/core/Constants.scala +++ b/compiler/src/dotty/tools/dotc/core/Constants.scala @@ -169,12 +169,12 @@ object Constants { def convertTo(pt: Type)(implicit ctx: Context): Constant = { def classBound(pt: Type): Type = pt.dealias.stripTypeVar match { case tref: TypeRef if !tref.symbol.isClass => classBound(tref.info.bounds.lo) - case param: PolyParam => + case param: TypeParamRef => ctx.typerState.constraint.entry(param) match { case TypeBounds(lo, hi) => if (hi.classSymbol.isPrimitiveValueClass) hi //constrain further with high bound else classBound(lo) - case NoType => classBound(param.binder.paramBounds(param.paramNum).lo) + case NoType => classBound(param.binder.paramInfos(param.paramNum).lo) case inst => classBound(inst) } case pt => pt diff --git a/compiler/src/dotty/tools/dotc/core/Constraint.scala b/compiler/src/dotty/tools/dotc/core/Constraint.scala index 50136a26c..a6c21c0d1 100644 --- a/compiler/src/dotty/tools/dotc/core/Constraint.scala +++ b/compiler/src/dotty/tools/dotc/core/Constraint.scala @@ -13,20 +13,20 @@ import config.Printers.constr /** Constraint over undetermined type parameters. Constraints are built * over values of the following types: * - * - PolyType A constraint constrains the type parameters of a set of PolyTypes - * - PolyParam The parameters of the constrained polytypes - * - TypeVar Every constrained parameter might be associated with a TypeVar - * that has the PolyParam as origin. + * - TypeLambda A constraint constrains the type parameters of a set of TypeLambdas + * - TypeParamRef The parameters of the constrained type lambdas + * - TypeVar Every constrained parameter might be associated with a TypeVar + * that has the TypeParamRef as origin. */ abstract class Constraint extends Showable { type This <: Constraint /** Does the constraint's domain contain the type parameters of `pt`? */ - def contains(pt: PolyType): Boolean + def contains(pt: TypeLambda): Boolean /** Does the constraint's domain contain the type parameter `param`? */ - def contains(param: PolyParam): Boolean + def contains(param: TypeParamRef): Boolean /** Does this constraint contain the type variable `tvar` and is it uninstantiated? */ def contains(tvar: TypeVar): Boolean @@ -34,43 +34,43 @@ abstract class Constraint extends Showable { /** The constraint entry for given type parameter `param`, or NoType if `param` is not part of * the constraint domain. Note: Low level, implementation dependent. */ - def entry(param: PolyParam): Type + def entry(param: TypeParamRef): Type /** The type variable corresponding to parameter `param`, or * NoType, if `param` is not in constrained or is not paired with a type variable. */ - def typeVarOfParam(param: PolyParam): Type + def typeVarOfParam(param: TypeParamRef): Type /** Is it known that `param1 <:< param2`? */ - def isLess(param1: PolyParam, param2: PolyParam): Boolean + def isLess(param1: TypeParamRef, param2: TypeParamRef): Boolean /** The parameters that are known to be smaller wrt <: than `param` */ - def lower(param: PolyParam): List[PolyParam] + def lower(param: TypeParamRef): List[TypeParamRef] /** The parameters that are known to be greater wrt <: than `param` */ - def upper(param: PolyParam): List[PolyParam] + def upper(param: TypeParamRef): List[TypeParamRef] /** lower(param) \ lower(butNot) */ - def exclusiveLower(param: PolyParam, butNot: PolyParam): List[PolyParam] + def exclusiveLower(param: TypeParamRef, butNot: TypeParamRef): List[TypeParamRef] /** upper(param) \ upper(butNot) */ - def exclusiveUpper(param: PolyParam, butNot: PolyParam): List[PolyParam] + def exclusiveUpper(param: TypeParamRef, butNot: TypeParamRef): List[TypeParamRef] /** The constraint bounds for given type parameter `param`. * Poly params that are known to be smaller or greater than `param` * are not contained in the return bounds. * @pre `param` is not part of the constraint domain. */ - def nonParamBounds(param: PolyParam): TypeBounds + def nonParamBounds(param: TypeParamRef): TypeBounds /** The lower bound of `param` including all known-to-be-smaller parameters */ - def fullLowerBound(param: PolyParam)(implicit ctx: Context): Type + def fullLowerBound(param: TypeParamRef)(implicit ctx: Context): Type /** The upper bound of `param` including all known-to-be-greater parameters */ - def fullUpperBound(param: PolyParam)(implicit ctx: Context): Type + def fullUpperBound(param: TypeParamRef)(implicit ctx: Context): Type /** The bounds of `param` including all known-to-be-smaller and -greater parameters */ - def fullBounds(param: PolyParam)(implicit ctx: Context): TypeBounds + def fullBounds(param: TypeParamRef)(implicit ctx: Context): TypeBounds /** A new constraint which is derived from this constraint by adding * entries for all type parameters of `poly`. @@ -79,7 +79,7 @@ abstract class Constraint extends Showable { * satisfiability but will solved to give instances of * type variables. */ - def add(poly: PolyType, tvars: List[TypeVar])(implicit ctx: Context): This + def add(poly: TypeLambda, tvars: List[TypeVar])(implicit ctx: Context): This /** A new constraint which is derived from this constraint by updating * the entry for parameter `param` to `tp`. @@ -90,18 +90,18 @@ abstract class Constraint extends Showable { * * @pre `this contains param`. */ - def updateEntry(param: PolyParam, tp: Type)(implicit ctx: Context): This + def updateEntry(param: TypeParamRef, tp: Type)(implicit ctx: Context): This /** A constraint that includes the relationship `p1 <: p2`. * `<:` relationships between parameters ("edges") are propagated, but * non-parameter bounds are left alone. */ - def addLess(p1: PolyParam, p2: PolyParam)(implicit ctx: Context): This + def addLess(p1: TypeParamRef, p2: TypeParamRef)(implicit ctx: Context): This /** A constraint resulting from adding p2 = p1 to this constraint, and at the same * time transferring all bounds of p2 to p1 */ - def unify(p1: PolyParam, p2: PolyParam)(implicit ctx: Context): This + def unify(p1: TypeParamRef, p2: TypeParamRef)(implicit ctx: Context): This /** A new constraint which is derived from this constraint by removing * the type parameter `param` from the domain and replacing all top-level occurrences @@ -109,25 +109,25 @@ abstract class Constraint extends Showable { * approximation of it if that is needed to avoid cycles. * Occurrences nested inside a refinement or prefix are not affected. */ - def replace(param: PolyParam, tp: Type)(implicit ctx: Context): This + def replace(param: TypeParamRef, tp: Type)(implicit ctx: Context): This /** Is entry associated with `pt` removable? This is the case if * all type parameters of the entry are associated with type variables * which have their `inst` fields set. */ - def isRemovable(pt: PolyType): Boolean + def isRemovable(pt: TypeLambda): Boolean /** A new constraint with all entries coming from `pt` removed. */ - def remove(pt: PolyType)(implicit ctx: Context): This + def remove(pt: TypeLambda)(implicit ctx: Context): This - /** The polytypes constrained by this constraint */ - def domainPolys: List[PolyType] + /** The type lambdas constrained by this constraint */ + def domainLambdas: List[TypeLambda] - /** The polytype parameters constrained by this constraint */ - def domainParams: List[PolyParam] + /** The type lambda parameters constrained by this constraint */ + def domainParams: List[TypeParamRef] /** Check whether predicate holds for all parameters in constraint */ - def forallParams(p: PolyParam => Boolean): Boolean + def forallParams(p: TypeParamRef => Boolean): Boolean /** Perform operation `op` on all typevars, or only on uninstantiated * typevars, depending on whether `uninstOnly` is set or not. @@ -143,6 +143,6 @@ abstract class Constraint extends Showable { /** Check that no constrained parameter contains itself as a bound */ def checkNonCyclic()(implicit ctx: Context): Unit - /** Check that constraint only refers to PolyParams bound by itself */ + /** Check that constraint only refers to TypeParamRefs bound by itself */ def checkClosed()(implicit ctx: Context): Unit } diff --git a/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala b/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala index a12936c58..de96f644a 100644 --- a/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala +++ b/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala @@ -42,12 +42,12 @@ trait ConstraintHandling { */ protected var homogenizeArgs = false - /** We are currently comparing polytypes. Used as a flag for + /** We are currently comparing type lambdas. Used as a flag for * optimization: when `false`, no need to do an expensive `pruneLambdaParams` */ - protected var comparedPolyTypes: Set[PolyType] = Set.empty + protected var comparedTypeLambdas: Set[TypeLambda] = Set.empty - private def addOneBound(param: PolyParam, bound: Type, isUpper: Boolean): Boolean = + private def addOneBound(param: TypeParamRef, bound: Type, isUpper: Boolean): Boolean = !constraint.contains(param) || { def occursIn(bound: Type): Boolean = { val b = bound.dealias @@ -75,7 +75,7 @@ trait ConstraintHandling { * If `isUpper` is true, ensure that `param <: `bound`, otherwise ensure * that `param >: bound`. */ - def narrowedBound(param: PolyParam, bound: Type, isUpper: Boolean)(implicit ctx: Context): TypeBounds = { + def narrowedBound(param: TypeParamRef, bound: Type, isUpper: Boolean)(implicit ctx: Context): TypeBounds = { val oldBounds @ TypeBounds(lo, hi) = constraint.nonParamBounds(param) val saved = homogenizeArgs homogenizeArgs = Config.alignArgsInAnd @@ -85,7 +85,7 @@ trait ConstraintHandling { finally homogenizeArgs = saved } - protected def addUpperBound(param: PolyParam, bound: Type): Boolean = { + protected def addUpperBound(param: TypeParamRef, bound: Type): Boolean = { def description = i"constraint $param <: $bound to\n$constraint" if (bound.isRef(defn.NothingClass) && ctx.typerState.isGlobalCommittable) { def msg = s"!!! instantiated to Nothing: $param, constraint = ${constraint.show}" @@ -101,7 +101,7 @@ trait ConstraintHandling { res } - protected def addLowerBound(param: PolyParam, bound: Type): Boolean = { + protected def addLowerBound(param: TypeParamRef, bound: Type): Boolean = { def description = i"constraint $param >: $bound to\n$constraint" constr.println(i"adding $description") val upper = constraint.upper(param) @@ -112,7 +112,7 @@ trait ConstraintHandling { res } - protected def addLess(p1: PolyParam, p2: PolyParam): Boolean = { + protected def addLess(p1: TypeParamRef, p2: TypeParamRef): Boolean = { def description = i"ordering $p1 <: $p2 to\n$constraint" val res = if (constraint.isLess(p2, p1)) unify(p2, p1) @@ -133,7 +133,7 @@ trait ConstraintHandling { /** Make p2 = p1, transfer all bounds of p2 to p1 * @pre less(p1)(p2) */ - private def unify(p1: PolyParam, p2: PolyParam): Boolean = { + private def unify(p1: TypeParamRef, p2: TypeParamRef): Boolean = { constr.println(s"unifying $p1 $p2") assert(constraint.isLess(p1, p2)) val down = constraint.exclusiveLower(p2, p1) @@ -191,7 +191,7 @@ trait ConstraintHandling { * @return the instantiating type * @pre `param` is in the constraint's domain. */ - final def approximation(param: PolyParam, fromBelow: Boolean): Type = { + final def approximation(param: TypeParamRef, fromBelow: Boolean): Type = { val avoidParam = new TypeMap { override def stopAtStatic = true def apply(tp: Type) = mapOver { @@ -235,7 +235,7 @@ trait ConstraintHandling { * a lower bound instantiation can be a singleton type only if the upper bound * is also a singleton type. */ - def instanceType(param: PolyParam, fromBelow: Boolean): Type = { + def instanceType(param: TypeParamRef, fromBelow: Boolean): Type = { def upperBound = constraint.fullUpperBound(param) def isSingleton(tp: Type): Boolean = tp match { case tp: SingletonType => true @@ -301,26 +301,26 @@ trait ConstraintHandling { } /** The current bounds of type parameter `param` */ - final def bounds(param: PolyParam): TypeBounds = { + final def bounds(param: TypeParamRef): TypeBounds = { val e = constraint.entry(param) - if (e.exists) e.bounds else param.binder.paramBounds(param.paramNum) + if (e.exists) e.bounds else param.binder.paramInfos(param.paramNum) } - /** Add polytype `pt`, possibly with type variables `tvars`, to current constraint + /** Add type lambda `tl`, possibly with type variables `tvars`, to current constraint * and propagate all bounds. * @param tvars See Constraint#add */ - def addToConstraint(pt: PolyType, tvars: List[TypeVar]): Unit = + def addToConstraint(tl: TypeLambda, tvars: List[TypeVar]): Unit = assert { - checkPropagated(i"initialized $pt") { - constraint = constraint.add(pt, tvars) - pt.paramNames.indices.forall { i => - val param = PolyParam(pt, i) + checkPropagated(i"initialized $tl") { + constraint = constraint.add(tl, tvars) + tl.paramNames.indices.forall { i => + val param = TypeParamRef(tl, i) val bounds = constraint.nonParamBounds(param) val lower = constraint.lower(param) val upper = constraint.upper(param) if (lower.nonEmpty && !bounds.lo.isRef(defn.NothingClass) || - upper.nonEmpty && !bounds.hi.isRef(defn.AnyClass)) constr.println(i"INIT*** $pt") + upper.nonEmpty && !bounds.hi.isRef(defn.AnyClass)) constr.println(i"INIT*** $tl") lower.forall(addOneBound(_, bounds.hi, isUpper = true)) && upper.forall(addOneBound(_, bounds.lo, isUpper = false)) } @@ -328,7 +328,7 @@ trait ConstraintHandling { } /** Can `param` be constrained with new bounds? */ - final def canConstrain(param: PolyParam): Boolean = + final def canConstrain(param: TypeParamRef): Boolean = !frozenConstraint && (constraint contains param) /** Add constraint `param <: bound` if `fromBelow` is false, `param >: bound` otherwise. @@ -338,7 +338,7 @@ trait ConstraintHandling { * not be AndTypes and lower bounds may not be OrTypes. This is assured by the * way isSubType is organized. */ - protected def addConstraint(param: PolyParam, bound: Type, fromBelow: Boolean): Boolean = { + protected def addConstraint(param: TypeParamRef, bound: Type, fromBelow: Boolean): Boolean = { def description = i"constr $param ${if (fromBelow) ">:" else "<:"} $bound:\n$constraint" //checkPropagated(s"adding $description")(true) // DEBUG in case following fails checkPropagated(s"added $description") { @@ -357,12 +357,12 @@ trait ConstraintHandling { * missing. */ def pruneLambdaParams(tp: Type) = - if (comparedPolyTypes.nonEmpty) { + if (comparedTypeLambdas.nonEmpty) { val approx = new ApproximatingTypeMap { def apply(t: Type): Type = t match { - case t @ PolyParam(pt: PolyType, n) if comparedPolyTypes contains pt => + case t @ TypeParamRef(tl: TypeLambda, n) if comparedTypeLambdas contains tl => val effectiveVariance = if (fromBelow) -variance else variance - val bounds = pt.paramBounds(n) + val bounds = tl.paramInfos(n) if (effectiveVariance > 0) bounds.lo else if (effectiveVariance < 0) bounds.hi else NoType @@ -374,7 +374,7 @@ trait ConstraintHandling { } else tp - def addParamBound(bound: PolyParam) = + def addParamBound(bound: TypeParamRef) = if (fromBelow) addLess(bound, param) else addLess(param, bound) /** Drop all constrained parameters that occur at the toplevel in `bound` and @@ -419,7 +419,7 @@ trait ConstraintHandling { else NoType case bound: TypeVar if constraint contains bound.origin => prune(bound.underlying) - case bound: PolyParam => + case bound: TypeParamRef => constraint.entry(bound) match { case NoType => pruneLambdaParams(bound) case _: TypeBounds => @@ -434,7 +434,7 @@ trait ConstraintHandling { } try bound match { - case bound: PolyParam if constraint contains bound => + case bound: TypeParamRef if constraint contains bound => addParamBound(bound) case _ => val pbound = prune(bound) @@ -446,7 +446,7 @@ trait ConstraintHandling { } /** Instantiate `param` to `tp` if the constraint stays satisfiable */ - protected def tryInstantiate(param: PolyParam, tp: Type): Boolean = { + protected def tryInstantiate(param: TypeParamRef, tp: Type): Boolean = { val saved = constraint constraint = if (addConstraint(param, tp, fromBelow = true) && @@ -461,7 +461,7 @@ trait ConstraintHandling { val saved = frozenConstraint frozenConstraint = true for (p <- constraint.domainParams) { - def check(cond: => Boolean, q: PolyParam, ordering: String, explanation: String): Unit = + def check(cond: => Boolean, q: TypeParamRef, ordering: String, explanation: String): Unit = assert(cond, i"propagation failure for $p $ordering $q: $explanation\n$msg") for (u <- constraint.upper(p)) check(bounds(p).hi <:< bounds(u).hi, u, "<:", "upper bound not propagated") diff --git a/compiler/src/dotty/tools/dotc/core/Contexts.scala b/compiler/src/dotty/tools/dotc/core/Contexts.scala index 6b7be12be..8707b66f9 100644 --- a/compiler/src/dotty/tools/dotc/core/Contexts.scala +++ b/compiler/src/dotty/tools/dotc/core/Contexts.scala @@ -306,13 +306,14 @@ object Contexts { sb.append(str) } - /** The next outer context whose tree is a template or package definition */ + /** The next outer context whose tree is a template or package definition + * Note: Currently unused def enclTemplate: Context = { var c = this while (c != NoContext && !c.tree.isInstanceOf[Template[_]] && !c.tree.isInstanceOf[PackageDef[_]]) c = c.outer c - } + }*/ /** The context for a supercall. This context is used for elaborating * the parents of a class and their arguments. diff --git a/compiler/src/dotty/tools/dotc/core/Decorators.scala b/compiler/src/dotty/tools/dotc/core/Decorators.scala index 0e8ae196a..c65dc5b97 100644 --- a/compiler/src/dotty/tools/dotc/core/Decorators.scala +++ b/compiler/src/dotty/tools/dotc/core/Decorators.scala @@ -185,7 +185,7 @@ object Decorators { * give more info about type variables and to disambiguate where needed. */ def ex(args: Any*)(implicit ctx: Context): String = - explained2(implicit ctx => em(args: _*)) + explained(implicit ctx => em(args: _*)) /** Formatter that adds syntax highlighting to all interpolated values */ def hl(args: Any*)(implicit ctx: Context): String = diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 4d4350f98..b70fcb093 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -150,9 +150,9 @@ class Definitions { private def enterPolyMethod(cls: ClassSymbol, name: TermName, typeParamCount: Int, resultTypeFn: PolyType => Type, flags: FlagSet = EmptyFlags) = { - val tparamNames = tpnme.syntheticTypeParamNames(typeParamCount) - val tparamBounds = tparamNames map (_ => TypeBounds.empty) - val ptype = PolyType(tparamNames, tparamNames.map(alwaysZero))(_ => tparamBounds, resultTypeFn) + val tparamNames = PolyType.syntheticParamNames(typeParamCount) + val tparamInfos = tparamNames map (_ => TypeBounds.empty) + val ptype = PolyType(tparamNames)(_ => tparamInfos, resultTypeFn) enterMethod(cls, name, ptype, flags) } @@ -242,7 +242,7 @@ class Definitions { lazy val Any_## = enterMethod(AnyClass, nme.HASHHASH, ExprType(IntType), Final) lazy val Any_getClass = enterMethod(AnyClass, nme.getClass_, MethodType(Nil, ClassClass.typeRef.appliedTo(TypeBounds.empty)), Final) lazy val Any_isInstanceOf = enterT1ParameterlessMethod(AnyClass, nme.isInstanceOf_, _ => BooleanType, Final) - lazy val Any_asInstanceOf = enterT1ParameterlessMethod(AnyClass, nme.asInstanceOf_, PolyParam(_, 0), Final) + lazy val Any_asInstanceOf = enterT1ParameterlessMethod(AnyClass, nme.asInstanceOf_, TypeParamRef(_, 0), Final) def AnyMethods = List(Any_==, Any_!=, Any_equals, Any_hashCode, Any_toString, Any_##, Any_getClass, Any_isInstanceOf, Any_asInstanceOf) @@ -268,7 +268,7 @@ class Definitions { lazy val Object_eq = enterMethod(ObjectClass, nme.eq, methOfAnyRef(BooleanType), Final) lazy val Object_ne = enterMethod(ObjectClass, nme.ne, methOfAnyRef(BooleanType), Final) lazy val Object_synchronized = enterPolyMethod(ObjectClass, nme.synchronized_, 1, - pt => MethodType(List(PolyParam(pt, 0)), PolyParam(pt, 0)), Final) + pt => MethodType(List(TypeParamRef(pt, 0)), TypeParamRef(pt, 0)), Final) lazy val Object_clone = enterMethod(ObjectClass, nme.clone_, MethodType(Nil, ObjectType), Protected) lazy val Object_finalize = enterMethod(ObjectClass, nme.finalize_, MethodType(Nil, UnitType), Protected) lazy val Object_notify = enterMethod(ObjectClass, nme.notify_, MethodType(Nil, UnitType)) @@ -283,7 +283,7 @@ class Definitions { /** Dummy method needed by elimByName */ lazy val dummyApply = enterPolyMethod( OpsPackageClass, nme.dummyApply, 1, - pt => MethodType(List(FunctionOf(Nil, PolyParam(pt, 0))), PolyParam(pt, 0))) + pt => MethodType(List(FunctionOf(Nil, TypeParamRef(pt, 0))), TypeParamRef(pt, 0))) /** Method representing a throw */ lazy val throwMethod = enterMethod(OpsPackageClass, nme.THROWkw, @@ -512,6 +512,10 @@ class Definitions { def DynamicClass(implicit ctx: Context) = DynamicType.symbol.asClass lazy val OptionType: TypeRef = ctx.requiredClassRef("scala.Option") def OptionClass(implicit ctx: Context) = OptionType.symbol.asClass + lazy val EnumType: TypeRef = ctx.requiredClassRef("scala.Enum") + def EnumClass(implicit ctx: Context) = EnumType.symbol.asClass + lazy val EnumValuesType: TypeRef = ctx.requiredClassRef("scala.runtime.EnumValues") + def EnumValuesClass(implicit ctx: Context) = EnumValuesType.symbol.asClass lazy val ProductType: TypeRef = ctx.requiredClassRef("scala.Product") def ProductClass(implicit ctx: Context) = ProductType.symbol.asClass lazy val Product_canEqualR = ProductClass.requiredMethodRef(nme.canEqual_) diff --git a/compiler/src/dotty/tools/dotc/core/Denotations.scala b/compiler/src/dotty/tools/dotc/core/Denotations.scala index f726cd0d1..7341b96af 100644 --- a/compiler/src/dotty/tools/dotc/core/Denotations.scala +++ b/compiler/src/dotty/tools/dotc/core/Denotations.scala @@ -213,7 +213,7 @@ object Denotations { def requiredMethod(name: PreName, argTypes: List[Type])(implicit ctx: Context): TermSymbol = info.member(name.toTermName).requiredSymbol(x=> - (x is Method) && x.info.paramTypess == List(argTypes) + (x is Method) && x.info.paramInfoss == List(argTypes) ).asTerm def requiredMethodRef(name: PreName, argTypes: List[Type])(implicit ctx: Context): TermRef = requiredMethod(name, argTypes).termRef @@ -252,13 +252,12 @@ object Denotations { else throw new Error(s"cannot merge ${showType(tp1)} with ${showType(tp2)}") // flip condition for debugging } - /** Merge two lists of names. If names in corresponding positions match, keep them, + /** Merge parameter names of lambda types. If names in corresponding positions match, keep them, * otherwise generate new synthetic names. */ - def mergeNames[N <: Name](names1: List[N], names2: List[N], syntheticName: Int => N): List[N] = { - for ((name1, name2, idx) <- (names1, names2, 0 until names1.length).zipped) - yield if (name1 == name2) name1 else syntheticName(idx) - }.toList + private def mergeParamNames(tp1: LambdaType, tp2: LambdaType): List[tp1.ThisName] = + (for ((name1, name2, idx) <- (tp1.paramNames, tp2.paramNames, tp1.paramNames.indices).zipped) + yield if (name1 == name2) name1 else tp1.companion.syntheticParamName(idx)).toList /** Form a denotation by conjoining with denotation `that`. * @@ -308,27 +307,17 @@ object Denotations { case tp2: TypeBounds if tp2 contains tp1 => tp1 case _ => mergeConflict(tp1, tp2) } - case tp1: MethodType if isTerm => + case tp1: MethodOrPoly => tp2 match { - case tp2: MethodType if ctx.typeComparer.matchingParams(tp1.paramTypes, tp2.paramTypes, tp1.isJava, tp2.isJava) && - tp1.isImplicit == tp2.isImplicit => - tp1.derivedMethodType( - mergeNames(tp1.paramNames, tp2.paramNames, nme.syntheticParamName), - tp1.paramTypes, + case tp2: MethodOrPoly + if ctx.typeComparer.matchingParams(tp1, tp2) && + tp1.isImplicit == tp2.isImplicit => + tp1.derivedLambdaType( + mergeParamNames(tp1, tp2), tp1.paramInfos, infoMeet(tp1.resultType, tp2.resultType.subst(tp2, tp1))) case _ => mergeConflict(tp1, tp2) } - case tp1: PolyType if isTerm => - tp2 match { - case tp2: PolyType if ctx.typeComparer.matchingTypeParams(tp1, tp2) => - tp1.derivedPolyType( - mergeNames(tp1.paramNames, tp2.paramNames, tpnme.syntheticTypeParamName), - tp1.paramBounds, - infoMeet(tp1.resultType, tp2.resultType.subst(tp2, tp1))) - case _: MethodicType => - mergeConflict(tp1, tp2) - } case _ => tp1 & tp2 } @@ -471,23 +460,14 @@ object Denotations { case tp2: TypeBounds if tp2 contains tp1 => tp2 case _ => mergeConflict(tp1, tp2) } - case tp1: MethodType => - tp2 match { - case tp2: MethodType - if ctx.typeComparer.matchingParams(tp1.paramTypes, tp2.paramTypes, tp1.isJava, tp2.isJava) && - tp1.isImplicit == tp2.isImplicit => - tp1.derivedMethodType( - mergeNames(tp1.paramNames, tp2.paramNames, nme.syntheticParamName), - tp1.paramTypes, tp1.resultType | tp2.resultType.subst(tp2, tp1)) - case _ => - mergeConflict(tp1, tp2) - } - case tp1: PolyType => + case tp1: MethodOrPoly => tp2 match { - case tp2: PolyType if ctx.typeComparer.matchingTypeParams(tp1, tp2) => - tp1.derivedPolyType( - mergeNames(tp1.paramNames, tp2.paramNames, tpnme.syntheticTypeParamName), - tp1.paramBounds, tp1.resultType | tp2.resultType.subst(tp2, tp1)) + case tp2: MethodOrPoly + if ctx.typeComparer.matchingParams(tp1, tp2) && + tp1.isImplicit == tp2.isImplicit => + tp1.derivedLambdaType( + mergeParamNames(tp1, tp2), tp1.paramInfos, + tp1.resultType | tp2.resultType.subst(tp2, tp1)) case _ => mergeConflict(tp1, tp2) } diff --git a/compiler/src/dotty/tools/dotc/core/Flags.scala b/compiler/src/dotty/tools/dotc/core/Flags.scala index c1267d8a2..29f1078a2 100644 --- a/compiler/src/dotty/tools/dotc/core/Flags.scala +++ b/compiler/src/dotty/tools/dotc/core/Flags.scala @@ -450,13 +450,22 @@ object Flags { /** Flags guaranteed to be set upon symbol creation */ final val FromStartFlags = - AccessFlags | Module | Package | Deferred | Final | MethodOrHKCommon | Param | ParamAccessor | Scala2ExistentialCommon | - Mutable.toCommonFlags | InSuperCall | Touched | JavaStatic | CovariantOrOuter | ContravariantOrLabel | ExpandedName | AccessorOrSealed | - CaseAccessorOrBaseTypeArg | Fresh | Frozen | Erroneous | ImplicitCommon | Permanent | Synthetic | - Inline | LazyOrTrait | SuperAccessorOrScala2x | SelfNameOrImplClass + Module | Package | Deferred | MethodOrHKCommon | Param | ParamAccessor | + Scala2ExistentialCommon | Mutable.toCommonFlags | InSuperCall | Touched | JavaStatic | + CovariantOrOuter | ContravariantOrLabel | ExpandedName | CaseAccessorOrBaseTypeArg | + Fresh | Frozen | Erroneous | ImplicitCommon | Permanent | Synthetic | + SuperAccessorOrScala2x | Inline + + /** Flags guaranteed to be set upon symbol creation, or, if symbol is a top-level + * class or object, when the class file defining the symbol is loaded (which + * is generally before the symbol is completed + */ + final val AfterLoadFlags = + FromStartFlags | AccessFlags | Final | AccessorOrSealed | LazyOrTrait | SelfNameOrImplClass assert(FromStartFlags.isTermFlags && FromStartFlags.isTypeFlags) // TODO: Should check that FromStartFlags do not change in completion + assert(AfterLoadFlags.isTermFlags && AfterLoadFlags.isTypeFlags) /** A value that's unstable unless complemented with a Stable flag */ final val UnstableValue = Mutable | Method diff --git a/compiler/src/dotty/tools/dotc/core/Mode.scala b/compiler/src/dotty/tools/dotc/core/Mode.scala index 406a84af6..c835f677e 100644 --- a/compiler/src/dotty/tools/dotc/core/Mode.scala +++ b/compiler/src/dotty/tools/dotc/core/Mode.scala @@ -34,7 +34,7 @@ object Mode { * context with typerstate and constraint. This is typically done when we * cache the eligibility of implicits. Caching needs to be done across different constraints. * Therefore, if TypevarsMissContext is set, subtyping becomes looser, and assumes - * that PolyParams can be sub- and supertypes of anything. See TypeComparer. + * that TypeParamRefs can be sub- and supertypes of anything. See TypeComparer. */ val TypevarsMissContext = newMode(4, "TypevarsMissContext") val CheckCyclic = newMode(5, "CheckCyclic") diff --git a/compiler/src/dotty/tools/dotc/core/NameOps.scala b/compiler/src/dotty/tools/dotc/core/NameOps.scala index ea905c19f..240ad359b 100644 --- a/compiler/src/dotty/tools/dotc/core/NameOps.scala +++ b/compiler/src/dotty/tools/dotc/core/NameOps.scala @@ -196,6 +196,31 @@ object NameOps { if (name.isModuleClassName) name.stripModuleClassSuffix.freshened.moduleClassName else likeTyped(ctx.freshName(name ++ NameTransformer.NAME_JOIN_STRING))) + /** Name with variance prefix: `+` for covariant, `-` for contravariant */ + def withVariance(v: Int): N = + if (hasVariance) dropVariance.withVariance(v) + else v match { + case -1 => likeTyped('-' +: name) + case 1 => likeTyped('+' +: name) + case 0 => name + } + + /** Does name have a `+`/`-` variance prefix? */ + def hasVariance: Boolean = + name.nonEmpty && name.head == '+' || name.head == '-' + + /** Drop variance prefix if name has one */ + def dropVariance: N = if (hasVariance) likeTyped(name.tail) else name + + /** The variance as implied by the variance prefix, or 0 if there is + * no variance prefix. + */ + def variance = name.head match { + case '-' => -1 + case '+' => 1 + case _ => 0 + } + /** Translate a name into a list of simple TypeNames and TermNames. * In all segments before the last, type/term is determined by whether * the following separator char is '.' or '#'. The last segment @@ -271,7 +296,6 @@ object NameOps { else -1 } - /** The number of hops specified in an outer-select name */ def outerSelectHops: Int = { require(isOuterSelect) diff --git a/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala b/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala index 61dd5a445..84b0bfc6d 100644 --- a/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala +++ b/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala @@ -14,13 +14,13 @@ import annotation.tailrec object OrderingConstraint { - type ArrayValuedMap[T] = SimpleMap[PolyType, Array[T]] + type ArrayValuedMap[T] = SimpleMap[TypeLambda, Array[T]] /** The type of `OrderingConstraint#boundsMap` */ type ParamBounds = ArrayValuedMap[Type] /** The type of `OrderingConstraint#lowerMap`, `OrderingConstraint#upperMap` */ - type ParamOrdering = ArrayValuedMap[List[PolyParam]] + type ParamOrdering = ArrayValuedMap[List[TypeParamRef]] /** A new constraint with given maps */ private def newConstraint(boundsMap: ParamBounds, lowerMap: ParamOrdering, upperMap: ParamOrdering)(implicit ctx: Context) : OrderingConstraint = { @@ -32,11 +32,11 @@ object OrderingConstraint { /** A lens for updating a single entry array in one of the three constraint maps */ abstract class ConstraintLens[T <: AnyRef: ClassTag] { - def entries(c: OrderingConstraint, poly: PolyType): Array[T] - def updateEntries(c: OrderingConstraint, poly: PolyType, entries: Array[T])(implicit ctx: Context): OrderingConstraint + def entries(c: OrderingConstraint, poly: TypeLambda): Array[T] + def updateEntries(c: OrderingConstraint, poly: TypeLambda, entries: Array[T])(implicit ctx: Context): OrderingConstraint def initial: T - def apply(c: OrderingConstraint, poly: PolyType, idx: Int) = { + def apply(c: OrderingConstraint, poly: TypeLambda, idx: Int) = { val es = entries(c, poly) if (es == null) initial else es(idx) } @@ -47,7 +47,7 @@ object OrderingConstraint { * parts of `current` which are not shared by `prev`. */ def update(prev: OrderingConstraint, current: OrderingConstraint, - poly: PolyType, idx: Int, entry: T)(implicit ctx: Context): OrderingConstraint = { + poly: TypeLambda, idx: Int, entry: T)(implicit ctx: Context): OrderingConstraint = { var es = entries(current, poly) if (es != null && (es(idx) eq entry)) current else { @@ -68,38 +68,38 @@ object OrderingConstraint { } def update(prev: OrderingConstraint, current: OrderingConstraint, - param: PolyParam, entry: T)(implicit ctx: Context): OrderingConstraint = + param: TypeParamRef, entry: T)(implicit ctx: Context): OrderingConstraint = update(prev, current, param.binder, param.paramNum, entry) def map(prev: OrderingConstraint, current: OrderingConstraint, - poly: PolyType, idx: Int, f: T => T)(implicit ctx: Context): OrderingConstraint = + poly: TypeLambda, idx: Int, f: T => T)(implicit ctx: Context): OrderingConstraint = update(prev, current, poly, idx, f(apply(current, poly, idx))) def map(prev: OrderingConstraint, current: OrderingConstraint, - param: PolyParam, f: T => T)(implicit ctx: Context): OrderingConstraint = + param: TypeParamRef, f: T => T)(implicit ctx: Context): OrderingConstraint = map(prev, current, param.binder, param.paramNum, f) } val boundsLens = new ConstraintLens[Type] { - def entries(c: OrderingConstraint, poly: PolyType): Array[Type] = + def entries(c: OrderingConstraint, poly: TypeLambda): Array[Type] = c.boundsMap(poly) - def updateEntries(c: OrderingConstraint, poly: PolyType, entries: Array[Type])(implicit ctx: Context): OrderingConstraint = + def updateEntries(c: OrderingConstraint, poly: TypeLambda, entries: Array[Type])(implicit ctx: Context): OrderingConstraint = newConstraint(c.boundsMap.updated(poly, entries), c.lowerMap, c.upperMap) def initial = NoType } - val lowerLens = new ConstraintLens[List[PolyParam]] { - def entries(c: OrderingConstraint, poly: PolyType): Array[List[PolyParam]] = + val lowerLens = new ConstraintLens[List[TypeParamRef]] { + def entries(c: OrderingConstraint, poly: TypeLambda): Array[List[TypeParamRef]] = c.lowerMap(poly) - def updateEntries(c: OrderingConstraint, poly: PolyType, entries: Array[List[PolyParam]])(implicit ctx: Context): OrderingConstraint = + def updateEntries(c: OrderingConstraint, poly: TypeLambda, entries: Array[List[TypeParamRef]])(implicit ctx: Context): OrderingConstraint = newConstraint(c.boundsMap, c.lowerMap.updated(poly, entries), c.upperMap) def initial = Nil } - val upperLens = new ConstraintLens[List[PolyParam]] { - def entries(c: OrderingConstraint, poly: PolyType): Array[List[PolyParam]] = + val upperLens = new ConstraintLens[List[TypeParamRef]] { + def entries(c: OrderingConstraint, poly: TypeLambda): Array[List[TypeParamRef]] = c.upperMap(poly) - def updateEntries(c: OrderingConstraint, poly: PolyType, entries: Array[List[PolyParam]])(implicit ctx: Context): OrderingConstraint = + def updateEntries(c: OrderingConstraint, poly: TypeLambda, entries: Array[List[TypeParamRef]])(implicit ctx: Context): OrderingConstraint = newConstraint(c.boundsMap, c.lowerMap, c.upperMap.updated(poly, entries)) def initial = Nil } @@ -109,20 +109,20 @@ import OrderingConstraint._ /** Constraint over undetermined type parameters that keeps separate maps to * reflect parameter orderings. - * @param boundsMap a map from PolyType to arrays. + * @param boundsMap a map from TypeLambda to arrays. * Each array contains twice the number of entries as there a type parameters - * in the PolyType. The first half of the array contains the type bounds that constrain the - * polytype's type parameters. The second half might contain type variables that + * in the TypeLambda. The first half of the array contains the type bounds that constrain the + * lambda's type parameters. The second half might contain type variables that * track the corresponding parameters, or is left empty (filled with nulls). * An instantiated type parameter is represented by having its instance type in * the corresponding array entry. The dual use of arrays for poly params * and typevars is to save space and hopefully gain some speed. * - * @param lowerMap a map from PolyTypes to arrays. Each array entry corresponds - * to a parameter P of the polytype; it contains all constrained parameters + * @param lowerMap a map from TypeLambdas to arrays. Each array entry corresponds + * to a parameter P of the type lambda; it contains all constrained parameters * Q that are known to be smaller than P, i.e. Q <: P. - * @param upperMap a map from PolyTypes to arrays. Each array entry corresponds - * to a parameter P of the polytype; it contains all constrained parameters + * @param upperMap a map from TypeLambdas to arrays. Each array entry corresponds + * to a parameter P of the type lambda; it contains all constrained parameters * Q that are known to be greater than P, i.e. P <: Q. */ class OrderingConstraint(private val boundsMap: ParamBounds, @@ -141,7 +141,7 @@ class OrderingConstraint(private val boundsMap: ParamBounds, entries(paramCount(entries) + n) /** The `boundsMap` entry corresponding to `param` */ - def entry(param: PolyParam): Type = { + def entry(param: TypeParamRef): Type = { val entries = boundsMap(param.binder) if (entries == null) NoType else entries(param.paramNum) @@ -149,9 +149,9 @@ class OrderingConstraint(private val boundsMap: ParamBounds, // ----------- Contains tests -------------------------------------------------- - def contains(pt: PolyType): Boolean = boundsMap(pt) != null + def contains(pt: TypeLambda): Boolean = boundsMap(pt) != null - def contains(param: PolyParam): Boolean = { + def contains(param: TypeParamRef): Boolean = { val entries = boundsMap(param.binder) entries != null && isBounds(entries(param.paramNum)) } @@ -167,43 +167,43 @@ class OrderingConstraint(private val boundsMap: ParamBounds, // ---------- Dependency handling ---------------------------------------------- - def lower(param: PolyParam): List[PolyParam] = lowerLens(this, param.binder, param.paramNum) - def upper(param: PolyParam): List[PolyParam] = upperLens(this, param.binder, param.paramNum) + def lower(param: TypeParamRef): List[TypeParamRef] = lowerLens(this, param.binder, param.paramNum) + def upper(param: TypeParamRef): List[TypeParamRef] = upperLens(this, param.binder, param.paramNum) - def minLower(param: PolyParam): List[PolyParam] = { + def minLower(param: TypeParamRef): List[TypeParamRef] = { val all = lower(param) all.filterNot(p => all.exists(isLess(p, _))) } - def minUpper(param: PolyParam): List[PolyParam] = { + def minUpper(param: TypeParamRef): List[TypeParamRef] = { val all = upper(param) all.filterNot(p => all.exists(isLess(_, p))) } - def exclusiveLower(param: PolyParam, butNot: PolyParam): List[PolyParam] = + def exclusiveLower(param: TypeParamRef, butNot: TypeParamRef): List[TypeParamRef] = lower(param).filterNot(isLess(_, butNot)) - def exclusiveUpper(param: PolyParam, butNot: PolyParam): List[PolyParam] = + def exclusiveUpper(param: TypeParamRef, butNot: TypeParamRef): List[TypeParamRef] = upper(param).filterNot(isLess(butNot, _)) -// ---------- Info related to PolyParams ------------------------------------------- +// ---------- Info related to TypeParamRefs ------------------------------------------- - def isLess(param1: PolyParam, param2: PolyParam): Boolean = + def isLess(param1: TypeParamRef, param2: TypeParamRef): Boolean = upper(param1).contains(param2) - def nonParamBounds(param: PolyParam): TypeBounds = + def nonParamBounds(param: TypeParamRef): TypeBounds = entry(param).asInstanceOf[TypeBounds] - def fullLowerBound(param: PolyParam)(implicit ctx: Context): Type = + def fullLowerBound(param: TypeParamRef)(implicit ctx: Context): Type = (nonParamBounds(param).lo /: minLower(param))(_ | _) - def fullUpperBound(param: PolyParam)(implicit ctx: Context): Type = + def fullUpperBound(param: TypeParamRef)(implicit ctx: Context): Type = (nonParamBounds(param).hi /: minUpper(param))(_ & _) - def fullBounds(param: PolyParam)(implicit ctx: Context): TypeBounds = + def fullBounds(param: TypeParamRef)(implicit ctx: Context): TypeBounds = nonParamBounds(param).derivedTypeBounds(fullLowerBound(param), fullUpperBound(param)) - def typeVarOfParam(param: PolyParam): Type = { + def typeVarOfParam(param: TypeParamRef): Type = { val entries = boundsMap(param.binder) if (entries == null) NoType else { @@ -212,15 +212,15 @@ class OrderingConstraint(private val boundsMap: ParamBounds, } } -// ---------- Adding PolyTypes -------------------------------------------------- +// ---------- Adding TypeLambdas -------------------------------------------------- /** The list of parameters P such that, for a fresh type parameter Q: * * Q <: tp implies Q <: P and isUpper = true, or * tp <: Q implies P <: Q and isUpper = false */ - def dependentParams(tp: Type, isUpper: Boolean): List[PolyParam] = tp match { - case param: PolyParam if contains(param) => + def dependentParams(tp: Type, isUpper: Boolean): List[TypeParamRef] = tp match { + case param: TypeParamRef if contains(param) => param :: (if (isUpper) upper(param) else lower(param)) case tp: AndOrType => val ps1 = dependentParams(tp.tp1, isUpper) @@ -255,9 +255,9 @@ class OrderingConstraint(private val boundsMap: ParamBounds, * * @param isUpper If true, `bound` is an upper bound, else a lower bound. */ - private def stripParams(tp: Type, paramBuf: mutable.ListBuffer[PolyParam], + private def stripParams(tp: Type, paramBuf: mutable.ListBuffer[TypeParamRef], isUpper: Boolean)(implicit ctx: Context): Type = tp match { - case param: PolyParam if contains(param) => + case param: TypeParamRef if contains(param) => if (!paramBuf.contains(param)) paramBuf += param NoType case tp: AndOrType if isUpper == tp.isAnd => @@ -275,16 +275,16 @@ class OrderingConstraint(private val boundsMap: ParamBounds, * A top or bottom type if type consists only of dependent parameters. * @param isUpper If true, `bound` is an upper bound, else a lower bound. */ - private def normalizedType(tp: Type, paramBuf: mutable.ListBuffer[PolyParam], + private def normalizedType(tp: Type, paramBuf: mutable.ListBuffer[TypeParamRef], isUpper: Boolean)(implicit ctx: Context): Type = stripParams(tp, paramBuf, isUpper) .orElse(if (isUpper) defn.AnyType else defn.NothingType) - def add(poly: PolyType, tvars: List[TypeVar])(implicit ctx: Context): This = { + def add(poly: TypeLambda, tvars: List[TypeVar])(implicit ctx: Context): This = { assert(!contains(poly)) val nparams = poly.paramNames.length val entries1 = new Array[Type](nparams * 2) - poly.paramBounds.copyToArray(entries1, 0) + poly.paramInfos.copyToArray(entries1, 0) tvars.copyToArray(entries1, nparams) newConstraint(boundsMap.updated(poly, entries1), lowerMap, upperMap).init(poly) } @@ -293,12 +293,12 @@ class OrderingConstraint(private val boundsMap: ParamBounds, * Update all bounds to be normalized and update ordering to account for * dependent parameters. */ - private def init(poly: PolyType)(implicit ctx: Context): This = { + private def init(poly: TypeLambda)(implicit ctx: Context): This = { var current = this - val loBuf, hiBuf = new mutable.ListBuffer[PolyParam] + val loBuf, hiBuf = new mutable.ListBuffer[TypeParamRef] var i = 0 while (i < poly.paramNames.length) { - val param = PolyParam(poly, i) + val param = TypeParamRef(poly, i) val bounds = nonParamBounds(param) val lo = normalizedType(bounds.lo, loBuf, isUpper = false) val hi = normalizedType(bounds.hi, hiBuf, isUpper = true) @@ -318,7 +318,7 @@ class OrderingConstraint(private val boundsMap: ParamBounds, /** Add the fact `param1 <: param2` to the constraint `current` and propagate * `<:<` relationships between parameters ("edges") but not bounds. */ - private def order(current: This, param1: PolyParam, param2: PolyParam)(implicit ctx: Context): This = + private def order(current: This, param1: TypeParamRef, param2: TypeParamRef)(implicit ctx: Context): This = if (param1 == param2 || current.isLess(param1, param2)) this else { assert(contains(param1)) @@ -330,10 +330,10 @@ class OrderingConstraint(private val boundsMap: ParamBounds, current2 } - def addLess(param1: PolyParam, param2: PolyParam)(implicit ctx: Context): This = + def addLess(param1: TypeParamRef, param2: TypeParamRef)(implicit ctx: Context): This = order(this, param1, param2) - def updateEntry(current: This, param: PolyParam, tp: Type)(implicit ctx: Context): This = { + def updateEntry(current: This, param: TypeParamRef, tp: Type)(implicit ctx: Context): This = { var current1 = boundsLens.update(this, current, param, tp) tp match { case TypeBounds(lo, hi) => @@ -346,10 +346,10 @@ class OrderingConstraint(private val boundsMap: ParamBounds, current1 } - def updateEntry(param: PolyParam, tp: Type)(implicit ctx: Context): This = + def updateEntry(param: TypeParamRef, tp: Type)(implicit ctx: Context): This = updateEntry(this, param, tp) - def unify(p1: PolyParam, p2: PolyParam)(implicit ctx: Context): This = { + def unify(p1: TypeParamRef, p2: TypeParamRef)(implicit ctx: Context): This = { val p1Bounds = (nonParamBounds(p1) & nonParamBounds(p2)).substParam(p2, p1) updateEntry(p1, p1Bounds).replace(p2, p1) } @@ -381,7 +381,7 @@ class OrderingConstraint(private val boundsMap: ParamBounds, * would not find out where we need to approximate. Occurrences of parameters * that are not top-level are not affected. */ - def replace(param: PolyParam, tp: Type)(implicit ctx: Context): OrderingConstraint = { + def replace(param: TypeParamRef, tp: Type)(implicit ctx: Context): OrderingConstraint = { val replacement = tp.dealias.stripTypeVar if (param == replacement) this else { @@ -389,10 +389,10 @@ class OrderingConstraint(private val boundsMap: ParamBounds, val poly = param.binder val idx = param.paramNum - def removeParam(ps: List[PolyParam]) = + def removeParam(ps: List[TypeParamRef]) = ps.filterNot(p => p.binder.eq(poly) && p.paramNum == idx) - def replaceParam(tp: Type, atPoly: PolyType, atIdx: Int): Type = tp match { + def replaceParam(tp: Type, atPoly: TypeLambda, atIdx: Int): Type = tp match { case bounds @ TypeBounds(lo, hi) => def recombine(andor: AndOrType, op: (Type, Boolean) => Type, isUpper: Boolean): Type = { @@ -404,7 +404,7 @@ class OrderingConstraint(private val boundsMap: ParamBounds, } def normalize(tp: Type, isUpper: Boolean): Type = tp match { - case p: PolyParam if p.binder == atPoly && p.paramNum == atIdx => + case p: TypeParamRef if p.binder == atPoly && p.paramNum == atIdx => if (isUpper) defn.AnyType else defn.NothingType case tp: AndOrType if isUpper == tp.isAnd => recombine(tp, normalize, isUpper) case _ => tp @@ -432,9 +432,9 @@ class OrderingConstraint(private val boundsMap: ParamBounds, } } - def remove(pt: PolyType)(implicit ctx: Context): This = { + def remove(pt: TypeLambda)(implicit ctx: Context): This = { def removeFromOrdering(po: ParamOrdering) = { - def removeFromBoundss(key: PolyType, bndss: Array[List[PolyParam]]): Array[List[PolyParam]] = { + def removeFromBoundss(key: TypeLambda, bndss: Array[List[TypeParamRef]]): Array[List[TypeParamRef]] = { val bndss1 = bndss.map(_.filterConserve(_.binder ne pt)) if (bndss.corresponds(bndss1)(_ eq _)) bndss else bndss1 } @@ -443,7 +443,7 @@ class OrderingConstraint(private val boundsMap: ParamBounds, newConstraint(boundsMap.remove(pt), removeFromOrdering(lowerMap), removeFromOrdering(upperMap)) } - def isRemovable(pt: PolyType): Boolean = { + def isRemovable(pt: TypeLambda): Boolean = { val entries = boundsMap(pt) @tailrec def allRemovable(last: Int): Boolean = if (last < 0) true @@ -456,24 +456,24 @@ class OrderingConstraint(private val boundsMap: ParamBounds, // ---------- Exploration -------------------------------------------------------- - def domainPolys: List[PolyType] = boundsMap.keys + def domainLambdas: List[TypeLambda] = boundsMap.keys - def domainParams: List[PolyParam] = + def domainParams: List[TypeParamRef] = for { (poly, entries) <- boundsMap.toList n <- 0 until paramCount(entries) if entries(n).exists - } yield PolyParam(poly, n) + } yield TypeParamRef(poly, n) - def forallParams(p: PolyParam => Boolean): Boolean = { + def forallParams(p: TypeParamRef => Boolean): Boolean = { boundsMap.foreachBinding { (poly, entries) => for (i <- 0 until paramCount(entries)) - if (isBounds(entries(i)) && !p(PolyParam(poly, i))) return false + if (isBounds(entries(i)) && !p(TypeParamRef(poly, i))) return false } true } - def foreachParam(p: (PolyType, Int) => Unit): Unit = + def foreachParam(p: (TypeLambda, Int) => Unit): Unit = boundsMap.foreachBinding { (poly, entries) => 0.until(poly.paramNames.length).foreach(p(poly, _)) } @@ -503,7 +503,7 @@ class OrderingConstraint(private val boundsMap: ParamBounds, merged } - def mergeParams(ps1: List[PolyParam], ps2: List[PolyParam]) = + def mergeParams(ps1: List[TypeParamRef], ps2: List[TypeParamRef]) = (ps1 /: ps2)((ps1, p2) => if (ps1.contains(p2)) ps1 else p2 :: ps1) def mergeEntries(e1: Type, e2: Type): Type = e1 match { @@ -532,13 +532,13 @@ class OrderingConstraint(private val boundsMap: ParamBounds, } override def checkClosed()(implicit ctx: Context): Unit = { - def isFreePolyParam(tp: Type) = tp match { - case PolyParam(binder: PolyType, _) => !contains(binder) + def isFreeTypeParamRef(tp: Type) = tp match { + case TypeParamRef(binder: TypeLambda, _) => !contains(binder) case _ => false } def checkClosedType(tp: Type, where: String) = if (tp != null) - assert(!tp.existsPart(isFreePolyParam), i"unclosed constraint: $this refers to $tp in $where") + assert(!tp.existsPart(isFreeTypeParamRef), i"unclosed constraint: $this refers to $tp in $where") boundsMap.foreachBinding((_, tps) => tps.foreach(checkClosedType(_, "bounds"))) lowerMap.foreachBinding((_, paramss) => paramss.foreach(_.foreach(checkClosedType(_, "lower")))) upperMap.foreachBinding((_, paramss) => paramss.foreach(_.foreach(checkClosedType(_, "upper")))) @@ -567,7 +567,7 @@ class OrderingConstraint(private val boundsMap: ParamBounds, def checkNonCyclic()(implicit ctx: Context): Unit = domainParams.foreach(checkNonCyclic) - private def checkNonCyclic(param: PolyParam)(implicit ctx: Context): Unit = + private def checkNonCyclic(param: TypeParamRef)(implicit ctx: Context): Unit = assert(!isLess(param, param), i"cyclic constraint involving $param in $this") // ---------- toText ----------------------------------------------------- @@ -584,7 +584,7 @@ class OrderingConstraint(private val boundsMap: ParamBounds, val uninstVarsText = " uninstVars = " ~ Text(uninstVars map (_.toText(printer)), ", ") ~ ";" val constrainedText = - " constrained types = " ~ Text(domainPolys map (_.toText(printer)), ", ") + " constrained types = " ~ Text(domainLambdas map (_.toText(printer)), ", ") val boundsText = " bounds = " ~ { val assocs = @@ -614,8 +614,8 @@ class OrderingConstraint(private val boundsMap: ParamBounds, case _ =>" := " + tp } val constrainedText = - " constrained types = " + domainPolys.mkString("\n") - val boundsText = + " constrained types = " + domainLambdas.mkString("\n") + val boundsText = domainLambdas " bounds = " + { val assocs = for (param <- domainParams) diff --git a/compiler/src/dotty/tools/dotc/core/TypeParamInfo.scala b/compiler/src/dotty/tools/dotc/core/ParamInfo.scala index 647c895db..46e378fc2 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeParamInfo.scala +++ b/compiler/src/dotty/tools/dotc/core/ParamInfo.scala @@ -1,13 +1,15 @@ package dotty.tools.dotc.core -import Names.TypeName +import Names.Name import Contexts.Context -import Types.{Type, TypeBounds} +import Types.Type /** A common super trait of Symbol and LambdaParam. * Used to capture the attributes of type parameters which can be implemented as either. */ -trait TypeParamInfo { +trait ParamInfo { + + type ThisName <: Name /** Is this the info of a type parameter? Will return `false` for symbols * that are not type parameters. @@ -15,26 +17,30 @@ trait TypeParamInfo { def isTypeParam(implicit ctx: Context): Boolean /** The name of the type parameter */ - def paramName(implicit ctx: Context): TypeName + def paramName(implicit ctx: Context): ThisName /** The info of the type parameter */ - def paramBounds(implicit ctx: Context): TypeBounds + def paramInfo(implicit ctx: Context): Type /** The info of the type parameter as seen from a prefix type. * For type parameter symbols, this is the `memberInfo` as seen from `prefix`. - * For type lambda parameters, it's the same as `paramBounds` as + * For type lambda parameters, it's the same as `paramInfos` as * `asSeenFrom` has already been applied to the whole type lambda. */ - def paramBoundsAsSeenFrom(pre: Type)(implicit ctx: Context): TypeBounds + def paramInfoAsSeenFrom(pre: Type)(implicit ctx: Context): Type /** The parameter bounds, or the completer if the type parameter * is an as-yet uncompleted symbol. */ - def paramBoundsOrCompleter(implicit ctx: Context): Type + def paramInfoOrCompleter(implicit ctx: Context): Type /** The variance of the type parameter */ def paramVariance(implicit ctx: Context): Int /** A type that refers to the parameter */ def paramRef(implicit ctx: Context): Type +} + +object ParamInfo { + type Of[N] = ParamInfo { type ThisName = N } }
\ No newline at end of file diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala index 5b7dc3d1d..e7928fd09 100644 --- a/compiler/src/dotty/tools/dotc/core/StdNames.scala +++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala @@ -132,6 +132,8 @@ object StdNames { val TRAIT_SETTER_SEPARATOR: N = "$_setter_$" val DIRECT_SUFFIX: N = "$direct" val LAZY_IMPLICIT_PREFIX: N = "$lazy_implicit$" + val DOLLAR_VALUES: N = "$values" + val DOLLAR_NEW: N = "$new" // value types (and AnyRef) are all used as terms as well // as (at least) arguments to the @specialize annotation. @@ -395,6 +397,7 @@ object StdNames { val elem: N = "elem" val emptyValDef: N = "emptyValDef" val ensureAccessible : N = "ensureAccessible" + val enumTag: N = "enumTag" val eq: N = "eq" val equalsNumChar : N = "equalsNumChar" val equalsNumNum : N = "equalsNumNum" @@ -499,6 +502,7 @@ object StdNames { val staticModule : N = "staticModule" val staticPackage : N = "staticPackage" val synchronized_ : N = "synchronized" + val tag: N = "tag" val tail: N = "tail" val `then` : N = "then" val this_ : N = "this" @@ -523,7 +527,7 @@ object StdNames { val updateDynamic: N = "updateDynamic" val value: N = "value" val valueOf : N = "valueOf" - val values : N = "values" + val values: N = "values" val view_ : N = "view" val wait_ : N = "wait" val withFilter: N = "withFilter" @@ -718,9 +722,6 @@ object StdNames { case _ => termName("_" + j) } - def syntheticParamNames(num: Int): List[TermName] = - (0 until num).map(syntheticParamName)(breakOut) - def localDummyName(clazz: Symbol)(implicit ctx: Context): TermName = LOCALDUMMY_PREFIX ++ clazz.name ++ ">" @@ -743,9 +744,6 @@ object StdNames { def syntheticTypeParamName(i: Int): TypeName = "X" + i - def syntheticTypeParamNames(num: Int): List[TypeName] = - (0 until num).map(syntheticTypeParamName)(breakOut) - final val Conforms = encode("<:<") final val Uninstantiated: TypeName = "?$" @@ -846,5 +844,4 @@ object StdNames { val tpnme = new ScalaTypeNames val jnme = new JavaTermNames val jtpnme = new JavaTypeNames - } diff --git a/compiler/src/dotty/tools/dotc/core/Substituters.scala b/compiler/src/dotty/tools/dotc/core/Substituters.scala index 23683608a..d565ec229 100644 --- a/compiler/src/dotty/tools/dotc/core/Substituters.scala +++ b/compiler/src/dotty/tools/dotc/core/Substituters.scala @@ -196,7 +196,7 @@ trait Substituters { this: Context => .mapOver(tp) } - final def substParam(tp: Type, from: ParamType, to: Type, theMap: SubstParamMap): Type = + final def substParam(tp: Type, from: ParamRef, to: Type, theMap: SubstParamMap): Type = tp match { case tp: BoundType => if (tp == from) to else tp @@ -216,7 +216,7 @@ trait Substituters { this: Context => final def substParams(tp: Type, from: BindingType, to: List[Type], theMap: SubstParamsMap): Type = tp match { - case tp: ParamType => + case tp: ParamRef => if (tp.binder == from) to(tp.paramNum) else tp case tp: NamedType => if (tp.currentSymbol.isStatic) tp @@ -269,7 +269,7 @@ trait Substituters { this: Context => def apply(tp: Type): Type = substRecThis(tp, from, to, this) } - final class SubstParamMap(from: ParamType, to: Type) extends DeepTypeMap { + final class SubstParamMap(from: ParamRef, to: Type) extends DeepTypeMap { def apply(tp: Type) = substParam(tp, from, to, this) } diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index db96463e0..602848a50 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -163,26 +163,31 @@ object SymDenotations { setFlag(flags & mask) } + private def isCurrent(fs: FlagSet) = + fs <= ( + if (myInfo.isInstanceOf[SymbolLoader]) FromStartFlags + else AfterLoadFlags) + /** Has this denotation one of the flags in `fs` set? */ final def is(fs: FlagSet)(implicit ctx: Context) = { - (if (fs <= FromStartFlags) myFlags else flags) is fs + (if (isCurrent(fs)) myFlags else flags) is fs } /** Has this denotation one of the flags in `fs` set, whereas none of the flags * in `butNot` are set? */ final def is(fs: FlagSet, butNot: FlagSet)(implicit ctx: Context) = - (if (fs <= FromStartFlags && butNot <= FromStartFlags) myFlags else flags) is (fs, butNot) + (if (isCurrent(fs) && isCurrent(butNot)) myFlags else flags) is (fs, butNot) /** Has this denotation all of the flags in `fs` set? */ final def is(fs: FlagConjunction)(implicit ctx: Context) = - (if (fs <= FromStartFlags) myFlags else flags) is fs + (if (isCurrent(fs)) myFlags else flags) is fs /** Has this denotation all of the flags in `fs` set, whereas none of the flags * in `butNot` are set? */ final def is(fs: FlagConjunction, butNot: FlagSet)(implicit ctx: Context) = - (if (fs <= FromStartFlags && butNot <= FromStartFlags) myFlags else flags) is (fs, butNot) + (if (isCurrent(fs) && isCurrent(butNot)) myFlags else flags) is (fs, butNot) /** The type info. * The info is an instance of TypeType iff this is a type denotation @@ -1141,14 +1146,13 @@ object SymDenotations { case tp: NamedType => hasSkolems(tp.prefix) case tp: RefinedType => hasSkolems(tp.parent) || hasSkolems(tp.refinedInfo) case tp: RecType => hasSkolems(tp.parent) - case tp: PolyType => tp.paramBounds.exists(hasSkolems) || hasSkolems(tp.resType) - case tp: MethodType => tp.paramTypes.exists(hasSkolems) || hasSkolems(tp.resType) + case tp: TypeBounds => hasSkolems(tp.lo) || hasSkolems(tp.hi) + case tp: TypeVar => hasSkolems(tp.inst) case tp: ExprType => hasSkolems(tp.resType) case tp: HKApply => hasSkolems(tp.tycon) || tp.args.exists(hasSkolems) + case tp: LambdaType => tp.paramInfos.exists(hasSkolems) || hasSkolems(tp.resType) case tp: AndOrType => hasSkolems(tp.tp1) || hasSkolems(tp.tp2) - case tp: TypeBounds => hasSkolems(tp.lo) || hasSkolems(tp.hi) case tp: AnnotatedType => hasSkolems(tp.tpe) - case tp: TypeVar => hasSkolems(tp.inst) case _ => false } diff --git a/compiler/src/dotty/tools/dotc/core/Symbols.scala b/compiler/src/dotty/tools/dotc/core/Symbols.scala index 33aba4d13..95ff1cb75 100644 --- a/compiler/src/dotty/tools/dotc/core/Symbols.scala +++ b/compiler/src/dotty/tools/dotc/core/Symbols.scala @@ -384,7 +384,7 @@ object Symbols { * @param coord The coordinates of the symbol (a position or an index) * @param id A unique identifier of the symbol (unique per ContextBase) */ - class Symbol private[Symbols] (val coord: Coord, val id: Int) extends DotClass with TypeParamInfo with printing.Showable { + class Symbol private[Symbols] (val coord: Coord, val id: Int) extends DotClass with ParamInfo with printing.Showable { type ThisName <: Name @@ -513,12 +513,12 @@ object Symbols { */ def pos: Position = if (coord.isPosition) coord.toPosition else NoPosition - // TypeParamInfo methods + // ParamInfo types and methods def isTypeParam(implicit ctx: Context) = denot.is(TypeParam) - def paramName(implicit ctx: Context) = name.asTypeName - def paramBounds(implicit ctx: Context) = denot.info.bounds - def paramBoundsAsSeenFrom(pre: Type)(implicit ctx: Context) = pre.memberInfo(this).bounds - def paramBoundsOrCompleter(implicit ctx: Context): Type = denot.infoOrCompleter + def paramName(implicit ctx: Context) = name.asInstanceOf[ThisName] + def paramInfo(implicit ctx: Context) = denot.info + def paramInfoAsSeenFrom(pre: Type)(implicit ctx: Context) = pre.memberInfo(this) + def paramInfoOrCompleter(implicit ctx: Context): Type = denot.infoOrCompleter def paramVariance(implicit ctx: Context) = denot.variance def paramRef(implicit ctx: Context) = denot.typeRef diff --git a/compiler/src/dotty/tools/dotc/core/TypeApplications.scala b/compiler/src/dotty/tools/dotc/core/TypeApplications.scala index ba3e6a461..23c3f96cc 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeApplications.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeApplications.scala @@ -21,6 +21,8 @@ import java.util.NoSuchElementException object TypeApplications { + type TypeParamInfo = ParamInfo.Of[TypeName] + /** Assert type is not a TypeBounds instance and return it unchanged */ val noBounds = (tp: Type) => tp match { case tp: TypeBounds => throw new AssertionError("no TypeBounds allowed") @@ -73,7 +75,7 @@ object TypeApplications { } def unapply(tp: Type)(implicit ctx: Context): Option[TypeRef] = tp match { - case tp @ PolyType(tparams, AppliedType(fn: TypeRef, args)) if (args == tparams.map(_.toArg)) => Some(fn) + case tp @ HKTypeLambda(tparams, AppliedType(fn: TypeRef, args)) if (args == tparams.map(_.toArg)) => Some(fn) case _ => None } } @@ -119,7 +121,7 @@ object TypeApplications { */ def EtaExpandIfHK(tparams: List[TypeParamInfo], args: List[Type])(implicit ctx: Context): List[Type] = if (tparams.isEmpty) args - else args.zipWithConserve(tparams)((arg, tparam) => arg.EtaExpandIfHK(tparam.paramBoundsOrCompleter)) + else args.zipWithConserve(tparams)((arg, tparam) => arg.EtaExpandIfHK(tparam.paramInfoOrCompleter)) /** A type map that tries to reduce (part of) the result type of the type lambda `tycon` * with the given `args`(some of which are wildcard arguments represented by type bounds). @@ -160,18 +162,18 @@ object TypeApplications { * result type. Using this mode, we can guarantee that `appliedTo` will never * produce a higher-kinded application with a type lambda as type constructor. */ - class Reducer(tycon: PolyType, args: List[Type])(implicit ctx: Context) extends TypeMap { + class Reducer(tycon: TypeLambda, args: List[Type])(implicit ctx: Context) extends TypeMap { private var available = (0 until args.length).toSet var allReplaced = true - def hasWildcardArg(p: PolyParam) = + def hasWildcardArg(p: TypeParamRef) = p.binder == tycon && args(p.paramNum).isInstanceOf[TypeBounds] - def canReduceWildcard(p: PolyParam) = + def canReduceWildcard(p: TypeParamRef) = !ctx.mode.is(Mode.AllowLambdaWildcardApply) || available.contains(p.paramNum) def apply(t: Type) = t match { - case t @ TypeAlias(p: PolyParam) if hasWildcardArg(p) && canReduceWildcard(p) => + case t @ TypeAlias(p: TypeParamRef) if hasWildcardArg(p) && canReduceWildcard(p) => available -= p.paramNum args(p.paramNum) - case p: PolyParam if p.binder == tycon => + case p: TypeParamRef if p.binder == tycon => args(p.paramNum) match { case TypeBounds(lo, hi) => if (ctx.mode.is(Mode.AllowLambdaWildcardApply)) { allReplaced = false; p } @@ -213,7 +215,7 @@ class TypeApplications(val self: Type) extends AnyVal { self match { case self: ClassInfo => self.cls.typeParams - case self: PolyType => + case self: HKTypeLambda => self.typeParams case self: TypeRef => val tsym = self.symbol @@ -251,7 +253,7 @@ class TypeApplications(val self: Type) extends AnyVal { def isHK(implicit ctx: Context): Boolean = self.dealias match { case self: TypeRef => self.info.isHK case self: RefinedType => false - case self: PolyType => true + case self: HKTypeLambda => true case self: SingletonType => false case self: TypeVar => // Using `origin` instead of `underlying`, as is done for typeParams, @@ -270,31 +272,6 @@ class TypeApplications(val self: Type) extends AnyVal { self } - /** Lambda abstract `self` with given type parameters. Examples: - * - * type T[X] = U becomes type T = [X] -> U - * type T[X] >: L <: U becomes type T >: L <: ([X] -> U) - * - * TODO: Handle parameterized lower bounds - */ - def LambdaAbstract(tparams: List[TypeParamInfo])(implicit ctx: Context): Type = { - def expand(tp: Type) = - PolyType( - tparams.map(_.paramName), tparams.map(_.paramVariance))( - tl => tparams.map(tparam => tl.lifted(tparams, tparam.paramBounds).bounds), - tl => tl.lifted(tparams, tp)) - if (tparams.isEmpty) self - else self match { - case self: TypeAlias => - self.derivedTypeAlias(expand(self.alias)) - case self @ TypeBounds(lo, hi) => - self.derivedTypeBounds( - if (lo.isRef(defn.NothingClass)) lo else expand(lo), - expand(hi)) - case _ => expand(self) - } - } - /** Convert a type constructor `TC` which has type parameters `T1, ..., Tn` * in a context where type parameters `U1,...,Un` are expected to * @@ -307,7 +284,7 @@ class TypeApplications(val self: Type) extends AnyVal { */ def EtaExpand(tparams: List[TypeSymbol])(implicit ctx: Context): Type = { val tparamsToUse = if (variancesConform(typeParams, tparams)) tparams else typeParamSymbols - self.appliedTo(tparams map (_.typeRef)).LambdaAbstract(tparamsToUse) + HKTypeLambda.fromParams(tparamsToUse, self.appliedTo(tparams map (_.typeRef))) //.ensuring(res => res.EtaReduce =:= self, s"res = $res, core = ${res.EtaReduce}, self = $self, hc = ${res.hashCode}") } @@ -362,11 +339,13 @@ class TypeApplications(val self: Type) extends AnyVal { if (hkParams.isEmpty) self else { def adaptArg(arg: Type): Type = arg match { - case arg @ PolyType(tparams, body) if + case arg @ HKTypeLambda(tparams, body) if !tparams.corresponds(hkParams)(_.paramVariance == _.paramVariance) && tparams.corresponds(hkParams)(varianceConforms) => - PolyType(tparams.map(_.paramName), hkParams.map(_.paramVariance))( - tl => arg.paramBounds.map(_.subst(arg, tl).bounds), + HKTypeLambda( + (tparams, hkParams).zipped.map((tparam, hkparam) => + tparam.paramName.withVariance(hkparam.paramVariance)))( + tl => arg.paramInfos.map(_.subst(arg, tl).bounds), tl => arg.resultType.subst(arg, tl) ) case arg @ TypeAlias(alias) => @@ -390,7 +369,7 @@ class TypeApplications(val self: Type) extends AnyVal { */ final def appliedTo(args: List[Type])(implicit ctx: Context): Type = /*>|>*/ track("appliedTo") /*<|<*/ { val typParams = self.typeParams - def matchParams(t: Type, tparams: List[TypeParamInfo], args: List[Type])(implicit ctx: Context): Type = args match { + def matchParams(t: Type, tparams: List[ParamInfo], args: List[Type])(implicit ctx: Context): Type = args match { case arg :: args1 => try { val tparam :: tparams1 = tparams @@ -407,7 +386,7 @@ class TypeApplications(val self: Type) extends AnyVal { val dealiased = stripped.safeDealias if (args.isEmpty || ctx.erasedTypes) self else dealiased match { - case dealiased: PolyType => + case dealiased: HKTypeLambda => def tryReduce = if (!args.exists(_.isInstanceOf[TypeBounds])) { val followAlias = Config.simplifyApplications && { @@ -426,7 +405,7 @@ class TypeApplications(val self: Type) extends AnyVal { // In this case we should always dealias since we cannot handle // higher-kinded applications to wildcard arguments. dealiased - .derivedPolyType(resType = tycon.safeDealias.appliedTo(args1)) + .derivedLambdaType(resType = tycon.safeDealias.appliedTo(args1)) .appliedTo(args) case _ => val reducer = new Reducer(dealiased, args) @@ -435,6 +414,8 @@ class TypeApplications(val self: Type) extends AnyVal { else HKApply(dealiased, args) } tryReduce + case dealiased: PolyType => + dealiased.instantiate(args) case dealiased: AndOrType => dealiased.derivedAndOrType(dealiased.tp1.appliedTo(args), dealiased.tp2.appliedTo(args)) case dealiased: TypeAlias => @@ -475,7 +456,7 @@ class TypeApplications(val self: Type) extends AnyVal { /** Turn this type, which is used as an argument for * type parameter `tparam`, into a TypeBounds RHS */ - final def toBounds(tparam: TypeParamInfo)(implicit ctx: Context): TypeBounds = self match { + final def toBounds(tparam: ParamInfo)(implicit ctx: Context): TypeBounds = self match { case self: TypeBounds => // this can happen for wildcard args self case _ => diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index 57dde3288..da6d63387 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -280,7 +280,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { case _ => } thirdTry(tp1, tp2) - case tp1: PolyParam => + case tp1: TypeParamRef => def flagNothingBound = { if (!frozenConstraint && tp2.isRef(defn.NothingClass) && state.isGlobalCommittable) { def msg = s"!!! instantiated to Nothing: $tp1, constraint = ${constraint.show}" @@ -289,13 +289,13 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { } true } - def comparePolyParam = + def compareTypeParamRef = ctx.mode.is(Mode.TypevarsMissContext) || isSubTypeWhenFrozen(bounds(tp1).hi, tp2) || { if (canConstrain(tp1)) addConstraint(tp1, tp2, fromBelow = false) && flagNothingBound else thirdTry(tp1, tp2) } - comparePolyParam + compareTypeParamRef case tp1: ThisType => val cls1 = tp1.cls tp2 match { @@ -373,8 +373,8 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { private def thirdTry(tp1: Type, tp2: Type): Boolean = tp2 match { case tp2: NamedType => thirdTryNamed(tp1, tp2) - case tp2: PolyParam => - def comparePolyParam = + case tp2: TypeParamRef => + def compareTypeParamRef = (ctx.mode is Mode.TypevarsMissContext) || { val alwaysTrue = // The following condition is carefully formulated to catch all cases @@ -391,7 +391,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { else fourthTry(tp1, tp2) } } - comparePolyParam + compareTypeParamRef case tp2: RefinedType => def compareRefinedSlow: Boolean = { val name2 = tp2.refinedName @@ -428,9 +428,9 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { compareRec case tp2 @ HKApply(tycon2, args2) => compareHkApply2(tp1, tp2, tycon2, args2) - case tp2 @ PolyType(tparams2, body2) => - def compareHkLambda: Boolean = tp1.stripTypeVar match { - case tp1 @ PolyType(tparams1, body1) => + case tp2: HKTypeLambda => + def compareTypeLambda: Boolean = tp1.stripTypeVar match { + case tp1: HKTypeLambda => /* Don't compare bounds of lambdas under language:Scala2, or t2994 will fail * The issue is that, logically, bounds should compare contravariantly, * but that would invalidate a pattern exploited in t2994: @@ -446,16 +446,16 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { */ def boundsOK = ctx.scala2Mode || - tparams1.corresponds(tparams2)((tparam1, tparam2) => - isSubType(tparam2.paramBounds.subst(tp2, tp1), tparam1.paramBounds)) - val saved = comparedPolyTypes - comparedPolyTypes += tp1 - comparedPolyTypes += tp2 + tp1.typeParams.corresponds(tp2.typeParams)((tparam1, tparam2) => + isSubType(tparam2.paramInfo.subst(tp2, tp1), tparam1.paramInfo)) + val saved = comparedTypeLambdas + comparedTypeLambdas += tp1 + comparedTypeLambdas += tp2 try - variancesConform(tparams1, tparams2) && + variancesConform(tp1.typeParams, tp2.typeParams) && boundsOK && - isSubType(body1, body2.subst(tp2, tp1)) - finally comparedPolyTypes = saved + isSubType(tp1.resType, tp2.resType.subst(tp2, tp1)) + finally comparedTypeLambdas = saved case _ => if (!tp1.isHK) { tp2 match { @@ -466,7 +466,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { } fourthTry(tp1, tp2) } - compareHkLambda + compareTypeLambda case OrType(tp21, tp22) => // Rewrite T1 <: (T211 & T212) | T22 to T1 <: (T211 | T22) and T1 <: (T212 | T22) // and analogously for T1 <: T21 | (T221 & T222) @@ -484,12 +484,12 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { case _ => } either(isSubType(tp1, tp21), isSubType(tp1, tp22)) || fourthTry(tp1, tp2) - case tp2: MethodType => + case tp2: MethodOrPoly => def compareMethod = tp1 match { - case tp1: MethodType => + case tp1: MethodOrPoly => (tp1.signature consistentParams tp2.signature) && - matchingParams(tp1.paramTypes, tp2.paramTypes, tp1.isJava, tp2.isJava) && - (tp1.isImplicit == tp2.isImplicit) && + matchingParams(tp1, tp2) && + tp1.isImplicit == tp2.isImplicit && isSubType(tp1.resultType, tp2.resultType.subst(tp2, tp1)) case _ => false @@ -618,7 +618,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { def isMatchingApply(tp1: Type): Boolean = tp1 match { case HKApply(tycon1, args1) => tycon1.dealias match { - case tycon1: PolyParam => + case tycon1: TypeParamRef => (tycon1 == tycon2 || canConstrain(tycon1) && tryInstantiate(tycon1, tycon2)) && isSubArgs(args1, args2, tparams) @@ -646,7 +646,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { * and the resulting type application is a supertype of `tp1`, * or fallback to fourthTry. */ - def canInstantiate(tycon2: PolyParam): Boolean = { + def canInstantiate(tycon2: TypeParamRef): Boolean = { /** Let * @@ -669,10 +669,10 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { val tparams1 = tparams1a.drop(lengthDiff) variancesConform(tparams1, tparams) && { if (lengthDiff > 0) - tycon1b = PolyType(tparams1.map(_.paramName), tparams1.map(_.paramVariance))( - tl => tparams1.map(tparam => tl.lifted(tparams, tparam.paramBounds).bounds), + tycon1b = HKTypeLambda(tparams1.map(_.paramName))( + tl => tparams1.map(tparam => tl.integrate(tparams, tparam.paramInfo).bounds), tl => tycon1a.appliedTo(args1.take(lengthDiff) ++ - tparams1.indices.toList.map(PolyParam(tl, _)))) + tparams1.indices.toList.map(TypeParamRef(tl, _)))) (ctx.mode.is(Mode.TypevarsMissContext) || tryInstantiate(tycon2, tycon1b.ensureHK)) && isSubType(tp1, tycon1b.appliedTo(args2)) @@ -725,7 +725,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { fallback(tycon2bounds.lo) tycon2 match { - case param2: PolyParam => + case param2: TypeParamRef => isMatchingApply(tp1) || { if (canConstrain(param2)) canInstantiate(param2) else compareLower(bounds(param2), tyconIsTypeRef = false) @@ -746,7 +746,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { */ def compareHkApply1(tp1: HKApply, tycon1: Type, args1: List[Type], tp2: Type): Boolean = tycon1 match { - case param1: PolyParam => + case param1: TypeParamRef => def canInstantiate = tp2 match { case AppliedType(tycon2, args2) => tryInstantiate(param1, tycon2.ensureHK) && isSubArgs(args1, args2, tycon2.typeParams) @@ -764,7 +764,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { /** Subtype test for corresponding arguments in `args1`, `args2` according to * variances in type parameters `tparams`. */ - def isSubArgs(args1: List[Type], args2: List[Type], tparams: List[TypeParamInfo]): Boolean = + def isSubArgs(args1: List[Type], args2: List[Type], tparams: List[ParamInfo]): Boolean = if (args1.isEmpty) args2.isEmpty else args2.nonEmpty && { val v = tparams.head.paramVariance @@ -806,7 +806,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { def fix(tp: Type): Type = tp.stripTypeVar match { case tp: RecType => fix(tp.parent).substRecThis(tp, anchor) case tp @ RefinedType(parent, rname, rinfo) => tp.derivedRefinedType(fix(parent), rname, rinfo) - case tp: PolyParam => fixOrElse(bounds(tp).hi, tp) + case tp: TypeParamRef => fixOrElse(bounds(tp).hi, tp) case tp: TypeProxy => fixOrElse(tp.underlying, tp) case tp: AndOrType => tp.derivedAndOrType(fix(tp.tp1), fix(tp.tp2)) case tp => tp @@ -967,7 +967,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { /** Defer constraining type variables when compared against prototypes */ def isMatchedByProto(proto: ProtoType, tp: Type) = tp.stripTypeVar match { - case tp: PolyParam if constraint contains tp => true + case tp: TypeParamRef if constraint contains tp => true case _ => proto.isMatchedBy(tp) } @@ -978,7 +978,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { * type variable with (the corresponding type in) `tp2` instead. */ private def isCappable(tp: Type): Boolean = tp match { - case tp: PolyParam => constraint contains tp + case tp: TypeParamRef => constraint contains tp case tp: TypeProxy => isCappable(tp.underlying) case tp: AndOrType => isCappable(tp.tp1) || isCappable(tp.tp2) case _ => false @@ -1021,7 +1021,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { tp2.widen match { case tp2: MethodType => // implicitness is ignored when matching - matchingParams(tp1.paramTypes, tp2.paramTypes, tp1.isJava, tp2.isJava) && + matchingParams(tp1, tp2) && matchesType(tp1.resultType, tp2.resultType.subst(tp2, tp1), relaxed) case tp2 => relaxed && tp1.paramNames.isEmpty && @@ -1031,7 +1031,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { tp2.widen match { case tp2: PolyType => sameLength(tp1.paramNames, tp2.paramNames) && - matchesType(tp1.resultType, tp2.resultType.subst(tp2, tp1), relaxed) + matchesType(tp1.resultType, tp2.resultType.subst(tp2, tp1), relaxed) case _ => false } @@ -1047,28 +1047,28 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { } } - /** Are `syms1` and `syms2` parameter lists with pairwise equivalent types? */ - def matchingParams(formals1: List[Type], formals2: List[Type], isJava1: Boolean, isJava2: Boolean): Boolean = formals1 match { - case formal1 :: rest1 => - formals2 match { - case formal2 :: rest2 => - (isSameTypeWhenFrozen(formal1, formal2) - || isJava1 && (formal2 isRef ObjectClass) && (formal1 isRef AnyClass) - || isJava2 && (formal1 isRef ObjectClass) && (formal2 isRef AnyClass)) && - matchingParams(rest1, rest2, isJava1, isJava2) - case nil => - false - } - case nil => - formals2.isEmpty - } - - /** Do generic types `poly1` and `poly2` have type parameters that - * have the same bounds (after renaming one set to the other)? + /** Do lambda types `lam1` and `lam2` have parameters that have the same types + * and the same implicit status? (after renaming one set to the other) */ - def matchingTypeParams(poly1: PolyType, poly2: PolyType): Boolean = - (poly1.paramBounds corresponds poly2.paramBounds)((b1, b2) => - isSameType(b1, b2.subst(poly2, poly1))) + def matchingParams(lam1: MethodOrPoly, lam2: MethodOrPoly): Boolean = { + /** Are `syms1` and `syms2` parameter lists with pairwise equivalent types? */ + def loop(formals1: List[Type], formals2: List[Type]): Boolean = formals1 match { + case formal1 :: rest1 => + formals2 match { + case formal2 :: rest2 => + val formal2a = if (lam2.isParamDependent) formal2.subst(lam2, lam1) else formal2 + (isSameTypeWhenFrozen(formal1, formal2a) + || lam1.isJava && (formal2 isRef ObjectClass) && (formal1 isRef AnyClass) + || lam2.isJava && (formal1 isRef ObjectClass) && (formal2 isRef AnyClass)) && + loop(rest1, rest2) + case nil => + false + } + case nil => + formals2.isEmpty + } + loop(lam1.paramInfos, lam2.paramInfos) + } // Type equality =:= @@ -1216,7 +1216,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { /** Form a normalized conjunction of two types. * Note: For certain types, `&` is distributed inside the type. This holds for * all types which are not value types (e.g. TypeBounds, ClassInfo, - * ExprType, MethodType, PolyType). Also, when forming an `&`, + * ExprType, LambdaType). Also, when forming an `&`, * instantiated TypeVars are dereferenced and annotations are stripped. * Finally, refined types with the same refined name are * opportunistically merged. @@ -1245,7 +1245,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { /** Form a normalized conjunction of two types. * Note: For certain types, `|` is distributed inside the type. This holds for * all types which are not value types (e.g. TypeBounds, ClassInfo, - * ExprType, MethodType, PolyType). Also, when forming an `|`, + * ExprType, LambdaType). Also, when forming an `|`, * instantiated TypeVars are dereferenced and annotations are stripped. * * Sometimes, the disjunction of two types cannot be formed because @@ -1276,20 +1276,20 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { val tparams2 = tp2.typeParams if (tparams1.isEmpty) if (tparams2.isEmpty) op(tp1, tp2) - else original(tp1, tp2.appliedTo(tp2.typeParams.map(_.paramBoundsAsSeenFrom(tp2)))) + else original(tp1, tp2.appliedTo(tp2.typeParams.map(_.paramInfoAsSeenFrom(tp2)))) else if (tparams2.isEmpty) - original(tp1.appliedTo(tp1.typeParams.map(_.paramBoundsAsSeenFrom(tp1))), tp2) + original(tp1.appliedTo(tp1.typeParams.map(_.paramInfoAsSeenFrom(tp1))), tp2) else - PolyType( - paramNames = tpnme.syntheticTypeParamNames(tparams1.length), - variances = (tparams1, tparams2).zipped.map((tparam1, tparam2) => - (tparam1.paramVariance + tparam2.paramVariance) / 2))( - paramBoundsExp = tl => (tparams1, tparams2).zipped.map((tparam1, tparam2) => - tl.lifted(tparams1, tparam1.paramBoundsAsSeenFrom(tp1)).bounds & - tl.lifted(tparams2, tparam2.paramBoundsAsSeenFrom(tp2)).bounds), + HKTypeLambda( + paramNames = (HKTypeLambda.syntheticParamNames(tparams1.length), tparams1, tparams2) + .zipped.map((pname, tparam1, tparam2) => + pname.withVariance((tparam1.paramVariance + tparam2.paramVariance) / 2)))( + paramInfosExp = tl => (tparams1, tparams2).zipped.map((tparam1, tparam2) => + tl.integrate(tparams1, tparam1.paramInfoAsSeenFrom(tp1)).bounds & + tl.integrate(tparams2, tparam2.paramInfoAsSeenFrom(tp2)).bounds), resultTypeExp = tl => - original(tl.lifted(tparams1, tp1).appliedTo(tl.paramRefs), - tl.lifted(tparams2, tp2).appliedTo(tl.paramRefs))) + original(tl.integrate(tparams1, tp1).appliedTo(tl.paramRefs), + tl.integrate(tparams2, tp2).appliedTo(tl.paramRefs))) } /** Try to distribute `&` inside type, detect and handle conflicts @@ -1392,8 +1392,8 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { case tp2: MethodType => def asGoodParams(formals1: List[Type], formals2: List[Type]) = (formals2 corresponds formals1)(isSubTypeWhenFrozen) - asGoodParams(tp1.paramTypes, tp2.paramTypes) && - (!asGoodParams(tp2.paramTypes, tp1.paramTypes) || + asGoodParams(tp1.paramInfos, tp2.paramInfos) && + (!asGoodParams(tp2.paramInfos, tp1.paramInfos) || isAsGood(tp1.resultType, tp2.resultType)) case _ => false @@ -1424,7 +1424,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { def showGoal(tp1: Type, tp2: Type)(implicit ctx: Context) = { println(ex"assertion failure for $tp1 <:< $tp2, frozen = $frozenConstraint") def explainPoly(tp: Type) = tp match { - case tp: PolyParam => ctx.echo(s"polyparam ${tp.show} found in ${tp.binder.show}") + case tp: TypeParamRef => ctx.echo(s"TypeParamRef ${tp.show} found in ${tp.binder.show}") case tp: TypeRef if tp.symbol.exists => ctx.echo(s"typeref ${tp.show} found in ${tp.symbol.owner.show}") case tp: TypeVar => ctx.echo(s"typevar ${tp.show}, origin = ${tp.origin}") case _ => ctx.echo(s"${tp.show} is a ${tp.getClass}") @@ -1503,7 +1503,7 @@ class ExplainingTypeComparer(initctx: Context) extends TypeComparer(initctx) { super.glb(tp1, tp2) } - override def addConstraint(param: PolyParam, bound: Type, fromBelow: Boolean): Boolean = + override def addConstraint(param: TypeParamRef, bound: Type, fromBelow: Boolean): Boolean = traceIndented(i"add constraint $param ${if (fromBelow) ">:" else "<:"} $bound $frozenConstraint, constraint = ${ctx.typerState.constraint}") { super.addConstraint(param, bound, fromBelow) } diff --git a/compiler/src/dotty/tools/dotc/core/TypeErasure.scala b/compiler/src/dotty/tools/dotc/core/TypeErasure.scala index fe3396fcb..f35752644 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeErasure.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeErasure.scala @@ -28,7 +28,7 @@ import scala.annotation.tailrec * WildcardType * ErrorType * - * only for isInstanceOf, asInstanceOf: PolyType, PolyParam, TypeBounds + * only for isInstanceOf, asInstanceOf: PolyType, TypeParamRef, TypeBounds * */ object TypeErasure { @@ -55,7 +55,7 @@ object TypeErasure { case ThisType(tref) => isErasedType(tref) case tp: MethodType => - tp.paramTypes.forall(isErasedType) && isErasedType(tp.resultType) + tp.paramInfos.forall(isErasedType) && isErasedType(tp.resultType) case tp @ ClassInfo(pre, _, parents, decls, _) => isErasedType(pre) && parents.forall(isErasedType) //&& decls.forall(sym => isErasedType(sym.info)) && isErasedType(tp.selfType) case NoType | NoPrefix | WildcardType | _: ErrorType | SuperType(_, _) => @@ -176,7 +176,7 @@ object TypeErasure { val erase = erasureFn(isJava, semiEraseVCs, sym.isConstructor, wildcardOK = false) def eraseParamBounds(tp: PolyType): Type = - tp.derivedPolyType( + tp.derivedLambdaType( tp.paramNames, tp.paramNames map (Function.const(TypeBounds.upper(defn.ObjectType))), tp.resultType) if (defn.isPolymorphicAfterErasure(sym)) eraseParamBounds(sym.info.asInstanceOf[PolyType]) @@ -186,7 +186,7 @@ object TypeErasure { case einfo: MethodType => if (sym.isGetter && einfo.resultType.isRef(defn.UnitClass)) MethodType(Nil, defn.BoxedUnitType) - else if (sym.isAnonymousFunction && einfo.paramTypes.length > MaxImplementedFunctionArity) + else if (sym.isAnonymousFunction && einfo.paramInfos.length > MaxImplementedFunctionArity) MethodType(nme.ALLARGS :: Nil, JavaArrayType(defn.ObjectType) :: Nil, einfo.resultType) else einfo @@ -204,7 +204,7 @@ object TypeErasure { !tp.symbol.isClass && !tp.derivesFrom(defn.ObjectClass) && !tp.symbol.is(JavaDefined) - case tp: PolyParam => + case tp: TypeParamRef => !tp.derivesFrom(defn.ObjectClass) && !tp.binder.resultType.isInstanceOf[JavaMethodType] case tp: TypeAlias => isUnboundedGeneric(tp.alias) @@ -304,7 +304,7 @@ object TypeErasure { case _: ClassInfo => true case _ => false } - case tp: PolyParam => false + case tp: TypeParamRef => false case tp: TypeProxy => hasStableErasure(tp.superType) case tp: AndOrType => hasStableErasure(tp.tp1) && hasStableErasure(tp.tp2) case _ => false @@ -382,13 +382,15 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean case tp: MethodType => def paramErasure(tpToErase: Type) = erasureFn(tp.isJava, semiEraseVCs, isConstructor, wildcardOK)(tpToErase) - val formals = tp.paramTypes.mapConserve(paramErasure) + val formals = tp.paramInfos.mapConserve(paramErasure) eraseResult(tp.resultType) match { case rt: MethodType => - tp.derivedMethodType(tp.paramNames ++ rt.paramNames, formals ++ rt.paramTypes, rt.resultType) + tp.derivedLambdaType(tp.paramNames ++ rt.paramNames, formals ++ rt.paramInfos, rt.resultType) case rt => - tp.derivedMethodType(tp.paramNames, formals, rt) + tp.derivedLambdaType(tp.paramNames, formals, rt) } + case tp: PolyType => + this(tp.resultType) case tp @ ClassInfo(pre, cls, classParents, decls, _) => if (cls is Package) tp else { @@ -517,6 +519,8 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean if (inst.exists) sigName(inst) else tpnme.Uninstantiated case tp: TypeProxy => sigName(tp.underlying) + case tp: PolyType => + sigName(tp.resultType) case _: ErrorType | WildcardType => tpnme.WILDCARD case tp: WildcardType => diff --git a/compiler/src/dotty/tools/dotc/core/TypeOps.scala b/compiler/src/dotty/tools/dotc/core/TypeOps.scala index 3d2906320..9593bfe93 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeOps.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeOps.scala @@ -157,7 +157,7 @@ trait TypeOps { this: Context => // TODO: Make standalone object. tp2 case tp1 => tp1 } - case tp: PolyParam => + case tp: TypeParamRef => typerState.constraint.typeVarOfParam(tp) orElse tp case _: ThisType | _: BoundType | NoPrefix => tp diff --git a/compiler/src/dotty/tools/dotc/core/TyperState.scala b/compiler/src/dotty/tools/dotc/core/TyperState.scala index 206438d86..b33b3aa29 100644 --- a/compiler/src/dotty/tools/dotc/core/TyperState.scala +++ b/compiler/src/dotty/tools/dotc/core/TyperState.scala @@ -42,7 +42,7 @@ class TyperState(r: Reporter) extends DotClass with Showable { */ def instType(tvar: TypeVar)(implicit ctx: Context): Type = constraint.entry(tvar.origin) match { case _: TypeBounds => NoType - case tp: PolyParam => + case tp: TypeParamRef => var tvar1 = constraint.typeVarOfParam(tp) if (tvar1.exists) tvar1 else tp case tp => tp @@ -155,14 +155,14 @@ extends TyperState(r) { } override def gc()(implicit ctx: Context): Unit = { - val toCollect = new mutable.ListBuffer[PolyType] + val toCollect = new mutable.ListBuffer[TypeLambda] constraint foreachTypeVar { tvar => if (!tvar.inst.exists) { val inst = instType(tvar) if (inst.exists && (tvar.owningState eq this)) { tvar.inst = inst - val poly = tvar.origin.binder - if (constraint.isRemovable(poly)) toCollect += poly + val lam = tvar.origin.binder + if (constraint.isRemovable(lam)) toCollect += lam } } } diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 3f40f8a06..8ae3aa7ad 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -54,10 +54,10 @@ object Types { * | | +--- ThisType * | | +--- SuperType * | | +--- ConstantType - * | | +--- MethodParam + * | | +--- TermParamRef * | | +----RecThis * | | +--- SkolemType - * | +- PolyParam + * | +- TypeParamRef * | +- RefinedOrRecType -+-- RefinedType * | | -+-- RecType * | +- HKApply @@ -65,12 +65,13 @@ object Types { * | +- ExprType * | +- AnnotatedType * | +- TypeVar - * | +- PolyType + * | +- HKTypeLambda * | * +- GroundType -+- AndType * +- OrType - * +- MethodType -----+- ImplicitMethodType - * | +- JavaMethodType + * +- MethodOrPoly ---+-- PolyType + * +-- MethodType ---+- ImplicitMethodType + * | +- JavaMethodType * +- ClassInfo * | * +- NoType @@ -103,7 +104,7 @@ object Types { final def isValueType: Boolean = this.isInstanceOf[ValueType] /** Is the is value type or type lambda? */ - final def isValueTypeOrLambda: Boolean = isValueType || this.isInstanceOf[PolyType] + final def isValueTypeOrLambda: Boolean = isValueType || this.isInstanceOf[TypeLambda] /** Does this type denote a stable reference (i.e. singleton type)? */ @tailrec final def isStable(implicit ctx: Context): Boolean = stripTypeVar match { @@ -215,17 +216,15 @@ object Types { /** Is this the type of a method that has a repeated parameter type as * last parameter type? */ - def isVarArgsMethod(implicit ctx: Context): Boolean = this match { - case tp: PolyType => tp.resultType.isVarArgsMethod - case mt: MethodType => mt.paramTypes.nonEmpty && mt.paramTypes.last.isRepeatedParam + def isVarArgsMethod(implicit ctx: Context): Boolean = stripPoly match { + case mt: MethodType => mt.paramInfos.nonEmpty && mt.paramInfos.last.isRepeatedParam case _ => false } /** Is this the type of a method with a leading empty parameter list? */ - def isNullaryMethod(implicit ctx: Context): Boolean = this match { + def isNullaryMethod(implicit ctx: Context): Boolean = stripPoly match { case MethodType(Nil) => true - case tp: PolyType => tp.resultType.isNullaryMethod case _ => false } @@ -443,10 +442,10 @@ object Types { case tp: TermRef => go (tp.underlying match { case mt: MethodType - if mt.paramTypes.isEmpty && (tp.symbol is Stable) => mt.resultType + if mt.paramInfos.isEmpty && (tp.symbol is Stable) => mt.resultType case tp1 => tp1 }) - case tp: PolyParam => + case tp: TypeParamRef => goParam(tp) case tp: RecType => goRec(tp) @@ -541,9 +540,9 @@ object Types { } def goApply(tp: HKApply) = tp.tycon match { - case tl: PolyType => + case tl: HKTypeLambda => go(tl.resType).mapInfo(info => - tl.derivedLambdaAbstraction(tl.paramNames, tl.paramBounds, info).appliedTo(tp.args)) + tl.derivedLambdaAbstraction(tl.paramNames, tl.paramInfos, info).appliedTo(tp.args)) case _ => go(tp.superType) } @@ -563,7 +562,7 @@ object Types { // loadClassWithPrivateInnerAndSubSelf in ShowClassTests go(tp.cls.typeRef) orElse d } - def goParam(tp: PolyParam) = { + def goParam(tp: TypeParamRef) = { val next = tp.underlying ctx.typerState.constraint.entry(tp) match { case bounds: TypeBounds if bounds ne next => @@ -814,6 +813,12 @@ object Types { */ def stripAnnots(implicit ctx: Context): Type = this + /** Strip PolyType prefix */ + def stripPoly(implicit ctx: Context): Type = this match { + case tp: PolyType => tp.resType.stripPoly + case _ => this + } + /** Widen from singleton type to its underlying non-singleton * base type by applying one or more `underlying` dereferences, * Also go from => T to T. @@ -1088,43 +1093,38 @@ object Types { } /** The parameter types of a PolyType or MethodType, Empty list for others */ - final def paramTypess(implicit ctx: Context): List[List[Type]] = this match { - case mt: MethodType => mt.paramTypes :: mt.resultType.paramTypess - case pt: PolyType => pt.resultType.paramTypess + final def paramInfoss(implicit ctx: Context): List[List[Type]] = stripPoly match { + case mt: MethodType => mt.paramInfos :: mt.resultType.paramInfoss case _ => Nil } /** The parameter names of a PolyType or MethodType, Empty list for others */ - final def paramNamess(implicit ctx: Context): List[List[TermName]] = this match { + final def paramNamess(implicit ctx: Context): List[List[TermName]] = stripPoly match { case mt: MethodType => mt.paramNames :: mt.resultType.paramNamess - case pt: PolyType => pt.resultType.paramNamess case _ => Nil } /** The parameter types in the first parameter section of a generic type or MethodType, Empty list for others */ - @tailrec final def firstParamTypes(implicit ctx: Context): List[Type] = this match { - case mt: MethodType => mt.paramTypes - case pt: PolyType => pt.resultType.firstParamTypes + final def firstParamTypes(implicit ctx: Context): List[Type] = stripPoly match { + case mt: MethodType => mt.paramInfos case _ => Nil } /** Is this either not a method at all, or a parameterless method? */ - @tailrec final def isParameterless(implicit ctx: Context): Boolean = this match { + final def isParameterless(implicit ctx: Context): Boolean = stripPoly match { case mt: MethodType => false - case pt: PolyType => pt.resultType.isParameterless case _ => true } - /** The resultType of a PolyType, MethodType, or ExprType, the type itself for others */ + /** The resultType of a LambdaType, or ExprType, the type itself for others */ def resultType(implicit ctx: Context): Type = this /** The final result type of a PolyType, MethodType, or ExprType, after skipping * all parameter sections, the type itself for all others. */ - def finalResultType(implicit ctx: Context): Type = resultType match { + def finalResultType(implicit ctx: Context): Type = resultType.stripPoly match { case mt: MethodType => mt.resultType.finalResultType - case pt: PolyType => pt.resultType.finalResultType case _ => resultType } @@ -1180,8 +1180,8 @@ object Types { final def substDealias(from: List[Symbol], to: List[Type])(implicit ctx: Context): Type = ctx.substDealias(this, from, to, null) - /** Substitute all types of the form `PolyParam(from, N)` by - * `PolyParam(to, N)`. + /** Substitute all types of the form `TypeParamRef(from, N)` by + * `TypeParamRef(to, N)`. */ final def subst(from: BindingType, to: BindingType)(implicit ctx: Context): Type = ctx.subst(this, from, to, null) @@ -1199,7 +1199,7 @@ object Types { ctx.substRecThis(this, binder, tp, null) /** Substitute a bound type by some other type */ - final def substParam(from: ParamType, to: Type)(implicit ctx: Context): Type = + final def substParam(from: ParamRef, to: Type)(implicit ctx: Context): Type = ctx.substParam(this, from, to, null) /** Substitute bound types by some other types */ @@ -1220,7 +1220,7 @@ object Types { */ def toFunctionType(dropLast: Int = 0)(implicit ctx: Context): Type = this match { case mt: MethodType if !mt.isDependent || ctx.mode.is(Mode.AllowDependentFunctions) => - val formals1 = if (dropLast == 0) mt.paramTypes else mt.paramTypes dropRight dropLast + val formals1 = if (dropLast == 0) mt.paramInfos else mt.paramInfos dropRight dropLast defn.FunctionOf( formals1 mapConserve (_.underlyingIfRepeated(mt.isJava)), mt.resultType, mt.isImplicit && !ctx.erasedTypes) } @@ -1360,7 +1360,7 @@ object Types { } /** A marker trait for types that bind other types that refer to them. - * Instances are: PolyType, MethodType, RefinedType. + * Instances are: LambdaType, RecType. */ trait BindingType extends Type @@ -2280,7 +2280,7 @@ object Types { if (tp1 eq tp2) tp1 else apply(tp1, tp2) } - // ----- Method types: MethodType/ExprType/PolyType ------------------------------- + // ----- ExprType and LambdaTypes ----------------------------------- // Note: method types are cached whereas poly types are not. The reason // is that most poly types are cyclic via poly params, @@ -2315,20 +2315,126 @@ object Types { } } - trait MethodOrPoly extends MethodicType + /** A by-name parameter type of the form `=> T`, or the type of a method with no parameter list. */ + abstract case class ExprType(resType: Type) + extends CachedProxyType with TermType with MethodicType { + override def resultType(implicit ctx: Context): Type = resType + override def underlying(implicit ctx: Context): Type = resType + protected def computeSignature(implicit ctx: Context): Signature = resultSignature + def derivedExprType(resType: Type)(implicit ctx: Context) = + if (resType eq this.resType) this else ExprType(resType) + override def computeHash = doHash(resType) + } - abstract case class MethodType(paramNames: List[TermName])( - paramTypesExp: MethodType => List[Type], - resultTypeExp: MethodType => Type) - extends CachedGroundType with BindingType with TermType with MethodOrPoly with NarrowCached { thisMethodType => - import MethodType._ + final class CachedExprType(resultType: Type) extends ExprType(resultType) + + object ExprType { + def apply(resultType: Type)(implicit ctx: Context) = { + assertUnerased() + unique(new CachedExprType(resultType)) + } + } - def isJava = false + /** The lambda type square: + * + * LambdaType | TermLambda | TypeLambda + * -------------+-------------------+------------------ + * HKLambda | HKTermLambda | HKTypeLambda + * MethodOrPoly | MethodType | PolyType + */ + trait LambdaType extends BindingType with MethodicType { self => + type ThisName <: Name + type PInfo <: Type + type This <: LambdaType{type PInfo = self.PInfo} + + def paramNames: List[ThisName] + def paramInfos: List[PInfo] + def resType: Type + def newParamRef(n: Int): ParamRef + + override def resultType(implicit ctx: Context) = resType + + def isJava: Boolean = false def isImplicit = false - val paramTypes = paramTypesExp(this) - private[core] val resType = resultTypeExp(this) - assert(resType.exists) + def isDependent(implicit ctx: Context): Boolean + def isParamDependent(implicit ctx: Context): Boolean + + final def isTermLambda = isInstanceOf[TermLambda] + final def isTypeLambda = isInstanceOf[TypeLambda] + final def isHigherKinded = isInstanceOf[TypeProxy] + + lazy val paramRefs: List[ParamRef] = paramNames.indices.toList.map(newParamRef) + + protected def computeSignature(implicit ctx: Context) = resultSignature + + final def instantiate(argTypes: => List[Type])(implicit ctx: Context): Type = + if (isDependent) resultType.substParams(this, argTypes) + else resultType + + def companion: LambdaTypeCompanion[ThisName, PInfo, This] + + /** The type `[tparams := paramRefs] tp`, where `tparams` can be + * either a list of type parameter symbols or a list of lambda parameters + */ + def integrate(tparams: List[ParamInfo], tp: Type)(implicit ctx: Context): Type = + tparams match { + case LambdaParam(lam, _) :: _ => tp.subst(lam, this) + case tparams: List[Symbol @unchecked] => tp.subst(tparams, paramRefs) + } + + final def derivedLambdaType(paramNames: List[ThisName] = this.paramNames, + paramInfos: List[PInfo] = this.paramInfos, + resType: Type = this.resType)(implicit ctx: Context) = + if ((paramNames eq this.paramNames) && (paramInfos eq this.paramInfos) && (resType eq this.resType)) this + else newLikeThis(paramNames, paramInfos, resType) + + final def newLikeThis(paramNames: List[ThisName], paramInfos: List[PInfo], resType: Type)(implicit ctx: Context): This = + companion(paramNames)( + x => paramInfos.mapConserve(_.subst(this, x).asInstanceOf[PInfo]), + x => resType.subst(this, x)) + + protected def prefixString: String + final override def toString = s"$prefixString($paramNames, $paramInfos, $resType)" + } + + abstract class HKLambda extends CachedProxyType with LambdaType { + final override def underlying(implicit ctx: Context) = resType + + final override def computeHash = doHash(paramNames, resType, paramInfos) + + // Defined here instead of in LambdaType for efficiency + final override def equals(that: Any) = that match { + case that: HKLambda => + this.paramNames == that.paramNames && + this.paramInfos == that.paramInfos && + this.resType == that.resType && + (this.companion eq that.companion) + case _ => + false + } + } + + abstract class MethodOrPoly extends CachedGroundType with LambdaType with TermType { + final override def computeHash = doHash(paramNames, resType, paramInfos) + + // Defined here instead of in LambdaType for efficiency + final override def equals(that: Any) = that match { + case that: MethodOrPoly => + this.paramNames == that.paramNames && + this.paramInfos == that.paramInfos && + this.resType == that.resType && + (this.companion eq that.companion) + case _ => + false + } + } + + trait TermLambda extends LambdaType { thisLambdaType => + import DepStatus._ + type ThisName = TermName + type PInfo = Type + type This <: TermLambda override def resultType(implicit ctx: Context): Type = if (dependencyStatus == FalseDeps) { // dealias all false dependencies @@ -2347,8 +2453,8 @@ object Types { } else resType - var myDependencyStatus: DependencyStatus = Unknown - var myParamDependencyStatus: DependencyStatus = Unknown + private var myDependencyStatus: DependencyStatus = Unknown + private var myParamDependencyStatus: DependencyStatus = Unknown private def depStatus(initial: DependencyStatus, tp: Type)(implicit ctx: Context): DependencyStatus = { def combine(x: DependencyStatus, y: DependencyStatus) = { @@ -2361,7 +2467,7 @@ object Types { if (status == TrueDeps) status else tp match { - case MethodParam(`thisMethodType`, _) => TrueDeps + case TermParamRef(`thisLambdaType`, _) => TrueDeps case tp: TypeRef => val status1 = foldOver(status, tp) tp.info match { // follow type alias to avoid dependency @@ -2401,8 +2507,8 @@ object Types { if (myParamDependencyStatus != Unknown) myParamDependencyStatus else { val result = - if (paramTypes.isEmpty) NoDeps - else (NoDeps /: paramTypes.tail)(depStatus(_, _)) + if (paramInfos.isEmpty) NoDeps + else (NoDeps /: paramInfos.tail)(depStatus(_, _)) if ((result & Provisional) == 0) myParamDependencyStatus = result (result & StatusMask).toByte } @@ -2418,75 +2524,90 @@ object Types { */ def isParamDependent(implicit ctx: Context): Boolean = paramDependencyStatus == TrueDeps - protected def computeSignature(implicit ctx: Context): Signature = - resultSignature.prepend(paramTypes, isJava) + def newParamRef(n: Int) = TermParamRef(this, n) + } - def derivedMethodType(paramNames: List[TermName] = this.paramNames, - paramTypes: List[Type] = this.paramTypes, - resType: Type = this.resType)(implicit ctx: Context) = - if ((paramNames eq this.paramNames) && (paramTypes eq this.paramTypes) && (resType eq this.resType)) this - else { - val paramTypesFn = (x: MethodType) => paramTypes.map(_.subst(this, x)) - val resTypeFn = (x: MethodType) => resType.subst(this, x) - if (isJava) JavaMethodType(paramNames)(paramTypesFn, resTypeFn) - else if (isImplicit) ImplicitMethodType(paramNames)(paramTypesFn, resTypeFn) - else MethodType(paramNames)(paramTypesFn, resTypeFn) - } + abstract case class MethodType(paramNames: List[TermName])( + paramInfosExp: MethodType => List[Type], + resultTypeExp: MethodType => Type) + extends MethodOrPoly with TermLambda with NarrowCached { thisMethodType => + import MethodType._ - def instantiate(argTypes: => List[Type])(implicit ctx: Context): Type = - if (isDependent) resultType.substParams(this, argTypes) - else resultType + type This = MethodType - override def equals(that: Any) = that match { - case that: MethodType => - this.paramNames == that.paramNames && - this.paramTypes == that.paramTypes && - this.resType == that.resType - case _ => - false - } + val paramInfos = paramInfosExp(this) + val resType = resultTypeExp(this) + assert(resType.exists) - override def computeHash = doHash(paramNames, resType, paramTypes) + override def computeSignature(implicit ctx: Context): Signature = + resultSignature.prepend(paramInfos, isJava) protected def prefixString = "MethodType" - override def toString = s"$prefixString($paramNames, $paramTypes, $resType)" } - final class CachedMethodType(paramNames: List[TermName])(paramTypesExp: MethodType => List[Type], resultTypeExp: MethodType => Type) - extends MethodType(paramNames)(paramTypesExp, resultTypeExp) { - override def equals(that: Any) = super.equals(that) && that.isInstanceOf[CachedMethodType] + final class CachedMethodType(paramNames: List[TermName])(paramInfosExp: MethodType => List[Type], resultTypeExp: MethodType => Type) + extends MethodType(paramNames)(paramInfosExp, resultTypeExp) { + def companion = MethodType } - final class JavaMethodType(paramNames: List[TermName])(paramTypesExp: MethodType => List[Type], resultTypeExp: MethodType => Type) - extends MethodType(paramNames)(paramTypesExp, resultTypeExp) { + final class JavaMethodType(paramNames: List[TermName])(paramInfosExp: MethodType => List[Type], resultTypeExp: MethodType => Type) + extends MethodType(paramNames)(paramInfosExp, resultTypeExp) { + def companion = JavaMethodType override def isJava = true - override def equals(that: Any) = super.equals(that) && that.isInstanceOf[JavaMethodType] - override def computeHash = addDelta(super.computeHash, 1) override protected def prefixString = "JavaMethodType" } - final class ImplicitMethodType(paramNames: List[TermName])(paramTypesExp: MethodType => List[Type], resultTypeExp: MethodType => Type) - extends MethodType(paramNames)(paramTypesExp, resultTypeExp) { + final class ImplicitMethodType(paramNames: List[TermName])(paramInfosExp: MethodType => List[Type], resultTypeExp: MethodType => Type) + extends MethodType(paramNames)(paramInfosExp, resultTypeExp) { + def companion = ImplicitMethodType override def isImplicit = true - override def equals(that: Any) = super.equals(that) && that.isInstanceOf[ImplicitMethodType] - override def computeHash = addDelta(super.computeHash, 2) override protected def prefixString = "ImplicitMethodType" } - abstract class MethodTypeCompanion { - def apply(paramNames: List[TermName])(paramTypesExp: MethodType => List[Type], resultTypeExp: MethodType => Type)(implicit ctx: Context): MethodType - def apply(paramNames: List[TermName], paramTypes: List[Type], resultType: Type)(implicit ctx: Context): MethodType = - apply(paramNames)(_ => paramTypes, _ => resultType) - def apply(paramTypes: List[Type])(resultTypeExp: MethodType => Type)(implicit ctx: Context): MethodType = - apply(nme.syntheticParamNames(paramTypes.length))(_ => paramTypes, resultTypeExp) - def apply(paramTypes: List[Type], resultType: Type)(implicit ctx: Context): MethodType = - apply(nme.syntheticParamNames(paramTypes.length), paramTypes, resultType) + abstract class LambdaTypeCompanion[N <: Name, PInfo <: Type, LT <: LambdaType] { + def syntheticParamName(n: Int): N + + @sharable private val memoizedNames = new mutable.HashMap[Int, List[N]] + def syntheticParamNames(n: Int): List[N] = synchronized { + memoizedNames.getOrElseUpdate(n, (0 until n).map(syntheticParamName).toList) + } + + def apply(paramNames: List[N])(paramInfosExp: LT => List[PInfo], resultTypeExp: LT => Type)(implicit ctx: Context): LT + def apply(paramNames: List[N], paramInfos: List[PInfo], resultType: Type)(implicit ctx: Context): LT = + apply(paramNames)(_ => paramInfos, _ => resultType) + def apply(paramInfos: List[PInfo])(resultTypeExp: LT => Type)(implicit ctx: Context): LT = + apply(syntheticParamNames(paramInfos.length))(_ => paramInfos, resultTypeExp) + def apply(paramInfos: List[PInfo], resultType: Type)(implicit ctx: Context): LT = + apply(syntheticParamNames(paramInfos.length), paramInfos, resultType) + + protected def paramName(param: ParamInfo.Of[N])(implicit ctx: Context): N = + param.paramName + + def fromParams[PI <: ParamInfo.Of[N]](params: List[PI], resultType: Type)(implicit ctx: Context): Type = + if (params.isEmpty) resultType + else apply(params.map(paramName))( + tl => params.map(param => tl.integrate(params, param.paramInfo).asInstanceOf[PInfo]), + tl => tl.integrate(params, resultType)) + } + + abstract class TermLambdaCompanion[LT <: TermLambda] + extends LambdaTypeCompanion[TermName, Type, LT] { + def syntheticParamName(n: Int) = nme.syntheticParamName(n) + } + + abstract class TypeLambdaCompanion[LT <: TypeLambda] + extends LambdaTypeCompanion[TypeName, TypeBounds, LT] { + def syntheticParamName(n: Int) = tpnme.syntheticTypeParamName(n) + } + + abstract class MethodTypeCompanion extends TermLambdaCompanion[MethodType] { /** Produce method type from parameter symbols, with special mappings for repeated - * and inline parameters. + * and inline parameters: + * - replace @repeated annotations on Seq or Array types by <repeated> types + * - add @inlineParam to inline call-by-value parameters */ def fromSymbols(params: List[Symbol], resultType: Type)(implicit ctx: Context) = { - /** Replace @repeated annotations on Seq or Array types by <repeated> types */ def translateRepeated(tp: Type): Type = tp match { case tp @ ExprType(tp1) => tp.derivedExprType(translateRepeated(tp1)) case AnnotatedType(tp, annot) if annot matches defn.RepeatedAnnot => @@ -2496,27 +2617,25 @@ object Types { case tp => tp } - /** Add @inlineParam to inline call-by-value parameters */ def translateInline(tp: Type): Type = tp match { case _: ExprType => tp case _ => AnnotatedType(tp, Annotation(defn.InlineParamAnnot)) } - def integrate(tp: Type, mt: MethodType) = - tp.subst(params, (0 until params.length).toList.map(MethodParam(mt, _))) - def paramInfo(param: Symbol): Type = { + def paramInfo(param: Symbol) = { val paramType = translateRepeated(param.info) if (param.is(Inline)) translateInline(paramType) else paramType } + apply(params.map(_.name.asTermName))( - mt => params.map(param => integrate(paramInfo(param), mt)), - mt => integrate(resultType, mt)) + tl => params.map(p => tl.integrate(params, paramInfo(p))), + tl => tl.integrate(params, resultType)) } def checkValid(mt: MethodType)(implicit ctx: Context): mt.type = { if (Config.checkMethodTypes) - for ((paramType, idx) <- mt.paramTypes.zipWithIndex) - paramType.foreachPart { - case MethodParam(`mt`, j) => assert(j < idx, mt) + for ((paramInfo, idx) <- mt.paramInfos.zipWithIndex) + paramInfo.foreachPart { + case TermParamRef(`mt`, j) => assert(j < idx, mt) case _ => } mt @@ -2524,114 +2643,97 @@ object Types { } object MethodType extends MethodTypeCompanion { - def apply(paramNames: List[TermName])(paramTypesExp: MethodType => List[Type], resultTypeExp: MethodType => Type)(implicit ctx: Context): MethodType = - checkValid(unique(new CachedMethodType(paramNames)(paramTypesExp, resultTypeExp))) - - private type DependencyStatus = Byte - private final val Unknown: DependencyStatus = 0 // not yet computed - private final val NoDeps: DependencyStatus = 1 // no dependent parameters found - private final val FalseDeps: DependencyStatus = 2 // all dependent parameters are prefixes of non-depended alias types - private final val TrueDeps: DependencyStatus = 3 // some truly dependent parameters exist - private final val StatusMask: DependencyStatus = 3 // the bits indicating actual dependency status - private final val Provisional: DependencyStatus = 4 // set if dependency status can still change due to type variable instantiations + def apply(paramNames: List[TermName])(paramInfosExp: MethodType => List[Type], resultTypeExp: MethodType => Type)(implicit ctx: Context): MethodType = + checkValid(unique(new CachedMethodType(paramNames)(paramInfosExp, resultTypeExp))) } object JavaMethodType extends MethodTypeCompanion { - def apply(paramNames: List[TermName])(paramTypesExp: MethodType => List[Type], resultTypeExp: MethodType => Type)(implicit ctx: Context): MethodType = - unique(new JavaMethodType(paramNames)(paramTypesExp, resultTypeExp)) + def apply(paramNames: List[TermName])(paramInfosExp: MethodType => List[Type], resultTypeExp: MethodType => Type)(implicit ctx: Context): MethodType = + unique(new JavaMethodType(paramNames)(paramInfosExp, resultTypeExp)) } object ImplicitMethodType extends MethodTypeCompanion { - def apply(paramNames: List[TermName])(paramTypesExp: MethodType => List[Type], resultTypeExp: MethodType => Type)(implicit ctx: Context): MethodType = - checkValid(unique(new ImplicitMethodType(paramNames)(paramTypesExp, resultTypeExp))) + def apply(paramNames: List[TermName])(paramInfosExp: MethodType => List[Type], resultTypeExp: MethodType => Type)(implicit ctx: Context): MethodType = + checkValid(unique(new ImplicitMethodType(paramNames)(paramInfosExp, resultTypeExp))) } /** A ternary extractor for MethodType */ object MethodTpe { def unapply(mt: MethodType)(implicit ctx: Context) = - Some((mt.paramNames, mt.paramTypes, mt.resultType)) - } - - /** A by-name parameter type of the form `=> T`, or the type of a method with no parameter list. */ - abstract case class ExprType(resType: Type) - extends CachedProxyType with TermType with MethodicType { - override def resultType(implicit ctx: Context): Type = resType - override def underlying(implicit ctx: Context): Type = resType - protected def computeSignature(implicit ctx: Context): Signature = resultSignature - def derivedExprType(resType: Type)(implicit ctx: Context) = - if (resType eq this.resType) this else ExprType(resType) - override def computeHash = doHash(resType) + Some((mt.paramNames, mt.paramInfos, mt.resultType)) } - final class CachedExprType(resultType: Type) extends ExprType(resultType) - - object ExprType { - def apply(resultType: Type)(implicit ctx: Context) = { - assertUnerased() - unique(new CachedExprType(resultType)) - } - } - - /** A type lambda of the form `[v_0 X_0, ..., v_n X_n] => T` */ - class PolyType(val paramNames: List[TypeName], val variances: List[Int])( - paramBoundsExp: PolyType => List[TypeBounds], resultTypeExp: PolyType => Type) - extends CachedProxyType with BindingType with MethodOrPoly { - - /** The bounds of the type parameters */ - val paramBounds: List[TypeBounds] = paramBoundsExp(this) - - /** The result type of a PolyType / body of a type lambda */ - val resType: Type = resultTypeExp(this) - - assert(resType.isInstanceOf[TermType], this) - assert(paramNames.nonEmpty) - - protected def computeSignature(implicit ctx: Context) = resultSignature + trait TypeLambda extends LambdaType { + type ThisName = TypeName + type PInfo = TypeBounds + type This <: TypeLambda - def isPolymorphicMethodType: Boolean = resType match { - case _: MethodType => true - case _ => false - } + def isDependent(implicit ctx: Context): Boolean = true + def isParamDependent(implicit ctx: Context): Boolean = true - /** PolyParam references to all type parameters of this type */ - lazy val paramRefs: List[PolyParam] = paramNames.indices.toList.map(PolyParam(this, _)) + def newParamRef(n: Int) = TypeParamRef(this, n) lazy val typeParams: List[LambdaParam] = paramNames.indices.toList.map(new LambdaParam(this, _)) - override def resultType(implicit ctx: Context) = resType - override def underlying(implicit ctx: Context) = resType - - /** Instantiate result type by substituting parameters with given arguments */ - final def instantiate(argTypes: List[Type])(implicit ctx: Context): Type = - resultType.substParams(this, argTypes) - /** Instantiate parameter bounds by substituting parameters with given arguments */ - final def instantiateBounds(argTypes: List[Type])(implicit ctx: Context): List[TypeBounds] = - paramBounds.mapConserve(_.substParams(this, argTypes).bounds) - - def newLikeThis(paramNames: List[TypeName], paramBounds: List[TypeBounds], resType: Type)(implicit ctx: Context): PolyType = - PolyType.apply(paramNames, variances)( - x => paramBounds mapConserve (_.subst(this, x).bounds), - x => resType.subst(this, x)) - - def derivedPolyType(paramNames: List[TypeName] = this.paramNames, - paramBounds: List[TypeBounds] = this.paramBounds, - resType: Type = this.resType)(implicit ctx: Context) = - if ((paramNames eq this.paramNames) && (paramBounds eq this.paramBounds) && (resType eq this.resType)) this - else newLikeThis(paramNames, paramBounds, resType) + final def instantiateBounds(argTypes: List[Type])(implicit ctx: Context): List[Type] = + paramInfos.mapConserve(_.substParams(this, argTypes)) - def derivedLambdaAbstraction(paramNames: List[TypeName], paramBounds: List[TypeBounds], resType: Type)(implicit ctx: Context): Type = + def derivedLambdaAbstraction(paramNames: List[TypeName], paramInfos: List[TypeBounds], resType: Type)(implicit ctx: Context): Type = resType match { case resType @ TypeAlias(alias) => - resType.derivedTypeAlias(newLikeThis(paramNames, paramBounds, alias)) + resType.derivedTypeAlias(newLikeThis(paramNames, paramInfos, alias)) case resType @ TypeBounds(lo, hi) => resType.derivedTypeBounds( - if (lo.isRef(defn.NothingClass)) lo else newLikeThis(paramNames, paramBounds, lo), - newLikeThis(paramNames, paramBounds, hi)) + if (lo.isRef(defn.NothingClass)) lo else newLikeThis(paramNames, paramInfos, lo), + newLikeThis(paramNames, paramInfos, hi)) case _ => - derivedPolyType(paramNames, paramBounds, resType) + derivedLambdaType(paramNames, paramInfos, resType) } + } + + /** A type lambda of the form `[X_0 B_0, ..., X_n B_n] => T` + * Variances are encoded in parameter names. A name starting with `+` + * designates a covariant parameter, a name starting with `-` designates + * a contravariant parameter, and every other name designates a non-variant parameter. + * + * @param paramNames The names `X_0`, ..., `X_n` + * @param paramInfosExp A function that, given the polytype itself, returns the + * parameter bounds `B_1`, ..., `B_n` + * @param resultTypeExp A function that, given the polytype itself, returns the + * result type `T`. + */ + class HKTypeLambda(val paramNames: List[TypeName])( + paramInfosExp: HKTypeLambda => List[TypeBounds], resultTypeExp: HKTypeLambda => Type) + extends HKLambda with TypeLambda { + type This = HKTypeLambda + def companion = HKTypeLambda + + val paramInfos: List[TypeBounds] = paramInfosExp(this) + val resType: Type = resultTypeExp(this) + + assert(resType.isInstanceOf[TermType], this) + assert(paramNames.nonEmpty) + + protected def prefixString = "HKTypeLambda" + } + + /** The type of a polymorphic method. It has the same form as HKTypeLambda, + * except it applies to terms and parameters do not have variances. + */ + class PolyType(val paramNames: List[TypeName])( + paramInfosExp: PolyType => List[TypeBounds], resultTypeExp: PolyType => Type) + extends MethodOrPoly with TypeLambda { + + type This = PolyType + def companion = PolyType + + val paramInfos: List[TypeBounds] = paramInfosExp(this) + val resType: Type = resultTypeExp(this) + + assert(resType.isInstanceOf[TermType], this) + assert(paramNames.nonEmpty) /** Merge nested polytypes into one polytype. nested polytypes are normally not supported * but can arise as temporary data structures. @@ -2640,67 +2742,95 @@ object Types { case that: PolyType => val shift = new TypeMap { def apply(t: Type) = t match { - case PolyParam(`that`, n) => PolyParam(that, n + paramNames.length) + case TypeParamRef(`that`, n) => TypeParamRef(that, n + paramNames.length) case t => mapOver(t) } } - PolyType(paramNames ++ that.paramNames, variances ++ that.variances)( - x => this.paramBounds.mapConserve(_.subst(this, x).bounds) ++ - that.paramBounds.mapConserve(shift(_).subst(that, x).bounds), + PolyType(paramNames ++ that.paramNames)( + x => this.paramInfos.mapConserve(_.subst(this, x).bounds) ++ + that.paramInfos.mapConserve(shift(_).subst(that, x).bounds), x => shift(that.resultType).subst(that, x).subst(this, x)) case _ => this } - /** The type `[tparams := paramRefs] tp`, where `tparams` can be - * either a list of type parameter symbols or a list of lambda parameters - */ - def lifted(tparams: List[TypeParamInfo], tp: Type)(implicit ctx: Context): Type = - tparams match { - case LambdaParam(poly, _) :: _ => tp.subst(poly, this) - case tparams: List[Symbol @unchecked] => tp.subst(tparams, paramRefs) - } + protected def prefixString = "PolyType" + } - override def equals(other: Any) = other match { - case other: PolyType => - other.paramNames == this.paramNames && - other.paramBounds == this.paramBounds && - other.resType == this.resType && - other.variances == this.variances - case _ => false + object HKTypeLambda extends TypeLambdaCompanion[HKTypeLambda] { + def apply(paramNames: List[TypeName])( + paramInfosExp: HKTypeLambda => List[TypeBounds], + resultTypeExp: HKTypeLambda => Type)(implicit ctx: Context): HKTypeLambda = { + unique(new HKTypeLambda(paramNames)(paramInfosExp, resultTypeExp)) } - override def toString = s"PolyType($variances, $paramNames, $paramBounds, $resType)" + def unapply(tl: HKTypeLambda): Some[(List[LambdaParam], Type)] = + Some((tl.typeParams, tl.resType)) + + def any(n: Int)(implicit ctx: Context) = + apply(syntheticParamNames(n))( + pt => List.fill(n)(TypeBounds.empty), pt => defn.AnyType) - override def computeHash = doHash(variances ::: paramNames, resType, paramBounds) + override def paramName(param: ParamInfo.Of[TypeName])(implicit ctx: Context): TypeName = + param.paramName.withVariance(param.paramVariance) + + /** Distributes Lambda inside type bounds. Examples: + * + * type T[X] = U becomes type T = [X] -> U + * type T[X] <: U becomes type T >: Nothign <: ([X] -> U) + * type T[X] >: L <: U becomes type T >: ([X] -> L) <: ([X] -> U) + */ + override def fromParams[PI <: ParamInfo.Of[TypeName]](params: List[PI], resultType: Type)(implicit ctx: Context): Type = { + def expand(tp: Type) = super.fromParams(params, tp) + resultType match { + case rt: TypeAlias => + rt.derivedTypeAlias(expand(rt.alias)) + case rt @ TypeBounds(lo, hi) => + rt.derivedTypeBounds( + if (lo.isRef(defn.NothingClass)) lo else expand(lo), expand(hi)) + case rt => + expand(rt) + } + } } - object PolyType { - def apply(paramNames: List[TypeName], variances: List[Int])( - paramBoundsExp: PolyType => List[TypeBounds], + object PolyType extends TypeLambdaCompanion[PolyType] { + def apply(paramNames: List[TypeName])( + paramInfosExp: PolyType => List[TypeBounds], resultTypeExp: PolyType => Type)(implicit ctx: Context): PolyType = { - unique(new PolyType(paramNames, variances)(paramBoundsExp, resultTypeExp)) + unique(new PolyType(paramNames)(paramInfosExp, resultTypeExp)) } def unapply(tl: PolyType): Some[(List[LambdaParam], Type)] = Some((tl.typeParams, tl.resType)) def any(n: Int)(implicit ctx: Context) = - apply(tpnme.syntheticTypeParamNames(n), List.fill(n)(0))( + apply(syntheticParamNames(n))( pt => List.fill(n)(TypeBounds.empty), pt => defn.AnyType) } + private object DepStatus { + type DependencyStatus = Byte + final val Unknown: DependencyStatus = 0 // not yet computed + final val NoDeps: DependencyStatus = 1 // no dependent parameters found + final val FalseDeps: DependencyStatus = 2 // all dependent parameters are prefixes of non-depended alias types + final val TrueDeps: DependencyStatus = 3 // some truly dependent parameters exist + final val StatusMask: DependencyStatus = 3 // the bits indicating actual dependency status + final val Provisional: DependencyStatus = 4 // set if dependency status can still change due to type variable instantiations + } + // ----- HK types: LambdaParam, HKApply --------------------- /** The parameter of a type lambda */ - case class LambdaParam(tl: PolyType, n: Int) extends TypeParamInfo { - def isTypeParam(implicit ctx: Context) = true - def paramName(implicit ctx: Context): TypeName = tl.paramNames(n) - def paramBounds(implicit ctx: Context): TypeBounds = tl.paramBounds(n) - def paramBoundsAsSeenFrom(pre: Type)(implicit ctx: Context): TypeBounds = paramBounds - def paramBoundsOrCompleter(implicit ctx: Context): Type = paramBounds - def paramVariance(implicit ctx: Context): Int = tl.variances(n) - def toArg: Type = PolyParam(tl, n) - def paramRef(implicit ctx: Context): Type = PolyParam(tl, n) + case class LambdaParam(tl: TypeLambda, n: Int) extends ParamInfo { + type ThisName = TypeName + def isTypeParam(implicit ctx: Context) = tl.paramNames.head.isTypeName + def paramName(implicit ctx: Context) = tl.paramNames(n) + def paramInfo(implicit ctx: Context) = tl.paramInfos(n) + def paramInfoAsSeenFrom(pre: Type)(implicit ctx: Context) = paramInfo + def paramInfoOrCompleter(implicit ctx: Context): Type = paramInfo + def paramVariance(implicit ctx: Context): Int = tl.paramNames(n).variance + def toArg: Type = TypeParamRef(tl, n) + def paramRef(implicit ctx: Context): Type = TypeParamRef(tl, n) } /** A higher kinded type application `C[T_1, ..., T_n]` */ @@ -2715,7 +2845,7 @@ object Types { override def superType(implicit ctx: Context): Type = { if (ctx.period != validSuper) { cachedSuper = tycon match { - case tp: PolyType => defn.AnyType + case tp: HKTypeLambda => defn.AnyType case tp: TypeVar if !tp.inst.exists => // supertype not stable, since underlying might change return tp.underlying.applyIfParameterized(args) @@ -2739,9 +2869,9 @@ object Types { NoType } - def typeParams(implicit ctx: Context): List[TypeParamInfo] = { + def typeParams(implicit ctx: Context): List[ParamInfo] = { val tparams = tycon.typeParams - if (tparams.isEmpty) PolyType.any(args.length).typeParams else tparams + if (tparams.isEmpty) HKTypeLambda.any(args.length).typeParams else tparams } def derivedAppliedType(tycon: Type, args: List[Type])(implicit ctx: Context): Type = @@ -2753,8 +2883,8 @@ object Types { protected def checkInst(implicit ctx: Context): this.type = { def check(tycon: Type): Unit = tycon.stripTypeVar match { case tycon: TypeRef if !tycon.symbol.isClass => - case _: PolyParam | _: ErrorType | _: WildcardType => - case _: PolyType => + case _: TypeParamRef | _: ErrorType | _: WildcardType => + case _: TypeLambda => assert(args.exists(_.isInstanceOf[TypeBounds]), s"unreduced type apply: $this") case tycon: AnnotatedType => check(tycon.underlying) @@ -2773,92 +2903,61 @@ object Types { unique(new CachedHKApply(tycon, args)).checkInst } - // ----- Bound types: MethodParam, PolyParam -------------------------- + // ----- BoundTypes: ParamRef, RecThis ---------------------------------------- abstract class BoundType extends CachedProxyType with ValueType { type BT <: Type - def binder: BT - // Dotty deviation: copyBoundType was copy, but - // dotty generates copy methods always automatically, and therefore - // does not accept same-named method definitions in subclasses. - // Scala2x, on the other hand, requires them (not sure why!) + val binder: BT def copyBoundType(bt: BT): Type } - abstract class ParamType extends BoundType { + abstract class ParamRef extends BoundType { + type BT <: LambdaType def paramNum: Int - def paramName: Name - } - - abstract case class MethodParam(binder: MethodType, paramNum: Int) extends ParamType with SingletonType { - type BT = MethodType - - def paramName = binder.paramNames(paramNum) + def paramName: binder.ThisName = binder.paramNames(paramNum) - override def underlying(implicit ctx: Context): Type = binder.paramTypes(paramNum) - def copyBoundType(bt: BT) = new MethodParamImpl(bt, paramNum) + override def underlying(implicit ctx: Context): Type = { + val infos = binder.paramInfos + if (infos == null) NoType // this can happen if the referenced generic type is not initialized yet + else infos(paramNum) + } - // need to customize hashCode and equals to prevent infinite recursion for dep meth types. - override def computeHash = addDelta(binder.identityHash, paramNum) + override def computeHash = doHash(paramNum, binder.identityHash) override def equals(that: Any) = that match { - case that: MethodParam => + case that: ParamRef => (this.binder eq that.binder) && this.paramNum == that.paramNum case _ => false } - override def toString = s"MethodParam($paramName)" + override def toString = + try s"ParamRef($paramName)" + catch { + case ex: IndexOutOfBoundsException => s"ParamRef(<bad index: $paramNum>)" + } } - class MethodParamImpl(binder: MethodType, paramNum: Int) extends MethodParam(binder, paramNum) - - object MethodParam { - def apply(binder: MethodType, paramNum: Int)(implicit ctx: Context): MethodParam = { - assertUnerased() - new MethodParamImpl(binder, paramNum) - } + case class TermParamRef(binder: TermLambda, paramNum: Int) extends ParamRef { + type BT = TermLambda + def copyBoundType(bt: BT) = TermParamRef(bt, paramNum) } - /** TODO Some docs would be nice here! */ - case class PolyParam(binder: PolyType, paramNum: Int) extends ParamType { - type BT = PolyType - def copyBoundType(bt: BT) = PolyParam(bt, paramNum) + case class TypeParamRef(binder: TypeLambda, paramNum: Int) extends ParamRef { + type BT = TypeLambda + def copyBoundType(bt: BT) = TypeParamRef(bt, paramNum) /** Looking only at the structure of `bound`, is one of the following true? * - fromBelow and param <:< bound * - !fromBelow and param >:> bound */ def occursIn(bound: Type, fromBelow: Boolean)(implicit ctx: Context): Boolean = bound.stripTypeVar match { - case bound: PolyParam => bound == this + case bound: ParamRef => bound == this case bound: AndOrType => def occ1 = occursIn(bound.tp1, fromBelow) def occ2 = occursIn(bound.tp2, fromBelow) if (fromBelow == bound.isAnd) occ1 && occ2 else occ1 || occ2 case _ => false } - - def paramName = binder.paramNames(paramNum) - - override def underlying(implicit ctx: Context): Type = { - val bounds = binder.paramBounds - if (bounds == null) NoType // this can happen if the referenced generic type is not initialized yet - else bounds(paramNum) - } - // no customized hashCode/equals needed because cycle is broken in PolyType - override def toString = - try s"PolyParam($paramName)" - catch { - case ex: IndexOutOfBoundsException => s"PolyParam(<bad index: $paramNum>)" - } - - override def computeHash = doHash(paramNum, binder.identityHash) - - override def equals(that: Any) = that match { - case that: PolyParam => - (this.binder eq that.binder) && this.paramNum == that.paramNum - case _ => - false - } } /** a self-reference to an enclosing recursive type. */ @@ -2929,7 +3028,7 @@ object Types { * `owningTree` and `owner` are used to determine whether a type-variable can be instantiated * at some given point. See `Inferencing#interpolateUndetVars`. */ - final class TypeVar(val origin: PolyParam, creatorState: TyperState, val bindingTree: untpd.Tree, val owner: Symbol) extends CachedProxyType with ValueType { + final class TypeVar(val origin: TypeParamRef, creatorState: TyperState, val bindingTree: untpd.Tree, val owner: Symbol) extends CachedProxyType with ValueType { /** The permanent instance type of the variable, or NoType is none is given yet */ private[core] var inst: Type = NoType @@ -3341,9 +3440,8 @@ object Types { object SAMType { def zeroParamClass(tp: Type)(implicit ctx: Context): Type = tp match { case tp: ClassInfo => - def zeroParams(tp: Type): Boolean = tp match { - case pt: PolyType => zeroParams(pt.resultType) - case mt: MethodType => mt.paramTypes.isEmpty && !mt.resultType.isInstanceOf[MethodType] + def zeroParams(tp: Type): Boolean = tp.stripPoly match { + case mt: MethodType => mt.paramInfos.isEmpty && !mt.resultType.isInstanceOf[MethodType] case et: ExprType => true case _ => false } @@ -3426,12 +3524,11 @@ object Types { tp.derivedClassInfo(pre) protected def derivedJavaArrayType(tp: JavaArrayType, elemtp: Type): Type = tp.derivedJavaArrayType(elemtp) - protected def derivedMethodType(tp: MethodType, formals: List[Type], restpe: Type): Type = - tp.derivedMethodType(tp.paramNames, formals, restpe) protected def derivedExprType(tp: ExprType, restpe: Type): Type = tp.derivedExprType(restpe) - protected def derivedPolyType(tp: PolyType, pbounds: List[TypeBounds], restpe: Type): Type = - tp.derivedPolyType(tp.paramNames, pbounds, restpe) + // note: currying needed because Scala2 does not support param-dependencies + protected def derivedLambdaType(tp: LambdaType)(formals: List[tp.PInfo], restpe: Type): Type = + tp.derivedLambdaType(tp.paramNames, formals, restpe) /** Map this function over given type */ def mapOver(tp: Type): Type = { @@ -3461,29 +3558,34 @@ object Types { variance = -variance derivedTypeBounds(tp, lo1, this(tp.hi)) - case tp: MethodType => - def mapOverMethod = { - variance = -variance - val ptypes1 = tp.paramTypes mapConserve this - variance = -variance - derivedMethodType(tp, ptypes1, this(tp.resultType)) + case tp: RecType => + derivedRecType(tp, this(tp.parent)) + + case tp: TypeVar => + val inst = tp.instanceOpt + if (inst.exists) apply(inst) else tp + + case tp: HKApply => + def mapArg(arg: Type, tparam: ParamInfo): Type = { + val saved = variance + variance *= tparam.paramVariance + try this(arg) + finally variance = saved } - mapOverMethod + derivedAppliedType(tp, this(tp.tycon), + tp.args.zipWithConserve(tp.typeParams)(mapArg)) case tp: ExprType => derivedExprType(tp, this(tp.resultType)) - case tp: PolyType => - def mapOverPoly = { + case tp: LambdaType => + def mapOverLambda = { variance = -variance - val bounds1 = tp.paramBounds.mapConserve(this).asInstanceOf[List[TypeBounds]] + val ptypes1 = tp.paramInfos.mapConserve(this).asInstanceOf[List[tp.PInfo]] variance = -variance - derivedPolyType(tp, bounds1, this(tp.resultType)) + derivedLambdaType(tp)(ptypes1, this(tp.resultType)) } - mapOverPoly - - case tp: RecType => - derivedRecType(tp, this(tp.parent)) + mapOverLambda case tp @ SuperType(thistp, supertp) => derivedSuperType(tp, this(thistp), this(supertp)) @@ -3494,21 +3596,7 @@ object Types { case tp: ClassInfo => mapClassInfo(tp) - case tp: TypeVar => - val inst = tp.instanceOpt - if (inst.exists) apply(inst) else tp - - case tp: HKApply => - def mapArg(arg: Type, tparam: TypeParamInfo): Type = { - val saved = variance - variance *= tparam.paramVariance - try this(arg) - finally variance = saved - } - derivedAppliedType(tp, this(tp.tycon), - tp.args.zipWithConserve(tp.typeParams)(mapArg)) - - case tp: AndOrType => + case tp: AndOrType => derivedAndOrType(tp, this(tp.tp1), this(tp.tp2)) case tp: SkolemType => @@ -3689,23 +3777,14 @@ object Types { this(y, hi) } - case tp: MethodType => - variance = -variance - val y = foldOver(x, tp.paramTypes) - variance = -variance - this(y, tp.resultType) + case tp: RecType => + this(x, tp.parent) case ExprType(restpe) => this(x, restpe) - case tp: PolyType => - variance = -variance - val y = foldOver(x, tp.paramBounds) - variance = -variance - this(y, tp.resultType) - - case tp: RecType => - this(x, tp.parent) + case tp: TypeVar => + this(x, tp.underlying) case SuperType(thistp, supertp) => this(this(x, thistp), supertp) @@ -3714,7 +3793,7 @@ object Types { this(x, prefix) case tp @ HKApply(tycon, args) => - @tailrec def foldArgs(x: T, tparams: List[TypeParamInfo], args: List[Type]): T = + @tailrec def foldArgs(x: T, tparams: List[ParamInfo], args: List[Type]): T = if (args.isEmpty) { assert(tparams.isEmpty) x @@ -3730,6 +3809,12 @@ object Types { } foldArgs(this(x, tycon), tp.typeParams, args) + case tp: LambdaType => + variance = -variance + val y = foldOver(x, tp.paramInfos) + variance = -variance + this(y, tp.resultType) + case tp: AndOrType => this(this(x, tp.tp1), tp.tp2) @@ -3739,9 +3824,6 @@ object Types { case AnnotatedType(underlying, annot) => this(applyToAnnot(x, annot), underlying) - case tp: TypeVar => - this(x, tp.underlying) - case tp: WildcardType => this(x, tp.optBounds) @@ -3804,9 +3886,7 @@ object Types { apply(x, tp.tref) case tp: ConstantType => apply(x, tp.underlying) - case tp: MethodParam => - apply(x, tp.underlying) - case tp: PolyParam => + case tp: ParamRef => apply(x, tp.underlying) case _ => foldOver(x, tp) diff --git a/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala b/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala index e0b233ce8..da875c906 100644 --- a/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala +++ b/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala @@ -199,7 +199,7 @@ class ClassfileParser( def stripOuterParamFromConstructor() = innerClasses.get(currentClassName) match { case Some(entry) if !isStatic(entry.jflags) => val mt @ MethodTpe(paramNames, paramTypes, resultType) = denot.info - denot.info = mt.derivedMethodType(paramNames.tail, paramTypes.tail, resultType) + denot.info = mt.derivedLambdaType(paramNames.tail, paramTypes.tail, resultType) case _ => } @@ -209,7 +209,7 @@ class ClassfileParser( def normalizeConstructorInfo() = { val mt @ MethodType(paramNames) = denot.info val rt = classRoot.typeRef appliedTo (classRoot.typeParams map (_.typeRef)) - denot.info = mt.derivedMethodType(paramNames, mt.paramTypes, rt) + denot.info = mt.derivedLambdaType(paramNames, mt.paramInfos, rt) addConstructorTypeParams(denot) } @@ -975,7 +975,7 @@ class ClassfileParser( if (name == nme.CONSTRUCTOR) tpe match { case tp: MethodType => - tp.derivedMethodType(tp.paramNames, tp.paramTypes, ownerTpe) + tp.derivedLambdaType(tp.paramNames, tp.paramInfos, ownerTpe) } p = (name, tpe) values(index) = p diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala b/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala index cb1b56c3c..8b2255e94 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala @@ -151,8 +151,9 @@ Standard-Section: "ASTs" TopLevelStat* BIND Length boundName_NameRef bounds_Type // for type-variables defined in a type pattern BYNAMEtype underlying_Type - POLYtype Length result_Type NamesTypes // variance encoded in front of name: +/-/= + POLYtype Length result_Type NamesTypes METHODtype Length result_Type NamesTypes // needed for refinements + TYPELAMBDAtype Length result_Type NamesTypes // variance encoded in front of name: +/-/(nothing) PARAMtype Length binder_ASTref paramNum_Nat // needed for refinements SHARED type_ASTRef NamesTypes = NameType* @@ -345,9 +346,10 @@ object TastyFormat { final val ORtpt = 169 final val METHODtype = 170 final val POLYtype = 171 - final val POLYtpt = 172 - final val PARAMtype = 173 - final val ANNOTATION = 174 + final val TYPELAMBDAtype = 172 + final val LAMBDAtpt = 173 + final val PARAMtype = 174 + final val ANNOTATION = 175 final val firstSimpleTreeTag = UNITconst final val firstNatTreeTag = SHARED @@ -397,7 +399,7 @@ object TastyFormat { | SINGLETONtpt | REFINEDtpt | APPLIEDtpt - | POLYtpt + | LAMBDAtpt | TYPEBOUNDStpt | ANNOTATEDtpt | ANDtpt @@ -528,8 +530,9 @@ object TastyFormat { case BYNAMEtype => "BYNAMEtype" case BYNAMEtpt => "BYNAMEtpt" case POLYtype => "POLYtype" - case POLYtpt => "POLYtpt" case METHODtype => "METHODtype" + case TYPELAMBDAtype => "TYPELAMBDAtype" + case LAMBDAtpt => "LAMBDAtpt" case PARAMtype => "PARAMtype" case ANNOTATION => "ANNOTATION" case PRIVATEqualified => "PRIVATEqualified" @@ -543,11 +546,7 @@ object TastyFormat { case VALDEF | DEFDEF | TYPEDEF | TYPEPARAM | PARAM | NAMEDARG | RETURN | BIND | SELFDEF | REFINEDtype => 1 case RENAMED | PARAMtype => 2 - case POLYtype | METHODtype => -1 + case POLYtype | METHODtype | TYPELAMBDAtype => -1 case _ => 0 } - - /** Map between variances and name prefixes */ - val varianceToPrefix = Map(-1 -> '-', 0 -> '=', 1 -> '+') - val prefixToVariance = Map('-' -> -1, '=' -> 0, '+' -> 1) } diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TastyPrinter.scala b/compiler/src/dotty/tools/dotc/core/tasty/TastyPrinter.scala index fb37c9e7d..ce3722ff1 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TastyPrinter.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TastyPrinter.scala @@ -77,7 +77,7 @@ class TastyPrinter(bytes: Array[Byte])(implicit ctx: Context) { printName(); printTree(); printTrees() case RETURN => printNat(); printTrees() - case METHODtype | POLYtype => + case METHODtype | POLYtype | TYPELAMBDAtype => printTree() until(end) { printName(); printTree() } case PARAMtype => diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index 80270aa25..902d01c21 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -253,23 +253,21 @@ class TreePickler(pickler: TastyPickler) { case tpe: ExprType => writeByte(BYNAMEtype) pickleType(tpe.underlying) - case tpe: PolyType => - writeByte(POLYtype) - val paramNames = tpe.typeParams.map(tparam => - varianceToPrefix(tparam.paramVariance) +: tparam.paramName) - pickleMethodic(tpe.resultType, paramNames, tpe.paramBounds) + case tpe: HKTypeLambda => + pickleMethodic(TYPELAMBDAtype, tpe) + case tpe: PolyType if richTypes => + pickleMethodic(POLYtype, tpe) case tpe: MethodType if richTypes => - writeByte(METHODtype) - pickleMethodic(tpe.resultType, tpe.paramNames, tpe.paramTypes) - case tpe: PolyParam => - if (!pickleParamType(tpe)) + pickleMethodic(METHODtype, tpe) + case tpe: TypeParamRef => + if (!pickleParamRef(tpe)) // TODO figure out why this case arises in e.g. pickling AbstractFileReader. ctx.typerState.constraint.entry(tpe) match { case TypeBounds(lo, hi) if lo eq hi => pickleNewType(lo, richTypes) case _ => assert(false, s"orphan poly parameter: $tpe") } - case tpe: MethodParam => - assert(pickleParamType(tpe), s"orphan method parameter: $tpe") + case tpe: TermParamRef => + assert(pickleParamRef(tpe), s"orphan method parameter: $tpe") case tpe: LazyRef => pickleType(tpe.ref) }} catch { @@ -283,15 +281,17 @@ class TreePickler(pickler: TastyPickler) { pickleName(qualifiedName(pkg)) } - def pickleMethodic(result: Type, names: List[Name], types: List[Type])(implicit ctx: Context) = + def pickleMethodic(tag: Int, tpe: LambdaType)(implicit ctx: Context) = { + writeByte(tag) withLength { - pickleType(result, richTypes = true) - (names, types).zipped.foreach { (name, tpe) => + pickleType(tpe.resultType, richTypes = true) + (tpe.paramNames, tpe.paramInfos).zipped.foreach { (name, tpe) => pickleName(name); pickleType(tpe) } } + } - def pickleParamType(tpe: ParamType)(implicit ctx: Context): Boolean = { + def pickleParamRef(tpe: ParamRef)(implicit ctx: Context): Boolean = { val binder = pickledTypes.get(tpe.binder) val pickled = binder != null if (pickled) { @@ -555,8 +555,8 @@ class TreePickler(pickler: TastyPickler) { case Annotated(tree, annot) => writeByte(ANNOTATEDtpt) withLength { pickleTree(tree); pickleTree(annot.tree) } - case PolyTypeTree(tparams, body) => - writeByte(POLYtpt) + case LambdaTypeTree(tparams, body) => + writeByte(LAMBDAtpt) withLength { pickleParams(tparams); pickleTree(body) } case TypeBoundsTree(lo, hi) => writeByte(TYPEBOUNDStpt) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index a9ea49ad1..a186d1ce4 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -38,7 +38,7 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table, posUnpickle /** A map from addresses of type entries to the types they define. * Currently only populated for types that might be recursively referenced - * from within themselves (i.e. RefinedTypes, PolyTypes, MethodTypes). + * from within themselves (i.e. RecTypes, LambdaTypes). */ private val typeAtAddr = new mutable.HashMap[Addr, Type] @@ -227,11 +227,17 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table, posUnpickle def readLengthType(): Type = { val end = readEnd() - def readNamesSkipParams: (List[Name], TreeReader) = { + def readMethodic[N <: Name, PInfo <: Type, LT <: LambdaType] + (companion: LambdaTypeCompanion[N, PInfo, LT], nameMap: Name => N): LT = { val nameReader = fork nameReader.skipTree() // skip result val paramReader = nameReader.fork - (nameReader.readParamNames(end), paramReader) + val paramNames = nameReader.readParamNames(end).map(nameMap) + val result = companion(paramNames)( + pt => registeringType(pt, paramReader.readParamTypes[PInfo](end)), + pt => readType()) + goto(end) + result } val result = @@ -268,25 +274,14 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table, posUnpickle registerSym(start, sym) TypeRef.withFixedSym(NoPrefix, sym.name, sym) case POLYtype => - val (rawNames, paramReader) = readNamesSkipParams - val (variances, paramNames) = rawNames - .map(name => (prefixToVariance(name.head), name.tail.toTypeName)).unzip - val result = PolyType(paramNames, variances)( - pt => registeringType(pt, paramReader.readParamTypes[TypeBounds](end)), - pt => readType()) - goto(end) - result + readMethodic(PolyType, _.toTypeName) case METHODtype => - val (names, paramReader) = readNamesSkipParams - val result = MethodType(names.map(_.toTermName))( - mt => registeringType(mt, paramReader.readParamTypes[Type](end)), - mt => readType()) - goto(end) - result + readMethodic(MethodType, _.toTermName) + case TYPELAMBDAtype => + readMethodic(HKTypeLambda, _.toTypeName) case PARAMtype => readTypeRef() match { - case binder: PolyType => PolyParam(binder, readNat()) - case binder: MethodType => MethodParam(binder, readNat()) + case binder: LambdaType => binder.newParamRef(readNat()) } case CLASSconst => ConstantType(Constant(readType())) @@ -412,7 +407,7 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table, posUnpickle } def isAbstractType(ttag: Int)(implicit ctx: Context): Boolean = nextUnsharedTag match { - case POLYtpt => + case LAMBDAtpt => val rdr = fork rdr.reader.readByte() // tag rdr.reader.readNat() // length @@ -736,7 +731,9 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table, posUnpickle // no longer necessary. goto(end) setPos(start, tree) - sym.info = ta.avoidPrivateLeaks(sym, tree.pos) + if (!sym.isType) { // Only terms might have leaky aliases, see the documentation of `checkNoPrivateLeaks` + sym.info = ta.avoidPrivateLeaks(sym, tree.pos) + } tree } @@ -1033,11 +1030,11 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table, posUnpickle OrTypeTree(readTpt(), readTpt()) case ANNOTATEDtpt => Annotated(readTpt(), readTerm()) - case POLYtpt => + case LAMBDAtpt => val localCtx = localNonClassCtx val tparams = readParams[TypeDef](TYPEPARAM)(localCtx) val body = readTpt()(localCtx) - PolyTypeTree(tparams, body) + LambdaTypeTree(tparams, body) case TYPEBOUNDStpt => TypeBoundsTree(readTpt(), readTpt()) case _ => @@ -1062,7 +1059,13 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table, posUnpickle } def readCases(end: Addr)(implicit ctx: Context): List[CaseDef] = - collectWhile(nextByte == CASEDEF && currentAddr != end) { readCase()(ctx.fresh.setNewScope) } + collectWhile((nextByte == CASEDEF || nextByte == SHARED) && currentAddr != end) { + if (nextByte == SHARED) { + readByte() + forkAt(readAddr()).readCase()(ctx.fresh.setNewScope) + } + else readCase()(ctx.fresh.setNewScope) + } def readCase()(implicit ctx: Context): CaseDef = { val start = currentAddr diff --git a/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala b/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala index 688a2d007..cf99bb022 100644 --- a/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala @@ -42,13 +42,15 @@ object Scala2Unpickler { /** Convert temp poly type to poly type and leave other types alone. */ def translateTempPoly(tp: Type)(implicit ctx: Context): Type = tp match { - case TempPolyType(tparams, restpe) => restpe.LambdaAbstract(tparams) + case TempPolyType(tparams, restpe) => + (if (tparams.head.owner.isTerm) PolyType else HKTypeLambda) + .fromParams(tparams, restpe) case tp => tp } def addConstructorTypeParams(denot: SymDenotation)(implicit ctx: Context) = { assert(denot.isConstructor) - denot.info = denot.info.LambdaAbstract(denot.owner.typeParams) + denot.info = PolyType.fromParams(denot.owner.typeParams, denot.info) } /** Convert array parameters denoting a repeated parameter of a Java method @@ -56,7 +58,7 @@ object Scala2Unpickler { */ def arrayToRepeated(tp: Type)(implicit ctx: Context): Type = tp match { case tp: MethodType => - val lastArg = tp.paramTypes.last + val lastArg = tp.paramInfos.last assert(lastArg isRef defn.ArrayClass) val elemtp0 :: Nil = lastArg.baseArgInfos(defn.ArrayClass) val elemtp = elemtp0 match { @@ -65,12 +67,12 @@ object Scala2Unpickler { case _ => elemtp0 } - tp.derivedMethodType( + tp.derivedLambdaType( tp.paramNames, - tp.paramTypes.init :+ defn.RepeatedParamType.appliedTo(elemtp), + tp.paramInfos.init :+ defn.RepeatedParamType.appliedTo(elemtp), tp.resultType) case tp: PolyType => - tp.derivedPolyType(tp.paramNames, tp.paramBounds, arrayToRepeated(tp.resultType)) + tp.derivedLambdaType(tp.paramNames, tp.paramInfos, arrayToRepeated(tp.resultType)) } def ensureConstructor(cls: ClassSymbol, scope: Scope)(implicit ctx: Context) = @@ -745,7 +747,7 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas TempClassInfoType(until(end, readTypeRef), symScope(clazz), clazz) case METHODtpe | IMPLICITMETHODtpe => val restpe = readTypeRef() - val params = until(end, readSymbolRef) + val params = until(end, readSymbolRef).asInstanceOf[List[TermSymbol]] def isImplicit = tag == IMPLICITMETHODtpe || params.nonEmpty && (params.head is Implicit) diff --git a/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala b/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala index 6b73a9456..47f201a09 100644 --- a/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala @@ -828,11 +828,11 @@ object JavaParsers { val superclazz = Apply(TypeApply( Select(New(javaLangDot(tpnme.Enum)), nme.CONSTRUCTOR), List(enumType)), List(Literal(Constant(null)),Literal(Constant(0)))) - val enum = atPos(start, nameOffset) { + val enumclazz = atPos(start, nameOffset) { TypeDef(name, makeTemplate(superclazz :: interfaces, body, List(), true)).withMods(mods | Flags.Enum) } - addCompanionObject(consts ::: statics ::: predefs, enum) + addCompanionObject(consts ::: statics ::: predefs, enumclazz) } def enumConst(enumType: Tree) = { diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index b644c94cc..3e3673e5e 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -49,6 +49,13 @@ object Parsers { val Class, Type, TypeParam, Def = Value } + private implicit class AddDeco(val buf: ListBuffer[Tree]) extends AnyVal { + def +++=(x: Tree) = x match { + case x: Thicket => buf ++= x.trees + case x => buf += x + } + } + /** The parse starting point depends on whether the source file is self-contained: * if not, the AST will be supplemented. */ @@ -721,7 +728,7 @@ object Parsers { val start = in.offset val tparams = typeParamClause(ParamOwner.TypeParam) if (in.token == ARROW) - atPos(start, in.skipToken())(PolyTypeTree(tparams, typ())) + atPos(start, in.skipToken())(LambdaTypeTree(tparams, typ())) else { accept(ARROW); typ() } } else infixType() @@ -1873,29 +1880,32 @@ object Parsers { mods1 } - /** Def ::= val PatDef - * | var VarDef - * | def DefDef - * | type {nl} TypeDcl - * | TmplDef - * Dcl ::= val ValDcl - * | var ValDcl - * | def DefDcl - * | type {nl} TypeDcl + /** Def ::= val PatDef + * | var VarDef + * | def DefDef + * | type {nl} TypeDcl + * | TmplDef + * Dcl ::= val ValDcl + * | var ValDcl + * | def DefDcl + * | type {nl} TypeDcl + * EnumCase ::= `case' (EnumClassDef | ObjectDef) */ def defOrDcl(start: Int, mods: Modifiers): Tree = in.token match { case VAL => val mod = atPos(in.skipToken()) { Mod.Val() } val mods1 = mods.withAddedMod(mod) - patDefOrDcl(start, mods1, in.getDocComment(start)) + patDefOrDcl(start, mods1) case VAR => val mod = atPos(in.skipToken()) { Mod.Var() } val mod1 = addMod(mods, mod) - patDefOrDcl(start, mod1, in.getDocComment(start)) + patDefOrDcl(start, mod1) case DEF => - defDefOrDcl(start, posMods(start, mods), in.getDocComment(start)) + defDefOrDcl(start, posMods(start, mods)) case TYPE => - typeDefOrDcl(start, posMods(start, mods), in.getDocComment(start)) + typeDefOrDcl(start, posMods(start, mods)) + case CASE => + enumCase(start, mods) case _ => tmplDef(start, mods) } @@ -1905,7 +1915,7 @@ object Parsers { * ValDcl ::= id {`,' id} `:' Type * VarDcl ::= id {`,' id} `:' Type */ - def patDefOrDcl(start: Offset, mods: Modifiers, docstring: Option[Comment] = None): Tree = atPos(start, nameStart) { + def patDefOrDcl(start: Offset, mods: Modifiers): Tree = atPos(start, nameStart) { val lhs = commaSeparated(pattern2) val tpt = typedOpt() val rhs = @@ -1920,7 +1930,7 @@ object Parsers { } else EmptyTree lhs match { case (id @ Ident(name: TermName)) :: Nil => { - ValDef(name, tpt, rhs).withMods(mods).setComment(docstring) + ValDef(name, tpt, rhs).withMods(mods).setComment(in.getDocComment(start)) } case _ => PatDef(mods, lhs, tpt, rhs) } @@ -1946,7 +1956,7 @@ object Parsers { * DefDcl ::= DefSig `:' Type * DefSig ::= id [DefTypeParamClause] ParamClauses */ - def defDefOrDcl(start: Offset, mods: Modifiers, docstring: Option[Comment] = None): Tree = atPos(start, nameStart) { + def defDefOrDcl(start: Offset, mods: Modifiers): Tree = atPos(start, nameStart) { def scala2ProcedureSyntax(resultTypeStr: String) = { val toInsert = if (in.token == LBRACE) s"$resultTypeStr =" @@ -1989,7 +1999,7 @@ object Parsers { accept(EQUALS) expr() } - DefDef(name, tparams, vparamss, tpt, rhs).withMods(mods1).setComment(docstring) + DefDef(name, tparams, vparamss, tpt, rhs).withMods(mods1).setComment(in.getDocComment(start)) } } @@ -2023,7 +2033,7 @@ object Parsers { /** TypeDef ::= type id [TypeParamClause] `=' Type * TypeDcl ::= type id [TypeParamClause] TypeBounds */ - def typeDefOrDcl(start: Offset, mods: Modifiers, docstring: Option[Comment] = None): Tree = { + def typeDefOrDcl(start: Offset, mods: Modifiers): Tree = { newLinesOpt() atPos(start, nameStart) { val name = ident().toTypeName @@ -2031,9 +2041,9 @@ object Parsers { in.token match { case EQUALS => in.nextToken() - TypeDef(name, lambdaAbstract(tparams, typ())).withMods(mods).setComment(docstring) + TypeDef(name, lambdaAbstract(tparams, typ())).withMods(mods).setComment(in.getDocComment(start)) case SUPERTYPE | SUBTYPE | SEMI | NEWLINE | NEWLINES | COMMA | RBRACE | EOF => - TypeDef(name, lambdaAbstract(tparams, typeBounds())).withMods(mods).setComment(docstring) + TypeDef(name, lambdaAbstract(tparams, typeBounds())).withMods(mods).setComment(in.getDocComment(start)) case _ => syntaxErrorOrIncomplete("`=', `>:', or `<:' expected") EmptyTree @@ -2041,43 +2051,51 @@ object Parsers { } } - /** TmplDef ::= ([`case'] `class' | `trait') ClassDef + /** TmplDef ::= ([`case' | `enum]'] ‘class’ | trait’) ClassDef * | [`case'] `object' ObjectDef + * | `enum' EnumDef */ def tmplDef(start: Int, mods: Modifiers): Tree = { - val docstring = in.getDocComment(start) in.token match { case TRAIT => - classDef(start, posMods(start, addFlag(mods, Trait)), docstring) + classDef(start, posMods(start, addFlag(mods, Trait))) case CLASS => - classDef(start, posMods(start, mods), docstring) + classDef(start, posMods(start, mods)) case CASECLASS => - classDef(start, posMods(start, mods | Case), docstring) + classDef(start, posMods(start, mods | Case)) case OBJECT => - objectDef(start, posMods(start, mods | Module), docstring) + objectDef(start, posMods(start, mods | Module)) case CASEOBJECT => - objectDef(start, posMods(start, mods | Case | Module), docstring) + objectDef(start, posMods(start, mods | Case | Module)) + case ENUM => + val enumMod = atPos(in.skipToken()) { Mod.Enum() } + if (in.token == CLASS) tmplDef(start, addMod(mods, enumMod)) + else enumDef(start, mods, enumMod) case _ => syntaxErrorOrIncomplete("expected start of definition") EmptyTree } } - /** ClassDef ::= id [ClsTypeParamClause] - * [ConstrMods] ClsParamClauses TemplateOpt + /** ClassDef ::= id ClassConstr TemplateOpt */ - def classDef(start: Offset, mods: Modifiers, docstring: Option[Comment]): TypeDef = atPos(start, nameStart) { - val name = ident().toTypeName - val constr = atPos(in.lastOffset) { - val tparams = typeParamClauseOpt(ParamOwner.Class) - val cmods = constrModsOpt(name) - val vparamss = paramClauses(name, mods is Case) + def classDef(start: Offset, mods: Modifiers): TypeDef = atPos(start, nameStart) { + classDefRest(start, mods, ident().toTypeName) + } - makeConstructor(tparams, vparamss).withMods(cmods) - } + def classDefRest(start: Offset, mods: Modifiers, name: TypeName): TypeDef = { + val constr = classConstr(name, isCaseClass = mods is Case) val templ = templateOpt(constr) + TypeDef(name, templ).withMods(mods).setComment(in.getDocComment(start)) + } - TypeDef(name, templ).withMods(mods).setComment(docstring) + /** ClassConstr ::= [ClsTypeParamClause] [ConstrMods] ClsParamClauses + */ + def classConstr(owner: Name, isCaseClass: Boolean = false): DefDef = atPos(in.lastOffset) { + val tparams = typeParamClauseOpt(ParamOwner.Class) + val cmods = constrModsOpt(owner) + val vparamss = paramClauses(owner, isCaseClass) + makeConstructor(tparams, vparamss).withMods(cmods) } /** ConstrMods ::= AccessModifier @@ -2093,11 +2111,73 @@ object Parsers { /** ObjectDef ::= id TemplateOpt */ - def objectDef(start: Offset, mods: Modifiers, docstring: Option[Comment] = None): ModuleDef = atPos(start, nameStart) { - val name = ident() + def objectDef(start: Offset, mods: Modifiers): ModuleDef = atPos(start, nameStart) { + objectDefRest(start, mods, ident()) + } + + def objectDefRest(start: Offset, mods: Modifiers, name: TermName): ModuleDef = { val template = templateOpt(emptyConstructor) + ModuleDef(name, template).withMods(mods).setComment(in.getDocComment(start)) + } + + /** id ClassConstr [`extends' [ConstrApps]] + * [nl] ‘{’ EnumCaseStats ‘}’ + */ + def enumDef(start: Offset, mods: Modifiers, enumMod: Mod): Thicket = { + val point = nameStart + val modName = ident() + val clsName = modName.toTypeName + val constr = classConstr(clsName) + val parents = + if (in.token == EXTENDS) { + in.nextToken(); + newLineOptWhenFollowedBy(LBRACE) + if (in.token == LBRACE) Nil else tokenSeparated(WITH, constrApp) + } + else Nil + val clsDef = atPos(start, point) { + TypeDef(clsName, Template(constr, parents, EmptyValDef, Nil)) + .withMods(addMod(mods, enumMod)).setComment(in.getDocComment(start)) + } + newLineOptWhenFollowedBy(LBRACE) + val modDef = atPos(in.offset) { + val body = inBraces(enumCaseStats) + ModuleDef(modName, Template(emptyConstructor, Nil, EmptyValDef, body)) + .withMods(mods) + } + Thicket(clsDef :: modDef :: Nil) + } + + /** EnumCaseStats = EnumCaseStat {semi EnumCaseStat */ + def enumCaseStats(): List[DefTree] = { + val cases = new ListBuffer[DefTree] += enumCaseStat() + while (in.token != RBRACE) { + acceptStatSep() + cases += enumCaseStat() + } + cases.toList + } + + /** EnumCaseStat = {Annotation [nl]} {Modifier} EnumCase */ + def enumCaseStat(): DefTree = + enumCase(in.offset, defAnnotsMods(modifierTokens)) - ModuleDef(name, template).withMods(mods).setComment(docstring) + /** EnumCase = `case' (EnumClassDef | ObjectDef) */ + def enumCase(start: Offset, mods: Modifiers): DefTree = { + val mods1 = mods.withAddedMod(atPos(in.offset)(Mod.EnumCase())) | Case + accept(CASE) + atPos(start, nameStart) { + val id = termIdent() + if (in.token == LBRACKET || in.token == LPAREN) + classDefRest(start, mods1, id.name.toTypeName) + else if (in.token == COMMA) { + in.nextToken() + val ids = commaSeparated(termIdent) + PatDef(mods1, id :: ids, TypeTree(), EmptyTree) + } + else + objectDefRest(start, mods1, id.name.asTermName) + } } /* -------- TEMPLATES ------------------------------------------- */ @@ -2191,7 +2271,7 @@ object Parsers { else if (in.token == IMPORT) stats ++= importClause() else if (in.token == AT || isTemplateIntro || isModifier) - stats += tmplDef(in.offset, defAnnotsMods(modifierTokens)) + stats +++= tmplDef(in.offset, defAnnotsMods(modifierTokens)) else if (!isStatSep) { if (in.token == CASE) syntaxErrorOrIncomplete("only `case class` or `case object` allowed") @@ -2209,8 +2289,8 @@ object Parsers { * TemplateStat ::= Import * | Annotations Modifiers Def * | Annotations Modifiers Dcl + * | EnumCaseStat * | Expr1 - * | super ArgumentExprs {ArgumentExprs} * | */ def templateStatSeq(): (ValDef, List[Tree]) = checkNoEscapingPlaceholders { @@ -2240,8 +2320,8 @@ object Parsers { stats ++= importClause() else if (isExprIntro) stats += expr1() - else if (isDefIntro(modifierTokens)) - stats += defOrDcl(in.offset, defAnnotsMods(modifierTokens)) + else if (isDefIntro(modifierTokensOrCase)) + stats +++= defOrDcl(in.offset, defAnnotsMods(modifierTokens)) else if (!isStatSep) { exitOnError = mustStartStat syntaxErrorOrIncomplete("illegal start of definition") @@ -2299,9 +2379,9 @@ object Parsers { val start = in.offset val imods = implicitMods() if (isBindingIntro) stats += implicitClosure(start, Location.InBlock, imods) - else stats += localDef(start, imods) + else stats +++= localDef(start, imods) } else { - stats += localDef(in.offset) + stats +++= localDef(in.offset) } else if (!isStatSep && (in.token != CASE)) { exitOnError = mustStartStat @@ -2323,8 +2403,7 @@ object Parsers { if (in.token == PACKAGE) { in.nextToken() if (in.token == OBJECT) { - val docstring = in.getDocComment(start) - ts += objectDef(start, atPos(start, in.skipToken()) { Modifiers(Package) }, docstring) + ts += objectDef(start, atPos(start, in.skipToken()) { Modifiers(Package) }) if (in.token != EOF) { acceptStatSep() ts ++= topStatSeq() diff --git a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala index 847f600c0..ff5019dc9 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala @@ -10,6 +10,7 @@ import util.Chars._ import Tokens._ import scala.annotation.{ switch, tailrec } import scala.collection.mutable +import scala.collection.immutable.SortedMap import mutable.ListBuffer import Utility.isNameStart import rewrite.Rewrites.patch @@ -174,37 +175,24 @@ object Scanners { class Scanner(source: SourceFile, override val startFrom: Offset = 0)(implicit ctx: Context) extends ScannerCommon(source)(ctx) { val keepComments = ctx.settings.YkeepComments.value - /** All doc comments as encountered, each list contains doc comments from - * the same block level. Starting with the deepest level and going upward - */ - private[this] var docsPerBlockStack: List[List[Comment]] = List(Nil) - - /** Adds level of nesting to docstrings */ - def enterBlock(): Unit = - docsPerBlockStack = List(Nil) ::: docsPerBlockStack - - /** Removes level of nesting for docstrings */ - def exitBlock(): Unit = docsPerBlockStack = docsPerBlockStack match { - case x :: Nil => List(Nil) - case _ => docsPerBlockStack.tail + /** All doc comments kept by their end position in a `Map` */ + private[this] var docstringMap: SortedMap[Int, Comment] = SortedMap.empty + + private[this] def addComment(comment: Comment): Unit = { + val lookahead = lookaheadReader + def nextPos: Int = (lookahead.getc(): @switch) match { + case ' ' | '\t' => nextPos + case CR | LF | FF => + // if we encounter line delimitng whitespace we don't count it, since + // it seems not to affect positions in source + nextPos - 1 + case _ => lookahead.charOffset - 1 + } + docstringMap = docstringMap + (nextPos -> comment) } /** Returns the closest docstring preceding the position supplied */ - def getDocComment(pos: Int): Option[Comment] = { - def closest(c: Comment, docstrings: List[Comment]): Comment = docstrings match { - case x :: xs if (c.pos.end < x.pos.end && x.pos.end <= pos) => closest(x, xs) - case Nil => c - } - - docsPerBlockStack match { - case (list @ (x :: xs)) :: _ => { - val c = closest(x, xs) - docsPerBlockStack = list.dropWhile(_ != c).tail :: docsPerBlockStack.tail - Some(c) - } - case _ => None - } - } + def getDocComment(pos: Int): Option[Comment] = docstringMap.get(pos) /** A buffer for comments */ val commentBuf = new StringBuilder @@ -294,7 +282,10 @@ object Scanners { if (!sepRegions.isEmpty && sepRegions.head == lastToken) sepRegions = sepRegions.tail case ARROW => - if (!sepRegions.isEmpty && sepRegions.head == lastToken) + if (!sepRegions.isEmpty && sepRegions.head == ARROW) + sepRegions = sepRegions.tail + case EXTENDS => + if (!sepRegions.isEmpty && sepRegions.head == ARROW) sepRegions = sepRegions.tail case STRINGLIT => if (inMultiLineInterpolation) @@ -330,7 +321,8 @@ object Scanners { if (isAfterLineEnd() && (canEndStatTokens contains lastToken) && (canStartStatTokens contains token) && - (sepRegions.isEmpty || sepRegions.head == RBRACE)) { + (sepRegions.isEmpty || sepRegions.head == RBRACE || + sepRegions.head == ARROW && token == CASE)) { next copyFrom this // todo: make offset line-end of previous line? offset = if (lineStartOffset <= offset) lineStartOffset else lastLineStartOffset @@ -535,13 +527,13 @@ object Scanners { case ',' => nextChar(); token = COMMA case '(' => - enterBlock(); nextChar(); token = LPAREN + nextChar(); token = LPAREN case '{' => - enterBlock(); nextChar(); token = LBRACE + nextChar(); token = LBRACE case ')' => - exitBlock(); nextChar(); token = RPAREN + nextChar(); token = RPAREN case '}' => - exitBlock(); nextChar(); token = RBRACE + nextChar(); token = RBRACE case '[' => nextChar(); token = LBRACKET case ']' => @@ -606,11 +598,12 @@ object Scanners { val start = lastCharOffset def finishComment(): Boolean = { if (keepComments) { - val pos = Position(start, charOffset, start) + val pos = Position(start, charOffset - 1, start) val comment = Comment(pos, flushBuf(commentBuf)) - if (comment.isDocComment) - docsPerBlockStack = (docsPerBlockStack.head :+ comment) :: docsPerBlockStack.tail + if (comment.isDocComment) { + addComment(comment) + } } true diff --git a/compiler/src/dotty/tools/dotc/parsing/Tokens.scala b/compiler/src/dotty/tools/dotc/parsing/Tokens.scala index 8d42e525a..d2ea9240c 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Tokens.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Tokens.scala @@ -92,6 +92,7 @@ abstract class TokensCommon { //final val THEN = 60; enter(THEN, "then") //final val FORSOME = 61; enter(FORSOME, "forSome") // TODO: deprecate //final val INLINE = 62; enter(INLINE, "inline") + //final val ENUM = 63; enter(ENUM, "enum") /** special symbols */ final val COMMA = 70; enter(COMMA, "','") @@ -175,6 +176,7 @@ object Tokens extends TokensCommon { final val THEN = 60; enter(THEN, "then") final val FORSOME = 61; enter(FORSOME, "forSome") // TODO: deprecate final val INLINE = 62; enter(INLINE, "inline") + final val ENUM = 63; enter(ENUM, "enum") /** special symbols */ final val NEWLINE = 78; enter(NEWLINE, "end of statement", "new line") @@ -192,7 +194,7 @@ object Tokens extends TokensCommon { /** XML mode */ final val XMLSTART = 96; enter(XMLSTART, "$XMLSTART$<") // TODO: deprecate - final val alphaKeywords = tokenRange(IF, INLINE) + final val alphaKeywords = tokenRange(IF, ENUM) final val symbolicKeywords = tokenRange(USCORE, VIEWBOUND) final val symbolicTokens = tokenRange(COMMA, VIEWBOUND) final val keywords = alphaKeywords | symbolicKeywords @@ -213,7 +215,7 @@ object Tokens extends TokensCommon { final val canStartBindingTokens = identifierTokens | BitSet(USCORE, LPAREN) - final val templateIntroTokens = BitSet(CLASS, TRAIT, OBJECT, CASECLASS, CASEOBJECT) + final val templateIntroTokens = BitSet(CLASS, TRAIT, OBJECT, ENUM, CASECLASS, CASEOBJECT) final val dclIntroTokens = BitSet(DEF, VAL, VAR, TYPE) @@ -228,6 +230,8 @@ object Tokens extends TokensCommon { final val modifierTokens = localModifierTokens | accessModifierTokens | BitSet( OVERRIDE) + final val modifierTokensOrCase = modifierTokens | BitSet(CASE) + /** Is token only legal as start of statement (eof also included)? */ final val mustStartStatTokens = defIntroTokens | modifierTokens | BitSet( IMPORT, PACKAGE) diff --git a/compiler/src/dotty/tools/dotc/printing/Formatting.scala b/compiler/src/dotty/tools/dotc/printing/Formatting.scala index 05f1af9d7..e8fa45403 100644 --- a/compiler/src/dotty/tools/dotc/printing/Formatting.scala +++ b/compiler/src/dotty/tools/dotc/printing/Formatting.scala @@ -107,7 +107,7 @@ object Formatting { else nonSensicalStartTag + str + nonSensicalEndTag } - private type Recorded = AnyRef /*Symbol | PolyParam*/ + private type Recorded = AnyRef /*Symbol | TypeParamRef*/ private class Seen extends mutable.HashMap[String, List[Recorded]] { @@ -135,8 +135,8 @@ object Formatting { if ((sym is ModuleClass) && sym.sourceModule.exists) simpleNameString(sym.sourceModule) else seen.record(super.simpleNameString(sym), sym) - override def polyParamNameString(param: PolyParam): String = - seen.record(super.polyParamNameString(param), param) + override def TypeParamRefNameString(param: TypeParamRef): String = + seen.record(super.TypeParamRefNameString(param), param) } /** Create explanation for single `Recorded` type or symbol */ @@ -161,7 +161,7 @@ object Formatting { } entry match { - case param: PolyParam => + case param: TypeParamRef => s"is a type variable${addendum("constraint", ctx.typeComparer.bounds(param))}" case sym: Symbol => s"is a ${ctx.printer.kindString(sym)}${sym.showExtendedLocation}${addendum("bounds", sym.info)}" @@ -175,7 +175,7 @@ object Formatting { */ private def explanations(seen: Seen)(implicit ctx: Context): String = { def needsExplanation(entry: Recorded) = entry match { - case param: PolyParam => ctx.typerState.constraint.contains(param) + case param: TypeParamRef => ctx.typerState.constraint.contains(param) case _ => false } @@ -213,9 +213,11 @@ object Formatting { * ex"disambiguate $tpe1 and $tpe2" * ``` */ - def explained2(op: Context => String)(implicit ctx: Context): String = { + def explained(op: Context => String)(implicit ctx: Context): String = { val seen = new Seen - op(explainCtx(seen)) ++ explanations(seen) + val msg = op(explainCtx(seen)) + val addendum = explanations(seen) + if (addendum.isEmpty) msg else msg ++ "\n\n" ++ addendum } /** When getting a type mismatch it is useful to disambiguate placeholders like: diff --git a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala index 67d44daa1..d5014b547 100644 --- a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala @@ -170,21 +170,20 @@ class PlainPrinter(_ctx: Context) extends Printer { def paramText(name: TermName, tp: Type) = toText(name) ~ ": " ~ toText(tp) changePrec(GlobalPrec) { (if (tp.isImplicit) "(implicit " else "(") ~ - Text((tp.paramNames, tp.paramTypes).zipped map paramText, ", ") ~ + Text((tp.paramNames, tp.paramInfos).zipped map paramText, ", ") ~ ")" ~ toText(tp.resultType) } case tp: ExprType => changePrec(GlobalPrec) { "=> " ~ toText(tp.resultType) } - case tp: PolyType => - def paramText(variance: Int, name: Name, bounds: TypeBounds): Text = - varianceString(variance) ~ name.toString ~ toText(bounds) + case tp: TypeLambda => + def paramText(name: Name, bounds: TypeBounds): Text = name.toString ~ toText(bounds) changePrec(GlobalPrec) { - "[" ~ Text((tp.variances, tp.paramNames, tp.paramBounds).zipped.map(paramText), ", ") ~ + "[" ~ Text((tp.paramNames, tp.paramInfos).zipped.map(paramText), ", ") ~ "]" ~ (" => " provided !tp.resultType.isInstanceOf[MethodType]) ~ toTextGlobal(tp.resultType) } - case tp: PolyParam => - polyParamNameString(tp) ~ polyHash(tp.binder) + case tp: TypeParamRef => + TypeParamRefNameString(tp) ~ lambdaHash(tp.binder) case AnnotatedType(tpe, annot) => toTextLocal(tpe) ~ " " ~ toText(annot) case HKApply(tycon, args) => @@ -207,17 +206,18 @@ class PlainPrinter(_ctx: Context) extends Printer { } }.close - protected def polyParamNameString(name: TypeName): String = name.toString + protected def TypeParamRefNameString(name: TypeName): String = name.toString - protected def polyParamNameString(param: PolyParam): String = polyParamNameString(param.binder.paramNames(param.paramNum)) + protected def TypeParamRefNameString(param: TypeParamRef): String = + TypeParamRefNameString(param.binder.paramNames(param.paramNum)) /** The name of the symbol without a unique id. Under refined printing, * the decoded original name. */ protected def simpleNameString(sym: Symbol): String = nameString(sym.name) - /** If -uniqid is set, the hashcode of the polytype, after a # */ - protected def polyHash(pt: PolyType): Text = + /** If -uniqid is set, the hashcode of the lambda type, after a # */ + protected def lambdaHash(pt: LambdaType): Text = if (ctx.settings.uniqid.value) "#" + pt.hashCode else "" /** If -uniqid is set, the unique id of symbol, after a # */ @@ -259,8 +259,8 @@ class PlainPrinter(_ctx: Context) extends Printer { "Super(" ~ toTextGlobal(thistpe) ~ ")" case tp @ ConstantType(value) => toText(value) - case MethodParam(mt, idx) => - nameString(mt.paramNames(idx)) + case pref: TermParamRef => + nameString(pref.binder.paramNames(pref.paramNum)) case tp: RecThis => val idx = openRecs.reverse.indexOf(tp.binder) if (idx >= 0) selfRecName(idx + 1) diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 8a33472b8..76bce7920 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -401,7 +401,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { toTextLocal(tpt) ~ " " ~ blockText(refines) case AppliedTypeTree(tpt, args) => toTextLocal(tpt) ~ "[" ~ Text(args map argText, ", ") ~ "]" - case PolyTypeTree(tparams, body) => + case LambdaTypeTree(tparams, body) => changePrec(GlobalPrec) { tparamsText(tparams) ~ " -> " ~ toText(body) } @@ -451,7 +451,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { (if (tree.hasType && ctx.settings.verbose.value) i"[decls = ${tree.symbol.info.decls}]" else "") case rhs: TypeBoundsTree => typeDefText(tparamsTxt, toText(rhs)) - case PolyTypeTree(tparams, body) => + case LambdaTypeTree(tparams, body) => recur(body, tparamsText(tparams)) case rhs => typeDefText(tparamsTxt, optText(rhs)(" = " ~ _)) @@ -604,7 +604,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { def optText[T >: Untyped](tree: List[Tree[T]])(encl: Text => Text): Text = if (tree.exists(!_.isEmpty)) encl(blockText(tree)) else "" - override protected def polyParamNameString(name: TypeName): String = + override protected def TypeParamRefNameString(name: TypeName): String = name.unexpandedName.toString override protected def treatAsTypeParam(sym: Symbol): Boolean = sym is TypeParam diff --git a/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala index 57365658e..87837fd82 100644 --- a/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala @@ -238,7 +238,7 @@ object messages { import core.Flags._ val maxDist = 3 val decls = site.decls.flatMap { sym => - if (sym.is(Synthetic | PrivateOrLocal) || sym.isConstructor) Nil + if (sym.flagsUNSAFE.is(Synthetic | PrivateOrLocal) || sym.isConstructor) Nil else List((sym.name.show, sym)) } @@ -617,7 +617,7 @@ object messages { |""" } - case class WrongNumberOfTypeArgs(fntpe: Type, expectedArgs: List[TypeParamInfo], actual: List[untpd.Tree])(implicit ctx: Context) + case class WrongNumberOfTypeArgs(fntpe: Type, expectedArgs: List[ParamInfo], actual: List[untpd.Tree])(implicit ctx: Context) extends Message(WrongNumberOfTypeArgsID) { val kind = "Syntax" @@ -625,7 +625,7 @@ object messages { private val actualCount = actual.length private val msgPrefix = if (actualCount > expectedCount) "Too many" else "Not enough" - //TODO add def simpleParamName to TypeParamInfo + //TODO add def simpleParamName to ParamInfo private val expectedArgString = fntpe .widen.typeParams .map(_.paramName.unexpandedName.show) diff --git a/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala b/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala index 082a80b58..8d704f9a2 100644 --- a/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala +++ b/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala @@ -286,7 +286,7 @@ private class ExtractAPICollector(implicit val ctx: Context) extends ThunkHolder def apiDef(sym: TermSymbol): api.Def = { def paramLists(t: Type, start: Int = 0): List[api.ParameterList] = t match { - case pt: PolyType => + case pt: TypeLambda => assert(start == 0) paramLists(pt.resultType) case mt @ MethodTpe(pnames, ptypes, restpe) => @@ -311,8 +311,8 @@ private class ExtractAPICollector(implicit val ctx: Context) extends ThunkHolder } val tparams = sym.info match { - case pt: PolyType => - (pt.paramNames, pt.paramBounds).zipped.map((pname, pbounds) => + case pt: TypeLambda => + (pt.paramNames, pt.paramInfos).zipped.map((pname, pbounds) => apiTypeParameter(pname.toString, 0, pbounds.lo, pbounds.hi)) case _ => Nil @@ -385,9 +385,9 @@ private class ExtractAPICollector(implicit val ctx: Context) extends ThunkHolder val apiTycon = simpleType(tycon) val apiArgs = args.map(processArg) new api.Parameterized(apiTycon, apiArgs.toArray) - case PolyType(tparams, res) => - val apiTparams = tparams.map(apiTypeParameter) - val apiRes = apiType(res) + case tl: TypeLambda => + val apiTparams = tl.typeParams.map(apiTypeParameter) + val apiRes = apiType(tl.resType) new api.Polymorphic(apiRes, apiTparams.toArray) case rt: RefinedType => val name = rt.refinedName.toString @@ -460,7 +460,7 @@ private class ExtractAPICollector(implicit val ctx: Context) extends ThunkHolder new api.Annotated(apiType(tpe), Array(apiAnnotation(annot))) case tp: ThisType => apiThis(tp.cls) - case tp: ParamType => + case tp: ParamRef => // TODO: Distinguishing parameters based on their names alone is not enough, // the binder is also needed (at least for type lambdas). new api.ParameterRef(tp.paramName.toString) @@ -497,9 +497,9 @@ private class ExtractAPICollector(implicit val ctx: Context) extends ThunkHolder new api.Singleton(new api.Path(pathComponents.toArray.reverse ++ Array(Constants.thisPath))) } - def apiTypeParameter(tparam: TypeParamInfo): api.TypeParameter = + def apiTypeParameter(tparam: ParamInfo): api.TypeParameter = apiTypeParameter(tparam.paramName.toString, tparam.paramVariance, - tparam.paramBounds.lo, tparam.paramBounds.hi) + tparam.paramInfo.bounds.lo, tparam.paramInfo.bounds.hi) def apiTypeParameter(name: String, variance: Int, lo: Type, hi: Type): api.TypeParameter = new api.TypeParameter(name, Array(), Array(), apiVariance(variance), diff --git a/compiler/src/dotty/tools/dotc/sbt/ExtractDependencies.scala b/compiler/src/dotty/tools/dotc/sbt/ExtractDependencies.scala index fefa63f6f..0a061c841 100644 --- a/compiler/src/dotty/tools/dotc/sbt/ExtractDependencies.scala +++ b/compiler/src/dotty/tools/dotc/sbt/ExtractDependencies.scala @@ -256,9 +256,7 @@ private class ExtractDependenciesCollector(implicit val ctx: Context) extends tp traverse(tp.underlying) case tp: ConstantType => traverse(tp.underlying) - case tp: MethodParam => - traverse(tp.underlying) - case tp: PolyParam => + case tp: ParamRef => traverse(tp.underlying) case _ => traverseChildren(tp) diff --git a/compiler/src/dotty/tools/dotc/transform/CollectEntryPoints.scala b/compiler/src/dotty/tools/dotc/transform/CollectEntryPoints.scala index 9f1e42e31..e82be4378 100644 --- a/compiler/src/dotty/tools/dotc/transform/CollectEntryPoints.scala +++ b/compiler/src/dotty/tools/dotc/transform/CollectEntryPoints.scala @@ -88,7 +88,7 @@ class CollectEntryPoints extends MiniPhaseTransform { case t: PolyType => fail("main methods cannot be generic.") case t: MethodType => - if (t.resultType :: t.paramTypes exists (_.typeSymbol.isAbstractType)) + if (t.resultType :: t.paramInfos exists (_.typeSymbol.isAbstractType)) fail("main methods cannot refer to type parameters or abstract types.", m.symbol.pos) else javaPlatform.isJavaMainMethod(m.symbol) || fail("main method must have exact signature (Array[String])Unit", m.symbol.pos) diff --git a/compiler/src/dotty/tools/dotc/transform/ElimByName.scala b/compiler/src/dotty/tools/dotc/transform/ElimByName.scala index 59da78590..839552799 100644 --- a/compiler/src/dotty/tools/dotc/transform/ElimByName.scala +++ b/compiler/src/dotty/tools/dotc/transform/ElimByName.scala @@ -93,7 +93,7 @@ class ElimByName extends MiniPhaseTransform with InfoTransformer { thisTransform } val mt @ MethodType(_) = tree.fun.tpe.widen - val args1 = tree.args.zipWithConserve(mt.paramTypes)(transformArg) + val args1 = tree.args.zipWithConserve(mt.paramInfos)(transformArg) cpy.Apply(tree)(tree.fun, args1) } diff --git a/compiler/src/dotty/tools/dotc/transform/ElimErasedValueType.scala b/compiler/src/dotty/tools/dotc/transform/ElimErasedValueType.scala index 24c8cdc8d..48be02fa1 100644 --- a/compiler/src/dotty/tools/dotc/transform/ElimErasedValueType.scala +++ b/compiler/src/dotty/tools/dotc/transform/ElimErasedValueType.scala @@ -44,9 +44,9 @@ class ElimErasedValueType extends MiniPhaseTransform with InfoTransformer { case ErasedValueType(_, underlying) => elimEVT(underlying) case tp: MethodType => - val paramTypes = tp.paramTypes.mapConserve(elimEVT) + val paramTypes = tp.paramInfos.mapConserve(elimEVT) val retType = elimEVT(tp.resultType) - tp.derivedMethodType(tp.paramNames, paramTypes, retType) + tp.derivedLambdaType(tp.paramNames, paramTypes, retType) case _ => tp } diff --git a/compiler/src/dotty/tools/dotc/transform/ElimRepeated.scala b/compiler/src/dotty/tools/dotc/transform/ElimRepeated.scala index ae3259509..683c8ac38 100644 --- a/compiler/src/dotty/tools/dotc/transform/ElimRepeated.scala +++ b/compiler/src/dotty/tools/dotc/transform/ElimRepeated.scala @@ -55,9 +55,9 @@ class ElimRepeated extends MiniPhaseTransform with InfoTransformer with Annotati val last = paramTypes.last.underlyingIfRepeated(tp.isJava) paramTypes.init :+ last } else paramTypes - tp.derivedMethodType(paramNames, paramTypes1, resultType1) + tp.derivedLambdaType(paramNames, paramTypes1, resultType1) case tp: PolyType => - tp.derivedPolyType(tp.paramNames, tp.paramBounds, elimRepeated(tp.resultType)) + tp.derivedLambdaType(tp.paramNames, tp.paramInfos, elimRepeated(tp.resultType)) case tp => tp } @@ -139,10 +139,10 @@ class ElimRepeated extends MiniPhaseTransform with InfoTransformer with Annotati /** Convert type from Scala to Java varargs method */ private def toJavaVarArgs(tp: Type)(implicit ctx: Context): Type = tp match { case tp: PolyType => - tp.derivedPolyType(tp.paramNames, tp.paramBounds, toJavaVarArgs(tp.resultType)) + tp.derivedLambdaType(tp.paramNames, tp.paramInfos, toJavaVarArgs(tp.resultType)) case tp: MethodType => - val inits :+ last = tp.paramTypes + val inits :+ last = tp.paramInfos val last1 = last.underlyingIfRepeated(isJava = true) - tp.derivedMethodType(tp.paramNames, inits :+ last1, tp.resultType) + tp.derivedLambdaType(tp.paramNames, inits :+ last1, tp.resultType) } } diff --git a/compiler/src/dotty/tools/dotc/transform/Erasure.scala b/compiler/src/dotty/tools/dotc/transform/Erasure.scala index f9c7a8e1e..d64120085 100644 --- a/compiler/src/dotty/tools/dotc/transform/Erasure.scala +++ b/compiler/src/dotty/tools/dotc/transform/Erasure.scala @@ -461,12 +461,12 @@ object Erasure extends TypeTestsCasts{ case mt: MethodType => val outers = outer.args(fun.asInstanceOf[tpd.Tree]) // can't use fun1 here because its type is already erased var args0 = outers ::: args ++ protoArgs(pt) - if (args0.length > MaxImplementedFunctionArity && mt.paramTypes.length == 1) { + if (args0.length > MaxImplementedFunctionArity && mt.paramInfos.length == 1) { val bunchedArgs = untpd.JavaSeqLiteral(args0, TypeTree(defn.ObjectType)) .withType(defn.ArrayOf(defn.ObjectType)) args0 = bunchedArgs :: Nil } - val args1 = args0.zipWithConserve(mt.paramTypes)(typedExpr) + val args1 = args0.zipWithConserve(mt.paramInfos)(typedExpr) untpd.cpy.Apply(tree)(fun1, args1) withType mt.resultType case _ => throw new MatchError(i"tree $tree has unexpected type of function ${fun1.tpe.widen}, was ${fun.typeOpt.widen}") @@ -547,8 +547,8 @@ object Erasure extends TypeTestsCasts{ case SAMType(sam) => val implType = meth.tpe.widen - val List(implParamTypes) = implType.paramTypess - val List(samParamTypes) = sam.info.paramTypess + val List(implParamTypes) = implType.paramInfoss + val List(samParamTypes) = sam.info.paramInfoss val implResultType = implType.resultType val samResultType = sam.info.resultType @@ -698,8 +698,8 @@ object Erasure extends TypeTestsCasts{ val rhs = paramss.foldLeft(sel)((fun, vparams) => fun.tpe.widen match { case mt: MethodType => - Apply(fun, (vparams, mt.paramTypes).zipped.map(adapt(_, _, untpd.EmptyTree))) - case a => + Apply(fun, (vparams, mt.paramInfos).zipped.map(adapt(_, _, untpd.EmptyTree))) + case a => error(s"can not resolve apply type $a") }) adapt(rhs, resultType) diff --git a/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala b/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala index f17808e62..a6e643992 100644 --- a/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala +++ b/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala @@ -331,7 +331,7 @@ object ExplicitOuter { def addParam(cls: ClassSymbol, tp: Type): Type = if (hasOuterParam(cls)) { val mt @ MethodTpe(pnames, ptypes, restpe) = tp - mt.derivedMethodType( + mt.derivedLambdaType( nme.OUTER :: pnames, cls.owner.enclosingClass.typeRef :: ptypes, restpe) } else tp diff --git a/compiler/src/dotty/tools/dotc/transform/FullParameterization.scala b/compiler/src/dotty/tools/dotc/transform/FullParameterization.scala index 0cb453b4c..d76a41946 100644 --- a/compiler/src/dotty/tools/dotc/transform/FullParameterization.scala +++ b/compiler/src/dotty/tools/dotc/transform/FullParameterization.scala @@ -107,13 +107,13 @@ trait FullParameterization { val firstArgType = if (liftThisType) thisParamType & clazz.thisType else thisParamType MethodType(nme.SELF :: Nil)( mt => firstArgType :: Nil, - mt => mapClassParams(origResult).substThisUnlessStatic(clazz, MethodParam(mt, 0))) + mt => mapClassParams(origResult).substThisUnlessStatic(clazz, mt.newParamRef(0))) } /** Replace class type parameters by the added type parameters of the polytype `pt` */ def mapClassParams(tp: Type, pt: PolyType): Type = { val classParamsRange = (mtparamCount until mtparamCount + ctparams.length).toList - tp.substDealias(ctparams, classParamsRange map (PolyParam(pt, _))) + tp.substDealias(ctparams, classParamsRange map (TypeParamRef(pt, _))) } /** The bounds for the added type parameters of the polytype `pt` */ @@ -122,14 +122,14 @@ trait FullParameterization { info match { case info: PolyType => - PolyType(info.paramNames ++ ctnames, info.variances ++ ctvariances)( + PolyType(info.paramNames ++ ctnames)( pt => - (info.paramBounds.map(mapClassParams(_, pt).bounds) ++ + (info.paramInfos.map(mapClassParams(_, pt).bounds) ++ mappedClassBounds(pt)).mapConserve(_.subst(info, pt).bounds), pt => resultType(mapClassParams(_, pt)).subst(info, pt)) case _ => if (ctparams.isEmpty) resultType(identity) - else PolyType(ctnames, ctvariances)(mappedClassBounds, pt => resultType(mapClassParams(_, pt))) + else PolyType(ctnames)(mappedClassBounds, pt => resultType(mapClassParams(_, pt))) } } @@ -233,7 +233,7 @@ trait FullParameterization { fun.appliedToArgss(originalDef.vparamss.nestedMap(vparam => ref(vparam.symbol))) else { // this type could have changed on forwarding. Need to insert a cast. - val args = (originalDef.vparamss, fun.tpe.paramTypess).zipped.map((vparams, paramTypes) => + val args = (originalDef.vparamss, fun.tpe.paramInfoss).zipped.map((vparams, paramTypes) => (vparams, paramTypes).zipped.map((vparam, paramType) => { assert(vparam.tpe <:< paramType.widen) // type should still conform to widened type ref(vparam.symbol).ensureConforms(paramType) @@ -256,7 +256,7 @@ object FullParameterization { case MethodTpe(nme.SELF :: Nil, _, restpe) => restpe.ensureMethodic.signature case info @ MethodTpe(nme.SELF :: otherNames, thisType :: otherTypes, restpe) => - info.derivedMethodType(otherNames, otherTypes, restpe).signature + info.derivedLambdaType(otherNames, otherTypes, restpe).signature case _ => Signature.NotAMethod } diff --git a/compiler/src/dotty/tools/dotc/transform/FunctionalInterfaces.scala b/compiler/src/dotty/tools/dotc/transform/FunctionalInterfaces.scala index 5fd89314a..bde902152 100644 --- a/compiler/src/dotty/tools/dotc/transform/FunctionalInterfaces.scala +++ b/compiler/src/dotty/tools/dotc/transform/FunctionalInterfaces.scala @@ -31,8 +31,8 @@ class FunctionalInterfaces extends MiniPhaseTransform { val maxArgsCount = 2 def shouldSpecialize(m: MethodType)(implicit ctx: Context) = - (m.paramTypes.size <= maxArgsCount) && - m.paramTypes.forall(x => allowedArgumentTypes.contains(x.typeSymbol)) && + (m.paramInfos.size <= maxArgsCount) && + m.paramInfos.forall(x => allowedArgumentTypes.contains(x.typeSymbol)) && allowedReturnTypes.contains(m.resultType.typeSymbol) val functionName = "JFunction".toTermName @@ -67,7 +67,7 @@ class FunctionalInterfaces extends MiniPhaseTransform { val names = ctx.atPhase(ctx.erasurePhase) { implicit ctx => functionSymbol.typeParams.map(_.name) } - val interfaceName = (functionName ++ m.paramTypes.length.toString).specializedFor(m.paramTypes ::: m.resultType :: Nil, names, Nil, Nil) + val interfaceName = (functionName ++ m.paramInfos.length.toString).specializedFor(m.paramInfos ::: m.resultType :: Nil, names, Nil, Nil) // symbols loaded from classpath aren't defined in periods earlier than when they where loaded val interface = ctx.withPhase(ctx.typerPhase).getClassIfDefined(functionPackage ++ interfaceName) diff --git a/compiler/src/dotty/tools/dotc/transform/MixinOps.scala b/compiler/src/dotty/tools/dotc/transform/MixinOps.scala index 9f7ceeaed..d91522c25 100644 --- a/compiler/src/dotty/tools/dotc/transform/MixinOps.scala +++ b/compiler/src/dotty/tools/dotc/transform/MixinOps.scala @@ -72,7 +72,7 @@ class MixinOps(cls: ClassSymbol, thisTransform: DenotTransformer)(implicit ctx: def hasPrimitiveMissMatch(tp1: Type, tp2: Type): Boolean = (tp1, tp2) match { case (tp1: MethodicType, tp2: MethodicType) => hasPrimitiveMissMatch(tp1.resultType, tp2.resultType) || - tp1.paramTypess.flatten.zip(tp1.paramTypess.flatten).exists(args => hasPrimitiveMissMatch(args._1, args._2)) + tp1.paramInfoss.flatten.zip(tp1.paramInfoss.flatten).exists(args => hasPrimitiveMissMatch(args._1, args._2)) case _ => def isPrimitiveOrValueClass(sym: Symbol): Boolean = sym.isPrimitiveValueClass || sym.isValueClass isPrimitiveOrValueClass(tp1.typeSymbol) ^ isPrimitiveOrValueClass(tp2.typeSymbol) diff --git a/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala b/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala index 197d18374..dbc7666f7 100644 --- a/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala +++ b/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala @@ -1766,7 +1766,7 @@ class PatternMatcher extends MiniPhaseTransform with DenotTransformer { def applyMethodTypes(method: Type): Extractor = { val whole = method.finalResultType - method.paramTypess.head match { + method.paramInfoss.head match { case init :+ last if last.isRepeatedParam => newExtractor(whole, init, repeatedFromVarargs(last)) case tps => newExtractor(whole, tps, NoRepeated) } diff --git a/compiler/src/dotty/tools/dotc/transform/ShortcutImplicits.scala b/compiler/src/dotty/tools/dotc/transform/ShortcutImplicits.scala index b5469610f..1a530b95c 100644 --- a/compiler/src/dotty/tools/dotc/transform/ShortcutImplicits.scala +++ b/compiler/src/dotty/tools/dotc/transform/ShortcutImplicits.scala @@ -82,8 +82,8 @@ class ShortcutImplicits extends MiniPhase with IdentityDenotTransformer { thisTr * @return The type of the `apply` member of `implicit Ts => R`. */ private def directInfo(info: Type)(implicit ctx: Context): Type = info match { - case info: PolyType => info.derivedPolyType(resType = directInfo(info.resultType)) - case info: MethodType => info.derivedMethodType(resType = directInfo(info.resultType)) + case info: PolyType => info.derivedLambdaType(resType = directInfo(info.resultType)) + case info: MethodType => info.derivedLambdaType(resType = directInfo(info.resultType)) case info: ExprType => directInfo(info.resultType) case info => info.member(nme.apply).info } diff --git a/compiler/src/dotty/tools/dotc/transform/SuperAccessors.scala b/compiler/src/dotty/tools/dotc/transform/SuperAccessors.scala index 3c11827fc..728c1696b 100644 --- a/compiler/src/dotty/tools/dotc/transform/SuperAccessors.scala +++ b/compiler/src/dotty/tools/dotc/transform/SuperAccessors.scala @@ -178,9 +178,9 @@ class SuperAccessors(thisTransformer: DenotTransformer) { val accType = { def accTypeOf(tpe: Type): Type = tpe match { case tpe: PolyType => - tpe.derivedPolyType(tpe.paramNames, tpe.paramBounds, accTypeOf(tpe.resultType)) + tpe.derivedLambdaType(tpe.paramNames, tpe.paramInfos, accTypeOf(tpe.resultType)) case _ => - MethodType(receiverType :: Nil)(mt => tpe.substThis(sym.owner.asClass, MethodParam(mt, 0))) + MethodType(receiverType :: Nil)(mt => tpe.substThis(sym.owner.asClass, mt.newParamRef(0))) } accTypeOf(sym.info) } @@ -230,9 +230,9 @@ class SuperAccessors(thisTransformer: DenotTransformer) { else clazz.classInfo.selfType def accTypeOf(tpe: Type): Type = tpe match { case tpe: PolyType => - tpe.derivedPolyType(tpe.paramNames, tpe.paramBounds, accTypeOf(tpe.resultType)) + tpe.derivedLambdaType(tpe.paramNames, tpe.paramInfos, accTypeOf(tpe.resultType)) case _ => - MethodType(receiverType :: Nil)(mt => tpe.substThis(sym.owner.asClass, MethodParam(mt, 0))) + MethodType(receiverType :: Nil)(mt => tpe.substThis(sym.owner.asClass, mt.newParamRef(0))) } val accType = accTypeOf(sym.info) val protectedAccessor = clazz.info.decl(accName).suchThat(_.signature == accType.signature).symbol orElse { diff --git a/compiler/src/dotty/tools/dotc/transform/TailRec.scala b/compiler/src/dotty/tools/dotc/transform/TailRec.scala index aa0845605..8a695bf22 100644 --- a/compiler/src/dotty/tools/dotc/transform/TailRec.scala +++ b/compiler/src/dotty/tools/dotc/transform/TailRec.scala @@ -325,6 +325,10 @@ class TailRec extends MiniPhaseTransform with DenotTransformer with FullParamete else rewriteApply(tree, meth) + case TypeApply(fun, targs) => + val meth = fun.symbol + rewriteApply(tree, meth) + case tree@Block(stats, expr) => tpd.cpy.Block(tree)( noTailTransforms(stats), diff --git a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala index dd4d95257..ebb5b605b 100644 --- a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala +++ b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala @@ -287,7 +287,7 @@ class TreeChecker extends Phase with SymTransformer { res } - /** Check that PolyParams and MethodParams refer to an enclosing type */ + /** Check that TypeParamRefs and MethodParams refer to an enclosing type */ def checkNoOrphans(tp: Type)(implicit ctx: Context) = new TypeMap() { val definedBinders = mutable.Set[Type]() def apply(tp: Type): Type = { @@ -296,7 +296,7 @@ class TreeChecker extends Phase with SymTransformer { definedBinders += tp mapOver(tp) definedBinders -= tp - case tp: ParamType => + case tp: ParamRef => assert(definedBinders.contains(tp.binder), s"orphan param: $tp") case tp: TypeVar => apply(tp.underlying) diff --git a/compiler/src/dotty/tools/dotc/transform/VCInlineMethods.scala b/compiler/src/dotty/tools/dotc/transform/VCInlineMethods.scala index ddd414417..ab24e1720 100644 --- a/compiler/src/dotty/tools/dotc/transform/VCInlineMethods.scala +++ b/compiler/src/dotty/tools/dotc/transform/VCInlineMethods.scala @@ -86,7 +86,7 @@ class VCInlineMethods extends MiniPhaseTransform with IdentityDenotTransformer { * by a call to the corresponding extension method, otherwise return it as is. */ private def rewireIfNeeded(tree: Tree)(implicit ctx: Context) = tree.tpe.widen match { - case tp: MethodOrPoly => + case tp: LambdaType => tree // The rewiring will be handled by a fully-applied parent node case _ => if (isMethodWithExtension(tree.symbol)) diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index 5dcf16b62..4e43e429b 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -34,7 +34,7 @@ object Applications { def extractorMember(tp: Type, name: Name)(implicit ctx: Context) = { def isPossibleExtractorType(tp: Type) = tp match { - case _: MethodType | _: PolyType => false + case _: MethodOrPoly => false case _ => true } tp.member(name).suchThat(d => isPossibleExtractorType(d.info)) @@ -195,7 +195,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic => } /** The function's type after widening and instantiating polytypes - * with polyparams in constraint set + * with TypeParamRefs in constraint set */ val methType = funType.widen match { case funType: MethodType => funType @@ -217,7 +217,14 @@ trait Applications extends Compatibility { self: Typer with Dynamic => // apply the result type constraint, unless method type is dependent val resultApprox = resultTypeApprox(methType) val savedConstraint = ctx.typerState.constraint - if (!constrainResult(resultApprox, resultType)) + if (!resultApprox.isInstanceOf[PolyType] && + // temporary fix before #2121 is in. The problem here is that errors in the code + // can lead to the result type begin higher-kinded. Then normalize gets confused + // and we end up with an assertion violation "s"inconsistent: no typevars were + // added to committable constraint". Once we distinguish between type lambdas + // and polytypes again this should hopefully become unnecessary. The error + // was triggered by neg/enums.scala. + !constrainResult(resultApprox, resultType)) if (ctx.typerState.isCommittable) // defer the problem until after the application; // it might be healed by an implicit conversion @@ -225,7 +232,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic => else fail(err.typeMismatchMsg(methType.resultType, resultType)) // match all arguments with corresponding formal parameters - matchArgs(orderedArgs, methType.paramTypes, 0) + matchArgs(orderedArgs, methType.paramInfos, 0) case _ => if (methType.isError) ok = false else fail(s"$methString does not take parameters") @@ -388,7 +395,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic => def addTyped(arg: Arg, formal: Type): Type => Type = { addArg(typedArg(arg, formal), formal) if (methodType.isParamDependent) - _.substParam(MethodParam(methodType, n), typeOfArg(arg)) + _.substParam(methodType.newParamRef(n), typeOfArg(arg)) else identity } @@ -756,7 +763,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic => val typedFn = typedExpr(tree.fun, PolyProto(typedArgs.tpes, pt)) typedFn.tpe.widen match { case pt: PolyType => - if (typedArgs.length <= pt.paramBounds.length && !isNamed) + if (typedArgs.length <= pt.paramInfos.length && !isNamed) if (typedFn.symbol == defn.Predef_classOf && typedArgs.nonEmpty) { val arg = typedArgs.head checkClassType(arg.tpe, arg.pos, traitReq = false, stablePrefixReq = false) @@ -876,8 +883,8 @@ trait Applications extends Compatibility { self: Typer with Dynamic => } unapplyFn.tpe.widen match { - case mt: MethodType if mt.paramTypes.length == 1 => - val unapplyArgType = mt.paramTypes.head + case mt: MethodType if mt.paramInfos.length == 1 => + val unapplyArgType = mt.paramInfos.head unapp.println(i"unapp arg tpe = $unapplyArgType, pt = $selType") val ownType = if (selType <:< unapplyArgType) { @@ -1049,17 +1056,17 @@ trait Applications extends Compatibility { self: Typer with Dynamic => case _ => if (tp.isRepeatedParam) tp.argTypesHi.head else tp } val formals1 = - if (tp1.isVarArgsMethod && tp2.isVarArgsMethod) tp1.paramTypes map repeatedToSingle - else tp1.paramTypes + if (tp1.isVarArgsMethod && tp2.isVarArgsMethod) tp1.paramInfos map repeatedToSingle + else tp1.paramInfos isApplicable(alt2, formals1, WildcardType) || - tp1.paramTypes.isEmpty && tp2.isInstanceOf[MethodOrPoly] + tp1.paramInfos.isEmpty && tp2.isInstanceOf[LambdaType] case tp1: PolyType => // (2) val tparams = ctx.newTypeParams(alt1.symbol, tp1.paramNames, EmptyFlags, tp1.instantiateBounds) isAsSpecific(alt1, tp1.instantiate(tparams map (_.typeRef)), alt2, tp2) case _ => // (3) tp2 match { case tp2: MethodType => true // (3a) - case tp2: PolyType if tp2.isPolymorphicMethodType => true // (3a) + case tp2: PolyType if tp2.resultType.isInstanceOf[MethodType] => true // (3a) case tp2: PolyType => // (3b) val nestedCtx = ctx.fresh.setExploreTyperState @@ -1118,7 +1125,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic => case mt: ImplicitMethodType => resultTypeApprox(mt) case pt: PolyType => - pt.derivedPolyType(pt.paramNames, pt.paramBounds, stripImplicit(pt.resultType)) + pt.derivedLambdaType(pt.paramNames, pt.paramInfos, stripImplicit(pt.resultType)) case _ => tp } @@ -1277,10 +1284,9 @@ trait Applications extends Compatibility { self: Typer with Dynamic => case x => x } - def sizeFits(alt: TermRef, tp: Type): Boolean = tp match { - case tp: PolyType => sizeFits(alt, tp.resultType) + def sizeFits(alt: TermRef, tp: Type): Boolean = tp.stripPoly match { case tp: MethodType => - val ptypes = tp.paramTypes + val ptypes = tp.paramInfos val numParams = ptypes.length def isVarArgs = ptypes.nonEmpty && ptypes.last.isRepeatedParam def hasDefault = alt.symbol.hasDefaultParams @@ -1405,12 +1411,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic => recur(altFormals.map(_.tail), args1) case _ => } - def paramTypes(alt: Type): List[Type] = alt match { - case mt: MethodType => mt.paramTypes - case mt: PolyType => paramTypes(mt.resultType) - case _ => Nil - } - recur(alts.map(alt => paramTypes(alt.widen)), pt.args) + recur(alts.map(_.widen.firstParamTypes), pt.args) } private def harmonizeWith[T <: AnyRef](ts: List[T])(tpe: T => Type, adapt: (T, Type) => T)(implicit ctx: Context): List[T] = { diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index b43391592..5d1c44efc 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -50,12 +50,12 @@ object Checking { arg.pos.focus) } - /** Check that type arguments `args` conform to corresponding bounds in `poly` + /** Check that type arguments `args` conform to corresponding bounds in `tl` * Note: This does not check the bounds of AppliedTypeTrees. These * are handled by method checkBounds in FirstTransform */ - def checkBounds(args: List[tpd.Tree], poly: PolyType)(implicit ctx: Context): Unit = - checkBounds(args, poly.paramBounds, _.substParams(poly, _)) + def checkBounds(args: List[tpd.Tree], tl: TypeLambda)(implicit ctx: Context): Unit = + checkBounds(args, tl.paramInfos, _.substParams(tl, _)) /** Check applied type trees for well-formedness. This means * - all arguments are within their corresponding bounds @@ -69,20 +69,20 @@ object Checking { // If `args` is a list of named arguments, return corresponding type parameters, // otherwise return type parameters unchanged val tparams = tycon.tpe.typeParams - def argNamed(tparam: TypeParamInfo) = args.find { + def argNamed(tparam: ParamInfo) = args.find { case NamedArg(name, _) => name == tparam.paramName case _ => false }.getOrElse(TypeTree(tparam.paramRef)) val orderedArgs = if (hasNamedArg(args)) tparams.map(argNamed) else args - val bounds = tparams.map(_.paramBoundsAsSeenFrom(tycon.tpe)) + val bounds = tparams.map(_.paramInfoAsSeenFrom(tycon.tpe).bounds) def instantiate(bound: Type, args: List[Type]) = - bound.LambdaAbstract(tparams).appliedTo(args) + HKTypeLambda.fromParams(tparams, bound).appliedTo(args) checkBounds(orderedArgs, bounds, instantiate) def checkWildcardHKApply(tp: Type, pos: Position): Unit = tp match { case tp @ HKApply(tycon, args) if args.exists(_.isInstanceOf[TypeBounds]) => tycon match { - case tycon: PolyType => + case tycon: TypeLambda => ctx.errorOrMigrationWarning( ex"unreducible application of higher-kinded type $tycon to wildcard arguments", pos) @@ -348,12 +348,23 @@ object Checking { /** Check the type signature of the symbol `M` defined by `tree` does not refer * to a private type or value which is invisible at a point where `M` is still - * visible. As an exception, we allow references to type aliases if the underlying - * type of the alias is not a leak. So type aliases are transparent as far as - * leak testing is concerned. + * visible. + * + * As an exception, we allow references to type aliases if the underlying + * type of the alias is not a leak, and if `sym` is not a type. The rationale + * for this is that the inferred type of a term symbol might contain leaky + * aliases which should be removed (see leak-inferred.scala for an example), + * but a type symbol definition will not contain leaky aliases unless the + * user wrote them, so we can ask the user to change his definition. The more + * practical reason for not transforming types is that `checkNoPrivateLeaks` + * can force a lot of denotations, and this restriction means that we never + * need to run `TypeAssigner#avoidPrivateLeaks` on type symbols when + * unpickling, which avoids some issues related to forcing order. + * + * See i997.scala for negative tests, and i1130.scala for a case where it + * matters that we transform leaky aliases away. + * * @return The `info` of `sym`, with problematic aliases expanded away. - * See i997.scala for tests, i1130.scala for a case where it matters that we - * transform leaky aliases away. */ def checkNoPrivateLeaks(sym: Symbol, pos: Position)(implicit ctx: Context): Type = { class NotPrivate extends TypeMap { @@ -388,7 +399,7 @@ object Checking { tp } else mapOver(tp) - if ((errors ne prevErrors) && tp.info.isAlias) { + if ((errors ne prevErrors) && !sym.isType && tp.info.isAlias) { // try to dealias to avoid a leak error val savedErrors = errors errors = prevErrors @@ -400,8 +411,14 @@ object Checking { case tp: ClassInfo => tp.derivedClassInfo( prefix = apply(tp.prefix), - classParents = tp.parentsWithArgs.map(p => - apply(p).underlyingClassRef(refinementOK = false).asInstanceOf[TypeRef])) + classParents = + tp.parentsWithArgs.map { p => + apply(p).underlyingClassRef(refinementOK = false) match { + case ref: TypeRef => ref + case _ => defn.ObjectType // can happen if class files are missing + } + } + ) case _ => mapOver(tp) } diff --git a/compiler/src/dotty/tools/dotc/typer/Dynamic.scala b/compiler/src/dotty/tools/dotc/typer/Dynamic.scala index 000cfd026..25fca546e 100644 --- a/compiler/src/dotty/tools/dotc/typer/Dynamic.scala +++ b/compiler/src/dotty/tools/dotc/typer/Dynamic.scala @@ -150,7 +150,7 @@ trait Dynamic { self: Typer with Applications => |Structural types only support methods taking up to ${Definitions.MaxStructuralMethodArity} arguments""") else { def issueError(msgFn: String => String): Unit = ctx.error(msgFn(""), tree.pos) - val ctags = tpe.paramTypes.map(pt => + val ctags = tpe.paramInfos.map(pt => inferImplicitArg(defn.ClassTagType.appliedTo(pt :: Nil), issueError, tree.pos.endPos)) structuralCall(nme.selectDynamicMethod, ctags).asInstance(tpe.toFunctionType()) } diff --git a/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala b/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala index a1690955f..6c72c16e0 100644 --- a/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala +++ b/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala @@ -46,7 +46,7 @@ object ErrorReporting { errorMsg(ex.toMessage, ctx) } - def wrongNumberOfTypeArgs(fntpe: Type, expectedArgs: List[TypeParamInfo], actual: List[untpd.Tree], pos: Position)(implicit ctx: Context) = + def wrongNumberOfTypeArgs(fntpe: Type, expectedArgs: List[ParamInfo], actual: List[untpd.Tree], pos: Position)(implicit ctx: Context) = errorType(WrongNumberOfTypeArgs(fntpe, expectedArgs, actual)(ctx), pos) class Errors(implicit ctx: Context) { @@ -74,7 +74,7 @@ object ErrorReporting { def anonymousTypeMemberStr(tpe: Type) = { val kind = tpe match { case _: TypeBounds => "type with bounds" - case _: PolyType | _: MethodType => "method" + case _: MethodOrPoly => "method" case _ => "value of type" } em"$kind $tpe" @@ -105,9 +105,9 @@ object ErrorReporting { def whyNoMatchStr(found: Type, expected: Type) = { def dropJavaMethod(tp: Type): Type = tp match { case tp: PolyType => - tp.derivedPolyType(resType = dropJavaMethod(tp.resultType)) + tp.derivedLambdaType(resType = dropJavaMethod(tp.resultType)) case tp: JavaMethodType => - MethodType(tp.paramNames, tp.paramTypes, dropJavaMethod(tp.resultType)) + MethodType(tp.paramNames, tp.paramInfos, dropJavaMethod(tp.resultType)) case tp => tp } val found1 = dropJavaMethod(found) @@ -121,12 +121,12 @@ object ErrorReporting { } def typeMismatchMsg(found: Type, expected: Type, postScript: String = "") = { - // replace constrained polyparams and their typevars by their bounds where possible + // replace constrained TypeParamRefs and their typevars by their bounds where possible object reported extends TypeMap { def setVariance(v: Int) = variance = v val constraint = ctx.typerState.constraint def apply(tp: Type): Type = tp match { - case tp: PolyParam => + case tp: TypeParamRef => constraint.entry(tp) match { case bounds: TypeBounds => if (variance < 0) apply(constraint.fullUpperBound(tp)) diff --git a/compiler/src/dotty/tools/dotc/typer/EtaExpansion.scala b/compiler/src/dotty/tools/dotc/typer/EtaExpansion.scala index 57c1808c9..5aee0fd54 100644 --- a/compiler/src/dotty/tools/dotc/typer/EtaExpansion.scala +++ b/compiler/src/dotty/tools/dotc/typer/EtaExpansion.scala @@ -60,7 +60,7 @@ object EtaExpansion { def liftArgs(defs: mutable.ListBuffer[Tree], methRef: Type, args: List[Tree])(implicit ctx: Context) = methRef.widen match { case mt: MethodType => - (args, mt.paramNames, mt.paramTypes).zipped map { (arg, name, tp) => + (args, mt.paramNames, mt.paramInfos).zipped map { (arg, name, tp) => if (tp.isInstanceOf[ExprType]) arg else liftArg(defs, arg, if (name contains '$') "" else name.toString + "$") } @@ -135,12 +135,12 @@ object EtaExpansion { val defs = new mutable.ListBuffer[tpd.Tree] val lifted: Tree = TypedSplice(liftApp(defs, tree)) val paramTypes: List[Tree] = - if (mt.paramTypes.length == xarity) mt.paramTypes map (_ => TypeTree()) - else mt.paramTypes map TypeTree + if (mt.paramInfos.length == xarity) mt.paramInfos map (_ => TypeTree()) + else mt.paramInfos map TypeTree val params = (mt.paramNames, paramTypes).zipped.map((name, tpe) => ValDef(name, tpe, EmptyTree).withFlags(Synthetic | Param).withPos(tree.pos)) var ids: List[Tree] = mt.paramNames map (name => Ident(name).withPos(tree.pos)) - if (mt.paramTypes.nonEmpty && mt.paramTypes.last.isRepeatedParam) + if (mt.paramInfos.nonEmpty && mt.paramInfos.last.isRepeatedParam) ids = ids.init :+ repeated(ids.last) var body: Tree = Apply(lifted, ids) mt.resultType match { diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 681045cc4..6c0be51df 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -66,16 +66,16 @@ object Implicits { def discardForView(tpw: Type, argType: Type): Boolean = tpw match { case mt: MethodType => mt.isImplicit || - mt.paramTypes.length != 1 || - !(argType relaxed_<:< mt.paramTypes.head)(ctx.fresh.setExploreTyperState) + mt.paramInfos.length != 1 || + !(argType relaxed_<:< mt.paramInfos.head)(ctx.fresh.setExploreTyperState) case poly: PolyType => // We do not need to call ProtoTypes#constrained on `poly` because // `refMatches` is always called with mode TypevarsMissContext enabled. poly.resultType match { case mt: MethodType => mt.isImplicit || - mt.paramTypes.length != 1 || - !(argType relaxed_<:< wildApprox(mt.paramTypes.head, null, Set.empty)(ctx.fresh.setExploreTyperState)) + mt.paramInfos.length != 1 || + !(argType relaxed_<:< wildApprox(mt.paramInfos.head, null, Set.empty)(ctx.fresh.setExploreTyperState)) case rtp => discardForView(wildApprox(rtp, null, Set.empty), argType) } @@ -107,9 +107,8 @@ object Implicits { !(isFunctionInS2 || isImplicitConverter || isConforms) } - def discardForValueType(tpw: Type): Boolean = tpw match { - case mt: MethodType => !mt.isImplicit - case mt: PolyType => discardForValueType(tpw.resultType) + def discardForValueType(tpw: Type): Boolean = tpw.stripPoly match { + case tpw: MethodType => !tpw.isImplicit case _ => false } @@ -387,7 +386,7 @@ trait ImplicitRunInfo { self: RunInfo => case _ => arg } (apply(tp.tycon) /: tp.args)((tc, arg) => AndType.make(tc, applyArg(arg))) - case tp: PolyType => + case tp: TypeLambda => apply(tp.resType) case _ => mapOver(tp) diff --git a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala index 86649d78e..632dbff76 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala @@ -119,11 +119,11 @@ object Inferencing { } } - /** If `tree` has a PolyType, infer its type parameters by comparing with expected type `pt` */ + /** If `tree` has a type lambda type, infer its type parameters by comparing with expected type `pt` */ def inferTypeParams(tree: Tree, pt: Type)(implicit ctx: Context): Tree = tree.tpe match { - case poly: PolyType => - val (poly1, tvars) = constrained(poly, tree) - val tree1 = tree.withType(poly1).appliedToTypeTrees(tvars) + case tl: TypeLambda => + val (tl1, tvars) = constrained(tl, tree) + val tree1 = tree.withType(tl1).appliedToTypeTrees(tvars) tree1.tpe <:< pt fullyDefinedType(tree1.tpe, "template parent", tree.pos) tree1 @@ -157,7 +157,7 @@ object Inferencing { case Apply(fn, _) => fn.tpe.widen match { case mtp: MethodType => - val (occ, nocc) = toTest.partition(tvar => mtp.paramTypes.exists(tvar.occursIn)) + val (occ, nocc) = toTest.partition(tvar => mtp.paramInfos.exists(tvar.occursIn)) occurring(fn, nocc, occ ::: acc) case _ => occurring(fn, toTest, acc) @@ -176,9 +176,9 @@ object Inferencing { * -1 (minimize) if constraint is uniformly from below, * 0 if unconstrained, or constraint is from below and above. */ - private def instDirection(param: PolyParam)(implicit ctx: Context): Int = { + private def instDirection(param: TypeParamRef)(implicit ctx: Context): Int = { val constrained = ctx.typerState.constraint.fullBounds(param) - val original = param.binder.paramBounds(param.paramNum) + val original = param.binder.paramInfos(param.paramNum) val cmp = ctx.typeComparer val approxBelow = if (!cmp.isSubTypeWhenFrozen(constrained.lo, original.lo)) 1 else 0 diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index 6e90367c8..f6d65fbb9 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -108,7 +108,7 @@ object Inliner { // Add qualifier type as leading method argument to argument `tp` def addQualType(tp: Type): Type = tp match { - case tp: PolyType => tp.derivedPolyType(tp.paramNames, tp.paramBounds, addQualType(tp.resultType)) + case tp: PolyType => tp.derivedLambdaType(tp.paramNames, tp.paramInfos, addQualType(tp.resultType)) case tp: ExprType => addQualType(tp.resultType) case tp => MethodType(qualType :: Nil, tp) } @@ -120,7 +120,8 @@ object Inliner { // Abstract accessed type over local refs def abstractQualType(mtpe: Type): Type = if (localRefs.isEmpty) mtpe - else mtpe.LambdaAbstract(localRefs.map(_.symbol)).asInstanceOf[PolyType].flatten + else PolyType.fromParams(localRefs.map(_.symbol.asType), mtpe) + .asInstanceOf[PolyType].flatten val accessorType = abstractQualType(addQualType(dealiasMap(accessedType))) val accessor = accessorSymbol(tree, accessorType).asTerm @@ -327,7 +328,7 @@ class Inliner(call: tpd.Tree, rhs: tpd.Tree)(implicit ctx: Context) { } computeParamBindings(tp.resultType, Nil, argss) case tp: MethodType => - (tp.paramNames, tp.paramTypes, argss.head).zipped.foreach { (name, paramtp, arg) => + (tp.paramNames, tp.paramInfos, argss.head).zipped.foreach { (name, paramtp, arg) => def isByName = paramtp.dealias.isInstanceOf[ExprType] paramBinding(name) = arg.tpe.stripAnnots.stripTypeVar match { case argtpe: SingletonType if isIdempotentExpr(arg) => argtpe @@ -358,7 +359,7 @@ class Inliner(call: tpd.Tree, rhs: tpd.Tree)(implicit ctx: Context) { * 2. If given type refers to a parameter, make `paramProxy` refer to the entry stored * in `paramNames` under the parameter's name. This roundabout way to bind parameter * references to proxies is done because we not known a priori what the parameter - * references of a method are (we only know the method's type, but that contains PolyParams + * references of a method are (we only know the method's type, but that contains TypeParamRefs * and MethodParams, not TypeRefs or TermRefs. */ private def registerType(tpe: Type): Unit = tpe match { diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index 94506f318..ce2030c01 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -113,9 +113,9 @@ trait NamerContextOps { this: Context => if (isJava) for (param <- params) if (param.info.isDirectRef(defn.ObjectClass)) param.info = defn.AnyType - make.fromSymbols(params, resultType) + make.fromSymbols(params.asInstanceOf[List[TermSymbol]], resultType) } - if (typeParams.nonEmpty) monotpe.LambdaAbstract(typeParams) + if (typeParams.nonEmpty) PolyType.fromParams(typeParams.asInstanceOf[List[TypeSymbol]], monotpe) else if (valueParamss.isEmpty) ExprType(monotpe) else monotpe } @@ -298,7 +298,7 @@ class Namer { typer: Typer => val inSuperCall1 = if (tree.mods is ParamOrAccessor) EmptyFlags else inSuperCall // suppress inSuperCall for constructor parameters val higherKinded = tree match { - case TypeDef(_, PolyTypeTree(_, _)) if isDeferred => HigherKinded + case TypeDef(_, LambdaTypeTree(_, _)) if isDeferred => HigherKinded case _ => EmptyFlags } @@ -564,7 +564,6 @@ class Namer { typer: Typer => case _ => } case _ => - } } @@ -796,7 +795,7 @@ class Namer { typer: Typer => myTypeParams = { implicit val ctx = nestedCtx val tparams = original.rhs match { - case PolyTypeTree(tparams, _) => tparams + case LambdaTypeTree(tparams, _) => tparams case _ => Nil } completeParams(tparams) @@ -1033,7 +1032,7 @@ class Namer { typer: Typer => } val defaultAlts = meth.altsWith(_.hasDefaultParams) if (defaultAlts.length == 1) - paramProto(defaultAlts.head.info.widen.paramTypess, idx) + paramProto(defaultAlts.head.info.widen.paramInfoss, idx) else WildcardType } @@ -1152,9 +1151,7 @@ class Namer { typer: Typer => } def typeDefSig(tdef: TypeDef, sym: Symbol, tparamSyms: List[TypeSymbol])(implicit ctx: Context): Type = { - def abstracted(tp: Type): Type = - if (tparamSyms.nonEmpty) tp.LambdaAbstract(tparamSyms) else tp - + def abstracted(tp: Type): Type = HKTypeLambda.fromParams(tparamSyms, tp) val dummyInfo = abstracted(TypeBounds.empty) sym.info = dummyInfo // Temporarily set info of defined type T to ` >: Nothing <: Any. @@ -1170,7 +1167,7 @@ class Namer { typer: Typer => val isDerived = tdef.rhs.isInstanceOf[untpd.DerivedTypeTree] val rhs = tdef.rhs match { - case PolyTypeTree(_, body) => body + case LambdaTypeTree(_, body) => body case rhs => rhs } val rhsBodyType = typedAheadType(rhs).tpe diff --git a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala index f96b8ae1d..ab342dc17 100644 --- a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala +++ b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala @@ -192,7 +192,7 @@ object ProtoTypes { /** Forget the types of any arguments that have been typed producing a constraint in a * typer state that is not yet committed into the one of the current context `ctx`. - * This is necessary to avoid "orphan" PolyParams that are referred to from + * This is necessary to avoid "orphan" TypeParamRefs that are referred to from * type variables in the typed arguments, but that are not registered in the * current constraint. A test case is pos/t1756.scala. * @return True if all arguments have types (in particular, no types were forgotten). @@ -369,53 +369,53 @@ object ProtoTypes { /** A prototype for type constructors that are followed by a type application */ @sharable object AnyTypeConstructorProto extends UncachedGroundType with MatchAlways - /** Add all parameters in given polytype `pt` to the constraint's domain. + /** Add all parameters of given type lambda `tl` to the constraint's domain. * If the constraint contains already some of these parameters in its domain, - * make a copy of the polytype and add the copy's type parameters instead. - * Return either the original polytype, or the copy, if one was made. + * make a copy of the type lambda and add the copy's type parameters instead. + * Return either the original type lambda, or the copy, if one was made. * Also, if `owningTree` is non-empty, add a type variable for each parameter. - * @return The added polytype, and the list of created type variables. + * @return The added type lambda, and the list of created type variables. */ - def constrained(pt: PolyType, owningTree: untpd.Tree)(implicit ctx: Context): (PolyType, List[TypeTree]) = { + def constrained(tl: TypeLambda, owningTree: untpd.Tree)(implicit ctx: Context): (TypeLambda, List[TypeTree]) = { val state = ctx.typerState assert(!(ctx.typerState.isCommittable && owningTree.isEmpty), s"inconsistent: no typevars were added to committable constraint ${state.constraint}") - def newTypeVars(pt: PolyType): List[TypeTree] = - for (n <- (0 until pt.paramNames.length).toList) + def newTypeVars(tl: TypeLambda): List[TypeTree] = + for (n <- (0 until tl.paramNames.length).toList) yield { val tt = new TypeTree().withPos(owningTree.pos) - tt.withType(new TypeVar(PolyParam(pt, n), state, tt, ctx.owner)) + tt.withType(new TypeVar(TypeParamRef(tl, n), state, tt, ctx.owner)) } val added = - if (state.constraint contains pt) pt.newLikeThis(pt.paramNames, pt.paramBounds, pt.resultType) - else pt + if (state.constraint contains tl) tl.newLikeThis(tl.paramNames, tl.paramInfos, tl.resultType) + else tl val tvars = if (owningTree.isEmpty) Nil else newTypeVars(added) ctx.typeComparer.addToConstraint(added, tvars.tpes.asInstanceOf[List[TypeVar]]) (added, tvars) } - /** Same as `constrained(pt, EmptyTree)`, but returns just the created polytype */ - def constrained(pt: PolyType)(implicit ctx: Context): PolyType = constrained(pt, EmptyTree)._1 + /** Same as `constrained(tl, EmptyTree)`, but returns just the created type lambda */ + def constrained(tl: TypeLambda)(implicit ctx: Context): TypeLambda = constrained(tl, EmptyTree)._1 - /** Create a new polyparam that represents a dependent method parameter singleton */ - def newDepPolyParam(tp: Type)(implicit ctx: Context): PolyParam = { - val poly = PolyType(ctx.freshName(nme.DEP_PARAM_PREFIX).toTypeName :: Nil, 0 :: Nil)( + /** Create a new TypeParamRef that represents a dependent method parameter singleton */ + def newDepTypeParamRef(tp: Type)(implicit ctx: Context): TypeParamRef = { + val poly = PolyType(ctx.freshName(nme.DEP_PARAM_PREFIX).toTypeName :: Nil)( pt => TypeBounds.upper(AndType(tp, defn.SingletonType)) :: Nil, pt => defn.AnyType) ctx.typeComparer.addToConstraint(poly, Nil) - PolyParam(poly, 0) + TypeParamRef(poly, 0) } /** The result type of `mt`, where all references to parameters of `mt` are - * replaced by either wildcards (if typevarsMissContext) or polyparams. + * replaced by either wildcards (if typevarsMissContext) or TypeParamRefs. */ def resultTypeApprox(mt: MethodType)(implicit ctx: Context): Type = if (mt.isDependent) { def replacement(tp: Type) = - if (ctx.mode.is(Mode.TypevarsMissContext)) WildcardType else newDepPolyParam(tp) - mt.resultType.substParams(mt, mt.paramTypes.map(replacement)) + if (ctx.mode.is(Mode.TypevarsMissContext)) WildcardType else newDepTypeParamRef(tp) + mt.resultType.substParams(mt, mt.paramInfos.map(replacement)) } else mt.resultType @@ -444,10 +444,10 @@ object ProtoTypes { val rt = normalize(mt.resultType, pt) pt match { case pt: IgnoredProto => mt - case pt: ApplyingProto => mt.derivedMethodType(mt.paramNames, mt.paramTypes, rt) + case pt: ApplyingProto => mt.derivedLambdaType(mt.paramNames, mt.paramInfos, rt) case _ => - val ft = defn.FunctionOf(mt.paramTypes, rt) - if (mt.paramTypes.nonEmpty || ft <:< pt) ft else rt + val ft = defn.FunctionOf(mt.paramInfos, rt) + if (mt.paramInfos.nonEmpty || ft <:< pt) ft else rt } } case et: ExprType => et.resultType @@ -458,7 +458,7 @@ object ProtoTypes { /** Approximate occurrences of parameter types and uninstantiated typevars * by wildcard types. */ - final def wildApprox(tp: Type, theMap: WildApproxMap, seen: Set[PolyParam])(implicit ctx: Context): Type = tp match { + final def wildApprox(tp: Type, theMap: WildApproxMap, seen: Set[TypeParamRef])(implicit ctx: Context): Type = tp match { case tp: NamedType => // default case, inlined for speed if (tp.symbol.isStatic) tp else tp.derivedSelect(wildApprox(tp.prefix, theMap, seen)) @@ -469,13 +469,13 @@ object ProtoTypes { wildApprox(tp.refinedInfo, theMap, seen)) case tp: TypeAlias => // default case, inlined for speed tp.derivedTypeAlias(wildApprox(tp.alias, theMap, seen)) - case tp @ PolyParam(poly, pnum) => + case tp @ TypeParamRef(poly, pnum) => def wildApproxBounds(bounds: TypeBounds) = if (bounds.lo.isInstanceOf[NamedType] && bounds.hi.isInstanceOf[NamedType]) WildcardType(wildApprox(bounds, theMap, seen).bounds) else if (seen.contains(tp)) WildcardType else WildcardType(wildApprox(bounds, theMap, seen + tp).bounds) - def unconstrainedApprox = wildApproxBounds(poly.paramBounds(pnum)) + def unconstrainedApprox = wildApproxBounds(poly.paramInfos(pnum)) def approxPoly = if (ctx.mode.is(Mode.TypevarsMissContext)) unconstrainedApprox else @@ -485,8 +485,8 @@ object ProtoTypes { case inst => wildApprox(inst, theMap, seen) } approxPoly - case MethodParam(mt, pnum) => - WildcardType(TypeBounds.upper(wildApprox(mt.paramTypes(pnum), theMap, seen))) + case TermParamRef(mt, pnum) => + WildcardType(TypeBounds.upper(wildApprox(mt.paramInfos(pnum), theMap, seen))) case tp: TypeVar => wildApprox(tp.underlying, theMap, seen) case tp @ HKApply(tycon, args) => @@ -532,7 +532,7 @@ object ProtoTypes { @sharable object AssignProto extends UncachedGroundType with MatchAlways - private[ProtoTypes] class WildApproxMap(val seen: Set[PolyParam])(implicit ctx: Context) extends TypeMap { + private[ProtoTypes] class WildApproxMap(val seen: Set[TypeParamRef])(implicit ctx: Context) extends TypeMap { def apply(tp: Type) = wildApprox(tp, this, seen) } diff --git a/compiler/src/dotty/tools/dotc/typer/ReTyper.scala b/compiler/src/dotty/tools/dotc/typer/ReTyper.scala index 6080c6644..73e676b57 100644 --- a/compiler/src/dotty/tools/dotc/typer/ReTyper.scala +++ b/compiler/src/dotty/tools/dotc/typer/ReTyper.scala @@ -86,7 +86,7 @@ class ReTyper extends Typer { override def handleUnexpectedFunType(tree: untpd.Apply, fun: Tree)(implicit ctx: Context): Tree = fun.tpe match { case mt: MethodType => - val args: List[Tree] = tree.args.zipWithConserve(mt.paramTypes)(typedExpr(_, _)).asInstanceOf[List[Tree]] + val args: List[Tree] = tree.args.zipWithConserve(mt.paramInfos)(typedExpr(_, _)).asInstanceOf[List[Tree]] assignType(untpd.cpy.Apply(tree)(fun, args), fun, args) case _ => super.handleUnexpectedFunType(tree, fun) diff --git a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala index 6d0fc08f9..2aa7036b4 100644 --- a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -106,7 +106,7 @@ trait TypeAssigner { val base = apply(tycon) var args = tp.baseArgInfos(base.typeSymbol) if (base.typeParams.length != args.length) - args = base.typeParams.map(_.paramBounds) + args = base.typeParams.map(_.paramInfo) apply(base.appliedTo(args)) case tp @ RefinedType(parent, name, rinfo) if variance > 0 => val parent1 = apply(tp.parent) @@ -318,9 +318,9 @@ trait TypeAssigner { def assignType(tree: untpd.Apply, fn: Tree, args: List[Tree])(implicit ctx: Context) = { val ownType = fn.tpe.widen match { case fntpe: MethodType => - if (sameLength(fntpe.paramTypes, args) || ctx.phase.prev.relaxedTyping) fntpe.instantiate(args.tpes) + if (sameLength(fntpe.paramInfos, args) || ctx.phase.prev.relaxedTyping) fntpe.instantiate(args.tpes) else - errorType(i"wrong number of arguments for $fntpe: ${fn.tpe}, expected: ${fntpe.paramTypes.length}, found: ${args.length}", tree.pos) + errorType(i"wrong number of arguments for $fntpe: ${fn.tpe}, expected: ${fntpe.paramInfos.length}, found: ${args.length}", tree.pos) case t => errorType(i"${err.exprStr(fn)} does not take parameters", tree.pos) } @@ -329,7 +329,7 @@ trait TypeAssigner { def assignType(tree: untpd.TypeApply, fn: Tree, args: List[Tree])(implicit ctx: Context) = { val ownType = fn.tpe.widen match { - case pt: PolyType => + case pt: TypeLambda => val paramNames = pt.paramNames if (hasNamedArg(args)) { // Type arguments which are specified by name (immutable after this first loop) @@ -348,7 +348,7 @@ trait TypeAssigner { val newIndex = gapBuf.length gapBuf += idx // Re-index unassigned type arguments that remain after transformation - PolyParam(pt, newIndex) + TypeParamRef(pt, newIndex) } // Type parameters after naming assignment, conserving paramNames order @@ -358,7 +358,7 @@ trait TypeAssigner { val transform = new TypeMap { def apply(t: Type) = t match { - case PolyParam(`pt`, idx) => normArgs(idx) + case TypeParamRef(`pt`, idx) => normArgs(idx) case _ => mapOver(t) } } @@ -366,9 +366,9 @@ trait TypeAssigner { if (gapBuf.isEmpty) resultType1 else { val gaps = gapBuf.toList - pt.derivedPolyType( + pt.derivedLambdaType( gaps.map(paramNames), - gaps.map(idx => transform(pt.paramBounds(idx)).bounds), + gaps.map(idx => transform(pt.paramInfos(idx)).bounds), resultType1) } } @@ -459,8 +459,8 @@ trait TypeAssigner { tree.withType(ownType) } - def assignType(tree: untpd.PolyTypeTree, tparamDefs: List[TypeDef], body: Tree)(implicit ctx: Context) = - tree.withType(body.tpe.LambdaAbstract(tparamDefs.map(_.symbol))) + def assignType(tree: untpd.LambdaTypeTree, tparamDefs: List[TypeDef], body: Tree)(implicit ctx: Context) = + tree.withType(HKTypeLambda.fromParams(tparamDefs.map(_.symbol.asType), body.tpe)) def assignType(tree: untpd.ByNameTypeTree, result: Tree)(implicit ctx: Context) = tree.withType(ExprType(result.tpe)) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index d4a9744e4..2760ceba9 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -741,14 +741,14 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit calleeType.widen match { case mtpe: MethodType => val pos = params indexWhere (_.name == param.name) - if (pos < mtpe.paramTypes.length) { - val ptype = mtpe.paramTypes(pos) + if (pos < mtpe.paramInfos.length) { + val ptype = mtpe.paramInfos(pos) if (isFullyDefined(ptype, ForceDegree.noBottom)) return ptype } case _ => } val ofFun = - if (nme.syntheticParamNames(args.length + 1) contains param.name) + if (MethodType.syntheticParamNames(args.length + 1) contains param.name) i" of expanded function $tree" else "" @@ -1074,10 +1074,10 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit wrongNumberOfTypeArgs(tpt1.tpe, tparams, args, tree.pos) args = args.take(tparams.length) } - def typedArg(arg: untpd.Tree, tparam: TypeParamInfo) = { + def typedArg(arg: untpd.Tree, tparam: ParamInfo) = { val (desugaredArg, argPt) = if (ctx.mode is Mode.Pattern) - (if (isVarPattern(arg)) desugar.patternVar(arg) else arg, tparam.paramBounds) + (if (isVarPattern(arg)) desugar.patternVar(arg) else arg, tparam.paramInfo) else (arg, WildcardType) if (tpt1.symbol.isClass) @@ -1096,12 +1096,12 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit } } - def typedPolyTypeTree(tree: untpd.PolyTypeTree)(implicit ctx: Context): Tree = track("typedPolyTypeTree") { - val PolyTypeTree(tparams, body) = tree + def typedLambdaTypeTree(tree: untpd.LambdaTypeTree)(implicit ctx: Context): Tree = track("typedLambdaTypeTree") { + val LambdaTypeTree(tparams, body) = tree indexAndAnnotate(tparams) val tparams1 = tparams.mapconserve(typed(_).asInstanceOf[TypeDef]) val body1 = typedType(tree.body) - assignType(cpy.PolyTypeTree(tree)(tparams1, body1), tparams1, body1) + assignType(cpy.LambdaTypeTree(tree)(tparams1, body1), tparams1, body1) } def typedByNameTypeTree(tree: untpd.ByNameTypeTree)(implicit ctx: Context): ByNameTypeTree = track("typedByNameTypeTree") { @@ -1262,10 +1262,10 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit val TypeDef(name, rhs) = tdef completeAnnotations(tdef, sym) val rhs1 = tdef.rhs match { - case rhs @ PolyTypeTree(tparams, body) => + case rhs @ LambdaTypeTree(tparams, body) => val tparams1 = tparams.map(typed(_)).asInstanceOf[List[TypeDef]] val body1 = typedType(body) - assignType(cpy.PolyTypeTree(rhs)(tparams1, body1), tparams1, body1) + assignType(cpy.LambdaTypeTree(rhs)(tparams1, body1), tparams1, body1) case rhs => typedType(rhs) } @@ -1285,9 +1285,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit * @param psym Its type symbol * @param cinfo The info of its constructor */ - def maybeCall(ref: Tree, psym: Symbol, cinfo: Type): Tree = cinfo match { - case cinfo: PolyType => - maybeCall(ref, psym, cinfo.resultType) + def maybeCall(ref: Tree, psym: Symbol, cinfo: Type): Tree = cinfo.stripPoly match { case cinfo @ MethodType(Nil) if cinfo.resultType.isInstanceOf[ImplicitMethodType] => val icall = New(ref).select(nme.CONSTRUCTOR).appliedToNone typedExpr(untpd.TypedSplice(icall))(superCtx) @@ -1555,7 +1553,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit case tree: untpd.OrTypeTree => typedOrTypeTree(tree) case tree: untpd.RefinedTypeTree => typedRefinedTypeTree(tree) case tree: untpd.AppliedTypeTree => typedAppliedTypeTree(tree) - case tree: untpd.PolyTypeTree => typedPolyTypeTree(tree)(localContext(tree, NoSymbol).setNewScope) + case tree: untpd.LambdaTypeTree => typedLambdaTypeTree(tree)(localContext(tree, NoSymbol).setNewScope) case tree: untpd.ByNameTypeTree => typedByNameTypeTree(tree) case tree: untpd.TypeBoundsTree => typedTypeBoundsTree(tree) case tree: untpd.Alternative => typedAlternative(tree, pt) @@ -1741,7 +1739,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit def adapt(tree: Tree, pt: Type, original: untpd.Tree = untpd.EmptyTree)(implicit ctx: Context): Tree = /*>|>*/ track("adapt") /*<|<*/ { /*>|>*/ ctx.traceIndented(i"adapting $tree of type ${tree.tpe} to $pt", typr, show = true) /*<|<*/ { if (tree.isDef) interpolateUndetVars(tree, tree.symbol) - else if (!tree.tpe.widen.isInstanceOf[MethodOrPoly]) interpolateUndetVars(tree, NoSymbol) + else if (!tree.tpe.widen.isInstanceOf[LambdaType]) interpolateUndetVars(tree, NoSymbol) tree.overwriteType(tree.tpe.simplified) adaptInterpolated(tree, pt, original) } @@ -1809,12 +1807,12 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit errorTree(tree, em"""none of the ${err.overloadedAltsStr(altDenots)} |match $expectedStr""") - def hasEmptyParams(denot: SingleDenotation) = denot.info.paramTypess == ListOfNil + def hasEmptyParams(denot: SingleDenotation) = denot.info.paramInfoss == ListOfNil pt match { case pt: FunProto => tryInsertApplyOrImplicit(tree, pt)(noMatches) case _ => - if (altDenots exists (_.info.paramTypess == ListOfNil)) + if (altDenots exists (_.info.paramInfoss == ListOfNil)) typed(untpd.Apply(untpd.TypedSplice(tree), Nil), pt) else noMatches @@ -1841,7 +1839,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit } def adaptToArgs(wtp: Type, pt: FunProto): Tree = wtp match { - case _: MethodType | _: PolyType => + case _: MethodOrPoly => if (pt.args.lengthCompare(1) > 0 && isUnary(wtp) && ctx.canAutoTuple) adaptInterpolated(tree, pt.tupled, original) else @@ -1871,7 +1869,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit case TypeBounds(lo, hi) if (lo eq hi) || (hi <:< lo)(ctx.fresh.setExploreTyperState) => inst(lo) - case tp: PolyParam => + case tp: TypeParamRef => constraint.typeVarOfParam(tp).orElse(tp) case _ => tp } @@ -1886,7 +1884,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit adaptInterpolated(tree.withType(wtp.resultType), pt, original) case wtp: ImplicitMethodType if constrainResult(wtp, followAlias(pt)) => val tvarsToInstantiate = tvarsInParams(tree) - wtp.paramTypes.foreach(instantiateSelected(_, tvarsToInstantiate)) + wtp.paramInfos.foreach(instantiateSelected(_, tvarsToInstantiate)) val constr = ctx.typerState.constraint def addImplicitArgs(implicit ctx: Context) = { val errors = new mutable.ListBuffer[() => String] @@ -1898,7 +1896,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit for (err <- errors) ctx.error(err(), tree.pos.endPos) tree.withType(wtp.resultType) } - val args = (wtp.paramNames, wtp.paramTypes).zipped map { (pname, formal) => + val args = (wtp.paramNames, wtp.paramInfos).zipped map { (pname, formal) => def implicitArgError(msg: String => String) = errors += (() => msg(em"parameter $pname of $methodStr")) if (errors.nonEmpty) EmptyTree @@ -1941,11 +1939,11 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit // prioritize method parameter types as parameter types of the eta-expanded closure 0 else defn.functionArity(pt) - else if (pt eq AnyFunctionProto) wtp.paramTypes.length + else if (pt eq AnyFunctionProto) wtp.paramInfos.length else -1 if (arity >= 0 && !tree.symbol.isConstructor) typed(etaExpand(tree, wtp, arity), pt) - else if (wtp.paramTypes.isEmpty) + else if (wtp.paramInfos.isEmpty) adaptInterpolated(tpd.Apply(tree, Nil), pt, EmptyTree) else if (wtp.isImplicit) err.typeMismatch(tree, pt) diff --git a/compiler/src/dotty/tools/dotc/typer/VarianceChecker.scala b/compiler/src/dotty/tools/dotc/typer/VarianceChecker.scala index d5dd5a024..f88098519 100644 --- a/compiler/src/dotty/tools/dotc/typer/VarianceChecker.scala +++ b/compiler/src/dotty/tools/dotc/typer/VarianceChecker.scala @@ -88,10 +88,8 @@ class VarianceChecker()(implicit ctx: Context) { if (sym.variance != 0 && base.isContainedIn(sym.owner)) checkVarianceOfSymbol(sym) else if (sym.isAliasType) this(status, sym.info.bounds.hi) else foldOver(status, tp) - case tp: MethodType => - this(status, tp.resultType) // params will be checked in their TypeDef nodes. - case tp: PolyType => - this(status, tp.resultType) // params will be checked in their ValDef nodes. + case tp: MethodOrPoly => + this(status, tp.resultType) // params will be checked in their TypeDef or ValDef nodes. case AnnotatedType(_, annot) if annot.symbol == defn.UncheckedVarianceAnnot => status //case tp: ClassInfo => diff --git a/compiler/src/dotty/tools/dotc/typer/Variances.scala b/compiler/src/dotty/tools/dotc/typer/Variances.scala index 5a1745930..aeeef0275 100644 --- a/compiler/src/dotty/tools/dotc/typer/Variances.scala +++ b/compiler/src/dotty/tools/dotc/typer/Variances.scala @@ -79,12 +79,12 @@ object Variances { varianceInType(parent)(tparam) & varianceInType(rinfo)(tparam) case tp: RecType => varianceInType(tp.parent)(tparam) - case tp: MethodType => - flip(varianceInTypes(tp.paramTypes)(tparam)) & varianceInType(tp.resultType)(tparam) + case tp: MethodOrPoly => + flip(varianceInTypes(tp.paramInfos)(tparam)) & varianceInType(tp.resultType)(tparam) case ExprType(restpe) => varianceInType(restpe)(tparam) case tp @ HKApply(tycon, args) => - def varianceInArgs(v: Variance, args: List[Type], tparams: List[TypeParamInfo]): Variance = + def varianceInArgs(v: Variance, args: List[Type], tparams: List[ParamInfo]): Variance = args match { case arg :: args1 => varianceInArgs( @@ -94,8 +94,6 @@ object Variances { v } varianceInArgs(varianceInType(tycon)(tparam), args, tycon.typeParams) - case tp: PolyType => - flip(varianceInTypes(tp.paramBounds)(tparam)) & varianceInType(tp.resultType)(tparam) case AnnotatedType(tp, annot) => varianceInType(tp)(tparam) & varianceInAnnot(annot)(tparam) case tp: AndOrType => diff --git a/compiler/test/dotty/tools/dotc/parsing/DocstringTests.scala b/compiler/test/dotty/tools/dotc/parsing/DocstringTests.scala index 930ec117a..81ac77761 100644 --- a/compiler/test/dotty/tools/dotc/parsing/DocstringTests.scala +++ b/compiler/test/dotty/tools/dotc/parsing/DocstringTests.scala @@ -488,4 +488,34 @@ class DocstringTests extends DocstringTest { checkDocString(c.rawComment.map(_.raw), "/** Class1 */") } } + + @Test def nestedComment = { + val source = + """ + |trait T { + | /** Cheeky comment */ + |} + |class C + """.stripMargin + + import dotty.tools.dotc.ast.untpd._ + checkFrontend(source) { + case p @ PackageDef(_, Seq(_, c: TypeDef)) => + assert(c.rawComment == None, s"class C is not supposed to have a docstring (${c.rawComment.get}) in:$source") + } + } + + @Test def eofComment = { + val source = + """ + |class C + |/** Cheeky comment */ + """.stripMargin + + import dotty.tools.dotc.ast.untpd._ + checkFrontend(source) { + case p @ PackageDef(_, Seq(c: TypeDef)) => + assert(c.rawComment == None, s"class C is not supposed to have a docstring (${c.rawComment.get}) in:$source") + } + } } /* End class */ diff --git a/compiler/test/dotty/tools/dotc/repl/TestREPL.scala b/compiler/test/dotty/tools/dotc/repl/TestREPL.scala index 131a88ab1..c5557b86e 100644 --- a/compiler/test/dotty/tools/dotc/repl/TestREPL.scala +++ b/compiler/test/dotty/tools/dotc/repl/TestREPL.scala @@ -4,7 +4,7 @@ package repl import core.Contexts.Context import collection.mutable -import java.io.StringWriter +import java.io.{StringWriter, PrintStream} import dotty.tools.io.{ PlainFile, Directory } import org.junit.Test @@ -58,8 +58,11 @@ class TestREPL(script: String) extends REPL { val printed = out.toString val transcript = printed.drop(printed.indexOf(config.prompt)) if (transcript.toString.lines.toList != script.lines.toList) { - println("input differs from transcript:") + println("input differs from transcript (copy is repl.transcript):") println(transcript) + val s = new PrintStream("repl.transcript") + s.print(transcript) + s.close() assert(false) } } diff --git a/doc-tool/resources/_layouts/main.html b/doc-tool/resources/_layouts/main.html index 7c63ec610..eb0dd51fd 100644 --- a/doc-tool/resources/_layouts/main.html +++ b/doc-tool/resources/_layouts/main.html @@ -13,6 +13,7 @@ integrity="sha384-AysaV+vQoT3kOAXZkl02PThvDr8HYKPZhNT5h/CXfBThSRXQ6jW5DO2ekP5ViFdi" crossorigin="anonymous" > + <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css" diff --git a/doc-tool/resources/_layouts/sidebar.html b/doc-tool/resources/_layouts/sidebar.html index b3947c884..24ed1bed7 100644 --- a/doc-tool/resources/_layouts/sidebar.html +++ b/doc-tool/resources/_layouts/sidebar.html @@ -33,9 +33,12 @@ layout: main </ul> </div> <div id="content-body"> - <div id="menu-toggle" onclick="toggleMenu()"> - <i class="fa fa-bars" aria-hidden="true"></i> - </div> + <button type="button" id="menu-toggle" onclick="toggleMenu()" aria-expanded="false"> + <span class="sr-only" aria-hidden="true">Toggle navigation</span> + <span class="icon-bar"></span> + <span class="icon-bar"></span> + <span class="icon-bar"></span> + </button> {{ content }} </div> </div> diff --git a/doc-tool/resources/css/dottydoc.css b/doc-tool/resources/css/dottydoc.css index a89e23375..140f15eba 100644 --- a/doc-tool/resources/css/dottydoc.css +++ b/doc-tool/resources/css/dottydoc.css @@ -1,23 +1,31 @@ +html, body { + overflow-x: hidden; +} + html { height: 100%; } + body { min-height: 100%; } div#content-wrapper { min-height: 100vh; - padding-left: 250px; - transition: all 0.5s ease; + padding-left: 0; } -div#content-wrapper.toggled { - padding-left: 0; +div#content-wrapper div#content-body { + background-color: #f4f3f4; + border-left: 1px solid #e0e0e0; + box-shadow: -3px 0 5px -2px rgba(0, 0, 0, 0.14); + padding: 40px 1px 1px 1px; + position: relative; + min-height: 100vh; } -div.index-wrapper { +div#content-wrapper div.index-wrapper { background-color: #fafafa; - width: 250px; position: fixed; top: 0; left: 0; @@ -26,6 +34,77 @@ div.index-wrapper { overflow-x: hidden; } +/** Animations: Easing for content body slide on toggle */ +div#content-wrapper, +div#content-wrapper div#content-body { + -webkit-transition: all .25s ease-out; + -o-transition: all .25s ease-out; + transition: all .25s ease-out; +} + +/** Mobile (x < 576px) sidebar: Defaults closed with 60% wide sidebar */ +div#content-wrapper {} +div#content-wrapper div#content-body { left: 0; } +div#content-wrapper div.index-wrapper { width: 60%; } + +div#content-wrapper.toggled {} +div#content-wrapper.toggled div#content-body { left: 60%; } +div#content-wrapper.toggled div.index-wrapper {} + +/** Tablet (576px <= x < 768px) sidebar: Defaults closed with 250px wide sidebar */ +@media screen and (min-width: 576px) { + + div#content-wrapper {} + div#content-wrapper div#content-body {} + div#content-wrapper div.index-wrapper { width: 250px; } + + div#content-wrapper.toggled {} + div#content-wrapper.toggled div#content-body { left: 250px; } + div#content-wrapper.toggled div.index-wrapper {} + +} + +/** Desktop (x >= 768px) sidebar: Defaults open with 250px wide sidebar */ +@media screen and (min-width: 768px) { + + div#content-wrapper { padding-left: 250px; } + div#content-wrapper div#content-body {} + div#content-wrapper div.index-wrapper { width: 250px; } + + div#content-wrapper.toggled { padding-left: 0; } + div#content-wrapper.toggled div#content-body { left: 0; } + div#content-wrapper.toggled div.index-wrapper {} + +} + +div#content-wrapper button#menu-toggle { + background: rgba(244, 243, 244, 0.4) none; + border: 1px solid transparent; + color: #837f84; + margin: -30px 0 0 10px; + padding: 9px 10px; + position: absolute; +} + +div#content-wrapper button#menu-toggle:hover { + cursor: pointer; +} + +div#content-wrapper button#menu-toggle:focus { + outline: none; +} + +div#content-wrapper button#menu-toggle span.icon-bar { + background-color: #837f84; + border-radius: 2px; + border-color: #837f84; + color: #837f84; + display: block; + margin: 3px 0; + width: 15px; + height: 2px; +} + div#doc-page-container > h1 { border-bottom: 1px solid #eee; padding-bottom: 0.3em; @@ -93,27 +172,6 @@ div#doc-page-container > h6 > a:focus { outline: none; } -div#content-body { - border-left: 1px solid #e0e0e0; - box-shadow: -3px 0px 5px -2px rgba(0,0,0,0.14); - position: relative; - padding: 10px; - background-color: #f4f3f4; - min-height: 100vh; -} - -div#menu-toggle { - color: #837F84; - outline: none; - padding-left: 20px; - padding-top: 10px; -} - -div#menu-toggle:hover { - color: rgba(0, 0, 0, 0.4); - cursor: pointer; -} - ul.index-entities { list-style-type: none; padding-left: 0; @@ -195,7 +253,7 @@ ul.index-entities > li > a.entity-name { font-size: 13px; display: block; padding: 0 0 0 24px; - color: rgba(0,0,0,.87); + color: rgba(0, 0, 0, 0.87); background: transparent; cursor: pointer; float: left; @@ -245,7 +303,7 @@ ul.toc > li > ul.hide { ul.index-entities > li.index-title > span { font-size: 16px; font-weight: bold; - color: rgba(0,0,0,.87); + color: rgba(0, 0, 0, 0.87); padding: 0 24px; } @@ -319,7 +377,7 @@ pre { background: rgba(244, 243, 244, 0.6); border-radius: 2px; margin-top: 20px; - border: 1px solid rgba(0,0,0,0.1); + border: 1px solid rgba(0, 0, 0, 0.1); } pre > code.language-none, diff --git a/doc-tool/src/dotty/tools/dottydoc/core/DocASTPhase.scala b/doc-tool/src/dotty/tools/dottydoc/core/DocASTPhase.scala index cfb66fa56..ad8981ea2 100644 --- a/doc-tool/src/dotty/tools/dottydoc/core/DocASTPhase.scala +++ b/doc-tool/src/dotty/tools/dottydoc/core/DocASTPhase.scala @@ -94,10 +94,7 @@ class DocASTPhase extends Phase { NonEntity else { val tparams = t.rhs.tpe match { - case tp: PolyType => tp.paramRefs.zip(tp.variances).map { case (tp, variance) => - val varianceSym = if (variance == 1) "+" else if (variance == -1) "-" else "" - varianceSym + tp.paramName.show - } + case tp: PolyType => tp.paramNames.map(_.show) case _ => Nil } TypeAliasImpl(sym, annotations(sym), flags(t), t.name.show.split("\\$\\$").last, path(sym), alias(t.rhs.tpe), tparams) diff --git a/doc-tool/src/dotty/tools/dottydoc/model/factories.scala b/doc-tool/src/dotty/tools/dottydoc/model/factories.scala index 3e766a990..03f11335e 100644 --- a/doc-tool/src/dotty/tools/dottydoc/model/factories.scala +++ b/doc-tool/src/dotty/tools/dottydoc/model/factories.scala @@ -79,7 +79,7 @@ object factories { case TypeBounds(lo, hi) => BoundsReference(expandTpe(lo), expandTpe(hi)) - case t: PolyParam => + case t: TypeParamRef => typeRef(t.paramName.show, params = params) case ExprType(tpe) => @@ -106,7 +106,7 @@ object factories { case ci: ClassInfo => typeRef(ci.cls.name.show, query = ci.typeSymbol.showFullName) - case tl: PolyType => + case tl: TypeLambda => expandTpe(tl.resType) case OrType(left, right) => @@ -127,7 +127,7 @@ object factories { def typeParams(sym: Symbol)(implicit ctx: Context): List[String] = sym.info match { - case pt: PolyType => // TODO: not sure if this case is needed anymore + case pt: TypeLambda => // TODO: not sure if this case is needed anymore pt.paramNames.map(_.show.split("\\$").last) case ClassInfo(_, _, _, decls, _) => decls.iterator @@ -156,11 +156,11 @@ object factories { constructors(sym).head def paramLists(tpe: Type)(implicit ctx: Context): List[ParamList] = tpe match { - case pt: PolyType => + case pt: TypeLambda => paramLists(pt.resultType) case mt: MethodType => - ParamListImpl(mt.paramNames.zip(mt.paramTypes).map { case (name, tpe) => + ParamListImpl(mt.paramNames.zip(mt.paramInfos).map { case (name, tpe) => NamedReference( name.decode.toString, returnType(tpe), @@ -169,13 +169,13 @@ object factories { ) }, mt.isImplicit) :: paramLists(mt.resultType) - case mp: MethodParam => + case mp: TermParamRef => paramLists(mp.underlying) case annot: AnnotatedType => paramLists(annot.tpe) - case (_: PolyParam | _: RefinedType | _: TypeRef | _: ThisType | + case (_: TypeParamRef | _: RefinedType | _: TypeRef | _: ThisType | _: ExprType | _: OrType | _: AndType | _: HKApply | _: TermRef | _: ConstantType) => Nil // return types should not be in the paramlist diff --git a/docs/docs/internals/syntax.md b/docs/docs/internals/syntax.md index 7f06cdc2a..e4285e20f 100644 --- a/docs/docs/internals/syntax.md +++ b/docs/docs/internals/syntax.md @@ -289,7 +289,9 @@ TemplateBody ::= [nl] ‘{’ [SelfType] TemplateStat {semi TemplateStat} TemplateStat ::= Import | {Annotation [nl]} {Modifier} Def | {Annotation [nl]} {Modifier} Dcl + | EnumCaseStat | Expr1 + | SelfType ::= id [‘:’ InfixType] ‘=>’ ValDef(_, name, tpt, _) | ‘this’ ‘:’ InfixType ‘=>’ @@ -328,13 +330,20 @@ DefDef ::= DefSig [‘:’ Type] ‘=’ Expr | ‘this’ DefParamClause DefParamClauses DefDef(_, <init>, Nil, vparamss, EmptyTree, expr | Block) (‘=’ ConstrExpr | [nl] ConstrBlock) -TmplDef ::= ([‘case’] ‘class’ | ‘trait’) ClassDef +TmplDef ::= ([‘case’ | `enum'] ‘class’ | trait’) ClassDef | [‘case’] ‘object’ ObjectDef -ClassDef ::= id [ClsTypeParamClause] ClassDef(mods, name, tparams, templ) - [ConstrMods] ClsParamClauses TemplateOpt with DefDef(_, <init>, Nil, vparamss, EmptyTree, EmptyTree) as first stat + | `enum' EnumDef +ClassDef ::= id ClassConstr TemplateOpt ClassDef(mods, name, tparams, templ) +ClassConstr ::= [ClsTypeParamClause] [ConstrMods] ClsParamClauses with DefDef(_, <init>, Nil, vparamss, EmptyTree, EmptyTree) as first stat ConstrMods ::= AccessModifier | Annotation {Annotation} (AccessModifier | ‘this’) ObjectDef ::= id TemplateOpt ModuleDef(mods, name, template) // no constructor +EnumDef ::= id ClassConstr [`extends' [ConstrApps]] EnumDef(mods, name, tparams, template) + [nl] ‘{’ EnumCaseStat {semi EnumCaseStat} ‘}’ +EnumCaseStat ::= {Annotation [nl]} {Modifier} EnumCase +EnumCase ::= `case' (EnumClassDef | ObjectDef | ids) +EnumClassDef ::= id [ClsTpeParamClause | ClsParamClause] ClassDef(mods, name, tparams, templ) + ClsParamClauses TemplateOpt TemplateOpt ::= [‘extends’ Template | [nl] TemplateBody] Template ::= ConstrApps [TemplateBody] | TemplateBody Template(constr, parents, self, stats) ConstrApps ::= ConstrApp {‘with’ ConstrApp} diff --git a/library/src/scala/Enum.scala b/library/src/scala/Enum.scala new file mode 100644 index 000000000..7d2eefb3d --- /dev/null +++ b/library/src/scala/Enum.scala @@ -0,0 +1,8 @@ +package scala + +/** A base trait of all enum classes */ +trait Enum { + + /** A number uniquely identifying a case of an enum */ + def enumTag: Int +} diff --git a/library/src/scala/runtime/EnumValues.scala b/library/src/scala/runtime/EnumValues.scala new file mode 100644 index 000000000..6f9d907b3 --- /dev/null +++ b/library/src/scala/runtime/EnumValues.scala @@ -0,0 +1,21 @@ +package scala.runtime + +import scala.collection.immutable.Map + +class EnumValues[E <: Enum] { + private var myMap: Map[Int, E] = Map() + private var fromNameCache: Map[String, E] = null + + def register(v: E) = { + require(!myMap.contains(v.enumTag)) + myMap = myMap.updated(v.enumTag, v) + fromNameCache = null + } + + def fromInt: Map[Int, E] = myMap + def fromName: Map[String, E] = { + if (fromNameCache == null) fromNameCache = myMap.values.map(v => v.toString -> v).toMap + fromNameCache + } + def values: Iterable[E] = myMap.values +} diff --git a/tests/neg/enums.scala b/tests/neg/enums.scala new file mode 100644 index 000000000..108ec4a6c --- /dev/null +++ b/tests/neg/enums.scala @@ -0,0 +1,20 @@ +enum List[+T] { + case Cons(x: T, xs: List[T]) + case Snoc[U](xs: List[U], x: U) // error: case with type parameters needs extends clause +} + +enum class X { + case Y // error: case not allowed here +} + +enum E1[T] { + case C // error: cannot determine type argument +} + +enum E2[+T, +U >: T] { + case C // error: cannot determine type argument +} + +enum E3[-T <: Ordered[T]] { + case C // error: cannot determine type argument +} diff --git a/tests/neg/leak-type.scala b/tests/neg/leak-type.scala new file mode 100644 index 000000000..30ecab70b --- /dev/null +++ b/tests/neg/leak-type.scala @@ -0,0 +1,13 @@ +trait A { + private type Foo = Int + + + class Inner[T <: Foo] { // error: non-private type T refers to private type Foo in its type signature + def get: T = ??? + } +} +class B extends A { + def foo(x: Inner[_]): Unit = { + val a = x.get // error: cannot resolve reference to type B(B.this).Foo + } +} diff --git a/tests/pickling/i2166.scala b/tests/pickling/i2166.scala new file mode 100644 index 000000000..7199b7a36 --- /dev/null +++ b/tests/pickling/i2166.scala @@ -0,0 +1,5 @@ +object Test { + @inline def f = "" match { case _ => false } + + def main(args: Array[String]): Unit = f +}
\ No newline at end of file diff --git a/tests/pos/enum-List-control.scala b/tests/pos/enum-List-control.scala new file mode 100644 index 000000000..d9df176d1 --- /dev/null +++ b/tests/pos/enum-List-control.scala @@ -0,0 +1,14 @@ +abstract sealed class List[T] extends Enum +object List { + final case class Cons[T](x: T, xs: List[T]) extends List[T] { + def enumTag = 0 + } + final case class Nil[T]() extends List[T] { + def enumTag = 1 + } +} +object Test { + import List._ + val xs = Cons(1, Cons(2, Cons(3, Nil()))) + def main(args: Array[String]) = println(xs) +} diff --git a/tests/pos/i1130.scala b/tests/pos/i1130.scala index 8d71de5e8..c28eaa169 100644 --- a/tests/pos/i1130.scala +++ b/tests/pos/i1130.scala @@ -3,4 +3,6 @@ trait A { def foo: Foo = 1 } -class B extends A +class B extends A { + foo +} diff --git a/tests/pos/leak-inferred.scala b/tests/pos/leak-inferred.scala new file mode 100644 index 000000000..5d8a7e3bc --- /dev/null +++ b/tests/pos/leak-inferred.scala @@ -0,0 +1,12 @@ +class A { + private val x = List(1,2) + + val elem = x.head +} + +class B extends A { + val a: Int = elem + // Without `checkNoPrivateLeaks`, we get: + // found: B.this.x.scala$collection$immutable$List$$A(B.this.elem) + // required: Int +} diff --git a/tests/pos/tailcall/i2024.scala b/tests/pos/tailcall/i2024.scala new file mode 100644 index 000000000..aba1db2e5 --- /dev/null +++ b/tests/pos/tailcall/i2024.scala @@ -0,0 +1,4 @@ +object Test { +// def main(args: Array[String]): Unit = { } + def assume[T]: Any = assume +} diff --git a/tests/pos/tailcall/return.scala b/tests/pos/tailcall/return.scala index 454686c3d..3724b5b32 100644 --- a/tests/pos/tailcall/return.scala +++ b/tests/pos/tailcall/return.scala @@ -1,4 +1,4 @@ -object Test { +object Return { def foo(x: Int): Int = return 3 diff --git a/tests/run/enum-Color.check b/tests/run/enum-Color.check new file mode 100644 index 000000000..865c47e49 --- /dev/null +++ b/tests/run/enum-Color.check @@ -0,0 +1,3 @@ +Red: 0 +Green: 1 +Blue: 2 diff --git a/tests/run/enum-Color.scala b/tests/run/enum-Color.scala new file mode 100644 index 000000000..683d18d9e --- /dev/null +++ b/tests/run/enum-Color.scala @@ -0,0 +1,11 @@ +enum Color { + case Red, Green, Blue +} + +object Test { + def main(args: Array[String]) = + for (color <- Color.enumValues) { + println(s"$color: ${color.enumTag}") + assert(Color.enumValue(color.enumTag) eq color) + } +} diff --git a/tests/run/enum-HList.scala b/tests/run/enum-HList.scala new file mode 100644 index 000000000..c019cb6cc --- /dev/null +++ b/tests/run/enum-HList.scala @@ -0,0 +1,22 @@ +enum HLst { + case HCons[+Hd, +Tl <: HLst](hd: Hd, tl: Tl) + case HNil +} + +object Test { + import HLst._ + def length(hl: HLst): Int = hl match { + case HCons(_, tl) => 1 + length(tl) + case HNil => 0 + } + def sumInts(hl: HLst): Int = hl match { + case HCons(x: Int, tl) => x + sumInts(tl) + case HCons(_, tl) => sumInts(tl) + case HNil => 0 + } + def main(args: Array[String]) = { + val hl = HCons(1, HCons("A", HNil)) + assert(length(hl) == 2, length(hl)) + assert(sumInts(hl) == 1) + } +} diff --git a/tests/run/enum-List1.check b/tests/run/enum-List1.check new file mode 100644 index 000000000..3ed5061b4 --- /dev/null +++ b/tests/run/enum-List1.check @@ -0,0 +1 @@ +Cons(1,Cons(2,Cons(3,Nil()))) diff --git a/tests/run/enum-List1.scala b/tests/run/enum-List1.scala new file mode 100644 index 000000000..bb75bec4a --- /dev/null +++ b/tests/run/enum-List1.scala @@ -0,0 +1,10 @@ +enum class List[T] +object List { + case Cons(x: T, xs: List[T]) + case Nil() +} +object Test { + import List._ + val xs = Cons(1, Cons(2, Cons(3, Nil()))) + def main(args: Array[String]) = println(xs) +} diff --git a/tests/run/enum-List2.check b/tests/run/enum-List2.check new file mode 100644 index 000000000..1d4812de1 --- /dev/null +++ b/tests/run/enum-List2.check @@ -0,0 +1 @@ +Cons(1,Cons(2,Cons(3,Nil))) diff --git a/tests/run/enum-List2.scala b/tests/run/enum-List2.scala new file mode 100644 index 000000000..030de0f84 --- /dev/null +++ b/tests/run/enum-List2.scala @@ -0,0 +1,11 @@ +enum class List[+T] +object List { + case Cons(x: T, xs: List[T]) + case Nil extends List[Nothing] +} +object Test { + import List._ + val xs = Cons(1, Cons(2, Cons(3, Nil))) + def main(args: Array[String]) = println(xs) +} + diff --git a/tests/run/enum-List2a.check b/tests/run/enum-List2a.check new file mode 100644 index 000000000..1d4812de1 --- /dev/null +++ b/tests/run/enum-List2a.check @@ -0,0 +1 @@ +Cons(1,Cons(2,Cons(3,Nil))) diff --git a/tests/run/enum-List2a.scala b/tests/run/enum-List2a.scala new file mode 100644 index 000000000..323a5587c --- /dev/null +++ b/tests/run/enum-List2a.scala @@ -0,0 +1,11 @@ +enum class List[+T] +object List { + case Cons(x: T, xs: List[T]) + case Nil +} +object Test { + import List._ + val xs = Cons(1, Cons(2, Cons(3, Nil))) + def main(args: Array[String]) = println(xs) +} + diff --git a/tests/run/enum-List3.check b/tests/run/enum-List3.check new file mode 100644 index 000000000..1d4812de1 --- /dev/null +++ b/tests/run/enum-List3.check @@ -0,0 +1 @@ +Cons(1,Cons(2,Cons(3,Nil))) diff --git a/tests/run/enum-List3.scala b/tests/run/enum-List3.scala new file mode 100644 index 000000000..e5ffe1a28 --- /dev/null +++ b/tests/run/enum-List3.scala @@ -0,0 +1,10 @@ +enum List[+T] { + case Cons(x: T, xs: List[T]) + case Nil extends List[Nothing] +} +object Test { + import List._ + val xs = Cons(1, Cons(2, Cons(3, Nil))) + def main(args: Array[String]) = println(xs) +} + diff --git a/tests/run/enum-Option.scala b/tests/run/enum-Option.scala new file mode 100644 index 000000000..76f5641b3 --- /dev/null +++ b/tests/run/enum-Option.scala @@ -0,0 +1,19 @@ +enum class Option[+T >: Null] extends Serializable { + def isDefined: Boolean +} +object Option { + def apply[T >: Null](x: T): Option[T] = if (x == null) None else Some(x) + case Some(x: T) { + def isDefined = true + } + case None { + def isDefined = false + } +} + +object Test { + def main(args: Array[String]) = { + assert(Some(None).isDefined) + Option("22") match { case Option.Some(x) => assert(x == "22") } + } +} diff --git a/tests/run/enum-Tree.check b/tests/run/enum-Tree.check new file mode 100644 index 000000000..02f5151be --- /dev/null +++ b/tests/run/enum-Tree.check @@ -0,0 +1 @@ +If(IsZero(Pred(Succ(Zero))),Succ(Succ(Zero)),Pred(Pred(Zero))) --> 2 diff --git a/tests/run/enum-Tree.scala b/tests/run/enum-Tree.scala new file mode 100644 index 000000000..ef5bd7a57 --- /dev/null +++ b/tests/run/enum-Tree.scala @@ -0,0 +1,29 @@ +enum Tree[T] { + case True extends Tree[Boolean] + case False extends Tree[Boolean] + case Zero extends Tree[Int] + case Succ(n: Tree[Int]) extends Tree[Int] + case Pred(n: Tree[Int]) extends Tree[Int] + case IsZero(n: Tree[Int]) extends Tree[Boolean] + case If(cond: Tree[Boolean], thenp: Tree[T], elsep: Tree[T]) +} + +object Test { + import Tree._ + + def eval[T](e: Tree[T]): T = e match { + case True => true + case False => false + case Zero => 0 + case Succ(f) => eval(f) + 1 + case Pred(f) => eval(f) - 1 + case IsZero(f) => eval(f) == 0 + case If(cond, thenp, elsep) => if (eval(cond)) eval(thenp) else eval(elsep) + } + + val data = If(IsZero(Pred(Succ(Zero))), Succ(Succ(Zero)), Pred(Pred(Zero))) + + def main(args: Array[String]) = { + println(s"$data --> ${eval(data)}") + } +} diff --git a/tests/run/enum-approx.scala b/tests/run/enum-approx.scala new file mode 100644 index 000000000..7811b3909 --- /dev/null +++ b/tests/run/enum-approx.scala @@ -0,0 +1,22 @@ +enum class Fun[-T, +U >: Null] { + def f: T => U = null +} +object Fun { + case Identity[T, U >: Null](override val f: T => U) extends Fun[T, U] + case ConstNull { + override def f = x => null + } + case ConstNullClass() { + override def f = x => null + } + case ConstNullSimple +} + +object Test { + def main(args: Array[String]) = { + val x: Null = Fun.ConstNull.f("abc") + val y: Null = Fun.ConstNullClass().f("abc") + assert(Fun.ConstNullSimple.f == null) + } +} + diff --git a/tests/run/enumList2a.check b/tests/run/enumList2a.check new file mode 100644 index 000000000..1d4812de1 --- /dev/null +++ b/tests/run/enumList2a.check @@ -0,0 +1 @@ +Cons(1,Cons(2,Cons(3,Nil))) diff --git a/tests/run/generic/Color.scala b/tests/run/generic/Color.scala index ed248295d..7f2a8818c 100644 --- a/tests/run/generic/Color.scala +++ b/tests/run/generic/Color.scala @@ -10,12 +10,17 @@ import Shapes._ */ sealed trait Color extends Enum -object Color extends EnumValues[Color](3) { +object Color { + + private val $values = new runtime.EnumValues[Color] + def valueOf = $values.fromInt + def withName = $values.fromName + def values = $values.values private def $new(tag: Int, name: String) = new Color { def enumTag = tag override def toString = name - registerEnumValue(this) + $values.register(this) } val Red: Color = $new(0, "Red") @@ -25,6 +30,6 @@ object Color extends EnumValues[Color](3) { implicit val ColorShape: Color `shaped` EnumValue[Color] = new (Color `shaped` EnumValue[Color]) { def toShape(x: Color) = EnumValue(x.enumTag) - def fromShape(x: EnumValue[Color]) = Color.value(x.tag) + def fromShape(x: EnumValue[Color]) = Color.valueOf(x.tag) } -}
\ No newline at end of file +} diff --git a/tests/run/generic/Enum.scala b/tests/run/generic/Enum.scala deleted file mode 100644 index dbdbfe8eb..000000000 --- a/tests/run/generic/Enum.scala +++ /dev/null @@ -1,18 +0,0 @@ -package generic - -import Shapes.Singleton - -trait Enum { - def enumTag: Int -} - -trait FiniteEnum extends Enum - -abstract class EnumValues[E <: Enum](numVals: Int) { - private var myValues = new Array[AnyRef](numVals) - - def registerEnumValue(v: E) = - myValues(v.enumTag) = v - - def value: IndexedSeq[E] = (myValues: IndexedSeq[AnyRef]).asInstanceOf[IndexedSeq[E]] -} diff --git a/tests/run/generic/List.scala b/tests/run/generic/List.scala index 3f3657656..bc01ce63f 100644 --- a/tests/run/generic/List.scala +++ b/tests/run/generic/List.scala @@ -46,7 +46,7 @@ object List0 { } } -/** enum List[T] { +/** enum List[+T] { * case Cons(x: T, xs: List[T]) * case Nil extends List[Nothing] * } diff --git a/tests/run/generic/SearchResult.scala b/tests/run/generic/SearchResult.scala index 1c86d1b4f..d4380a072 100644 --- a/tests/run/generic/SearchResult.scala +++ b/tests/run/generic/SearchResult.scala @@ -11,12 +11,17 @@ import Shapes._ */ sealed trait SearchResult extends Enum -object SearchResult extends EnumValues[SearchResult](3) { +object SearchResult extends { + + private val $values = new runtime.EnumValues[SearchResult] + def valueOf = $values.fromInt + def withName = $values.fromName + def values = $values.values private def $new(tag: Int, name: String) = new SearchResult { def enumTag = tag override def toString = name - registerEnumValue(this) + $values.register(this) } abstract case class Success(result: Color) extends SearchResult { @@ -31,8 +36,8 @@ object SearchResult extends EnumValues[SearchResult](3) { } } - val Diverging = $new(1, "Diverging") - val NoMatch = $new(2, "NoMatch") + val Diverging: SearchResult = $new(1, "Diverging") + val NoMatch: SearchResult = $new(2, "NoMatch") abstract case class Ambiguous(alt1: SearchResult, alt2: SearchResult) extends SearchResult { def enumTag = 3 @@ -58,7 +63,7 @@ object SearchResult extends EnumValues[SearchResult](3) { def fromShape(x: Sum[Success, Sum[Ambiguous, EnumValue[SearchResult]]]): SearchResult = x match { case Fst(s) => s case Snd(Fst(a)) => a - case Snd(Snd(ev)) => value(ev.tag) + case Snd(Snd(ev)) => valueOf(ev.tag) } } -}
\ No newline at end of file +} diff --git a/tests/run/generic/Tree.scala b/tests/run/generic/Tree.scala index f4e706944..673506b07 100644 --- a/tests/run/generic/Tree.scala +++ b/tests/run/generic/Tree.scala @@ -2,14 +2,14 @@ package generic import Shapes._ -/** enum Tree[TS] { +/** enum Tree[T] { * case True extends Tree[Boolean] * case False extends Tree[Boolean] * case Zero extends Tree[Int] * case Succ(n: Tree[Int]) extends Tree[Int] * case Pred(n: Tree[Int]) extends Tree[Int] * case IsZero(n: Tree[Int]) extends Tree[Boolean] - * case If(cond: Boolean, thenp: Tree[T], elsep: Tree[T]) extends Tree[T] + * case If(cond: Boolean, thenp: Tree[T], elsep: Tree[T]) * } */ sealed trait Tree[TR] extends Enum @@ -110,4 +110,4 @@ object Tree { case Snd(Snd(_if)) => _if } } -}
\ No newline at end of file +} diff --git a/tests/run/planets.check b/tests/run/planets.check new file mode 100644 index 000000000..feb6f737d --- /dev/null +++ b/tests/run/planets.check @@ -0,0 +1,8 @@ +Your weight on MERCURY is 37.775761520093525 +Your weight on SATURN is 106.60155388115666 +Your weight on VENUS is 90.49990998410455 +Your weight on URANUS is 90.51271993894251 +Your weight on EARTH is 100.0 +Your weight on NEPTUNE is 113.83280724696579 +Your weight on MARS is 37.873718403712886 +Your weight on JUPITER is 253.05575254957407 diff --git a/tests/run/planets.scala b/tests/run/planets.scala new file mode 100644 index 000000000..bcbfd7eeb --- /dev/null +++ b/tests/run/planets.scala @@ -0,0 +1,26 @@ +enum class Planet(mass: Double, radius: Double) { + private final val G = 6.67300E-11 + def surfaceGravity = G * mass / (radius * radius) + def surfaceWeight(otherMass: Double) = otherMass * surfaceGravity +} +object Planet { + case MERCURY extends Planet(3.303e+23, 2.4397e6) + case VENUS extends Planet(4.869e+24, 6.0518e6) + case EARTH extends Planet(5.976e+24, 6.37814e6) + case MARS extends Planet(6.421e+23, 3.3972e6) + case JUPITER extends Planet(1.9e+27, 7.1492e7) + case SATURN extends Planet(5.688e+26, 6.0268e7) + case URANUS extends Planet(8.686e+25, 2.5559e7) + case NEPTUNE extends Planet(1.024e+26, 2.4746e7) +} +object Test { + def main(args: Array[String]) = { + import Planet._ + assert(enumValueNamed("SATURN") == SATURN) + assert(enumValue(2) == EARTH) + val earthWeight = 100 + val mass = earthWeight/EARTH.surfaceGravity + for (p <- enumValues) + println(s"Your weight on $p is ${p.surfaceWeight(mass)}") + } +} |