diff options
18 files changed, 420 insertions, 148 deletions
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/opt/Inliner.scala b/src/compiler/scala/tools/nsc/backend/jvm/opt/Inliner.scala index b7523bbf06..64638ca34d 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/opt/Inliner.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/Inliner.scala @@ -142,6 +142,7 @@ class Inliner[BT <: BTypes](val btypes: BT) { elided += r else result += r + () } result.toList } diff --git a/src/compiler/scala/tools/nsc/classpath/DirectoryClassPath.scala b/src/compiler/scala/tools/nsc/classpath/DirectoryClassPath.scala index aba941e043..c4be59d7eb 100644 --- a/src/compiler/scala/tools/nsc/classpath/DirectoryClassPath.scala +++ b/src/compiler/scala/tools/nsc/classpath/DirectoryClassPath.scala @@ -5,6 +5,9 @@ package scala.tools.nsc.classpath import java.io.File import java.net.URL +import java.util +import java.util.Comparator + import scala.reflect.io.{AbstractFile, PlainFile} import scala.tools.nsc.util.{ClassPath, ClassRepresentation} import FileUtils._ @@ -87,9 +90,24 @@ trait JFileDirectoryLookup[FileEntryType <: ClassRepresentation] extends Directo if (packageDir.exists && packageDir.isDirectory) Some(packageDir) else None } - protected def listChildren(dir: File, filter: Option[File => Boolean]): Array[File] = filter match { - case Some(f) => dir.listFiles(mkFileFilter(f)) - case None => dir.listFiles() + protected def listChildren(dir: File, filter: Option[File => Boolean]): Array[File] = { + val listing = filter match { + case Some(f) => dir.listFiles(mkFileFilter(f)) + case None => dir.listFiles() + } + + // Sort by file name for stable order of directory .class entries in package scope. + // This gives stable results ordering of base type sequences for unrelated classes + // with the same base type depth. + // + // Notably, this will stably infer`Product with Serializable` + // as the type of `case class C(); case class D(); List(C(), D()).head`, rather than the opposite order. + // On Mac, the HFS performs this sorting transparently, but on Linux the order is unspecified. + // + // Note this behaviour can be enabled with in javac with `javac -XDsortfiles`, but that's only + // intended to improve determinism of the compiler for compiler hackers. + util.Arrays.sort(listing, (o1: File, o2: File) => o1.getName.compareTo(o2.getName)) + listing } protected def getName(f: File): String = f.getName protected def toAbstractFile(f: File): AbstractFile = new PlainFile(new scala.reflect.io.File(f)) diff --git a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala index 99ef4ed373..e0b64a7600 100644 --- a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala +++ b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala @@ -321,13 +321,20 @@ trait MethodSynthesis { // starts compiling (instead of failing like it's supposed to) because the typer // expects to be able to identify escaping locals in typedDefDef, and fails to // spot that brand of them. In other words it's an artifact of the implementation. + // + // JZ: ... or we could go back to uniformly using explicit result types in all cases + // if we fix `dropExistential`. More details https://github.com/scala/scala-dev/issues/165 val getterTp = derivedSym.tpe_*.finalResultType - val tpt = getterTp.widen match { - // Range position errors ensue if we don't duplicate this in some - // circumstances (at least: concrete vals with existential types.) - case _: ExistentialType => TypeTree() setOriginal (tree.tpt.duplicate setPos tree.tpt.pos.focus) - case _ if tree.mods.isDeferred => TypeTree() setOriginal tree.tpt // keep type tree of original abstract field - case _ => TypeTree(getterTp) + // Range position errors ensue if we don't duplicate this in some + // circumstances (at least: concrete vals with existential types.) + def inferredTpt = TypeTree() setOriginal (tree.tpt.duplicate setPos tree.tpt.pos.focus) + val tpt = getterTp match { + case _: ExistentialType => inferredTpt + case _ => getterTp.widen match { + case _: ExistentialType => inferredTpt + case _ if tree.mods.isDeferred => TypeTree() setOriginal tree.tpt // keep type tree of original abstract field + case _ => TypeTree(getterTp) + } } tpt setPos tree.tpt.pos.focus } diff --git a/src/reflect/scala/reflect/internal/BaseTypeSeqs.scala b/src/reflect/scala/reflect/internal/BaseTypeSeqs.scala index 81281b5eb4..78f9721713 100644 --- a/src/reflect/scala/reflect/internal/BaseTypeSeqs.scala +++ b/src/reflect/scala/reflect/internal/BaseTypeSeqs.scala @@ -56,42 +56,44 @@ trait BaseTypeSeqs { if(pending contains i) { pending.clear() throw CyclicInheritance - } else - elems(i) match { - case rtp @ RefinedType(variants, decls) => - // can't assert decls.isEmpty; see t0764 - //if (!decls.isEmpty) abort("computing closure of "+this+":"+this.isInstanceOf[RefinedType]+"/"+closureCache(j)) - //Console.println("compute closure of "+this+" => glb("+variants+")") - pending += i - try { - mergePrefixAndArgs(variants, Variance.Contravariant, lubDepth(variants)) match { - case NoType => typeError("no common type instance of base types "+(variants mkString ", and ")+" exists.") - case tp0 => - pending(i) = false - elems(i) = tp0 - tp0 - } - } - catch { - case CyclicInheritance => - typeError( - "computing the common type instance of base types "+(variants mkString ", and ")+" leads to a cycle.") + } else { + def computeLazyType(rtp: RefinedType): Type = { + if (!isIntersectionTypeForLazyBaseType(rtp)) + devWarning("unexpected RefinedType in base type seq, lazy BTS elements should be created via intersectionTypeForLazyBaseType: " + rtp) + val variants = rtp.parents + // can't assert decls.isEmpty; see t0764 + //if (!decls.isEmpty) abort("computing closure of "+this+":"+this.isInstanceOf[RefinedType]+"/"+closureCache(j)) + //Console.println("compute closure of "+this+" => glb("+variants+")") + pending += i + try { + mergePrefixAndArgs(variants, Variance.Contravariant, lubDepth(variants)) match { + case NoType => typeError("no common type instance of base types " + (variants mkString ", and ") + " exists.") + case tp0 => + pending(i) = false + elems(i) = tp0 + tp0 } + } + catch { + case CyclicInheritance => + typeError( + "computing the common type instance of base types " + (variants mkString ", and ") + " leads to a cycle.") + } + } + elems(i) match { + case rtp@RefinedType(variants, decls) => + computeLazyType(rtp) + case et @ ExistentialType(quantified, rtp: RefinedType) => + existentialAbstraction(quantified, computeLazyType(rtp)) case tp => tp } + } def rawElem(i: Int) = elems(i) - /** The type symbol of the type at i'th position in this sequence; - * no evaluation needed. - */ - def typeSymbol(i: Int): Symbol = { - elems(i) match { - case RefinedType(v :: vs, _) => v.typeSymbol - case tp => tp.typeSymbol - } - } + /** The type symbol of the type at i'th position in this sequence */ + def typeSymbol(i: Int): Symbol = elems(i).typeSymbol /** Return all evaluated types in this sequence as a list */ def toList: List[Type] = elems.toList @@ -215,7 +217,7 @@ trait BaseTypeSeqs { } i += 1 } - buf += intersectionType(minTypes) + buf += intersectionTypeForLazyBaseType(minTypes) // TODO this reverses the order. Does this matter? Or should this be minTypes.reverse? btsSize += 1 } } diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala index 7dda805378..f8679616d1 100644 --- a/src/reflect/scala/reflect/internal/Types.scala +++ b/src/reflect/scala/reflect/internal/Types.scala @@ -172,11 +172,16 @@ trait Types trait RewrappingTypeProxy extends SimpleTypeProxy { protected def maybeRewrap(newtp: Type) = ( if (newtp eq underlying) this - // BoundedWildcardTypes reach here during erroneous compilation: neg/t6258 - // Higher-kinded exclusion is because [x]CC[x] compares =:= to CC: pos/t3800 - // Otherwise, if newtp =:= underlying, don't rewrap it. - else if (!newtp.isWildcard && !newtp.isHigherKinded && (newtp =:= underlying)) this - else rewrap(newtp) + else { + // - BoundedWildcardTypes reach here during erroneous compilation: neg/t6258 + // - Higher-kinded exclusion is because [x]CC[x] compares =:= to CC: pos/t3800 + // - Avoid reusing the existing Wrapped(RefinedType) when we've be asked to wrap an =:= RefinementTypeRef, the + // distinction is important in base type sequences. See TypesTest.testExistentialRefinement + // - Otherwise, if newtp =:= underlying, don't rewrap it. + val hasSpecialMeaningBeyond_=:= = newtp.isWildcard || newtp.isHigherKinded || newtp.isInstanceOf[RefinementTypeRef] + if (!hasSpecialMeaningBeyond_=:= && (newtp =:= underlying)) this + else rewrap(newtp) + } ) protected def rewrap(newtp: Type): Type @@ -1589,13 +1594,11 @@ trait Types */ case class RefinedType(override val parents: List[Type], override val decls: Scope) extends CompoundType with RefinedTypeApi { - override def isHigherKinded = ( parents.nonEmpty && (parents forall typeIsHigherKinded) && !phase.erasedTypes ) - override def typeParams = if (isHigherKinded) firstParent.typeParams else super.typeParams @@ -1897,7 +1900,7 @@ trait Types require(sym.isRefinementClass, sym) // I think this is okay, but see #1241 (r12414), #2208, and typedTypeConstructor in Typers - override protected def normalizeImpl: Type = sym.info.normalize + override protected def normalizeImpl: Type = pre.memberInfo(sym).normalize override protected def finishPrefix(rest: String) = "" + sym.info } @@ -2704,6 +2707,7 @@ trait Types def isRepresentableWithWildcards = { val qset = quantified.toSet underlying match { + case _: RefinementTypeRef => false case TypeRef(pre, sym, args) => def isQuantified(tpe: Type): Boolean = { (tpe exists (t => qset contains t.typeSymbol)) || @@ -3521,7 +3525,9 @@ trait Types if ((parents eq original.parents) && (decls eq original.decls)) original else { val owner = original.typeSymbol.owner - val result = refinedType(parents, owner) + val result = + if (isIntersectionTypeForLazyBaseType(original)) intersectionTypeForLazyBaseType(parents) + else refinedType(parents, owner) val syms1 = decls.toList for (sym <- syms1) result.decls.enter(sym.cloneSymbol(result.typeSymbol)) @@ -3596,6 +3602,14 @@ trait Types case tp :: Nil => tp case _ => refinedType(tps, commonOwner(tps)) } + def intersectionTypeForLazyBaseType(tps: List[Type]) = tps match { + case tp :: Nil => tp + case _ => RefinedType(tps, newScope, tps.head.typeSymbolDirect) + } + def isIntersectionTypeForLazyBaseType(tp: RefinedType) = tp.parents match { + case head :: _ => tp.typeSymbolDirect eq head.typeSymbolDirect + case _ => false + } /**** This implementation to merge parents was checked in in commented-out form and has languished unaltered for five years. I think we should @@ -4403,89 +4417,120 @@ trait Types finally foreach2(tvs, saved)(_.suspended = _) } + final def stripExistentialsAndTypeVars(ts: List[Type], expandLazyBaseType: Boolean = false): (List[Type], List[Symbol]) = { + val needsStripping = ts.exists { + case _: RefinedType | _: TypeVar | _: ExistentialType => true + case _ => false + } + if (!needsStripping) (ts, Nil) // fast path for common case + else { + val tparams = mutable.ListBuffer[Symbol]() + val stripped = mutable.ListBuffer[Type]() + def stripType(tp: Type): Unit = tp match { + case rt: RefinedType if isIntersectionTypeForLazyBaseType(rt) => + if (expandLazyBaseType) + rt.parents foreach stripType + else { + devWarning(s"Unexpected RefinedType in stripExistentialsAndTypeVars $ts, not expanding") + stripped += tp + } + case ExistentialType(qs, underlying) => + tparams ++= qs + stripType(underlying) + case tv@TypeVar(_, constr) => + if (tv.instValid) stripType(constr.inst) + else if (tv.untouchable) stripped += tv + else abort("trying to do lub/glb of typevar " + tv) + case tp => stripped += tp + } + ts foreach stripType + (stripped.toList, tparams.toList) + } + } + /** Compute lub (if `variance == Covariant`) or glb (if `variance == Contravariant`) of given list * of types `tps`. All types in `tps` are typerefs or singletypes * with the same symbol. * Return `x` if the computation succeeds with result `x`. * Return `NoType` if the computation fails. */ - def mergePrefixAndArgs(tps: List[Type], variance: Variance, depth: Depth): Type = tps match { - case tp :: Nil => tp - case TypeRef(_, sym, _) :: rest => - val pres = tps map (_.prefix) // prefix normalizes automatically + def mergePrefixAndArgs(tps0: List[Type], variance: Variance, depth: Depth): Type = { + val (tps, tparams) = stripExistentialsAndTypeVars(tps0, expandLazyBaseType = true) + + val merged = tps match { + case tp :: Nil => tp + case TypeRef(_, sym, _) :: rest => + val pres = tps map (_.prefix) // prefix normalizes automatically val pre = if (variance.isPositive) lub(pres, depth) else glb(pres, depth) - val argss = tps map (_.normalize.typeArgs) // symbol equality (of the tp in tps) was checked using typeSymbol, which normalizes, so should normalize before retrieving arguments + val argss = tps map (_.normalize.typeArgs) // symbol equality (of the tp in tps) was checked using typeSymbol, which normalizes, so should normalize before retrieving arguments val capturedParams = new ListBuffer[Symbol] - try { - if (sym == ArrayClass && phase.erasedTypes) { - // special treatment for lubs of array types after erasure: - // if argss contain one value type and some other type, the lub is Object - // if argss contain several reference types, the lub is an array over lub of argtypes - if (argss exists typeListIsEmpty) { - NoType // something is wrong: an array without a type arg. - } - else { - val args = argss map (_.head) - if (args.tail forall (_ =:= args.head)) typeRef(pre, sym, List(args.head)) - else if (args exists (arg => isPrimitiveValueClass(arg.typeSymbol))) ObjectTpe - else typeRef(pre, sym, List(lub(args))) + try { + if (sym == ArrayClass && phase.erasedTypes) { + // special treatment for lubs of array types after erasure: + // if argss contain one value type and some other type, the lub is Object + // if argss contain several reference types, the lub is an array over lub of argtypes + if (argss exists typeListIsEmpty) { + NoType // something is wrong: an array without a type arg. + } + else { + val args = argss map (_.head) + if (args.tail forall (_ =:= args.head)) typeRef(pre, sym, List(args.head)) + else if (args exists (arg => isPrimitiveValueClass(arg.typeSymbol))) ObjectTpe + else typeRef(pre, sym, List(lub(args))) + } } - } - else transposeSafe(argss) match { - case None => - // transpose freaked out because of irregular argss - // catching just in case (shouldn't happen, but also doesn't cost us) - // [JZ] It happens: see SI-5683. - debuglog(s"transposed irregular matrix!? tps=$tps argss=$argss") - NoType - case Some(argsst) => - val args = map2(sym.typeParams, argsst) { (tparam, as0) => - val as = as0.distinct - if (as.size == 1) as.head - else if (depth.isZero) { - log("Giving up merging args: can't unify %s under %s".format(as.mkString(", "), tparam.fullLocationString)) - // Don't return "Any" (or "Nothing") when we have to give up due to - // recursion depth. Return NoType, which prevents us from poisoning - // lublist's results. It can recognize the recursion and deal with it, but - // only if we aren't returning invalid types. - NoType - } - else { - if (tparam.variance == variance) lub(as, depth.decr) - else if (tparam.variance == variance.flip) glb(as, depth.decr) + else transposeSafe(argss) match { + case None => + // transpose freaked out because of irregular argss + // catching just in case (shouldn't happen, but also doesn't cost us) + // [JZ] It happens: see SI-5683. + debuglog(s"transposed irregular matrix!? tps=$tps argss=$argss") + NoType + case Some(argsst) => + val args = map2(sym.typeParams, argsst) { (tparam, as0) => + val as = as0.distinct + if (as.size == 1) as.head + else if (depth.isZero) { + log("Giving up merging args: can't unify %s under %s".format(as.mkString(", "), tparam.fullLocationString)) + // Don't return "Any" (or "Nothing") when we have to give up due to + // recursion depth. Return NoType, which prevents us from poisoning + // lublist's results. It can recognize the recursion and deal with it, but + // only if we aren't returning invalid types. + NoType + } else { - val l = lub(as, depth.decr) - val g = glb(as, depth.decr) - if (l <:< g) l - else { // Martin: I removed this, because incomplete. Not sure there is a good way to fix it. For the moment we - // just err on the conservative side, i.e. with a bound that is too high. - // if(!(tparam.info.bounds contains tparam)) //@M can't deal with f-bounds, see #2251 - - val qvar = commonOwner(as) freshExistential "" setInfo TypeBounds(g, l) - capturedParams += qvar - qvar.tpe + if (tparam.variance == variance) lub(as, depth.decr) + else if (tparam.variance == variance.flip) glb(as, depth.decr) + else { + val l = lub(as, depth.decr) + val g = glb(as, depth.decr) + if (l <:< g) l + else { // Martin: I removed this, because incomplete. Not sure there is a good way to fix it. For the moment we + // just err on the conservative side, i.e. with a bound that is too high. + // if(!(tparam.info.bounds contains tparam)) //@M can't deal with f-bounds, see #2251 + + val qvar = commonOwner(as) freshExistential "" setInfo TypeBounds(g, l) + capturedParams += qvar + qvar.tpe + } } } } - } - if (args contains NoType) NoType - else existentialAbstraction(capturedParams.toList, typeRef(pre, sym, args)) + if (args contains NoType) NoType + else existentialAbstraction(capturedParams.toList, typeRef(pre, sym, args)) + } + } catch { + case ex: MalformedType => NoType } - } catch { - case ex: MalformedType => NoType - } - case SingleType(_, sym) :: rest => - val pres = tps map (_.prefix) - val pre = if (variance.isPositive) lub(pres, depth) else glb(pres, depth) - try singleType(pre, sym) - catch { case ex: MalformedType => NoType } - case ExistentialType(tparams, quantified) :: rest => - mergePrefixAndArgs(quantified :: rest, variance, depth) match { - case NoType => NoType - case tpe => existentialAbstraction(tparams, tpe) - } - case _ => - abort(s"mergePrefixAndArgs($tps, $variance, $depth): unsupported tps") + case SingleType(_, sym) :: rest => + val pres = tps map (_.prefix) + val pre = if (variance.isPositive) lub(pres, depth) else glb(pres, depth) + try singleType(pre, sym) + catch { case ex: MalformedType => NoType } + case _ => + abort(s"mergePrefixAndArgs($tps, $variance, $depth): unsupported tps") + } + existentialAbstraction(tparams, merged) } def addMember(thistp: Type, tp: Type, sym: Symbol): Unit = addMember(thistp, tp, sym, AnyDepth) diff --git a/src/reflect/scala/reflect/internal/tpe/GlbLubs.scala b/src/reflect/scala/reflect/internal/tpe/GlbLubs.scala index 123b44aa05..108ce45cca 100644 --- a/src/reflect/scala/reflect/internal/tpe/GlbLubs.scala +++ b/src/reflect/scala/reflect/internal/tpe/GlbLubs.scala @@ -118,7 +118,7 @@ private[internal] trait GlbLubs { // ts0 is the 1-dimensional frontier of symbols cutting through 2-dimensional tsBts. // Invariant: all symbols "under" (closer to the first row) the frontier // are smaller (according to _.isLess) than the ones "on and beyond" the frontier - val ts0 = tsBts map (_.head) + val ts0 = tsBts map (_.head) // Is the frontier made up of types with the same symbol? val isUniformFrontier = (ts0: @unchecked) match { @@ -210,24 +210,6 @@ private[internal] trait GlbLubs { } } - private def stripExistentialsAndTypeVars(ts: List[Type]): (List[Type], List[Symbol]) = { - val quantified = ts flatMap { - case ExistentialType(qs, _) => qs - case t => List() - } - def stripType(tp: Type): Type = tp match { - case ExistentialType(_, res) => - res - case tv@TypeVar(_, constr) => - if (tv.instValid) stripType(constr.inst) - else if (tv.untouchable) tv - else abort("trying to do lub/glb of typevar "+tp) - case t => t - } - val strippedTypes = ts mapConserve stripType - (strippedTypes, quantified) - } - /** Does this set of types have the same weak lub as * it does regular lub? This is exposed so lub callers * can discover whether the trees they are typing will diff --git a/src/reflect/scala/reflect/internal/tpe/TypeMaps.scala b/src/reflect/scala/reflect/internal/tpe/TypeMaps.scala index c3f92f1bce..ba4f2bec4b 100644 --- a/src/reflect/scala/reflect/internal/tpe/TypeMaps.scala +++ b/src/reflect/scala/reflect/internal/tpe/TypeMaps.scala @@ -610,11 +610,26 @@ private[internal] trait TypeMaps { } // Does the candidate symbol match the given prefix and class? - // Since pre may be something like ThisType(A) where trait A { self: B => }, - // we have to test the typeSymbol of the widened type, not pre.typeSymbol, or - // B will not be considered. - private def matchesPrefixAndClass(pre: Type, clazz: Symbol)(candidate: Symbol) = - (clazz == candidate) && (pre.widen.typeSymbol isSubClass clazz) + private def matchesPrefixAndClass(pre: Type, clazz: Symbol)(candidate: Symbol) = (clazz == candidate) && { + val pre1 = pre match { + case tv: TypeVar => + // Needed with existentials in prefixes, e.g. test/files/pos/typevar-in-prefix.scala + // Perhaps the base type sequence of a type var should include its bounds? + tv.origin + case _ => pre + } + // widen needed (at least) because of https://github.com/scala/scala-dev/issues/166 + ( + if (clazz.isRefinementClass) + // base type seqs of aliases over refinement types have copied refinement types based on beta reduction + // for reliable lookup we need to consult the base type of the type symbol. (example: pos/t8177b.scala) + pre1.widen.typeSymbol isSubClass clazz + else + // In the general case, we look at the base type sequence of the prefix itself, + // which can have more concrete base classes than `.typeSymbol.baseClasses` (example: t5294, t6161) + pre1.widen.baseTypeIndex(clazz) != -1 + ) + } // Whether the annotation tree currently being mapped over has had a This(_) node rewritten. private[this] var wroteAnnotation = false @@ -1012,6 +1027,9 @@ private[internal] trait TypeMaps { case _ => tp.normalize match { case TypeRef(_, sym1, _) if (sym == sym1) => result = true + case refined: RefinedType => + mapOver(tp.prefix) + mapOver(refined) case SingleType(_, sym1) if (sym == sym1) => result = true case _ => mapOver(tp) } diff --git a/test/files/neg/lub-from-hell-2.check b/test/files/neg/lub-from-hell-2.check new file mode 100644 index 0000000000..3ef935f93b --- /dev/null +++ b/test/files/neg/lub-from-hell-2.check @@ -0,0 +1,7 @@ +lub-from-hell-2.scala:3: error: type arguments [Any,Iterable[Any] with Int => Any with scala.collection.generic.Subtractable[Any,Iterable[Any] with Int => Any with scala.collection.generic.Subtractable[Any,Iterable[Any] with Int => Any]{def seq: Iterable[Any] with Int => Any}]{def seq: Iterable[Any] with Int => Any{def seq: Iterable[Any] with Int => Any}}] do not conform to trait Subtractable's type parameter bounds [A,+Repr <: scala.collection.generic.Subtractable[A,Repr]] + def foo(a: Boolean, b: collection.mutable.Set[Any], c: collection.mutable.ListBuffer[Any]) = if (a) b else c + ^ +lub-from-hell-2.scala:4: error: type arguments [Any,scala.collection.mutable.Iterable[Any] with scala.collection.mutable.Cloneable[scala.collection.mutable.Iterable[Any] with scala.collection.mutable.Cloneable[scala.collection.mutable.Iterable[Any] with Cloneable with Int => Any] with Int => Any{def seq: scala.collection.mutable.Iterable[Any] with Cloneable with Int => Any}] with scala.collection.generic.Growable[Any] with Int => Any with scala.collection.generic.Shrinkable[Any] with scala.collection.generic.Subtractable[Any,Iterable[Any] with Int => Any with scala.collection.generic.Subtractable[Any,Iterable[Any] with Int => Any]{def seq: Iterable[Any] with Int => Any}] with scala.collection.script.Scriptable[Any]] do not conform to trait Subtractable's type parameter bounds [A,+Repr <: scala.collection.generic.Subtractable[A,Repr]] + def bar(a: Boolean, b: scala.collection.mutable.SetLike[Any,scala.collection.mutable.Set[Any]], c: scala.collection.mutable.Buffer[Any]) = if (a) b else c + ^ +two errors found diff --git a/test/files/neg/lub-from-hell-2.scala b/test/files/neg/lub-from-hell-2.scala new file mode 100644 index 0000000000..18c99dfada --- /dev/null +++ b/test/files/neg/lub-from-hell-2.scala @@ -0,0 +1,13 @@ +class Test { + trait Tree + def foo(a: Boolean, b: collection.mutable.Set[Any], c: collection.mutable.ListBuffer[Any]) = if (a) b else c + def bar(a: Boolean, b: scala.collection.mutable.SetLike[Any,scala.collection.mutable.Set[Any]], c: scala.collection.mutable.Buffer[Any]) = if (a) b else c + // bar produces an ill-bounded LUB in 2.11.8. After this commit, which fixes a bug in existential+refinement lubs, foo also fails. +} +// This test case minimizes a case that stated to fail compile after my fixes in SI-5294. +// `foo` used to compile for the wrong reason, `mergePrefixAndArgs` failed to transpose a +// ragged matrix and skipped to the next level of the base type sequences to find a common type symbol. +// +// My changes fixed the root cause of the ragged matrix, which uncovered the latent bug. +// For comparison, `bar` failed to compile before _and_ after my changes for the same reason: +// f-bounded types involved in LUBs can sometimes produce an ill-bounded LUB. diff --git a/test/files/pos/lub-from-hell.scala b/test/files/pos/lub-from-hell.scala new file mode 100644 index 0000000000..cb4b1733c7 --- /dev/null +++ b/test/files/pos/lub-from-hell.scala @@ -0,0 +1,6 @@ +class Test { + trait Tree + def foo(b: Boolean, buf: collection.mutable.ArrayBuffer[Any], acc: StringBuilder) = if (b) buf else acc +} +// This test case minimizes a case that failed to compile due to a bug in my work on +// SI-5294. After refining my patches, it compiles again, as expected.
\ No newline at end of file diff --git a/test/files/pos/t5294b.scala b/test/files/pos/t5294b.scala new file mode 100644 index 0000000000..038d2ff806 --- /dev/null +++ b/test/files/pos/t5294b.scala @@ -0,0 +1,36 @@ +class Test { + def test = { + + val l1 = null: Int #: String #: Boolean #: String #: HNil.type + + type _2 = Succ[Succ[Zero.type]] + + val t1: Boolean = null.asInstanceOf[ l1.type#Drop[_2]#Head ] + + val t2: Boolean = null.asInstanceOf[ l1.type#Apply[_2] ] + } +} + + +sealed trait Nat { + type Fold[U, F[_ <: U] <: U, Z <: U] <: U +} + +final object Zero extends Nat { + type Fold[U, F[_ <: U] <: U, Z <: U] = Z +} + +final class Succ[N <: Nat] extends Nat { + type Fold[U, F[_ <: U] <: U, Z <: U] = F[N#Fold[U, F, Z]] +} + +trait HList { + type Head + type Tail <: HList + type Drop[N <: Nat] = N#Fold[HList, ({ type L[X <: HList] = X#Tail })#L, this.type] + type Apply[N <: Nat] = Drop[N]#Head +} + +class #: [H, T <: HList] extends HList { type Head = H; type Tail = T } + +object HNil extends HList { type Head = Nothing; type Tail = Nothing } diff --git a/test/files/pos/t5294c.scala b/test/files/pos/t5294c.scala new file mode 100644 index 0000000000..2709098988 --- /dev/null +++ b/test/files/pos/t5294c.scala @@ -0,0 +1,30 @@ +sealed trait Nat { + type IsZero[U, A <: U, B <: U] <: U +} + +final object Zero extends Nat { + type IsZero[U, T <: U, F <: U] = T +} + +final class Succ[N <: Nat] extends Nat { + type IsZero[U, T <: U, F <: U] = F +} + +trait HList { + type Head + type Tail <: HList + type Drop[N <: Nat] = N#IsZero[HList, this.type, Tail] + type Apply[N <: Nat] = Drop[N]#Head // typechecks as HList.this.Head +} + +object Test { + type ::[H, T <: HList] = HList { type Head = H; type Tail = T} + type HNil <: HList + type T = Int :: String :: HNil + + type U = T#Drop[Succ[Zero.type]]#Head + type V = T#Apply[Succ[Zero.type]] + var u: U = ??? + var v: V = ??? + u = v +} diff --git a/test/pending/pos/t6161.scala b/test/files/pos/t6161b.scala index 5783cc85f2..5783cc85f2 100644 --- a/test/pending/pos/t6161.scala +++ b/test/files/pos/t6161b.scala diff --git a/test/files/pos/typevar-in-prefix.scala b/test/files/pos/typevar-in-prefix.scala new file mode 100644 index 0000000000..929648b789 --- /dev/null +++ b/test/files/pos/typevar-in-prefix.scala @@ -0,0 +1,9 @@ +trait Test1 { + abstract class Setting + def Bool: Setting + + class C[T <: Setting](val s: T) + val setting1 = null.asInstanceOf[_1.s.type forSome { val _1: C[Setting] }] + // the derived accessor for this val was not using an inferred type, as was + // the intention of the implementation in MethodSynthesis. +} diff --git a/test/files/presentation/callcc-interpreter/Runner.scala b/test/files/presentation/callcc-interpreter/Runner.scala index a5698be5c2..1c03e3d5ba 100644 --- a/test/files/presentation/callcc-interpreter/Runner.scala +++ b/test/files/presentation/callcc-interpreter/Runner.scala @@ -1,6 +1,3 @@ import scala.tools.nsc.interactive.tests._ -object Test extends InteractiveTest { - // Normalize ordering of LUB - override def normalize(s: String) = s.replace("Serializable with Product", "Product with Serializable") -} +object Test extends InteractiveTest diff --git a/test/files/run/t5294.scala b/test/files/run/t5294.scala new file mode 100644 index 0000000000..2551ae89a6 --- /dev/null +++ b/test/files/run/t5294.scala @@ -0,0 +1,22 @@ +import scala.language.higherKinds + +package p { + trait T[+A, +CC] { + def t: CC + } + class C { + def test[CC[X] <: T[X,String] with T[X,Int]](from: CC[_]): Unit = () + } +} + +object Test { + def main(args: Array[String]): Unit = { + val symtab = reflect.runtime.universe.asInstanceOf[reflect.internal.SymbolTable] + val CTpe = reflect.runtime.universe.typeOf[p.C].asInstanceOf[symtab.Type] + val TClass = reflect.runtime.universe.symbolOf[p.T[_, _]].asInstanceOf[symtab.Symbol] + import symtab._ + val from = CTpe.member(TermName("test")).paramss.head.head + assert(from.baseClasses contains TClass) + assert(from.info.baseTypeIndex(TClass) != -1) // was failing! + } +} diff --git a/test/files/run/t7747-repl.scala b/test/files/run/t7747-repl.scala index 0094d3ba98..8203f4c802 100644 --- a/test/files/run/t7747-repl.scala +++ b/test/files/run/t7747-repl.scala @@ -10,9 +10,7 @@ object Test extends ReplTest { override def normalize(s: String) = { // replace indylambda function names by <function0> - val s2 = """\$Lambda.*""".r.replaceAllIn(s, "<function0>") - // Normalize ordering of LUB - s2.replace("Serializable with Product", "Product with Serializable") + """\$Lambda.*""".r.replaceAllIn(s, "<function0>") } def code = """ diff --git a/test/junit/scala/reflect/internal/TypesTest.scala b/test/junit/scala/reflect/internal/TypesTest.scala index 05a77cfb47..585493280b 100644 --- a/test/junit/scala/reflect/internal/TypesTest.scala +++ b/test/junit/scala/reflect/internal/TypesTest.scala @@ -58,4 +58,85 @@ class TypesTest { Assert.fail(xs.mkString("\n")) } } + + @Test + def testRefinementContains(): Unit = { + val refinement = typeOf[{def foo: Int}] + assert(refinement.isInstanceOf[RefinedType]) + assert(refinement.contains(IntClass)) + val elem0 = refinement.baseTypeSeq(0) + assert(elem0.isInstanceOf[RefinementTypeRef]) + assert(elem0.contains(IntClass)) + } + + @Test + def testRefinedLubs(): Unit = { + // https://github.com/scala/scala-dev/issues/168 + assertEquals(typeOf[Option[AnyVal]], lub(typeOf[Option[Int] with Option[Char]] :: typeOf[Option[Boolean] with Option[Short]] :: Nil)) + assertEquals(typeOf[Option[AnyVal]], lub(typeOf[Option[Int] with Option[Char]] :: typeOf[Option[Boolean]] :: Nil)) + assertEquals(typeOf[Option[AnyVal]], lub((typeOf[Option[Int] with Option[Char]] :: typeOf[Option[Boolean] with Option[Short]] :: Nil).reverse)) + assertEquals(typeOf[Option[AnyVal]], lub((typeOf[Option[Int] with Option[Char]] :: typeOf[Option[Boolean]] :: Nil).reverse)) + } + + @Test + def testExistentialRefinement(): Unit = { + import rootMirror.EmptyPackageClass + + // class M[A] + val MClass = EmptyPackageClass.newClass("M") + val A = MClass.newTypeParameter("A").setInfo(TypeBounds.empty) + MClass.setInfo(PolyType(A :: Nil, ClassInfoType(ObjectClass.tpeHK :: Nil, newScopeWith(), MClass))) + + // (M[Int] with M[X] { def m: Any }) forSome { type X } + val X = NoSymbol.newExistential("X").setInfo(TypeBounds.empty) + val T: Type = { + val decls = newScopeWith(MClass.newMethod("m").setInfo(NullaryMethodType(AnyClass.tpeHK))) + val refined = refinedType(appliedType(MClass, IntClass.tpeHK) :: appliedType(MClass, X.tpeHK) :: Nil, NoSymbol, decls, NoPosition) + newExistentialType(X :: Nil, refined) + } + + val RefinementClass = T.underlying.typeSymbol + assertTrue(RefinementClass.isRefinementClass) + TypeRef(NoPrefix, RefinementClass, Nil) match { + case rtr : RefinementTypeRef => + // ContainsCollector needs to look inside the info of symbols of RefinementTypeRefs + assert(rtr.contains(X)) + } + + val underlying = T.underlying + val baseTypeSeqIndices = T.baseTypeSeq.toList.indices + for (i <- baseTypeSeqIndices) { + // Elements of the existential type should have the same type symbol as underlying + assertEquals(T.baseTypeSeq.typeSymbol(i), underlying.baseTypeSeq.typeSymbol(i)) + } + + // Type symbols should be distinct + def checkDistinctTypeSyms(bts: BaseTypeSeq): Unit = { + val syms = baseTypeSeqIndices.map(T.baseTypeSeq.typeSymbol) + assertEquals(syms, syms.distinct) + } + checkDistinctTypeSyms(T.baseTypeSeq) + checkDistinctTypeSyms(T.underlying.baseTypeSeq) + + // This is the entry for the refinement class + assertTrue(T.baseTypeSeq.typeSymbol(0).isRefinementClass) + assertEquals("M[Int] with M[X]{def m: Any} forSome { type X }", T.baseTypeSeq.rawElem(0).toString) + + // This is the entry for M. The raw entry is an existential over a RefinedType which encodes a lazily computed base type + assertEquals(T.baseTypeSeq.typeSymbol(1), MClass) + assertEquals("M[X] with M[Int] forSome { type X }", T.baseTypeSeq.rawElem(1).toString) + // calling `apply` merges the prefix/args of the elements ot the RefinedType and rewraps in the existential + assertEquals("M[_1] forSome { type X; type _1 >: X with Int }", T.baseTypeSeq.apply(1).toString) + } + + @Test + def testExistentialMerge(): Unit = { + val ts = typeOf[Set[Any]] :: typeOf[Set[X] forSome { type X <: Y; type Y <: Int}] :: Nil + def merge(ts: List[Type]) = mergePrefixAndArgs(ts, Variance.Contravariant, lubDepth(ts)) + val merged1 = merge(ts) + val merged2 = merge(ts.reverse) + assert(ts.forall(_ <:< merged1)) // use to fail before fix to mergePrefixAndArgs for existentials + assert(ts.forall(_ <:< merged2)) + assert(merged1 =:= merged2) + } } |