diff options
author | Paul Phillips <paulp@improving.org> | 2012-01-07 10:50:44 -0800 |
---|---|---|
committer | Paul Phillips <paulp@improving.org> | 2012-01-07 13:48:51 -0800 |
commit | dc1bbb919eda7e3ec49e4b5cd9d726f58d318cf7 (patch) | |
tree | 5bb7b72ab1f7ea5732c8ab8a3c64140ceef0baaf | |
parent | 27d19715af59e2e438808ae668c093ad61c8f728 (diff) | |
download | scala-dc1bbb919eda7e3ec49e4b5cd9d726f58d318cf7.tar.gz scala-dc1bbb919eda7e3ec49e4b5cd9d726f58d318cf7.tar.bz2 scala-dc1bbb919eda7e3ec49e4b5cd9d726f58d318cf7.zip |
TypeConstraint/TypeVar refinement.
I zeroed in on the actual conditions under which the parameter bounds
can be utilized without poisoning the well. Also fixed a bug in
ClassfileParser where it would get confused and set Any as a lower
bound, as well as a bug or at least misbehavior where a TypeBounds with
only Any/Nothing as an upper/lower bound would be treated differently
than one with no bound at all.
Review by @moors.
-rw-r--r-- | src/compiler/scala/reflect/internal/Types.scala | 55 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala | 7 | ||||
-rw-r--r-- | test/files/continuations-neg/t2949.check | 2 |
3 files changed, 39 insertions, 25 deletions
diff --git a/src/compiler/scala/reflect/internal/Types.scala b/src/compiler/scala/reflect/internal/Types.scala index f5876291ea..0b1196c1d0 100644 --- a/src/compiler/scala/reflect/internal/Types.scala +++ b/src/compiler/scala/reflect/internal/Types.scala @@ -90,6 +90,10 @@ trait Types extends api.Types { self: SymbolTable => private final val traceTypeVars = sys.props contains "scalac.debug.tvar" /** In case anyone wants to turn off lub verification without reverting anything. */ private final val verifyLubs = true + /** In case anyone wants to turn off type parameter bounds being used + * to seed type constraints. + */ + private final val propagateParameterBoundsToTypeVars = !(sys.props contains "scalac.debug.no-prop-constraints") protected val enableTypeVarExperimentals = settings.Xexperimental.value @@ -1314,6 +1318,7 @@ trait Types extends api.Types { self: SymbolTable => case TypeBounds(_, _) => that <:< this case _ => lo <:< that && that <:< hi } + def isEmptyBounds = (lo.typeSymbolDirect eq NothingClass) && (hi.typeSymbolDirect eq AnyClass) // override def isNullable: Boolean = NullClass.tpe <:< lo; override def safeToString = ">: " + lo + " <: " + hi override def kind = "TypeBoundsType" @@ -2446,25 +2451,23 @@ A type's typeSymbol should never be inspected directly. /** Create a new TypeConstraint based on the given symbol. */ private def deriveConstraint(tparam: Symbol): TypeConstraint = { - // Force the info of a higher-order tparam's parameters. - // Otherwise things don't end well. See SI-5359. However - // we can't force all info, so we have to discriminate - // carefully. - val isHigher = tparam.isAbstractType && tparam.typeParams.nonEmpty - // See pos/tcpoly_infer_implicit_tuple_wrapper for the test which - // fails if I initialize the type constraint with the type parameter - // bounds. It seems that in that instance it interferes with the - // inference. Thus, the isHigherOrderTypeParameter condition. - val isExclude = isHigher && tparam.info.bounds.exists(_.typeSymbol.isHigherOrderTypeParameter) - - def message = "" + tparam.name + " in " + tparam.owner + ( - if (isExclude) ", empty due to higher order type parameter in bounds" - else "" - ) - /*TypeVar.trace[TypeConstraint]("constr", message)*/( - if (isHigher && !isExclude) new TypeConstraint(tparam.info.bounds) - else new TypeConstraint - ) + /** Must force the type parameter's info at this point + * or things don't end well for higher-order type params. + * See SI-5359. + */ + val bounds = tparam.info.bounds + /** We can seed the type constraint with the type parameter + * bounds as long as the types are concrete. This should lower + * the complexity of the search even if it doesn't improve + * any results. + */ + if (propagateParameterBoundsToTypeVars) { + val exclude = bounds.isEmptyBounds || bounds.exists(_.typeSymbolDirect.isNonClassType) + + if (exclude) new TypeConstraint + else TypeVar.trace("constraint", "For " + tparam.fullLocationString)(new TypeConstraint(bounds)) + } + else new TypeConstraint } def unapply(tv: TypeVar): Some[(Type, TypeConstraint)] = Some((tv.origin, tv.constr)) def apply(origin: Type, constr: TypeConstraint): TypeVar = apply(origin, constr, Nil, Nil) @@ -3342,8 +3345,12 @@ A type's typeSymbol should never be inspected directly. def this(bounds: TypeBounds) = this(List(bounds.lo), List(bounds.hi)) def this() = this(List(), List()) - private var lobounds = lo0 - private var hibounds = hi0 + /** Guard these lists against AnyClass and NothingClass appearing, + * else loBounds.isEmpty will have different results for an empty + * constraint and one with Nothing as a lower bound. + */ + private var lobounds = lo0 filterNot (_.typeSymbolDirect eq NothingClass) + private var hibounds = hi0 filterNot (_.typeSymbolDirect eq AnyClass) private var numlo = numlo0 private var numhi = numhi0 private var avoidWidening = avoidWidening0 @@ -3359,7 +3366,8 @@ A type's typeSymbol should never be inspected directly. else if (!isNumericSubType(tp, numlo)) numlo = numericLoBound } - else lobounds ::= tp + else if (tp.typeSymbolDirect ne NothingClass) + lobounds ::= tp } def checkWidening(tp: Type) { @@ -3378,7 +3386,8 @@ A type's typeSymbol should never be inspected directly. else if (!isNumericSubType(numhi, tp)) numhi = numericHiBound } - else hibounds ::= tp + else if (tp.typeSymbolDirect ne AnyClass) + hibounds ::= tp } def isWithinBounds(tp: Type): Boolean = diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala index ac72b4d22c..de11f3aa28 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala @@ -717,7 +717,12 @@ abstract class ClassfileParser { index += 1 val bounds = variance match { case '+' => TypeBounds.upper(objToAny(sig2type(tparams, skiptvs))) - case '-' => TypeBounds.lower(sig2type(tparams, skiptvs)) + case '-' => + val tp = sig2type(tparams, skiptvs) + // sig2type seems to return AnyClass regardless of the situation: + // we don't want Any as a LOWER bound. + if (tp.typeSymbol == definitions.AnyClass) TypeBounds.empty + else TypeBounds.lower(tp) case '*' => TypeBounds.empty } val newtparam = sym.newExistential(sym.pos, newTypeName("?"+i)) setInfo bounds diff --git a/test/files/continuations-neg/t2949.check b/test/files/continuations-neg/t2949.check index dd9768807c..411aed1b5b 100644 --- a/test/files/continuations-neg/t2949.check +++ b/test/files/continuations-neg/t2949.check @@ -1,6 +1,6 @@ t2949.scala:13: error: type mismatch; found : Int - required: ? @scala.util.continuations.cpsParam[List[?],Any] + required: ? @scala.util.continuations.cpsParam[List[?],?] x * y ^ one error found |