From b9082321219f78a86e80fcea2a71d96ee4beb397 Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Fri, 2 Mar 2012 17:33:34 +0100 Subject: cleaned up inferConstructorInstance; same behavior --- .../scala/tools/nsc/typechecker/Infer.scala | 91 +++++++++++++--------- 1 file changed, 54 insertions(+), 37 deletions(-) diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala index c09e535117..310198ec0c 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala @@ -1051,49 +1051,66 @@ trait Infer { * @param pt the expected result type of the instance */ def inferConstructorInstance(tree: Tree, undetparams: List[Symbol], pt0: Type) { - val pt = widen(pt0) - //println("infer constr inst "+tree+"/"+undetparams+"/"+pt0) - var restpe = tree.tpe.finalResultType - var tvars = undetparams map freshVar + val pt = widen(pt0) + val ptparams = freeTypeParamsOfTerms.collect(pt) + val ctorTp = tree.tpe + val resTp = ctorTp.finalResultType - /** Compute type arguments for undetermined params and substitute them in given tree. + debuglog("infer constr inst "+ tree +"/"+ undetparams +"/ pt= "+ pt +" pt0= "+ pt0 +" resTp: "+ resTp) + + /** Compute type arguments for undetermined params */ - def computeArgs = - try { - val targs = solvedTypes(tvars, undetparams, undetparams map varianceInType(restpe), - true, lubDepth(List(restpe, pt))) -// checkBounds(tree, NoPrefix, NoSymbol, undetparams, targs, "inferred ") -// no checkBounds here. If we enable it, test bug602 fails. - new TreeTypeSubstituter(undetparams, targs).traverse(tree) - } catch ifNoInstance{ msg => - NoConstructorInstanceError(tree, restpe, pt, msg) + def inferFor(pt: Type): Option[List[Type]] = { + val tvars = undetparams map freshVar + val resTpV = resTp.instantiateTypeParams(undetparams, tvars) + + if (resTpV <:< pt) { + try { + // debuglog("TVARS "+ (tvars map (_.constr))) + // look at the argument types of the primary constructor corresponding to the pattern + val variances = undetparams map varianceInType(ctorTp) + val targs = solvedTypes(tvars, undetparams, variances, true, lubDepth(List(resTp, pt))) + // checkBounds(tree, NoPrefix, NoSymbol, undetparams, targs, "inferred ") + // no checkBounds here. If we enable it, test bug602 fails. + // TODO: reinstate checkBounds, return params that fail to meet their bounds to undetparams + Some(targs) + } catch ifNoInstance { msg => + debuglog("NO INST "+ (tvars, tvars map (_.constr))) + NoConstructorInstanceError(tree, resTp, pt, msg) + None + } + } else { + debuglog("not a subtype: "+ resTpV +" WildcardType)) - tvars = undetparams map freshVar - if (restpe.instantiateTypeParams(undetparams, tvars) <:< ptWithWildcards) { - computeArgs - restpe = skipImplicit(tree.tpe.resultType) - debuglog("new tree = " + tree + ":" + restpe) - val ptvars = ptparams map freshVar - val pt1 = pt.instantiateTypeParams(ptparams, ptvars) - if (isPopulated(restpe, pt1)) { - ptvars foreach instantiateTypeVar - } else { if (settings.debug.value) Console.println("no instance: "); instError } - } else { if (settings.debug.value) Console.println("not a subtype " + restpe.instantiateTypeParams(undetparams, tvars) + " of " + ptWithWildcards); instError } - } else { if (settings.debug.value) Console.println("not fully defined: " + pt); instError } + + def inferForApproxPt = + if (isFullyDefined(pt)) { + inferFor(pt.instantiateTypeParams(ptparams, ptparams map (x => WildcardType))) flatMap { targs => + val ctorTpInst = tree.tpe.instantiateTypeParams(undetparams, targs) + val resTpInst = skipImplicit(ctorTpInst.finalResultType) + val ptvars = ptparams map freshVar + val ptV = pt.instantiateTypeParams(ptparams, ptvars) + + if (isPopulated(resTpInst, ptV)) { + ptvars foreach instantiateTypeVar + debuglog("isPopulated "+ resTpInst +", "+ ptV +" vars= "+ ptvars) + Some(targs) + } else None + } + } else None + + (inferFor(pt) orElse inferForApproxPt) map { targs => + new TreeTypeSubstituter(undetparams, targs).traverse(tree) + } getOrElse { + debugwarn("failed inferConstructorInstance for "+ tree +" : "+ tree.tpe +" under "+ undetparams +" pt = "+ pt +(if(isFullyDefined(pt)) " (fully defined)" else " (not fully defined)")) + // if (settings.explaintypes.value) explainTypes(resTp.instantiateTypeParams(undetparams, tvars), pt) + ConstrInstantiationError(tree, resTp, pt) + } } + def instBounds(tvar: TypeVar): (Type, Type) = { val tparam = tvar.origin.typeSymbol val instType = toOrigin(tvar.constr.inst) -- cgit v1.2.3 From 0cffdf38d9e2d88e66d8649d317f8815716b2748 Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Fri, 2 Mar 2012 17:58:08 +0100 Subject: SI-5189 1/2: inferConstrInst uses correct variance fixed concurrent.impl.Promise by making FState invariant (it would be unsound to make it covariant) --- src/compiler/scala/tools/nsc/typechecker/Infer.scala | 2 +- src/library/scala/concurrent/impl/Promise.scala | 10 ++++++++-- test/files/neg/t5189.check | 6 ++++++ test/files/neg/t5189.scala | 5 +++++ 4 files changed, 20 insertions(+), 3 deletions(-) create mode 100644 test/files/neg/t5189.check create mode 100644 test/files/neg/t5189.scala diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala index 310198ec0c..7b72169ba9 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala @@ -1068,7 +1068,7 @@ trait Infer { try { // debuglog("TVARS "+ (tvars map (_.constr))) // look at the argument types of the primary constructor corresponding to the pattern - val variances = undetparams map varianceInType(ctorTp) + val variances = undetparams map varianceInType(ctorTp.paramTypes.headOption getOrElse ctorTp) val targs = solvedTypes(tvars, undetparams, variances, true, lubDepth(List(resTp, pt))) // checkBounds(tree, NoPrefix, NoSymbol, undetparams, targs, "inferred ") // no checkBounds here. If we enable it, test bug602 fails. diff --git a/src/library/scala/concurrent/impl/Promise.scala b/src/library/scala/concurrent/impl/Promise.scala index 585f71f3cf..0087b71ea8 100644 --- a/src/library/scala/concurrent/impl/Promise.scala +++ b/src/library/scala/concurrent/impl/Promise.scala @@ -80,8 +80,10 @@ object Promise { def EmptyPending[T](): FState[T] = emptyPendingValue.asInstanceOf[FState[T]] /** Represents the internal state. + * + * [adriaan] it's unsound to make FState covariant (tryComplete won't type check) */ - sealed trait FState[+T] { def value: Option[Try[T]] } + sealed trait FState[T] { def value: Option[Try[T]] } case class Pending[T](listeners: List[Try[T] => Any] = Nil) extends FState[T] { def value: Option[Try[T]] = None @@ -155,7 +157,11 @@ object Promise { def tryComplete(v: Try[T]): List[Try[T] => Any] = { getState match { case cur @ Pending(listeners) => - if (updateState(cur, if (v.isFailure) Failure(Some(v.asInstanceOf[util.Failure[T]])) else Success(Some(v.asInstanceOf[util.Success[T]])))) listeners + val newState = + if (v.isFailure) Failure(Some(v.asInstanceOf[util.Failure[T]])) + else Success(Some(v.asInstanceOf[util.Success[T]])) + + if (updateState(cur, newState)) listeners else tryComplete(v) case _ => null } diff --git a/test/files/neg/t5189.check b/test/files/neg/t5189.check new file mode 100644 index 0000000000..7762f465dc --- /dev/null +++ b/test/files/neg/t5189.check @@ -0,0 +1,6 @@ +t5189.scala:3: error: type mismatch; + found : Nothing => Any + required: Any => Any + def f(x: Any): Any => Any = x match { case Foo(bar) => bar } + ^ +one error found \ No newline at end of file diff --git a/test/files/neg/t5189.scala b/test/files/neg/t5189.scala new file mode 100644 index 0000000000..19e8e74667 --- /dev/null +++ b/test/files/neg/t5189.scala @@ -0,0 +1,5 @@ +class TestNeg1 { + case class Foo[T, U](f: T => U) + def f(x: Any): Any => Any = x match { case Foo(bar) => bar } + // uh-oh, Any => Any should be Nothing => Any. +} -- cgit v1.2.3 From 29bcadefb451bbf546e6f763027dac16f5b6f51b Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Tue, 6 Mar 2012 15:58:58 +0100 Subject: SI-5189 fixed: safe type infer for constr pattern several fixes to the standard library due to - the safer type checker this fix gives us (thus, some casts had to be inserted) - SI-5548 - type inference gets a bit more complicated, it needs help (chainl1 in combinator.Parsers) To deal with the type slack between actual (run-time) types and statically known types, for each abstract type T, reflect its variance as a skolem that is upper-bounded by T (covariant position), or lower-bounded by T (contravariant). Consider the following example: class AbsWrapperCov[+A] case class Wrapper[B](x: Wrapped[B]) extends AbsWrapperCov[B] def unwrap[T](x: AbsWrapperCov[T]): Wrapped[T] = x match { case Wrapper(wrapped) => // Wrapper's type parameter must not be assumed to be equal to T, // it's *upper-bounded* by it wrapped // : Wrapped[_ <: T] } this method should type check if and only if Wrapped is covariant in its type parameter before inferring Wrapper's type parameter B from x's type AbsWrapperCov[T], we must take into account that x's actual type is: AbsWrapperCov[Tactual] forSome {type Tactual <: T} since AbsWrapperCov is covariant in A -- in other words, we must not assume we know T exactly, all we know is its upper bound since method application is the only way to generate this slack between run-time and compile-time types (TODO: right!?), we can simply replace skolems that represent method type parameters as seen from the method's body by other skolems that are (upper/lower)-bounded by that type-parameter skolem (depending on the variance position of the skolem in the statically assumed type of the scrutinee, pt) this type slack is introduced by adaptConstrPattern: before it calls inferConstructorInstance, it creates a new context that holds the new existential skolems the context created by adaptConstrPattern must not be a CaseDef, since that confuses instantiateTypeVar and the whole pushTypeBounds/restoreTypeBounds dance (CaseDef contexts remember the bounds of the type params that we clobbered during GADT typing) typedCase deskolemizes the existential skolems back to the method skolems, since they don't serve any further purpose (except confusing the old pattern matcher) typedCase is now better at finding that context (using nextEnclosing) --- src/compiler/scala/reflect/internal/Symbols.scala | 6 +- src/compiler/scala/tools/nsc/io/Pickler.scala | 2 +- .../scala/tools/nsc/typechecker/Infer.scala | 10 ++- .../scala/tools/nsc/typechecker/Typers.scala | 86 ++++++++++++++++++++-- src/library/scala/collection/JavaConversions.scala | 12 +-- .../scala/collection/immutable/IntMap.scala | 8 +- .../scala/collection/immutable/LongMap.scala | 8 +- .../scala/util/parsing/combinator/Parsers.scala | 5 +- .../scala/tools/scalap/scalax/rules/Rules.scala | 2 +- test/files/jvm/typerep.scala | 2 +- test/files/neg/t5189b.check | 8 ++ test/files/neg/t5189b.scala | 62 ++++++++++++++++ 12 files changed, 180 insertions(+), 31 deletions(-) create mode 100644 test/files/neg/t5189b.check create mode 100644 test/files/neg/t5189b.scala diff --git a/src/compiler/scala/reflect/internal/Symbols.scala b/src/compiler/scala/reflect/internal/Symbols.scala index 62b0206c28..446dbad03f 100644 --- a/src/compiler/scala/reflect/internal/Symbols.scala +++ b/src/compiler/scala/reflect/internal/Symbols.scala @@ -269,9 +269,9 @@ trait Symbols extends api.Symbols { self: SymbolTable => /** Create a new existential type skolem with this symbol its owner, * based on the given symbol and origin. */ - def newExistentialSkolem(basis: Symbol, origin: AnyRef): TypeSkolem = { - val skolem = newTypeSkolemSymbol(basis.name.toTypeName, origin, basis.pos, (basis.flags | EXISTENTIAL) & ~PARAM) - skolem setInfo (basis.info cloneInfo skolem) + def newExistentialSkolem(basis: Symbol, origin: AnyRef, name: TypeName = null, info: Type = null): TypeSkolem = { + val skolem = newTypeSkolemSymbol(if (name eq null) basis.name.toTypeName else name, origin, basis.pos, (basis.flags | EXISTENTIAL) & ~PARAM) + skolem setInfo (if (info eq null) basis.info cloneInfo skolem else info) } final def newExistential(name: TypeName, pos: Position = NoPosition, newFlags: Long = 0L): Symbol = diff --git a/src/compiler/scala/tools/nsc/io/Pickler.scala b/src/compiler/scala/tools/nsc/io/Pickler.scala index 5bb8bdda35..80b6e086da 100644 --- a/src/compiler/scala/tools/nsc/io/Pickler.scala +++ b/src/compiler/scala/tools/nsc/io/Pickler.scala @@ -165,7 +165,7 @@ object Pickler { def pkl[T: Pickler] = implicitly[Pickler[T]] /** A class represenenting `~`-pairs */ - case class ~[S, T](fst: S, snd: T) + case class ~[+S, +T](fst: S, snd: T) /** A wrapper class to be able to use `~` s an infix method */ class TildeDecorator[S](x: S) { diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala index 7b72169ba9..50c45a2dc0 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala @@ -1090,7 +1090,15 @@ trait Infer { inferFor(pt.instantiateTypeParams(ptparams, ptparams map (x => WildcardType))) flatMap { targs => val ctorTpInst = tree.tpe.instantiateTypeParams(undetparams, targs) val resTpInst = skipImplicit(ctorTpInst.finalResultType) - val ptvars = ptparams map freshVar + val ptvars = + ptparams map { + // since instantiateTypeVar wants to modify the skolem that corresponds to the method's type parameter, + // and it uses the TypeVar's origin to locate it, deskolemize the existential skolem to the method tparam skolem + // (the existential skolem was created by adaptConstrPattern to introduce the type slack necessary to soundly deal with variant type parameters) + case skolem if skolem.isExistentialSkolem => freshVar(skolem.deSkolemize.asInstanceOf[TypeSymbol]) + case p => freshVar(p) + } + val ptV = pt.instantiateTypeParams(ptparams, ptvars) if (isPopulated(resTpInst, ptV)) { diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 556c680cda..e300fdacf9 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -852,6 +852,33 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { } } + /** + * To deal with the type slack between actual (run-time) types and statically known types, for each abstract type T, + * reflect its variance as a skolem that is upper-bounded by T (covariant position), or lower-bounded by T (contravariant). + * + * Consider the following example: + * + * class AbsWrapperCov[+A] + * case class Wrapper[B](x: Wrapped[B]) extends AbsWrapperCov[B] + * + * def unwrap[T](x: AbsWrapperCov[T]): Wrapped[T] = x match { + * case Wrapper(wrapped) => // Wrapper's type parameter must not be assumed to be equal to T, it's *upper-bounded* by it + * wrapped // : Wrapped[_ <: T] + * } + * + * this method should type check if and only if Wrapped is covariant in its type parameter + * + * when inferring Wrapper's type parameter B from x's type AbsWrapperCov[T], + * we must take into account that x's actual type is AbsWrapperCov[Tactual] forSome {type Tactual <: T} + * as AbsWrapperCov is covariant in A -- in other words, we must not assume we know T exactly, all we know is its upper bound + * + * since method application is the only way to generate this slack between run-time and compile-time types (TODO: right!?), + * we can simply replace skolems that represent method type parameters as seen from the method's body + * by other skolems that are (upper/lower)-bounded by that type-parameter skolem + * (depending on the variance position of the skolem in the statically assumed type of the scrutinee, pt) + * + * see test/files/../t5189*.scala + */ def adaptConstrPattern(): Tree = { // (5) val extractor = tree.symbol.filter(sym => reallyExists(unapplyMember(sym.tpe))) if (extractor != NoSymbol) { @@ -865,7 +892,32 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { val tree1 = TypeTree(clazz.primaryConstructor.tpe.asSeenFrom(prefix, clazz.owner)) .setOriginal(tree) - inferConstructorInstance(tree1, clazz.typeParams, pt) + val skolems = new mutable.ListBuffer[TypeSymbol] + object variantToSkolem extends VariantTypeMap { + def apply(tp: Type) = mapOver(tp) match { + case TypeRef(NoPrefix, tpSym, Nil) if variance != 0 && tpSym.isTypeParameterOrSkolem && tpSym.owner.isTerm => + val bounds = if (variance == 1) TypeBounds.upper(tpSym.tpe) else TypeBounds.lower(tpSym.tpe) + val skolem = context.owner.newExistentialSkolem(tpSym, tpSym, unit.freshTypeName("?"+tpSym.name), bounds) + // println("mapping "+ tpSym +" to "+ skolem + " : "+ bounds +" -- pt= "+ pt) + skolems += skolem + skolem.tpe + case tp1 => tp1 + } + } + + // have to open up the existential and put the skolems in scope + // can't simply package up pt in an ExistentialType, because that takes us back to square one (List[_ <: T] == List[T] due to covariance) + val ptSafe = variantToSkolem(pt) // TODO: pt.skolemizeExistential(context.owner, tree) ? + val freeVars = skolems.toList + + // use "tree" for the context, not context.tree: don't make another CaseDef context, + // as instantiateTypeVar's bounds would end up there + val ctorContext = context.makeNewScope(tree, context.owner) + freeVars foreach ctorContext.scope.enter + newTyper(ctorContext).infer.inferConstructorInstance(tree1, clazz.typeParams, ptSafe) + + // tree1's type-slack skolems will be deskolemized (to the method type parameter skolems) + // once the containing CaseDef has been type checked (see typedCase) tree1 } else { tree @@ -1986,15 +2038,35 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { val guard1: Tree = if (cdef.guard == EmptyTree) EmptyTree else typed(cdef.guard, BooleanClass.tpe) var body1: Tree = typed(cdef.body, pt) - if (!context.savedTypeBounds.isEmpty) { - body1.tpe = context.restoreTypeBounds(body1.tpe) - if (isFullyDefined(pt) && !(body1.tpe <:< pt)) { - // @M no need for pt.normalize here, is done in erasure + + val contextWithTypeBounds = context.nextEnclosing(_.tree.isInstanceOf[CaseDef]) + if (contextWithTypeBounds.savedTypeBounds nonEmpty) { + body1.tpe = contextWithTypeBounds restoreTypeBounds body1.tpe + + // insert a cast if something typechecked under the GADT constraints, + // but not in real life (i.e., now that's we've reset the method's type skolems' + // infos back to their pre-GADT-constraint state) + if (isFullyDefined(pt) && !(body1.tpe <:< pt)) body1 = typedPos(body1.pos)(gen.mkCast(body1, pt)) - } + } + // body1 = checkNoEscaping.locals(context.scope, pt, body1) - treeCopy.CaseDef(cdef, pat1, guard1, body1) setType body1.tpe + val treeWithSkolems = treeCopy.CaseDef(cdef, pat1, guard1, body1) setType body1.tpe + + // undo adaptConstrPattern's evil deeds, as they confuse the old pattern matcher + // TODO: Paul, can we do the deskolemization lazily in the old pattern matcher + object deskolemizeOnce extends TypeMap { + def apply(tp: Type): Type = mapOver(tp) match { + case TypeRef(pre, sym, args) if sym.isExistentialSkolem && sym.deSkolemize.isSkolem && sym.deSkolemize.owner.isTerm => + typeRef(NoPrefix, sym.deSkolemize, args) + case tp1 => tp1 + } + } + + new TypeMapTreeSubstituter(deskolemizeOnce).traverse(treeWithSkolems) + + treeWithSkolems // now without skolems, actually } def typedCases(cases: List[CaseDef], pattp: Type, pt: Type): List[CaseDef] = diff --git a/src/library/scala/collection/JavaConversions.scala b/src/library/scala/collection/JavaConversions.scala index d5011fc6aa..50919e506a 100644 --- a/src/library/scala/collection/JavaConversions.scala +++ b/src/library/scala/collection/JavaConversions.scala @@ -69,7 +69,7 @@ object JavaConversions { * @return A Java Iterator view of the argument. */ implicit def asJavaIterator[A](it: Iterator[A]): ju.Iterator[A] = it match { - case JIteratorWrapper(wrapped) => wrapped + case JIteratorWrapper(wrapped) => wrapped.asInstanceOf[ju.Iterator[A]] case _ => IteratorWrapper(it) } @@ -87,7 +87,7 @@ object JavaConversions { * @return A Java Enumeration view of the argument. */ implicit def asJavaEnumeration[A](it: Iterator[A]): ju.Enumeration[A] = it match { - case JEnumerationWrapper(wrapped) => wrapped + case JEnumerationWrapper(wrapped) => wrapped.asInstanceOf[ju.Enumeration[A]] case _ => IteratorWrapper(it) } @@ -105,7 +105,7 @@ object JavaConversions { * @return A Java Iterable view of the argument. */ implicit def asJavaIterable[A](i: Iterable[A]): jl.Iterable[A] = i match { - case JIterableWrapper(wrapped) => wrapped + case JIterableWrapper(wrapped) => wrapped.asInstanceOf[jl.Iterable[A]] case _ => IterableWrapper(i) } @@ -121,7 +121,7 @@ object JavaConversions { * @return A Java Collection view of the argument. */ implicit def asJavaCollection[A](it: Iterable[A]): ju.Collection[A] = it match { - case JCollectionWrapper(wrapped) => wrapped + case JCollectionWrapper(wrapped) => wrapped.asInstanceOf[ju.Collection[A]] case _ => new IterableWrapper(it) } @@ -179,7 +179,7 @@ object JavaConversions { * @return A Java List view of the argument. */ implicit def seqAsJavaList[A](seq: Seq[A]): ju.List[A] = seq match { - case JListWrapper(wrapped) => wrapped + case JListWrapper(wrapped) => wrapped.asInstanceOf[ju.List[A]] case _ => new SeqWrapper(seq) } @@ -286,7 +286,7 @@ object JavaConversions { */ implicit def mapAsJavaMap[A, B](m: Map[A, B]): ju.Map[A, B] = m match { //case JConcurrentMapWrapper(wrapped) => wrapped - case JMapWrapper(wrapped) => wrapped + case JMapWrapper(wrapped) => wrapped.asInstanceOf[ju.Map[A, B]] case _ => new MapWrapper(m) } diff --git a/src/library/scala/collection/immutable/IntMap.scala b/src/library/scala/collection/immutable/IntMap.scala index dd6b066878..3c9c0c2f24 100644 --- a/src/library/scala/collection/immutable/IntMap.scala +++ b/src/library/scala/collection/immutable/IntMap.scala @@ -353,19 +353,19 @@ extends AbstractMap[Int, T] def unionWith[S >: T](that : IntMap[S], f : (Int, S, S) => S) : IntMap[S] = (this, that) match{ case (IntMap.Bin(p1, m1, l1, r1), that@(IntMap.Bin(p2, m2, l2, r2))) => if (shorter(m1, m2)) { - if (!hasMatch(p2, p1, m1)) join(p1, this, p2, that); + if (!hasMatch(p2, p1, m1)) join[S](p1, this, p2, that); // TODO: remove [S] when SI-5548 is fixed else if (zero(p2, m1)) IntMap.Bin(p1, m1, l1.unionWith(that, f), r1); else IntMap.Bin(p1, m1, l1, r1.unionWith(that, f)); } else if (shorter(m2, m1)){ - if (!hasMatch(p1, p2, m2)) join(p1, this, p2, that); + if (!hasMatch(p1, p2, m2)) join[S](p1, this, p2, that); // TODO: remove [S] when SI-5548 is fixed else if (zero(p1, m2)) IntMap.Bin(p2, m2, this.unionWith(l2, f), r2); else IntMap.Bin(p2, m2, l2, this.unionWith(r2, f)); } else { if (p1 == p2) IntMap.Bin(p1, m1, l1.unionWith(l2,f), r1.unionWith(r2, f)); - else join(p1, this, p2, that); + else join[S](p1, this, p2, that); // TODO: remove [S] when SI-5548 is fixed } - case (IntMap.Tip(key, value), x) => x.updateWith(key, value, (x, y) => f(key, y, x)); + case (IntMap.Tip(key, value), x) => x.updateWith[S](key, value, (x, y) => f(key, y, x)); case (x, IntMap.Tip(key, value)) => x.updateWith[S](key, value, (x, y) => f(key, x, y)); case (IntMap.Nil, x) => x; case (x, IntMap.Nil) => x; diff --git a/src/library/scala/collection/immutable/LongMap.scala b/src/library/scala/collection/immutable/LongMap.scala index 963ddac762..11b5d1e311 100644 --- a/src/library/scala/collection/immutable/LongMap.scala +++ b/src/library/scala/collection/immutable/LongMap.scala @@ -349,19 +349,19 @@ extends AbstractMap[Long, T] def unionWith[S >: T](that : LongMap[S], f : (Long, S, S) => S) : LongMap[S] = (this, that) match{ case (LongMap.Bin(p1, m1, l1, r1), that@(LongMap.Bin(p2, m2, l2, r2))) => if (shorter(m1, m2)) { - if (!hasMatch(p2, p1, m1)) join(p1, this, p2, that); + if (!hasMatch(p2, p1, m1)) join[S](p1, this, p2, that); // TODO: remove [S] when SI-5548 is fixed else if (zero(p2, m1)) LongMap.Bin(p1, m1, l1.unionWith(that, f), r1); else LongMap.Bin(p1, m1, l1, r1.unionWith(that, f)); } else if (shorter(m2, m1)){ - if (!hasMatch(p1, p2, m2)) join(p1, this, p2, that); + if (!hasMatch(p1, p2, m2)) join[S](p1, this, p2, that); // TODO: remove [S] when SI-5548 is fixed else if (zero(p1, m2)) LongMap.Bin(p2, m2, this.unionWith(l2, f), r2); else LongMap.Bin(p2, m2, l2, this.unionWith(r2, f)); } else { if (p1 == p2) LongMap.Bin(p1, m1, l1.unionWith(l2,f), r1.unionWith(r2, f)); - else join(p1, this, p2, that); + else join[S](p1, this, p2, that); // TODO: remove [S] when SI-5548 is fixed } - case (LongMap.Tip(key, value), x) => x.updateWith(key, value, (x, y) => f(key, y, x)); + 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 case (x, LongMap.Tip(key, value)) => x.updateWith[S](key, value, (x, y) => f(key, x, y)); case (LongMap.Nil, x) => x; case (x, LongMap.Nil) => x; diff --git a/src/library/scala/util/parsing/combinator/Parsers.scala b/src/library/scala/util/parsing/combinator/Parsers.scala index 27e9112fce..9aaf0aeb54 100644 --- a/src/library/scala/util/parsing/combinator/Parsers.scala +++ b/src/library/scala/util/parsing/combinator/Parsers.scala @@ -794,7 +794,7 @@ trait Parsers { */ def chainl1[T, U](first: => Parser[T], p: => Parser[U], q: => Parser[(T, U) => T]): Parser[T] = first ~ rep(q ~ p) ^^ { - case x ~ xs => xs.foldLeft(x){(_, _) match {case (a, f ~ b) => f(a, b)}} + case x ~ xs => xs.foldLeft(x: T){case (a, f ~ b) => f(a, b)} // x's type annotation is needed to deal with changed type inference due to SI-5189 } /** A parser generator that generalises the `rep1sep` generator so that `q`, @@ -812,8 +812,7 @@ trait Parsers { */ def chainr1[T, U](p: => Parser[T], q: => Parser[(T, U) => U], combine: (T, U) => U, first: U): Parser[U] = p ~ rep(q ~ p) ^^ { - case x ~ xs => (new ~(combine, x) :: xs). - foldRight(first){(_, _) match {case (f ~ a, b) => f(a, b)}} + case x ~ xs => (new ~(combine, x) :: xs).foldRight(first){case (f ~ a, b) => f(a, b)} } /** A parser generator for optional sub-phrases. diff --git a/src/scalap/scala/tools/scalap/scalax/rules/Rules.scala b/src/scalap/scala/tools/scalap/scalax/rules/Rules.scala index 43f9c20b1d..70926208b3 100644 --- a/src/scalap/scala/tools/scalap/scalax/rules/Rules.scala +++ b/src/scalap/scala/tools/scalap/scalax/rules/Rules.scala @@ -130,7 +130,7 @@ trait StateRules { def rep(in : S, t : T) : Result[S, T, X] = { if (finished(t)) Success(in, t) else rule(in) match { - case Success(out, f) => rep(out, f(t)) + case Success(out, f) => rep(out, f(t)) // SI-5189 f.asInstanceOf[T => T] case Failure => Failure case Error(x) => Error(x) } diff --git a/test/files/jvm/typerep.scala b/test/files/jvm/typerep.scala index 49a216c05c..3befc7ff3f 100644 --- a/test/files/jvm/typerep.scala +++ b/test/files/jvm/typerep.scala @@ -161,7 +161,7 @@ object TypeRep { }).asInstanceOf[TypeRep[Option[A]]] def getType[A](x: List[A])(implicit rep: TypeRep[A]): TypeRep[List[A]] = (x match { - case h :: t => ListRep(getType(h)) + case h :: t => ListRep(rep) case Nil => NilRep }).asInstanceOf[TypeRep[List[A]]] diff --git a/test/files/neg/t5189b.check b/test/files/neg/t5189b.check new file mode 100644 index 0000000000..7f78cbb438 --- /dev/null +++ b/test/files/neg/t5189b.check @@ -0,0 +1,8 @@ +t5189b.scala:25: error: type mismatch; + found : TestNeg.Wrapped[?T2] where type ?T2 <: T + required: TestNeg.Wrapped[T] +Note: ?T2 <: T, but class Wrapped is invariant in type W. +You may wish to define W as +W instead. (SLS 4.5) + case Wrapper/*[_ <: T ]*/(wrapped) => wrapped // : Wrapped[_ <: T], which is a subtype of Wrapped[T] if and only if Wrapped is covariant in its type parameter + ^ +one error found diff --git a/test/files/neg/t5189b.scala b/test/files/neg/t5189b.scala new file mode 100644 index 0000000000..1750f14084 --- /dev/null +++ b/test/files/neg/t5189b.scala @@ -0,0 +1,62 @@ +class TestPos { + class AbsWrapperCov[+A] + case class Wrapper[B](x: B) extends AbsWrapperCov[B] + + def unwrap[T](x: AbsWrapperCov[T]): T = x match { + case Wrapper/*[_ <: T ]*/(x) => x // _ <: T, which is a subtype of T + } +} + +object TestNeg extends App { + class AbsWrapperCov[+A] + case class Wrapper[B](x: Wrapped[B]) extends AbsWrapperCov[B] + + /* + when inferring Wrapper's type parameter B from x's type AbsWrapperCov[T], + we must take into account that x's actual type is AbsWrapperCov[Tactual] forSome {type Tactual <: T} + as AbsWrapperCov is covariant in A -- in other words, we must not assume we know T exactly, all we know is its upper bound + + since method application is the only way to generate this slack between run-time and compile-time types, + we'll simply replace the skolems that represent method type parameters as seen from the method's body by + other skolems that are (upper/lower)-bounded by the type-parameter skolems + (depending on whether the skolem appears in a covariant/contravariant position) + */ + def unwrap[T](x: AbsWrapperCov[T]): Wrapped[T] = x match { + case Wrapper/*[_ <: T ]*/(wrapped) => wrapped // : Wrapped[_ <: T], which is a subtype of Wrapped[T] if and only if Wrapped is covariant in its type parameter + } + + class Wrapped[W](var cell: W) // must be invariant (to trigger the bug) + + // class A { def imNotAB = println("notB")} + // class B + // + // val w = new Wrapped(new A) + // unwrap[Any](Wrapper(w)).cell = new B + // w.cell.imNotAB +} + +// class TestPos1 { +// class Base[T] +// case class C[T](x: T) extends Base[T] +// def foo[T](b: Base[T]): T = b match { case C(x) => x } +// +// case class Span[K <: Ordered[K]](low: Option[K], high: Option[K]) extends Function1[K, Boolean] { +// override def equals(x$1: Any): Boolean = x$1 match { +// case Span((low$0 @ _), (high$0 @ _)) if low$0.equals(low).$amp$amp(high$0.equals(high)) => true +// case _ => false +// } +// def apply(k: K): Boolean = this match { +// case Span(Some(low), Some(high)) => (k >= low && k <= high) +// case Span(Some(low), None) => (k >= low) +// case Span(None, Some(high)) => (k <= high) +// case _ => false +// } +// } +// } +// +// class TestNeg1 { +// case class Foo[T, U](f: T => U) +// def f(x: Any): Any => Any = x match { case Foo(bar) => bar } +// // uh-oh, Any => Any should be Nothing => Any. +// } + -- cgit v1.2.3 From 292a861ca83b85e1b7c6a1e673c8710930aadc22 Mon Sep 17 00:00:00 2001 From: Josh Suereth Date: Sun, 11 Mar 2012 00:45:32 -0500 Subject: Updated flow for creating new STARRs. * SHA1's are deleted when creating new STARR builds. * SHA1's are also deleted if new fjbg/forkjoin/jline jars are made. * Normal push/pull flow should continue as normal with less frustration. --- build.xml | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/build.xml b/build.xml index 9bd5f8dac0..0a3f7e782f 100644 --- a/build.xml +++ b/build.xml @@ -2010,9 +2010,28 @@ STABLE REFERENCE (STARR) + + + + + + + + + + + + + + + + + + + - +