golang/go

spec: add __FILE__ and __LINE__ macro

j16sdiz opened this issue · 7 comments

The current Caller() method is more flexible, but it encourage hardcoded stack depth constant (see https://golang.org/src/log/log.go#173 ). This is easy to bitrot.

Constant (or marco) like __FILE__ push the responsibility to the caller. Maybe a little bit hard to use, but it is simpler to maintain.

Reference: https://groups.google.com/d/msg/golang-nuts/3_UFKPKIzRU/HKCC8KqOhQYJ

minux commented

Go doesn't have anything like this before, so I'd hesitate
to introduce these.

Also FILE and LINE look quite ugly in Go.

The reference you cited is a few years old, and I'm wondering
if @ianlancetaylor still thinks we might want to add these to
Go?

I can see the use for access to what are constants known to the compiler, and it's not entirely unprecedented in Go: The predeclared constant iota exposes such a mechanism (it counts semicolons inside a const declaration, not unlike __LINE__ counts lines inside a file).

But I think introducing more such predeclared constants is not the right path. For one, __FILE__ and __LINE__ don't really fit into the aesthetics of Go. Also, it opens the door for ever more explicitly named such constants.

We do have other means to handle such things in a more Go-like way. I can think of 2:

  1. Introduce a new predeclared built-in function (better name choice welcome):
// compilerinfo accepts a string argument "filename", "line", or "timestamp" and returns the
// filename of the file being compiled, the line number of the compilerinfo invocation, or the
// time of compilation, respectively. The argument must be a constant string, and the result is
// a constant string.
func compilerinfo(string) string

Such a mechanism would be easily extensible. It would mean that we extend the language by this additional builtin.

  1. Introduce a new compiler-known magic package, say "compiler" (similar to package "unsafe"). Or maybe it should be in "unsafe" since results depend on file names, line numbers etc. which might not be considered "portable". Either way, such a packet could export functions like Filename(), Line(), Timestamp(), etc. that produce constant results (package unsafe does this already, so maybe that's the right place for these).

Something to think about.

I (now) agree that __FILE__ and __LINE__ can't be right.

It's hard for me to picture how these would be used. Not many people write out __FILE__ and __LINE__ in C code. Instead, they refer to them in macros. But obviously Go doesn't have macros, and you can get already the information in a function by using runtime.Caller. I don't think the concern about a hardcoded stack depth constant is valid, particularly if that depth is 1. That is, the equivalent of a C trace #define that uses __FILE__ and __LINE__ is a Go function that uses runtime.Caller(1) to find out its caller's file and line.

So I would like to see a real example of how these would be used before we even consider adding them.

@ianlancetaylor
I was trying to solve the concern in this comment sirupsen/logrus#63 (comment)

logrus have multiple logging code path and unstable internal API. Hardcoded stack depth are prone to bitrot.

I agree compiler magics are hard to use, but they are hard to break too.

minux commented

Without going through the long issue, could you please give
an example where FILE and LINE macro could
help whereas calling runtime.Caller(0) won't?

It occurs to me that you can actually write these yourself very easily as functions.

func file() string {
    _, file, _, _ := runtime.Caller(0)
    return file
}

func line() int {
    _, _, line, _ := runtime.Caller(0)
    return line
}

Now for __FILE__ write file() and for __LINE__ write line().

I'm going to close this.

Here is an implementation of it.
Go 1.6.2:
mingodad@d7eed61

Go master (1.7):
mingodad@6f7c067

For gccgo (6.1):
https://gist.github.com/mingodad/e9ccc835021f9004ef9d82d178c0f460