If expr was evaluable in the original code then why not rewrite in place after assigning temporaries?
go fn(e1,e2) { t1,t2 := e1,e2 go func() { defer instrument() fn(t1,t2) } On Fri, Nov 8, 2019 at 8:38 AM André Eriksson <ean...@gmail.com> wrote: > I am working on a type of Go preprocessor that rewrites source code to add > additional instrumentation to certain types of statements. > > One such statement is the go statement. I would like to instrument the > newly created goroutine, injecting some instrumentation code at the start > and finish of the goroutine. > > In the simple case, the rewrite is straightforward: > > go fn() > > becomes > > go func() { > defer instrument()() > fn() > }() > > However this approach does not work when fn takes parameters. > If we were to rewrite go fn(expr) into the equivalent form above: > > go func() { > defer instrument()() > fn(expr) > }() > > > the semantics change, since in the rewrite expr gets evaluated inside the > newly created goroutine, which can change the behavior and introduce data > races. > > My attempts to address this have not been particularly fruitful. > > One cannot pass in expr as an argument to the closure, because the type > of the expression may not have a valid name in the current package (for > example if expr evaluates to a private type in some other package). > > Similarly, if expr is a constant expression (like 1 or nil) the type may > depend on the corresponding parameter in fn’s signature. > > The only semantics-preserving rewrite I can think of revolves around using > package reflect, and rewriting like so: > > go func(fn reflect.Value, vals …reflect.Value) { > defer instrument() > fn.Call(vals) > }(reflect.ValueOf(fn), reflect.ValueOf(expr)) > > As far as I understand, this should be semantics-preserving, although with > a slight performance cost. (Though I imagine the cost of a reflection-based > call is dwarfed by the cost of spawning a goroutine.) > > Unfortunately this also comes with a major downside: the rewritten code > does not typecheck identically to the original code. Ideally I would like > the rewritten form to cause identical typechecking failures to the old > code, so that these errors are caught at compile time without requiring a > separate typechecking pass for the original code. > > Am I correct in the above reasoning? Can anyone think of a way to do this > sort of rewrite in a semantics-preserving and typechecking-preserving way? > > -- > You received this message because you are subscribed to the Google Groups > "golang-nuts" group. > To unsubscribe from this group and stop receiving emails from it, send an > email to golang-nuts+unsubscr...@googlegroups.com. > To view this discussion on the web visit > https://groups.google.com/d/msgid/golang-nuts/a92641f3-2eda-4d4a-ab02-d2b40e3bde75%40googlegroups.com > <https://groups.google.com/d/msgid/golang-nuts/a92641f3-2eda-4d4a-ab02-d2b40e3bde75%40googlegroups.com?utm_medium=email&utm_source=footer> > . > -- *Michael T. jonesmichael.jo...@gmail.com <michael.jo...@gmail.com>* -- You received this message because you are subscribed to the Google Groups "golang-nuts" group. To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/CALoEmQx4n-PLJ_scPHfcqNtTnyCo2y1RG_enpV-Rqe4FtxwaQg%40mail.gmail.com.