2022-01-07 14:04:31 +03:00
|
|
|
(* This file is part of the Catala compiler, a specification language for tax
|
|
|
|
and social benefits computation rules. Copyright (C) 2022 Inria, contributor:
|
2022-01-18 17:13:16 +03:00
|
|
|
Denis Merigoux <denis.merigoux@inria.fr>, Alain Delaët
|
|
|
|
<alain.delaet--tixeuil@inria.fr>
|
2022-01-07 14:04:31 +03:00
|
|
|
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
|
|
|
use this file except in compliance with the License. You may obtain a copy of
|
|
|
|
the License at
|
|
|
|
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
|
|
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
|
|
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
|
|
License for the specific language governing permissions and limitations under
|
|
|
|
the License. *)
|
|
|
|
|
|
|
|
open Utils
|
2022-01-08 20:37:04 +03:00
|
|
|
open Dcalc
|
2022-01-07 14:04:31 +03:00
|
|
|
open Ast
|
|
|
|
|
2022-01-18 17:13:16 +03:00
|
|
|
(** {1 Helpers and type definitions}*)
|
|
|
|
|
2022-07-06 11:08:27 +03:00
|
|
|
type 'm vc_return = 'm marked_expr * typ Marked.pos VarMap.t
|
2022-01-12 17:52:15 +03:00
|
|
|
(** The return type of VC generators is the VC expression plus the types of any
|
|
|
|
locally free variable inside that expression. *)
|
|
|
|
|
2022-04-04 09:56:48 +03:00
|
|
|
type ctx = {
|
|
|
|
current_scope_name : ScopeName.t;
|
|
|
|
decl : decl_ctx;
|
|
|
|
input_vars : Var.t list;
|
2022-05-30 12:20:48 +03:00
|
|
|
scope_variables_typs : typ Marked.pos VarMap.t;
|
2022-04-04 09:56:48 +03:00
|
|
|
}
|
2022-01-18 19:59:15 +03:00
|
|
|
|
2022-07-06 11:08:27 +03:00
|
|
|
let conjunction (args : 'm vc_return list) (mark : 'm mark) : 'm vc_return =
|
2022-01-12 17:52:15 +03:00
|
|
|
let acc, list =
|
|
|
|
match args with
|
|
|
|
| hd :: tl -> hd, tl
|
2022-07-06 11:08:27 +03:00
|
|
|
| [] -> ((ELit (LBool true), mark), VarMap.empty), []
|
2022-01-12 17:52:15 +03:00
|
|
|
in
|
2022-01-07 14:04:31 +03:00
|
|
|
List.fold_left
|
2022-01-12 17:52:15 +03:00
|
|
|
(fun (acc, acc_ty) (arg, arg_ty) ->
|
2022-07-06 11:08:27 +03:00
|
|
|
( (EApp ((EOp (Binop And), mark), [arg; acc]), mark),
|
2022-01-12 17:52:15 +03:00
|
|
|
VarMap.union (fun _ _ _ -> failwith "should not happen") acc_ty arg_ty ))
|
2022-01-07 14:04:31 +03:00
|
|
|
acc list
|
|
|
|
|
2022-07-06 11:08:27 +03:00
|
|
|
let negation ((arg, arg_ty) : 'm vc_return) (mark : 'm mark) : 'm vc_return =
|
|
|
|
(EApp ((EOp (Unop Not), mark), [arg]), mark), arg_ty
|
2022-01-10 18:25:20 +03:00
|
|
|
|
2022-07-06 11:08:27 +03:00
|
|
|
let disjunction (args : 'm vc_return list) (mark : 'm mark) : 'm vc_return =
|
2022-01-12 17:52:15 +03:00
|
|
|
let acc, list =
|
|
|
|
match args with
|
|
|
|
| hd :: tl -> hd, tl
|
2022-07-06 11:08:27 +03:00
|
|
|
| [] -> ((ELit (LBool false), mark), VarMap.empty), []
|
2022-01-12 17:52:15 +03:00
|
|
|
in
|
2022-01-07 14:04:31 +03:00
|
|
|
List.fold_left
|
2022-07-06 11:08:27 +03:00
|
|
|
(fun ((acc, acc_ty) : 'm vc_return) (arg, arg_ty) ->
|
|
|
|
( (EApp ((EOp (Binop Or), mark), [arg; acc]), mark),
|
2022-01-12 17:52:15 +03:00
|
|
|
VarMap.union (fun _ _ _ -> failwith "should not happen") acc_ty arg_ty ))
|
2022-01-07 14:04:31 +03:00
|
|
|
acc list
|
|
|
|
|
2022-01-18 17:13:16 +03:00
|
|
|
(** [half_product \[a1,...,an\] \[b1,...,bm\] returns \[(a1,b1),...(a1,bn),...(an,b1),...(an,bm)\]] *)
|
|
|
|
let half_product (l1 : 'a list) (l2 : 'b list) : ('a * 'b) list =
|
|
|
|
l1
|
|
|
|
|> List.mapi (fun i ei ->
|
|
|
|
List.filteri (fun j _ -> i < j) l2 |> List.map (fun ej -> ei, ej))
|
|
|
|
|> List.concat
|
|
|
|
|
|
|
|
(** This code skims through the topmost layers of the terms like this:
|
|
|
|
[log (error_on_empty < reentrant_variable () | true :- e1 >)] for scope
|
|
|
|
variables, or [fun () -> e1] for subscope variables. But what we really want
|
|
|
|
to analyze is only [e1], so we match this outermost structure explicitely
|
|
|
|
and have a clean verification condition generator that only runs on [e1] *)
|
2022-07-06 11:08:27 +03:00
|
|
|
let match_and_ignore_outer_reentrant_default (ctx : ctx) (e : 'm marked_expr) :
|
|
|
|
'm marked_expr =
|
2022-05-30 12:20:48 +03:00
|
|
|
match Marked.unmark e with
|
2022-04-12 12:14:39 +03:00
|
|
|
| ErrorOnEmpty
|
|
|
|
( EDefault
|
2022-06-03 17:40:03 +03:00
|
|
|
( [(EApp ((EVar x, _), [(ELit LUnit, _)]), _)],
|
2022-04-12 12:14:39 +03:00
|
|
|
(ELit (LBool true), _),
|
|
|
|
cons ),
|
|
|
|
_ )
|
2022-07-06 11:08:27 +03:00
|
|
|
when List.exists (fun x' -> Var.eq (Var.t x) x') ctx.input_vars ->
|
2022-01-18 17:13:16 +03:00
|
|
|
(* scope variables*)
|
|
|
|
cons
|
2022-06-03 17:40:03 +03:00
|
|
|
| EAbs (binder, [(TLit TUnit, _)]) ->
|
2022-02-10 12:05:14 +03:00
|
|
|
(* context sub-scope variables *)
|
2022-01-18 17:13:16 +03:00
|
|
|
let _, body = Bindlib.unmbind binder in
|
2022-04-12 12:14:39 +03:00
|
|
|
body
|
|
|
|
| ErrorOnEmpty d ->
|
2022-02-10 12:05:14 +03:00
|
|
|
d (* input subscope variables and non-input scope variable *)
|
2022-01-18 17:13:16 +03:00
|
|
|
| _ ->
|
2022-07-06 11:08:27 +03:00
|
|
|
Errors.raise_spanned_error (pos e)
|
2022-03-08 15:04:27 +03:00
|
|
|
"Internal error: this expression does not have the structure expected by \
|
|
|
|
the VC generator:\n\
|
|
|
|
%a"
|
|
|
|
(Print.format_expr ~debug:true ctx.decl)
|
|
|
|
e
|
2022-01-18 17:13:16 +03:00
|
|
|
|
|
|
|
(** {1 Verification conditions generator}*)
|
|
|
|
|
|
|
|
(** [generate_vc_must_not_return_empty e] returns the dcalc boolean expression
|
|
|
|
[b] such that if [b] is true, then [e] will never return an empty error. It
|
|
|
|
also returns a map of all the types of locally free variables inside the
|
2022-01-13 14:15:37 +03:00
|
|
|
expression. *)
|
2022-07-06 11:08:27 +03:00
|
|
|
let rec generate_vc_must_not_return_empty (ctx : ctx) (e : 'm marked_expr) :
|
|
|
|
'm vc_return =
|
2022-01-07 14:04:31 +03:00
|
|
|
let out =
|
2022-05-30 12:20:48 +03:00
|
|
|
match Marked.unmark e with
|
2022-01-07 14:04:31 +03:00
|
|
|
| ETuple (args, _) | EArray args ->
|
|
|
|
conjunction
|
|
|
|
(List.map (generate_vc_must_not_return_empty ctx) args)
|
2022-05-30 12:20:48 +03:00
|
|
|
(Marked.get_mark e)
|
2022-01-07 14:04:31 +03:00
|
|
|
| EMatch (arg, arms, _) ->
|
|
|
|
conjunction
|
|
|
|
(List.map (generate_vc_must_not_return_empty ctx) (arg :: arms))
|
2022-05-30 12:20:48 +03:00
|
|
|
(Marked.get_mark e)
|
2022-01-07 14:04:31 +03:00
|
|
|
| ETupleAccess (e1, _, _, _)
|
|
|
|
| EInj (e1, _, _, _)
|
|
|
|
| EAssert e1
|
|
|
|
| ErrorOnEmpty e1 ->
|
|
|
|
(generate_vc_must_not_return_empty ctx) e1
|
2022-01-12 17:52:15 +03:00
|
|
|
| EAbs (binder, typs) ->
|
2022-01-07 18:18:26 +03:00
|
|
|
(* Hot take: for a function never to return an empty error when called, it has to do
|
|
|
|
so whatever its input. So we universally quantify over the variable of the function
|
|
|
|
when inspecting the body, resulting in simply traversing through in the code here. *)
|
2022-06-03 17:40:03 +03:00
|
|
|
let vars, body = Bindlib.unmbind binder in
|
2022-01-12 17:52:15 +03:00
|
|
|
let vc_body_expr, vc_body_ty =
|
|
|
|
(generate_vc_must_not_return_empty ctx) body
|
|
|
|
in
|
|
|
|
( vc_body_expr,
|
|
|
|
List.fold_left
|
2022-07-06 11:08:27 +03:00
|
|
|
(fun acc (var, ty) -> VarMap.add (Var.t var) ty acc)
|
2022-01-12 17:52:15 +03:00
|
|
|
vc_body_ty
|
|
|
|
(List.map2 (fun x y -> x, y) (Array.to_list vars) typs) )
|
2022-01-07 14:04:31 +03:00
|
|
|
| EApp (f, args) ->
|
2022-01-18 17:13:16 +03:00
|
|
|
(* We assume here that function calls never return empty error, which implies
|
|
|
|
all functions have been checked never to return empty errors. *)
|
2022-01-07 14:04:31 +03:00
|
|
|
conjunction
|
|
|
|
(List.map (generate_vc_must_not_return_empty ctx) (f :: args))
|
2022-05-30 12:20:48 +03:00
|
|
|
(Marked.get_mark e)
|
2022-01-07 14:04:31 +03:00
|
|
|
| EIfThenElse (e1, e2, e3) ->
|
2022-02-10 18:49:01 +03:00
|
|
|
let e1_vc, vc_typ1 = generate_vc_must_not_return_empty ctx e1 in
|
|
|
|
let e2_vc, vc_typ2 = generate_vc_must_not_return_empty ctx e2 in
|
|
|
|
let e3_vc, vc_typ3 = generate_vc_must_not_return_empty ctx e3 in
|
2022-01-07 14:04:31 +03:00
|
|
|
conjunction
|
2022-02-10 18:49:01 +03:00
|
|
|
[
|
|
|
|
e1_vc, vc_typ1;
|
2022-05-30 12:20:48 +03:00
|
|
|
( (EIfThenElse (e1, e2_vc, e3_vc), Marked.get_mark e),
|
2022-02-10 18:49:01 +03:00
|
|
|
VarMap.union
|
|
|
|
(fun _ _ _ -> failwith "should not happen")
|
|
|
|
vc_typ2 vc_typ3 );
|
|
|
|
]
|
2022-05-30 12:20:48 +03:00
|
|
|
(Marked.get_mark e)
|
|
|
|
| ELit LEmptyError ->
|
|
|
|
Marked.same_mark_as (ELit (LBool false)) e, VarMap.empty
|
2022-01-07 18:18:26 +03:00
|
|
|
| EVar _
|
|
|
|
(* Per default calculus semantics, you cannot call a function with an argument
|
|
|
|
that evaluates to the empty error. Thus, all variable evaluate to non-empty-error terms. *)
|
|
|
|
| ELit _ | EOp _ ->
|
2022-05-30 12:20:48 +03:00
|
|
|
Marked.same_mark_as (ELit (LBool true)) e, VarMap.empty
|
2022-01-07 14:04:31 +03:00
|
|
|
| EDefault (exceptions, just, cons) ->
|
|
|
|
(* <e1 ... en | ejust :- econs > never returns empty if and only if:
|
|
|
|
- first we look if e1 .. en ejust can return empty;
|
|
|
|
- if no, we check that if ejust is true, whether econs can return empty.
|
|
|
|
*)
|
|
|
|
disjunction
|
|
|
|
(List.map (generate_vc_must_not_return_empty ctx) exceptions
|
|
|
|
@ [
|
|
|
|
conjunction
|
|
|
|
[
|
|
|
|
generate_vc_must_not_return_empty ctx just;
|
2022-01-12 17:52:15 +03:00
|
|
|
(let vc_just_expr, vc_just_ty =
|
|
|
|
generate_vc_must_not_return_empty ctx cons
|
|
|
|
in
|
|
|
|
( ( EIfThenElse
|
|
|
|
( just,
|
2022-01-18 17:13:16 +03:00
|
|
|
(* Comment from Alain: the justification is not checked for holding an default term.
|
2022-01-12 17:52:15 +03:00
|
|
|
In such cases, we need to encode the logic of the default terms within
|
|
|
|
the generation of the verification condition (Z3encoding.translate_expr).
|
|
|
|
Answer from Denis: Normally, there is a structural invariant from the
|
|
|
|
surface language to intermediate representation translation preventing
|
|
|
|
any default terms to appear in justifications.*)
|
|
|
|
vc_just_expr,
|
2022-05-30 12:20:48 +03:00
|
|
|
(ELit (LBool false), Marked.get_mark e) ),
|
|
|
|
Marked.get_mark e ),
|
2022-01-12 17:52:15 +03:00
|
|
|
vc_just_ty ));
|
2022-01-07 14:04:31 +03:00
|
|
|
]
|
2022-05-30 12:20:48 +03:00
|
|
|
(Marked.get_mark e);
|
2022-01-07 14:04:31 +03:00
|
|
|
])
|
2022-05-30 12:20:48 +03:00
|
|
|
(Marked.get_mark e)
|
2022-01-07 14:04:31 +03:00
|
|
|
in
|
|
|
|
out
|
|
|
|
[@@ocamlformat "wrap-comments=false"]
|
|
|
|
|
2022-01-18 17:13:16 +03:00
|
|
|
(** [generate_vs_must_not_return_confict e] returns the dcalc boolean expression
|
|
|
|
[b] such that if [b] is true, then [e] will never return a conflict error.
|
|
|
|
It also returns a map of all the types of locally free variables inside the
|
|
|
|
expression. *)
|
2022-07-06 11:08:27 +03:00
|
|
|
let rec generate_vs_must_not_return_confict (ctx : ctx) (e : 'm marked_expr) :
|
|
|
|
'm vc_return =
|
2022-01-10 18:25:20 +03:00
|
|
|
let out =
|
2022-01-18 17:13:16 +03:00
|
|
|
(* See the code of [generate_vc_must_not_return_empty] for a list of invariants on which this
|
|
|
|
function relies on. *)
|
2022-05-30 12:20:48 +03:00
|
|
|
match Marked.unmark e with
|
2022-01-10 18:25:20 +03:00
|
|
|
| ETuple (args, _) | EArray args ->
|
|
|
|
conjunction
|
|
|
|
(List.map (generate_vs_must_not_return_confict ctx) args)
|
2022-05-30 12:20:48 +03:00
|
|
|
(Marked.get_mark e)
|
2022-01-10 18:25:20 +03:00
|
|
|
| EMatch (arg, arms, _) ->
|
|
|
|
conjunction
|
|
|
|
(List.map (generate_vs_must_not_return_confict ctx) (arg :: arms))
|
2022-05-30 12:20:48 +03:00
|
|
|
(Marked.get_mark e)
|
2022-01-10 18:25:20 +03:00
|
|
|
| ETupleAccess (e1, _, _, _)
|
|
|
|
| EInj (e1, _, _, _)
|
|
|
|
| EAssert e1
|
|
|
|
| ErrorOnEmpty e1 ->
|
|
|
|
generate_vs_must_not_return_confict ctx e1
|
2022-01-12 17:52:15 +03:00
|
|
|
| EAbs (binder, typs) ->
|
2022-06-03 17:40:03 +03:00
|
|
|
let vars, body = Bindlib.unmbind binder in
|
2022-01-12 17:52:15 +03:00
|
|
|
let vc_body_expr, vc_body_ty =
|
|
|
|
(generate_vs_must_not_return_confict ctx) body
|
|
|
|
in
|
|
|
|
( vc_body_expr,
|
|
|
|
List.fold_left
|
2022-07-06 11:08:27 +03:00
|
|
|
(fun acc (var, ty) -> VarMap.add (Var.t var) ty acc)
|
2022-01-12 17:52:15 +03:00
|
|
|
vc_body_ty
|
|
|
|
(List.map2 (fun x y -> x, y) (Array.to_list vars) typs) )
|
2022-01-10 18:25:20 +03:00
|
|
|
| EApp (f, args) ->
|
|
|
|
conjunction
|
|
|
|
(List.map (generate_vs_must_not_return_confict ctx) (f :: args))
|
2022-05-30 12:20:48 +03:00
|
|
|
(Marked.get_mark e)
|
2022-01-10 18:25:20 +03:00
|
|
|
| EIfThenElse (e1, e2, e3) ->
|
2022-02-10 18:49:01 +03:00
|
|
|
let e1_vc, vc_typ1 = generate_vs_must_not_return_confict ctx e1 in
|
|
|
|
let e2_vc, vc_typ2 = generate_vs_must_not_return_confict ctx e2 in
|
|
|
|
let e3_vc, vc_typ3 = generate_vs_must_not_return_confict ctx e3 in
|
2022-01-10 18:25:20 +03:00
|
|
|
conjunction
|
2022-02-10 18:49:01 +03:00
|
|
|
[
|
|
|
|
e1_vc, vc_typ1;
|
2022-05-30 12:20:48 +03:00
|
|
|
( (EIfThenElse (e1, e2_vc, e3_vc), Marked.get_mark e),
|
2022-02-10 18:49:01 +03:00
|
|
|
VarMap.union
|
|
|
|
(fun _ _ _ -> failwith "should not happen")
|
|
|
|
vc_typ2 vc_typ3 );
|
|
|
|
]
|
2022-05-30 12:20:48 +03:00
|
|
|
(Marked.get_mark e)
|
2022-01-12 17:52:15 +03:00
|
|
|
| EVar _ | ELit _ | EOp _ ->
|
2022-05-30 12:20:48 +03:00
|
|
|
Marked.same_mark_as (ELit (LBool true)) e, VarMap.empty
|
2022-01-10 18:25:20 +03:00
|
|
|
| EDefault (exceptions, just, cons) ->
|
2022-01-18 17:13:16 +03:00
|
|
|
(* <e1 ... en | ejust :- econs > never returns conflict if and only if:
|
|
|
|
- neither e1 nor ... nor en nor ejust nor econs return conflict
|
|
|
|
- there is no two differents ei ej that are not empty. *)
|
2022-01-10 18:25:20 +03:00
|
|
|
let quadratic =
|
|
|
|
negation
|
|
|
|
(disjunction
|
|
|
|
(List.map
|
|
|
|
(fun (e1, e2) ->
|
|
|
|
conjunction
|
|
|
|
[
|
|
|
|
generate_vc_must_not_return_empty ctx e1;
|
|
|
|
generate_vc_must_not_return_empty ctx e2;
|
|
|
|
]
|
2022-05-30 12:20:48 +03:00
|
|
|
(Marked.get_mark e))
|
2022-01-10 18:25:20 +03:00
|
|
|
(half_product exceptions exceptions))
|
2022-05-30 12:20:48 +03:00
|
|
|
(Marked.get_mark e))
|
|
|
|
(Marked.get_mark e)
|
2022-01-10 18:25:20 +03:00
|
|
|
in
|
2022-01-10 19:29:53 +03:00
|
|
|
let others =
|
|
|
|
List.map
|
|
|
|
(generate_vs_must_not_return_confict ctx)
|
|
|
|
(just :: cons :: exceptions)
|
|
|
|
in
|
2022-05-30 12:20:48 +03:00
|
|
|
let out = conjunction (quadratic :: others) (Marked.get_mark e) in
|
2022-01-10 19:29:53 +03:00
|
|
|
out
|
2022-01-10 19:53:48 +03:00
|
|
|
in
|
2022-01-10 18:25:20 +03:00
|
|
|
out
|
2022-01-18 17:13:16 +03:00
|
|
|
[@@ocamlformat "wrap-comments=false"]
|
2022-01-10 18:25:20 +03:00
|
|
|
|
2022-01-18 17:13:16 +03:00
|
|
|
(** {1 Interface}*)
|
2022-01-13 14:15:37 +03:00
|
|
|
|
2022-01-10 12:28:14 +03:00
|
|
|
type verification_condition_kind = NoEmptyError | NoOverlappingExceptions
|
|
|
|
|
2022-07-06 11:08:27 +03:00
|
|
|
type 'm verification_condition = {
|
|
|
|
vc_guard : 'm marked_expr;
|
2022-01-10 12:28:14 +03:00
|
|
|
(* should have type bool *)
|
|
|
|
vc_kind : verification_condition_kind;
|
2022-01-12 18:49:44 +03:00
|
|
|
vc_scope : ScopeName.t;
|
2022-05-30 12:20:48 +03:00
|
|
|
vc_variable : Var.t Marked.pos;
|
|
|
|
vc_free_vars_typ : typ Marked.pos VarMap.t;
|
2022-01-10 12:28:14 +03:00
|
|
|
}
|
|
|
|
|
2022-04-04 09:56:48 +03:00
|
|
|
let rec generate_verification_conditions_scope_body_expr
|
2022-04-12 11:53:07 +03:00
|
|
|
(ctx : ctx)
|
2022-07-06 11:08:27 +03:00
|
|
|
(scope_body_expr : ('m expr, 'm) scope_body_expr) :
|
|
|
|
ctx * 'm verification_condition list =
|
2022-04-04 09:56:48 +03:00
|
|
|
match scope_body_expr with
|
|
|
|
| Result _ -> ctx, []
|
|
|
|
| ScopeLet scope_let ->
|
|
|
|
let scope_let_var, scope_let_next =
|
|
|
|
Bindlib.unbind scope_let.scope_let_next
|
|
|
|
in
|
|
|
|
let new_ctx, vc_list =
|
|
|
|
match scope_let.scope_let_kind with
|
|
|
|
| DestructuringInputStruct ->
|
2022-07-06 11:08:27 +03:00
|
|
|
{ ctx with input_vars = Var.t scope_let_var :: ctx.input_vars }, []
|
2022-04-04 09:56:48 +03:00
|
|
|
| ScopeVarDefinition | SubScopeVarDefinition ->
|
|
|
|
(* For scope variables, we should check both that they never evaluate to
|
|
|
|
emptyError nor conflictError. But for subscope variable definitions,
|
|
|
|
what we're really doing is adding exceptions to something defined in
|
|
|
|
the subscope so we just ought to verify only that the exceptions
|
|
|
|
overlap. *)
|
2022-04-12 12:14:39 +03:00
|
|
|
let e = Bindlib.unbox (remove_logging_calls scope_let.scope_let_expr) in
|
|
|
|
let e = match_and_ignore_outer_reentrant_default ctx e in
|
2022-04-04 09:56:48 +03:00
|
|
|
let vc_confl, vc_confl_typs =
|
2022-01-12 17:52:15 +03:00
|
|
|
generate_vs_must_not_return_confict ctx e
|
2022-05-04 18:40:55 +03:00
|
|
|
in
|
2022-04-04 09:56:48 +03:00
|
|
|
let vc_confl =
|
|
|
|
if !Cli.optimize_flag then
|
|
|
|
Bindlib.unbox (Optimizations.optimize_expr ctx.decl vc_confl)
|
|
|
|
else vc_confl
|
2022-05-04 18:40:55 +03:00
|
|
|
in
|
2022-04-04 09:56:48 +03:00
|
|
|
let vc_list =
|
2022-05-04 18:40:55 +03:00
|
|
|
[
|
|
|
|
{
|
2022-05-30 12:20:48 +03:00
|
|
|
vc_guard = Marked.same_mark_as (Marked.unmark vc_confl) e;
|
2022-04-04 09:56:48 +03:00
|
|
|
vc_kind = NoOverlappingExceptions;
|
|
|
|
vc_free_vars_typ =
|
|
|
|
VarMap.union
|
|
|
|
(fun _ _ -> failwith "should not happen")
|
|
|
|
ctx.scope_variables_typs vc_confl_typs;
|
|
|
|
vc_scope = ctx.current_scope_name;
|
2022-07-06 11:08:27 +03:00
|
|
|
vc_variable = Var.t scope_let_var, scope_let.scope_let_pos;
|
2022-05-04 18:40:55 +03:00
|
|
|
};
|
|
|
|
]
|
|
|
|
in
|
2022-04-04 09:56:48 +03:00
|
|
|
let vc_list =
|
|
|
|
match scope_let.scope_let_kind with
|
|
|
|
| ScopeVarDefinition ->
|
|
|
|
let vc_empty, vc_empty_typs =
|
|
|
|
generate_vc_must_not_return_empty ctx e
|
|
|
|
in
|
|
|
|
let vc_empty =
|
|
|
|
if !Cli.optimize_flag then
|
|
|
|
Bindlib.unbox (Optimizations.optimize_expr ctx.decl vc_empty)
|
|
|
|
else vc_empty
|
|
|
|
in
|
2022-05-04 18:40:55 +03:00
|
|
|
{
|
2022-05-30 12:20:48 +03:00
|
|
|
vc_guard = Marked.same_mark_as (Marked.unmark vc_empty) e;
|
2022-04-04 09:56:48 +03:00
|
|
|
vc_kind = NoEmptyError;
|
|
|
|
vc_free_vars_typ =
|
|
|
|
VarMap.union
|
|
|
|
(fun _ _ -> failwith "should not happen")
|
|
|
|
ctx.scope_variables_typs vc_empty_typs;
|
|
|
|
vc_scope = ctx.current_scope_name;
|
2022-07-06 11:08:27 +03:00
|
|
|
vc_variable = Var.t scope_let_var, scope_let.scope_let_pos;
|
2022-05-04 18:40:55 +03:00
|
|
|
}
|
2022-04-04 09:56:48 +03:00
|
|
|
:: vc_list
|
|
|
|
| _ -> vc_list
|
2022-05-04 18:40:55 +03:00
|
|
|
in
|
2022-04-04 09:56:48 +03:00
|
|
|
ctx, vc_list
|
|
|
|
| _ -> ctx, []
|
|
|
|
in
|
|
|
|
let new_ctx, new_vcs =
|
|
|
|
generate_verification_conditions_scope_body_expr
|
|
|
|
{
|
|
|
|
new_ctx with
|
|
|
|
scope_variables_typs =
|
2022-07-06 11:08:27 +03:00
|
|
|
VarMap.add (Var.t scope_let_var) scope_let.scope_let_typ
|
2022-04-04 09:56:48 +03:00
|
|
|
new_ctx.scope_variables_typs;
|
|
|
|
}
|
|
|
|
scope_let_next
|
|
|
|
in
|
|
|
|
new_ctx, vc_list @ new_vcs
|
|
|
|
|
|
|
|
let rec generate_verification_conditions_scopes
|
2022-04-12 11:53:07 +03:00
|
|
|
(decl_ctx : decl_ctx)
|
2022-07-06 11:08:27 +03:00
|
|
|
(scopes : ('m expr, 'm) scopes)
|
|
|
|
(s : ScopeName.t option) : 'm verification_condition list =
|
2022-04-04 09:56:48 +03:00
|
|
|
match scopes with
|
|
|
|
| Nil -> []
|
|
|
|
| ScopeDef scope_def ->
|
2022-03-17 19:52:26 +03:00
|
|
|
let is_selected_scope =
|
|
|
|
match s with
|
2022-04-04 09:56:48 +03:00
|
|
|
| Some s when Dcalc.Ast.ScopeName.compare s scope_def.scope_name = 0 ->
|
|
|
|
true
|
2022-03-17 19:52:26 +03:00
|
|
|
| None -> true
|
|
|
|
| _ -> false
|
|
|
|
in
|
2022-04-04 09:56:48 +03:00
|
|
|
let vcs =
|
|
|
|
if is_selected_scope then
|
|
|
|
let _scope_input_var, scope_body_expr =
|
|
|
|
Bindlib.unbind scope_def.scope_body.scope_body_expr
|
|
|
|
in
|
|
|
|
let ctx =
|
|
|
|
{
|
|
|
|
current_scope_name = scope_def.scope_name;
|
|
|
|
decl = decl_ctx;
|
|
|
|
input_vars = [];
|
|
|
|
scope_variables_typs =
|
|
|
|
VarMap.empty
|
|
|
|
(* We don't need to add the typ of the scope input var here
|
|
|
|
because it will never appear in an expression for which we
|
|
|
|
generate a verification conditions (the big struct is
|
|
|
|
destructured with a series of let bindings just after. )*);
|
|
|
|
}
|
|
|
|
in
|
|
|
|
let _, vcs =
|
|
|
|
generate_verification_conditions_scope_body_expr ctx scope_body_expr
|
|
|
|
in
|
|
|
|
vcs
|
|
|
|
else []
|
|
|
|
in
|
|
|
|
let _scope_var, next = Bindlib.unbind scope_def.scope_next in
|
|
|
|
generate_verification_conditions_scopes decl_ctx next s @ vcs
|
|
|
|
|
|
|
|
let generate_verification_conditions
|
2022-07-06 11:08:27 +03:00
|
|
|
(p : 'm program)
|
|
|
|
(s : Dcalc.Ast.ScopeName.t option) : 'm verification_condition list =
|
2022-04-04 18:51:41 +03:00
|
|
|
let vcs = generate_verification_conditions_scopes p.decl_ctx p.scopes s in
|
|
|
|
(* We sort this list by scope name and then variable name to ensure consistent
|
|
|
|
output for testing*)
|
|
|
|
List.sort
|
|
|
|
(fun vc1 vc2 ->
|
|
|
|
let to_str vc =
|
|
|
|
Format.asprintf "%s.%s"
|
|
|
|
(Format.asprintf "%a" ScopeName.format_t vc.vc_scope)
|
2022-07-06 11:08:27 +03:00
|
|
|
(Bindlib.name_of (Var.get (Marked.unmark vc.vc_variable)))
|
2022-04-04 18:51:41 +03:00
|
|
|
in
|
|
|
|
String.compare (to_str vc1) (to_str vc2))
|
|
|
|
vcs
|