import std.stdio;
import std.variant : Variant;
struct var {
private Variant[string] values;
@property
Variant opDispatch(string name)() const {
return values[name];
}
@property
void opDispatch(string name, T)(T val) {
values[name] = val;
}
}
void main()
{
var test;
// Normal flow
test.foo = "test";
test.bar = 50;
writeln("test.foo = ", test.foo);
writeln("test.bar = ", test.bar);
// Error flow
with (test) {
foobar = 3.14;
}
writeln(test.foobar);
}
The program will exit with a "Range Violation" exception. The reason is that the compiler frontend generates a different AST when calling @property opDispatch().
Here is how the AST looks like outside of "with":
test.opDispatch("test");
test.opDispatch(50);
And here is what happens inside the WithStatement:
with (test)
{
(VariantN!32LU __tmpfordtor701 = ((VariantN!32LU __tmpfordtor700 = (*__withSym).opDispatch();) , __tmpfordtor700).opAssign(3.14);) , __tmpfordtor701;
}
Which means that it first calls the getter, captures a reference to a variable, and finally calls opDispatch setter on this variable.
The right behavior would be to call the setter directly like this:
with (test)
{
var __tmp = test;
__tmp.opDispatch(3.14);
}
Dlang online sandbox link: https://run.dlang.io/is/pwbm0d
Gist: https://gist.github.com/run-dlang/a8fbbc5dc49346b887b9aaee8758ffca
Comment #1 by ag0aep6g — 2019-01-16T00:25:11Z
I'm adding the "wrong-code" keyword again, assuming that you removed it by accident, Mike Franklin.
Comment #2 by robert.schadek — 2024-12-13T19:02:01Z