Cleaning flags - #49
Conversation
| (* Yanni : Deprecated flag *) | ||
| mutable step_typechecking_mode : Flags.typechecking_mode; | ||
| (* mutable step_flag_check_validity : bool; (* state of flag check_validity at start; must be the same at end *) *) |
There was a problem hiding this comment.
I assume line 323 is deprecated, not 322 ?
There was a problem hiding this comment.
That is correct, I just deleted the comment in the end.
| type typechecking_mode = | ||
| | Unverified (* equivalent to `resource_typing_enabled = false && check_validity = false` *) | ||
| | Annotated (* equivalent to `check_validity = false` *) | ||
| | AnnotatedAndVerified (* equivalent to `check_validity := true && preserve_specs_only = false` *) |
There was a problem hiding this comment.
I'd suggest describing their semantics independently from the previous flags so that the description stands on its own. Especially since the semantics of the previous flags was unclear, and we will delete them.
There was a problem hiding this comment.
For example, Unverified should be closer to resource_typing_enabled = false && check_validity = true in my opinion, in the sense that while the code is not verified and has no annotations, we are in this mode responsible for applying semantics-preserving transformations (not relative to the spec, but relative to the code itself).
There was a problem hiding this comment.
Alternative naming option going through my head:
Unverified-->SemanticsPreserving, we do not have specifications and proofs, so no annotations, therefore the code stands as its own specification and we must ensure that we preserve its semanticsAnnotatedAndVerified-->ProofPreserving, we have specifications and proofs in the form of annotations and we are responsible for preserving them so that typechecking can still verify the code against themAnnotated-->ProofRepairing, we have specifications and proofs in the form of annotations and we are in the process of repairing the proof, so typechecking might not be able to verify the code right now, but we promise to go back toProofPreservingat some point.
The first name matches the standard notion of semantics-preserving transformations.
The second names matches the OptiTrust notion of proof-preserving transformations (not a new notion but had different names in prior work).
The third name matches the standard notion of proof repair where the tool is being guided towards from a broken proof to a fixed proof.
There was a problem hiding this comment.
I am not sure I agree with the meaning of the Unverified flag.
For me, any transformation in this mode should be allowed to do pretty much anything. Because such transformations are suppposed to be used as intermediate steps, not high level modification.
In particular, I would expect to be able to have very powerful transformations, such as code deletion, or code addition with no restruction.
Also, is there a way to verify that the semantics are preserved? Or is it in the sense that, "We guarantee, as developers of Optitrust, that this transformation is preserving"?
There was a problem hiding this comment.
Then we might need 4 regimes with both Unverified (previously called "TrustMe") and SemanticsPreserving. Here are simple transformations that preserve semantics and do not require separation logic typechecking : deleting an empty sequence, deleting an empty loop whose range expression is effect-free (can be a syntactic check).
| (* TODO : depreciate transformation *) | ||
| (** [clear_nosimpl tg]: clears all the marks on all the instructions that where | ||
| skipped by the simplifier *) | ||
| let%transfo clear_nosimpl (tg : target) : unit = | ||
| Marks.remove Arith_core.mark_nosimpl [nbMulti; cMark Arith_core.mark_nosimpl] | ||
|
|
||
| (* TODO : depreciate transformation *) | ||
| (** [nosimplf tg]: mark all the instructions targeted by [tg] as "__arith_core_nosimpl" *) | ||
| let%transfo nosimpl (tg : target) : unit = | ||
| Marks.add Arith_core.mark_nosimpl tg |
There was a problem hiding this comment.
these look like they should still work in all modes, they're just putting marks, so deprecation might not be needed.
There was a problem hiding this comment.
You are right, I removed the comments.
| (* TODO : depreciate transformation *) | ||
| let%transfo insert_barrier (tg: target) = | ||
| Sequence_basic.insert ~reparse:false (magic_barrier ()) tg |
There was a problem hiding this comment.
I wouldn't deprecate this one.
| let%transfo convert_tail_thread_for (loops : int list) (leaf: target) = | ||
| let fission_helper tg = | ||
| Flags.with_flag Flags.check_validity true (fun () -> Loop.fission tg) in | ||
| Flags.with_flag (* Flags.check_validity true *) Flags.typechecking_mode Flags.AnnotatedAndVerified (fun () -> Loop.fission tg) in |
There was a problem hiding this comment.
There's an interested question here : if we set the flag to verified, do we expect the code to be verifiable in the sense that typechecking should succeed, or do we expect the code to be verified in the sense that typechecking did run successfully and that the type information fields are up to date ?
There was a problem hiding this comment.
Now that we mention it, I can't really visualize an example where it is relevant to wrap a function with stricter flags (in this example, going from ProofRepairing to ProofPreserving for the fission). Especially in this case since I believe the actual fission code does not have a different behavior w.r.t the flags.
There was a problem hiding this comment.
the fission code needs typechecking metadata that needs to be up-to-date, that's not possible to have during proof repair unless we define what that means
There was a problem hiding this comment.
In that case does it make sense to call it in a context where the policy is "ProofRepairing", which is the case in this case study.
There was a problem hiding this comment.
do we actually run this code in ProofRepairing ? maybe it should just be ProofPreserving throughout. I can't remember the details there.
There was a problem hiding this comment.
Ok I checked, I probably confused it with something else, this function is always called in ProofPreserving context.
| trm_fail cond "condition is not pure, more advanced checks not yet supported" | ||
| end; | ||
| end; *) | ||
| Target.reparse_after ~reparse (Target.apply_at_target_paths (insert_on cond mark mark_then mark_else else_branch)) tg |
There was a problem hiding this comment.
note that reparse_after should also be deprecated : it is used when the user might insert arbitrary plain text code that requires reparsing into a proper AST ; ideally we don't need this anymore or find a way to do this locally without reparsing the entire program.
There was a problem hiding this comment.
I removed every occurence of reparse_after that I could, but I could not remove the one in rewrite.ml. Do you think I can remove it/could you explain to me what the function actually does?
There was a problem hiding this comment.
it will print out the entire AST to text and reparse it freshly
There was a problem hiding this comment.
it's fine to deal with this after this PR though, if removal is non trivial
| let loop_mark = next_mark () in | ||
| Loop_basic.move_out ~loop_mark [cPath seq_path; Constr_depth (DepthAt 0); tSpan [tFirst] [cMarkSpanStop mark_moved]]; | ||
| if !Flags.check_validity then Resources.loop_minimize [cMark loop_mark]; | ||
| if (* !Flags.check_validity *) Flags.annotated () then Resources.loop_minimize [cMark loop_mark]; |
There was a problem hiding this comment.
I'm not sure loop minimization makes any sense if the types are not up to date, I remember it to be driven by the typing fields that need to be up-to-date. Some of these might need strengthening to "verified".
There was a problem hiding this comment.
Changed this flag, and added a condition inside the loop_minimize function, with a warning in case the flag is not ProofPreserving. We'll see in the future if this needs some strengthening/relaxing.
| into: | ||
|
|
||
| // stars_j R(j) * V | ||
| // stars_j |
There was a problem hiding this comment.
Probably a missclick on my part. Reverted the change.
| open Prelude | ||
|
|
||
| let _ = Flags.check_validity := true | ||
| let _ = Flags.typechecking_mode := Flags.AnnotatedAndVerified |
There was a problem hiding this comment.
Note that the specifications in this test don't mention values. When setting annotated and verified, specifications should probably mention values, otherwise the program is under-specified and many transformations will succeed while they probably shouldn't.
There was a problem hiding this comment.
alternative: accept weakening the test to the weaker specification
There was a problem hiding this comment.
I simply weakened the flag to ProofRepairing. I'll slowly do it to the other tests with time
| let _ = Flags.check_validity := true | ||
| (* let _ = Flags.check_validity := true *) | ||
| let _ = Flags.typechecking_mode := Flags.AnnotatedAndVerified | ||
| let _ = Flags.use_resources_with_models := true |
There was a problem hiding this comment.
I believe this flag should also be deprecated.
|
|
||
| let _ = Flags.check_validity := true | ||
| (* let _ = Flags.check_validity := true *) | ||
| let _ = Flags.typechecking_mode := Flags.AnnotatedAndVerified |
There was a problem hiding this comment.
this test was probably not designed for full functional verification.
| @@ -1,7 +1,8 @@ | |||
| (* Deprecated *) | |||
There was a problem hiding this comment.
this one should work ?
| @@ -1,7 +1,8 @@ | |||
| (* Deprecated *) | |||
There was a problem hiding this comment.
I think this test should work with validity checks but without using resource annotations ?
There was a problem hiding this comment.
this is also used in the unverified cleanup transformation
There was a problem hiding this comment.
Since we have a _models variant, we probably want to promote loop_reorder_at_models.ml into loop_reorder_at.ml. Then either we override this file, effectively removing one test, or we demote this file into something like loop_reorder_at_novalues.ml : we can still typecheck the transformation but against under-specified semantics where changing the computed values is not considered wrong (this can still be useful if you only want to check the absence of race conditions in Rust-style for example, some people actually asked me about that usage scenario at events).
| @@ -1,8 +1,9 @@ | |||
| (* Deprecated *) | |||
There was a problem hiding this comment.
we use this in the unverified cleanup, so should probably be re-enabled before merging.
There was a problem hiding this comment.
Importing models on the cpp file appears to work, so I removed the deprecated comment.
| @@ -1,7 +1,8 @@ | |||
| (* Deprecated *) | |||
There was a problem hiding this comment.
used in unverified cleanup
|
|
||
| let _ = Flags.check_validity := true | ||
| (* let _ = Flags.check_validity := true *) | ||
| let _ = Flags.use_resources_with_models := true |
There was a problem hiding this comment.
this flag should not be set anymore ?
| let _ = Flags.use_resources_with_models := true | ||
| let _ = Flags.preserve_specs_only := true | ||
| (* let _ = Flags.preserve_specs_only := true *) | ||
| let _ = Flags.typechecking_mode := Flags.Annotated |
| !! Ghost.flatten_expr_rewrites (sum_tg @ [dRHS]); | ||
| !! replace_with_tree_reduce (trm_int log_tpb) sum_tg; | ||
| !! Flags.with_flag Flags.check_validity false (fun () -> Function.inline_def [cFunDef "tree_reduce"]); | ||
| !! Flags.with_flag (* Flags.check_validity false *) Flags.typechecking_mode Flags.Unverified (fun () -> Function.inline_def [cFunDef "tree_reduce"]); |
There was a problem hiding this comment.
should this not still be Annotated mode ?
| (* Yanni : might change this flag to [Flags.Annotated] instead *) | ||
| (** [without_substep_validity_checks f] executes [f] with | ||
| the flag [check_validity] temporarily set to false. | ||
| Only for internal use; user scripts should use the [trustme] function. *) | ||
| let without_substep_validity_checks (f: unit -> 'a): 'a = | ||
| Flags.with_flag Flags.check_validity false f | ||
| Flags.with_flag (* Flags.check_validity false *) Flags.typechecking_mode Flags.Unverified f |
There was a problem hiding this comment.
I'd suggest renaming this function and checking all call sites for the intended semantics (multiple call sites may disagree on what they want).
| (* Check that [Flags.check_validity] is like at the start of the step *) | ||
| if not on_error && (!Flags.check_validity && (not !Flags.use_resources_with_models)) <> infos.step_flag_check_validity | ||
| if not on_error && (!Flags.typechecking_mode <> infos.step_typechecking_mode) (* (!Flags.check_validity && (not !Flags.use_resources_with_models)) <> infos.step_flag_check_validity *) | ||
| then raise (TraceFailure "At finalize_step, Flags.check_validity is not same as when step was opened."); |
There was a problem hiding this comment.
- need to update the failure message
- how does that work with transformations like unverified cleanup that transition from one mode to another ?
| let resource_error_expected (f: unit -> unit): unit = | ||
| failure_expected (function | ||
| | Resource_computation.ResourceError _ -> true | ||
| | _ -> false) (fun () -> f (); recompute_resources ()) |
There was a problem hiding this comment.
I think we have Resources.ensure_computed () which is potentially cheaper (avoid duplicated triggers).
| let dump_ast_details : bool ref = ref false | ||
|
|
||
| (* TODO : deprecate once optilambda surface display works *) | ||
| (* TODO Yanni : deprecate once optilambda surface display works *) |
There was a problem hiding this comment.
we can deprecate right now and incrementally improve the optilambda display to fit our needs
| let justif_correct (why : string) : unit = | ||
| if !Flags.check_validity then begin | ||
| ensure_computed (); | ||
| Trace.justif (sprintf "resources are correct: %s" why) | ||
| end | ||
| (* if !Flags.check_validity then begin *) | ||
| if Flags.annotated () then ensure_computed (); | ||
| Trace.justif (sprintf "resources are correct: %s" why) |
There was a problem hiding this comment.
I'd say this is only useful in Unverified/SemanticsPreserving mode, where there is no self-certifying proof. Calling it in another mode should trigger a warning.
There was a problem hiding this comment.
I noticed in at least one example (loop_reorder_at_models.ml) that it recomputation is necessary even with the ProofPreserving flag.
This is because some linear resource is not normalized, and can not be found in a context where it is (think 'n + 0' not found in a context where there is 'n').
I did not find an easy fix, so I removed the condition altogether, and keep it as future work to handle local renormalization of resources, instead of a full ast recomputation.
| let (unfolds, folds) = if (* !Flags.check_validity *) Flags.annotated () then begin | ||
| let (res_start, res_stop) = Resources.around_instrs span_instrs in |
There was a problem hiding this comment.
Ideally, reading any kind of computed resource information should not be allowed if they are not up-to-date. This means that this code should fail if not in AnnotatedAndVerified/ProofPreserving.
There was a problem hiding this comment.
Looks like nice progress. Some thoughts:
- We have too many deprecated tests, we should take the time to add models mentioning values for a bunch of them, we may also alternatively try running the verified mode on weaker models that don't constrain values : it is a weaker test than semantic preservation but still better than nothing.
- We have many deprecated transformations, but a lot of them would probably be worth updating to the new modes. We should make it clear that this wave of deprecation does not mean "you shouldn't use this transformation anymore", but rather "you should reimplement this transformation before using it, or truly deprecate it if you have a better API".
- In terms of short-term priority to counter-act all of this deprecation, I think we need to enforce the following before merging and going forward: every non-deprecated transformation should have a working non-deprecated unit test (including a trivial _doc test), ideally proof-preserving models mentioning values, possibly proof-preserving weaker models not mentioning values, possibly semantics-preserving without annotations/proof.
…re expected for a global reparse to remove "trm_arbitrary" arguments. (wip : solve this issue by either implementing local parsing, or meta-programmation tools for arguments)
…dentify how to add them back)
Added new flag policy, and cleaned code along the way.
I did my best to correct any bug I created, but this cleaning is far from bugproof, so I believe it is important to clean incrementally (that is : comment during the first pass, a.k.a this PR, then delete when coming on the code later on, with more experience on the usage of the new flags/the meaning of the past flag policies).