diff options
Diffstat (limited to 'src')
7 files changed, 214 insertions, 141 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) } |