Skip to content
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

Caller tracking. #99

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
94 changes: 94 additions & 0 deletions proposals/0000-track-caller.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# Caller tracking.

* Proposal: [HXP-NNNN](NNNN-filename.md)
* Author: [Zeta](https://github.com/Apprentice-Alchemist)

## Introduction

`@:trackCaller` metadata + `haxe.Magic.callerLocation()` as a replacement for `?pos:haxe.PosInfos`.

## Motivation

The magic `?pos:haxe.PosInfos` argument does not work with rest arguments.

## Detailed design

```hx
@:trackCaller
function foo() {
trace(haxe.Magic.callerLocation());
}

foo();

// turns into

function foo(caller:haxe.PosInfos) {
trace(caller);
}

foo($currentLocation);
```
When rest arguments are involved, the exact implementation may be different (possibly depending on how they are implemented on the target).

`var closureOfTrackCallerFun = foo` turns into `var closureOfTrackCallerFun = foo.bind($currentLocation)`.

`haxe.Magic.callerLocation()` returns the position of the last call of a `@:trackCaller` function in a non-trackcaller functions.
This behaviour could be then be turned off by using `@:trackCaller(noPropagate)`.

For example:
```hx
@:trackCaller
function bar() {
foo();
}

@:trackCaller
function foo() {
trace(haxe.Magic.callerLocation());
}

// turns into

function bar(caller: haxe.PosInfos) {
foo(caller);
}

function foo(caller:haxe.PosInfos) {
trace(caller);
}
```

## Impact on existing code

None.

## Drawbacks

None.

## Alternatives

The magic `?pos:haxe.PosInfos` argument could be made even more magic by being allowed after Rest arguments.

Walking up the stack in `callerLocation` is also a possibility, but would break when inlining is involved.
It also requires generation of some kind of debug info.

Make the compiler allow `callerLocation` in default arguments eg `pos:haxe.PosInfos = callerLocation()`.
Would break in the case `(pos:haxe.PosInfos = callerLocation(), ...rest:haxe.PosInfos)`.

Make `@:callerLocation` meta on default argument eg `@:callerLocation pos:haxe.PosInfos = cast null`.
Would break in the case `(@:callerLocation pos:haxe.PosInfos = cast null, ...rest:haxe.PosInfos)` unless magic semantics are given to the default argument when annotated with `@:callerLocation`.

## Opening possibilities

Turn `trace` into `@:trackCaller(noPropagate) function trace(...args:Dynamic)`.
Then the only magic left would be auto-importing it everywhere.

## Unresolved questions

`haxe.Magic` is a bit too magic. Where should the `callerLocation` function live? A module-level function in `haxe.PosInfos`?

How this would work with interfaces and abstract/overriden methods.

Is `noPropagate` the best name to disable propagation? Maybe `@:trackCaller(ignorePropagation)` is better?