-
Notifications
You must be signed in to change notification settings - Fork 109
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
Implement debug expressions #684
Conversation
fixes #683 see also ACT-118
it sadly dies there...
this cries for implicit variables Next: look into - functorising for release being true/false (sode size duplication?) - using a function reading a mutable cell both bring their own ugliness, but what is the best?
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.
Just a few things to be aware of.
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.
Frontend stuff LGTM, leaving backend to Joachim.
I was looking for a blog post that I read about the functor configuration pattern in OCaml, but can't seem to find it. This comes close: https://discuss.ocaml.org/t/is-idomatic-in-ocaml-to-take-configuration-and-return-a-module-that-uses-it/2628 Maybe a first class module is even easier: https://v1.realworldocaml.org/v1/en/html/first-class-modules.html |
@@ -779,6 +779,9 @@ and infer_exp'' env exp : T.typ = | |||
let t = check_typ env typ in | |||
if not env.pre then check_exp (add_lab env id.it t) t exp1; | |||
t | |||
| DebugE exp1 -> | |||
if not env.pre then check_exp env T.unit exp1; | |||
T.unit |
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 guess we didn't discuss the typing rule(s) for debug blocks.
It makes sense to insist that they return unit type.
A more permissive approach, which I think we should skip for now, would permit each debug block to have a return type that becomes optional via the debug block (the debug block would introduce the optional type around the block expression's type), where the debug-on and debug-off cases correspond with returning the some (?_
) value and returning the none (null
) value, respectively.
An argument in favor (and also against!) this optional typing rule is that it permits a program to dynamically know the debug status within the program's dynamic logic, should that ever be needed for conditional logic that can't be simply turned on/off via conditional compilation. I don't have concrete use cases in mind, admittedly. It also seems like it may be a little too powerful for the common case.
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.
We discuss the typing in #683. You can check the debug status by setting a mutable variable from within the debug
block. Isn't that enough?
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.
Ah, thanks for the pointer; I'll copy the comment above there to continue.
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.
[After writing the comment further up, I thought of a use case and have developed a stronger preference toward optional-valued debug blocks over this version. #683 has that discussion.]
@rossberg @crusso @nomeata @matthewhammer I have adapted the "configuration by functor" idea. Still not sure if this is the best way. PTAL. |
adds `-r` flag to run.sh and a test/run-release directory where the release tests live
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.
No backend changes to speak of, so happy with this.
src/pipeline/pipeline.ml
Outdated
|
||
(* Interpretation (Source) *) | ||
|
||
let interpret_prog denv prog : (Value.value * Interpret.scope) option = | ||
let open Interpret in | ||
phase "Interpreting" prog.Source.note; | ||
let flags = { trace = !Flags.trace; print_depth = !Flags.print_depth } in | ||
let result = Interpret.interpret_prog flags denv prog in | ||
let module Interpreter = MakeInterpreter(val conf ()) 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.
Why (repeatedly) instantiate this locally and not simply up where you define the conf argument?
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.
Trying that next. My gut feeling is that it'll evaluate let release = !Flags.release_mode
too early. I'll see.
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.
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 see, doing it eagerly is reading the flag too early. I think the mix of state and parameterisation as the PR now does isn't a healthy mix. If you want to parameterise, then you'll need to do so on all levels, such that you can get rid of the global state. That is, the main module, after configuring the flags, has to instantiate the pipeline etc., e.g., as a functor. That's possible but somewhat tedious and intrusive, which is why the preference was resorting to state, as with existing flags.
I suggest sticking to state for this PR and leaving a broader configuration refactoring to a later PR (that we can do when there is an urgent enough need for change).
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.
Yep, the weight-to-gain ratio is pretty bad. I have committed (e6f5e69) a workable solution wich would still need factoring (library as_config
out). Anyway I'll back out all the functor mumbo-jumbo, and we can bring it back any time from this PR. I'll simply use bool ref
s for this purpose.
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 is now implemented in d551c87 (and a small follow-up). PTAL.
Still to do: - avoid repeated `module type Conf`( in 4 places now) - avoide repeated `struct`
Causes regression due to premature evaluation of !Flags.release_mode. This commit will be reverted unless the following remedy is applied: make `release` in the parameter signature a function `unit -> bool`. This swould mean that - the parameter gets more weight (being passed around recursively) - churn in the many copies of `Conf` signatures The latter has a cure, by moving it into an `as_config` library.
Removes the fancy functor stuff for now.
73275f7
to
d7ed2eb
Compare
@rossberg ok to go in? |
src/exes/asc.ml
Outdated
Arg.Unit | ||
(fun () -> | ||
As_interpreter.Interpret.release_mode := true; | ||
Lowering.Desugar.release_mode := true), |
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.
Hm, it doesn't seem good to duplicate the state. Can't we have just one? Pipeline.Flags seems like the obvious place for now.
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.
Well, lowering
or as_interpreter
can’t depend on pipeline
.
That’s why I argued for a dedicated flags
or config
library earlier.
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.
Yeah, now that we have subdirs, why not simply pull out pipeline/Pipeline.Flags into pipeline/Flags?
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.
Totally agree (that redundant state is suboptimal), but how will we distribute the central state? By passing the value to the interpreter and desugarer? Then what?
- in the respective routines pass down the flag recursively. This is what I did in the very first implementation for the desugarer, and it is highly tedious. I did not repeat the same exercise for the interpreter, as I expected even more ugliness.
- we could also set module local state based on that input to the bottleneck routines and use that confined flag locally.
I chose with the current implementation the same pragmatics as the backend uses for selection of the target embedder (compile_mode
). There only one reference cell exists, which is directly set from the main program asc.ml
. It is not orchestrated by pipeline.ml
. The situation here is only different in the regard that the same information is needed by two modules.
Do you suggest to implement one to the above two avenues? I have I overlooked another possibility. To me none of the two seem to bring particular benefits.
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.
All I'm saying is: make Pipeline.Flags a separate module instead of a nested one and put the flag in there.
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.
@ggreif, sorry, I don't follow. AFAICS, there should not be any need for introducing a new dir or a new library, just add a new module.
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.
@rossberg We are dealing with dune
-managed entities here. There are library dependencies. pipeline
lib depends on lowering
lib (as stated in its dune
file). dune
(the build tool) won't let you grab a library's module unless you depend on it. E.g. for lowering
to use library pipeline
's module Flags
there would be a circular library dependency. Which is forbidden in dune
. @nomeata please correct me if I'm wrong (or just dense).
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.
Oh, I see, you are saying that this would make the dune libraries recursive. Sorry, I misread what you and Joachim were saying.
Okay, in that case, yeah, we'd need a new dir, or perhaps the simplest thing is to create a new flags module in as_def?
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.
Please look at my recent commit, and keep bike shedding :-) Of course we can have Flags
module in as_def
. I am pretty much neutral on this one.
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.
Actually, looking at as_def
it is very much just the AST of terms, so flags would be rather alien in there, as they also have codegen/execution aspects.
and thius eliminate redundantly duplicated state `release_mode`
This reverts commit 20f40d8.
Commit 20f40d8 will be part of a different PR. It slipped in unintentionally and has been reverted. |
Implements #683.