diff options
Diffstat (limited to 'src/monomorphise.ml')
| -rw-r--r-- | src/monomorphise.ml | 244 |
1 files changed, 139 insertions, 105 deletions
diff --git a/src/monomorphise.ml b/src/monomorphise.ml index 7f9eea57..82a35580 100644 --- a/src/monomorphise.ml +++ b/src/monomorphise.ml @@ -35,6 +35,7 @@ let subst_nexp substs nexp = | Nexp_minus (n1,n2) -> re (Nexp_minus (s_snexp n1, s_snexp n2)) | Nexp_exp ne -> re (Nexp_exp (s_snexp ne)) | Nexp_neg ne -> re (Nexp_neg (s_snexp ne)) + | Nexp_app (id,args) -> re (Nexp_app (id,List.map s_snexp args)) in s_snexp substs nexp let rec subst_nc substs (NC_aux (nc,l) as n_constraint) = @@ -270,6 +271,7 @@ let rec size_nvars_nexp (Nexp_aux (ne,_)) = | Nexp_exp n | Nexp_neg n -> size_nvars_nexp n + | Nexp_app (_,args) -> List.concat (List.map size_nvars_nexp args) (* Given a type for a constructor, work out which refinements we ought to produce *) (* TODO collision avoidance *) @@ -596,73 +598,12 @@ let fabricate_nexp l = function | _ -> raise (Reporting_basic.err_general l ("Undefined value at unsupported type " ^ string_of_typ typ)) +(* Used for constant propagation in pattern matches *) type 'a matchresult = | DoesMatch of 'a | DoesNotMatch | GiveUp -let can_match_with_env env (E_aux (e,(l,annot)) as exp0) cases = - let rec findpat_generic check_pat description = function - | [] -> (Reporting_basic.print_err false true l "Monomorphisation" - ("Failed to find a case for " ^ description); None) - | [Pat_aux (Pat_exp (P_aux (P_wild,_),exp),_)] -> Some (exp,[],[]) - | (Pat_aux (Pat_exp (P_aux (P_typ (_,p),_),exp),ann))::tl -> - findpat_generic check_pat description ((Pat_aux (Pat_exp (p,exp),ann))::tl) - | (Pat_aux (Pat_exp (P_aux (P_id id',_),exp),_))::tl - when pat_id_is_variable env id' -> - Some (exp, [(id', exp0)], []) - | (Pat_aux (Pat_when _,_))::_ -> None - | (Pat_aux (Pat_exp (p,exp),_))::tl -> - match check_pat p with - | DoesNotMatch -> findpat_generic check_pat description tl - | DoesMatch (subst,ksubst) -> Some (exp,subst,ksubst) - | GiveUp -> None - in - match e with - | E_id id -> - (match Env.lookup_id id env with - | Enum _ -> - let checkpat = function - | P_aux (P_id id',_) - | P_aux (P_app (id',[]),_) -> - if Id.compare id id' = 0 then DoesMatch ([],[]) else DoesNotMatch - | P_aux (_,(l',_)) -> - (Reporting_basic.print_err false true l' "Monomorphisation" - "Unexpected kind of pattern for enumeration"; GiveUp) - in findpat_generic checkpat (string_of_id id) cases - | _ -> None) - | E_lit (L_aux (lit_e, lit_l)) -> - let checkpat = function - | P_aux (P_lit (L_aux (lit_p, _)),_) -> - if lit_match (lit_e,lit_p) then DoesMatch ([],[]) else DoesNotMatch - | P_aux (P_var (P_aux (P_id id,p_id_annot), kid),_) -> - begin - match lit_e with - | L_num i -> - DoesMatch ([id, E_aux (e,(l,annot))], - [kid,Nexp_aux (Nexp_constant i,Unknown)]) - (* For undefined we fix the type-level size (because there's no good - way to construct an undefined size), but leave the term as undefined - to make the meaning clear. *) - | L_undef -> - let nexp = fabricate_nexp l annot in - let typ = subst_src_typ (KBindings.singleton kid nexp) (typ_of_annot p_id_annot) in - DoesMatch ([id, E_aux (E_cast (typ,E_aux (e,(l,None))),(l,None))], - [kid,nexp]) - | _ -> - (Reporting_basic.print_err false true lit_l "Monomorphisation" - "Unexpected kind of literal for var match"; GiveUp) - end - | P_aux (_,(l',_)) -> - (Reporting_basic.print_err false true l' "Monomorphisation" - "Unexpected kind of pattern for literal"; GiveUp) - in findpat_generic checkpat "literal" cases - | _ -> None - -let can_match exp cases = - let env = Type_check.env_of exp in - can_match_with_env env exp cases - (* Remove top-level casts from an expression. Useful when we need to look at subexpressions to reduce something, but could break type-checking if we used it everywhere. *) @@ -971,7 +912,7 @@ let split_defs splits defs = re (E_field (e',id)) assigns | E_case (e,cases) -> let e',assigns = const_prop_exp substs assigns e in - (match can_match e' cases with + (match can_match e' cases substs assigns with | None -> let assigned_in = List.fold_left (fun vs pe -> IdSet.union vs (assigned_vars_in_pexp pe)) @@ -995,7 +936,7 @@ let split_defs splits defs = re (E_let (LB_aux (LB_val (p,e'), annot), e2')) assigns in if is_value e' && not (is_value e) then - match can_match e' [Pat_aux (Pat_exp (p,e2),(Unknown,None))] with + match can_match e' [Pat_aux (Pat_exp (p,e2),(Unknown,None))] substs assigns with | None -> plain () | Some (e'',bindings,kbindings) -> let e'' = nexp_subst_exp (kbindings_from_list kbindings) e'' in @@ -1105,12 +1046,96 @@ let split_defs splits defs = let cases = List.map (function | FCL_aux (FCL_Funcl (_,pat,exp), ann) -> Pat_aux (Pat_exp (pat,exp),ann)) fcls in - match can_match_with_env env arg cases with + match can_match_with_env env arg cases Bindings.empty Bindings.empty with | Some (exp,bindings,kbindings) -> let substs = bindings_from_list bindings in let result,_ = const_prop_exp substs Bindings.empty exp in if is_value result then Some result else None | None -> None + + and can_match_with_env env (E_aux (e,(l,annot)) as exp0) cases substs assigns = + let rec findpat_generic check_pat description assigns = function + | [] -> (Reporting_basic.print_err false true l "Monomorphisation" + ("Failed to find a case for " ^ description); None) + | [Pat_aux (Pat_exp (P_aux (P_wild,_),exp),_)] -> Some (exp,[],[]) + | (Pat_aux (Pat_exp (P_aux (P_typ (_,p),_),exp),ann))::tl -> + findpat_generic check_pat description assigns ((Pat_aux (Pat_exp (p,exp),ann))::tl) + | (Pat_aux (Pat_exp (P_aux (P_id id',_),exp),_))::tlx + when pat_id_is_variable env id' -> + Some (exp, [(id', exp0)], []) + | (Pat_aux (Pat_when (P_aux (P_id id',_),guard,exp),_))::tl + when pat_id_is_variable env id' -> begin + let substs = Bindings.add id' exp0 substs in + let (E_aux (guard,_)),assigns = const_prop_exp substs assigns guard in + match guard with + | E_lit (L_aux (L_true,_)) -> Some (exp,[(id',exp0)],[]) + | E_lit (L_aux (L_false,_)) -> findpat_generic check_pat description assigns tl + | _ -> None + end + | (Pat_aux (Pat_when (p,guard,exp),_))::tl -> begin + match check_pat p with + | DoesNotMatch -> findpat_generic check_pat description assigns tl + | DoesMatch (vsubst,ksubst) -> begin + let guard = nexp_subst_exp (kbindings_from_list ksubst) guard in + let substs = bindings_union substs (bindings_from_list vsubst) in + let (E_aux (guard,_)),assigns = const_prop_exp substs assigns guard in + match guard with + | E_lit (L_aux (L_true,_)) -> Some (exp,vsubst,ksubst) + | E_lit (L_aux (L_false,_)) -> findpat_generic check_pat description assigns tl + | _ -> None + end + | GiveUp -> None + end + | (Pat_aux (Pat_exp (p,exp),_))::tl -> + match check_pat p with + | DoesNotMatch -> findpat_generic check_pat description assigns tl + | DoesMatch (subst,ksubst) -> Some (exp,subst,ksubst) + | GiveUp -> None + in + match e with + | E_id id -> + (match Env.lookup_id id env with + | Enum _ -> + let checkpat = function + | P_aux (P_id id',_) + | P_aux (P_app (id',[]),_) -> + if Id.compare id id' = 0 then DoesMatch ([],[]) else DoesNotMatch + | P_aux (_,(l',_)) -> + (Reporting_basic.print_err false true l' "Monomorphisation" + "Unexpected kind of pattern for enumeration"; GiveUp) + in findpat_generic checkpat (string_of_id id) assigns cases + | _ -> None) + | E_lit (L_aux (lit_e, lit_l)) -> + let checkpat = function + | P_aux (P_lit (L_aux (lit_p, _)),_) -> + if lit_match (lit_e,lit_p) then DoesMatch ([],[]) else DoesNotMatch + | P_aux (P_var (P_aux (P_id id,p_id_annot), kid),_) -> + begin + match lit_e with + | L_num i -> + DoesMatch ([id, E_aux (e,(l,annot))], + [kid,Nexp_aux (Nexp_constant i,Unknown)]) + (* For undefined we fix the type-level size (because there's no good + way to construct an undefined size), but leave the term as undefined + to make the meaning clear. *) + | L_undef -> + let nexp = fabricate_nexp l annot in + let typ = subst_src_typ (KBindings.singleton kid nexp) (typ_of_annot p_id_annot) in + DoesMatch ([id, E_aux (E_cast (typ,E_aux (e,(l,None))),(l,None))], + [kid,nexp]) + | _ -> + (Reporting_basic.print_err false true lit_l "Monomorphisation" + "Unexpected kind of literal for var match"; GiveUp) + end + | P_aux (_,(l',_)) -> + (Reporting_basic.print_err false true l' "Monomorphisation" + "Unexpected kind of pattern for literal"; GiveUp) + in findpat_generic checkpat "literal" assigns cases + | _ -> None + + and can_match exp = + let env = Type_check.env_of exp in + can_match_with_env env exp in let subst_exp substs exp = @@ -1917,7 +1942,7 @@ let deps_of_uvar kid_deps arg_deps = function and dependencies on mutable variables. The latter are quite conservative, we currently drop variables assigned inside loops, for example. *) -let rec analyse_exp env assigns (E_aux (e,(l,annot)) as exp) = +let rec analyse_exp fn_id env assigns (E_aux (e,(l,annot)) as exp) = let remove_assigns es message = let assigned = List.fold_left (fun vs exp -> IdSet.union vs (assigned_vars exp)) @@ -1929,7 +1954,7 @@ let rec analyse_exp env assigns (E_aux (e,(l,annot)) as exp) = in let non_det es = let assigns = remove_assigns es " assigned in non-deterministic expressions" in - let deps, _, rs = split3 (List.map (analyse_exp env assigns) es) in + let deps, _, rs = split3 (List.map (analyse_exp fn_id env assigns) es) in (deps, assigns, List.fold_left merge empty rs) in let merge_deps deps = @@ -1939,9 +1964,9 @@ let rec analyse_exp env assigns (E_aux (e,(l,annot)) as exp) = | E_block es -> let rec aux assigns = function | [] -> (dempty, assigns, empty) - | [e] -> analyse_exp env assigns e + | [e] -> analyse_exp fn_id env assigns e | e::es -> - let _, assigns, r' = analyse_exp env assigns e in + let _, assigns, r' = analyse_exp fn_id env assigns e in let d, assigns, r = aux assigns es in d, assigns, merge r r' in @@ -1964,28 +1989,37 @@ let rec analyse_exp env assigns (E_aux (e,(l,annot)) as exp) = Unknown (l, string_of_id id ^ " is not in the environment"),assigns,empty end | E_lit _ -> (env.control_deps,assigns,empty) - | E_cast (_,e) -> analyse_exp env assigns e + | E_cast (_,e) -> analyse_exp fn_id env assigns e | E_app (id,args) -> let deps, assigns, r = non_det args in let kid_inst = instantiation_of exp in + (* Change kids in instantiation to the canonical ones from the type signature *) + let kid_inst = KBindings.fold (fun kid -> KBindings.add (orig_kid kid)) kid_inst KBindings.empty in let kid_deps = KBindings.map (deps_of_uvar env.kid_deps deps) kid_inst in - let r' = { empty with split_on_call = Bindings.singleton id (deps, kid_deps) } in + let r' = + if Id.compare fn_id id == 0 then + let bad = Unknown (l,"Recursive call of " ^ string_of_id id) in + let deps = List.map (fun _ -> bad) deps in + let kid_deps = KBindings.map (fun _ -> bad) kid_deps in + { empty with split_on_call = Bindings.singleton id (deps, kid_deps) } + else + { empty with split_on_call = Bindings.singleton id (deps, kid_deps) } in (merge_deps deps, assigns, merge r r') | E_tuple es | E_list es -> let deps, assigns, r = non_det es in (merge_deps deps, assigns, r) | E_if (e1,e2,e3) -> - let d1,assigns,r1 = analyse_exp env assigns e1 in + let d1,assigns,r1 = analyse_exp fn_id env assigns e1 in let env' = { env with control_deps = dmerge env.control_deps d1 } in - let d2,a2,r2 = analyse_exp env' assigns e2 in - let d3,a3,r3 = analyse_exp env' assigns e3 in + let d2,a2,r2 = analyse_exp fn_id env' assigns e2 in + let d3,a3,r3 = analyse_exp fn_id env' assigns e3 in (dmerge d2 d3, dep_bindings_merge a2 a3, merge r1 (merge r2 r3)) | E_loop (_,e1,e2) -> let assigns = remove_assigns [e1;e2] " assigned in a loop" in - let d1,a1,r1 = analyse_exp env assigns e1 in + let d1,a1,r1 = analyse_exp fn_id env assigns e1 in let env' = { env with control_deps = dmerge env.control_deps d1 } in - let d2,a2,r2 = analyse_exp env' assigns e2 in + let d2,a2,r2 = analyse_exp fn_id env' assigns e2 in (dempty, assigns, merge r1 r2) | E_for (var,efrom,eto,eby,ord,body) -> let d1,assigns,r1 = non_det [efrom;eto;eby] in @@ -1995,7 +2029,7 @@ let rec analyse_exp env assigns (E_aux (e,(l,annot)) as exp) = let env' = { env with control_deps = d; kid_deps = KBindings.add loop_kid d env.kid_deps} in - let d2,a2,r2 = analyse_exp env' assigns body in + let d2,a2,r2 = analyse_exp fn_id env' assigns body in (dempty, assigns, merge r1 r2) | E_vector es -> let ds, assigns, r = non_det es in @@ -2020,18 +2054,18 @@ let rec analyse_exp env assigns (E_aux (e,(l,annot)) as exp) = let es = List.map (function (FE_aux (FE_Fexp (_,e),_)) -> e) fexps in let ds, assigns, r = non_det (e::es) in (merge_deps ds, assigns, r) - | E_field (e,_) -> analyse_exp env assigns e + | E_field (e,_) -> analyse_exp fn_id env assigns e | E_case (e,cases) -> - let deps,assigns,r = analyse_exp env assigns e in + let deps,assigns,r = analyse_exp fn_id env assigns e in let analyse_case (Pat_aux (pexp,_)) = match pexp with | Pat_exp (pat,e1) -> let env = update_env env deps pat in - analyse_exp env assigns e1 + analyse_exp fn_id env assigns e1 | Pat_when (pat,e1,e2) -> let env = update_env env deps pat in - let d1,assigns,r1 = analyse_exp env assigns e1 in - let d2,assigns,r2 = analyse_exp env assigns e2 in + let d1,assigns,r1 = analyse_exp fn_id env assigns e1 in + let d2,assigns,r2 = analyse_exp fn_id env assigns e2 in (dmerge d1 d2, assigns, merge r1 r2) in let ds,assigns,rs = split3 (List.map analyse_case cases) in @@ -2039,40 +2073,40 @@ let rec analyse_exp env assigns (E_aux (e,(l,annot)) as exp) = List.fold_left dep_bindings_merge Bindings.empty assigns, List.fold_left merge r rs) | E_let (LB_aux (LB_val (pat,e1),_),e2) -> - let d1,assigns,r1 = analyse_exp env assigns e1 in + let d1,assigns,r1 = analyse_exp fn_id env assigns e1 in let env = update_env env d1 pat in - let d2,assigns,r2 = analyse_exp env assigns e2 in + let d2,assigns,r2 = analyse_exp fn_id env assigns e2 in (d2,assigns,merge r1 r2) | E_assign (lexp,e1) -> - let d1,assigns,r1 = analyse_exp env assigns e1 in - let assigns,r2 = analyse_lexp env assigns d1 lexp in + let d1,assigns,r1 = analyse_exp fn_id env assigns e1 in + let assigns,r2 = analyse_lexp fn_id env assigns d1 lexp in (dempty, assigns, merge r1 r2) | E_sizeof nexp -> (deps_of_nexp env.kid_deps [] nexp, assigns, empty) | E_return e | E_exit e | E_throw e -> - let _, _, r = analyse_exp env assigns e in + let _, _, r = analyse_exp fn_id env assigns e in (Unknown (l,"non-local flow"), Bindings.empty, r) | E_try (e,cases) -> - let deps,_,r = analyse_exp env assigns e in + let deps,_,r = analyse_exp fn_id env assigns e in let assigns = remove_assigns [e] " assigned in try expression" in let analyse_handler (Pat_aux (pexp,_)) = match pexp with | Pat_exp (pat,e1) -> let env = update_env env (Unknown (l,"Exception")) pat in - analyse_exp env assigns e1 + analyse_exp fn_id env assigns e1 | Pat_when (pat,e1,e2) -> let env = update_env env (Unknown (l,"Exception")) pat in - let d1,assigns,r1 = analyse_exp env assigns e1 in - let d2,assigns,r2 = analyse_exp env assigns e2 in + let d1,assigns,r1 = analyse_exp fn_id env assigns e1 in + let d2,assigns,r2 = analyse_exp fn_id env assigns e2 in (dmerge d1 d2, assigns, merge r1 r2) in let ds,assigns,rs = split3 (List.map analyse_handler cases) in (merge_deps (deps::ds), List.fold_left dep_bindings_merge Bindings.empty assigns, List.fold_left merge r rs) - | E_assert (e1,_) -> analyse_exp env assigns e1 + | E_assert (e1,_) -> analyse_exp fn_id env assigns e1 | E_app_infix _ | E_internal_cast _ @@ -2088,9 +2122,9 @@ let rec analyse_exp env assigns (E_aux (e,(l,annot)) as exp) = | E_internal_let (lexp,e1,e2) -> (* Really we ought to remove the assignment after e2 *) - let d1,assigns,r1 = analyse_exp env assigns e1 in - let assigns,r' = analyse_lexp env assigns d1 lexp in - let d2,assigns,r2 = analyse_exp env assigns e2 in + let d1,assigns,r1 = analyse_exp fn_id env assigns e1 in + let assigns,r' = analyse_lexp fn_id env assigns d1 lexp in + let d2,assigns,r2 = analyse_exp fn_id env assigns e2 in (dempty, assigns, merge r1 (merge r' r2)) | E_constraint nc -> (deps_of_nc env.kid_deps nc, assigns, empty) @@ -2121,29 +2155,29 @@ let rec analyse_exp env assigns (E_aux (e,(l,annot)) as exp) = in (deps, assigns, r) -and analyse_lexp env assigns deps (LEXP_aux (lexp,_)) = +and analyse_lexp fn_id env assigns deps (LEXP_aux (lexp,_)) = (* TODO: maybe subexps and sublexps should be non-det (and in const_prop_lexp, too?) *) match lexp with | LEXP_id id | LEXP_cast (_,id) -> Bindings.add id deps assigns, empty | LEXP_memory (id,es) -> - let _, assigns, r = analyse_exp env assigns (E_aux (E_tuple es,(Unknown,None))) in + let _, assigns, r = analyse_exp fn_id env assigns (E_aux (E_tuple es,(Unknown,None))) in assigns, r | LEXP_tup lexps -> List.fold_left (fun (assigns,r) lexp -> - let assigns,r' = analyse_lexp env assigns deps lexp + let assigns,r' = analyse_lexp fn_id env assigns deps lexp in assigns,merge r r') (assigns,empty) lexps | LEXP_vector (lexp,e) -> - let _, assigns, r1 = analyse_exp env assigns e in - let assigns, r2 = analyse_lexp env assigns deps lexp in + let _, assigns, r1 = analyse_exp fn_id env assigns e in + let assigns, r2 = analyse_lexp fn_id env assigns deps lexp in assigns, merge r1 r2 | LEXP_vector_range (lexp,e1,e2) -> - let _, assigns, r1 = analyse_exp env assigns e1 in - let _, assigns, r2 = analyse_exp env assigns e2 in - let assigns, r3 = analyse_lexp env assigns deps lexp in + let _, assigns, r1 = analyse_exp fn_id env assigns e1 in + let _, assigns, r2 = analyse_exp fn_id env assigns e2 in + let assigns, r3 = analyse_lexp fn_id env assigns deps lexp in assigns, merge r3 (merge r1 r2) - | LEXP_field (lexp,_) -> analyse_lexp env assigns deps lexp + | LEXP_field (lexp,_) -> analyse_lexp fn_id env assigns deps lexp let translate_id (Id_aux (_,l) as id) = @@ -2232,7 +2266,7 @@ let print_result r = let analyse_funcl debug tenv (FCL_aux (FCL_Funcl (id,pat,body),_)) = let (tq,_) = Env.get_val_spec id tenv in let aenv = initial_env id tq pat in - let _,_,r = analyse_exp aenv Bindings.empty body in + let _,_,r = analyse_exp id aenv Bindings.empty body in let _ = if debug > 2 then (print_endline (string_of_id id); |
