Skip to content

Commit ee51b6e

Browse files
committed
Merge remote-tracking branches 'adriaanm/ticket/5189' and 'jsuereth/better-starr-flow' into develop
3 parents 79e937b + 29bcade + 292a861 commit ee51b6e

File tree

16 files changed

+272
-70
lines changed

16 files changed

+272
-70
lines changed

build.xml

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2010,9 +2010,28 @@ STABLE REFERENCE (STARR)
20102010
<include name="forkjoin.jar"/>
20112011
</fileset>
20122012
</copy>
2013+
<!-- remove SHA1 files for no starr, so we don't loose artifacts. -->
2014+
<delete>
2015+
<fileset dir="${lib.dir}">
2016+
<include name="fjbg.jar.desired.sha1"/>
2017+
<include name="msil.jar.desired.sha1"/>
2018+
<include name="forkjoin.jar.desired.sha1"/>
2019+
</fileset>
2020+
</delete>
2021+
</target>
2022+
2023+
<target name="starr.removesha1" depends="starr.libs">
2024+
<!-- remove SHA1 files for no starr, so we don't loose artifacts. -->
2025+
<delete>
2026+
<fileset dir="${lib.dir}">
2027+
<include name="scala-compiler.jar.desired.sha1"/>
2028+
<include name="scala-library.jar.desired.sha1"/>
2029+
<include name="scala-library-src.jar.desired.sha1"/>
2030+
</fileset>
2031+
</delete>
20132032
</target>
20142033

2015-
<target name="starr.done" depends="starr.libs"/>
2034+
<target name="starr.done" depends="starr.libs, starr.removesha1"/>
20162035

20172036
<!-- ===========================================================================
20182037
FORWARDED TARGETS FOR PACKAGING

src/compiler/scala/reflect/internal/Symbols.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -269,9 +269,9 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
269269
/** Create a new existential type skolem with this symbol its owner,
270270
* based on the given symbol and origin.
271271
*/
272-
def newExistentialSkolem(basis: Symbol, origin: AnyRef): TypeSkolem = {
273-
val skolem = newTypeSkolemSymbol(basis.name.toTypeName, origin, basis.pos, (basis.flags | EXISTENTIAL) & ~PARAM)
274-
skolem setInfo (basis.info cloneInfo skolem)
272+
def newExistentialSkolem(basis: Symbol, origin: AnyRef, name: TypeName = null, info: Type = null): TypeSkolem = {
273+
val skolem = newTypeSkolemSymbol(if (name eq null) basis.name.toTypeName else name, origin, basis.pos, (basis.flags | EXISTENTIAL) & ~PARAM)
274+
skolem setInfo (if (info eq null) basis.info cloneInfo skolem else info)
275275
}
276276

277277
final def newExistential(name: TypeName, pos: Position = NoPosition, newFlags: Long = 0L): Symbol =

src/compiler/scala/tools/nsc/io/Pickler.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ object Pickler {
165165
def pkl[T: Pickler] = implicitly[Pickler[T]]
166166

167167
/** A class represenenting `~`-pairs */
168-
case class ~[S, T](fst: S, snd: T)
168+
case class ~[+S, +T](fst: S, snd: T)
169169

170170
/** A wrapper class to be able to use `~` s an infix method */
171171
class TildeDecorator[S](x: S) {

src/compiler/scala/tools/nsc/typechecker/Infer.scala

Lines changed: 62 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1051,49 +1051,74 @@ trait Infer {
10511051
* @param pt the expected result type of the instance
10521052
*/
10531053
def inferConstructorInstance(tree: Tree, undetparams: List[Symbol], pt0: Type) {
1054-
val pt = widen(pt0)
1055-
//println("infer constr inst "+tree+"/"+undetparams+"/"+pt0)
1056-
var restpe = tree.tpe.finalResultType
1057-
var tvars = undetparams map freshVar
1054+
val pt = widen(pt0)
1055+
val ptparams = freeTypeParamsOfTerms.collect(pt)
1056+
val ctorTp = tree.tpe
1057+
val resTp = ctorTp.finalResultType
10581058

1059-
/** Compute type arguments for undetermined params and substitute them in given tree.
1059+
debuglog("infer constr inst "+ tree +"/"+ undetparams +"/ pt= "+ pt +" pt0= "+ pt0 +" resTp: "+ resTp)
1060+
1061+
/** Compute type arguments for undetermined params
10601062
*/
1061-
def computeArgs =
1062-
try {
1063-
val targs = solvedTypes(tvars, undetparams, undetparams map varianceInType(restpe),
1064-
true, lubDepth(List(restpe, pt)))
1065-
// checkBounds(tree, NoPrefix, NoSymbol, undetparams, targs, "inferred ")
1066-
// no checkBounds here. If we enable it, test bug602 fails.
1067-
new TreeTypeSubstituter(undetparams, targs).traverse(tree)
1068-
} catch ifNoInstance{ msg =>
1069-
NoConstructorInstanceError(tree, restpe, pt, msg)
1063+
def inferFor(pt: Type): Option[List[Type]] = {
1064+
val tvars = undetparams map freshVar
1065+
val resTpV = resTp.instantiateTypeParams(undetparams, tvars)
1066+
1067+
if (resTpV <:< pt) {
1068+
try {
1069+
// debuglog("TVARS "+ (tvars map (_.constr)))
1070+
// look at the argument types of the primary constructor corresponding to the pattern
1071+
val variances = undetparams map varianceInType(ctorTp.paramTypes.headOption getOrElse ctorTp)
1072+
val targs = solvedTypes(tvars, undetparams, variances, true, lubDepth(List(resTp, pt)))
1073+
// checkBounds(tree, NoPrefix, NoSymbol, undetparams, targs, "inferred ")
1074+
// no checkBounds here. If we enable it, test bug602 fails.
1075+
// TODO: reinstate checkBounds, return params that fail to meet their bounds to undetparams
1076+
Some(targs)
1077+
} catch ifNoInstance { msg =>
1078+
debuglog("NO INST "+ (tvars, tvars map (_.constr)))
1079+
NoConstructorInstanceError(tree, resTp, pt, msg)
1080+
None
1081+
}
1082+
} else {
1083+
debuglog("not a subtype: "+ resTpV +" </:< "+ pt)
1084+
None
10701085
}
1071-
def instError = {
1072-
if (settings.debug.value) Console.println("ici " + tree + " " + undetparams + " " + pt)
1073-
if (settings.explaintypes.value) explainTypes(restpe.instantiateTypeParams(undetparams, tvars), pt)
1074-
ConstrInstantiationError(tree, restpe, pt)
10751086
}
1076-
if (restpe.instantiateTypeParams(undetparams, tvars) <:< pt) {
1077-
computeArgs
1078-
} else if (isFullyDefined(pt)) {
1079-
debuglog("infer constr " + tree + ":" + restpe + ", pt = " + pt)
1080-
var ptparams = freeTypeParamsOfTerms.collect(pt)
1081-
debuglog("free type params = " + ptparams)
1082-
val ptWithWildcards = pt.instantiateTypeParams(ptparams, ptparams map (ptparam => WildcardType))
1083-
tvars = undetparams map freshVar
1084-
if (restpe.instantiateTypeParams(undetparams, tvars) <:< ptWithWildcards) {
1085-
computeArgs
1086-
restpe = skipImplicit(tree.tpe.resultType)
1087-
debuglog("new tree = " + tree + ":" + restpe)
1088-
val ptvars = ptparams map freshVar
1089-
val pt1 = pt.instantiateTypeParams(ptparams, ptvars)
1090-
if (isPopulated(restpe, pt1)) {
1091-
ptvars foreach instantiateTypeVar
1092-
} else { if (settings.debug.value) Console.println("no instance: "); instError }
1093-
} else { if (settings.debug.value) Console.println("not a subtype " + restpe.instantiateTypeParams(undetparams, tvars) + " of " + ptWithWildcards); instError }
1094-
} else { if (settings.debug.value) Console.println("not fully defined: " + pt); instError }
1087+
1088+
def inferForApproxPt =
1089+
if (isFullyDefined(pt)) {
1090+
inferFor(pt.instantiateTypeParams(ptparams, ptparams map (x => WildcardType))) flatMap { targs =>
1091+
val ctorTpInst = tree.tpe.instantiateTypeParams(undetparams, targs)
1092+
val resTpInst = skipImplicit(ctorTpInst.finalResultType)
1093+
val ptvars =
1094+
ptparams map {
1095+
// since instantiateTypeVar wants to modify the skolem that corresponds to the method's type parameter,
1096+
// and it uses the TypeVar's origin to locate it, deskolemize the existential skolem to the method tparam skolem
1097+
// (the existential skolem was created by adaptConstrPattern to introduce the type slack necessary to soundly deal with variant type parameters)
1098+
case skolem if skolem.isExistentialSkolem => freshVar(skolem.deSkolemize.asInstanceOf[TypeSymbol])
1099+
case p => freshVar(p)
1100+
}
1101+
1102+
val ptV = pt.instantiateTypeParams(ptparams, ptvars)
1103+
1104+
if (isPopulated(resTpInst, ptV)) {
1105+
ptvars foreach instantiateTypeVar
1106+
debuglog("isPopulated "+ resTpInst +", "+ ptV +" vars= "+ ptvars)
1107+
Some(targs)
1108+
} else None
1109+
}
1110+
} else None
1111+
1112+
(inferFor(pt) orElse inferForApproxPt) map { targs =>
1113+
new TreeTypeSubstituter(undetparams, targs).traverse(tree)
1114+
} getOrElse {
1115+
debugwarn("failed inferConstructorInstance for "+ tree +" : "+ tree.tpe +" under "+ undetparams +" pt = "+ pt +(if(isFullyDefined(pt)) " (fully defined)" else " (not fully defined)"))
1116+
// if (settings.explaintypes.value) explainTypes(resTp.instantiateTypeParams(undetparams, tvars), pt)
1117+
ConstrInstantiationError(tree, resTp, pt)
1118+
}
10951119
}
10961120

1121+
10971122
def instBounds(tvar: TypeVar): (Type, Type) = {
10981123
val tparam = tvar.origin.typeSymbol
10991124
val instType = toOrigin(tvar.constr.inst)

src/compiler/scala/tools/nsc/typechecker/Typers.scala

Lines changed: 79 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -864,6 +864,33 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
864864
}
865865
}
866866

867+
/**
868+
* To deal with the type slack between actual (run-time) types and statically known types, for each abstract type T,
869+
* reflect its variance as a skolem that is upper-bounded by T (covariant position), or lower-bounded by T (contravariant).
870+
*
871+
* Consider the following example:
872+
*
873+
* class AbsWrapperCov[+A]
874+
* case class Wrapper[B](x: Wrapped[B]) extends AbsWrapperCov[B]
875+
*
876+
* def unwrap[T](x: AbsWrapperCov[T]): Wrapped[T] = x match {
877+
* case Wrapper(wrapped) => // Wrapper's type parameter must not be assumed to be equal to T, it's *upper-bounded* by it
878+
* wrapped // : Wrapped[_ <: T]
879+
* }
880+
*
881+
* this method should type check if and only if Wrapped is covariant in its type parameter
882+
*
883+
* when inferring Wrapper's type parameter B from x's type AbsWrapperCov[T],
884+
* we must take into account that x's actual type is AbsWrapperCov[Tactual] forSome {type Tactual <: T}
885+
* as AbsWrapperCov is covariant in A -- in other words, we must not assume we know T exactly, all we know is its upper bound
886+
*
887+
* since method application is the only way to generate this slack between run-time and compile-time types (TODO: right!?),
888+
* we can simply replace skolems that represent method type parameters as seen from the method's body
889+
* by other skolems that are (upper/lower)-bounded by that type-parameter skolem
890+
* (depending on the variance position of the skolem in the statically assumed type of the scrutinee, pt)
891+
*
892+
* see test/files/../t5189*.scala
893+
*/
867894
def adaptConstrPattern(): Tree = { // (5)
868895
val extractor = tree.symbol.filter(sym => reallyExists(unapplyMember(sym.tpe)))
869896
if (extractor != NoSymbol) {
@@ -877,7 +904,32 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
877904
val tree1 = TypeTree(clazz.primaryConstructor.tpe.asSeenFrom(prefix, clazz.owner))
878905
.setOriginal(tree)
879906

880-
inferConstructorInstance(tree1, clazz.typeParams, pt)
907+
val skolems = new mutable.ListBuffer[TypeSymbol]
908+
object variantToSkolem extends VariantTypeMap {
909+
def apply(tp: Type) = mapOver(tp) match {
910+
case TypeRef(NoPrefix, tpSym, Nil) if variance != 0 && tpSym.isTypeParameterOrSkolem && tpSym.owner.isTerm =>
911+
val bounds = if (variance == 1) TypeBounds.upper(tpSym.tpe) else TypeBounds.lower(tpSym.tpe)
912+
val skolem = context.owner.newExistentialSkolem(tpSym, tpSym, unit.freshTypeName("?"+tpSym.name), bounds)
913+
// println("mapping "+ tpSym +" to "+ skolem + " : "+ bounds +" -- pt= "+ pt)
914+
skolems += skolem
915+
skolem.tpe
916+
case tp1 => tp1
917+
}
918+
}
919+
920+
// have to open up the existential and put the skolems in scope
921+
// can't simply package up pt in an ExistentialType, because that takes us back to square one (List[_ <: T] == List[T] due to covariance)
922+
val ptSafe = variantToSkolem(pt) // TODO: pt.skolemizeExistential(context.owner, tree) ?
923+
val freeVars = skolems.toList
924+
925+
// use "tree" for the context, not context.tree: don't make another CaseDef context,
926+
// as instantiateTypeVar's bounds would end up there
927+
val ctorContext = context.makeNewScope(tree, context.owner)
928+
freeVars foreach ctorContext.scope.enter
929+
newTyper(ctorContext).infer.inferConstructorInstance(tree1, clazz.typeParams, ptSafe)
930+
931+
// tree1's type-slack skolems will be deskolemized (to the method type parameter skolems)
932+
// once the containing CaseDef has been type checked (see typedCase)
881933
tree1
882934
} else {
883935
tree
@@ -1998,15 +2050,35 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser {
19982050
val guard1: Tree = if (cdef.guard == EmptyTree) EmptyTree
19992051
else typed(cdef.guard, BooleanClass.tpe)
20002052
var body1: Tree = typed(cdef.body, pt)
2001-
if (!context.savedTypeBounds.isEmpty) {
2002-
body1.tpe = context.restoreTypeBounds(body1.tpe)
2003-
if (isFullyDefined(pt) && !(body1.tpe <:< pt)) {
2004-
// @M no need for pt.normalize here, is done in erasure
2053+
2054+
val contextWithTypeBounds = context.nextEnclosing(_.tree.isInstanceOf[CaseDef])
2055+
if (contextWithTypeBounds.savedTypeBounds nonEmpty) {
2056+
body1.tpe = contextWithTypeBounds restoreTypeBounds body1.tpe
2057+
2058+
// insert a cast if something typechecked under the GADT constraints,
2059+
// but not in real life (i.e., now that's we've reset the method's type skolems'
2060+
// infos back to their pre-GADT-constraint state)
2061+
if (isFullyDefined(pt) && !(body1.tpe <:< pt))
20052062
body1 = typedPos(body1.pos)(gen.mkCast(body1, pt))
2006-
}
2063+
20072064
}
2065+
20082066
// body1 = checkNoEscaping.locals(context.scope, pt, body1)
2009-
treeCopy.CaseDef(cdef, pat1, guard1, body1) setType body1.tpe
2067+
val treeWithSkolems = treeCopy.CaseDef(cdef, pat1, guard1, body1) setType body1.tpe
2068+
2069+
// undo adaptConstrPattern's evil deeds, as they confuse the old pattern matcher
2070+
// TODO: Paul, can we do the deskolemization lazily in the old pattern matcher
2071+
object deskolemizeOnce extends TypeMap {
2072+
def apply(tp: Type): Type = mapOver(tp) match {
2073+
case TypeRef(pre, sym, args) if sym.isExistentialSkolem && sym.deSkolemize.isSkolem && sym.deSkolemize.owner.isTerm =>
2074+
typeRef(NoPrefix, sym.deSkolemize, args)
2075+
case tp1 => tp1
2076+
}
2077+
}
2078+
2079+
new TypeMapTreeSubstituter(deskolemizeOnce).traverse(treeWithSkolems)
2080+
2081+
treeWithSkolems // now without skolems, actually
20102082
}
20112083

20122084
def typedCases(cases: List[CaseDef], pattp: Type, pt: Type): List[CaseDef] =

src/library/scala/collection/JavaConversions.scala

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ object JavaConversions {
6969
* @return A Java Iterator view of the argument.
7070
*/
7171
implicit def asJavaIterator[A](it: Iterator[A]): ju.Iterator[A] = it match {
72-
case JIteratorWrapper(wrapped) => wrapped
72+
case JIteratorWrapper(wrapped) => wrapped.asInstanceOf[ju.Iterator[A]]
7373
case _ => IteratorWrapper(it)
7474
}
7575

@@ -87,7 +87,7 @@ object JavaConversions {
8787
* @return A Java Enumeration view of the argument.
8888
*/
8989
implicit def asJavaEnumeration[A](it: Iterator[A]): ju.Enumeration[A] = it match {
90-
case JEnumerationWrapper(wrapped) => wrapped
90+
case JEnumerationWrapper(wrapped) => wrapped.asInstanceOf[ju.Enumeration[A]]
9191
case _ => IteratorWrapper(it)
9292
}
9393

@@ -105,7 +105,7 @@ object JavaConversions {
105105
* @return A Java Iterable view of the argument.
106106
*/
107107
implicit def asJavaIterable[A](i: Iterable[A]): jl.Iterable[A] = i match {
108-
case JIterableWrapper(wrapped) => wrapped
108+
case JIterableWrapper(wrapped) => wrapped.asInstanceOf[jl.Iterable[A]]
109109
case _ => IterableWrapper(i)
110110
}
111111

@@ -121,7 +121,7 @@ object JavaConversions {
121121
* @return A Java Collection view of the argument.
122122
*/
123123
implicit def asJavaCollection[A](it: Iterable[A]): ju.Collection[A] = it match {
124-
case JCollectionWrapper(wrapped) => wrapped
124+
case JCollectionWrapper(wrapped) => wrapped.asInstanceOf[ju.Collection[A]]
125125
case _ => new IterableWrapper(it)
126126
}
127127

@@ -179,7 +179,7 @@ object JavaConversions {
179179
* @return A Java List view of the argument.
180180
*/
181181
implicit def seqAsJavaList[A](seq: Seq[A]): ju.List[A] = seq match {
182-
case JListWrapper(wrapped) => wrapped
182+
case JListWrapper(wrapped) => wrapped.asInstanceOf[ju.List[A]]
183183
case _ => new SeqWrapper(seq)
184184
}
185185

@@ -286,7 +286,7 @@ object JavaConversions {
286286
*/
287287
implicit def mapAsJavaMap[A, B](m: Map[A, B]): ju.Map[A, B] = m match {
288288
//case JConcurrentMapWrapper(wrapped) => wrapped
289-
case JMapWrapper(wrapped) => wrapped
289+
case JMapWrapper(wrapped) => wrapped.asInstanceOf[ju.Map[A, B]]
290290
case _ => new MapWrapper(m)
291291
}
292292

src/library/scala/collection/immutable/IntMap.scala

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -353,19 +353,19 @@ extends AbstractMap[Int, T]
353353
def unionWith[S >: T](that : IntMap[S], f : (Int, S, S) => S) : IntMap[S] = (this, that) match{
354354
case (IntMap.Bin(p1, m1, l1, r1), that@(IntMap.Bin(p2, m2, l2, r2))) =>
355355
if (shorter(m1, m2)) {
356-
if (!hasMatch(p2, p1, m1)) join(p1, this, p2, that);
356+
if (!hasMatch(p2, p1, m1)) join[S](p1, this, p2, that); // TODO: remove [S] when SI-5548 is fixed
357357
else if (zero(p2, m1)) IntMap.Bin(p1, m1, l1.unionWith(that, f), r1);
358358
else IntMap.Bin(p1, m1, l1, r1.unionWith(that, f));
359359
} else if (shorter(m2, m1)){
360-
if (!hasMatch(p1, p2, m2)) join(p1, this, p2, that);
360+
if (!hasMatch(p1, p2, m2)) join[S](p1, this, p2, that); // TODO: remove [S] when SI-5548 is fixed
361361
else if (zero(p1, m2)) IntMap.Bin(p2, m2, this.unionWith(l2, f), r2);
362362
else IntMap.Bin(p2, m2, l2, this.unionWith(r2, f));
363363
}
364364
else {
365365
if (p1 == p2) IntMap.Bin(p1, m1, l1.unionWith(l2,f), r1.unionWith(r2, f));
366-
else join(p1, this, p2, that);
366+
else join[S](p1, this, p2, that); // TODO: remove [S] when SI-5548 is fixed
367367
}
368-
case (IntMap.Tip(key, value), x) => x.updateWith(key, value, (x, y) => f(key, y, x));
368+
case (IntMap.Tip(key, value), x) => x.updateWith[S](key, value, (x, y) => f(key, y, x));
369369
case (x, IntMap.Tip(key, value)) => x.updateWith[S](key, value, (x, y) => f(key, x, y));
370370
case (IntMap.Nil, x) => x;
371371
case (x, IntMap.Nil) => x;

src/library/scala/collection/immutable/LongMap.scala

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -349,19 +349,19 @@ extends AbstractMap[Long, T]
349349
def unionWith[S >: T](that : LongMap[S], f : (Long, S, S) => S) : LongMap[S] = (this, that) match{
350350
case (LongMap.Bin(p1, m1, l1, r1), that@(LongMap.Bin(p2, m2, l2, r2))) =>
351351
if (shorter(m1, m2)) {
352-
if (!hasMatch(p2, p1, m1)) join(p1, this, p2, that);
352+
if (!hasMatch(p2, p1, m1)) join[S](p1, this, p2, that); // TODO: remove [S] when SI-5548 is fixed
353353
else if (zero(p2, m1)) LongMap.Bin(p1, m1, l1.unionWith(that, f), r1);
354354
else LongMap.Bin(p1, m1, l1, r1.unionWith(that, f));
355355
} else if (shorter(m2, m1)){
356-
if (!hasMatch(p1, p2, m2)) join(p1, this, p2, that);
356+
if (!hasMatch(p1, p2, m2)) join[S](p1, this, p2, that); // TODO: remove [S] when SI-5548 is fixed
357357
else if (zero(p1, m2)) LongMap.Bin(p2, m2, this.unionWith(l2, f), r2);
358358
else LongMap.Bin(p2, m2, l2, this.unionWith(r2, f));
359359
}
360360
else {
361361
if (p1 == p2) LongMap.Bin(p1, m1, l1.unionWith(l2,f), r1.unionWith(r2, f));
362-
else join(p1, this, p2, that);
362+
else join[S](p1, this, p2, that); // TODO: remove [S] when SI-5548 is fixed
363363
}
364-
case (LongMap.Tip(key, value), x) => x.updateWith(key, value, (x, y) => f(key, y, x));
364+
case (LongMap.Tip(key, value), x) => x.updateWith[S](key, value, (x, y) => f(key, y, x)); // TODO: remove [S] when SI-5548 is fixed
365365
case (x, LongMap.Tip(key, value)) => x.updateWith[S](key, value, (x, y) => f(key, x, y));
366366
case (LongMap.Nil, x) => x;
367367
case (x, LongMap.Nil) => x;

0 commit comments

Comments
 (0)