@@ -3,7 +3,9 @@ package dotc
3
3
package transform
4
4
package patmat
5
5
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 .*
7
9
import ast .* , tpd .*
8
10
import config .Printers .*
9
11
import printing .{ Printer , * }, Texts .*
@@ -359,7 +361,7 @@ object SpaceEngine {
359
361
val funRef = fun1.tpe.asInstanceOf [TermRef ]
360
362
if (fun.symbol.name == nme.unapplySeq)
361
363
val (arity, elemTp, resultTp) = unapplySeqInfo(fun.tpe.widen.finalResultType, fun.srcPos)
362
- if ( fun.symbol.owner == defn.SeqFactoryClass && defn. ListType .appliedTo(elemTp) <:< pat.tpe)
364
+ if fun.symbol.owner == defn.SeqFactoryClass && pat.tpe.hasClassSymbol(defn. ListClass ) then
363
365
// The exhaustivity and reachability logic already handles decomposing sum types (into its subclasses)
364
366
// and product types (into its components). To get better counter-examples for patterns that are of type
365
367
// List (or a super-type of list, like LinearSeq) we project them into spaces that use `::` and Nil.
@@ -531,14 +533,26 @@ object SpaceEngine {
531
533
val mt : MethodType = unapp.widen match {
532
534
case mt : MethodType => mt
533
535
case pt : PolyType =>
536
+ val locked = ctx.typerState.ownedVars
534
537
val tvars = constrained(pt)
535
538
val mt = pt.instantiate(tvars).asInstanceOf [MethodType ]
536
539
scrutineeTp <:< mt.paramInfos(0 )
537
540
// force type inference to infer a narrower type: could be singleton
538
541
// see tests/patmat/i4227.scala
539
542
mt.paramInfos(0 ) <:< scrutineeTp
540
- instantiateSelected(mt, tvars)
541
- isFullyDefined(mt, ForceDegree .all)
543
+ maximizeType(mt.paramInfos(0 ), Spans .NoSpan )
544
+ if ! (ctx.typerState.ownedVars -- locked).isEmpty then
545
+ // constraining can create type vars out of wildcard types
546
+ // (in legalBound, by using a LevelAvoidMap)
547
+ // maximise will only do one pass at maximising the type vars in the target type
548
+ // which means we can maximise to types that include other type vars
549
+ // this fails TreeChecker's "non-empty constraint at end of $fusedPhase" check
550
+ // e.g. run-macros/string-context-implicits
551
+ // I can't prove that a second call won't also create type vars,
552
+ // but I'd rather have an unassigned new-new type var, than an infinite loop.
553
+ // After all, there's nothing strictly "wrong" with unassigned type vars,
554
+ // it just fails TreeChecker's linting.
555
+ maximizeType(mt.paramInfos(0 ), Spans .NoSpan )
542
556
mt
543
557
}
544
558
@@ -552,7 +566,7 @@ object SpaceEngine {
552
566
// Case unapplySeq:
553
567
// 1. return the type `List[T]` where `T` is the element type of the unapplySeq return type `Seq[T]`
554
568
555
- val resTp = ctx.typeAssigner.safeSubstMethodParams(mt, scrutineeTp :: Nil ).finalResultType
569
+ val resTp = wildApprox( ctx.typeAssigner.safeSubstMethodParams(mt, scrutineeTp :: Nil ).finalResultType)
556
570
557
571
val sig =
558
572
if (resTp.isRef(defn.BooleanClass ))
@@ -573,14 +587,14 @@ object SpaceEngine {
573
587
if (arity > 0 )
574
588
productSelectorTypes(resTp, unappSym.srcPos)
575
589
else {
576
- val getTp = resTp.select( nme.get).finalResultType.widenTermRefExpr
590
+ val getTp = extractorMemberType(resTp, nme.get, unappSym.srcPos)
577
591
if (argLen == 1 ) getTp :: Nil
578
592
else productSelectorTypes(getTp, unappSym.srcPos)
579
593
}
580
594
}
581
595
}
582
596
583
- sig.map(_.annotatedToRepeated)
597
+ sig.map { case tp : WildcardType => tp.bounds.hi case tp => tp }
584
598
}
585
599
586
600
/** Whether the extractor covers the given type */
@@ -618,14 +632,36 @@ object SpaceEngine {
618
632
case tp if tp.classSymbol.isAllOf(JavaEnumTrait ) => tp.classSymbol.children.map(_.termRef)
619
633
// the class of a java enum value is the enum class, so this must follow SingletonType to not loop infinitely
620
634
621
- case tp @ AppliedType (Parts (parts), targs) if tp.classSymbol.children.isEmpty =>
635
+ case Childless ( tp @ AppliedType (Parts (parts), targs)) =>
622
636
// It might not obvious that it's OK to apply the type arguments of a parent type to child types.
623
637
// But this is guarded by `tp.classSymbol.children.isEmpty`,
624
638
// meaning we'll decompose to the same class, just not the same type.
625
639
// For instance, from i15029, `decompose((X | Y).Field[T]) = [X.Field[T], Y.Field[T]]`.
626
640
parts.map(tp.derivedAppliedType(_, targs))
627
641
628
- case tp if tp.isDecomposableToChildren =>
642
+ case tpOriginal if tpOriginal.isDecomposableToChildren =>
643
+ // isDecomposableToChildren uses .classSymbol.is(Sealed)
644
+ // But that classSymbol could be from an AppliedType
645
+ // where the type constructor is a non-class type
646
+ // E.g. t11620 where `?1.AA[X]` returns as "sealed"
647
+ // but using that we're not going to infer A1[X] and A2[X]
648
+ // but end up with A1[<?>] and A2[<?>].
649
+ // So we widen (like AppliedType superType does) away
650
+ // non-class type constructors.
651
+ //
652
+ // Can't use `tpOriginal.baseType(cls)` because it causes
653
+ // i15893 to return exhaustivity warnings, because instead of:
654
+ // <== refineUsingParent(N, class Succ, []) = Succ[<? <: NatT>]
655
+ // <== isSub(Succ[<? <: NatT>] <:< Succ[Succ[<?>]]) = true
656
+ // we get
657
+ // <== refineUsingParent(NatT, class Succ, []) = Succ[NatT]
658
+ // <== isSub(Succ[NatT] <:< Succ[Succ[<?>]]) = false
659
+ def getAppliedClass (tp : Type ): Type = tp match
660
+ case tp @ AppliedType (_ : HKTypeLambda , _) => tp
661
+ case tp @ AppliedType (tycon : TypeRef , _) if tycon.symbol.isClass => tp
662
+ case tp @ AppliedType (tycon : TypeProxy , _) => getAppliedClass(tycon.superType.applyIfParameterized(tp.args))
663
+ case tp => tp
664
+ val tp = getAppliedClass(tpOriginal)
629
665
def getChildren (sym : Symbol ): List [Symbol ] =
630
666
sym.children.flatMap { child =>
631
667
if child eq sym then List (sym) // i3145: sealed trait Baz, val x = new Baz {}, Baz.children returns Baz...
@@ -678,6 +714,12 @@ object SpaceEngine {
678
714
final class PartsExtractor (val get : List [Type ]) extends AnyVal :
679
715
def isEmpty : Boolean = get == ListOfNoType
680
716
717
+ object Childless :
718
+ def unapply (tp : Type )(using Context ): Result =
719
+ Result (if tp.classSymbol.children.isEmpty then tp else NoType )
720
+ class Result (val get : Type ) extends AnyVal :
721
+ def isEmpty : Boolean = ! get.exists
722
+
681
723
/** Show friendly type name with current scope in mind
682
724
*
683
725
* E.g. C.this.B --> B if current owner is C
@@ -774,12 +816,15 @@ object SpaceEngine {
774
816
doShow(s)
775
817
}
776
818
777
- private def exhaustivityCheckable (sel : Tree )(using Context ): Boolean = {
819
+ extension (self : Type ) private def stripUnsafeNulls ()(using Context ): Type =
820
+ if Nullables .unsafeNullsEnabled then self.stripNull else self
821
+
822
+ private def exhaustivityCheckable (sel : Tree )(using Context ): Boolean = trace(i " exhaustivityCheckable( $sel ${sel.className}) " ) {
778
823
val seen = collection.mutable.Set .empty[Symbol ]
779
824
780
825
// Possible to check everything, but be compatible with scalac by default
781
- def isCheckable (tp : Type ): Boolean =
782
- val tpw = tp.widen.dealias
826
+ def isCheckable (tp : Type ): Boolean = trace( i " isCheckable( $tp ${tp.className} ) " ) :
827
+ val tpw = tp.widen.dealias.stripUnsafeNulls()
783
828
val classSym = tpw.classSymbol
784
829
classSym.is(Sealed ) && ! tpw.isLargeGenericTuple || // exclude large generic tuples from exhaustivity
785
830
// requires an unknown number of changes to make work
@@ -814,7 +859,7 @@ object SpaceEngine {
814
859
/** Return the underlying type of non-module, non-constant, non-enum case singleton types.
815
860
* Also widen ExprType to its result type, and rewrap any annotation wrappers.
816
861
* For example, with `val opt = None`, widen `opt.type` to `None.type`. */
817
- def toUnderlying (tp : Type )(using Context ): Type = trace(i " toUnderlying( $tp) " )(tp match {
862
+ def toUnderlying (tp : Type )(using Context ): Type = trace(i " toUnderlying( $tp ${tp.className} ) " )(tp match {
818
863
case _ : ConstantType => tp
819
864
case tp : TermRef if tp.symbol.is(Module ) => tp
820
865
case tp : TermRef if tp.symbol.isAllOf(EnumCase ) => tp
@@ -825,7 +870,7 @@ object SpaceEngine {
825
870
})
826
871
827
872
def checkExhaustivity (m : Match )(using Context ): Unit = trace(i " checkExhaustivity( $m) " ) {
828
- val selTyp = toUnderlying(m.selector.tpe).dealias
873
+ val selTyp = toUnderlying(m.selector.tpe.stripUnsafeNulls() ).dealias
829
874
val targetSpace = trace(i " targetSpace( $selTyp) " )(project(selTyp))
830
875
831
876
val patternSpace = Or (m.cases.foldLeft(List .empty[Space ]) { (acc, x) =>
@@ -903,9 +948,6 @@ object SpaceEngine {
903
948
}
904
949
905
950
def checkMatch (m : Match )(using Context ): Unit =
906
- checkMatchExhaustivityOnly(m)
907
- if reachabilityCheckable(m.selector) then checkReachability(m)
908
-
909
- def checkMatchExhaustivityOnly (m : Match )(using Context ): Unit =
910
951
if exhaustivityCheckable(m.selector) then checkExhaustivity(m)
952
+ if reachabilityCheckable(m.selector) then checkReachability(m)
911
953
}
0 commit comments