Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix #19607: Allow to instantiate *wildcard* type captures to TypeBounds. #19627

Merged
merged 1 commit into from
Feb 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 23 additions & 18 deletions compiler/src/dotty/tools/dotc/core/TypeComparer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3410,29 +3410,38 @@ class TrackingTypeComparer(initctx: Context) extends TypeComparer(initctx) {
// Actual matching logic

val instances = Array.fill[Type](spec.captureCount)(NoType)
val noInstances = mutable.ListBuffer.empty[(TypeName, TypeBounds)]

def rec(pattern: MatchTypeCasePattern, scrut: Type, variance: Int, scrutIsWidenedAbstract: Boolean): Boolean =
pattern match
case MatchTypeCasePattern.Capture(num, isWildcard) =>
case MatchTypeCasePattern.Capture(num, /* isWildcard = */ true) =>
// instantiate the wildcard in a way that the subtype test always succeeds
instances(num) = variance match
case 1 => scrut.hiBound // actually important if we are not in a class type constructor
case -1 => scrut.loBound
case 0 => scrut
!instances(num).isError

case MatchTypeCasePattern.Capture(num, /* isWildcard = */ false) =>
def failNotSpecific(bounds: TypeBounds): TypeBounds =
noInstances += spec.origMatchCase.paramNames(num) -> bounds
bounds

instances(num) = scrut match
case scrut: TypeBounds =>
if isWildcard then
// anything will do, as long as it conforms to the bounds for the subsequent `scrut <:< instantiatedPat` test
scrut.hi
else if scrutIsWidenedAbstract then
// always keep the TypeBounds so that we can report the correct NoInstances
scrut
if scrutIsWidenedAbstract then
failNotSpecific(scrut)
else
variance match
case 1 => scrut.hi
case -1 => scrut.lo
case 0 => scrut
case 0 => failNotSpecific(scrut)
case _ =>
if !isWildcard && scrutIsWidenedAbstract && variance != 0 then
// force a TypeBounds to report the correct NoInstances
if scrutIsWidenedAbstract && variance != 0 then
// fail as not specific
// the Nothing and Any bounds are used so that they are not displayed; not for themselves in particular
if variance > 0 then TypeBounds(defn.NothingType, scrut)
else TypeBounds(scrut, defn.AnyType)
if variance > 0 then failNotSpecific(TypeBounds(defn.NothingType, scrut))
else failNotSpecific(TypeBounds(scrut, defn.AnyType))
else
scrut
!instances(num).isError
Expand Down Expand Up @@ -3508,12 +3517,8 @@ class TrackingTypeComparer(initctx: Context) extends TypeComparer(initctx) {
MatchResult.Stuck

if rec(spec.pattern, scrut, variance = 1, scrutIsWidenedAbstract = false) then
if instances.exists(_.isInstanceOf[TypeBounds]) then
MatchResult.NoInstance {
constrainedCaseLambda.paramNames.zip(instances).collect {
case (name, bounds: TypeBounds) => (name, bounds)
}
}
if noInstances.nonEmpty then
MatchResult.NoInstance(noInstances.toList)
else
val defn.MatchCase(instantiatedPat, reduced) =
instantiateParamsSpec(instances, constrainedCaseLambda)(constrainedCaseLambda.resultType): @unchecked
Expand Down
12 changes: 12 additions & 0 deletions tests/pos/i19607.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
trait Foo
trait Bar[T]

type MatchType[T] = T match
case Bar[?] => Nothing
case _ => T

object Test:
def foo(b: Bar[? >: Foo]): Unit =
summon[MatchType[b.type] =:= Nothing]
summon[MatchType[Bar[? >: Foo]] =:= Nothing]
end Test
Loading