@@ -936,46 +936,84 @@ object SpaceEngine {
936936 * ^ pat ^ selector
937937 *
938938 */
939- private def selectorIsBoundVar (selector : Tree , pat : Tree )(using Context ): Boolean =
940- pat match
941- case b : Bind => selector.symbol == b.symbol
942- case _ => false
939+ private object SelectorBoundVar :
940+ def unapply (args : (Tree , Tree ))(using Context ): Boolean =
941+ val (selector, pat) = args
942+ pat match
943+ case b : Bind => selector.symbol == b.symbol
944+ case _ => false
943945
944946 /** Find the index of the parameter in an outer UnApply pattern that directly binds the selector symbol.
945947 *
946948 * case Wrapper(c) if c match
947949 * ^ returns Some(0)
948950 *
949951 */
950- private def selectorParamIndex (selector : Tree , pat : Tree )(using Context ): Option [Int ] =
951- unbind(pat) match
952- case UnApply (_, _, pats) =>
953- val idx = pats.indexWhere {
954- case b : Bind => b.symbol == selector.symbol
955- case _ => false
956- }
957- if idx >= 0 then Some (idx) else None
952+ private object SelectorParamIndex :
953+ def unapply (args : (Tree , Tree ))(using Context ): Option [Int ] =
954+ val (selector, pat) = args
955+ unbind(pat) match
956+ case UnApply (_, _, pats) =>
957+ val idx = pats.indexWhere {
958+ case b : Bind => b.symbol == selector.symbol
959+ case _ => false
960+ }
961+ Option .when(idx >= 0 )(idx)
962+ case _ => None
963+
964+ /** Find the constructor parameter index corresponding to a field access on the outer pattern's bound var.
965+ *
966+ * case x if x.version match -- returns Some(1) for Document(title, version)
967+ * ^^^^^^^^^ selector
968+ *
969+ */
970+ private object SelectorFieldIndex :
971+ def unapply (args : (Tree , Tree ))(using Context ): Option [Int ] =
972+ args match
973+ case (Select (qual, fieldName), b : Bind ) if b.symbol == qual.symbol =>
974+ val cls = toUnderlying(qual.tpe).classSymbol
975+ if cls.is(CaseClass ) && ! cls.isOneOf(AbstractOrTrait ) then
976+ val idx = cls.caseAccessors.indexWhere(_.name == fieldName)
977+ Option .when(idx >= 0 )(idx)
978+ else None
979+ case _ => None
980+
981+ private def narrowProdParam (patSpace : Space , idx : Int , subSpace : Space )(using Context ): Option [Space ] =
982+ def narrow (prod : Prod ): Option [Space ] =
983+ val Prod (tp, unappTp, params) = prod
984+ if idx >= params.length then None
985+ else
986+ val narrowedParam = simplify(intersect(params(idx), subSpace))
987+ Some (simplify(Prod (tp, unappTp, params.updated(idx, narrowedParam))))
988+ patSpace match
989+ case prod @ Prod (tp, unappTp1, _) =>
990+ expandCaseClass(tp) match
991+ case null => None
992+ case Prod (_, unappTp2, _) if isSameUnapply(unappTp1, unappTp2) => narrow(prod)
993+ case Typ (tp, _) =>
994+ expandCaseClass(tp) match
995+ case null => None
996+ case prod => narrow(prod)
958997 case _ => None
959998
960999 private def projectSubMatch (pat : Tree , sm : SubMatch )(using Context ): Option [Space ] =
9611000 val Match (selector, cases) = sm
9621001
9631002 val subSpace = Or (cases.map(projectCaseDef))
1003+ if simplify(subSpace) == Empty then return None // all sub-cases are guarded or empty; treat outer case as partial
9641004 def selTyp = toUnderlying(selector.tpe)
9651005 def patSpace = project(pat)
9661006
967- if selectorIsBoundVar(selector, pat) then
968- Some (simplify(intersect(patSpace, subSpace)))
969- else selectorParamIndex(selector, pat) match
970- case Some (idx) =>
971- patSpace match
972- case Prod (tp, unappTp, params) =>
973- val narrowedParam = simplify(intersect(params(idx), subSpace))
974- Some (simplify(Prod (tp, unappTp, params.updated(idx, narrowedParam))))
975- case _ => None
976- case None =>
977- if simplify(minus(project(selTyp), subSpace)) == Empty then Some (patSpace)
978- else None
1007+ (selector, pat) match
1008+ case SelectorBoundVar () =>
1009+ Some (simplify(intersect(patSpace, subSpace)))
1010+ case SelectorParamIndex (idx) =>
1011+ narrowProdParam(patSpace, idx, subSpace)
1012+ case SelectorFieldIndex (idx) =>
1013+ narrowProdParam(patSpace, idx, subSpace)
1014+ case _ if simplify(minus(project(selTyp), subSpace)) == Empty =>
1015+ Some (patSpace)
1016+ case _ => None
9791017
9801018 /** Resolve the space covered by a case and whether it may be partial.
9811019 * @return (space, maybePartial) where maybePartial is true when the case
0 commit comments