-
Notifications
You must be signed in to change notification settings - Fork 17
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[RFC] Add support for the Model Checking Intermediate Language (MCIL) #170
base: master
Are you sure you want to change the base?
Conversation
Co-authored-by: kian-weimer <[email protected]>
Thank you for the contribution ! I haven't had time to read everything yet, but I already have some questions/remarks:
|
@Gbury , thank you for your quick feedback. I addressed your first two points. I'm not sure about the last point. Unless you feel strongly about that change, I would postpone it. |
Agreed, ^^ Another question: why add a dedicated command for declaring enumeration ( |
I don't know the answer. I only know that the dedicated command is part of the current MCIL specification. I can ask around to figure out the rationale. |
I asked around. The |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall, the code is mostly good. I did a general overview and left comments to address the major design choices (e.g. having the code checking systems in a separate file instead of in thf.ml
).
One question I've been pondering is whether we want to keep the name System
, as it lacks a bit of context to know exactly what it's referring to (in particular in a context that is otherwise usually used for theorem proving). Maybe something that more explicitly mentions model checking would be better.
src/typecheck/thf.ml
Outdated
@@ -2527,9 +2551,268 @@ module Make | |||
) d.contents | |||
end | |||
|
|||
(* High-level parsing function *) | |||
(* MCIL Only System Definitions and Checks *) | |||
(* ************************************************************************ *) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that most of this new code could actually live in a separate functor and file. This can be done using the get_global_custom
and set_global_custom
functions to manage the table of defined systems (instead of adding it in the type definition for env
). It should not require to change code too much, and makes the model-checking part more independant.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And, in that process of moving that code to another file and functor, you end up needing some functions from thf.ml
to be exposed, don't hesitate to add them to the intf.ml
file so that they can be accessed from outside.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Addressed.
src/typecheck/thf.ml
Outdated
Was causing errors and its purpose is unknown. *) | ||
(* List.iter (check_used_term_var ~kind:`Function_param env) input; | ||
List.iter (check_used_term_var ~kind:`Function_param env) output; | ||
List.iter (check_used_term_var ~kind:`Function_param env) local; *) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These lines are used to emit warnings in cases when some of the variables are not used. If the warnings emitted are spurious, it may be because the mark_term_var_as_used
function was not called at a point where it should have been called.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I put the code back again, and now it works fine.
src/typecheck/thf.ml
Outdated
|
||
let id, f = id_for_sig ssig_env input output local d in | ||
|
||
(* [Kian] Finalize Def -- What do these do? *) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll try and answer what's the use of the functions below, ^^
Additionally, if you could make a list of the functions/places where you think additional documentation would be useful so that I can add it, that'd be great !
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for your explanations! I'll let you know.
src/typecheck/thf.ml
Outdated
(* [Kian] Finalize Def -- What do these do? *) | ||
|
||
(* let def = parse_def ssig t in | ||
let ssig = close_wildcards_in_sig ssig t in |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function's goal is to resolve any leftover type constraints.
For more context: in order to accommodate some other languages (e.g. alt-ergo's and zipperposition's native formats), the typechecker can perform some type inference. It does so by creating some type wildcards, which are akin to type "holes" that can be filled in when we unify some types (which happen when typechecking an application of a pattern match for instance). That means that once we get to the top-level of a definition, we have to check that there aren't any "holes" left (and either fill these using a heuristic, or emit errors), and that's the goal of that function.
In addition, some languages (mainly ae's native language) have implicit type quantification, so the close_wildcards_in_sig
function can sometimes add some type variables that arise from type inference.
In this case here that should not happen (at least not for now), so I think that instead of that you can simply use the check_no_free_wildcards
function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Addressed.
src/typecheck/thf.ml
Outdated
|
||
(* [Kian] Finalize Def -- What do these do? *) | ||
|
||
(* let def = parse_def ssig t in |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That function is here because in the general case of recursive definitions, you have to first determine the signatures/type/sorts of the defined symbols, add them to the env, and then you can type the definition. In the case of automata systems, I don't think that you can define recursive systems, so the distinction between determining the signature and the actual definition is not needed, and therefore you can just remove this line.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got it. Thanks!
The new code uses the `get_global_custom` and `set_global_custom` functions to manage the table of defined systems, and calls the check_no_free_wildcards function. This commit also removes the system constant that is not necessary anymore. The code that emits warnings for unused variables is enabled again; no issues were found.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Apologies for the very late review.
Report.Error.mk ~code ~mnemonic:"duplicate-definition" | ||
~message:(fun fmt (id, file, old) -> | ||
Format.fprintf fmt | ||
"Duplicate declaration of %a, which was already defined at %a" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The error name and mnemonic uses definition
but the error message here says declaration
, is it intentional ? is there a difference between declaration/definition in mcil ?
@@ -213,3 +243,6 @@ val print_group : | |||
Format.formatter -> 'a group -> unit | |||
(* Printer for groups. *) | |||
|
|||
val print_def_sys : Format.formatter -> sys_def -> unit | |||
(* Printer for system definition. *) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
print_chk_sys
could probably also be exposed here ?
val file : env -> Dolmen.Std.Loc.file | ||
(** Get the file for an env. *) | ||
|
||
val add_term_var : env -> Dolmen.Std.Id.t -> term_var -> Dolmen.Std.Term.t -> env |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function (and the two functions after) lack doc comments, tell me if you need me to write these.
| Duplicate_definition : Dolmen.Std.Id.t * Dolmen.Std.Loc.t -> Dolmen.Std.Loc.t Type.err | ||
|
||
|
||
let key = Dolmen.Std.Tag.create () |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it would be better to rename this to a more informative name, such as sys_definitions
parse_subsystems env d | ||
|
||
let finalize_sys (d : Stmt.sys_def) ((env, _), input, output, local) = | ||
Type.check_no_free_wildcards env d.init; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These functions should be called right after typing the corresponding ast. In essence, wildcards are added to the global state
stored inside the env
, hence why the check_no_free_wildcards
should be called right after typing, to ensure that all free wildcards were actually created when typing some ast.
Type.check_no_free_wildcards env d.init; | ||
Type.check_no_free_wildcards env d.trans; | ||
Type.check_no_free_wildcards env d.inv; | ||
let input, output, local = vars input, vars output, vars local in |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This way of writing code is a bit mislead I think. Instead I think it'd be better to 'inline' the call to vars
for each one of the List.iter
below.
if (n1 != n2) then _bad_arity env (Id id) n1 n2 a ; | ||
List.iter2 | ||
(fun (_, v1, _) (_, v2, a2) -> | ||
ensure env a2 (Type.T.of_var v2) (Type.T.Var.ty v1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that here it would be better to directly check for equality of the types, and raise an error if the equality does not hold.
if not (M.mem s cond_ids) then | ||
Type._error env (Ast ast) (Type.Cannot_find (s, "")); | ||
) | ||
| _ -> assert false |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this should be a proper error, not an assert false
(** Parse a transition system definition *) | ||
|
||
val parse_check : Type.env -> Dolmen.Std.Statement.sys_check -> [> `Sys_check ] | ||
(** Parse a transition system check *) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As mentionned in other comments: the current types here (and in parse_def
) will lose information (e.g. the transitions in parse_def
, and everything in this case), and we should not lose information
This PR adds MCIL as a version of the smtlib2 language. The current specification of the language can be found here.