diff --git a/compiler/src/dotty/tools/dotc/Run.scala b/compiler/src/dotty/tools/dotc/Run.scala index f8e7967e89d8..d8e452f2f81f 100644 --- a/compiler/src/dotty/tools/dotc/Run.scala +++ b/compiler/src/dotty/tools/dotc/Run.scala @@ -105,7 +105,7 @@ class Run(comp: Compiler, ictx: Context) { compileUnits()(ctx) } - protected def compileUnits()(implicit ctx: Context) = Stats.monitorHeartBeat { + protected def compileUnits()(implicit ctx: Context) = Stats.maybeMonitored { ctx.checkSingleThreaded() // If testing pickler, make sure to stop after pickling phase: @@ -118,17 +118,18 @@ class Run(comp: Compiler, ictx: Context) { ctx.usePhases(phases) var lastPrintedTree: PrintedTree = NoPrintedTree for (phase <- ctx.allPhases) - if (phase.isRunnable) { - val start = System.currentTimeMillis - units = phase.runOn(units) - if (ctx.settings.Xprint.value.containsPhase(phase)) { - for (unit <- units) { - lastPrintedTree = - printTree(lastPrintedTree)(ctx.fresh.setPhase(phase.next).setCompilationUnit(unit)) + if (phase.isRunnable) + Stats.trackTime(s"$phase ms ") { + val start = System.currentTimeMillis + units = phase.runOn(units) + if (ctx.settings.Xprint.value.containsPhase(phase)) { + for (unit <- units) { + lastPrintedTree = + printTree(lastPrintedTree)(ctx.fresh.setPhase(phase.next).setCompilationUnit(unit)) + } } + ctx.informTime(s"$phase ", start) } - ctx.informTime(s"$phase ", start) - } if (!ctx.reporter.hasErrors) Rewrites.writeBack() for (unit <- units) Stats.record("retained typed trees at end", unit.tpdTree.treeSize) diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index 4b3ea0df7e75..d1dc727428a2 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -5,7 +5,7 @@ package ast import core._ import util.Positions._, Types._, Contexts._, Constants._, Names._, NameOps._, Flags._ import SymDenotations._, Symbols._, StdNames._, Annotations._, Trees._ -import Decorators._ +import Decorators._, transform.SymUtils._ import NameKinds.{UniqueName, EvidenceParamName, DefaultGetterName} import language.higherKinds import typer.FrontEnd @@ -70,7 +70,7 @@ object desugar { def apply(tp: Type) = tp match { case tp: NamedType if tp.symbol.exists && (tp.symbol.owner eq originalOwner) => val defctx = ctx.outersIterator.dropWhile(_.scope eq ctx.scope).next() - var local = defctx.denotNamed(tp.name).suchThat(_ is ParamOrAccessor).symbol + var local = defctx.denotNamed(tp.name).suchThat(_.isParamOrAccessor).symbol if (local.exists) (defctx.owner.thisType select local).dealias else { def msg = @@ -238,23 +238,6 @@ object desugar { Nil } - /** Fill in empty type bounds with Nothing/Any. Expand private local type parameters as follows: - * - * class C[v T] - * ==> - * class C { type v C$T; type v T = C$T } - */ - def typeDef(tdef: TypeDef)(implicit ctx: Context): Tree = { - if (tdef.mods is PrivateLocalParam) { - val tparam = cpy.TypeDef(tdef)(name = tdef.name.expandedName(ctx.owner)) - .withMods(tdef.mods &~ PrivateLocal) - val alias = cpy.TypeDef(tdef)(rhs = refOfDef(tparam)) - .withMods(tdef.mods & VarianceFlags | PrivateLocalParamAccessor | Synthetic) - Thicket(tparam, alias) - } - else tdef - } - @sharable private val synthetic = Modifiers(Synthetic) private def toDefParam(tparam: TypeDef): TypeDef = @@ -696,7 +679,7 @@ object desugar { def defTree(tree: Tree)(implicit ctx: Context): Tree = tree match { case tree: ValDef => valDef(tree) - case tree: TypeDef => if (tree.isClassDef) classDef(tree) else typeDef(tree) + case tree: TypeDef => if (tree.isClassDef) classDef(tree) else tree case tree: DefDef => defDef(tree) case tree: ModuleDef => moduleDef(tree) case tree: PatDef => patDef(tree) @@ -1132,7 +1115,7 @@ object desugar { */ def refinedTypeToClass(parent: tpd.Tree, refinements: List[Tree])(implicit ctx: Context): TypeDef = { def stripToCore(tp: Type): List[Type] = tp match { - case tp: RefinedType if tp.argInfos.nonEmpty => tp :: Nil // parameterized class type + case tp: AppliedType => tp :: Nil case tp: TypeRef if tp.symbol.isClass => tp :: Nil // monomorphic class type case tp: TypeProxy => stripToCore(tp.underlying) case AndType(tp1, tp2) => stripToCore(tp1) ::: stripToCore(tp2) diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index 016db8e2c01b..660c4b81de80 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -213,8 +213,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { ta.assignType(untpd.TypeDef(sym.name, TypeTree(sym.info)), sym) def ClassDef(cls: ClassSymbol, constr: DefDef, body: List[Tree], superArgs: List[Tree] = Nil)(implicit ctx: Context): TypeDef = { - val firstParentRef :: otherParentRefs = cls.info.parents - val firstParent = cls.typeRef.baseTypeWithArgs(firstParentRef.symbol) + val firstParent :: otherParents = cls.info.parents val superRef = if (cls is Trait) TypeTree(firstParent) else { @@ -229,7 +228,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { val constr = firstParent.decl(nme.CONSTRUCTOR).suchThat(constr => isApplicable(constr.info)) New(firstParent, constr.symbol.asTerm, superArgs) } - val parents = superRef :: otherParentRefs.map(TypeTree(_)) + val parents = superRef :: otherParents.map(TypeTree(_)) val selfType = if (cls.classInfo.selfInfo ne NoType) ValDef(ctx.newSelfSym(cls)) @@ -305,7 +304,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { true case pre: ThisType => pre.cls.isStaticOwner || - tp.symbol.is(ParamOrAccessor) && !pre.cls.is(Trait) && ctx.owner.enclosingClass == pre.cls + tp.symbol.isParamOrAccessor && !pre.cls.is(Trait) && ctx.owner.enclosingClass == pre.cls // was ctx.owner.enclosingClass.derivesFrom(pre.cls) which was not tight enough // and was spuriously triggered in case inner class would inherit from outer one // eg anonymous TypeMap inside TypeMap.andThen @@ -381,7 +380,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { /** new C(args), calling given constructor `constr` of C */ def New(tp: Type, constr: TermSymbol, args: List[Tree])(implicit ctx: Context): Apply = { val targs = tp.argTypes - val tycon = tp.withoutArgs(targs) + val tycon = tp.typeConstructor New(tycon) .select(TermRef.withSig(tycon, constr)) .appliedToTypes(targs) @@ -698,8 +697,10 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { */ def select(sym: Symbol)(implicit ctx: Context): Select = { val tp = - if (sym.isType) + if (sym.isType) { + assert(!sym.is(TypeParam)) TypeRef(tree.tpe, sym.name.asTypeName) + } else TermRef.withSigAndDenot(tree.tpe, sym.name.asTermName, sym.signature, sym.denot.asSeenFrom(tree.tpe)) diff --git a/compiler/src/dotty/tools/dotc/ast/untpd.scala b/compiler/src/dotty/tools/dotc/ast/untpd.scala index 52a49de47f88..29fcea80dbad 100644 --- a/compiler/src/dotty/tools/dotc/ast/untpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala @@ -296,9 +296,9 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { (tycon, targs) case TypedSplice(AppliedTypeTree(tycon, targs)) => (TypedSplice(tycon), targs map (TypedSplice(_))) - case TypedSplice(tpt1: Tree) => + case TypedSplice(tpt1: tpd.Tree) => + val tycon = tpt1.tpe.typeConstructor val argTypes = tpt1.tpe.argTypesLo - val tycon = tpt1.tpe.withoutArgs(argTypes) def wrap(tpe: Type) = TypeTree(tpe) withPos tpt.pos (wrap(tycon), argTypes map wrap) case _ => diff --git a/compiler/src/dotty/tools/dotc/config/Config.scala b/compiler/src/dotty/tools/dotc/config/Config.scala index ffc4d65632cf..d7980b55f36a 100644 --- a/compiler/src/dotty/tools/dotc/config/Config.scala +++ b/compiler/src/dotty/tools/dotc/config/Config.scala @@ -84,19 +84,12 @@ object Config { /** If this flag is set, take the fast path when comparing same-named type-aliases and types */ final val fastPathForRefinedSubtype = true - /** If this flag is set, `TypeOps.normalizeToClassRefs` will insert forwarders - * for type parameters of base classes. This is an optimization, which avoids - * long alias chains. We should not rely on the optimization, though. So changing - * the flag to false can be used for checking that everything works OK without it. - */ - final val forwardTypeParams = true - - /** If this flag is set, and we compute `T1 { X = S1 }` & `T2 { X = S2 }` as a new - * upper bound of a constrained parameter, try to align the refinements by computing + /** If this flag is set, and we compute `T1[X1]` & `T2[X2]` as a new + * upper bound of a constrained parameter, try to align the arguments by computing * `S1 =:= S2` (which might instantiate type parameters). * This rule is contentious because it cuts the constraint set. * - * For more info, see the comment in `TypeComparer#distributeAnd`. + * For more info, see the comment in `TypeComparer#glbArgs`. */ final val alignArgsInAnd = true @@ -113,6 +106,13 @@ object Config { */ final val checkTypeRefCycles = false + /** If this flag is set, it is checked that class type parameters are + * only references with NoPrefix or ThisTypes as prefixes. This option + * is usally disabled, because there are still some legitimate cases where + * this can arise (e.g. for pos/Map.scala, in LambdaType.integrate). + */ + final val checkTypeParamRefs = false + /** The recursion depth for showing a summarized string */ final val summarizeDepth = 2 diff --git a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala index b96bdb47f160..ddb6d0d142e8 100644 --- a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -67,7 +67,6 @@ class ScalaSettings extends Settings.SettingGroup { val Ycheck = PhasesSetting("-Ycheck", "Check the tree at the end of") val YcheckMods = BooleanSetting("-Ycheck-mods", "Check that symbols and their defining trees have modifiers in sync") val debug = BooleanSetting("-Ydebug", "Increase the quantity of debugging output.") - val debugAlias = BooleanSetting("-Ydebug-alias", "Never follow alias when printing types") val debugTrace = BooleanSetting("-Ydebug-trace", "Trace core operations") val debugFlags = BooleanSetting("-Ydebug-flags", "Print all flags of definitions") val debugNames = BooleanSetting("-Ydebug-names", "Show internal representation of names") @@ -87,7 +86,8 @@ class ScalaSettings extends Settings.SettingGroup { val YmethodInfer = BooleanSetting("-Yinfer-argument-types", "Infer types for arguments of overriden methods.") val YtraceContextCreation = BooleanSetting("-Ytrace-context-creation", "Store stack trace of context creations.") val YshowSuppressedErrors = BooleanSetting("-Yshow-suppressed-errors", "Also show follow-on errors and warnings that are normally supressed.") - val Yheartbeat = BooleanSetting("-Yheartbeat", "show heartbeat stack trace of compiler operations.") + val YdetailedStats = BooleanSetting("-Ydetailed-stats", "show detailed internal compiler stats (needs Stats.enabled to be set to true).") + val Yheartbeat = BooleanSetting("-Ydetailed-stats", "show heartbeat stack trace of compiler operations (needs Stats.enabled to be set to true).") val Yprintpos = BooleanSetting("-Yprintpos", "show tree positions.") val YnoDeepSubtypes = BooleanSetting("-Yno-deep-subtypes", "throw an exception on deep subtyping call stacks.") val YnoPatmatOpt = BooleanSetting("-Yno-patmat-opt", "disable all pattern matching optimizations.") diff --git a/compiler/src/dotty/tools/dotc/core/Annotations.scala b/compiler/src/dotty/tools/dotc/core/Annotations.scala index 17b1dba722ef..136edfc6b22d 100644 --- a/compiler/src/dotty/tools/dotc/core/Annotations.scala +++ b/compiler/src/dotty/tools/dotc/core/Annotations.scala @@ -103,7 +103,7 @@ object Annotations { private def resolveConstructor(atp: Type, args:List[Tree])(implicit ctx: Context): Tree = { val targs = atp.argTypes - tpd.applyOverloaded(New(atp withoutArgs targs), nme.CONSTRUCTOR, args, targs, atp, isAnnotConstructor = true) + tpd.applyOverloaded(New(atp.typeConstructor), nme.CONSTRUCTOR, args, targs, atp, isAnnotConstructor = true) } def applyResolve(atp: Type, args: List[Tree])(implicit ctx: Context): Annotation = { diff --git a/compiler/src/dotty/tools/dotc/core/CheckRealizable.scala b/compiler/src/dotty/tools/dotc/core/CheckRealizable.scala index 78ec685fc114..67ff6f505335 100644 --- a/compiler/src/dotty/tools/dotc/core/CheckRealizable.scala +++ b/compiler/src/dotty/tools/dotc/core/CheckRealizable.scala @@ -33,6 +33,12 @@ object CheckRealizable { class HasProblemBounds(typ: SingleDenotation)(implicit ctx: Context) extends Realizability(i" has a member $typ with possibly conflicting bounds ${typ.info.bounds.lo} <: ... <: ${typ.info.bounds.hi}") + class HasProblemBaseArg(typ: Type, argBounds: TypeBounds)(implicit ctx: Context) + extends Realizability(i" has a base type $typ with possibly conflicting parameter bounds ${argBounds.lo} <: ... <: ${argBounds.hi}") + + class HasProblemBase(base1: Type, base2: Type)(implicit ctx: Context) + extends Realizability(i" has conflicting base types $base1 and $base2") + class HasProblemField(fld: SingleDenotation, problem: Realizability)(implicit ctx: Context) extends Realizability(i" has a member $fld which is not a legal path\n since ${fld.symbol.name}: ${fld.info}${problem.msg}") @@ -89,18 +95,36 @@ class CheckRealizable(implicit ctx: Context) { else boundsRealizability(tp).andAlso(memberRealizability(tp)) } - /** `Realizable` if `tp` has good bounds, a `HasProblemBounds` instance - * pointing to a bad bounds member otherwise. + /** `Realizable` if `tp` has good bounds, a `HasProblem...` instance + * pointing to a bad bounds member otherwise. "Has good bounds" means: + * + * - all type members have good bounds + * - all base types are class types, and if their arguments are wildcards + * they have good bounds. */ private def boundsRealizability(tp: Type) = { - def hasBadBounds(mbr: SingleDenotation) = { - val bounds = mbr.info.bounds - !(bounds.lo <:< bounds.hi) - } - tp.nonClassTypeMembers.find(hasBadBounds) match { - case Some(mbr) => new HasProblemBounds(mbr) - case _ => Realizable + val mbrProblems = + for { + mbr <- tp.nonClassTypeMembers + if !(mbr.info.loBound <:< mbr.info.hiBound) + } + yield new HasProblemBounds(mbr) + + def baseTypeProblems(base: Type) = base match { + case AndType(base1, base2) => + new HasProblemBase(base1, base2) :: Nil + case base => + base.argInfos.collect { + case bounds @ TypeBounds(lo, hi) if !(lo <:< hi) => + new HasProblemBaseArg(base, bounds) + } } + val baseProblems = + tp.baseClasses.map(_.baseTypeOf(tp)).flatMap(baseTypeProblems) + + (((Realizable: Realizability) + /: mbrProblems)(_ andAlso _) + /: baseProblems)(_ andAlso _) } /** `Realizable` if all of `tp`'s non-struct fields have realizable types, diff --git a/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala b/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala index 81ee865aa96c..69f2666d6f18 100644 --- a/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala +++ b/compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala @@ -6,7 +6,7 @@ import Types._, Contexts._, Symbols._ import Decorators._ import config.Config import config.Printers.{constr, typr} -import TypeApplications.EtaExpansion +import TypeApplications.{EtaExpansion, TypeParamInfo} import collection.mutable /** Methods for adding constraints and solving them. @@ -194,9 +194,15 @@ trait ConstraintHandling { final def approximation(param: TypeParamRef, fromBelow: Boolean): Type = { val avoidParam = new TypeMap { override def stopAtStatic = true + def avoidInArg(arg: Type, formal: TypeParamInfo): Type = + if (param.occursIn(arg)) TypeBounds.empty else arg def apply(tp: Type) = mapOver { tp match { - case tp: RefinedType if param occursIn tp.refinedInfo => tp.parent + case tp @ AppliedType(tycon, args) => + tp.derivedAppliedType(tycon, + args.zipWithConserve(tycon.typeParams)(avoidInArg)) + case tp: RefinedType if param occursIn tp.refinedInfo => + tp.parent case tp: WildcardType => val bounds = tp.optBounds.orElse(TypeBounds.empty).bounds // Try to instantiate the wildcard to a type that is known to conform to it. @@ -306,7 +312,12 @@ trait ConstraintHandling { /** The current bounds of type parameter `param` */ final def bounds(param: TypeParamRef): TypeBounds = { val e = constraint.entry(param) - if (e.exists) e.bounds else param.binder.paramInfos(param.paramNum) + if (e.exists) e.bounds + else { + val pinfos = param.binder.paramInfos + if (pinfos != null) pinfos(param.paramNum) // pinfos == null happens in pos/i536.scala + else TypeBounds.empty + } } /** Add type lambda `tl`, possibly with type variables `tvars`, to current constraint @@ -318,7 +329,7 @@ trait ConstraintHandling { checkPropagated(i"initialized $tl") { constraint = constraint.add(tl, tvars) tl.paramNames.indices.forall { i => - val param = TypeParamRef(tl, i) + val param = tl.paramRefs(i) val bounds = constraint.nonParamBounds(param) val lower = constraint.lower(param) val upper = constraint.upper(param) @@ -376,7 +387,12 @@ trait ConstraintHandling { else tp def addParamBound(bound: TypeParamRef) = - if (fromBelow) addLess(bound, param) else addLess(param, bound) + constraint.entry(param) match { + case _: TypeBounds => + if (fromBelow) addLess(bound, param) else addLess(param, bound) + case tp => + if (fromBelow) isSubType(bound, tp) else isSubType(tp, bound) + } /** Drop all constrained parameters that occur at the toplevel in `bound` and * handle them by `addLess` calls. diff --git a/compiler/src/dotty/tools/dotc/core/Contexts.scala b/compiler/src/dotty/tools/dotc/core/Contexts.scala index bdc0be6e6ca3..955faf505301 100644 --- a/compiler/src/dotty/tools/dotc/core/Contexts.scala +++ b/compiler/src/dotty/tools/dotc/core/Contexts.scala @@ -14,7 +14,6 @@ import NameOps._ import Uniques._ import SymDenotations._ import Comments._ -import Flags.ParamAccessor import util.Positions._ import ast.Trees._ import ast.untpd @@ -335,7 +334,7 @@ object Contexts { * from constructor parameters to class parameter accessors. */ def superCallContext: Context = { - val locals = newScopeWith(owner.asClass.paramAccessors: _*) + val locals = newScopeWith(owner.typeParams ++ owner.asClass.paramAccessors: _*) superOrThisCallContext(owner.primaryConstructor, locals) } @@ -604,20 +603,20 @@ object Contexts { override def hash(x: Type): Int = x.hash } - /** A table for hash consing unique refined types */ - private[dotc] val uniqueRefinedTypes = new RefinedUniques + /** A table for hash consing unique applied types */ + private[dotc] val uniqueAppliedTypes = new AppliedUniques /** A table for hash consing unique named types */ private[core] val uniqueNamedTypes = new NamedTypeUniques - /** A table for hash consing unique type bounds */ - private[core] val uniqueTypeAliases = new TypeAliasUniques + /** A table for hash consing unique symbolic named types */ + private[core] val uniqueWithFixedSyms = new WithFixedSymUniques private def uniqueSets = Map( "uniques" -> uniques, - "uniqueRefinedTypes" -> uniqueRefinedTypes, - "uniqueNamedTypes" -> uniqueNamedTypes, - "uniqueTypeAliases" -> uniqueTypeAliases) + "uniqueAppliedTypes" -> uniqueAppliedTypes, + "uniqueWithFixedSyms" -> uniqueWithFixedSyms, + "uniqueNamedTypes" -> uniqueNamedTypes) /** A map that associates label and size of all uniques sets */ def uniquesSizes: Map[String, Int] = uniqueSets.mapValues(_.size) diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index ce2ce247060c..ca879d95e03b 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -80,10 +80,9 @@ class Definitions { val typeParam = enterSyntheticTypeParam(cls, paramFlags, paramDecls) def instantiate(tpe: Type) = if (tpe.typeParams.nonEmpty) tpe.appliedTo(typeParam.typeRef) - else tpe - val parents = parentConstrs.toList map instantiate - val parentRefs: List[TypeRef] = ctx.normalizeToClassRefs(parents, cls, paramDecls) - denot.info = ClassInfo(ScalaPackageClass.thisType, cls, parentRefs, paramDecls) + else tpe.dealias + val parents = parentConstrs.toList + denot.info = ClassInfo(ScalaPackageClass.thisType, cls, parents, paramDecls) } } newClassSymbol(ScalaPackageClass, name, EmptyFlags, completer).entered @@ -123,7 +122,7 @@ class Definitions { if (name.firstPart.startsWith(str.ImplicitFunction)) { val superTrait = FunctionType(arity).appliedTo(argParams.map(_.typeRef) ::: resParam.typeRef :: Nil) - (ImplicitMethodType, ctx.normalizeToClassRefs(superTrait :: Nil, cls, decls)) + (ImplicitMethodType, superTrait :: Nil) } else (MethodType, Nil) val applyMeth = @@ -250,7 +249,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_, TypeParamRef(_, 0), Final) + lazy val Any_asInstanceOf = enterT1ParameterlessMethod(AnyClass, nme.asInstanceOf_, _.paramRefs(0), Final) lazy val Any_typeTest = enterT1ParameterlessMethod(AnyClass, nme.isInstanceOfPM, _ => BooleanType, Final | Synthetic) // generated by pattern matcher, eliminated by erasure @@ -278,7 +277,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(TypeParamRef(pt, 0)), TypeParamRef(pt, 0)), Final) + pt => MethodType(List(pt.paramRefs(0)), pt.paramRefs(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)) @@ -295,7 +294,7 @@ class Definitions { */ lazy val cbnArg = enterPolyMethod( OpsPackageClass, nme.cbnArg, 1, - pt => MethodType(List(FunctionOf(Nil, TypeParamRef(pt, 0))), TypeParamRef(pt, 0))) + pt => MethodType(List(FunctionOf(Nil, pt.paramRefs(0))), pt.paramRefs(0))) /** Method representing a throw */ lazy val throwMethod = enterMethod(OpsPackageClass, nme.THROWkw, @@ -719,7 +718,7 @@ class Definitions { if (ctx.erasedTypes) JavaArrayType(elem) else ArrayType.appliedTo(elem :: Nil) def unapply(tp: Type)(implicit ctx: Context): Option[Type] = tp.dealias match { - case at: RefinedType if (at isRef ArrayType.symbol) && at.argInfos.length == 1 => Some(at.argInfos.head) + case AppliedType(at, arg :: Nil) if at isRef ArrayType.symbol => Some(arg) case _ => None } } diff --git a/compiler/src/dotty/tools/dotc/core/Denotations.scala b/compiler/src/dotty/tools/dotc/core/Denotations.scala index 14a93191e4a6..1041ca74cfe9 100644 --- a/compiler/src/dotty/tools/dotc/core/Denotations.scala +++ b/compiler/src/dotty/tools/dotc/core/Denotations.scala @@ -644,6 +644,9 @@ object Denotations { // ------ Forming types ------------------------------------------- /** The TypeRef representing this type denotation at its original location. */ + def appliedRef(implicit ctx: Context): Type = + typeRef.appliedTo(symbol.typeParams.map(_.typeRef)) + def typeRef(implicit ctx: Context): TypeRef = TypeRef(symbol.owner.thisType, symbol.name.asTypeName, this) diff --git a/compiler/src/dotty/tools/dotc/core/Flags.scala b/compiler/src/dotty/tools/dotc/core/Flags.scala index 59135bf21441..175fe3acc115 100644 --- a/compiler/src/dotty/tools/dotc/core/Flags.scala +++ b/compiler/src/dotty/tools/dotc/core/Flags.scala @@ -1,4 +1,5 @@ -package dotty.tools.dotc.core +package dotty.tools.dotc +package core import language.implicitConversions @@ -248,9 +249,7 @@ object Flags { /** A field generated for a primary constructor parameter (no matter if it's a 'val' or not), * or an accessor of such a field. */ - final val ParamAccessor = commonFlag(14, "") - final val TermParamAccessor = ParamAccessor.toTermFlags - final val TypeParamAccessor = ParamAccessor.toTypeFlags + final val ParamAccessor = termFlag(14, "") /** A value or class implementing a module */ final val Module = commonFlag(15, "module") @@ -304,12 +303,6 @@ object Flags { /** A case parameter accessor */ final val CaseAccessor = termFlag(25, "") - /** A binding for a type parameter of a base class or trait. - */ - final val BaseTypeArg = typeFlag(25, "") - - final val CaseAccessorOrBaseTypeArg = CaseAccessor.toCommonFlags - /** A super accessor */ final val Scala2SuperAccessor = termFlag(26, "") @@ -450,9 +443,9 @@ object Flags { /** Flags guaranteed to be set upon symbol creation */ final val FromStartFlags = - Module | Package | Deferred | MethodOrHKCommon | Param | ParamAccessor | + Module | Package | Deferred | MethodOrHKCommon | Param | ParamAccessor.toCommonFlags | Scala2ExistentialCommon | Mutable.toCommonFlags | Touched | JavaStatic | - CovariantOrOuter | ContravariantOrLabel | CaseAccessorOrBaseTypeArg | + CovariantOrOuter | ContravariantOrLabel | CaseAccessor.toCommonFlags | Fresh | Erroneous | ImplicitCommon | Permanent | Synthetic | SuperAccessorOrScala2x | Inline @@ -495,7 +488,8 @@ object Flags { final val SelfSymFlags = Private | Local | Deferred /** The flags of a class type parameter */ - final def ClassTypeParamCreationFlags = TypeParam | Deferred | Protected | Local + final val ClassTypeParamCreationFlags = + TypeParam | Deferred | Private | Local /** Flags that can apply to both a module val and a module class, except those that * are added at creation anyway @@ -555,8 +549,8 @@ object Flags { /** An inline parameter */ final val InlineParam = allOf(Inline, Param) - /** A parameter or parameter accessor */ - final val ParamOrAccessor = Param | ParamAccessor + /** A term parameter or parameter accessor */ + final val TermParamOrAccessor = Param | ParamAccessor /** A lazy or deferred value */ final val LazyOrDeferred = Lazy | Deferred @@ -567,18 +561,12 @@ object Flags { /** A synthetic or private definition */ final val SyntheticOrPrivate = Synthetic | Private - /** A type parameter or type parameter accessor */ - final val TypeParamOrAccessor = TypeParam | TypeParamAccessor - /** A deferred member or a parameter accessor (these don't have right hand sides) */ final val DeferredOrParamAccessor = Deferred | ParamAccessor /** value that's final or inline */ final val FinalOrInline = Final | Inline - /** If symbol of a type alias has these flags, prefer the alias */ - final val AliasPreferred = TypeParam | BaseTypeArg - /** A covariant type parameter instance */ final val LocalCovariant = allOf(Local, Covariant) @@ -591,6 +579,9 @@ object Flags { /** Is valid forever */ final val ValidForever = Package | Permanent | Scala2ExistentialCommon + /** A type parameter of a class or trait */ + final val ClassTypeParam = allOf(TypeParam, Private) + /** Is a default parameter in Scala 2*/ final val DefaultParameter = allOf(Param, DefaultParameterized) diff --git a/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala b/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala index b83061e8bc35..279e545cc2bd 100644 --- a/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala +++ b/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala @@ -298,7 +298,7 @@ class OrderingConstraint(private val boundsMap: ParamBounds, val loBuf, hiBuf = new mutable.ListBuffer[TypeParamRef] var i = 0 while (i < poly.paramNames.length) { - val param = TypeParamRef(poly, i) + val param = poly.paramRefs(i) val bounds = nonParamBounds(param) val lo = normalizedType(bounds.lo, loBuf, isUpper = false) val hi = normalizedType(bounds.hi, hiBuf, isUpper = true) @@ -463,12 +463,12 @@ class OrderingConstraint(private val boundsMap: ParamBounds, (poly, entries) <- boundsMap.toList n <- 0 until paramCount(entries) if entries(n).exists - } yield TypeParamRef(poly, n) + } yield poly.paramRefs(n) def forallParams(p: TypeParamRef => Boolean): Boolean = { boundsMap.foreachBinding { (poly, entries) => for (i <- 0 until paramCount(entries)) - if (isBounds(entries(i)) && !p(TypeParamRef(poly, i))) return false + if (isBounds(entries(i)) && !p(poly.paramRefs(i))) return false } true } diff --git a/compiler/src/dotty/tools/dotc/core/Periods.scala b/compiler/src/dotty/tools/dotc/core/Periods.scala index 892a1f59a5c4..3d4a288915d6 100644 --- a/compiler/src/dotty/tools/dotc/core/Periods.scala +++ b/compiler/src/dotty/tools/dotc/core/Periods.scala @@ -38,6 +38,15 @@ abstract class Periods extends DotClass { self: Context => } Period(runId, first, nxTrans) } + + /** Are all base types in the current period guaranteed to be the same as in period `p`? */ + def hasSameBaseTypesAs(p: Period) = { + val period = this.period + period == p || + period.runId == p.runId && + this.phases(period.phaseId).sameParentsStartId == + this.phases(p.phaseId).sameParentsStartId + } } object Periods { diff --git a/compiler/src/dotty/tools/dotc/core/Substituters.scala b/compiler/src/dotty/tools/dotc/core/Substituters.scala index d565ec2293aa..fabcd5712d26 100644 --- a/compiler/src/dotty/tools/dotc/core/Substituters.scala +++ b/compiler/src/dotty/tools/dotc/core/Substituters.scala @@ -16,7 +16,7 @@ trait Substituters { this: Context => else tp.derivedSelect(subst(tp.prefix, from, to, theMap)) case _: ThisType | NoPrefix => tp - case tp: RefinedType => + case tp: RefinedType => // @!!! remove tp.derivedRefinedType(subst(tp.parent, from, to, theMap), tp.refinedName, subst(tp.refinedInfo, from, to, theMap)) case tp: TypeAlias => tp.derivedTypeAlias(subst(tp.alias, from, to, theMap)) diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index 473ae32f9dc1..07e4f0ed0128 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -46,8 +46,8 @@ trait SymDenotations { this: Context => else { val initial = denot.initial val firstPhaseId = initial.validFor.firstPhaseId.max(ctx.typerPhase.id) - if ((initial ne denot) || ctx.phaseId != firstPhaseId) { - ctx.withPhase(firstPhaseId).stillValidInOwner(initial) || + if ((initial ne denot) || ctx.phaseId != firstPhaseId) { + ctx.withPhase(firstPhaseId).stillValidInOwner(initial) || // Workaround #1895: A symbol might not be entered into an owner // until the second phase where it exists (denot.validFor.containsPhaseId(firstPhaseId + 1)) && @@ -699,7 +699,7 @@ object SymDenotations { | is not a subclass of ${owner.showLocated} where target is defined""") else if ( !( isType // allow accesses to types from arbitrary subclasses fixes #4737 - || pre.baseTypeRef(cls).exists // ??? why not use derivesFrom ??? + || pre.derivesFrom(cls) || isConstructor || (owner is ModuleClass) // don't perform this check for static members )) @@ -1174,7 +1174,7 @@ object SymDenotations { 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: AppliedType => 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: AnnotatedType => hasSkolems(tp.tpe) @@ -1269,10 +1269,10 @@ object SymDenotations { private[this] var myMemberCache: LRUCache[Name, PreDenotation] = null private[this] var myMemberCachePeriod: Period = Nowhere - /** A cache from types T to baseTypeRef(T, C) */ - type BaseTypeRefMap = java.util.HashMap[CachedType, Type] - private[this] var myBaseTypeRefCache: BaseTypeRefMap = null - private[this] var myBaseTypeRefCachePeriod: Period = Nowhere + /** A cache from types T to baseType(T, C) */ + type BaseTypeMap = java.util.HashMap[CachedType, Type] + private[this] var myBaseTypeCache: BaseTypeMap = null + private[this] var myBaseTypeCachePeriod: Period = Nowhere private var baseDataCache: BaseData = BaseData.None private var memberNamesCache: MemberNames = MemberNames.None @@ -1285,14 +1285,12 @@ object SymDenotations { myMemberCache } - private def baseTypeRefCache(implicit ctx: Context): BaseTypeRefMap = { - if (myBaseTypeRefCachePeriod != ctx.period && - (myBaseTypeRefCachePeriod.runId != ctx.runId || - ctx.phases(myBaseTypeRefCachePeriod.phaseId).sameParentsStartId != ctx.phase.sameParentsStartId)) { - myBaseTypeRefCache = new BaseTypeRefMap - myBaseTypeRefCachePeriod = ctx.period + private def baseTypeCache(implicit ctx: Context): BaseTypeMap = { + if (!ctx.hasSameBaseTypesAs(myBaseTypeCachePeriod)) { + myBaseTypeCache = new BaseTypeMap + myBaseTypeCachePeriod = ctx.period } - myBaseTypeRefCache + myBaseTypeCache } private def invalidateBaseDataCache() = { @@ -1305,9 +1303,9 @@ object SymDenotations { memberNamesCache = MemberNames.None } - def invalidateBaseTypeRefCache() = { - myBaseTypeRefCache = null - myBaseTypeRefCachePeriod = Nowhere + def invalidateBaseTypeCache() = { + myBaseTypeCache = null + myBaseTypeCachePeriod = Nowhere } override def copyCaches(from: SymDenotation, phase: Phase)(implicit ctx: Context): this.type = { @@ -1316,7 +1314,7 @@ object SymDenotations { if (from.memberNamesCache.isValidAt(phase)) memberNamesCache = from.memberNamesCache if (from.baseDataCache.isValidAt(phase)) { baseDataCache = from.baseDataCache - myBaseTypeRefCache = from.baseTypeRefCache + myBaseTypeCache = from.baseTypeCache } case _ => } @@ -1367,9 +1365,8 @@ object SymDenotations { super.info_=(tp) } - /** The denotations of all parents in this class. */ - def classParents(implicit ctx: Context): List[TypeRef] = info match { - case classInfo: ClassInfo => classInfo.classParents + def classParents(implicit ctx: Context): List[Type] = info match { + case classInfo: ClassInfo => classInfo.parents case _ => Nil } @@ -1382,6 +1379,14 @@ object SymDenotations { NoSymbol } + /** The explicitly given self type (self types of modules are assumed to be + * explcitly given here). + */ + def givenSelfType(implicit ctx: Context) = classInfo.selfInfo match { + case tp: Type => tp + case self: Symbol => self.info + } + // ------ class-specific operations ----------------------------------- private[this] var myThisType: Type = null @@ -1397,16 +1402,9 @@ object SymDenotations { } private def computeThisType(implicit ctx: Context): Type = - ThisType.raw( - TypeRef(if (this is Package) NoPrefix else owner.thisType, symbol.asType)) -/* else { - val pre = owner.thisType - if (this is Module) - if (isMissing(pre)) TermRef(pre, sourceModule.asTerm) - else TermRef.withSig(pre, name.sourceModuleName, Signature.NotAMethod) - else ThisType.raw(TypeRef(pre, symbol.asType)) - } -*/ + ThisType.raw(TypeRef( + if (this is Package) NoPrefix else owner.thisType, symbol.asType)) + private[this] var myTypeRef: TypeRef = null override def typeRef(implicit ctx: Context): TypeRef = { @@ -1414,6 +1412,8 @@ object SymDenotations { myTypeRef } + override def appliedRef(implicit ctx: Context): Type = classInfo.appliedRef + private def baseData(implicit onBehalf: BaseData, ctx: Context): (List[ClassSymbol], BaseClassSet) = { if (!baseDataCache.isValid) baseDataCache = BaseData.newCache() baseDataCache(this) @@ -1435,7 +1435,10 @@ object SymDenotations { if (classParents.isEmpty && !emptyParentsExpected) onBehalf.signalProvisional() val builder = new BaseDataBuilder - for (p <- classParents) builder.addAll(p.symbol.asClass.baseClasses) + for (p <- classParents) { + assert(p.typeSymbol.isClass, s"$this has $p") + builder.addAll(p.typeSymbol.asClass.baseClasses) + } (classSymbol :: builder.baseClasses, builder.baseClassSet) } @@ -1561,10 +1564,10 @@ object SymDenotations { val ownDenots = info.decls.denotsNamed(name, selectNonPrivate) if (debugTrace) // DEBUG println(s"$this.member($name), ownDenots = $ownDenots") - def collect(denots: PreDenotation, parents: List[TypeRef]): PreDenotation = parents match { + def collect(denots: PreDenotation, parents: List[Type]): PreDenotation = parents match { case p :: ps => val denots1 = collect(denots, ps) - p.symbol.denot match { + p.typeSymbol.denot match { case parentd: ClassDenotation => denots1 union parentd.nonPrivateMembersNamed(name) @@ -1584,11 +1587,11 @@ object SymDenotations { raw.filterExcluded(excluded).asSeenFrom(pre).toDenot(pre) } - /** Compute tp.baseTypeRef(this) */ - final def baseTypeRefOf(tp: Type)(implicit ctx: Context): Type = { + /** Compute tp.baseType(this) */ + final def baseTypeOf(tp: Type)(implicit ctx: Context): Type = { def foldGlb(bt: Type, ps: List[Type]): Type = ps match { - case p :: ps1 => foldGlb(bt & baseTypeRefOf(p), ps1) + case p :: ps1 => foldGlb(bt & baseTypeOf(p), ps1) case _ => bt } @@ -1600,40 +1603,70 @@ object SymDenotations { * and this changes subtyping relations. As a shortcut, we do not * cache ErasedValueType at all. */ - def isCachable(tp: Type, btrCache: BaseTypeRefMap): Boolean = { + def isCachable(tp: Type, btrCache: BaseTypeMap): Boolean = { def inCache(tp: Type) = btrCache.containsKey(tp) tp match { case _: TypeErasure.ErasedValueType => false case tp: TypeRef if tp.symbol.isClass => true case tp: TypeVar => tp.inst.exists && inCache(tp.inst) - case tp: TypeProxy => inCache(tp.underlying) - case tp: AndOrType => inCache(tp.tp1) && inCache(tp.tp2) + //case tp: TypeProxy => inCache(tp.underlying) // disabled, can re-enable insyead of last two lines for performance testing + //case tp: AndOrType => inCache(tp.tp1) && inCache(tp.tp2) + case tp: TypeProxy => isCachable(tp.underlying, btrCache) + case tp: AndOrType => isCachable(tp.tp1, btrCache) && isCachable(tp.tp2, btrCache) case _ => true } } - def computeBaseTypeRefOf(tp: Type): Type = { - Stats.record("computeBaseTypeOf") - if (symbol.isStatic && tp.derivesFrom(symbol)) + def computeBaseTypeOf(tp: Type): Type = { + if (Stats.monitored) { + Stats.record("computeBaseType, total") + Stats.record(s"computeBaseType, ${tp.getClass}") + } + if (symbol.isStatic && tp.derivesFrom(symbol) && symbol.typeParams.isEmpty) symbol.typeRef else tp match { - case tp: TypeRef => - val subcls = tp.symbol - if (subcls eq symbol) - tp - else subcls.denot match { - case cdenot: ClassDenotation => - if (cdenot.baseClassSet contains symbol) foldGlb(NoType, tp.parents) + case tp @ TypeRef(prefix, _) => + val subsym = tp.symbol + if (subsym eq symbol) tp + else subsym.denot match { + case clsd: ClassDenotation => + val owner = clsd.owner + val isOwnThis = prefix match { + case prefix: ThisType => prefix.cls eq owner + case NoPrefix => true + case _ => false + } + if (isOwnThis) + if (clsd.baseClassSet.contains(symbol)) foldGlb(NoType, clsd.classParents) + else NoType + else + baseTypeOf(clsd.typeRef).asSeenFrom(prefix, owner) + case _ => + baseTypeOf(tp.superType) + } + case tp @ AppliedType(tycon, args) => + val subsym = tycon.typeSymbol + if (subsym eq symbol) tp + else subsym.denot match { + case clsd: ClassDenotation => + val tparams = clsd.typeParams + if (tparams.hasSameLengthAs(args)) baseTypeOf(tycon).subst(tparams, args) else NoType case _ => - baseTypeRefOf(tp.superType) + baseTypeOf(tp.superType) } case tp: TypeProxy => - baseTypeRefOf(tp.superType) + baseTypeOf(tp.superType) case AndType(tp1, tp2) => - baseTypeRefOf(tp1) & baseTypeRefOf(tp2) + baseTypeOf(tp1) & baseTypeOf(tp2) match { + case AndType(tp1a, tp2a) if (tp1a eq tp1) && (tp2a eq tp2) => tp + case res => res + } case OrType(tp1, tp2) => - baseTypeRefOf(tp1) | baseTypeRefOf(tp2) + baseTypeOf(tp1) | baseTypeOf(tp2) match { + case OrType(tp1a, tp2a) if (tp1a eq tp1) && (tp2a eq tp2) => tp + case res => res + } case JavaArrayType(_) if symbol == defn.ObjectClass => this.typeRef case _ => @@ -1641,16 +1674,22 @@ object SymDenotations { } } - /*>|>*/ ctx.debugTraceIndented(s"$tp.baseTypeRef($this)") /*<|<*/ { - tp match { + /*>|>*/ ctx.debugTraceIndented(s"$tp.baseType($this)") /*<|<*/ { + Stats.record("baseTypeOf") + tp.stripTypeVar match { // @!!! dealias? case tp: CachedType => - val btrCache = baseTypeRefCache + val btrCache = baseTypeCache try { var basetp = btrCache get tp if (basetp == null) { btrCache.put(tp, NoPrefix) - basetp = computeBaseTypeRefOf(tp) - if (isCachable(tp, baseTypeRefCache)) btrCache.put(tp, basetp) + basetp = computeBaseTypeOf(tp) + if (!basetp.exists) Stats.record("base type miss") + if (isCachable(tp, btrCache)) { + if (basetp.exists) Stats.record("cached base type hit") + else Stats.record("cached base type miss") + btrCache.put(tp, basetp) + } else btrCache.remove(tp) } else if (basetp == NoPrefix) throw CyclicReference(this) @@ -1661,8 +1700,8 @@ object SymDenotations { btrCache.put(tp, null) throw ex } - case _ => - computeBaseTypeRefOf(tp) + case tp => + computeBaseTypeOf(tp) } } } @@ -1679,7 +1718,7 @@ object SymDenotations { var names = Set[Name]() def maybeAdd(name: Name) = if (keepOnly(thisType, name)) names += name for (p <- classParents) - for (name <- p.symbol.asClass.memberNames(keepOnly)) + for (name <- p.typeSymbol.asClass.memberNames(keepOnly)) maybeAdd(name) val ownSyms = if (keepOnly eq implicitFilter) @@ -1711,11 +1750,11 @@ object SymDenotations { else constrNamed(nme.CONSTRUCTOR).orElse(constrNamed(nme.TRAIT_CONSTRUCTOR)) } - /** The parameter accessors of this class. Term and type accessors, - * getters and setters are all returned int his list + /** The term parameter accessors of this class. + * Both getters and setters are returned in this list. */ def paramAccessors(implicit ctx: Context): List[Symbol] = - unforcedDecls.filter(_ is ParamAccessor).toList + unforcedDecls.filter(_.is(ParamAccessor)).toList /** If this class has the same `decls` scope reference in `phase` and * `phase.next`, install a new denotation with a cloned scope in `phase.next`. diff --git a/compiler/src/dotty/tools/dotc/core/Symbols.scala b/compiler/src/dotty/tools/dotc/core/Symbols.scala index b5c3baac2cf1..a0ede493042e 100644 --- a/compiler/src/dotty/tools/dotc/core/Symbols.scala +++ b/compiler/src/dotty/tools/dotc/core/Symbols.scala @@ -123,8 +123,7 @@ trait Symbols { this: Context => def complete(denot: SymDenotation)(implicit ctx: Context): Unit = { val cls = denot.asClass.classSymbol val decls = newScope - val parentRefs: List[TypeRef] = normalizeToClassRefs(parentTypes, cls, decls) - denot.info = ClassInfo(owner.thisType, cls, parentRefs, decls) + denot.info = ClassInfo(owner.thisType, cls, parentTypes.map(_.dealias), decls) } } newClassSymbol(owner, name, flags, completer, privateWithin, coord, assocFile) diff --git a/compiler/src/dotty/tools/dotc/core/TypeApplications.scala b/compiler/src/dotty/tools/dotc/core/TypeApplications.scala index 9397eeb09d7b..449901579e6a 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeApplications.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeApplications.scala @@ -15,7 +15,7 @@ import NameKinds._ import Flags._ import StdNames.tpnme import util.Positions.Position -import config.Printers.core +import config.Printers.{core, typr} import collection.mutable import dotty.tools.dotc.config.Config import java.util.NoSuchElementException @@ -25,23 +25,11 @@ object TypeApplications { type TypeParamInfo = ParamInfo.Of[TypeName] /** Assert type is not a TypeBounds instance and return it unchanged */ - val noBounds = (tp: Type) => tp match { + def noBounds(tp: Type) = tp match { case tp: TypeBounds => throw new AssertionError("no TypeBounds allowed") case _ => tp } - /** If `tp` is a TypeBounds instance return its lower bound else return `tp` */ - val boundsToLo = (tp: Type) => tp match { - case tp: TypeBounds => tp.lo - case _ => tp - } - - /** If `tp` is a TypeBounds instance return its upper bound else return `tp` */ - val boundsToHi = (tp: Type) => tp match { - case tp: TypeBounds => tp.hi - case _ => tp - } - /** Does variance `v1` conform to variance `v2`? * This is the case if the variances are the same or `sym` is nonvariant. */ @@ -76,48 +64,11 @@ object TypeApplications { } def unapply(tp: Type)(implicit ctx: Context): Option[TypeRef] = tp match { - case tp @ HKTypeLambda(tparams, AppliedType(fn: TypeRef, args)) if (args == tparams.map(_.toArg)) => Some(fn) + case tp @ HKTypeLambda(tparams, AppliedType(fn: TypeRef, args)) if (args == tparams.map(_.paramRef)) => Some(fn) case _ => None } } - /** Extractor for type application T[U_1, ..., U_n]. This is the refined type - * - * T { type p_1 v_1= U_1; ...; type p_n v_n= U_n } - * - * where v_i, p_i are the variances and names of the type parameters of T. - */ - object AppliedType { - def apply(tp: Type, args: List[Type])(implicit ctx: Context): Type = tp.appliedTo(args) - - def unapply(tp: Type)(implicit ctx: Context): Option[(Type, List[Type])] = tp match { - case tp: RefinedType => - var refinements: List[RefinedType] = Nil - var tycon = tp.stripTypeVar - while (tycon.isInstanceOf[RefinedType]) { - val rt = tycon.asInstanceOf[RefinedType] - refinements = rt :: refinements - tycon = rt.parent.stripTypeVar - } - def collectArgs(tparams: List[TypeParamInfo], - refinements: List[RefinedType], - argBuf: mutable.ListBuffer[Type]): Option[(Type, List[Type])] = refinements match { - case Nil if tparams.isEmpty && argBuf.nonEmpty => - Some((tycon, argBuf.toList)) - case RefinedType(_, rname, rinfo) :: refinements1 - if tparams.nonEmpty && rname == tparams.head.paramName => - collectArgs(tparams.tail, refinements1, argBuf += rinfo.argInfo) - case _ => - None - } - collectArgs(tycon.typeParams, refinements, new mutable.ListBuffer[Type]) - case HKApply(tycon, args) => - Some((tycon, args)) - case _ => - None - } - } - /** Adapt all arguments to possible higher-kinded type parameters using etaExpandIfHK */ def EtaExpandIfHK(tparams: List[TypeParamInfo], args: List[Type])(implicit ctx: Context): List[Type] = @@ -170,10 +121,23 @@ object TypeApplications { p.binder == tycon && args(p.paramNum).isInstanceOf[TypeBounds] def canReduceWildcard(p: TypeParamRef) = !ctx.mode.is(Mode.AllowLambdaWildcardApply) || available.contains(p.paramNum) + + // If this is a reference to a reducable type parameter corresponding to a + // wildcard argument, return the wildcard argument, otherwise apply recursively. + def applyArg(arg: Type): Type = arg match { + case p: TypeParamRef if hasWildcardArg(p) && canReduceWildcard(p) => + available -= p.paramNum + args(p.paramNum) + case _ => + apply(arg) + } + def apply(t: Type) = t match { case t @ TypeAlias(p: TypeParamRef) if hasWildcardArg(p) && canReduceWildcard(p) => - available -= p.paramNum + available -= p.paramNum // @!!! needed in the future? args(p.paramNum) + case t @ AppliedType(tycon, args1) if tycon.typeSymbol.isClass => + t.derivedAppliedType(apply(tycon), args1.mapConserve(applyArg)) case p: TypeParamRef if p.binder == tycon => args(p.paramNum) match { case TypeBounds(lo, hi) => @@ -183,7 +147,7 @@ object TypeApplications { case arg => arg } - case _: TypeBounds | _: HKApply => + case _: TypeBounds | _: AppliedType => val saved = available available = Set() try mapOver(t) @@ -205,32 +169,29 @@ class TypeApplications(val self: Type) extends AnyVal { * For a typeref referring to a Lambda class, the type parameters of * its right hand side or upper bound. * For a refinement type, the type parameters of its parent, dropping - * any type parameter that is-rebound by the refinement. "Re-bind" means: - * The refinement contains a TypeAlias for the type parameter, or - * it introduces bounds for the type parameter, and we are not in the - * special case of a type Lambda, where a LambdaTrait gets refined - * with the bounds on its hk args. See `LambdaAbstract`, where these - * types get introduced, and see `isBoundedLambda` below for the test. + * any type parameter that is-rebound by the refinement. */ final def typeParams(implicit ctx: Context): List[TypeParamInfo] = /*>|>*/ track("typeParams") /*<|<*/ { self match { - case self: ClassInfo => - self.cls.typeParams - case self: HKTypeLambda => - self.typeParams case self: TypeRef => val tsym = self.symbol if (tsym.isClass) tsym.typeParams else if (!tsym.isCompleting) tsym.info.typeParams else Nil + case self: ClassInfo => + self.cls.typeParams + case self: HKTypeLambda => + self.typeParams case self: RefinedType => - self.parent.typeParams.filterNot(_.paramName == self.refinedName) + self.parent.typeParams case self: RecType => self.parent.typeParams case _: SingletonType => Nil case self: WildcardType => self.optBounds.typeParams + case self: AppliedType if self.tycon.typeSymbol.isClass => + Nil case self: TypeProxy => self.superType.typeParams case _ => @@ -257,6 +218,7 @@ class TypeApplications(val self: Type) extends AnyVal { def hkResult(implicit ctx: Context): Type = self.dealias match { case self: TypeRef => self.info.hkResult case self: RefinedType => NoType + case self: AppliedType => NoType case self: HKTypeLambda => self.resultType case self: SingletonType => NoType case self: TypeVar => @@ -289,19 +251,12 @@ class TypeApplications(val self: Type) extends AnyVal { self } - /** Convert a type constructor `TC` which has type parameters `T1, ..., Tn` - * in a context where type parameters `U1,...,Un` are expected to - * - * LambdaXYZ { Apply = TC[hk$0, ..., hk$n] } - * - * Here, XYZ corresponds to the variances of - * - `U1,...,Un` if the variances of `T1,...,Tn` are pairwise compatible with `U1,...,Un`, - * - `T1,...,Tn` otherwise. - * v1 is compatible with v2, if v1 = v2 or v2 is non-variant. + /** Convert a type constructor `TC` which has type parameters `X1, ..., Xn` + * to `[X1, ..., Xn] -> TC[X1, ..., Xn]`. */ def EtaExpand(tparams: List[TypeSymbol])(implicit ctx: Context): Type = { val tparamsToUse = if (variancesConform(typeParams, tparams)) tparams else typeParamSymbols - HKTypeLambda.fromParams(tparamsToUse, self.appliedTo(tparams map (_.typeRef))) + HKTypeLambda.fromParams(tparamsToUse, self.appliedTo(tparams.map(_.typeRef))) //.ensuring(res => res.EtaReduce =:= self, s"res = $res, core = ${res.EtaReduce}, self = $self, hc = ${res.hashCode}") } @@ -386,19 +341,6 @@ 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[ParamInfo], args: List[Type])(implicit ctx: Context): Type = args match { - case arg :: args1 => - try { - val tparam :: tparams1 = tparams - matchParams(RefinedType(t, tparam.paramName, arg.toBounds(tparam)), tparams1, args1) - } catch { - case ex: MatchError => - println(s"applied type mismatch: $self with underlying ${self.underlyingIfProxy}, args = $args, typeParams = $typParams") // !!! DEBUG - //println(s"precomplete decls = ${self.typeSymbol.unforcedDecls.toList.map(_.denot).mkString("\n ")}") - throw ex - } - case nil => t - } val stripped = self.stripTypeVar val dealiased = stripped.safeDealias if (args.isEmpty || ctx.erasedTypes) self @@ -418,7 +360,7 @@ class TypeApplications(val self: Type) extends AnyVal { } } if ((dealiased eq stripped) || followAlias) dealiased.instantiate(args) - else HKApply(self, args) + else AppliedType(self, args) } else dealiased.resType match { case AppliedType(tycon, args1) if tycon.safeDealias ne tycon => @@ -431,7 +373,7 @@ class TypeApplications(val self: Type) extends AnyVal { val reducer = new Reducer(dealiased, args) val reduced = reducer(dealiased.resType) if (reducer.allReplaced) reduced - else HKApply(dealiased, args) + else AppliedType(dealiased, args) } tryReduce case dealiased: PolyType => @@ -448,10 +390,8 @@ class TypeApplications(val self: Type) extends AnyVal { WildcardType(dealiased.optBounds.appliedTo(args).bounds) case dealiased: TypeRef if dealiased.symbol == defn.NothingClass => dealiased - case _ if typParams.isEmpty || typParams.head.isInstanceOf[LambdaParam] => - HKApply(self, args) case dealiased => - matchParams(dealiased, typParams, args) + AppliedType(self, args) } } @@ -468,80 +408,15 @@ class TypeApplications(val self: Type) extends AnyVal { */ final def safeAppliedTo(args: List[Type])(implicit ctx: Context) = self match { case self: TypeRef if !self.symbol.isClass && self.symbol.isCompleting => - HKApply(self, args) + AppliedType(self, args) case _ => appliedTo(args) } - /** Turn this type, which is used as an argument for - * type parameter `tparam`, into a TypeBounds RHS - */ - final def toBounds(tparam: ParamInfo)(implicit ctx: Context): TypeBounds = self match { - case self: TypeBounds => // this can happen for wildcard args - self - case _ => - val v = tparam.paramVariance - TypeAlias(self, v) - } - - /** The type arguments of this type's base type instance wrt. `base`. - * Existential types in arguments are returned as TypeBounds instances. - */ - final def baseArgInfos(base: Symbol)(implicit ctx: Context): List[Type] = - if (self derivesFrom base) - self.dealias match { - case self: TypeRef if !self.symbol.isClass => self.superType.baseArgInfos(base) - case self: HKApply => self.superType.baseArgInfos(base) - case _ => base.typeParams.map(param => self.member(param.name).info.argInfo) - } - else - Nil - - /** The type arguments of this type's base type instance wrt.`base`. - * Existential types in arguments are disallowed. - */ - final def baseArgTypes(base: Symbol)(implicit ctx: Context): List[Type] = - baseArgInfos(base) mapConserve noBounds - - /** The type arguments of this type's base type instance wrt.`base`. - * Existential types in arguments are approximated by their lower bound. - */ - final def baseArgTypesLo(base: Symbol)(implicit ctx: Context): List[Type] = - baseArgInfos(base) mapConserve boundsToLo - - /** The type arguments of this type's base type instance wrt.`base`. - * Existential types in arguments are approximated by their upper bound. - */ - final def baseArgTypesHi(base: Symbol)(implicit ctx: Context): List[Type] = - baseArgInfos(base) mapConserve boundsToHi - - /** The base type including all type arguments and applicable refinements - * of this type. Refinements are applicable if they refine a member of - * the parent type which furthermore is not a name-mangled type parameter. - * Existential types in arguments are returned as TypeBounds instances. - */ - final def baseTypeWithArgs(base: Symbol)(implicit ctx: Context): Type = ctx.traceIndented(s"btwa ${self.show} wrt $base", core, show = true) { - def default = self.baseTypeRef(base).appliedTo(baseArgInfos(base)) - def isExpandedTypeParam(sym: Symbol) = sym.is(TypeParam) && sym.name.is(ExpandedName) - self match { - case tp: TypeRef => - tp.info match { - case TypeBounds(_, hi) => hi.baseTypeWithArgs(base) - case _ => default - } - case tp @ RefinedType(parent, name, _) if !isExpandedTypeParam(tp.member(name).symbol) => - tp.wrapIfMember(parent.baseTypeWithArgs(base)) - case tp: TermRef => - tp.underlying.baseTypeWithArgs(base) - case tp: HKApply => - tp.superType.baseTypeWithArgs(base) - case AndType(tp1, tp2) => - tp1.baseTypeWithArgs(base) & tp2.baseTypeWithArgs(base) - case OrType(tp1, tp2) => - tp1.baseTypeWithArgs(base) | tp2.baseTypeWithArgs(base) - case _ => - default - } + /** Turns non-bounds types to type aliases */ + final def toBounds(implicit ctx: Context): TypeBounds = self match { + case self: TypeBounds => self // this can happen for wildcard args + case _ => TypeAlias(self) } /** Translate a type of the form From[T] to To[T], keep other types as they are. @@ -552,9 +427,7 @@ class TypeApplications(val self: Type) extends AnyVal { case self @ ExprType(tp) => self.derivedExprType(tp.translateParameterized(from, to)) case _ => - if (self.derivesFrom(from)) - if (ctx.erasedTypes) to.typeRef - else RefinedType(to.typeRef, to.typeParams.head.name, self.member(from.typeParams.head.name).info) + if (self.derivesFrom(from)) to.typeRef.appliedTo(self.baseType(from).argInfos) else self } @@ -581,25 +454,10 @@ class TypeApplications(val self: Type) extends AnyVal { def argTypes(implicit ctx: Context) = argInfos mapConserve noBounds /** Argument types where existential types in arguments are approximated by their lower bound */ - def argTypesLo(implicit ctx: Context) = argInfos mapConserve boundsToLo + def argTypesLo(implicit ctx: Context) = argInfos.mapConserve(_.loBound) /** Argument types where existential types in arguments are approximated by their upper bound */ - def argTypesHi(implicit ctx: Context) = argInfos mapConserve boundsToHi - - /** The core type without any type arguments. - * @param `typeArgs` must be the type arguments of this type. - */ - final def withoutArgs(typeArgs: List[Type]): Type = self match { - case HKApply(tycon, args) => tycon - case _ => - typeArgs match { - case _ :: typeArgs1 => - val RefinedType(tycon, _, _) = self - tycon.withoutArgs(typeArgs1) - case nil => - self - } - } + def argTypesHi(implicit ctx: Context) = argInfos.mapConserve(_.hiBound) /** If this is the image of a type argument; recover the type argument, * otherwise NoType. @@ -620,6 +478,6 @@ class TypeApplications(val self: Type) extends AnyVal { def elemType(implicit ctx: Context): Type = self match { case defn.ArrayOf(elemtp) => elemtp case JavaArrayType(elemtp) => elemtp - case _ => baseArgInfos(defn.SeqClass).headOption.getOrElse(NoType) + case _ => self.baseType(defn.SeqClass).argInfos.headOption.getOrElse(NoType) } } diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index 7dec61044aa9..b94ab60eee33 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -336,11 +336,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { else thirdTry(tp1, tp2) case tp1 @ OrType(tp11, tp12) => def joinOK = tp2.dealias match { - case _: HKApply => - // If we apply the default algorithm for `A[X] | B[Y] <: C[Z]` where `C` is a - // type parameter, we will instantiate `C` to `A` and then fail when comparing - // with `B[Y]`. To do the right thing, we need to instantiate `C` to the - // common superclass of `A` and `B`. + case tp2: AppliedType if !tp2.tycon.typeSymbol.isClass => isSubType(tp1.join, tp2) case _ => false @@ -368,14 +364,19 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { case _ => val cls2 = tp2.symbol if (cls2.isClass) { - val base = tp1.baseTypeRef(cls2) - if (base.exists && (base ne tp1)) return isSubType(base, tp2) + val base = tp1.baseType(cls2) + if (base.exists) { + if (cls2.is(JavaDefined)) return base.typeSymbol == cls2 + if (base ne tp1) return isSubType(base, tp2) + } if (cls2 == defn.SingletonClass && tp1.isStable) return true } fourthTry(tp1, tp2) } private def thirdTry(tp1: Type, tp2: Type): Boolean = tp2 match { + case tp2 @ AppliedType(tycon2, args2) => + compareAppliedType2(tp1, tp2, tycon2, args2) case tp2: NamedType => thirdTryNamed(tp1, tp2) case tp2: TypeParamRef => @@ -427,19 +428,17 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { case tp2: RecType => def compareRec = tp1.safeDealias match { case tp1: RecType => - val rthis1 = RecThis(tp1) + val rthis1 = tp1.recThis isSubType(tp1.parent, tp2.parent.substRecThis(tp2, rthis1)) case _ => val tp1stable = ensureStableSingleton(tp1) isSubType(fixRecs(tp1stable, tp1stable.widenExpr), tp2.parent.substRecThis(tp2, tp1stable)) } compareRec - case tp2 @ HKApply(tycon2, args2) => - compareHkApply2(tp1, tp2, tycon2, args2) 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 + /* 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: * @@ -521,11 +520,13 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { case _ => isSubType(tp1.widenExpr, restpe2) } compareExpr + case tp2: TypeArgRef => + isSubType(tp1, tp2.underlying.loBound) || fourthTry(tp1, tp2) case tp2 @ TypeBounds(lo2, hi2) => def compareTypeBounds = tp1 match { case tp1 @ TypeBounds(lo1, hi1) => - (tp2.variance > 0 && tp1.variance >= 0 || (lo2 eq NothingType) || isSubType(lo2, lo1)) && - (tp2.variance < 0 && tp1.variance <= 0 || (hi2 eq AnyType) || isSubType(hi1, hi2)) + ((lo2 eq NothingType) || isSubType(lo2, lo1)) && + ((hi2 eq AnyType) || isSubType(hi1, hi2)) case tp1: ClassInfo => tp2 contains tp1 case _ => @@ -560,6 +561,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { def isNullable(tp: Type): Boolean = tp.widenDealias match { case tp: TypeRef => tp.symbol.isNullableClass case tp: RefinedOrRecType => isNullable(tp.parent) + case tp: AppliedType => isNullable(tp.tycon) case AndType(tp1, tp2) => isNullable(tp1) && isNullable(tp2) case OrType(tp1, tp2) => isNullable(tp1) || isNullable(tp2) case _ => false @@ -569,6 +571,8 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { (sym1 eq NullClass) && isNullable(tp2) || (sym1 eq PhantomNothingClass) && tp1.topType == tp2.topType } + case tp1 @ AppliedType(tycon1, args1) => + compareAppliedType1(tp1, tycon1, args1, tp2) case tp1: SingletonType => /** if `tp2 == p.type` and `p: q.type` then try `tp1 <:< q.type` as a last effort.*/ def comparePaths = tp2 match { @@ -588,8 +592,8 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { isNewSubType(tp1.parent, tp2) case tp1: RecType => isNewSubType(tp1.parent, tp2) - case tp1 @ HKApply(tycon1, args1) => - compareHkApply1(tp1, tycon1, args1, tp2) + case tp1: TypeArgRef => + isSubType(tp1.underlying.hiBound, tp2) case tp1: HKTypeLambda => def compareHKLambda = tp1 match { case EtaExpansion(tycon1) => isSubType(tycon1, tp2) @@ -633,9 +637,10 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { false } + /** Subtype test for the hk application `tp2 = tycon2[args2]`. */ - def compareHkApply2(tp1: Type, tp2: HKApply, tycon2: Type, args2: List[Type]): Boolean = { + def compareAppliedType2(tp1: Type, tp2: AppliedType, tycon2: Type, args2: List[Type]): Boolean = { val tparams = tycon2.typeParams if (tparams.isEmpty) return false // can happen for ill-typed programs, e.g. neg/tcpoly_overloaded.scala @@ -643,17 +648,17 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { * corresponding arguments are subtypes relative to their variance (see `isSubArgs`). */ def isMatchingApply(tp1: Type): Boolean = tp1 match { - case HKApply(tycon1, args1) => + case AppliedType(tycon1, args1) => tycon1.dealias match { case tycon1: TypeParamRef => (tycon1 == tycon2 || canConstrain(tycon1) && tryInstantiate(tycon1, tycon2)) && - isSubArgs(args1, args2, tparams) + isSubArgs(args1, args2, tp1, tparams) case tycon1: TypeRef => tycon2.dealias match { case tycon2: TypeRef if tycon1.symbol == tycon2.symbol => isSubType(tycon1.prefix, tycon2.prefix) && - isSubArgs(args1, args2, tparams) + isSubArgs(args1, args2, tp1, tparams) case _ => false } @@ -688,35 +693,36 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { * * such that the resulting type application is a supertype of `tp1`. */ - def tyconOK(tycon1a: Type, args1: List[Type]) = { - var tycon1b = tycon1a - val tparams1a = tycon1a.typeParams - val lengthDiff = tparams1a.length - tparams.length - lengthDiff >= 0 && { - val tparams1 = tparams1a.drop(lengthDiff) - variancesConform(tparams1, tparams) && { - if (lengthDiff > 0) - 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(TypeParamRef(tl, _)))) - (ctx.mode.is(Mode.TypevarsMissContext) || - tryInstantiate(tycon2, tycon1b.ensureHK)) && - isSubType(tp1, tycon1b.appliedTo(args2)) + def appOK(tp1base: Type) = tp1base match { + case tp1base: AppliedType => + var tycon1 = tp1base.tycon + var args1 = tp1base.args + val tparams1all = tycon1.typeParams + val lengthDiff = tparams1all.length - tparams.length + lengthDiff >= 0 && { + val tparams1 = tparams1all.drop(lengthDiff) + variancesConform(tparams1, tparams) && { + if (lengthDiff > 0) + tycon1 = HKTypeLambda(tparams1.map(_.paramName))( + tl => tparams1.map(tparam => tl.integrate(tparams, tparam.paramInfo).bounds), + tl => tp1base.tycon.appliedTo(args1.take(lengthDiff) ++ + tparams1.indices.toList.map(tl.paramRefs(_)))) + (ctx.mode.is(Mode.TypevarsMissContext) || + tryInstantiate(tycon2, tycon1.ensureHK)) && + isSubType(tp1, tycon1.appliedTo(args2)) + } } - } + case _ => false } tp1.widen match { - case tp1w @ HKApply(tycon1, args1) => - tyconOK(tycon1, args1) + case tp1w: AppliedType => appOK(tp1w) case tp1w => tp1w.typeSymbol.isClass && { val classBounds = tycon2.classSymbols def liftToBase(bcs: List[ClassSymbol]): Boolean = bcs match { case bc :: bcs1 => - classBounds.exists(bc.derivesFrom) && - tyconOK(tp1w.baseTypeRef(bc), tp1w.baseArgInfos(bc)) || + classBounds.exists(bc.derivesFrom) && appOK(tp1w.baseType(bc)) || liftToBase(bcs1) case _ => false @@ -757,30 +763,42 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { canConstrain(param2) && canInstantiate(param2) || compareLower(bounds(param2), tyconIsTypeRef = false) case tycon2: TypeRef => - isMatchingApply(tp1) || - compareLower(tycon2.info.bounds, tyconIsTypeRef = true) + isMatchingApply(tp1) || { + tycon2.info match { + case info2: TypeBounds => + compareLower(info2, tyconIsTypeRef = true) + case info2: ClassInfo => + val base = tp1.baseType(info2.cls) + if (base.exists && base.ne(tp1)) isSubType(base, tp2) + else fourthTry(tp1, tp2) + case _ => + fourthTry(tp1, tp2) + } + } case _: TypeVar | _: AnnotatedType => isSubType(tp1, tp2.superType) - case tycon2: HKApply => + case tycon2: AppliedType => fallback(tycon2.lowerBound) case _ => false } } - /** Subtype test for the hk application `tp1 = tycon1[args1]`. + /** Subtype test for the application `tp1 = tycon1[args1]`. */ - def compareHkApply1(tp1: HKApply, tycon1: Type, args1: List[Type], tp2: Type): Boolean = + def compareAppliedType1(tp1: AppliedType, tycon1: Type, args1: List[Type], tp2: Type): Boolean = tycon1 match { case param1: TypeParamRef => def canInstantiate = tp2 match { case AppliedType(tycon2, args2) => - tryInstantiate(param1, tycon2.ensureHK) && isSubArgs(args1, args2, tycon2.typeParams) + tryInstantiate(param1, tycon2.ensureHK) && isSubArgs(args1, args2, tp1, tycon2.typeParams) case _ => false } canConstrain(param1) && canInstantiate || isSubType(bounds(param1).hi.applyIfParameterized(args1), tp2) + case tycon1: TypeRef if tycon1.symbol.isClass => + false case tycon1: TypeProxy => isSubType(tp1.superType, tp2) case _ => @@ -790,19 +808,42 @@ 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[ParamInfo]): Boolean = + def isSubArgs(args1: List[Type], args2: List[Type], tp1: Type, tparams: List[ParamInfo]): Boolean = if (args1.isEmpty) args2.isEmpty else args2.nonEmpty && { - val v = tparams.head.paramVariance - def isSub(tp1: Type, tp2: Type) = tp2 match { - case tp2: TypeBounds => - tp2.contains(tp1) + val tparam = tparams.head + val v = tparam.paramVariance + + def compareCaptured(arg1: Type, arg2: Type): Boolean = arg1 match { + case arg1: TypeBounds => + val captured = TypeArgRef.fromParam(tp1, tparam.asInstanceOf[TypeSymbol]) + isSubArg(captured, arg2) + case _ => + false + } + + def isSubArg(arg1: Type, arg2: Type): Boolean = arg2 match { + case arg2: TypeBounds => + arg2.contains(arg1) || compareCaptured(arg1, arg2) case _ => - (v > 0 || isSubType(tp2, tp1)) && - (v < 0 || isSubType(tp1, tp2)) + arg1 match { + case arg1: TypeBounds => + compareCaptured(arg1, arg2) + case _ => + (v > 0 || isSubType(arg2, arg1)) && + (v < 0 || isSubType(arg1, arg2)) + } + } + + val arg1 = args1.head + val arg2 = args2.head + isSubArg(arg1, arg2) || { + // last effort: try to adapt variances of higher-kinded types if this is sound. + // TODO: Move this to eta-expansion? + val adapted2 = arg2.adaptHkVariances(tparam.paramInfo) + adapted2.ne(arg2) && isSubArg(arg1, adapted2) } - isSub(args1.head, args2.head) - } && isSubArgs(args1.tail, args2.tail, tparams.tail) + } && isSubArgs(args1.tail, args2.tail, tp1, tparams.tail) /** Test whether `tp1` has a base type of the form `B[T1, ..., Tn]` where * - `B` derives from one of the class symbols of `tp2`, @@ -813,12 +854,11 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { val classBounds = tp2.classSymbols def recur(bcs: List[ClassSymbol]): Boolean = bcs match { case bc :: bcs1 => - val baseRef = tp1.baseTypeRef(bc) (classBounds.exists(bc.derivesFrom) && - variancesConform(baseRef.typeParams, tparams) && - p(baseRef.appliedTo(tp1.baseArgInfos(bc))) - || - recur(bcs1)) + variancesConform(bc.typeParams, tparams) && + p(tp1.baseType(bc)) + || + recur(bcs1)) case nil => false } @@ -923,8 +963,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { tp2.refinedInfo match { case rinfo2: TypeBounds => val ref1 = tp1.widenExpr.select(name) - (rinfo2.variance > 0 || isSubType(rinfo2.lo, ref1)) && - (rinfo2.variance < 0 || isSubType(ref1, rinfo2.hi)) + isSubType(rinfo2.lo, ref1) && isSubType(ref1, rinfo2.hi) case _ => false } @@ -974,6 +1013,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { def hasSubRefinement(tp1: RefinedType, refine2: Type): Boolean = { isSubType(tp1.refinedInfo, refine2) || { // last effort: try to adapt variances of higher-kinded types if this is sound. + // TODO: Move this to eta-expansion? val adapted2 = refine2.adaptHkVariances(tp1.parent.member(tp1.refinedName).symbol.info) adapted2.ne(refine2) && hasSubRefinement(tp1, adapted2) } @@ -986,10 +1026,11 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { /** A type has been covered previously in subtype checking if it * is some combination of TypeRefs that point to classes, where the - * combiners are RefinedTypes, RecTypes, And/Or-Types or AnnotatedTypes. + * combiners are AppliedTypes, RefinedTypes, RecTypes, And/Or-Types or AnnotatedTypes. */ private def isCovered(tp: Type): Boolean = tp.dealias.stripTypeVar match { case tp: TypeRef => tp.symbol.isClass && tp.symbol != NothingClass && tp.symbol != NullClass + case tp: AppliedType => isCovered(tp.tycon) case tp: RefinedOrRecType => isCovered(tp.parent) case tp: AnnotatedType => isCovered(tp.underlying) case tp: AndOrType => isCovered(tp.tp1) && isCovered(tp.tp2) @@ -1203,6 +1244,56 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { final def lub(tps: List[Type]): Type = ((defn.NothingType: Type) /: tps)(lub(_,_, canConstrain = false)) + def lubArgs(args1: List[Type], args2: List[Type], tparams: List[TypeParamInfo], canConstrain: Boolean = false): List[Type] = + tparams match { + case tparam :: tparamsRest => + val arg1 :: args1Rest = args1 + val arg2 :: args2Rest = args2 + val v = tparam.paramVariance + val lubArg = + if (v > 0) lub(arg1.hiBound, arg2.hiBound, canConstrain) + else if (v < 0) glb(arg1.loBound, arg2.loBound) + else TypeBounds(glb(arg1.loBound, arg2.loBound), + lub(arg1.hiBound, arg2.hiBound, canConstrain)) + lubArg :: lubArgs(args1Rest, args2Rest, tparamsRest, canConstrain) + case nil => + Nil + } + + /** Try to produce joint arguments for a glb `A[T_1, ..., T_n] & A[T_1', ..., T_n']` using + * the following strategies: + * + * - if corresponding parameter variance is co/contra-variant, the glb/lub. + * - if arguments are the same, that argument. + * - if at least one of the arguments if a TypeBounds, the union of + * the bounds. + * - if homogenizeArgs is set, and arguments can be unified by instantiating + * type parameters, the unified argument. + * - otherwise NoType + * + * The unification rule is contentious because it cuts the constraint set. + * Therefore it is subject to Config option `alignArgsInAnd`. + */ + def glbArgs(args1: List[Type], args2: List[Type], tparams: List[TypeParamInfo]): List[Type] = + tparams match { + case tparam :: tparamsRest => + val arg1 :: args1Rest = args1 + val arg2 :: args2Rest = args2 + val v = tparam.paramVariance + val glbArg = + if (isSameTypeWhenFrozen(arg1, arg2)) arg1 + else if (v > 0) glb(arg1.hiBound, arg2.hiBound) + else if (v < 0) lub(arg1.loBound, arg2.loBound) + else if (arg1.isInstanceOf[TypeBounds] || arg2.isInstanceOf[TypeBounds]) + TypeBounds(lub(arg1.loBound, arg2.loBound), + glb(arg1.hiBound, arg2.hiBound)) + else if (homogenizeArgs && !frozenConstraint && isSameType(arg1, arg2)) arg1 + else NoType + glbArg :: glbArgs(args1Rest, args2Rest, tparamsRest) + case nil => + Nil + } + private def recombineAndOr(tp: AndOrType, tp1: Type, tp2: Type) = if (!tp1.exists) tp2 else if (!tp2.exists) tp1 @@ -1342,34 +1433,23 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { * @pre !(tp1 <: tp2) && !(tp2 <:< tp1) -- these cases were handled before */ private def distributeAnd(tp1: Type, tp2: Type): Type = tp1 match { + case tp1 @ AppliedType(tycon1, args1) => + tp2 match { + case AppliedType(tycon2, args2) if tycon1.typeSymbol == tycon2.typeSymbol => + val jointArgs = glbArgs(args1, args2, tycon1.typeParams) + if (jointArgs.forall(_.exists)) (tycon1 & tycon2).appliedTo(jointArgs) + else NoType + case _ => + NoType + } // opportunistically merge same-named refinements // this does not change anything semantically (i.e. merging or not merging // gives =:= types), but it keeps the type smaller. case tp1: RefinedType => tp2 match { case tp2: RefinedType if tp1.refinedName == tp2.refinedName => - // Given two refinements `T1 { X = S1 }` and `T2 { X = S2 }` rewrite to - // `T1 & T2 { X B }` where `B` is the conjunction of the bounds of `X` in `T1` and `T2`. - // - // However, if `homogenizeArgs` is set, and both aliases `X = Si` are - // nonvariant, and `S1 =:= S2` (possibly by instantiating type parameters), - // rewrite instead to `T1 & T2 { X = S1 }`. This rule is contentious because - // it cuts the constraint set. On the other hand, without it we would replace - // the two aliases by `T { X >: S1 | S2 <: S1 & S2 }`, which looks weird - // and is probably not what's intended. - val rinfo1 = tp1.refinedInfo - val rinfo2 = tp2.refinedInfo - val parent = tp1.parent & tp2.parent - - def isNonvariantAlias(tp: Type) = tp match { - case tp: TypeAlias => tp.variance == 0 - case _ => false - } - if (homogenizeArgs && - isNonvariantAlias(rinfo1) && isNonvariantAlias(rinfo2)) - isSameType(rinfo1, rinfo2) // establish new constraint - - tp1.derivedRefinedType(parent, tp1.refinedName, rinfo1 & rinfo2) + tp1.derivedRefinedType(tp1.parent & tp2.parent, tp1.refinedName, + tp1.refinedInfo & tp2.refinedInfo) case _ => NoType } @@ -1468,15 +1548,17 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { /** Show subtype goal that led to an assertion failure */ def showGoal(tp1: Type, tp2: Type)(implicit ctx: Context) = { - println(ex"assertion failure for $tp1 <:< $tp2, frozen = $frozenConstraint") + println(i"assertion failure for $tp1 <:< $tp2, frozen = $frozenConstraint") def explainPoly(tp: Type) = tp match { 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}") } - explainPoly(tp1) - explainPoly(tp2) + if (Config.verboseExplainSubtype) { + explainPoly(tp1) + explainPoly(tp2) + } } /** Record statistics about the total number of subtype checks @@ -1556,17 +1638,17 @@ class ExplainingTypeComparer(initctx: Context) extends TypeComparer(initctx) { override def copyIn(ctx: Context) = new ExplainingTypeComparer(ctx) - override def compareHkApply2(tp1: Type, tp2: HKApply, tycon2: Type, args2: List[Type]): Boolean = { + override def compareAppliedType2(tp1: Type, tp2: AppliedType, tycon2: Type, args2: List[Type]): Boolean = { def addendum = "" - traceIndented(i"compareHkApply2 $tp1, $tp2$addendum") { - super.compareHkApply2(tp1, tp2, tycon2, args2) + traceIndented(i"compareAppliedType2 $tp1, $tp2$addendum") { + super.compareAppliedType2(tp1, tp2, tycon2, args2) } } - override def compareHkApply1(tp1: HKApply, tycon1: Type, args1: List[Type], tp2: Type): Boolean = { + override def compareAppliedType1(tp1: AppliedType, tycon1: Type, args1: List[Type], tp2: Type): Boolean = { def addendum = "" - traceIndented(i"compareHkApply1 $tp1, $tp2$addendum") { - super.compareHkApply1(tp1, tycon1, args1, tp2) + traceIndented(i"compareAppliedType1 $tp1, $tp2$addendum") { + super.compareAppliedType1(tp1, tycon1, args1, tp2) } } diff --git a/compiler/src/dotty/tools/dotc/core/TypeErasure.scala b/compiler/src/dotty/tools/dotc/core/TypeErasure.scala index 5e67e191e5cf..18c58ba6b456 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeErasure.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeErasure.scala @@ -381,20 +381,19 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean else if (defn.isPhantomTerminalClass(sym)) PhantomErasure.erasedPhantomType else if (sym eq defn.PhantomClass) defn.ObjectType // To erase the definitions of Phantom.{assume, Any, Nothing} else eraseNormalClassRef(tp) - case tp: RefinedType => - val parent = tp.parent - if (parent isRef defn.ArrayClass) eraseArray(tp) - else this(parent) + case tp: AppliedType => + if (tp.tycon.isRef(defn.ArrayClass)) eraseArray(tp) + else apply(tp.superType) case _: TermRef | _: ThisType => this(tp.widen) case SuperType(thistpe, supertpe) => SuperType(this(thistpe), this(supertpe)) case ExprType(rt) => defn.FunctionType(0) + case tp: TypeProxy => + this(tp.underlying) case AndType(tp1, tp2) => erasedGlb(this(tp1), this(tp2), isJava) - case tp: HKApply => - apply(tp.superType) case OrType(tp1, tp2) => ctx.typeComparer.orType(this(tp1), this(tp2), erased = true) case tp: MethodType => @@ -412,13 +411,12 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean } case tp: PolyType => this(tp.resultType) - case tp @ ClassInfo(pre, cls, classParents, decls, _) => + case tp @ ClassInfo(pre, cls, parents, decls, _) => if (cls is Package) tp else { - def eraseTypeRef(p: TypeRef) = this(p).asInstanceOf[TypeRef] - val parents: List[TypeRef] = + val erasedParents: List[Type] = if ((cls eq defn.ObjectClass) || cls.isPrimitiveValueClass) Nil - else classParents.mapConserve(eraseTypeRef) match { + else parents.mapConserve(apply) match { case tr :: trs1 => assert(!tr.classSymbol.is(Trait), cls) val tr1 = if (cls is Trait) defn.ObjectType else tr @@ -426,18 +424,16 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean case nil => nil } val erasedDecls = decls.filteredScope(sym => !sym.isType || sym.isClass) - tp.derivedClassInfo(NoPrefix, parents, erasedDecls, erasedRef(tp.selfType)) + tp.derivedClassInfo(NoPrefix, erasedParents, erasedDecls, erasedRef(tp.selfType)) // can't replace selftype by NoType because this would lose the sourceModule link } case NoType | NoPrefix | _: ErrorType | JavaArrayType(_) => tp case tp: WildcardType if wildcardOK => tp - case tp: TypeProxy => - this(tp.underlying) } - private def eraseArray(tp: RefinedType)(implicit ctx: Context) = { + private def eraseArray(tp: Type)(implicit ctx: Context) = { val defn.ArrayOf(elemtp) = tp def arrayErasure(tpToErase: Type) = erasureFn(isJava, semiEraseVCs = false, isConstructor, wildcardOK)(tpToErase) @@ -488,8 +484,10 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean // constructor method should not be semi-erased. else if (isConstructor && isDerivedValueClass(sym)) eraseNormalClassRef(tp) else this(tp) - case RefinedType(parent, _, _) if !(parent isRef defn.ArrayClass) => + case RefinedType(parent, _, _) if !(parent isRef defn.ArrayClass) => // @!!! eraseResult(parent) + case AppliedType(tycon, _) if !(tycon isRef defn.ArrayClass) => + eraseResult(tycon) case _ => this(tp) } @@ -509,8 +507,6 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean */ private def sigName(tp: Type)(implicit ctx: Context): TypeName = try { tp match { - case ErasedValueType(_, underlying) => - sigName(underlying) case tp: TypeRef => if (!tp.denot.exists) throw new MissingType(tp.prefix, tp.name) val sym = tp.symbol @@ -529,10 +525,13 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean sigName(PhantomErasure.erasedPhantomType) else normalizeClass(sym.asClass).fullName.asTypeName - case defn.ArrayOf(elem) => - sigName(this(tp)) - case tp: HKApply => - sigName(tp.superType) + case tp: AppliedType => + sigName( + if (tp.tycon.isRef(defn.ArrayClass)) this(tp) + else if (tp.tycon.typeSymbol.isClass) tp.underlying + else tp.superType) + case ErasedValueType(_, underlying) => + sigName(underlying) case JavaArrayType(elem) => sigName(elem) ++ "[]" case tp: TermRef => @@ -544,8 +543,6 @@ 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 8fbda8daf7b5..1d8a4da23f17 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeOps.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeOps.scala @@ -11,6 +11,7 @@ import NameKinds.DepParamName import Decorators._ import StdNames._ import Annotations._ +import annotation.tailrec import config.Config import util.{SimpleMap, Property} import collection.mutable @@ -38,30 +39,30 @@ trait TypeOps { this: Context => // TODO: Make standalone object. else pre match { case pre: SuperType => toPrefix(pre.thistpe, cls, thiscls) case _ => - if (thiscls.derivesFrom(cls) && pre.baseTypeRef(thiscls).exists) + if (thiscls.derivesFrom(cls) && pre.baseType(thiscls).exists) if (variance <= 0 && !isLegalPrefix(pre)) range(pre.bottomType, pre) else pre else if ((pre.termSymbol is Package) && !(thiscls is Package)) toPrefix(pre.select(nme.PACKAGE), cls, thiscls) else - toPrefix(pre.baseTypeRef(cls).normalizedPrefix, cls.owner, thiscls) + toPrefix(pre.baseType(cls).normalizedPrefix, cls.owner, thiscls) } } /*>|>*/ ctx.conditionalTraceIndented(TypeOps.track, s"asSeen ${tp.show} from (${pre.show}, ${cls.show})", show = true) /*<|<*/ { // !!! DEBUG - // One `case ThisType` is specific to asSeenFrom, all other cases are inlined for performance + // All cases except for ThisType are the same as in Map. Inlined for performance + // TODO: generalize the inlining trick? tp match { case tp: NamedType => - if (tp.symbol.isStatic) tp + val sym = tp.symbol + if (sym.isStatic) tp else derivedSelect(tp, atVariance(variance max 0)(this(tp.prefix))) case tp: ThisType => toPrefix(pre, cls, tp.cls) case _: BoundType | NoPrefix => tp - case tp: RefinedType => + case tp: RefinedType => //@!!! derivedRefinedType(tp, apply(tp.parent), apply(tp.refinedInfo)) - case tp: TypeAlias if tp.variance == 1 => // if variance != 1, need to do the variance calculation - derivedTypeAlias(tp, apply(tp.alias)) case _ => mapOver(tp) } @@ -69,7 +70,7 @@ trait TypeOps { this: Context => // TODO: Make standalone object. } override def reapply(tp: Type) = - // derives infos have already been subjected to asSeenFrom, hence to need to apply the map again. + // derived infos have already been subjected to asSeenFrom, hence to need to apply the map again. tp } @@ -102,16 +103,19 @@ trait TypeOps { this: Context => // TODO: Make standalone object. val bounds = ctx.typeComparer.bounds(tp) if (bounds.lo.isRef(defn.NothingClass)) bounds.hi else bounds.lo } - else typerState.constraint.typeVarOfParam(tp) orElse tp + else { + val tvar = typerState.constraint.typeVarOfParam(tp) + if (tvar.exists) tvar else tp + } case _: ThisType | _: BoundType | NoPrefix => tp - case tp: RefinedType => + case tp: RefinedType => // @!!! tp.derivedRefinedType(simplify(tp.parent, theMap), tp.refinedName, simplify(tp.refinedInfo, theMap)) case tp: TypeAlias => tp.derivedTypeAlias(simplify(tp.alias, theMap)) - case AndType(l, r) => + case AndType(l, r) if !ctx.mode.is(Mode.Type) => simplify(l, theMap) & simplify(r, theMap) - case OrType(l, r) => + case OrType(l, r) if !ctx.mode.is(Mode.Type) => simplify(l, theMap) | simplify(r, theMap) case _ => (if (theMap != null) theMap else new SimplifyMap).mapOver(tp) @@ -152,14 +156,22 @@ trait TypeOps { this: Context => // TODO: Make standalone object. defn.ObjectClass :: Nil } - def mergeRefined(tp1: Type, tp2: Type): Type = { + def mergeRefinedOrApplied(tp1: Type, tp2: Type): Type = { def fail = throw new AssertionError(i"Failure to join alternatives $tp1 and $tp2") tp1 match { case tp1 @ RefinedType(parent1, name1, rinfo1) => tp2 match { case RefinedType(parent2, `name1`, rinfo2) => tp1.derivedRefinedType( - mergeRefined(parent1, parent2), name1, rinfo1 | rinfo2) + mergeRefinedOrApplied(parent1, parent2), name1, rinfo1 | rinfo2) + case _ => fail + } + case tp1 @ AppliedType(tycon1, args1) => + tp2 match { + case AppliedType(tycon2, args2) => + tp1.derivedAppliedType( + mergeRefinedOrApplied(tycon1, tycon2), + ctx.typeComparer.lubArgs(args1, args2, tycon1.typeParams)) case _ => fail } case tp1 @ TypeRef(pre1, name1) => @@ -175,6 +187,7 @@ trait TypeOps { this: Context => // TODO: Make standalone object. def approximateOr(tp1: Type, tp2: Type): Type = { def isClassRef(tp: Type): Boolean = tp match { case tp: TypeRef => tp.symbol.isClass + case tp: AppliedType => isClassRef(tp.tycon) case tp: RefinedType => isClassRef(tp.parent) case _ => false } @@ -193,12 +206,8 @@ trait TypeOps { this: Context => // TODO: Make standalone object. case _ => val commonBaseClasses = tp.mapReduceOr(_.baseClasses)(intersect) val doms = dominators(commonBaseClasses, Nil) - def baseTp(cls: ClassSymbol): Type = { - val base = - if (tp1.typeParams.nonEmpty) tp.baseTypeRef(cls) - else tp.baseTypeWithArgs(cls) - base.mapReduceOr(identity)(mergeRefined) - } + def baseTp(cls: ClassSymbol): Type = + tp.baseType(cls).mapReduceOr(identity)(mergeRefinedOrApplied) doms.map(baseTp).reduceLeft(AndType.apply) } } @@ -212,35 +221,6 @@ trait TypeOps { this: Context => // TODO: Make standalone object. } } - /** Not currently needed: - * - def liftToRec(f: (Type, Type) => Type)(tp1: Type, tp2: Type)(implicit ctx: Context) = { - def f2(tp1: Type, tp2: Type): Type = tp2 match { - case tp2: RecType => tp2.rebind(f(tp1, tp2.parent)) - case _ => f(tp1, tp2) - } - tp1 match { - case tp1: RecType => tp1.rebind(f2(tp1.parent, tp2)) - case _ => f2(tp1, tp2) - } - } - */ - - private def enterArgBinding(formal: Symbol, info: Type, cls: ClassSymbol, decls: Scope) = { - val lazyInfo = new LazyType { // needed so we do not force `formal`. - def complete(denot: SymDenotation)(implicit ctx: Context): Unit = { - denot setFlag formal.flags & RetainedTypeArgFlags - denot.info = info - } - } - val sym = ctx.newSymbol( - cls, formal.name, - formal.flagsUNSAFE & RetainedTypeArgFlags | BaseTypeArg | Override, - lazyInfo, - coord = cls.coord) - cls.enter(sym, decls) - } - /** If `tpe` is of the form `p.x` where `p` refers to a package * but `x` is not owned by a package, expand it to * @@ -263,119 +243,6 @@ trait TypeOps { this: Context => // TODO: Make standalone object. } } - /** Normalize a list of parent types of class `cls` that may contain refinements - * to a list of typerefs referring to classes, by converting all refinements to member - * definitions in scope `decls`. Can add members to `decls` as a side-effect. - */ - def normalizeToClassRefs(parents: List[Type], cls: ClassSymbol, decls: Scope): List[TypeRef] = { - // println(s"normalizing $parents of $cls in ${cls.owner}") // !!! DEBUG - - // A map consolidating all refinements arising from parent type parameters - var refinements: SimpleMap[TypeName, Type] = SimpleMap.Empty - - // A map of all formal type parameters of base classes that get refined - var formals: SimpleMap[TypeName, Symbol] = SimpleMap.Empty // A map of all formal parent parameter - - // Strip all refinements from parent type, populating `refinements` and `formals` maps. - def normalizeToRef(tp: Type): TypeRef = { - def fail = throw new TypeError(s"unexpected parent type: $tp") - tp.dealias match { - case tp: TypeRef => - tp - case tp @ RefinedType(tp1, name: TypeName, rinfo) => - val prevInfo = refinements(name) - refinements = refinements.updated(name, - if (prevInfo == null) tp.refinedInfo else prevInfo & tp.refinedInfo) - formals = formals.updated(name, tp1.typeParamNamed(name)) - normalizeToRef(tp1) - case tp @ RefinedType(tp1, _: TermName, _) => - normalizeToRef(tp1) - case _: ErrorType => - defn.AnyType - case AnnotatedType(tpe, _) => - normalizeToRef(tpe) - case HKApply(tycon: TypeRef, args) => - tycon.info match { - case TypeAlias(alias) => normalizeToRef(alias.appliedTo(args)) - case _ => fail - } - case _ => - fail - } - } - - val parentRefs = parents map normalizeToRef - - // Enter all refinements into current scope. - refinements foreachBinding { (name, refinedInfo) => - assert(decls.lookup(name) == NoSymbol, // DEBUG - s"redefinition of ${decls.lookup(name).debugString} in ${cls.showLocated}") - enterArgBinding(formals(name), refinedInfo, cls, decls) - } - - if (Config.forwardTypeParams) - forwardParamBindings(parentRefs, refinements, cls, decls) - - parentRefs - } - - /** Forward parameter bindings in baseclasses to argument types of - * class `cls` if possible. - * If there have member definitions - * - * type param v= middle - * type middle v= to - * - * where the variances of both alias are the same, then enter a new definition - * - * type param v= to - * - * If multiple forwarders would be generated, join their `to` types with an `&`. - * - * @param cls The class for which parameter bindings should be forwarded - * @param decls Its scope - * @param parentRefs The parent type references of `cls` - * @param paramBindings The type parameter bindings generated for `cls` - * - */ - def forwardParamBindings(parentRefs: List[TypeRef], - paramBindings: SimpleMap[TypeName, Type], - cls: ClassSymbol, decls: Scope)(implicit ctx: Context) = { - - def forwardRef(argSym: Symbol, from: TypeName, to: TypeAlias) = argSym.info match { - case info @ TypeAlias(TypeRef(_: ThisType, `from`)) if info.variance == to.variance => - val existing = decls.lookup(argSym.name) - if (existing.exists) existing.info = existing.info & to - else enterArgBinding(argSym, to, cls, decls) - case _ => - } - - def forwardRefs(from: TypeName, to: Type) = to match { - case to: TypeAlias => - for (pref <- parentRefs) { - def forward()(implicit ctx: Context): Unit = - for (argSym <- pref.decls) - if (argSym is BaseTypeArg) forwardRef(argSym, from, to) - pref.info match { - case info: TempClassInfo => info.addSuspension(implicit ctx => forward()) - case _ => forward() - } - } - case _ => - } - - paramBindings.foreachBinding(forwardRefs) - } - - /** Used only for debugging: All BaseTypeArg definitions in - * `cls` and all its base classes. - */ - def allBaseTypeArgs(cls: ClassSymbol)(implicit ctx: Context) = - for { bc <- cls.baseClasses - sym <- bc.info.decls.toList - if sym.is(BaseTypeArg) - } yield sym - /** An argument bounds violation is a triple consisting of * - the argument tree * - a string "upper" or "lower" indicating which bound is violated diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 32ce37b10a9f..c0c271864a44 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -62,7 +62,7 @@ object Types { * | +- TypeParamRef * | +- RefinedOrRecType -+-- RefinedType * | | -+-- RecType - * | +- HKApply + * | +- AppliedType * | +- TypeBounds * | +- ExprType * | +- AnnotatedType @@ -129,22 +129,17 @@ object Types { case TypeAlias(tp) => assert((tp ne this) && (tp ne this1), s"$tp / $this") tp.isRef(sym) - case _ => this1.symbol eq sym + case _ => this1.symbol eq sym } - case this1: RefinedOrRecType => this1.parent.isRef(sym) - case this1: HKApply => + case this1: RefinedOrRecType => + this1.parent.isRef(sym) + case this1: AppliedType => val this2 = this1.dealias if (this2 ne this1) this2.isRef(sym) else this1.underlying.isRef(sym) case _ => false } - /** Is this type exactly Nothing (no vars, aliases, refinements etc allowed)? */ - def isBottomType(implicit ctx: Context): Boolean = this match { - case tp: TypeRef => tp.symbol eq defn.NothingClass - case _ => false - } - /** Is this type a (neither aliased nor applied) reference to class `sym`? */ def isDirectRef(sym: Symbol)(implicit ctx: Context): Boolean = stripTypeVar match { case this1: TypeRef => @@ -154,14 +149,6 @@ object Types { false } - def isInfixType(implicit ctx: Context): Boolean = this match { - case TypeApplications.AppliedType(tycon, args) => - args.length == 2 && - !Character.isUnicodeIdentifierStart(tycon.typeSymbol.name.toString.head) - // TODO: Once we use the 2.12 stdlib, also check the @showAsInfix annotation - case _ => false - } - /** Does this type refer exactly to class symbol `sym`, instead of to a subclass of `sym`? * Implemented like `isRef`, but follows more types: all type proxies as well as and- and or-types */ @@ -221,13 +208,29 @@ object Types { else defn.NothingType } + /** Is this type exactly Nothing (no vars, aliases, refinements etc allowed)? */ + def isBottomType(implicit ctx: Context): Boolean = this match { + case tp: TypeRef => + val sym = tp.symbol + (sym eq defn.NothingClass) || (sym eq defn.Phantom_NothingClass) + case _ => false + } + + /** Is this type exactly Any (no vars, aliases, refinements etc allowed)? */ + def isTopType(implicit ctx: Context): Boolean = this match { + case tp: TypeRef => + val sym = tp.symbol + (sym eq defn.AnyClass) || (sym eq defn.Phantom_AnyClass) + case _ => false + } + /** Returns the type of the phantom lattice (i.e. the prefix of the phantom type) * - XYZ if XYZ extends scala.Phantom and this type is upper bounded XYZ.Any * - NoType otherwise */ private final def phantomLatticeType(implicit ctx: Context): Type = widen match { case tp: ClassInfo if defn.isPhantomTerminalClass(tp.classSymbol) => tp.prefix - case tp: TypeProxy if tp.superType ne this => tp.underlying.phantomLatticeType + case tp: TypeProxy if tp.superType ne this => tp.underlying.phantomLatticeType // ??? guard needed ??? case tp: AndOrType => tp.tp1.phantomLatticeType case _ => NoType } @@ -467,10 +470,8 @@ object Types { final def memberExcluding(name: Name, excluding: FlagSet)(implicit ctx: Context): Denotation = { // We need a valid prefix for `asSeenFrom` val pre = this match { - case tp: ClassInfo => - tp.typeRef - case _ => - widenIfUnstable + case tp: ClassInfo => tp.appliedRef + case _ => widenIfUnstable } findMember(name, pre, excluding) } @@ -490,6 +491,8 @@ object Types { }) case tp: TypeRef => tp.denot.findMember(name, pre, excluded) + case tp: AppliedType => + goApplied(tp) case tp: ThisType => goThis(tp) case tp: RefinedType => @@ -500,8 +503,6 @@ object Types { goParam(tp) case tp: SuperType => goSuper(tp) - case tp: HKApply => - goApply(tp) case tp: TypeProxy => go(tp.underlying) case tp: ClassInfo => @@ -552,7 +553,7 @@ object Types { val rt = if (tp.opened) { // defensive copy tp.openedTwice = true - RecType(rt => tp.parent.substRecThis(tp, RecThis(rt))) + RecType(rt => tp.parent.substRecThis(tp, rt.recThis)) } else tp rt.opened = true try go(rt.parent).mapInfo(_.substRecThis(rt, pre)) @@ -569,18 +570,7 @@ object Types { if (rinfo.isAlias) rinfo else if (pdenot.info.isAlias) pdenot.info else if (ctx.pendingMemberSearches.contains(name)) pdenot.info safe_& rinfo - else - try pdenot.info & rinfo - catch { - case ex: CyclicReference => - // happens for tests/pos/sets.scala. findMember is called from baseTypeRef. - // The & causes a subtype check which calls baseTypeRef again with the same - // superclass. In the observed case, the superclass was Any, and - // the special shortcut for Any in derivesFrom was as yet absent. To reproduce, - // remove the special treatment of Any in derivesFrom and compile - // sets.scala. - pdenot.info safe_& rinfo - } + else pdenot.info recoverable_& rinfo pdenot.asSingleDenotation.derivedSingleDenotation(pdenot.symbol, jointInfo) } else { pdenot & ( @@ -590,10 +580,12 @@ object Types { } } - def goApply(tp: HKApply) = tp.tycon match { + def goApplied(tp: AppliedType) = tp.tycon match { case tl: HKTypeLambda => go(tl.resType).mapInfo(info => tl.derivedLambdaAbstraction(tl.paramNames, tl.paramInfos, info).appliedTo(tp.args)) + case tc: TypeRef if tc.symbol.isClass => + go(tc) case _ => go(tp.superType) } @@ -833,9 +825,9 @@ object Types { /** The basetype TypeRef of this type with given class symbol, * but without including any type arguments */ - final def baseTypeRef(base: Symbol)(implicit ctx: Context): Type = /*ctx.traceIndented(s"$this baseTypeRef $base")*/ /*>|>*/ track("baseTypeRef") /*<|<*/ { + final def baseType(base: Symbol)(implicit ctx: Context): Type = /*ctx.traceIndented(s"$this baseType $base")*/ /*>|>*/ track("base type") /*<|<*/ { base.denot match { - case classd: ClassDenotation => classd.baseTypeRefOf(this) + case classd: ClassDenotation => classd.baseTypeOf(this) case _ => NoType } } @@ -846,17 +838,29 @@ object Types { /** Safer version of `&`. * - * This version does not simplify the upper bound of the intersection of + * This version does not simplify the bounds of the intersection of * two TypeBounds. The simplification done by `&` requires subtyping checks * which may end up calling `&` again, in most cases this should be safe * but because of F-bounded types, this can result in an infinite loop * (which will be masked unless `-Yno-deep-subtypes` is enabled). + * pos/i536 demonstrates that the infinite loop can also invole lower bounds.wait */ def safe_& (that: Type)(implicit ctx: Context): Type = (this, that) match { - case (TypeBounds(lo1, hi1), TypeBounds(lo2, hi2)) => TypeBounds(lo1 | lo2, AndType(hi1, hi2)) + case (TypeBounds(lo1, hi1), TypeBounds(lo2, hi2)) => TypeBounds(OrType(lo1, lo2), AndType(hi1, hi2)) case _ => this & that } + /** `this & that`, but handle CyclicReferences by falling back to `safe_&`. + */ + def recoverable_&(that: Type)(implicit ctx: Context): Type = + try this & that + catch { + case ex: CyclicReference => this safe_& that + // A test case where this happens is tests/pos/i536.scala. + // The & causes a subtype check which calls baseTypeRef again with the same + // superclass. + } + def | (that: Type)(implicit ctx: Context): Type = track("|") { ctx.typeComparer.lub(this, that) } @@ -965,6 +969,10 @@ object Types { case TypeAlias(tp) => tp.dealias(keepAnnots): @tailrec case _ => tp } + case app @ AppliedType(tycon, args) => + val tycon1 = tycon.dealias(keepAnnots) + if (tycon1 ne tycon) app.superType.dealias(keepAnnots): @tailrec + else this case tp: TypeVar => val tp1 = tp.instanceOpt if (tp1.exists) tp1.dealias(keepAnnots): @tailrec else tp @@ -973,10 +981,6 @@ object Types { if (keepAnnots) tp.derivedAnnotatedType(tp1, tp.annot) else tp1 case tp: LazyRef => tp.ref.dealias(keepAnnots): @tailrec - case app @ HKApply(tycon, args) => - val tycon1 = tycon.dealias(keepAnnots) - if (tycon1 ne tycon) app.superType.dealias(keepAnnots): @tailrec - else this case _ => this } @@ -1008,6 +1012,12 @@ object Types { case _ => this } + /** The type constructor of an applied type, otherwise the type itself */ + final def typeConstructor(implicit ctx: Context): Type = this match { + case AppliedType(tycon, _) => tycon + case _ => this + } + /** If this is a (possibly aliased, annotated, and/or parameterized) reference to * a class, the class type ref, otherwise NoType. * @param refinementOK If `true` we also skip non-parameter refinements. @@ -1017,6 +1027,8 @@ object Types { if (tp.symbol.isClass) tp else if (tp.symbol.isAliasType) tp.underlying.underlyingClassRef(refinementOK) else NoType + case tp: AppliedType => + tp.superType.underlyingClassRef(refinementOK) case tp: AnnotatedType => tp.underlying.underlyingClassRef(refinementOK) case tp: RefinedType => @@ -1150,35 +1162,27 @@ object Types { NoType } - /** For a ClassInfo type, its parents, - * Inherited by all type proxies. Empty for all other types. - * Overwritten in ClassInfo, where parents is cached. - */ - def parents(implicit ctx: Context): List[TypeRef] = this match { - case tp: TypeProxy => tp.underlying.parents - case _ => List() - } - /** The full parent types, including all type arguments */ - def parentsWithArgs(implicit ctx: Context): List[Type] = this match { - case tp: TypeProxy => tp.superType.parentsWithArgs - case _ => List() + def parents(implicit ctx: Context): List[Type] = this match { + case tp @ AppliedType(tycon, args) if tycon.typeSymbol.isClass => + tycon.parents.map(_.subst(tycon.typeSymbol.typeParams, args)) // @!!! cache? + case tp: TypeRef => + if (tp.info.isInstanceOf[TempClassInfo]) { + tp.reloadDenot() + assert(!tp.info.isInstanceOf[TempClassInfo]) + } + tp.info.parents + case tp: TypeProxy => + tp.superType.parents + case _ => Nil } /** The first parent of this type, AnyRef if list of parents is empty */ - def firstParent(implicit ctx: Context): TypeRef = parents match { + def firstParent(implicit ctx: Context): Type = parents match { case p :: _ => p case _ => defn.AnyType } - /** the self type of the underlying classtype */ - def givenSelfType(implicit ctx: Context): Type = this match { - case tp: RefinedType => tp.wrapIfMember(tp.parent.givenSelfType) - case tp: ThisType => tp.tref.givenSelfType - case tp: TypeProxy => tp.superType.givenSelfType - case _ => NoType - } - /** The parameter types of a PolyType or MethodType, Empty list for others */ final def paramInfoss(implicit ctx: Context): List[List[Type]] = stripPoly match { case mt: MethodType => mt.paramInfos :: mt.resultType.paramInfoss @@ -1218,7 +1222,7 @@ object Types { /** This type seen as a TypeBounds */ final def bounds(implicit ctx: Context): TypeBounds = this match { case tp: TypeBounds => tp - case ci: ClassInfo => TypeAlias(ci.typeRef) + case ci: ClassInfo => TypeAlias(ci.appliedRef) case wc: WildcardType => wc.optBounds match { case bounds: TypeBounds => bounds @@ -1376,7 +1380,7 @@ object Types { /** The closest supertype of this type. This is the same as `underlying`, * except that * - instead of a TyperBounds type it returns its upper bound, and - * - for HKApplys it returns the upper bound of the constructor re-applied to the arguments. + * - for applied types it returns the upper bound of the constructor re-applied to the arguments. */ def superType(implicit ctx: Context): Type = underlying match { case TypeBounds(_, hi) => hi @@ -1539,7 +1543,7 @@ object Types { } /** Hook for adding debug check code when denotations are assigned */ - final def checkDenot()(implicit ctx: Context) = + final def checkDenot()(implicit ctx: Context) = { if (Config.checkTypeRefCycles) lastDenotation match { case d: SingleDenotation => @@ -1551,6 +1555,16 @@ object Types { } case _ => } + if (Config.checkTypeParamRefs) + lastDenotation match { + case d: SingleDenotation if d.symbol.is(ClassTypeParam) => + prefix match { + case prefix: Types.ThisType => assert(prefix.cls == d.symbol.owner, this) + case _ => assert(false, this) + } + case _ => + } + } /** A second fallback to recompute the denotation if necessary */ private def computeDenot(implicit ctx: Context): Denotation = { @@ -1614,10 +1628,8 @@ object Types { } private def checkSymAssign(sym: Symbol)(implicit ctx: Context) = { - def selfTypeOf(sym: Symbol) = sym.owner.info match { - case info: ClassInfo => info.givenSelfType - case _ => NoType - } + def selfTypeOf(sym: Symbol) = + if (sym.isClass) sym.asClass.givenSelfType else NoType assert( (lastSymbol eq sym) || @@ -1707,6 +1719,8 @@ object Types { } } + def reloadDenot()(implicit ctx: Context) = setDenot(loadDenot) + protected def asMemberOf(prefix: Type, allowPrivate: Boolean)(implicit ctx: Context): Denotation = if (name.is(ShadowedName)) prefix.nonPrivateMember(name.exclude(ShadowedName)) else if (!allowPrivate) prefix.nonPrivateMember(name) @@ -1771,6 +1785,43 @@ object Types { ctx.underlyingRecursions -= 1 } + // def noArg = throw new AssertionError(s"$pre contains no matching argument for ${sym.showLocated}") + + /** The argument corresponding to class type parameter `tparam` as seen from + * prefix `pre`. + */ + def argForParam(pre: Type)(implicit ctx: Context): Type = { + val tparam = symbol + val cls = tparam.owner + val base = pre.baseType(cls) + base match { + case AppliedType(_, allArgs) => + var tparams = cls.typeParams + var args = allArgs + var idx = 0 + while (tparams.nonEmpty && args.nonEmpty) { + if (tparams.head.eq(tparam)) + return args.head match { + case _: TypeBounds => TypeArgRef(pre, cls.typeRef, idx) + case arg => arg + } + tparams = tparams.tail + args = args.tail + idx += 1 + } + NoType + case OrType(base1, base2) => argForParam(base1) | argForParam(base2) + case AndType(base1, base2) => argForParam(base1) & argForParam(base2) + case _ => + if (pre.termSymbol is Package) argForParam(pre.select(nme.PACKAGE)) + else if (pre.isBottomType) pre + else NoType + } + } + + def isClassParam(implicit ctx: Context) = // @!!! test flag combination instead? + symbol.is(TypeParam) && symbol.owner.isClass + /** A selection of the same kind, but with potentially a different prefix. * The following normalizations are performed for type selections T#A: * @@ -1787,7 +1838,7 @@ object Types { if (prefix eq this.prefix) this else if (prefix.isBottomType) prefix else if (isType) { - val res = prefix.lookupRefined(name) + val res = if (isClassParam) argForParam(prefix) else prefix.lookupRefined(name) if (res.exists) res else if (Config.splitProjections) prefix match { @@ -1964,7 +2015,7 @@ object Types { case that: WithFixedSym => this.prefix == that.prefix && (this.fixedSym eq that.fixedSym) case _ => false } - override def computeHash = doHash(fixedSym, prefix) + override def computeHash = unsupported("computeHash") } final class CachedTermRef(prefix: Type, name: TermName, hc: Int) extends TermRef(prefix, name) { @@ -1980,8 +2031,12 @@ object Types { } // Those classes are non final as Linker extends them. - class TermRefWithFixedSym(prefix: Type, name: TermName, val fixedSym: TermSymbol) extends TermRef(prefix, name) with WithFixedSym - class TypeRefWithFixedSym(prefix: Type, name: TypeName, val fixedSym: TypeSymbol) extends TypeRef(prefix, name) with WithFixedSym + class TermRefWithFixedSym(prefix: Type, name: TermName, val fixedSym: TermSymbol, hc: Int) extends TermRef(prefix, name) with WithFixedSym { + myHash = hc + } + class TypeRefWithFixedSym(prefix: Type, name: TypeName, val fixedSym: TypeSymbol, hc: Int) extends TypeRef(prefix, name) with WithFixedSym { + myHash = hc + } /** Assert current phase does not have erasure semantics */ private def assertUnerased()(implicit ctx: Context) = @@ -2038,7 +2093,7 @@ object Types { * with given prefix, name, and signature */ def withFixedSym(prefix: Type, name: TermName, sym: TermSymbol)(implicit ctx: Context): TermRef = - unique(new TermRefWithFixedSym(prefix, name, sym)) + ctx.uniqueWithFixedSyms.enterIfNew(prefix, name, sym).asInstanceOf[TermRef] /** Create a term ref referring to given symbol with given name, taking the signature * from the symbol if it is completed, or creating a term ref without @@ -2092,7 +2147,7 @@ object Types { * with given prefix, name, and symbol. */ def withFixedSym(prefix: Type, name: TypeName, sym: TypeSymbol)(implicit ctx: Context): TypeRef = - unique(new TypeRefWithFixedSym(prefix, name, sym)) + ctx.uniqueWithFixedSyms.enterIfNew(prefix, name, sym).asInstanceOf[TypeRef] /** Create a type ref referring to given symbol with given name. * This is very similar to TypeRef(Type, Symbol), @@ -2139,6 +2194,8 @@ object Types { */ abstract case class SuperType(thistpe: Type, supertpe: Type) extends CachedProxyType with SingletonType { override def underlying(implicit ctx: Context) = supertpe + override def superType(implicit ctx: Context) = + thistpe.baseType(supertpe.typeSymbol) def derivedSuperType(thistpe: Type, supertpe: Type)(implicit ctx: Context) = if ((thistpe eq this.thistpe) && (supertpe eq this.supertpe)) this else SuperType(thistpe, supertpe) @@ -2202,7 +2259,8 @@ object Types { abstract case class RefinedType(parent: Type, refinedName: Name, refinedInfo: Type) extends RefinedOrRecType { if (refinedName.isTermName) assert(refinedInfo.isInstanceOf[TermType]) - else assert(refinedInfo.isInstanceOf[TypeType]) + else assert(refinedInfo.isInstanceOf[TypeType], this) + assert(!refinedName.is(NameKinds.ExpandedName), this) override def underlying(implicit ctx: Context) = parent @@ -2215,28 +2273,16 @@ object Types { if ((parent eq this.parent) && (refinedName eq this.refinedName) && (refinedInfo eq this.refinedInfo)) this else RefinedType(parent, refinedName, refinedInfo) - /** Add this refinement to `parent`, provided If `refinedName` is a member of `parent`. */ + /** Add this refinement to `parent`, provided `refinedName` is a member of `parent`. */ def wrapIfMember(parent: Type)(implicit ctx: Context): Type = if (parent.member(refinedName).exists) derivedRefinedType(parent, refinedName, refinedInfo) else parent - override def equals(that: Any) = that match { - case that: RefinedType => - this.parent == that.parent && - this.refinedName == that.refinedName && - this.refinedInfo == that.refinedInfo - case _ => - false - } override def computeHash = doHash(refinedName, refinedInfo, parent) - override def toString = s"RefinedType($parent, $refinedName, $refinedInfo)" } - class CachedRefinedType(parent: Type, refinedName: Name, refinedInfo: Type, hc: Int) - extends RefinedType(parent, refinedName, refinedInfo) { - myHash = hc - override def computeHash = unsupported("computeHash") - } + class CachedRefinedType(parent: Type, refinedName: Name, refinedInfo: Type) + extends RefinedType(parent, refinedName, refinedInfo) object RefinedType { @tailrec def make(parent: Type, names: List[Name], infos: List[Type])(implicit ctx: Context): Type = @@ -2245,7 +2291,7 @@ object Types { def apply(parent: Type, name: Name, info: Type)(implicit ctx: Context): RefinedType = { assert(!ctx.erasedTypes) - ctx.base.uniqueRefinedTypes.enterIfNew(parent, name, info).checkInst + unique(new CachedRefinedType(parent, name, info)).checkInst } } @@ -2257,15 +2303,22 @@ object Types { val parent = parentExp(this) + private[this] var myRecThis: RecThis = null + + def recThis: RecThis = { + if (myRecThis == null) myRecThis = new RecThis(this) {} + myRecThis + } + override def underlying(implicit ctx: Context): Type = parent def derivedRecType(parent: Type)(implicit ctx: Context): RecType = if (parent eq this.parent) this - else RecType(rt => parent.substRecThis(this, RecThis(rt))) + else RecType(rt => parent.substRecThis(this, rt.recThis)) def rebind(parent: Type)(implicit ctx: Context): Type = if (parent eq this.parent) this - else RecType.closeOver(rt => parent.substRecThis(this, RecThis(rt))) + else RecType.closeOver(rt => parent.substRecThis(this, rt.recThis)) override def equals(other: Any) = other match { case other: RecType => other.parent == this.parent @@ -2308,7 +2361,7 @@ object Types { val rt = new RecType(parentExp) def normalize(tp: Type): Type = tp.stripTypeVar match { case tp: RecType => - normalize(tp.parent.substRecThis(tp, RecThis(rt))) + normalize(tp.parent.substRecThis(tp, rt.recThis)) case tp @ RefinedType(parent, rname, rinfo) => val rinfo1 = rinfo match { case TypeAlias(TypeRef(RecThis(`rt`), `rname`)) => TypeBounds.empty @@ -2518,11 +2571,12 @@ object Types { type ThisName <: Name type PInfo <: Type type This <: LambdaType{type PInfo = self.PInfo} + type ParamRefType <: ParamRef def paramNames: List[ThisName] def paramInfos: List[PInfo] def resType: Type - def newParamRef(n: Int): ParamRef + protected def newParamRef(n: Int): ParamRefType override def resultType(implicit ctx: Context) = resType @@ -2536,7 +2590,12 @@ object Types { final def isTypeLambda = isInstanceOf[TypeLambda] final def isHigherKinded = isInstanceOf[TypeProxy] - lazy val paramRefs: List[ParamRef] = paramNames.indices.toList.map(newParamRef) + private var myParamRefs: List[ParamRefType] = null + + def paramRefs: List[ParamRefType] = { + if (myParamRefs == null) myParamRefs = paramNames.indices.toList.map(newParamRef) + myParamRefs + } protected def computeSignature(implicit ctx: Context) = resultSignature @@ -2607,6 +2666,7 @@ object Types { type ThisName = TermName type PInfo = Type type This <: TermLambda + type ParamRefType = TermParamRef override def resultType(implicit ctx: Context): Type = if (dependencyStatus == FalseDeps) { // dealias all false dependencies @@ -2662,7 +2722,7 @@ object Types { * def f(x: C)(y: x.S) // dependencyStatus = TrueDeps * def f(x: C)(y: x.T) // dependencyStatus = FalseDeps, i.e. * // dependency can be eliminated by dealiasing. - */ + */ private def dependencyStatus(implicit ctx: Context): DependencyStatus = { if (myDependencyStatus != Unknown) myDependencyStatus else { @@ -2696,7 +2756,7 @@ object Types { */ def isParamDependent(implicit ctx: Context): Boolean = paramDependencyStatus == TrueDeps - def newParamRef(n: Int) = TermParamRef(this, n) + def newParamRef(n: Int) = new TermParamRef(this, n) {} } abstract case class MethodType(paramNames: List[TermName])( @@ -2846,11 +2906,12 @@ object Types { type ThisName = TypeName type PInfo = TypeBounds type This <: TypeLambda + type ParamRefType = TypeParamRef def isDependent(implicit ctx: Context): Boolean = true def isParamDependent(implicit ctx: Context): Boolean = true - def newParamRef(n: Int) = TypeParamRef(this, n) + def newParamRef(n: Int) = new TypeParamRef(this, n) {} lazy val typeParams: List[LambdaParam] = paramNames.indices.toList.map(new LambdaParam(this, _)) @@ -2919,16 +2980,16 @@ object Types { */ def flatten(implicit ctx: Context): PolyType = resType match { case that: PolyType => - val shift = new TypeMap { + val shiftedSubst = (x: PolyType) => new TypeMap { def apply(t: Type) = t match { - case TypeParamRef(`that`, n) => TypeParamRef(that, n + paramNames.length) + case TypeParamRef(`that`, n) => x.paramRefs(n + paramNames.length) case t => mapOver(t) } } 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)) + that.paramInfos.mapConserve(shiftedSubst(x)(_).bounds), + x => shiftedSubst(x)(that.resultType).subst(this, x)) case _ => this } @@ -2997,7 +3058,7 @@ object Types { final val Provisional: DependencyStatus = 4 // set if dependency status can still change due to type variable instantiations } - // ----- HK types: LambdaParam, HKApply --------------------- + // ----- Type application: LambdaParam, AppliedType, TypeArgRef --------------------- /** The parameter of a type lambda */ case class LambdaParam(tl: TypeLambda, n: Int) extends ParamInfo { @@ -3008,12 +3069,11 @@ object Types { 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) + def paramRef(implicit ctx: Context): Type = tl.paramRefs(n) } - /** A higher kinded type application `C[T_1, ..., T_n]` */ - abstract case class HKApply(tycon: Type, args: List[Type]) + /** A type application `C[T_1, ..., T_n]` */ + abstract case class AppliedType(tycon: Type, args: List[Type]) extends CachedProxyType with ValueType { private var validSuper: Period = Nowhere @@ -3025,14 +3085,16 @@ object Types { if (ctx.period != validSuper) { validSuper = ctx.period cachedSuper = tycon match { - case tp: HKTypeLambda => defn.AnyType - case tp: TypeVar if !tp.inst.exists => + case tycon: HKTypeLambda => defn.AnyType + case tycon: TypeVar if !tycon.inst.exists => // supertype not stable, since underlying might change validSuper = Nowhere - tp.underlying.applyIfParameterized(args) - case tp: TypeProxy => - if (tp.typeSymbol.is(Provisional)) validSuper = Nowhere - tp.superType.applyIfParameterized(args) + tycon.underlying.applyIfParameterized(args) + case tycon: TypeRef if tycon.symbol.isClass => + tycon + case tycon: TypeProxy => + if (tycon.typeSymbol.is(Provisional)) validSuper = Nowhere + tycon.superType.applyIfParameterized(args) case _ => defn.AnyType } } @@ -3059,30 +3121,77 @@ object Types { def derivedAppliedType(tycon: Type, args: List[Type])(implicit ctx: Context): Type = if ((tycon eq this.tycon) && (args eq this.args)) this else tycon.appliedTo(args) + } - override def computeHash = doHash(tycon, args) + final class CachedAppliedType(tycon: Type, args: List[Type], hc: Int) extends AppliedType(tycon, args) { + myHash = hc + override def computeHash = unsupported("computeHash") + } - protected def checkInst(implicit ctx: Context): this.type = { - def check(tycon: Type): Unit = tycon.stripTypeVar match { - case tycon: TypeRef if !tycon.symbol.isClass => - case _: TypeParamRef | _: ErrorType | _: WildcardType => - case _: TypeLambda => - assert(!args.exists(_.isInstanceOf[TypeBounds]), s"unreduced type apply: $this") - case tycon: AnnotatedType => - check(tycon.underlying) - case _ => - assert(false, s"illegal type constructor in $this") + object AppliedType { + def apply(tycon: Type, args: List[Type])(implicit ctx: Context) = { + assertUnerased() + ctx.base.uniqueAppliedTypes.enterIfNew(tycon, args) + } + } + + /** A reference to wildcard argument `p.` + * where `p: C[... _ ...]` + */ + abstract case class TypeArgRef(prefix: Type, clsRef: TypeRef, idx: Int) extends CachedProxyType with ValueType { + assert(prefix.isInstanceOf[ValueType]) + assert(idx >= 0) + + private[this] var underlyingCache: Type = _ + private[this] var underlyingCachePeriod = Nowhere + + def computeUnderlying(implicit ctx: Context): Type = { + val cls = clsRef.symbol + val args = prefix.baseType(cls).argInfos + val typeParams = cls.typeParams + + val concretized = TypeArgRef.concretizeArgs(args, prefix, clsRef) + def rebase(arg: Type) = arg.subst(typeParams, concretized) + + val arg = args(idx) + val tparam = typeParams(idx) + val v = tparam.paramVariance + val pbounds = tparam.paramInfo + if (v > 0 && pbounds.loBound.dealias.isBottomType) arg.hiBound & rebase(pbounds.hiBound) + else if (v < 0 && pbounds.hiBound.dealias.isTopType) arg.loBound | rebase(pbounds.loBound) + else arg recoverable_& rebase(pbounds) + } + + override def underlying(implicit ctx: Context): Type = { + if (!ctx.hasSameBaseTypesAs(underlyingCachePeriod)) { + underlyingCache = computeUnderlying + underlyingCachePeriod = ctx.period } - if (Config.checkHKApplications) check(tycon) - this + underlyingCache } + + def derivedTypeArgRef(prefix: Type)(implicit ctx: Context): Type = + if (prefix eq this.prefix) this else TypeArgRef(prefix, clsRef, idx) + override def computeHash = doHash(idx, prefix, clsRef) } - final class CachedHKApply(tycon: Type, args: List[Type]) extends HKApply(tycon, args) + final class CachedTypeArgRef(prefix: Type, clsRef: TypeRef, idx: Int) extends TypeArgRef(prefix, clsRef, idx) - object HKApply { - def apply(tycon: Type, args: List[Type])(implicit ctx: Context) = - unique(new CachedHKApply(tycon, args)).checkInst + object TypeArgRef { + def apply(prefix: Type, clsRef: TypeRef, idx: Int)(implicit ctx: Context) = + unique(new CachedTypeArgRef(prefix, clsRef, idx)) + def fromParam(prefix: Type, tparam: TypeSymbol)(implicit ctx: Context) = { + val cls = tparam.owner + apply(prefix, cls.typeRef, cls.typeParams.indexOf(tparam)) + } + + def concretizeArgs(args: List[Type], prefix: Type, clsRef: TypeRef)(implicit ctx: Context): List[Type] = { + def concretize(arg: Type, j: Int) = arg match { + case arg: TypeBounds => TypeArgRef(prefix, clsRef, j) + case arg => arg + } + args.zipWithConserve(args.indices.toList)(concretize) + } } // ----- BoundTypes: ParamRef, RecThis ---------------------------------------- @@ -3119,14 +3228,20 @@ object Types { } } - case class TermParamRef(binder: TermLambda, paramNum: Int) extends ParamRef { + /** Only created in `binder.paramRefs`. Use `binder.paramRefs(paramNum)` to + * refer to `TermParamRef(binder, paramNum)`. + */ + abstract case class TermParamRef(binder: TermLambda, paramNum: Int) extends ParamRef { type BT = TermLambda - def copyBoundType(bt: BT) = TermParamRef(bt, paramNum) + def copyBoundType(bt: BT) = bt.paramRefs(paramNum) } - case class TypeParamRef(binder: TypeLambda, paramNum: Int) extends ParamRef { + /** Only created in `binder.paramRefs`. Use `binder.paramRefs(paramNum)` to + * refer to `TypeParamRef(binder, paramNum)`. + */ + abstract case class TypeParamRef(binder: TypeLambda, paramNum: Int) extends ParamRef { type BT = TypeLambda - def copyBoundType(bt: BT) = TypeParamRef(bt, paramNum) + def copyBoundType(bt: BT) = bt.paramRefs(paramNum) /** Looking only at the structure of `bound`, is one of the following true? * - fromBelow and param <:< bound @@ -3142,11 +3257,13 @@ object Types { } } - /** a self-reference to an enclosing recursive type. */ - case class RecThis(binder: RecType) extends BoundType with SingletonType { + /** a self-reference to an enclosing recursive type. The only creation method is + * `binder.recThis`, returning `RecThis(binder)`. + */ + abstract case class RecThis(binder: RecType) extends BoundType with SingletonType { type BT = RecType override def underlying(implicit ctx: Context) = binder - def copyBoundType(bt: BT) = RecThis(bt) + def copyBoundType(bt: BT) = bt.recThis // need to customize hashCode and equals to prevent infinite recursion // between RecTypes and RecRefs. @@ -3296,10 +3413,13 @@ object Types { abstract case class ClassInfo( prefix: Type, cls: ClassSymbol, - classParents: List[TypeRef], + classParents: List[Type], decls: Scope, selfInfo: DotClass /* should be: Type | Symbol */) extends CachedGroundType with TypeType { + private var selfTypeCache: Type = null + private var appliedRefCache: Type = null + /** The self type of a class is the conjunction of * - the explicit self type if given (or the info of a given self symbol), and * - the fully applied reference to the class itself. @@ -3307,110 +3427,71 @@ object Types { def selfType(implicit ctx: Context): Type = { if (selfTypeCache == null) selfTypeCache = { - def fullRef = fullyAppliedRef - val given = givenSelfType - val raw = - if (!given.exists) fullRef - else if (cls is Module) given - else if (ctx.erasedTypes) fullRef - else AndType(given, fullRef) - raw//.asSeenFrom(prefix, cls.owner) + val given = cls.givenSelfType + if (!given.exists) appliedRef + else if (cls is Module) given + else if (ctx.erasedTypes) appliedRef + else AndType(given, appliedRef) } selfTypeCache } - /** The explicitly given self type (self types of modules are assumed to be - * explcitly given here). - */ - override def givenSelfType(implicit ctx: Context): Type = selfInfo match { - case tp: Type => tp - case self: Symbol => self.info - } - - private var selfTypeCache: Type = null - - private def fullyAppliedRef(base: Type, tparams: List[TypeSymbol])(implicit ctx: Context): Type = tparams match { - case tparam :: tparams1 => - fullyAppliedRef( - RefinedType(base, tparam.name, TypeRef(cls.thisType, tparam).toBounds(tparam)), - tparams1) - case nil => - base - } - - /** The class type with all type parameters */ - def fullyAppliedRef(implicit ctx: Context): Type = fullyAppliedRef(cls.typeRef, cls.typeParams) - - private var typeRefCache: TypeRef = null - - def typeRef(implicit ctx: Context): TypeRef = { + def appliedRef(implicit ctx: Context): Type = { def clsDenot = if (prefix eq cls.owner.thisType) cls.denot else cls.denot.copySymDenotation(info = this) - if (typeRefCache == null) - typeRefCache = + if (appliedRefCache == null) { + val tref = if ((cls is PackageClass) || cls.owner.isTerm) symbolicTypeRef else TypeRef(prefix, cls.name, clsDenot) - typeRefCache + appliedRefCache = + tref.appliedTo(cls.typeParams.map(_.typeRef)) + } + appliedRefCache } def symbolicTypeRef(implicit ctx: Context): TypeRef = TypeRef(prefix, cls) // cached because baseType needs parents - private var parentsCache: List[TypeRef] = null + private var parentsCache: List[Type] = null - /** The parent type refs as seen from the given prefix */ - override def parents(implicit ctx: Context): List[TypeRef] = { + override def parents(implicit ctx: Context): List[Type] = { if (parentsCache == null) - parentsCache = cls.classParents.mapConserve(_.asSeenFrom(prefix, cls.owner).asInstanceOf[TypeRef]) + parentsCache = classParents.mapConserve(_.asSeenFrom(prefix, cls.owner)) parentsCache } - /** The parent types with all type arguments */ - override def parentsWithArgs(implicit ctx: Context): List[Type] = - parents mapConserve { pref => - ((pref: Type) /: pref.classSymbol.typeParams) { (parent, tparam) => - val targSym = decls.lookup(tparam.name) - if (targSym.exists) RefinedType(parent, targSym.name, targSym.info) - else parent - } - } - def derivedClassInfo(prefix: Type)(implicit ctx: Context) = if (prefix eq this.prefix) this else ClassInfo(prefix, cls, classParents, decls, selfInfo) - def derivedClassInfo(prefix: Type = this.prefix, classParents: List[TypeRef] = classParents, decls: Scope = this.decls, selfInfo: DotClass = this.selfInfo)(implicit ctx: Context) = + def derivedClassInfo(prefix: Type = this.prefix, classParents: List[Type] = this.classParents, decls: Scope = this.decls, selfInfo: DotClass = this.selfInfo)(implicit ctx: Context) = if ((prefix eq this.prefix) && (classParents eq this.classParents) && (decls eq this.decls) && (selfInfo eq this.selfInfo)) this else ClassInfo(prefix, cls, classParents, decls, selfInfo) override def computeHash = doHash(cls, prefix) - override def toString = s"ClassInfo($prefix, $cls)" + override def toString = s"ClassInfo($prefix, $cls, $classParents)" } - class CachedClassInfo(prefix: Type, cls: ClassSymbol, classParents: List[TypeRef], decls: Scope, selfInfo: DotClass) + class CachedClassInfo(prefix: Type, cls: ClassSymbol, classParents: List[Type], decls: Scope, selfInfo: DotClass) extends ClassInfo(prefix, cls, classParents, decls, selfInfo) - /** A class for temporary class infos where `parents` are not yet known. */ + /** A class for temporary class infos where `parents` are not yet known */ final class TempClassInfo(prefix: Type, cls: ClassSymbol, decls: Scope, selfInfo: DotClass) extends CachedClassInfo(prefix, cls, Nil, decls, selfInfo) { - /** A list of actions that were because they rely on the class info of `cls` to - * be no longer temporary. These actions will be performed once `cls` gets a real - * ClassInfo. - */ - private var suspensions: List[Context => Unit] = Nil + /** Install classinfo with known parents in `denot` s */ + def finalize(denot: SymDenotation, parents: List[Type])(implicit ctx: Context) = + denot.info = ClassInfo(prefix, cls, parents, decls, selfInfo) - def addSuspension(suspension: Context => Unit): Unit = suspensions ::= suspension + override def derivedClassInfo(prefix: Type)(implicit ctx: Context) = + if (prefix eq this.prefix) this + else new TempClassInfo(prefix, cls, decls, selfInfo) - /** Install classinfo with known parents in `denot` and resume all suspensions */ - def finalize(denot: SymDenotation, parents: List[TypeRef])(implicit ctx: Context) = { - denot.info = derivedClassInfo(classParents = parents) - suspensions.foreach(_(ctx)) - } + override def toString = s"TempClassInfo($prefix, $cls)" } object ClassInfo { - def apply(prefix: Type, cls: ClassSymbol, classParents: List[TypeRef], decls: Scope, selfInfo: DotClass = NoType)(implicit ctx: Context) = + def apply(prefix: Type, cls: ClassSymbol, classParents: List[Type], decls: Scope, selfInfo: DotClass = NoType)(implicit ctx: Context) = unique(new CachedClassInfo(prefix, cls, classParents, decls, selfInfo)) } @@ -3420,23 +3501,13 @@ object Types { assert(lo.isInstanceOf[TermType]) assert(hi.isInstanceOf[TermType]) - def variance: Int = 0 - override def underlying(implicit ctx: Context): Type = hi /** The non-alias type bounds type with given bounds */ def derivedTypeBounds(lo: Type, hi: Type)(implicit ctx: Context) = - if ((lo eq this.lo) && (hi eq this.hi) && (variance == 0)) this + if ((lo eq this.lo) && (hi eq this.hi)) this else TypeBounds(lo, hi) - /** If this is an alias, a derived alias with the new variance, - * Otherwise the type itself. - */ - def withVariance(variance: Int)(implicit ctx: Context) = this match { - case tp: TypeAlias => tp.derivedTypeAlias(tp.alias, variance) - case _ => this - } - def contains(tp: Type)(implicit ctx: Context): Boolean = tp match { case tp: TypeBounds => lo <:< tp.lo && tp.hi <:< hi case tp: ClassInfo => @@ -3466,57 +3537,39 @@ object Types { case _ => super.| (that) } - /** The implied bounds, where aliases are mapped to intervals from - * Nothing/Any - */ - def boundsInterval(implicit ctx: Context): TypeBounds = this - - /** If this type and that type have the same variance, this variance, otherwise 0 */ - final def commonVariance(that: TypeBounds): Int = (this.variance + that.variance) / 2 + override def computeHash = doHash(lo, hi) - override def computeHash = doHash(variance, lo, hi) + // @!!! we are not systematic when we do referntial vs structural comparisons. + // Do referential everywhere? override def equals(that: Any): Boolean = that match { + case that: TypeAlias => + false case that: TypeBounds => - (this.lo eq that.lo) && (this.hi eq that.hi) && (this.variance == that.variance) + (this.lo eq that.lo) && (this.hi eq that.hi) case _ => false } - - override def toString = - if (lo eq hi) s"TypeAlias($lo, $variance)" else s"TypeBounds($lo, $hi)" } class RealTypeBounds(lo: Type, hi: Type) extends TypeBounds(lo, hi) - abstract class TypeAlias(val alias: Type, override val variance: Int) extends TypeBounds(alias, alias) { + abstract class TypeAlias(val alias: Type) extends TypeBounds(alias, alias) { + /** pre: this is a type alias */ - def derivedTypeAlias(alias: Type, variance: Int = this.variance)(implicit ctx: Context) = - if ((alias eq this.alias) && (variance == this.variance)) this - else TypeAlias(alias, variance) + def derivedTypeAlias(alias: Type)(implicit ctx: Context) = + if (alias eq this.alias) this else TypeAlias(alias) - override def & (that: TypeBounds)(implicit ctx: Context): TypeBounds = { - val v = this commonVariance that - if (v > 0) derivedTypeAlias(this.hi & that.hi, v) - else if (v < 0) derivedTypeAlias(this.lo | that.lo, v) - else super.& (that) - } + override def computeHash = doHash(alias) - override def | (that: TypeBounds)(implicit ctx: Context): TypeBounds = { - val v = this commonVariance that - if (v > 0) derivedTypeAlias(this.hi | that.hi, v) - else if (v < 0) derivedTypeAlias(this.lo & that.lo, v) - else super.| (that) + override def equals(that: Any): Boolean = that match { + case that: TypeAlias => + this.alias eq that.alias + case _ => + false } - - override def boundsInterval(implicit ctx: Context): TypeBounds = - if (variance == 0) this - else if (variance < 0) TypeBounds.lower(alias) - else TypeBounds.upper(alias) } - class CachedTypeAlias(alias: Type, variance: Int, hc: Int) extends TypeAlias(alias, variance) { - myHash = hc - } + class CachedTypeAlias(alias: Type) extends TypeAlias(alias) object TypeBounds { def apply(lo: Type, hi: Type)(implicit ctx: Context): TypeBounds = @@ -3527,8 +3580,8 @@ object Types { } object TypeAlias { - def apply(alias: Type, variance: Int = 0)(implicit ctx: Context) = - ctx.uniqueTypeAliases.enterIfNew(alias, variance) + def apply(alias: Type)(implicit ctx: Context) = + unique(new CachedTypeAlias(alias)) def unapply(tp: TypeAlias): Option[Type] = Some(tp.alias) } @@ -3642,6 +3695,8 @@ object Types { } if ((tp.cls is Trait) || zeroParams(tp.cls.primaryConstructor.info)) tp // !!! needs to be adapted once traits have parameters else NoType + case tp: AppliedType => + zeroParamClass(tp.superType) case tp: TypeRef => zeroParamClass(tp.underlying) case tp: RefinedType => @@ -3650,8 +3705,6 @@ object Types { zeroParamClass(tp.underlying) case tp: TypeVar => zeroParamClass(tp.underlying) - case tp: HKApply => - zeroParamClass(tp.superType) case _ => NoType } @@ -3708,7 +3761,13 @@ object Types { def apply(tp: Type): Type protected def derivedSelect(tp: NamedType, pre: Type): Type = - tp.derivedSelect(pre) + tp.derivedSelect(pre) match { + case tp: TypeArgRef if variance != 0 => + val tp1 = tp.underlying + if (variance > 0) tp1.hiBound else tp1.loBound + case tp => + tp + } protected def derivedRefinedType(tp: RefinedType, parent: Type, info: Type): Type = tp.derivedRefinedType(parent, tp.refinedName, info) protected def derivedRecType(tp: RecType, parent: Type): Type = @@ -3719,8 +3778,10 @@ object Types { tp.derivedTypeBounds(lo, hi) protected def derivedSuperType(tp: SuperType, thistp: Type, supertp: Type): Type = tp.derivedSuperType(thistp, supertp) - protected def derivedAppliedType(tp: HKApply, tycon: Type, args: List[Type]): Type = + protected def derivedAppliedType(tp: AppliedType, tycon: Type, args: List[Type]): Type = tp.derivedAppliedType(tycon, args) + protected def derivedTypeArgRef(tp: TypeArgRef, prefix: Type): Type = + tp.derivedTypeArgRef(prefix) protected def derivedAndOrType(tp: AndOrType, tp1: Type, tp2: Type): Type = tp.derivedAndOrType(tp1, tp2) protected def derivedAnnotatedType(tp: AnnotatedType, underlying: Type, annot: Annotation): Type = @@ -3757,11 +3818,26 @@ object Types { | _: BoundType | NoPrefix => tp + case tp: AppliedType => + def mapArgs(args: List[Type], tparams: List[ParamInfo]): List[Type] = args match { + case arg :: otherArgs => + val arg1 = arg match { + case arg: TypeBounds => this(arg) + case arg => atVariance(variance * tparams.head.paramVariance)(this(arg)) + } + val otherArgs1 = mapArgs(otherArgs, tparams.tail) + if ((arg1 eq arg) && (otherArgs1 eq otherArgs)) args + else arg1 :: otherArgs1 + case nil => + nil + } + derivedAppliedType(tp, this(tp.tycon), mapArgs(tp.args, tp.typeParams)) + case tp: RefinedType => derivedRefinedType(tp, this(tp.parent), this(tp.refinedInfo)) case tp: TypeAlias => - derivedTypeAlias(tp, atVariance(variance * tp.variance)(this(tp.alias))) + derivedTypeAlias(tp, atVariance(0)(this(tp.alias))) case tp: TypeBounds => variance = -variance @@ -3776,12 +3852,6 @@ object Types { val inst = tp.instanceOpt if (inst.exists) apply(inst) else tp - case tp: HKApply => - def mapArg(arg: Type, tparam: ParamInfo): Type = - atVariance(variance * tparam.paramVariance)(this(arg)) - derivedAppliedType(tp, this(tp.tycon), - tp.args.zipWithConserve(tp.typeParams)(mapArg)) - case tp: ExprType => derivedExprType(tp, this(tp.resultType)) @@ -3794,6 +3864,9 @@ object Types { } mapOverLambda + case tp @ TypeArgRef(prefix, _, _) => + derivedTypeArgRef(tp, atVariance(0)(this(prefix))) + case tp @ SuperType(thistp, supertp) => derivedSuperType(tp, this(thistp), this(supertp)) @@ -3858,7 +3931,7 @@ object Types { abstract class DeepTypeMap(implicit ctx: Context) extends TypeMap { override def mapClassInfo(tp: ClassInfo) = { val prefix1 = this(tp.prefix) - val parents1 = (tp.parents mapConserve this).asInstanceOf[List[TypeRef]] + val parents1 = tp.parents mapConserve this val selfInfo1 = tp.selfInfo match { case selfInfo: Type => this(selfInfo) case selfInfo => selfInfo @@ -3907,7 +3980,7 @@ object Types { * The possible cases are listed inline in the code. Return `default` if no widening is * possible. */ - def tryWiden(tp: NamedType, pre: Type)(default: => Type): Type = + def tryWiden(tp: NamedType, pre: Type): Type = pre.member(tp.name) match { case d: SingleDenotation => d.info match { @@ -3924,9 +3997,9 @@ object Types { // hence we can replace with y.type under all variances reapply(info) case _ => - default + NoType } - case _ => default + case _ => NoType } /** Derived selection. @@ -3936,9 +4009,13 @@ object Types { if (pre eq tp.prefix) tp else pre match { case Range(preLo, preHi) => - tryWiden(tp, preHi)(range(tp.derivedSelect(preLo), tp.derivedSelect(preHi))) + val forwarded = + if (tp.isClassParam) tp.argForParam(preHi) + else tryWiden(tp, preHi) + forwarded.orElse( + range(super.derivedSelect(tp, preLo), super.derivedSelect(tp, preHi))) case _ => - tp.derivedSelect(pre) + super.derivedSelect(tp, pre) } override protected def derivedRefinedType(tp: RefinedType, parent: Type, info: Type) = @@ -3953,22 +4030,12 @@ object Types { else info match { case Range(infoLo: TypeBounds, infoHi: TypeBounds) => assert(variance == 0) - val v1 = infoLo.variance - val v2 = infoHi.variance - // There's some weirdness coming from the way aliases can have variance - // If infoLo and infoHi are both aliases with the same non-zero variance - // we can propagate to a range of the refined types. If they are both - // non-alias ranges we know that infoLo <:< infoHi and therefore we can - // propagate to refined types with infoLo and infoHi as bounds. - // In all other cases, Nothing..Any is the only interval that contains - // the range. i966.scala is a test case. - if (v1 > 0 && v2 > 0) propagate(infoLo, infoHi) - else if (v1 < 0 && v2 < 0) propagate(infoHi, infoLo) - else if (!infoLo.isAlias && !infoHi.isAlias) propagate(infoLo, infoHi) + if (!infoLo.isAlias && !infoHi.isAlias) propagate(infoLo, infoHi) else range(tp.bottomType, tp.topType) // Using `parent` instead of `tp.topType` would be better for normal refinements, // but it would also turn *-types into hk-types, which is not what we want. // We should revisit this point in case we represent applied types not as refinements anymore. + // @!!! revisit case Range(infoLo, infoHi) => propagate(infoLo, infoHi) case _ => @@ -4003,7 +4070,7 @@ object Types { if (isRange(thistp) || isRange(supertp)) range(thistp.bottomType, thistp.topType) else tp.derivedSuperType(thistp, supertp) - override protected def derivedAppliedType(tp: HKApply, tycon: Type, args: List[Type]): Type = + override protected def derivedAppliedType(tp: AppliedType, tycon: Type, args: List[Type]): Type = tycon match { case Range(tyconLo, tyconHi) => range(derivedAppliedType(tp, tyconLo, args), derivedAppliedType(tp, tyconHi, args)) @@ -4050,6 +4117,14 @@ object Types { else range(lower(tp1) | lower(tp2), upper(tp1) | upper(tp2)) else tp.derivedAndOrType(tp1, tp2) + override protected def derivedTypeArgRef(tp: TypeArgRef, prefix: Type): Type = + if (isRange(prefix)) + tp.underlying match { + case TypeBounds(lo, hi) => range(atVariance(-variance)(reapply(lo)), reapply(hi)) + case _ => range(tp.bottomType, tp.topType) + } + else tp.derivedTypeArgRef(prefix) + override protected def derivedAnnotatedType(tp: AnnotatedType, underlying: Type, annot: Annotation) = underlying match { case Range(lo, hi) => @@ -4120,11 +4195,27 @@ object Types { | _: BoundType | NoPrefix => x + case tp @ AppliedType(tycon, args) => + @tailrec def foldArgs(x: T, tparams: List[ParamInfo], args: List[Type]): T = + if (args.isEmpty) { + assert(tparams.isEmpty) + x + } + else { + val tparam = tparams.head + val acc = args.head match { + case arg: TypeBounds => this(x, arg) + case arg => atVariance(variance * tparam.paramVariance)(this(x, arg)) + } + foldArgs(acc, tparams.tail, args.tail) + } + foldArgs(this(x, tycon), tp.typeParams, args) + case tp: RefinedType => this(this(x, tp.parent), tp.refinedInfo) case bounds @ TypeBounds(lo, hi) => - if (lo eq hi) atVariance(variance * bounds.variance)(this(x, lo)) + if (lo eq hi) atVariance(0)(this(x, lo)) else { variance = -variance val y = this(x, lo) @@ -4147,23 +4238,6 @@ object Types { case tp @ ClassInfo(prefix, _, _, _, _) => this(x, prefix) - case tp @ HKApply(tycon, args) => - @tailrec def foldArgs(x: T, tparams: List[ParamInfo], args: List[Type]): T = - if (args.isEmpty) { - assert(tparams.isEmpty) - x - } - else { - val tparam = tparams.head - val saved = variance - variance *= tparam.paramVariance - val acc = - try this(x, args.head) - finally variance = saved - foldArgs(acc, tparams.tail, args.tail) - } - foldArgs(this(x, tycon), tp.typeParams, args) - case tp: LambdaType => variance = -variance val y = foldOver(x, tp.paramInfos) @@ -4176,6 +4250,12 @@ object Types { case tp: SkolemType => this(x, tp.info) + case tp @ TypeArgRef(prefix, _, _) => + val saved = variance + variance = 0 + try this(x, prefix) + finally variance = saved + case AnnotatedType(underlying, annot) => this(applyToAnnot(x, annot), underlying) @@ -4318,7 +4398,7 @@ object Types { } private def otherReason(pre: Type)(implicit ctx: Context): String = pre match { - case pre: ThisType if pre.givenSelfType.exists => + case pre: ThisType if pre.cls.givenSelfType.exists => i"\nor the self type of $pre might not contain all transitive dependencies" case _ => "" } diff --git a/compiler/src/dotty/tools/dotc/core/Uniques.scala b/compiler/src/dotty/tools/dotc/core/Uniques.scala index cb9670c69956..74b755e22c9a 100644 --- a/compiler/src/dotty/tools/dotc/core/Uniques.scala +++ b/compiler/src/dotty/tools/dotc/core/Uniques.scala @@ -1,7 +1,7 @@ package dotty.tools.dotc package core -import Types._, Contexts._, util.Stats._, Hashable._, Names._ +import Types._, Symbols._, Contexts._, util.Stats._, Hashable._, Names._ import config.Config import util.HashSet @@ -54,7 +54,7 @@ object Uniques { def enterIfNew(prefix: Type, name: Name): NamedType = { val h = doHash(name, prefix) - if (monitored) recordCaching(h, classOf[CachedTermRef]) + if (monitored) recordCaching(h, classOf[NamedType]) def newType = if (name.isTypeName) new CachedTypeRef(prefix, name.asTypeName, h) else new CachedTermRef(prefix, name.asTermName, h) @@ -66,63 +66,59 @@ object Uniques { } } - final class TypeAliasUniques extends HashSet[TypeAlias](Config.initialUniquesCapacity) with Hashable { - override def hash(x: TypeAlias): Int = x.hash + final class WithFixedSymUniques extends HashSet[WithFixedSym](Config.initialUniquesCapacity) with Hashable { + override def hash(x: WithFixedSym): Int = x.hash - private def findPrevious(h: Int, alias: Type, variance: Int): TypeAlias = { + private def findPrevious(h: Int, prefix: Type, sym: Symbol): NamedType = { var e = findEntryByHash(h) while (e != null) { - if ((e.alias eq alias) && (e.variance == variance)) return e + if ((e.prefix eq prefix) && (e.fixedSym eq sym)) return e e = nextEntryByHash(h) } e } - def enterIfNew(alias: Type, variance: Int): TypeAlias = { - val h = doHash(variance, alias) - if (monitored) recordCaching(h, classOf[TypeAlias]) - def newAlias = new CachedTypeAlias(alias, variance, h) - if (h == NotCached) newAlias + def enterIfNew(prefix: Type, name: Name, sym: Symbol): NamedType = { + val h = doHash(sym, prefix) + if (monitored) recordCaching(h, classOf[WithFixedSym]) + def newType = + if (name.isTypeName) new TypeRefWithFixedSym(prefix, name.asTypeName, sym.asInstanceOf[TypeSymbol], h) + else new TermRefWithFixedSym(prefix, name.asTermName, sym.asInstanceOf[TermSymbol], h) + if (h == NotCached) newType else { - val r = findPrevious(h, alias, variance) - if (r ne null) r - else addEntryAfterScan(newAlias) + val r = findPrevious(h, prefix, sym) + if (r ne null) r else addEntryAfterScan(newType) } } } - final class RefinedUniques extends HashSet[RefinedType](Config.initialUniquesCapacity) with Hashable { - override val hashSeed = classOf[CachedRefinedType].hashCode // some types start life as CachedRefinedTypes, need to have same hash seed - override def hash(x: RefinedType): Int = x.hash + final class AppliedUniques extends HashSet[AppliedType](Config.initialUniquesCapacity) with Hashable { + override def hash(x: AppliedType): Int = x.hash - private def findPrevious(h: Int, parent: Type, refinedName: Name, refinedInfo: Type): RefinedType = { + private def findPrevious(h: Int, tycon: Type, args: List[Type]): AppliedType = { var e = findEntryByHash(h) while (e != null) { - if ((e.parent eq parent) && (e.refinedName eq refinedName) && (e.refinedInfo eq refinedInfo)) - return e + def sameArgs(args1: List[Type], args2: List[Type]): Boolean = { + val empty1 = args1.isEmpty + val empty2 = args2.isEmpty + if (empty1) empty2 + else (!empty2 && (args1.head eq args2.head) && sameArgs(args1.tail, args2.tail)) + } + if ((e.tycon eq tycon) && sameArgs(e.args, args)) return e e = nextEntryByHash(h) } e } - def enterIfNew(parent: Type, refinedName: Name, refinedInfo: Type): RefinedType = { - val h = doHash(refinedName, refinedInfo, parent) - def newType = new CachedRefinedType(parent, refinedName, refinedInfo, h) - if (monitored) recordCaching(h, classOf[CachedRefinedType]) + def enterIfNew(tycon: Type, args: List[Type]): AppliedType = { + val h = doHash(tycon, args) + def newType = new CachedAppliedType(tycon, args, h) + if (monitored) recordCaching(h, classOf[CachedAppliedType]) if (h == NotCached) newType else { - val r = findPrevious(h, parent, refinedName, refinedInfo) + val r = findPrevious(h, tycon, args) if (r ne null) r else addEntryAfterScan(newType) } } - - def enterIfNew(rt: RefinedType) = { - if (monitored) recordCaching(rt) - if (rt.hash == NotCached) rt - else { - val r = findPrevious(rt.hash, rt.parent, rt.refinedName, rt.refinedInfo) - if (r ne null) r else addEntryAfterScan(rt) - } - } } } diff --git a/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala b/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala index 4c73115cac50..b0c5b722d281 100644 --- a/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala +++ b/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala @@ -308,17 +308,12 @@ class ClassfileParser( case tp: TypeRef => if (sig(index) == '<') { accept('<') - var tp1: Type = tp - var formals: List[Symbol] = - if (skiptvs) - null - else - tp.typeParamSymbols + val argsBuf = if (skiptvs) null else new ListBuffer[Type] while (sig(index) != '>') { - sig(index) match { + val arg = sig(index) match { case variance @ ('+' | '-' | '*') => index += 1 - val bounds = variance match { + variance match { case '+' => objToAny(TypeBounds.upper(sig2type(tparams, skiptvs))) case '-' => val tp = sig2type(tparams, skiptvs) @@ -328,18 +323,12 @@ class ClassfileParser( else TypeBounds.lower(tp) case '*' => TypeBounds.empty } - if (formals != null) - tp1 = RefinedType(tp1, formals.head.name, bounds) - case _ => - val info = sig2type(tparams, skiptvs) - if (formals != null) - tp1 = RefinedType(tp1, formals.head.name, TypeAlias(info)) + case _ => sig2type(tparams, skiptvs) } - if (formals != null) - formals = formals.tail + if (argsBuf != null) argsBuf += arg } accept('>') - tp1 + if (skiptvs) tp else tp.appliedTo(argsBuf.toList) } else tp case tp => assert(sig(index) != '<', tp) @@ -423,9 +412,8 @@ class ClassfileParser( val start = index while (sig(index) != '>') { val tpname = subName(':'.==).toTypeName - val expname = if (owner.isClass) tpname.expandedName(owner) else tpname val s = ctx.newSymbol( - owner, expname, owner.typeParamCreationFlags, + owner, tpname, owner.typeParamCreationFlags, typeParamCompleter(index), coord = indexCoord(index)) if (owner.isClass) owner.asClass.enter(s) tparams = tparams + (tpname -> s) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala b/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala index df8caa1b8653..e1547a958a59 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala @@ -153,21 +153,22 @@ Standard-Section: "ASTs" TopLevelStat* TYPEREFpkg fullyQualified_NameRef TYPEREF possiblySigned_NameRef qual_Type RECtype parent_Type + TYPEALIAS alias_Type SUPERtype Length this_Type underlying_Type REFINEDtype Length underlying_Type refinement_NameRef info_Type APPLIEDtype Length tycon_Type arg_Type* TYPEBOUNDS Length low_Type high_Type - TYPEALIAS Length alias_Type (COVARIANT | CONTRAVARIANT)? ANNOTATEDtype Length underlying_Type fullAnnotation_Term ANDtype Length left_Type right_Type ORtype Length left_Type right_Type BIND Length boundName_NameRef bounds_Type // for type-variables defined in a type pattern BYNAMEtype underlying_Type + PARAMtype Length binder_ASTref paramNum_Nat + TYPEARGtype Length prefix_Type clsRef_Type idx_Nat 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* NameType = paramName_NameRef typeOrBounds_ASTRef @@ -321,7 +322,8 @@ object TastyFormat { final val PRIVATEqualified = 104 final val PROTECTEDqualified = 105 final val RECtype = 106 - final val SINGLETONtpt = 107 + final val TYPEALIAS = 107 + final val SINGLETONtpt = 108 final val IDENT = 112 final val IDENTtpt = 113 @@ -369,7 +371,6 @@ object TastyFormat { final val APPLIEDtpt = 162 final val TYPEBOUNDS = 163 final val TYPEBOUNDStpt = 164 - final val TYPEALIAS = 165 final val ANDtype = 166 final val ANDtpt = 167 final val ORtype = 168 @@ -380,6 +381,7 @@ object TastyFormat { final val LAMBDAtpt = 173 final val PARAMtype = 174 final val ANNOTATION = 175 + final val TYPEARGtype = 176 final val firstSimpleTreeTag = UNITconst final val firstNatTreeTag = SHARED @@ -562,6 +564,7 @@ object TastyFormat { case ENUMconst => "ENUMconst" case SINGLETONtpt => "SINGLETONtpt" case SUPERtype => "SUPERtype" + case TYPEARGtype => "TYPEARGtype" case REFINEDtype => "REFINEDtype" case REFINEDtpt => "REFINEDtpt" case APPLIEDtype => "APPLIEDtype" diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index 338a395ab685..b49232bf1f8a 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -144,8 +144,6 @@ class TreePickler(pickler: TastyPickler) { withLength { pickleType(tycon); args.foreach(pickleType(_)) } case ConstantType(value) => pickleConstant(value) - case tpe: TypeRef if tpe.info.isAlias && tpe.symbol.isAliasPreferred => - pickleType(tpe.superType) case tpe: WithFixedSym => val sym = tpe.symbol def pickleRef() = @@ -197,7 +195,10 @@ class TreePickler(pickler: TastyPickler) { } case tpe: SuperType => writeByte(SUPERtype) - withLength { pickleType(tpe.thistpe); pickleType(tpe.supertpe)} + withLength { pickleType(tpe.thistpe); pickleType(tpe.supertpe) } + case tpe: TypeArgRef => + writeByte(TYPEARGtype) + withLength { pickleType(tpe.prefix); pickleType(tpe.clsRef); writeNat(tpe.idx) } case tpe: RecThis => writeByte(RECthis) val binderAddr = pickledTypes.get(tpe.binder) @@ -217,14 +218,7 @@ class TreePickler(pickler: TastyPickler) { pickleType(tpe.parent) case tpe: TypeAlias => writeByte(TYPEALIAS) - withLength { - pickleType(tpe.alias, richTypes) - tpe.variance match { - case 1 => writeByte(COVARIANT) - case -1 => writeByte(CONTRAVARIANT) - case 0 => - } - } + pickleType(tpe.alias, richTypes) case tpe: TypeBounds => writeByte(TYPEBOUNDS) withLength { pickleType(tpe.lo, richTypes); pickleType(tpe.hi, richTypes) } diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index bf3b593b9a94..00c9e7d210e7 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -220,8 +220,6 @@ class TreeUnpickler(reader: TastyReader, nameAtRef: NameRef => TermName, posUnpi val result = (tag: @switch) match { - case SUPERtype => - SuperType(readType(), readType()) case REFINEDtype => var name: Name = readName() val parent = readType() @@ -234,19 +232,16 @@ class TreeUnpickler(reader: TastyReader, nameAtRef: NameRef => TermName, posUnpi readType().appliedTo(until(end)(readType())) case TYPEBOUNDS => TypeBounds(readType(), readType()) - case TYPEALIAS => - val alias = readType() - val variance = - if (nextByte == COVARIANT) { readByte(); 1 } - else if (nextByte == CONTRAVARIANT) { readByte(); -1 } - else 0 - TypeAlias(alias, variance) case ANNOTATEDtype => AnnotatedType(readType(), Annotation(readTerm())) case ANDtype => AndType(readType(), readType()) case ORtype => OrType(readType(), readType()) + case SUPERtype => + SuperType(readType(), readType()) + case TYPEARGtype => + TypeArgRef(readType(), readType().asInstanceOf[TypeRef], readNat()) case BIND => val sym = ctx.newSymbol(ctx.owner, readName().toTypeName, BindDefinedType, readType()) registerSym(start, sym) @@ -260,7 +255,7 @@ class TreeUnpickler(reader: TastyReader, nameAtRef: NameRef => TermName, posUnpi readMethodic(HKTypeLambda, _.toTypeName) case PARAMtype => readTypeRef() match { - case binder: LambdaType => binder.newParamRef(readNat()) + case binder: LambdaType => binder.paramRefs(readNat()) } case CLASSconst => ConstantType(Constant(readType())) @@ -293,7 +288,9 @@ class TreeUnpickler(reader: TastyReader, nameAtRef: NameRef => TermName, posUnpi case RECtype => RecType(rt => registeringType(rt, readType())) case RECthis => - RecThis(readTypeRef().asInstanceOf[RecType]) + readTypeRef().asInstanceOf[RecType].recThis + case TYPEALIAS => + TypeAlias(readType()) case SHARED => val ref = readAddr() typeAtAddr.getOrElseUpdate(ref, forkAt(ref).readType()) @@ -370,7 +367,7 @@ class TreeUnpickler(reader: TastyReader, nameAtRef: NameRef => TermName, posUnpi private def normalizeFlags(tag: Int, givenFlags: FlagSet, name: Name, isAbsType: Boolean, rhsIsEmpty: Boolean)(implicit ctx: Context): FlagSet = { val lacksDefinition = rhsIsEmpty && - name.isTermName && !name.isConstructorName && !givenFlags.is(ParamOrAccessor) || + name.isTermName && !name.isConstructorName && !givenFlags.is(TermParamOrAccessor) || isAbsType var flags = givenFlags if (lacksDefinition && tag != PARAM) flags |= Deferred @@ -678,7 +675,7 @@ class TreeUnpickler(reader: TastyReader, nameAtRef: NameRef => TermName, posUnpi sym.info = NoCompleter sym.info = rhs.tpe match { case _: TypeBounds | _: ClassInfo => checkNonCyclic(sym, rhs.tpe, reportErrors = false) - case _ => TypeAlias(rhs.tpe, sym.variance) + case _ => TypeAlias(rhs.tpe) } TypeDef(rhs) } @@ -713,13 +710,11 @@ class TreeUnpickler(reader: TastyReader, nameAtRef: NameRef => TermName, posUnpi private def readTemplate(implicit ctx: Context): Template = { val start = currentAddr val cls = ctx.owner.asClass - def setClsInfo(parents: List[TypeRef], selfType: Type) = - cls.info = ClassInfo(cls.owner.thisType, cls, parents, cls.unforcedDecls, selfType) val assumedSelfType = if (cls.is(Module) && cls.owner.isClass) TermRef.withSig(cls.owner.thisType, cls.name.sourceModuleName, Signature.NotAMethod) else NoType - setClsInfo(Nil, assumedSelfType) + cls.info = new TempClassInfo(cls.owner.thisType, cls, cls.unforcedDecls, assumedSelfType) val localDummy = symbolAtCurrent() assert(readByte() == TEMPLATE) val end = readEnd() @@ -731,14 +726,15 @@ class TreeUnpickler(reader: TastyReader, nameAtRef: NameRef => TermName, posUnpi case _ => readTpt() } } - val parentRefs = ctx.normalizeToClassRefs(parents.map(_.tpe), cls, cls.unforcedDecls) + val parentTypes = parents.map(_.tpe.dealias) val self = if (nextByte == SELFDEF) { readByte() untpd.ValDef(readName(), readTpt(), EmptyTree).withType(NoType) } else EmptyValDef - setClsInfo(parentRefs, if (self.isEmpty) NoType else self.tpt.tpe) + cls.info = ClassInfo(cls.owner.thisType, cls, parentTypes, cls.unforcedDecls, + if (self.isEmpty) NoType else self.tpt.tpe) cls.setNoInitsFlags(fork.indexStats(end)) val constr = readIndexedDef().asInstanceOf[DefDef] diff --git a/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala b/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala index c36ce1dc14bb..ad2b74d73890 100644 --- a/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala @@ -62,7 +62,7 @@ object Scala2Unpickler { case tp: MethodType => val lastArg = tp.paramInfos.last assert(lastArg isRef defn.ArrayClass) - val elemtp0 :: Nil = lastArg.baseArgInfos(defn.ArrayClass) + val elemtp0 :: Nil = lastArg.baseType(defn.ArrayClass).argInfos val elemtp = elemtp0 match { case AndType(t1, t2) if t1.typeSymbol.isAbstractType && (t2 isRef defn.ObjectClass) => t1 // drop intersection with Object for abstract types in varargs. UnCurry can handle them. @@ -97,10 +97,11 @@ object Scala2Unpickler { // `denot.sourceModule.exists` provision i859.scala crashes in the backend. denot.owner.thisType select denot.sourceModule else selfInfo - val tempInfo = new TempClassInfo(denot.owner.thisType, denot.classSymbol, decls, ost) + val tempInfo = new TempClassInfo(denot.owner.thisType, cls, decls, ost) denot.info = tempInfo // first rough info to avoid CyclicReferences - var parentRefs = ctx.normalizeToClassRefs(parents, cls, decls) - if (parentRefs.isEmpty) parentRefs = defn.ObjectType :: Nil + val normalizedParents = + if (parents.isEmpty) defn.ObjectType :: Nil + else parents.map(_.dealias) for (tparam <- tparams) { val tsym = decls.lookup(tparam.name) if (tsym.exists) tsym.setFlag(TypeParam) @@ -124,7 +125,7 @@ object Scala2Unpickler { registerCompanionPair(scalacCompanion, denot.classSymbol) } - tempInfo.finalize(denot, parentRefs) // install final info, except possibly for typeparams ordering + tempInfo.finalize(denot, normalizedParents) // install final info, except possibly for typeparams ordering denot.ensureTypeParamsInCorrectOrder() } } @@ -485,10 +486,7 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas case TYPEsym | ALIASsym => var name1 = name.asTypeName var flags1 = flags - if (flags is TypeParam) { - name1 = name1.expandedName(owner) - flags1 |= owner.typeParamCreationFlags - } + if (flags is TypeParam) flags1 |= owner.typeParamCreationFlags ctx.newSymbol(owner, name1, flags1, localMemberUnpickler, coord = start) case CLASSsym => var infoRef = readNat() @@ -662,7 +660,7 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas case info => tp.derivedRefinedType(parent1, name, info) } - case tp @ HKApply(tycon, args) => + case tp @ AppliedType(tycon, args) => val tycon1 = tycon.safeDealias def mapArg(arg: Type) = arg match { case arg: TypeRef if isBound(arg) => arg.symbol.info @@ -727,7 +725,7 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas if (sym.owner != thispre.cls) { val overriding = thispre.cls.info.decls.lookup(sym.name) if (overriding.exists && overriding != sym) { - val base = pre.baseTypeWithArgs(sym.owner) + val base = pre.baseType(sym.owner) assert(base.exists) pre = SuperType(thispre, base) } @@ -759,7 +757,7 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas if (decls.isEmpty) parent else { def subst(info: Type, rt: RecType) = - if (clazz.isClass) info.substThis(clazz.asClass, RecThis(rt)) + if (clazz.isClass) info.substThis(clazz.asClass, rt.recThis) else info // turns out some symbols read into `clazz` are not classes, not sure why this is the case. def addRefinement(tp: Type, sym: Symbol) = RefinedType(tp, sym.name, sym.info) val refined = (parent /: decls.toList)(addRefinement) @@ -929,7 +927,7 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas // println(atp) val targs = atp.argTypes - tpd.applyOverloaded(tpd.New(atp withoutArgs targs), nme.CONSTRUCTOR, args, targs, atp) + tpd.applyOverloaded(tpd.New(atp.typeConstructor), nme.CONSTRUCTOR, args, targs, atp) } /** Read an annotation and as a side effect store it into diff --git a/compiler/src/dotty/tools/dotc/interactive/Interactive.scala b/compiler/src/dotty/tools/dotc/interactive/Interactive.scala index fc6def3190ee..e0eee6a721a9 100644 --- a/compiler/src/dotty/tools/dotc/interactive/Interactive.scala +++ b/compiler/src/dotty/tools/dotc/interactive/Interactive.scala @@ -78,7 +78,7 @@ object Interactive { def scopeCompletions: List[Symbol] = boundary.enclosingClass match { case csym: ClassSymbol => - val classRef = csym.classInfo.typeRef + val classRef = csym.classInfo.appliedRef completions(classRef, boundary) case _ => Nil diff --git a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala index add58d0d73ad..ce8b4240fcac 100644 --- a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala @@ -4,7 +4,6 @@ package printing import core._ import Texts._, Types._, Flags._, Names._, Symbols._, NameOps._, Constants._, Denotations._ import Contexts.Context, Scopes.Scope, Denotations.Denotation, Annotations.Annotation -import TypeApplications.AppliedType import StdNames.{nme, tpnme} import ast.Trees._, ast._ import typer.Implicits._ @@ -62,7 +61,7 @@ class PlainPrinter(_ctx: Context) extends Printer { homogenize(tp.info) case tp: LazyRef => homogenize(tp.ref) - case HKApply(tycon, args) => + case AppliedType(tycon, args) => tycon.dealias.appliedTo(args) case _ => tp @@ -129,11 +128,10 @@ class PlainPrinter(_ctx: Context) extends Printer { } /** The longest sequence of refinement types, starting at given type - * and following parents, but stopping at applied types. + * and following parents. */ private def refinementChain(tp: Type): List[Type] = tp :: (tp match { - case AppliedType(_, _) => Nil case tp: RefinedType => refinementChain(tp.parent.stripTypeVar) case _ => Nil }) @@ -164,6 +162,11 @@ class PlainPrinter(_ctx: Context) extends Printer { "{" ~ selfRecName(openRecs.length) ~ " => " ~ toTextGlobal(tp.parent) ~ "}" } finally openRecs = openRecs.tail + case TypeArgRef(prefix, clsRef, idx) => + val cls = clsRef.symbol + val tparams = cls.typeParams + val paramName = if (tparams.length > idx) nameString(tparams(idx)) else "" + toTextPrefix(prefix) ~ s"" case AndType(tp1, tp2) => changePrec(AndPrec) { toText(tp1) ~ " & " ~ toText(tp2) } case OrType(tp1, tp2) => @@ -199,7 +202,7 @@ class PlainPrinter(_ctx: Context) extends Printer { ParamRefNameString(tp) ~ ".type" case AnnotatedType(tpe, annot) => toTextLocal(tpe) ~ " " ~ toText(annot) - case HKApply(tycon, args) => + case AppliedType(tycon, args) => toTextLocal(tycon) ~ "[" ~ Text(args.map(argText), ", ") ~ "]" case tp: TypeVar => if (tp.isInstantiated) @@ -308,17 +311,11 @@ class PlainPrinter(_ctx: Context) extends Printer { /** String representation of a definition's type following its name */ protected def toTextRHS(tp: Type): Text = controlled { homogenize(tp) match { + case tp: TypeAlias => + " = " ~ toText(tp.alias) case tp @ TypeBounds(lo, hi) => - if (lo eq hi) { - val eql = - if (tp.variance == 1) " =+ " - else if (tp.variance == -1) " =- " - else " = " - eql ~ toText(lo) - } - else - (if (lo isRef defn.NothingClass) Text() else " >: " ~ toText(lo)) ~ - (if (hi isRef defn.AnyClass) Text() else " <: " ~ toText(hi)) + (if (lo isRef defn.NothingClass) Text() else " >: " ~ toText(lo)) ~ + (if (hi isRef defn.AnyClass) Text() else " <: " ~ toText(hi)) case tp @ ClassInfo(pre, cls, cparents, decls, selfInfo) => val preText = toTextLocal(pre) val (tparams, otherDecls) = decls.toList partition treatAsTypeParam diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 49da2dc136ee..b8631fb517a4 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -62,10 +62,8 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { override def nameString(name: Name): String = if (ctx.settings.debugNames.value) name.debugString else name.toString - override protected def simpleNameString(sym: Symbol): String = { - val name = if (ctx.property(XprintMode).isEmpty) sym.originalName else sym.name - nameString(if (sym.is(TypeParam)) name.asTypeName.unexpandedName else name) - } + override protected def simpleNameString(sym: Symbol): String = + nameString(if (ctx.property(XprintMode).isEmpty) sym.originalName else sym.name) override def fullNameString(sym: Symbol): String = if (isEmptyPrefix(sym.maybeOwner)) nameString(sym) @@ -125,6 +123,14 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { ("implicit " provided isImplicit) ~ argStr ~ " => " ~ argText(args.last) } + def isInfixType(tp: Type): Boolean = tp match { + case AppliedType(tycon, args) => + args.length == 2 && + !Character.isUnicodeIdentifierStart(tycon.typeSymbol.name.toString.head) + // TODO: Once we use the 2.12 stdlib, also check the @showAsInfix annotation + case _ => false + } + def toTextInfixType(op: Type, args: List[Type]): Text = { /* SLS 3.2.8: all infix types have the same precedence. * In A op B op' C, op and op' need the same associativity. @@ -132,8 +138,8 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { * needs to be parenthesized if it's an infix type, and vice versa. */ val l :: r :: Nil = args val isRightAssoc = op.typeSymbol.name.endsWith(":") - val leftArg = if (isRightAssoc && l.isInfixType) "(" ~ toText(l) ~ ")" else toText(l) - val rightArg = if (!isRightAssoc && r.isInfixType) "(" ~ toText(r) ~ ")" else toText(r) + val leftArg = if (isRightAssoc && isInfixType(l)) "(" ~ argText(l) ~ ")" else argText(l) + val rightArg = if (!isRightAssoc && isInfixType(r)) "(" ~ argText(r) ~ ")" else argText(r) leftArg ~ " " ~ toTextLocal(op) ~ " " ~ rightArg } @@ -146,25 +152,18 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { if (tycon.isRepeatedParam) return toTextLocal(args.head) ~ "*" if (defn.isFunctionClass(cls)) return toTextFunction(args, cls.name.isImplicitFunction) if (defn.isTupleClass(cls)) return toTextTuple(args) - if (tp.isInfixType) return toTextInfixType(tycon, args) + if (isInfixType(tp)) return toTextInfixType(tycon, args) case EtaExpansion(tycon) => return toText(tycon) case tp: TypeRef => - val hideType = !ctx.settings.debugAlias.value && (tp.symbol.isAliasPreferred) - if (hideType && !ctx.phase.erasedTypes && !tp.symbol.isCompleting) { - tp.info match { - case TypeAlias(alias) => return toText(alias) - case _ => if (tp.prefix.isInstanceOf[ThisType]) return nameString(tp.symbol) - } - } - else if (tp.symbol.isAnonymousClass && !ctx.settings.uniqid.value) + if (tp.symbol.isAnonymousClass && !ctx.settings.uniqid.value) return toText(tp.info) case ExprType(result) => return "=> " ~ toText(result) case ErasedValueType(tycon, underlying) => return "ErasedValueType(" ~ toText(tycon) ~ ", " ~ toText(underlying) ~ ")" case tp: ClassInfo => - return toTextParents(tp.parentsWithArgs) ~ "{...}" + return toTextParents(tp.parents) ~ "{...}" case JavaArrayType(elemtp) => return toText(elemtp) ~ "[]" case tp: AnnotatedType if homogenizedView => diff --git a/compiler/src/dotty/tools/dotc/printing/UserFacingPrinter.scala b/compiler/src/dotty/tools/dotc/printing/UserFacingPrinter.scala index 23ea813b6e81..0b0bbe13f47b 100644 --- a/compiler/src/dotty/tools/dotc/printing/UserFacingPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/UserFacingPrinter.scala @@ -47,6 +47,11 @@ class UserFacingPrinter(_ctx: Context) extends RefinedPrinter(_ctx) { override def toText(const: Constant): Text = Str(const.value.toString) + override def argText(tp: Type): Text = tp match { + case arg: TypeArgRef => argText(arg.underlying) + case _ => super.argText(tp) + } + override def toText(tp: Type): Text = tp match { case ExprType(result) => ":" ~~ toText(result) case tp: ConstantType => toText(tp.value) diff --git a/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala index 848832dd8c0c..348a4f62ea12 100644 --- a/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala @@ -1273,7 +1273,7 @@ object messages { val msg = hl"""|$qual does not name a parent of $cls""" val kind = "Reference" - private val parents: Seq[String] = (cls.info.parents map (_.name.show)).sorted + private val parents: Seq[String] = (cls.info.parents map (_.typeSymbol.name.show)).sorted val explanation = hl"""|When a qualifier ${"T"} is used in a ${"super"} prefix of the form ${"C.super[T]"}, diff --git a/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala b/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala index 5488d1979649..0533de0f36f6 100644 --- a/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala +++ b/compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala @@ -179,7 +179,7 @@ private class ExtractAPICollector(implicit val ctx: Context) extends ThunkHolder else dt.Module } else dt.ClassDef - val selfType = apiType(sym.classInfo.givenSelfType) + val selfType = apiType(sym.givenSelfType) val name = if (sym.is(ModuleClass)) sym.fullName.sourceModuleName else sym.fullName @@ -234,9 +234,9 @@ private class ExtractAPICollector(implicit val ctx: Context) extends ThunkHolder } def linearizedAncestorTypes(info: ClassInfo): List[Type] = { - val ref = info.fullyAppliedRef + val ref = info.appliedRef // Note that the ordering of classes in `baseClasses` is important. - info.baseClasses.tail.map(ref.baseTypeWithArgs) + info.baseClasses.tail.map(ref.baseType) } def apiDefinitions(defs: List[Symbol]): List[api.Definition] = { @@ -370,7 +370,7 @@ private class ExtractAPICollector(implicit val ctx: Context) extends ThunkHolder else tp.prefix new api.Projection(simpleType(prefix), sym.name.toString) - case TypeApplications.AppliedType(tycon, args) => + case AppliedType(tycon, args) => def processArg(arg: Type): api.Type = arg match { case arg @ TypeBounds(lo, hi) => // Handle wildcard parameters if (lo.isDirectRef(defn.NothingClass) && hi.isDirectRef(defn.AnyClass)) @@ -379,7 +379,7 @@ private class ExtractAPICollector(implicit val ctx: Context) extends ThunkHolder val name = "_" val ref = new api.ParameterRef(name) new api.Existential(ref, - Array(apiTypeParameter(name, arg.variance, lo, hi))) + Array(apiTypeParameter(name, 0, lo, hi))) } case _ => apiType(arg) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckReentrant.scala b/compiler/src/dotty/tools/dotc/transform/CheckReentrant.scala index c9eefb22f75e..878b3af95f12 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckReentrant.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckReentrant.scala @@ -82,7 +82,7 @@ class CheckReentrant extends MiniPhaseTransform { thisTransformer => } } for (parent <- cls.classInfo.classParents) - addVars(parent.symbol.asClass) + addVars(parent.typeSymbol.asClass) } } } diff --git a/compiler/src/dotty/tools/dotc/transform/CrossCastAnd.scala b/compiler/src/dotty/tools/dotc/transform/CrossCastAnd.scala index 4fc4ef10b65c..838286e81181 100644 --- a/compiler/src/dotty/tools/dotc/transform/CrossCastAnd.scala +++ b/compiler/src/dotty/tools/dotc/transform/CrossCastAnd.scala @@ -24,7 +24,7 @@ class CrossCastAnd extends MiniPhaseTransform { thisTransform => lazy val qtype = tree.qualifier.tpe.widen val sym = tree.symbol if (sym.is(Flags.Private) && qtype.typeSymbol != sym.owner) - cpy.Select(tree)(tree.qualifier.asInstance(AndType(qtype.baseTypeWithArgs(sym.owner), tree.qualifier.tpe)), tree.name) + cpy.Select(tree)(tree.qualifier.asInstance(AndType(qtype.baseType(sym.owner), tree.qualifier.tpe)), tree.name) else tree } } diff --git a/compiler/src/dotty/tools/dotc/transform/ElimErasedValueType.scala b/compiler/src/dotty/tools/dotc/transform/ElimErasedValueType.scala index d32774243a59..913264081207 100644 --- a/compiler/src/dotty/tools/dotc/transform/ElimErasedValueType.scala +++ b/compiler/src/dotty/tools/dotc/transform/ElimErasedValueType.scala @@ -86,7 +86,21 @@ class ElimErasedValueType extends MiniPhaseTransform with InfoTransformer { val site = root.thisType val info1 = site.memberInfo(sym1) val info2 = site.memberInfo(sym2) - if (!info1.matchesLoosely(info2)) + def isDefined(sym: Symbol) = sym.initialDenot.validFor.firstPhaseId <= ctx.phaseId + if (isDefined(sym1) && isDefined(sym2) && !info1.matchesLoosely(info2)) + // The reason for the `isDefined` condition is that we need to exclude mixin forwarders + // from the tests. For instance, in compileStdLib, compiling scala.immutable.SetProxy, line 29: + // new AbstractSet[B] with SetProxy[B] { val self = newSelf } + // This generates two forwarders, one in AbstractSet, the other in the anonymous class itself. + // Their signatures are: + // method map: [B, That] + // (f: B => B)(implicit bf: scala.collection.generic.CanBuildFrom[scala.collection.immutable.Set[B], B, That]): That override in anonymous class scala.collection.AbstractSet[B] with scala.collection.immutable.SetProxy[B]{...} and + // method map: [B, That](f: B => B)(implicit bf: scala.collection.generic.CanBuildFrom[scala.collection.Set[B], B, That]): That override in class AbstractSet + // These have same type after erasure: + // (f: Function1, bf: scala.collection.generic.CanBuildFrom): Object + // + // The problem is that `map` was forwarded twice, with different instantiated types. + // Maybe we should move mixin forwarding after erasure to avoid redundant forwarders like these. ctx.error( em"""double definition: |$sym1: $info1 in ${sym1.owner} and diff --git a/compiler/src/dotty/tools/dotc/transform/ElimRepeated.scala b/compiler/src/dotty/tools/dotc/transform/ElimRepeated.scala index 1a05dc9f48b8..60da2e7c33b6 100644 --- a/compiler/src/dotty/tools/dotc/transform/ElimRepeated.scala +++ b/compiler/src/dotty/tools/dotc/transform/ElimRepeated.scala @@ -11,6 +11,7 @@ import Flags._ import Contexts.Context import Symbols._ import Constants._ +import Decorators._ import Denotations._, SymDenotations._ import Decorators.StringInterpolators import dotty.tools.dotc.ast.tpd @@ -73,18 +74,23 @@ class ElimRepeated extends MiniPhaseTransform with InfoTransformer with Annotati transformTypeOfTree(tree) override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo): Tree = { - val args1 = tree.args.map { - case arg: Typed if isWildcardStarArg(arg) => - if (tree.fun.symbol.is(JavaDefined) && arg.expr.tpe.derivesFrom(defn.SeqClass)) - seqToArray(arg.expr) - else arg.expr - case arg => arg + val formals = (tree.fun.tpe.widen: @unchecked) match { + case mt: MethodType => mt.paramInfos + } + val args1 = tree.args.zipWithConserve(formals) { (arg, formal) => + arg match { + case arg: Typed if isWildcardStarArg(arg) => + if (tree.fun.symbol.is(JavaDefined) && arg.expr.tpe.derivesFrom(defn.SeqClass)) + seqToArray(arg.expr, formal.translateParameterized(defn.RepeatedParamClass, defn.ArrayClass)) + else arg.expr + case arg => arg + } } transformTypeOfTree(cpy.Apply(tree)(tree.fun, args1)) } - /** Convert sequence argument to Java array */ - private def seqToArray(tree: Tree)(implicit ctx: Context): Tree = tree match { + /** Convert sequence argument to Java array of type `pt` */ + private def seqToArray(tree: Tree, pt: Type)(implicit ctx: Context): Tree = tree match { case SeqLiteral(elems, elemtpt) => JavaSeqLiteral(elems, elemtpt) case _ => @@ -95,7 +101,7 @@ class ElimRepeated extends MiniPhaseTransform with InfoTransformer with Annotati .select(nme.seqToArray) .appliedToType(elemType) .appliedTo(tree, Literal(Constant(elemClass.typeRef))) - .ensureConforms(defn.ArrayOf(elemType)) + .ensureConforms(pt) // Because of phantomclasses, the Java array's type might not conform to the return type } diff --git a/compiler/src/dotty/tools/dotc/transform/Erasure.scala b/compiler/src/dotty/tools/dotc/transform/Erasure.scala index 0fa79214ddc8..4d2a6c454644 100644 --- a/compiler/src/dotty/tools/dotc/transform/Erasure.scala +++ b/compiler/src/dotty/tools/dotc/transform/Erasure.scala @@ -409,7 +409,7 @@ object Erasure { cpy.Super(qual)(thisQual, untpd.Ident(sym.owner.asClass.name)) .withType(SuperType(thisType, sym.owner.typeRef)) else - qual.withType(SuperType(thisType, thisType.firstParent)) + qual.withType(SuperType(thisType, thisType.firstParent.typeConstructor)) case _ => qual } diff --git a/compiler/src/dotty/tools/dotc/transform/ExpandSAMs.scala b/compiler/src/dotty/tools/dotc/transform/ExpandSAMs.scala index 29e9d3813abf..76ba7245d6c8 100644 --- a/compiler/src/dotty/tools/dotc/transform/ExpandSAMs.scala +++ b/compiler/src/dotty/tools/dotc/transform/ExpandSAMs.scala @@ -41,10 +41,10 @@ class ExpandSAMs extends MiniPhaseTransform { thisTransformer => checkRefinements(tpe, fn.pos) tree case tpe => - checkRefinements(tpe, fn.pos) - val Seq(samDenot) = tpe.abstractTermMembers.filter(!_.symbol.isSuperAccessor) + val tpe1 = checkRefinements(tpe, fn.pos) + val Seq(samDenot) = tpe1.abstractTermMembers.filter(!_.symbol.isSuperAccessor) cpy.Block(tree)(stats, - AnonClass(tpe :: Nil, fn.symbol.asTerm :: Nil, samDenot.symbol.asTerm.name :: Nil)) + AnonClass(tpe1 :: Nil, fn.symbol.asTerm :: Nil, samDenot.symbol.asTerm.name :: Nil)) } case _ => tree @@ -88,12 +88,13 @@ class ExpandSAMs extends MiniPhaseTransform { thisTransformer => cpy.Block(tree)(List(applyDef, isDefinedAtDef), anonCls) } - private def checkRefinements(tpe: Type, pos: Position)(implicit ctx: Context): Unit = tpe match { + private def checkRefinements(tpe: Type, pos: Position)(implicit ctx: Context): Type = tpe match { case RefinedType(parent, name, _) => if (name.isTermName && tpe.member(name).symbol.ownersIterator.isEmpty) // if member defined in the refinement ctx.error("Lambda does not define " + name, pos) checkRefinements(parent, pos) case _ => + tpe } } diff --git a/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala b/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala index 937490ab060a..83491c19efef 100644 --- a/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala +++ b/compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala @@ -105,7 +105,7 @@ class ExplicitOuter extends MiniPhaseTransform with InfoTransformer { thisTransf for (parentTrait <- cls.mixins) { if (needsOuterIfReferenced(parentTrait)) { - val parentTp = cls.denot.thisType.baseTypeRef(parentTrait) + val parentTp = cls.denot.thisType.baseType(parentTrait) val outerAccImpl = newOuterAccessor(cls, parentTrait).enteredAfter(thisTransformer) newDefs += DefDef(outerAccImpl, singleton(fixThis(outerPrefix(parentTp)))) } @@ -155,9 +155,28 @@ object ExplicitOuter { private def newOuterAccessors(cls: ClassSymbol)(implicit ctx: Context) = newOuterAccessor(cls, cls) :: (if (cls is Trait) Nil else newOuterParamAccessor(cls) :: Nil) - /** A new outer accessor or param accessor */ + /** A new outer accessor or param accessor. + * @param owner The class where the outer accessor is located + * @param cls The class relative to which the outer is computed (can be a base class of owner) + * @param name The name of the outer access + * @param flags The flags of the outer accessor + * + * The type of the outer accessor is computed as follows: + * Let O[X1, .., Xn] be the class eclosing `cls`. + * - if owner == cls, O[X1, ..., Xn] + * - otherwise, if the class P enclosing `owner` derives from O, the + * base type of P.this of class O + * - otherwise O[_, ..., _] + */ private def newOuterSym(owner: ClassSymbol, cls: ClassSymbol, name: TermName, flags: FlagSet)(implicit ctx: Context) = { - val target = cls.owner.enclosingClass.typeRef + val outerThis = owner.owner.enclosingClass.thisType + val outerCls = cls.owner.enclosingClass + val target = + if (owner == cls) + outerCls.appliedRef + else + outerThis.baseType(outerCls).orElse( + outerCls.typeRef.appliedTo(outerCls.typeParams.map(_ => TypeBounds.empty))) val info = if (flags.is(Method)) ExprType(target) else target ctx.withPhaseNoEarlier(ctx.explicitOuterPhase.next) // outer accessors are entered at explicitOuter + 1, should not be defined before. .newSymbol(owner, name, Synthetic | flags, info, coord = cls.coord) @@ -194,7 +213,7 @@ object ExplicitOuter { needsOuterIfReferenced(cls) && (!hasLocalInstantiation(cls) || // needs outer because we might not know whether outer is referenced or not cls.mixins.exists(needsOuterIfReferenced) || // needs outer for parent traits - cls.classInfo.parents.exists(parent => // needs outer to potentially pass along to parent + cls.info.parents.exists(parent => // needs outer to potentially pass along to parent needsOuterIfReferenced(parent.classSymbol.asClass))) /** Class is always instantiated in the compilation unit where it is defined */ diff --git a/compiler/src/dotty/tools/dotc/transform/ExplicitSelf.scala b/compiler/src/dotty/tools/dotc/transform/ExplicitSelf.scala index 7bb65e5755b2..3592471823ff 100644 --- a/compiler/src/dotty/tools/dotc/transform/ExplicitSelf.scala +++ b/compiler/src/dotty/tools/dotc/transform/ExplicitSelf.scala @@ -38,9 +38,8 @@ class ExplicitSelf extends MiniPhaseTransform { thisTransform => override def transformSelect(tree: Select)(implicit ctx: Context, info: TransformerInfo): Tree = tree match { case Select(thiz: This, name) if name.isTermName => val cls = thiz.symbol.asClass - val cinfo = cls.classInfo - if (cinfo.givenSelfType.exists && !cls.derivesFrom(tree.symbol.owner)) - cpy.Select(tree)(thiz.asInstance(AndType(cinfo.selfType, thiz.tpe)), name) + if (cls.givenSelfType.exists && !cls.derivesFrom(tree.symbol.owner)) + cpy.Select(tree)(thiz.asInstance(AndType(cls.classInfo.selfType, thiz.tpe)), name) else tree case _ => tree } diff --git a/compiler/src/dotty/tools/dotc/transform/ExtensionMethods.scala b/compiler/src/dotty/tools/dotc/transform/ExtensionMethods.scala index 02cf547b72e5..f05afe82da48 100644 --- a/compiler/src/dotty/tools/dotc/transform/ExtensionMethods.scala +++ b/compiler/src/dotty/tools/dotc/transform/ExtensionMethods.scala @@ -102,7 +102,7 @@ class ExtensionMethods extends MiniPhaseTransform with DenotTransformer with Ful moduleClassSym.copySymDenotation(info = cinfo.derivedClassInfo( // FIXME: use of VC*Companion superclasses is disabled until the conflicts with SyntheticMethods are solved. - //classParents = ctx.normalizeToClassRefs(List(newSuperClass), moduleSym, decls1), + //classParents = List(newSuperClass) decls = decls1)) case _ => moduleClassSym diff --git a/compiler/src/dotty/tools/dotc/transform/FullParameterization.scala b/compiler/src/dotty/tools/dotc/transform/FullParameterization.scala index d76a41946b39..fbf48e6a15de 100644 --- a/compiler/src/dotty/tools/dotc/transform/FullParameterization.scala +++ b/compiler/src/dotty/tools/dotc/transform/FullParameterization.scala @@ -113,7 +113,7 @@ trait FullParameterization { /** 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 (TypeParamRef(pt, _))) + tp.substDealias(ctparams, classParamsRange map (pt.paramRefs(_))) } /** The bounds for the added type parameters of the polytype `pt` */ @@ -163,7 +163,7 @@ trait FullParameterization { def rewireCall(thisArg: Tree): Tree = { val rewired = rewiredTarget(tree, derived) if (rewired.exists) { - val base = thisArg.tpe.baseTypeWithArgs(origClass) + val base = thisArg.tpe.baseType(origClass) assert(base.exists) ref(rewired.termRef) .appliedToTypeTrees(targs ++ base.argInfos.map(TypeTree(_))) diff --git a/compiler/src/dotty/tools/dotc/transform/FunctionalInterfaces.scala b/compiler/src/dotty/tools/dotc/transform/FunctionalInterfaces.scala index bde902152414..d8920fc6b043 100644 --- a/compiler/src/dotty/tools/dotc/transform/FunctionalInterfaces.scala +++ b/compiler/src/dotty/tools/dotc/transform/FunctionalInterfaces.scala @@ -72,7 +72,7 @@ class FunctionalInterfaces extends MiniPhaseTransform { // symbols loaded from classpath aren't defined in periods earlier than when they where loaded val interface = ctx.withPhase(ctx.typerPhase).getClassIfDefined(functionPackage ++ interfaceName) if (interface.exists) { - val tpt = tpd.TypeTree(interface.asType.typeRef) + val tpt = tpd.TypeTree(interface.asType.appliedRef) tpd.Closure(tree.env, tree.meth, tpt) } else tree } else tree diff --git a/compiler/src/dotty/tools/dotc/transform/HoistSuperArgs.scala b/compiler/src/dotty/tools/dotc/transform/HoistSuperArgs.scala index 8737c4c9bf49..8a83c19324f8 100644 --- a/compiler/src/dotty/tools/dotc/transform/HoistSuperArgs.scala +++ b/compiler/src/dotty/tools/dotc/transform/HoistSuperArgs.scala @@ -64,7 +64,7 @@ class HoistSuperArgs extends MiniPhaseTransform with IdentityDenotTransformer { val constr = cdef.symbol lazy val origParams = // The parameters that can be accessed in the supercall if (constr == cls.primaryConstructor) - cls.info.decls.filter(d => d.is(TypeParam) || d.is(TermParamAccessor)) + cls.info.decls.filter(d => d.is(TypeParam) || d.is(ParamAccessor)) else (cdef.tparams ::: cdef.vparamss.flatten).map(_.symbol) @@ -127,7 +127,7 @@ class HoistSuperArgs extends MiniPhaseTransform with IdentityDenotTransformer { def apply(tp: Type) = tp match { case tp: NamedType if (tp.symbol.owner == cls || tp.symbol.owner == constr) && - tp.symbol.is(ParamOrAccessor) => + tp.symbol.isParamOrAccessor => val mappedSym = origToParam(tp.symbol) if (tp.symbol.isType) mappedSym.typeRef else mappedSym.termRef case _ => diff --git a/compiler/src/dotty/tools/dotc/transform/PatternMatcherOld.scala b/compiler/src/dotty/tools/dotc/transform/PatternMatcherOld.scala index 55d0d7a0c034..4f196280c2de 100644 --- a/compiler/src/dotty/tools/dotc/transform/PatternMatcherOld.scala +++ b/compiler/src/dotty/tools/dotc/transform/PatternMatcherOld.scala @@ -1359,7 +1359,7 @@ class PatternMatcherOld extends MiniPhaseTransform with DenotTransformer { // don't go looking for selectors if we only expect one pattern def rawSubPatTypes = aligner.extractedTypes - def typeArgOfBaseTypeOr(tp: Type, baseClass: Symbol)(or: => Type): Type = (tp.baseTypeWithArgs(baseClass)).argInfos match { + def typeArgOfBaseTypeOr(tp: Type, baseClass: Symbol)(or: => Type): Type = (tp.baseType(baseClass)).argInfos match { case x :: Nil => x case _ => or } diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index 9db297324014..f620191c5d92 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -94,7 +94,15 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisTran private var inJavaAnnot: Boolean = false - private var parentNews: Set[New] = Set() + private var noCheckNews: Set[New] = Set() + + def withNoCheckNews[T](ts: List[New])(op: => T): T = { + val saved = noCheckNews + noCheckNews ++= ts + try op finally noCheckNews = saved + } + + def isCheckable(t: New) = !inJavaAnnot && !noCheckNews.contains(t) private def transformAnnot(annot: Tree)(implicit ctx: Context): Tree = { val saved = inJavaAnnot @@ -185,6 +193,16 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisTran if (ctx.owner.enclosingMethod.isInlineMethod) ctx.error(SuperCallsNotAllowedInline(ctx.owner), tree.pos) super.transform(tree) + case tree: Apply => + methPart(tree) match { + case Select(nu: New, nme.CONSTRUCTOR) if isCheckable(nu) => + // need to check instantiability here, because the type of the New itself + // might be a type constructor. + Checking.checkInstantiable(tree.tpe, nu.pos) + withNoCheckNews(nu :: Nil)(super.transform(tree)) + case _ => + super.transform(tree) + } case tree: TypeApply => val tree1 @ TypeApply(fn, args) = normalizeTypeArgs(tree) Checking.checkBounds(args, fn.tpe.widen.asInstanceOf[PolyType]) @@ -212,15 +230,12 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisTran val callTrace = Ident(call.symbol.topLevelClass.typeRef).withPos(call.pos) cpy.Inlined(tree)(callTrace, transformSub(bindings), transform(expansion)) case tree: Template => - val saved = parentNews - parentNews ++= tree.parents.flatMap(newPart) - try { + withNoCheckNews(tree.parents.flatMap(newPart)) { val templ1 = paramFwd.forwardParamAccessors(tree) synthMth.addSyntheticMethods( superAcc.wrapTemplate(templ1)( super.transform(_).asInstanceOf[Template])) } - finally parentNews = saved case tree: DefDef => transformMemberDef(tree) superAcc.wrapDefDef(tree)(super.transform(tree).asInstanceOf[DefDef]) @@ -236,7 +251,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisTran // Add Child annotation to sealed parents unless current class is anonymous if (!sym.isAnonymousClass) // ignore anonymous class - sym.asClass.classInfo.classParents.foreach { parent => + sym.asClass.classParents.foreach { parent => val sym2 = if (sym.is(Module)) sym.sourceModule else sym registerChild(sym2, parent) } @@ -247,7 +262,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisTran case tree: MemberDef => transformMemberDef(tree) super.transform(tree) - case tree: New if !inJavaAnnot && !parentNews.contains(tree) => + case tree: New if isCheckable(tree) => Checking.checkInstantiable(tree.tpe, tree.pos) super.transform(tree) case tree @ Annotated(annotated, annot) => diff --git a/compiler/src/dotty/tools/dotc/transform/ResolveSuper.scala b/compiler/src/dotty/tools/dotc/transform/ResolveSuper.scala index 64bcc4a63afd..d8e2414d19c8 100644 --- a/compiler/src/dotty/tools/dotc/transform/ResolveSuper.scala +++ b/compiler/src/dotty/tools/dotc/transform/ResolveSuper.scala @@ -62,11 +62,17 @@ class ResolveSuper extends MiniPhaseTransform with IdentityDenotTransformer { th def superAccessors(mixin: ClassSymbol): List[Tree] = for (superAcc <- mixin.info.decls.filter(_.isSuperAccessor).toList) - yield polyDefDef(implementation(superAcc.asTerm), forwarder(rebindSuper(cls, superAcc))) + yield { + util.Stats.record("super accessors") + polyDefDef(implementation(superAcc.asTerm), forwarder(rebindSuper(cls, superAcc))) + } def methodOverrides(mixin: ClassSymbol): List[Tree] = for (meth <- mixin.info.decls.toList if needsForwarder(meth)) - yield polyDefDef(implementation(meth.asTerm), forwarder(meth)) + yield { + util.Stats.record("method forwarders") + polyDefDef(implementation(meth.asTerm), forwarder(meth)) + } val overrides = mixins.flatMap(mixin => superAccessors(mixin) ::: methodOverrides(mixin)) diff --git a/compiler/src/dotty/tools/dotc/transform/Splitter.scala b/compiler/src/dotty/tools/dotc/transform/Splitter.scala index d62be1a827e0..dbd9f15e8532 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splitter.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splitter.scala @@ -92,7 +92,7 @@ class Splitter extends MiniPhaseTransform { thisTransform => else { def choose(qual: Tree, syms: List[Symbol]): Tree = { def testOrCast(which: Symbol, mbr: Symbol) = - qual.select(which).appliedToType(mbr.owner.typeRef) + qual.select(which).appliedToType(mbr.owner.appliedRef) def select(sym: Symbol) = { val qual1 = if (qual.tpe derivesFrom sym.owner) qual diff --git a/compiler/src/dotty/tools/dotc/transform/SuperAccessors.scala b/compiler/src/dotty/tools/dotc/transform/SuperAccessors.scala index 9a505ce8d49e..8cba190c0437 100644 --- a/compiler/src/dotty/tools/dotc/transform/SuperAccessors.scala +++ b/compiler/src/dotty/tools/dotc/transform/SuperAccessors.scala @@ -297,7 +297,7 @@ class SuperAccessors(thisTransformer: DenotTransformer) { val clazz = currentClass val host = hostForAccessorOf(sym, clazz) def accessibleThroughSubclassing = - validCurrentClass && (clazz.classInfo.selfType <:< sym.owner.typeRef) && !clazz.is(Trait) + validCurrentClass && clazz.classInfo.selfType.derivesFrom(sym.owner) && !clazz.is(Trait) val isCandidate = ( sym.is(Protected) @@ -308,7 +308,7 @@ class SuperAccessors(thisTransformer: DenotTransformer) { && (sym.enclosingPackageClass == sym.accessBoundary(sym.enclosingPackageClass)) ) val hostSelfType = host.classInfo.selfType - def isSelfType = !(host.typeRef <:< hostSelfType) && { + def isSelfType = !(host.appliedRef <:< hostSelfType) && { if (hostSelfType.typeSymbol.is(JavaDefined)) ctx.restrictionError( s"cannot accesses protected $sym from within $clazz with host self type $hostSelfType", pos) @@ -332,7 +332,7 @@ class SuperAccessors(thisTransformer: DenotTransformer) { */ private def hostForAccessorOf(sym: Symbol, referencingClass: ClassSymbol)(implicit ctx: Context): ClassSymbol = if (referencingClass.derivesFrom(sym.owner) - || referencingClass.classInfo.selfType <:< sym.owner.typeRef + || referencingClass.classInfo.selfType <:< sym.owner.appliedRef || referencingClass.enclosingPackageClass == sym.owner.enclosingPackageClass) { assert(referencingClass.isClass, referencingClass) referencingClass diff --git a/compiler/src/dotty/tools/dotc/transform/SymUtils.scala b/compiler/src/dotty/tools/dotc/transform/SymUtils.scala index 319cb49d2008..4f35c5e614ae 100644 --- a/compiler/src/dotty/tools/dotc/transform/SymUtils.scala +++ b/compiler/src/dotty/tools/dotc/transform/SymUtils.scala @@ -55,11 +55,12 @@ class SymUtils(val self: Symbol) extends AnyVal { def isAnyOverride(implicit ctx: Context) = self.is(Override) || self.is(AbsOverride) // careful: AbsOverride is a term only flag. combining with Override would catch only terms. - def isAliasPreferred(implicit ctx: Context) = - self.is(AliasPreferred) || self.name.is(ExpandedName) - def isSuperAccessor(implicit ctx: Context) = self.name.is(SuperAccessorName) + /** A type or term parameter or a term parameter accessor */ + def isParamOrAccessor(implicit ctx: Context) = + self.is(Param) || self.is(ParamAccessor) + /** If this is a constructor, its owner: otherwise this. */ final def skipConstructor(implicit ctx: Context): Symbol = if (self.isConstructor) self.owner else self @@ -87,8 +88,8 @@ class SymUtils(val self: Symbol) extends AnyVal { def accessorNamed(name: TermName)(implicit ctx: Context): Symbol = self.owner.info.decl(name).suchThat(_ is Accessor).symbol - def termParamAccessors(implicit ctx: Context): List[Symbol] = - self.info.decls.filter(_ is TermParamAccessor).toList + def paramAccessors(implicit ctx: Context): List[Symbol] = + self.info.decls.filter(_ is ParamAccessor).toList def caseAccessors(implicit ctx:Context) = self.info.decls.filter(_ is CaseAccessor).toList diff --git a/compiler/src/dotty/tools/dotc/transform/SyntheticMethods.scala b/compiler/src/dotty/tools/dotc/transform/SyntheticMethods.scala index 20a8a08b40d3..8e974e930aa4 100644 --- a/compiler/src/dotty/tools/dotc/transform/SyntheticMethods.scala +++ b/compiler/src/dotty/tools/dotc/transform/SyntheticMethods.scala @@ -54,12 +54,10 @@ class SyntheticMethods(thisTransformer: DenotTransformer) { /** The synthetic methods of the case or value class `clazz`. */ def syntheticMethods(clazz: ClassSymbol)(implicit ctx: Context): List[Tree] = { - val clazzType = clazz.typeRef + val clazzType = clazz.appliedRef lazy val accessors = - if (isDerivedValueClass(clazz)) - clazz.termParamAccessors - else - clazz.caseAccessors + if (isDerivedValueClass(clazz)) clazz.paramAccessors + else clazz.caseAccessors val symbolsToSynthesize: List[Symbol] = if (clazz.is(Case)) caseSymbols diff --git a/compiler/src/dotty/tools/dotc/transform/TailRec.scala b/compiler/src/dotty/tools/dotc/transform/TailRec.scala index 10c18e165dfb..a99a41921abd 100644 --- a/compiler/src/dotty/tools/dotc/transform/TailRec.scala +++ b/compiler/src/dotty/tools/dotc/transform/TailRec.scala @@ -244,7 +244,7 @@ class TailRec extends MiniPhaseTransform with DenotTransformer with FullParamete if (isRecursiveCall) { if (ctx.tailPos) { - val receiverIsSame = enclosingClass.typeRef.widenDealias =:= recvWiden + val receiverIsSame = enclosingClass.appliedRef.widenDealias =:= recvWiden val receiverIsThis = prefix.tpe =:= thisType || prefix.tpe.widen =:= thisType def rewriteTailCall(recv: Tree): Tree = { @@ -254,7 +254,7 @@ class TailRec extends MiniPhaseTransform with DenotTransformer with FullParamete val callTargs: List[tpd.Tree] = if (abstractOverClass) { - val classTypeArgs = recv.tpe.baseTypeWithArgs(enclosingClass).argInfos + val classTypeArgs = recv.tpe.baseType(enclosingClass).argInfos targs ::: classTypeArgs.map(x => ref(x.typeSymbol)) } else targs @@ -282,7 +282,7 @@ class TailRec extends MiniPhaseTransform with DenotTransformer with FullParamete } else fail(defaultReason) } else { - val receiverIsSuper = (method.name eq sym) && enclosingClass.typeRef.widen <:< recvWiden + val receiverIsSuper = (method.name eq sym) && enclosingClass.appliedRef.widen <:< recvWiden if (receiverIsSuper) fail("it contains a recursive call targeting a supertype") else continue diff --git a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala index 8fe3df9978bf..758d8e0143b2 100644 --- a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala +++ b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala @@ -487,8 +487,6 @@ object TreeChecker { assert(definedBinders.get(tp.binder) != null, s"orphan param: ${tp.show}, hash of binder = ${System.identityHashCode(tp.binder)}, tree = ${tree.show}, type = $tp0") case tp: TypeVar => apply(tp.underlying) - case tp: TypeRef if tp.info.isAlias && tp.symbol.isAliasPreferred => - apply(tp.superType) case _ => mapOver(tp) } diff --git a/compiler/src/dotty/tools/dotc/transform/VCInlineMethods.scala b/compiler/src/dotty/tools/dotc/transform/VCInlineMethods.scala index af950a209f67..146d30697bae 100644 --- a/compiler/src/dotty/tools/dotc/transform/VCInlineMethods.scala +++ b/compiler/src/dotty/tools/dotc/transform/VCInlineMethods.scala @@ -23,11 +23,12 @@ import collection.mutable.ListBuffer * type parameter, then we can rewrite: * e.foo[X, Y, ...](args) * as: - * V.foo$extension[X, Y, ..., e.A, e.B, ...](e)(args) + * V.foo$extension[X, Y, ..., A', B', ...](e)(args) + * where A', B', ... are the class type parameters A, B, ... as seen from `e`. * Otherwise, we need to evaluate e first: * { * val ev = e - * V.foo$extension[X, Y, ..., ev.A, ev.B, ...](ev)(args) + * V.foo$extension[X, Y, ..., A', B', ...](ev)(args) * } * * This phase needs to be placed after phases which may introduce calls to @@ -63,12 +64,14 @@ class VCInlineMethods extends MiniPhaseTransform with IdentityDenotTransformer { rewire(qual, mtArgs2, mArgss) case sel @ Select(qual, _) => val origMeth = sel.symbol - val ctParams = origMeth.enclosingClass.typeParams + val origCls = origMeth.enclosingClass + val ctParams = origCls.typeParams val extensionMeth = extensionMethod(origMeth) if (!ctParams.isEmpty) { evalOnce(qual) { ev => - val ctArgs = ctParams map (ev.select(_)) + val ctArgs = ctParams.map(tparam => + TypeTree(tparam.typeRef.asSeenFrom(ev.tpe, origCls))) ref(extensionMeth) .appliedToTypeTrees(mtArgs ++ ctArgs) .appliedTo(ev) diff --git a/compiler/src/dotty/tools/dotc/transform/ValueClasses.scala b/compiler/src/dotty/tools/dotc/transform/ValueClasses.scala index 00d491486a6c..4642b34c5ced 100644 --- a/compiler/src/dotty/tools/dotc/transform/ValueClasses.scala +++ b/compiler/src/dotty/tools/dotc/transform/ValueClasses.scala @@ -31,7 +31,7 @@ object ValueClasses { /** The member of a derived value class that unboxes it. */ def valueClassUnbox(d: ClassDenotation)(implicit ctx: Context): Symbol = // (info.decl(nme.unbox)).orElse(...) uncomment once we accept unbox methods - d.classInfo.decls.find(_.is(TermParamAccessor)) + d.classInfo.decls.find(_.is(ParamAccessor)) /** For a value class `d`, this returns the synthetic cast from the underlying type to * ErasedValueType defined in the companion module. This method is added to the module diff --git a/compiler/src/dotty/tools/dotc/transform/localopt/InlineCaseIntrinsics.scala b/compiler/src/dotty/tools/dotc/transform/localopt/InlineCaseIntrinsics.scala index 8e47f84c17dc..e4f291679afa 100644 --- a/compiler/src/dotty/tools/dotc/transform/localopt/InlineCaseIntrinsics.scala +++ b/compiler/src/dotty/tools/dotc/transform/localopt/InlineCaseIntrinsics.scala @@ -7,6 +7,7 @@ import core.StdNames._ import core.Symbols._ import core.Types._ import core.Flags._ +import core.TypeApplications.noBounds import ast.Trees._ import transform.SymUtils._ import Simplify.desugarIdent @@ -82,7 +83,7 @@ class InlineCaseIntrinsics(val simplifyPhase: Simplify) extends Optimisation { def some(e: Tree) = { val accessors = e.tpe.widenDealias.classSymbol.caseAccessors.filter(_.is(Method)) val fields = accessors.map(x => e.select(x).ensureApplied) - val tplType = a.tpe.baseArgTypes(defn.OptionClass).head + val tplType = noBounds(a.tpe.baseType(defn.OptionClass).argInfos.head) val someTpe = a.tpe.translateParameterized(defn.OptionClass, defn.SomeClass) if (fields.tail.nonEmpty) diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index 0b7d063cdc9d..e17b85cbb0f2 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -7,16 +7,19 @@ import Types._ import Contexts._ import Flags._ import ast.Trees._ -import ast.tpd +import ast.{tpd, untpd} import Decorators._ import Symbols._ import StdNames._ import NameOps._ import Constants._ -import typer.Applications._ +import typer._ +import Applications._ +import Inferencing._ +import ProtoTypes._ import transform.SymUtils._ import reporting.diagnostic.messages._ -import config.Printers.{ exhaustivity => debug } +import config.Printers.{exhaustivity => debug} /** Space logic for checking exhaustivity and unreachability of pattern matching * @@ -400,12 +403,23 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { else Prod(pat.tpe.stripAnnots, fun.tpe.widen, fun.symbol, pats.map(project), irrefutable(fun)) case Typed(pat @ UnApply(_, _, _), _) => project(pat) - case Typed(expr, _) => Typ(expr.tpe.stripAnnots, true) + case Typed(expr, _) => Typ(erase(expr.tpe.stripAnnots), true) case _ => debug.println(s"unknown pattern: $pat") Empty } + /* Erase a type binding according to erasure semantics in pattern matching */ + def erase(tp: Type): Type = tp match { + case tp@AppliedType(tycon, args) => erase(tp.superType) + if (tycon.isRef(defn.ArrayClass)) tp.derivedAppliedType(tycon, args.map(erase)) + else tp.derivedAppliedType(tycon, args.map(t => WildcardType(TypeBounds.empty))) + case OrType(tp1, tp2) => + OrType(erase(tp1), erase(tp2)) + case AndType(tp1, tp2) => + AndType(erase(tp1), erase(tp2)) + case _ => tp + } /** Space of the pattern: unapplySeq(a, b, c: _*) */ @@ -413,7 +427,7 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { if (pats.isEmpty) return Typ(scalaNilType, false) val (items, zero) = if (pats.last.tpe.isRepeatedParam) - (pats.init, Typ(scalaListType.appliedTo(pats.head.tpe.widen), false)) + (pats.init, Typ(scalaListType.appliedTo(pats.last.tpe.argTypes.head), false)) else (pats, Typ(scalaNilType, false)) @@ -425,41 +439,9 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { } } - - /* Erase a type binding according to erasure semantics in pattern matching */ - def erase(tp: Type): Type = { - def doErase(tp: Type): Type = tp match { - case tp: HKApply => erase(tp.superType) - case tp: RefinedType => erase(tp.parent) - case _ => tp - } - - tp match { - case OrType(tp1, tp2) => - OrType(erase(tp1), erase(tp2)) - case AndType(tp1, tp2) => - AndType(erase(tp1), erase(tp2)) - case _ => - val origin = doErase(tp) - if (origin =:= defn.ArrayType) tp else origin - } - } - /** Is `tp1` a subtype of `tp2`? */ def isSubType(tp1: Type, tp2: Type): Boolean = { - // `erase` is a workaround to make the following code pass the check: - // - // def f(e: Either[Int, String]) = e match { - // case Left(i) => i - // case Right(s) => 0 - // } - // - // The problem is that when decompose `Either[Int, String]`, `Type.wrapIfMember` - // only refines the type member inherited from `Either` -- it's complex to refine - // the type members in `Left` and `Right`. - // - // FIXME: remove this hack - val res = tp1 <:< erase(tp2) + val res = tp1 <:< tp2 debug.println(s"${tp1.show} <:< ${tp2.show} = $res") res } @@ -524,20 +506,10 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { case tp => val parts = children.map { sym => if (sym.is(ModuleClass)) - refine(tp, sym.sourceModule.termRef) - else if (sym.isTerm) - refine(tp, sym.termRef) - else if (sym.info.typeParams.length > 0 || tp.isInstanceOf[TypeRef]) - refine(tp, sym.typeRef) + refine(tp, sym.sourceModule) else - sym.typeRef - } filter { tpe => - // Child class may not always be subtype of parent: - // GADT & path-dependent types - val res = tpe <:< expose(tp) - if (!res) debug.println(s"unqualified child ousted: ${tpe.show} !< ${tp.show}") - res - } + refine(tp, sym) + } filter(_.exists) debug.println(s"${tp.show} decomposes to [${parts.map(_.show).mkString(", ")}]") @@ -545,22 +517,97 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { } } - /** Refine tp2 based on tp1 + /** Refine child based on parent + * + * In child class definition, we have: + * + * class Child[Ts] extends path.Parent[Us] with Es + * object Child extends path.Parent[Us] with Es + * val child = new path.Parent[Us] with Es // enum values * - * E.g. if `tp1` is `Option[Int]`, `tp2` is `Some`, then return - * `Some[Int]`. + * Given a parent type `parent` and a child symbol `child`, we infer the prefix + * and type parameters for the child: + * + * prefix.child[Vs] <:< parent + * + * where `Vs` are fresh type variables and `prefix` is the symbol prefix with all + * non-module and non-package `ThisType` replaced by fresh type variables. + * + * If the subtyping is true, the instantiated type `p.child[Vs]` is + * returned. Otherwise, `NoType` is returned. * - * If `tp1` is `path1.A`, `tp2` is `path2.B`, and `path1` is subtype of - * `path2`, then return `path1.B`. */ - def refine(tp1: Type, tp2: Type): Type = (tp1, tp2) match { - case (tp1: RefinedType, _: TypeRef) => tp1.wrapIfMember(refine(tp1.parent, tp2)) - case (tp1: HKApply, _) => refine(tp1.superType, tp2) - case (TypeRef(ref1: TypeProxy, _), tp2 @ TypeRef(ref2: TypeProxy, _)) => - if (ref1.underlying <:< ref2.underlying) tp2.derivedSelect(ref1) else tp2 - case (TypeRef(ref1: TypeProxy, _), tp2 @ TermRef(ref2: TypeProxy, _)) => - if (ref1.underlying <:< ref2.underlying) tp2.derivedSelect(ref1) else tp2 - case _ => tp2 + def refine(parent: Type, child: Symbol): Type = { + if (child.isTerm && child.is(Case, butNot = Module)) return child.termRef // enum vals always match + + val childTp = if (child.isTerm) child.termRef else child.typeRef + + val resTp = instantiate(childTp, parent)(ctx.fresh.setNewTyperState) + + if (!resTp.exists) { + debug.println(s"[refine] unqualified child ousted: ${childTp.show} !< ${parent.show}") + NoType + } + else { + debug.println(s"$child instantiated ------> $resTp") + resTp.dealias + } + } + + /** Instantiate type `tp1` to be a subtype of `tp2` + * + * Return the instantiated type if type parameters and this type + * in `tp1` can be instantiated such that `tp1 <:< tp2`. + * + * Otherwise, return NoType. + * + */ + def instantiate(tp1: Type, tp2: Type)(implicit ctx: Context): Type = { + // map `ThisType` of `tp1` to a type variable + // precondition: `tp1` should have the shape `path.Child`, thus `ThisType` is always covariant + val thisTypeMap = new TypeMap { + def apply(t: Type): Type = t match { + case tp @ ThisType(tref) if !tref.symbol.isStaticOwner && !tref.symbol.is(Module) => + // TODO: stackoverflow here + // newTypeVar(TypeBounds.upper(mapOver(tp.underlying))) + newTypeVar(TypeBounds.upper(mapOver(tref & tref.classSymbol.asClass.givenSelfType))) + case _ => + mapOver(t) + } + } + + val tvars = tp1.typeParams.map { tparam => newTypeVar(tparam.paramInfo.bounds) } + val protoTp1 = thisTypeMap(tp1.appliedTo(tvars)) + + // replace type parameter references with fresh type vars or bounds + val typeParamMap = new TypeMap { + def apply(t: Type): Type = t match { + + case tp: TypeRef if tp.underlying.isInstanceOf[TypeBounds] => + // See tests/patmat/gadt.scala tests/patmat/exhausting.scala + val bound = + if (variance == 0) tp.underlying.bounds // non-variant case is not well-founded + else if (variance == 1) TypeBounds.upper(tp) + else TypeBounds.lower(tp) + newTypeVar(bound) + case tp: RefinedType if tp.refinedInfo.isInstanceOf[TypeBounds] => + // Ideally, we would expect type inference to do the job + // Check tests/patmat/t9657.scala + expose(tp) + case _ => + mapOver(t) + } + } + + if (protoTp1 <:< tp2 && isFullyDefined(protoTp1, ForceDegree.noBottom)) protoTp1 + else { + val protoTp2 = typeParamMap(tp2) + if (protoTp1 <:< protoTp2 && isFullyDefined(protoTp1 & protoTp2, ForceDegree.noBottom)) protoTp1 + else { + debug.println(s"$protoTp1 <:< $protoTp2 = false") + NoType + } + } } /** Abstract sealed types, or-types, Boolean and Java enums can be decomposed */ @@ -590,13 +637,13 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { * */ def showType(tp: Type): String = { - val enclosingCls = ctx.owner.enclosingClass.asClass.classInfo.symbolicTypeRef + val enclosingCls = ctx.owner.enclosingClass def isOmittable(sym: Symbol) = sym.isEffectiveRoot || sym.isAnonymousClass || sym.name.isReplWrapperName || ctx.definitions.UnqualifiedOwnerTypes.exists(_.symbol == sym) || sym.showFullName.startsWith("scala.") || - sym == enclosingCls.typeSymbol + sym == enclosingCls || sym == enclosingCls.sourceModule def refinePrefix(tp: Type): String = tp match { case NoPrefix => "" @@ -604,10 +651,13 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { case tp: ThisType => refinePrefix(tp.tref) case tp: RefinedType => refinePrefix(tp.parent) case tp: NamedType => tp.name.show.stripSuffix("$") + case tp: TypeVar => refinePrefix(tp.instanceOpt) + case _ => tp.show } def refine(tp: Type): String = tp match { case tp: RefinedType => refine(tp.parent) + case tp: AppliedType => refine(tp.typeConstructor) case tp: ThisType => refine(tp.tref) case tp: NamedType => val pre = refinePrefix(tp.prefix) @@ -696,64 +746,50 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { } - /** Expose refined type to eliminate reference to type variables - * - * A = B M { type T = A } ~~> M { type T = B } - * - * A <: X :> Y M { type T = A } ~~> M { type T <: X :> Y } + /** Eliminate reference to type parameters in refinements * * A <: X :> Y B <: U :> V M { type T <: A :> B } ~~> M { type T <: X :> V } - * - * A = X B = Y M { type T <: A :> B } ~~> M { type T <: X :> Y } */ - def expose(tp: Type): Type = { - def follow(tp: Type, up: Boolean): Type = tp match { - case tp: TypeProxy => - tp.underlying match { - case TypeBounds(lo, hi) => - follow(if (up) hi else lo, up) - case _ => - tp - } - case OrType(tp1, tp2) => - OrType(follow(tp1, up), follow(tp2, up)) - case AndType(tp1, tp2) => - AndType(follow(tp1, up), follow(tp2, up)) - } + def expose(tp: Type, refineCtx: Boolean = false, up: Boolean = true): Type = tp match { + case tp: AppliedType => + tp.derivedAppliedType(expose(tp.tycon, refineCtx, up), tp.args.map(expose(_, refineCtx, up))) - tp match { - case tp: RefinedType => - tp.refinedInfo match { - case tpa : TypeAlias => - val hi = follow(tpa.alias, true) - val lo = follow(tpa.alias, false) - val refined = if (hi =:= lo) - tpa.derivedTypeAlias(hi) - else - tpa.derivedTypeBounds(lo, hi) - - tp.derivedRefinedType( - expose(tp.parent), - tp.refinedName, - refined - ) - case tpb @ TypeBounds(lo, hi) => - tp.derivedRefinedType( - expose(tp.parent), - tp.refinedName, - tpb.derivedTypeBounds(follow(lo, false), follow(hi, true)) - ) - case _ => - tp.derivedRefinedType( - expose(tp.parent), - tp.refinedName, - tp.refinedInfo - ) - } - case _ => tp - } + case tp: TypeAlias => + val hi = expose(tp.alias, refineCtx, up) + val lo = expose(tp.alias, refineCtx, up) + + if (hi =:= lo) + tp.derivedTypeAlias(hi) + else + tp.derivedTypeBounds(lo, hi) + + case tp @ TypeBounds(lo, hi) => + tp.derivedTypeBounds(expose(lo, refineCtx, false), expose(hi, refineCtx, true)) + + case tp: RefinedType => + tp.derivedRefinedType( + expose(tp.parent), + tp.refinedName, + expose(tp.refinedInfo, true, up) + ) + case tp: TypeProxy if refineCtx => + tp.underlying match { + case TypeBounds(lo, hi) => + expose(if (up) hi else lo, refineCtx, up) + case _ => + tp + } + + case OrType(tp1, tp2) => + OrType(expose(tp1, refineCtx, up), expose(tp2, refineCtx, up)) + + case AndType(tp1, tp2) => + AndType(expose(tp1, refineCtx, up), expose(tp2, refineCtx, up)) + + case _ => tp } + def checkExhaustivity(_match: Match): Unit = { val Match(sel, cases) = _match val selTyp = sel.tpe.widen.dealias diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index 548824ce2684..acbb1298d542 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -98,7 +98,7 @@ object Applications { if (unapplyName == nme.unapplySeq) { if (unapplyResult derivesFrom defn.SeqClass) seqSelector :: Nil else if (isGetMatch(unapplyResult, pos)) { - val seqArg = boundsToHi(getTp.elemType) + val seqArg = getTp.elemType.hiBound if (seqArg.exists) args.map(Function.const(seqArg)) else fail } @@ -382,12 +382,12 @@ trait Applications extends Compatibility { self: Typer with Dynamic => // default getters for class constructors are found in the companion object val cls = meth.owner val companion = cls.companionModule - receiver.tpe.baseTypeRef(cls) match { - case tp: TypeRef if companion.isTerm => - selectGetter(ref(TermRef(tp.prefix, companion.asTerm))) - case _ => - EmptyTree + if (companion.isTerm) { + val prefix = receiver.tpe.baseType(cls).normalizedPrefix + if (prefix.exists) selectGetter(ref(TermRef(prefix, companion.asTerm))) + else EmptyTree } + else EmptyTree } } } @@ -1096,7 +1096,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic => 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) + isAsSpecific(alt1, tp1.instantiate(tparams.map(_.typeRef)), alt2, tp2) case _ => // (3) tp2 match { case tp2: MethodType => true // (3a) @@ -1146,12 +1146,16 @@ trait Applications extends Compatibility { self: Typer with Dynamic => else { val flip = new TypeMap { def apply(t: Type) = t match { - case t: TypeAlias if variance > 0 && t.variance < 0 => t.derivedTypeAlias(t.alias, 1) case t: TypeBounds => t + case t @ AppliedType(tycon, args) => + def mapArg(arg: Type, tparam: TypeParamInfo) = + if (variance > 0 && tparam.paramVariance < 0) defn.FunctionOf(arg :: Nil, defn.UnitType) + else arg + mapOver(t.derivedAppliedType(tycon, args.zipWithConserve(tycon.typeParams)(mapArg))) case _ => mapOver(t) } } - isCompatible(flip(tp1), flip(tp2)) + (flip(tp1) relaxed_<:< flip(tp2)) || viewExists(tp1, tp2) } /** Drop any implicit parameter section */ diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index a79cff397dbf..c958c2b80178 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -83,21 +83,21 @@ object Checking { 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]) => + def checkWildcardApply(tp: Type, pos: Position): Unit = tp match { + case tp @ AppliedType(tycon, args) if args.exists(_.isInstanceOf[TypeBounds]) => tycon match { case tycon: TypeLambda => ctx.errorOrMigrationWarning( ex"unreducible application of higher-kinded type $tycon to wildcard arguments", pos) case _ => - checkWildcardHKApply(tp.superType, pos) + checkWildcardApply(tp.superType, pos) } case _ => } - def checkValidIfHKApply(implicit ctx: Context): Unit = - checkWildcardHKApply(tycon.tpe.appliedTo(args.map(_.tpe)), tree.pos) - checkValidIfHKApply(ctx.addMode(Mode.AllowLambdaWildcardApply)) + def checkValidIfApply(implicit ctx: Context): Unit = + checkWildcardApply(tycon.tpe.appliedTo(args.map(_.tpe)), tree.pos) + checkValidIfApply(ctx.addMode(Mode.AllowLambdaWildcardApply)) } /** Check that kind of `arg` has the same outline as the kind of paramBounds. @@ -133,7 +133,7 @@ object Checking { // Create a synthetic singleton type instance, and check whether // it conforms to the self type of the class as seen from that instance. val stp = SkolemType(tp) - val selfType = tref.givenSelfType.asSeenFrom(stp, cls) + val selfType = cls.asClass.givenSelfType.asSeenFrom(stp, cls) if (selfType.exists && !(stp <:< selfType)) ctx.error(DoesNotConformToSelfTypeCantBeInstantiated(tp, selfType), pos) } @@ -213,12 +213,12 @@ object Checking { case tp: TermRef => this(tp.info) mapOver(tp) + case tp @ AppliedType(tycon, args) => + tp.derivedAppliedType(this(tycon), args.map(this(_, nestedCycleOK, nestedCycleOK))) case tp @ RefinedType(parent, name, rinfo) => tp.derivedRefinedType(this(parent), name, this(rinfo, nestedCycleOK, nestedCycleOK)) case tp: RecType => tp.rebind(this(tp.parent)) - case tp @ HKApply(tycon, args) => - tp.derivedAppliedType(this(tycon), args.map(this(_, nestedCycleOK, nestedCycleOK))) case tp @ TypeRef(pre, name) => try { // A prefix is interesting if it might contain (transitively) a reference @@ -234,7 +234,7 @@ object Checking { case SuperType(thistp, _) => isInteresting(thistp) case AndType(tp1, tp2) => isInteresting(tp1) || isInteresting(tp2) case OrType(tp1, tp2) => isInteresting(tp1) && isInteresting(tp2) - case _: RefinedOrRecType | _: HKApply => true + case _: RefinedOrRecType | _: AppliedType => true case _ => false } if (isInteresting(pre)) { @@ -415,7 +415,7 @@ object Checking { * @pre The signature of `sym` refers to `other` */ def isLeaked(other: Symbol) = - other.is(Private) && { + other.is(Private, butNot = TypeParam) && { val otherBoundary = other.owner val otherLinkedBoundary = otherBoundary.linkedClass !(symBoundary.isContainedIn(otherBoundary) || @@ -445,9 +445,10 @@ object Checking { tp.derivedClassInfo( prefix = apply(tp.prefix), classParents = - tp.parentsWithArgs.map { p => - apply(p).underlyingClassRef(refinementOK = false) match { + tp.parents.map { p => + apply(p).stripAnnots match { case ref: TypeRef => ref + case ref: AppliedType => ref case _ => defn.ObjectType // can happen if class files are missing } } @@ -532,10 +533,10 @@ trait Checking { if (!tp.isStable) ctx.error(ex"$tp is not stable", pos) /** Check that all type members of `tp` have realizable bounds */ - def checkRealizableBounds(tp: Type, pos: Position)(implicit ctx: Context): Unit = { - val rstatus = boundsRealizability(tp) + def checkRealizableBounds(cls: Symbol, pos: Position)(implicit ctx: Context): Unit = { + val rstatus = boundsRealizability(cls.thisType) if (rstatus ne Realizable) - ctx.error(ex"$tp cannot be instantiated since it${rstatus.msg}", pos) + ctx.error(ex"$cls cannot be instantiated since it${rstatus.msg}", pos) } /** Check that `tp` is a class type. @@ -565,20 +566,29 @@ trait Checking { case _ => } - /** Check that any top-level type arguments in this type are feasible, i.e. that - * their lower bound conforms to their upper bound. If a type argument is - * infeasible, issue and error and continue with upper bound. + /** Check that `tp` is a class type and that any top-level type arguments in this type + * are feasible, i.e. that their lower bound conforms to their upper bound. If a type + * argument is infeasible, issue and error and continue with upper bound. */ - def checkFeasible(tp: Type, pos: Position, where: => String = "")(implicit ctx: Context): Type = tp match { - case tp: RefinedType => - tp.derivedRefinedType(tp.parent, tp.refinedName, checkFeasible(tp.refinedInfo, pos, where)) - case tp: RecType => - tp.rebind(tp.parent) - case tp @ TypeBounds(lo, hi) if !(lo <:< hi) => - ctx.error(ex"no type exists between low bound $lo and high bound $hi$where", pos) - TypeAlias(hi) - case _ => - tp + def checkFeasibleParent(tp: Type, pos: Position, where: => String = "")(implicit ctx: Context): Type = { + def checkGoodBounds(tp: Type) = tp match { + case tp @ TypeBounds(lo, hi) if !(lo <:< hi) => + ctx.error(ex"no type exists between low bound $lo and high bound $hi$where", pos) + TypeBounds(hi, hi) + case _ => + tp + } + tp match { + case tp @ AndType(tp1, tp2) => + ctx.error(s"conflicting type arguments$where", pos) + tp1 + case tp @ AppliedType(tycon, args) => + tp.derivedAppliedType(tycon, args.mapConserve(checkGoodBounds)) + case tp: RefinedType => + tp.derivedRefinedType(tp.parent, tp.refinedName, checkGoodBounds(tp.refinedInfo)) + case _ => + tp + } } /** Check that `tree` is a pure expression of constant type */ @@ -721,7 +731,7 @@ trait NoChecking extends Checking { override def checkStable(tp: Type, pos: Position)(implicit ctx: Context): Unit = () override def checkClassType(tp: Type, pos: Position, traitReq: Boolean, stablePrefixReq: Boolean)(implicit ctx: Context): Type = tp override def checkImplicitParamsNotSingletons(vparamss: List[List[ValDef]])(implicit ctx: Context): Unit = () - override def checkFeasible(tp: Type, pos: Position, where: => String = "")(implicit ctx: Context): Type = tp + override def checkFeasibleParent(tp: Type, pos: Position, where: => String = "")(implicit ctx: Context): Type = tp override def checkInlineConformant(tree: Tree, what: => String)(implicit ctx: Context) = () override def checkNoDoubleDefs(cls: Symbol)(implicit ctx: Context): Unit = () override def checkParentCall(call: Tree, caller: ClassSymbol)(implicit ctx: Context) = () diff --git a/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala b/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala index c48510d91b34..3b087dd08ae3 100644 --- a/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala +++ b/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala @@ -134,6 +134,9 @@ object ErrorReporting { def typeMismatchMsg(found: Type, expected: Type, postScript: String = "") = { // replace constrained TypeParamRefs and their typevars by their bounds where possible + // the idea is that if the bounds are also not-subtypes of each other to report + // the type mismatch on the bounds instead of the original TypeParamRefs, since + // these are usually easier to analyze. object reported extends TypeMap { def setVariance(v: Int) = variance = v val constraint = ctx.typerState.constraint @@ -154,7 +157,9 @@ object ErrorReporting { val found1 = reported(found) reported.setVariance(-1) val expected1 = reported(expected) - TypeMismatch(found1, expected1, whyNoMatchStr(found, expected), postScript) + val (found2, expected2) = + if (found1 <:< expected1) (found, expected) else (found1, expected1) + TypeMismatch(found2, expected2, whyNoMatchStr(found, expected), postScript) } /** Format `raw` implicitNotFound argument, replacing all diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 10cfcf87cf15..c0e1967ede28 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -100,10 +100,11 @@ object Implicits { // nothing since it only relates subtype with supertype. // // We keep the old behavior under -language:Scala2. - val isFunctionInS2 = ctx.scala2Mode && tpw.derivesFrom(defn.FunctionClass(1)) + val isFunctionInS2 = + ctx.scala2Mode && tpw.derivesFrom(defn.FunctionClass(1)) && ref.symbol != defn.Predef_conforms val isImplicitConverter = tpw.derivesFrom(defn.Predef_ImplicitConverter) - val isConforms = - tpw.derivesFrom(defn.Predef_Conforms) && ref.symbol != defn.Predef_conforms + val isConforms = // An implementation of <:< counts as a view, except that $conforms is always omitted + tpw.derivesFrom(defn.Predef_Conforms) && ref.symbol != defn.Predef_conforms !(isFunctionInS2 || isImplicitConverter || isConforms) } @@ -385,7 +386,7 @@ trait ImplicitRunInfo { self: RunInfo => (lead /: tp.classSymbols)(joinClass) case tp: TypeVar => apply(tp.underlying) - case tp: HKApply => + case tp: AppliedType if !tp.tycon.typeSymbol.isClass => def applyArg(arg: Type) = arg match { case TypeBounds(lo, hi) => AndType.make(lo, hi) case _: WildcardType => defn.AnyType @@ -431,11 +432,8 @@ trait ImplicitRunInfo { self: RunInfo => else if (compSym.exists) comps += companion.asSeenFrom(pre, compSym.owner).asInstanceOf[TermRef] } - def addParentScope(parent: TypeRef): Unit = { - iscopeRefs(parent) foreach addRef - for (param <- parent.typeParamSymbols) - comps ++= iscopeRefs(tp.member(param.name).info) - } + def addParentScope(parent: Type): Unit = + iscopeRefs(tp.baseType(parent.typeSymbol)) foreach addRef val companion = cls.companionModule if (companion.exists) addRef(companion.valRef) cls.classParents foreach addParentScope @@ -935,10 +933,12 @@ trait Implicits { self: Typer => */ class SearchHistory(val searchDepth: Int, val seen: Map[ClassSymbol, Int]) { - /** The number of RefinementTypes in this type, after all aliases are expanded */ + /** The number of applications and refinements in this type, after all aliases are expanded */ private def typeSize(tp: Type)(implicit ctx: Context): Int = { val accu = new TypeAccumulator[Int] { def apply(n: Int, tp: Type): Int = tp match { + case tp: AppliedType => + foldOver(n + 1, tp) case tp: RefinedType => foldOver(n + 1, tp) case tp: TypeRef if tp.info.isAlias => diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index 6976de7225ba..c2ec17b729f2 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -92,7 +92,7 @@ trait NamerContextOps { this: Context => * type of the constructed instance is returned */ def effectiveResultType(sym: Symbol, typeParams: List[Symbol], given: Type) = - if (sym.name == nme.CONSTRUCTOR) sym.owner.typeRef.appliedTo(typeParams map (_.typeRef)) + if (sym.name == nme.CONSTRUCTOR) sym.owner.typeRef.appliedTo(typeParams.map(_.typeRef)) else given /** if isConstructor, make sure it has one non-implicit parameter list */ @@ -336,7 +336,7 @@ class Namer { typer: Typer => */ def enterSymbol(sym: Symbol)(implicit ctx: Context) = { if (sym.exists) { - typr.println(s"entered: $sym in ${ctx.owner} and ${ctx.effectiveScope}") + typr.println(s"entered: $sym in ${ctx.owner}") ctx.enter(sym) } sym @@ -860,7 +860,7 @@ class Namer { typer: Typer => * (4) If the class is sealed, it is defined in the same compilation unit as the current class */ def checkedParentType(parent: untpd.Tree): Type = { - val ptype = parentType(parent)(ctx.superCallContext) + val ptype = parentType(parent)(ctx.superCallContext).dealias if (cls.isRefinementClass) ptype else { val pt = checkClassType(ptype, parent.pos, @@ -913,16 +913,15 @@ class Namer { typer: Typer => indexAndAnnotate(rest)(inClassContext(selfInfo)) symbolOfTree(constr).ensureCompleted() - val parentTypes = ensureFirstIsClass(parents.map(checkedParentType(_))) - val parentRefs = ctx.normalizeToClassRefs(parentTypes, cls, decls) - typr.println(s"completing $denot, parents = $parents, parentTypes = $parentTypes, parentRefs = $parentRefs") + val parentTypes = ensureFirstIsClass(parents.map(checkedParentType(_)), cls.pos) + typr.println(i"completing $denot, parents = $parents%, %, parentTypes = $parentTypes%, %") - tempInfo.finalize(denot, parentRefs) + tempInfo.finalize(denot, parentTypes) Checking.checkWellFormed(cls) if (isDerivedValueClass(cls)) cls.setFlag(Final) cls.info = avoidPrivateLeaks(cls, cls.pos) - cls.baseClasses.foreach(_.invalidateBaseTypeRefCache()) // we might have looked before and found nothing + cls.baseClasses.foreach(_.invalidateBaseTypeCache()) // we might have looked before and found nothing } } @@ -1179,10 +1178,7 @@ class Namer { typer: Typer => } val rhsBodyType = typedAheadType(rhs).tpe val rhsType = if (isDerived) rhsBodyType else abstracted(rhsBodyType) - val unsafeInfo = rhsType match { - case bounds: TypeBounds => bounds - case alias => TypeAlias(alias, if (sym is Local) sym.variance else 0) - } + val unsafeInfo = rhsType.toBounds if (isDerived) sym.info = unsafeInfo else { sym.info = NoCompleter diff --git a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala index 4906a26966a2..9f36fbed9d34 100644 --- a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala +++ b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala @@ -83,6 +83,7 @@ object ProtoTypes { def isMatchedBy(tp1: Type)(implicit ctx: Context) = true def map(tm: TypeMap)(implicit ctx: Context): ProtoType = this def fold[T](x: T, ta: TypeAccumulator[T])(implicit ctx: Context): T = x + override def toString = getClass.toString } /** A class marking ignored prototypes that can be revealed by `deepenProto` */ @@ -390,7 +391,7 @@ object ProtoTypes { for (n <- (0 until tl.paramNames.length).toList) yield { val tt = new TypeTree().withPos(owningTree.pos) - tt.withType(new TypeVar(TypeParamRef(tl, n), state, tt, ctx.owner)) + tt.withType(new TypeVar(tl.paramRefs(n), state, tt, ctx.owner)) } val added = @@ -474,6 +475,11 @@ object ProtoTypes { case tp: NamedType => // default case, inlined for speed if (tp.symbol.isStatic) tp else tp.derivedSelect(wildApprox(tp.prefix, theMap, seen)) + case tp @ AppliedType(tycon, args) => + wildApprox(tycon, theMap, seen) match { + case _: WildcardType => WildcardType // this ensures we get a * type + case tycon1 => tp.derivedAppliedType(tycon1, args.mapConserve(wildApprox(_, theMap, seen))) + } case tp: RefinedType => // default case, inlined for speed tp.derivedRefinedType( wildApprox(tp.parent, theMap, seen), @@ -501,11 +507,6 @@ object ProtoTypes { WildcardType(TypeBounds.upper(wildApprox(mt.paramInfos(pnum), theMap, seen))) case tp: TypeVar => wildApprox(tp.underlying, theMap, seen) - case tp @ HKApply(tycon, args) => - wildApprox(tycon, theMap, seen) match { - case _: WildcardType => WildcardType // this ensures we get a * type - case tycon1 => tp.derivedAppliedType(tycon1, args.mapConserve(wildApprox(_, theMap, seen))) - } case tp: AndType => def approxAnd = { val tp1a = wildApprox(tp.tp1, theMap, seen) @@ -528,8 +529,6 @@ object ProtoTypes { tp.derivedOrType(tp1a, tp2a) } approxOr - case tp: LazyRef => - WildcardType case tp: SelectionProto => tp.derivedSelectionProto(tp.name, wildApprox(tp.memberProto, theMap, seen), NoViewsAllowed) case tp: ViewProto => diff --git a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala index 97d50b07e34e..2fe5a0a6f69c 100644 --- a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala +++ b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala @@ -13,6 +13,7 @@ import scala.collection.{ mutable, immutable } import ast._ import Trees._ import TreeTransforms._ +import config.Printers.{checks, noPrinter} import util.DotClass import scala.util.{Try, Success, Failure} import config.{ScalaVersion, NoScalaVersion} @@ -94,16 +95,16 @@ object RefChecks { */ private def checkParents(cls: Symbol)(implicit ctx: Context): Unit = cls.info match { case cinfo: ClassInfo => - def checkSelfConforms(other: TypeRef, category: String, relation: String) = { + def checkSelfConforms(other: ClassSymbol, category: String, relation: String) = { val otherSelf = other.givenSelfType.asSeenFrom(cls.thisType, other.classSymbol) if (otherSelf.exists && !(cinfo.selfType <:< otherSelf)) ctx.error(DoesNotConformToSelfType(category, cinfo.selfType, cls, otherSelf, relation, other.classSymbol), cls.pos) } for (parent <- cinfo.classParents) - checkSelfConforms(parent, "illegal inheritance", "parent") - for (reqd <- cinfo.givenSelfType.classSymbols) - checkSelfConforms(reqd.typeRef, "missing requirement", "required") + checkSelfConforms(parent.typeSymbol.asClass, "illegal inheritance", "parent") + for (reqd <- cinfo.cls.givenSelfType.classSymbols) + checkSelfConforms(reqd, "missing requirement", "required") case _ => } @@ -251,7 +252,6 @@ object RefChecks { def compatibleTypes(memberTp: Type, otherTp: Type): Boolean = try if (member.isType) // intersection of bounds to refined types must be nonempty - member.is(BaseTypeArg) || memberTp.bounds.hi.hasSameKindAs(otherTp.bounds.hi) && ((memberTp frozen_<:< otherTp) || !member.owner.derivesFrom(other.owner) && { @@ -535,19 +535,15 @@ object RefChecks { def subclassMsg(c1: Symbol, c2: Symbol) = s": ${c1.showLocated} is a subclass of ${c2.showLocated}, but method parameter types must match exactly." val addendum = - if (abstractSym == concreteSym) { - val paArgs = pa.argInfos - val pcArgs = pc.argInfos - val paConstr = pa.withoutArgs(paArgs) - val pcConstr = pc.withoutArgs(pcArgs) - (paConstr, pcConstr) match { + if (abstractSym == concreteSym) + (pa.typeConstructor, pc.typeConstructor) match { case (TypeRef(pre1, _), TypeRef(pre2, _)) => if (pre1 =:= pre2) ": their type parameters differ" else ": their prefixes (i.e. enclosing instances) differ" case _ => "" } - } else if (abstractSym isSubClass concreteSym) + else if (abstractSym isSubClass concreteSym) subclassMsg(abstractSym, concreteSym) else if (concreteSym isSubClass abstractSym) subclassMsg(concreteSym, abstractSym) @@ -651,7 +647,13 @@ object RefChecks { // 4. Check that every defined member with an `override` modifier overrides some other member. for (member <- clazz.info.decls) if (member.isAnyOverride && !(clazz.thisType.baseClasses exists (hasMatchingSym(_, member)))) { - // for (bc <- clazz.info.baseClasses.tail) Console.println("" + bc + " has " + bc.info.decl(member.name) + ":" + bc.info.decl(member.name).tpe);//DEBUG + if (checks != noPrinter) { + for (bc <- clazz.info.baseClasses.tail) { + val sym = bc.info.decl(member.name).symbol + if (sym.exists) + checks.println(i"$bc has $sym: ${clazz.thisType.memberInfo(sym)}") + } + } val nonMatching = clazz.info.member(member.name).altsWith(alt => alt.owner != clazz) nonMatching match { diff --git a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala index fe3b80f3417f..c7eaa5bc1e6a 100644 --- a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -6,7 +6,6 @@ import core._ import ast._ import Scopes._, Contexts._, Constants._, Types._, Symbols._, Names._, Flags._, Decorators._ import ErrorReporting._, Annotations._, Denotations._, SymDenotations._, StdNames._, TypeErasure._ -import TypeApplications.AppliedType import util.Positions._ import config.Printers.typr import ast.Trees._ @@ -39,14 +38,19 @@ trait TypeAssigner { } } - /** Given a class info, the intersection of its parents, refined by all - * non-private fields, methods, and type members. + /** An abstraction of a class info, consisting of + * - the intersection of its parents, + * - refined by all non-private fields, methods, and type members, + * - abstracted over all type parameters (into a type lambda) + * - where all references to `this` of the class are closed over in a RecType. */ def classBound(info: ClassInfo)(implicit ctx: Context): Type = { - val parentType = info.parentsWithArgs.reduceLeft(ctx.typeComparer.andType(_, _)) + val cls = info.cls + val parentType = info.parents.reduceLeft(ctx.typeComparer.andType(_, _)) + def addRefinement(parent: Type, decl: Symbol) = { val inherited = - parentType.findMember(decl.name, info.cls.thisType, excluded = Private) + parentType.findMember(decl.name, cls.thisType, excluded = Private) .suchThat(decl.matches(_)) val inheritedInfo = inherited.info if (inheritedInfo.exists && decl.info <:< inheritedInfo && !(inheritedInfo <:< decl.info)) { @@ -57,10 +61,16 @@ trait TypeAssigner { else parent } - val refinableDecls = info.decls.filter( - sym => !(sym.is(TypeParamAccessor | Private) || sym.isConstructor)) + + def close(tp: Type) = RecType.closeOver(rt => tp.substThis(cls, rt.recThis)) + + def isRefinable(sym: Symbol) = !sym.is(Private) && !sym.isConstructor + val refinableDecls = info.decls.filter(isRefinable) val raw = (parentType /: refinableDecls)(addRefinement) - RecType.closeOver(rt => raw.substThis(info.cls, RecThis(rt))) + HKTypeLambda.fromParams(cls.typeParams, raw) match { + case tl: HKTypeLambda => tl.derivedLambdaType(resType = close(tl.resType)) + case tp => close(tp) + } } /** An upper approximation of the given type `tp` that does not refer to any symbol in `symsToAvoid`. @@ -120,7 +130,7 @@ trait TypeAssigner { override def derivedSelect(tp: NamedType, pre: Type) = if (pre eq tp.prefix) tp - else tryWiden(tp, tp.prefix) { + else tryWiden(tp, tp.prefix).orElse { if (tp.isTerm && variance > 0 && !pre.isInstanceOf[SingletonType]) apply(tp.info.widenExpr) else if (upper(pre).member(tp.name).exists) @@ -198,8 +208,6 @@ trait TypeAssigner { else errorType(ex"$what cannot be accessed as a member of $pre$where.$whyNot", pos) } } - else if (d.symbol is TypeParamAccessor) - ensureAccessible(d.info.bounds.hi, superAccess, pos) else ctx.makePackageObjPrefixExplicit(tpe withDenot d) case _ => @@ -297,20 +305,20 @@ trait TypeAssigner { case err: ErrorType => untpd.cpy.Super(tree)(qual, mix).withType(err) case qtype @ ThisType(_) => val cls = qtype.cls - def findMixinSuper(site: Type): Type = site.parents filter (_.name == mix.name) match { + def findMixinSuper(site: Type): Type = site.parents filter (_.typeSymbol.name == mix.name) match { case p :: Nil => - p + p.typeConstructor case Nil => errorType(SuperQualMustBeParent(mix, cls), tree.pos) case p :: q :: _ => errorType("ambiguous parent class qualifier", tree.pos) } val owntype = - if (mixinClass.exists) mixinClass.typeRef + if (mixinClass.exists) mixinClass.appliedRef else if (!mix.isEmpty) findMixinSuper(cls.info) - else if (inConstrCall || ctx.erasedTypes) cls.info.firstParent + else if (inConstrCall || ctx.erasedTypes) cls.info.firstParent.typeConstructor else { - val ps = cls.classInfo.parentsWithArgs + val ps = cls.classInfo.parents if (ps.isEmpty) defn.AnyType else ps.reduceLeft((x: Type, y: Type) => x & y) } tree.withType(SuperType(cls.thisType, owntype)) @@ -370,7 +378,7 @@ trait TypeAssigner { val newIndex = gapBuf.length gapBuf += idx // Re-index unassigned type arguments that remain after transformation - TypeParamRef(pt, newIndex) + pt.paramRefs(newIndex) } // Type parameters after naming assignment, conserving paramNames order @@ -457,10 +465,10 @@ trait TypeAssigner { tree.withType(ref.tpe) def assignType(tree: untpd.AndTypeTree, left: Tree, right: Tree)(implicit ctx: Context) = - tree.withType(inSameUniverse(_ & _, left.tpe, right, "an `&`")) + tree.withType(inSameUniverse(AndType(_, _), left.tpe, right, "an `&`")) def assignType(tree: untpd.OrTypeTree, left: Tree, right: Tree)(implicit ctx: Context) = - tree.withType(inSameUniverse(_ | _, left.tpe, right, "an `|`")) + tree.withType(inSameUniverse(OrType(_, _), left.tpe, right, "an `|`")) /** Assign type of RefinedType. * Refinements are typed as if they were members of refinement class `refineCls`. @@ -474,7 +482,7 @@ trait TypeAssigner { else RefinedType(parent, rsym.name, rinfo) } val refined = (parent.tpe /: refinements)(addRefinement) - tree.withType(RecType.closeOver(rt => refined.substThis(refineCls, RecThis(rt)))) + tree.withType(RecType.closeOver(rt => refined.substThis(refineCls, rt.recThis))) } def assignType(tree: untpd.AppliedTypeTree, tycon: Tree, args: List[Tree])(implicit ctx: Context) = { @@ -517,17 +525,15 @@ trait TypeAssigner { private def symbolicIfNeeded(sym: Symbol)(implicit ctx: Context) = { val owner = sym.owner - owner.infoOrCompleter match { - case info: ClassInfo if info.givenSelfType.exists => - // In that case a simple typeRef/termWithWithSig could return a member of - // the self type, not the symbol itself. To avoid this, we make the reference - // symbolic. In general it seems to be faster to keep the non-symblic - // reference, since there is less pressure on the uniqueness tables that way - // and less work to update all the different references. That's why symbolic references - // are only used if necessary. - NamedType.withFixedSym(owner.thisType, sym) - case _ => NoType - } + if (owner.isClass && owner.isCompleted && owner.asClass.givenSelfType.exists) + // In that case a simple typeRef/termWithWithSig could return a member of + // the self type, not the symbol itself. To avoid this, we make the reference + // symbolic. In general it seems to be faster to keep the non-symblic + // reference, since there is less pressure on the uniqueness tables that way + // and less work to update all the different references. That's why symbolic references + // are only used if necessary. + NamedType.withFixedSym(owner.thisType, sym) + else NoType } def assertExists(tp: Type) = { assert(tp != NoType); tp } diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index bd5ff738ff2d..3e2f3e7887b2 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -519,7 +519,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit if (untpd.isWildcardStarArg(tree)) cases( ifPat = ascription(TypeTree(defn.RepeatedParamType.appliedTo(pt)), isWildcard = true), - ifExpr = seqToRepeated(typedExpr(tree.expr, defn.SeqType)), + ifExpr = seqToRepeated(typedExpr(tree.expr, defn.SeqType.appliedTo(defn.AnyType))), wildName = nme.WILDCARD_STAR) else { def typedTpt = checkSimpleKinded(typedType(tree.tpt)) @@ -1394,7 +1394,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit completeAnnotations(cdef, cls) val constr1 = typed(constr).asInstanceOf[DefDef] - val parentsWithClass = ensureFirstIsClass(parents mapconserve typedParent, cdef.namePos) + val parentsWithClass = ensureFirstTreeIsClass(parents mapconserve typedParent, cdef.namePos) val parents1 = ensureConstrCall(cls, parentsWithClass)(superCtx) val self1 = typed(self)(ctx.outer).asInstanceOf[ValDef] // outer context where class members are not visible if (self1.tpt.tpe.isError || classExistsOnSelf(cls.unforcedDecls, self1)) { @@ -1412,7 +1412,8 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit val impl1 = cpy.Template(impl)(constr1, parents1, self1, body1) .withType(dummy.nonMemberTermRef) checkVariance(impl1) - if (!cls.is(AbstractOrTrait) && !ctx.isAfterTyper) checkRealizableBounds(cls.thisType, cdef.namePos) + if (!cls.is(AbstractOrTrait) && !ctx.isAfterTyper) + checkRealizableBounds(cls, cdef.namePos) val cdef1 = assignType(cpy.TypeDef(cdef)(name, impl1), cls) if (ctx.phase.isTyper && cdef1.tpe.derivesFrom(defn.DynamicClass) && !ctx.dynamicsEnabled) { val isRequired = parents1.exists(_.tpe.isRef(defn.DynamicClass)) @@ -1421,7 +1422,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit } // Check that phantom lattices are defined in a static object - if (cls.classParents.exists(_.classSymbol eq defn.PhantomClass) && !cls.isStaticOwner) + if (cls.classParents.exists(_.typeSymbol eq defn.PhantomClass) && !cls.isStaticOwner) ctx.error("only static objects can extend scala.Phantom", cdef.pos) // check value class constraints @@ -1451,12 +1452,12 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit * - has C as its class symbol, and * - for all parents P_i: If P_i derives from C then P_i <:< CT. */ - def ensureFirstIsClass(parents: List[Type])(implicit ctx: Context): List[Type] = { + def ensureFirstIsClass(parents: List[Type], pos: Position)(implicit ctx: Context): List[Type] = { def realClassParent(cls: Symbol): ClassSymbol = if (!cls.isClass) defn.ObjectClass else if (!(cls is Trait)) cls.asClass else cls.asClass.classParents match { - case parentRef :: _ => realClassParent(parentRef.symbol) + case parentRef :: _ => realClassParent(parentRef.typeSymbol) case nil => defn.ObjectClass } def improve(candidate: ClassSymbol, parent: Type): ClassSymbol = { @@ -1467,20 +1468,16 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit case p :: _ if p.classSymbol.isRealClass => parents case _ => val pcls = (defn.ObjectClass /: parents)(improve) - typr.println(i"ensure first is class $parents%, % --> ${parents map (_ baseTypeWithArgs pcls)}%, %") - val ptype = ctx.typeComparer.glb( - defn.ObjectType :: (parents map (_ baseTypeWithArgs pcls))) - ptype :: parents + typr.println(i"ensure first is class $parents%, % --> ${parents map (_ baseType pcls)}%, %") + val first = ctx.typeComparer.glb(defn.ObjectType :: parents.map(_.baseType(pcls))) + checkFeasibleParent(first, pos, em" in inferred superclass $first") :: parents } } /** Ensure that first parent tree refers to a real class. */ - def ensureFirstIsClass(parents: List[Tree], pos: Position)(implicit ctx: Context): List[Tree] = parents match { + def ensureFirstTreeIsClass(parents: List[Tree], pos: Position)(implicit ctx: Context): List[Tree] = parents match { case p :: ps if p.tpe.classSymbol.isRealClass => parents - case _ => - // add synthetic class type - val first :: _ = ensureFirstIsClass(parents.tpes) - TypeTree(checkFeasible(first, pos, em"\n in inferred parent $first")).withPos(pos) :: parents + case _ => TypeTree(ensureFirstIsClass(parents.tpes, pos).head).withPos(pos) :: parents } /** If this is a real class, make sure its first parent is a @@ -2041,13 +2038,14 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit // Follow proxies and approximate type paramrefs by their upper bound // in the current constraint in order to figure out robustly // whether an expected type is some sort of function type. - def underlyingRefined(tp: Type): Type = tp.stripTypeVar match { + def underlyingApplied(tp: Type): Type = tp.stripTypeVar match { case tp: RefinedType => tp - case tp: TypeParamRef => underlyingRefined(ctx.typeComparer.bounds(tp).hi) - case tp: TypeProxy => underlyingRefined(tp.superType) + case tp: AppliedType => tp + case tp: TypeParamRef => underlyingApplied(ctx.typeComparer.bounds(tp).hi) + case tp: TypeProxy => underlyingApplied(tp.superType) case _ => tp } - val ptNorm = underlyingRefined(pt) + val ptNorm = underlyingApplied(pt) val arity = if (defn.isFunctionType(ptNorm)) if (!isFullyDefined(pt, ForceDegree.none) && isFullyDefined(wtp, ForceDegree.none)) @@ -2204,16 +2202,24 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit case _ => } // try an implicit conversion - inferView(tree, pt) match { - case SearchSuccess(inferred, _, _, _) => - adapt(inferred, pt)(ctx.retractMode(Mode.ImplicitsEnabled)) - case failure: SearchFailure => - val prevConstraint = ctx.typerState.constraint - if (pt.isInstanceOf[ProtoType] && !failure.isInstanceOf[AmbiguousImplicits]) tree - else if (isFullyDefined(wtp, force = ForceDegree.all) && - ctx.typerState.constraint.ne(prevConstraint)) adapt(tree, pt) - else err.typeMismatch(tree, pt, failure) - } + val prevConstraint = ctx.typerState.constraint + def recover(failure: SearchFailure) = + if (isFullyDefined(wtp, force = ForceDegree.all) && + ctx.typerState.constraint.ne(prevConstraint)) adapt(tree, pt) + else err.typeMismatch(tree, pt, failure) + if (ctx.mode.is(Mode.ImplicitsEnabled)) + inferView(tree, pt) match { + case SearchSuccess(inferred, _, _, _) => + adapt(inferred, pt)(ctx.retractMode(Mode.ImplicitsEnabled)) + case failure: SearchFailure => + if (pt.isInstanceOf[ProtoType] && !failure.isInstanceOf[AmbiguousImplicits]) + // don't report the failure but return the tree unchanged. This + // wil cause a failure at the next level out, which usually gives + // a better error message. + tree + else recover(failure) + } + else recover(NoImplicitMatches) } def adaptType(tp: Type): Tree = { diff --git a/compiler/src/dotty/tools/dotc/typer/VarianceChecker.scala b/compiler/src/dotty/tools/dotc/typer/VarianceChecker.scala index 9b9866b5b03a..7cc81ceafdcf 100644 --- a/compiler/src/dotty/tools/dotc/typer/VarianceChecker.scala +++ b/compiler/src/dotty/tools/dotc/typer/VarianceChecker.scala @@ -62,7 +62,7 @@ class VarianceChecker()(implicit ctx: Context) { /** Check variance of abstract type `tvar` when referred from `base`. */ private def checkVarianceOfSymbol(tvar: Symbol): Option[VarianceError] = { val relative = relativeVariance(tvar, base) - if (relative == Bivariant || tvar.is(BaseTypeArg)) None + if (relative == Bivariant) None else { val required = compose(relative, this.variance) def tvar_s = s"$tvar (${varianceString(tvar.flags)} ${tvar.showLocated})" diff --git a/compiler/src/dotty/tools/dotc/typer/Variances.scala b/compiler/src/dotty/tools/dotc/typer/Variances.scala index aeeef02755c5..2635f9c26434 100644 --- a/compiler/src/dotty/tools/dotc/typer/Variances.scala +++ b/compiler/src/dotty/tools/dotc/typer/Variances.scala @@ -73,7 +73,7 @@ object Variances { case tp @ TypeRef(pre, _) => if (tp.symbol == tparam) Covariant else varianceInType(pre)(tparam) case tp @ TypeBounds(lo, hi) => - if (lo eq hi) compose(varianceInType(hi)(tparam), tp.variance) + if (lo eq hi) cut(varianceInType(hi)(tparam)) else flip(varianceInType(lo)(tparam)) & varianceInType(hi)(tparam) case tp @ RefinedType(parent, _, rinfo) => varianceInType(parent)(tparam) & varianceInType(rinfo)(tparam) @@ -83,7 +83,7 @@ object Variances { flip(varianceInTypes(tp.paramInfos)(tparam)) & varianceInType(tp.resultType)(tparam) case ExprType(restpe) => varianceInType(restpe)(tparam) - case tp @ HKApply(tycon, args) => + case tp @ AppliedType(tycon, args) => def varianceInArgs(v: Variance, args: List[Type], tparams: List[ParamInfo]): Variance = args match { case arg :: args1 => diff --git a/compiler/src/dotty/tools/dotc/util/Stats.scala b/compiler/src/dotty/tools/dotc/util/Stats.scala index b7e0996f5fc1..970b44248d70 100644 --- a/compiler/src/dotty/tools/dotc/util/Stats.scala +++ b/compiler/src/dotty/tools/dotc/util/Stats.scala @@ -42,6 +42,16 @@ import collection.mutable finally stack = stack.tail } else op + @inline + def trackTime[T](fn: String)(op: => T) = + if (enabled) doTrackTime(fn)(op) else op + + def doTrackTime[T](fn: String)(op: => T) = + if (monitored) { + val start = System.nanoTime + try op finally record(fn, ((System.nanoTime - start) / 1000).toInt) + } else op + class HeartBeat extends Thread() { @volatile private[Stats] var continue = true @@ -61,10 +71,10 @@ import collection.mutable } } - def monitorHeartBeat[T](op: => T)(implicit ctx: Context) = { - if (ctx.settings.Yheartbeat.value) { - var hb = new HeartBeat() - hb.start() + def maybeMonitored[T](op: => T)(implicit ctx: Context) = { + if (ctx.settings.YdetailedStats.value) { + val hb = new HeartBeat() + if (ctx.settings.Yheartbeat.value) hb.start() monitored = true try op finally { diff --git a/compiler/test-resources/repl/i2554 b/compiler/test-resources/repl/i2554 index b8d88dee119c..10fda5a7a780 100644 --- a/compiler/test-resources/repl/i2554 +++ b/compiler/test-resources/repl/i2554 @@ -4,4 +4,4 @@ scala> import foo._ scala> implicit val shape: Shape[_ <: FlatShapeLevel, Int, Int, _] = null implicit val shape: foo.Shape[_ <: foo.FlatShapeLevel, Int, Int, _] = null scala> def hint = Shape.tuple2Shape(shape, shape) -def hint: foo.Shape[foo.FlatShapeLevel, (Int, Int), (Int, Int), ()] +def hint: foo.Shape[foo.FlatShapeLevel, (Int, Int), (Int, Int), (_, _)] diff --git a/compiler/test/dotc/tests.scala b/compiler/test/dotc/tests.scala index a7af432610bd..6b01f2f99d9c 100644 --- a/compiler/test/dotc/tests.scala +++ b/compiler/test/dotc/tests.scala @@ -104,8 +104,8 @@ class tests extends CompilerTest { val typerDir = dotcDir + "typer/" val libDir = "../library/src/" - def dottyBootedLib = compileDir(libDir, ".", List("-deep", "-Ycheck-reentrant", "-strict") ::: defaultOptions)(allowDeepSubtypes) // note the -deep argument - def dottyDependsOnBootedLib = compileDir(dottyDir, ".", List("-deep", "-Ycheck-reentrant") ::: defaultOptions)(allowDeepSubtypes) // note the -deep argument + def dottyBootedLib = compileDir(libDir, ".", List("-deep", "-Ycheck-reentrant", "-strict"))(allowDeepSubtypes) // note the -deep argument + def dottyDependsOnBootedLib = compileDir(dottyDir, ".", List("-deep", "-Ycheck-reentrant"))(allowDeepSubtypes) // note the -deep argument @Before def cleanup(): Unit = { // remove class files from stdlib and tests compilation @@ -194,7 +194,6 @@ class tests extends CompilerTest { @Test def neg_i1240 = compileFile(negCustomArgs, "i1240")(allowDoubleBindings) @Test def neg_i2002 = compileFile(negCustomArgs, "i2002")(allowDoubleBindings) @Test def neg_valueclasses_doubledefs = compileFile(negCustomArgs, "valueclasses-doubledefs")(allowDoubleBindings) - @Test def neg_valueclasses_doubledefs2 = compileFile(negCustomArgs, "valueclasses-doubledefs2")(allowDoubleBindings) @Test def neg_valueclasses_pavlov = compileFile(negCustomArgs, "valueclasses-pavlov")(allowDoubleBindings) @Test def neg_trailingUnderscore = compileFile(negCustomArgs, "trailingUnderscore", args = "-strict" :: Nil) diff --git a/compiler/test/dotty/tools/dotc/CompilationTests.scala b/compiler/test/dotty/tools/dotc/CompilationTests.scala index d8cfee48c423..7721e540ef26 100644 --- a/compiler/test/dotty/tools/dotc/CompilationTests.scala +++ b/compiler/test/dotty/tools/dotc/CompilationTests.scala @@ -27,6 +27,11 @@ class CompilationTests extends ParallelTesting { // Positive tests ------------------------------------------------------------ + // @Test // enable to test compileStdLib separately with detailed stats + def compileStdLib: Unit = { + compileList("compileStdLib", StdLibSources.whitelisted, scala2Mode.and("-migration", "-Yno-inline", "-Ydetailed-stats")) + }.checkCompile() + @Test def compilePos: Unit = { compileList("compileStdLib", StdLibSources.whitelisted, scala2Mode.and("-migration", "-Yno-inline")) + compileDir("../collection-strawman/src/main", defaultOptions) + @@ -86,6 +91,7 @@ class CompilationTests extends ParallelTesting { compileFilesInDir("../tests/new", defaultOptions) + compileFilesInDir("../tests/pos-scala2", scala2Mode) + compileFilesInDir("../tests/pos", defaultOptions) + + compileFilesInDir("../tests/pos-deep-subtype", allowDeepSubtypes) + compileFile( // succeeds despite -Xfatal-warnings because of -nowarn "../tests/neg/customArgs/xfatalWarnings.scala", @@ -194,8 +200,8 @@ class CompilationTests extends ParallelTesting { // lower level of concurrency as to not kill their running VMs @Test def testPickling: Unit = { - compileDir("../compiler/src/dotty/tools", picklingOptions) + - compileDir("../compiler/src/dotty/tools/dotc", picklingOptions) + + compileDir("../compiler/src/dotty/tools", picklingOptionsAllowDeepSubTypes) + + compileDir("../compiler/src/dotty/tools/dotc", picklingOptionsAllowDeepSubTypes) + compileFilesInDir("../tests/new", picklingOptions) + compileFilesInDir("../tests/pickling", picklingOptions) + compileDir("../library/src/dotty/runtime", picklingOptions) + @@ -207,7 +213,7 @@ class CompilationTests extends ParallelTesting { compileDir("../compiler/src/dotty/tools/dotc/printing", picklingOptions) + compileDir("../compiler/src/dotty/tools/repl", picklingOptions) + compileDir("../compiler/src/dotty/tools/dotc/rewrite", picklingOptions) + - compileDir("../compiler/src/dotty/tools/dotc/transform", picklingOptions) + + compileDir("../compiler/src/dotty/tools/dotc/transform", picklingOptionsAllowDeepSubTypes) + compileDir("../compiler/src/dotty/tools/dotc/typer", picklingOptions) + compileDir("../compiler/src/dotty/tools/dotc/util", picklingOptions) + compileDir("../compiler/src/dotty/tools/io", picklingOptions) + diff --git a/compiler/test/dotty/tools/dotc/ast/DesugarTests.scala b/compiler/test/dotty/tools/dotc/ast/DesugarTests.scala index 75c25611f76f..c61773c84015 100644 --- a/compiler/test/dotty/tools/dotc/ast/DesugarTests.scala +++ b/compiler/test/dotty/tools/dotc/ast/DesugarTests.scala @@ -15,8 +15,6 @@ class DesugarTests extends DottyTest { assert( // remaining symbols must be either synthetic: sym.is(Synthetic) || - // or be a type argument from product: - (sym.isType && sym.is(BaseTypeArg)) || // or be a constructor: sym.name == nme.CONSTRUCTOR, s"found: $sym (${sym.flags})" diff --git a/compiler/test/dotty/tools/dotc/transform/PatmatExhaustivityTest.scala b/compiler/test/dotty/tools/dotc/transform/PatmatExhaustivityTest.scala index ee2d05cc9ff7..1a2855bc4c43 100644 --- a/compiler/test/dotty/tools/dotc/transform/PatmatExhaustivityTest.scala +++ b/compiler/test/dotty/tools/dotc/transform/PatmatExhaustivityTest.scala @@ -65,7 +65,8 @@ class PatmatExhaustivityTest { (file, checkContent, actual) } - @Test def patmatExhaustivity: Unit = { + @Test + def patmatExhaustivity: Unit = { val res = Directory(testsDir).list.toList .filter(f => f.extension == "scala" || f.isDirectory) .map { f => diff --git a/compiler/test/dotty/tools/vulpix/TestConfiguration.scala b/compiler/test/dotty/tools/vulpix/TestConfiguration.scala index 06022833dbb8..fb6c6aff3b3c 100644 --- a/compiler/test/dotty/tools/vulpix/TestConfiguration.scala +++ b/compiler/test/dotty/tools/vulpix/TestConfiguration.scala @@ -64,6 +64,7 @@ object TestConfiguration { "-Ytest-pickler", "-Yprintpos" ) + val picklingOptionsAllowDeepSubTypes = picklingOptions diff Array("-Yno-deep-subtypes") val scala2Mode = defaultOptions ++ Array("-language:Scala2") val explicitUTF8 = defaultOptions ++ Array("-encoding", "UTF8") val explicitUTF16 = defaultOptions ++ Array("-encoding", "UTF16") diff --git a/doc-tool/src/dotty/tools/dottydoc/model/factories.scala b/doc-tool/src/dotty/tools/dottydoc/model/factories.scala index 03f11335ead1..4208fd7608f2 100644 --- a/doc-tool/src/dotty/tools/dottydoc/model/factories.scala +++ b/doc-tool/src/dotty/tools/dottydoc/model/factories.scala @@ -176,7 +176,7 @@ object factories { paramLists(annot.tpe) case (_: TypeParamRef | _: RefinedType | _: TypeRef | _: ThisType | - _: ExprType | _: OrType | _: AndType | _: HKApply | + _: ExprType | _: OrType | _: AndType | _: AppliedType | _: TermRef | _: ConstantType) => Nil // return types should not be in the paramlist } @@ -198,7 +198,7 @@ object factories { case _ => false } - cd.classParents.collect { + cd.classParents.map(_.typeConstructor).collect { case t: TypeRef if !isJavaLangObject(t) && !isProductWithArity(t) => UnsetLink(t.name.toString, path(t.symbol).mkString(".")) } diff --git a/tests/pos/Iter3.scala b/tests/neg/Iter3.scala similarity index 96% rename from tests/pos/Iter3.scala rename to tests/neg/Iter3.scala index d0ae79f1f213..68dfdc1ac6de 100644 --- a/tests/pos/Iter3.scala +++ b/tests/neg/Iter3.scala @@ -59,19 +59,19 @@ object Iter2 { if (isEmpty) 0 else 1 + tail.length } - case class Cons[A](x: A, xs: List[A]) extends List[A] { + case class Cons[A](x: A, xs: List[A]) extends List[A] { // error: cannot be instantiated def isEmpty = false def head = x def tail = xs } - case object Nil extends List[Nothing] { + case object Nil extends List[Nothing] { // error: cannot be instantiated def isEmpty = true def head = ??? def tail = ??? } - class ArrayBuffer[A] private (initElems: Array[AnyRef], initLen: Int) extends Seq[A] with FromIterator[ArrayBuffer] { + class ArrayBuffer[A] private (initElems: Array[AnyRef], initLen: Int) extends Seq[A] with FromIterator[ArrayBuffer] { // error: cannot be instantiated def this() = this(new Array[AnyRef](16), 0) def this(it: ArrayIterator[A]) = this(it.elems, it.len) private var elems: Array[AnyRef] = initElems diff --git a/tests/neg/boundspropagation.scala b/tests/neg/boundspropagation.scala index dd4ebf5139e8..0fc36e8a3d9e 100644 --- a/tests/neg/boundspropagation.scala +++ b/tests/neg/boundspropagation.scala @@ -25,8 +25,7 @@ object test3 { } } -// Example contributed by Jason. I believe this should not typecheck, -// even though scalac does typecheck it. +// Example contributed by Jason. object test4 { class Base { type N @@ -34,7 +33,7 @@ object test4 { class Tree[-S, -T >: Option[S]] def g(x: Any): Tree[_, _ <: Option[N]] = x match { - case y: Tree[_, _] => y // error + case y: Tree[_, _] => y // works now (because of capture conversion?) } } } diff --git a/tests/neg/i1181c.scala b/tests/neg/i1181c.scala new file mode 100644 index 000000000000..281640d95275 --- /dev/null +++ b/tests/neg/i1181c.scala @@ -0,0 +1,21 @@ +// This program compiles with Dotty using refined types for application, but +// does not compile with native applications. The reason is that in previous +// Dotty the parameter reference to the lambda [X, Y] => Foo[X, Y] was a TypeRef +// which could be selected for partial application. But now the type lambda gets +// substituted directly, which prevents that conversion. The program compiles +// if the type lambda is replaced by a type alias (see pos/i1181c.scala). +class Foo[A] + +trait Bar[DD[_,_]] { + val x: DD[Int, Int] +} + +object Test { + type F[X, Y] = Foo[X] + + trait Baz extends Bar[[X,Y] => Foo[X]] { + def foo[M[_,_]](x: M[Int, Int]) = x + + foo(x) // error: found: Foo[Int](Baz.this.x) required: M[Int, Int] + } +} diff --git a/tests/neg/leak-type.scala b/tests/neg/leak-type.scala index 30ecab70bcbb..892512c5b025 100644 --- a/tests/neg/leak-type.scala +++ b/tests/neg/leak-type.scala @@ -1,13 +1,13 @@ trait A { - private type Foo = Int + private class Foo - class Inner[T <: Foo] { // error: non-private type T refers to private type Foo in its type signature + class Inner[T <: Foo] { // error: non-private type T refers to private class 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 + val a = x.get } } diff --git a/tests/neg/points.scala b/tests/neg/points.scala new file mode 100644 index 000000000000..e5a48e8de0a6 --- /dev/null +++ b/tests/neg/points.scala @@ -0,0 +1,18 @@ +trait Comparable[T] { + def compareTo(other: T): Int +} + +class Point extends Comparable[Point] { + override def compareTo(other: Point): Int = 1 +} + +class ColoredPoint extends Point with Comparable[ColoredPoint] { // error: cannot be instantiated + override def compareTo(other: ColoredPoint): Int = -1 +} + +object Test extends App { + val c: Point = new ColoredPoint + def cmp[T <: Comparable[T]](p1: Comparable[T], p2: T) = + p1.compareTo(p2) + println(cmp(c, c)) +} diff --git a/tests/neg/templateParents.scala b/tests/neg/templateParents.scala index a039625254eb..0ee134d2f594 100644 --- a/tests/neg/templateParents.scala +++ b/tests/neg/templateParents.scala @@ -12,5 +12,5 @@ object templateParentsNeg1 { trait D extends C[String] trait E extends C[Int] - val x = new D with E // error no type fits between inferred bounds + val x = new D with E // error: conflicting type arguments inferred type } diff --git a/tests/neg/customArgs/valueclasses-doubledefs2.scala b/tests/neg/valueclasses-doubledefs2.scala similarity index 70% rename from tests/neg/customArgs/valueclasses-doubledefs2.scala rename to tests/neg/valueclasses-doubledefs2.scala index dcd7bdf8d444..c4000dfd479d 100644 --- a/tests/neg/customArgs/valueclasses-doubledefs2.scala +++ b/tests/neg/valueclasses-doubledefs2.scala @@ -7,4 +7,4 @@ trait B { def apply(x: Meter) = x.toString } -object Test extends A with B // error: double def +object Test extends A with B // error: double definition diff --git a/tests/patmat/andtype-opentype-interaction.check b/tests/patmat/andtype-opentype-interaction.check index c8e212e2f2c5..4ef4731d64bd 100644 --- a/tests/patmat/andtype-opentype-interaction.check +++ b/tests/patmat/andtype-opentype-interaction.check @@ -3,4 +3,4 @@ 31: Pattern Match Exhaustivity: _: Trait & OpenClass 35: Pattern Match Exhaustivity: _: Trait & OpenTrait & OpenClass 43: Pattern Match Exhaustivity: _: Trait & OpenAbstractClass -47: Pattern Match Exhaustivity: _: Trait & OpenTrait & OpenClassSubclass +47: Pattern Match Exhaustivity: _: Trait & OpenClass & OpenTrait & OpenClassSubclass diff --git a/tests/patmat/andtype-refinedtype-interaction.check b/tests/patmat/andtype-refinedtype-interaction.check index 149a20fd5fb8..2f8687a868e5 100644 --- a/tests/patmat/andtype-refinedtype-interaction.check +++ b/tests/patmat/andtype-refinedtype-interaction.check @@ -2,9 +2,7 @@ 48: Pattern Match Exhaustivity: _: Clazz & (C1 | C2 | T1){x: Int} & (C3 | C4 | T2){x: Int}, _: Trait & (C1 | C2 | T1){x: Int} & (C3 | C4 | T2){x: Int} 54: Pattern Match Exhaustivity: _: Trait & (C1 | C2 | T1){x: Int} & C3{x: Int} 65: Pattern Match Exhaustivity: _: Trait & (C1 | C2){x: Int} & (C3 | SubC1){x: Int} -72: Pattern Match Exhaustivity: _: Trait & (T1 & C1 | T1 & SubC2){x: Int} & - (T2 & C2 | T2 & C3 | T2 & SubC1){x: Int} - & SubSubC1{x: Int} -79: Pattern Match Exhaustivity: _: Trait & (T1 & C1 | T1 & SubC2){x: Int} & - (T2 & C2 | T2 & C3 | T2 & SubC1){x: Int} - & SubSubC2{x: Int} +72: Pattern Match Exhaustivity: _: Trait & (T1 & (C1 | SubC2)){x: Int} & (T2 & (C2 | C3 | SubC1)){x: Int} & + SubSubC1{x: Int} +79: Pattern Match Exhaustivity: _: Trait & (T1 & (C1 | SubC2)){x: Int} & (T2 & (C2 | C3 | SubC1)){x: Int} & + SubSubC2{x: Int} diff --git a/tests/patmat/exhausting.scala b/tests/patmat/exhausting.scala index 03e8198dd075..75daf4b9112a 100644 --- a/tests/patmat/exhausting.scala +++ b/tests/patmat/exhausting.scala @@ -2,7 +2,7 @@ object Test { sealed abstract class Foo[T] case object Bar1 extends Foo[Int] case object Bar2 extends Foo[String] - case object Bar3 extends Foo[Any] + case object Bar3 extends Foo[AnyRef] def ex1[T](xs: List[T]) = xs match { case ys: List[_] => "ok" diff --git a/tests/patmat/partial-function.check b/tests/patmat/partial-function.check new file mode 100644 index 000000000000..fdfa33c052a8 --- /dev/null +++ b/tests/patmat/partial-function.check @@ -0,0 +1 @@ +10: Pattern Match Exhaustivity: CC(_, B2) diff --git a/tests/neg/existentials.scala b/tests/pending/neg/existentials.scala similarity index 96% rename from tests/neg/existentials.scala rename to tests/pending/neg/existentials.scala index 351febc7949b..a553656729a4 100644 --- a/tests/neg/existentials.scala +++ b/tests/pending/neg/existentials.scala @@ -44,7 +44,7 @@ class TestX { type D[X] <: C[X] type DD = [X] => D[D[X]] - val z: DD[_] = ??? // error: unreducible + //val z: DD[_] = ??? // error: unreducible val g = x.get diff --git a/tests/pending/pos/i2671.scala b/tests/pending/pos/i2671.scala new file mode 100644 index 000000000000..de942b4f9799 --- /dev/null +++ b/tests/pending/pos/i2671.scala @@ -0,0 +1,11 @@ +object Foo { + + def map[E](f: implicit E => Int): (implicit E => Int) = ??? + + implicit def i: Int = ??? + + def f: implicit Int => Int = ??? + + val a: Int = map(f) + +} diff --git a/tests/pos/sets.scala b/tests/pos-deep-subtype/sets.scala similarity index 100% rename from tests/pos/sets.scala rename to tests/pos-deep-subtype/sets.scala diff --git a/tests/pos/Patterns.scala b/tests/pos/Patterns.scala index fd0d7e97ace4..ecbde54df0b0 100644 --- a/tests/pos/Patterns.scala +++ b/tests/pos/Patterns.scala @@ -12,8 +12,7 @@ object Patterns { } } d match { - case WildcardType(bounds: TypeBounds) => - bounds.variance + case WildcardType(bounds: TypeBounds) => bounds.lo case a @ Assign(Ident(id), rhs) => id case a: Object => a } diff --git a/tests/pos/boundspropagation.scala b/tests/pos/boundspropagation.scala index c2396c2c6ca3..8be3ff1cda9f 100644 --- a/tests/pos/boundspropagation.scala +++ b/tests/pos/boundspropagation.scala @@ -11,7 +11,9 @@ object test1 { } class Derived extends Base { def g(x: Any): Tree[N] = x match { - case y: Tree[_] => y // now succeeds in dotc + case y: Tree[_] => y.asInstanceOf + // without the cast: fails in scalac and new dotc + // used to succeed in dotc if type args are refinements } } } diff --git a/tests/pos/i1181c.scala b/tests/pos/i1181c.scala index 58bd9976645b..c46b34eb3bf3 100644 --- a/tests/pos/i1181c.scala +++ b/tests/pos/i1181c.scala @@ -1,11 +1,17 @@ +// See also neg/i1181c.scala for a variant which doe not compile class Foo[A] trait Bar[DD[_,_]] { val x: DD[Int, Int] } -trait Baz extends Bar[[X,Y] => Foo[X]] { - def foo[M[_,_]](x: M[Int, Int]) = x +object Test { + type F[X, Y] = Foo[X] - foo(x) + type LAMBDA[X,Y] = Foo[X] + trait Baz extends Bar[LAMBDA] { + def foo[M[_,_]](x: M[Int, Int]) = x + + foo(x) // error: found: Foo[Int](Baz.this.x) required: M[Int, Int] + } } diff --git a/tests/pos/i2974.scala b/tests/pos/i2974.scala new file mode 100644 index 000000000000..75c6a24a41bb --- /dev/null +++ b/tests/pos/i2974.scala @@ -0,0 +1,12 @@ +trait Foo[-T] + +trait Bar[-T] extends Foo[T] + +object Test { + implicit val fa: Foo[Any] = ??? + implicit val ba: Bar[Int] = ??? + + def test: Unit = { + implicitly[Foo[Int]] + } +} diff --git a/tests/pos/i2982.scala b/tests/pos/i2982.scala new file mode 100644 index 000000000000..e4a9bbfad430 --- /dev/null +++ b/tests/pos/i2982.scala @@ -0,0 +1,8 @@ +object A { + def fun[E >: B](a: A[E]): E => Unit = ??? + val x = fun(new A[C]) +} +class B extends C +class C + +class A[-X >: B] diff --git a/tests/pos/i536.scala b/tests/pos/i536.scala index db9fb9b389c4..f2b8f9ce6b28 100644 --- a/tests/pos/i536.scala +++ b/tests/pos/i536.scala @@ -1,3 +1,12 @@ +trait Comp[T] +trait Coll[T] +class C extends Comp[C] object Max { + def max[M <: Comp[_ >: M]](x: Coll[_ <: M]): M = ??? + def max[M](x: Coll[_ <: M], cmp: Object): M = ??? + val xs: Coll[C] = ??? + val m1 = max(xs) + val m2 = max(null) + java.util.Collections.max(null) } diff --git a/tests/pos/points.scala b/tests/pos/points.scala deleted file mode 100644 index db6104c883e5..000000000000 --- a/tests/pos/points.scala +++ /dev/null @@ -1,8 +0,0 @@ -class Point extends Comparable[Point] { - override def compareTo(other: Point): Int = ??? -} - -class ColoredPoint extends Point with Comparable[ColoredPoint] { - override def compareTo(other: ColoredPoint): Int = ??? -} - diff --git a/tests/run/t8280.scala b/tests/run/t8280.scala index 5fcbad0a3ab1..7578e0417ff4 100644 --- a/tests/run/t8280.scala +++ b/tests/run/t8280.scala @@ -83,7 +83,8 @@ object Moop3 { implicit val f1: ImplicitConverter[Int, String] = _ => "Int" implicit val f2: ImplicitConverter[Long, String] = _ => "Long" - println(5: String) + println((5: Int): String) + // println(5: String) // error: ambiguity, since both f1 and f2 are applicable to 5. } }