1 (* Copyright (C) 2009,2011,2017 Matthew Fluet.
2 * Copyright (C) 1999-2008 Henry Cejtin, Matthew Fluet, Suresh
3 * Jagannathan, and Stephen Weeks.
4 * Copyright (C) 1997-2000 NEC Research Institute.
6 * MLton is released under a BSD-style license.
7 * See the file MLton-LICENSE for details.
10 functor TypeCheck (S: TYPE_CHECK_STRUCTS): TYPE_CHECK =
15 datatype z = datatype Exp.t
16 datatype z = datatype Transfer.t
18 fun checkScopes (program as
19 Program.T {datatypes, globals, functions, main}): unit =
26 fun make' (layout, plist) =
29 Property.getSet (plist, Property.initConst Undefined)
32 Undefined => set (x, InScope v)
33 | _ => Error.bug ("Ssa.TypeCheck.checkScopes: duplicate definition of "
34 ^ (Layout.toString (layout x)))
38 | _ => Error.bug (concat
39 ["Ssa.TypeCheck.checkScopes: reference to ",
40 Layout.toString (layout x),
42 fun unbind x = set (x, Defined)
43 in (bind, ignore o reference, reference, unbind)
45 fun make (layout, plist) =
46 let val (bind, reference, _, unbind) = make' (layout, plist)
47 in (fn x => bind (x, ()), reference, unbind)
50 val (bindTycon, getTycon, getTycon', _) = make' (Tycon.layout, Tycon.plist)
51 val (bindCon, getCon, _) = make (Con.layout, Con.plist)
52 val (bindVar, getVar, getVar', unbindVar) = make' (Var.layout, Var.plist)
53 fun getVars xs = Vector.foreach (xs, getVar)
54 val (bindLabel, getLabel, unbindLabel) = make (Label.layout, Label.plist)
55 val (bindFunc, getFunc, _) = make (Func.layout, Func.plist)
57 val {destroy = destroyLoopType, get = loopType, ...} =
63 datatype z = datatype Type.dest
66 Array ty => loopType ty
68 | Datatype tycon => getTycon tycon
71 | Ref ty => loopType ty
73 | Tuple tys => Vector.foreach (tys, loopType)
74 | Vector ty => loopType ty
75 | Weak ty => loopType ty
80 fun loopTypes tys = Vector.foreach (tys, loopType)
81 (* Redefine bindVar to check well-formedness of types. *)
82 val bindVar = fn (x, ty) => (loopType ty; bindVar (x, ty))
87 ConApp {con, args, ...} => (getCon con ; getVars args)
89 | PrimApp {args, targs, ...} => (loopTypes targs; getVars args)
91 | Select {tuple, ...} => getVar tuple
92 | Tuple xs => getVars xs
97 fun loopStatement (s as Statement.T {var, ty, exp, ...}) =
101 val _ = Option.app (var, fn x => bindVar (x, ty))
105 handle exn => Error.reraiseSuffix (exn, concat [" in ", Layout.toString (Statement.layout s)])
107 fn Arith {args, ty, ...} => (getVars args; loopType ty)
109 | Call {func, args, ...} => (getFunc func; getVars args)
110 | Case {test, cases, default, ...} =>
112 fun doit (cases: ('a * 'b) vector,
113 equals: 'a * 'a -> bool,
115 numExhaustiveCases: IntInf.t) =
117 val table = HashSet.new {hash = hash}
124 (table, hash x, fn y => equals (x, y),
126 fn _ => Error.bug "Ssa.TypeCheck.loopTransfer: redundant branch in case")
130 val numCases = Int.toIntInf (Vector.length cases)
132 case (IntInf.equals (numCases, numExhaustiveCases), isSome default) of
134 Error.bug "Ssa.TypeCheck.loopTransfer: exhaustive case has default"
136 Error.bug "Ssa.TypeCheck.loopTransfer: non-exhaustive case has no default"
139 fun doitWord (ws, cases) =
140 doit (cases, WordX.equals, WordX.hash, WordSize.cardinality ws)
143 val numExhaustiveCases =
144 case Type.dest (getVar' test) of
145 Type.Datatype t => Int.toIntInf (getTycon' t)
146 | _ => Error.bug "Ssa.TypeCheck.loopTransfer: case test is not a datatype"
148 doit (cases, Con.equals, Con.hash, numExhaustiveCases)
153 Cases.Con cs => doitCon cs
154 | Cases.Word (ws, cs) => doitWord (ws, cs)
158 | Goto {args, ...} => getVars args
159 | Raise xs => getVars xs
160 | Return xs => getVars xs
161 | Runtime {args, ...} => getVars args
165 handle exn => Error.reraiseSuffix (exn, concat [" in ", Layout.toString (Transfer.layout t)])
166 fun loopFunc (f: Function.t) =
168 val {args, blocks, raises, returns, start, ...} = Function.dest f
169 (* Descend the dominator tree, verifying that variable definitions
170 * dominate variable uses.
172 fun loop (Tree.T (block, children)): unit =
174 val Block.T {label, args, statements, transfer, ...} = block
175 val _ = (Vector.foreach (args, bindVar)
176 ; Vector.foreach (statements, loopStatement)
177 ; loopTransfer transfer)
178 handle exn => Error.reraiseSuffix (exn, concat [" in ", Label.toString label])
179 val _ = Vector.foreach (children, loop)
180 val _ = Vector.foreach
182 Option.app (Statement.var s, unbindVar))
183 val _ = Vector.foreach (args, unbindVar o #1)
187 val _ = Vector.foreach (args, bindVar)
188 val _ = Vector.foreach (blocks, bindLabel o Block.label)
189 (* Check that 'start' and all transfer labels are in scope.
190 * In the case that something is not in scope,
191 * "getLabel" gives a more informative error message
192 * than the CFG/dominatorTree construction failure.
194 val _ = getLabel start
195 val _ = Vector.foreach
196 (blocks, fn Block.T {transfer, ...} =>
197 Transfer.foreachLabel (transfer, getLabel))
198 val _ = loop (Function.dominatorTree f)
199 val _ = Vector.foreach (blocks, unbindLabel o Block.label)
200 val _ = Vector.foreach (args, unbindVar o #1)
201 val _ = Option.app (returns, loopTypes)
202 val _ = Option.app (raises, loopTypes)
203 val _ = Function.clear f
207 handle exn => Error.reraiseSuffix (exn, concat [" in ", Func.toString (Function.name f)])
208 val _ = Vector.foreach
209 (datatypes, fn Datatype.T {tycon, cons} =>
210 (bindTycon (tycon, Vector.length cons)
211 ; Vector.foreach (cons, fn {con, ...} =>
213 handle exn => Error.reraiseSuffix (exn, concat [" in Datatypes"])
214 val _ = Vector.foreach
215 (datatypes, fn Datatype.T {cons, ...} =>
216 Vector.foreach (cons, fn {args, ...} =>
217 Vector.foreach (args, loopType)))
218 handle exn => Error.reraiseSuffix (exn, concat [" in Datatypes"])
219 val _ = Vector.foreach (globals, loopStatement)
220 handle exn => Error.reraiseSuffix (exn, concat [" in Globals"])
221 val _ = List.foreach (functions, bindFunc o Function.name)
223 val _ = List.foreach (functions, loopFunc)
224 val _ = Program.clearTop program
225 val _ = destroyLoopType ()
230 val checkScopes = Control.trace (Control.Detail, "checkScopes") checkScopes
236 fun checkProf (f: t): unit =
239 val {blocks, start, ...} = dest f
240 val {get = labelInfo, rem, set = setLabelInfo, ...} =
243 Property.initRaise ("info", Label.layout))
244 val _ = Vector.foreach (blocks, fn b as Block.T {label, ...} =>
247 sources = ref NONE}))
248 fun goto (l: Label.t, sources: SourceInfo.t list) =
250 fun bug (msg: string): 'a =
254 (blocks, fn Block.T {label, ...} =>
256 val {sources, ...} = labelInfo label
260 (seq [Label.layout label,
263 (List.layout SourceInfo.layout)
269 (concat ["Ssa.TypeCheck.checkProf: bug found in ", Label.toString l,
279 outputl (seq [str "goto (",
282 List.layout SourceInfo.layout sources,
286 val {block, sources = r} = labelInfo l
291 val _ = r := SOME sources
292 val Block.T {statements, transfer, ...} = block
293 datatype z = datatype Statement.t
294 datatype z = datatype ProfileExp.t
297 (statements, sources,
298 fn (Statement.T {exp, ...}, sources) =>
302 Enter s => s :: sources
305 [] => bug "unmatched Leave"
307 if SourceInfo.equals (s, s')
309 else bug "mismatched Leave"))
318 outputl (List.layout SourceInfo.layout sources,
323 Call {return, ...} =>
325 datatype z = datatype Return.t
335 then (case sources of
337 | _ => bug "nonempty sources when leaving function")
340 Transfer.foreachLabel
341 (transfer, fn l => goto (l, sources))
344 if List.equals (sources, sources', SourceInfo.equals)
346 else bug "mismatched block"
348 val _ = goto (start, [])
349 val _ = Vector.foreach (blocks, fn Block.T {label, ...} => rem label)
355 fun checkProf (Program.T {functions, ...}): unit =
356 List.foreach (functions, fn f => Function.checkProf f)
358 val checkProf = Control.trace (Control.Detail, "checkProf") checkProf
360 fun typeCheck (program as Program.T {datatypes, ...}): unit =
362 val _ = checkScopes program
364 if !Control.profile <> Control.ProfileNone
365 then checkProf program
367 fun coerce {from: Type.t, to: Type.t}: unit =
368 if Type.equals (from, to)
370 else Error.bug (concat ["Ssa.TypeCheck.coerce (",
371 (Layout.toString o Layout.record)
372 [("from", Type.layout from),
373 ("to", Type.layout to)],
375 fun coerces (from, to) =
376 Vector.foreach2 (from, to, fn (from, to) =>
377 coerce {from = from, to = to})
379 Trace.trace ("Ssa.TypeCheck.coerce",
380 fn {from, to} => let open Layout
381 in record [("from", Type.layout from),
382 ("to", Type.layout to)]
385 fun select {tuple: Type.t, offset: int, resultType = _}: Type.t =
386 case Type.deTupleOpt tuple of
387 NONE => Error.bug ("Ssa.TypeCheck.select (non tuple)")
388 | SOME ts => Vector.sub (ts, offset)
389 val {get = conInfo: Con.t -> {args: Type.t vector,
391 set = setConInfo, ...} =
393 (Con.plist, Property.initRaise ("Ssa.TypeCheck.conInfo", Con.layout))
396 (datatypes, fn Datatype.T {tycon, cons} =>
397 let val result = Type.datatypee tycon
399 (cons, fn {con, args} =>
400 setConInfo (con, {args = args,
403 fun conApp {con, args} =
405 val {args = args', result, ...} = conInfo con
406 val _ = coerces (args', args)
410 fun filter (test, con, args) =
412 val {result, args = args'} = conInfo con
413 val _ = coerce {from = test, to = result}
414 val _ = coerces (args', args)
417 fun primApp {args, prim, resultType, resultVar = _, targs} =
419 datatype z = datatype Prim.Name.t
421 if Type.checkPrimApp {args = args,
426 else Error.bug (concat ["Ssa.TypeCheck.primApp (",
432 if Vector.isEmpty targs
434 else Vector.layout Type.layout targs,
436 Vector.layout Type.layout args]
446 const = Type.ofConst,
448 filterWord = fn (from, s) => coerce {from = from,
450 fromType = fn x => x,
451 layout = Type.layout,
456 useFromTypeOnBinds = true
458 val _ = Program.clear program
463 val typeCheck = fn p =>
465 handle exn => Error.reraisePrefix (exn, "TypeError (SSA): ")
467 val typeCheck = Control.trace (Control.Pass, "typeCheck") typeCheck