2 % (c) The GRASP/AQUA Project, Glasgow University, 1998
4 \section[UConSet]{UsageSP constraint solver}
6 This code is (based on) PhD work of Keith Wansbrough <kw217@cl.cam.ac.uk>,
7 February 1998 .. April 1999.
9 Keith Wansbrough 1998-02-16..1999-04-29
12 module UConSet ( UConSet,
22 #include "HsVersions.h"
25 import Type ( UsageAnn(..) )
27 import Bag ( Bag, unitBag, emptyBag, unionBags, foldlBag, bagToList )
32 ======================================================================
37 First, individual constraints on particular variables. This is
38 private to the implementation.
41 data UCon = UCEq UVar UVar -- j = k (equivalence)
42 | UCBound [UVar] UVar [UVar] -- {..} <= j <= {..}
43 | UCUsOnce UVar -- j = 1
44 | UCUsMany UVar -- j = omega
47 Next, the public (but abstract) data type for a usage constraint set:
48 either a bag of mappings from @UVar@ to @UCon@, or an error message
49 for an inconsistent constraint set.
52 data UConSet = UConSet (Bag (VarEnv UCon))
56 The idea is that the @VarEnv@s (which will eventually be merged into a
57 single @VarEnv@) are union-find data structures: a variable is either
58 equal to another variable, or it is bounded or has a value. The
59 equalities form a forest pointing to a root node for each equality
60 class, on which is found the bound or value for that class.
62 The @Bag@ enables two-phase operation: we merely collect constraints
63 in the first phase, an donly union them at solution time. This gives
64 a much more efficient algorithm, as we make only a single pass over
67 Note that the absence of a variable from the @VarEnv@ is exactly
68 equivalent to it being mapped to @UCBound [] _ []@.
74 @emptyUConSet@ gives an empty constraint set.
75 @eqManyUConSet@ constrains an annotation to be Many.
76 @eqUConSet@ constrains two annotations to be equal.
77 @leqUConSet@ constrains one annotation to be less than or equal to
78 another (with Once < Many).
81 mkUCS = UConSet . unitBag -- helper function not exported
83 emptyUConSet :: UConSet
84 emptyUConSet = UConSet emptyBag
86 eqManyUConSet :: UsageAnn -> UConSet
88 eqManyUConSet UsOnce = UConFail (text "Once /= Many")
89 eqManyUConSet UsMany = emptyUConSet
90 eqManyUConSet (UsVar uv) = mkUCS $ unitVarEnv uv (UCUsMany uv)
92 eqUConSet :: UsageAnn -> UsageAnn -> UConSet
94 eqUConSet UsOnce UsOnce = emptyUConSet
95 eqUConSet UsOnce (UsVar uv) = mkUCS $ unitVarEnv uv (UCUsOnce uv)
96 eqUConSet UsMany UsMany = emptyUConSet
97 eqUConSet UsMany (UsVar uv) = mkUCS $ unitVarEnv uv (UCUsMany uv)
98 eqUConSet (UsVar uv) UsOnce = mkUCS $ unitVarEnv uv (UCUsOnce uv)
99 eqUConSet (UsVar uv) UsMany = mkUCS $ unitVarEnv uv (UCUsMany uv)
100 eqUConSet (UsVar uv) (UsVar uv') = if uv==uv'
102 else mkUCS $ unitVarEnv uv (UCEq uv uv')
103 eqUConSet UsMany UsOnce = UConFail (text "Many /= Once")
104 eqUConSet UsOnce UsMany = UConFail (text "Once /= Many")
106 leqUConSet :: UsageAnn -> UsageAnn -> UConSet
108 leqUConSet UsOnce _ = emptyUConSet
109 leqUConSet _ UsMany = emptyUConSet
110 leqUConSet UsMany UsOnce = UConFail (text "Many /<= Once")
111 leqUConSet UsMany (UsVar uv) = mkUCS $ unitVarEnv uv (UCUsMany uv)
112 leqUConSet (UsVar uv) UsOnce = mkUCS $ unitVarEnv uv (UCUsOnce uv)
113 leqUConSet (UsVar uv) (UsVar uv') = mkUCS $ mkVarEnv [(uv, UCBound [] uv [uv']),
114 (uv',UCBound [uv] uv' [] )]
117 @unionUCS@ forms the union of two @UConSet@s.
118 @unionUCSs@ forms the `big union' of a list of @UConSet@s.
121 unionUCS :: UConSet -> UConSet -> UConSet
123 unionUCS (UConSet b1) (UConSet b2) = UConSet (b1 `unionBags` b2)
124 unionUCS ucs@(UConFail _) _ = ucs -- favour first error
125 unionUCS (UConSet _) ucs@(UConFail _) = ucs
127 unionUCSs :: [UConSet] -> UConSet
129 unionUCSs ucss = foldl unionUCS emptyUConSet ucss
133 @solveUCS@ finds the minimal solution to the constraint set, returning
134 it as @Just@ a substitution function taking usage variables to usage
135 annotations (@UsOnce@ or @UsMany@). If this is not possible (for an
136 inconsistent constraint set), @solveUCS@ returns @Nothing@.
138 The minimal solution is found by simply reading off the known
139 variables, and for unknown ones substituting @UsOnce@.
142 solveUCS :: UConSet -> Maybe (UVar -> UsageAnn)
144 solveUCS (UConSet css)
145 = case foldlBag (\cs1 jcs2 -> foldVarEnv addUCS cs1 jcs2)
148 Left cs -> let cs' = mapVarEnv conToSub cs
149 sub uv = case lookupVarEnv cs' uv of
152 conToSub (UCEq _ uv') = case lookupVarEnv cs uv' of
154 Just con' -> conToSub con'
155 conToSub (UCUsOnce _ ) = UsOnce
156 conToSub (UCUsMany _ ) = UsMany
157 conToSub (UCBound _ _ _ ) = UsOnce
159 Right err -> solveUCS (UConFail err)
161 solveUCS (UConFail why) =
163 pprTrace "UConFail:" why $
168 ======================================================================
173 In the internals, we use the @VarEnv UCon@ explicitly, or occasionally
174 @Either (VarEnv UCon) SDoc@. In other words, the @Bag@ is no longer
177 @findUCon@ finds the root of an equivalence class.
178 @changeUConUVar@ copies a constraint, but changes the variable constrained.
181 findUCon :: VarEnv UCon -> UVar -> UVar
184 = case lookupVarEnv cs uv of
185 Just (UCEq _ uv') -> findUCon cs uv'
189 changeUConUVar :: UCon -> UVar -> UCon
191 changeUConUVar (UCEq _ v ) uv' = (UCEq uv' v )
192 changeUConUVar (UCBound us _ vs) uv' = (UCBound us uv' vs)
193 changeUConUVar (UCUsOnce _ ) uv' = (UCUsOnce uv' )
194 changeUConUVar (UCUsMany _ ) uv' = (UCUsMany uv' )
197 @mergeUVars@ tests to see if a set of @UVar@s can be constrained. If
198 they can, it returns the set of root @UVar@s represented (with no
199 duplicates); if they can't, it returns @Nothing@.
202 mergeUVars :: VarEnv UCon -- current constraint set
203 -> Bool -- True/False = try to constrain to Many/Once
204 -> [UVar] -- list of UVars to constrain
205 -> Maybe [UVar] -- Just [root uvars to force], or Nothing if conflict
207 mergeUVars cs isMany vs = foldl muv (Just []) vs
209 muv :: Maybe [UVar] -> UVar -> Maybe [UVar]
213 = let rv = findUCon cs v
218 case lookupVarEnv cs rv of -- never UCEq
219 Nothing -> Just (rv:vs)
220 Just (UCBound _ _ _) -> Just (rv:vs)
221 Just (UCUsOnce _) -> if isMany then Nothing else jvs
222 Just (UCUsMany _) -> if isMany then jvs else Nothing
225 @addUCS@ adds an individual @UCon@ on a @UVar@ to a @UConSet@. This
226 is the core of the algorithm. As such, it could probably use some
230 addUCS :: UCon -- constraint to add
231 -> Either (VarEnv UCon) SDoc -- old constraint set or error
232 -> Either (VarEnv UCon) SDoc -- new constraint set or error
234 addUCS _ jcs@(Right _) = jcs -- propagate errors
236 addUCS (UCEq uv1 uv2) jcs@(Left cs)
237 = let ruv1 = findUCon cs uv1
238 ruv2 = findUCon cs uv2
240 then jcs -- no change if already equal
241 else let cs' = Left $ extendVarEnv cs ruv1 (UCEq ruv1 ruv2) -- merge trees
242 in case lookupVarEnv cs ruv1 of
244 -> addUCS (changeUConUVar uc' ruv2) cs' -- merge old constraints
248 addUCS (UCBound us uv1 vs) jcs@(Left cs)
249 = let ruv1 = findUCon cs uv1
250 in case lookupWithDefaultVarEnv cs (UCBound [] ruv1 []) ruv1 of -- never UCEq
252 -> case (mergeUVars cs False (us'++us),
253 mergeUVars cs True (vs'++vs)) of
254 (Just us'',Just vs'') -- update
255 -> Left $ extendVarEnv cs ruv1 (UCBound us'' ruv1 vs'')
256 (Nothing, Just vs'') -- set
257 -> addUCS (UCUsMany ruv1)
258 (forceUVars UCUsMany vs'' jcs)
259 (Just us'',Nothing) -- set
260 -> addUCS (UCUsOnce ruv1)
261 (forceUVars UCUsOnce us'' jcs)
262 (Nothing, Nothing) -- fail
263 -> Right (text "union failed[B] at" <+> ppr uv1)
265 -> forceUVars UCUsOnce us jcs
267 -> forceUVars UCUsMany vs jcs
269 addUCS (UCUsOnce uv1) jcs@(Left cs)
270 = let ruv1 = findUCon cs uv1
271 in case lookupWithDefaultVarEnv cs (UCBound [] ruv1 []) ruv1 of -- never UCEq
273 -> forceUVars UCUsOnce us (Left $ extendVarEnv cs ruv1 (UCUsOnce ruv1))
277 -> Right (text "union failed[O] at" <+> ppr uv1)
279 addUCS (UCUsMany uv1) jcs@(Left cs)
280 = let ruv1 = findUCon cs uv1
281 in case lookupWithDefaultVarEnv cs (UCBound [] ruv1 []) ruv1 of -- never UCEq
283 -> forceUVars UCUsMany vs (Left $ extendVarEnv cs ruv1 (UCUsMany ruv1))
285 -> Right (text "union failed[M] at" <+> ppr uv1)
289 -- helper function forcing a set of UVars to either Once or Many:
290 forceUVars :: (UVar -> UCon)
292 -> Either (VarEnv UCon) SDoc
293 -> Either (VarEnv UCon) SDoc
294 forceUVars uc uvs cs0 = foldl (\cs uv -> addUCS (uc uv) cs) cs0 uvs
297 ======================================================================
303 -- Printing a usage constraint.
305 pprintUCon :: VarEnv UCon -> UCon -> SDoc
307 pprintUCon fm (UCEq uv1 uv2)
308 = ppr uv1 <+> text "=" <+> ppr uv2 <> text ":"
309 <+> let uv2' = findUCon fm uv2
310 in case lookupVarEnv fm uv2' of
311 Just uc -> pprintUCon fm uc
312 Nothing -> text "unconstrained"
314 pprintUCon fm (UCBound us uv vs)
315 = lbrace <> hcat (punctuate comma (map ppr us)) <> rbrace
316 <+> text "<=" <+> ppr uv <+> text "<="
317 <+> lbrace <> hcat (punctuate comma (map ppr vs)) <> rbrace
319 pprintUCon fm (UCUsOnce uv)
320 = ppr uv <+> text "=" <+> ppr UsOnce
322 pprintUCon fm (UCUsMany uv)
323 = ppr uv <+> text "=" <+> ppr UsMany
325 -- Printing a usage constraint set.
327 instance Outputable UConSet where
329 = text "UConSet:" <+> lbrace
330 $$ vcat (map (\fm -> nest 2 (vcat (map (pprintUCon fm) (rngVarEnv fm))))
335 = hang (text "UConSet inconsistent:")
339 ======================================================================