Hey guys, I'm trying to implement my own dot operator for a type and the following problem(s) keep me occupied:
import macros
type Foo = object
x, y: int
template `.`*(this: Foo, field: static[string]): expr =
when field == "bar": this.x
elif field == "baz": this.y
else: error("Non-existent field!")
var foo = Foo(x: 4, y: 2) #edit: fixed a typo
echo foo.bar # works, prints 4
echo foo.baz # works, prints 2
echo foo.bam
# Here, instead of yielding the intended error message, compilation instead fails with:
# Error: type mismatch: got (empty) but expected one of:
# system.$(x: int)
# system.$(x: T)
# etc.
I guess as long as trying to access an invalid (pseudo-)field this way yields a compile-time error, I'm happy, but I'd like to get a clearer error message in this case. Is there a way to achieve this? Also, if I try to replace the when-elif-else construct with a case-of, I get "Error: value of type 'int' has to be discarded" on the first branch. It's pretty confusing and if someone knows the reasons for this behavior, please let me know.
I had to change var foo: Foo(x: 4, y: 2) to var foo = Foo(x: 4, y: 2) to make it compile at all.
When I replace the else: with:
else:
error("Non-existent field!")
0
or use error("something") as first line in the template (to test it) I get a segfault at compile time.
Removing the "error()" and just have else: 0 it "works" so I think there is something going on with macros.error()
Sorry for the typo, I fixed it in the first post. After doing some more experimenting based on your suggestion, I settled for the 'fatal' pragma to enforce a compilation error (see code below). This works, but while testing it I stumbled upon the following quirk:
type Foo = object
x, y: int
template `.`*(this: Foo, field: static[string]): expr =
when field == "bar": this.x
elif field == "baz": this.y
else: {.fatal: "undeclared field: '" & field & "'".}
var foo = Foo(x: 4, y: 2)
echo foo.bar # works, prints 4
echo foo.baz # works, prints 2
echo foo.bam # compilation fails with the correct error message
var x = f.x # works normally
var bar = f.bar # yields a type error:
# Error: type mismatch: got (expr) but expected one of:
# system.$(x: int)
# system.$(x: T)
# etc.
# here, the compiler should give the intended error message
var bam = 0
echo f.bam
# but instead it yields the same type error as before
This happens every time there is an already declared symbol with the same identifier ('bar' and 'bam' in this case) as the dot expression I'm trying to call. My guess is that the compiler somehow thinks that 'f.bam' is a call to the symbol 'bam' (which isn't callable). Is this the intended behavior? Is there some way around it?