summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2013-03-13 11:13:09 -0700
committerPaul Phillips <paulp@improving.org>2013-03-13 11:13:09 -0700
commit79d653a183bf74e27ec77cfd591e9649f154d41b (patch)
tree0eccdac7e0a326b22742469425d2a309755a23b1
parent184e0cc126c9499ea7572621663b14c37dba4734 (diff)
parent6ef63e49f8d762ac02367225ee737ea93f52a738 (diff)
downloadscala-79d653a183bf74e27ec77cfd591e9649f154d41b.tar.gz
scala-79d653a183bf74e27ec77cfd591e9649f154d41b.tar.bz2
scala-79d653a183bf74e27ec77cfd591e9649f154d41b.zip
Merge pull request #2215 from paulp/issue/7228
SI-7228, bug in subtyping.
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Implicits.scala24
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Namers.scala30
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala9
-rw-r--r--src/reflect/scala/reflect/internal/Definitions.scala2
-rw-r--r--src/reflect/scala/reflect/internal/Types.scala4
-rw-r--r--src/reflect/scala/reflect/internal/tpe/TypeComparers.scala31
-rw-r--r--test/files/pos/switch-small.flags1
-rw-r--r--test/files/pos/t7228.scala75
-rw-r--r--test/pending/pos/no-widen-locals.flags1
-rw-r--r--test/pending/pos/no-widen-locals.scala (renamed from test/files/pos/no-widen-locals.scala)0
10 files changed, 128 insertions, 49 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
index 2331f82a58..5b11adf127 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
@@ -268,7 +268,7 @@ trait Implicits {
*/
object Function1 {
val Sym = FunctionClass(1)
- def unapply(tp: Type) = tp match {
+ def unapply(tp: Type) = tp baseType Sym match {
case TypeRef(_, Sym, arg1 :: arg2 :: _) => Some((arg1, arg2))
case _ => None
}
@@ -431,10 +431,8 @@ trait Implicits {
val start = if (Statistics.canEnable) Statistics.startTimer(matchesPtNanos) else null
val result = normSubType(tp, pt) || isView && {
pt match {
- case TypeRef(_, Function1.Sym, arg1 :: arg2 :: Nil) =>
- matchesPtView(tp, arg1, arg2, undet)
- case _ =>
- false
+ case Function1(arg1, arg2) => matchesPtView(tp, arg1, arg2, undet)
+ case _ => false
}
}
if (Statistics.canEnable) Statistics.stopTimer(matchesPtNanos, start)
@@ -575,21 +573,21 @@ trait Implicits {
)
def fail(reason: String): SearchResult = failure(itree, reason)
+ def fallback = typed1(itree, EXPRmode, wildPt)
try {
- val itree1 =
- if (isView) {
- val arg1 :: arg2 :: _ = pt.typeArgs
+ val itree1 = if (!isView) fallback else pt match {
+ case Function1(arg1, arg2) =>
typed1(
atPos(itree.pos)(Apply(itree, List(Ident("<argument>") setType approximate(arg1)))),
EXPRmode,
approximate(arg2)
)
- }
- else
- typed1(itree, EXPRmode, wildPt)
-
- if (context.hasErrors)
+ case _ => fallback
+ }
+ if (context.hasErrors) {
+ log("implicit adapt failed: " + context.errBuffer.head.errMsg)
return fail(context.errBuffer.head.errMsg)
+ }
if (Statistics.canEnable) Statistics.incCounter(typedImplicits)
diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala
index 007c7c6a83..d5da4967be 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala
@@ -805,23 +805,19 @@ trait Namers extends MethodSynthesis {
case _ =>
false
}
-
- val tpe1 = dropIllegalStarTypes(tpe.deconst)
- val tpe2 = tpe1.widen
-
- // This infers Foo.type instead of "object Foo"
- // See Infer#adjustTypeArgs for the polymorphic case.
- if (tpe.typeSymbolDirect.isModuleClass) tpe1
- else if (sym.isVariable || sym.isMethod && !sym.hasAccessorFlag)
- if (tpe2 <:< pt) tpe2 else tpe1
- else if (isHidden(tpe)) tpe2
- // In an attempt to make pattern matches involving method local vals
- // compilable into switches, for a time I had a more generous condition:
- // `if (sym.isFinal || sym.isLocal) tpe else tpe1`
- // This led to issues with expressions like classOf[List[_]] which apparently
- // depend on being deconst-ed here, so this is again the original:
- else if (!sym.isFinal) tpe1
- else tpe
+ val shouldWiden = (
+ !tpe.typeSymbolDirect.isModuleClass // Infer Foo.type instead of "object Foo"
+ && (tpe.widen <:< pt) // Don't widen our way out of conforming to pt
+ && ( sym.isVariable
+ || sym.isMethod && !sym.hasAccessorFlag
+ || isHidden(tpe)
+ )
+ )
+ dropIllegalStarTypes(
+ if (shouldWiden) tpe.widen
+ else if (sym.isFinal) tpe // "final val" allowed to retain constant type
+ else tpe.deconst
+ )
}
/** Computes the type of the body in a ValDef or DefDef, and
* assigns the type to the tpt's node. Returns the type.
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index 92f53f4956..ba34ae4871 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -1124,16 +1124,19 @@ trait Typers extends Adaptations with Tags {
else {
if (mode.inExprModeButNot(FUNmode)) {
pt.dealias match {
- case TypeRef(_, sym, _) =>
+ // The <: Any requirement inhibits attempts to adapt continuation types
+ // to non-continuation types.
+ case TypeRef(_, sym, _) if tree.tpe <:< AnyClass.tpe =>
// note: was if (pt.typeSymbol == UnitClass) but this leads to a potentially
// infinite expansion if pt is constant type ()
- if (sym == UnitClass && tree.tpe <:< AnyClass.tpe) { // (12)
+ if (sym == UnitClass) { // (12)
if (settings.warnValueDiscard.value)
context.unit.warning(tree.pos, "discarded non-Unit value")
return typedPos(tree.pos, mode, pt) {
Block(List(tree), Literal(Constant()))
}
- } else if (isNumericValueClass(sym) && isNumericSubType(tree.tpe, pt)) {
+ }
+ else if (isNumericValueClass(sym) && isNumericSubType(tree.tpe, pt)) {
if (settings.warnNumericWiden.value)
context.unit.warning(tree.pos, "implicit numeric widening")
return typedPos(tree.pos, mode, pt) {
diff --git a/src/reflect/scala/reflect/internal/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala
index fe5a5c81e2..bfba81c654 100644
--- a/src/reflect/scala/reflect/internal/Definitions.scala
+++ b/src/reflect/scala/reflect/internal/Definitions.scala
@@ -1131,7 +1131,7 @@ trait Definitions extends api.StandardDefinitions {
/** Is type's symbol a numeric value class? */
def isNumericValueType(tp: Type): Boolean = tp match {
case TypeRef(_, sym, _) => isNumericValueClass(sym)
- case _ => false
+ case _ => false
}
// todo: reconcile with javaSignature!!!
diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala
index e1433d1893..ce514e9a89 100644
--- a/src/reflect/scala/reflect/internal/Types.scala
+++ b/src/reflect/scala/reflect/internal/Types.scala
@@ -1217,7 +1217,7 @@ trait Types
protected def rewrap(newtp: Type): Type = NotNullType(newtp)
override def isNotNull: Boolean = true
override def notNull = this
- override def deconst: Type = underlying //todo: needed?
+ override def deconst: Type = underlying.deconst //todo: needed?
override def safeToString: String = underlying.toString + " with NotNull"
override def kind = "NotNullType"
}
@@ -1989,7 +1989,7 @@ trait Types
assert(underlying.typeSymbol != UnitClass)
override def isTrivial: Boolean = true
override def isNotNull = value.value != null
- override def deconst: Type = underlying
+ override def deconst: Type = underlying.deconst
override def safeToString: String =
underlying.toString + "(" + value.escapedStringValue + ")"
override def kind = "ConstantType"
diff --git a/src/reflect/scala/reflect/internal/tpe/TypeComparers.scala b/src/reflect/scala/reflect/internal/tpe/TypeComparers.scala
index 82321f61c2..a03ab1610e 100644
--- a/src/reflect/scala/reflect/internal/tpe/TypeComparers.scala
+++ b/src/reflect/scala/reflect/internal/tpe/TypeComparers.scala
@@ -5,6 +5,7 @@ package tpe
import scala.collection.{ mutable }
import Flags._
import util.Statistics
+import scala.annotation.tailrec
trait TypeComparers {
self: SymbolTable =>
@@ -583,9 +584,9 @@ trait TypeComparers {
def isWeakSubType(tp1: Type, tp2: Type) =
- tp1.deconst.normalize match {
+ tp1.dealiasWiden match {
case TypeRef(_, sym1, _) if isNumericValueClass(sym1) =>
- tp2.deconst.normalize match {
+ tp2.deconst.dealias match {
case TypeRef(_, sym2, _) if isNumericValueClass(sym2) =>
isNumericSubClass(sym1, sym2)
case tv2 @ TypeVar(_, _) =>
@@ -594,7 +595,7 @@ trait TypeComparers {
isSubType(tp1, tp2)
}
case tv1 @ TypeVar(_, _) =>
- tp2.deconst.normalize match {
+ tp2.deconst.dealias match {
case TypeRef(_, sym2, _) if isNumericValueClass(sym2) =>
tv1.registerBound(tp2, isLowerBound = false, isNumericBound = true)
case _ =>
@@ -604,14 +605,18 @@ trait TypeComparers {
isSubType(tp1, tp2)
}
- /** The isNumericValueType tests appear redundant, but without them
- * test/continuations-neg/function3.scala goes into an infinite loop.
- * (Even if the calls are to typeSymbolDirect.)
- */
- def isNumericSubType(tp1: Type, tp2: Type): Boolean = (
- isNumericValueType(tp1)
- && isNumericValueType(tp2)
- && isNumericSubClass(tp1.typeSymbol, tp2.typeSymbol)
- )
-
+ def isNumericSubType(tp1: Type, tp2: Type) = (
+ isNumericSubClass(primitiveBaseClass(tp1.dealiasWiden), primitiveBaseClass(tp2.dealias))
+ )
+
+ /** If the given type has a primitive class among its base classes,
+ * the symbol of that class. Otherwise, NoSymbol.
+ */
+ private def primitiveBaseClass(tp: Type): Symbol = {
+ @tailrec def loop(bases: List[Symbol]): Symbol = bases match {
+ case Nil => NoSymbol
+ case x :: xs => if (isPrimitiveValueClass(x)) x else loop(xs)
+ }
+ loop(tp.baseClasses)
+ }
}
diff --git a/test/files/pos/switch-small.flags b/test/files/pos/switch-small.flags
new file mode 100644
index 0000000000..85d8eb2ba2
--- /dev/null
+++ b/test/files/pos/switch-small.flags
@@ -0,0 +1 @@
+-Xfatal-warnings
diff --git a/test/files/pos/t7228.scala b/test/files/pos/t7228.scala
new file mode 100644
index 0000000000..5d936f6529
--- /dev/null
+++ b/test/files/pos/t7228.scala
@@ -0,0 +1,75 @@
+object AdaptWithWeaklyConformantType {
+ implicit class D(d: Double) { def double = d*2 }
+
+ val x1: Int = 1
+ var x2: Int = 2
+ val x3 = 3
+ var x4 = 4
+ final val x5 = 5
+ final var x6 = 6
+
+ def f1 = x1.double
+ def f2 = x2.double
+ def f3 = x3.double
+ def f4 = x4.double
+ def f5 = x5.double
+ def f6 = x6.double
+}
+
+object AdaptAliasWithWeaklyConformantType {
+ implicit class D(d: Double) { def double = d*2 }
+ type T = Int
+
+ val x1: T = 1
+ var x2: T = 2
+ val x3 = (3: T)
+ var x4 = (4: T)
+ final val x5 = (5: T)
+ final var x6 = (6: T)
+
+ def f1 = x1.double
+ def f2 = x2.double
+ def f3 = x3.double
+ def f4 = x4.double
+ def f5 = x5.double
+ def f6 = x6.double
+}
+
+object AdaptToAliasWithWeaklyConformantType {
+ type U = Double
+ implicit class D(d: U) { def double = d*2 }
+
+ val x1: Int = 1
+ var x2: Int = 2
+ val x3 = (3: Int)
+ var x4 = (4: Int)
+ final val x5 = (5: Int)
+ final var x6 = (6: Int)
+
+ def f1 = x1.double
+ def f2 = x2.double
+ def f3 = x3.double
+ def f4 = x4.double
+ def f5 = x5.double
+ def f6 = x6.double
+}
+
+object AdaptAliasToAliasWithWeaklyConformantType {
+ type U = Double
+ type T = Int
+ implicit class D(d: U) { def double = d*2 }
+
+ val x1: T = 1
+ var x2: T = 2
+ val x3 = (3: T)
+ var x4 = (4: T)
+ final val x5 = (5: T)
+ final var x6 = (6: T)
+
+ def f1 = x1.double
+ def f2 = x2.double
+ def f3 = x3.double
+ def f4 = x4.double
+ def f5 = x5.double
+ def f6 = x6.double
+}
diff --git a/test/pending/pos/no-widen-locals.flags b/test/pending/pos/no-widen-locals.flags
new file mode 100644
index 0000000000..85d8eb2ba2
--- /dev/null
+++ b/test/pending/pos/no-widen-locals.flags
@@ -0,0 +1 @@
+-Xfatal-warnings
diff --git a/test/files/pos/no-widen-locals.scala b/test/pending/pos/no-widen-locals.scala
index 013e63f0a2..013e63f0a2 100644
--- a/test/files/pos/no-widen-locals.scala
+++ b/test/pending/pos/no-widen-locals.scala