Bug 17474 – non-property being treated as a property

Status
NEW
Severity
major
Priority
P2
Component
dmd
Product
D
Version
D2
Platform
All
OS
All
Creation time
2017-06-06T23:08:51Z
Last change time
2024-12-13T18:52:25Z
Keywords
industry
Assigned to
No Owner
Creator
Eyal
Moved to GitHub: dmd#19257 →

Comments

Comment #0 by eyal — 2017-06-06T23:08:51Z
int* gInt; ref int* getTheIntPtr(string str = "Hello") { assert(str !is null); return gInt; } unittest { int x; getTheIntPtr = &x; getTheIntPtr = null; // oops, assertion failure } This has blown up and took us quite a while to figure out we were passing null as an argument to a function when we use ASSIGNMENT syntax(!!).
Comment #1 by tomer — 2017-06-07T07:44:20Z
I would like to elaborate a little. The existence/absence of a @property doesn't matter here. The thing is, the compiler first tries to lower `foo = bar` to `foo(bar)`. If it doesn't work, it will try `foo() = bar` which is what we expect. However, in the case of `foo = null`, the lowering to `foo(null)` does match because you can pass `null` for a string. So, depending on the *value* I'm assigning, it will choose different code paths, even though I'm expecting only the second behavior. This bug was caught in a UT, but figuring it out required I look at the generated assembly because it was impossible to understand otherwise. I don't know what's the "right semantics" here. It's basically the interaction of several different features that cause this, but I definitely know it's not what I expect from looking at the code.
Comment #2 by dfj1esp02 — 2017-06-07T11:12:06Z
If you think it's not a property, you should invoke it with braces.
Comment #3 by uplink.coder — 2017-06-09T21:22:30Z
I have a pr for this. Could be a bit more polished though. Comments welcome. https://github.com/dlang/dmd/pull/6881
Comment #4 by bugzilla — 2017-06-10T02:01:32Z
I'm not sure what the "right" semantics are, either, because each of the behaviors can be justified. But I can suggest avoiding using default function arguments. Default arguments are a good solution for adding parameters without breaking existing code, but are not a good practice otherwise. The behavior observed here, as well as its interaction with overloading, are examples. The only other thing I can think of is issuing a warning if a function with a default argument is being used as a setter.
Comment #5 by uplink.coder — 2017-06-10T04:04:53Z
(In reply to Walter Bright from comment #4) > I'm not sure what the "right" semantics are, either, because each of the > behaviors can be justified. I am pretty sure the last thing you want is a function to be invoked as a setter property if it returns an Lvalue.
Comment #6 by tomer — 2017-06-10T06:11:59Z
(In reply to Walter Bright from comment #4) > But I can suggest avoiding using default function arguments. Default > arguments are a good solution for adding parameters without breaking > existing code, but are not a good practice otherwise. that's exactly what happened -- i added this default parameter to an existing function that did not have parameters, and it broke in a very peculiar way. i ended up using ref T _fiberLocal(T)(FiberLocalBlock* block) {...} ref T fiberLocal(T)() {_fiberLocal!T(_currentFiberLocalBlock);} again, i'm fine with this workaround, but unless our UTs caught that early on, debugging it on a live system would have been impossible. i agree with stefan that a function returning `ref` shouldn't be allowed in property-setter syntax. either it shouldn't be allowed at all, i.e., `foo = bar` won't compile if foo returns `ref` and takes an argument -- or only allow the lowering to `foo() = bar`
Comment #7 by tomer — 2017-06-10T06:12:20Z
(In reply to Stefan Koch from comment #5) > I am pretty sure the last thing you want is a function to be invoked as a > setter property if it returns an Lvalue. +1
Comment #8 by eyal — 2017-06-10T06:22:18Z
Requiring @property on a for a=b to invoke a(b) sounds much more reasonable than the opposite. I don't see how a=b invoking a(b) when a isn't a @property is justifiable.
Comment #9 by dlang-bugzilla — 2017-06-10T15:56:44Z
(In reply to Eyal from comment #8) > I don't see how a=b invoking a(b) when a isn't a @property is justifiable. Yep, that `writeln = "Hello, world!";` works is an infamous weirdness about D properties.
Comment #10 by dfj1esp02 — 2017-06-14T14:08:58Z
(In reply to Stefan Koch from comment #5) > I am pretty sure the last thing you want is a function to be invoked as a > setter property if it returns an Lvalue. AFAIK it's deliberate as this way you can implement getter and setter with one function. The right semantics here will be difficult to define due to sheer number of reasons why a(b) may fail to compile and which of them should be skipped and which shouldn't. (In reply to Eyal from comment #8) > Requiring @property on a for a=b to invoke a(b) sounds much more reasonable > than the opposite. > > I don't see how a=b invoking a(b) when a isn't a @property is justifiable. It reduces code littering (and it's not easier to check). UFCS relies on property syntax too.
Comment #11 by eyal — 2017-06-14T18:11:12Z
(In reply to anonymous4 from comment #10) > (In reply to Eyal from comment #8) > > Requiring @property on a for a=b to invoke a(b) sounds much more reasonable > > than the opposite. > > > > I don't see how a=b invoking a(b) when a isn't a @property is justifiable. > > It reduces code littering (and it's not easier to check). UFCS relies on > property syntax too. What code littering does it reduce? @property is not littering, it is informative. UCFS doesn't rely on property syntax, lack of parenthesis relies on property syntax - but not on *setter* syntax, just *getter*. So I repeat: There is no justification for x=y calling x(y) when x is not a @property.
Comment #12 by robert.schadek — 2024-12-13T18:52:25Z
THIS ISSUE HAS BEEN MOVED TO GITHUB https://github.com/dlang/dmd/issues/19257 DO NOT COMMENT HERE ANYMORE, NOBODY WILL SEE IT, THIS ISSUE HAS BEEN MOVED TO GITHUB