8000 Preserve singleton type when inferring override · scala/scala@85c18a6 · GitHub
[go: up one dir, main page]

Skip to content

Commit 85c18a6

Browse files
committed
Preserve singleton type when inferring override
Update comment on nullary/nilary distinction.
1 parent 0fd61f7 commit 85c18a6

File tree

6 files changed

+113
-51
lines changed

6 files changed

+113
-51
lines changed

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

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1413,13 +1413,25 @@ trait Namers extends MethodSynthesis {
14131413
case mt => (Nil, mt)
14141414
}
14151415

1416-
@tailrec @inline def applyFully(tp: Type, paramss: List[List[Symbol]]): Type =
1417-
if (paramss.isEmpty) tp match {
1418-
case NullaryMethodType(rtpe) => rtpe
1419-
case MethodType(Nil, rtpe) => rtpe
1420-
case tp => tp
1421-
}
1422-
else applyFully(tp.resultType(paramss.head.map(_.tpe)), paramss.tail)
1416+
@tailrec @inline def applyFully(tp: Type, paramss: List[List[Symbol]]): Type =
1417+
paramss match {
1418+
case ps :: rest =>
1419+
val resTp =
1420+
tp match {
1421+
case MethodType(qs, SingleType(NoPrefix, sym)) if qs.contains(sym) =>
1422+
val i = qs.indexWhere(_ == sym)
1423+
singleType(NoPrefix, ps(i))
1424+
case _ =>
1425+
tp.resultType(ps.map(_.tpe))
1426+
}
1427+
applyFully(resTp, rest)
1428+
case _ =>
1429+
tp match {
1430+
case NullaryMethodType(rtpe) => rtpe
1431+
case MethodType(Nil, rtpe) => rtpe
1432+
case tp => tp
1433+
}
1434+
}
14231435

14241436
if (inferResTp) {
14251437
// scala/bug#7668 Substitute parameters from the parent method with those of the overriding method.
@@ -1430,8 +1442,10 @@ trait Namers extends MethodSynthesis {
14301442
// this also prevents cycles in implicit search, see comment in scala/bug#10471
14311443
meth setInfo deskolemizedPolySig(vparamSymss, overriddenResTp)
14321444
overriddenResTp
1433-
} else resTpGiven
1445+
}
1446+
else resTpGiven
14341447
}
1448+
//end resTpFromOverride
14351449

14361450
// issue an error for missing parameter types
14371451
// (computing resTpFromOverride may have required inferring some, meanwhile)

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

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2744,21 +2744,19 @@ trait Types
27442744
}
27452745

27462746
object TypeRef extends TypeRefExtractor {
2747-
def apply(pre: Type, sym: Symbol, args: List[Type]): Type = unique({
2748-
if (args ne Nil) {
2747+
def apply(pre: Type, sym: Symbol, args: List[Type]): Type = unique {
2748+
if (args ne Nil)
27492749
if (sym.isAliasType) new AliasArgsTypeRef(pre, sym, args)
27502750
else if (sym.isAbstractType) new AbstractArgsTypeRef(pre, sym, args)
27512751
else new ClassArgsTypeRef(pre, sym, args)
2752-
}
2753-
else {
2752+
else
27542753
if (sym.isAliasType) new AliasNoArgsTypeRef(pre, sym)
27552754
else if (sym.isAbstractType) new AbstractNoArgsTypeRef(pre, sym)
27562755
else if (sym.isRefinementClass) new RefinementTypeRef(pre, sym)
27572756
else if (sym.isPackageClass) new PackageTypeRef(pre, sym)
27582757
else if (sym.isModuleClass) new ModuleTypeRef(pre, sym)
27592758
else new ClassNoArgsTypeRef(pre, sym)
2760-
}
2761-
})
2759+
}
27622760
}
27632761

27642762
protected def defineNormalized(tr: TypeRef): Unit = {
@@ -2933,8 +2931,10 @@ trait Types
29332931

29342932
object MethodType extends MethodTypeExtractor
29352933

2936-
// TODO: rename so it's more appropriate for the type that is for a method without argument lists
2937-
// ("nullary" erroneously implies it has an argument list with zero arguments, it actually has zero argument lists)
2934+
/** A method with zero parameter lists.
2935+
*
2936+
* As a clarifying convention, a method type with an empty parameter list is called "Nilary" where `paramss.head eq Nil`.
2937+
*/
29382938
case class NullaryMethodType(override val resultType: Type) extends Type with NullaryMethodTypeApi {
29392939
override def isTrivial = resultType.isTrivial && (resultType eq resultType.withoutAnnotations)
29402940
override def prefix: Type = resultType.prefix

src/reflect/scala/reflect/internal/tpe/TypeMaps.scala

Lines changed: 34 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,13 @@ private[internal] trait TypeMaps {
2727
import definitions._
2828

2929
/** Normalize any type aliases within this type (@see Type#normalize).
30-
* Note that this depends very much on the call to "normalize", not "dealias",
31-
* so it is no longer carries the too-stealthy name "deAlias".
32-
*/
30+
* Note that this depends very much on the call to "normalize", not "dealias",
31+
* so it no longer carries the too-stealthy name "deAlias".
32+
*/
3333
object normalizeAliases extends TypeMap {
3434
def apply(tp: Type): Type = (tp match {
35-
case TypeRef(_, sym, _) if sym.isAliasType && tp.isHigherKinded => logResult(s"Normalized type alias function $tp")(tp.normalize)
36-
case TypeRef(_, sym, _) if sym.isAliasType => tp.normalize
37-
case tp => tp
35+
case TypeRef(_, sym, _) if sym.isAliasType => logResultIf(s"Normalized type alias function $tp", (_: Type) => tp.isHigherKinded)(tp.normalize)
36+
case tp => tp
3837
}).mapOver(this)
3938
}
4039

@@ -98,10 +97,8 @@ private[internal] trait TypeMaps {
9897
def keepAnnotation(annot: AnnotationInfo) = annot matches TypeConstraintClass
9998
}
10099

101-
// todo. move these into scala.reflect.api
102-
103100
/** A prototype for mapping a function over all possible types
104-
*/
101+
*/
105102
abstract class TypeMap extends (Type => Type) {
106103
def apply(tp: Type): Type
107104

@@ -314,7 +311,7 @@ private[internal] trait TypeMaps {
314311
*/
315312

316313
/** Used by existentialAbstraction.
317-
*/
314+
*/
318315
class ExistentialExtrapolation(tparams: List[Symbol]) extends VariancedTypeMap {
319316
private[this] val occurCount = mutable.HashMap[Symbol, Int]()
320317
private[this] val anyContains = new ContainsAnyKeyCollector(occurCount)
@@ -327,7 +324,8 @@ private[internal] trait TypeMaps {
327324
}
328325
}
329326
def extrapolate(tpe: Type): Type = {
330-
tparams foreach (t => occurCount(t) = 0)
327+
for (tparam <- tparams)
328+
occurCount(tparam) = 0
331329
countOccs(tpe)
332330
for (tparam <- tparams)
333331
countOccs(tparam.info)
@@ -336,12 +334,12 @@ private[internal] trait TypeMaps {
336334
}
337335

338336
/** If these conditions all hold:
339-
* 1) we are in covariant (or contravariant) position
340-
* 2) this type occurs exactly once in the existential scope
341-
* 3) the widened upper (or lower) bound of this type contains no references to tparams
342-
* Then we replace this lone occurrence of the type with the widened upper (or lower) bound.
343-
* All other types pass through unchanged.
344-
*/
337+
* 1) we are in covariant (or contravariant) position
338+
* 2) this type occurs exactly once in the existential scope
339+
* 3) the widened upper (or lower) bound of this type contains no references to tparams
340+
* Then we replace this lone occurrence of the type with the widened upper (or lower) bound.
341+
* All other types pass through unchanged.
342+
*/
345343
def apply(tp: Type): Type = {
346344
val tp1 = mapOver(tp)
347345
if (variance.isInvariant) tp1
@@ -352,7 +350,7 @@ private[internal] trait TypeMaps {
352350
val word = if (variance.isPositive) "upper" else "lower"
353351
s"Widened lone occurrence of $tp1 inside existential to $word bound"
354352
}
355-
if (!repl.typeSymbol.isBottomClass && ! anyContains.collect(repl))
353+
if (!repl.typeSymbol.isBottomClass && !anyContains.collect(repl))
356354
debuglogResult(msg)(repl)
357355
else
358356
tp1
@@ -918,7 +916,7 @@ private[internal] trait TypeMaps {
918916
}
919917

920918
/** Note: This map is needed even for non-dependent method types, despite what the name might imply.
921-
*/
919+
*/
922920
class InstantiateDependentMap(params: List[Symbol], actuals0: List[Type]) extends TypeMap with KeepOnlyTypeConstraints {
923921
private[this] var _actuals: Array[Type] = _
924922
private[this] var _existentials: Array[Symbol] = _
@@ -946,37 +944,38 @@ private[internal] trait TypeMaps {
946944

947945
private object StableArgTp {
948946
// type of actual arg corresponding to param -- if the type is stable
949-
def unapply(param: Symbol): Option[Type] = (params indexOf param) match {
947+
def unapply(param: Symbol): Option[Type] = params.indexOf(param) match {
950948
case -1 => None
951949
case pid =>
952950
val tp = actuals(pid)
953-
if (tp.isStable && (tp.typeSymbol != NothingClass)) Some(tp)
951+
if (tp.isStable && tp.typeSymbol != NothingClass) Some(tp)
954952
else None
955953
}
956954
}
957955

958956
/** Return the type symbol for referencing a parameter that's instantiated to an unstable actual argument.
959957
*
960-
* To soundly abstract over an unstable value (x: T) while retaining the most type information,
961-
* use `x.type forSome { type x.type <: T with Singleton}`
962-
* `typeOf[T].narrowExistentially(symbolOf[x])`.
958+
* To soundly abstract over an unstable value (x: T) while retaining the most type information,
959+
* use `x.type forSome { type x.type <: T with Singleton}`
960+
* `typeOf[T].narrowExistentially(symbolOf[x])`.
963961
*
964-
* See also: captureThis in AsSeenFromMap.
962+
* See also: captureThis in AsSeenFromMap.
965963
*/
966-
private def existentialFor(pid: Int) = {
967-
if (existentials(pid) eq null) {
968-
val param = params(pid)
969-
existentials(pid) = (
970-
param.owner.newExistential(param.name.toTypeName append nme.SINGLETON_SUFFIX, param.pos, param.flags)
971-
setInfo singletonBounds(actuals(pid))
972-
)
964+
private def existentialFor(pid: Int) =
965+
existentials(pid) match {
966+
case null =>
967+
val param = params(pid)
968+
val exst =
969+
param.owner.newExistential(param.name.toTypeName.append(nme.SINGLETON_SUFFIX), param.pos, param.flags)
970+
.setInfo(singletonBounds(actuals(pid)))
971+
existentials(pid) = exst
972+
exst
973+
case exst => exst
973974
}
974-
existentials(pid)
975-
}
976975

977976
private object UnstableArgTp {
978977
// existential quantifier and type of corresponding actual arg with unstable type
979-
def unapply(param: Symbol): Option[(Symbol, Type)] = (params indexOf param) match {
978+
def unapply(param: Symbol): Option[(Symbol, Type)] = params.indexOf(param) match {
980979
case -1 => None
981980
case pid =>
982981
val sym = existentialFor(pid)

test/files/neg/t12621.check

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
t12621.scala:9: error: type mismatch;
2+
found : StringBuilder
3+
required: xb.type
4+
override def f(xb: StringBuilder) = new StringBuilder
5+
^
6+
1 error

test/files/neg/t12621.scala

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
2+
// scalac: -Xsource:3
3+
4+
class C {
5+
def f(sb: StringBuilder): sb.type = sb
6+
}
7+
8+
class C2 extends C {
9+
override def f(xb: StringBuilder) = new StringBuilder
10+
}

test/files/pos/t12621.scala

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
2+
// scalac: -Xsource:3
3+
4+
class C {
5+
def f(sb: StringBuilder): sb.type = sb
6+
}
7+
8+
class C1 extends C {
9+
override def f(yb: StringBuilder): yb.type = yb // explicit already ok
10+
}
11+
12+
class C2 extends C {
13+
override def f(xb: StringBuilder) = xb // inferred StringBuilder
14+
}
15+
16+
class D {
17+
val x: StringBuilder = new StringBuilder
18+
def f(sb: x.type): sb.type = sb
19+
}
20+
21+
class D1 extends D {
22+
override def f(sb: x.type) = sb // stable type already ok
23+
}
24+
25+
/*
26+
was:
27+
t12621.scala:13: error: incompatible type in overriding
28+
def f(sb: StringBuilder): sb.type (defined in class C);
29+
found : (sb: StringBuilder): StringBuilder
30+
required: (sb: StringBuilder): sb.type
31+
override def f(sb: StringBuilder) = sb
32+
^
33+
*/

0 commit comments

Comments
 (0)
0