Skip to content

Commit 79d653a

Browse files
committed
Merge pull request scala#2215 from paulp/issue/7228
SI-7228, bug in subtyping.
2 parents 184e0cc + 6ef63e4 commit 79d653a

File tree

10 files changed

+128
-49
lines changed

10 files changed

+128
-49
lines changed

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

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,7 @@ trait Implicits {
268268
*/
269269
object Function1 {
270270
val Sym = FunctionClass(1)
271-
def unapply(tp: Type) = tp match {
271+
def unapply(tp: Type) = tp baseType Sym match {
272272
case TypeRef(_, Sym, arg1 :: arg2 :: _) => Some((arg1, arg2))
273273
case _ => None
274274
}
@@ -431,10 +431,8 @@ trait Implicits {
431431
val start = if (Statistics.canEnable) Statistics.startTimer(matchesPtNanos) else null
432432
val result = normSubType(tp, pt) || isView && {
433433
pt match {
434-
case TypeRef(_, Function1.Sym, arg1 :: arg2 :: Nil) =>
435-
matchesPtView(tp, arg1, arg2, undet)
436-
case _ =>
437-
false
434+
case Function1(arg1, arg2) => matchesPtView(tp, arg1, arg2, undet)
435+
case _ => false
438436
}
439437
}
440438
if (Statistics.canEnable) Statistics.stopTimer(matchesPtNanos, start)
@@ -575,21 +573,21 @@ trait Implicits {
575573
)
576574

577575
def fail(reason: String): SearchResult = failure(itree, reason)
576+
def fallback = typed1(itree, EXPRmode, wildPt)
578577
try {
579-
val itree1 =
580-
if (isView) {
581-
val arg1 :: arg2 :: _ = pt.typeArgs
578+
val itree1 = if (!isView) fallback else pt match {
579+
case Function1(arg1, arg2) =>
582580
typed1(
583581
atPos(itree.pos)(Apply(itree, List(Ident("<argument>") setType approximate(arg1)))),
584582
EXPRmode,
585583
approximate(arg2)
586584
)
587-
}
588-
else
589-
typed1(itree, EXPRmode, wildPt)
590-
591-
if (context.hasErrors)
585+
case _ => fallback
586+
}
587+
if (context.hasErrors) {
588+
log("implicit adapt failed: " + context.errBuffer.head.errMsg)
592589
return fail(context.errBuffer.head.errMsg)
590+
}
593591

594592
if (Statistics.canEnable) Statistics.incCounter(typedImplicits)
595593

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

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -805,23 +805,19 @@ trait Namers extends MethodSynthesis {
805805
case _ =>
806806
false
807807
}
808-
809-
val tpe1 = dropIllegalStarTypes(tpe.deconst)
810-
val tpe2 = tpe1.widen
811-
812-
// This infers Foo.type instead of "object Foo"
813-
// See Infer#adjustTypeArgs for the polymorphic case.
814-
if (tpe.typeSymbolDirect.isModuleClass) tpe1
815-
else if (sym.isVariable || sym.isMethod && !sym.hasAccessorFlag)
816-
if (tpe2 <:< pt) tpe2 else tpe1
817-
else if (isHidden(tpe)) tpe2
818-
// In an attempt to make pattern matches involving method local vals
819-
// compilable into switches, for a time I had a more generous condition:
820-
// `if (sym.isFinal || sym.isLocal) tpe else tpe1`
821-
// This led to issues with expressions like classOf[List[_]] which apparently
822-
// depend on being deconst-ed here, so this is again the original:
823-
else if (!sym.isFinal) tpe1
824-
else tpe
808+
val shouldWiden = (
809+
!tpe.typeSymbolDirect.isModuleClass // Infer Foo.type instead of "object Foo"
810+
&& (tpe.widen <:< pt) // Don't widen our way out of conforming to pt
811+
&& ( sym.isVariable
812+
|| sym.isMethod && !sym.hasAccessorFlag
813+
|| isHidden(tpe)
814+
)
815+
)
816+
dropIllegalStarTypes(
817+
if (shouldWiden) tpe.widen
818+
else if (sym.isFinal) tpe // "final val" allowed to retain constant type
819+
else tpe.deconst
820+
)
825821
}
826822
/** Computes the type of the body in a ValDef or DefDef, and
827823
* assigns the type to the tpt's node. Returns the type.

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

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1124,16 +1124,19 @@ trait Typers extends Adaptations with Tags {
11241124
else {
11251125
if (mode.inExprModeButNot(FUNmode)) {
11261126
pt.dealias match {
1127-
case TypeRef(_, sym, _) =>
1127+
// The <: Any requirement inhibits attempts to adapt continuation types
1128+
// to non-continuation types.
1129+
case TypeRef(_, sym, _) if tree.tpe <:< AnyClass.tpe =>
11281130
// note: was if (pt.typeSymbol == UnitClass) but this leads to a potentially
11291131
// infinite expansion if pt is constant type ()
1130-
if (sym == UnitClass && tree.tpe <:< AnyClass.tpe) { // (12)
1132+
if (sym == UnitClass) { // (12)
11311133
if (settings.warnValueDiscard.value)
11321134
context.unit.warning(tree.pos, "discarded non-Unit value")
11331135
return typedPos(tree.pos, mode, pt) {
11341136
Block(List(tree), Literal(Constant()))
11351137
}
1136-
} else if (isNumericValueClass(sym) && isNumericSubType(tree.tpe, pt)) {
1138+
}
1139+
else if (isNumericValueClass(sym) && isNumericSubType(tree.tpe, pt)) {
11371140
if (settings.warnNumericWiden.value)
11381141
context.unit.warning(tree.pos, "implicit numeric widening")
11391142
return typedPos(tree.pos, mode, pt) {

src/reflect/scala/reflect/internal/Definitions.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1131,7 +1131,7 @@ trait Definitions extends api.StandardDefinitions {
11311131
/** Is type's symbol a numeric value class? */
11321132
def isNumericValueType(tp: Type): Boolean = tp match {
11331133
case TypeRef(_, sym, _) => isNumericValueClass(sym)
1134-
case _ => false
1134+
case _ => false
11351135
}
11361136

11371137
// todo: reconcile with javaSignature!!!

src/reflect/scala/reflect/internal/Types.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1217,7 +1217,7 @@ trait Types
12171217
protected def rewrap(newtp: Type): Type = NotNullType(newtp)
12181218
override def isNotNull: Boolean = true
12191219
override def notNull = this
1220-
override def deconst: Type = underlying //todo: needed?
1220+
override def deconst: Type = underlying.deconst //todo: needed?
12211221
override def safeToString: String = underlying.toString + " with NotNull"
12221222
override def kind = "NotNullType"
12231223
}
@@ -1989,7 +1989,7 @@ trait Types
19891989
assert(underlying.typeSymbol != UnitClass)
19901990
override def isTrivial: Boolean = true
19911991
override def isNotNull = value.value != null
1992-
override def deconst: Type = underlying
1992+
override def deconst: Type = underlying.deconst
19931993
override def safeToString: String =
19941994
underlying.toString + "(" + value.escapedStringValue + ")"
19951995
override def kind = "ConstantType"

src/reflect/scala/reflect/internal/tpe/TypeComparers.scala

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package tpe
55
import scala.collection.{ mutable }
66
import Flags._
77
import util.Statistics
8+
import scala.annotation.tailrec
89

910
trait TypeComparers {
1011
self: SymbolTable =>
@@ -583,9 +584,9 @@ trait TypeComparers {
583584

584585

585586
def isWeakSubType(tp1: Type, tp2: Type) =
586-
tp1.deconst.normalize match {
587+
tp1.dealiasWiden match {
587588
case TypeRef(_, sym1, _) if isNumericValueClass(sym1) =>
588-
tp2.deconst.normalize match {
589+
tp2.deconst.dealias match {
589590
case TypeRef(_, sym2, _) if isNumericValueClass(sym2) =>
590591
isNumericSubClass(sym1, sym2)
591592
case tv2 @ TypeVar(_, _) =>
@@ -594,7 +595,7 @@ trait TypeComparers {
594595
isSubType(tp1, tp2)
595596
}
596597
case tv1 @ TypeVar(_, _) =>
597-
tp2.deconst.normalize match {
598+
tp2.deconst.dealias match {
598599
case TypeRef(_, sym2, _) if isNumericValueClass(sym2) =>
599600
tv1.registerBound(tp2, isLowerBound = false, isNumericBound = true)
600601
case _ =>
@@ -604,14 +605,18 @@ trait TypeComparers {
604605
isSubType(tp1, tp2)
605606
}
606607

607-
/** The isNumericValueType tests appear redundant, but without them
608-
* test/continuations-neg/function3.scala goes into an infinite loop.
609-
* (Even if the calls are to typeSymbolDirect.)
610-
*/
611-
def isNumericSubType(tp1: Type, tp2: Type): Boolean = (
612-
isNumericValueType(tp1)
613-
&& isNumericValueType(tp2)
614-
&& isNumericSubClass(tp1.typeSymbol, tp2.typeSymbol)
615-
)
616-
608+
def isNumericSubType(tp1: Type, tp2: Type) = (
609+
isNumericSubClass(primitiveBaseClass(tp1.dealiasWiden), primitiveBaseClass(tp2.dealias))
610+
)
611+
612+
/** If the given type has a primitive class among its base classes,
613+
* the symbol of that class. Otherwise, NoSymbol.
614+
*/
615+
private def primitiveBaseClass(tp: Type): Symbol = {
616+
@tailrec def loop(bases: List[Symbol]): Symbol = bases match {
617+
case Nil => NoSymbol
618+
case x :: xs => if (isPrimitiveValueClass(x)) x else loop(xs)
619+
}
620+
loop(tp.baseClasses)
621+
}
617622
}

test/files/pos/switch-small.flags

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
-Xfatal-warnings

test/files/pos/t7228.scala

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
object AdaptWithWeaklyConformantType {
2+
implicit class D(d: Double) { def double = d*2 }
3+
4+
val x1: Int = 1
5+
var x2: Int = 2
6+
val x3 = 3
7+
var x4 = 4
8+
final val x5 = 5
9+
final var x6 = 6
10+
11+
def f1 = x1.double
12+
def f2 = x2.double
13+
def f3 = x3.double
14+
def f4 = x4.double
15+
def f5 = x5.double
16+
def f6 = x6.double
17+
}
18+
19+
object AdaptAliasWithWeaklyConformantType {
20+
implicit class D(d: Double) { def double = d*2 }
21+
type T = Int
22+
23+
val x1: T = 1
24+
var x2: T = 2
25+
val x3 = (3: T)
26+
var x4 = (4: T)
27+
final val x5 = (5: T)
28+
final var x6 = (6: T)
29+
30+
def f1 = x1.double
31+
def f2 = x2.double
32+
def f3 = x3.double
33+
def f4 = x4.double
34+
def f5 = x5.double
35+
def f6 = x6.double
36+
}
37+
38+
object AdaptToAliasWithWeaklyConformantType {
39+
type U = Double
40+
implicit class D(d: U) { def double = d*2 }
41+
42+
val x1: Int = 1
43+
var x2: Int = 2
44+
val x3 = (3: Int)
45+
var x4 = (4: Int)
46+
final val x5 = (5: Int)
47+
final var x6 = (6: Int)
48+
49+
def f1 = x1.double
50+
def f2 = x2.double
51+
def f3 = x3.double
52+
def f4 = x4.double
53+
def f5 = x5.double
54+
def f6 = x6.double
55+
}
56+
57+
object AdaptAliasToAliasWithWeaklyConformantType {
58+
type U = Double
59+
type T = Int
60+
implicit class D(d: U) { def double = d*2 }
61+
62+
val x1: T = 1
63+
var x2: T = 2
64+
val x3 = (3: T)
65+
var x4 = (4: T)
66+
final val x5 = (5: T)
67+
final var x6 = (6: T)
68+
69+
def f1 = x1.double
70+
def f2 = x2.double
71+
def f3 = x3.double
72+
def f4 = x4.double
73+
def f5 = x5.double
74+
def f6 = x6.double
75+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
-Xfatal-warnings
File renamed without changes.

0 commit comments

Comments
 (0)