diff --git a/src/dotty/tools/dotc/ast/Desugar.scala b/src/dotty/tools/dotc/ast/Desugar.scala index 72dd8cb4c7e6..12f3e44068c1 100644 --- a/src/dotty/tools/dotc/ast/Desugar.scala +++ b/src/dotty/tools/dotc/ast/Desugar.scala @@ -21,6 +21,13 @@ object desugar { /** Info of a variable in a pattern: The named tree and its type */ private type VarInfo = (NameTree, Tree) + /** Names of methods that are added unconditionally to case classes */ + def isDesugaredCaseClassMethodName(name: Name)(implicit ctx: Context) = + name == nme.isDefined || + name == nme.copy || + name == nme.productArity || + name.isSelectorName + // ----- DerivedTypeTrees ----------------------------------- class SetterParamTree extends DerivedTypeTree { diff --git a/src/dotty/tools/dotc/ast/tpd.scala b/src/dotty/tools/dotc/ast/tpd.scala index feeaad49dc36..b78e4c79f3b2 100644 --- a/src/dotty/tools/dotc/ast/tpd.scala +++ b/src/dotty/tools/dotc/ast/tpd.scala @@ -845,15 +845,22 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def applyOverloaded(receiver: Tree, method: TermName, args: List[Tree], targs: List[Type], expectedType: Type, isAnnotConstructor: Boolean = false)(implicit ctx: Context): Tree = { val typer = ctx.typer val proto = new FunProtoTyped(args, expectedType, typer) - val alts = receiver.tpe.member(method).alternatives.map(_.termRef) - - val alternatives = ctx.typer.resolveOverloaded(alts, proto, Nil) - assert(alternatives.size == 1, - i"multiple overloads available for $method on ${receiver.tpe.widenDealias} with targs: $targs, args: $args and expectedType: $expectedType." + - i" isAnnotConstructor = $isAnnotConstructor.\n" + - i"alternatives: $alternatives") // this is parsed from bytecode tree. there's nothing user can do about it - - val selected = alternatives.head + val denot = receiver.tpe.member(method) + assert(denot.exists, i"no member $receiver . $method, members = ${receiver.tpe.decls}") + val selected = + if (denot.isOverloaded) { + val allAlts = denot.alternatives.map(_.termRef) + val alternatives = + ctx.typer.resolveOverloaded(allAlts, proto, Nil) + assert(alternatives.size == 1, + i"${if (alternatives.isEmpty) "no" else "multiple"} overloads available for " + + i"$method on ${receiver.tpe.widenDealias} with targs: $targs, args: $args and expectedType: $expectedType." + + i" isAnnotConstructor = $isAnnotConstructor.\n" + + i"all alternatives: ${allAlts.map(_.symbol.showDcl).mkString(", ")}\n" + + i"matching alternatives: ${alternatives.map(_.symbol.showDcl).mkString(", ")}.") // this is parsed from bytecode tree. there's nothing user can do about it + alternatives.head + } + else denot.asSingleDenotation.termRef val fun = receiver .select(TermRef.withSig(receiver.tpe, selected.termSymbol.asTerm)) .appliedToTypes(targs) diff --git a/src/dotty/tools/dotc/config/Printers.scala b/src/dotty/tools/dotc/config/Printers.scala index 8b9cc45c4ce4..21147fe6fa86 100644 --- a/src/dotty/tools/dotc/config/Printers.scala +++ b/src/dotty/tools/dotc/config/Printers.scala @@ -22,13 +22,13 @@ object Printers { val implicitsDetailed: Printer = noPrinter val subtyping: Printer = noPrinter val unapp: Printer = noPrinter - val gadts = noPrinter - val hk = noPrinter - val variances = noPrinter - val incremental = noPrinter - val config = noPrinter - val transforms = noPrinter - val completions = noPrinter - val cyclicErrors = noPrinter - val pickling = noPrinter + val gadts: Printer = noPrinter + val hk: Printer = noPrinter + val variances: Printer = noPrinter + val incremental: Printer = noPrinter + val config: Printer = noPrinter + val transforms: Printer = noPrinter + val completions: Printer = noPrinter + val cyclicErrors: Printer = noPrinter + val pickling: Printer = noPrinter } diff --git a/src/dotty/tools/dotc/core/ConstraintHandling.scala b/src/dotty/tools/dotc/core/ConstraintHandling.scala index 577b958c848e..ff7afe99aeff 100644 --- a/src/dotty/tools/dotc/core/ConstraintHandling.scala +++ b/src/dotty/tools/dotc/core/ConstraintHandling.scala @@ -232,6 +232,16 @@ trait ConstraintHandling { } } + /** Instantiate `param` to `tp` if the constraint stays satisfiable */ + protected def tryInstantiate(param: PolyParam, tp: Type): Boolean = { + val saved = constraint + constraint = + if (addConstraint(param, tp, fromBelow = true) && + addConstraint(param, tp, fromBelow = false)) constraint.replace(param, tp) + else saved + constraint ne saved + } + /** Check that constraint is fully propagated. See comment in Config.checkConstraintsPropagated */ def checkPropagated(msg: => String)(result: Boolean): Boolean = { if (Config.checkConstraintsPropagated && result && addConstraintInvocations == 0) { diff --git a/src/dotty/tools/dotc/core/Definitions.scala b/src/dotty/tools/dotc/core/Definitions.scala index 4556dd9d59fc..5f794f2d51de 100644 --- a/src/dotty/tools/dotc/core/Definitions.scala +++ b/src/dotty/tools/dotc/core/Definitions.scala @@ -588,6 +588,12 @@ class Definitions { name.startsWith(prefix) && name.drop(prefix.length).forall(_.isDigit) } + def isBottomClass(cls: Symbol) = cls == NothingClass || cls == NullClass + def isBottomType(tp: Type) = tp match { + case tp: TypeRef => isBottomClass(tp.symbol) + case _ => false + } + def isFunctionClass(cls: Symbol) = isVarArityClass(cls, tpnme.Function) def isAbstractFunctionClass(cls: Symbol) = isVarArityClass(cls, tpnme.AbstractFunction) def isTupleClass(cls: Symbol) = isVarArityClass(cls, tpnme.Tuple) @@ -642,9 +648,9 @@ class Definitions { * to be the type parameters of a higher-kided type). This is a class symbol that * would be generated by the following schema. * - * class LambdaXYZ extends Object with P1 with ... with Pn { - * type v_1 $hk$Arg0; ...; type v_N $hk$ArgN; - * type Apply + * trait LambdaXYZ extends Object with P1 with ... with Pn { + * type v_1 hk$0; ...; type v_N hk$N; + * type +$Apply * } * * Here: @@ -669,7 +675,7 @@ class Definitions { val cls = denot.asClass.classSymbol val paramDecls = newScope for (i <- 0 until vcs.length) - newTypeParam(cls, tpnme.LambdaArgName(i), varianceFlags(vcs(i)), paramDecls) + newTypeParam(cls, tpnme.hkArg(i), varianceFlags(vcs(i)), paramDecls) newTypeField(cls, tpnme.hkApply, Covariant, paramDecls) val parentTraitRefs = for (i <- 0 until vcs.length if vcs(i) != 0) @@ -679,7 +685,7 @@ class Definitions { } } - val traitName = tpnme.LambdaTraitName(vcs) + val traitName = tpnme.hkLambda(vcs) def createTrait = { val cls = newClassSymbol( diff --git a/src/dotty/tools/dotc/core/Denotations.scala b/src/dotty/tools/dotc/core/Denotations.scala index b6519e1cd9da..fcd60ef16a16 100644 --- a/src/dotty/tools/dotc/core/Denotations.scala +++ b/src/dotty/tools/dotc/core/Denotations.scala @@ -324,7 +324,7 @@ object Denotations { info1 // follow Scala2 linearization - // compare with way merge is performed in SymDenotation#computeMembersNamed else - throw new MergeError(s"${ex.getMessage} as members of type ${pre.show}") + throw new MergeError(s"${ex.getMessage} as members of type ${pre.show}", ex.tp1, ex.tp2) } new JointRefDenotation(sym, jointInfo, denot1.validFor & denot2.validFor) } diff --git a/src/dotty/tools/dotc/core/Flags.scala b/src/dotty/tools/dotc/core/Flags.scala index c8a9dc13bab4..42d06c2ab0f8 100644 --- a/src/dotty/tools/dotc/core/Flags.scala +++ b/src/dotty/tools/dotc/core/Flags.scala @@ -306,10 +306,12 @@ object Flags { /** A case parameter accessor */ final val CaseAccessor = termFlag(25, "") - /** An type parameter which is an alias for some other (non-visible) type parameter */ - final val TypeArgument = typeFlag(25, "") + /** A binding for a type parameter of a base class or trait. + * TODO: Replace with combination of isType, ExpandedName, and Override? + */ + final val BaseTypeArg = typeFlag(25, "") - final val CaseAccessorOrTypeArgument = CaseAccessor.toCommonFlags + final val CaseAccessorOrBaseTypeArg = CaseAccessor.toCommonFlags /** A super accessor */ final val SuperAccessor = termFlag(26, "") @@ -446,7 +448,7 @@ object Flags { final val FromStartFlags = AccessFlags | Module | Package | Deferred | Final | MethodOrHKCommon | Param | ParamAccessor | Scala2ExistentialCommon | InSuperCall | Touched | JavaStatic | CovariantOrOuter | ContravariantOrLabel | ExpandedName | AccessorOrSealed | - CaseAccessorOrTypeArgument | Fresh | Frozen | Erroneous | ImplicitCommon | Permanent | + CaseAccessorOrBaseTypeArg | Fresh | Frozen | Erroneous | ImplicitCommon | Permanent | LazyOrTrait | SuperAccessorOrScala2x | SelfNameOrImplClass assert(FromStartFlags.isTermFlags && FromStartFlags.isTypeFlags) @@ -545,7 +547,7 @@ object Flags { final val TypeParamOrAccessor = TypeParam | TypeParamAccessor /** If symbol of a type alias has these flags, prefer the alias */ - final val AliasPreferred = TypeParam | TypeArgument | ExpandedName + final val AliasPreferred = TypeParam | BaseTypeArg | ExpandedName /** A covariant type parameter instance */ final val LocalCovariant = allOf(Local, Covariant) @@ -583,6 +585,9 @@ object Flags { /** A private[this] parameter accessor */ final val PrivateLocalParamAccessor = allOf(Private, Local, ParamAccessor) + /** A parameter forwarder */ + final val ParamForwarder = allOf(Method, Stable, ParamAccessor) + /** A private[this] parameter */ final val PrivateLocalParam = allOf(Private, Local, Param) diff --git a/src/dotty/tools/dotc/core/NameOps.scala b/src/dotty/tools/dotc/core/NameOps.scala index 1a26463473ac..6c1930c9f511 100644 --- a/src/dotty/tools/dotc/core/NameOps.scala +++ b/src/dotty/tools/dotc/core/NameOps.scala @@ -81,6 +81,7 @@ object NameOps { def isScala2LocalSuffix = name.endsWith(" ") def isModuleVarName(name: Name): Boolean = name.stripAnonNumberSuffix endsWith MODULE_VAR_SUFFIX + def isSelectorName = name.startsWith(" ") && name.tail.forall(_.isDigit) /** Is name a variable name? */ def isVariableName: Boolean = name.length > 0 && { @@ -99,19 +100,22 @@ object NameOps { } /** Is this the name of a higher-kinded type parameter of a Lambda? */ - def isLambdaArgName = + def isHkArgName = name.length > 0 && - name.head == tpnme.LAMBDA_ARG_PREFIXhead && - name.startsWith(tpnme.LAMBDA_ARG_PREFIX) && { - val digits = name.drop(tpnme.LAMBDA_ARG_PREFIX.length) + name.head == tpnme.hkArgPrefixHead && + name.startsWith(tpnme.hkArgPrefix) && { + val digits = name.drop(tpnme.hkArgPrefixLength) digits.length <= 4 && digits.forall(_.isDigit) } /** The index of the higher-kinded type parameter with this name. * Pre: isLambdaArgName. */ - def LambdaArgIndex: Int = - name.drop(tpnme.LAMBDA_ARG_PREFIX.length).toString.toInt + def hkArgIndex: Int = + name.drop(tpnme.hkArgPrefixLength).toString.toInt + + def isLambdaTraitName(implicit ctx: Context): Boolean = + name.startsWith(tpnme.hkLambdaPrefix) /** If the name ends with $nn where nn are * all digits, strip the $ and the digits. diff --git a/src/dotty/tools/dotc/core/StdNames.scala b/src/dotty/tools/dotc/core/StdNames.scala index e8cddd3d4743..e2add1a520a4 100644 --- a/src/dotty/tools/dotc/core/StdNames.scala +++ b/src/dotty/tools/dotc/core/StdNames.scala @@ -173,8 +173,6 @@ object StdNames { final val WILDCARD_STAR: N = "_*" final val REIFY_TREECREATOR_PREFIX: N = "$treecreator" final val REIFY_TYPECREATOR_PREFIX: N = "$typecreator" - final val LAMBDA_ARG_PREFIX: N = "hk$" - final val LAMBDA_ARG_PREFIXhead: Char = LAMBDA_ARG_PREFIX.head final val AbstractFunction: N = "AbstractFunction" final val Any: N = "Any" @@ -314,7 +312,6 @@ object StdNames { val AnnotatedType: N = "AnnotatedType" val AppliedTypeTree: N = "AppliedTypeTree" - val hkApply: N = "$apply" val ArrayAnnotArg: N = "ArrayAnnotArg" val Constant: N = "Constant" val ConstantType: N = "ConstantType" @@ -322,7 +319,6 @@ object StdNames { val Flag : N = "Flag" val Ident: N = "Ident" val Import: N = "Import" - val LambdaPrefix: N = "Lambda$" val Literal: N = "Literal" val LiteralAnnotArg: N = "LiteralAnnotArg" val Modifiers: N = "Modifiers" @@ -530,9 +526,14 @@ object StdNames { val nothingRuntimeClass: N = "scala.runtime.Nothing$" val nullRuntimeClass: N = "scala.runtime.Null$" - val synthSwitch: N = "$synthSwitch" + val hkApply: N = "$Apply" + val hkArgPrefix: N = "$hk" + val hkLambdaPrefix: N = "Lambda$" + val hkArgPrefixHead: Char = hkArgPrefix.head + val hkArgPrefixLength: Int = hkArgPrefix.length + // unencoded operators object raw { final val AMP : N = "&" @@ -742,14 +743,13 @@ object StdNames { def syntheticTypeParamNames(num: Int): List[TypeName] = (0 until num).map(syntheticTypeParamName)(breakOut) - def LambdaTraitName(vcs: List[Int]): TypeName = LambdaPrefix ++ vcs.map(varianceSuffix).mkString - def LambdaArgName(n: Int) = LAMBDA_ARG_PREFIX ++ n.toString - - final val Conforms = encode("<:<") + def hkLambda(vcs: List[Int]): TypeName = hkLambdaPrefix ++ vcs.map(varianceSuffix).mkString + def hkArg(n: Int): TypeName = hkArgPrefix ++ n.toString def varianceSuffix(v: Int): Char = varianceSuffixes.charAt(v + 1) - val varianceSuffixes = "NIP" + + final val Conforms = encode("<:<") } abstract class JavaNames[N <: Name] extends DefinedNames[N] { diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala index d7fa183c9ed7..e351557a31c0 100644 --- a/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/src/dotty/tools/dotc/core/SymDenotations.scala @@ -167,7 +167,6 @@ object SymDenotations { completions.println(i"${" " * indent}completing ${if (isType) "type" else "val"} $name") indent += 1 } - indent += 1 if (myFlags is Touched) throw CyclicReference(this) myFlags |= Touched @@ -446,7 +445,7 @@ object SymDenotations { /** is this symbol a trait representing a type lambda? */ final def isLambdaTrait(implicit ctx: Context): Boolean = - isClass && name.startsWith(tpnme.LambdaPrefix) && owner == defn.ScalaPackageClass + isClass && name.startsWith(tpnme.hkLambdaPrefix) && owner == defn.ScalaPackageClass /** Is this symbol a package object or its module class? */ def isPackageObject(implicit ctx: Context): Boolean = { diff --git a/src/dotty/tools/dotc/core/Symbols.scala b/src/dotty/tools/dotc/core/Symbols.scala index e33f9651ecd7..2a76f18d8ff6 100644 --- a/src/dotty/tools/dotc/core/Symbols.scala +++ b/src/dotty/tools/dotc/core/Symbols.scala @@ -368,7 +368,7 @@ object Symbols { type ThisName <: Name - //assert(_id != 30214) + //assert(id != 4285) /** The last denotation of this symbol */ private[this] var lastDenot: SymDenotation = _ diff --git a/src/dotty/tools/dotc/core/TypeApplications.scala b/src/dotty/tools/dotc/core/TypeApplications.scala index 661975dabce9..fbab4ee39744 100644 --- a/src/dotty/tools/dotc/core/TypeApplications.scala +++ b/src/dotty/tools/dotc/core/TypeApplications.scala @@ -36,6 +36,145 @@ object TypeApplications { case tp: TypeBounds => tp.hi case _ => tp } + + /** Does the variance of `sym1` conform to the variance of `sym2`? + * This is the case if the variances are the same or `sym` is nonvariant. + */ + def varianceConforms(sym1: TypeSymbol, sym2: TypeSymbol)(implicit ctx: Context) = + sym1.variance == sym2.variance || sym2.variance == 0 + + def variancesConform(syms1: List[TypeSymbol], syms2: List[TypeSymbol])(implicit ctx: Context) = + syms1.corresponds(syms2)(varianceConforms) + + /** Extractor for + * + * [v1 X1: B1, ..., vn Xn: Bn] -> T + * ==> + * Lambda$_v1...vn { type $hk_i: B_i, type $Apply = [X_i := this.$Arg_i] T } + */ + object TypeLambda { + def apply(variances: List[Int], + argBoundsFns: List[RefinedType => TypeBounds], + bodyFn: RefinedType => Type)(implicit ctx: Context): Type = { + def argRefinements(parent: Type, i: Int, bs: List[RefinedType => TypeBounds]): Type = bs match { + case b :: bs1 => + argRefinements(RefinedType(parent, tpnme.hkArg(i), b), i + 1, bs1) + case nil => + parent + } + assert(variances.nonEmpty) + assert(argBoundsFns.length == variances.length) + RefinedType( + argRefinements(defn.LambdaTrait(variances).typeRef, 0, argBoundsFns), + tpnme.hkApply, bodyFn(_).bounds.withVariance(1)) + } + + def unapply(tp: Type)(implicit ctx: Context): Option[(List[Int], List[TypeBounds], Type)] = tp match { + case app @ RefinedType(parent, tpnme.hkApply) => + val cls = parent.typeSymbol + val variances = cls.typeParams.map(_.variance) + def collectBounds(t: Type, acc: List[TypeBounds]): List[TypeBounds] = t match { + case t @ RefinedType(p, rname) => + assert(rname.isHkArgName) + collectBounds(p, t.refinedInfo.bounds :: acc) + case TypeRef(_, lname) => + assert(lname.isLambdaTraitName) + acc + } + val argBounds = collectBounds(parent, Nil) + Some((variances, argBounds, app.refinedInfo.argInfo)) + case _ => + None + } + } + + /** Extractor for + * + * [v1 X1: B1, ..., vn Xn: Bn] -> C[X1, ..., Xn] + * + * where v1, ..., vn and B1, ..., Bn are the variances and bounds of the type parameters + * of the class C. + * + * @param tycon C + */ + object EtaExpansion { + def apply(tycon: TypeRef)(implicit ctx: Context) = { + assert(tycon.isEtaExpandable) + tycon.EtaExpand(tycon.typeParams) + } + + def unapply(tp: Type)(implicit ctx: Context): Option[TypeRef] = { + def argsAreForwarders(args: List[Type], n: Int): Boolean = args match { + case Nil => + n == 0 + case TypeRef(RefinedThis(rt), sel) :: args1 => + rt.eq(tp) && sel == tpnme.hkArg(n - 1) && argsAreForwarders(args1, n - 1) + case _ => + false + } + tp match { + case TypeLambda(_, argBounds, AppliedType(fn: TypeRef, args)) + if argsAreForwarders(args, tp.typeParams.length) => 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, + * If `T`'s class symbol is a lambda trait, follow the refined type with a + * projection + * + * T { ... } # $Apply + */ + 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 TypeRef(prefix, tpnme.hkApply) => unapp(prefix) + case _ => + unapp(tp) match { + case Some((tycon: TypeRef, _)) if tycon.symbol.isLambdaTrait => + // We are seeing part of a lambda abstraction, not an applied type + None + case x => x + } + } + + private def unapp(tp: Type)(implicit ctx: Context): Option[(Type, List[Type])] = tp match { + case _: RefinedType => + val tparams = tp.classSymbol.typeParams + if (tparams.isEmpty) None + else { + val argBuf = new mutable.ListBuffer[Type] + def stripArgs(tp: Type, n: Int): Type = + if (n == 0) tp + else tp match { + case tp @ RefinedType(parent, pname) if pname == tparams(n - 1).name => + val res = stripArgs(parent, n - 1) + if (res.exists) argBuf += tp.refinedInfo.argInfo + res + case _ => + NoType + } + val res = stripArgs(tp, tparams.length) + if (res.exists) Some((res, argBuf.toList)) else None + } + case _ => None + } + } + + /** Adapt all arguments to possible higher-kinded type parameters using etaExpandIfHK + */ + def etaExpandIfHK(tparams: List[Symbol], args: List[Type])(implicit ctx: Context): List[Type] = + if (tparams.isEmpty) args + else args.zipWithConserve(tparams)((arg, tparam) => arg.etaExpandIfHK(tparam.infoOrCompleter)) + + def argRefs(rt: RefinedType, n: Int)(implicit ctx: Context) = + List.range(0, n).map(i => RefinedThis(rt).select(tpnme.hkArg(i))) } import TypeApplications._ @@ -61,27 +200,27 @@ class TypeApplications(val self: Type) extends AnyVal { case self: ClassInfo => self.cls.typeParams case self: TypeRef => - val tsym = self.typeSymbol + val tsym = self.symbol if (tsym.isClass) tsym.typeParams else if (tsym.isAliasType) self.underlying.typeParams - else { - val lam = LambdaClass(forcing = false) - if (lam.exists) lam.typeParams else Nil - } + else if (tsym.isCompleting) + // We are facing a problem when computing the type parameters of an uncompleted + // abstract type. We can't access the bounds of the symbol yet because that + // would cause a cause a cyclic reference. So we return `Nil` instead + // and try to make up for it later. The acrobatics in Scala2Unpicker#readType + // for reading a TypeRef show what's neeed. + Nil + else tsym.info.typeParams case self: RefinedType => - def isBoundedLambda(tp: Type): Boolean = tp match { - case tp: TypeRef => tp.typeSymbol.isLambdaTrait - case tp: RefinedType => tp.refinedName.isLambdaArgName && isBoundedLambda(tp.parent) - case _ => false - } - val tparams = self.parent.typeParams - self.refinedInfo match { - case rinfo: TypeBounds if rinfo.isAlias || isBoundedLambda(self) => - tparams.filterNot(_.name == self.refinedName) - case _ => - tparams + // inlined and optimized version of + // val sym = self.LambdaTrait + // if (sym.exists) return sym.typeParams + if (self.refinedName == tpnme.hkApply) { + val sym = self.parent.classSymbol + if (sym.isLambdaTrait) return sym.typeParams } - case self: SingletonType => + self.parent.typeParams.filterNot(_.name == self.refinedName) + case self: SingletonType => Nil case self: TypeProxy => self.underlying.typeParams @@ -90,76 +229,24 @@ class TypeApplications(val self: Type) extends AnyVal { } } - /** The type parameters of the underlying class. - * This is like `typeParams`, except for 3 differences. - * First, it does not adjust type parameters in refined types. I.e. type arguments - * do not remove corresponding type parameters. - * Second, it will return Nil for BoundTypes because we might get a NullPointer exception - * on PolyParam#underlying otherwise (demonstrated by showClass test). - * Third, it won't return abstract higher-kinded type parameters, i.e. the type parameters of - * an abstract type are always empty. - */ - final def rawTypeParams(implicit ctx: Context): List[TypeSymbol] = { - self match { - case self: ClassInfo => - self.cls.typeParams - case self: TypeRef => - val tsym = self.typeSymbol - if (tsym.isClass) tsym.typeParams - else if (tsym.isAliasType) self.underlying.rawTypeParams - else Nil - case _: BoundType | _: SingletonType => - Nil - case self: TypeProxy => - self.underlying.rawTypeParams - case _ => - Nil - } + /** The Lambda trait underlying a type lambda */ + def LambdaTrait(implicit ctx: Context): Symbol = self.stripTypeVar match { + case RefinedType(parent, tpnme.hkApply) => + val sym = self.classSymbol + if (sym.isLambdaTrait) sym else NoSymbol + case TypeBounds(lo, hi) => hi.LambdaTrait + case _ => NoSymbol } - /** If type `self` is equal, aliased-to, or upperbounded-by a type of the form - * `LambdaXYZ { ... }`, the class symbol of that type, otherwise NoSymbol. - * symbol of that type, otherwise NoSymbol. - * @param forcing if set, might force completion. If not, never forces - * but returns NoSymbol when it would have to otherwise. - */ - def LambdaClass(forcing: Boolean)(implicit ctx: Context): Symbol = track("LambdaClass") { self.stripTypeVar match { - case self: TypeRef => - val sym = self.symbol - if (sym.isLambdaTrait) sym - else if (sym.isClass || sym.isCompleting && !forcing) NoSymbol - else self.info.LambdaClass(forcing) - case self: TypeProxy => - self.underlying.LambdaClass(forcing) - case _ => - NoSymbol - }} - - /** Is type `self` equal, aliased-to, or upperbounded-by a type of the form - * `LambdaXYZ { ... }`? - */ - def isLambda(implicit ctx: Context): Boolean = - LambdaClass(forcing = true).exists - - /** Same is `isLambda`, except that symbol denotations are not forced - * Symbols in completion count as not lambdas. - */ - def isSafeLambda(implicit ctx: Context): Boolean = - LambdaClass(forcing = false).exists - - /** Is type `self` a Lambda with all hk$i fields fully instantiated? */ - def isInstantiatedLambda(implicit ctx: Context): Boolean = - isSafeLambda && typeParams.isEmpty - /** Is receiver type higher-kinded (i.e. of kind != "*")? */ def isHK(implicit ctx: Context): Boolean = self.dealias match { case self: TypeRef => self.info.isHK - case RefinedType(_, name) => name == tpnme.hkApply || name.isLambdaArgName + case RefinedType(_, name) => name == tpnme.hkApply case TypeBounds(_, hi) => hi.isHK case _ => false } - /** is receiver of the form T#$apply? */ + /** is receiver of the form T#$Apply? */ def isHKApply: Boolean = self match { case TypeRef(_, name) => name == tpnme.hkApply case _ => false @@ -173,101 +260,237 @@ class TypeApplications(val self: Type) extends AnyVal { * * but without forcing anything. */ - def noHK(implicit ctx: Context): Boolean = self.stripTypeVar match { + def classNotLambda(implicit ctx: Context): Boolean = self.stripTypeVar match { case self: RefinedType => - self.parent.noHK + self.parent.classNotLambda case self: TypeRef => - (self.denot.exists) && { + self.denot.exists && { val sym = self.symbol if (sym.isClass) !sym.isLambdaTrait - else sym.isCompleted && self.info.isAlias && self.info.bounds.hi.noHK + else sym.isCompleted && self.info.isAlias && self.info.bounds.hi.classNotLambda } case _ => false } - /** Encode the type resulting from applying this type to given arguments */ - final def appliedTo(args: List[Type])(implicit ctx: Context): Type = /*>|>*/ track("appliedTo") /*<|<*/ { - def matchParams(tp: Type, tparams: List[TypeSymbol], args: List[Type]): Type = args match { - case arg :: args1 => - if (tparams.isEmpty) { - println(s"applied type mismatch: $self $args, typeParams = $typeParams, tsym = ${self.typeSymbol.debugString}") // !!! DEBUG - println(s"precomplete decls = ${self.typeSymbol.unforcedDecls.toList.map(_.denot).mkString("\n ")}") - } - val tparam = tparams.head - val tp1 = RefinedType(tp, tparam.name, arg.toBounds(tparam)) - matchParams(tp1, tparams.tail, args1) - case nil => tp - } + /** Lambda abstract `self` with given type parameters. Examples: + * + * type T[X] = U becomes type T = [X] -> U + * type T[X] >: L <: U becomes type T >: L <: ([X] -> _ <: U) + */ + def LambdaAbstract(tparams: List[Symbol])(implicit ctx: Context): Type = { - /** Instantiate type `tp` with `args`. - * @param original The original type for which we compute the type parameters - * This makes a difference for refinement types, because - * refinements bind type parameters and thereby remove them - * from `typeParams`. + /** Replace references to type parameters with references to hk arguments `this.$hk_i` + * Care is needed not to cause cycles, hence `SafeSubstMap`. */ - def instantiate(tp: Type, original: Type): Type = tp match { - case tp: TypeRef => - val tsym = tp.symbol - if (tsym.isAliasType) tp.underlying.appliedTo(args) - else { - val safeTypeParams = - if (tsym.isClass || !tp.typeSymbol.isCompleting) original.typeParams - else { - ctx.warning(i"encountered F-bounded higher-kinded type parameters for $tsym; assuming they are invariant") - defn.LambdaTrait(args map alwaysZero).typeParams // @@@ can we force? - } - matchParams(tp, safeTypeParams, args) - } - case tp: RefinedType => - val redux = tp.EtaReduce - if (redux.exists) redux.appliedTo(args) // Rewrite ([hk$0] => C[hk$0])(T) to C[T] - else tp.derivedRefinedType( - instantiate(tp.parent, original), - tp.refinedName, - tp.refinedInfo) - case tp: TypeProxy => - instantiate(tp.underlying, original) - case tp: PolyType => - tp.instantiate(args) - case ErrorType => - tp + def internalize[T <: Type](tp: T) = + (rt: RefinedType) => + new ctx.SafeSubstMap(tparams, argRefs(rt, tparams.length)) + .apply(tp).asInstanceOf[T] + + def expand(tp: Type) = { + TypeLambda( + tparams.map(_.variance), + tparams.map(tparam => internalize(self.memberInfo(tparam).bounds)), + internalize(tp)) } + self match { + case self: TypeAlias => + self.derivedTypeAlias(expand(self.alias)) + case self @ TypeBounds(lo, hi) => + self.derivedTypeBounds(lo, expand(TypeBounds.upper(hi))) + case _ => expand(self) + } + } - /** Same as isHK, except we classify all abstract types as HK, - * (they must be, because they are applied). This avoids some forcing and - * CyclicReference errors of the standard isHK. - */ - def isKnownHK(tp: Type): Boolean = tp match { - case tp: TypeRef => - val sym = tp.symbol - if (sym.isClass) sym.isLambdaTrait - else !sym.isAliasType || isKnownHK(tp.info) - case tp: TypeProxy => isKnownHK(tp.underlying) - case _ => false + /** A type ref is eta expandable if it refers to a non-lambda class. + * In that case we can look for parameterized base types of the type + * to eta expand them. + */ + def isEtaExpandable(implicit ctx: Context) = self match { + case self: TypeRef => self.symbol.isClass && !self.name.isLambdaTraitName + case _ => false + } + + /** 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. + */ + def EtaExpand(tparams: List[TypeSymbol])(implicit ctx: Context): Type = { + val tparamsToUse = if (variancesConform(typeParams, tparams)) tparams else typeParams + self.appliedTo(tparams map (_.typeRef)).LambdaAbstract(tparamsToUse) + //.ensuring(res => res.EtaReduce =:= self, s"res = $res, core = ${res.EtaReduce}, self = $self, hc = ${res.hashCode}") + } + + /** Eta expand the prefix in front of any refinements. + * @param tparamsForBottom Type parameters to use if core is a bottom type + */ + def EtaExpandCore(tparamsForBottom: List[TypeSymbol])(implicit ctx: Context): Type = self.stripTypeVar match { + case self: RefinedType => + self.derivedRefinedType(self.parent.EtaExpandCore(tparamsForBottom), self.refinedName, self.refinedInfo) + case tp: TypeRef if defn.isBottomClass(tp.symbol) => + self.LambdaAbstract(tparamsForBottom) + case _ => + self.EtaExpand(self.typeParams) + } + + /** Eta expand if `self` is a (non-lambda) class reference and `bound` is a higher-kinded type */ + def etaExpandIfHK(bound: Type)(implicit ctx: Context): Type = { + val boundLambda = bound.LambdaTrait + val hkParams = boundLambda.typeParams + if (hkParams.isEmpty) self + else self match { + case self: TypeRef if self.symbol.isClass && self.typeParams.length == hkParams.length => + EtaExpansion(self) + case _ => self } + } - if (args.isEmpty || ctx.erasedTypes) self + /** If argument A and type parameter P are higher-kinded, adapt the variances + * of A to those of P, ensuring that the variances of the type lambda A + * agree with the variances of corresponding higher-kinded type parameters of P. Example: + * + * class GenericCompanion[+CC[X]] + * GenericCompanion[List] + * + * with adaptHkVariances, the argument `List` will expand to + * + * [X] => List[X] + * + * instead of + * + * [+X] => List[X] + * + * even though `List` is covariant. This adaptation is necessary to ignore conflicting + * variances in overriding members that have types of hk-type parameters such as + * `GenericCompanion[GenTraversable]` or `GenericCompanion[ListBuffer]`. + * When checking overriding, we need to validate the subtype relationship + * + * GenericCompanion[[X] -> ListBuffer[X]] <: GenericCompanion[[+X] -> GenTraversable[X]] + * + * Without adaptation, this would be false, and hence an overriding error would + * result. But with adaptation, the rhs argument will be adapted to + * + * [X] -> GenTraversable[X] + * + * which makes the subtype test succeed. The crucial point here is that, since + * GenericCompanion only expects a non-variant CC, the fact that GenTraversable + * is covariant is irrelevant, so can be ignored. + */ + def adaptHkVariances(bound: Type)(implicit ctx: Context): Type = { + val boundLambda = bound.LambdaTrait + val hkParams = boundLambda.typeParams + if (hkParams.isEmpty) self else { - val res = instantiate(self, self) - if (isKnownHK(res)) TypeRef(res, tpnme.hkApply) else res + def adaptArg(arg: Type): Type = arg match { + case arg: TypeRef if arg.symbol.isLambdaTrait && + !arg.symbol.typeParams.corresponds(hkParams)(_.variance == _.variance) && + arg.symbol.typeParams.corresponds(hkParams)(varianceConforms) => + arg.prefix.select(boundLambda) + case arg: RefinedType => + arg.derivedRefinedType(adaptArg(arg.parent), arg.refinedName, arg.refinedInfo) + case arg @ TypeAlias(alias) => + arg.derivedTypeAlias(adaptArg(alias)) + case arg @ TypeBounds(lo, hi) => + arg.derivedTypeBounds(lo, adaptArg(hi)) + case _ => + arg + } + adaptArg(self) } } - /** Simplify a fully instantiated type of the form `LambdaX{... type Apply = T } # Apply` to `T`. + /** Encode + * + * T[U1, ..., Un] + * + * where + * @param self = `T` + * @param args = `U1,...,Un` + * performing the following simplifications + * + * 1. If `T` is an eta expansion `[X1,..,Xn] -> C[X1,...,Xn]` of class `C` compute + * `C[U1, ..., Un]` instead. + * 2. If `T` is some other type lambda `[X1,...,Xn] -> S` none of the arguments + * `U1,...,Un` is a wildcard, compute `[X1:=U1, ..., Xn:=Un]S` instead. + * 3. If `T` is a polytype, instantiate it to `U1,...,Un`. */ - def simplifyApply(implicit ctx: Context): Type = self match { - case self @ TypeRef(prefix, tpnme.hkApply) if prefix.isInstantiatedLambda => - prefix.member(tpnme.hkApply).info match { - case TypeAlias(alias) => alias - case _ => self + final def appliedTo(args: List[Type])(implicit ctx: Context): Type = /*>|>*/ track("appliedTo") /*<|<*/ { + def substHkArgs = new TypeMap { + def apply(tp: Type): Type = tp match { + case TypeRef(RefinedThis(rt), name) if rt.eq(self) && name.isHkArgName => + args(name.hkArgIndex) + case _ => + mapOver(tp) } - case _ => self + } + if (args.isEmpty || ctx.erasedTypes) self + else self.stripTypeVar match { + case EtaExpansion(self1) => + self1.appliedTo(args) + case TypeLambda(_, _, body) if !args.exists(_.isInstanceOf[TypeBounds]) => + substHkArgs(body) + case self: PolyType => + self.instantiate(args) + case _ => + appliedTo(args, typeParams) + } + } + + /** Encode application `T[U1, ..., Un]` without simplifications, where + * @param self = `T` + * @param args = `U1, ..., Un` + * @param tparams are assumed to be the type parameters of `T`. + */ + final def appliedTo(args: List[Type], typParams: List[TypeSymbol])(implicit ctx: Context): Type = { + def matchParams(t: Type, tparams: List[TypeSymbol], args: List[Type])(implicit ctx: Context): Type = args match { + case arg :: args1 => + try { + val tparam :: tparams1 = tparams + matchParams(RefinedType(t, tparam.name, arg.toBounds(tparam)), tparams1, args1) + } catch { + case ex: MatchError => + println(s"applied type mismatch: $self $args, typeParams = $typParams") // !!! DEBUG + //println(s"precomplete decls = ${self.typeSymbol.unforcedDecls.toList.map(_.denot).mkString("\n ")}") + throw ex + } + case nil => t + } + assert(args.nonEmpty) + matchParams(self, typParams, args) match { + case refined @ RefinedType(_, pname) if pname.isHkArgName => + TypeRef(refined, tpnme.hkApply) + case refined => + refined + } } final def appliedTo(arg: Type)(implicit ctx: Context): Type = appliedTo(arg :: Nil) final def appliedTo(arg1: Type, arg2: Type)(implicit ctx: Context): Type = appliedTo(arg1 :: arg2 :: Nil) + /** A cycle-safe version of `appliedTo` where computing type parameters do not force + * the typeconstructor. Instead, if the type constructor is completing, we make + * up hk type parameters matching the arguments. This is needed when unpickling + * Scala2 files such as `scala.collection.generic.Mapfactory`. + */ + final def safeAppliedTo(args: List[Type])(implicit ctx: Context) = { + val safeTypeParams = self match { + case self: TypeRef if !self.symbol.isClass && self.symbol.isCompleting => + // This happens when unpickling e.g. scala$collection$generic$GenMapFactory$$CC + ctx.warning(i"encountered F-bounded higher-kinded type parameters for ${self.symbol}; assuming they are invariant") + defn.LambdaTrait(args map alwaysZero).typeParams + case _ => + typeParams + } + appliedTo(args, safeTypeParams) + } + /** Turn this type, which is used as an argument for * type parameter `tparam`, into a TypeBounds RHS */ @@ -281,7 +504,7 @@ class TypeApplications(val self: Type) extends AnyVal { else TypeAlias(self, v) } - /** The type arguments of this type's base type instance wrt.`base`. + /** 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] = @@ -370,26 +593,9 @@ class TypeApplications(val self: Type) extends AnyVal { * otherwise return Nil. * Existential types in arguments are returned as TypeBounds instances. */ - final def argInfos(implicit ctx: Context): List[Type] = { - var tparams: List[TypeSymbol] = null - def recur(tp: Type, refineCount: Int): mutable.ListBuffer[Type] = tp.stripTypeVar match { - case tp @ RefinedType(tycon, name) => - val buf = recur(tycon, refineCount + 1) - if (buf == null) null - else { - if (tparams == null) tparams = tycon.typeParams - if (buf.size < tparams.length) { - val tparam = tparams(buf.size) - if (name == tparam.name) buf += tp.refinedInfo.argInfo - else null - } else null - } - case _ => - if (refineCount == 0) null - else new mutable.ListBuffer[Type] - } - val buf = recur(self, 0) - if (buf == null) Nil else buf.toList + final def argInfos(implicit ctx: Context): List[Type] = self match { + case AppliedType(tycon, args) => args + case _ => Nil } /** Argument types where existential types in arguments are disallowed */ @@ -412,6 +618,11 @@ class TypeApplications(val self: Type) extends AnyVal { self } + final def typeConstructor(implicit ctx: Context): Type = self.stripTypeVar match { + case AppliedType(tycon, _) => tycon + case self => self + } + /** If this is the image of a type argument; recover the type argument, * otherwise NoType. */ @@ -421,6 +632,12 @@ class TypeApplications(val self: Type) extends AnyVal { case _ => NoType } + /** If this is a type alias, its underlying type, otherwise the type itself */ + def dropAlias(implicit ctx: Context): Type = self match { + case TypeAlias(alias) => alias + case _ => self + } + /** The element type of a sequence or array */ def elemType(implicit ctx: Context): Type = self match { case defn.ArrayOf(elemtp) => elemtp @@ -428,6 +645,9 @@ class TypeApplications(val self: Type) extends AnyVal { case _ => firstBaseArgInfo(defn.SeqClass) } + /** Does this type contain RefinedThis type with `target` as its underling + * refinement type? + */ def containsRefinedThis(target: Type)(implicit ctx: Context): Boolean = { def recur(tp: Type): Boolean = tp.stripTypeVar match { case RefinedThis(tp) => @@ -451,201 +671,4 @@ class TypeApplications(val self: Type) extends AnyVal { } recur(self) } - - /** The typed lambda abstraction of this type `T` relative to `boundSyms`. - * This is: - * - * LambdaXYZ{ bounds }{ type Apply = toHK(T) } - * - * where - * - XYZ reflects the variances of the bound symbols, - * - `bounds` consists of type declarations `type hk$i >: toHK(L) <: toHK(U), - * one for each type parameter in `T` with non-trivial bounds L,U. - * - `toHK` is a substitution that replaces every bound symbol sym_i by - * `this.hk$i`. - * - * TypeBounds are lambda abstracting by lambda abstracting their upper bound. - * - * @param cycleParanoid If `true` don't force denotation of a TypeRef unless - * its name matches one of `boundSyms`. Needed to avoid cycles - * involving F-boundes hk-types when reading Scala2 collection classes - * with new hk-scheme. - */ - def LambdaAbstract(boundSyms: List[Symbol], cycleParanoid: Boolean = false)(implicit ctx: Context): Type = { - def expand(tp: Type): Type = { - val lambda = defn.LambdaTrait(boundSyms.map(_.variance)) - def toHK(tp: Type) = (rt: RefinedType) => { - val argRefs = boundSyms.indices.toList.map(i => - RefinedThis(rt).select(tpnme.LambdaArgName(i))) - val substituted = - if (cycleParanoid) new ctx.SafeSubstMap(boundSyms, argRefs).apply(tp) - else tp.subst(boundSyms, argRefs) - substituted.bounds.withVariance(1) - } - val boundNames = new mutable.ListBuffer[Name] - val boundss = new mutable.ListBuffer[TypeBounds] - for (sym <- boundSyms) { - val bounds = sym.info.bounds - if (!(TypeBounds.empty frozen_<:< bounds)) { - boundNames += sym.name - boundss += bounds - } - } - val lambdaWithBounds = - RefinedType.make(lambda.typeRef, boundNames.toList, boundss.toList.map(toHK)) - RefinedType(lambdaWithBounds, tpnme.hkApply, toHK(tp)) - } - self match { - case self @ TypeBounds(lo, hi) => - self.derivedTypeBounds(lo, expand(TypeBounds.upper(hi))) - case _ => - expand(self) - } - } - - /** Convert a type constructor `TC` which has type parameters `T1, ..., Tn` - * in a context where type parameters `U1,...,Un` are expected to - * - * 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. - */ - def EtaExpand(tparams: List[Symbol])(implicit ctx: Context): Type = { - def varianceCompatible(actual: Symbol, formal: Symbol) = - formal.variance == 0 || actual.variance == formal.variance - val tparamsToUse = - if (typeParams.corresponds(tparams)(varianceCompatible)) tparams else typeParams - self.appliedTo(tparams map (_.typeRef)).LambdaAbstract(tparamsToUse) - //.ensuring(res => res.EtaReduce =:= self, s"res = $res, core = ${res.EtaReduce}, self = $self, hc = ${res.hashCode}") - } - - /** Eta expand if `bound` is a higher-kinded type */ - def EtaExpandIfHK(bound: Type)(implicit ctx: Context): Type = - if (bound.isHK && !isHK && self.typeSymbol.isClass && typeParams.nonEmpty) EtaExpand(bound.typeParams) - else self - - /** Eta expand the prefix in front of any refinements. */ - def EtaExpandCore(implicit ctx: Context): Type = self.stripTypeVar match { - case self: RefinedType => - self.derivedRefinedType(self.parent.EtaExpandCore, self.refinedName, self.refinedInfo) - case _ => - self.EtaExpand(self.typeParams) - } - - /** If `self` is a (potentially partially instantiated) eta expansion of type T, return T, - * otherwise NoType. More precisely if `self` is of the form - * - * T { type $apply = U[T1, ..., Tn] } - * - * where - * - * - hk$0, ..., hk${m-1} are the type parameters of T - * - a sublist of the arguments Ti_k (k = 0,...,m_1) are of the form T{...}.this.hk$i_k - * - * rewrite `self` to - * - * U[T'1,...T'j] - * - * where - * - * T'j = _ >: Lj <: Uj if j is in the i_k list defined above - * where Lj and Uj are the bounds of hk$j mapped using `fromHK`. - * = fromHK(Tj) otherwise. - * - * `fromHK` is the function that replaces every occurrence of `.this.hk$i` by the - * corresponding parameter reference in `U[T'1,...T'j]` - */ - def EtaReduce(implicit ctx: Context): Type = { - def etaCore(tp: Type, tparams: List[Symbol]): Type = tparams match { - case Nil => tp - case tparam :: otherParams => - tp match { - case tp: RefinedType => - tp.refinedInfo match { - case TypeAlias(TypeRef(RefinedThis(rt), rname)) - if (rname == tparam.name) && (rt eq self) => - // we have a binding T = Lambda$XYZ{...}.this.hk$i where hk$i names the current `tparam`. - val pcore = etaCore(tp.parent, otherParams) - val hkBounds = self.member(rname).info.bounds - if (TypeBounds.empty frozen_<:< hkBounds) pcore - else tp.derivedRefinedType(pcore, tp.refinedName, hkBounds) - case _ => - val pcore = etaCore(tp.parent, tparams) - if (pcore.exists) tp.derivedRefinedType(pcore, tp.refinedName, tp.refinedInfo) - else NoType - } - case _ => - NoType - } - } - // Map references `Lambda$XYZ{...}.this.hk$i to corresponding parameter references of the reduced core. - def fromHK(reduced: Type) = reduced match { - case reduced: RefinedType => - new TypeMap { - def apply(tp: Type): Type = tp match { - case TypeRef(RefinedThis(binder), name) if binder eq self => - assert(name.isLambdaArgName) - RefinedThis(reduced).select(reduced.typeParams.apply(name.LambdaArgIndex)) - case _ => - mapOver(tp) - } - }.apply(reduced) - case _ => - reduced - } - - self match { - case self @ RefinedType(parent, tpnme.hkApply) => - val lc = parent.LambdaClass(forcing = false) - self.refinedInfo match { - case TypeAlias(alias) if lc.exists => - fromHK(etaCore(alias, lc.typeParams.reverse)) - case _ => NoType - } - case _ => NoType - } - } - - /** Test whether this type has a base type of the form `B[T1, ..., Tn]` where - * the type parameters of `B` match one-by-one the variances of `tparams`, - * and where the lambda abstracted type - * - * LambdaXYZ { type Apply = B[hk$0, ..., hk${n-1}] } - * { type hk$0 = T1; ...; type hk${n-1} = Tn } - * - * satisfies predicate `p`. Try base types in the order of their occurrence in `baseClasses`. - * A type parameter matches a variance V if it has V as its variance or if V == 0. - * @param classBounds A hint to bound the search. Only types that derive from one of the - * classes in classBounds are considered. - */ - def testLifted(tparams: List[Symbol], p: Type => Boolean, classBounds: List[ClassSymbol])(implicit ctx: Context): Boolean = { - def tryLift(bcs: List[ClassSymbol]): Boolean = bcs match { - case bc :: bcs1 => - val tp = self.baseTypeWithArgs(bc) - val targs = tp.argInfos - val tycon = tp.withoutArgs(targs) - def variancesMatch(param1: Symbol, param2: Symbol) = - param2.variance == param2.variance || param2.variance == 0 - if (classBounds.exists(tycon.derivesFrom(_)) && - tycon.typeParams.corresponds(tparams)(variancesMatch)) { - val expanded = tycon.EtaExpand(tparams) - val lifted = (expanded /: targs) { (partialInst, targ) => - val tparam = partialInst.typeParams.head - RefinedType(partialInst, tparam.name, targ.bounds.withVariance(tparam.variance)) - } - ctx.traceIndented(i"eta lifting $self --> $lifted", hk) { - p(lifted) || tryLift(bcs1) - } - } - else tryLift(bcs1) - case nil => - false - } - tparams.nonEmpty && - (typeParams.hasSameLengthAs(tparams) && p(EtaExpand(tparams)) || - classBounds.nonEmpty && tryLift(self.baseClasses)) - } } diff --git a/src/dotty/tools/dotc/core/TypeComparer.scala b/src/dotty/tools/dotc/core/TypeComparer.scala index 1d9928e2dc57..71dede951137 100644 --- a/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/src/dotty/tools/dotc/core/TypeComparer.scala @@ -12,6 +12,7 @@ import util.{Stats, DotClass, SimpleMap} import config.Config import config.Printers._ import TypeErasure.{erasedLub, erasedGlb} +import TypeApplications._ import scala.util.control.NonFatal /** Provides methods to compare types. @@ -143,27 +144,46 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { def compareNamed(tp1: Type, tp2: NamedType): Boolean = { implicit val ctx: Context = this.ctx tp2.info match { - case info2: TypeAlias => firstTry(tp1, info2.alias) + case info2: TypeAlias if tp2.prefix.isStable => + // If prefix is not stable (i.e. is not a path), then we have a true + // projection `T # A` which is treated as the existential type + // `ex(x: T)x.A`. We need to deal with the existential first before + // following the alias. If we did follow the alias we could be + // unsound as well as incomplete. An example of this was discovered in Iter2.scala. + // It failed to validate the subtype test + // + // (([+X] -> Seq[X]) & C)[SA] <: C[SA] + // + // Both sides are projections of $Apply. The left $Apply does have an + // aliased info, namely, Seq[SA]. But that is not a subtype of C[SA]. + // The problem is that, with the prefix not being a path, an aliased info + // does not necessarily give all of the information of the original projection. + // So we can't follow the alias without a backup strategy. If the alias + // would appear on the right then I believe this can be turned into a case + // of unsoundness. + isSubType(tp1, info2.alias) case _ => tp1 match { case tp1: NamedType => tp1.info match { - case info1: TypeAlias => compareNamed(info1.alias, tp2) + case info1: TypeAlias if tp1.prefix.isStable => + isSubType(info1.alias, tp2) case _ => val sym1 = tp1.symbol - (if ((sym1 ne NoSymbol) && (sym1 eq tp2.symbol)) - ctx.erasedTypes || - sym1.isStaticOwner || - isSubType(tp1.prefix, tp2.prefix) || - thirdTryNamed(tp1, tp2) - else - (tp1.name eq tp2.name) && - isSubType(tp1.prefix, tp2.prefix) && - (tp1.signature == tp2.signature) && - !tp1.isInstanceOf[WithFixedSym] && - !tp2.isInstanceOf[WithFixedSym] || - compareHK(tp1, tp2, inOrder = true) || - compareHK(tp2, tp1, inOrder = false) || - thirdTryNamed(tp1, tp2)) + if ((sym1 ne NoSymbol) && (sym1 eq tp2.symbol)) + ctx.erasedTypes || + sym1.isStaticOwner || + isSubType(tp1.prefix, tp2.prefix) || + thirdTryNamed(tp1, tp2) + else + ( (tp1.name eq tp2.name) + && isSubType(tp1.prefix, tp2.prefix) + && tp1.signature == tp2.signature + && !tp1.isInstanceOf[WithFixedSym] + && !tp2.isInstanceOf[WithFixedSym] + ) || + compareHK(tp1, tp2, inOrder = true) || + compareHK(tp2, tp1, inOrder = false) || + thirdTryNamed(tp1, tp2) } case _ => compareHK(tp2, tp1, inOrder = false) || @@ -239,9 +259,9 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { case tp1: NamedType => tp1.info match { case info1: TypeAlias => isSubType(info1.alias, tp2) - case _ => compareHK(tp1, tp2, inOrder = true) || thirdTry(tp1, tp2) - // Note: If we change the order here, doing compareHK first and following aliases second, - // we get a -Ycheck error when compiling dotc/transform. Need to investigate. + case _ => + compareHK(tp1, tp2, inOrder = true) || + thirdTry(tp1, tp2) } case tp1: PolyParam => def flagNothingBound = { @@ -336,21 +356,23 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { isSubType(tp1, tp2.parent) && (name2 == nme.WILDCARD || hasMatchingMember(name2, tp1, tp2)) } + def etaExpandedSubType(tp1: Type) = + isSubType(tp1.typeConstructor.EtaExpand(tp2.typeParams), tp2) def compareRefined: Boolean = { val tp1w = tp1.widen val skipped2 = skipMatching(tp1w, tp2) - if ((skipped2 eq tp2) || !Config.fastPathForRefinedSubtype) { - val normalPath = tp1 match { + if ((skipped2 eq tp2) || !Config.fastPathForRefinedSubtype) + tp1 match { case tp1: AndType => // Delay calling `compareRefinedSlow` because looking up a member // of an `AndType` can lead to a cascade of subtyping checks + // This twist is needed to make collection/generic/ParFactory.scala compile fourthTry(tp1, tp2) || compareRefinedSlow case _ => - compareRefinedSlow || fourthTry(tp1, tp2) + compareRefinedSlow || + fourthTry(tp1, tp2) || + needsEtaLift(tp1, tp2) && testLifted(tp1, tp2, tp2.typeParams, etaExpandedSubType) } - normalPath || - needsEtaLift(tp1, tp2) && tp1.testLifted(tp2.typeParams, isSubType(_, tp2), classBounds(tp2)) - } else // fast path, in particular for refinements resulting from parameterization. isSubType(tp1, skipped2) && isSubRefinements(tp1w.asInstanceOf[RefinedType], tp2, skipped2) @@ -471,7 +493,9 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { isNewSubType(tp1.underlying.widenExpr, tp2) || comparePaths case tp1: RefinedType => isNewSubType(tp1.parent, tp2) || - needsEtaLift(tp2, tp1) && tp2.testLifted(tp1.typeParams, isSubType(tp1, _), Nil) + needsEtaLift(tp2, tp1) && + tp2.typeParams.length == tp1.typeParams.length && + isSubType(tp1, tp2.EtaExpand(tp1.typeParams)) case AndType(tp11, tp12) => // Rewrite (T111 | T112) & T12 <: T2 to (T111 & T12) <: T2 and (T112 | T12) <: T2 // and analogously for T11 & (T121 | T122) & T12 <: T2 @@ -502,19 +526,89 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { false } - /** If `projection` is a hk projection T#$apply - * and `other` is not a hk projection, then convert `other` to a hk projection `U`, and - * continue with `T <:< U` if `inOrder` is true and `U <:< T` otherwise. + /** Does `tp` need to be eta lifted to be comparable to `target`? + * This is the case if: + * - target is a type lambda, and + * - `tp` is eta-expandable (i.e. is a non-lambda class ref) */ - def compareHK(projection: NamedType, other: Type, inOrder: Boolean) = - projection.name == tpnme.hkApply && - !other.isHKApply && - other.testLifted(projection.prefix.LambdaClass(forcing = true).typeParams, - if (inOrder) isSubType(projection.prefix, _) else isSubType(_, projection.prefix), - if (inOrder) Nil else classBounds(projection.prefix)) + private def needsEtaLift(tp: Type, target: RefinedType): Boolean = + target.refinedName == tpnme.hkApply && tp.isEtaExpandable - /** The class symbols bounding the type of the `Apply` member of `tp` */ - private def classBounds(tp: Type) = tp.member(tpnme.hkApply).info.classSymbols + /** Test whether `tp1` has a base type of the form `B[T1, ..., Tn]` where + * - `B` derives from one of the class symbols of `tp2`, + * - the type parameters of `B` match one-by-one the variances of `tparams`, + * - `B` satisfies predicate `p`. + */ + private def testLifted(tp1: Type, tp2: Type, tparams: List[TypeSymbol], p: Type => Boolean): Boolean = { + val classBounds = tp2.member(tpnme.hkApply).info.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)) + case nil => + false + } + recur(tp1.baseClasses) + } + + /** If `projection` is a hk projection T#$apply with a constrainable poly param + * as type constructor and `other` is not a hk projection, then perform the following + * steps: + * + * (1) If not `inOrder` then perform the next steps until they all succeed + * for each base type of other which + * - derives from a class bound of `projection`, + * - has the same number of type parameters than `projection` + * - has type parameter variances which conform to those of `projection`. + * If `inOrder` then perform the same steps on the original `other` type. + * + * (2) Try to eta expand the constructor of `other`. + * + * (3a) In mode `TypevarsMissConetxt` replace the projection's hk constructor parameter + * by the eta expansion of step (2) reapplied to the projection's arguments. + * (3b) In normal mode, try to unify the projection's hk constructor parameter with + * the eta expansion of step(2) + * + * (4) If `inOrder`, test `projection <: other` else test `other <: projection`. + */ + def compareHK(projection: NamedType, other: Type, inOrder: Boolean): Boolean = { + def tryInfer(tp: Type): Boolean = ctx.traceIndented(i"compareHK($projection, $other, inOrder = $inOrder, constr = $tp)", subtyping) { + tp match { + case tp: TypeVar => tryInfer(tp.underlying) + case param: PolyParam if canConstrain(param) => + + def unifyWith(liftedOther: Type): Boolean = { + subtyping.println(i"unify with $liftedOther") + liftedOther.typeConstructor.widen match { + case tycon: TypeRef if tycon.isEtaExpandable && tycon.typeParams.nonEmpty => + val (ok, projection1) = + if (ctx.mode.is(Mode.TypevarsMissContext)) + (true, EtaExpansion(tycon).appliedTo(projection.argInfos)) + else + (tryInstantiate(param, EtaExpansion(tycon)), projection) + ok && + (if (inOrder) isSubType(projection1, other) else isSubType(other, projection1)) + case _ => + false + } + } + val hkTypeParams = param.typeParams + subtyping.println(i"classBounds = ${projection.prefix.member(tpnme.hkApply).info.classSymbols}") + subtyping.println(i"base classes = ${other.baseClasses}") + subtyping.println(i"type params = $hkTypeParams") + if (inOrder) unifyWith(other) + else testLifted(other, projection.prefix, hkTypeParams, unifyWith) + case _ => + false + } + } + projection.name == tpnme.hkApply && !other.isHKApply && + tryInfer(projection.prefix.typeConstructor.dealias) + } /** Returns true iff either `tp11 <:< tp21` or `tp12 <:< tp22`, trying at the same time * to keep the constraint as wide as possible. Specifically, if @@ -627,11 +721,19 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { * Further, no refinement refers back to the refined type via a refined this. * The precondition is established by `skipMatching`. */ - private def isSubRefinements(tp1: RefinedType, tp2: RefinedType, limit: Type): Boolean = - isSubType(tp1.refinedInfo, tp2.refinedInfo) && ( + private def isSubRefinements(tp1: RefinedType, tp2: RefinedType, limit: Type): Boolean = { + 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. + val adapted2 = refine2.adaptHkVariances(tp1.parent.member(tp1.refinedName).symbol.info) + adapted2.ne(refine2) && hasSubRefinement(tp1, adapted2) + } + } + hasSubRefinement(tp1, tp2.refinedInfo) && ( (tp2.parent eq limit) || isSubRefinements( tp1.parent.asInstanceOf[RefinedType], tp2.parent.asInstanceOf[RefinedType], limit)) + } /** A type has been covered previously in subtype checking if it * is some combination of TypeRefs that point to classes, where the @@ -665,14 +767,6 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { case _ => false } - /** Does `tp` need to be eta lifted to be comparable to `target`? */ - private def needsEtaLift(tp: Type, target: RefinedType): Boolean = { - // if (tp.isLambda != tp.isHK) println(i"discrepancy for $tp, isLambda = ${tp.isLambda}, isHK = ${tp.isHK}") - val name = target.refinedName - (name.isLambdaArgName || (name eq tpnme.hkApply)) && - tp.exists && !tp.isLambda // we do encounter Lambda classes without any arguments here - } - /** Narrow gadt.bounds for the type parameter referenced by `tr` to include * `bound` as an upper or lower bound (which depends on `isUpper`). * Test that the resulting bounds are still satisfiable. @@ -1074,7 +1168,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { case bounds: TypeBounds => i"type bounds $bounds" case _ => tp.show } - throw new MergeError(s"cannot merge ${showType(tp1)} with ${showType(tp2)}") + throw new MergeError(s"cannot merge ${showType(tp1)} with ${showType(tp2)}", tp1, tp2) } /** Merge two lists of names. If names in corresponding positions match, keep them, diff --git a/src/dotty/tools/dotc/core/TypeOps.scala b/src/dotty/tools/dotc/core/TypeOps.scala index f3884e11a1da..3dfe698f0910 100644 --- a/src/dotty/tools/dotc/core/TypeOps.scala +++ b/src/dotty/tools/dotc/core/TypeOps.scala @@ -61,18 +61,19 @@ trait TypeOps { this: Context => // TODO: Make standalone object. def toPrefix(pre: Type, cls: Symbol, thiscls: ClassSymbol): Type = /*>|>*/ ctx.conditionalTraceIndented(TypeOps.track, s"toPrefix($pre, $cls, $thiscls)") /*<|<*/ { if ((pre eq NoType) || (pre eq NoPrefix) || (cls is PackageClass)) tp - else if (thiscls.derivesFrom(cls) && pre.baseTypeRef(thiscls).exists) { - if (theMap != null && theMap.currentVariance <= 0 && !isLegalPrefix(pre)) - theMap.unstable = true - pre match { - case SuperType(thispre, _) => thispre - case _ => pre - } + else pre match { + case pre: SuperType => toPrefix(pre.thistpe, cls, thiscls) + case _ => + if (thiscls.derivesFrom(cls) && pre.baseTypeRef(thiscls).exists) { + if (theMap != null && theMap.currentVariance <= 0 && !isLegalPrefix(pre)) + theMap.unstable = true + 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) } - 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) } /*>|>*/ ctx.conditionalTraceIndented(TypeOps.track, s"asSeen ${tp.show} from (${pre.show}, ${cls.show})", show = true) /*<|<*/ { // !!! DEBUG @@ -384,10 +385,9 @@ trait TypeOps { this: Context => // TODO: Make standalone object. denot.info = info } } - val typeArgFlag = if (formal is Local) TypeArgument else EmptyFlags val sym = ctx.newSymbol( cls, formal.name, - formal.flagsUNSAFE & RetainedTypeArgFlags | typeArgFlag | Override, + formal.flagsUNSAFE & RetainedTypeArgFlags | BaseTypeArg | Override, lazyInfo, coord = cls.coord) cls.enter(sym, decls) @@ -463,14 +463,20 @@ trait TypeOps { this: Context => // TODO: Make standalone object. case to @ TypeBounds(lo1, hi1) if lo1 eq hi1 => for (pref <- prefs) for (argSym <- pref.decls) - if (argSym is TypeArgument) + if (argSym is BaseTypeArg) forwardRef(argSym, from, to, cls, decls) case _ => } // 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 - var formals: SimpleMap[TypeName, Symbol] = 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 = tp.dealias match { case tp: TypeRef => tp @@ -488,13 +494,17 @@ trait TypeOps { this: Context => // TODO: Make standalone object. throw new TypeError(s"unexpected parent type: $tp") } 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) } - // These two loops cannot be fused because second loop assumes that - // all arguments have been entered in `decls`. + // Forward definitions in super classes that have one of the refined paramters + // as aliases directly to the refined info. + // Note that this cannot be fused bwith the previous loop because we now + // assume that all arguments have been entered in `decls`. refinements foreachBinding { (name, refinedInfo) => forwardRefs(formals(name), refinedInfo, parentRefs) } diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index 2558dcbb7de4..0691d979ac0e 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -100,7 +100,7 @@ object Types { case _ => false } - /** Is this type a (possibly aliased and/or partially applied) type reference + /** Is this type a (possibly refined or applied or aliased) type reference * to the given type symbol? * @sym The symbol to compare to. It must be a class symbol or abstract type. * It makes no sense for it to be an alias type because isRef would always @@ -113,8 +113,7 @@ object Types { case _ => this1.symbol eq sym } case this1: RefinedType => - // make sure all refinements are type arguments - this1.parent.isRef(sym) && this.argInfos.nonEmpty + this1.parent.isRef(sym) case _ => false } @@ -142,20 +141,23 @@ object Types { } /** Is this type an instance of a non-bottom subclass of the given class `cls`? */ - final def derivesFrom(cls: Symbol)(implicit ctx: Context): Boolean = this match { - case tp: TypeRef => - val sym = tp.symbol - if (sym.isClass) sym.derivesFrom(cls) else tp.underlying.derivesFrom(cls) - case tp: TypeProxy => - tp.underlying.derivesFrom(cls) - case tp: AndType => - tp.tp1.derivesFrom(cls) || tp.tp2.derivesFrom(cls) - case tp: OrType => - tp.tp1.derivesFrom(cls) && tp.tp2.derivesFrom(cls) - case tp: JavaArrayType => - cls == defn.ObjectClass - case _ => - false + final def derivesFrom(cls: Symbol)(implicit ctx: Context): Boolean = { + def loop(tp: Type) = tp match { + case tp: TypeRef => + val sym = tp.symbol + if (sym.isClass) sym.derivesFrom(cls) else tp.underlying.derivesFrom(cls) + case tp: TypeProxy => + tp.underlying.derivesFrom(cls) + case tp: AndType => + tp.tp1.derivesFrom(cls) || tp.tp2.derivesFrom(cls) + case tp: OrType => + tp.tp1.derivesFrom(cls) && tp.tp2.derivesFrom(cls) + case tp: JavaArrayType => + cls == defn.ObjectClass + case _ => + false + } + cls == defn.AnyClass || loop(this) } /** Is this type guaranteed not to have `null` as a value? @@ -448,7 +450,18 @@ object Types { if (rinfo.isAlias) rinfo else if (pdenot.info.isAlias) pdenot.info else if (ctx.pendingMemberSearches.contains(name)) safeAnd(pdenot.info, rinfo) - else pdenot.info & 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. + safeAnd(pdenot.info, rinfo) + } pdenot.asSingleDenotation.derivedSingleDenotation(pdenot.symbol, jointInfo) } else pdenot & (new JointRefDenotation(NoSymbol, rinfo, Period.allInRun(ctx.runId)), pre) @@ -856,7 +869,7 @@ object Types { object instantiate extends TypeMap { var isSafe = true def apply(tp: Type): Type = tp match { - case TypeRef(RefinedThis(`pre`), name) if name.isLambdaArgName => + case TypeRef(RefinedThis(`pre`), name) if name.isHkArgName => member(name).info match { case TypeAlias(alias) => alias case _ => isSafe = false; tp @@ -869,7 +882,7 @@ object Types { } } def instArg(tp: Type): Type = tp match { - case tp @ TypeAlias(TypeRef(RefinedThis(`pre`), name)) if name.isLambdaArgName => + case tp @ TypeAlias(TypeRef(RefinedThis(`pre`), name)) if name.isHkArgName => member(name).info match { case TypeAlias(alias) => tp.derivedTypeAlias(alias) // needed to keep variance case bounds => bounds @@ -884,19 +897,17 @@ object Types { instantiate(tp) } /** Reduce rhs of $hkApply to make it stand alone */ - def betaReduce(tp: Type) = - if (pre.parent.isSafeLambda) { - val reduced = instTop(tp) - if (instantiate.isSafe) reduced else NoType - } - else NoType + def betaReduce(tp: Type) = { + val reduced = instTop(tp) + if (instantiate.isSafe) reduced else NoType + } pre.refinedInfo match { case TypeAlias(alias) => if (pre.refinedName ne name) loop(pre.parent) else if (!pre.refinementRefersToThis) alias else alias match { case TypeRef(RefinedThis(`pre`), aliasName) => lookupRefined(aliasName) // (1) - case _ => if (name == tpnme.hkApply) betaReduce(alias) else NoType // (2) + case _ => if (name == tpnme.hkApply) betaReduce(alias) else NoType // (2) // ### use TypeApplication's betaReduce } case _ => loop(pre.parent) } @@ -1375,7 +1386,11 @@ object Types { if (owner.isTerm) d else d.asSeenFrom(prefix) } - private def checkSymAssign(sym: Symbol)(implicit ctx: Context) = + private def checkSymAssign(sym: Symbol)(implicit ctx: Context) = { + def selfTypeOf(sym: Symbol) = sym.owner.info match { + case info: ClassInfo => info.givenSelfType + case _ => NoType + } assert( (lastSymbol eq sym) || (lastSymbol eq null) || { @@ -1387,9 +1402,16 @@ object Types { (lastDefRunId == NoRunId) } || (lastSymbol.infoOrCompleter == ErrorType || - sym.owner.derivesFrom(lastSymbol.owner) && sym.owner != lastSymbol.owner - ), - s"data race? overwriting symbol of ${this.show} / $this / ${this.getClass} / ${lastSymbol.id} / ${sym.id} / ${sym.owner} / ${lastSymbol.owner} / ${ctx.phase} at run ${ctx.runId}") + sym.owner != lastSymbol.owner && + (sym.owner.derivesFrom(lastSymbol.owner) || + selfTypeOf(sym).derivesFrom(lastSymbol.owner) || + selfTypeOf(lastSymbol).derivesFrom(sym.owner))), + s"""data race? overwriting symbol of type ${this.show}, + |long form = $this of class ${this.getClass}, + |last sym id = ${lastSymbol.id}, new sym id = ${sym.id}, + |last owner = ${lastSymbol.owner}, new owner = ${sym.owner}, + |period = ${ctx.phase} at run ${ctx.runId}""".stripMargin) + } protected def sig: Signature = Signature.NotAMethod @@ -1516,7 +1538,12 @@ object Types { else { val res = prefix.lookupRefined(name) if (res.exists) res - else if (name == tpnme.hkApply && prefix.noHK) derivedSelect(prefix.EtaExpandCore) + else if (name == tpnme.hkApply && prefix.classNotLambda) { + // After substitution we might end up with a type like + // `C { type hk$0 = T0; ...; type hk$n = Tn } # $Apply` + // where C is a class. In that case we eta expand `C`. + derivedSelect(prefix.EtaExpandCore(this.prefix.typeConstructor.typeParams)) + } else newLikeThis(prefix) } @@ -1560,6 +1587,15 @@ object Types { case _ => false } + + /* A version of toString which also prints aliases. Can be used for debugging + override def toString = + if (isTerm) s"TermRef($prefix, $name)" + else s"TypeRef($prefix, $name)${ + if (lastDenotation != null && lastDenotation.infoOrCompleter.isAlias) + s"@@@ ${lastDenotation.infoOrCompleter.asInstanceOf[TypeAlias].hi}" + else ""}" + */ } abstract case class TermRef(override val prefix: Type, name: TermName) extends NamedType with SingletonType { @@ -1756,8 +1792,8 @@ object Types { object TypeRef { def checkProjection(prefix: Type, name: TypeName)(implicit ctx: Context) = - if (name == tpnme.hkApply && prefix.noHK) - assert(false, s"bad type : $prefix.$name should not be $$applied") + if (name == tpnme.hkApply && prefix.classNotLambda) + assert(false, s"bad type : $prefix.$name does not allow $$Apply projection") /** Create type ref with given prefix and name */ def apply(prefix: Type, name: TypeName)(implicit ctx: Context): TypeRef = { @@ -1897,20 +1933,15 @@ object Types { override def underlying(implicit ctx: Context) = parent - private def checkInst(implicit ctx: Context): this.type = { - if (Config.checkLambdaVariance) - refinedInfo match { - case refinedInfo: TypeBounds if refinedInfo.variance != 0 && refinedName.isLambdaArgName => - val cls = parent.LambdaClass(forcing = false) - if (cls.exists) - assert(refinedInfo.variance == cls.typeParams.apply(refinedName.LambdaArgIndex).variance, - s"variance mismatch for $this, $cls, ${cls.typeParams}, ${cls.typeParams.apply(refinedName.LambdaArgIndex).variance}, ${refinedInfo.variance}") - case _ => + private def badInst = + throw new AssertionError(s"bad instantiation: $this") + + def checkInst(implicit ctx: Context): this.type = { + if (refinedName == tpnme.hkApply) + parent.stripTypeVar match { + case RefinedType(_, name) if name.isHkArgName => // ok + case _ => badInst } - if (Config.checkProjections && - (refinedName == tpnme.hkApply || refinedName.isLambdaArgName) && - parent.noHK) - assert(false, s"illegal refinement of first-order type: $this") this } @@ -1932,7 +1963,7 @@ object Types { false } override def computeHash = doHash(refinedName, refinedInfo, parent) - override def toString = s"RefinedType($parent, $refinedName, $refinedInfo | $hashCode)" // !!! TODO: remove + override def toString = s"RefinedType($parent, $refinedName, $refinedInfo)" } class CachedRefinedType(parent: Type, refinedName: Name, infoFn: RefinedType => Type) extends RefinedType(parent, refinedName) { @@ -2746,9 +2777,9 @@ object Types { abstract class TypeAlias(val alias: Type, override val variance: Int) extends TypeBounds(alias, alias) { /** pre: this is a type alias */ - def derivedTypeAlias(tp: Type, variance: Int = this.variance)(implicit ctx: Context) = - if ((lo eq tp) && (variance == this.variance)) this - else TypeAlias(tp, variance) + def derivedTypeAlias(alias: Type, variance: Int = this.variance)(implicit ctx: Context) = + if ((alias eq this.alias) && (variance == this.variance)) this + else TypeAlias(alias, variance) override def & (that: TypeBounds)(implicit ctx: Context): TypeBounds = { val v = this commonVariance that @@ -3287,7 +3318,7 @@ object Types { } } - class MergeError(msg: String) extends TypeError(msg) + class MergeError(msg: String, val tp1: Type, val tp2: Type) extends TypeError(msg) // ----- Debug --------------------------------------------------------- diff --git a/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/src/dotty/tools/dotc/core/tasty/TreePickler.scala index c0136538bb47..d11d6f4b737b 100644 --- a/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -9,6 +9,7 @@ import Contexts._, Symbols._, Types._, Names._, Constants._, Decorators._, Annot import collection.mutable import NameOps._ import TastyBuffer._ +import TypeApplications._ class TreePickler(pickler: TastyPickler) { val buf = new TreeBuffer @@ -142,6 +143,9 @@ class TreePickler(pickler: TastyPickler) { } def pickleNewType(tpe: Type, richTypes: Boolean): Unit = try { tpe match { + case AppliedType(tycon, args) => + writeByte(APPLIEDtype) + withLength { pickleType(tycon); args.foreach(pickleType(_)) } case ConstantType(value) => pickleConstant(value) case tpe: TypeRef if tpe.info.isAlias && tpe.symbol.is(Flags.AliasPreferred) => @@ -181,15 +185,10 @@ class TreePickler(pickler: TastyPickler) { pickleNameAndSig(tpe.name, tpe.signature); pickleType(tpe.prefix) } case tpe: NamedType => - if (tpe.name == tpnme.hkApply && tpe.prefix.argInfos.nonEmpty && tpe.prefix.isInstantiatedLambda) - // instantiated lambdas are pickled as APPLIEDTYPE; #Apply will - // be reconstituted when unpickling. - pickleType(tpe.prefix) - else if (isLocallyDefined(tpe.symbol)) { + if (isLocallyDefined(tpe.symbol)) { writeByte(if (tpe.isType) TYPEREFsymbol else TERMREFsymbol) pickleSymRef(tpe.symbol); pickleType(tpe.prefix) - } - else { + } else { writeByte(if (tpe.isType) TYPEREF else TERMREF) pickleName(tpe.name); pickleType(tpe.prefix) } @@ -211,18 +210,11 @@ class TreePickler(pickler: TastyPickler) { case tpe: SkolemType => pickleType(tpe.info) case tpe: RefinedType => - val args = tpe.argInfos - if (args.isEmpty) { - writeByte(REFINEDtype) - withLength { - pickleType(tpe.parent) - pickleName(tpe.refinedName) - pickleType(tpe.refinedInfo, richTypes = true) - } - } - else { - writeByte(APPLIEDtype) - withLength { pickleType(tpe.withoutArgs(args)); args.foreach(pickleType(_)) } + writeByte(REFINEDtype) + withLength { + pickleType(tpe.parent) + pickleName(tpe.refinedName) + pickleType(tpe.refinedInfo, richTypes = true) } case tpe: TypeAlias => writeByte(TYPEALIAS) diff --git a/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala b/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala index cf7b487bbf85..618e3ceeac43 100644 --- a/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala +++ b/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala @@ -21,6 +21,7 @@ import typer.Mode import PickleBuffer._ import scala.reflect.internal.pickling.PickleFormat._ import Decorators._ +import TypeApplications._ import classfile.ClassfileParser import scala.collection.{ mutable, immutable } import scala.collection.mutable.ListBuffer @@ -52,7 +53,7 @@ object Scala2Unpickler { case TempPolyType(tparams, restpe) => if (denot.isType) { assert(!denot.isClass) - restpe.LambdaAbstract(tparams, cycleParanoid = true) + restpe.LambdaAbstract(tparams) } else PolyType.fromSymbols(tparams, restpe) @@ -142,51 +143,6 @@ object Scala2Unpickler { denot.info = ClassInfo( // final info denot.owner.thisType, denot.classSymbol, parentRefs, declsInRightOrder, ost) } - - /** Adapt arguments to type parameters so that variance of type lambda arguments - * agrees with variance of corresponding higherkinded type parameters. Example: - * - * class Companion[+CC[X]] - * Companion[List] - * - * with adaptArgs, this will expand to - * - * Companion[[X] => List[X]] - * - * instead of - * - * Companion[[+X] => List[X]] - * - * even though `List` is covariant. This adaptation is necessary to ignore conflicting - * variances in overriding members that have types of hk-type parameters such as `Companion[GenTraversable]` - * or `Companion[ListBuffer]`. Without the adaptation we would end up with - * - * Companion[[+X] => GenTraversable[X]] - * Companion[[X] => List[X]] - * - * and the second is not a subtype of the first. So if we have overridding memebrs of the two - * types we get an error. - */ - def adaptArgs(tparams: List[Symbol], args: List[Type])(implicit ctx: Context): List[Type] = tparams match { - case tparam :: tparams1 => - val boundLambda = tparam.infoOrCompleter match { - case TypeBounds(_, hi) => hi.LambdaClass(forcing = false) - case _ => NoSymbol - } - def adaptArg(arg: Type): Type = arg match { - case arg: TypeRef if arg.symbol.isLambdaTrait => - assert(arg.symbol.typeParams.length == boundLambda.typeParams.length) - arg.prefix.select(boundLambda) - case arg: RefinedType => - arg.derivedRefinedType(adaptArg(arg.parent), arg.refinedName, arg.refinedInfo) - case _ => - arg - } - val arg = args.head - val adapted = if (boundLambda.exists) adaptArg(arg) else arg - adapted :: adaptArgs(tparams1, args.tail) - case nil => args - } } /** Unpickle symbol table information descending from a class and/or module root @@ -759,8 +715,9 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas else TypeRef(pre, sym.name.asTypeName) val args = until(end, readTypeRef) if (sym == defn.ByNameParamClass2x) ExprType(args.head) - else if (args.isEmpty && sym.typeParams.nonEmpty) tycon.EtaExpand(sym.typeParams) - else tycon.appliedTo(adaptArgs(sym.typeParams, args)) + else if (args.nonEmpty) tycon.safeAppliedTo(etaExpandIfHK(sym.typeParams, args)) + else if (sym.typeParams.nonEmpty) tycon.EtaExpand(sym.typeParams) + else tycon case TYPEBOUNDStpe => TypeBounds(readTypeRef(), readTypeRef()) case REFINEDtpe => diff --git a/src/dotty/tools/dotc/printing/PlainPrinter.scala b/src/dotty/tools/dotc/printing/PlainPrinter.scala index 0d925a27dc7e..9e5ab5d8ccbe 100644 --- a/src/dotty/tools/dotc/printing/PlainPrinter.scala +++ b/src/dotty/tools/dotc/printing/PlainPrinter.scala @@ -4,7 +4,7 @@ package printing import core._ import Texts._, Types._, Flags._, Names._, Symbols._, NameOps._, Constants._, Denotations._ import Contexts.Context, Scopes.Scope, Denotations.Denotation, Annotations.Annotation -import StdNames.nme +import StdNames.{nme, tpnme} import ast.Trees._, ast._ import java.lang.Integer.toOctalString import config.Config.summarizeDepth @@ -49,9 +49,11 @@ class PlainPrinter(_ctx: Context) extends Printer { homogenize(tp1) & homogenize(tp2) case OrType(tp1, tp2) => homogenize(tp1) | homogenize(tp2) - case _ => - val tp1 = tp.simplifyApply + case tp @ TypeRef(_, tpnme.hkApply) => + val tp1 = tp.reduceProjection if (tp1 eq tp) tp else homogenize(tp1) + case _ => + tp } else tp @@ -264,12 +266,13 @@ class PlainPrinter(_ctx: Context) extends Printer { homogenize(tp) match { case tp @ TypeBounds(lo, hi) => if (lo eq hi) { - val eql = - if (tp.variance == 1) " =+ " - else if (tp.variance == -1) " =- " - else " = " + val eql = + if (tp.variance == 1) " =+ " + else if (tp.variance == -1) " =- " + else " = " eql ~ toText(lo) - } else + } + else (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) => @@ -344,7 +347,9 @@ class PlainPrinter(_ctx: Context) extends Printer { Text(sym.flagsUNSAFE.flagStrings map stringToText, " ") /** String representation of symbol's variance or "" if not applicable */ - protected def varianceString(sym: Symbol): String = sym.variance match { + protected def varianceString(sym: Symbol): String = varianceString(sym.variance) + + protected def varianceString(v: Int): String = v match { case -1 => "-" case 1 => "+" case _ => "" diff --git a/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 06fe0c9eff17..204fc95f089d 100644 --- a/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -10,6 +10,8 @@ import ast.{Trees, untpd, tpd} import typer.Namer import typer.ProtoTypes.{SelectionProto, ViewProto, FunProto, IgnoredProto, dummyTreeOfType} import Trees._ +import TypeApplications._ +import Decorators._ import scala.annotation.switch import language.implicitConversions @@ -17,7 +19,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { /** A stack of enclosing DefDef, TypeDef, or ClassDef, or ModuleDefs nodes */ private var enclosingDef: untpd.Tree = untpd.EmptyTree - + private var lambdaNestingLevel: Int = 0 private var myCtx: Context = _ctx override protected[this] implicit def ctx: Context = myCtx @@ -108,26 +110,41 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { argStr ~ " => " ~ toText(args.last) } homogenize(tp) match { - case tp: RefinedType => - val args = tp.argInfos - if (args.nonEmpty) { - val tycon = tp.unrefine - val cls = tycon.typeSymbol - if (cls.typeParams.length == args.length) { - if (tycon.isRepeatedParam) return toTextLocal(args.head) ~ "*" - if (defn.isFunctionClass(cls)) return toTextFunction(args) - if (defn.isTupleClass(cls)) return toTextTuple(args) - } - return (toTextLocal(tycon) ~ "[" ~ Text(args map argText, ", ") ~ "]").close - } - if (tp.isSafeLambda) { - val (prefix, body, bindings) = decomposeHKApply(tp) - prefix match { - case prefix: TypeRef if prefix.symbol.isLambdaTrait && body.exists => - return typeLambdaText(prefix.symbol, body, bindings) + case AppliedType(tycon, args) => + val cls = tycon.typeSymbol + if (tycon.isRepeatedParam) return toTextLocal(args.head) ~ "*" + if (defn.isFunctionClass(cls)) return toTextFunction(args) + if (defn.isTupleClass(cls)) return toTextTuple(args) + return (toTextLocal(tycon) ~ "[" ~ Text(args map argText, ", ") ~ "]").close + case tp @ TypeLambda(variances, argBoundss, body) => + val prefix = ((('X' - 'A') + lambdaNestingLevel) % 26 + 'A').toChar + val paramNames = variances.indices.toList.map(prefix.toString + _) + val instantiate = new TypeMap { + def contains(tp1: Type, tp2: Type): Boolean = + tp1.eq(tp2) || { + tp1.stripTypeVar match { + case RefinedType(parent, _) => contains(parent, tp2) + case _ => false + } + } + def apply(t: Type): Type = t match { + case TypeRef(RefinedThis(rt), name) if name.isHkArgName && contains(tp, rt) => + // Make up a name that prints as "Xi". Need to be careful we do not + // accidentally unique-hash to something else. That's why we can't + // use prefix = NoPrefix or a WithFixedSym instance. + TypeRef.withSymAndName( + defn.EmptyPackageClass.thisType, defn.AnyClass, + paramNames(name.hkArgIndex).toTypeName) case _ => + mapOver(t) } } + val instArgs = argBoundss.map(instantiate).asInstanceOf[List[TypeBounds]] + val instBody = instantiate(body).dropAlias + lambdaNestingLevel += 1 + try + return typeLambdaText(paramNames, variances, instArgs, instBody) + finally lambdaNestingLevel -=1 case tp: TypeRef => val hideType = tp.symbol is AliasPreferred if (hideType && !ctx.phase.erasedTypes && !tp.symbol.isCompleting) { @@ -167,71 +184,23 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { def blockText[T >: Untyped](trees: List[Tree[T]]): Text = "{" ~ toText(trees, "\n") ~ "}" - /** If type `tp` represents a potential type Lambda of the form - * - * parent { type Apply = body; argBindings? } - * - * split it into - * - * - the `parent` - * - the simplified `body` - * - the bindings HK$ members, if there are any - * - * The body is simplified as follows - * - if it is a TypeAlias, follow it - * - replace all references to of the form .HK$i by references - * without a prefix, because the latter print nicer. - * - */ - def decomposeHKApply(tp: Type): (Type, Type, List[(Name, Type)]) = tp.stripTypeVar match { - case tp @ RefinedType(parent, name) => - if (name == tpnme.hkApply) { - // simplify arguments so that parameters just print HK$i and not - // LambdaI{...}.HK$i - val simplifyArgs = new TypeMap { - override def apply(tp: Type) = tp match { - case tp @ TypeRef(RefinedThis(_), name) if name.isLambdaArgName => - TypeRef(NoPrefix, tp.symbol.asType) - case _ => - mapOver(tp) - } - } - (parent, simplifyArgs(tp.refinedInfo.followTypeAlias), Nil) - } else if (name.isLambdaArgName) { - val (prefix, body, argBindings) = decomposeHKApply(parent) - (prefix, body, (name, tp.refinedInfo) :: argBindings) - } else (tp, NoType, Nil) - case _ => - (tp, NoType, Nil) - } - /** The text for a TypeLambda * - * LambdaXYZ { type Apply = body'; bindings? } + * [v_1 p_1: B_1, ..., v_n p_n: B_n] -> T * * where - * @param lambdaCls The class symbol for `LambdaXYZ` - * @param body The simplified lambda body - * @param bindings The bindings of any HK$i arguments - * - * @return A text of the form - * - * [HK$0, ..., HK$n] => body - * - * possibly followed by bindings - * - * [HK$i = arg_i, ..., HK$k = arg_k] + * @param paramNames = p_1, ..., p_n + * @param variances = v_1, ..., v_n + * @param argBoundss = B_1, ..., B_n + * @param body = T */ - def typeLambdaText(lambdaCls: Symbol, body: Type, bindings: List[(Name, Type)]): Text = { - def lambdaParamText(tparam: Symbol): Text = { - varianceString(tparam) ~ nameString(tparam.name) - } - def lambdaText = changePrec(GlobalPrec) { - "[" ~ Text(lambdaCls.typeParams.map(lambdaParamText), ", ") ~ "] => " ~ toTextGlobal(body) + def typeLambdaText(paramNames: List[String], variances: List[Int], argBoundss: List[TypeBounds], body: Type): Text = { + def lambdaParamText(variance: Int, name: String, bounds: TypeBounds): Text = + varianceString(variance) ~ name ~ toText(bounds) + changePrec(GlobalPrec) { + "[" ~ Text((variances, paramNames, argBoundss).zipped.map(lambdaParamText), ", ") ~ + "] -> " ~ toTextGlobal(body) } - def bindingText(binding: (Name, Type)) = binding._1.toString ~ toTextGlobal(binding._2) - if (bindings.isEmpty) lambdaText - else atPrec(DotPrec)(lambdaText) ~ "[" ~ Text(bindings.map(bindingText), ", ") ~ "]" } override def toText[T >: Untyped](tree: Tree[T]): Text = controlled { @@ -648,7 +617,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { } override def toText(denot: Denotation): Text = denot match { - case denot: MultiDenotation => denot.toString + case denot: MultiDenotation => Text(denot.alternatives.map(dclText), " ") case NoDenotation => "NoDenotation" case _ => if (denot.symbol.exists) toText(denot.symbol) diff --git a/src/dotty/tools/dotc/transform/ParamForwarding.scala b/src/dotty/tools/dotc/transform/ParamForwarding.scala index 2e6a97bcf7d9..9571c387bfc9 100644 --- a/src/dotty/tools/dotc/transform/ParamForwarding.scala +++ b/src/dotty/tools/dotc/transform/ParamForwarding.scala @@ -80,4 +80,15 @@ class ParamForwarding(thisTransformer: DenotTransformer) { cpy.Template(impl)(body = fwd(impl.body)(ctx.withPhase(thisTransformer))) } + + def adaptRef[T <: RefTree](tree: T)(implicit ctx: Context): T = tree.tpe match { + case tpe: TermRefWithSignature + if tpe.sig == Signature.NotAMethod && tpe.symbol.is(Method) => + // It's a param forwarder; adapt the signature + tree.withType( + TermRef.withSig(tpe.prefix, tpe.name, tpe.prefix.memberInfo(tpe.symbol).signature)) + .asInstanceOf[T] + case _ => + tree + } } diff --git a/src/dotty/tools/dotc/transform/PostTyper.scala b/src/dotty/tools/dotc/transform/PostTyper.scala index 75c98ae8af96..3266d3a02bc5 100644 --- a/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/src/dotty/tools/dotc/transform/PostTyper.scala @@ -157,10 +157,10 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisTran case tree: Ident => tree.tpe match { case tpe: ThisType => This(tpe.cls).withPos(tree.pos) - case _ => tree + case _ => paramFwd.adaptRef(tree) } case tree: Select => - transformSelect(tree, Nil) + transformSelect(paramFwd.adaptRef(tree), Nil) case tree @ TypeApply(sel: Select, args) => val args1 = transform(args) val sel1 = transformSelect(sel, args1) @@ -170,11 +170,12 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisTran case tree: Template => val saved = parentNews parentNews ++= tree.parents.flatMap(newPart) - try + try { + val templ1 = paramFwd.forwardParamAccessors(tree) synthMth.addSyntheticMethods( - paramFwd.forwardParamAccessors( - superAcc.wrapTemplate(tree)( - super.transform(_).asInstanceOf[Template]))) + superAcc.wrapTemplate(templ1)( + super.transform(_).asInstanceOf[Template])) + } finally parentNews = saved case tree: DefDef => transformAnnots(tree) diff --git a/src/dotty/tools/dotc/transform/SuperAccessors.scala b/src/dotty/tools/dotc/transform/SuperAccessors.scala index 31cfef9140d7..2febd267386b 100644 --- a/src/dotty/tools/dotc/transform/SuperAccessors.scala +++ b/src/dotty/tools/dotc/transform/SuperAccessors.scala @@ -99,7 +99,8 @@ class SuperAccessors(thisTransformer: DenotTransformer) { assert(sup.symbol.exists, s"missing symbol in $sel: ${sup.tpe}") val clazz = sup.symbol.asClass - if ((sym.isTerm) && !(sym is Method) || (sym is Accessor)) + if (sym.isTerm && !sym.is(Method, butNot = Accessor) && !ctx.owner.is(ParamForwarder)) + // ParamForwaders as installed ParamForwarding.scala do use super calls to vals ctx.error(s"super may be not be used on ${sym.underlyingSymbol}", sel.pos) else if (isDisallowed(sym)) ctx.error(s"super not allowed here: use this.${sel.name.decode} instead", sel.pos) diff --git a/src/dotty/tools/dotc/typer/Applications.scala b/src/dotty/tools/dotc/typer/Applications.scala index 4ca9c39af5b3..b3cda20b822d 100644 --- a/src/dotty/tools/dotc/typer/Applications.scala +++ b/src/dotty/tools/dotc/typer/Applications.scala @@ -33,7 +33,7 @@ object Applications { def hasNamedArg(args: List[Any]) = args exists isNamedArg def extractorMemberType(tp: Type, name: Name, errorPos: Position = NoPosition)(implicit ctx:Context) = { - val ref = tp member name + val ref = tp.member(name).suchThat(_.info.isParameterless) if (ref.isOverloaded) errorType(i"Overloaded reference to $ref is not allowed in extractor", errorPos) else if (ref.info.isInstanceOf[PolyType]) @@ -541,27 +541,27 @@ trait Applications extends Compatibility { self: Typer => // a modified tree but this would be more convoluted and less efficient. if (proto.isTupled) proto = proto.tupled - methPart(fun1).tpe match { - case funRef: TermRef => - tryEither { implicit ctx => - val app = - if (proto.argsAreTyped) new ApplyToTyped(tree, fun1, funRef, proto.typedArgs, pt) - else new ApplyToUntyped(tree, fun1, funRef, proto, pt)(argCtx) - val result = app.result - convertNewArray(ConstFold(result)) - } { (failedVal, failedState) => - val fun2 = tryInsertImplicitOnQualifier(fun1, proto) - if (fun1 eq fun2) { - failedState.commit() - failedVal - } else typedApply( - cpy.Apply(tree)(untpd.TypedSplice(fun2), proto.typedArgs map untpd.TypedSplice), pt) - } - case _ => - fun1.tpe match { - case ErrorType => tree.withType(ErrorType) - case tp => handleUnexpectedFunType(tree, fun1) - } + fun1.tpe match { + case ErrorType => tree.withType(ErrorType) + case _ => methPart(fun1).tpe match { + case funRef: TermRef => + tryEither { implicit ctx => + val app = + if (proto.argsAreTyped) new ApplyToTyped(tree, fun1, funRef, proto.typedArgs, pt) + else new ApplyToUntyped(tree, fun1, funRef, proto, pt)(argCtx) + val result = app.result + convertNewArray(ConstFold(result)) + } { (failedVal, failedState) => + val fun2 = tryInsertImplicitOnQualifier(fun1, proto) + if (fun1 eq fun2) { + failedState.commit() + failedVal + } else typedApply( + cpy.Apply(tree)(untpd.TypedSplice(fun2), proto.typedArgs map untpd.TypedSplice), pt) + } + case _ => + handleUnexpectedFunType(tree, fun1) + } } } @@ -614,7 +614,7 @@ trait Applications extends Compatibility { self: Typer => } def adaptTypeArg(tree: tpd.Tree, bound: Type)(implicit ctx: Context): tpd.Tree = - tree.withType(tree.tpe.EtaExpandIfHK(bound)) + tree.withType(tree.tpe.etaExpandIfHK(bound)) /** Rewrite `new Array[T](....)` trees to calls of newXYZArray methods. */ def convertNewArray(tree: tpd.Tree)(implicit ctx: Context): tpd.Tree = tree match { diff --git a/src/dotty/tools/dotc/typer/Checking.scala b/src/dotty/tools/dotc/typer/Checking.scala index 9dfe462329ee..57032c4d9ecb 100644 --- a/src/dotty/tools/dotc/typer/Checking.scala +++ b/src/dotty/tools/dotc/typer/Checking.scala @@ -401,6 +401,17 @@ trait Checking { errorTree(tpt, d"missing type parameter for ${tpt.tpe}") } else tpt + + def checkLowerNotHK(sym: Symbol, tparams: List[Symbol], pos: Position)(implicit ctx: Context) = + if (tparams.nonEmpty) + sym.info match { + case info: TypeAlias => // ok + case TypeBounds(lo, _) => + for (tparam <- tparams) + if (tparam.typeRef.occursIn(lo)) + ctx.error(i"type parameter ${tparam.name} may not occur in lower bound $lo", pos) + case _ => + } } trait NoChecking extends Checking { @@ -414,4 +425,5 @@ trait NoChecking extends Checking { override def checkNoDoubleDefs(cls: Symbol)(implicit ctx: Context): Unit = () override def checkParentCall(call: Tree, caller: ClassSymbol)(implicit ctx: Context) = () override def checkSimpleKinded(tpt: Tree)(implicit ctx: Context): Tree = tpt + override def checkLowerNotHK(sym: Symbol, tparams: List[Symbol], pos: Position)(implicit ctx: Context) = () } diff --git a/src/dotty/tools/dotc/typer/Inferencing.scala b/src/dotty/tools/dotc/typer/Inferencing.scala index 1e8dcf4b27a1..9717b5625d05 100644 --- a/src/dotty/tools/dotc/typer/Inferencing.scala +++ b/src/dotty/tools/dotc/typer/Inferencing.scala @@ -89,7 +89,7 @@ object Inferencing { val minimize = variance >= 0 && !( force == ForceDegree.noBottom && - isBottomType(ctx.typeComparer.approximation(tvar.origin, fromBelow = true))) + defn.isBottomType(ctx.typeComparer.approximation(tvar.origin, fromBelow = true))) if (minimize) instantiate(tvar, fromBelow = true) else toMaximize = true } @@ -173,9 +173,6 @@ object Inferencing { approxAbove - approxBelow } - def isBottomType(tp: Type)(implicit ctx: Context) = - tp == defn.NothingType || tp == defn.NullType - /** Recursively widen and also follow type declarations and type aliases. */ def widenForMatchSelector(tp: Type)(implicit ctx: Context): Type = tp.widen match { case tp: TypeRef if !tp.symbol.isClass => widenForMatchSelector(tp.info.bounds.hi) diff --git a/src/dotty/tools/dotc/typer/Namer.scala b/src/dotty/tools/dotc/typer/Namer.scala index 5eebdbad1cc8..b24916be8005 100644 --- a/src/dotty/tools/dotc/typer/Namer.scala +++ b/src/dotty/tools/dotc/typer/Namer.scala @@ -18,6 +18,7 @@ import config.Printers._ import Annotations._ import Inferencing._ import transform.ValueClasses._ +import TypeApplications._ import language.implicitConversions trait NamerContextOps { this: Context => @@ -578,7 +579,12 @@ class Namer { typer: Typer => else { val pt = checkClassTypeWithStablePrefix(ptype, parent.pos, traitReq = parent ne parents.head) if (pt.derivesFrom(cls)) { - ctx.error(i"cyclic inheritance: $cls extends itself", parent.pos) + val addendum = parent match { + case Select(qual: Super, _) if ctx.scala2Mode => + "\n(Note that inheriting a class of the same name is no longer allowed)" + case _ => "" + } + ctx.error(i"cyclic inheritance: $cls extends itself$addendum", parent.pos) defn.ObjectType } else pt @@ -788,9 +794,36 @@ class Namer { typer: Typer => /** The type signature of a DefDef with given symbol */ def defDefSig(ddef: DefDef, sym: Symbol)(implicit ctx: Context) = { val DefDef(name, tparams, vparamss, _, _) = ddef - completeParams(tparams) - vparamss foreach completeParams val isConstructor = name == nme.CONSTRUCTOR + + // The following 3 lines replace what was previously just completeParams(tparams). + // But that can cause bad bounds being computed, as witnessed by + // tests/pos/paramcycle.scala. The problematic sequence is this: + // 0. Class constructor gets completed. + // 1. Type parameter CP of constructor gets completed + // 2. As a first step CP's bounds are set to Nothing..Any. + // 3. CP's real type bound demands the completion of corresponding type parameter DP + // of enclosing class. + // 4. Type parameter DP has a rhs a DerivedFromParam tree, as installed by + // desugar.classDef + // 5. The completion of DP then copies the current bounds of CP, which are still Nothing..Any. + // 6. The completion of CP finishes installing the real type bounds. + // Consequence: CP ends up with the wrong bounds! + // To avoid this we always complete type parameters of a class before the type parameters + // of the class constructor, but after having indexed the constructor parameters (because + // indexing is needed to provide a symbol to copy for DP's completion. + // With the patch, we get instead the following sequence: + // 0. Class constructor gets completed. + // 1. Class constructor parameter CP is indexed. + // 2. Class parameter DP starts completion. + // 3. Info of CP is computed (to be copied to DP). + // 4. CP is completed. + // 5. Info of CP is copied to DP and DP is completed. + index(tparams) + if (isConstructor) sym.owner.typeParams.foreach(_.ensureCompleted()) + for (tparam <- tparams) typedAheadExpr(tparam) + + vparamss foreach completeParams def typeParams = tparams map symbolOfTree val paramSymss = ctx.normalizeIfConstructor(vparamss.nestedMap(symbolOfTree), isConstructor) def wrapMethType(restpe: Type): Type = { @@ -833,33 +866,32 @@ class Namer { typer: Typer => case bounds: TypeBounds => bounds case alias => TypeAlias(alias, if (sym is Local) sym.variance else 0) } - sym.info = NoCompleter - sym.info = checkNonCyclic(sym, unsafeInfo, reportErrors = true) + if (isDerived) sym.info = unsafeInfo + else { + sym.info = NoCompleter + sym.info = checkNonCyclic(sym, unsafeInfo, reportErrors = true) + } etaExpandArgs.apply(sym.info) } /** Eta expand all class types C appearing as arguments to a higher-kinded * type parameter to type lambdas, e.g. [HK0] => C[HK0]. This is necessary - * because in `typedAppliedTypeTree` we might ahve missed some eta expansions + * because in `typedAppliedTypeTree` we might have missed some eta expansions * of arguments in F-bounds, because the recursive type was initialized with * TypeBounds.empty. */ def etaExpandArgs(implicit ctx: Context) = new TypeMap { - def apply(tp: Type): Type = { - tp match { - case tp: RefinedType => - val args = tp.argInfos.mapconserve(this) - if (args.nonEmpty) { - val tycon = tp.withoutArgs(args) - val tparams = tycon.typeParams - if (args.length == tparams.length) { // if lengths differ, problem is caught in typedTypeApply - val args1 = args.zipWithConserve(tparams)((arg, tparam) => arg.EtaExpandIfHK(tparam.info)) - if (args1 ne args) return this(tycon).appliedTo(args1) - } - } - case _ => - } - mapOver(tp) + def apply(tp: Type): Type = tp match { + case tp: RefinedType => + val args = tp.argInfos.mapconserve(this) + if (args.nonEmpty) { + val tycon = tp.withoutArgs(args) + val tycon1 = this(tycon) + val tparams = tycon.typeParams + val args1 = if (args.length == tparams.length) etaExpandIfHK(tparams, args) else args + if ((tycon1 eq tycon) && (args1 eq args)) tp else tycon1.appliedTo(args1) + } else mapOver(tp) + case _ => mapOver(tp) } } } diff --git a/src/dotty/tools/dotc/typer/RefChecks.scala b/src/dotty/tools/dotc/typer/RefChecks.scala index 3ffbb8079350..44d5ee576816 100644 --- a/src/dotty/tools/dotc/typer/RefChecks.scala +++ b/src/dotty/tools/dotc/typer/RefChecks.scala @@ -15,6 +15,7 @@ import TreeTransforms._ import util.DotClass import scala.util.{Try, Success, Failure} import config.{ScalaVersion, NoScalaVersion} +import Decorators._ import typer.ErrorReporting._ import DenotTransformers._ import ValueClasses.isDerivedValueClass @@ -287,7 +288,7 @@ object RefChecks { !member.isAnyOverride) { // (*) Exclusion for default getters, fixes SI-5178. We cannot assign the Override flag to // the default getter: one default getter might sometimes override, sometimes not. Example in comment on ticket. - if (member.name == nme.isDefined && member.is(Synthetic)) // isDefined methods are added automatially, can't have an override preset. + if (member.is(Synthetic) && desugar.isDesugaredCaseClassMethodName(member.name)) // such names are added automatically, can't have an override preset. member.setFlag(Override) else if (member.owner != clazz && other.owner != clazz && !(other.owner derivesFrom member.owner)) emitOverrideError( @@ -340,10 +341,20 @@ object RefChecks { }*/ } - val opc = new OverridingPairs.Cursor(clazz) - while (opc.hasNext) { - checkOverride(opc.overriding, opc.overridden) - opc.next() + try { + val opc = new OverridingPairs.Cursor(clazz) + while (opc.hasNext) { + checkOverride(opc.overriding, opc.overridden) + opc.next() + } + } catch { + case ex: MergeError => + val addendum = ex.tp1 match { + case tp1: ClassInfo => + "\n(Note that having same-named member classes in types of a mixin composition is no longer allowed)" + case _ => "" + } + ctx.error(ex.getMessage + addendum, clazz.pos) } printMixinOverrideErrors() diff --git a/src/dotty/tools/dotc/typer/TypeAssigner.scala b/src/dotty/tools/dotc/typer/TypeAssigner.scala index 30d6baf8a47c..2b7eb3936d00 100644 --- a/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -392,13 +392,30 @@ trait TypeAssigner { tree.withType(proto) def assignType(tree: untpd.ValDef, sym: Symbol)(implicit ctx: Context) = - tree.withType(if (sym.exists) sym.valRef else NoType) + tree.withType(if (sym.exists) assertExists(symbolicIfNeeded(sym).orElse(sym.valRef)) else NoType) def assignType(tree: untpd.DefDef, sym: Symbol)(implicit ctx: Context) = - tree.withType(sym.termRefWithSig) + tree.withType(symbolicIfNeeded(sym).orElse(sym.termRefWithSig)) def assignType(tree: untpd.TypeDef, sym: Symbol)(implicit ctx: Context) = - tree.withType(sym.typeRef) + tree.withType(symbolicIfNeeded(sym).orElse(sym.typeRef)) + + 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 + } + } + + def assertExists(tp: Type) = { assert(tp != NoType); tp } def assignType(tree: untpd.Import, sym: Symbol)(implicit ctx: Context) = tree.withType(sym.nonMemberTermRef) diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index 3f5c4f47ef89..e5509d50f1cb 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -332,7 +332,12 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit case pt: SelectionProto if pt.name == nme.CONSTRUCTOR => true case _ => false } - assignType(cpy.Super(tree)(qual1, tree.mix), qual1, inConstrCall) + pt match { + case pt: SelectionProto if pt.name.isTypeName => + qual1 // don't do super references for types; they are meaningless anyway + case _ => + assignType(cpy.Super(tree)(qual1, tree.mix), qual1, inConstrCall) + } } def typedLiteral(tree: untpd.Literal)(implicit ctx: Context) = track("typedLiteral") { @@ -348,10 +353,9 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit typed(cpy.Block(tree)(clsDef :: Nil, New(Ident(x), Nil)), pt) case _ => var tpt1 = typedType(tree.tpt) - if (tpt1.tpe.isHK) { - val deAliased = tpt1.tpe.dealias.EtaReduce - if (deAliased.exists && deAliased.ne(tpt1.tpe)) - tpt1 = tpt1.withType(deAliased) + tpt1.tpe.dealias match { + case TypeApplications.EtaExpansion(tycon) => tpt1 = tpt1.withType(tycon) + case _ => } checkClassTypeWithStablePrefix(tpt1.tpe, tpt1.pos, traitReq = false) assignType(cpy.New(tree)(tpt1), tpt1) @@ -931,6 +935,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit def typedTypeDef(tdef: untpd.TypeDef, sym: Symbol)(implicit ctx: Context): Tree = track("typedTypeDef") { val TypeDef(name, rhs) = tdef + checkLowerNotHK(sym, tdef.tparams.map(symbolOfTree), tdef.pos) completeAnnotations(tdef, sym) val _ = typedType(rhs) // unused, typecheck only to remove from typedTree assignType(cpy.TypeDef(tdef)(name, TypeTree(sym.info), Nil), sym) @@ -1416,7 +1421,13 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit implicitArgError(d"no implicit argument of type $formal found for $where" + failure.postscript) } } - if (args.exists(_.isEmpty)) { assert(constr eq ctx.typerState.constraint); tree } + if (args.exists(_.isEmpty)) { + // If there are several arguments, some arguments might already + // have influenced the context, binfing variables, but later ones + // might fail. In that case the constraint needs to be reset. + ctx.typerState.constraint = constr + tree + } else adapt(tpd.Apply(tree, args), pt) } if ((pt eq WildcardType) || original.isEmpty) addImplicitArgs diff --git a/src/dotty/tools/dotc/typer/VarianceChecker.scala b/src/dotty/tools/dotc/typer/VarianceChecker.scala index 86b1676c5147..bbe391726d81 100644 --- a/src/dotty/tools/dotc/typer/VarianceChecker.scala +++ b/src/dotty/tools/dotc/typer/VarianceChecker.scala @@ -60,7 +60,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) None + if (relative == Bivariant || tvar.is(BaseTypeArg)) None else { val required = compose(relative, this.variance) def tvar_s = s"$tvar (${varianceString(tvar.flags)} ${tvar.showLocated})" @@ -84,7 +84,7 @@ class VarianceChecker()(implicit ctx: Context) { case tp: TypeRef => val sym = tp.symbol if (sym.variance != 0 && base.isContainedIn(sym.owner)) checkVarianceOfSymbol(sym) - else if (sym.isAliasType) this(status, sym.info) + else if (sym.isAliasType) this(status, sym.info.bounds.hi) else foldOver(status, tp) case tp: MethodType => this(status, tp.resultType) // params will be checked in their TypeDef nodes. diff --git a/test/dotc/scala-collections.whitelist b/test/dotc/scala-collections.whitelist index 61faf8eb69c1..328d040c15bf 100644 --- a/test/dotc/scala-collections.whitelist +++ b/test/dotc/scala-collections.whitelist @@ -80,8 +80,6 @@ ./scala-scala/src/library/scala/collection/immutable/MapProxy.scala ./scala-scala/src/library/scala/collection/immutable/PagedSeq.scala ./scala-scala/src/library/scala/collection/immutable/Queue.scala - -# https://github.com/lampepfl/dotty/issues/916 ./scala-scala/src/library/scala/collection/immutable/Seq.scala ./scala-scala/src/library/scala/collection/mutable/IndexedSeq.scala ./scala-scala/src/library/scala/collection/mutable/ListBuffer.scala @@ -134,13 +132,8 @@ ./scala-scala/src/library/scala/collection/GenMapLike.scala ./scala-scala/src/library/scala/collection/GenSeq.scala ./scala-scala/src/library/scala/collection/GenSeqLike.scala - -# seems https://github.com/lampepfl/dotty/issues/916 -#./scala-scala/src/library/scala/collection/GenSet.scala - +./scala-scala/src/library/scala/collection/GenSet.scala ./scala-scala/src/library/scala/collection/GenSetLike.scala - -# breaks genMapLike https://github.com/lampepfl/dotty/issues/937 ./scala-scala/src/library/scala/collection/GenTraversable.scala ./scala-scala/src/library/scala/collection/GenTraversableLike.scala @@ -161,21 +154,12 @@ ./scala-scala/src/library/scala/collection/Parallel.scala ./scala-scala/src/library/scala/collection/Parallelizable.scala ./scala-scala/src/library/scala/collection/Searching.scala - - ./scala-scala/src/library/scala/collection/Seq.scala - ./scala-scala/src/library/scala/collection/SeqExtractors.scala - -# https://github.com/lampepfl/dotty/issues/945 ./scala-scala/src/library/scala/collection/SeqLike.scala - ./scala-scala/src/library/scala/collection/SeqProxy.scala ./scala-scala/src/library/scala/collection/SeqProxyLike.scala - -# seems https://github.com/lampepfl/dotty/issues/916 -#./scala-scala/src/library/scala/collection/Set.scala - +./scala-scala/src/library/scala/collection/Set.scala ./scala-scala/src/library/scala/collection/SetLike.scala ./scala-scala/src/library/scala/collection/SetProxy.scala ./scala-scala/src/library/scala/collection/SetProxyLike.scala @@ -184,40 +168,35 @@ ./scala-scala/src/library/scala/collection/SortedSet.scala ./scala-scala/src/library/scala/collection/SortedSetLike.scala ./scala-scala/src/library/scala/collection/Traversable.scala - -# https://github.com/lampepfl/dotty/issues/938 (but relies also on #937 being fixed) -#./scala-scala/src/library/scala/collection/TraversableLike.scala +./scala-scala/src/library/scala/collection/TraversableLike.scala ./scala-scala/src/library/scala/collection/TraversableProxy.scala ./scala-scala/src/library/scala/collection/TraversableProxyLike.scala ./scala-scala/src/library/scala/collection/package.scala +./scala-scala/src/library/scala/collection/IterableView.scala +./scala-scala/src/library/scala/collection/SeqView.scala +./scala-scala/src/library/scala/collection/TraversableView.scala + ## those classes use early initialisers. -#./scala-scala/src/library/scala/collection/IterableView.scala -#./scala-scala/src/library/scala/collection/IterableViewLike.scala -#./scala-scala/src/library/scala/collection/SeqView.scala -#./scala-scala/src/library/scala/collection/SeqViewLike.scala -#./scala-scala/src/library/scala/collection/TraversableView.scala -#./scala-scala/src/library/scala/collection/TraversableViewLike.scala -#./scala-scala/src/library/scala/collection/immutable/StreamViewLike.scala -#./scala-scala/src/library/scala/collection/immutable/TrieIterator.scala +./scala-scala/src/library/scala/collection/TraversableViewLike.scala +./scala-scala/src/library/scala/collection/IterableViewLike.scala +./scala-scala/src/library/scala/collection/SeqViewLike.scala +./scala-scala/src/library/scala/collection/mutable/IndexedSeqView.scala +./scala-scala/src/library/scala/collection/immutable/StreamViewLike.scala +## This class causes a crash in backend -> @darkdimius +#./scala-scala/src/library/scala/collection/immutable/TrieIterator.scala ./scala-scala/src/library/scala/collection/immutable/HashMap.scala +./scala-scala/src/library/scala/collection/immutable/HashSet.scala -# seems https://github.com/lampepfl/dotty/issues/916 -#./scala-scala/src/library/scala/collection/immutable/HashSet.scala - -# https://github.com/lampepfl/dotty/issues/939 +# https://github.com/lampepfl/dotty/issues/939 -> @darkdimius #./scala-scala/src/library/scala/collection/immutable/IntMap.scala - - ./scala-scala/src/library/scala/collection/immutable/ListMap.scala +./scala-scala/src/library/scala/collection/immutable/ListSet.scala -# seems https://github.com/lampepfl/dotty/issues/916 -#./scala-scala/src/library/scala/collection/immutable/ListSet.scala - -# https://github.com/lampepfl/dotty/issues/939 +# https://github.com/lampepfl/dotty/issues/939 -> @darkdimius #./scala-scala/src/library/scala/collection/immutable/LongMap.scala ./scala-scala/src/library/scala/collection/immutable/Map.scala @@ -226,8 +205,8 @@ ./scala-scala/src/library/scala/collection/immutable/Range.scala ./scala-scala/src/library/scala/collection/immutable/RedBlackTree.scala -# uses refinements that dotty does not support -#./scala-scala/src/library/scala/collection/immutable/Set.scala +# had a variance error, now fixed. +./scala-scala/src/library/scala/collection/immutable/Set.scala ./scala-scala/src/library/scala/collection/immutable/SetProxy.scala @@ -288,10 +267,10 @@ ./scala-scala/src/library/scala/collection/generic/SetFactory.scala ./scala-scala/src/library/scala/collection/generic/ParFactory.scala -# https://github.com/lampepfl/dotty/issues/974 +# https://github.com/lampepfl/dotty/issues/974 -> @smarter #./scala-scala/src/library/scala/collection/generic/MutableSortedSetFactory.scala -# cyclic reference, maybe related to #974 +# cyclic reference, maybe related to #974 -> @smarter #./scala-scala/src/library/scala/collection/generic/ParSetFactory.scala ./scala-scala/src/library/scala/collection/generic/OrderedTraversableFactory.scala diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala index 0ac043daf5e0..c57f32b58f27 100644 --- a/test/dotc/tests.scala +++ b/test/dotc/tests.scala @@ -100,6 +100,7 @@ class tests extends CompilerTest { @Test def pos_all = compileFiles(posDir) // twice omitted to make tests run faster + @Test def pos_sets = compileFile(posSpecialDir, "sets")(allowDeepSubtypes) @Test def pos_t2613 = compileFile(posSpecialDir, "t2613")(allowDeepSubtypes) @Test def pos_i871 = compileFile(posSpecialDir, "i871", scala2mode) @Test def pos_variancesConstr = compileFile(posSpecialDir, "variances-constr", scala2mode) @@ -120,7 +121,8 @@ class tests extends CompilerTest { @Test def neg_autoTupling2 = compileFile(negDir, "autoTuplingTest", xerrors = 3) @Test def neg_companions = compileFile(negDir, "companions", xerrors = 1) @Test def neg_over = compileFile(negDir, "over", xerrors = 3) - @Test def neg_overrides = compileFile(negDir, "overrides", xerrors = 11) + @Test def neg_overrides = compileFile(negDir, "overrides", xerrors = 10) + @Test def neg_overrideClass = compileFile(negDir, "overrideClass", List("-language:Scala2"), xerrors = 1) @Test def neg_i39 = compileFile(negDir, "i39", xerrors = 2) @Test def neg_i50_volatile = compileFile(negDir, "i50-volatile", xerrors = 6) @Test def neg_zoo = compileFile(negDir, "zoo", xerrors = 12) @@ -147,6 +149,7 @@ class tests extends CompilerTest { @Test def neg_cycles = compileFile(negDir, "cycles", xerrors = 8) @Test def neg_boundspropagation = compileFile(negDir, "boundspropagation", xerrors = 5) @Test def neg_refinedSubtyping = compileFile(negDir, "refinedSubtyping", xerrors = 2) + @Test def neg_hklower = compileFile(negDir, "hklower", xerrors = 3) @Test def neg_i0091_infpaths = compileFile(negDir, "i0091-infpaths", xerrors = 3) @Test def neg_i0248_inherit_refined = compileFile(negDir, "i0248-inherit-refined", xerrors = 4) @Test def neg_i0281 = compileFile(negDir, "i0281-null-primitive-conforms", xerrors = 3) @@ -158,8 +161,9 @@ class tests extends CompilerTest { @Test def neg_moduleSubtyping = compileFile(negDir, "moduleSubtyping", xerrors = 4) @Test def neg_escapingRefs = compileFile(negDir, "escapingRefs", xerrors = 2) @Test def neg_instantiateAbstract = compileFile(negDir, "instantiateAbstract", xerrors = 8) + @Test def neg_partialApplications = compileFile(negDir, "partialApplications", xerrors = 3) @Test def neg_selfInheritance = compileFile(negDir, "selfInheritance", xerrors = 6) - @Test def neg_selfreq = compileFile(negDir, "selfreq", xerrors = 3) + @Test def neg_selfreq = compileFile(negDir, "selfreq", xerrors = 2) @Test def neg_singletons = compileFile(negDir, "singletons", xerrors = 8) @Test def neg_shadowedImplicits = compileFile(negDir, "arrayclone-new", xerrors = 2) @Test def neg_traitParamsTyper = compileFile(negDir, "traitParamsTyper", xerrors = 5) @@ -180,7 +184,7 @@ class tests extends CompilerTest { .toList @Test def compileStdLib = compileList("compileStdLib", stdlibFiles, "-migration" :: scala2mode) - @Test def dotty = compileDir(dottyDir, ".", "-deep" :: "-Ycheck-reentrant" :: allowDeepSubtypes) // note the -deep argument + @Test def dotty = compileDir(dottyDir, ".", List("-deep", "-Ycheck-reentrant"))(allowDeepSubtypes) // note the -deep argument @Test def dotc_ast = compileDir(dotcDir, "ast") @Test def dotc_config = compileDir(dotcDir, "config") @@ -189,7 +193,7 @@ class tests extends CompilerTest { // This directory doesn't exist anymore // @Test def dotc_core_pickling = compileDir(coreDir, "pickling")(allowDeepSubtypes)// twice omitted to make tests run faster - @Test def dotc_transform = compileDir(dotcDir, "transform")// twice omitted to make tests run faster + @Test def dotc_transform = compileDir(dotcDir, "transform")(allowDeepSubtypes)// twice omitted to make tests run faster @Test def dotc_parsing = compileDir(dotcDir, "parsing") // twice omitted to make tests run faster diff --git a/tests/neg/hklower.scala b/tests/neg/hklower.scala new file mode 100644 index 000000000000..e29a1545e8d1 --- /dev/null +++ b/tests/neg/hklower.scala @@ -0,0 +1,11 @@ +class Test { + + type T[X] // OK + type U[X] = T[X] // OK + + type V[X] >: T[X] // error + type W[X] >: T[X] <: T[X] // error + + def f[C[X] >: T[X]]() = ??? // error + +} diff --git a/tests/neg/overrideClass.scala b/tests/neg/overrideClass.scala new file mode 100644 index 000000000000..803d97dd92d7 --- /dev/null +++ b/tests/neg/overrideClass.scala @@ -0,0 +1,20 @@ + abstract class FooA { + type A <: Ax; + abstract class Ax; + abstract class InnerA { + type B <: A; + def doB : B; + } + } + trait FooB extends FooA { + type A <: Ax; + trait Ax extends super.Ax { def xxx : Int; } // error: cyclic inheritance: trait Ax extends itself + // (Note that inheriting a class of the same name is no longer allowed) + abstract class InnerB extends InnerA { + // type B <: A; + val a : A = doB; + a.xxx; + doB.xxx; + } + } + diff --git a/tests/neg/overrides.scala b/tests/neg/overrides.scala index 9fe06d93020c..b7e5981846b1 100644 --- a/tests/neg/overrides.scala +++ b/tests/neg/overrides.scala @@ -1,22 +1,3 @@ - abstract class FooA { - type A <: Ax; - abstract class Ax; - abstract class InnerA { - type B <: A; - def doB : B; - } - } - trait FooB extends FooA { - type A <: Ax; - trait Ax extends super.Ax { def xxx : Int; } - abstract class InnerB extends InnerA { - // type B <: A; - val a : A = doB; - a.xxx; - doB.xxx; - } - } - package p1 { abstract class T1 { protected def bug(p: Int = 1): Int // without 'protected' compiles fine @@ -31,7 +12,7 @@ package p2 { // all being in the same package compiles fine } abstract class T3 extends T2 { - class A { + class A { // error: classes cannot be overridden bug() } } @@ -45,7 +26,7 @@ class A[T] { class B extends A[Int] { - def f(x: Int)(y: Int) = y + def f(x: Int)(y: Int) = y // error: needs `override' modifier f(2)() @@ -55,7 +36,7 @@ class X { def f: A[Int] = ??? } class Y extends X { - def f: A[Int] = ??? + def f: A[Int] = ??? // error: needs `override' modifier } @@ -66,18 +47,18 @@ class X1 { def f(): A1 = ??? } class Y1 extends X1 { - override def f(): B1 = ??? + override def f(): B1 = ??? // error: has incompatible type } class X2 { type T = A1 } class Y2 extends X2 { - type T = B1 + type T = B1 // error: needs `override' modifier } class X3 { - override type T = A1 + override type T = A1 // error: overrides nothing } package p3 { @@ -97,14 +78,14 @@ trait TOverrider { this: TCommon => override def f = "in TOverrider" // The overridden self-type member... } -class C2 extends C1 with TOverrider // ... fails to override, here. +class C2 extends C1 with TOverrider // ... fails to override, here. // error: accidental override } package p4 { abstract class C[T] { def head: T } - case class D[T](head: Int) extends C[T] + case class D[T](head: Int) extends C[T] // error: has incompatible type } @@ -114,10 +95,10 @@ class A { } class B extends A { - override val m: Int = 42 + override val m: Int = 42 // error: has incompatible type } class C extends A { - override def m: Int = 42 + override def m: Int = 42 // error: has incompatible type } } diff --git a/tests/neg/partialApplications.scala b/tests/neg/partialApplications.scala new file mode 100644 index 000000000000..d186273aa2e3 --- /dev/null +++ b/tests/neg/partialApplications.scala @@ -0,0 +1,16 @@ +object Test2 { + type Histogram = Map[_, Int] // this is now an existential type! + + type StringlyHistogram = Histogram[_ >: String] // Error: Test2.Histogram does not take type parameters + + val xs: Histogram[String] = Map[String, Int]() // Error: Test2.Histogram does not take type parameters + + val ys: StringlyHistogram[String] = xs // Error: Test2.StringlyHistogram does not take type parameters + + val zs: StringlyHistogram = xs + + val xs1 = xs + val ys1 = ys + val zs1 = zs + +} diff --git a/tests/pickling/partialApplications.scala b/tests/pickling/partialApplications.scala index f517011b995a..c4c4328f6509 100644 --- a/tests/pickling/partialApplications.scala +++ b/tests/pickling/partialApplications.scala @@ -1,13 +1,29 @@ -object PartialApplications { +object partialApplications { - type Histogram = Map[_, Int] + type Histogram[X] = Map[X, Int] - type StringlyHistogram = Histogram[_ >: String] + type StringlyHistogram[X >: String] = Histogram[X] val xs: Histogram[String] = Map[String, Int]() val ys: StringlyHistogram[String] = xs - val zs: StringlyHistogram = xs + def e = xs + + val zs: StringlyHistogram[_] = e + + type IntMap[Y] = Map[Int, Y] + + val is = Map[Int, Boolean]() + + val js: IntMap[Boolean] = is + + val ks: IntMap[_] = is + + type RMap[X, Y] = Map[Y, X] + + val rs = Map[Int, Float]() + + val ss: RMap[Float, Int] = rs } diff --git a/tests/pickling/selfSym.scala b/tests/pickling/selfSym.scala new file mode 100644 index 000000000000..ab8db8513cff --- /dev/null +++ b/tests/pickling/selfSym.scala @@ -0,0 +1,26 @@ +// A test which exercises both param forwarding and explicit self types, +// so param forwarder definitions will get symbolic references. +// It leads to tricky situations which manifest themselves by pickle +// failures. Before pickling, a param accessor still had +// the value type (which is wrong), when reading back the +// pickled info, this type is then the correct ExprType. +// Fixed by adapating references oto param forwarders in ParamForwarding.scala +// Without the symblolic reference, this error was somehow masked by +// the fact that the reference cache was already updated to the +// good info. +package test + +class Base(val x: Int) + +abstract class Middle(x: Int) extends Base(x) { self: Sub => + + def f(y: Int): Int = x + y + +} + +class Sub extends Middle(2) { + + override def f(x: Int) = x + +} + diff --git a/tests/pos/sets.scala b/tests/pos-special/sets.scala similarity index 100% rename from tests/pos/sets.scala rename to tests/pos-special/sets.scala diff --git a/tests/pos/collections.scala b/tests/pos/collections.scala index 08c3010c8605..5edcff986aab 100644 --- a/tests/pos/collections.scala +++ b/tests/pos/collections.scala @@ -10,7 +10,7 @@ object collections { val s = Set(1, 2, 3) val ss = s map (_ + 1) - val cbf: CanBuildFrom[List, Int, List[Int]] = scala.collection.immutable.List.canBuildFrom + val cbf: CanBuildFrom[List[_], Int, List[Int]] = scala.collection.immutable.List.canBuildFrom val nil = Nil val ints1 = 1 :: Nil diff --git a/tests/pos/desugar.scala b/tests/pos/desugar.scala index 0d3b6d8ca624..cc679498578f 100644 --- a/tests/pos/desugar.scala +++ b/tests/pos/desugar.scala @@ -72,7 +72,7 @@ object desugar { object misc { 'hello s"this is a $x + ${x + y} string" - type ~ = Tuple2 + type ~[X, Y] = Tuple2[X, Y] val pair: Int ~ String = 1 -> "abc" def foo(xs: Int*) = xs.length foo(list: _*) diff --git a/tests/pos/overrideDataRace.scala b/tests/pos/overrideDataRace.scala new file mode 100644 index 000000000000..830ffd668749 --- /dev/null +++ b/tests/pos/overrideDataRace.scala @@ -0,0 +1,13 @@ +package test + +trait Traversable { + def mkString: String = ??? +} + +trait ViewMkString { + self: Traversable => + + def mkString: String = mkString("") + def mkString(s: String) = ??? + +} diff --git a/tests/pos/paramcycle.scala b/tests/pos/paramcycle.scala new file mode 100644 index 000000000000..d894fdf126dc --- /dev/null +++ b/tests/pos/paramcycle.scala @@ -0,0 +1,18 @@ +import scala.collection._ +import scala.collection.generic._ + +trait ViewMkString[+A] + +trait TraversableViewLike[+A, + +Coll, + +This <: TraversableView[A, Coll] with TraversableViewLike[A, Coll, This]] + extends Traversable[A] with TraversableLike[A, This] with ViewMkString[A] { self => + + def f[B](pf: PartialFunction[A, B]) = + filter(pf.isDefinedAt).map(pf) + +} + +trait TraversableView[+A, +Coll] extends TraversableViewLike[A, Coll, TraversableView[A, Coll]] { } + + diff --git a/tests/pos/partialApplications.scala b/tests/pos/partialApplications.scala index 696c544e78eb..fae6849fc3fc 100644 --- a/tests/pos/partialApplications.scala +++ b/tests/pos/partialApplications.scala @@ -27,20 +27,3 @@ object Test { val ss: RMap[Float, Int] = rs } - -object Test2 { - type Histogram = Map[_, Int] - - type StringlyHistogram = Histogram[_ >: String] - - val xs: Histogram[String] = Map[String, Int]() - - val ys: StringlyHistogram[String] = xs - - val zs: StringlyHistogram = xs - - val xs1 = xs - val ys1 = ys - val zs1 = zs - -} diff --git a/tests/pos/t2693.scala b/tests/pos/t2693.scala index 5d4d0380c418..537e6d8abdf9 100644 --- a/tests/pos/t2693.scala +++ b/tests/pos/t2693.scala @@ -1,6 +1,6 @@ class A { - trait T[A] + trait Tr[A] def usetHk[T[_], A](ta: T[A]) = 0 - usetHk(new T[Int]{}: T[Int]) - usetHk(new T[Int]{}) // fails with: found: java.lang.Object with T[Int], required: ?T[ ?A ] + usetHk(new Tr[Int]{}: Tr[Int]) + usetHk(new Tr[Int]{}) // fails with: found: java.lang.Object with T[Int], required: ?T[ ?A ] }