typecheck.ml 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  1. (*
  2. * Do a number of checks:
  3. * - A void function must not return a value.
  4. * - A non-void function must return a value of the correct type.
  5. * - Array indices must be of type integer.
  6. * - The number of array indices must match the number of array dimensions.
  7. * - The type on the right-hand side of an assignment must match the type on
  8. * the left-hand side.
  9. * - The number of arguments used for a function call must match the number of
  10. * parameters for that function.
  11. * - The types of the function arguments must match the types of parameters.
  12. * - The operands of a unary or binary operation must have valid types.
  13. * - The predicate expression of an if, while, or do-while statement must be
  14. * a boolean.
  15. * - Only values of a basic type can be type cast.
  16. *)
  17. open Printf
  18. open Types
  19. open Util
  20. open Stringify
  21. let node_error (node, msg) = FatalError (NodeMsg (node, msg))
  22. (* Stringify a list of types for use in error messages.
  23. * ctype list -> string *)
  24. let rec types2str = function
  25. | [] -> ""
  26. | [ctype] -> type2str ctype
  27. | ctype :: tail -> type2str ctype ^ " or " ^ (types2str tail)
  28. let array_depth = function
  29. | ArrayDims (_, dims) -> List.length dims
  30. | _ -> raise InvalidNode
  31. let spec = function
  32. | ArrayDims (ctype, dims) -> (ctype, List.length dims)
  33. | ctype -> (ctype, 0)
  34. let type2str_error = function
  35. | ArrayDims (ctype, dims) ->
  36. type2str ctype ^ "[" ^ repeat "," (List.length dims - 1) ^ "]"
  37. | ctype ->
  38. type2str ctype
  39. let check_type ?(msg="") expected node =
  40. let got = typeof node in
  41. if (spec got) <> (spec expected) then begin
  42. let msg = match msg with
  43. | "" -> sprintf "type mismatch: expected type %s, got %s"
  44. (type2str_error expected) (type2str_error got)
  45. | _ -> msg
  46. in
  47. raise (node_error (node, msg))
  48. end
  49. let op_types = function
  50. | Not | And | Or -> [Bool]
  51. | Mod -> [Int]
  52. | Neg | Sub | Div | Lt | Le | Gt | Ge -> [Int; Float]
  53. | Add | Mul | Eq | Ne -> [Bool; Int; Float]
  54. let op_result_type opnd_type = function
  55. | Not | And | Or | Eq | Ne | Lt | Le | Gt | Ge -> Bool
  56. | Neg | Add | Sub | Mul | Div | Mod -> opnd_type
  57. (* Check if the given operator can be applied to the given type *)
  58. let check_type_op allowed_types desc node =
  59. let got = typeof node in
  60. if not (List.mem got allowed_types) then (
  61. let msg = sprintf
  62. "%s cannot be applied to type %s, only to %s"
  63. desc (type2str got) (types2str allowed_types)
  64. in
  65. raise (node_error (node, msg))
  66. ); ()
  67. let check_dims_match dims dec_type errnode =
  68. match (List.length dims, array_depth dec_type) with
  69. | (got, expected) when got != expected ->
  70. let msg = sprintf
  71. "dimension mismatch: expected %d indices, got %d" expected got
  72. in
  73. raise (node_error (errnode, msg))
  74. | _ -> ()
  75. let rec typecheck node =
  76. let check_trav ctype node =
  77. let node = typecheck node in
  78. check_type ctype node;
  79. node
  80. in
  81. match node with
  82. | FunUse ((FunDec (ret_type, name, params, _) as dec), args, ann)
  83. | FunUse ((FunDef (_, ret_type, name, params, _, _) as dec), args, ann) ->
  84. begin
  85. match (List.length args, List.length params) with
  86. | (nargs, nparams) when nargs != nparams ->
  87. let msg = sprintf
  88. "function \"%s\" expects %d arguments, got %d"
  89. name nparams nargs
  90. in
  91. raise (node_error (node, msg))
  92. | _ ->
  93. let args = List.map typecheck args in
  94. let check_arg_type arg param =
  95. check_type (typeof param) arg;
  96. in
  97. List.iter2 check_arg_type args params;
  98. FunUse (dec, args, Type ret_type :: ann)
  99. end
  100. (* Operators match operand types and get a new type based on the operator *)
  101. | Monop (op, opnd, ann) ->
  102. let opnd = typecheck opnd in
  103. let desc = sprintf "unary operator \"%s\"" (op2str op) in
  104. check_type_op (op_types op) desc opnd;
  105. Monop (op, opnd, Type (op_result_type (typeof opnd) op) :: ann)
  106. | Binop (op, left, right, ann) ->
  107. let left = typecheck left in
  108. let right = typecheck right in
  109. let desc = sprintf "binary operator \"%s\"" (op2str op) in
  110. check_type_op (op_types op) desc left;
  111. check_type (typeof left) right;
  112. (* Check for division by zero *)
  113. begin
  114. match (op, right) with
  115. | (Div, Const (IntVal 0, _)) -> node_warning right "division by zero"
  116. | _ -> ()
  117. end;
  118. Binop (op, left, right, Type (op_result_type (typeof left) op) :: ann)
  119. (* Conditions must be bool, and right-hand type must match left-hand type *)
  120. | Cond (cond, texpr, fexpr, ann) ->
  121. let cond = check_trav Bool cond in
  122. let texpr = typecheck texpr in
  123. let fexpr = check_trav (typeof texpr) fexpr in
  124. Cond (cond, texpr, fexpr, Type (typeof texpr) :: ann)
  125. (* Only basic types can be typecasted *)
  126. | TypeCast (ctype, value, ann) ->
  127. let value = typecheck value in
  128. check_type_op [Bool; Int; Float] "typecast" value;
  129. TypeCast (ctype, value, Type ctype :: ann)
  130. (* Array allocation dimensions must have type int *)
  131. | Allocate (dec, dims, ann) ->
  132. Allocate (dec, List.map (check_trav Int) dims, ann)
  133. (* Array dimensions are always integers *)
  134. | Dim (name, ann) ->
  135. Dim (name, Type Int :: ann)
  136. (* Functions and parameters must be traversed to give types to Dim nodes *)
  137. (*
  138. | FunDec (ret_type, name, params, ann) ->
  139. FunDec (ret_type, name, List.map typecheck params, ann)
  140. | Param (ArrayDims (ctype, dims), name, ann) ->
  141. Param (ArrayDims (ctype, List.map typecheck dims), name, ann)
  142. *)
  143. (* Void functions may have no return statement, other functions must have a
  144. * return statement of valid type *)
  145. | FunDef (export, ret_type, name, params, body, ann) ->
  146. let params = List.map typecheck params in
  147. let body = typecheck body in
  148. let rec find_return = function
  149. | [] -> None
  150. | [Return (value, _) as ret] -> Some (ret, typeof value)
  151. | hd :: tl -> find_return tl
  152. in
  153. begin
  154. match (ret_type, find_return (block_body body)) with
  155. | (Void, Some (ret, _)) ->
  156. raise (node_error (ret, "void function should not have a return value"))
  157. | ((Bool | Int | Float), None) ->
  158. let msg = sprintf
  159. "expected return value of type %s for function \"%s\""
  160. (type2str ret_type) name
  161. in
  162. raise (node_error (node, msg))
  163. | ((Bool | Int | Float), Some (ret, t)) when t != ret_type ->
  164. let msg = sprintf
  165. "function \"%s\" has return type %s, got %s"
  166. name (type2str ret_type) (type2str t)
  167. in
  168. raise (node_error (ret, msg))
  169. | _ -> FunDef (export, ret_type, name, params, body, ann)
  170. end
  171. (* Conditions in must have type bool *)
  172. | If (cond, body, ann) ->
  173. If (check_trav Bool cond, typecheck body, ann)
  174. | IfElse (cond, tbody, fbody, ann) ->
  175. IfElse (check_trav Bool cond, typecheck tbody, typecheck fbody, ann)
  176. | While (cond, body, ann) ->
  177. While (check_trav Bool cond, typecheck body, ann)
  178. | DoWhile (cond, body, ann) ->
  179. DoWhile (check_trav Bool cond, typecheck body, ann)
  180. (* Constants *)
  181. | Const (BoolVal value, ann) ->
  182. Const (BoolVal value, Type Bool :: ann)
  183. | Const (IntVal value, ann) ->
  184. (* Do a bound check on integers (use Int32 because default ints in ocaml
  185. * are 31- or 63-bit *)
  186. let cmpval = Nativeint.of_int value in
  187. let min = Nativeint.of_int32 Int32.min_int in
  188. let max = Nativeint.of_int32 Int32.max_int in
  189. if cmpval < min || cmpval > max then (
  190. raise (node_error (node, "integer value out of range (signed 32-bit)"))
  191. );
  192. Const (IntVal value, Type Int :: ann)
  193. | Const (FloatVal value, ann) ->
  194. Const (FloatVal value, Type Float :: ann)
  195. (* Extern arrays variables are transformed to imported functions, so the
  196. * pointer cannot be passed *)
  197. | VarUse (GlobalDec (ArrayDims _, _, _), None, _) ->
  198. raise (node_error (node, "imported array pointers may only be \
  199. dereferenced, not used directly"))
  200. (* Variables inherit the type of their declaration *)
  201. | VarUse (dec, None, ann) ->
  202. VarUse (dec, None, Type (typeof dec) :: ann)
  203. | VarUse (dec, Some dims, ann) ->
  204. let dims = List.map typecheck dims in
  205. List.iter (check_type Int) dims;
  206. check_dims_match dims (typeof dec) node;
  207. VarUse (dec, Some dims, Type (basetypeof dec) :: ann)
  208. (* Array pointers cannot be re-assigned, because array dimension reduction
  209. * makes assumptions about dimensions of an array *)
  210. | VarLet (dec, None, _, _) when is_array dec ->
  211. raise (node_error (node, "cannot assign value to array pointer after \
  212. initialisation"))
  213. (* Assigned values must match variable declaration *)
  214. | VarLet (dec, None, value, ann) ->
  215. VarLet (dec, None, check_trav (typeof dec) value, ann)
  216. | VarLet (dec, Some dims, value, ann) ->
  217. (* Number of assigned indices must match array definition *)
  218. check_dims_match dims (typeof dec) node;
  219. (* Array indices must be ints *)
  220. let dims = List.map typecheck dims in
  221. List.iter (check_type Int) dims;
  222. (* Assigned value must match array base type *)
  223. let value = typecheck value in
  224. check_type (basetypeof dec) value;
  225. VarLet (dec, Some dims, value, ann)
  226. (* ArrayConst initialisations are transformed during desugaring, so any
  227. * occurrences that are left are illegal *)
  228. | ArrayConst _ ->
  229. raise (node_error (node, "array constants can only be used in array \
  230. initialisation"))
  231. | _ -> traverse_unit typecheck node
  232. let phase = function
  233. | Ast node -> Ast (typecheck node)
  234. | _ -> raise InvalidInput