Skip to content

Commit a6411b5

Browse files
committed
CN: Support accesses in specifications (fix #371)
This commit unifies the handling of definition and declaration specifications. Previously, decl specs did not support accesses, cn_function, or trusted. The requires and ensures clauses they did support was desugared in the frontend Lem code. This commit moves the desugaring of the decl specs to backend/cn, specifically Core_to_mucore.normalise_fun_map_decl, alongside the definition specs parsing and desugaring. It also moves the spec symbol resolution and the argument resolution to the frontend too, I couldn't figure out how to save the scope _after_ declaring arguments but _before_ the function body. The logic for for combining multiple function specs into the same module, outside of Parse, into its own Spec helper module using records with types and names instead of tuples. To prevent introducing a new shift/reduce conflict in the grammar, the commit adds a new CN_LIFT_FUNCTION token. It also uses named nonterminals for better auto-gen error messages.
1 parent 0832c78 commit a6411b5

39 files changed

+970
-876
lines changed

backend/cn/lib/core_to_mucore.ml

+184-91
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ module Desugar = struct
2020
let cn_assertion = CF.Cabs_to_ail.desugar_cn_assertion
2121

2222
let cn_expr = CF.Cabs_to_ail.desugar_cn_expr
23+
24+
let cn_arg = CF.Cabs_to_ail.desugar_cn_arg
2325
end
2426

2527
let get_loc = CF.Annot.get_loc
@@ -997,7 +999,7 @@ let make_function_args f_i loc env args (accesses, requires) =
997999
aux [] [] env C.LocalState.init_st args
9981000

9991001

1000-
let make_fun_with_spec_args f_i loc env args requires =
1002+
let make_fun_with_spec_args f_i loc env args (accesses, requires) =
10011003
let rec aux good_lcs env st = function
10021004
| ((pure_arg, cn_bt), ct_ct) :: rest ->
10031005
let ct = convert_ct loc ct_ct in
@@ -1028,7 +1030,7 @@ let make_fun_with_spec_args f_i loc env args requires =
10281030
let@ at = aux (* good_lc :: *) good_lcs env st rest in
10291031
return (Mu.mComputational ((pure_arg, bt), (loc, None)) at)
10301032
| [] ->
1031-
let@ lat = make_largs_with_accesses f_i env st ([], requires) in
1033+
let@ lat = make_largs_with_accesses f_i env st (accesses, requires) in
10321034
return (Mu.L (Mu.mConstraints (List.rev good_lcs) lat))
10331035
in
10341036
aux [] env C.LocalState.init_st args
@@ -1204,23 +1206,153 @@ let normalise_label
12041206
| None -> assert_error loc !^"non-loop labels")
12051207

12061208

1207-
let add_spec_arg_renames
1208-
loc
1209-
args
1210-
arg_cts
1211-
(spec : (CF.Symbol.sym, Ctype.ctype) Cn.cn_fun_spec)
1212-
env
1213-
=
1214-
List.fold_right
1215-
(fun ((fun_sym, _), (ct, (spec_sym, _))) env ->
1216-
C.add_renamed_computational
1217-
spec_sym
1218-
fun_sym
1219-
(Memory.sbt_of_sct (convert_ct loc ct))
1220-
env)
1221-
(List.combine args (List.combine arg_cts spec.Cn.cn_spec_args))
1222-
env
1209+
module Spec = struct
1210+
type 'a parsed =
1211+
{ trusted : Mu.trusted;
1212+
accesses : (Cerb_location.t * Id.t) list;
1213+
requires : (Cerb_location.t * (Id.t, 'a) Cn.cn_condition) list;
1214+
ensures : (Cerb_location.t * (Id.t, 'a) Cn.cn_condition) list;
1215+
functions : (Cerb_location.t * Id.t) list
1216+
}
1217+
1218+
let default : _ parsed =
1219+
{ trusted = Mucore.Checked;
1220+
accesses = [];
1221+
requires = [];
1222+
ensures = [];
1223+
functions = []
1224+
}
1225+
1226+
1227+
let combine parsed : _ parsed t =
1228+
let process
1229+
Cn.{ cn_func_trusted; cn_func_acc_func; cn_func_requires; cn_func_ensures }
1230+
: _ parsed
1231+
=
1232+
let cross_fst x =
1233+
match x with None -> [] | Some (a, bs) -> List.map (fun b -> (a, b)) bs
1234+
in
1235+
let trusted =
1236+
match cn_func_trusted with
1237+
| None -> Mucore.Checked
1238+
| Some loc -> Mucore.Trusted loc
1239+
in
1240+
let accesses, functions =
1241+
match cn_func_acc_func with
1242+
| None -> ([], [])
1243+
| Some (loc, Cn.CN_mk_function nm) -> ([], [ (loc, nm) ])
1244+
| Some (loc, Cn.CN_accesses ids) -> (cross_fst (Some (loc, ids)), [])
1245+
in
1246+
let requires = cross_fst cn_func_requires in
1247+
let ensures = cross_fst cn_func_ensures in
1248+
{ trusted; accesses; requires; ensures; functions }
1249+
in
1250+
let parsed = List.map process parsed in
1251+
match parsed with
1252+
| [] -> return default
1253+
| [ condition ] -> return condition
1254+
| _ :: _ :: _ ->
1255+
let trust left right =
1256+
match (left, right) with
1257+
| Mucore.Trusted loc, _ -> Mucore.Trusted loc
1258+
| _, Mucore.Trusted loc -> Mucore.Trusted loc
1259+
| _, _ -> Mucore.Checked
1260+
in
1261+
let combine left right =
1262+
match (left, right) with
1263+
| { ensures = _ :: _ as ens; _ }, { requires = (loc, _) :: _; _ } ->
1264+
let ens_loc = fst (Option.get (List.last ens)) in
1265+
fail { loc; msg = Requires_after_ensures { ens_loc } }
1266+
| ( { trusted = t1; accesses = a1; requires = r1; ensures = e1; functions = f1 },
1267+
{ trusted = t2; accesses = a2; requires = r2; ensures = e2; functions = f2 } )
1268+
->
1269+
return
1270+
{ trusted = trust t1 t2;
1271+
accesses = a1 @ a2;
1272+
requires = r1 @ r2;
1273+
ensures = e1 @ e2;
1274+
functions = f1 @ f2
1275+
}
1276+
in
1277+
ListM.fold_leftM combine default parsed
1278+
1279+
1280+
let there_can_only_be_one loc fname opt_spec parsed_decl_spec parsed_defn_specs =
1281+
match (parsed_decl_spec, parsed_defn_specs) with
1282+
| [], [] -> return default
1283+
| [], (_ :: _ as parsed_defn_specs) -> combine parsed_defn_specs
1284+
| parsed_decl_spec :: _, [] -> combine [ parsed_decl_spec ]
1285+
| _ :: _, _ :: _ ->
1286+
let _, spec = Option.get opt_spec in
1287+
let doc =
1288+
Sym.pp fname
1289+
^^ colon
1290+
^^^ !^"re-specification of CN annotations from:"
1291+
^^ break 1
1292+
^^^ Locations.pp spec.Cn.cn_decl_loc
1293+
in
1294+
fail { loc; msg = Generic doc }
1295+
12231296

1297+
let desugar_and_add_args decl_d_st spec_args =
1298+
do_ail_desugar_op decl_d_st
1299+
@@ CF.State_exception.stExpect_mapM (Desugar.cn_arg Cn.CN_vars) spec_args
1300+
1301+
1302+
let add_spec_arg_renames loc args arg_cts cn_spec_args env =
1303+
List.fold_right
1304+
(fun ((fun_sym, _), (ct, (spec_sym, _))) env ->
1305+
C.add_renamed_computational
1306+
spec_sym
1307+
fun_sym
1308+
(Memory.sbt_of_sct (convert_ct loc ct))
1309+
env)
1310+
(List.combine args (List.combine arg_cts cn_spec_args))
1311+
env
1312+
1313+
1314+
let setup_env_desugaring_state loc defn_marker markers_env opt_spec env args arg_cts =
1315+
match opt_spec with
1316+
| None -> return (env, CAE.{ inner = Pmap.find defn_marker markers_env; markers_env })
1317+
| Some (decl_marker, spec) ->
1318+
let decl_d_st = CAE.{ inner = Pmap.find decl_marker markers_env; markers_env } in
1319+
let@ spec_args, decl_d_st = desugar_and_add_args decl_d_st spec.Cn.cn_decl_args in
1320+
return (add_spec_arg_renames loc args arg_cts spec_args env, decl_d_st)
1321+
1322+
1323+
let logical_fun_syms d_st mk_functions =
1324+
ListM.mapM
1325+
(fun (loc, id) ->
1326+
(* from Thomas's convert_c_logical_funs *)
1327+
let@ logical_fun_sym = do_ail_desugar_rdonly d_st (CAE.lookup_cn_function id) in
1328+
return (loc, logical_fun_sym))
1329+
mk_functions
1330+
1331+
1332+
type desugared =
1333+
{ trusted : Mu.trusted;
1334+
accesses : (Cerb_location.t * (Sym.t * Ctype.ctype)) list;
1335+
requires : (Sym.t, Ctype.ctype) Cn.cn_condition list;
1336+
ensures : (Sym.t, Ctype.ctype) Cn.cn_condition list;
1337+
functions : (Cerb_location.t * Sym.t) list
1338+
}
1339+
1340+
let desugar
1341+
global_types
1342+
d_st
1343+
({ trusted; accesses; requires; ensures; functions } : _ parsed)
1344+
: (desugared * _ * _) t
1345+
=
1346+
let@ functions = logical_fun_syms d_st functions in
1347+
let@ accesses = ListM.mapM (desugar_access d_st global_types) accesses in
1348+
let@ requires, d_st = desugar_conds d_st (List.map snd requires) in
1349+
debug 6 (lazy (string "desugared requires conds"));
1350+
let here = Locations.other __LOC__ in
1351+
let@ ret_s, ret_d_st = register_new_cn_local (Id.make here "return") d_st in
1352+
let@ ensures, _ = desugar_conds ret_d_st (List.map snd ensures) in
1353+
debug 6 (lazy (string "desugared ensures conds"));
1354+
return ({ trusted; accesses; requires; ensures; functions }, ret_s, ret_d_st)
1355+
end
12241356

12251357
let normalise_fun_map_decl
12261358
~inherit_loc
@@ -1241,67 +1373,32 @@ let normalise_fun_map_decl
12411373
| CF.Milicore.Mi_Fun (_bt, _args, _pe) -> assert false
12421374
| Mi_Proc (loc, _mrk, _ret_bt, args, body, labels) ->
12431375
debug 2 (lazy (item "normalising procedure" (Sym.pp fname)));
1244-
let _, ail_marker, _, ail_args, _ =
1245-
List.assoc Sym.equal fname ail_prog.CF.AilSyntax.function_definitions
1376+
let@ parsed_defn_specs = Parse.function_spec attrs in
1377+
let opt_spec, parsed_decl_spec =
1378+
match Sym.Map.find_opt fname fun_specs with
1379+
| None -> (None, [])
1380+
| Some (decl_marker, spec) -> (Some (decl_marker, spec), [ spec.Cn.cn_func_spec ])
12461381
in
1247-
(* let ail_env = Pmap.find ail_marker ail_prog.markers_env in *)
1248-
(* let d_st = CAE.set_cn_c_identifier_env ail_env d_st in *)
1249-
let d_st = CAE.{ inner = Pmap.find ail_marker markers_env; markers_env } in
1250-
let@ trusted, accesses, requires, ensures, mk_functions =
1251-
Parse.function_spec loc attrs
1382+
let@ parsed =
1383+
Spec.there_can_only_be_one loc fname opt_spec parsed_decl_spec parsed_defn_specs
12521384
in
12531385
debug 6 (lazy (string "parsed spec attrs"));
1254-
let@ mk_functions =
1255-
ListM.mapM
1256-
(fun (loc, `Make_Logical_Function id) ->
1257-
(* from Thomas's convert_c_logical_funs *)
1258-
let@ logical_fun_sym =
1259-
do_ail_desugar_rdonly d_st (CAE.lookup_cn_function id)
1260-
in
1261-
return (loc, logical_fun_sym))
1262-
mk_functions
1386+
let _, defn_marker, _, ail_args, _ =
1387+
List.assoc Sym.equal fname ail_prog.CF.AilSyntax.function_definitions
12631388
in
1264-
let defn_spec_sites =
1265-
List.map fst requires @ List.map fst ensures @ List.map fst accesses
1389+
let@ env, d_st =
1390+
Spec.setup_env_desugaring_state
1391+
loc
1392+
defn_marker
1393+
markers_env
1394+
opt_spec
1395+
env
1396+
args
1397+
(List.map snd arg_cts)
12661398
in
1267-
let@ accesses = ListM.mapM (desugar_access d_st global_types) accesses in
1268-
let@ requires, d_st = desugar_conds d_st (List.map snd requires) in
1269-
debug 6 (lazy (string "desugared requires conds"));
1270-
let here = Locations.other __LOC__ in
1271-
let@ ret_s, ret_d_st = register_new_cn_local (Id.make here "return") d_st in
1272-
let@ ensures, _ret_d_st = desugar_conds ret_d_st (List.map snd ensures) in
1273-
debug 6 (lazy (string "desugared ensures conds"));
1274-
let@ spec_req, spec_ens, env =
1275-
match Sym.Map.find_opt fname fun_specs with
1276-
| Some (_, spec) ->
1277-
let@ () =
1278-
match defn_spec_sites with
1279-
| [] -> return ()
1280-
| loc2 :: _ ->
1281-
fail
1282-
{ loc = loc2;
1283-
msg =
1284-
Generic
1285-
(Sym.pp fname
1286-
^^ colon
1287-
^^^ !^"re-specification of CN annotations from:"
1288-
^^ break 1
1289-
^^^ Locations.pp spec.Cn.cn_spec_loc)
1290-
}
1291-
in
1292-
let env = add_spec_arg_renames loc args (List.map snd arg_cts) spec env in
1293-
let env =
1294-
C.add_renamed_computational
1295-
spec.Cn.cn_spec_ret_name
1296-
ret_s
1297-
(Memory.sbt_of_sct (convert_ct loc ret_ct))
1298-
env
1299-
in
1300-
return (spec.cn_spec_requires, spec.cn_spec_ensures, env)
1301-
| _ -> return ([], [], env)
1399+
let@ { trusted; accesses; requires; ensures; functions }, ret_s, d_st =
1400+
Spec.desugar global_types d_st parsed
13021401
in
1303-
let requires = requires @ spec_req in
1304-
let ensures = ensures @ spec_ens in
13051402
debug 6 (lazy (!^"function requires/ensures" ^^^ Sym.pp fname));
13061403
debug 6 (lazy (CF.Pp_ast.pp_doc_tree (dtree_of_accesses accesses)));
13071404
debug 6 (lazy (CF.Pp_ast.pp_doc_tree (dtree_of_requires requires)));
@@ -1345,38 +1442,34 @@ let normalise_fun_map_decl
13451442
(List.combine (List.combine ail_args arg_cts) args)
13461443
(accesses, requires)
13471444
in
1348-
(* let ft = at_of_arguments (fun (_body, _labels, rt) -> rt) args_and_body in *)
13491445
let desugared_spec = Mu.{ accesses = List.map snd accesses; requires; ensures } in
1350-
return
1351-
(Some (Mu.Proc { loc; args_and_body; trusted; desugared_spec }, mk_functions))
1446+
return (Some (Mu.Proc { loc; args_and_body; trusted; desugared_spec }, functions))
13521447
| Mi_ProcDecl (loc, ret_bt, _bts) ->
13531448
(match Sym.Map.find_opt fname fun_specs with
1354-
| Some (_ail_marker, (spec : (CF.Symbol.sym, Ctype.ctype) Cn.cn_fun_spec)) ->
1449+
| Some (ail_marker, (spec : _ Cn.cn_decl_spec)) ->
13551450
let@ () =
13561451
check_against_core_bt loc ret_bt (Memory.bt_of_sct (convert_ct loc ret_ct))
13571452
in
1358-
(* let@ (requires, d_st2) = desugar_conds d_st spec.cn_spec_requires in *)
1359-
(* FIXME: do we need to note the return var somehow? *)
1360-
(* let@ (ensures, _) = desugar_conds d_st spec.cn_spec_ensures in *)
1453+
let@ parsed = Spec.combine [ spec.cn_func_spec ] in
1454+
let d_st = CAE.{ inner = Pmap.find ail_marker markers_env; markers_env } in
1455+
let@ spec_args, d_st = Spec.desugar_and_add_args d_st spec.cn_decl_args in
1456+
let@ { trusted = _; accesses; requires; ensures; functions }, ret_s, _ =
1457+
Spec.desugar global_types d_st parsed
1458+
in
13611459
let@ args_and_rt =
13621460
make_fun_with_spec_args
13631461
(fun env st ->
13641462
let@ returned =
1365-
C.make_rt
1366-
loc
1367-
env
1368-
st
1369-
(spec.cn_spec_ret_name, ret_ct)
1370-
([], spec.cn_spec_ensures)
1463+
C.make_rt loc env st (ret_s, ret_ct) (accesses, ensures)
13711464
in
13721465
return returned)
13731466
loc
13741467
env
1375-
(List.combine spec.cn_spec_args (List.map snd arg_cts))
1376-
spec.cn_spec_requires
1468+
(List.combine spec_args (List.map snd arg_cts))
1469+
(accesses, requires)
13771470
in
13781471
let ft = at_of_arguments Tools.id args_and_rt in
1379-
return (Some (Mu.ProcDecl (loc, Some ft), []))
1472+
return (Some (Mu.ProcDecl (loc, Some ft), functions))
13801473
| _ -> return (Some (Mu.ProcDecl (loc, None), [])))
13811474
| Mi_BuiltinDecl (_loc, _bt, _bts) -> assert false)
13821475

@@ -1556,8 +1649,8 @@ let normalise_file ~inherit_loc ((fin_markers_env : CAE.fin_markers_env), ail_pr
15561649
let env = List.fold_left register_glob env globs in
15571650
let fun_specs_map =
15581651
List.fold_right
1559-
(fun (id, spec) acc -> Sym.Map.add spec.Cn.cn_spec_name (id, spec) acc)
1560-
ail_prog.cn_fun_specs
1652+
(fun (id, key, spec) acc -> Sym.Map.add key (id, spec) acc)
1653+
ail_prog.cn_decl_specs
15611654
Sym.Map.empty
15621655
in
15631656
let@ funs, mk_functions =

0 commit comments

Comments
 (0)