summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/_tags2
-rw-r--r--src/ast.ml229
-rw-r--r--src/ast_util.ml322
-rw-r--r--src/ast_util.mli95
-rw-r--r--src/constraint.ml306
-rw-r--r--src/constraint.mli30
-rw-r--r--src/initial_check.ml5
-rw-r--r--src/lexer.mll3
-rw-r--r--src/parse_ast.ml2
-rw-r--r--src/parser.mly14
-rw-r--r--src/pretty_print.mli4
-rw-r--r--src/pretty_print_common.ml2
-rw-r--r--src/pretty_print_sail.ml22
-rw-r--r--src/process_file.ml14
-rw-r--r--src/process_file.mli5
-rw-r--r--src/sail.ml12
-rw-r--r--src/type_check.ml5
-rw-r--r--src/type_check_new.ml2109
-rw-r--r--src/type_check_new.mli186
19 files changed, 3241 insertions, 126 deletions
diff --git a/src/_tags b/src/_tags
index 8ec7acb9..83ca12e0 100644
--- a/src/_tags
+++ b/src/_tags
@@ -1,7 +1,7 @@
true: -traverse, debug
<**/*.ml>: bin_annot, annot
<lem_interp> or <test>: include
-<sail.{byte,native}>: use_pprint, use_nums
+<sail.{byte,native}>: use_pprint, use_nums, use_unix
<pprint> or <pprint/src>: include
# see http://caml.inria.fr/mantis/view.php?id=4943
diff --git a/src/ast.ml b/src/ast.ml
index c923ae3f..f7745eeb 100644
--- a/src/ast.ml
+++ b/src/ast.ml
@@ -42,7 +42,6 @@
(* generated by Ott 0.25 from: l2.ott *)
-
type text = string
type l = Parse_ast.l
@@ -53,7 +52,7 @@ type 'a annot = l * 'a
type x = text (* identifier *)
type ix = text (* infix identifier *)
-type
+type
base_kind_aux = (* base kind *)
BK_type (* kind of types *)
| BK_nat (* kind of natural number size expressions *)
@@ -61,43 +60,43 @@ base_kind_aux = (* base kind *)
| BK_effect (* kind of effect sets *)
-type
-base_kind =
+type
+base_kind =
BK_aux of base_kind_aux * l
-type
+type
id_aux = (* Identifier *)
Id of x
| DeIid of x (* remove infix status *)
-type
+type
kid_aux = (* variables with kind, ticked to differntiate from program variables *)
Var of x
-type
+type
kind_aux = (* kinds *)
K_kind of (base_kind) list
-type
-id =
+type
+id =
Id_aux of id_aux * l
-type
-kid =
+type
+kid =
Kid_aux of kid_aux * l
-type
-kind =
+type
+kind =
K_aux of kind_aux * l
-type
+type
nexp_aux = (* expression of kind Nat, for vector sizes and origins *)
Nexp_id of id (* identifier, bound by def Nat x = nexp *)
| Nexp_var of kid (* variable *)
@@ -108,11 +107,11 @@ nexp_aux = (* expression of kind Nat, for vector sizes and origins *)
| Nexp_exp of nexp (* exponential *)
| Nexp_neg of nexp (* For internal use *)
-and nexp =
+and nexp =
Nexp_aux of nexp_aux * l
-type
+type
base_effect_aux = (* effect *)
BE_rreg (* read register *)
| BE_wreg (* write register *)
@@ -133,76 +132,78 @@ base_effect_aux = (* effect *)
| BE_lret (* Local return happened; not user-writable *)
-type
-base_effect =
+type
+base_effect =
BE_aux of base_effect_aux * l
-type
+type
order_aux = (* vector order specifications, of kind Order *)
Ord_var of kid (* variable *)
| Ord_inc (* increasing (little-endian) *)
| Ord_dec (* decreasing (big-endian) *)
-type
+type
effect_aux = (* effect set, of kind Effects *)
Effect_var of kid
| Effect_set of (base_effect) list (* effect set *)
-type
-order =
+type
+order =
Ord_aux of order_aux * l
-type
-effect =
+type
+effect =
Effect_aux of effect_aux * l
-type
+type
kinded_id_aux = (* optionally kind-annotated identifier *)
KOpt_none of kid (* identifier *)
| KOpt_kind of kind * kid (* kind-annotated variable *)
-type
+type
n_constraint_aux = (* constraint over kind $_$ *)
NC_fixed of nexp * nexp
| NC_bounded_ge of nexp * nexp
| NC_bounded_le of nexp * nexp
| NC_nat_set_bounded of kid * (int) list
+ (* We need this for the new typechecker when as nexp is substituted for an id *)
+ | NC_set_subst of nexp * int list
-type
-kinded_id =
+type
+kinded_id =
KOpt_aux of kinded_id_aux * l
-type
-n_constraint =
+type
+n_constraint =
NC_aux of n_constraint_aux * l
-type
+type
quant_item_aux = (* Either a kinded identifier or a nexp constraint for a typquant *)
QI_id of kinded_id (* An optionally kinded identifier *)
| QI_const of n_constraint (* A constraint for this type *)
-type
-quant_item =
+type
+quant_item =
QI_aux of quant_item_aux * l
-type
+type
typquant_aux = (* type quantifiers and constraints *)
TypQ_tq of (quant_item) list
| TypQ_no_forall (* sugar, omitting quantifier and constraints *)
-type
+type
lit_aux = (* Literal constant *)
L_unit (* $() : _$ *)
| L_zero (* $_ : _$ *)
@@ -216,12 +217,11 @@ lit_aux = (* Literal constant *)
| L_string of string (* string constant *)
-type
-typquant =
+type
+typquant =
TypQ_aux of typquant_aux * l
-
-type
+type
typ_aux = (* Type expressions, of kind $_$ *)
Typ_wild (* Unspecified type *)
| Typ_id of id (* Defined type *)
@@ -230,7 +230,7 @@ typ_aux = (* Type expressions, of kind $_$ *)
| Typ_tup of (typ) list (* Tuple type *)
| Typ_app of id * (typ_arg) list (* type constructor application *)
-and typ =
+and typ =
Typ_aux of typ_aux * l
and typ_arg_aux = (* Type constructor arguments of all kinds *)
@@ -239,21 +239,21 @@ and typ_arg_aux = (* Type constructor arguments of all kinds *)
| Typ_arg_order of order
| Typ_arg_effect of effect
-and typ_arg =
+and typ_arg =
Typ_arg_aux of typ_arg_aux * l
-type
-lit =
+type
+lit =
L_aux of lit_aux * l
-type
+type
typschm_aux = (* type scheme *)
TypSchm_ts of typquant * typ
-type
+type
'a pat_aux = (* Pattern *)
P_lit of lit (* literal constant pattern *)
| P_wild (* wildcard *)
@@ -268,27 +268,27 @@ type
| P_tup of ('a pat) list (* tuple pattern *)
| P_list of ('a pat) list (* list pattern *)
-and 'a pat =
+and 'a pat =
P_aux of 'a pat_aux * 'a annot
and 'a fpat_aux = (* Field pattern *)
FP_Fpat of id * 'a pat
-and 'a fpat =
+and 'a fpat =
FP_aux of 'a fpat_aux * 'a annot
-type
-typschm =
+type
+typschm =
TypSchm_aux of typschm_aux * l
-type
-'a reg_id_aux =
+type
+'a reg_id_aux =
RI_id of id
-type
+type
'a exp_aux = (* Expression *)
E_block of ('a exp) list (* block *)
| E_nondet of ('a exp) list (* nondeterminisitic block, expressions evaluate in an unspecified order, or concurrently *)
@@ -329,7 +329,7 @@ type
| E_internal_plet of 'a pat * 'a exp * 'a exp (* This is an internal node, used to distinguised some introduced lets during processing from original ones *)
| E_internal_return of 'a exp (* For internal use to embed into monad definition *)
-and 'a exp =
+and 'a exp =
E_aux of 'a exp_aux * 'a annot
and 'a lexp_aux = (* lvalue expression *)
@@ -341,82 +341,82 @@ and 'a lexp_aux = (* lvalue expression *)
| LEXP_vector_range of 'a lexp * 'a exp * 'a exp (* subvector *)
| LEXP_field of 'a lexp * id (* struct field *)
-and 'a lexp =
+and 'a lexp =
LEXP_aux of 'a lexp_aux * 'a annot
and 'a fexp_aux = (* Field-expression *)
FE_Fexp of id * 'a exp
-and 'a fexp =
+and 'a fexp =
FE_aux of 'a fexp_aux * 'a annot
and 'a fexps_aux = (* Field-expression list *)
FES_Fexps of ('a fexp) list * bool
-and 'a fexps =
+and 'a fexps =
FES_aux of 'a fexps_aux * 'a annot
and 'a opt_default_aux = (* Optional default value for indexed vectors, to define a defualt value for any unspecified positions in a sparse map *)
Def_val_empty
| Def_val_dec of 'a exp
-and 'a opt_default =
+and 'a opt_default =
Def_val_aux of 'a opt_default_aux * 'a annot
and 'a pexp_aux = (* Pattern match *)
Pat_exp of 'a pat * 'a exp
-and 'a pexp =
+and 'a pexp =
Pat_aux of 'a pexp_aux * 'a annot
and 'a letbind_aux = (* Let binding *)
LB_val_explicit of typschm * 'a pat * 'a exp (* value binding, explicit type ('a pat must be total) *)
| LB_val_implicit of 'a pat * 'a exp (* value binding, implicit type ('a pat must be total) *)
-and 'a letbind =
+and 'a letbind =
LB_aux of 'a letbind_aux * 'a annot
-type
-'a reg_id =
+type
+'a reg_id =
RI_aux of 'a reg_id_aux * 'a annot
-type
+type
type_union_aux = (* Type union constructors *)
Tu_id of id
| Tu_ty_id of typ * id
-type
+type
name_scm_opt_aux = (* Optional variable-naming-scheme specification for variables of defined type *)
Name_sect_none
| Name_sect_some of string
-type
+type
effect_opt_aux = (* Optional effect annotation for functions *)
Effect_opt_pure (* sugar for empty effect set *)
| Effect_opt_effect of effect
-type
+type
'a funcl_aux = (* Function clause *)
FCL_Funcl of id * 'a pat * 'a exp
-type
+type
rec_opt_aux = (* Optional recursive annotation for functions *)
Rec_nonrec (* non-recursive *)
| Rec_rec (* recursive *)
-type
+type
tannot_opt_aux = (* Optional type annotation for functions *)
Typ_annot_opt_some of typquant * typ
-type
+type
'a alias_spec_aux = (* Register alias expression forms. Other than where noted, each id must refer to an unaliased register of type vector *)
AL_subreg of 'a reg_id * id
| AL_bit of 'a reg_id * 'a exp
@@ -424,60 +424,60 @@ type
| AL_concat of 'a reg_id * 'a reg_id
-type
-type_union =
+type
+type_union =
Tu_aux of type_union_aux * l
-type
+type
index_range_aux = (* index specification, for bitfields in register types *)
BF_single of int (* single index *)
| BF_range of int * int (* index range *)
| BF_concat of index_range * index_range (* concatenation of index ranges *)
-and index_range =
+and index_range =
BF_aux of index_range_aux * l
-type
-name_scm_opt =
+type
+name_scm_opt =
Name_sect_aux of name_scm_opt_aux * l
-type
-effect_opt =
+type
+effect_opt =
Effect_opt_aux of effect_opt_aux * l
-type
-'a funcl =
+type
+'a funcl =
FCL_aux of 'a funcl_aux * 'a annot
-type
-rec_opt =
+type
+rec_opt =
Rec_aux of rec_opt_aux * l
-type
-tannot_opt =
+type
+tannot_opt =
Typ_annot_opt_aux of tannot_opt_aux * l
-type
-'a alias_spec =
+type
+'a alias_spec =
AL_aux of 'a alias_spec_aux * 'a annot
-type
-'a default_spec_aux = (* Default kinding or typing assumption *)
+type
+default_spec_aux = (* Default kinding or typing assumption *)
DT_kind of base_kind * kid
| DT_order of order
| DT_typ of typschm * id
-type
-'a type_def_aux = (* Type definition body *)
+type
+type_def_aux = (* Type definition body *)
TD_abbrev of id * name_scm_opt * typschm (* type abbreviation *)
| TD_record of id * name_scm_opt * typquant * ((typ * id)) list * bool (* struct type definition *)
| TD_variant of id * name_scm_opt * typquant * (type_union) list * bool (* union type definition *)
@@ -485,14 +485,15 @@ type
| TD_register of id * nexp * nexp * ((index_range * id)) list (* register mutable bitfield type definition *)
-type
-'a val_spec_aux = (* Value type specification *)
+type
+val_spec_aux = (* Value type specification *)
VS_val_spec of typschm * id
| VS_extern_no_rename of typschm * id
| VS_extern_spec of typschm * id * string (* Specify the type and id of a function from Lem, where the string must provide an explicit path to the required function but will not be checked *)
+ | VS_cast_spec of typschm * id
-type
+type
'a kind_def_aux = (* Definition body for elements of kind; many are shorthands for type\_defs *)
KD_nabbrev of kind * id * name_scm_opt * nexp (* nexp abbreviation *)
| KD_abbrev of kind * id * name_scm_opt * typschm (* type abbreviation *)
@@ -502,7 +503,7 @@ type
| KD_register of kind * id * nexp * nexp * ((index_range * id)) list (* register mutable bitfield type definition *)
-type
+type
'a scattered_def_aux = (* Function and type union definitions that can be spread across
a file. Each one must end in $_$ *)
SD_scattered_function of rec_opt * tannot_opt * effect_opt * id (* scattered function definition header *)
@@ -512,54 +513,54 @@ type
| SD_scattered_end of id (* scattered definition end *)
-type
+type
'a fundef_aux = (* Function definition *)
FD_function of rec_opt * tannot_opt * effect_opt * ('a funcl) list
-type
+type
'a dec_spec_aux = (* Register declarations *)
DEC_reg of typ * id
| DEC_alias of id * 'a alias_spec
| DEC_typ_alias of typ * id * 'a alias_spec
-type
-'a default_spec =
- DT_aux of 'a default_spec_aux * l
+type
+'a default_spec =
+ DT_aux of default_spec_aux * l
-type
-'a type_def =
- TD_aux of 'a type_def_aux * 'a annot
+type
+'a type_def =
+ TD_aux of type_def_aux * 'a annot
-type
-'a val_spec =
- VS_aux of 'a val_spec_aux * 'a annot
+type
+'a val_spec =
+ VS_aux of val_spec_aux * 'a annot
-type
-'a kind_def =
+type
+'a kind_def =
KD_aux of 'a kind_def_aux * 'a annot
-type
-'a scattered_def =
+type
+'a scattered_def =
SD_aux of 'a scattered_def_aux * 'a annot
-type
-'a fundef =
+type
+'a fundef =
FD_aux of 'a fundef_aux * 'a annot
-type
-'a dec_spec =
+type
+'a dec_spec =
DEC_aux of 'a dec_spec_aux * 'a annot
-type
+type
'a dec_comm = (* Top-level generated comments *)
DC_comm of string (* generated unstructured comment *)
| DC_comm_struct of 'a def (* generated structured comment *)
@@ -570,13 +571,13 @@ and 'a def = (* Top-level definition *)
| DEF_fundef of 'a fundef (* function definition *)
| DEF_val of 'a letbind (* value definition *)
| DEF_spec of 'a val_spec (* top-level type constraint *)
+ | DEF_overload of id * id list (* operator overload specification *)
| DEF_default of 'a default_spec (* default kind and type assumptions *)
| DEF_scattered of 'a scattered_def (* scattered function and type definition *)
| DEF_reg_dec of 'a dec_spec (* register declaration *)
| DEF_comm of 'a dec_comm (* generated comments *)
-
-type
+type
'a defs = (* Definition sequence *)
Defs of ('a def) list
diff --git a/src/ast_util.ml b/src/ast_util.ml
new file mode 100644
index 00000000..59e19e0b
--- /dev/null
+++ b/src/ast_util.ml
@@ -0,0 +1,322 @@
+(**************************************************************************)
+(* Sail *)
+(* *)
+(* Copyright (c) 2013-2017 *)
+(* Kathyrn Gray *)
+(* Shaked Flur *)
+(* Stephen Kell *)
+(* Gabriel Kerneis *)
+(* Robert Norton-Wright *)
+(* Christopher Pulte *)
+(* Peter Sewell *)
+(* Alasdair Armstrong *)
+(* *)
+(* All rights reserved. *)
+(* *)
+(* This software was developed by the University of Cambridge Computer *)
+(* Laboratory as part of the Rigorous Engineering of Mainstream Systems *)
+(* (REMS) project, funded by EPSRC grant EP/K008528/1. *)
+(* *)
+(* Redistribution and use in source and binary forms, with or without *)
+(* modification, are permitted provided that the following conditions *)
+(* are met: *)
+(* 1. Redistributions of source code must retain the above copyright *)
+(* notice, this list of conditions and the following disclaimer. *)
+(* 2. Redistributions in binary form must reproduce the above copyright *)
+(* notice, this list of conditions and the following disclaimer in *)
+(* the documentation and/or other materials provided with the *)
+(* distribution. *)
+(* *)
+(* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' *)
+(* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED *)
+(* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *)
+(* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR *)
+(* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, *)
+(* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT *)
+(* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF *)
+(* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND *)
+(* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, *)
+(* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT *)
+(* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF *)
+(* SUCH DAMAGE. *)
+(**************************************************************************)
+
+open Ast
+open Util
+open Big_int
+
+let rec map_exp_annot f (E_aux (exp, annot)) = E_aux (map_exp_annot_aux f exp, f annot)
+and map_exp_annot_aux f = function
+ | E_block xs -> E_block (List.map (map_exp_annot f) xs)
+ | E_nondet xs -> E_nondet (List.map (map_exp_annot f) xs)
+ | E_id id -> E_id id
+ | E_lit lit -> E_lit lit
+ | E_cast (typ, exp) -> E_cast (typ, map_exp_annot f exp)
+ | E_app (id, xs) -> E_app (id, List.map (map_exp_annot f) xs)
+ | E_app_infix (x, op, y) -> E_app_infix (map_exp_annot f x, op, map_exp_annot f y)
+ | E_tuple xs -> E_tuple (List.map (map_exp_annot f) xs)
+ | E_if (cond, t, e) -> E_if (map_exp_annot f cond, map_exp_annot f t, map_exp_annot f e)
+ | E_for (v, e1, e2, e3, o, e4) -> E_for (v, map_exp_annot f e1, map_exp_annot f e2, map_exp_annot f e3, o, map_exp_annot f e4)
+ | E_vector exps -> E_vector (List.map (map_exp_annot f) exps)
+ | E_vector_indexed (iexps, opt_default) ->
+ E_vector_indexed (List.map (fun (i, exp) -> (i, map_exp_annot f exp)) iexps, map_opt_default_annot f opt_default)
+ | E_vector_access (exp1, exp2) -> E_vector_access (map_exp_annot f exp1, map_exp_annot f exp2)
+ | E_vector_subrange (exp1, exp2, exp3) -> E_vector_subrange (map_exp_annot f exp1, map_exp_annot f exp2, map_exp_annot f exp3)
+ | E_vector_update_subrange (exp1, exp2, exp3, exp4) ->
+ E_vector_update_subrange (map_exp_annot f exp1, map_exp_annot f exp2, map_exp_annot f exp3, map_exp_annot f exp4)
+ | E_vector_append (exp1, exp2) -> E_vector_append (map_exp_annot f exp1, map_exp_annot f exp2)
+ | E_list xs -> E_list (List.map (map_exp_annot f) xs)
+ | E_cons (exp1, exp2) -> E_cons (map_exp_annot f exp1, map_exp_annot f exp2)
+ | E_record fexps -> E_record (map_fexps_annot f fexps)
+ | E_record_update (exp, fexps) -> E_record_update (map_exp_annot f exp, map_fexps_annot f fexps)
+ | E_field (exp, id) -> E_field (map_exp_annot f exp, id)
+ | E_case (exp, cases) -> E_case (map_exp_annot f exp, List.map (map_pexp_annot f) cases)
+ | E_let (letbind, exp) -> E_let (map_letbind_annot f letbind, map_exp_annot f exp)
+ | E_assign (lexp, exp) -> E_assign (map_lexp_annot f lexp, map_exp_annot f exp)
+ | E_sizeof nexp -> E_sizeof nexp
+ | E_exit exp -> E_exit (map_exp_annot f exp)
+ | E_return exp -> E_return (map_exp_annot f exp)
+ | E_assert (test, msg) -> E_assert (map_exp_annot f test, map_exp_annot f msg)
+ | E_internal_cast (annot, exp) -> E_internal_cast (f annot, map_exp_annot f exp)
+ | E_internal_exp annot -> E_internal_exp (f annot)
+ | E_sizeof_internal annot -> E_sizeof_internal (f annot)
+ | E_internal_exp_user (annot1, annot2) -> E_internal_exp_user (f annot1, f annot2)
+ | E_comment str -> E_comment str
+ | E_comment_struc exp -> E_comment_struc (map_exp_annot f exp)
+ | E_internal_let (lexp, exp1, exp2) -> E_internal_let (map_lexp_annot f lexp, map_exp_annot f exp1, map_exp_annot f exp2)
+ | E_internal_plet (pat, exp1, exp2) -> E_internal_plet (map_pat_annot f pat, map_exp_annot f exp1, map_exp_annot f exp2)
+ | E_internal_return exp -> E_internal_return (map_exp_annot f exp)
+and map_opt_default_annot f (Def_val_aux (df, annot)) = Def_val_aux (map_opt_default_annot_aux f df, f annot)
+and map_opt_default_annot_aux f = function
+ | Def_val_empty -> Def_val_empty
+ | Def_val_dec exp -> Def_val_dec (map_exp_annot f exp)
+and map_fexps_annot f (FES_aux (FES_Fexps (fexps, b), annot)) = FES_aux (FES_Fexps (List.map (map_fexp_annot f) fexps, b), f annot)
+and map_fexp_annot f (FE_aux (FE_Fexp (id, exp), annot)) = FE_aux (FE_Fexp (id, map_exp_annot f exp), f annot)
+and map_pexp_annot f (Pat_aux (Pat_exp (pat, exp), annot)) = Pat_aux (Pat_exp (map_pat_annot f pat, map_exp_annot f exp), f annot)
+and map_pat_annot f (P_aux (pat, annot)) = P_aux (map_pat_annot_aux f pat, f annot)
+and map_pat_annot_aux f = function
+ | P_lit lit -> P_lit lit
+ | P_wild -> P_wild
+ | P_as (pat, id) -> P_as (map_pat_annot f pat, id)
+ | P_typ (typ, pat) -> P_typ (typ, map_pat_annot f pat)
+ | P_id id -> P_id id
+ | P_app (id, pats) -> P_app (id, List.map (map_pat_annot f) pats)
+ | P_record (fpats, b) -> P_record (List.map (map_fpat_annot f) fpats, b)
+ | P_tup pats -> P_tup (List.map (map_pat_annot f) pats)
+ | P_list pats -> P_list (List.map (map_pat_annot f) pats)
+ | P_vector_concat pats -> P_vector_concat (List.map (map_pat_annot f) pats)
+ | P_vector_indexed ipats -> P_vector_indexed (List.map (fun (i, pat) -> (i, map_pat_annot f pat)) ipats)
+ | P_vector pats -> P_vector (List.map (map_pat_annot f) pats)
+and map_fpat_annot f (FP_aux (FP_Fpat (id, pat), annot)) = FP_aux (FP_Fpat (id, map_pat_annot f pat), f annot)
+and map_letbind_annot f (LB_aux (lb, annot)) = LB_aux (map_letbind_annot_aux f lb, f annot)
+and map_letbind_annot_aux f = function
+ | LB_val_explicit (typschm, pat, exp) -> LB_val_explicit (typschm, map_pat_annot f pat, map_exp_annot f exp)
+ | LB_val_implicit (pat, exp) -> LB_val_implicit (map_pat_annot f pat, map_exp_annot f exp)
+and map_lexp_annot f (LEXP_aux (lexp, annot)) = LEXP_aux (map_lexp_annot_aux f lexp, f annot)
+and map_lexp_annot_aux f = function
+ | LEXP_id id -> LEXP_id id
+ | LEXP_memory (id, exps) -> LEXP_memory (id, List.map (map_exp_annot f) exps)
+ | LEXP_cast (typ, id) -> LEXP_cast (typ, id)
+ | LEXP_tup lexps -> LEXP_tup (List.map (map_lexp_annot f) lexps)
+ | LEXP_vector (lexp, exp) -> LEXP_vector (map_lexp_annot f lexp, map_exp_annot f exp)
+ | LEXP_vector_range (lexp, exp1, exp2) -> LEXP_vector_range (map_lexp_annot f lexp, map_exp_annot f exp1, map_exp_annot f exp2)
+ | LEXP_field (lexp, id) -> LEXP_field (map_lexp_annot f lexp, id)
+
+let id_loc = function
+ | Id_aux (_, l) -> l
+
+let kid_loc = function
+ | Kid_aux (_, l) -> l
+
+let string_of_id = function
+ | Id_aux (Id v, _) -> v
+ | Id_aux (DeIid v, _) -> "(deinfix " ^ v ^ ")"
+
+let string_of_kid = function
+ | Kid_aux (Var v, _) -> v
+
+let string_of_base_effect_aux = function
+ | BE_rreg -> "rreg"
+ | BE_wreg -> "wreg"
+ | BE_rmem -> "rmem"
+ | BE_rmemt -> "rmemt"
+ | BE_wmem -> "wmem"
+ | BE_eamem -> "eamem"
+ | BE_exmem -> "exmem"
+ | BE_wmv -> "wmv"
+ | BE_wmvt -> "wmvt"
+ | BE_barr -> "barr"
+ | BE_depend -> "depend"
+ | BE_undef -> "undef"
+ | BE_unspec -> "unspec"
+ | BE_nondet -> "nondet"
+ | BE_escape -> "escape"
+ | BE_lset -> "lset"
+ | BE_lret -> "lret"
+
+let string_of_base_kind_aux = function
+ | BK_type -> "Type"
+ | BK_nat -> "Nat"
+ | BK_order -> "Order"
+ | BK_effect -> "Effect"
+
+let string_of_base_kind (BK_aux (bk, _)) = string_of_base_kind_aux bk
+
+let string_of_kind (K_aux (K_kind bks, _)) = string_of_list " -> " string_of_base_kind bks
+
+let string_of_base_effect = function
+ | BE_aux (beff, _) -> string_of_base_effect_aux beff
+
+let string_of_effect = function
+ | Effect_aux (Effect_var kid, _) ->
+ string_of_kid kid
+ | Effect_aux (Effect_set [], _) -> "pure"
+ | Effect_aux (Effect_set beffs, _) ->
+ "{" ^ string_of_list ", " string_of_base_effect beffs ^ "}"
+
+let string_of_order = function
+ | Ord_aux (Ord_var kid, _) -> string_of_kid kid
+ | Ord_aux (Ord_inc, _) -> "inc"
+ | Ord_aux (Ord_dec, _) -> "dec"
+
+let rec string_of_nexp = function
+ | Nexp_aux (nexp, _) -> string_of_nexp_aux nexp
+and string_of_nexp_aux = function
+ | Nexp_id id -> string_of_id id
+ | Nexp_var kid -> string_of_kid kid
+ | Nexp_constant c -> string_of_int c
+ | Nexp_times (n1, n2) -> "(" ^ string_of_nexp n1 ^ " * " ^ string_of_nexp n2 ^ ")"
+ | Nexp_sum (n1, n2) -> "(" ^ string_of_nexp n1 ^ " + " ^ string_of_nexp n2 ^ ")"
+ | Nexp_minus (n1, n2) -> "(" ^ string_of_nexp n1 ^ " - " ^ string_of_nexp n2 ^ ")"
+ | Nexp_exp n -> "2 ^ " ^ string_of_nexp n
+ | Nexp_neg n -> "- " ^ string_of_nexp n
+
+let rec string_of_typ = function
+ | Typ_aux (typ, l) -> string_of_typ_aux typ
+and string_of_typ_aux = function
+ | Typ_wild -> "_"
+ | Typ_id id -> string_of_id id
+ | Typ_var kid -> string_of_kid kid
+ | Typ_tup typs -> "(" ^ string_of_list ", " string_of_typ typs ^ ")"
+ | Typ_app (id, args) -> string_of_id id ^ "<" ^ string_of_list ", " string_of_typ_arg args ^ ">"
+ | Typ_fn (typ_arg, typ_ret, eff) ->
+ string_of_typ typ_arg ^ " -> " ^ string_of_typ typ_ret ^ " effect " ^ string_of_effect eff
+and string_of_typ_arg = function
+ | Typ_arg_aux (typ_arg, l) -> string_of_typ_arg_aux typ_arg
+and string_of_typ_arg_aux = function
+ | Typ_arg_nexp n -> string_of_nexp n
+ | Typ_arg_typ typ -> string_of_typ typ
+ | Typ_arg_order o -> string_of_order o
+ | Typ_arg_effect eff -> string_of_effect eff
+
+let string_of_n_constraint = function
+ | NC_aux (NC_fixed (n1, n2), _) -> string_of_nexp n1 ^ " = " ^ string_of_nexp n2
+ | NC_aux (NC_bounded_ge (n1, n2), _) -> string_of_nexp n1 ^ " >= " ^ string_of_nexp n2
+ | NC_aux (NC_bounded_le (n1, n2), _) -> string_of_nexp n1 ^ " <= " ^ string_of_nexp n2
+ | NC_aux (NC_set_subst (nexp, ns), _) ->
+ string_of_nexp nexp ^ " IN {" ^ string_of_list ", " string_of_int ns ^ "}"
+ | NC_aux (NC_nat_set_bounded (kid, ns), _) ->
+ string_of_kid kid ^ " IN {" ^ string_of_list ", " string_of_int ns ^ "}"
+
+let string_of_quant_item_aux = function
+ | QI_id (KOpt_aux (KOpt_none kid, _)) -> string_of_kid kid
+ | QI_id (KOpt_aux (KOpt_kind (k, kid), _)) -> string_of_kind k ^ " " ^ string_of_kid kid
+ | QI_const constr -> string_of_n_constraint constr
+
+let string_of_quant_item = function
+ | QI_aux (qi, _) -> string_of_quant_item_aux qi
+
+let string_of_typquant_aux = function
+ | TypQ_tq quants -> "forall " ^ string_of_list ", " string_of_quant_item quants
+ | TypQ_no_forall -> ""
+
+let string_of_typquant = function
+ | TypQ_aux (quant, _) -> string_of_typquant_aux quant
+
+let string_of_typschm (TypSchm_aux (TypSchm_ts (quant, typ), _)) =
+ string_of_typquant quant ^ ". " ^ string_of_typ typ
+let string_of_lit (L_aux (lit, _)) =
+ match lit with
+ | L_unit -> "()"
+ | L_zero -> "bitzero"
+ | L_one -> "bitone"
+ | L_true -> "true"
+ | L_false -> "false"
+ | L_num n -> string_of_int n
+ | L_hex n -> "0x" ^ n
+ | L_bin n -> "0b" ^ n
+ | L_undef -> "undefined"
+ | L_string str -> "\"" ^ str ^ "\""
+
+let rec string_of_exp (E_aux (exp, _)) =
+ match exp with
+ | E_block exps -> "{ " ^ string_of_list "; " string_of_exp exps ^ " }"
+ | E_id v -> string_of_id v
+ | E_sizeof nexp -> "sizeof " ^ string_of_nexp nexp
+ | E_lit lit -> string_of_lit lit
+ | E_return exp -> "return " ^ string_of_exp exp
+ | E_app (f, args) -> string_of_id f ^ "(" ^ string_of_list ", " string_of_exp args ^ ")"
+ | E_app_infix (x, op, y) -> "(" ^ string_of_exp x ^ " " ^ string_of_id op ^ " " ^ string_of_exp y ^ ")"
+ | E_tuple exps -> "(" ^ string_of_list ", " string_of_exp exps ^ ")"
+ | E_case (exp, cases) ->
+ "switch " ^ string_of_exp exp ^ " { case " ^ string_of_list " case " string_of_pexp cases ^ "}"
+ | E_let (letbind, exp) -> "let " ^ string_of_letbind letbind ^ " in " ^ string_of_exp exp
+ | E_assign (lexp, bind) -> string_of_lexp lexp ^ " := " ^ string_of_exp bind
+ | E_cast (typ, exp) -> "(" ^ string_of_typ typ ^ ") " ^ string_of_exp exp
+ | E_vector vec -> "[" ^ string_of_list ", " string_of_exp vec ^ "]"
+ | E_vector_access (v, n) -> string_of_exp v ^ "[" ^ string_of_exp n ^ "]"
+ | E_vector_subrange (v, n1, n2) -> string_of_exp v ^ "[" ^ string_of_exp n1 ^ " .. " ^ string_of_exp n2 ^ "]"
+ | E_vector_append (v1, v2) -> string_of_exp v1 ^ " : " ^ string_of_exp v2
+ | E_if (cond, then_branch, else_branch) ->
+ "if " ^ string_of_exp cond ^ " then " ^ string_of_exp then_branch ^ " else " ^ string_of_exp else_branch
+ | E_field (exp, id) -> string_of_exp exp ^ "." ^ string_of_id id
+ | _ -> "INTERNAL"
+and string_of_pexp (Pat_aux (Pat_exp (pat, exp), _)) = string_of_pat pat ^ " -> " ^ string_of_exp exp
+and string_of_pat (P_aux (pat, l)) =
+ match pat with
+ | P_lit lit -> string_of_lit lit
+ | P_wild -> "_"
+ | P_id v -> string_of_id v
+ | P_typ (typ, pat) -> "(" ^ string_of_typ typ ^ ") " ^ string_of_pat pat
+ | P_tup pats -> "(" ^ string_of_list ", " string_of_pat pats ^ ")"
+ | _ -> "PAT"
+and string_of_lexp (LEXP_aux (lexp, _)) =
+ match lexp with
+ | LEXP_id v -> string_of_id v
+ | LEXP_cast (typ, v) -> "(" ^ string_of_typ typ ^ ") " ^ string_of_id v
+ | LEXP_tup lexps -> "(" ^ string_of_list ", " string_of_lexp lexps ^ ")"
+ | LEXP_vector (lexp, exp) -> string_of_lexp lexp ^ "[" ^ string_of_exp exp ^ "]"
+ | LEXP_field (lexp, id) -> string_of_lexp lexp ^ "." ^ string_of_id id
+ | _ -> "LEXP"
+and string_of_letbind (LB_aux (lb, l)) =
+ match lb with
+ | LB_val_implicit (pat, exp) -> string_of_pat pat ^ " = " ^ string_of_exp exp
+ | LB_val_explicit (typschm, pat, exp) ->
+ string_of_typschm typschm ^ " " ^ string_of_pat pat ^ " = " ^ string_of_exp exp
+
+let rec string_of_index_range (BF_aux (ir, _)) =
+ match ir with
+ | BF_single n -> string_of_int n
+ | BF_range (n, m) -> string_of_int n ^ " .. " ^ string_of_int m
+ | BF_concat (ir1, ir2) -> "(" ^ string_of_index_range ir1 ^ ") : (" ^ string_of_index_range ir2 ^ ")"
+
+module Kid = struct
+ type t = kid
+ let compare kid1 kid2 = String.compare (string_of_kid kid1) (string_of_kid kid2)
+end
+
+module BE = struct
+ type t = base_effect
+ let compare be1 be2 = String.compare (string_of_base_effect be1) (string_of_base_effect be2)
+end
+
+module Id = struct
+ type t = id
+ let compare id1 id2 =
+ match (id1, id2) with
+ | Id_aux (Id x, _), Id_aux (Id y, _) -> String.compare x y
+ | Id_aux (DeIid x, _), Id_aux (DeIid y, _) -> String.compare x y
+ | Id_aux (Id _, _), Id_aux (DeIid _, _) -> -1
+ | Id_aux (DeIid _, _), Id_aux (Id _, _) -> 1
+end
+
diff --git a/src/ast_util.mli b/src/ast_util.mli
new file mode 100644
index 00000000..edce060d
--- /dev/null
+++ b/src/ast_util.mli
@@ -0,0 +1,95 @@
+(**************************************************************************)
+(* Sail *)
+(* *)
+(* Copyright (c) 2013-2017 *)
+(* Kathyrn Gray *)
+(* Shaked Flur *)
+(* Stephen Kell *)
+(* Gabriel Kerneis *)
+(* Robert Norton-Wright *)
+(* Christopher Pulte *)
+(* Peter Sewell *)
+(* Alasdair Armstrong *)
+(* *)
+(* All rights reserved. *)
+(* *)
+(* This software was developed by the University of Cambridge Computer *)
+(* Laboratory as part of the Rigorous Engineering of Mainstream Systems *)
+(* (REMS) project, funded by EPSRC grant EP/K008528/1. *)
+(* *)
+(* Redistribution and use in source and binary forms, with or without *)
+(* modification, are permitted provided that the following conditions *)
+(* are met: *)
+(* 1. Redistributions of source code must retain the above copyright *)
+(* notice, this list of conditions and the following disclaimer. *)
+(* 2. Redistributions in binary form must reproduce the above copyright *)
+(* notice, this list of conditions and the following disclaimer in *)
+(* the documentation and/or other materials provided with the *)
+(* distribution. *)
+(* *)
+(* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' *)
+(* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED *)
+(* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *)
+(* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR *)
+(* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, *)
+(* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT *)
+(* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF *)
+(* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND *)
+(* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, *)
+(* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT *)
+(* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF *)
+(* SUCH DAMAGE. *)
+(**************************************************************************)
+
+open Ast
+
+(* Functions to map over the annotations in sub-expressions *)
+val map_exp_annot : ('a annot -> 'b annot) -> 'a exp -> 'b exp
+val map_pat_annot : ('a annot -> 'b annot) -> 'a pat -> 'b pat
+val map_lexp_annot : ('a annot -> 'b annot) -> 'a lexp -> 'b lexp
+
+(* Extract locations from identifiers *)
+val id_loc : id -> Parse_ast.l
+val kid_loc : kid -> Parse_ast.l
+
+(* For debugging and error messages only: Not guaranteed to produce
+ parseable SAIL, or even print all language constructs! *)
+(* TODO: replace with existing pretty-printer *)
+val string_of_id : id -> string
+val string_of_kid : kid -> string
+val string_of_base_effect_aux : base_effect_aux -> string
+val string_of_base_kind_aux : base_kind_aux -> string
+val string_of_base_kind : base_kind -> string
+val string_of_kind : kind -> string
+val string_of_base_effect : base_effect -> string
+val string_of_effect : effect -> string
+val string_of_order : order -> string
+val string_of_nexp : nexp -> string
+val string_of_typ : typ -> string
+val string_of_typ_arg : typ_arg -> string
+val string_of_n_constraint : n_constraint -> string
+val string_of_quant_item : quant_item -> string
+val string_of_typquant : typquant -> string
+val string_of_typschm : typschm -> string
+val string_of_lit : lit -> string
+val string_of_exp : 'a exp -> string
+val string_of_pexp : 'a pexp -> string
+val string_of_lexp : 'a lexp -> string
+val string_of_pat : 'a pat -> string
+val string_of_letbind : 'a letbind -> string
+val string_of_index_range : index_range -> string
+
+module Id : sig
+ type t = id
+ val compare : id -> id -> int
+end
+
+module Kid : sig
+ type t = kid
+ val compare : kid -> kid -> int
+end
+
+module BE : sig
+ type t = base_effect
+ val compare : base_effect -> base_effect -> int
+end
diff --git a/src/constraint.ml b/src/constraint.ml
new file mode 100644
index 00000000..f71193b2
--- /dev/null
+++ b/src/constraint.ml
@@ -0,0 +1,306 @@
+open Big_int
+open Util
+
+(* ===== Integer Constraints ===== *)
+
+type nexp_op = Plus | Minus | Mult
+
+type nexp =
+ | NFun of (nexp_op * nexp * nexp)
+ | N2n of nexp
+ | NConstant of big_int
+ | NVar of int
+
+let big_int_op : nexp_op -> big_int -> big_int -> big_int = function
+ | Plus -> add_big_int
+ | Minus -> sub_big_int
+ | Mult -> mult_big_int
+
+let rec arith constr =
+ let constr' = match constr with
+ | NFun (op, x, y) -> NFun (op, arith x, arith y)
+ | N2n c -> arith c
+ | c -> c
+ in
+ match constr' with
+ | NFun (op, NConstant x, NConstant y) -> NConstant (big_int_op op x y)
+ | N2n (NConstant x) -> NConstant (power_int_positive_big_int 2 x)
+ | c -> c
+
+(* ===== Boolean Constraints ===== *)
+
+type constraint_bool_op = And | Or
+
+type constraint_compare_op = Gt | Lt | GtEq | LtEq | Eq | NEq
+
+let negate_comparison = function
+ | Gt -> LtEq
+ | Lt -> GtEq
+ | GtEq -> Lt
+ | LtEq -> Gt
+ | Eq -> NEq
+ | NEq -> Eq
+
+type 'a constraint_bool =
+ | BFun of (constraint_bool_op * 'a constraint_bool * 'a constraint_bool)
+ | Not of 'a constraint_bool
+ | CFun of (constraint_compare_op * 'a * 'a)
+ | Branch of ('a constraint_bool list)
+ | Boolean of bool
+
+let rec pairs (xs : 'a list) (ys : 'a list) : ('a * 'b) list =
+ match xs with
+ | [] -> []
+ | (x :: xs) -> List.map (fun y -> (x, y)) ys @ pairs xs ys
+
+let rec unbranch : 'a constraint_bool -> 'a constraint_bool list = function
+ | Branch xs -> List.map unbranch xs |> List.concat
+ | Not x -> unbranch x |> List.map (fun y -> Not y)
+ | BFun (op, x, y) ->
+ let xs, ys = unbranch x, unbranch y in
+ List.map (fun (z, w) -> BFun (op, z, w)) (pairs xs ys)
+ | c -> [c]
+
+(* Apply De Morgan's laws to push all negations to just before integer
+ constraints *)
+let rec de_morgan : 'a constraint_bool -> 'a constraint_bool = function
+ | Not (Not x) -> de_morgan x
+ | Not (BFun (And, x, y)) -> BFun (Or, de_morgan (Not x), de_morgan (Not y))
+ | Not (BFun (Or, x, y)) -> BFun (And, de_morgan (Not x), de_morgan (Not y))
+ | Not (Boolean b) -> Boolean (not b)
+ | BFun (op, x, y) -> BFun (op, de_morgan x, de_morgan y)
+ | c -> c
+
+(* Once De Morgan's laws are applied we can push all the Nots into
+ comparison constraints *)
+let rec remove_nots : 'a constraint_bool -> 'a constraint_bool = function
+ | BFun (op, x, y) -> BFun (op, remove_nots x, remove_nots y)
+ | Not (CFun (c, x, y)) -> CFun (negate_comparison c, x, y)
+ | c -> c
+
+(* Apply distributivity so all Or clauses are within And clauses *)
+let rec distrib_step : 'a constraint_bool -> ('a constraint_bool * int) = function
+ | BFun (Or, x, BFun (And, y, z)) ->
+ let (xy, n) = distrib_step (BFun (Or, x, y)) in
+ let (xz, m) = distrib_step (BFun (Or, x, z)) in
+ BFun (And, xy, xz), n + m + 1
+ | BFun (Or, BFun (And, x, y), z) ->
+ let (xz, n) = distrib_step (BFun (Or, x, z)) in
+ let (yz, m) = distrib_step (BFun (Or, y, z)) in
+ BFun (And, xz, yz), n + m + 1
+ | BFun (op, x, y) ->
+ let (x', n) = distrib_step x in
+ let (y', m) = distrib_step y in
+ BFun (op, x', y'), n + m
+ | c -> (c, 0)
+
+let rec distrib (c : 'a constraint_bool) : 'a constraint_bool =
+ let (c', n) = distrib_step c in
+ if n = 0 then c else distrib c'
+
+(* Once these steps have been applied, the constraint language is a
+ list of And clauses, each a list of Or clauses, with either
+ explicit booleans (LBool) or integer comparisons LFun. The flatten
+ function coverts from a constraint_bool to this representation. *)
+type 'a constraint_leaf =
+ | LFun of (constraint_compare_op * 'a * 'a)
+ | LBoolean of bool
+
+let rec flatten_or : 'a constraint_bool -> 'a constraint_leaf list = function
+ | BFun (Or, x, y) -> flatten_or x @ flatten_or y
+ | CFun comparison -> [LFun comparison]
+ | Boolean b -> [LBoolean b]
+ | _ -> assert false
+
+let rec flatten : 'a constraint_bool -> 'a constraint_leaf list list = function
+ | BFun (And, x, y) -> flatten x @ flatten y
+ | Boolean b -> [[LBoolean b]]
+ | c -> [flatten_or c]
+
+let normalize (constr : 'a constraint_bool) : 'a constraint_leaf list list =
+ constr
+ |> de_morgan
+ |> remove_nots
+ |> distrib
+ |> flatten
+
+(* Get a set of variables from a constraint *)
+module IntSet = Set.Make(
+ struct
+ let compare = Pervasives.compare
+ type t = int
+ end)
+
+let rec int_expr_vars : nexp -> IntSet.t = function
+ | NConstant _ -> IntSet.empty
+ | NVar v -> IntSet.singleton v
+ | NFun (_, x, y) -> IntSet.union (int_expr_vars x) (int_expr_vars y)
+ | N2n x -> int_expr_vars x
+
+let leaf_expr_vars : nexp constraint_leaf -> IntSet .t = function
+ | LBoolean _ -> IntSet.empty
+ | LFun (_, x, y) -> IntSet.union (int_expr_vars x) (int_expr_vars y)
+
+let constraint_vars constr : IntSet.t =
+ constr
+ |> List.map (List.map leaf_expr_vars)
+ |> List.map (List.fold_left IntSet.union IntSet.empty)
+ |> List.fold_left IntSet.union IntSet.empty
+
+(* SMTLIB v2.0 format is based on S-expressions so we have a
+ lightweight representation of those here. *)
+type sexpr = List of (sexpr list) | Atom of string
+
+let sfun (fn : string) (xs : sexpr list) : sexpr = List (Atom fn :: xs)
+
+let rec pp_sexpr : sexpr -> string = function
+ | List xs -> "(" ^ string_of_list " " pp_sexpr xs ^ ")"
+ | Atom x -> x
+
+let var_decs constr =
+ constraint_vars constr
+ |> IntSet.elements
+ |> List.map (fun var -> sfun "declare-const" [Atom ("v" ^ string_of_int var); Atom "Int"])
+ |> string_of_list "\n" pp_sexpr
+
+let cop_sexpr op x y =
+ match op with
+ | Gt -> sfun ">" [x; y]
+ | Lt -> sfun "<" [x; y]
+ | GtEq -> sfun ">=" [x; y]
+ | LtEq -> sfun "<=" [x; y]
+ | Eq -> sfun "=" [x; y]
+ | NEq -> sfun "not" [sfun "=" [x; y]]
+
+let iop_sexpr op x y =
+ match op with
+ | Plus -> sfun "+" [x; y]
+ | Minus -> sfun "-" [x; y]
+ | Mult -> sfun "*" [x; y]
+
+let rec sexpr_of_nexp = function
+ | NFun (op, x, y) -> iop_sexpr op (sexpr_of_nexp x) (sexpr_of_nexp y)
+ | N2n x -> sfun "^" [Atom "2"; sexpr_of_nexp x]
+ | NConstant c -> Atom (string_of_big_int c) (* CHECK: do we do negative constants right? *)
+ | NVar var -> Atom ("v" ^ string_of_int var)
+
+let rec sexpr_of_cbool = function
+ | BFun (And, x, y) -> sfun "and" [sexpr_of_cbool x; sexpr_of_cbool y]
+ | BFun (Or, x, y) -> sfun "or" [sexpr_of_cbool x; sexpr_of_cbool y]
+ | Not x -> sfun "not" [sexpr_of_cbool x]
+ | CFun (op, x, y) -> cop_sexpr op (sexpr_of_nexp x) (sexpr_of_nexp y)
+ | Branch xs -> sfun "BRANCH" (List.map sexpr_of_cbool xs)
+ | Boolean true -> Atom "true"
+ | Boolean false -> Atom "false"
+
+let sexpr_of_constraint_leaf = function
+ | LFun (op, x, y) -> cop_sexpr op (sexpr_of_nexp x) (sexpr_of_nexp y)
+ | LBoolean true -> Atom "true"
+ | LBoolean false -> Atom "false"
+
+let sexpr_of_constraint constr : sexpr =
+ constr
+ |> List.map (List.map sexpr_of_constraint_leaf)
+ |> List.map (fun xs -> match xs with [x] -> x | _ -> (sfun "or" xs))
+ |> sfun "and"
+
+let smtlib_of_constraint constr : string =
+ "(push)\n"
+ ^ var_decs constr ^ "\n"
+ ^ pp_sexpr (sfun "define-fun" [Atom "constraint"; List []; Atom "Bool"; sexpr_of_constraint constr])
+ ^ "\n(assert constraint)\n(check-sat)\n(pop)"
+
+type t = nexp constraint_bool
+
+type smt_result = Unknown of t list | Unsat of t
+
+let rec call_z3 constraints : smt_result =
+ let problems = unbranch constraints in
+ let z3_file =
+ problems
+ |> List.map normalize
+ |> List.map smtlib_of_constraint
+ |> string_of_list "\n" (fun x -> x)
+ in
+
+ (* prerr_endline (Printf.sprintf "SMTLIB2 constraints are: \n%s%!" z3_file); *)
+
+ let rec input_lines chan = function
+ | 0 -> []
+ | n ->
+ begin
+ let l = input_line chan in
+ let ls = input_lines chan (n - 1) in
+ l :: ls
+ end
+ in
+
+ begin
+ let (input_file, tmp_chan) = Filename.open_temp_file "constraint_" ".sat" in
+ output_string tmp_chan z3_file;
+ close_out tmp_chan;
+ let z3_chan = Unix.open_process_in ("z3 -t:1000 -T:10 " ^ input_file) in
+ let z3_output = List.combine problems (input_lines z3_chan (List.length problems)) in
+ let _ = Unix.close_process_in z3_chan in
+ Sys.remove input_file;
+ try
+ let (problem, _) = List.find (fun (_, result) -> result = "unsat") z3_output in
+ Unsat problem
+ with
+ | Not_found ->
+ z3_output
+ |> List.filter (fun (_, result) -> result = "unknown")
+ |> List.map fst
+ |> (fun unsolved -> Unknown unsolved)
+ end
+
+let string_of constr =
+ constr
+ |> unbranch
+ |> List.map normalize
+ |> List.map (fun c -> smtlib_of_constraint c)
+ |> string_of_list "\n" (fun x -> x)
+
+(* ===== Abstract API for building constraints ===== *)
+
+(* These functions are exported from constraint.mli, and ensure that
+ the internal representation of constraints remains opaque. *)
+
+let implies (x : t) (y : t) : t =
+ BFun (Or, Not x, y)
+
+let conj (x : t) (y : t) : t =
+ BFun (And, x, y)
+
+let disj (x : t) (y : t) : t =
+ BFun (Or, x, y)
+
+let negate (x : t) : t = Not x
+
+let branch (xs : t list) : t = Branch xs
+
+let literal (b : bool) : t = Boolean b
+
+let lt x y : t = CFun (Lt, x, y)
+
+let lteq x y : t = CFun (LtEq, x, y)
+
+let gt x y : t = CFun (Gt, x, y)
+
+let gteq x y : t = CFun (GtEq, x, y)
+
+let eq x y : t = CFun (Eq, x, y)
+
+let neq x y : t = CFun (NEq, x, y)
+
+let pow2 x : nexp = N2n x
+
+let add x y : nexp = NFun (Plus, x, y)
+
+let sub x y : nexp = NFun (Minus, x, y)
+
+let mult x y : nexp = NFun (Mult, x, y)
+
+let constant (x : big_int) : nexp = NConstant x
+
+let variable (v : int) : nexp = NVar v
diff --git a/src/constraint.mli b/src/constraint.mli
new file mode 100644
index 00000000..3fb3d2f6
--- /dev/null
+++ b/src/constraint.mli
@@ -0,0 +1,30 @@
+type nexp
+type t
+
+type smt_result = Unknown of t list | Unsat of t
+
+val call_z3 : t -> smt_result
+
+val string_of : t -> string
+
+val implies : t -> t -> t
+val conj : t -> t -> t
+val disj : t -> t -> t
+val negate : t -> t
+val branch : t list -> t
+val literal : bool -> t
+
+val lt : nexp -> nexp -> t
+val lteq : nexp -> nexp -> t
+val gt : nexp -> nexp -> t
+val gteq : nexp -> nexp -> t
+val eq : nexp -> nexp -> t
+val neq : nexp -> nexp -> t
+
+val pow2 : nexp -> nexp
+val add : nexp -> nexp -> nexp
+val sub : nexp -> nexp -> nexp
+val mult : nexp -> nexp -> nexp
+
+val constant : Big_int.big_int -> nexp
+val variable : int -> nexp
diff --git a/src/initial_check.ml b/src/initial_check.ml
index 0e37b418..b6d4f863 100644
--- a/src/initial_check.ml
+++ b/src/initial_check.ml
@@ -615,6 +615,9 @@ let to_ast_spec (names,k_env,default_order) (val_:Parse_ast.val_spec) : (tannot
| Parse_ast.VS_extern_spec(ts,id,s) ->
let typsch,_,_ = to_ast_typschm k_env default_order ts in
VS_aux(VS_extern_spec(typsch,to_ast_id id,s),(l,NoTyp)),(names,k_env,default_order)
+ | Parse_ast.VS_cast_spec(ts,id) ->
+ let typsch,_,_ = to_ast_typschm k_env default_order ts in
+ VS_aux(VS_cast_spec(typsch,to_ast_id id),(l,NoTyp)),(names,k_env,default_order)
| Parse_ast.VS_extern_no_rename(ts,id) ->
let typsch,_,_ = to_ast_typschm k_env default_order ts in
VS_aux(VS_extern_no_rename(typsch,to_ast_id id),(l,NoTyp)),(names,k_env,default_order))
@@ -844,6 +847,8 @@ let to_ast_dec (names,k_env,def_ord) (Parse_ast.DEC_aux(regdec,l)) =
let to_ast_def (names, k_env, def_ord) partial_defs def : def_progress envs_out * (id * partial_def) list =
let envs = (names,k_env,def_ord) in
match def with
+ | Parse_ast.DEF_overload(id,ids) ->
+ ((Finished(DEF_overload(to_ast_id id, List.map to_ast_id ids))),envs),partial_defs
| Parse_ast.DEF_kind(k_def) ->
let kd,envs = to_ast_kdef envs k_def in
((Finished(DEF_kind(kd))),envs),partial_defs
diff --git a/src/lexer.mll b/src/lexer.mll
index 7f11355f..99965e20 100644
--- a/src/lexer.mll
+++ b/src/lexer.mll
@@ -76,10 +76,12 @@ let kw_table =
("else", (fun _ -> Else));
("exit", (fun _ -> Exit));
("extern", (fun _ -> Extern));
+ ("cast", (fun _ -> Cast));
("false", (fun _ -> False));
("forall", (fun _ -> Forall));
("foreach", (fun _ -> Foreach));
("function", (fun x -> Function_));
+ ("overload", (fun _ -> Overload));
("if", (fun x -> If_));
("in", (fun x -> In));
("inc", (fun _ -> Inc));
@@ -87,6 +89,7 @@ let kw_table =
("let", (fun x -> Let_));
("member", (fun x -> Member));
("Nat", (fun x -> Nat));
+ ("Num", (fun x -> NatNum));
("Order", (fun x -> Order));
("pure", (fun x -> Pure));
("rec", (fun x -> Rec));
diff --git a/src/parse_ast.ml b/src/parse_ast.ml
index e069462a..8b52b2ab 100644
--- a/src/parse_ast.ml
+++ b/src/parse_ast.ml
@@ -418,6 +418,7 @@ val_spec_aux = (* Value type specification *)
VS_val_spec of typschm * id
| VS_extern_no_rename of typschm * id
| VS_extern_spec of typschm * id * string
+ | VS_cast_spec of typschm * id
type
@@ -487,6 +488,7 @@ def = (* Top-level definition *)
| DEF_type of type_def (* type definition *)
| DEF_fundef of fundef (* function definition *)
| DEF_val of letbind (* value definition *)
+ | DEF_overload of id * id list (* operator overload specifications *)
| DEF_spec of val_spec (* top-level type constraint *)
| DEF_default of default_typing_spec (* default kind and type assumptions *)
| DEF_scattered of scattered_def (* scattered definition *)
diff --git a/src/parser.mly b/src/parser.mly
index bd68cfdc..0240e368 100644
--- a/src/parser.mly
+++ b/src/parser.mly
@@ -129,7 +129,7 @@ let make_vector_sugar order_set is_inc typ typ1 =
/*Terminals with no content*/
%token And Alias As Assert Bitzero Bitone Bits By Case Clause Const Dec Def Default Deinfix Effect EFFECT End
-%token Enumerate Else Exit Extern False Forall Foreach Function_ If_ In IN Inc Let_ Member Nat Order
+%token Enumerate Else Exit Extern False Forall Foreach Overload Function_ If_ In IN Inc Let_ Member Nat NatNum Order Cast
%token Pure Rec Register Return Scattered Sizeof Struct Switch Then True TwoStarStar Type TYPE Typedef
%token Undefined Union With Val
%token Barr Depend Rreg Wreg Rmem Rmemt Wmem Wmv Wmvt Eamem Exmem Undef Unspec Nondet Escape
@@ -226,7 +226,7 @@ id:
| Lparen Deinfix AmpAmp Rparen
{ idl (DeIid($3)) }
| Lparen Deinfix Bar Rparen
- { idl (DeIid("||")) }
+ { idl (DeIid("|")) }
| Lparen Deinfix BarBar Rparen
{ idl (DeIid("||")) }
| Lparen Deinfix CarrotCarrot Rparen
@@ -283,6 +283,8 @@ atomic_kind:
{ bkloc BK_type }
| Nat
{ bkloc BK_nat }
+ | NatNum
+ { bkloc BK_nat }
| Order
{ bkloc BK_order }
| EFFECT
@@ -413,6 +415,8 @@ exp_typ:
{ $1 }
| TwoStarStar atomic_typ
{ tloc (ATyp_exp($2)) }
+ | TwoStarStar atomic_typ Minus Num
+ { tloc (ATyp_minus (tloc (ATyp_exp $2), tloc (ATyp_constant $4))) }
| TwoStarStar Num
{ tloc (ATyp_exp (tloc (ATyp_constant $2))) }
@@ -1023,6 +1027,10 @@ val_spec:
{ vloc (VS_val_spec(mk_typschm $2 $3 2 3,$4)) }
| Val typ id
{ vloc (VS_val_spec(mk_typschm (mk_typqn ()) $2 2 2,$3)) }
+ | Val Cast typquant typ id
+ { vloc (VS_cast_spec (mk_typschm $3 $4 3 4,$5)) }
+ | Val Cast typ id
+ { vloc (VS_cast_spec (mk_typschm (mk_typqn ()) $3 3 3, $4)) }
| Val Extern typquant typ id
{ vloc (VS_extern_no_rename (mk_typschm $3 $4 3 4,$5)) }
| Val Extern typ id
@@ -1266,6 +1274,8 @@ def:
{ dloc (DEF_spec($1)) }
| default_typ
{ dloc (DEF_default($1)) }
+ | Overload id Lsquare enum_body Rsquare
+ { dloc (DEF_overload($2,$4)) }
| Register typ id
{ dloc (DEF_reg_dec(DEC_aux(DEC_reg($2,$3),loc ()))) }
| Register Alias id Eq exp
diff --git a/src/pretty_print.mli b/src/pretty_print.mli
index 9a002454..67475616 100644
--- a/src/pretty_print.mli
+++ b/src/pretty_print.mli
@@ -44,9 +44,9 @@ open Ast
open Type_internal
(* Prints the defs following source syntax *)
-val pp_defs : out_channel -> tannot defs -> unit
+val pp_defs : out_channel -> 'a defs -> unit
val pp_exp : Buffer.t -> exp -> unit
-val pat_to_string : tannot pat -> string
+val pat_to_string : 'a pat -> string
(* Prints on formatter the defs as Lem Ast nodes *)
val pp_lem_defs : Format.formatter -> tannot defs -> unit
diff --git a/src/pretty_print_common.ml b/src/pretty_print_common.ml
index 3699e1ac..5c3339c6 100644
--- a/src/pretty_print_common.ml
+++ b/src/pretty_print_common.ml
@@ -125,6 +125,7 @@ let doc_typ, doc_atomic_typ, doc_nexp =
(*TODO Need to un bid-endian-ify this here, since both can transform to the shorthand, especially with <: and :> *)
(* Special case simple vectors to improve legibility
* XXX we assume big-endian here, as usual *)
+(*
| Typ_app(Id_aux (Id "vector", _), [
Typ_arg_aux(Typ_arg_nexp (Nexp_aux(Nexp_constant n, _)), _);
Typ_arg_aux(Typ_arg_nexp (Nexp_aux(Nexp_constant m, _)), _);
@@ -159,6 +160,7 @@ let doc_typ, doc_atomic_typ, doc_nexp =
Typ_arg_aux (Typ_arg_order (Ord_aux (Ord_dec, _)), _);
Typ_arg_aux (Typ_arg_typ (Typ_aux (Typ_id id, _)), _)]) ->
(doc_id id) ^^ (brackets (if n' = m_nexp then nexp m_nexp else doc_op colon (nexp m_nexp) (nexp n_n)))
+ *)
| Typ_app(Id_aux (Id "range", _), [
Typ_arg_aux(Typ_arg_nexp (Nexp_aux(Nexp_constant n, _)), _);
Typ_arg_aux(Typ_arg_nexp m, _);]) ->
diff --git a/src/pretty_print_sail.ml b/src/pretty_print_sail.ml
index 22cb707b..9c33c841 100644
--- a/src/pretty_print_sail.ml
+++ b/src/pretty_print_sail.ml
@@ -40,7 +40,7 @@
(* SUCH DAMAGE. *)
(**************************************************************************)
-open Type_internal
+(* open Type_internal *)
open Ast
open PPrint
open Pretty_print_common
@@ -238,6 +238,7 @@ let doc_exp, doc_let =
| E_id id -> doc_id id
| E_lit lit -> doc_lit lit
| E_cast(typ,e) -> prefix 2 1 (parens (doc_typ typ)) (group (atomic_exp e))
+ (*
| E_internal_cast((_,NoTyp),e) -> atomic_exp e
| E_internal_cast((_,Base((_,t),_,_,_,_,bindings)), (E_aux(_,(_,eannot)) as e)) ->
(match t.t,eannot with
@@ -247,6 +248,7 @@ let doc_exp, doc_let =
| Tapp("vector",[TA_nexp n1;_;_;_]),Base((_,{t=Tapp("vector",[TA_nexp n2;_;_;_])}),_,_,_,_,_)
when nexp_eq n1 n2 -> atomic_exp e
| _ -> prefix 2 1 (parens (doc_typ (t_to_typ t))) (group (atomic_exp e)))
+ *)
| E_tuple exps ->
parens (separate_map comma exp exps)
| E_record(FES_aux(FES_Fexps(fexps,_),_)) ->
@@ -325,6 +327,7 @@ let doc_exp, doc_let =
(* doc_op (doc_id op) (exp l) (exp r) *)
| E_comment s -> comment (string s)
| E_comment_struc e -> comment (exp e)
+ (*
| E_internal_exp((l, Base((_,t),_,_,_,_,bindings))) -> (*TODO use bindings, and other params*)
(match t.t with
| Tapp("register",[TA_typ {t=Tapp("vector",[TA_nexp _;TA_nexp r;_;_])}])
@@ -342,7 +345,7 @@ let doc_exp, doc_let =
| _ -> raise (Reporting_basic.err_unreachable l ("Internal exp given non-vector, non-implicit " ^ t_to_string t)))
| E_internal_exp_user _ -> raise (Reporting_basic.err_unreachable Unknown ("internal_exp_user not rewritten away"))
| E_internal_cast ((_, Overload (_, _,_ )), _) | E_internal_exp _ -> assert false
-
+ *)
and let_exp (LB_aux(lb,_)) = match lb with
| LB_val_explicit(ts,pat,e) ->
(match ts with
@@ -391,16 +394,18 @@ let doc_exp, doc_let =
let doc_default (DT_aux(df,_)) = match df with
| DT_kind(bk,v) -> separate space [string "default"; doc_bkind bk; doc_var v]
| DT_typ(ts,id) -> separate space [string "default"; doc_typscm ts; doc_id id]
- | DT_order(ord) -> separate space [string "default"; string "order"; doc_ord ord]
+ | DT_order(ord) -> separate space [string "default"; string "Order"; doc_ord ord]
let doc_spec (VS_aux(v,_)) = match v with
| VS_val_spec(ts,id) ->
- separate space [string "val"; doc_typscm ts; doc_id id]
+ separate space [string "val"; doc_typscm ts; doc_id id]
+ | VS_cast_spec (ts, id) ->
+ separate space [string "val"; string "cast"; doc_typscm ts; doc_id id]
| VS_extern_no_rename(ts,id) ->
- separate space [string "val"; string "extern"; doc_typscm ts; doc_id id]
+ separate space [string "val"; string "extern"; doc_typscm ts; doc_id id]
| VS_extern_spec(ts,id,s) ->
- separate space [string "val"; string "extern"; doc_typscm ts;
- doc_op equals (doc_id id) (dquotes (string s))]
+ separate space [string "val"; string "extern"; doc_typscm ts;
+ doc_op equals (doc_id id) (dquotes (string s))]
let doc_namescm (Name_sect_aux(ns,_)) = match ns with
| Name_sect_none -> empty
@@ -549,6 +554,9 @@ let rec doc_def def = group (match def with
| DEF_val lbind -> doc_let lbind
| DEF_reg_dec dec -> doc_dec dec
| DEF_scattered sdef -> doc_scattered sdef
+ | DEF_overload (id, ids) ->
+ let ids_doc = group (separate_map (semi ^^ break 1) doc_id ids) in
+ string "overload" ^^ space ^^ doc_id id ^^ space ^^ brackets ids_doc
| DEF_comm (DC_comm s) -> comment (string s)
| DEF_comm (DC_comm_struct d) -> comment (doc_def d)
) ^^ hardline
diff --git a/src/process_file.ml b/src/process_file.ml
index 273979cf..42d1402b 100644
--- a/src/process_file.ml
+++ b/src/process_file.ml
@@ -88,6 +88,11 @@ let convert_ast (defs : Parse_ast.defs) : (Type_internal.tannot Ast.defs * kind
let initi_check_ast (defs : Type_internal.tannot Ast.defs) : (Type_internal.tannot Ast.defs * kind Envmap.t * Ast.order)=
Initial_check_full_ast.to_checked_ast Nameset.empty Type_internal.initial_kind_env (Ast.Ord_aux(Ast.Ord_inc,Parse_ast.Unknown)) defs
+let opt_new_typecheck = ref false
+let opt_just_check = ref false
+let opt_ddump_tc_ast = ref false
+let opt_dno_cast = ref false
+
let check_ast (defs : Type_internal.tannot Ast.defs) (k : kind Envmap.t) (o:Ast.order) : Type_internal.tannot Ast.defs * Type_check.envs =
let d_env = { Type_internal.k_env = k; Type_internal.abbrevs = Type_internal.initial_abbrev_env;
Type_internal.nabbrevs = Envmap.empty;
@@ -97,6 +102,15 @@ let check_ast (defs : Type_internal.tannot Ast.defs) (k : kind Envmap.t) (o:Ast.
{Type_internal.order = (match o with | (Ast.Ord_aux(Ast.Ord_inc,_)) -> Type_internal.Oinc
| (Ast.Ord_aux(Ast.Ord_dec,_)) -> Type_internal.Odec
| _ -> Type_internal.Oinc)};} in
+ if !opt_new_typecheck
+ then
+ let ienv = if !opt_dno_cast then Type_check_new.Env.no_casts Type_check_new.initial_env else Type_check_new.initial_env in
+ let ast, _ = Type_check_new.check ienv defs in
+ if !opt_ddump_tc_ast then Pretty_print.pp_defs stdout ast else ()
+ else ();
+ if !opt_just_check
+ then exit 0
+ else ();
Type_check.check (Type_check.Env (d_env, Type_internal.initial_typ_env,Type_internal.nob,Envmap.empty)) defs
let rewrite_ast (defs: Type_internal.tannot Ast.defs) = Rewriter.rewrite_defs defs
diff --git a/src/process_file.mli b/src/process_file.mli
index 2c18b830..aa8d3265 100644
--- a/src/process_file.mli
+++ b/src/process_file.mli
@@ -48,6 +48,11 @@ val rewrite_ast: Type_internal.tannot Ast.defs -> Type_internal.tannot Ast.defs
val rewrite_ast_lem : Type_internal.tannot Ast.defs -> Type_internal.tannot Ast.defs
val rewrite_ast_ocaml : Type_internal.tannot Ast.defs -> Type_internal.tannot Ast.defs
+val opt_new_typecheck : bool ref
+val opt_just_check : bool ref
+val opt_ddump_tc_ast : bool ref
+val opt_dno_cast : bool ref
+
type out_type =
| Lem_ast_out
| Lem_out of string option (* If present, the string is a file to open in the lem backend*)
diff --git a/src/sail.ml b/src/sail.ml
index a9fd4f3f..41c42fe4 100644
--- a/src/sail.ml
+++ b/src/sail.ml
@@ -94,6 +94,18 @@ let options = Arg.align ([
| [fname;line;var] -> opt_mono_split := ((fname,int_of_string line),var)::!opt_mono_split
| _ -> raise (Arg.Bad (s ^ " not of form <filename>:<line>:<variable>"))),
"<filename>:<line>:<variable> to case split for monomorphisation");
+ ( "-new_typecheck",
+ Arg.Set opt_new_typecheck,
+ " (experimental) use new typechecker with Z3 constraint solving");
+ ( "-just_check",
+ Arg.Tuple [Arg.Set opt_new_typecheck; Arg.Set opt_just_check],
+ " (experimental) terminate immediately after typechecking, implies -new_typecheck");
+ ( "-ddump_tc_ast",
+ Arg.Set opt_ddump_tc_ast,
+ " (debug) dump the typechecked ast to stdout");
+ ( "-dno_cast",
+ Arg.Set opt_dno_cast,
+ " (debug) typecheck without any implicit casting");
( "-v",
Arg.Set opt_print_version,
" print version");
diff --git a/src/type_check.ml b/src/type_check.ml
index e285477a..73520825 100644
--- a/src/type_check.ml
+++ b/src/type_check.ml
@@ -2261,6 +2261,10 @@ let check_val_spec envs (VS_aux(vs,(l,annot))) =
let tannot = typschm_to_tannot envs true true typs (External None) in
(VS_aux(vs,(l,tannot)),
Env(d_env,(Envmap.insert t_env (id_to_string id,tannot)),b_env,tp_env))
+ | VS_cast_spec(typs,id) ->
+ let tannot = typschm_to_tannot envs true true typs (External None) in
+ (VS_aux(VS_val_spec(typs,id),(l,tannot)),
+ Env(d_env,(Envmap.insert t_env (id_to_string id,tannot)),b_env,tp_env))
| VS_extern_spec(typs,id,s) ->
let tannot = typschm_to_tannot envs true true typs (External (Some s)) in
(VS_aux(vs,(l,tannot)),
@@ -2524,6 +2528,7 @@ let check_def envs def =
let rec check envs (Defs defs) =
match defs with
| [] -> (Defs []),envs
+ | (DEF_overload (_, _)::defs) -> check envs (Defs defs)
| def::defs -> let (def, envs) = check_def envs def in
let (Defs defs, envs) = check envs (Defs defs) in
(Defs (def::defs)), envs
diff --git a/src/type_check_new.ml b/src/type_check_new.ml
new file mode 100644
index 00000000..30bb97b0
--- /dev/null
+++ b/src/type_check_new.ml
@@ -0,0 +1,2109 @@
+(**************************************************************************)
+(* Sail *)
+(* *)
+(* Copyright (c) 2013-2017 *)
+(* Kathyrn Gray *)
+(* Shaked Flur *)
+(* Stephen Kell *)
+(* Gabriel Kerneis *)
+(* Robert Norton-Wright *)
+(* Christopher Pulte *)
+(* Peter Sewell *)
+(* Alasdair Armstrong *)
+(* *)
+(* All rights reserved. *)
+(* *)
+(* This software was developed by the University of Cambridge Computer *)
+(* Laboratory as part of the Rigorous Engineering of Mainstream Systems *)
+(* (REMS) project, funded by EPSRC grant EP/K008528/1. *)
+(* *)
+(* Redistribution and use in source and binary forms, with or without *)
+(* modification, are permitted provided that the following conditions *)
+(* are met: *)
+(* 1. Redistributions of source code must retain the above copyright *)
+(* notice, this list of conditions and the following disclaimer. *)
+(* 2. Redistributions in binary form must reproduce the above copyright *)
+(* notice, this list of conditions and the following disclaimer in *)
+(* the documentation and/or other materials provided with the *)
+(* distribution. *)
+(* *)
+(* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' *)
+(* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED *)
+(* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *)
+(* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR *)
+(* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, *)
+(* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT *)
+(* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF *)
+(* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND *)
+(* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, *)
+(* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT *)
+(* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF *)
+(* SUCH DAMAGE. *)
+(**************************************************************************)
+
+open Ast
+open Util
+open Ast_util
+open Big_int
+
+let debug = ref 0
+let depth = ref 0
+
+let rec indent n = match n with
+ | 0 -> ""
+ | n -> "| " ^ indent (n - 1)
+
+let typ_debug m = if !debug > 1 then prerr_endline (indent !depth ^ m) else ()
+
+let typ_print m = if !debug > 0 then prerr_endline (indent !depth ^ m) else ()
+
+let typ_warning m = prerr_endline ("Warning: " ^ m)
+
+exception Type_error of l * string;;
+
+let typ_error l m = raise (Type_error (l, m))
+
+let deinfix = function
+ | Id_aux (Id v, l) -> Id_aux (DeIid v, l)
+ | Id_aux (DeIid v, l) -> Id_aux (DeIid v, l)
+
+let string_of_bind (typquant, typ) = string_of_typquant typquant ^ ". " ^ string_of_typ typ
+
+let unaux_nexp (Nexp_aux (nexp, _)) = nexp
+let unaux_order (Ord_aux (ord, _)) = ord
+let unaux_typ (Typ_aux (typ, _)) = typ
+
+let mk_typ typ = Typ_aux (typ, Parse_ast.Unknown)
+let mk_typ_arg arg = Typ_arg_aux (arg, Parse_ast.Unknown)
+let mk_id str = Id_aux (Id str, Parse_ast.Unknown)
+let mk_infix_id str = Id_aux (DeIid str, Parse_ast.Unknown)
+
+let mk_id_typ id = Typ_aux (Typ_id id, Parse_ast.Unknown)
+
+let int_typ = mk_id_typ (mk_id "int")
+let nat_typ = mk_id_typ (mk_id "nat")
+let unit_typ = mk_id_typ (mk_id "unit")
+let bit_typ = mk_id_typ (mk_id "bit")
+let atom_typ nexp = mk_typ (Typ_app (mk_id "atom", [mk_typ_arg (Typ_arg_nexp nexp)]))
+let range_typ nexp1 nexp2 = mk_typ (Typ_app (mk_id "range", [mk_typ_arg (Typ_arg_nexp nexp1); mk_typ_arg (Typ_arg_nexp nexp2)]))
+let bool_typ = mk_id_typ (mk_id "bool")
+let string_typ = mk_id_typ (mk_id "string")
+
+let vector_typ n m ord typ =
+ mk_typ (Typ_app (mk_id "vector",
+ [mk_typ_arg (Typ_arg_nexp n);
+ mk_typ_arg (Typ_arg_nexp m);
+ mk_typ_arg (Typ_arg_order ord);
+ mk_typ_arg (Typ_arg_typ typ)]))
+
+let nconstant c = Nexp_aux (Nexp_constant c, Parse_ast.Unknown)
+let nminus n1 n2 = Nexp_aux (Nexp_minus (n1, n2), Parse_ast.Unknown)
+let nsum n1 n2 = Nexp_aux (Nexp_sum (n1, n2), Parse_ast.Unknown)
+let nvar kid = Nexp_aux (Nexp_var kid, Parse_ast.Unknown)
+
+(* Utilities for constructing effect sets *)
+
+let mk_effect effs =
+ Effect_aux (Effect_set (List.map (fun be_aux -> BE_aux (be_aux, Parse_ast.Unknown)) effs), Parse_ast.Unknown)
+
+let no_effect = mk_effect []
+
+module BESet = Set.Make(BE)
+
+let union_effects e1 e2 =
+ match e1, e2 with
+ | Effect_aux (Effect_set base_effs1, _), Effect_aux (Effect_set base_effs2, _) ->
+ let base_effs3 = BESet.elements (BESet.of_list (base_effs1 @ base_effs2)) in
+ Effect_aux (Effect_set base_effs3, Parse_ast.Unknown)
+ | _, _ -> assert false (* We don't do Effect variables *)
+
+let equal_effects e1 e2 =
+ match e1, e2 with
+ | Effect_aux (Effect_set base_effs1, _), Effect_aux (Effect_set base_effs2, _) ->
+ BESet.compare (BESet.of_list base_effs1) (BESet.of_list base_effs2) = 0
+ | _, _ -> assert false (* We don't do Effect variables *)
+
+(* Sets and maps for identifiers and kind identifiers *)
+
+module Bindings = Map.Make(Id)
+module IdSet = Set.Make(Id)
+module KBindings = Map.Make(Kid)
+module KidSet = Set.Make(Kid)
+
+(* An index_sort is a more general form of range type: it can either
+ be IS_int, which represents every natural number, or some set of
+ natural numbers given by an IS_prop expression of the form
+ {'n. f('n) <= g('n) /\ ...} *)
+type index_sort =
+ | IS_int
+ | IS_prop of kid * (nexp * nexp) list
+
+let string_of_index_sort = function
+ | IS_int -> "INT"
+ | IS_prop (kid, constraints) ->
+ "{" ^ string_of_kid kid ^ " | "
+ ^ string_of_list " & " (fun (x, y) -> string_of_nexp x ^ " <= " ^ string_of_nexp y) constraints
+ ^ "}"
+
+(**************************************************************************)
+(* 1. Substitutions *)
+(**************************************************************************)
+
+let rec nexp_subst sv subst (Nexp_aux (nexp, l)) = Nexp_aux (nexp_subst_aux sv subst nexp, l)
+and nexp_subst_aux sv subst = function
+ | Nexp_id v -> Nexp_id v
+ | Nexp_var kid -> if Kid.compare kid sv = 0 then subst else Nexp_var kid
+ | Nexp_constant c -> Nexp_constant c
+ | Nexp_times (nexp1, nexp2) -> Nexp_times (nexp_subst sv subst nexp1, nexp_subst sv subst nexp2)
+ | Nexp_sum (nexp1, nexp2) -> Nexp_sum (nexp_subst sv subst nexp1, nexp_subst sv subst nexp2)
+ | Nexp_minus (nexp1, nexp2) -> Nexp_minus (nexp_subst sv subst nexp1, nexp_subst sv subst nexp2)
+ | Nexp_exp nexp -> Nexp_exp (nexp_subst sv subst nexp)
+ | Nexp_neg nexp -> Nexp_neg (nexp_subst sv subst nexp)
+
+let rec nc_subst_nexp sv subst (NC_aux (nc, l)) = NC_aux (nc_subst_nexp_aux l sv subst nc, l)
+and nc_subst_nexp_aux l sv subst = function
+ | NC_fixed (n1, n2) -> NC_fixed (nexp_subst sv subst n1, nexp_subst sv subst n2)
+ | NC_bounded_ge (n1, n2) -> NC_bounded_ge (nexp_subst sv subst n1, nexp_subst sv subst n2)
+ | NC_bounded_le (n1, n2) -> NC_bounded_le (nexp_subst sv subst n1, nexp_subst sv subst n2)
+ | NC_nat_set_bounded (kid, ints) as set_nc ->
+ if Kid.compare kid sv = 0
+ then NC_set_subst (Nexp_aux (subst, Parse_ast.Unknown), ints)
+ else set_nc
+ | NC_set_subst (nexp, ints) -> NC_set_subst (nexp_subst sv subst nexp, ints)
+
+let rec typ_subst_nexp sv subst (Typ_aux (typ, l)) = Typ_aux (typ_subst_nexp_aux sv subst typ, l)
+and typ_subst_nexp_aux sv subst = function
+ | Typ_wild -> Typ_wild
+ | Typ_id v -> Typ_id v
+ | Typ_var kid -> Typ_var kid
+ | Typ_fn (typ1, typ2, effs) -> Typ_fn (typ_subst_nexp sv subst typ1, typ_subst_nexp sv subst typ2, effs)
+ | Typ_tup typs -> Typ_tup (List.map (typ_subst_nexp sv subst) typs)
+ | Typ_app (f, args) -> Typ_app (f, List.map (typ_subst_arg_nexp sv subst) args)
+and typ_subst_arg_nexp sv subst (Typ_arg_aux (arg, l)) = Typ_arg_aux (typ_subst_arg_nexp_aux sv subst arg, l)
+and typ_subst_arg_nexp_aux sv subst = function
+ | Typ_arg_nexp nexp -> Typ_arg_nexp (nexp_subst sv subst nexp)
+ | Typ_arg_typ typ -> Typ_arg_typ (typ_subst_nexp sv subst typ)
+ | Typ_arg_order ord -> Typ_arg_order ord
+ | Typ_arg_effect eff -> Typ_arg_effect eff
+
+let rec typ_subst_typ sv subst (Typ_aux (typ, l)) = Typ_aux (typ_subst_typ_aux sv subst typ, l)
+and typ_subst_typ_aux sv subst = function
+ | Typ_wild -> Typ_wild
+ | Typ_id v -> Typ_id v
+ | Typ_var kid -> if Kid.compare kid sv = 0 then subst else Typ_var kid
+ | Typ_fn (typ1, typ2, effs) -> Typ_fn (typ_subst_typ sv subst typ1, typ_subst_typ sv subst typ2, effs)
+ | Typ_tup typs -> Typ_tup (List.map (typ_subst_typ sv subst) typs)
+ | Typ_app (f, args) -> Typ_app (f, List.map (typ_subst_arg_typ sv subst) args)
+and typ_subst_arg_typ sv subst (Typ_arg_aux (arg, l)) = Typ_arg_aux (typ_subst_arg_typ_aux sv subst arg, l)
+and typ_subst_arg_typ_aux sv subst = function
+ | Typ_arg_nexp nexp -> Typ_arg_nexp nexp
+ | Typ_arg_typ typ -> Typ_arg_typ (typ_subst_typ sv subst typ)
+ | Typ_arg_order ord -> Typ_arg_order ord
+ | Typ_arg_effect eff -> Typ_arg_effect eff
+
+let order_subst_aux sv subst = function
+ | Ord_var kid -> if Kid.compare kid sv = 0 then subst else Ord_var kid
+ | Ord_inc -> Ord_inc
+ | Ord_dec -> Ord_dec
+
+let order_subst sv subst (Ord_aux (ord, l)) = Ord_aux (order_subst_aux sv subst ord, l)
+
+let rec typ_subst_order sv subst (Typ_aux (typ, l)) = Typ_aux (typ_subst_order_aux sv subst typ, l)
+and typ_subst_order_aux sv subst = function
+ | Typ_wild -> Typ_wild
+ | Typ_id v -> Typ_id v
+ | Typ_var kid -> Typ_var kid
+ | Typ_fn (typ1, typ2, effs) -> Typ_fn (typ_subst_order sv subst typ1, typ_subst_order sv subst typ2, effs)
+ | Typ_tup typs -> Typ_tup (List.map (typ_subst_order sv subst) typs)
+ | Typ_app (f, args) -> Typ_app (f, List.map (typ_subst_arg_order sv subst) args)
+and typ_subst_arg_order sv subst (Typ_arg_aux (arg, l)) = Typ_arg_aux (typ_subst_arg_order_aux sv subst arg, l)
+and typ_subst_arg_order_aux sv subst = function
+ | Typ_arg_nexp nexp -> Typ_arg_nexp nexp
+ | Typ_arg_typ typ -> Typ_arg_typ (typ_subst_order sv subst typ)
+ | Typ_arg_order ord -> Typ_arg_order (order_subst sv subst ord)
+ | Typ_arg_effect eff -> Typ_arg_effect eff
+
+let rec typ_subst_kid sv subst (Typ_aux (typ, l)) = Typ_aux (typ_subst_kid_aux sv subst typ, l)
+and typ_subst_kid_aux sv subst = function
+ | Typ_wild -> Typ_wild
+ | Typ_id v -> Typ_id v
+ | Typ_var kid -> if Kid.compare kid sv = 0 then Typ_var subst else Typ_var kid
+ | Typ_fn (typ1, typ2, effs) -> Typ_fn (typ_subst_kid sv subst typ1, typ_subst_kid sv subst typ2, effs)
+ | Typ_tup typs -> Typ_tup (List.map (typ_subst_kid sv subst) typs)
+ | Typ_app (f, args) -> Typ_app (f, List.map (typ_subst_arg_kid sv subst) args)
+and typ_subst_arg_kid sv subst (Typ_arg_aux (arg, l)) = Typ_arg_aux (typ_subst_arg_kid_aux sv subst arg, l)
+and typ_subst_arg_kid_aux sv subst = function
+ | Typ_arg_nexp nexp -> Typ_arg_nexp (nexp_subst sv (Nexp_var subst) nexp)
+ | Typ_arg_typ typ -> Typ_arg_typ (typ_subst_kid sv subst typ)
+ | Typ_arg_order ord -> Typ_arg_order (order_subst sv (Ord_var subst) ord)
+ | Typ_arg_effect eff -> Typ_arg_effect eff
+
+let quant_item_subst_kid_aux sv subst = function
+ | QI_id (KOpt_aux (KOpt_none kid, l)) as qid ->
+ if Kid.compare kid sv = 0 then QI_id (KOpt_aux (KOpt_none subst, l)) else qid
+ | QI_id (KOpt_aux (KOpt_kind (k, kid), l)) as qid ->
+ if Kid.compare kid sv = 0 then QI_id (KOpt_aux (KOpt_kind (k, subst), l)) else qid
+ | QI_const nc -> QI_const (nc_subst_nexp sv (Nexp_var subst) nc)
+
+let rec nexp_simp (Nexp_aux (nexp, l)) = Nexp_aux (nexp_simp_aux nexp, l)
+and nexp_simp_aux = function
+ | Nexp_sum (n1, n2) ->
+ begin
+ let (Nexp_aux (n1_simp, _) as n1) = nexp_simp n1 in
+ let (Nexp_aux (n2_simp, _) as n2) = nexp_simp n2 in
+ match n1_simp, n2_simp with
+ | Nexp_constant c1, Nexp_constant c2 -> Nexp_constant (c1 + c2)
+ | _, _ -> Nexp_sum (n1, n2)
+ end
+ | Nexp_times (n1, n2) ->
+ begin
+ let (Nexp_aux (n1_simp, _) as n1) = nexp_simp n1 in
+ let (Nexp_aux (n2_simp, _) as n2) = nexp_simp n2 in
+ match n1_simp, n2_simp with
+ | Nexp_constant c1, Nexp_constant c2 -> Nexp_constant (c1 * c2)
+ | _, _ -> Nexp_times (n1, n2)
+ end
+ | Nexp_minus (n1, n2) ->
+ begin
+ let (Nexp_aux (n1_simp, _) as n1) = nexp_simp n1 in
+ let (Nexp_aux (n2_simp, _) as n2) = nexp_simp n2 in
+ typ_debug ("SIMP: " ^ string_of_nexp n1 ^ " - " ^ string_of_nexp n2);
+ match n1_simp, n2_simp with
+ | Nexp_constant c1, Nexp_constant c2 -> Nexp_constant (c1 - c2)
+ | _, _ -> Nexp_minus (n1, n2)
+ end
+ | nexp -> nexp
+
+let quant_item_subst_kid sv subst (QI_aux (quant, l)) = QI_aux (quant_item_subst_kid_aux sv subst quant, l)
+
+let typquant_subst_kid_aux sv subst = function
+ | TypQ_tq quants -> TypQ_tq (List.map (quant_item_subst_kid sv subst) quants)
+ | TypQ_no_forall -> TypQ_no_forall
+
+let typquant_subst_kid sv subst (TypQ_aux (typq, l)) = TypQ_aux (typquant_subst_kid_aux sv subst typq, l)
+
+(**************************************************************************)
+(* 2. Environment *)
+(**************************************************************************)
+
+type mut = Immutable | Mutable
+
+type lvar = Register of typ | Enum of typ | Local of mut * typ | Unbound
+
+module Env : sig
+ type t
+ val add_val_spec : id -> typquant * typ -> t -> t
+ val get_val_spec : id -> t -> typquant * typ
+ val add_record : id -> typquant -> (typ * id) list -> t -> t
+ val is_record : id -> t -> bool
+ val get_accessor : id -> t -> typquant * typ
+ val add_local : id -> mut * typ -> t -> t
+ val add_flow : id -> (typ -> typ) -> t -> t
+ val get_flow : id -> t -> typ -> typ
+ val get_register : id -> t -> typ
+ val add_register : id -> typ -> t -> t
+ val add_regtyp : id -> int -> int -> (index_range * id) list -> t -> t
+ val is_regtyp : id -> t -> bool
+ val get_regtyp : id -> t -> int * int * (index_range * id) list
+ val is_mutable : id -> t -> bool
+ val get_constraints : t -> n_constraint list
+ val add_constraint : n_constraint -> t -> t
+ val get_typ_var : kid -> t -> base_kind_aux
+ val add_typ_var : kid -> base_kind_aux -> t -> t
+ val get_ret_typ : t -> typ option
+ val add_ret_typ : typ -> t -> t
+ val add_typ_synonym : id -> (typ_arg list -> typ) -> t -> t
+ val get_typ_synonym : id -> t -> typ_arg list -> typ
+ val add_overloads : id -> id list -> t -> t
+ val get_overloads : id -> t -> id list
+ val get_default_order : t -> order
+ val set_default_order_inc : t -> t
+ val set_default_order_dec : t -> t
+ val add_enum : id -> id list -> t -> t
+ val get_casts : t -> id list
+ val allow_casts : t -> bool
+ val no_casts : t -> t
+ val add_cast : id -> t -> t
+ val lookup_id : id -> t -> lvar
+ val fresh_kid : t -> kid
+ val expand_synonyms : t -> typ -> typ
+ val empty : t
+end = struct
+ type t =
+ { top_val_specs : (typquant * typ) Bindings.t;
+ locals : (mut * typ) Bindings.t;
+ registers : typ Bindings.t;
+ regtyps : (int * int * (index_range * id) list) Bindings.t;
+ typ_vars : base_kind_aux KBindings.t;
+ typ_synonyms : (typ_arg list -> typ) Bindings.t;
+ overloads : (id list) Bindings.t;
+ flow : (typ -> typ) Bindings.t;
+ enums : IdSet.t Bindings.t;
+ records : (typquant * (typ * id) list) Bindings.t;
+ accessors : (typquant * typ) Bindings.t;
+ casts : id list;
+ allow_casts : bool;
+ constraints : n_constraint list;
+ default_order : order option;
+ ret_typ : typ option
+ }
+
+ let empty =
+ { top_val_specs = Bindings.empty;
+ locals = Bindings.empty;
+ registers = Bindings.empty;
+ regtyps = Bindings.empty;
+ typ_vars = KBindings.empty;
+ typ_synonyms = Bindings.empty;
+ overloads = Bindings.empty;
+ flow = Bindings.empty;
+ enums = Bindings.empty;
+ records = Bindings.empty;
+ accessors = Bindings.empty;
+ casts = [];
+ allow_casts = true;
+ constraints = [];
+ default_order = None;
+ ret_typ = None;
+ }
+
+ let counter = ref 0
+
+ let fresh_kid env =
+ let fresh = Kid_aux (Var ("'fv" ^ string_of_int !counter), Parse_ast.Unknown) in
+ incr counter; fresh
+
+ let freshen_kid env kid (typq, typ) =
+ let fresh = fresh_kid env in
+ (typquant_subst_kid kid fresh typq, typ_subst_kid kid fresh typ)
+
+ let get_val_spec id env =
+ try
+ let bind = Bindings.find id env.top_val_specs in
+ typ_debug ("get_val_spec: Env has " ^ string_of_list ", " (fun (kid, bk) -> string_of_kid kid ^ " => " ^ string_of_base_kind_aux bk) (KBindings.bindings env.typ_vars));
+ let bind' = List.fold_left (fun bind (kid, _) -> freshen_kid env kid bind) bind (KBindings.bindings env.typ_vars) in
+ typ_debug ("get_val_spec: freshened to " ^ string_of_bind bind');
+ bind'
+ with
+ | Not_found -> typ_error (id_loc id) ("No val spec found for " ^ string_of_id id)
+
+ let add_val_spec id bind env =
+ if Bindings.mem id env.top_val_specs
+ then typ_error (id_loc id) ("Identifier " ^ string_of_id id ^ " is already bound")
+ else
+ begin
+ typ_print ("Adding val spec binding " ^ string_of_id id ^ " :: " ^ string_of_bind bind);
+ { env with top_val_specs = Bindings.add id bind env.top_val_specs }
+ end
+
+ let get_typ_var kid env =
+ try KBindings.find kid env.typ_vars with
+ | Not_found -> typ_error (kid_loc kid) ("No kind identifier " ^ string_of_kid kid)
+
+ (* FIXME: Add an IdSet for builtin types *)
+ let bound_typ_id env id =
+ Bindings.mem id env.typ_synonyms
+ || Bindings.mem id env.records
+ || Bindings.mem id env.regtyps
+ || Bindings.mem id env.enums
+ || Id.compare id (mk_id "range") = 0
+ || Id.compare id (mk_id "vector") = 0
+ || Id.compare id (mk_id "register") = 0
+ || Id.compare id (mk_id "bit") = 0
+ || Id.compare id (mk_id "unit") = 0
+ || Id.compare id (mk_id "int") = 0
+ || Id.compare id (mk_id "nat") = 0
+ || Id.compare id (mk_id "bool") = 0
+
+ (* Check if a type, order, or n-expression is well-formed. Throws a
+ type error if the type is badly formed. FIXME: Add arity to type
+ constructors, although arity checking for the builtin types does
+ seem to be done by the initial ast check. *)
+ let rec wf_typ env (Typ_aux (typ_aux, l)) =
+ match typ_aux with
+ | Typ_wild -> ()
+ | Typ_id id when bound_typ_id env id -> ()
+ | Typ_id id -> typ_error l ("Undefined type " ^ string_of_id id)
+ | Typ_var kid when KBindings.mem kid env.typ_vars -> ()
+ | Typ_var kid -> typ_error l ("Unbound kind identifier " ^ string_of_kid kid)
+ | Typ_fn (typ_arg, typ_ret, effs) -> wf_typ env typ_arg; wf_typ env typ_ret
+ | Typ_tup typs -> List.iter (wf_typ env) typs
+ | Typ_app (id, args) when bound_typ_id env id -> List.iter (wf_typ_arg env) args
+ | Typ_app (id, _) -> typ_error l ("Undefined type " ^ string_of_id id)
+ and wf_typ_arg env (Typ_arg_aux (typ_arg_aux, _)) =
+ match typ_arg_aux with
+ | Typ_arg_nexp nexp -> wf_nexp env nexp
+ | Typ_arg_typ typ -> wf_typ env typ
+ | Typ_arg_order ord -> wf_order env ord
+ | Typ_arg_effect _ -> () (* Check: is this ever used? *)
+ and wf_nexp env (Nexp_aux (nexp_aux, l)) =
+ match nexp_aux with
+ | Nexp_id _ -> typ_error l "Unimplemented: Nexp_id"
+ | Nexp_var kid ->
+ begin
+ match get_typ_var kid env with
+ | BK_nat -> ()
+ | kind -> typ_error l ("Constraint is badly formed, "
+ ^ string_of_kid kid ^ " has kind "
+ ^ string_of_base_kind_aux kind ^ " but should have kind Nat")
+ end
+ | Nexp_constant _ -> ()
+ | Nexp_times (nexp1, nexp2) -> wf_nexp env nexp1; wf_nexp env nexp2
+ | Nexp_sum (nexp1, nexp2) -> wf_nexp env nexp1; wf_nexp env nexp2
+ | Nexp_minus (nexp1, nexp2) -> wf_nexp env nexp1; wf_nexp env nexp2
+ | Nexp_exp nexp -> wf_nexp env nexp (* MAYBE: Could put restrictions on what is allowed here *)
+ | Nexp_neg nexp -> wf_nexp env nexp
+ and wf_order env (Ord_aux (ord_aux, l)) =
+ match ord_aux with
+ | Ord_var kid ->
+ begin
+ match get_typ_var kid env with
+ | BK_order -> ()
+ | kind -> typ_error l ("Order is badly formed, "
+ ^ string_of_kid kid ^ " has kind "
+ ^ string_of_base_kind_aux kind ^ " but should have kind Order")
+ end
+ | Ord_inc | Ord_dec -> ()
+
+ let add_enum id ids env =
+ if bound_typ_id env id
+ then typ_error (id_loc id) ("Cannot create enum " ^ string_of_id id ^ ", type name is already bound")
+ else
+ begin
+ typ_print ("Adding enum " ^ string_of_id id);
+ { env with enums = Bindings.add id (IdSet.of_list ids) env.enums }
+ end
+
+ let is_record id env = Bindings.mem id env.records
+
+ let add_record id typq fields env =
+ if bound_typ_id env id
+ then typ_error (id_loc id) ("Cannot create record " ^ string_of_id id ^ ", type name is already bound")
+ else
+ begin
+ typ_print ("Adding record " ^ string_of_id id);
+ let fold_accessors accs (typ, fid) =
+ let acc_typ = mk_typ (Typ_fn (mk_id_typ id, typ, Effect_aux (Effect_set [], Parse_ast.Unknown))) in
+ typ_print (indent 1 ^ "Adding accessor " ^ string_of_id fid ^ " :: " ^ string_of_bind (typq, acc_typ));
+ Bindings.add fid (typq, acc_typ) accs
+ in
+ { env with records = Bindings.add id (typq, fields) env.records;
+ accessors = List.fold_left fold_accessors env.accessors fields }
+ end
+
+ let get_accessor id env =
+ let freshen_bind bind = List.fold_left (fun bind (kid, _) -> freshen_kid env kid bind) bind (KBindings.bindings env.typ_vars) in
+ try freshen_bind (Bindings.find id env.accessors)
+ with
+ | Not_found -> typ_error (id_loc id) ("No accessor found for " ^ string_of_id id)
+
+ let is_mutable id env =
+ try
+ let (mut, _) = Bindings.find id env.locals in
+ match mut with
+ | Mutable -> true
+ | Immutable -> false
+ with
+ | Not_found -> typ_error (id_loc id) ("No local binding found for " ^ string_of_id id)
+
+ let string_of_mtyp (mut, typ) = match mut with
+ | Immutable -> string_of_typ typ
+ | Mutable -> "ref<" ^ string_of_typ typ ^ ">"
+
+ let add_local id mtyp env =
+ begin
+ wf_typ env (snd mtyp);
+ typ_print ("Adding local binding " ^ string_of_id id ^ " :: " ^ string_of_mtyp mtyp);
+ { env with locals = Bindings.add id mtyp env.locals }
+ end
+
+ let get_flow id env =
+ try Bindings.find id env.flow with
+ | Not_found -> fun typ -> typ
+
+ let add_flow id f env =
+ begin
+ typ_print ("Adding flow constraints for " ^ string_of_id id);
+ { env with flow = Bindings.add id (fun typ -> f (get_flow id env typ)) env.flow }
+ end
+
+ let get_register id env =
+ try Bindings.find id env.registers with
+ | Not_found -> typ_error (id_loc id) ("No register binding found for " ^ string_of_id id)
+
+ let get_overloads id env =
+ try Bindings.find id env.overloads with
+ | Not_found -> []
+
+ let add_overloads id ids env =
+ typ_print ("Adding overloads for " ^ string_of_id id ^ " [" ^ string_of_list ", " string_of_id ids ^ "]");
+ { env with overloads = Bindings.add id ids env.overloads }
+
+ let get_casts env = env.casts
+
+ let check_index_range cmp f t (BF_aux (ir, l)) =
+ match ir with
+ | BF_single n ->
+ if cmp f n && cmp n t
+ then n
+ else typ_error l ("Badly ordered index range: " ^ string_of_list ", " string_of_int [f; n; t])
+ | BF_range (n1, n2) ->
+ if cmp f n1 && cmp n1 n2 && cmp n2 t
+ then n2
+ else typ_error l ("Badly ordered index range: " ^ string_of_list ", " string_of_int [f; n1; n2; t])
+ | BF_concat _ -> typ_error l "Index range concatenation currently unsupported"
+
+ let rec check_index_ranges ids cmp base top = function
+ | [] -> ()
+ | ((range, id) :: ranges) ->
+ if IdSet.mem id ids
+ then typ_error (id_loc id) ("Duplicate id " ^ string_of_id id ^ " in register typedef")
+ else
+ begin
+ let base' = check_index_range cmp base top range in
+ check_index_ranges (IdSet.add id ids) cmp base' top ranges
+ end
+
+ let add_register id typ env =
+ if Bindings.mem id env.registers
+ then typ_error (id_loc id) ("Register " ^ string_of_id id ^ " is already bound")
+ else
+ begin
+ typ_print ("Adding register binding " ^ string_of_id id ^ " :: " ^ string_of_typ typ);
+ { env with registers = Bindings.add id typ env.registers }
+ end
+
+ let add_regtyp id base top ranges env =
+ if Bindings.mem id env.regtyps
+ then typ_error (id_loc id) ("Register type " ^ string_of_id id ^ " is already bound")
+ else
+ begin
+ typ_print ("Adding register type " ^ string_of_id id);
+ if base > top
+ then check_index_ranges IdSet.empty (fun x y -> x > y) (base + 1) (top - 1) ranges
+ else check_index_ranges IdSet.empty (fun x y -> x < y) (base - 1) (top + 1) ranges;
+ { env with regtyps = Bindings.add id (base, top, ranges) env.regtyps }
+ end
+
+ let is_regtyp id env = Bindings.mem id env.regtyps
+
+ let get_regtyp id env =
+ try Bindings.find id env.regtyps with
+ | Not_found -> typ_error (id_loc id) (string_of_id id ^ " is not a register type")
+
+ let lookup_id id env =
+ try
+ let (mut, typ) = Bindings.find id env.locals in
+ let flow = get_flow id env in
+ Local (mut, flow typ)
+ with
+ | Not_found ->
+ begin
+ try Register (Bindings.find id env.registers) with
+ | Not_found ->
+ begin
+ try
+ let (enum, _) = List.find (fun (enum, ctors) -> IdSet.mem id ctors) (Bindings.bindings env.enums) in
+ Enum (mk_typ (Typ_id enum))
+ with
+ | Not_found -> Unbound
+ end
+ end
+
+ let add_typ_var kid k env =
+ if KBindings.mem kid env.typ_vars
+ then typ_error (kid_loc kid) ("Kind identifier " ^ string_of_kid kid ^ " is already bound")
+ else
+ begin
+ typ_debug ("Adding kind identifier binding " ^ string_of_kid kid ^ " :: " ^ string_of_base_kind_aux k);
+ { env with typ_vars = KBindings.add kid k env.typ_vars }
+ end
+
+ let wf_constraint env (NC_aux (nc, _)) =
+ match nc with
+ | NC_fixed (n1, n2) -> wf_nexp env n1; wf_nexp env n2
+ | NC_bounded_ge (n1, n2) -> wf_nexp env n1; wf_nexp env n2
+ | NC_bounded_le (n1, n2) -> wf_nexp env n1; wf_nexp env n2
+ | NC_nat_set_bounded (kid, ints) -> () (* MAYBE: We could demand that ints are all unique here *)
+ | NC_set_subst (nexp, ints) -> wf_nexp env nexp
+
+ let get_constraints env = env.constraints
+
+ let add_constraint (NC_aux (_, l) as constr) env =
+ wf_constraint env constr;
+ begin
+ typ_debug ("Adding constraint " ^ string_of_n_constraint constr);
+ { env with constraints = constr :: env.constraints }
+ end
+
+ let get_ret_typ env = env.ret_typ
+
+ let add_ret_typ typ env = { env with ret_typ = Some typ }
+
+ let allow_casts env = env.allow_casts
+
+ let no_casts env = { env with allow_casts = false }
+
+ let add_cast cast env =
+ typ_print ("Adding cast " ^ string_of_id cast);
+ { env with casts = cast :: env.casts }
+
+ let add_typ_synonym id synonym env =
+ if Bindings.mem id env.typ_synonyms
+ then typ_error (id_loc id) ("Type synonym " ^ string_of_id id ^ " already exists")
+ else
+ begin
+ typ_debug ("Adding type synonym " ^ string_of_id id);
+ { env with typ_synonyms = Bindings.add id synonym env.typ_synonyms }
+ end
+
+ let get_typ_synonym id env = Bindings.find id env.typ_synonyms
+
+ let rec expand_synonyms env (Typ_aux (typ, l)) =
+ match typ with
+ | Typ_tup typs -> Typ_aux (Typ_tup (List.map (expand_synonyms env) typs), l)
+ | Typ_fn (typ1, typ2, effs) -> Typ_aux (Typ_fn (expand_synonyms env typ1, expand_synonyms env typ2, effs), l)
+ | Typ_app (id, args) ->
+ begin
+ try
+ let synonym = Bindings.find id env.typ_synonyms in
+ expand_synonyms env (synonym args)
+ with
+ | Not_found -> Typ_aux (Typ_app (id, List.map (expand_synonyms_arg env) args), l)
+ end
+ | Typ_id id ->
+ begin
+ try
+ let synonym = Bindings.find id env.typ_synonyms in
+ expand_synonyms env (synonym [])
+ with
+ | Not_found -> Typ_aux (Typ_id id, l)
+ end
+ | typ -> Typ_aux (typ, l)
+ and expand_synonyms_arg env (Typ_arg_aux (typ_arg, l)) =
+ match typ_arg with
+ | Typ_arg_typ typ -> Typ_arg_aux (Typ_arg_typ (expand_synonyms env typ), l)
+ | arg -> Typ_arg_aux (arg, l)
+
+ let get_default_order env =
+ match env.default_order with
+ | None -> typ_error Parse_ast.Unknown ("No default order has been set")
+ | Some ord -> ord
+
+ let set_default_order o env =
+ match env.default_order with
+ | None -> { env with default_order = Some (Ord_aux (o, Parse_ast.Unknown)) }
+ | Some _ -> typ_error Parse_ast.Unknown ("Cannot change default order once already set")
+
+ let set_default_order_inc = set_default_order Ord_inc
+ let set_default_order_dec = set_default_order Ord_dec
+
+end
+
+
+let add_typquant (quant : typquant) (env : Env.t) : Env.t =
+ let rec add_quant_item env = function
+ | QI_aux (qi, _) -> add_quant_item_aux env qi
+ and add_quant_item_aux env = function
+ | QI_const constr -> Env.add_constraint constr env
+ | QI_id (KOpt_aux (KOpt_none kid, _)) -> Env.add_typ_var kid BK_nat env
+ | QI_id (KOpt_aux (KOpt_kind (K_aux (K_kind [BK_aux (k, _)], _), kid), _)) -> Env.add_typ_var kid k env
+ | QI_id (KOpt_aux (_, l)) -> typ_error l "Type variable had non base kinds!"
+ in
+ match quant with
+ | TypQ_aux (TypQ_no_forall, _) -> env
+ | TypQ_aux (TypQ_tq quants, _) -> List.fold_left add_quant_item env quants
+
+(* Create vectors with the default order from the environment *)
+
+let dvector_typ env n m typ = vector_typ n m (Env.get_default_order env) typ
+
+let lvector_typ env l typ =
+ match Env.get_default_order env with
+ | Ord_aux (Ord_inc, _) as ord ->
+ vector_typ (nconstant 0) l ord typ
+ | Ord_aux (Ord_dec, _) as ord ->
+ vector_typ (nminus l (nconstant 1)) l ord typ
+
+let initial_env =
+ Env.empty
+ |> Env.add_typ_synonym (mk_id "atom") (fun args -> mk_typ (Typ_app (mk_id "range", args @ args)))
+
+(**************************************************************************)
+(* 3. Subtyping and constraint solving *)
+(**************************************************************************)
+
+let order_eq (Ord_aux (ord_aux1, _)) (Ord_aux (ord_aux2, _)) =
+ match ord_aux1, ord_aux2 with
+ | Ord_inc, Ord_inc -> true
+ | Ord_dec, Ord_dec -> true
+ | Ord_var kid1, Ord_var kid2 -> Kid.compare kid1 kid2 = 0
+ | _, _ -> false
+
+let rec props_subst sv subst props =
+ match props with
+ | [] -> []
+ | ((nexp1, nexp2) :: props) -> (nexp_subst sv subst nexp1, nexp_subst sv subst nexp2) :: props_subst sv subst props
+
+type tnf =
+ | Tnf_wild
+ | Tnf_id of id
+ | Tnf_var of kid
+ | Tnf_tup of tnf list
+ | Tnf_index_sort of index_sort
+ | Tnf_app of id * tnf_arg list
+and tnf_arg =
+ | Tnf_arg_nexp of nexp
+ | Tnf_arg_typ of tnf
+ | Tnf_arg_order of order
+ | Tnf_arg_effect of effect
+
+let rec string_of_tnf = function
+ | Tnf_wild -> "_"
+ | Tnf_id id -> string_of_id id
+ | Tnf_var kid -> string_of_kid kid
+ | Tnf_tup tnfs -> "(" ^ string_of_list ", " string_of_tnf tnfs ^ ")"
+ | Tnf_app (id, args) -> string_of_id id ^ "<" ^ string_of_list ", " string_of_tnf_arg args ^ ">"
+ | Tnf_index_sort IS_int -> "INT"
+ | Tnf_index_sort (IS_prop (kid, props)) ->
+ "{" ^ string_of_kid kid ^ " | " ^ string_of_list " & " (fun (n1, n2) -> string_of_nexp n1 ^ " <= " ^ string_of_nexp n2) props ^ "}"
+and string_of_tnf_arg = function
+ | Tnf_arg_nexp n -> string_of_nexp n
+ | Tnf_arg_typ tnf -> string_of_tnf tnf
+ | Tnf_arg_order o -> string_of_order o
+ | Tnf_arg_effect eff -> string_of_effect eff
+
+let rec normalize_typ env (Typ_aux (typ, l)) =
+ match typ with
+ | Typ_wild -> Tnf_wild
+ | Typ_id (Id_aux (Id "int", _)) -> Tnf_index_sort IS_int
+ | Typ_id (Id_aux (Id "nat", _)) ->
+ let kid = Env.fresh_kid env in Tnf_index_sort (IS_prop (kid, [(nconstant 0, nvar kid)]))
+ | Typ_id v ->
+ begin
+ try normalize_typ env (Env.get_typ_synonym v env []) with
+ | Not_found -> Tnf_id v
+ end
+ | Typ_var kid -> Tnf_var kid
+ | Typ_tup typs -> Tnf_tup (List.map (normalize_typ env) typs)
+ | Typ_app (Id_aux (Id "range", _), [Typ_arg_aux (Typ_arg_nexp n1, _); Typ_arg_aux (Typ_arg_nexp n2, _)]) ->
+ let kid = Env.fresh_kid env in
+ Tnf_index_sort (IS_prop (kid, [(n1, nvar kid); (nvar kid, n2)]))
+ | Typ_app ((Id_aux (Id "vector", _) as vector), args) ->
+ Tnf_app (vector, List.map (normalize_typ_arg env) args)
+ | Typ_app (id, args) ->
+ begin
+ try normalize_typ env (Env.get_typ_synonym id env args) with
+ | Not_found -> Tnf_app (id, List.map (normalize_typ_arg env) args)
+ end
+ | Typ_fn _ -> typ_error l ("Cannot normalize function type " ^ string_of_typ (Typ_aux (typ, l)))
+and normalize_typ_arg env (Typ_arg_aux (typ_arg, _)) =
+ match typ_arg with
+ | Typ_arg_nexp n -> Tnf_arg_nexp n
+ | Typ_arg_typ typ -> Tnf_arg_typ (normalize_typ env typ)
+ | Typ_arg_order o -> Tnf_arg_order o
+ | Typ_arg_effect e -> Tnf_arg_effect e
+
+(* Here's how the constraint generation works for subtyping
+
+X(b,c...) --> {a. Y(a,b,c...)} \subseteq {a. Z(a,b,c...)}
+
+this is equivalent to
+
+\forall b c. X(b,c) --> \forall a. Y(a,b,c) --> Z(a,b,c)
+
+\forall b c. X(b,c) --> \forall a. !Y(a,b,c) \/ !Z^-1(a,b,c)
+
+\forall b c. X(b,c) --> !\exists a. Y(a,b,c) /\ Z^-1(a,b,c)
+
+\forall b c. !X(b,c) \/ !\exists a. Y(a,b,c) /\ Z^-1(a,b,c)
+
+!\exists b c. X(b,c) /\ \exists a. Y(a,b,c) /\ Z^-1(a,b,c)
+
+!\exists a b c. X(b,c) /\ Y(a,b,c) /\ Z^-1(a,b,c)
+
+which is then a problem we can feed to the constraint solver expecting unsat.
+ *)
+
+let rec nexp_constraint var_of (Nexp_aux (nexp, l)) =
+ match nexp with
+ | Nexp_id v -> typ_error l "Unimplemented: Cannot generate constraint from Nexp_id"
+ | Nexp_var kid -> Constraint.variable (var_of kid)
+ | Nexp_constant c -> Constraint.constant (big_int_of_int c)
+ | Nexp_times (nexp1, nexp2) -> Constraint.mult (nexp_constraint var_of nexp1) (nexp_constraint var_of nexp2)
+ | Nexp_sum (nexp1, nexp2) -> Constraint.add (nexp_constraint var_of nexp1) (nexp_constraint var_of nexp2)
+ | Nexp_minus (nexp1, nexp2) -> Constraint.sub (nexp_constraint var_of nexp1) (nexp_constraint var_of nexp2)
+ | Nexp_exp nexp -> Constraint.pow2 (nexp_constraint var_of nexp)
+ | Nexp_neg nexp -> Constraint.sub (Constraint.constant (big_int_of_int 0)) (nexp_constraint var_of nexp)
+
+let rec nc_constraint var_of (NC_aux (nc, l)) =
+ match nc with
+ | NC_fixed (nexp1, nexp2) -> Constraint.eq (nexp_constraint var_of nexp1) (nexp_constraint var_of nexp2)
+ | NC_bounded_ge (nexp1, nexp2) -> Constraint.gteq (nexp_constraint var_of nexp1) (nexp_constraint var_of nexp2)
+ | NC_bounded_le (nexp1, nexp2) -> Constraint.lteq (nexp_constraint var_of nexp1) (nexp_constraint var_of nexp2)
+ | NC_nat_set_bounded (kid, ints) -> nc_constraint var_of (NC_aux (NC_set_subst (nvar kid, ints), l))
+ | NC_set_subst (_, []) -> Constraint.literal false
+ | NC_set_subst (nexp, (int :: ints)) ->
+ List.fold_left Constraint.disj
+ (Constraint.eq (nexp_constraint var_of nexp) (Constraint.constant (big_int_of_int int)))
+ (List.map (fun i -> Constraint.eq (nexp_constraint var_of nexp) (Constraint.constant (big_int_of_int i))) ints)
+
+let rec nc_constraints var_of ncs =
+ match ncs with
+ | [] -> Constraint.literal true
+ | [nc] -> nc_constraint var_of nc
+ | (nc :: ncs) ->
+ Constraint.conj (nc_constraint var_of nc) (nc_constraints var_of ncs)
+
+let prove env nc =
+ typ_print ("Prove " ^ string_of_list ", " string_of_n_constraint (Env.get_constraints env) ^ " |- " ^ string_of_n_constraint nc);
+ let module Bindings = Map.Make(Kid) in
+ let bindings = ref Bindings.empty in
+ let fresh_var kid =
+ let n = Bindings.cardinal !bindings in
+ bindings := Bindings.add kid n !bindings;
+ n
+ in
+ let var_of kid =
+ try Bindings.find kid !bindings with
+ | Not_found -> fresh_var kid
+ in
+ let constr = Constraint.conj (nc_constraints var_of (Env.get_constraints env)) (Constraint.negate (nc_constraint var_of nc)) in
+ match Constraint.call_z3 constr with
+ | Constraint.Unsat _ -> typ_debug "unsat"; true
+ | Constraint.Unknown [] -> typ_debug "sat"; false
+ | Constraint.Unknown _ -> typ_debug "unknown"; false
+
+let rec subtyp_tnf env tnf1 tnf2 =
+ typ_print ("Subset " ^ string_of_list ", " string_of_n_constraint (Env.get_constraints env) ^ " |- " ^ string_of_tnf tnf1 ^ " " ^ string_of_tnf tnf2);
+ let module Bindings = Map.Make(Kid) in
+ let bindings = ref Bindings.empty in
+ let fresh_var kid =
+ let n = Bindings.cardinal !bindings in
+ bindings := Bindings.add kid n !bindings;
+ n
+ in
+ let var_of kid =
+ try Bindings.find kid !bindings with
+ | Not_found -> fresh_var kid
+ in
+ let rec neg_props props =
+ match props with
+ | [] -> Constraint.literal false
+ | [(nexp1, nexp2)] -> Constraint.gt (nexp_constraint var_of nexp1) (nexp_constraint var_of nexp2)
+ | ((nexp1, nexp2) :: props) ->
+ Constraint.disj (Constraint.gt (nexp_constraint var_of nexp1) (nexp_constraint var_of nexp2)) (neg_props props)
+ in
+ let rec pos_props props =
+ match props with
+ | [] -> Constraint.literal true
+ | [(nexp1, nexp2)] -> Constraint.lteq (nexp_constraint var_of nexp1) (nexp_constraint var_of nexp2)
+ | ((nexp1, nexp2) :: props) ->
+ Constraint.conj (Constraint.lteq (nexp_constraint var_of nexp1) (nexp_constraint var_of nexp2)) (pos_props props)
+ in
+ match (tnf1, tnf2) with
+ | Tnf_wild, Tnf_wild -> true
+ | Tnf_id v1, Tnf_id v2 -> Id.compare v1 v2 = 0
+ | Tnf_var kid1, Tnf_var kid2 -> Kid.compare kid1 kid2 = 0
+ | Tnf_tup tnfs1, Tnf_tup tnfs2 ->
+ begin
+ try List.for_all2 (subtyp_tnf env) tnfs1 tnfs2 with
+ | Invalid_argument _ -> false
+ end
+ | Tnf_app (v1, args1), Tnf_app (v2, args2) -> Id.compare v1 v2 = 0 && List.for_all2 (tnf_args_eq env) args1 args2
+ | Tnf_index_sort IS_int, Tnf_index_sort IS_int -> true
+ | Tnf_index_sort (IS_prop _), Tnf_index_sort IS_int -> true
+ | Tnf_index_sort (IS_prop (kid1, prop1)), Tnf_index_sort (IS_prop (kid2, prop2)) ->
+ begin
+ let kid3 = Env.fresh_kid env in
+ let (prop1, prop2) = props_subst kid1 (Nexp_var kid3) prop1, props_subst kid2 (Nexp_var kid3) prop2 in
+ let constr = Constraint.conj (nc_constraints var_of (Env.get_constraints env)) (Constraint.conj (pos_props prop1) (neg_props prop2)) in
+ match Constraint.call_z3 constr with
+ | Constraint.Unsat _ -> typ_debug "unsat"; true
+ | Constraint.Unknown [] -> typ_debug "sat"; false
+ | Constraint.Unknown _ -> typ_debug "unknown"; false
+ end
+ | _, _ -> false
+
+and tnf_args_eq env arg1 arg2 =
+ match arg1, arg2 with
+ | Tnf_arg_nexp n1, Tnf_arg_nexp n2 -> prove env (NC_aux (NC_fixed (n1, n2), Parse_ast.Unknown))
+ | Tnf_arg_order ord1, Tnf_arg_order ord2 -> order_eq ord1 ord2
+ | Tnf_arg_typ tnf1, Tnf_arg_typ tnf2 -> subtyp_tnf env tnf1 tnf2 && subtyp_tnf env tnf2 tnf1
+ | _, _ -> assert false
+
+let subtyp l env typ1 typ2 =
+ if subtyp_tnf env (normalize_typ env typ1) (normalize_typ env typ2)
+ then ()
+ else typ_error l (string_of_typ typ1
+ ^ " is not a subtype of " ^ string_of_typ typ2
+ ^ " in context " ^ string_of_list ", " string_of_n_constraint (Env.get_constraints env))
+
+let typ_equality l env typ1 typ2 =
+ subtyp l env typ1 typ2; subtyp l env typ2 typ1
+
+(**************************************************************************)
+(* 4. Unification *)
+(**************************************************************************)
+
+let rec nexp_frees (Nexp_aux (nexp, l)) =
+ match nexp with
+ | Nexp_id _ -> typ_error l "Unimplemented Nexp_id in nexp_frees"
+ | Nexp_var kid -> KidSet.singleton kid
+ | Nexp_constant _ -> KidSet.empty
+ | Nexp_times (n1, n2) -> KidSet.union (nexp_frees n1) (nexp_frees n2)
+ | Nexp_sum (n1, n2) -> KidSet.union (nexp_frees n1) (nexp_frees n2)
+ | Nexp_minus (n1, n2) -> KidSet.union (nexp_frees n1) (nexp_frees n2)
+ | Nexp_exp n -> nexp_frees n
+ | Nexp_neg n -> nexp_frees n
+
+let rec nexp_identical (Nexp_aux (nexp1, _)) (Nexp_aux (nexp2, _)) =
+ match nexp1, nexp2 with
+ | Nexp_id v1, Nexp_id v2 -> Id.compare v1 v2 = 0
+ | Nexp_var kid1, Nexp_var kid2 -> Kid.compare kid1 kid2 = 0
+ | Nexp_constant c1, Nexp_constant c2 -> c1 = c2
+ | Nexp_times (n1a, n1b), Nexp_times (n2a, n2b) -> nexp_identical n1a n2a && nexp_identical n1b n2b
+ | Nexp_sum (n1a, n1b), Nexp_sum (n2a, n2b) -> nexp_identical n1a n2a && nexp_identical n1b n2b
+ | Nexp_minus (n1a, n1b), Nexp_minus (n2a, n2b) -> nexp_identical n1a n2a && nexp_identical n1b n2b
+ | Nexp_exp n1, Nexp_exp n2 -> nexp_identical n1 n2
+ | Nexp_neg n1, Nexp_neg n2 -> nexp_identical n1 n2
+ | _, _ -> false
+
+type uvar =
+ | U_nexp of nexp
+ | U_order of order
+ | U_effect of effect
+ | U_typ of typ
+
+exception Unification_error of l * string;;
+
+let unify_error l str = raise (Unification_error (l, str))
+
+let rec unify_nexps l (Nexp_aux (nexp_aux1, _) as nexp1) (Nexp_aux (nexp_aux2, _) as nexp2) =
+ typ_debug ("UNIFYING NEXPS " ^ string_of_nexp nexp1 ^ " AND " ^ string_of_nexp nexp2);
+ match nexp_aux1 with
+ | Nexp_id v -> unify_error l "Unimplemented Nexp_id in unify nexp"
+ | Nexp_var kid -> Some (kid, nexp2)
+ | Nexp_constant c1 ->
+ begin
+ match nexp_aux2 with
+ | Nexp_constant c2 -> if c1 = c2 then None else unify_error l "Constants are not the same"
+ | _ -> unify_error l "Unification error"
+ end
+ | Nexp_sum (n1a, n1b) ->
+ if KidSet.is_empty (nexp_frees n1b)
+ then unify_nexps l n1a (nminus nexp2 n1b)
+ else
+ if KidSet.is_empty (nexp_frees n1a)
+ then unify_nexps l n1b (nminus nexp2 n1a)
+ else unify_error l ("Both sides of Nat expression " ^ string_of_nexp nexp1
+ ^ " contain free type variables so it cannot be unified with " ^ string_of_nexp nexp2)
+ | Nexp_minus (n1a, n1b) ->
+ if KidSet.is_empty (nexp_frees n1b)
+ then unify_nexps l n1a (nsum nexp2 n1b)
+ else unify_error l ("Cannot unify minus Nat expression " ^ string_of_nexp nexp1 ^ " with " ^ string_of_nexp nexp2)
+
+ | _ -> unify_error l ("Cannot unify Nat expression " ^ string_of_nexp nexp1 ^ " with " ^ string_of_nexp nexp2)
+
+let string_of_uvar = function
+ | U_nexp n -> string_of_nexp n
+ | U_order o -> string_of_order o
+ | U_effect eff -> string_of_effect eff
+ | U_typ typ -> string_of_typ typ
+
+let unify_order l (Ord_aux (ord_aux1, _) as ord1) (Ord_aux (ord_aux2, _) as ord2) =
+ typ_debug ("UNIFYING ORDERS " ^ string_of_order ord1 ^ " AND " ^ string_of_order ord2);
+ match ord_aux1, ord_aux2 with
+ | Ord_var kid, _ -> KBindings.singleton kid (U_order ord2)
+ | Ord_inc, Ord_inc -> KBindings.empty
+ | Ord_dec, Ord_dec -> KBindings.empty
+ | _, _ -> unify_error l (string_of_order ord1 ^ " cannot be unified with " ^ string_of_order ord2)
+
+let subst_unifiers unifiers typ =
+ let subst_unifier typ (kid, uvar) =
+ match uvar with
+ | U_nexp nexp -> typ_subst_nexp kid (unaux_nexp nexp) typ
+ | U_order ord -> typ_subst_order kid (unaux_order ord) typ
+ | U_typ subst -> typ_subst_typ kid (unaux_typ subst) typ
+ | _ -> typ_error Parse_ast.Unknown "Cannot subst unifier"
+ in
+ List.fold_left subst_unifier typ (KBindings.bindings unifiers)
+
+let subst_args_unifiers unifiers typ_args =
+ let subst_unifier typ_args (kid, uvar) =
+ match uvar with
+ | U_nexp nexp -> List.map (typ_subst_arg_nexp kid (unaux_nexp nexp)) typ_args
+ | U_order ord -> List.map (typ_subst_arg_order kid (unaux_order ord)) typ_args
+ | U_typ subst -> List.map (typ_subst_arg_typ kid (unaux_typ subst)) typ_args
+ | _ -> typ_error Parse_ast.Unknown "Cannot subst unifier"
+ in
+ List.fold_left subst_unifier typ_args (KBindings.bindings unifiers)
+
+let unify l env typ1 typ2 =
+ let merge_unifiers l kid uvar1 uvar2 =
+ match uvar1, uvar2 with
+ | Some (U_nexp n1), Some (U_nexp n2) ->
+ if nexp_identical n1 n2 then Some (U_nexp n1)
+ else unify_error l ("Multiple non-identical unifiers for " ^ string_of_kid kid
+ ^ ": " ^ string_of_nexp n1 ^ " and " ^ string_of_nexp n2)
+ | Some _, Some _ -> unify_error l "Multiple non-identical non-nexp unifiers"
+ | None, Some u2 -> Some u2
+ | Some u1, None -> Some u1
+ | None, None -> None
+ in
+ let rec unify_typ l (Typ_aux (typ1_aux, _) as typ1) (Typ_aux (typ2_aux, _) as typ2) =
+ typ_debug ("UNIFYING TYPES " ^ string_of_typ typ1 ^ " AND " ^ string_of_typ typ2);
+ match typ1_aux, typ2_aux with
+ | Typ_wild, Typ_wild -> KBindings.empty
+ | Typ_id v1, Typ_id v2 ->
+ if Id.compare v1 v2 = 0 then KBindings.empty
+ else unify_error l (string_of_typ typ1 ^ " cannot be unified with " ^ string_of_typ typ2)
+ | Typ_var kid, _ -> KBindings.singleton kid (U_typ typ2)
+ | Typ_tup typs1, Typ_tup typs2 ->
+ begin
+ try List.fold_left (KBindings.merge (merge_unifiers l)) KBindings.empty (List.map2 (unify_typ l) typs1 typs2) with
+ | Invalid_argument _ -> unify_error l (string_of_typ typ1 ^ " cannot be unified with " ^ string_of_typ typ2
+ ^ " tuple type is of different length")
+ end
+ | Typ_app (f1, args1), Typ_app (f2, args2) when Id.compare f1 f2 = 0 ->
+ unify_typ_arg_list 0 KBindings.empty [] [] args1 args2
+ | _, _ -> unify_error l (string_of_typ typ1 ^ " cannot be unified with " ^ string_of_typ typ2)
+
+ and unify_typ_arg_list unified acc uargs1 uargs2 args1 args2 =
+ match args1, args2 with
+ | [], [] when unified = 0 && List.length uargs1 > 0 ->
+ unify_error l "Could not unify arg lists" (*FIXME improve error *)
+ | [], [] when unified > 0 && List.length uargs1 > 0 -> unify_typ_arg_list 0 acc [] [] uargs1 uargs2
+ | [], [] when List.length uargs1 = 0 -> acc
+ | (a1 :: a1s), (a2 :: a2s) ->
+ begin
+ let unifiers, success =
+ try unify_typ_args l a1 a2, true with
+ | Unification_error _ -> KBindings.empty, false
+ in
+ let a1s = subst_args_unifiers unifiers a1s in
+ let a2s = subst_args_unifiers unifiers a2s in
+ let uargs1 = subst_args_unifiers unifiers uargs1 in
+ let uargs2 = subst_args_unifiers unifiers uargs2 in
+ if success
+ then unify_typ_arg_list (unified + 1) (KBindings.merge (merge_unifiers l) unifiers acc) uargs1 uargs2 a1s a2s
+ else unify_typ_arg_list unified acc (a1 :: uargs1) (a2 :: uargs2) a1s a2s
+ end
+ | _, _ -> unify_error l "Cannot unify type lists of different length"
+
+ and unify_typ_args l (Typ_arg_aux (typ_arg_aux1, _) as typ_arg1) (Typ_arg_aux (typ_arg_aux2, _) as typ_arg2) =
+ match typ_arg_aux1, typ_arg_aux2 with
+ | Typ_arg_nexp n1, Typ_arg_nexp n2 ->
+ begin
+ match unify_nexps l (nexp_simp n1) (nexp_simp n2) with
+ | Some (kid, unifier) -> KBindings.singleton kid (U_nexp unifier)
+ | None -> KBindings.empty
+ end
+ | Typ_arg_typ typ1, Typ_arg_typ typ2 -> unify_typ l typ1 typ2
+ | Typ_arg_order ord1, Typ_arg_order ord2 -> unify_order l ord1 ord2
+ | Typ_arg_effect _, Typ_arg_effect _ -> assert false
+ | _, _ -> unify_error l (string_of_typ_arg typ_arg1 ^ " cannot be unified with type argument " ^ string_of_typ_arg typ_arg2)
+ in
+ let typ1, typ2 = Env.expand_synonyms env typ1, Env.expand_synonyms env typ2 in
+ unify_typ l typ1 typ2
+
+(**************************************************************************)
+(* 5. Type checking expressions *)
+(**************************************************************************)
+
+(* The type checker produces a fully annoted AST - tannot is the type
+ of these type annotations. *)
+type tannot = (Env.t * typ * effect) option
+
+let infer_lit env (L_aux (lit_aux, l) as lit) =
+ match lit_aux with
+ | L_unit -> mk_typ (Typ_id (mk_id "unit"))
+ | L_zero -> mk_typ (Typ_id (mk_id "bit"))
+ | L_one -> mk_typ (Typ_id (mk_id "bit"))
+ | L_num n -> mk_typ (Typ_app (mk_id "atom", [mk_typ_arg (Typ_arg_nexp (nconstant n))]))
+ | L_true -> mk_typ (Typ_id (mk_id "bool"))
+ | L_false -> mk_typ (Typ_id (mk_id "bool"))
+ | L_string _ -> mk_typ (Typ_id (mk_id "string"))
+ | L_bin str ->
+ begin
+ match Env.get_default_order env with
+ | Ord_aux (Ord_inc, _) ->
+ dvector_typ env (nconstant 0) (nconstant (String.length str)) (mk_typ (Typ_id (mk_id "bit")))
+ | Ord_aux (Ord_dec, _) ->
+ dvector_typ env
+ (nconstant (String.length str - 1))
+ (nconstant (String.length str))
+ (mk_typ (Typ_id (mk_id "bit")))
+ end
+ | L_hex str ->
+ begin
+ match Env.get_default_order env with
+ | Ord_aux (Ord_inc, _) ->
+ dvector_typ env (nconstant 0) (nconstant (String.length str * 4)) (mk_typ (Typ_id (mk_id "bit")))
+ | Ord_aux (Ord_dec, _) ->
+ dvector_typ env
+ (nconstant (String.length str * 4 - 1))
+ (nconstant (String.length str * 4))
+ (mk_typ (Typ_id (mk_id "bit")))
+ end
+ | L_undef -> typ_error l "Cannot infer the type of undefined"
+
+let quant_items : typquant -> quant_item list = function
+ | TypQ_aux (TypQ_tq qis, _) -> qis
+ | TypQ_aux (TypQ_no_forall, _) -> []
+
+let is_nat_kid kid = function
+ | KOpt_aux (KOpt_none kid', _) -> Kid.compare kid kid' = 0
+ | KOpt_aux (KOpt_kind (K_aux (K_kind [BK_aux (BK_nat, _)], _), kid'), _) -> Kid.compare kid kid' = 0
+ | _ -> false
+
+let is_order_kid kid = function
+ | KOpt_aux (KOpt_kind (K_aux (K_kind [BK_aux (BK_order, _)], _), kid'), _) -> Kid.compare kid kid' = 0
+ | _ -> false
+
+let is_typ_kid kid = function
+ | KOpt_aux (KOpt_kind (K_aux (K_kind [BK_aux (BK_type, _)], _), kid'), _) -> Kid.compare kid kid' = 0
+ | _ -> false
+
+let rec instantiate_quants quants kid uvar = match quants with
+ | [] -> []
+ | ((QI_aux (QI_id kinded_id, _) as quant) :: quants) ->
+ begin
+ match uvar with
+ | U_nexp nexp ->
+ if is_nat_kid kid kinded_id
+ then instantiate_quants quants kid uvar
+ else quant :: instantiate_quants quants kid uvar
+ | U_order ord ->
+ if is_order_kid kid kinded_id
+ then instantiate_quants quants kid uvar
+ else quant :: instantiate_quants quants kid uvar
+ | U_typ typ ->
+ if is_typ_kid kid kinded_id
+ then instantiate_quants quants kid uvar
+ else quant :: instantiate_quants quants kid uvar
+ | _ -> typ_error Parse_ast.Unknown "Cannot instantiate quantifier"
+ end
+ | ((QI_aux (QI_const nc, l)) :: quants) ->
+ begin
+ match uvar with
+ | U_nexp nexp ->
+ QI_aux (QI_const (nc_subst_nexp kid (unaux_nexp nexp) nc), l) :: instantiate_quants quants kid uvar
+ | _ -> (QI_aux (QI_const nc, l)) :: instantiate_quants quants kid uvar
+ end
+
+let destructure_vec_typ l = function
+ | Typ_aux (Typ_app (id, [Typ_arg_aux (Typ_arg_nexp n1, _);
+ Typ_arg_aux (Typ_arg_nexp n2, _);
+ Typ_arg_aux (Typ_arg_order o, _);
+ Typ_arg_aux (Typ_arg_typ vtyp, _)]
+ ), _) when string_of_id id = "vector" -> (n1, n2, o, vtyp)
+ | typ -> typ_error l ("Expected vector type, got " ^ string_of_typ typ)
+
+let typ_of (E_aux (_, (_, tannot))) = match tannot with
+ | Some (_, typ, _) -> typ
+ | None -> assert false
+
+let pat_typ_of (P_aux (_, (_, tannot))) = match tannot with
+ | Some (_, typ, _) -> typ
+ | None -> assert false
+
+(* Flow typing *)
+
+let destructure_atom (Typ_aux (typ_aux, _)) =
+ match typ_aux with
+ | Typ_app (f, [Typ_arg_aux (Typ_arg_nexp (Nexp_aux (Nexp_constant c, _)), _)])
+ when string_of_id f = "atom" -> c
+ | Typ_app (f, [Typ_arg_aux (Typ_arg_nexp (Nexp_aux (Nexp_constant c1, _)), _); Typ_arg_aux (Typ_arg_nexp (Nexp_aux (Nexp_constant c2, _)), _)])
+ when string_of_id f = "range" && c1 = c2 -> c1
+ | _ -> assert false
+
+let restrict_range_upper c1 (Typ_aux (typ_aux, l) as typ) =
+ match typ_aux with
+ | Typ_app (f, [Typ_arg_aux (Typ_arg_nexp nexp, _); Typ_arg_aux (Typ_arg_nexp (Nexp_aux (Nexp_constant c2, _)), _)])
+ when string_of_id f = "range" ->
+ range_typ nexp (nconstant (min c1 c2))
+ | _ -> typ
+
+let restrict_range_lower c1 (Typ_aux (typ_aux, l) as typ) =
+ match typ_aux with
+ | Typ_app (f, [Typ_arg_aux (Typ_arg_nexp (Nexp_aux (Nexp_constant c2, _)), _); Typ_arg_aux (Typ_arg_nexp nexp, _)])
+ when string_of_id f = "range" ->
+ range_typ (nconstant (max c1 c2)) nexp
+ | _ -> typ
+
+type flow_constraint =
+ | Flow_lteq of int
+ | Flow_gteq of int
+
+let apply_flow_constraint = function
+ | Flow_lteq c -> (restrict_range_upper c, restrict_range_lower (c + 1))
+ | Flow_gteq c -> (restrict_range_lower c, restrict_range_upper (c - 1))
+
+let rec infer_flow env (E_aux (exp_aux, (l, _))) =
+ match exp_aux with
+ | E_app (f, [E_aux (E_id v, _); y]) when string_of_id f = "lt_range_atom" ->
+ let kid = Env.fresh_kid env in
+ let c = destructure_atom (typ_of y) in
+ [(v, Flow_lteq (c - 1))]
+ | E_app (f, [E_aux (E_id v, _); y]) when string_of_id f = "lteq_range_atom" ->
+ let kid = Env.fresh_kid env in
+ let c = destructure_atom (typ_of y) in
+ [(v, Flow_lteq c)]
+ | E_app (f, [E_aux (E_id v, _); y]) when string_of_id f = "gt_range_atom" ->
+ let kid = Env.fresh_kid env in
+ let c = destructure_atom (typ_of y) in
+ [(v, Flow_gteq (c + 1))]
+ | E_app (f, [E_aux (E_id v, _); y]) when string_of_id f = "gteq_range_atom" ->
+ let kid = Env.fresh_kid env in
+ let c = destructure_atom (typ_of y) in
+ [(v, Flow_gteq c)]
+ | _ -> []
+
+let rec add_flows b flows env =
+ match flows with
+ | [] -> env
+ | (id, flow) :: flows when b -> add_flows true flows (Env.add_flow id (fst (apply_flow_constraint flow)) env)
+ | (id, flow) :: flows -> add_flows false flows (Env.add_flow id (snd (apply_flow_constraint flow)) env)
+
+let crule r env exp typ =
+ incr depth;
+ typ_print ("Check " ^ string_of_exp exp ^ " <= " ^ string_of_typ typ);
+ try
+ let checked_exp = r env exp typ in
+ decr depth; checked_exp
+ with
+ | Type_error (l, m) -> decr depth; typ_error l m
+
+let irule r env exp =
+ incr depth;
+ try
+ let inferred_exp = r env exp in
+ typ_print ("Infer " ^ string_of_exp exp ^ " => " ^ string_of_typ (typ_of inferred_exp));
+ decr depth;
+ inferred_exp
+ with
+ | Type_error (l, m) -> decr depth; typ_error l m
+
+let strip_exp : 'a exp -> unit exp = function exp -> map_exp_annot (fun (l, _) -> (l, ())) exp
+let strip_pat : 'a pat -> unit pat = function pat -> map_pat_annot (fun (l, _) -> (l, ())) pat
+
+let rec check_exp env (E_aux (exp_aux, (l, ())) as exp : unit exp) (Typ_aux (typ_aux, _) as typ) : tannot exp =
+ let annot_exp_effect exp typ eff = E_aux (exp, (l, Some (env, typ, eff))) in
+ let annot_exp exp typ = annot_exp_effect exp typ no_effect in
+ match (exp_aux, typ_aux) with
+ | E_block exps, _ ->
+ begin
+ let rec check_block l env exps typ = match exps with
+ | [] -> typ_error l "Empty block found"
+ | [exp] -> [crule check_exp env exp typ]
+ | (E_aux (E_assign (lexp, bind), _) :: exps) ->
+ let texp, env = bind_assignment env lexp bind in
+ texp :: check_block l env exps typ
+ | (exp :: exps) ->
+ let texp = crule check_exp env exp (mk_typ (Typ_id (mk_id "unit"))) in
+ texp :: check_block l env exps typ
+ in
+ annot_exp (E_block (check_block l env exps typ)) typ
+ end
+ | E_case (exp, cases), _ ->
+ let inferred_exp = irule infer_exp env exp in
+ let check_case (Pat_aux (Pat_exp (pat, case), (l, _))) typ =
+ let tpat, env = bind_pat env pat (typ_of inferred_exp) in
+ Pat_aux (Pat_exp (tpat, crule check_exp env case typ), (l, None))
+ in
+ annot_exp (E_case (inferred_exp, List.map (fun case -> check_case case typ) cases)) typ
+ | E_let (LB_aux (letbind, (let_loc, _)), exp), _ ->
+ begin
+ match letbind with
+ | LB_val_explicit (typschm, pat, bind) -> assert false
+ | LB_val_implicit (pat, bind) ->
+ let inferred_bind = irule infer_exp env bind in
+ let tpat, env = bind_pat env pat (typ_of inferred_bind) in
+ annot_exp (E_let (LB_aux (LB_val_implicit (tpat, inferred_bind), (let_loc, None)), crule check_exp env exp typ)) typ
+ end
+ | E_app_infix (x, op, y), _ when List.length (Env.get_overloads (deinfix op) env) > 0 -> check_exp env (E_aux (E_app (deinfix op, [x; y]), (l, ()))) typ
+ | E_app (f, xs), _ when List.length (Env.get_overloads f env) > 0 ->
+ let rec try_overload m1 = function
+ | [] -> typ_error l (m1 ^ "\nNo valid overloading for " ^ string_of_exp exp)
+ | (f :: fs) -> begin
+ typ_print ("Overload: " ^ string_of_id f ^ "(" ^ string_of_list ", " string_of_exp xs ^ ")");
+ try crule check_exp env (E_aux (E_app (f, xs), (l, ()))) typ with
+ | Type_error (_, m2) -> try_overload (m1 ^ "\nand " ^ m2) fs
+ end
+ in
+ try_overload "Overloading error" (Env.get_overloads f env)
+ | E_app (f, xs), _ ->
+ let inferred_exp = infer_funapp l env f xs (Some typ) in
+ type_coercion env inferred_exp typ
+ | E_if (cond, then_branch, else_branch), _ ->
+ let cond' = crule check_exp env cond (mk_typ (Typ_id (mk_id "bool"))) in
+ let flows = infer_flow env cond' in
+ let then_branch' = crule check_exp (add_flows true flows env) then_branch typ in
+ let else_branch' = crule check_exp (add_flows false flows env) else_branch typ in
+ annot_exp (E_if (cond', then_branch', else_branch')) typ
+ | E_exit exp, _ ->
+ let checked_exp = crule check_exp env exp (mk_typ (Typ_id (mk_id "unit"))) in
+ annot_exp_effect (E_exit checked_exp) typ (mk_effect [BE_escape])
+ | E_vector vec, _ ->
+ begin
+ let (start, len, ord, vtyp) = destructure_vec_typ l typ in
+ let checked_items = List.map (fun i -> crule check_exp env i vtyp) vec in
+ match len with
+ | Nexp_aux (Nexp_constant lenc, _) ->
+ if List.length vec = lenc then annot_exp (E_vector checked_items) typ
+ else typ_error l "List length didn't match" (* FIXME: improve error message *)
+ | _ -> typ_error l "Cannot check list constant against non-constant length vector type"
+ end
+ | E_lit (L_aux (L_undef, _) as lit), _ ->
+ annot_exp_effect (E_lit lit) typ (mk_effect [BE_undef])
+ (* This rule allows registers of type t to be passed by name with type register<t>*)
+ | E_id reg, Typ_app (id, [Typ_arg_aux (Typ_arg_typ typ, _)]) when string_of_id id = "register" ->
+ let rtyp = Env.get_register reg env in
+ subtyp l env rtyp typ; annot_exp (E_id reg) typ (* CHECK: is this subtyp the correct way around? *)
+ | _, _ ->
+ let inferred_exp = irule infer_exp env exp in
+ type_coercion env inferred_exp typ
+
+(* type_coercion env exp typ takes a fully annoted (i.e. already type
+ checked) expression exp, and attempts to cast (coerce) it to the
+ type typ by inserting a coercion function that transforms the
+ annotated expression into the correct type. Returns an annoted
+ expression consisting of a type coercion function applied to exp,
+ or throws a type error if the coercion cannot be performed. *)
+and type_coercion env (E_aux (_, (l, _)) as annotated_exp) typ =
+ let strip exp_aux = strip_exp (E_aux (exp_aux, (Parse_ast.Unknown, None))) in
+ let annot_exp exp typ = E_aux (exp, (l, Some (env, typ, no_effect))) in
+ let rec try_casts m = function
+ | [] -> typ_error l ("No valid casts:\n" ^ m)
+ | (cast :: casts) -> begin
+ typ_print ("Casting with " ^ string_of_id cast ^ " expression " ^ string_of_exp annotated_exp ^ " to " ^ string_of_typ typ);
+ try
+ let checked_cast = crule check_exp (Env.no_casts env) (strip (E_app (cast, [annotated_exp]))) typ in
+ annot_exp (E_cast (typ, checked_cast)) typ
+ with
+ | Type_error (_, m) -> try_casts m casts
+ end
+ in
+ begin
+ try
+ typ_debug "PERFORMING TYPE COERCION";
+ subtyp l env (typ_of annotated_exp) typ; annotated_exp
+ with
+ | Type_error (_, m) when Env.allow_casts env -> try_casts "" (Env.get_casts env)
+ | Type_error (l, m) -> typ_error l ("Subtype error " ^ m)
+ end
+
+(* type_coercion_unify env exp typ attempts to coerce exp to a type
+ exp_typ in the same way as type_coercion, except it is only
+ required that exp_typ unifies with typ. Returns the annotated
+ coercion as with type_coercion and also a set of unifiers, or
+ throws a unification error *)
+and type_coercion_unify env (E_aux (_, (l, _)) as annotated_exp) typ =
+ let strip exp_aux = strip_exp (E_aux (exp_aux, (Parse_ast.Unknown, None))) in
+ let annot_exp exp typ = E_aux (exp, (l, Some (env, typ, no_effect))) in
+ let rec try_casts m = function
+ | [] -> unify_error l ("No valid casts resulted in unification:\n" ^ m)
+ | (cast :: casts) -> begin
+ typ_print ("Casting with " ^ string_of_id cast ^ " expression " ^ string_of_exp annotated_exp ^ " for unification");
+ try
+ let inferred_cast = irule infer_exp (Env.no_casts env) (strip (E_app (cast, [annotated_exp]))) in
+ let ityp = typ_of inferred_cast in
+ annot_exp (E_cast (ityp, inferred_cast)) ityp, unify l env typ ityp
+ with
+ | Type_error (_, m) -> try_casts m casts
+ | Unification_error (_, m) -> try_casts m casts
+ end
+ in
+ begin
+ try
+ typ_debug "PERFORMING COERCING UNIFICATION";
+ annotated_exp, unify l env typ (typ_of annotated_exp)
+ with
+ | Unification_error (_, m) when Env.allow_casts env -> try_casts "" (Env.get_casts env)
+ end
+
+and bind_pat env (P_aux (pat_aux, (l, ())) as pat) (Typ_aux (typ_aux, _) as typ) =
+ let annot_pat pat typ = P_aux (pat, (l, Some (env, typ, no_effect))) in
+ let switch_typ (P_aux (pat_aux, (l, Some (env, _, eff)))) typ = P_aux (pat_aux, (l, Some (env, typ, eff))) in
+ match pat_aux with
+ | P_id v ->
+ begin
+ match Env.lookup_id v env with
+ | Local (Immutable, _) | Unbound -> annot_pat (P_id v) typ, Env.add_local v (Immutable, typ) env
+ | Local (Mutable, _) | Register _ ->
+ typ_error l ("Cannot shadow mutable local or register in switch statement pattern " ^ string_of_pat pat)
+ | Enum enum -> subtyp l env enum typ; annot_pat (P_id v) typ, env
+ end
+ | P_wild -> annot_pat P_wild typ, env
+ | P_tup pats ->
+ begin
+ match typ_aux with
+ | Typ_tup typs ->
+ let bind_tuple_pat (tpats, env) pat typ =
+ let tpat, env = bind_pat env pat typ in tpat :: tpats, env
+ in
+ let tpats, env =
+ try List.fold_left2 bind_tuple_pat ([], env) pats typs with
+ | Invalid_argument _ -> typ_error l "Tuple pattern and tuple type have different length"
+ in
+ annot_pat (P_tup (List.rev tpats)) typ, env
+ | _ -> typ_error l "Cannot bind tuple pattern against non tuple type"
+ end
+ | _ ->
+ let (inferred_pat, env) = infer_pat env pat in
+ subtyp l env (pat_typ_of inferred_pat) typ;
+ switch_typ inferred_pat typ, env
+
+and infer_pat env (P_aux (pat_aux, (l, ())) as pat) =
+ let annot_pat pat typ = P_aux (pat, (l, Some (env, typ, no_effect))) in
+ match pat_aux with
+ | P_id v ->
+ begin
+ match Env.lookup_id v env with
+ | Local (Immutable, _) | Unbound ->
+ typ_error l ("Cannot infer identifier in pattern " ^ string_of_pat pat ^ " - try adding a type annotation")
+ | Local (Mutable, _) | Register _ ->
+ typ_error l ("Cannot shadow mutable local or register in switch statement pattern " ^ string_of_pat pat)
+ | Enum enum -> annot_pat (P_id v) enum, env
+ end
+ | P_typ (typ_annot, pat) ->
+ let (typed_pat, env) = bind_pat env pat typ_annot in
+ annot_pat (P_typ (typ_annot, typed_pat)) typ_annot, env
+ | P_lit lit ->
+ annot_pat (P_lit lit) (infer_lit env lit), env
+ | P_vector_concat (pat :: pats) ->
+ let fold_pats (pats, env) pat =
+ let inferred_pat, env = infer_pat env pat in
+ pats @ [inferred_pat], env
+ in
+ let (inferred_pat :: inferred_pats), env = List.fold_left fold_pats ([], env) (pat :: pats) in
+ let (_, len, _, vtyp) = destructure_vec_typ l (pat_typ_of inferred_pat) in
+ let fold_len len pat =
+ let (_, len', _, vtyp') = destructure_vec_typ l (pat_typ_of pat) in
+ typ_equality l env vtyp vtyp';
+ nsum len len'
+ in
+ let len = nexp_simp (List.fold_left fold_len len inferred_pats) in
+ annot_pat (P_vector_concat (inferred_pat :: inferred_pats)) (lvector_typ env len vtyp), env
+ | _ -> typ_error l ("Couldn't infer type of pattern " ^ string_of_pat pat)
+
+and bind_assignment env (LEXP_aux (lexp_aux, _) as lexp) (E_aux (_, (l, ())) as exp) =
+ let annot_assign lexp exp = E_aux (E_assign (lexp, exp), (l, Some (env, mk_typ (Typ_id (mk_id "unit")), no_effect))) in
+ let annot_lexp_effect lexp typ eff = LEXP_aux (lexp, (l, Some (env, typ, eff))) in
+ let annot_lexp lexp typ = annot_lexp_effect lexp typ no_effect in
+ match lexp_aux with
+ | LEXP_field (LEXP_aux (LEXP_id v, _), field) ->
+ begin
+ let regtyp = match Env.lookup_id v env with
+ | Register typ -> typ
+ | _ -> typ_error l "l-expression field is not a register"
+ in
+ match Env.expand_synonyms env regtyp with
+ | Typ_aux (Typ_id regtyp_id, _) when Env.is_regtyp regtyp_id env ->
+ let base, top, ranges = Env.get_regtyp regtyp_id env in
+ let range, _ =
+ try List.find (fun (_, id) -> Id.compare id field = 0) ranges with
+ | Not_found -> typ_error l ("Field " ^ string_of_id field ^ " doesn't exist for register type " ^ string_of_id regtyp_id)
+ in
+ let vec_typ = match range, Env.get_default_order env with
+ | BF_aux (BF_single n, _), Ord_aux (Ord_dec, _) ->
+ dvector_typ env (nconstant n) (nconstant 1) (mk_typ (Typ_id (mk_id "bit")))
+ | BF_aux (BF_range (n, m), _), Ord_aux (Ord_dec, _) ->
+ dvector_typ env (nconstant n) (nconstant (n - m + 1)) (mk_typ (Typ_id (mk_id "bit")))
+ | _, _ -> typ_error l "Not implemented this register field type yet..."
+ in
+ let checked_exp = crule check_exp env exp vec_typ in
+ annot_assign (annot_lexp (LEXP_field (annot_lexp_effect (LEXP_id v) regtyp (mk_effect [BE_wreg]), field)) vec_typ) checked_exp, env
+ | _ -> typ_error l "Field l-expression has invalid type"
+ end
+ | _ ->
+ let inferred_exp = irule infer_exp env exp in
+ let tlexp, env' = bind_lexp env lexp (typ_of inferred_exp) in
+ annot_assign tlexp inferred_exp, env'
+
+and bind_lexp env (LEXP_aux (lexp_aux, (l, ())) as lexp) typ =
+ let annot_lexp_effect lexp typ eff = LEXP_aux (lexp, (l, Some (env, typ, eff))) in
+ let annot_lexp lexp typ = annot_lexp_effect lexp typ no_effect in
+ match lexp_aux with
+ | LEXP_id v ->
+ begin
+ match Env.lookup_id v env with
+ | Local (Immutable, _) | Enum _ ->
+ typ_error l ("Cannot modify let-bound constant or enumeration constructor " ^ string_of_id v)
+ | Local (Mutable, vtyp) -> subtyp l env typ vtyp; annot_lexp (LEXP_id v) typ, env
+ | Register vtyp -> subtyp l env typ vtyp; annot_lexp_effect (LEXP_id v) typ (mk_effect [BE_wreg]), env
+ | Unbound -> annot_lexp (LEXP_id v) typ, Env.add_local v (Mutable, typ) env
+ end
+ | LEXP_cast (typ_annot, v) ->
+ begin
+ match Env.lookup_id v env with
+ | Local (Immutable, _) | Enum _ ->
+ typ_error l ("Cannot modify let-bound constant or enumeration constructor " ^ string_of_id v)
+ | Local (Mutable, vtyp) ->
+ begin
+ subtyp l env typ typ_annot;
+ subtyp l env typ_annot vtyp;
+ annot_lexp (LEXP_cast (typ_annot, v)) typ, env
+ end
+ | Register vtyp ->
+ begin
+ subtyp l env typ typ_annot;
+ subtyp l env typ_annot vtyp;
+ annot_lexp_effect (LEXP_cast (typ_annot, v)) typ (mk_effect [BE_wreg]), env
+ end
+ | Unbound ->
+ begin
+ subtyp l env typ typ_annot;
+ annot_lexp (LEXP_cast (typ_annot, v)) typ, Env.add_local v (Mutable, typ_annot) env
+ end
+ end
+ | LEXP_tup lexps ->
+ begin
+ let (Typ_aux (typ_aux, _)) = typ in
+ match typ_aux with
+ | Typ_tup typs ->
+ let bind_tuple_lexp (tlexps, env) lexp typ =
+ let tlexp, env = bind_lexp env lexp typ in tlexp :: tlexps, env
+ in
+ let tlexps, env =
+ try List.fold_left2 bind_tuple_lexp ([], env) lexps typs with
+ | Invalid_argument _ -> typ_error l "Tuple l-expression and tuple type have different length"
+ in
+ annot_lexp (LEXP_tup tlexps) typ, env
+ | _ -> typ_error l "Cannot bind tuple l-expression against non tuple type"
+ end
+ (* Not sure about this case... can the left lexp be anything other than an identifier? *)
+ | LEXP_vector (LEXP_aux (LEXP_id v, _), exp) ->
+ begin
+ let is_immutable, vtyp = match Env.lookup_id v env with
+ | Unbound -> typ_error l "Cannot assign to element of unbound vector"
+ | Enum _ -> typ_error l "Cannot vector assign to enumeration element"
+ | Local (Immutable, vtyp) -> true, vtyp
+ | Local (Mutable, vtyp) | Register vtyp -> false, vtyp
+ in
+ let access = infer_exp env (E_aux (E_app (mk_id "vector_access", [E_aux (E_id v, (l, ())); exp]), (l, ()))) in
+ let E_aux (E_app (_, [_; inferred_exp]), _) = access in
+ match typ_of access with
+ | Typ_aux (Typ_app (id, [Typ_arg_aux (Typ_arg_typ deref_typ, _)]), _) when string_of_id id = "register" ->
+ subtyp l env typ deref_typ;
+ annot_lexp (LEXP_vector (annot_lexp_effect (LEXP_id v) vtyp (mk_effect [BE_wreg]), inferred_exp)) typ, env
+ | _ when not is_immutable ->
+ subtyp l env typ (typ_of access);
+ annot_lexp (LEXP_vector (annot_lexp (LEXP_id v) vtyp, inferred_exp)) typ, env
+ | _ -> typ_error l ("Bad vector assignment: " ^ string_of_lexp lexp)
+ end
+ | _ -> typ_error l ("Unhandled l-expression")
+
+and infer_exp env (E_aux (exp_aux, (l, ())) as exp) =
+ let annot_exp_effect exp typ eff = E_aux (exp, (l, Some (env, typ, eff))) in
+ let annot_exp exp typ = annot_exp_effect exp typ no_effect in
+ match exp_aux with
+ | E_nondet exps ->
+ annot_exp (E_nondet (List.map (fun exp -> check_exp env exp unit_typ) exps)) unit_typ
+ | E_id v ->
+ begin
+ match Env.lookup_id v env with
+ | Local (_, typ) | Enum typ -> annot_exp (E_id v) typ
+ | Register typ -> annot_exp_effect (E_id v) typ (mk_effect [BE_rreg])
+ | Unbound -> typ_error l ("Identifier " ^ string_of_id v ^ " is unbound")
+ end
+ | E_lit lit -> annot_exp (E_lit lit) (infer_lit env lit)
+ | E_sizeof nexp -> annot_exp (E_sizeof nexp) (mk_typ (Typ_app (mk_id "atom", [mk_typ_arg (Typ_arg_nexp nexp)])))
+ | E_return exp ->
+ begin
+ match Env.get_ret_typ env with
+ | Some typ -> annot_exp (E_return (crule check_exp env exp typ)) (mk_typ (Typ_id (mk_id "unit")))
+ | None -> typ_error l "Return found in non-function environment"
+ end
+ | E_field (exp, field) ->
+ begin
+ let inferred_exp = infer_exp env exp in
+ match Env.expand_synonyms env (typ_of inferred_exp) with
+ (* Accessing a (bit) field of a register *)
+ | Typ_aux (Typ_id regtyp, _) when Env.is_regtyp regtyp env ->
+ let base, top, ranges = Env.get_regtyp regtyp env in
+ let range, _ =
+ try List.find (fun (_, id) -> Id.compare id field = 0) ranges with
+ | Not_found -> typ_error l ("Field " ^ string_of_id field ^ " doesn't exist for register type " ^ string_of_id regtyp)
+ in
+ begin
+ match range, Env.get_default_order env with
+ | BF_aux (BF_single n, _), Ord_aux (Ord_dec, _) ->
+ let vec_typ = dvector_typ env (nconstant n) (nconstant 1) (mk_typ (Typ_id (mk_id "bit"))) in
+ annot_exp (E_field (inferred_exp, field)) vec_typ
+ | BF_aux (BF_range (n, m), _), Ord_aux (Ord_dec, _) ->
+ let vec_typ = dvector_typ env (nconstant n) (nconstant (n - m + 1)) (mk_typ (Typ_id (mk_id "bit"))) in
+ annot_exp (E_field (inferred_exp, field)) vec_typ
+ | _, _ -> typ_error l "Not implemented this register field type yet..." (* FIXME *)
+ end
+ (* Accessing a field of a record *)
+ | Typ_aux (Typ_id rectyp, _) as typ when Env.is_record rectyp env ->
+ begin
+ let inferred_acc = infer_funapp' l (Env.no_casts env) field (Env.get_accessor field env) [strip_exp inferred_exp] None in
+ match inferred_acc with
+ | E_aux (E_app (field, [inferred_exp]) ,_) -> annot_exp (E_field (inferred_exp, field)) (typ_of inferred_acc)
+ | _ -> assert false (* Unreachable *)
+ end
+ | _ -> typ_error l ("Field expression " ^ string_of_exp exp ^ " :: " ^ string_of_typ (typ_of inferred_exp) ^ " is not valid")
+ end
+ | E_tuple exps ->
+ let inferred_exps = List.map (irule infer_exp env) exps in
+ annot_exp (E_tuple inferred_exps) (mk_typ (Typ_tup (List.map typ_of inferred_exps)))
+ | E_assign (lexp, bind) ->
+ fst (bind_assignment env lexp bind)
+ | E_cast (typ, exp) ->
+ let checked_exp = crule check_exp env exp typ in
+ annot_exp (E_cast (typ, checked_exp)) typ
+ | E_app_infix (x, op, y) when List.length (Env.get_overloads (deinfix op) env) > 0 -> infer_exp env (E_aux (E_app (deinfix op, [x; y]), (l, ())))
+ | E_app (f, xs) when List.length (Env.get_overloads f env) > 0 ->
+ let rec try_overload m1 = function
+ | [] -> typ_error l (m1 ^ "\nNo valid overloading for " ^ string_of_exp exp)
+ | (f :: fs) -> begin
+ typ_print ("Overload: " ^ string_of_id f ^ "(" ^ string_of_list ", " string_of_exp xs ^ ")");
+ try irule infer_exp env (E_aux (E_app (f, xs), (l, ()))) with
+ | Type_error (_, m2) -> try_overload (m1 ^ "\nand " ^ m2) fs
+ end
+ in
+ try_overload "Overloading error" (Env.get_overloads f env)
+ | E_app (f, xs) -> infer_funapp l env f xs None
+ | E_if (cond, then_branch, else_branch) ->
+ let cond' = crule check_exp env cond (mk_typ (Typ_id (mk_id "bool"))) in
+ let flows = infer_flow env cond' in
+ let then_branch' = irule infer_exp (add_flows true flows env) then_branch in
+ let else_branch' = crule check_exp (add_flows false flows env) else_branch (typ_of then_branch') in
+ annot_exp (E_if (cond', then_branch', else_branch')) (typ_of then_branch')
+ | E_vector_access (v, n) -> infer_exp env (E_aux (E_app (mk_id "vector_access", [v; n]), (l, ())))
+ | E_vector_append (v1, v2) -> infer_exp env (E_aux (E_app (mk_id "vector_append", [v1; v2]), (l, ())))
+ | E_vector_subrange (v, n, m) -> infer_exp env (E_aux (E_app (mk_id "vector_subrange", [v; n; m]), (l, ())))
+ | E_vector [] -> typ_error l "Cannot infer type of empty vector"
+ | E_vector ((item :: items) as vec) ->
+ let inferred_item = irule infer_exp env item in
+ let checked_items = List.map (fun i -> crule check_exp env i (typ_of inferred_item)) items in
+ let vec_typ = match Env.get_default_order env with
+ | Ord_aux (Ord_inc, _) ->
+ mk_typ (Typ_app (mk_id "vector",
+ [mk_typ_arg (Typ_arg_nexp (nconstant 0));
+ mk_typ_arg (Typ_arg_nexp (nconstant (List.length vec)));
+ mk_typ_arg (Typ_arg_order (Env.get_default_order env));
+ mk_typ_arg (Typ_arg_typ (typ_of inferred_item))]))
+ | Ord_aux (Ord_dec, _) ->
+ mk_typ (Typ_app (mk_id "vector",
+ [mk_typ_arg (Typ_arg_nexp (nconstant (List.length vec - 1)));
+ mk_typ_arg (Typ_arg_nexp (nconstant (List.length vec)));
+ mk_typ_arg (Typ_arg_order (Env.get_default_order env));
+ mk_typ_arg (Typ_arg_typ (typ_of inferred_item))]))
+ in
+ annot_exp (E_vector (inferred_item :: checked_items)) vec_typ
+ | E_assert (test, msg) ->
+ let checked_test = check_exp env test bool_typ in
+ let checked_msg = check_exp env msg string_typ in
+ annot_exp (E_assert (checked_test, checked_msg)) unit_typ
+ | _ -> typ_error l ("Cannot infer type of: " ^ string_of_exp exp)
+
+and infer_funapp l env f xs ret_ctx_typ = infer_funapp' l env f (Env.get_val_spec f env) xs ret_ctx_typ
+
+and infer_funapp' l env f (typq, f_typ) xs ret_ctx_typ =
+ let annot_exp exp typ eff = E_aux (exp, (l, Some (env, typ, eff))) in
+ let rec number n = function
+ | [] -> []
+ | (x :: xs) -> (n, x) :: number (n + 1) xs
+ in
+ let solve_quant = function
+ | QI_aux (QI_id _, _) -> false
+ | QI_aux (QI_const nc, _) -> prove env nc
+ in
+ let rec instantiate quants typs ret_typ args =
+ match typs, args with
+ | (utyps, []), (uargs, []) ->
+ begin
+ typ_debug ("Got unresolved args: " ^ string_of_list ", " (fun (_, exp) -> string_of_exp exp) uargs);
+ if List.for_all solve_quant quants
+ then
+ let iuargs = List.map2 (fun utyp (n, uarg) -> (n, crule check_exp env uarg utyp)) utyps uargs in
+ (iuargs, ret_typ)
+ else typ_error l ("Quantifiers " ^ string_of_list ", " string_of_quant_item quants
+ ^ " not resolved during application of " ^ string_of_id f)
+ end
+ | (utyps, (typ :: typs)), (uargs, ((n, arg) :: args)) ->
+ begin
+ typ_debug ("INSTANTIATE: " ^ string_of_exp arg ^ " with " ^ string_of_typ typ ^ " NF " ^ string_of_tnf (normalize_typ env typ));
+ let iarg = irule infer_exp env arg in
+ typ_debug ("INFER: " ^ string_of_exp arg ^ " type " ^ string_of_typ (typ_of iarg) ^ " NF " ^ string_of_tnf (normalize_typ env (typ_of iarg)));
+ try
+ let iarg, unifiers = type_coercion_unify env iarg typ in
+ typ_debug (string_of_list ", " (fun (kid, uvar) -> string_of_kid kid ^ " => " ^ string_of_uvar uvar) (KBindings.bindings unifiers));
+ let utyps' = List.map (subst_unifiers unifiers) utyps in
+ let typs' = List.map (subst_unifiers unifiers) typs in
+ let quants' = List.fold_left (fun qs (kid, uvar) -> instantiate_quants qs kid uvar) quants (KBindings.bindings unifiers) in
+ let ret_typ' = subst_unifiers unifiers ret_typ in
+ let (iargs, ret_typ'') = instantiate quants' (utyps', typs') ret_typ' (uargs, args) in
+ ((n, iarg) :: iargs, ret_typ'')
+ with
+ | Unification_error (l, str) ->
+ typ_debug ("Unification error: " ^ str);
+ instantiate quants (typ :: utyps, typs) ret_typ ((n, arg) :: uargs, args)
+ end
+ | (_, []), _ -> typ_error l ("Function " ^ string_of_id f ^ " applied to too many arguments")
+ | _, (_, []) -> typ_error l ("Function " ^ string_of_id f ^ " not applied to enough arguments")
+ in
+ let instantiate_ret quants typs ret_typ =
+ match ret_ctx_typ with
+ | None -> (quants, typs, ret_typ)
+ | Some rct ->
+ begin
+ let unifiers = try unify l env ret_typ rct with Unification_error _ -> typ_debug "UERROR"; KBindings.empty in
+ typ_debug (string_of_list ", " (fun (kid, uvar) -> string_of_kid kid ^ " => " ^ string_of_uvar uvar) (KBindings.bindings unifiers));
+ let typs' = List.map (subst_unifiers unifiers) typs in
+ let quants' = List.fold_left (fun qs (kid, uvar) -> instantiate_quants qs kid uvar) quants (KBindings.bindings unifiers) in
+ let ret_typ' = subst_unifiers unifiers ret_typ in
+ (quants', typs', ret_typ')
+ end
+ in
+ match f_typ with
+ | Typ_aux (Typ_fn (Typ_aux (Typ_tup typ_args, _), typ_ret, eff), _) ->
+ let (quants, typ_args, typ_ret) = instantiate_ret (quant_items typq) typ_args typ_ret in
+ let (xs_instantiated, typ_ret) = instantiate quants ([], typ_args) typ_ret ([], number 0 xs) in
+ let xs_reordered = List.map snd (List.sort (fun (n, _) (m, _) -> compare n m) xs_instantiated) in
+ annot_exp (E_app (f, xs_reordered)) typ_ret eff
+ | Typ_aux (Typ_fn (typ_arg, typ_ret, eff), _) ->
+ let (quants, typ_args, typ_ret) = instantiate_ret (quant_items typq) [typ_arg] typ_ret in
+ let (xs_instantiated, typ_ret) = instantiate quants ([], typ_args) typ_ret ([], number 0 xs) in
+ let xs_reordered = List.map snd (List.sort (fun (n, _) (m, _) -> compare n m) xs_instantiated) in
+ annot_exp (E_app (f, xs_reordered)) typ_ret eff
+ | _ -> typ_error l (string_of_typ f_typ ^ " is not a function type")
+
+(**************************************************************************)
+(* 6. Effect system *)
+(**************************************************************************)
+
+let effect_of (E_aux (exp, (l, annot))) =
+ match annot with
+ | Some (_, _, eff) -> eff
+ | None -> no_effect
+
+let add_effect (E_aux (exp, (l, annot))) eff1 =
+ match annot with
+ | Some (env, typ, eff2) -> E_aux (exp, (l, Some (env, typ, union_effects eff1 eff2)))
+ | None -> assert false
+
+let effect_of_lexp (LEXP_aux (exp, (l, annot))) =
+ match annot with
+ | Some (_, _, eff) -> eff
+ | None -> no_effect
+
+let add_effect_lexp (LEXP_aux (lexp, (l, annot))) eff1 =
+ match annot with
+ | Some (env, typ, eff2) -> LEXP_aux (lexp, (l, Some (env, typ, union_effects eff1 eff2)))
+ | None -> assert false
+
+let effect_of_pat (P_aux (exp, (l, annot))) =
+ match annot with
+ | Some (_, _, eff) -> eff
+ | None -> no_effect
+
+let add_effect_pat (P_aux (pat, (l, annot))) eff1 =
+ match annot with
+ | Some (env, typ, eff2) -> P_aux (pat, (l, Some (env, typ, union_effects eff1 eff2)))
+ | None -> assert false
+
+let collect_effects xs = List.fold_left union_effects no_effect (List.map effect_of xs)
+
+let collect_effects_lexp xs = List.fold_left union_effects no_effect (List.map effect_of_lexp xs)
+
+let collect_effects_pat xs = List.fold_left union_effects no_effect (List.map effect_of_pat xs)
+
+(* Traversal that propagates effects upwards through expressions *)
+
+let rec propagate_exp_effect (E_aux (exp, annot)) =
+ let propagated_exp, eff = propagate_exp_effect_aux exp in
+ add_effect (E_aux (propagated_exp, annot)) eff
+and propagate_exp_effect_aux = function
+ | E_block xs ->
+ let propagated_xs = List.map propagate_exp_effect xs in
+ E_block propagated_xs, collect_effects propagated_xs
+ | E_nondet xs ->
+ let propagated_xs = List.map propagate_exp_effect xs in
+ E_nondet propagated_xs, collect_effects propagated_xs
+ | E_id id -> E_id id, no_effect
+ | E_lit lit -> E_lit lit, no_effect
+ | E_cast (typ, exp) ->
+ let propagated_exp = propagate_exp_effect exp in
+ E_cast (typ, propagated_exp), effect_of propagated_exp
+ | E_app (id, xs) ->
+ let propagated_xs = List.map propagate_exp_effect xs in
+ E_app (id, propagated_xs), collect_effects propagated_xs
+ | E_tuple xs ->
+ let propagated_xs = List.map propagate_exp_effect xs in
+ E_tuple propagated_xs, collect_effects propagated_xs
+ | E_if (cond, t, e) ->
+ let propagated_cond = propagate_exp_effect cond in
+ let propagated_t = propagate_exp_effect t in
+ let propagated_e = propagate_exp_effect e in
+ E_if (propagated_cond, propagated_t, propagated_e), collect_effects [propagated_cond; propagated_t; propagated_e]
+ | E_case (exp, cases) ->
+ let propagated_exp = propagate_exp_effect exp in
+ let propagated_cases = List.map propagate_pexp_effect cases in
+ let case_eff = List.fold_left union_effects no_effect (List.map snd propagated_cases) in
+ E_case (propagated_exp, List.map fst propagated_cases), union_effects (effect_of propagated_exp) case_eff
+ | E_let (letbind, exp) ->
+ let propagated_lb, eff = propagate_letbind_effect letbind in
+ let propagated_exp = propagate_exp_effect exp in
+ E_let (propagated_lb, propagated_exp), union_effects (effect_of propagated_exp) eff
+ | E_assign (lexp, exp) ->
+ let propagated_lexp = propagate_lexp_effect lexp in
+ let propagated_exp = propagate_exp_effect exp in
+ E_assign (propagated_lexp, propagated_exp), union_effects (effect_of propagated_exp) (effect_of_lexp propagated_lexp)
+ | E_sizeof nexp -> E_sizeof nexp, no_effect
+ | E_exit exp ->
+ let propagated_exp = propagate_exp_effect exp in
+ E_exit propagated_exp, effect_of propagated_exp
+ | E_return exp ->
+ let propagated_exp = propagate_exp_effect exp in
+ E_return propagated_exp, effect_of propagated_exp
+ | E_assert (test, msg) ->
+ let propagated_test = propagate_exp_effect test in
+ let propagated_msg = propagate_exp_effect msg in
+ E_assert (propagated_test, propagated_msg), collect_effects [propagated_test; propagated_msg]
+ | E_field (exp, id) ->
+ let propagated_exp = propagate_exp_effect exp in
+ E_field (propagated_exp, id), effect_of propagated_exp
+ | exp_aux -> typ_error Parse_ast.Unknown ("Unimplemented: Cannot propagate effect in expression")
+
+and propagate_pexp_effect (Pat_aux (Pat_exp (pat, exp), (l, annot))) =
+ let propagated_pat = propagate_pat_effect pat in
+ let propagated_exp = propagate_exp_effect exp in
+ let propagated_eff = union_effects (effect_of_pat propagated_pat) (effect_of propagated_exp) in
+ match annot with
+ | Some (typq, typ, eff) ->
+ Pat_aux (Pat_exp (propagated_pat, propagated_exp), (l, Some (typq, typ, union_effects eff propagated_eff))),
+ union_effects eff propagated_eff
+ | None -> Pat_aux (Pat_exp (propagated_pat, propagated_exp), (l, None)), propagated_eff
+
+and propagate_pat_effect (P_aux (pat, annot)) =
+ let propagated_pat, eff = propagate_pat_effect_aux pat in
+ add_effect_pat (P_aux (propagated_pat, annot)) eff
+and propagate_pat_effect_aux = function
+ | P_lit lit -> P_lit lit, no_effect
+ | P_wild -> P_wild, no_effect
+ | P_as (pat, id) ->
+ let propagated_pat = propagate_pat_effect pat in
+ P_as (propagated_pat, id), effect_of_pat propagated_pat
+ | P_typ (typ, pat) ->
+ let propagated_pat = propagate_pat_effect pat in
+ P_typ (typ, propagated_pat), effect_of_pat propagated_pat
+ | P_id id -> P_id id, no_effect
+ | P_app (id, pats) ->
+ let propagated_pats = List.map propagate_pat_effect pats in
+ P_app (id, propagated_pats), collect_effects_pat propagated_pats
+ | P_tup pats ->
+ let propagated_pats = List.map propagate_pat_effect pats in
+ P_tup propagated_pats, collect_effects_pat propagated_pats
+ | P_list pats ->
+ let propagated_pats = List.map propagate_pat_effect pats in
+ P_list propagated_pats, collect_effects_pat propagated_pats
+ | P_vector_concat pats ->
+ let propagated_pats = List.map propagate_pat_effect pats in
+ P_vector_concat propagated_pats, collect_effects_pat propagated_pats
+ | P_vector pats ->
+ let propagated_pats = List.map propagate_pat_effect pats in
+ P_vector propagated_pats, collect_effects_pat propagated_pats
+ | _ -> typ_error Parse_ast.Unknown "Unimplemented: Cannot propagate effect in pat"
+
+and propagate_letbind_effect (LB_aux (lb, (l, annot))) =
+ let propagated_lb, eff = propagate_letbind_effect_aux lb in
+ match annot with
+ | Some (typq, typ, eff) -> LB_aux (propagated_lb, (l, Some (typq, typ, eff))), eff
+ | None -> LB_aux (propagated_lb, (l, None)), eff
+and propagate_letbind_effect_aux = function
+ | LB_val_explicit (typschm, pat, exp) ->
+ let propagated_pat = propagate_pat_effect pat in
+ let propagated_exp = propagate_exp_effect exp in
+ LB_val_explicit (typschm, propagated_pat, propagated_exp),
+ union_effects (effect_of_pat propagated_pat) (effect_of propagated_exp)
+ | LB_val_implicit (pat, exp) ->
+ let propagated_pat = propagate_pat_effect pat in
+ let propagated_exp = propagate_exp_effect exp in
+ LB_val_implicit (propagated_pat, propagated_exp),
+ union_effects (effect_of_pat propagated_pat) (effect_of propagated_exp)
+
+and propagate_lexp_effect (LEXP_aux (lexp, annot)) =
+ let propagated_lexp, eff = propagate_lexp_effect_aux lexp in
+ add_effect_lexp (LEXP_aux (propagated_lexp, annot)) eff
+and propagate_lexp_effect_aux = function
+ | LEXP_id id -> LEXP_id id, no_effect
+ | LEXP_memory (id, exps) ->
+ let propagated_exps = List.map propagate_exp_effect exps in
+ LEXP_memory (id, propagated_exps), collect_effects propagated_exps
+ | LEXP_cast (typ, id) -> LEXP_cast (typ, id), no_effect
+ | LEXP_tup lexps ->
+ let propagated_lexps = List.map propagate_lexp_effect lexps in
+ LEXP_tup propagated_lexps, collect_effects_lexp propagated_lexps
+ | LEXP_vector (lexp, exp) ->
+ let propagated_lexp = propagate_lexp_effect lexp in
+ let propagated_exp = propagate_exp_effect exp in
+ LEXP_vector (propagated_lexp, propagated_exp), union_effects (effect_of propagated_exp) (effect_of_lexp propagated_lexp)
+ | LEXP_field (lexp, id) ->
+ let propagated_lexp = propagate_lexp_effect lexp in
+ LEXP_field (propagated_lexp, id),effect_of_lexp propagated_lexp
+ | _ -> typ_error Parse_ast.Unknown "Unimplemented: Cannot propagate effect in lexp"
+
+(**************************************************************************)
+(* 6. Checking toplevel definitions *)
+(**************************************************************************)
+
+let check_letdef env (LB_aux (letbind, (l, _))) =
+ begin
+ match letbind with
+ | LB_val_explicit (typschm, pat, bind) -> assert false
+ | LB_val_implicit (P_aux (P_typ (typ_annot, pat), _), bind) ->
+ let checked_bind = crule check_exp env (strip_exp bind) typ_annot in
+ let tpat, env = bind_pat env (strip_pat pat) typ_annot in
+ DEF_val (LB_aux (LB_val_implicit (P_aux (P_typ (typ_annot, tpat), (l, Some (env, typ_annot, no_effect))), checked_bind), (l, None))), env
+ | LB_val_implicit (pat, bind) ->
+ let inferred_bind = irule infer_exp env (strip_exp bind) in
+ let tpat, env = bind_pat env (strip_pat pat) (typ_of inferred_bind) in
+ DEF_val (LB_aux (LB_val_implicit (tpat, inferred_bind), (l, None))), env
+ end
+
+let check_funcl env (FCL_aux (FCL_Funcl (id, pat, exp), (l, _))) typ =
+ match typ with
+ | Typ_aux (Typ_fn (typ_arg, typ_ret, eff), _) ->
+ begin
+ let typed_pat, env = bind_pat env (strip_pat pat) typ_arg in
+ let env = Env.add_ret_typ typ_ret env in
+ let exp = propagate_exp_effect (crule check_exp env (strip_exp exp) typ_ret) in
+ FCL_aux (FCL_Funcl (id, typed_pat, exp), (l, Some (env, typ, effect_of exp)))
+ end
+ | _ -> typ_error l ("Function clause must have function type: " ^ string_of_typ typ ^ " is not a function type")
+
+let funcl_effect (FCL_aux (FCL_Funcl (id, typed_pat, exp), (l, annot))) =
+ match annot with
+ | Some (_, _, eff) -> eff
+ | None -> no_effect (* Maybe could be assert false. This should never happen *)
+
+let infer_funtyp l env tannotopt funcls =
+ let Typ_annot_opt_aux (Typ_annot_opt_some (quant, ret_typ), _) = tannotopt in
+ let rec typ_from_pat (P_aux (pat_aux, (l, _)) as pat) =
+ match pat_aux with
+ | P_lit lit -> infer_lit env lit
+ | P_typ (typ, _) -> typ
+ | P_tup pats -> mk_typ (Typ_tup (List.map typ_from_pat pats))
+ | _ -> typ_error l ("Cannot infer type from pattern " ^ string_of_pat pat)
+ in
+ match funcls with
+ | [FCL_aux (FCL_Funcl (_, pat, _), _)] ->
+ let arg_typ = typ_from_pat pat in
+ let fn_typ = mk_typ (Typ_fn (arg_typ, ret_typ, Effect_aux (Effect_set [], Parse_ast.Unknown))) in
+ (quant, fn_typ)
+ | _ -> typ_error l "Cannot infer function type for function with multiple clauses"
+
+let check_fundef env (FD_aux (FD_function (recopt, tannotopt, effectopt, funcls), (l, _)) as fd_aux) =
+ let (Typ_annot_opt_aux (Typ_annot_opt_some (annot_quant, annot_typ1), _)) = tannotopt in
+ let id =
+ match (List.fold_right
+ (fun (FCL_aux (FCL_Funcl (id, _, _), _)) id' ->
+ match id' with
+ | Some id' -> if string_of_id id' = string_of_id id then Some id'
+ else typ_error l ("Function declaration expects all definitions to have the same name, "
+ ^ string_of_id id ^ " differs from other definitions of " ^ string_of_id id')
+ | None -> Some id) funcls None)
+ with
+ | Some id -> id
+ | None -> typ_error l "funcl list is empty"
+ in
+ typ_print ("\nChecking function " ^ string_of_id id);
+ let have_val_spec, (quant, (Typ_aux (Typ_fn (vtyp_arg, vtyp_ret, declared_eff), vl) as typ)), env =
+ try true, Env.get_val_spec id env, env with
+ | Type_error (l, _) ->
+ let (quant, typ) = infer_funtyp l env tannotopt funcls in
+ false, (quant, typ), env
+ in
+ typ_debug ("Checking fundef " ^ string_of_id id ^ " has type " ^ string_of_bind (quant, typ));
+ let funcl_env = add_typquant quant env in
+ let funcls = List.map (fun funcl -> check_funcl funcl_env funcl typ) funcls in
+ let eff = List.fold_left union_effects no_effect (List.map funcl_effect funcls) in
+ let env, declared_eff =
+ if not have_val_spec
+ then Env.add_val_spec id (quant, Typ_aux (Typ_fn (vtyp_arg, vtyp_ret, eff), vl)) env, eff
+ else env, declared_eff
+ in
+ if equal_effects eff declared_eff
+ then DEF_fundef (FD_aux (FD_function (recopt, tannotopt, effectopt, funcls), (l, None))), env
+ else typ_error l ("Effects do not match: " ^ string_of_effect declared_eff ^ " declared and " ^ string_of_effect eff ^ " found")
+
+(* Checking a val spec simply adds the type as a binding in the
+ context. We have to destructure the various kinds of val specs, but
+ the difference is irrelevant for the typechecker. *)
+let check_val_spec env (VS_aux (vs, (l, _))) =
+ let (id, quants, typ, env) = match vs with
+ | VS_val_spec (TypSchm_aux (TypSchm_ts (quants, typ), _), id) -> (id, quants, typ, env)
+ | VS_cast_spec (TypSchm_aux (TypSchm_ts (quants, typ), _), id) -> (id, quants, typ, Env.add_cast id env)
+ | VS_extern_no_rename (TypSchm_aux (TypSchm_ts (quants, typ), _), id) -> (id, quants, typ, env)
+ | VS_extern_spec (TypSchm_aux (TypSchm_ts (quants, typ), _), id, _) -> (id, quants, typ, env) in
+ DEF_spec (VS_aux (vs, (l, None))), Env.add_val_spec id (quants, typ) env
+
+let check_default env (DT_aux (ds, l)) =
+ match ds with
+ | DT_kind _ -> DEF_default (DT_aux (ds,l)), env (* Check: Is this supposed to do nothing? *)
+ | DT_order (Ord_aux (Ord_inc, _)) -> DEF_default (DT_aux (ds, l)), Env.set_default_order_inc env
+ | DT_order (Ord_aux (Ord_dec, _)) -> DEF_default (DT_aux (ds, l)), Env.set_default_order_dec env
+ | DT_order (Ord_aux (Ord_var _, _)) -> typ_error l "Cannot have variable default order"
+ (* This branch allows us to write something like: default forall Nat 'n. [|'n|] name... what does this even mean?! *)
+ | DT_typ (typschm, id) -> typ_error l ("Unsupported default construct")
+
+let check_register env id base top ranges =
+ match base, top with
+ | Nexp_aux (Nexp_constant basec, _), Nexp_aux (Nexp_constant topc, _) ->
+ let no_typq = TypQ_aux (TypQ_tq [], Parse_ast.Unknown) (* Maybe could be TypQ_no_forall? *) in
+ (* FIXME: wrong for default Order inc? *)
+ let cast_typ = mk_typ (Typ_fn (mk_id_typ id, dvector_typ env base (nconstant ((basec - topc) + 1)) bit_typ, no_effect)) in
+ env
+ |> Env.add_regtyp id basec topc ranges
+ |> Env.add_val_spec (mk_id ("cast_" ^ string_of_id id)) (no_typq, cast_typ)
+ |> Env.add_cast (mk_id ("cast_" ^ string_of_id id))
+ | _, _ -> typ_error (id_loc id) "Num expressions in register type declaration do not evaluate to constants"
+
+let check_typedef env (TD_aux (tdef, (l, _))) =
+ let td_err () = raise (Reporting_basic.err_unreachable Parse_ast.Unknown "Unimplemented Typedef") in
+ match tdef with
+ | TD_abbrev(id, nmscm, (TypSchm_aux (TypSchm_ts (typq, typ), _))) ->
+ DEF_type (TD_aux (tdef, (l, None))), Env.add_typ_synonym id (fun _ -> typ) env
+ | TD_record(id, nmscm, typq, fields, _) ->
+ DEF_type (TD_aux (tdef, (l, None))), Env.add_record id typq fields env
+ | TD_variant(id, nmscm, typq, arms, _) -> td_err ()
+ | TD_enum(id, nmscm, ids, _) ->
+ DEF_type (TD_aux (tdef, (l, None))), Env.add_enum id ids env
+ | TD_register(id, base, top, ranges) -> DEF_type (TD_aux (tdef, (l, None))), check_register env id base top ranges
+
+let rec check_def env def =
+ let cd_err () = raise (Reporting_basic.err_unreachable Parse_ast.Unknown "Unimplemented Case") in
+ match def with
+ | DEF_kind kdef -> cd_err ()
+ | DEF_type tdef -> check_typedef env tdef
+ | DEF_fundef fdef -> check_fundef env fdef
+ | DEF_val letdef -> check_letdef env letdef
+ | DEF_spec vs -> check_val_spec env vs
+ | DEF_default default -> check_default env default
+ | DEF_overload (id, ids) -> DEF_overload (id, ids), Env.add_overloads id ids env
+ | DEF_reg_dec (DEC_aux (DEC_reg (typ, id), (l, _))) ->
+ DEF_reg_dec (DEC_aux (DEC_reg (typ, id), (l, None))), Env.add_register id typ env
+ | DEF_reg_dec (DEC_aux (DEC_alias (id, aspec), (l, annot))) -> cd_err ()
+ | DEF_reg_dec (DEC_aux (DEC_typ_alias (typ, id, aspec), (l, tannot))) -> cd_err ()
+ | DEF_scattered _ -> raise (Reporting_basic.err_unreachable Parse_ast.Unknown "Scattered given to type checker")
+ | DEF_comm (DC_comm str) -> DEF_comm (DC_comm str), env
+ | DEF_comm (DC_comm_struct def) ->
+ let def, env = check_def env def
+ in DEF_comm (DC_comm_struct def), env
+
+let rec check' env (Defs defs) =
+ match defs with
+ | [] -> (Defs []), env
+ | def :: defs ->
+ let (def, env) = check_def env def in
+ let (Defs defs, env) = check' env (Defs defs) in
+ (Defs (def::defs)), env
+
+let check env defs =
+ try check' env defs with
+ | Type_error (l, m) -> raise (Reporting_basic.err_typ l m)
diff --git a/src/type_check_new.mli b/src/type_check_new.mli
new file mode 100644
index 00000000..6c67d84b
--- /dev/null
+++ b/src/type_check_new.mli
@@ -0,0 +1,186 @@
+(**************************************************************************)
+(* Sail *)
+(* *)
+(* Copyright (c) 2013-2017 *)
+(* Kathyrn Gray *)
+(* Shaked Flur *)
+(* Stephen Kell *)
+(* Gabriel Kerneis *)
+(* Robert Norton-Wright *)
+(* Christopher Pulte *)
+(* Peter Sewell *)
+(* Alasdair Armstrong *)
+(* *)
+(* All rights reserved. *)
+(* *)
+(* This software was developed by the University of Cambridge Computer *)
+(* Laboratory as part of the Rigorous Engineering of Mainstream Systems *)
+(* (REMS) project, funded by EPSRC grant EP/K008528/1. *)
+(* *)
+(* Redistribution and use in source and binary forms, with or without *)
+(* modification, are permitted provided that the following conditions *)
+(* are met: *)
+(* 1. Redistributions of source code must retain the above copyright *)
+(* notice, this list of conditions and the following disclaimer. *)
+(* 2. Redistributions in binary form must reproduce the above copyright *)
+(* notice, this list of conditions and the following disclaimer in *)
+(* the documentation and/or other materials provided with the *)
+(* distribution. *)
+(* *)
+(* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' *)
+(* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED *)
+(* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *)
+(* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR *)
+(* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, *)
+(* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT *)
+(* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF *)
+(* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND *)
+(* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, *)
+(* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT *)
+(* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF *)
+(* SUCH DAMAGE. *)
+(**************************************************************************)
+
+open Ast
+
+exception Type_error of l * string;;
+
+type mut = Immutable | Mutable
+
+type lvar = Register of typ | Enum of typ | Local of mut * typ | Unbound
+
+module Env : sig
+ (* Env.t is the type of environments *)
+ type t
+
+ (* Note: Most get_ functions assume the identifiers exist, and throw type
+ errors if it doesn't. *)
+
+ val get_val_spec : id -> t -> typquant * typ
+
+ val get_register : id -> t -> typ
+
+ val get_regtyp : id -> t -> int * int * (index_range * id) list
+
+ (* Returns true if id is a register type, false otherwise *)
+ val is_regtyp : id -> t -> bool
+
+ (* Check if a local variable is mutable. Throws Type_error if it
+ isn't a mutable variable. Probably best to use Env.lookup_id
+ instead *)
+ val is_mutable : id -> t -> bool
+
+ (* Get the current set of constraints. *)
+ val get_constraints : t -> n_constraint list
+
+ val get_typ_var : kid -> t -> base_kind_aux
+
+ val is_record : id -> t -> bool
+
+ val get_accessor : id -> t -> typquant * typ
+
+ (* If the environment is checking a function, then this will get the
+ expected return type of the function. It's useful for checking or
+ inserting early returns. Returns an option type and won't throw
+ any exceptions. *)
+ val get_ret_typ : t -> typ option
+
+ val get_typ_synonym : id -> t -> typ_arg list -> typ
+
+ val get_overloads : id -> t -> id list
+
+ (* Lookup id searchs for a specified id in the environment, and
+ returns it's type and what kind of identifier it is, using the
+ lvar type. Returns Unbound if the identifier is unbound, and
+ won't throw any exceptions. *)
+ val lookup_id : id -> t -> lvar
+
+ (* Return a fresh kind identifier that doesn't exist in the environment *)
+ val fresh_kid : t -> kid
+
+ val expand_synonyms : t -> typ -> typ
+
+ (* no_casts removes all the implicit type casts/coercions from the
+ environment, so checking a term with such an environment will
+ guarantee not to insert any casts. Not that this is only about
+ the implicit casting and has nothing to do with the E_cast AST
+ node. *)
+ val no_casts : t -> t
+
+ (* Is casting allowed by the environment? *)
+ val allow_casts : t -> bool
+
+ val empty : t
+end
+
+(* Some handy utility functions for constructing types. *)
+val mk_typ : typ_aux -> typ
+val mk_typ_arg : typ_arg_aux -> typ_arg
+val mk_id : string -> id
+val mk_id_typ : id -> typ
+
+val no_effect : effect
+val mk_effect : base_effect_aux list -> effect
+
+val union_effects : effect -> effect -> effect
+val equal_effects : effect -> effect -> bool
+
+(* Sail builtin types. *)
+val int_typ : typ
+val nat_typ : typ
+val atom_typ : nexp -> typ
+val range_typ : nexp -> nexp -> typ
+val bit_typ : typ
+val bool_typ : typ
+val unit_typ : typ
+val string_typ : typ
+val vector_typ : nexp -> nexp -> order -> typ -> typ
+
+(* Vector with default order. *)
+val dvector_typ : Env.t -> nexp -> nexp -> typ -> typ
+
+(* Vector of specific length with default order, i.e. lvector_typ env n bit_typ = bit[n]. *)
+val lvector_typ : Env.t -> nexp -> typ -> typ
+
+type tannot = (Env.t * typ * effect) option
+
+(* Strip the type annotations from an expression. *)
+val strip_exp : 'a exp -> unit exp
+
+(* Check an expression has some type. Returns a fully annotated
+ version of the expression, where each subexpression is annotated
+ with it's type and the Environment used while checking it. The can
+ be used to re-start the typechecking process on any
+ sub-expression. so local modifications to the AST can be
+ re-checked. *)
+val check_exp : Env.t -> unit exp -> typ -> tannot exp
+
+(* Partial functions: The expressions and patterns passed to these
+ functions must be guaranteed to have tannots of the form Some (env,
+ typ) for these to work. *)
+val typ_of : tannot exp -> typ
+
+val pat_typ_of : tannot pat -> typ
+
+val effect_of : tannot exp -> effect
+
+(* Fully type-check an AST
+
+Some invariants that will hold of a fully checked AST are:
+
+ * No internal nodes, such as E_internal_exp, or E_comment nodes.
+
+ * E_vector_access nodes and similar will be replaced by function
+ calls E_app to vector access functions. This is different to the
+ old type checker.
+
+ * Every expressions type annotation (tanot) will be Some (typ, env).
+
+ * Also every pattern will be annotated with the type it matches.
+
+ * Toplevel expressions such as typedefs and some subexpressions such
+ as letbinds may have None as their tannots if it doesn't make sense
+ for them to have type annotations. *)
+val check : Env.t -> 'a defs -> tannot defs * Env.t
+
+val initial_env : Env.t