| open Util |
| open Source |
| open Ast |
| open Xl |
| open Env |
| |
| |
| (* Environment *) |
| |
| type env = Env.t |
| type subst = Subst.t |
| |
| |
| (* Helpers *) |
| |
| let assume_coherent_matches = ref true |
| |
| type 'a result = ('a, 'a) Result.t |
| |
| let static f env x = |
| let (Ok y | Error y) = f true env x in Ok y |
| |
| let static3 f env x1 x2 x3 = |
| let (Ok y | Error y) = f true env x1 x2 x3 in Ok y |
| |
| let (let*) = Option.bind |
| |
| let (let**) xr f = |
| match xr with |
| | Ok x -> f x |
| | Error x -> let Ok y | Error y = f x in Error y |
| |
| let (let***) xor f = |
| let** xo = xor in |
| match xo with |
| | None -> Ok None |
| | Some x -> f x |
| |
| let rec map_results f = function |
| | [] -> Ok [] |
| | x::xs -> let** y = f x in let** ys = map_results f xs in Ok (y::ys) |
| |
| |
| let error_typ at t expect = |
| Error.error at "validation" |
| ("type `" ^ Print.string_of_typ t ^ "` is not " ^ expect) |
| |
| |
| let ($>) it e = {e with it} |
| |
| let unordered s1 s2 = not Set.(subset s1 s2 || subset s2 s1) |
| |
| let il_result f = function |
| | Ok x -> f x |
| | Error x -> "FAIL(" ^ f x ^ ")" |
| |
| let il_opt_result f = function |
| | Ok (Some x) -> f x |
| | Ok None -> "?" |
| | Error _ -> "FAIL" |
| |
| |
| let of_bool_exp = function |
| | BoolE b -> Some b |
| | _ -> None |
| |
| let of_num_exp = function |
| | NumE n -> Some n |
| | _ -> None |
| |
| let to_bool_exp b = BoolE b |
| let to_num_exp n = NumE n |
| |
| let as_opt_exp e = |
| match e.it with |
| | OptE eo -> eo |
| | _ -> failwith "as_opt_exp" |
| |
| let as_list_exp e = |
| match e.it with |
| | ListE es -> es |
| | _ -> failwith "as_list_exp" |
| |
| |
| (* Matching Lists *) |
| |
| let _match_opt match_x env s xo1 xo2 : subst option result = |
| match xo1, xo2 with |
| | None, None -> Ok (Some s) |
| | Some x1, Some x2 -> match_x env s x1 x2 |
| | _, _ -> Error None |
| |
| let rec match_list match_x env s xs1 xs2 : subst option result = |
| match xs1, xs2 with |
| | [], [] -> Ok (Some s) |
| | x1::xs1', x2::xs2' -> |
| let*** s' = match_x env s x1 x2 in |
| match_list match_x env (Subst.union s s') xs1' xs2' |
| | _, _ -> Error None |
| |
| let equiv_list equiv_x env xs1 xs2 = |
| List.length xs1 = List.length xs2 && List.for_all2 (equiv_x env) xs1 xs2 |
| |
| |
| (* Type Reduction (weak-head) *) |
| |
| let rec reduce_typ env t : typ result = |
| Debug.(log_if "il.reduce_typ" (t.it <> NumT `NatT) |
| (fun _ -> fmt "%s" (il_typ t)) |
| (fun r -> fmt "%s" (il_result il_typ r)) |
| ) @@ fun _ -> |
| match t.it with |
| | VarT (id, as_) -> |
| let** as' = map_results (static reduce_arg env) as_ in |
| let** dto = reduce_typ_app env id as' (Env.find_opt_typ env id) t.at in |
| (match dto with |
| | Some {it = AliasT t'; _} -> reduce_typ env t' |
| | _ -> Ok (VarT (id, as') $ t.at) |
| ) |
| | _ -> Ok t |
| |
| and reduce_typdef env t : deftyp result = |
| let** t' = reduce_typ env t in |
| match t'.it with |
| | VarT (id, as_) -> |
| let** dto = reduce_typ_app env id as_ (Env.find_opt_typ env id) t'.at in |
| (match dto with |
| | Some dt -> Ok dt |
| | None -> Ok (AliasT t $ t'.at) |
| ) |
| | _ -> Ok (AliasT t $ t'.at) |
| |
| and reduce_typ_app env id as_ tdo at : deftyp option result = |
| Debug.(log "il.reduce_typ_app" |
| (fun _ -> fmt "%s(%s)" id.it (il_args as_)) |
| (fun r -> fmt "%s" (il_opt_result il_deftyp r)) |
| ) @@ fun _ -> |
| match tdo with |
| | None -> Ok None (* id is a type parameter *) |
| | Some (_ps, []) -> if !assume_coherent_matches then Ok None else Error None |
| | Some (ps, {it = InstD (_ps, as', dt); _}::insts') -> |
| match match_list (static3 match_arg) env Subst.empty as_ as' with |
| | Ok (Some s) -> Ok (Some (Subst.subst_deftyp s dt)) |
| | Error _ -> reduce_typ_app env id as_ (Some (ps, insts')) at |
| | Ok None -> |
| if not !assume_coherent_matches then Ok None else |
| match reduce_typ_app env id as_ (Some (ps, insts')) at with |
| | Error _ -> Ok None |
| | ok -> ok |
| |
| and reduce_typ_ok env t = |
| match reduce_typ env t with |
| | Ok t -> t |
| | Error _ -> error_typ t.at t "defined" |
| |
| and reduce_typdef_ok env t = |
| match reduce_typdef env t with |
| | Ok dt -> dt |
| | Error _ -> error_typ t.at t "defined" |
| |
| and as_tup_typ env t at : (id * typ) list = |
| match (reduce_typ_ok env t).it with |
| | TupT xts -> xts |
| | _ -> error_typ at t "a tuple" |
| |
| and as_iter_typ env t at : typ * iter = |
| match (reduce_typ_ok env t).it with |
| | IterT (t1, it) -> t1, it |
| | _ -> error_typ at t "an iteration" |
| |
| and as_struct_typ env t at : typfield list = |
| match (reduce_typdef_ok env t).it with |
| | StructT tfs -> tfs |
| | _ -> error_typ at t "a record" |
| |
| and as_variant_typ env t at : typcase list = |
| match (reduce_typdef_ok env t).it with |
| | VariantT tcs -> tcs |
| | _ -> error_typ at t "a variant" |
| |
| and find_typfield t tfs atom at = |
| match List.find_opt (fun (atom', _, _) -> Eq.eq_atom atom' atom) tfs with |
| | Some (_, x, _) -> x |
| | None -> |
| error_typ at t ("a record with field `" ^ Print.string_of_atom atom ^ "`") |
| |
| and find_typcase t tcs op at = |
| match List.find_opt (fun (op', _, _) -> Eq.eq_mixop op' op) tcs with |
| | Some (_, x, _) -> x |
| | None -> |
| error_typ at t ("a variant with case `" ^ Print.string_of_mixop op ^ "`") |
| |
| |
| (* Expression Reduction *) |
| |
| and is_normal_arg static env a = (* only for assertions *) |
| match reduce_arg static env a with |
| | Ok a' | Error a' -> Eq.eq_arg a a' |
| |
| and is_normal_exp static env e = (* only for assertions *) |
| match reduce_exp static env e with |
| | Ok e' | Error e' -> Eq.eq_exp e e' |
| |
| and is_head_normal_exp e = |
| match e.it with |
| | BoolE _ | NumE _ | TextE _ |
| | OptE _ | ListE _ | TupE _ | CaseE _ | StrE _ -> true |
| | SubE (e, _, _) -> is_head_normal_exp e |
| | _ -> false |
| |
| and is_value_exp e = |
| match e.it with |
| | BoolE _ | NumE _ | TextE _ -> true |
| | ListE es | TupE es -> List.for_all is_value_exp es |
| | OptE None -> true |
| | OptE (Some e) | CaseE (_, e, Checked) | SubE (e, _, _) -> is_value_exp e |
| | StrE (efs, Checked) -> List.for_all (fun (_, e) -> is_value_exp e) efs |
| | _ -> false |
| |
| and reduce_exp static env e : exp result = |
| Debug.(log ("il.reduce_exp" ^ if static then " static" else "") |
| (fun _ -> fmt "%s" (il_exp e)) |
| (fun e' -> fmt "%s" (il_result il_exp e')) |
| ) @@ fun _ -> |
| match e.it with |
| | VarE _ | BoolE _ | NumE _ | TextE _ -> Ok e |
| | UnE (op, ot, e1) -> |
| let** e1' = reduce_exp static env e1 in |
| (match op, e1'.it with |
| | #Bool.unop as op', BoolE b1 -> Ok (BoolE (Bool.un op' b1) $> e) |
| | #Num.unop as op', NumE n1 -> |
| (match Num.un op' n1 with |
| | Some n -> Ok (NumE n $> e) |
| | None -> Error (UnE (op, ot, e1') $> e) |
| ) |
| | `NotOp, UnE (`NotOp, _, e11') -> Ok e11' |
| | `MinusOp, UnE (`MinusOp, _, e11') -> Ok e11' |
| | _ -> Ok (UnE (op, ot, e1') $> e) |
| ) |
| | BinE (op, ot, e1, e2) -> |
| let** e1' = reduce_exp static env e1 in |
| let** e2' = reduce_exp static env e2 in |
| (match op with |
| | #Bool.binop as op' -> |
| (match Bool.bin_partial op' e1'.it e2'.it of_bool_exp to_bool_exp with |
| | None -> Ok (BinE (op, ot, e1', e2') $> e) |
| | Some e' -> Ok (e' $> e) |
| ) |
| | #Num.binop as op' -> |
| (match Num.bin_partial op' e1'.it e2'.it of_num_exp to_num_exp with |
| | None -> Ok (BinE (op, ot, e1', e2') $> e) |
| | Some None -> Error (BinE (op, ot, e1', e2') $> e) |
| | Some (Some e') -> Ok (e' $> e) |
| ) |
| ) |
| | CmpE (op, ot, e1, e2) -> |
| let** e1' = reduce_exp static env e1 in |
| let** e2' = reduce_exp static env e2 in |
| (match op, e1'.it, e2'.it with |
| | `EqOp, _, _ when static || is_value_exp e1' && is_value_exp e2' -> |
| Ok (BoolE (Eq.eq_exp e1' e2') $> e) |
| | `NeOp, _, _ when is_value_exp e1' && is_value_exp e2' -> |
| Ok (BoolE (not (Eq.eq_exp e1' e2')) $> e) |
| | #Num.cmpop as op', NumE n1, NumE n2 -> |
| (match Num.cmp op' n1 n2 with |
| | Some b -> Ok (BoolE b $> e) |
| | None -> Ok (CmpE (op, ot, e1', e2') $> e) |
| ) |
| | _ -> Ok (CmpE (op, ot, e1', e2') $> e) |
| ) |
| | IdxE (e1, e2) -> |
| let** e1' = reduce_exp static env e1 in |
| let** e2' = reduce_exp static env e2 in |
| (match e1'.it, e2'.it with |
| | ListE es, NumE (`Nat i) when i < Z.of_int (List.length es) -> |
| Ok (List.nth es (Z.to_int i)) |
| | _ -> Error (IdxE (e1', e2') $> e) |
| ) |
| | SliceE (e1, e2, e3) -> |
| let** e1' = reduce_exp static env e1 in |
| let** e2' = reduce_exp static env e2 in |
| let** e3' = reduce_exp static env e3 in |
| (match e1'.it, e2'.it, e3'.it with |
| | ListE es, NumE (`Nat i), NumE (`Nat n) when Z.(i + n) < Z.of_int (List.length es) -> |
| Ok (ListE (Lib.List.take (Z.to_int n) (Lib.List.drop (Z.to_int i) es)) $> e) |
| | _ -> Error (SliceE (e1', e2', e3') $> e) |
| ) |
| | UpdE (e1, p, e2) -> |
| let** e1' = reduce_exp static env e1 in |
| let** e2' = reduce_exp static env e2 in |
| reduce_path static env e1' p |
| (fun e' p' -> |
| if p'.it = RootP |
| then Ok e2' |
| else Ok (UpdE (e', p', e2') $> e') |
| ) |
| | ExtE (e1, p, e2) -> |
| let** e1' = reduce_exp static env e1 in |
| let** e2' = reduce_exp static env e2 in |
| reduce_path static env e1' p |
| (fun e' p' -> |
| if p'.it = RootP |
| then reduce_exp static env (CatE (e', e2') $> e') |
| else Ok (ExtE (e', p', e2') $> e') |
| ) |
| | StrE (efs, ch) -> |
| let tfs = as_struct_typ env e.note e.at in |
| let** ef_chs' = map_results (reduce_expfield static env e.note tfs ch) efs in |
| let efs', chs' = List.split ef_chs' in |
| let ch' = if List.for_all ((=) Checked) chs' then Checked else Unchecked in |
| Ok (StrE (efs', ch') $> e) |
| | DotE (e1, atom) -> |
| let** e1' = reduce_exp static env e1 in |
| (match e1'.it with |
| | StrE (efs, Checked) when static || is_value_exp e1' -> |
| Ok (snd (List.find (fun (atomN, _) -> Atom.eq atomN atom) efs)) |
| | _ -> Ok (DotE (e1', atom) $> e) |
| ) |
| | CompE (e1, e2) -> |
| (* TODO(4, rossberg): avoid overlap with CatE? *) |
| let** e1' = reduce_exp static env e1 in |
| let** e2' = reduce_exp static env e2 in |
| (match e1'.it, e2'.it with |
| | ListE es1, ListE es2 -> Ok (ListE (es1 @ es2) $> e) |
| | OptE None, OptE _ -> Ok e2' |
| | OptE _, OptE None -> Ok e1' |
| | StrE (efs1, Checked), StrE (efs2, Checked) -> |
| let tfs = as_struct_typ env e.note e.at in |
| let merge ((atom1, e1), (atom2, e2)) = |
| assert (Atom.eq atom1 atom2); |
| reduce_expfield static env e.note tfs Checked (atom1, CompE (e1, e2) $> e1) |
| in |
| assert (List.length efs1 = List.length efs2); |
| let** ef_chs' = map_results merge (List.combine efs1 efs2) in |
| let efs', _chs = List.split ef_chs' in |
| Ok (StrE (efs', Checked) $> e) |
| | _ -> Ok (CompE (e1', e2') $> e) |
| ) |
| | MemE (e1, e2) -> |
| let** e1' = reduce_exp static env e1 in |
| let** e2' = reduce_exp static env e2 in |
| (match e2'.it with |
| | OptE None -> Ok (BoolE false $> e) |
| | OptE (Some e2') when static || is_value_exp e1' && is_value_exp e2' -> |
| Ok (BoolE (Eq.eq_exp e1' e2') $> e) |
| | ListE [] -> Ok (BoolE false $> e) |
| | ListE es2' when static || is_value_exp e1' && List.for_all is_value_exp es2' -> |
| Ok (BoolE (List.exists (Eq.eq_exp e1') es2') $> e) |
| | _ -> Ok (MemE (e1', e2') $> e) |
| ) |
| | LenE e1 -> |
| let** e1' = reduce_exp static env e1 in |
| (match e1'.it with |
| | ListE es when static || is_value_exp e1' -> |
| Ok (NumE (`Nat (Z.of_int (List.length es))) $> e) |
| | _ -> Ok (LenE e1' $> e) |
| ) |
| | TupE es -> |
| let** es' = map_results (reduce_exp static env) es in |
| Ok (TupE es' $> e) |
| | CallE (id, as_) -> |
| let _ps, _t, clauses = Env.find_def env id in |
| let** as' = map_results (reduce_arg static env) as_ in |
| (* Allow for parameters or uninterpreted functions *) |
| if clauses = [] then |
| Ok (CallE (id, as') $> e) |
| else |
| let** eo = reduce_exp_call static env id as' clauses e.at in |
| (match eo with |
| | None -> Ok (CallE (id, as') $> e) |
| | Some e -> reduce_exp static env e |
| ) |
| | IterE (e1, iterexp) -> |
| let** e1' = reduce_exp static env e1 in |
| let** (iter', xes') as iterexp' = reduce_iterexp static env iterexp in |
| let ids, es' = List.split xes' in |
| if iter' <= List1 && es' = [] then |
| (* Lists with no iteration vars are invalid except as patterns *) |
| Ok (IterE (e1', iterexp') $> e) |
| else if not (List.for_all is_head_normal_exp es') then |
| Ok (IterE (e1', iterexp') $> e) |
| else |
| (match iter' with |
| | Opt -> |
| let eos' = List.map as_opt_exp es' in |
| if List.for_all Option.is_none eos' then |
| Ok (OptE None $> e) |
| else if List.for_all Option.is_some eos' then |
| let es1' = List.map Option.get eos' in |
| let s = List.fold_left2 Subst.add_varid Subst.empty ids es1' in |
| reduce_exp static env (Subst.subst_exp s e1') |
| else |
| Ok (IterE (e1', iterexp') $> e) |
| | List | List1 -> |
| let n = List.length (as_list_exp (List.hd es')) in |
| if iter' = List || n >= 1 then |
| let en = NumE (`Nat (Z.of_int n)) $$ e.at % (NumT `NatT $ e.at) in |
| reduce_exp static env (IterE (e1', (ListN (en, None), xes')) $> e) |
| else |
| Error (IterE (e1', iterexp') $> e) |
| | ListN ({it = NumE (`Nat n'); _}, ido) -> |
| let ess' = List.map as_list_exp es' in |
| let ns = List.map List.length ess' in |
| let n = Z.to_int n' in |
| if List.for_all ((=) n) ns then |
| (ListE (List.init n (fun i -> |
| let esI' = List.map (fun es -> List.nth es i) ess' in |
| let s = List.fold_left2 Subst.add_varid Subst.empty ids esI' in |
| let s' = |
| Option.fold ido ~none:s ~some:(fun id -> |
| let en = NumE (`Nat (Z.of_int i)) $$ id.at % (NumT `NatT $ id.at) in |
| Subst.add_varid s id en |
| ) |
| in Subst.subst_exp s' e1' |
| )) $> e) |> reduce_exp static env |
| else |
| Error (IterE (e1', iterexp') $> e) |
| | ListN _ -> |
| Ok (IterE (e1', iterexp') $> e) |
| ) |
| | ProjE (e1, i) -> |
| let** e1' = reduce_exp static env e1 in |
| (match e1'.it with |
| | TupE es when static || is_value_exp e1' -> Ok (List.nth es i) |
| | _ -> Ok (ProjE (e1', i) $> e) |
| ) |
| | UncaseE (e1, mixop) -> |
| let** e1' = reduce_exp static env e1 in |
| (match e1'.it with |
| | CaseE (mixop', e11', Checked) when Mixop.eq mixop mixop' -> Ok e11' |
| | CaseE (mixop', _, _) when not (Mixop.eq mixop mixop') -> |
| Error (UncaseE (e1', mixop) $> e) |
| | _ -> Ok (UncaseE (e1', mixop) $> e) |
| ) |
| | OptE eo -> |
| (match eo with |
| | None -> Ok e |
| | Some e1 -> |
| let** e1' = reduce_exp static env e1 in |
| Ok (OptE (Some e1') $> e) |
| ) |
| | TheE e1 -> |
| let** e1' = reduce_exp static env e1 in |
| (match e1'.it with |
| | OptE (Some e11) -> Ok e11 |
| | OptE None -> Error (TheE e1' $> e) |
| | _ -> Ok (TheE e1' $> e) |
| ) |
| | ListE es -> |
| let** es' = map_results (reduce_exp static env) es in |
| Ok (ListE es' $> e) |
| | LiftE e1 -> |
| let** e1' = reduce_exp static env e1 in |
| (match e1'.it with |
| | OptE None -> Ok (ListE [] $> e) |
| | OptE (Some e11') -> Ok (ListE [e11'] $> e) |
| | _ -> Ok (LiftE e1' $> e) |
| ) |
| | CatE (e1, e2) -> |
| let** e1' = reduce_exp static env e1 in |
| let** e2' = reduce_exp static env e2 in |
| (match e1'.it, e2'.it with |
| | ListE es1, ListE es2 -> Ok (ListE (es1 @ es2) $> e) |
| | OptE None, OptE _ -> Ok e2' |
| | OptE _, OptE None -> Ok e1' |
| | OptE _, OptE _ -> Error (CatE (e1', e2') $> e) |
| | _ -> Ok (CatE (e1', e2') $> e) |
| ) |
| | CaseE (op, e1, Unchecked) when not static -> |
| let tcs = as_variant_typ env e.note e.at in |
| let t, _qs, prems = find_typcase e.note tcs op e.at in |
| let** e1' = reduce_exp static env e1 in |
| let** so = match_exp_typ static env Subst.empty e1' t in |
| (match so with |
| | Some s -> |
| let** so' = reduce_prems env s prems in |
| Ok (CaseE (op, e1', if so' <> None then Checked else Unchecked) $> e) |
| | None -> |
| Ok (CaseE (op, e1', Unchecked) $> e) |
| ) |
| | CaseE (op, e1, _ch) -> |
| let** e1' = reduce_exp static env e1 in |
| Ok (CaseE (op, e1', Checked) $> e) |
| | CvtE (e1, nt1, nt2) -> |
| let** e1' = reduce_exp static env e1 in |
| (match e1'.it with |
| | NumE n -> |
| (match Num.cvt nt2 n with |
| | Some n' -> Ok (NumE n' $> e) |
| | None -> Error (CvtE (e1', nt1, nt2) $> e) |
| ) |
| | _ -> Ok (CvtE (e1', nt1, nt2) $> e) |
| ) |
| | SubE (e1, t1, t2) -> |
| let** e1' = reduce_exp static env e1 in |
| let** t1' = reduce_typ env t1 in |
| let** t2' = reduce_typ env t2 in |
| if equiv_typ env t1' t2' then Ok e1' else |
| (match e1'.it with |
| | SubE (e11', t11', _t12') -> |
| reduce_exp static env (SubE (e11', t11', t2') $> e) |
| | TupE es' -> |
| let xts1 = as_tup_typ env t1' t1.at in |
| let xts2 = as_tup_typ env t2' t2.at in |
| let** _, _, res' = |
| List.fold_left2 (fun opt eI ((x1I, t1I), (x2I, t2I)) -> |
| let** s1, s2, res' = opt in |
| let t1I' = Subst.subst_typ s1 t1I in |
| let t2I' = Subst.subst_typ s2 t2I in |
| let s1' = Subst.add_varid s1 x1I eI in |
| let s2' = Subst.add_varid s2 x2I eI in |
| let** eI' = |
| reduce_exp static env (SubE (eI, t1I', t2I') $$ eI.at % t2I') in |
| Ok (s1', s2', eI'::res') |
| ) (Ok (Subst.empty, Subst.empty, [])) es' (List.combine xts1 xts2) |
| in Ok (TupE (List.rev res') $> e) |
| | StrE (efs', _ch) -> |
| let tfs1 = as_struct_typ env t1' t1.at in |
| let tfs2 = as_struct_typ env t2' t2.at in |
| let efs'' = |
| List.map2 (fun (atomI, eI) ((atom1I, (t1I, _, _), _), (atom2I, (t2I, _, _), _)) -> |
| assert (Atom.eq atomI atom1I); |
| assert (Atom.eq atomI atom2I); |
| (atomI, SubE (eI, t1I, t2I) $$ eI.at % t2I) |
| ) efs' (List.combine tfs1 tfs2) |
| in reduce_exp static env (StrE (efs'', Unchecked) $> e) |
| | CaseE (op, e11', _ch) -> |
| let tcs1 = as_variant_typ env t1' t1.at in |
| let tcs2 = as_variant_typ env t2' t2.at in |
| let t1', _qs, _prems = find_typcase t1' tcs1 op t1.at in |
| let t2', _qs, _prems = find_typcase t2' tcs2 op t2.at in |
| reduce_exp static env |
| (CaseE (op, SubE (e11', t1', t2') $$ e11'.at % t2', Unchecked) $> e) |
| | _ when is_head_normal_exp e1' -> |
| Ok {e1' with note = e.note} |
| | _ -> Ok (SubE (e1', t1', t2') $> e) |
| ) |
| |
| and reduce_iter static env iter : iter result = |
| match iter with |
| | ListN (e, ido) -> |
| let** e' = reduce_exp static env e in |
| Ok (ListN (e', ido)) |
| | iter -> Ok iter |
| |
| and reduce_iterexp static env (iter, xes) : iterexp result = |
| let** iter' = reduce_iter static env iter in |
| let** es' = map_results (reduce_exp static env) (List.map snd xes) in |
| Ok (iter', List.map2 (fun (xI, _) eI' -> xI, eI') xes es') |
| |
| and reduce_expfield static env t tfs ch (atom, e) : (expfield * check) result = |
| match ch with |
| | Unchecked when not static -> |
| let _t, _qs, prems = find_typfield t tfs atom atom.at in |
| let** e' = reduce_exp static env e in |
| let** so = match_exp_typ static env Subst.empty e' t in |
| (match so with |
| | Some s -> |
| let** so' = reduce_prems env s prems in |
| Ok ((atom, e'), if so' <> None then Checked else Unchecked) |
| | None -> |
| Ok ((atom, e'), Unchecked) |
| ) |
| | _ch -> |
| let** e' = reduce_exp static env e in |
| Ok ((atom, e'), Checked) |
| |
| and reduce_path static env e p k : exp result = |
| match p.it with |
| | RootP -> k e p |
| | IdxP (p1, e1) -> |
| let** e1' = reduce_exp static env e1 in |
| let k' e' p1' = |
| match e'.it, e1'.it with |
| | ListE es, NumE (`Nat i) |
| when (static || is_value_exp e') && i < Z.of_int (List.length es) -> |
| let** es' = |
| map_results (fun (j, eJ) -> |
| if Z.of_int j = i then k eJ p1' else Ok eJ |
| ) (List.mapi Pair.make es) |
| in |
| Ok (ListE es' $> e') |
| | ListE _es, NumE (`Nat _) -> |
| let** e'' = k e' (IdxP (p1', e1') $> p) in |
| Error e'' |
| | _ -> |
| k e' (IdxP (p1', e1') $> p) |
| in |
| reduce_path static env e p1 k' |
| | SliceP (p1, e1, e2) -> |
| let** e1' = reduce_exp static env e1 in |
| let** e2' = reduce_exp static env e2 in |
| let k' e' p1' = |
| match e'.it, e1'.it, e2'.it with |
| | ListE es, NumE (`Nat i), NumE (`Nat n) when Z.(i + n) < Z.of_int (List.length es) -> |
| let e1' = ListE Lib.List.(take (Z.to_int i) es) $> e' in |
| let e2' = ListE Lib.List.(take (Z.to_int n) (drop (Z.to_int i) es)) $> e' in |
| let e3' = ListE Lib.List.(drop Z.(to_int (i + n)) es) $> e' in |
| let** e2'' = k e2' p1' in |
| reduce_exp static env (CatE (e1', CatE (e2'', e3') $> e') $> e') |
| | ListE _es, NumE (`Nat _), NumE (`Nat _) -> |
| let** e'' = k e' (SliceP (p1', e1', e2') $> p) in |
| Error e'' |
| | _ -> |
| k e' (SliceP (p1', e1', e2') $> p) |
| in |
| reduce_path static env e p1 k' |
| | DotP (p1, atom) -> |
| let k' e' p1' = |
| match e'.it with |
| | StrE (efs, Checked) -> |
| let tfs = as_struct_typ env e'.note e'.at in |
| let** ef_chs' = |
| map_results (fun (atomI, eI) -> |
| if Eq.eq_atom atomI atom then |
| let** eI' = k eI p1' in |
| reduce_expfield static env e'.note tfs Unchecked (atomI, eI') |
| else Ok ((atomI, eI), Checked) |
| ) efs |
| in |
| let efs', chs' = List.split ef_chs' in |
| let ch' = if List.for_all ((=) Checked) chs' then Checked else Unchecked in |
| Ok (StrE (efs', ch') $> e') |
| | _ -> |
| k e' (DotP (p1', atom) $> p) |
| in |
| reduce_path static env e p1 k' |
| |
| and reduce_arg static env a : arg result = |
| Debug.(log "il.reduce_arg" |
| (fun _ -> fmt "%s" (il_arg a)) |
| (fun a' -> fmt "%s" (il_result il_arg a')) |
| ) @@ fun _ -> |
| match a.it with |
| | ExpA e -> let** e' = reduce_exp static env e in Ok (ExpA e' $ a.at) |
| | TypA _t -> Ok a (* types are reduced on demand *) |
| | DefA _id -> Ok a |
| | GramA _g -> Ok a |
| |
| and reduce_exp_call static env id args clauses at : exp option result = |
| match clauses with |
| | [] -> |
| Debug.(log "il.reduce_exp_call" |
| (fun _ -> fmt "$%s%s / -" id.it (il_args args)) |
| (fun r -> fmt "%s" (il_opt_result il_exp r)) |
| ) @@ fun _ -> |
| Error None |
| | {it = DefD (_ps, args', e, prems); _}::clauses' -> |
| Debug.(log "il.reduce_exp_call" |
| (fun _ -> fmt "$%s%s / $%s%s" id.it (il_args args) id.it (il_args args')) |
| (fun r -> fmt "%s" (il_opt_result il_exp r)) |
| ) @@ fun _ -> |
| assert (List.for_all (is_normal_arg static env) args); |
| match match_list (match_arg static) env Subst.empty args args' with |
| | Error _ -> reduce_exp_call static env id args clauses' at |
| | Ok None -> |
| if not !assume_coherent_matches then Ok None else |
| (match reduce_exp_call static env id args clauses' at with |
| | Error _ -> Ok None |
| | ok -> ok |
| ) |
| | Ok (Some s) -> |
| match reduce_prems env s prems with |
| | Ok (Some _s') -> Ok (Some (Subst.subst_exp s e)) |
| | Error _ -> reduce_exp_call static env id args clauses' at |
| | Ok None -> |
| if not !assume_coherent_matches then Ok None else |
| match reduce_exp_call static env id args clauses' at with |
| | Error _ -> Ok None |
| | ok -> ok |
| |
| and reduce_prems env s prems : subst option result = |
| Debug.(log "il.reduce_prems" |
| (fun _ -> fmt "%s [%s]" (list il_prem prems) (il_subst s)) |
| (fun r -> fmt "%s" (il_opt_result il_subst r)) |
| ) @@ fun _ -> |
| match prems with |
| | [] -> Ok (Some s) |
| | prem::prems -> |
| let** so = reduce_prem env (Subst.subst_prem s prem) in |
| match so with |
| | Some s' -> reduce_prems env (Subst.union s s') prems |
| | None -> Ok None |
| |
| and reduce_prem env prem : subst option result = |
| match prem.it with |
| | RulePr _ -> Ok None |
| | IfPr e -> |
| let** e' = reduce_exp false env e in |
| (match e'.it with |
| | BoolE b -> if b then Ok (Some Subst.empty) else Error None |
| | _ -> Ok None |
| ) |
| | ElsePr -> Ok (Some Subst.empty) |
| | LetPr (_qs, e1, e2) -> |
| let** e1' = reduce_exp false env e1 in |
| let** e2' = reduce_exp false env e2 in |
| match_exp false env Subst.empty e2' e1' |
| | IterPr (prem1, iterexp) -> |
| let** iter', xes' = reduce_iterexp false env iterexp in |
| (* Distinguish between let-defined variables, which flow outwards, |
| * and others, which are assumed to flow inwards. *) |
| let rec is_let_bound prem (x, _) = |
| match prem.it with |
| | LetPr (qs, _, _) -> Free.(Set.mem x.it (bound_quants qs).varid) |
| | IterPr (premI, (_iter1', xes1')) -> |
| let xes1_out, _ = List.partition (is_let_bound premI) xes1' in |
| List.exists (fun (_, e1) -> Free.(Set.mem x.it (free_exp e1).varid)) xes1_out |
| | _ -> false |
| in |
| let xes_out, xes_in = List.partition (is_let_bound prem) xes' in |
| let xs_out, es_out = List.split xes_out in |
| let xs_in, es_in = List.split xes_in in |
| if not (List.for_all is_head_normal_exp es_in) || iter' <= List1 && es_in = [] then |
| (* We don't know the number of iterations (yet): can't do anything. *) |
| Ok None |
| else |
| (match iter' with |
| | Opt -> |
| (* Iterationen values es_in are in hnf, so got to be options. *) |
| let eos_in = List.map as_opt_exp es_in in |
| if List.for_all Option.is_none eos_in then |
| (* Iterating over empty options: nothing to do. *) |
| Ok (Some Subst.empty) |
| else if List.for_all Option.is_some eos_in then |
| (* All iteration variables are non-empty: reduce body. *) |
| let es1_in = List.map Option.get eos_in in |
| (* s substitutes in-bound iteration variables with corresponding |
| * values. *) |
| let s = List.fold_left2 Subst.add_varid Subst.empty xs_in es1_in in |
| let*** s' = reduce_prem env (Subst.subst_prem s prem1) in |
| (* Body is true: now reverse-match out-bound iteration values |
| * against iteration sources. *) |
| List.fold_left (fun r (xI, eI) -> |
| let*** s = r in |
| let tI = match eI.note.it with IterT (tI, _) -> tI | _ -> assert false in |
| match_exp' false env s (OptE (Some (Subst.subst_exp s' (VarE xI $$ xI.at % tI))) $> eI) eI |
| ) (Ok (Some Subst.empty)) xes_out |
| else |
| (* Inconsistent arity of iteration values. *) |
| Error None |
| | List | List1 -> |
| (* Unspecified iteration count: get length from (first) iteration value |
| * and start over; es_in are in hnf, so got to be lists. *) |
| let n = List.length (as_list_exp (List.hd es_in)) in |
| if iter' = List || n >= 1 then |
| let en = NumE (`Nat (Z.of_int n)) $$ prem.at % (NumT `NatT $ prem.at) in |
| reduce_prem env (IterPr (prem1, (ListN (en, None), xes')) $> prem) |
| else |
| (* List is empty although it is List1: inconsistency. *) |
| Error None |
| | ListN ({it = NumE (`Nat n'); _}, xo) -> |
| (* Iterationen values es_in are in hnf, so got to be lists. *) |
| let ess_in = List.map as_list_exp es_in in |
| let ns = List.map List.length ess_in in |
| let n = Z.to_int n' in |
| if List.for_all ((=) n) ns then |
| (* All in-bound lists have the expected length: reduce body, |
| * once for each tuple of values from the iterated lists. *) |
| let** rs = List.init n (fun i -> |
| let esI_in = List.map (fun es -> List.nth es i) ess_in in |
| (* s substitutes in-bound iteration variables with corresponding |
| * values for this respective iteration. *) |
| let s = List.fold_left2 Subst.add_varid Subst.empty xs_in esI_in in |
| (* Add iteration counter variable if used. *) |
| let s' = |
| Option.fold xo ~none:s ~some:(fun x -> |
| let en = NumE (`Nat (Z.of_int i)) $$ x.at % (NumT `NatT $ x.at) in |
| Subst.add_varid s x en |
| ) |
| in |
| reduce_prem env (Subst.subst_prem s' prem1) |
| ) |> map_results Fun.id |
| in |
| if List.mem None rs then Ok None else |
| (* Body was true in every iteration: now reverse-match out-bound |
| * iteration variables against iteration sources. *) |
| let ss = List.map Option.get rs in |
| (* Aggregate the out-lists for each out-bound variable. *) |
| let es_out' = |
| List.map2 (fun xI eI -> |
| let tI = match eI.note.it with IterT (tI, _) -> tI | _ -> assert false in |
| let esI = List.map (fun sJ -> |
| Subst.subst_exp sJ (VarE xI $$ xI.at % tI) |
| ) ss |
| in ListE esI $> eI |
| ) xs_out es_out |
| in |
| (* Reverse-match out-bound list values against iteration sources. *) |
| match_list (match_exp false) env Subst.empty es_out' es_out |
| else |
| (* Inconsistent list lengths: can't perform mapping. |
| * (This is a stuck computation, i.e., undefined.) *) |
| Error None |
| | ListN _ -> Ok None |
| ) |
| |
| |
| (* Matching *) |
| |
| (* Iteration *) |
| |
| and match_iter static env s iter1 iter2 : subst option result = |
| match iter1, iter2 with |
| | Opt, Opt -> Ok (Some s) |
| | List, List -> Ok (Some s) |
| | List1, List1 -> Ok (Some s) |
| | ListN (e1, _ido1), ListN (e2, _ido2) -> match_exp static env s e1 e2 |
| | (Opt | List1 | ListN _), List -> Ok (Some s) |
| | _, _ -> Error None |
| |
| |
| (* Types *) |
| |
| and match_typ env s t1 t2 : subst option result = |
| Debug.(log "il.match_typ" |
| (fun _ -> fmt "%s / %s" (il_typ t1) (il_typ (Subst.subst_typ s t2))) |
| (fun r -> fmt "%s" (il_result (opt il_subst) r)) |
| ) @@ fun _ -> |
| match t1.it, t2.it with |
| | _, VarT (id, []) when Subst.mem_typid s id -> |
| match_typ env s t1 (Subst.subst_typ s t2) |
| | _, VarT (id, []) when not (Map.mem id.it env.typs) -> |
| (* An unbound type is treated as a pattern variable *) |
| Ok (Some (Subst.add_typid s id t1)) |
| | VarT (id1, args1), VarT (id2, args2) when id1.it = id2.it -> |
| (* Optimization for the common case where args are absent or equivalent. *) |
| (match match_list (static3 match_arg) env s args1 args2 with |
| | Ok so -> Ok so |
| | Error _ -> |
| (* If that fails, fall back to reduction. *) |
| let** t1' = reduce_typ env t1 in |
| let** t2' = reduce_typ env t2 in |
| if Eq.(eq_typ t1 t1' && eq_typ t2 t2') then Ok None else |
| match_typ env s t1' t2' |
| ) |
| | VarT _, _ -> |
| let** t1' = reduce_typ env t1 in |
| if Eq.eq_typ t1 t1' then Ok None else |
| match_typ env s t1' t2 |
| | _, VarT _ -> |
| let** t2' = reduce_typ env t2 in |
| if Eq.eq_typ t2 t2' then Ok None else |
| match_typ env s t1 t2' |
| | TupT xts1, TupT xts2 -> |
| match_list match_typbind env s xts1 xts2 |
| | IterT (t11, iter1), IterT (t21, iter2) -> |
| let*** s' = match_typ env s t11 t21 in |
| static3 match_iter env s' iter1 iter2 |
| | _, _ -> Ok None |
| |
| and match_typbind env s (x1, t1) (x2, t2) = |
| let s' = Subst.add_varid s x2 (VarE x1 $$ x1.at % t1) in |
| match_typ env s' t1 (Subst.subst_typ s t2) |
| |
| |
| (* Expressions *) |
| |
| and match_exp_typ static env s e t : subst option result = |
| Debug.(log "il.match_exp_typ" |
| (fun _ -> fmt "%s / %s [%s]" (il_exp e) (il_typ t) (il_subst s)) |
| (fun r -> fmt "%s" (il_result (opt il_subst) r)) |
| ) @@ fun _ -> |
| let** e' = reduce_exp static env e in |
| let** t' = reduce_typ env t in |
| match e'.it, t'.it with |
| | TupE es, TupT xts when List.length es = List.length xts -> |
| Ok (Some (List.fold_left (fun s (e, (x, _)) -> |
| Subst.add_varid s x (Subst.subst_exp s e) |
| ) s (List.combine es xts))) |
| | _, _ -> Error None |
| |
| and match_exp static env s e1 e2 : subst option result = |
| let** e1' = reduce_exp static env e1 in |
| match_exp' static env s e1' e2 |
| |
| and match_exp' static env s e1 e2 : subst option result = |
| Debug.(log "il.match_exp" |
| (fun _ -> fmt "%s : %s / %s" (il_exp e1) (il_typ e1.note) (il_exp (Subst.subst_exp s e2))) |
| (fun r -> fmt "%s" (il_result (opt il_subst) r)) |
| ) @@ fun _ -> |
| assert (is_normal_exp static env e1); |
| let** e2' = reduce_exp static env (Subst.subst_exp s e2) in |
| (* HACK around subtype elim pass introducing calls on LHS's *) |
| if Eq.eq_exp e1 e2 && (static || is_value_exp e1 && is_value_exp e2) then Ok (Some s) else |
| match e1.it, e2'.it with |
| | _, VarE id when Subst.mem_varid s id -> |
| (* A pattern variable already in the substitution is non-linear *) |
| if equiv_exp static env e1 (Subst.subst_exp s e2) then |
| Ok (Some s) |
| else |
| Error None |
| | _, VarE id -> |
| (* Treat as a fresh pattern variable. *) |
| let** e1' = reduce_exp static env (SubE (e1, e1.note, e2.note) $$ e1.at % e2.note) in |
| Ok (Some (Subst.add_varid s id e1')) |
| | BoolE b1, BoolE b2 when b1 = b2 -> Ok (Some s) |
| | NumE n1, NumE n2 when n1 = n2 -> Ok (Some s) |
| | TextE s1, TextE s2 when s1 = s2 -> Ok (Some s) |
| | NumE n1, UnE (`PlusOp, _, e21) when not (Num.is_neg n1) -> |
| match_exp static env s e1 e21 |
| | NumE n1, UnE (`MinusOp, _, e21) when Num.is_neg n1 -> |
| let** e1' = reduce_exp static env (NumE (Num.abs n1) $> e1) in |
| match_exp static env s e1' e21 |
| | NumE n1, CvtE (e21, nt1, _nt2) -> |
| (match Num.cvt nt1 n1 with |
| | Some n1' -> match_exp static env s (NumE n1' $> e1) e21 |
| | None -> Error None |
| ) |
| (* |
| | UnE (op1, _, e11), UnE (op2, _, e21) when op1 = op2 -> match_exp' env s e11 e21 |
| | BinE (e11, op1, e12), BinE (e21, op2, e22) when op1 = op2 -> |
| let** s' = match_exp' env s e11 e21 in match_exp' env s' e12 e22 |
| | CmpE (e11, op1, e12), CmpE (e21, op2, e22) when op1 = op2 -> |
| let** s' = match_exp' env s e11 e21 in match_exp' env s' e12 e22 |
| | (EpsE | SeqE []), (EpsE | SeqE []) -> Some s |
| *) |
| | ListE es1, ListE es2 |
| | TupE es1, TupE es2 -> match_list (match_exp' static) env s es1 es2 |
| | _, TupE es2 -> |
| let es1 = eta_tup_exp env e1 in |
| match_list (match_exp' static) env s es1 es2 |
| | ListE es1, CatE ({it = ListE es21; _} as e21, e22) |
| when List.length es21 <= List.length es1 -> |
| let es11, es12 = Lib.List.split (List.length es21) es1 in |
| let*** s' = match_exp' static env s (ListE es11 $> e1) e21 in |
| match_exp' static env s' (ListE es12 $> e1) e22 |
| | ListE es1, CatE (e21, ({it = ListE es22; _} as e22)) |
| when List.length es22 <= List.length es1 -> |
| let es11, es12 = Lib.List.split (List.length es22) es1 in |
| let*** s' = match_exp' static env s (ListE es11 $> e1) e21 in |
| match_exp' static env s' (ListE es12 $> e1) e22 |
| (* |
| | IdxE (e11, e12), IdxE (e21, e22) |
| | CommaE (e11, e12), CommaE (e21, e22) |
| | CompE (e11, e12), CompE (e21, e22) -> |
| let** s' = match_exp' env s e11 e21 in match_exp' env s' e12 e22 |
| | SliceE (e11, e12, e13), SliceE (e21, e22, e23) -> |
| let** s' = match_exp' env s e11 e21 in |
| let** s'' = match_exp' env s' e12 e22 in |
| match_exp' env s'' e13 e23 |
| | UpdE (e11, p1, e12), UpdE (e21, p2, e22) |
| | ExtE (e11, p1, e12), ExtE (e21, p2, e22) -> |
| let** s' = match_exp' env s e11 e21 in |
| let** s'' = match_path env s' p1 p2 in |
| match_exp' env s'' e12 e22 |
| *) |
| | StrE (efs1, Checked), StrE (efs2, _) -> |
| match_list (match_expfield static) env s efs1 efs2 |
| (* |
| | DotE (e11, atom1), DotE (e21, atom2) when Eq.eq_atom atom1 atom2 -> |
| match_exp' env s e11 e21 |
| | LenE e11, LenE e21 -> match_exp' env s e11 e21 |
| *) |
| | CaseE (op1, _, _), CaseE (op2, e21, _) when Eq.eq_mixop op1 op2 -> |
| (* Beta-expand to allow unchecked e1 without losing checks *) |
| let** e11' = reduce_exp static env (UncaseE (e1, op2) $> e21) in |
| match_exp' static env s e11' e21 |
| | _, CaseE (op2, e21, _) |
| when List.length (as_variant_typ env e2.note e2.at) = 1 -> |
| (* Beta-expand irrefutable case pattern *) |
| let** e11' = reduce_exp static env (UncaseE (e1, op2) $> e21) in |
| match_exp' static env s e11' e21 |
| (* |
| | CallE (id1, args1), CallE (id2, args2) when id1.it = id2.it -> |
| match_list (match_arg static) env s args1 args2 |
| *) |
| | _, UncaseE (e21, mixop) -> |
| let** e1' = |
| reduce_exp static env (CaseE (mixop, e1, Unchecked) $$ e1.at % e21.note) in |
| match_exp' static env s e1' e21 |
| | _, ProjE (e21, 0) -> (* only valid on unary tuples! *) |
| match_exp' static env s (TupE [e1] $$ e1.at % e21.note) e21 |
| (* |
| | IterE (e11, iter1), IterE (e21, iter2) -> |
| let** s' = match_exp' env s e11 e21 in |
| match_iterexp env s' iter1 iter2 |
| | _, IterE (e21, iter2) -> |
| let e11, iter1 = eta_iter_exp env e1 in |
| let** s' = match_exp' env s e11 e21 in |
| match_iterexp env s' iter1 iter2 |
| *) |
| | OptE None, IterE (_e21, (Opt, xes)) -> |
| List.fold_left (fun r (_xI, eI) -> |
| let*** s = r in match_exp' static env s e1 eI |
| ) (Ok (Some s)) xes |
| | OptE (Some e11), IterE (e21, (Opt, xes)) -> |
| let*** s' = match_exp' static env s e11 e21 in |
| let*** s'' = |
| List.fold_left (fun r (xI, exI) -> |
| let*** s = r in |
| let tI = match exI.note.it with IterT (tI, _) -> tI | _ -> assert false in |
| match_exp' static env s (OptE (Some (Subst.subst_exp s' (VarE xI $$ exI.at % tI))) $> e2) exI |
| ) (Ok (Some (List.fold_left Subst.remove_varid s (List.map fst xes)))) xes |
| in Ok (Some (Subst.union s'' s)) (* re-add possibly locally shadowed bindings *) |
| | ListE _es1, IterE (e21, (List, xes)) -> |
| let en = VarE ("_" $ e2.at) $$ e2.at % (NumT `NatT $ e2.at) in |
| match_exp' static env s e1 (IterE (e21, (ListN (en, None), xes)) $> e2) |
| | ListE es1, IterE (e21, (List1, xes)) -> |
| if es1 = [] then Error None else |
| let en = VarE ("_" $ e2.at) $$ e2.at % (NumT `NatT $ e2.at) in |
| match_exp' static env s e1 (IterE (e21, (ListN (en, None), xes)) $> e2) |
| | ListE es1, IterE (e21, (ListN (en, id_opt), xes)) -> |
| let en' = NumE (`Nat (Z.of_int (List.length es1))) $$ e1.at % (NumT `NatT $ e1.at) in |
| let*** s' = match_exp' static env s en' en in |
| let s'' = List.fold_left Subst.remove_varid s' (List.map fst xes) in (* local subst *) |
| (* match each list element against iteration body for corresponding subst *) |
| let** sos = |
| map_results (fun (j, e1J) -> |
| let s''' = |
| match id_opt with |
| | None -> s'' |
| | Some xJ -> |
| Subst.add_varid s'' xJ |
| (NumE (`Nat (Z.of_int j)) $$ e1.at % (NumT `NatT $ e1.at)) |
| in match_exp' static env s''' e1J (Subst.subst_exp s''' e21) |
| ) (List.mapi Pair.make es1) |
| in |
| let*** ss = Ok (Lib.List.flatten_opt sos) in |
| (* now project list for each iteration variable and match against rhs's *) |
| let xs, exs = List.split xes in |
| let*** s''' = |
| match_list (fun env s xI exI -> |
| let tI = match exI.note.it with IterT (tI, _) -> tI | _ -> assert false in |
| let eI = ListE (List.map (fun sJ -> Subst.subst_exp sJ (VarE xI $$ exI.at % tI)) ss) $> e2 in |
| match_exp' static env s eI exI |
| ) env s' xs exs |
| in Ok (Some (Subst.union s''' s)) (* re-add possibly locally shadowed bindings *) |
| | _, IterE (e21, iter2) -> |
| let e11, iter1 = eta_iter_exp env e1 in |
| let** e11' = reduce_exp static env e11 in |
| let** iter1' = reduce_iterexp static env iter1 in |
| let*** s' = match_exp' static env s e11' e21 in |
| match_iterexp static env s' iter1' iter2 |
| | SubE (e11, t11, _t12), SubE (e21, t21, _t22) when sub_typ env t11 t21 -> |
| let** e11' = reduce_exp static env (SubE (e11, t11, t21) $> e21) in |
| match_exp' static env s e11' e21 |
| | SubE (_e11, t11, _t12), SubE (_e21, t21, _t22) when disj_typ env t11 t21 -> |
| Error None |
| | _, SubE (e21, t21, _t22) -> |
| if sub_typ env e1.note t21 then |
| let** e1' = reduce_exp static env (SubE (e1, e1.note, t21) $> e21) in |
| match_exp' static env s e1' e21 |
| else if is_head_normal_exp e1 then |
| let** t21' = reduce_typ env t21 in |
| let** b = |
| match e1.it, t21'.it with |
| | BoolE _, BoolT |
| | NumE _, NumT _ |
| | TextE _, TextT -> Ok true |
| | CaseE (op, _, _), VarT _ -> |
| let** dt = reduce_typdef env t21 in |
| (match dt.it with |
| | VariantT tcs -> |
| (* Assumes that we only have shallow subtyping. *) |
| Ok (List.exists (fun (opN, _, _) -> Eq.eq_mixop opN op) tcs) |
| | _ -> Error false |
| ) |
| | VarE id1, _ -> |
| let t1 = Env.find_var env id1 in |
| let** t1' = reduce_typ env t1 in |
| Ok (sub_typ env t1' t21) |
| | _, _ -> Error false |
| in |
| if b |
| then match_exp' static env s {e1 with note = t21} e21 |
| else Ok None |
| else Ok None |
| | _, _ when is_head_normal_exp e1 -> Error None |
| | _, _ -> Ok None |
| |
| and match_expfield static env s (atom1, e1) (atom2, e2) = |
| assert (Eq.eq_atom atom1 atom2); |
| match_exp' static env s e1 (Subst.subst_exp s e2) |
| |
| and match_iterexp static env s (iter1, _ids1) (iter2, _ids2) = |
| match_iter static env s iter1 iter2 |
| |
| |
| and eta_tup_exp env e : exp list = |
| let xts = as_tup_typ env e.note e.at in |
| List.fold_left (fun (res', i, s) (xI, tI) -> |
| let eI' = ProjE (e, i) $$ e.at % Subst.subst_typ s tI in |
| let s' = Subst.add_varid s xI eI' in |
| (eI'::res', i + 1, s') |
| ) ([], 0, Subst.empty) xts |> Lib.fst3 |> List.rev |
| |
| and eta_iter_exp env e : exp * iterexp = |
| let t, it = as_iter_typ env e.note e.at in |
| match it with |
| | Opt -> TheE e $$ e.at % t, (Opt, []) |
| | List -> |
| let id = "_i_" $ e.at in |
| let len = LenE e $$ e.at % (NumT `NatT $ e.at) in |
| IdxE (e, VarE id $$ e.at % (NumT `NatT $ e.at)) $$ e.at % t, |
| (ListN (len, Some id), []) |
| | _ -> assert false |
| |
| |
| (* Grammars *) |
| |
| and match_sym env s g1 g2 : subst option result = |
| Debug.(log "il.match_sym" |
| (fun _ -> fmt "%s / %s" (il_sym g1) (il_sym g2)) |
| (fun r -> fmt "%s" (il_result (opt il_subst) r)) |
| ) @@ fun _ -> |
| match g1.it, g2.it with |
| | _, VarG (id, []) when Subst.mem_gramid s id -> |
| match_sym env s g1 (Subst.subst_sym s g2) |
| | _, VarG (id, []) when not (Map.mem id.it env.grams) -> |
| (* An unbound grammar is treated as a pattern variable *) |
| Ok (Some (Subst.add_gramid s id g1)) |
| | VarG (id1, args1), VarG (id2, args2) when id1.it = id2.it -> |
| match_list (static3 match_arg) env s args1 args2 |
| | IterG (g11, iter1), IterG (g21, iter2) -> |
| let*** s' = match_sym env s g11 g21 in |
| static3 match_iterexp env s' iter1 iter2 |
| | _, _ -> Ok None |
| |
| |
| (* Parameters *) |
| |
| and match_arg static env s a1 a2 : subst option result = |
| Debug.(log "il.match_arg" |
| (fun _ -> fmt "%s / %s" (il_arg a1) (il_arg a2)) |
| (fun r -> fmt "%s" (il_result (opt il_subst) r)) |
| ) @@ fun _ -> |
| match a1.it, a2.it with |
| | ExpA e1, ExpA e2 -> match_exp static env s e1 e2 |
| | TypA t1, TypA t2 -> match_typ env s t1 t2 |
| | DefA id1, DefA id2 -> Ok (Some (Subst.add_defid s id1 id2)) |
| | GramA g1, GramA g2 -> match_sym env s g1 g2 |
| | _, _ -> assert false |
| |
| |
| (* Type Equivalence *) |
| |
| and equiv_typ env t1 t2 = |
| Debug.(log "il.equiv_typ" |
| (fun _ -> fmt "%s == %s" (il_typ t1) (il_typ t2)) Bool.to_string |
| ) @@ fun _ -> |
| match t1.it, t2.it with |
| | VarT (id1, as1), VarT (id2, as2) -> |
| id1.it = id2.it && equiv_list equiv_arg env as1 as2 || |
| (* optimization *) |
| let t1' = reduce_typ_ok env t1 in |
| let t2' = reduce_typ_ok env t2 in |
| (* TODO(3, rossberg): be more expressive *) |
| (t1 <> t1' || t2 <> t2') && equiv_typ env t1' t2' || |
| Eq.eq_deftyp (reduce_typdef_ok env t1') (reduce_typdef_ok env t2') |
| | VarT _, _ -> |
| let t1' = reduce_typ_ok env t1 in |
| t1 <> t1' && equiv_typ env t1' t2 |
| | _, VarT _ -> |
| let t2' = reduce_typ_ok env t2 in |
| t2 <> t2' && equiv_typ env t1 t2' |
| | TupT ets1, TupT ets2 -> equiv_tup env Subst.empty ets1 ets2 |
| | IterT (t11, iter1), IterT (t21, iter2) -> |
| equiv_typ env t11 t21 && equiv_iter env iter1 iter2 |
| | _, _ -> |
| t1.it = t2.it |
| |
| and equiv_tup env s xts1 xts2 = |
| match xts1, xts2 with |
| | (x1, t1)::xts1', (x2, t2)::xts2' -> |
| equiv_typ env t1 (Subst.subst_typ s t2) && |
| equiv_tup env (Subst.add_varid s x2 (VarE x1 $$ x1.at % t1)) xts1' xts2' |
| | _, _ -> xts1 = xts2 |
| |
| and equiv_iter env iter1 iter2 = |
| match iter1, iter2 with |
| | ListN (e1, ido1), ListN (e2, ido2) -> |
| equiv_exp true env e1 e2 && Option.equal (fun id1 id2 -> id1.it = id2.it) ido1 ido2 |
| | _, _ -> iter1 = iter2 |
| |
| (* |
| and equiv_prem env pr1 pr2 = |
| match pr1.it, pr2.it with |
| | RulePr (id1, mixop1, e1), RulePr (id2, mixop2, e2) -> |
| id1.it = id2.it && Eq.eq_mixop mixop1 mixop2 && equiv_exp env e1 e2 |
| | IfPr e1, IfPr e2 -> equiv_exp env e1 e2 |
| | LetPr (_qs1, e11, e12), LetPr (_qs2, e21, e22) -> |
| equiv_exp env e11 e21 && equiv_exp env e12 e22 |
| | IterPr (pr11, iter1), IterPr (pr21, iter2) -> |
| equiv_prem env pr11 pr21 && equiv_iter env (fst iter1) (fst iter2) |
| | pr1', pr2' -> pr1' = pr2' |
| *) |
| |
| and equiv_exp static env e1 e2 = |
| Debug.(log "il.equiv_exp" |
| (fun _ -> fmt "%s == %s" (il_exp e1) (il_exp e2)) Bool.to_string |
| ) @@ fun _ -> |
| (* TODO(3, rossberg): this does not reduce inner type arguments *) |
| match reduce_exp static env e1, reduce_exp static env e2 with |
| | Ok e1', Ok e2' -> Eq.eq_exp e1' e2' |
| | (Ok e1' | Error e1'), (Ok e2' | Error e2') when static -> Eq.eq_exp e1' e2' |
| | Error _, _ -> |
| Error.error e1.at "validation" |
| "expression failed to evaluate during pattern-matching" |
| | _, Error _ -> |
| Error.error e2.at "validation" |
| "expression failed to evaluate during pattern-matching" |
| |
| and equiv_sym _env g1 g2 = |
| Debug.(log "il.equiv_sym" |
| (fun _ -> fmt "%s == %s" (il_sym g1) (il_sym g2)) Bool.to_string |
| ) @@ fun _ -> |
| Eq.eq_sym g1 g2 |
| |
| and equiv_arg env a1 a2 = |
| Debug.(log "il.equiv_arg" |
| (fun _ -> fmt "%s == %s" (il_arg a1) (il_arg a2)) Bool.to_string |
| ) @@ fun _ -> |
| match a1.it, a2.it with |
| | ExpA e1, ExpA e2 -> equiv_exp true env e1 e2 |
| | TypA t1, TypA t2 -> equiv_typ env t1 t2 |
| | DefA id1, DefA id2 -> id1.it = id2.it |
| | GramA g1, GramA g2 -> equiv_sym env g1 g2 |
| | _, _ -> false |
| |
| |
| and equiv_functyp env (ps1, t1) (ps2, t2) = |
| List.length ps1 = List.length ps2 && |
| match equiv_params env ps1 ps2 with |
| | None -> false |
| | Some s -> equiv_typ env t1 (Subst.subst_typ s t2) |
| |
| and equiv_params env ps1 ps2 = |
| List.fold_left2 (fun s_opt p1 p2 -> |
| let* s = s_opt in |
| match p1.it, (Subst.subst_param s p2).it with |
| | ExpP (id1, t1), ExpP (id2, t2) -> |
| if not (equiv_typ env t1 t2) then None else |
| Some (Subst.add_varid s id2 (VarE id1 $$ p1.at % t1)) |
| | TypP _, TypP _ -> Some s |
| | DefP (id1, ps1, t1), DefP (id2, ps2, t2) -> |
| if not (equiv_functyp env (ps1, t1) (ps2, t2)) then None else |
| Some (Subst.add_defid s id2 id1) |
| | GramP (id1, ps1, t1), GramP (id2, ps2, t2) -> |
| if not (equiv_functyp env (ps1, t1) (ps2, t2)) then None else |
| Some (Subst.add_gramid s id2 (VarG (id1, []) $ p1.at)) |
| | _, _ -> assert false |
| ) (Some Subst.empty) ps1 ps2 |
| |
| |
| (* Subtyping *) |
| |
| and sub_prems _env prems1 prems2 = |
| prems2 = [] || |
| List.length prems1 = List.length prems2 && |
| List.for_all2 Eq.eq_prem prems1 prems2 |
| |
| and sub_typ env t1 t2 = sub_typ' env [] t1 t2 |
| |
| and sub_typ' env assum t1 t2 = |
| Debug.(log "il.sub_typ" |
| (fun _ -> fmt "%s <: %s" (il_typ t1) (il_typ t2)) Bool.to_string |
| ) @@ fun _ -> |
| equiv_typ env t1 t2 || |
| List.exists (fun (t1', t2') -> Eq.eq_typ t1 t1' && Eq.eq_typ t2 t2') assum || |
| let t1' = reduce_typ_ok env t1 in |
| let t2' = reduce_typ_ok env t2 in |
| let assum' = (t1, t2)::assum in |
| match t1'.it, t2'.it with |
| (*| NumT nt1, NumT nt2 -> Num.sub nt1 nt2*) |
| | TupT ets1, TupT ets2 -> sub_tup env assum' Subst.empty ets1 ets2 |
| | VarT _, VarT _ -> |
| (match (reduce_typdef_ok env t1').it, (reduce_typdef_ok env t2').it with |
| | StructT tfs1, StructT tfs2 -> |
| List.for_all (fun (atom, (t21, _qs2, prems2), _) -> |
| match find_field tfs1 atom with |
| | Some (t11, _qs1, prems1) -> |
| sub_typ' env assum' t11 t21 && sub_prems env prems1 prems2 |
| | None -> false |
| ) tfs2 |
| | VariantT tcs1, VariantT tcs2 -> |
| List.for_all (fun (mixop, (t11, _qs1, prems1), _) -> |
| match find_case tcs2 mixop with |
| | Some (t21, _qs2, prems2) -> |
| sub_typ' env assum' t11 t21 && sub_prems env prems1 prems2 |
| | None -> false |
| ) tcs1 |
| | _, _ -> false |
| ) |
| | IterT (t11, it1), IterT (t21, it2) -> |
| sub_typ' env assum' t11 t21 && Eq.eq_iter it1 it2 |
| | _, _ -> |
| false |
| |
| and sub_tup env assum s xts1 xts2 = |
| match xts1, xts2 with |
| | (x1, t1)::xts1', (x2, t2)::xts2' -> |
| sub_typ' env assum t1 (Subst.subst_typ s t2) && |
| sub_tup env assum (Subst.add_varid s x2 (VarE x1 $$ x1.at % t1)) xts1' xts2' |
| | _, _ -> xts1 = xts2 |
| |
| |
| and find_field tfs atom = |
| List.find_opt (fun (atom', _, _) -> Eq.eq_atom atom' atom) tfs |> Option.map Lib.snd3 |
| |
| and find_case tcs op = |
| List.find_opt (fun (op', _, _) -> Eq.eq_mixop op' op) tcs |> Option.map Lib.snd3 |
| |
| |
| (* Type Disjointness *) |
| |
| and disj_typ env t1 t2 = |
| Debug.(log "il.disj_typ" |
| (fun _ -> fmt "%s >< %s" (il_typ t1) (il_typ t2)) Bool.to_string |
| ) @@ fun _ -> |
| match t1.it, t2.it with |
| | VarT _, VarT _ -> |
| (match (reduce_typdef_ok env t1).it, (reduce_typdef_ok env t2).it with |
| | StructT tfs1, StructT tfs2 -> |
| unordered (atoms tfs1) (atoms tfs2) || |
| List.exists (fun (atom, (t2, _qs2, _prems2), _) -> |
| match find_field tfs1 atom with |
| | Some (t1, _qs1, _prems1) -> disj_typ env t1 t2 |
| | None -> true |
| ) tfs2 |
| | VariantT tcs1, VariantT tcs2 -> |
| List.for_all (fun (mixop, (t1, _qs1, _prems1), _) -> |
| match find_case tcs2 mixop with |
| | Some (t2, _qs2, _prems2) -> disj_typ env t1 t2 |
| | None -> true |
| ) tcs1 |
| | _, _ -> true |
| ) |
| | VarT _, _ -> |
| let t1' = reduce_typ_ok env t1 in |
| t1 <> t1' && disj_typ env t1' t2 |
| | _, VarT _ -> |
| let t2' = reduce_typ_ok env t2 in |
| t2 <> t2' && disj_typ env t1 t2' |
| | TupT ets1, TupT ets2 -> disj_tup env Subst.empty ets1 ets2 |
| | IterT (t11, iter1), IterT (t21, iter2) -> |
| disj_typ env t11 t21 || not (Eq.eq_iter iter1 iter2) |
| | _, _ -> |
| not (equiv_typ env t1 t2) |
| |
| and atoms xs = Set.of_list (List.map Print.string_of_atom (List.map Lib.fst3 xs)) |
| |
| and disj_tup env s xts1 xts2 = |
| match xts1, xts2 with |
| | (x1, t1)::xts1', (x2, t2)::xts2' -> |
| disj_typ env t1 (Subst.subst_typ s t2) || |
| disj_tup env (Subst.add_varid s x2 (VarE x1 $$ x1.at % t1)) xts1' xts2' |
| | _, _ -> xts1 <> xts2 |
| |
| |
| |
| (* Export (since OCaml's signature match can't instantiate opt args) *) |
| |
| let reduce_exp = reduce_exp false |
| let reduce_arg = reduce_arg false |
| |
| let match_iter = match_iter false |
| let match_exp = match_exp false |
| let match_arg = match_arg false |