Skip to content

Commit 492aa72

Browse files
authored
Merge pull request #77 from scala/backport-lts-3.3-21848
Backport "Fix use of class terms in match analysis" to 3.3 LTS
2 parents 4759929 + d41b267 commit 492aa72

File tree

4 files changed

+118
-62
lines changed

4 files changed

+118
-62
lines changed

Diff for: compiler/src/dotty/tools/dotc/core/TypeOps.scala

+69-62
Original file line numberDiff line numberDiff line change
@@ -782,6 +782,67 @@ object TypeOps:
782782
* Otherwise, return NoType.
783783
*/
784784
private def instantiateToSubType(tp1: NamedType, tp2: Type, mixins: List[Type])(using Context): Type = trace(i"instantiateToSubType($tp1, $tp2, $mixins)", typr) {
785+
/** Gather GADT symbols and singletons found in `tp2`, ie. the scrutinee. */
786+
object TraverseTp2 extends TypeTraverser:
787+
val singletons = util.HashMap[Symbol, SingletonType]()
788+
val gadtSyms = new mutable.ListBuffer[Symbol]
789+
790+
def traverse(tp: Type) = try
791+
val tpd = tp.dealias
792+
if tpd ne tp then traverse(tpd)
793+
else tp match
794+
case tp: ThisType if !singletons.contains(tp.tref.symbol) && !tp.tref.symbol.isStaticOwner =>
795+
singletons(tp.tref.symbol) = tp
796+
traverseChildren(tp.tref)
797+
case tp: TermRef =>
798+
singletons(tp.typeSymbol) = tp
799+
traverseChildren(tp)
800+
case tp: TypeRef if !gadtSyms.contains(tp.symbol) && tp.symbol.isAbstractOrParamType =>
801+
gadtSyms += tp.symbol
802+
traverseChildren(tp)
803+
// traverse abstract type infos, to add any singletons
804+
// for example, i16451.CanForward.scala, add `Namer.this`, from the info of the type parameter `A1`
805+
// also, i19031.ci-reg2.scala, add `out`, from the info of the type parameter `A1` (from synthetic applyOrElse)
806+
traverseChildren(tp.info)
807+
case _ =>
808+
traverseChildren(tp)
809+
catch case ex: Throwable => handleRecursive("traverseTp2", tp.show, ex)
810+
TraverseTp2.traverse(tp2)
811+
val singletons = TraverseTp2.singletons
812+
val gadtSyms = TraverseTp2.gadtSyms.toList
813+
814+
// Prefix inference, given `p.C.this.Child`:
815+
// 1. return it as is, if `C.this` is found in `tp`, i.e. the scrutinee; or
816+
// 2. replace it with `X.Child` where `X <: p.C`, stripping ThisType in `p` recursively.
817+
//
818+
// See tests/patmat/i3938.scala, tests/pos/i15029.more.scala, tests/pos/i16785.scala
819+
class InferPrefixMap extends TypeMap {
820+
var prefixTVar: Type | Null = null
821+
def apply(tp: Type): Type = tp match {
822+
case tp: TermRef if singletons.contains(tp.symbol) =>
823+
prefixTVar = singletons(tp.symbol) // e.g. tests/pos/i19031.ci-reg2.scala, keep out
824+
prefixTVar.uncheckedNN
825+
case ThisType(tref) if !tref.symbol.isStaticOwner =>
826+
val symbol = tref.symbol
827+
if singletons.contains(symbol) then
828+
prefixTVar = singletons(symbol) // e.g. tests/pos/i16785.scala, keep Outer.this
829+
prefixTVar.uncheckedNN
830+
else if symbol.is(Module) then
831+
TermRef(this(tref.prefix), symbol.sourceModule)
832+
else if (prefixTVar != null)
833+
this(tref.applyIfParameterized(tref.typeParams.map(_ => WildcardType)))
834+
else {
835+
prefixTVar = WildcardType // prevent recursive call from assigning it
836+
// e.g. tests/pos/i15029.more.scala, create a TypeVar for `Instances`' B, so we can disregard `Ints`
837+
val tvars = tref.typeParams.map { tparam => newTypeVar(tparam.paramInfo.bounds, DepParamName.fresh(tparam.paramName)) }
838+
val tref2 = this(tref.applyIfParameterized(tvars))
839+
prefixTVar = newTypeVar(TypeBounds.upper(tref2), DepParamName.fresh(tref.name))
840+
prefixTVar.uncheckedNN
841+
}
842+
case tp => mapOver(tp)
843+
}
844+
}
845+
785846
// In order for a child type S to qualify as a valid subtype of the parent
786847
// T, we need to test whether it is possible S <: T.
787848
//
@@ -803,8 +864,15 @@ object TypeOps:
803864
// then to avoid it failing the <:<
804865
// we'll approximate by widening to its bounds
805866

867+
case tp: TermRef if singletons.contains(tp.symbol) =>
868+
singletons(tp.symbol)
869+
806870
case ThisType(tref: TypeRef) if !tref.symbol.isStaticOwner =>
807-
tref
871+
val symbol = tref.symbol
872+
if singletons.contains(symbol) then
873+
singletons(symbol)
874+
else
875+
tref
808876

809877
case tp: TypeRef if !tp.symbol.isClass =>
810878
val lookup = boundTypeParams.lookup(tp)
@@ -855,67 +923,6 @@ object TypeOps:
855923
}
856924
}
857925

858-
/** Gather GADT symbols and singletons found in `tp2`, ie. the scrutinee. */
859-
object TraverseTp2 extends TypeTraverser:
860-
val singletons = util.HashMap[Symbol, SingletonType]()
861-
val gadtSyms = new mutable.ListBuffer[Symbol]
862-
863-
def traverse(tp: Type) = try
864-
val tpd = tp.dealias
865-
if tpd ne tp then traverse(tpd)
866-
else tp match
867-
case tp: ThisType if !singletons.contains(tp.tref.symbol) && !tp.tref.symbol.isStaticOwner =>
868-
singletons(tp.tref.symbol) = tp
869-
traverseChildren(tp.tref)
870-
case tp: TermRef if tp.symbol.is(Param) =>
871-
singletons(tp.typeSymbol) = tp
872-
traverseChildren(tp)
873-
case tp: TypeRef if !gadtSyms.contains(tp.symbol) && tp.symbol.isAbstractOrParamType =>
874-
gadtSyms += tp.symbol
875-
traverseChildren(tp)
876-
// traverse abstract type infos, to add any singletons
877-
// for example, i16451.CanForward.scala, add `Namer.this`, from the info of the type parameter `A1`
878-
// also, i19031.ci-reg2.scala, add `out`, from the info of the type parameter `A1` (from synthetic applyOrElse)
879-
traverseChildren(tp.info)
880-
case _ =>
881-
traverseChildren(tp)
882-
catch case ex: Throwable => handleRecursive("traverseTp2", tp.show, ex)
883-
TraverseTp2.traverse(tp2)
884-
val singletons = TraverseTp2.singletons
885-
val gadtSyms = TraverseTp2.gadtSyms.toList
886-
887-
// Prefix inference, given `p.C.this.Child`:
888-
// 1. return it as is, if `C.this` is found in `tp`, i.e. the scrutinee; or
889-
// 2. replace it with `X.Child` where `X <: p.C`, stripping ThisType in `p` recursively.
890-
//
891-
// See tests/patmat/i3938.scala, tests/pos/i15029.more.scala, tests/pos/i16785.scala
892-
class InferPrefixMap extends TypeMap {
893-
var prefixTVar: Type | Null = null
894-
def apply(tp: Type): Type = tp match {
895-
case tp: TermRef if singletons.contains(tp.symbol) =>
896-
prefixTVar = singletons(tp.symbol) // e.g. tests/pos/i19031.ci-reg2.scala, keep out
897-
prefixTVar.uncheckedNN
898-
case ThisType(tref) if !tref.symbol.isStaticOwner =>
899-
val symbol = tref.symbol
900-
if singletons.contains(symbol) then
901-
prefixTVar = singletons(symbol) // e.g. tests/pos/i16785.scala, keep Outer.this
902-
prefixTVar.uncheckedNN
903-
else if symbol.is(Module) then
904-
TermRef(this(tref.prefix), symbol.sourceModule)
905-
else if (prefixTVar != null)
906-
this(tref.applyIfParameterized(tref.typeParams.map(_ => WildcardType)))
907-
else {
908-
prefixTVar = WildcardType // prevent recursive call from assigning it
909-
// e.g. tests/pos/i15029.more.scala, create a TypeVar for `Instances`' B, so we can disregard `Ints`
910-
val tvars = tref.typeParams.map { tparam => newTypeVar(tparam.paramInfo.bounds, DepParamName.fresh(tparam.paramName)) }
911-
val tref2 = this(tref.applyIfParameterized(tvars))
912-
prefixTVar = newTypeVar(TypeBounds.upper(tref2), DepParamName.fresh(tref.name))
913-
prefixTVar.uncheckedNN
914-
}
915-
case tp => mapOver(tp)
916-
}
917-
}
918-
919926
val inferThisMap = new InferPrefixMap
920927
val tvars = tp1.typeParams.map { tparam => newTypeVar(tparam.paramInfo.bounds, DepParamName.fresh(tparam.paramName)) }
921928
val protoTp1 = inferThisMap.apply(tp1).appliedTo(tvars)

Diff for: compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala

+1
Original file line numberDiff line numberDiff line change
@@ -1043,6 +1043,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
10431043
recur(fn) ~ "(" ~ toTextGlobal(explicitArgs, ", ") ~ ")"
10441044
case TypeApply(fn, args) => recur(fn) ~ "[" ~ toTextGlobal(args, ", ") ~ "]"
10451045
case Select(qual, nme.CONSTRUCTOR) => recur(qual)
1046+
case id @ Ident(tpnme.BOUNDTYPE_ANNOT) => "@" ~ toText(id.symbol.name)
10461047
case New(tpt) => recur(tpt)
10471048
case _ =>
10481049
val annotSym = sym.orElse(tree.symbol.enclosingClass)

Diff for: tests/warn/i21845.orig.scala

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
trait Init[ScopeType]:
2+
sealed trait Initialize[A1]
3+
final class Bind[S, A1](val f: S => Initialize[A1], val in: Initialize[S])
4+
extends Initialize[A1]
5+
final class Value[A1](val value: () => A1) extends Initialize[A1]
6+
final class ValidationCapture[A1](val key: ScopedKey[A1], val selfRefOk: Boolean)
7+
extends Initialize[ScopedKey[A1]]
8+
final class TransformCapture(val f: [x] => Initialize[x] => Initialize[x])
9+
extends Initialize[[x] => Initialize[x] => Initialize[x]]
10+
final class Optional[S, A1](val a: Option[Initialize[S]], val f: Option[S] => A1)
11+
extends Initialize[A1]
12+
object StaticScopes extends Initialize[Set[ScopeType]]
13+
14+
sealed trait Keyed[S, A1] extends Initialize[A1]
15+
trait KeyedInitialize[A1] extends Keyed[A1, A1]
16+
sealed case class ScopedKey[A](scope: ScopeType, key: AttributeKey[A]) extends KeyedInitialize[A]
17+
sealed trait AttributeKey[A]
18+
19+
abstract class EvaluateSettings[ScopeType]:
20+
protected val init: Init[ScopeType]
21+
import init._
22+
23+
val transform: [A] => Initialize[A] => Unit = [A] =>
24+
(fa: Initialize[A]) =>
25+
fa match
26+
case k: Keyed[s, A] => ???
27+
case b: Bind[s, A] => ???
28+
case v: Value[A] => ???
29+
case v: ValidationCapture[a] => ??? // unrearchable warning
30+
case t: TransformCapture => ??? // unrearchable warning
31+
case o: Optional[s, A] => ??? // unrearchable warning
32+
case StaticScopes => ??? // unrearchable warning
33+

Diff for: tests/warn/i21845.scala

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
trait Outer[O1]:
2+
sealed trait Foo[A1]
3+
final class Bar[A2] extends Foo[A2]
4+
final class Baz[A4] extends Foo[Bar[A4]]
5+
final class Qux extends Foo[[a5] => Foo[a5] => Foo[a5]]
6+
7+
trait Test[O2]:
8+
val outer: Outer[O2]
9+
import outer.*
10+
11+
def test[X](fa: Foo[X]): Unit =
12+
fa match // was: inexhaustive: fail on _: (Outer[<?>] & (Test#outer : Outer[Test#O2]))#Qux
13+
case _: Bar[X] => ???
14+
case _: Baz[x] => ??? // was: unrearchable warning
15+
case _: Qux => ??? // was: unrearchable warning

0 commit comments

Comments
 (0)