@@ -3,7 +3,9 @@ package dotc
33package transform
44package patmat
55
6- import core .* , Constants .* , Contexts .* , Decorators .* , Flags .* , Names .* , NameOps .* , StdNames .* , Symbols .* , Types .*
6+ import core .*
7+ import Constants .* , Contexts .* , Decorators .* , Flags .* , NullOpsDecorator .* , Symbols .* , Types .*
8+ import Names .* , NameOps .* , StdNames .*
79import ast .* , tpd .*
810import config .Printers .*
911import printing .{ Printer , * }, Texts .*
@@ -350,7 +352,7 @@ object SpaceEngine {
350352 val funRef = fun1.tpe.asInstanceOf [TermRef ]
351353 if (fun.symbol.name == nme.unapplySeq)
352354 val (arity, elemTp, resultTp) = unapplySeqInfo(fun.tpe.widen.finalResultType, fun.srcPos)
353- if ( fun.symbol.owner == defn.SeqFactoryClass && defn. ListType .appliedTo(elemTp) <:< pat.tpe)
355+ if fun.symbol.owner == defn.SeqFactoryClass && pat.tpe.hasClassSymbol(defn. ListClass ) then
354356 // The exhaustivity and reachability logic already handles decomposing sum types (into its subclasses)
355357 // and product types (into its components). To get better counter-examples for patterns that are of type
356358 // List (or a super-type of list, like LinearSeq) we project them into spaces that use `::` and Nil.
@@ -522,14 +524,26 @@ object SpaceEngine {
522524 val mt : MethodType = unapp.widen match {
523525 case mt : MethodType => mt
524526 case pt : PolyType =>
527+ val locked = ctx.typerState.ownedVars
525528 val tvars = constrained(pt)
526529 val mt = pt.instantiate(tvars).asInstanceOf [MethodType ]
527530 scrutineeTp <:< mt.paramInfos(0 )
528531 // force type inference to infer a narrower type: could be singleton
529532 // see tests/patmat/i4227.scala
530533 mt.paramInfos(0 ) <:< scrutineeTp
531- instantiateSelected(mt, tvars)
532- isFullyDefined(mt, ForceDegree .all)
534+ maximizeType(mt.paramInfos(0 ), Spans .NoSpan )
535+ if ! (ctx.typerState.ownedVars -- locked).isEmpty then
536+ // constraining can create type vars out of wildcard types
537+ // (in legalBound, by using a LevelAvoidMap)
538+ // maximise will only do one pass at maximising the type vars in the target type
539+ // which means we can maximise to types that include other type vars
540+ // this fails TreeChecker's "non-empty constraint at end of $fusedPhase" check
541+ // e.g. run-macros/string-context-implicits
542+ // I can't prove that a second call won't also create type vars,
543+ // but I'd rather have an unassigned new-new type var, than an infinite loop.
544+ // After all, there's nothing strictly "wrong" with unassigned type vars,
545+ // it just fails TreeChecker's linting.
546+ maximizeType(mt.paramInfos(0 ), Spans .NoSpan )
533547 mt
534548 }
535549
@@ -543,7 +557,7 @@ object SpaceEngine {
543557 // Case unapplySeq:
544558 // 1. return the type `List[T]` where `T` is the element type of the unapplySeq return type `Seq[T]`
545559
546- val resTp = ctx.typeAssigner.safeSubstMethodParams(mt, scrutineeTp :: Nil ).finalResultType
560+ val resTp = wildApprox( ctx.typeAssigner.safeSubstMethodParams(mt, scrutineeTp :: Nil ).finalResultType)
547561
548562 val sig =
549563 if (resTp.isRef(defn.BooleanClass ))
@@ -564,20 +578,14 @@ object SpaceEngine {
564578 if (arity > 0 )
565579 productSelectorTypes(resTp, unappSym.srcPos)
566580 else {
567- val getTp = resTp.select(nme.get).finalResultType match
568- case tp : TermRef if ! tp.isOverloaded =>
569- // Like widenTermRefExpr, except not recursively.
570- // For example, in i17184 widen Option[foo.type]#get
571- // to Option[foo.type] instead of Option[Int].
572- tp.underlying.widenExpr
573- case tp => tp
581+ val getTp = extractorMemberType(resTp, nme.get, unappSym.srcPos)
574582 if (argLen == 1 ) getTp :: Nil
575583 else productSelectorTypes(getTp, unappSym.srcPos)
576584 }
577585 }
578586 }
579587
580- sig.map(_.annotatedToRepeated)
588+ sig.map { case tp : WildcardType => tp.bounds.hi case tp => tp }
581589 }
582590
583591 /** Whether the extractor covers the given type */
@@ -616,14 +624,36 @@ object SpaceEngine {
616624 case tp if tp.classSymbol.isAllOf(JavaEnum ) => tp.classSymbol.children.map(_.termRef)
617625 // the class of a java enum value is the enum class, so this must follow SingletonType to not loop infinitely
618626
619- case tp @ AppliedType (Parts (parts), targs) if tp.classSymbol.children.isEmpty =>
627+ case Childless ( tp @ AppliedType (Parts (parts), targs)) =>
620628 // It might not obvious that it's OK to apply the type arguments of a parent type to child types.
621629 // But this is guarded by `tp.classSymbol.children.isEmpty`,
622630 // meaning we'll decompose to the same class, just not the same type.
623631 // For instance, from i15029, `decompose((X | Y).Field[T]) = [X.Field[T], Y.Field[T]]`.
624632 parts.map(tp.derivedAppliedType(_, targs))
625633
626- case tp if tp.isDecomposableToChildren =>
634+ case tpOriginal if tpOriginal.isDecomposableToChildren =>
635+ // isDecomposableToChildren uses .classSymbol.is(Sealed)
636+ // But that classSymbol could be from an AppliedType
637+ // where the type constructor is a non-class type
638+ // E.g. t11620 where `?1.AA[X]` returns as "sealed"
639+ // but using that we're not going to infer A1[X] and A2[X]
640+ // but end up with A1[<?>] and A2[<?>].
641+ // So we widen (like AppliedType superType does) away
642+ // non-class type constructors.
643+ //
644+ // Can't use `tpOriginal.baseType(cls)` because it causes
645+ // i15893 to return exhaustivity warnings, because instead of:
646+ // <== refineUsingParent(N, class Succ, []) = Succ[<? <: NatT>]
647+ // <== isSub(Succ[<? <: NatT>] <:< Succ[Succ[<?>]]) = true
648+ // we get
649+ // <== refineUsingParent(NatT, class Succ, []) = Succ[NatT]
650+ // <== isSub(Succ[NatT] <:< Succ[Succ[<?>]]) = false
651+ def getAppliedClass (tp : Type ): Type = tp match
652+ case tp @ AppliedType (_ : HKTypeLambda , _) => tp
653+ case tp @ AppliedType (tycon : TypeRef , _) if tycon.symbol.isClass => tp
654+ case tp @ AppliedType (tycon : TypeProxy , _) => getAppliedClass(tycon.superType.applyIfParameterized(tp.args))
655+ case tp => tp
656+ val tp = getAppliedClass(tpOriginal)
627657 def getChildren (sym : Symbol ): List [Symbol ] =
628658 sym.children.flatMap { child =>
629659 if child eq sym then List (sym) // i3145: sealed trait Baz, val x = new Baz {}, Baz.children returns Baz...
@@ -676,6 +706,12 @@ object SpaceEngine {
676706 final class PartsExtractor (val get : List [Type ]) extends AnyVal :
677707 def isEmpty : Boolean = get == ListOfNoType
678708
709+ object Childless :
710+ def unapply (tp : Type )(using Context ): Result =
711+ Result (if tp.classSymbol.children.isEmpty then tp else NoType )
712+ class Result (val get : Type ) extends AnyVal :
713+ def isEmpty : Boolean = ! get.exists
714+
679715 /** Show friendly type name with current scope in mind
680716 *
681717 * E.g. C.this.B --> B if current owner is C
@@ -772,12 +808,15 @@ object SpaceEngine {
772808 doShow(s)
773809 }
774810
775- private def exhaustivityCheckable (sel : Tree )(using Context ): Boolean = {
811+ extension (self : Type ) private def stripUnsafeNulls ()(using Context ): Type =
812+ if Nullables .unsafeNullsEnabled then self.stripNull() else self
813+
814+ private def exhaustivityCheckable (sel : Tree )(using Context ): Boolean = trace(i " exhaustivityCheckable( $sel ${sel.className}) " ) {
776815 val seen = collection.mutable.Set .empty[Symbol ]
777816
778817 // Possible to check everything, but be compatible with scalac by default
779- def isCheckable (tp : Type ): Boolean =
780- val tpw = tp.widen.dealias
818+ def isCheckable (tp : Type ): Boolean = trace( i " isCheckable( $tp ${tp.className} ) " ) :
819+ val tpw = tp.widen.dealias.stripUnsafeNulls()
781820 val classSym = tpw.classSymbol
782821 classSym.is(Sealed ) && ! tpw.isLargeGenericTuple || // exclude large generic tuples from exhaustivity
783822 // requires an unknown number of changes to make work
@@ -813,7 +852,7 @@ object SpaceEngine {
813852 /** Return the underlying type of non-module, non-constant, non-enum case singleton types.
814853 * Also widen ExprType to its result type, and rewrap any annotation wrappers.
815854 * For example, with `val opt = None`, widen `opt.type` to `None.type`. */
816- def toUnderlying (tp : Type )(using Context ): Type = trace(i " toUnderlying( $tp) " )(tp match {
855+ def toUnderlying (tp : Type )(using Context ): Type = trace(i " toUnderlying( $tp ${tp.className} ) " )(tp match {
817856 case _ : ConstantType => tp
818857 case tp : TermRef if tp.symbol.is(Module ) => tp
819858 case tp : TermRef if tp.symbol.isAllOf(EnumCase ) => tp
@@ -824,7 +863,7 @@ object SpaceEngine {
824863 })
825864
826865 def checkExhaustivity (m : Match )(using Context ): Unit = trace(i " checkExhaustivity( $m) " ) {
827- val selTyp = toUnderlying(m.selector.tpe).dealias
866+ val selTyp = toUnderlying(m.selector.tpe.stripUnsafeNulls() ).dealias
828867 val targetSpace = trace(i " targetSpace( $selTyp) " )(project(selTyp))
829868
830869 val patternSpace = Or (m.cases.foldLeft(List .empty[Space ]) { (acc, x) =>
@@ -902,9 +941,6 @@ object SpaceEngine {
902941 }
903942
904943 def checkMatch (m : Match )(using Context ): Unit =
905- checkMatchExhaustivityOnly(m)
906- if reachabilityCheckable(m.selector) then checkReachability(m)
907-
908- def checkMatchExhaustivityOnly (m : Match )(using Context ): Unit =
909944 if exhaustivityCheckable(m.selector) then checkExhaustivity(m)
945+ if reachabilityCheckable(m.selector) then checkReachability(m)
910946}
0 commit comments