1{-# OPTIONS_GHC -Wall #-}
2module Canonicalize.Expression
12import qualified Data.Graph as Graph
13import qualified Data.Map.Strict as Map
15import qualified AST.Canonical as Can
16import qualified AST.Source as Src
17import qualified AST.Module.Name as ModuleName
18import qualified AST.Utils.Binop as Binop
19import qualified AST.Utils.Type as Type
20import qualified AST.Valid as Valid
21import qualified Canonicalize.Environment as Env
22import qualified Canonicalize.Environment.Dups as Dups
23import qualified Canonicalize.Pattern as Pattern
24import qualified Canonicalize.Type as Type
25import qualified Data.Bag as Bag
26import qualified Data.Index as Index
27import qualified Data.OneOrMore as OneOrMore
28import qualified Elm.Name as N
29import qualified Reporting.Annotation as A
30import qualified Reporting.Error.Canonicalize as Error
31import qualified Reporting.Region as R
32import qualified Reporting.Result as Result
33import qualified Reporting.Warning as W
41 Result.Result i w Error.Error a
50 { _direct :: {-# UNPACK #-} !Int
51 , _delayed :: {-# UNPACK #-} !Int
59canonicalize :: Env.Env -> Valid.Expr -> Result FreeLocals [W.Warning] Can.Expr
60canonicalize env (A.At region expression) =
64 Result.ok (Can.Str string)
67 Result.ok (Can.Chr char)
70 Result.ok (Can.Int int)
73 Result.ok (Can.Float float)
75 Valid.Var maybePrefix name ->
76 findVar region env maybePrefix name
79 Can.List <$> traverse (canonicalize env) exprs
82 do (Env.Binop _ home name annotation _ _) <- Env.findBinop region env op
83 return (Can.VarOperator op home name annotation)
86 Can.Negate <$> canonicalize env expr
88 Valid.Binops ops final ->
89 A.toValue <$> canonicalizeBinops region env ops final
91 Valid.Lambda srcArgs body ->
93 do (args, bindings) <-
94 Pattern.verify Error.DPLambdaArgs $
95 traverse (Pattern.canonicalize env) srcArgs
98 Env.addLocals bindings env
100 (cbody, freeLocals) <-
101 verifyBindings W.Pattern bindings (canonicalize newEnv body)
103 return (Can.Lambda args cbody, freeLocals)
105 Valid.Call func args ->
107 <$> canonicalize env func
108 <*> traverse (canonicalize env) args
110 Valid.If branches finally ->
112 <$> traverse (canonicalizeIfBranch env) branches
113 <*> canonicalize env finally
115 Valid.Let defs expr ->
116 A.toValue <$> canonicalizeLet region env defs expr
118 Valid.Case expr branches ->
120 <$> canonicalize env expr
121 <*> traverse (canonicalizeCaseBranch env) branches
123 Valid.Accessor field ->
124 Result.ok $ Can.Accessor field
126 Valid.Access record field ->
128 <$> canonicalize env record
131 Valid.Update (A.At reg name) fields ->
132 do fieldDict <- Dups.checkFields fields
133 name_ <- findVar reg env Nothing name
134 Can.Update (A.At reg name_)
135 <$> traverse (canonicalize env) fieldDict
137 Valid.Record fields ->
138 do fieldDict <- Dups.checkFields fields
139 Can.Record <$> traverse (canonicalize env) fieldDict
144 Valid.Tuple a b cs ->
146 <$> canonicalize env a
147 <*> canonicalize env b
148 <*> canonicalizeTupleExtras region env cs
150 Valid.Shader uid src tipe ->
151 Result.ok (Can.Shader uid src tipe)
155-- CANONICALIZE TUPLE EXTRAS
158canonicalizeTupleExtras :: R.Region -> Env.Env -> [Valid.Expr] -> Result FreeLocals [W.Warning] (Maybe Can.Expr)
159canonicalizeTupleExtras region env extras =
165 Just <$> canonicalize env three
168 Result.throw (Error.TupleLargerThanThree region)
172-- CANONICALIZE IF BRANCH
175canonicalizeIfBranch :: Env.Env -> (Valid.Expr, Valid.Expr) -> Result FreeLocals [W.Warning] (Can.Expr, Can.Expr)
176canonicalizeIfBranch env (condition, branch) =
178 <$> canonicalize env condition
179 <*> canonicalize env branch
183-- CANONICALIZE CASE BRANCH
186canonicalizeCaseBranch :: Env.Env -> (Src.Pattern, Valid.Expr) -> Result FreeLocals [W.Warning] Can.CaseBranch
187canonicalizeCaseBranch env (pattern, expr) =
189 do (cpattern, bindings) <-
190 Pattern.verify Error.DPCaseBranch $
191 Pattern.canonicalize env pattern
192 newEnv <- Env.addLocals bindings env
194 (cexpr, freeLocals) <-
195 verifyBindings W.Pattern bindings (canonicalize newEnv expr)
197 return (Can.CaseBranch cpattern cexpr, freeLocals)
201-- CANONICALIZE BINOPS
204canonicalizeBinops :: R.Region -> Env.Env -> [(Valid.Expr, A.Located N.Name)] -> Valid.Expr -> Result FreeLocals [W.Warning] Can.Expr
205canonicalizeBinops overallRegion env ops final =
207 canonicalizeHelp (expr, A.At region op) =
209 <$> canonicalize env expr
210 <*> Env.findBinop region env op
212 runBinopStepper overallRegion =<< (
214 <$> traverse canonicalizeHelp ops
215 <*> canonicalize env final
221 | More [(Can.Expr, Env.Binop)] Can.Expr
222 | Error Env.Binop Env.Binop
225runBinopStepper :: R.Region -> Step -> Result FreeLocals w Can.Expr
226runBinopStepper overallRegion step =
234 More ( (expr, op) : rest ) final ->
235 runBinopStepper overallRegion $
236 toBinopStep (toBinop op expr) op rest final
238 Error (Env.Binop op1 _ _ _ _ _) (Env.Binop op2 _ _ _ _ _) ->
239 Result.throw (Error.Binop overallRegion op1 op2)
242toBinopStep :: (Can.Expr -> Can.Expr) -> Env.Binop -> [(Can.Expr, Env.Binop)] -> Can.Expr -> Step
243toBinopStep makeBinop rootOp@(Env.Binop _ _ _ _ rootAssociativity rootPrecedence) middle final =
246 Done (makeBinop final)
248 ( expr, op@(Env.Binop _ _ _ _ associativity precedence) ) : rest ->
249 if precedence < rootPrecedence then
251 More ((makeBinop expr, op) : rest) final
253 else if precedence > rootPrecedence then
255 case toBinopStep (toBinop op expr) op rest final of
257 Done (makeBinop newLast)
259 More newMiddle newLast ->
260 toBinopStep makeBinop rootOp newMiddle newLast
267 case (rootAssociativity, associativity) of
268 (Binop.Left, Binop.Left) ->
269 toBinopStep (\right -> toBinop op (makeBinop expr) right) op rest final
271 (Binop.Right, Binop.Right) ->
272 toBinopStep (\right -> makeBinop (toBinop op expr right)) op rest final
278toBinop :: Env.Binop -> Can.Expr -> Can.Expr -> Can.Expr
279toBinop (Env.Binop op home name annotation _ _) left right =
280 A.merge left right (Can.Binop op home name annotation left right)
287canonicalizeLet :: R.Region -> Env.Env -> [Valid.Def] -> Valid.Expr -> Result FreeLocals [W.Warning] Can.Expr
288canonicalizeLet letRegion env defs body =
290 do let keyedDefs = zip defs [ 0 .. length defs ]
291 let names = foldr addBindings Dups.none keyedDefs
292 nameDict <- Dups.detect (Error.DuplicatePattern Error.DPLetBinding) names
294 let bindings = Map.map A.toRegion nameDict
295 let defKeys = Map.map A.toValue nameDict
296 newEnv <- Env.addLocals bindings env
298 ((nodes, cbody), freeLocals) <-
299 verifyBindings W.Def bindings $
301 <$> traverse (defToNode defKeys newEnv) keyedDefs
302 <*> canonicalize newEnv body
305 detectCycles letRegion (Graph.stronglyConnComp nodes) cbody
307 return (letExpr, freeLocals)
315 Dups.Dict (A.Located Int)
318addBindings :: (Valid.Def, Int) -> Bindings -> Bindings
319addBindings (def, key) bindings =
321 Valid.Define defRegion (A.At region name) _ _ _ ->
322 Dups.insert name region (A.At defRegion key) bindings
324 Valid.Destruct defRegion pattern _ ->
325 addBindingsHelp (A.At defRegion key) pattern bindings
328addBindingsHelp :: A.Located Int -> Src.Pattern -> Bindings -> Bindings
329addBindingsHelp key (A.At region pattern) bindings =
335 Dups.insert name region key bindings
337 Src.PRecord fields ->
339 addField (A.At fieldRegion name) dict =
340 Dups.insert name fieldRegion key dict
342 foldr addField bindings fields
348 foldr (addBindingsHelp key) bindings (a:b:cs)
350 Src.PCtor _ _ _ patterns ->
351 foldr (addBindingsHelp key) bindings patterns
353 Src.PList patterns ->
354 foldr (addBindingsHelp key) bindings patterns
356 Src.PCons first rest ->
357 addBindingsHelp key rest (addBindingsHelp key first bindings)
359 Src.PAlias ptrn (A.At reg name) ->
360 addBindingsHelp key ptrn (Dups.insert name reg key bindings)
373-- BUILD BINDINGS GRAPH
377 (Binding, Int, [Int])
381 = Define R.Region Can.Def
382 | Destruct R.Region Can.Pattern Can.Expr
385defToNode :: Map.Map N.Name Int -> Env.Env -> (Valid.Def, Int) -> Result FreeLocals [W.Warning] Node
386defToNode defKeys env (def, key) =
388 Valid.Define region aname@(A.At _ name) srcArgs body Nothing ->
389 do (args, argBindings) <-
390 Pattern.verify (Error.DPFuncArgs name) $
391 traverse (Pattern.canonicalize env) srcArgs
394 Env.addLocals argBindings env
396 (cbody, freeLocals) <-
397 verifyBindings W.Pattern argBindings (canonicalize newEnv body)
399 logLetLocals args freeLocals
400 ( Define region (Can.Def aname args cbody)
402 , Map.elems (Map.intersection defKeys freeLocals)
405 Valid.Destruct region pattern body ->
406 do (cpattern, _bindings) <-
407 Pattern.verify Error.DPDestruct $
408 Pattern.canonicalize env pattern
410 Result.Result $ \freeLocals warnings bad good ->
411 case canonicalize env body of
414 (\_ ws es -> bad freeLocals ws es)
415 (\newFreeLocals warnings1 cbody ->
417 (Map.unionWith combineUses freeLocals newFreeLocals)
419 ( Destruct region cpattern cbody
421 , Map.elems (Map.intersection defKeys freeLocals)
425 Valid.Define region aname@(A.At _ name) srcArgs body (Just tipe) ->
426 do (Can.Forall freeVars ctipe) <- Type.toAnnotation env tipe
427 ((args, resultType), argBindings) <-
428 Pattern.verify (Error.DPFuncArgs name) $
429 gatherTypedArgs env name srcArgs ctipe Index.first []
432 Env.addLocals argBindings env
434 (cbody, freeLocals) <-
435 verifyBindings W.Pattern argBindings (canonicalize newEnv body)
437 logLetLocals args freeLocals
438 ( Define region (Can.TypedDef aname freeVars args cbody resultType)
440 , Map.elems (Map.intersection defKeys freeLocals)
444logLetLocals :: [arg] -> FreeLocals -> value -> Result FreeLocals w value
445logLetLocals args letLocals value =
446 Result.Result $ \freeLocals warnings _ good ->
448 ( Map.unionWith combineUses freeLocals $
451 _ -> Map.map delayUse letLocals
467 -> [(Can.Pattern, Can.Type)]
468 -> Result Pattern.DupsDict w ([(Can.Pattern, Can.Type)], Can.Type)
469gatherTypedArgs env name srcArgs tipe index revTypedArgs =
472 return (reverse revTypedArgs, tipe)
474 srcArg : otherSrcArgs ->
475 case Type.iteratedDealias tipe of
476 Can.TLambda argType resultType ->
477 do arg <- Pattern.canonicalize env srcArg
478 gatherTypedArgs env name otherSrcArgs resultType (Index.next index) $
479 (arg, argType) : revTypedArgs
482 let (A.At start _, A.At end _) = (head srcArgs, last srcArgs) in
484 Error.AnnotationTooShort (R.merge start end) name index (length srcArgs)
491detectCycles :: R.Region -> [Graph.SCC Binding] -> Can.Expr -> Result i w Can.Expr
492detectCycles letRegion sccs body =
500 Graph.AcyclicSCC binding ->
504 <$> detectCycles letRegion subSccs body
506 Destruct _ pattern expr ->
507 Can.LetDestruct pattern expr
508 <$> detectCycles letRegion subSccs body
510 Graph.CyclicSCC bindings ->
511 case traverse requireFunction bindings of
514 <$> detectCycles letRegion subSccs body
517 Result.throw (Error.RecursiveLet letRegion (map toCycleNodes bindings))
520requireFunction :: Binding -> Maybe Can.Def
521requireFunction binding =
523 Define _ def@(Can.Def _ (_:_) _) ->
526 Define _ def@(Can.TypedDef _ _ (_:_) _ _) ->
533toCycleNodes :: Binding -> Error.CycleNode
534toCycleNodes binding =
538 Can.Def name args _ ->
539 if null args then Error.CycleValue name else Error.CycleFunc name
541 Can.TypedDef name _ args _ _ ->
542 if null args then Error.CycleValue name else Error.CycleFunc name
544 Destruct _ pattern _ ->
545 Error.CyclePattern pattern
552logVar :: N.Name -> a -> Result FreeLocals w a
554 Result.Result $ \freeLocals warnings _ good ->
555 good (Map.insertWith combineUses name oneDirectUse freeLocals) warnings value
558{-# NOINLINE oneDirectUse #-}
564combineUses :: Uses -> Uses -> Uses
565combineUses (Uses a b) (Uses x y) =
569delayUse :: Uses -> Uses
570delayUse (Uses direct delayed) =
571 Uses 0 (direct + delayed)
581 -> Result FreeLocals [W.Warning] value
582 -> Result info [W.Warning] (value, FreeLocals)
583verifyBindings context bindings (Result.Result k) =
584 Result.Result $ \info warnings bad good ->
587 bad info warnings1 err
589 (\freeLocals warnings1 value ->
592 Map.difference freeLocals bindings
595 -- NOTE: Uses Map.size for O(1) lookup. This means there is
596 -- no dictionary allocation unless a problem is detected.
597 if Map.size bindings + Map.size outerFreeLocals == Map.size freeLocals then
600 Map.foldlWithKey (addUnusedWarning context) warnings1 $
601 Map.difference bindings freeLocals
603 good info warnings2 (value, outerFreeLocals)
607addUnusedWarning :: W.Context -> [W.Warning] -> N.Name -> R.Region -> [W.Warning]
608addUnusedWarning context warnings name region =
609 W.UnusedVariable region context name : warnings
612directUsage :: Result () w (expr, FreeLocals) -> Result FreeLocals w expr
613directUsage (Result.Result k) =
614 Result.Result $ \freeLocals warnings bad good ->
616 (\() ws es -> bad freeLocals ws es)
617 (\() ws (value, newFreeLocals) ->
618 good (Map.unionWith combineUses freeLocals newFreeLocals) ws value
622delayedUsage :: Result () w (expr, FreeLocals) -> Result FreeLocals w expr
623delayedUsage (Result.Result k) =
624 Result.Result $ \freeLocals warnings bad good ->
626 (\() ws es -> bad freeLocals ws es)
627 (\() ws (value, newFreeLocals) ->
628 let delayedLocals = Map.map delayUse newFreeLocals in
629 good (Map.unionWith combineUses freeLocals delayedLocals) ws value
637findVar :: R.Region -> Env.Env -> Maybe N.Name -> N.Name -> Result FreeLocals w Can.Expr_
638findVar region (Env.Env localHome vars _ _ _) maybePrefix name =
639 case Map.lookup name vars of
643 Result.throw (error "TODO no name" region)
646 if ModuleName.isKernel prefix then
647 Result.ok $ Can.VarKernel (ModuleName.getKernel prefix) name
649 Result.throw (error "TODO no name" region)
651 Just (Env.VarHomes unqualified qualified) ->
656 logVar name (Can.VarLocal name)
659 logVar name (Can.VarTopLevel localHome name)
663 Bag.One (Env.ForeignVarHome home annotation) ->
665 if home == ModuleName.debug then
666 Can.VarDebug localHome name annotation
668 Can.VarForeign home name annotation
671 Result.throw (error "TODO no unqualified" region)
674 Result.throw (error "TODO ambiguous unqualified" region)
677 case Map.lookup prefix qualified of
679 Result.throw (error "TODO no qualified" region)
681 Just (OneOrMore.One (Env.ForeignVarHome home annotation)) ->
683 if home == ModuleName.debug then
684 Can.VarDebug localHome name annotation
686 Can.VarForeign home name annotation
689 Result.throw (error "TODO ambiguous qualified" region)
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list