summaryrefslogtreecommitdiff
path: root/src/compiler
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2009-11-06 17:56:38 +0000
committerMartin Odersky <odersky@gmail.com>2009-11-06 17:56:38 +0000
commit1b807250a30c8526851e67271f0545c67362b365 (patch)
tree1ae2d7b0299cadb47a2b7f8e88590bdcf9245c9f /src/compiler
parent1e93e1784387edd386b109bc22e91595ea3da60c (diff)
downloadscala-1b807250a30c8526851e67271f0545c67362b365.tar.gz
scala-1b807250a30c8526851e67271f0545c67362b365.tar.bz2
scala-1b807250a30c8526851e67271f0545c67362b365.zip
added numeric widening and weak conformance.
Diffstat (limited to 'src/compiler')
-rw-r--r--src/compiler/scala/tools/nsc/Main.scala5
-rw-r--r--src/compiler/scala/tools/nsc/symtab/Definitions.scala28
-rw-r--r--src/compiler/scala/tools/nsc/symtab/Types.scala310
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Infer.scala63
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala38
5 files changed, 208 insertions, 236 deletions
diff --git a/src/compiler/scala/tools/nsc/Main.scala b/src/compiler/scala/tools/nsc/Main.scala
index 58df3f8665..53a8773ef1 100644
--- a/src/compiler/scala/tools/nsc/Main.scala
+++ b/src/compiler/scala/tools/nsc/Main.scala
@@ -80,8 +80,7 @@ object Main extends AnyRef with EvalLoop {
val command = new CompilerCommand(args.toList, settings, error, true)
buildManager.update(fileSet(command.files), Set.empty)
}
- }
- else {
+ } else {
if (command.settings.target.value == "msil") {
val libpath = System.getProperty("msil.libpath")
if (libpath != null)
@@ -115,7 +114,7 @@ object Main extends AnyRef with EvalLoop {
case ex @ FatalError(msg) =>
if (true || command.settings.debug.value) // !!!
ex.printStackTrace();
- reporter.error(null, "fatal error: " + msg)
+ reporter.error(null, "fatal error: " + msg)
}
}
}
diff --git a/src/compiler/scala/tools/nsc/symtab/Definitions.scala b/src/compiler/scala/tools/nsc/symtab/Definitions.scala
index afc604188d..97df429445 100644
--- a/src/compiler/scala/tools/nsc/symtab/Definitions.scala
+++ b/src/compiler/scala/tools/nsc/symtab/Definitions.scala
@@ -65,14 +65,14 @@ trait Definitions {
// the scala value classes
lazy val UnitClass = newClass(ScalaPackageClass, nme.Unit, anyvalparam).setFlag(ABSTRACT | FINAL)
- lazy val ByteClass = newValueClass(nme.Byte, 'B')
- lazy val ShortClass = newValueClass(nme.Short, 'S')
- lazy val CharClass = newValueClass(nme.Char, 'C')
- lazy val IntClass = newValueClass(nme.Int, 'I')
- lazy val LongClass = newValueClass(nme.Long, 'L')
- lazy val FloatClass = newValueClass(nme.Float, 'F')
- lazy val DoubleClass = newValueClass(nme.Double, 'D')
- lazy val BooleanClass = newValueClass(nme.Boolean, 'Z')
+ lazy val ByteClass = newValueClass(nme.Byte, 'B', 1)
+ lazy val ShortClass = newValueClass(nme.Short, 'S', 2)
+ lazy val CharClass = newValueClass(nme.Char, 'C', 2)
+ lazy val IntClass = newValueClass(nme.Int, 'I', 3)
+ lazy val LongClass = newValueClass(nme.Long, 'L', 4)
+ lazy val FloatClass = newValueClass(nme.Float, 'F', 5)
+ lazy val DoubleClass = newValueClass(nme.Double, 'D', 6)
+ lazy val BooleanClass = newValueClass(nme.Boolean, 'Z', -1)
def Boolean_and = getMember(BooleanClass, nme.ZAND)
def Boolean_or = getMember(BooleanClass, nme.ZOR)
@@ -539,8 +539,9 @@ trait Definitions {
val refClass = new HashMap[Symbol, Symbol]
val abbrvTag = new HashMap[Symbol, Char]
+ val numericWidth = new HashMap[Symbol, Int]
- private def newValueClass(name: Name, tag: Char): Symbol = {
+ private def newValueClass(name: Name, tag: Char, width: Int): Symbol = {
val boxedName = sn.Boxed(name)
val clazz = newClass(ScalaPackageClass, name, anyvalparam) setFlag (ABSTRACT | FINAL)
@@ -549,6 +550,7 @@ trait Definitions {
boxedArrayClass(clazz) = getClass("scala.runtime.Boxed" + name + "Array")
refClass(clazz) = getClass("scala.runtime." + name + "Ref")
abbrvTag(clazz) = tag
+ if (width > 0) numericWidth(clazz) = width
val module = ScalaPackageClass.newModule(NoPosition, name)
ScalaPackageClass.info.decls.enter(module)
@@ -693,7 +695,13 @@ trait Definitions {
/** Is symbol a numeric value class? */
def isNumericValueClass(sym: Symbol): Boolean =
- (sym ne BooleanClass) && (boxedClass contains sym)
+ numericWidth contains sym
+
+ /** Is symbol a numeric value class? */
+ def isNumericValueType(tp: Type): Boolean = tp match {
+ case TypeRef(_, sym, _) => isNumericValueClass(sym)
+ case _ => false
+ }
def signature(tp: Type): String = {
def erasure(tp: Type): Type = tp match {
diff --git a/src/compiler/scala/tools/nsc/symtab/Types.scala b/src/compiler/scala/tools/nsc/symtab/Types.scala
index 4d7d200c58..e155f18489 100644
--- a/src/compiler/scala/tools/nsc/symtab/Types.scala
+++ b/src/compiler/scala/tools/nsc/symtab/Types.scala
@@ -580,7 +580,7 @@ trait Types {
/** Is this type a subtype of that type? */
def <:<(that: Type): Boolean = {
// val startTime = if (util.Statistics.enabled) System.nanoTime() else 0l
- val result =
+// val result =
((this eq that) ||
(if (explainSwitch) explain("<:", isSubType, this, that)
else isSubType(this, that, AnyDepth)))
@@ -588,9 +588,23 @@ trait Types {
// subtypeNanos += System.nanoTime() - startTime
// subtypeCount += 1
// }
- result
+// result
}
+ /** Is this type a weak subtype of that type? True also for numeric types, i.e. Int weak_<:< Long.
+ */
+ def weak_<:<(that: Type): Boolean =
+// val startTime = if (util.Statistics.enabled) System.nanoTime() else 0l
+// val result =
+ ((this eq that) ||
+ (if (explainSwitch) explain("weak_<:", isWeakSubType, this, that)
+ else isWeakSubType(this, that)))
+// if (util.Statistics.enabled) {
+// subtypeNanos += System.nanoTime() - startTime
+// subtypeCount += 1
+// }
+// result
+
/** Is this type equivalent to that type? */
def =:=(that: Type): Boolean = (
(this eq that) ||
@@ -1974,7 +1988,7 @@ A type's typeSymbol should never be inspected directly.
object TypeVar {
def unapply(tv: TypeVar): Some[(Type, TypeConstraint)] = Some((tv.origin, tv.constr))
def apply(origin: Type, constr: TypeConstraint) = new TypeVar(origin, constr, List(), List())
- def apply(tparam: Symbol) = new TypeVar(tparam.tpeHK, new TypeConstraint(List(),List()), List(), tparam.typeParams)
+ def apply(tparam: Symbol) = new TypeVar(tparam.tpeHK, new TypeConstraint, List(), tparam.typeParams)
def apply(origin: Type, constr: TypeConstraint, args: List[Type], params: List[Symbol]) = new TypeVar(origin, constr, args, params)
}
@@ -2042,19 +2056,25 @@ A type's typeSymbol should never be inspected directly.
* registerBound returns whether this TypeVar could plausibly be a subtype of tp and,
* if so, tracks tp as a upper bound of this type variable
*/
- def registerBound(tp: Type, isLowerBound: Boolean): Boolean = { //println("regBound: "+(safeToString, debugString(tp), isLowerBound)) //@MDEBUG
+ def registerBound(tp: Type, isLowerBound: Boolean, numBound: Boolean = false): Boolean = { //println("regBound: "+(safeToString, debugString(tp), isLowerBound)) //@MDEBUG
if(isLowerBound) assert(tp != this)
undoLog record this
def checkSubtype(tp1: Type, tp2: Type) =
- if(isLowerBound) tp1 <:< tp2
- else tp2 <:< tp1
+ if (numBound)
+ if (isLowerBound) tp1 weak_<:< tp2
+ else tp2 weak_<:< tp1
+ else
+ if(isLowerBound) tp1 <:< tp2
+ else tp2 <:< tp1
+
def addBound(tp: Type) = {
- if(isLowerBound) constr.lobounds = tp :: constr.lobounds
- else constr.hibounds = tp :: constr.hibounds
+ if (isLowerBound) constr.addLoBound(tp, numBound)
+ else constr.addHiBound(tp, numBound)
// println("addedBound: "+(this, tp)) // @MDEBUG
- }
+ }
+
def checkArgs(args1: List[Type], args2: List[Type], params: List[Symbol]) =
if(isLowerBound) isSubArgs(args1, args2, params)
else isSubArgs(args2, args1, params)
@@ -2086,7 +2106,7 @@ A type's typeSymbol should never be inspected directly.
undoLog record this
val newInst = wildcardToTypeVarMap(tp)
- if (constr.lobounds.forall(_ <:< newInst) && constr.hibounds.forall(newInst <:< _)) {
+ if (constr.isWithinBounds(newInst)) {
setInst(tp)
true
} else false
@@ -2588,25 +2608,61 @@ A type's typeSymbol should never be inspected directly.
/** A class expressing upper and lower bounds constraints of type variables,
* as well as their instantiations.
*/
- class TypeConstraint(lo0: List[Type], hi0: List[Type]) {
+ class TypeConstraint(lo0: List[Type], hi0: List[Type], numlo0: Type, numhi0: Type) {
//var self: Type = _ //DEBUG
+ def this(lo0: List[Type], hi0: List[Type]) = this(lo0, hi0, NoType, NoType)
def this() = this(List(), List())
- var lobounds: List[Type] = lo0
- var hibounds: List[Type] = hi0
+
+ private var lobounds = lo0
+ private var hibounds = hi0
+ private var numlo = numlo0
+ private var numhi = numhi0
+
+ def loBounds: List[Type] = if (numlo == NoType) lobounds else numlo :: lobounds
+ def hiBounds: List[Type] = if (numhi == NoType) hibounds else numhi :: hibounds
+
+/* not needed
+ def numLoBound: Type = numlo
+ def numHiBound: Type = numhi
+ def nonNumLoBounds: List[Type] = lobounds
+ def nonNumHiBounds: List[Type] = hibounds
+*/
+
+ def addLoBound(tp: Type, numBound: Boolean = false) {
+ if (numBound && isNumericValueType(tp)) {
+ if (!isNumericSubType(tp, numlo)) numlo = tp
+ } else {
+ lobounds = tp :: lobounds
+ }
+ }
+
+ def addHiBound(tp: Type, numBound: Boolean = false) {
+ if (numBound && isNumericValueType(tp)) {
+ if (!isNumericSubType(numhi, tp)) numhi = tp
+ } else {
+ hibounds = tp :: hibounds
+ }
+ }
+
+ def isWithinBounds(tp: Type): Boolean =
+ lobounds.forall(_ <:< tp) &&
+ hibounds.forall(tp <:< _) &&
+ (numlo == NoType || (numlo weak_<:< tp)) &&
+ (numhi == NoType || (tp weak_<:< numhi))
var inst: Type = NoType // @M reduce visibility?
def instValid = (inst ne null) && (inst ne NoType)
def cloneInternal = {
- val tc = new TypeConstraint(lobounds, hibounds)
+ val tc = new TypeConstraint(lobounds, hibounds, numlo, numhi)
tc.inst = inst
tc
}
override def toString =
- (lobounds map (_.safeToString)).mkString("[ _>:(", ",", ") ") +
- (hibounds map (_.safeToString)).mkString("| _<:(", ",", ") ] _= ") +
+ (loBounds map (_.safeToString)).mkString("[ _>:(", ",", ") ") +
+ (hiBounds map (_.safeToString)).mkString("| _<:(", ",", ") ] _= ") +
inst.safeToString
}
@@ -4106,152 +4162,6 @@ A type's typeSymbol should never be inspected directly.
firstTry
}
-/* DEAD
- /** Does type `tp1' conform to `tp2'?
- */
- private def isSubType1(tp1: Type, tp2: Type, depth: Int): Boolean = {
- ((tp1, tp2) match {
- case (ErrorType, _) => true
- case (WildcardType, _) => true
- case (_, ErrorType) => true
- case (_, WildcardType) => true
-
- case (NoType, _) => false
- case (NoPrefix, _) => tp2 == NoPrefix || tp2.typeSymbol.isPackageClass
- case (_, NoType) => false
- case (_, NoPrefix) => tp1 == NoPrefix || tp1.typeSymbol.isPackageClass
-
- case (ThisType(_), ThisType(_)) => tp1 =:= tp2
- case (ThisType(_), SingleType(_, _)) => tp1 =:= tp2
- case (SingleType(_, _), ThisType(_)) => tp1 =:= tp2
- case (SingleType(_, _), SingleType(_, _)) => tp1 =:= tp2
- case (ConstantType(_), ConstantType(_)) => tp1 =:= tp2
- case (TypeRef(pre1, sym1, args1), TypeRef(pre2, sym2, args2))
- if !(tp1.isHigherKinded || tp2.isHigherKinded) =>
- //Console.println("isSubType " + tp1 + " " + tp2);//DEBUG
- ((if (sym1 == sym2) phase.erasedTypes || pre1 <:< pre2
- else (sym1.name == sym2.name) && isUnifiable(pre1, pre2)) &&
- (sym2 == AnyClass || isSubArgs(args1, args2, sym1.typeParams)) //@M: Any is kind-polymorphic
- ||
- sym1.isAbstractType && isDifferentTypeConstructor(tp1, tp1.bounds.hi) && (tp1.bounds.hi <:< tp2)
- ||
- sym2.isAbstractType && isDifferentTypeConstructor(tp2, tp2.bounds.lo) && (tp1 <:< tp2.bounds.lo)
- ||
- sym2.isClass &&
- ({ val base = tp1 baseType sym2; !(base eq tp1) && (base <:< tp2) })
- ||
- sym1 == NothingClass
- ||
- //{ Console.println("last chance " + sym1 + " " + sym2 + " " + sym2.isClass + " " + (sym2 isSubClass ObjectClass)); true } &&
- sym1 == NullClass &&
- sym2.isClass && (sym2 isNonBottomSubClass ObjectClass) && (!(tp2.normalize.typeSymbol isNonBottomSubClass NotNullClass))
- ||
- {
- val tp1n = normalizePlus(tp1)
- val tp2n = normalizePlus(tp2)
- ((tp1n ne tp1) || (tp2n ne tp2)) && isSubType(tp1n, tp2n, depth)
- })
- case (MethodType(params1, res1), MethodType(params2, res2)) =>
- (params1.length == params2.length &&
- matchingParams(tp1.paramTypes, tp2.paramTypes, tp1.isInstanceOf[JavaMethodType], tp2.isInstanceOf[JavaMethodType]) &&
- (res1 <:< res2) &&
- tp1.isInstanceOf[ImplicitMethodType] == tp2.isInstanceOf[ImplicitMethodType])
- case (PolyType(tparams1, res1), PolyType(tparams2, res2)) =>
- tparams1.length == tparams2.length && {
- if(tparams1.isEmpty) res1 <:< res2 // fast-path: monomorphic nullary method type
- else if(tparams1.head.owner.isMethod) { // fast-path: polymorphic method type -- type params cannot be captured
- List.forall2(tparams1, tparams2)((p1, p2) =>
- p2.info.substSym(tparams2, tparams1) <:< p1.info) &&
- res1 <:< res2.substSym(tparams2, tparams1)
- } else { // normalized higher-kinded type
- //@M for an example of why we need to generate fresh symbols, see neg/tcpoly_ticket2101.scala
- val tpsFresh = cloneSymbols(tparams1) // @M cloneSymbols(tparams2) should be equivalent -- TODO: check
-
- (List.forall2(tparams1, tparams2)((p1, p2) =>
- p2.info.substSym(tparams2, tpsFresh) <:< p1.info.substSym(tparams1, tpsFresh)) &&
- res1.substSym(tparams1, tpsFresh) <:< res2.substSym(tparams2, tpsFresh))
- //@M TODO: should also check that the kinds of the type parameters conform (ticket 2066)
- //@M the forall in the previous test could be optimised to the following,
- // but not worth the extra complexity since it only shaves 1s from quick.comp
- // (List.forall2(tpsFresh/*optimisation*/, tparams2)((p1, p2) =>
- // p2.info.substSym(tparams2, tpsFresh) <:< p1.info /*optimisation, == (p1 from tparams1).info.substSym(tparams1, tpsFresh)*/) &&
- // this optimisation holds because inlining cloneSymbols in `val tpsFresh = cloneSymbols(tparams1)` gives:
- // val tpsFresh = tparams1 map (_.cloneSymbol)
- // for (tpFresh <- tpsFresh) tpFresh.setInfo(tpFresh.info.substSym(tparams1, tpsFresh))
- }
- }
- case (TypeBounds(lo1, hi1), TypeBounds(lo2, hi2)) =>
- lo2 <:< lo1 && hi1 <:< hi2
- case (AnnotatedType(_,_,_), _) =>
- annotationsConform(tp1, tp2) && tp1.withoutAnnotations <:< tp2.withoutAnnotations
- case (_, AnnotatedType(_,_,_)) =>
- annotationsConform(tp1, tp2) && tp1.withoutAnnotations <:< tp2.withoutAnnotations
- case (BoundedWildcardType(bounds), _) =>
- bounds.lo <:< tp2
- case (_, BoundedWildcardType(bounds)) =>
- tp1 <:< bounds.hi
- case (tp, tv @ TypeVar(_,_)) =>
- tv.registerBound(tp, true)
- case (tv @ TypeVar(_,_), tp) =>
- tv.registerBound(tp, false)
- case (_, _) if (tp1.isHigherKinded || tp2.isHigherKinded) =>
- (tp1.typeSymbol == NothingClass
- ||
- tp2.typeSymbol == AnyClass // @M Any and Nothing are super-type resp. subtype of every well-kinded type
- || // @M! normalize reduces higher-kinded case to PolyType's
- (tp1.isHigherKinded && tp2.isHigherKinded) && {
- val tp1a = tp1.normalize
- val tp2a = tp2.normalize
- assert((tp1a ne tp1) || (tp2a ne tp2))
- isSubType0(tp1a, tp2a, depth)
- })
- case (_, TypeRef(pre2, sym2, args2))
- if (sym2.isAbstractType && isDifferentTypeConstructor(tp2, tp2.bounds.lo) && (tp1 <:< tp2.bounds.lo) ||
- sym2 == NotNullClass && tp1.isNotNull) =>
- true
- case (_, TypeRef(pre2, sym2, args2))
- if (sym2 == SingletonClass && tp1.isStable) =>
- true
- case (_, RefinedType(parents2, ref2)) =>
- (parents2 forall (tp2 => tp1 <:< tp2)) &&
- (ref2.toList forall tp1.specializes) /* &&
- removed, replaced by stricter condition on stable values.
- (tp1.typeSymbol != NullClass || !parents2.exists(_.typeSymbol.isAbstractType))
-*/
- case (ExistentialType(_, _), _) =>
- try {
- skolemizationLevel += 1
- tp1.skolemizeExistential(NoSymbol, null) <:< tp2
- } finally {
- skolemizationLevel -= 1
- }
- case (_, et: ExistentialType) if et.withTypeVars(tp1 <:< _, depth) =>
- true
- case (RefinedType(parents1, ref1), _) =>
- parents1 exists (_ <:< tp2)
- case (_, NotNullType(ntp2)) =>
- tp1.isNotNull && tp1 <:< ntp2
- case (NotNullType(ntp1), _) =>
- ntp1 <:< tp2
- case ((_: ThisType | _: SingleType | _: ConstantType), _) =>
- tp1.underlying <:< tp2
-
- case (TypeRef(pre1, sym1, args1), _) =>
- (sym1 == NothingClass && tp2 <:< AnyClass.tpe
- ||
- sym1 == NullClass && tp2.isInstanceOf[SingletonType] && (tp1 <:< tp2.widen)
- ||
- sym1.isAbstractType && (tp1.bounds.hi <:< tp2))
- case _ =>
- false
- }) || {
- val tp1n = normalizePlus(tp1)
- val tp2n = normalizePlus(tp2)
- ((tp1n ne tp1) || (tp2n ne tp2)) && isSubType(tp1n, tp2n, depth)
- }
- }
-*/
-
/** Are `tps1' and `tps2' lists of equal length such
* that all elements of `tps1' conform to corresponding elements
* of `tps2'?
@@ -4372,51 +4282,39 @@ A type's typeSymbol should never be inspected directly.
}
if (!cyclic) {
if (up) {
- if (bound.typeSymbol != AnyClass) {
- tvar.constr.hibounds =
- bound.instantiateTypeParams(tparams, tvars) :: tvar.constr.hibounds
- }
+ if (bound.typeSymbol != AnyClass)
+ tvar.constr addHiBound bound.instantiateTypeParams(tparams, tvars)
for (tparam2 <- tparams)
if (tparam2.info.bounds.lo =:= tparam.tpe) //@M TODO: should probably be .tpeHK
- tvar.constr.hibounds =
- tparam2.tpe.instantiateTypeParams(tparams, tvars) :: tvar.constr.hibounds
+ tvar.constr addHiBound tparam2.tpe.instantiateTypeParams(tparams, tvars)
} else {
- if (bound.typeSymbol != NothingClass && bound.typeSymbol != tparam) {
- tvar.constr.lobounds =
- bound.instantiateTypeParams(tparams, tvars) :: tvar.constr.lobounds
- }
+ if (bound.typeSymbol != NothingClass && bound.typeSymbol != tparam)
+ tvar.constr addLoBound bound.instantiateTypeParams(tparams, tvars)
for (tparam2 <- tparams)
if (tparam2.info.bounds.hi =:= tparam.tpe) //@M TODO: should probably be .tpeHK
- tvar.constr.lobounds =
- tparam2.tpe.instantiateTypeParams(tparams, tvars) :: tvar.constr.lobounds
+ tvar.constr addLoBound tparam2.tpe.instantiateTypeParams(tparams, tvars)
}
}
tvar.constr.inst = NoType // necessary because hibounds/lobounds may contain tvar
// println("solveOne(useGlb, glb, lub): "+ (up, //@MDEBUG
- // if (depth != AnyDepth) glb(tvar.constr.hibounds, depth) else glb(tvar.constr.hibounds),
- // if (depth != AnyDepth) lub(tvar.constr.lobounds, depth) else lub(tvar.constr.lobounds)))
+ // if (depth != AnyDepth) glb(tvar.constr.hiBounds, depth) else glb(tvar.constr.hiBounds),
+ // if (depth != AnyDepth) lub(tvar.constr.loBounds, depth) else lub(tvar.constr.loBounds)))
tvar setInst (
if (up) {
- if (depth != AnyDepth) glb(tvar.constr.hibounds, depth) else glb(tvar.constr.hibounds)
+ if (depth != AnyDepth) glb(tvar.constr.hiBounds, depth) else glb(tvar.constr.hiBounds)
} else {
- if (depth != AnyDepth) lub(tvar.constr.lobounds, depth) else lub(tvar.constr.lobounds)
+ if (depth != AnyDepth) lub(tvar.constr.loBounds, depth) else lub(tvar.constr.loBounds)
})
- // Console.println("solving "+tvar+" "+up+" "+(if (up) (tvar.constr.hibounds) else tvar.constr.lobounds)+((if (up) (tvar.constr.hibounds) else tvar.constr.lobounds) map (_.widen))+" = "+tvar.constr.inst)//@MDEBUG
+ // Console.println("solving "+tvar+" "+up+" "+(if (up) (tvar.constr.hiBounds) else tvar.constr.loBounds)+((if (up) (tvar.constr.hiBounds) else tvar.constr.loBounds) map (_.widen))+" = "+tvar.constr.inst)//@MDEBUG
}
}
for ((tvar, (tparam, variance)) <- config)
solveOne(tvar, tparam, variance)
- var ok = true
- for (tvar <- tvars)
- if (!(tvar.constr.lobounds forall (_ <:< tvar.constr.inst)) ||
- !(tvar.constr.hibounds forall (tvar.constr.inst <:< _))) {
- ok = false
- }
- ok
+ tvars forall (tvar => tvar.constr.isWithinBounds(tvar.constr.inst))
}
/** Do type arguments `targs' conform to formal parameters
@@ -4537,6 +4435,46 @@ A type's typeSymbol should never be inspected directly.
(strippedTypes, quantified)
}
+ def weakLub(ts: List[Type]) =
+ if (ts.nonEmpty && (ts forall isNumericValueType)) numericLub(ts)
+ else lub(ts)
+
+ def weakGlb(ts: List[Type]) =
+ if (ts.nonEmpty && (ts forall isNumericValueType)) numericGlb(ts)
+ else glb(ts)
+
+ def numericLub(ts: List[Type]) =
+ (ByteClass.tpe /: ts) ((t1, t2) => if (isNumericSubType(t1, t2)) t2 else t1)
+
+ def numericGlb(ts: List[Type]) =
+ (DoubleClass.tpe /: ts) ((t1, t2) => if (isNumericSubType(t1, t2)) t1 else t2)
+
+ def isWeakSubType(tp1: Type, tp2: Type) =
+ tp1 match {
+ case TypeRef(_, sym1, _) if isNumericValueClass(sym1) =>
+ tp2 match {
+ case TypeRef(_, sym2, _) if isNumericValueClass(sym2) =>
+ sym1 == sym2 || numericWidth(sym1) < numericWidth(sym2)
+ case tv2 @ TypeVar(_, _) =>
+ tv2.registerBound(tp1, isLowerBound = true, numBound = true)
+ case _ =>
+ isSubType(tp1, tp2)
+ }
+ case tv1 @ TypeVar(_, _) =>
+ tp2 match {
+ case TypeRef(_, sym2, _) if isNumericValueClass(sym2) =>
+ tv1.registerBound(tp2, isLowerBound = false, numBound = true)
+ case _ =>
+ isSubType(tp1, tp2)
+ }
+ case _ =>
+ isSubType(tp1, tp2)
+ }
+
+ def isNumericSubType(tp1: Type, tp2: Type) =
+ isNumericValueType(tp1) && isNumericValueType(tp2) &&
+ (tp1.typeSymbol == tp2.typeSymbol || numericWidth(tp1.typeSymbol) < numericWidth(tp2.typeSymbol))
+
def lub(ts: List[Type]): Type = lub(ts, lubDepth(ts))
/** The least upper bound wrt &lt;:&lt; of a list of types */
diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala
index b01a765714..46a4066624 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala
@@ -151,8 +151,8 @@ trait Infer {
variances: List[Int], upper: Boolean, depth: Int): List[Type] = {
// def boundsString(tvar: TypeVar) =
// "\n "+
-// ((tvar.constr.lobounds map (_ + " <: " + tvar.origin.typeSymbol.name)) :::
-// (tvar.constr.hibounds map (tvar.origin.typeSymbol.name + " <: " + _)) mkString ", ")
+// ((tvar.constr.loBounds map (_ + " <: " + tvar.origin.typeSymbol.name)) :::
+// (tvar.constr.hiBounds map (tvar.origin.typeSymbol.name + " <: " + _)) mkString ", ")
if (!solve(tvars, tparams, variances, upper, depth)) {
// no panic, it's good enough to just guess a solution, we'll find out
// later whether it works.
@@ -443,7 +443,12 @@ trait Infer {
def isCompatible(tp: Type, pt: Type): Boolean = {
val tp1 = normalize(tp)
- (tp1 <:< pt) || isCoercible(tp1, pt) //@M: was isCoercible(tp, pt), but I assume this was a typo...
+ (tp1 <:< pt) || isCoercible(tp1, pt)
+ }
+
+ def isCompatibleArg(tp: Type, pt: Type): Boolean = {
+ val tp1 = normalize(tp)
+ (tp1 weak_<:< pt) || isCoercible(tp1, pt)
}
def isWeaklyCompatible(tp: Type, pt: Type): Boolean =
@@ -467,8 +472,8 @@ trait Infer {
def isCoercible(tp: Type, pt: Type): Boolean = false
- def isCompatible(tps: List[Type], pts: List[Type]): Boolean =
- List.map2(tps, pts)((tp, pt) => isCompatible(tp, pt)) forall (x => x)
+ def isCompatibleArgs(tps: List[Type], pts: List[Type]): Boolean =
+ List.map2(tps, pts)((tp, pt) => isCompatibleArg(tp, pt)) forall (x => x)
/* -- Type instantiation------------------------------------------------ */
@@ -547,25 +552,26 @@ trait Infer {
/** Map type variable to its instance, or, if `variance' is covariant/contravariant,
* to its upper/lower bound */
def instantiateToBound(tvar: TypeVar, variance: Int): Type = try {
- //Console.println("instantiate "+tvar+tvar.constr+" variance = "+variance);//DEBUG
- if (tvar.constr.inst != NoType) {
- instantiate(tvar.constr.inst)
- } else if ((variance & COVARIANT) != 0 && !tvar.constr.hibounds.isEmpty) {
- tvar setInst glb(tvar.constr.hibounds)
- assertNonCyclic(tvar)//debug
- instantiate(tvar.constr.inst)
- } else if ((variance & CONTRAVARIANT) != 0 && !tvar.constr.lobounds.isEmpty) {
- tvar setInst lub(tvar.constr.lobounds)
+ lazy val hiBounds = tvar.constr.hiBounds
+ lazy val loBounds = tvar.constr.loBounds
+ lazy val upper = glb(hiBounds)
+ lazy val lower = lub(loBounds)
+ def setInst(tp: Type): Type = {
+ tvar setInst tp
assertNonCyclic(tvar)//debug
instantiate(tvar.constr.inst)
- } else if (!tvar.constr.hibounds.isEmpty && !tvar.constr.lobounds.isEmpty &&
- glb(tvar.constr.hibounds) <:< lub(tvar.constr.lobounds)) {
- tvar setInst glb(tvar.constr.hibounds)
- assertNonCyclic(tvar)//debug
+ }
+ //Console.println("instantiate "+tvar+tvar.constr+" variance = "+variance);//DEBUG
+ if (tvar.constr.inst != NoType)
instantiate(tvar.constr.inst)
- } else {
+ else if ((variance & COVARIANT) != 0 && hiBounds.nonEmpty)
+ setInst(upper)
+ else if ((variance & CONTRAVARIANT) != 0 && loBounds.nonEmpty)
+ setInst(lower)
+ else if (hiBounds.nonEmpty && loBounds.nonEmpty && upper <:< lower)
+ setInst(upper)
+ else
WildcardType
- }
} catch {
case ex: NoInstance => WildcardType
}
@@ -660,8 +666,8 @@ trait Infer {
// Then define remaining type variables from argument types.
List.map2(argtpes, formals) {(argtpe, formal) =>
//@M isCompatible has side-effect: isSubtype0 will register subtype checks in the tvar's bounds
- if (!isCompatible(argtpe.deconst.instantiateTypeParams(tparams, tvars),
- formal.instantiateTypeParams(tparams, tvars))) {
+ if (!isCompatibleArg(argtpe.deconst.instantiateTypeParams(tparams, tvars),
+ formal.instantiateTypeParams(tparams, tvars))) {
throw new DeferredNoInstance(() =>
"argument expression's type is not compatible with formal parameter type" +
foundReqMsg(argtpe.deconst.instantiateTypeParams(tparams, tvars), formal.instantiateTypeParams(tparams, tvars)))
@@ -776,8 +782,7 @@ trait Infer {
def typesCompatible(argtpes: List[Type]) = {
val restpe = ftpe.resultType(argtpes)
if (undetparams.isEmpty) {
- (isCompatible(argtpes, formals) &&
- isWeaklyCompatible(restpe, pt))
+ isCompatibleArgs(argtpes, formals) && isWeaklyCompatible(restpe, pt)
} else {
try {
val uninstantiated = new ListBuffer[Symbol]
@@ -861,7 +866,11 @@ trait Infer {
case mt: ImplicitMethodType =>
isAsSpecific(ftpe1.resultType, ftpe2)
case MethodType(params @ (x :: xs), _) =>
- isApplicable(List(), ftpe2, params map (_.tpe), WildcardType)
+ var argtpes = params map (_.tpe)
+ if (isVarArgs(argtpes) && isVarArgs(ftpe2.paramTypes))
+ argtpes = argtpes map (argtpe =>
+ if (isRepeatedParamType(argtpe)) argtpe.typeArgs.head else argtpe)
+ isApplicable(List(), ftpe2, argtpes, WildcardType)
case PolyType(tparams, mt: ImplicitMethodType) =>
isAsSpecific(PolyType(tparams, mt.resultType), ftpe2)
case PolyType(_, MethodType(params @ (x :: xs), _)) =>
@@ -917,7 +926,7 @@ trait Infer {
val subClassCount = (if (isInProperSubClassOrObject(sym1, sym2)) 1 else 0) -
(if (isInProperSubClassOrObject(sym2, sym1)) 1 else 0)
//println("is more specific? "+sym1+sym1.locationString+"/"+sym2+sym2.locationString+":"+
- // specificCount+"/"+subClassCount+"/"+)
+ // specificCount+"/"+subClassCount)
specificCount + subClassCount > 0
}
}
@@ -1309,7 +1318,7 @@ trait Infer {
val instType = toOrigin(tvar.constr.inst)
val (loBounds, hiBounds) =
if (instType != NoType && isFullyDefined(instType)) (List(instType), List(instType))
- else (tvar.constr.lobounds, tvar.constr.hibounds)
+ else (tvar.constr.loBounds, tvar.constr.hiBounds)
val lo = lub(tparam.info.bounds.lo :: loBounds map toOrigin)
val hi = glb(tparam.info.bounds.hi :: hiBounds map toOrigin)
(lo, hi)
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index e64abf9204..389e26f273 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -937,6 +937,8 @@ trait Typers { self: Analyzer =>
// infinite expansion if pt is constant type ()
if (sym == UnitClass && tree.tpe <:< AnyClass.tpe) // (12)
return typed(atPos(tree.pos)(Block(List(tree), Literal(()))), mode, pt)
+ else if (isNumericValueClass(sym) && isNumericSubType(tree.tpe, pt))
+ return typed(atPos(tree.pos)(Select(tree, "to"+sym.name)), mode, pt)
case _ =>
}
if (!context.undetparams.isEmpty) {
@@ -2755,7 +2757,7 @@ trait Typers { self: Analyzer =>
*/
protected def typed1(tree: Tree, mode: Int, pt: Type): Tree = {
//Console.println("typed1("+tree.getClass()+","+Integer.toHexString(mode)+","+pt+")")
- def ptOrLub(tps: List[Type]) = if (isFullyDefined(pt)) pt else lub(tps map (_.deconst))
+ def ptOrLub(tps: List[Type]) = if (isFullyDefined(pt)) pt else weakLub(tps map (_.deconst))
//@M! get the type of the qualifier in a Select tree, otherwise: NoType
def prefixType(fun: Tree): Type = fun match {
@@ -2920,9 +2922,14 @@ trait Typers { self: Analyzer =>
val thenp1 = typed(thenp, UnitClass.tpe)
treeCopy.If(tree, cond1, thenp1, elsep) setType thenp1.tpe
} else {
- val thenp1 = typed(thenp, pt)
- val elsep1 = typed(elsep, pt)
- treeCopy.If(tree, cond1, thenp1, elsep1) setType ptOrLub(List(thenp1.tpe, elsep1.tpe))
+ var thenp1 = typed(thenp, pt)
+ var elsep1 = typed(elsep, pt)
+ val owntype = ptOrLub(List(thenp1.tpe, elsep1.tpe))
+ if (isNumericValueType(owntype)) {
+ thenp1 = adapt(thenp1, mode, owntype)
+ elsep1 = adapt(elsep1, mode, owntype)
+ }
+ treeCopy.If(tree, cond1, thenp1, elsep1) setType owntype
}
}
@@ -3553,6 +3560,9 @@ trait Typers { self: Analyzer =>
}
}
+ def adaptCase(cdef: CaseDef, tpe: Type): CaseDef =
+ treeCopy.CaseDef(cdef, cdef.pat, cdef.guard, adapt(cdef.body, mode, tpe))
+
// begin typed1
val sym: Symbol = tree.symbol
if ((sym ne null) && (sym ne NoSymbol)) sym.initialize
@@ -3648,20 +3658,28 @@ trait Typers { self: Analyzer =>
typed1(atPos(tree.pos) { Function(params, body) }, mode, pt)
} else {
val selector1 = checkDead(typed(selector))
- val cases1 = typedCases(tree, cases, selector1.tpe.widen, pt)
- treeCopy.Match(tree, selector1, cases1) setType ptOrLub(cases1 map (_.tpe))
+ var cases1 = typedCases(tree, cases, selector1.tpe.widen, pt)
+ val owntype = ptOrLub(cases1 map (_.tpe))
+ if (isNumericValueType(owntype)) {
+ cases1 = cases1 map (adaptCase(_, owntype))
+ }
+ treeCopy.Match(tree, selector1, cases1) setType owntype
}
case Return(expr) =>
typedReturn(expr)
case Try(block, catches, finalizer) =>
- val block1 = typed(block, pt)
- val catches1 = typedCases(tree, catches, ThrowableClass.tpe, pt)
+ var block1 = typed(block, pt)
+ var catches1 = typedCases(tree, catches, ThrowableClass.tpe, pt)
val finalizer1 = if (finalizer.isEmpty) finalizer
else typed(finalizer, UnitClass.tpe)
- treeCopy.Try(tree, block1, catches1, finalizer1)
- .setType(ptOrLub(block1.tpe :: (catches1 map (_.tpe))))
+ val owntype = ptOrLub(block1.tpe :: (catches1 map (_.tpe)))
+ if (isNumericValueType(owntype)) {
+ block1 = adapt(block1, mode, owntype)
+ catches1 = catches1 map (adaptCase(_, owntype))
+ }
+ treeCopy.Try(tree, block1, catches1, finalizer1) setType owntype
case Throw(expr) =>
val expr1 = typed(expr, ThrowableClass.tpe)