Source file meta_parse.ml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
open Logic_ptree
open Logic_typing
open Cil_types
open Meta_options
open Meta_utils
type context =
| Weak_invariant
| Strong_invariant
| Calling
| Writing
| Reading
| Postcond
| Precond
| Conditional_invariant
type target =
| TgAll
| TgSet of StrSet.t
| TgUnion of target list
| TgInter of target list
| TgDiff of target * target
| TgCallees of target
| TgCallers of target
| TgFile of string
type mp_proof_method =
| MmLocal
| MmDeduction
| MmAxiom
type mp_translation =
| MtCheck
| MtAssert
| MtDefault
| MtNone
type replaced_kind =
| RepVariable of term
| RepApp of (string * term) list
type metaproperty = {
mp_name : string;
mp_target : target;
mp_context : context;
mp_property : Kernel_function.t -> (string * replaced_kind) list -> predicate;
mp_proof_method : mp_proof_method;
mp_translation : mp_translation;
mp_loc : Cil_types.location
}
let mp_commands = ["\\prop"]
let mp_contexts = [
"\\weak_invariant";
"\\strong_invariant";
"\\writing";
"\\calling";
"\\reading";
"\\postcond";
"\\precond";
"\\conditional_invariant"
]
let mp_metavariables = [
"\\written";
"\\lhost_written";
"\\read";
"\\lhost_read";
"\\called";
"\\called_arg";
"\\func";
]
let mp_utils = [
"\\diff";
"\\ALL"
]
let mp_preds = mp_commands @ mp_contexts
let mp_terms = mp_metavariables @ mp_utils
let gathered_props = ref []
let meta_type_predicate loc orig_ctxt meta_ctxt env expr =
match expr.lexpr_node with
| PLapp (pname, _, args) when List.mem pname mp_preds ->
let terms = List.map (meta_ctxt.type_term meta_ctxt env) args in
let logic_info = List.hd (Logic_env.find_all_logic_functions pname) in
Logic_const.papp (logic_info, [], terms)
| PLapp ("\\fguard", _, [dangerous]) ->
begin try meta_ctxt.type_predicate meta_ctxt env dangerous
with _ -> Logic_const.pfalse end
| PLapp ("\\tguard", _, [dangerous]) ->
begin try meta_ctxt.type_predicate meta_ctxt env dangerous
with _ -> Logic_const.ptrue end
| PLapp ("\\assert_type", _, [{lexpr_node = PLcast (ltyp, lexpr)}]) ->
let typed_term = meta_ctxt.type_term meta_ctxt env lexpr in
let desired_type = meta_ctxt.logic_type meta_ctxt loc env ltyp in
let real_type = typed_term.term_type in
if Cil_datatype.Logic_type_NoUnroll.equal real_type desired_type then
Logic_const.ptrue
else meta_ctxt.error loc "%a has type %a instead of %a"
Printer.pp_term typed_term Printer.pp_logic_type real_type
Printer.pp_logic_type desired_type
| _ -> orig_ctxt.type_predicate meta_ctxt env expr
let meta_type_term termassoc quantifiers kf loc orig_ctxt meta_ctxt env expr =
match expr.lexpr_node with
| PLat(e, l) when List.mem l ["After";"Before"] ->
let e_t = meta_ctxt.type_term meta_ctxt env e in
let label = FormalLabel l in
Logic_const.tat (e_t, label)
| PLapp ("\\formal", _, [{lexpr_node = PLvar param}]) ->
(match Globals.Syntactic_search.find_in_scope param (Formal kf) with
| Some vi -> vi |> Cil.cvar_to_lvar |> Logic_const.tvar
| None ->
meta_ctxt.error loc "%s is not a formal in %a"
param Kernel_function.pretty kf)
| PLapp ("\\bound", _, [bound]) ->
Meta_bindings.parse_bound meta_ctxt loc quantifiers bound
| PLapp (app_name, _, [{lexpr_node = PLvar arg}])
when List.mem app_name mp_terms ->
begin match List.assoc_opt app_name termassoc with
| Some RepApp l ->
begin match List.assoc_opt arg l with
| Some term -> term
| None -> meta_ctxt.error loc
"%s is not a valid argument for %s here" arg app_name
end
| None -> meta_ctxt.error loc
"Application of %s forbidden in this context" app_name
| _ -> meta_ctxt.error loc
"%s expects no arguments but has been provided with one"
app_name
end
| PLvar vname when List.mem vname mp_terms ->
begin match List.assoc_opt vname termassoc with
| Some RepVariable a -> a
| None -> meta_ctxt.error loc
"Variable %s forbidden in this context" vname
| _ -> meta_ctxt.error loc
"%s expects one argument but has been provided with none" vname
end
| _ -> orig_ctxt.type_term meta_ctxt env expr
let process_meta_termassoc typing_context loc kf termassoc expr =
let quantifiers = Str_Hashtbl.create 5 in
let meta_tc =
{typing_context with
type_predicate = (meta_type_predicate loc typing_context);
type_term = (meta_type_term termassoc quantifiers kf loc typing_context)
}
in
let fenv = Logic_typing.append_here_label meta_tc.pre_state in
let fenv2 = Logic_typing.append_pre_label fenv in
let fenv3 = Logic_typing.append_old_and_post_labels fenv2 in
let pred = meta_tc.type_predicate meta_tc fenv3 expr in
Meta_bindings.after_parse meta_tc pred quantifiers
let process_single_target tc expr =
match expr.lexpr_node with
| PLvar func -> func
| _ -> tc.error expr.lexpr_loc "Target is not a variable"
let process_string tc f e = match e.lexpr_node with
| PLconstant (StringConstant n) -> n
| PLvar n -> n
| _ -> tc.error e.lexpr_loc f
let rec process_targets tc expr = match expr.lexpr_node with
| PLempty -> TgSet StrSet.empty
| PLvar "\\ALL" -> TgAll
| PLset elems ->
let s = elems |> List.map (process_single_target tc) |> StrSet.of_list in
TgSet s
| PLunion l -> TgUnion (List.map (process_targets tc) l)
| PLinter l -> TgInter (List.map (process_targets tc) l)
| PLapp ("\\diff", _, [s1; s2]) ->
TgDiff (process_targets tc s1, process_targets tc s2)
| PLapp ("\\callees", _, [t]) -> TgCallees (process_targets tc t)
| PLapp ("\\callers", _, [t]) -> TgCallers (process_targets tc t)
| PLapp ("\\in_file", _, [t]) -> TgFile (process_string tc "Expected filename" t)
| _ -> TgSet (StrSet.singleton @@ process_single_target tc expr)
let pp_aslist fmt l =
let pp_replaced_kind fmt = function
| RepVariable v -> Printer.pp_term fmt v
| RepApp l ->
let pp_singular fmt (a, b) =
Format.fprintf fmt "(%s, %a)" a Printer.pp_term b
in Format.pp_print_list pp_singular fmt l
in
let pp_assoc fmt (a, b) =
Format.fprintf fmt "(%s, %a)" a pp_replaced_kind b
in Format.pp_print_list pp_assoc fmt l
let typing_error hloc mp_name kf_name metavars (loc, error_msg) =
let metav_msg =
if metavars = [] then "there were no meta-variables."
else Format.asprintf "meta-variables were: %a" pp_aslist metavars
in
Self.abort "Error during the typing of a HILARE!\n\
What: the HILARE named '%s' (%a)\n\
Where: in function '%s', at %a\n\
Why: %s\n\
Environment: %s"
mp_name Cil_datatype.Location.pretty hloc
kf_name Cil_datatype.Location.pretty loc
error_msg
metav_msg
let delay_prop tc name lexpr kf aslist =
let dfind_var ?label:_ var =
try tc.find_var var
with Not_found -> try
let vi = Globals.Vars.find_from_astinfo var Global in
Cil.cvar_to_lvar vi
with Not_found ->
let kf = Globals.Functions.find_by_name var in
let vi = Kernel_function.get_vi kf in
Cil.cvar_to_lvar vi
in
let dfet s =
try tc.find_enum_tag s
with _ -> Globals.Types.find_enum_tag s
in
let dft a b = Globals.Types.find_type a b
in
let delayed_tc = {tc with find_var = dfind_var;
find_enum_tag = dfet;
find_type = dft} in
let loc = lexpr.lexpr_loc in
delayed_tc.on_error
(process_meta_termassoc delayed_tc loc kf aslist)
(typing_error loc name (Kernel_function.get_name kf) aslist)
lexpr
let rec macro_replacer changer lexpr =
let f = macro_replacer changer in
let lexpr_node = match changer lexpr.lexpr_node with
| PLapp (a, b, c) -> PLapp (a, b, List.map f c)
| PLlambda (a, b) -> PLlambda (a, f b)
| PLlet (a, b, c) -> PLlet (a, f b, f c)
| PLunop (a, b) -> PLunop (a, f b)
| PLbinop (a, b, c) -> PLbinop (f a, b, f c)
| PLdot (a, b) -> PLdot (f a, b)
| PLarrow (a, b) -> PLarrow (f a, b)
| PLarrget (a, b) -> PLarrget (f a, f b)
| PLold a -> PLold (f a)
| PLat (a, b) -> PLat (f a, b)
| PLcast (a, b) -> PLcast (a, f b)
| PLrange (a, b) -> PLrange (Option.map f a, Option.map f b)
| PLsizeofE a -> PLsizeofE (f a)
| PLupdate (a, b, c) -> PLupdate (f a, b, c)
| PLtypeof a -> PLtypeof (f a)
| PLrel (a, b, c) -> PLrel (f a, b, f c)
| PLand (a, b) -> PLand (f a, f b)
| PLor (a, b) -> PLor (f a, f b)
| PLxor (a, b) -> PLxor (f a, f b)
| PLimplies (a, b) -> PLimplies (f a, f b)
| PLiff (a, b) -> PLiff (f a, f b)
| PLnot a -> PLnot (f a)
| PLif (a, b, c) -> PLif (f a, f b, f c)
| PLforall (a, b) -> PLforall (a, f b)
| PLexists (a, b) -> PLexists (a, f b)
| PLbase_addr (a, b) -> PLbase_addr (a, f b)
| PLoffset (a, b) -> PLoffset (a, f b)
| PLblock_length (a, b) -> PLblock_length (a, f b)
| PLvalid (a, b) -> PLvalid (a, f b)
| PLvalid_read (a, b) -> PLvalid_read (a, f b)
| PLvalid_function a -> PLvalid_function (f a)
| PLallocable (a, b) -> PLallocable (a, f b)
| PLfreeable (a, b) -> PLfreeable (a, f b)
| PLinitialized (a, b) -> PLinitialized (a, f b)
| PLdangling (a, b) -> PLdangling (a, f b)
| PLfresh (a, b, c) -> PLfresh (a, f b, f c)
| PLseparated a -> PLseparated (List.map f a)
| PLnamed (a, b) -> PLnamed (a, f b)
| PLcomprehension (a, b, c) -> PLcomprehension (f a, b, Option.map f c)
| PLset a -> PLset (List.map f a)
| PLunion a -> PLunion (List.map f a)
| PLinter a -> PLset (List.map f a)
| PLlist a -> PLset (List.map f a)
| PLrepeat (a, b) -> PLrepeat (f a, f b)
| l -> l
in {lexpr with lexpr_node}
let macro_table = Hashtbl.create 2
let process_macro tc loc = function
| [
{lexpr_node = PLapp ("\\name", _, [ename])};
{lexpr_node = PLapp ("\\arg_nb", _, [eargnb])};
etemplate
] ->
let name = match ename.lexpr_node with
| PLvar n -> n
| _ -> tc.error ename.lexpr_loc "Name must be a single word"
in let arg_nb = match eargnb.lexpr_node with
| PLconstant (IntConstant i) -> int_of_string i
| _ -> tc.error eargnb.lexpr_loc "arg_nb must be a positive integer"
in
Hashtbl.add macro_table name (arg_nb, etemplate);
Ext_terms [Logic_const.tstring ~loc ("macro_" ^ name)]
| _ -> tc.error loc "Invalid macro shape"
let expand_macros tc loc prop =
let prefix = "\\param_" in
let macro_filter = function
| PLapp (name, _, params) as a ->
if Hashtbl.mem macro_table name then
let argnb, template = Hashtbl.find macro_table name in
if List.length params = argnb then
let p =
Extlib.string_del_prefix prefix p
|> Option.map (int_of_string)
|> fun o -> Option.bind o (fun n ->
if n <= argnb && n > 0 then Some (n - 1) else None
)
in
let param_replacer = function
| (PLvar v) as e -> begin match extract_param v with
| Some n -> (List.nth params n).lexpr_node
| None -> e
end
| (PLlet (s, b, r)) as e -> begin match extract_param s with
| Some n -> begin match (List.nth params n).lexpr_node with
| PLvar na -> PLlet (na, b, r)
| _ -> tc.error loc "Param replacement in \\let should be a simple var"
end
| None -> e
end
| e -> e
in (macro_replacer param_replacer template).lexpr_node
else tc.error loc "%s takes %d args but %d were given" name argnb
(List.length params)
else a
| e -> e
in macro_replacer macro_filter prop
let process_flags tc loc = function
| {lexpr_node = PLapp ("\\flags", _, l)} ->
List.map (fun x -> match x.lexpr_node with
| PLnamed ("proof", {lexpr_node=PLvar v}) -> begin match v with
| "axiom" -> `Axiom
| "deduce" -> `Deduction
| "local" -> `Local
| _ -> tc.error loc "Invalid value %s for flag proof" v
end
| PLnamed ("translate", {lexpr_node=PLvar v}) -> begin match v with
| "true" | "yes" -> `Translate
| "false" | "no" -> `NoTranslate
| "check" -> `ForceCheck
| "assert" -> `ForceAssert
| _ -> tc.error loc "Invalid value %s for flag translate" v
end
| PLnamed (k, {lexpr_node=PLvar _}) -> tc.error loc "Unknown flag %s" k
| _ -> tc.error loc "A flag should has the shape key:value"
) l
| _ -> tc.error loc "Flags should be absent or listed with \\flags"
let get_status tc loc options =
let pm = ref MmLocal in
let tr = ref MtDefault in
List.iter (function
| `Axiom -> pm := MmAxiom
| `Local -> pm := MmLocal
| `Deduction -> pm := MmDeduction
| `ForceAssert -> tr := MtAssert
| `ForceCheck -> tr := MtCheck
| `Translate -> tr := MtDefault
| `NoTranslate -> tr := MtNone
) options;
if !pm = MmLocal && !tr = MtNone then
tc.error loc "When proof method is local, translation must be enabled"
; (!pm, !tr)
let process_property tc loc = function
| {lexpr_node = PLapp ("\\name", _, [ename])} ::
{lexpr_node = PLapp ("\\targets", _, [etargets])} ::
{lexpr_node = PLapp ("\\context", _, [econtext])} :: tail ->
let mp_name = process_string tc "Prop name must be a string" ename in
let mp_target = process_targets tc etargets in
let mp_context = match econtext.lexpr_node with
| PLvar "\\weak_invariant" -> Weak_invariant
| PLvar "\\strong_invariant" -> Strong_invariant
| PLvar "\\conditional_invariant" -> Conditional_invariant
| PLvar "\\writing" -> Writing
| PLvar "\\reading" -> Reading
| PLvar "\\calling" -> Calling
| PLvar "\\precond" -> Precond
| PLvar "\\postcond" -> Postcond
| _ -> tc.error econtext.lexpr_loc "Invalid context %a"
Logic_print.print_lexpr econtext
in
let eproperty, options = match tail with
| [] -> tc.error loc "Missing actual property"
| [x] -> x, []
| [o; x] -> x, process_flags tc loc o
| _ -> tc.error loc "Too many trailing arguments in MP"
in
let mp_proof_method, mp_translation = get_status tc loc options in
let expanded = expand_macros tc loc eproperty in
let mp_property = delay_prop tc mp_name expanded in
gathered_props := {mp_name; mp_target; mp_context; mp_proof_method;
mp_translation; mp_property; mp_loc = loc} :: !gathered_props;
Ext_terms [Logic_const.tstring ~loc mp_name]
| _ -> tc.error loc "Invalid property shape"
let process_meta tc loc l =
begin match l with
| command :: t -> begin match command.lexpr_node with
| PLvar "\\prop" -> process_property tc loc t
| PLvar "\\macro" -> process_macro tc loc t
| PLconstant StringConstant str -> Ext_terms [Logic_const.tstring ~loc str]
| _ -> tc.error loc "Invalid command"
end
| _ -> tc.error loc "Missing command"
end
let process_inline tc loc = function
| [{lexpr_node = PLvar "lenient"}] ->
Ext_terms [Logic_const.tstring "lenient"]
| [{lexpr_node = PLapp("\\bind", _, [{lexpr_node = PLvar cvar_name};
{lexpr_node = PLvar gvar_name}])}] ->
Meta_bindings.parse_bind tc loc cvar_name gvar_name
| _ -> Self.warning "%a: invalid inline annotation" Printer.pp_location loc;
Ext_id (-1)
let register_parsing () =
let bl_from_name predicate bl_name = {
bl_name; bl_labels = []; bl_params = []; bl_profile = [];
bl_type = if predicate then None else Some (Lvar "dummy")
} in
mp_preds |> List.map (bl_from_name true) |> List.iter Logic_builtin.register;
mp_terms |> List.map (bl_from_name false) |> List.iter Logic_builtin.register;
Acsl_extension.register_global ~plugin:"meta" "meta" process_meta true;
Acsl_extension.register_code_annot_next_stmt ~plugin:"meta" "imeta"
process_inline ~visitor:Meta_bindings.process_imeta false
let rec check_for_duplicates = function
| [] -> None
| [_] -> None
| h1 :: h2 :: _ when h1.mp_name = h2.mp_name -> Some (h1, h2)
| _ :: t -> check_for_duplicates t
let metaproperties () =
let sorted_props = List.sort (fun a b -> compare a.mp_name b.mp_name)
!gathered_props in
begin match check_for_duplicates sorted_props with
| None -> ()
| Some (m1, m2) ->
Self.abort "The meta-property named %s is defined at \
least twice :@,Here: %a@,and here: %a"
m1.mp_name Printer.pp_location m1.mp_loc Printer.pp_location m2.mp_loc
end ;
List.rev !gathered_props