[Git][ghc/ghc][wip/sjakobi/T27379] Base subVarSet/subDVarSet on a proper subset operation
Simon Jakobi pushed to branch wip/sjakobi/T27379 at Glasgow Haskell Compiler / GHC Commits: 3f0299bd by Simon Jakobi at 2026-06-16T12:42:42+02:00 Base subVarSet/subDVarSet on a proper subset operation Previously these were defined as isEmpty (s1 minus s2), which materializes the entire difference map just to test it for emptiness. The new subUFM/subUDFM use Word64Map.keysAreSubsetOf, which short-circuits on the first non-member and allocates nothing. Complexity-wise the recursion is driven by the first map and only probes the second to depth W (the word width) at each step, never traversing it in full. So keysAreSubsetOf is O(n*W), i.e. O(n) in the size of the first map for fixed W, rather than the O(n+m) of the difference-based version. Closes #27379 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> - - - - - 5 changed files: - compiler/GHC/Data/Word64Map/Internal.hs - compiler/GHC/Data/Word64Map/Lazy.hs - compiler/GHC/Types/Unique/DFM.hs - compiler/GHC/Types/Unique/FM.hs - compiler/GHC/Types/Var/Set.hs Changes: ===================================== compiler/GHC/Data/Word64Map/Internal.hs ===================================== @@ -235,6 +235,7 @@ module GHC.Data.Word64Map.Internal ( -- * Submap , isSubmapOf, isSubmapOfBy , isProperSubmapOf, isProperSubmapOfBy + , keysAreSubsetOf -- * Min\/Max , lookupMin @@ -2441,6 +2442,21 @@ isSubmapOfBy predicate (Tip k x) t = case lookup k t of Nothing -> False isSubmapOfBy _ Nil _ = True +-- | \(O(n \cdot W)\). Are all of the first map's keys present in the second? +-- +-- Like @'isSubmapOfBy' (\\_ _ -> True)@ but fully value-agnostic. +keysAreSubsetOf :: Word64Map a -> Word64Map b -> Bool +keysAreSubsetOf t1@(Bin p1 m1 l1 r1) (Bin p2 m2 l2 r2) + | shorter m1 m2 = False + | shorter m2 m1 = match p1 p2 m2 && + if zero p1 m2 + then keysAreSubsetOf t1 l2 + else keysAreSubsetOf t1 r2 + | otherwise = p1 == p2 && keysAreSubsetOf l1 l2 && keysAreSubsetOf r1 r2 +keysAreSubsetOf (Bin _ _ _ _) _ = False +keysAreSubsetOf (Tip k _) t = member k t +keysAreSubsetOf Nil _ = True + {-------------------------------------------------------------------- Mapping --------------------------------------------------------------------} ===================================== compiler/GHC/Data/Word64Map/Lazy.hs ===================================== @@ -206,6 +206,7 @@ module GHC.Data.Word64Map.Lazy ( -- * Submap , isSubmapOf, isSubmapOfBy , isProperSubmapOf, isProperSubmapOfBy + , keysAreSubsetOf -- * Min\/Max , lookupMin ===================================== compiler/GHC/Types/Unique/DFM.hs ===================================== @@ -48,7 +48,7 @@ module GHC.Types.Unique.DFM ( isNullUDFM, sizeUDFM, intersectUDFM, udfmIntersectUFM, - disjointUDFM, disjointUdfmUfm, + disjointUDFM, disjointUdfmUfm, subUDFM, equalKeysUDFM, minusUDFM, listToUDFM, listToUDFM_Directly, @@ -388,6 +388,10 @@ disjointUDFM (UDFM x _i) (UDFM y _j) = M.disjoint x y disjointUdfmUfm :: UniqDFM key elt -> UniqFM key elt2 -> Bool disjointUdfmUfm (UDFM x _i) y = M.disjoint x (ufmToIntMap y) +-- | True if the first map's keys are a subset of the second's. +subUDFM :: UniqDFM key elt1 -> UniqDFM key elt2 -> Bool +subUDFM (UDFM x _i) (UDFM y _j) = M.keysAreSubsetOf x y + minusUDFM :: UniqDFM key elt1 -> UniqDFM key elt2 -> UniqDFM key elt1 minusUDFM (UDFM x i) (UDFM y _j) = UDFM (M.difference x y) i -- M.difference returns a subset of a left set, so `i` is a good upper ===================================== compiler/GHC/Types/Unique/FM.hs ===================================== @@ -66,6 +66,7 @@ module GHC.Types.Unique.FM ( intersectUFM_C, strictIntersectUFM_C, disjointUFM, + subUFM, equalKeysUFM, diffUFM, nonDetStrictFoldUFM, nonDetFoldUFM, nonDetStrictFoldUFM_DirectlyM, @@ -430,6 +431,10 @@ strictIntersectUFM_C f (UFM x) (UFM y) = UFM (MS.intersectionWith f x y) disjointUFM :: UniqFM key elt1 -> UniqFM key elt2 -> Bool disjointUFM (UFM x) (UFM y) = M.disjoint x y +-- | True if the first map's keys are a subset of the second's. +subUFM :: UniqFM key elt1 -> UniqFM key elt2 -> Bool +subUFM (UFM x) (UFM y) = M.keysAreSubsetOf x y + -- | Fold over a 'UniqFM'. -- -- Non-deterministic, unless the folding function is commutative ===================================== compiler/GHC/Types/Var/Set.hs ===================================== @@ -53,8 +53,8 @@ import GHC.Types.Unique import GHC.Types.Name ( Name ) import GHC.Types.Unique.Set import GHC.Types.Unique.DSet -import GHC.Types.Unique.FM( disjointUFM, pluralUFM, pprUFM ) -import GHC.Types.Unique.DFM( disjointUDFM, udfmToUfm, anyUDFM, allUDFM ) +import GHC.Types.Unique.FM( disjointUFM, subUFM, pluralUFM, pprUFM ) +import GHC.Types.Unique.DFM( disjointUDFM, subUDFM, udfmToUfm, anyUDFM, allUDFM ) import GHC.Utils.Outputable (SDoc) -- | A non-deterministic Variable Set @@ -141,7 +141,7 @@ mapUnionVarSet get_set xs = foldr (unionVarSet . get_set) emptyVarSet xs -- See comments with type signatures intersectsVarSet s1 s2 = not (s1 `disjointVarSet` s2) disjointVarSet s1 s2 = disjointUFM (getUniqSet s1) (getUniqSet s2) -subVarSet s1 s2 = isEmptyVarSet (s1 `minusVarSet` s2) +subVarSet s1 s2 = subUFM (getUniqSet s1) (getUniqSet s2) anyVarSet :: (Var -> Bool) -> VarSet -> Bool anyVarSet = uniqSetAny @@ -261,7 +261,7 @@ dVarSetElems :: DVarSet -> [Var] dVarSetElems = uniqDSetToList subDVarSet :: DVarSet -> DVarSet -> Bool -subDVarSet s1 s2 = isEmptyDVarSet (s1 `minusDVarSet` s2) +subDVarSet s1 s2 = subUDFM (getUniqDSet s1) (getUniqDSet s2) unionDVarSet :: DVarSet -> DVarSet -> DVarSet unionDVarSet = unionUniqDSets View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/3f0299bde8063675d36432ff2ebf44a2... -- View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/3f0299bde8063675d36432ff2ebf44a2... You're receiving this email because of your account on gitlab.haskell.org.
participants (1)
-
Simon Jakobi (@sjakobi2)