1{-# OPTIONS_GHC -Wall #-}
2{-# LANGUAGE OverloadedStrings #-}
3module Canonicalize.Expression
13import Control.Monad (foldM)
14import qualified Data.Graph as Graph
15import qualified Data.List as List
16import qualified Data.Map.Strict as Map
17import qualified Data.Text as Text
19import qualified AST.Canonical as Can
20import qualified AST.Source as Src
21import qualified AST.Module.Name as ModuleName
22import qualified AST.Utils.Binop as Binop
23import qualified AST.Utils.Type as Type
24import qualified AST.Valid as Valid
25import qualified Canonicalize.Environment as Env
26import qualified Canonicalize.Environment.Dups as Dups
27import qualified Canonicalize.Pattern as Pattern
28import qualified Canonicalize.Type as Type
29import qualified Data.Index as Index
30import qualified Data.OneOrMore as OneOrMore
31import qualified Elm.Name as N
32import qualified Reporting.Annotation as A
33import qualified Reporting.Error.Canonicalize as Error
34import qualified Reporting.Region as R
35import qualified Reporting.Result as Result
36import qualified Reporting.Warning as W
44 Result.Result i w Error.Error a
53 { _direct :: {-# UNPACK #-} !Int
54 , _delayed :: {-# UNPACK #-} !Int
62canonicalize :: Env.Env -> Valid.Expr -> Result FreeLocals [W.Warning] Can.Expr
63canonicalize env (A.At region expression) =
67 Result.ok (Can.Str string)
70 Result.ok (Can.Chr char)
73 Result.ok (Can.Int int)
76 Result.ok (Can.Float float)
78 Valid.Var varType name ->
80 Src.Value -> findVar region env name
81 Src.Ctor -> toVarCtor name <$> Env.findCtor region env name
83 Valid.VarQual varType prefix name ->
85 Src.Value -> findVarQual region env prefix name
86 Src.Ctor -> toVarCtor name <$> Env.findCtorQual region env prefix name
89 Can.List <$> traverse (canonicalize env) exprs
92 do (Env.Binop _ home name annotation _ _) <- Env.findBinop region env op
93 return (Can.VarOperator op home name annotation)
96 Can.Negate <$> canonicalize env expr
98 Valid.Binops ops final ->
99 A.toValue <$> canonicalizeBinops region env ops final
101 Valid.Lambda srcArgs body ->
103 do (args, bindings) <-
104 Pattern.verify Error.DPLambdaArgs $
105 traverse (Pattern.canonicalize env) srcArgs
108 Env.addLocals bindings env
110 (cbody, freeLocals) <-
111 verifyBindings W.Pattern bindings (canonicalize newEnv body)
113 return (Can.Lambda args cbody, freeLocals)
115 Valid.Call func args ->
117 <$> canonicalize env func
118 <*> traverse (canonicalize env) args
120 Valid.If branches finally ->
122 <$> traverse (canonicalizeIfBranch env) branches
123 <*> canonicalize env finally
125 Valid.Let defs expr ->
126 A.toValue <$> canonicalizeLet region env defs expr
128 Valid.Case expr branches ->
130 <$> canonicalize env expr
131 <*> traverse (canonicalizeCaseBranch env) branches
133 Valid.Accessor field ->
134 Result.ok $ Can.Accessor field
136 Valid.Access record field ->
138 <$> canonicalize env record
141 Valid.Update (A.At reg name) fields ->
142 do fieldDict <- Dups.checkFields fields
143 name_ <- findVar reg env name
144 Can.Update (A.At reg name_)
145 <$> traverse (canonicalize env) fieldDict
147 Valid.Record fields ->
148 do fieldDict <- Dups.checkFields fields
149 Can.Record <$> traverse (canonicalize env) fieldDict
154 Valid.Tuple a b cs ->
156 <$> canonicalize env a
157 <*> canonicalize env b
158 <*> canonicalizeTupleExtras region env cs
160 Valid.Shader uid src tipe ->
161 Result.ok (Can.Shader uid src tipe)
165-- CANONICALIZE TUPLE EXTRAS
168canonicalizeTupleExtras :: R.Region -> Env.Env -> [Valid.Expr] -> Result FreeLocals [W.Warning] (Maybe Can.Expr)
169canonicalizeTupleExtras region env extras =
175 Just <$> canonicalize env three
178 Result.throw (Error.TupleLargerThanThree region)
182-- CANONICALIZE IF BRANCH
185canonicalizeIfBranch :: Env.Env -> (Valid.Expr, Valid.Expr) -> Result FreeLocals [W.Warning] (Can.Expr, Can.Expr)
186canonicalizeIfBranch env (condition, branch) =
188 <$> canonicalize env condition
189 <*> canonicalize env branch
193-- CANONICALIZE CASE BRANCH
196canonicalizeCaseBranch :: Env.Env -> (Src.Pattern, Valid.Expr) -> Result FreeLocals [W.Warning] Can.CaseBranch
197canonicalizeCaseBranch env (pattern, expr) =
199 do (cpattern, bindings) <-
200 Pattern.verify Error.DPCaseBranch $
201 Pattern.canonicalize env pattern
202 newEnv <- Env.addLocals bindings env
204 (cexpr, freeLocals) <-
205 verifyBindings W.Pattern bindings (canonicalize newEnv expr)
207 return (Can.CaseBranch cpattern cexpr, freeLocals)
211-- CANONICALIZE BINOPS
214canonicalizeBinops :: R.Region -> Env.Env -> [(Valid.Expr, A.Located N.Name)] -> Valid.Expr -> Result FreeLocals [W.Warning] Can.Expr
215canonicalizeBinops overallRegion env ops final =
217 canonicalizeHelp (expr, A.At region op) =
219 <$> canonicalize env expr
220 <*> Env.findBinop region env op
222 runBinopStepper overallRegion =<< (
224 <$> traverse canonicalizeHelp ops
225 <*> canonicalize env final
231 | More [(Can.Expr, Env.Binop)] Can.Expr
232 | Error Env.Binop Env.Binop
235runBinopStepper :: R.Region -> Step -> Result FreeLocals w Can.Expr
236runBinopStepper overallRegion step =
244 More ( (expr, op) : rest ) final ->
245 runBinopStepper overallRegion $
246 toBinopStep (toBinop op expr) op rest final
248 Error (Env.Binop op1 _ _ _ _ _) (Env.Binop op2 _ _ _ _ _) ->
249 Result.throw (Error.Binop overallRegion op1 op2)
252toBinopStep :: (Can.Expr -> Can.Expr) -> Env.Binop -> [(Can.Expr, Env.Binop)] -> Can.Expr -> Step
253toBinopStep makeBinop rootOp@(Env.Binop _ _ _ _ rootAssociativity rootPrecedence) middle final =
256 Done (makeBinop final)
258 ( expr, op@(Env.Binop _ _ _ _ associativity precedence) ) : rest ->
259 if precedence < rootPrecedence then
261 More ((makeBinop expr, op) : rest) final
263 else if precedence > rootPrecedence then
265 case toBinopStep (toBinop op expr) op rest final of
267 Done (makeBinop newLast)
269 More newMiddle newLast ->
270 toBinopStep makeBinop rootOp newMiddle newLast
277 case (rootAssociativity, associativity) of
278 (Binop.Left, Binop.Left) ->
279 toBinopStep (\right -> toBinop op (makeBinop expr) right) op rest final
281 (Binop.Right, Binop.Right) ->
282 toBinopStep (\right -> makeBinop (toBinop op expr right)) op rest final
288toBinop :: Env.Binop -> Can.Expr -> Can.Expr -> Can.Expr
289toBinop (Env.Binop op home name annotation _ _) left right =
290 A.merge left right (Can.Binop op home name annotation left right)
297canonicalizeLet :: R.Region -> Env.Env -> [Valid.Def] -> Valid.Expr -> Result FreeLocals [W.Warning] Can.Expr
298canonicalizeLet letRegion env defs body =
301 Dups.detect (Error.DuplicatePattern Error.DPLetBinding) $
302 List.foldl' addBindings Dups.none defs
304 newEnv <- Env.addLocals bindings env
306 verifyBindings W.Def bindings $
307 do nodes <- foldM (addDefNodes newEnv) [] defs
308 cbody <- canonicalize newEnv body
309 detectCycles letRegion (Graph.stronglyConnComp nodes) cbody
316addBindings :: Dups.Dict R.Region -> Valid.Def -> Dups.Dict R.Region
317addBindings bindings def =
319 Valid.Define _ (A.At region name) _ _ _ ->
320 Dups.insert name region region bindings
322 Valid.Destruct _ pattern _ ->
323 addBindingsHelp bindings pattern
326addBindingsHelp :: Dups.Dict R.Region -> Src.Pattern -> Dups.Dict R.Region
327addBindingsHelp bindings (A.At region pattern) =
333 Dups.insert name region region bindings
335 Src.PRecord fields ->
337 addField dict (A.At fieldRegion name) =
338 Dups.insert name fieldRegion fieldRegion dict
340 List.foldl' addField bindings fields
346 List.foldl' addBindingsHelp bindings (a:b:cs)
348 Src.PCtor _ _ patterns ->
349 List.foldl' addBindingsHelp bindings patterns
351 Src.PCtorQual _ _ _ patterns ->
352 List.foldl' addBindingsHelp bindings patterns
354 Src.PList patterns ->
355 List.foldl' addBindingsHelp bindings patterns
358 addBindingsHelp (addBindingsHelp bindings hd) tl
360 Src.PAlias aliasPattern (A.At nameRegion name) ->
361 Dups.insert name nameRegion nameRegion $
362 addBindingsHelp bindings aliasPattern
375-- BUILD BINDINGS GRAPH
379 (Binding, N.Name, [N.Name])
384 | Edge (A.Located N.Name)
385 | Destruct Can.Pattern Can.Expr
388addDefNodes :: Env.Env -> [Node] -> Valid.Def -> Result FreeLocals [W.Warning] [Node]
389addDefNodes env nodes def =
391 Valid.Define _ aname@(A.At _ name) srcArgs body maybeType ->
394 do (args, argBindings) <-
395 Pattern.verify (Error.DPFuncArgs name) $
396 traverse (Pattern.canonicalize env) srcArgs
399 Env.addLocals argBindings env
401 (cbody, freeLocals) <-
402 verifyBindings W.Pattern argBindings (canonicalize newEnv body)
404 let cdef = Can.Def aname args cbody
405 let node = ( Define cdef, name, Map.keys freeLocals )
406 logLetLocals args freeLocals (node:nodes)
409 do (Can.Forall freeVars ctipe) <- Type.toAnnotation env tipe
410 ((args, resultType), argBindings) <-
411 Pattern.verify (Error.DPFuncArgs name) $
412 gatherTypedArgs env name srcArgs ctipe Index.first []
415 Env.addLocals argBindings env
417 (cbody, freeLocals) <-
418 verifyBindings W.Pattern argBindings (canonicalize newEnv body)
420 let cdef = Can.TypedDef aname freeVars args cbody resultType
421 let node = ( Define cdef, name, Map.keys freeLocals )
422 logLetLocals args freeLocals (node:nodes)
424 Valid.Destruct _ pattern body ->
425 do (cpattern, _bindings) <-
426 Pattern.verify Error.DPDestruct $
427 Pattern.canonicalize env pattern
429 Result.Result $ \fs ws bad good ->
430 case canonicalize env body of
433 (\freeLocals warnings errors ->
434 bad (Map.unionWith combineUses freeLocals fs) warnings errors
436 (\freeLocals warnings cbody ->
438 names = getPatternNames [] pattern
439 name = Text.cons '$' (Text.intercalate "$" (map A.toValue names))
440 node = ( Destruct cpattern cbody, name, Map.keys freeLocals )
443 (Map.unionWith combineUses fs freeLocals)
445 (List.foldl' (addEdge [name]) (node:nodes) names)
449logLetLocals :: [arg] -> FreeLocals -> value -> Result FreeLocals w value
450logLetLocals args letLocals value =
451 Result.Result $ \freeLocals warnings _ good ->
453 ( Map.unionWith combineUses freeLocals $
456 _ -> Map.map delayUse letLocals
462addEdge :: [N.Name] -> [Node] -> A.Located N.Name -> [Node]
463addEdge edges nodes aname@(A.At _ name) =
464 (Edge aname, name, edges) : nodes
467getPatternNames :: [A.Located N.Name] -> Src.Pattern -> [A.Located N.Name]
468getPatternNames names (A.At region pattern) =
470 Src.PAnything -> names
471 Src.PVar name -> A.At region name : names
472 Src.PRecord fields -> fields ++ names
473 Src.PAlias ptrn name -> getPatternNames (name : names) ptrn
475 Src.PTuple a b cs -> List.foldl' getPatternNames (getPatternNames (getPatternNames names a) b) cs
476 Src.PCtor _ _ args -> List.foldl' getPatternNames names args
477 Src.PCtorQual _ _ _ args -> List.foldl' getPatternNames names args
478 Src.PList patterns -> List.foldl' getPatternNames names patterns
479 Src.PCons hd tl -> getPatternNames (getPatternNames names hd) tl
495 -> [(Can.Pattern, Can.Type)]
496 -> Result Pattern.DupsDict w ([(Can.Pattern, Can.Type)], Can.Type)
497gatherTypedArgs env name srcArgs tipe index revTypedArgs =
500 return (reverse revTypedArgs, tipe)
502 srcArg : otherSrcArgs ->
503 case Type.iteratedDealias tipe of
504 Can.TLambda argType resultType ->
505 do arg <- Pattern.canonicalize env srcArg
506 gatherTypedArgs env name otherSrcArgs resultType (Index.next index) $
507 (arg, argType) : revTypedArgs
510 let (A.At start _, A.At end _) = (head srcArgs, last srcArgs) in
512 Error.AnnotationTooShort (R.merge start end) name index (length srcArgs)
519detectCycles :: R.Region -> [Graph.SCC Binding] -> Can.Expr -> Result i w Can.Expr
520detectCycles letRegion sccs body =
527 Graph.AcyclicSCC binding ->
530 A.At letRegion . Can.Let def <$> detectCycles letRegion subSccs body
533 detectCycles letRegion subSccs body
535 Destruct pattern expr ->
536 A.At letRegion . Can.LetDestruct pattern expr <$> detectCycles letRegion subSccs body
538 Graph.CyclicSCC bindings ->
541 <$> checkCycle bindings []
542 <*> detectCycles letRegion subSccs body
546checkCycle :: [Binding] -> [Can.Def] -> Result i w [Can.Def]
547checkCycle bindings defs =
552 binding : otherBindings ->
554 Define def@(Can.Def name args _) ->
556 Result.throw (Error.RecursiveLet name (toNames otherBindings defs))
558 checkCycle otherBindings (def:defs)
560 Define def@(Can.TypedDef name _ args _ _) ->
562 Result.throw (Error.RecursiveLet name (toNames otherBindings defs))
564 checkCycle otherBindings (def:defs)
567 Result.throw (Error.RecursiveLet name (toNames otherBindings defs))
570 -- a Destruct cannot appear in a cycle without any Edge values
571 -- so we just keep going until we get to the edges
572 checkCycle otherBindings defs
575toNames :: [Binding] -> [Can.Def] -> [N.Name]
576toNames bindings revDefs =
579 reverse (map getDefName revDefs)
581 binding : otherBindings ->
583 Define def -> getDefName def : toNames otherBindings revDefs
584 Edge (A.At _ name) -> name : toNames otherBindings revDefs
585 Destruct _ _ -> toNames otherBindings revDefs
588getDefName :: Can.Def -> N.Name
591 Can.Def (A.At _ name) _ _ ->
594 Can.TypedDef (A.At _ name) _ _ _ _ ->
602logVar :: N.Name -> a -> Result FreeLocals w a
604 Result.Result $ \freeLocals warnings _ good ->
605 good (Map.insertWith combineUses name oneDirectUse freeLocals) warnings value
608{-# NOINLINE oneDirectUse #-}
614combineUses :: Uses -> Uses -> Uses
615combineUses (Uses a b) (Uses x y) =
619delayUse :: Uses -> Uses
620delayUse (Uses direct delayed) =
621 Uses 0 (direct + delayed)
631 -> Result FreeLocals [W.Warning] value
632 -> Result info [W.Warning] (value, FreeLocals)
633verifyBindings context bindings (Result.Result k) =
634 Result.Result $ \info warnings bad good ->
637 bad info warnings1 err
639 (\freeLocals warnings1 value ->
642 Map.difference freeLocals bindings
645 -- NOTE: Uses Map.size for O(1) lookup. This means there is
646 -- no dictionary allocation unless a problem is detected.
647 if Map.size bindings + Map.size outerFreeLocals == Map.size freeLocals then
650 Map.foldlWithKey (addUnusedWarning context) warnings1 $
651 Map.difference bindings freeLocals
653 good info warnings2 (value, outerFreeLocals)
657addUnusedWarning :: W.Context -> [W.Warning] -> N.Name -> R.Region -> [W.Warning]
658addUnusedWarning context warnings name region =
659 W.UnusedVariable region context name : warnings
662directUsage :: Result () w (expr, FreeLocals) -> Result FreeLocals w expr
663directUsage (Result.Result k) =
664 Result.Result $ \freeLocals warnings bad good ->
666 (\() ws es -> bad freeLocals ws es)
667 (\() ws (value, newFreeLocals) ->
668 good (Map.unionWith combineUses freeLocals newFreeLocals) ws value
672delayedUsage :: Result () w (expr, FreeLocals) -> Result FreeLocals w expr
673delayedUsage (Result.Result k) =
674 Result.Result $ \freeLocals warnings bad good ->
676 (\() ws es -> bad freeLocals ws es)
677 (\() ws (value, newFreeLocals) ->
678 let delayedLocals = Map.map delayUse newFreeLocals in
679 good (Map.unionWith combineUses freeLocals delayedLocals) ws value
687findVar :: R.Region -> Env.Env -> N.Name -> Result FreeLocals w Can.Expr_
688findVar region (Env.Env localHome vars _ _ _ _ _ _) name =
689 case Map.lookup name vars of
693 logVar name (Can.VarLocal name)
696 logVar name (Can.VarTopLevel localHome name)
700 OneOrMore.One (Env.ForeignVar home annotation) ->
702 if home == ModuleName.debug then
703 Can.VarDebug localHome name annotation
705 Can.VarForeign home name annotation
707 OneOrMore.More _ _ ->
708 Result.throw (error "TODO ambiguous unqualified" region)
711 Result.throw (error "TODO no name" region)
714findVarQual :: R.Region -> Env.Env -> N.Name -> N.Name -> Result FreeLocals w Can.Expr_
715findVarQual region (Env.Env localHome _ _ _ _ vars _ _) prefix name =
716 case Map.lookup prefix vars of
718 case Map.lookup name qualified of
719 Just (OneOrMore.One (Env.ForeignVar home annotation)) ->
721 if home == ModuleName.debug then
722 Can.VarDebug localHome name annotation
724 Can.VarForeign home name annotation
727 Result.throw (error "TODO ambiguous qualified" region)
730 Result.throw (error "TODO no qualified" region)
733 if ModuleName.isKernel prefix then
734 Result.ok $ Can.VarKernel (ModuleName.getKernel prefix) name
736 Result.throw (error "TODO no name" region)
742toVarCtor :: N.Name -> Env.Ctor -> Can.Expr_
745 Env.Ctor home typeName (Can.Union vars _ _ opts) index args ->
747 freeVars = Map.fromList (map (\v -> (v, ())) vars)
748 result = Can.TType home typeName (map Can.TVar vars)
749 tipe = foldr Can.TLambda result args
751 Can.VarCtor opts home name index (Can.Forall freeVars tipe)
753 Env.RecordCtor home vars tipe ->
755 freeVars = Map.fromList (map (\v -> (v, ())) vars)
757 Can.VarCtor Can.Normal home name Index.first (Can.Forall freeVars tipe)
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