www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Unittest Absurdity

reply Ruby The Roobster <rubytheroobster yandex.com> writes:
How do I get unittests to actually execute operator overloads?

E.g.:

```d
struct Struct
{
     void opOpAssign(string op)(Struct rhs) //Assume that the 
operator `/=` is implemented here
     {
         //...
     }
}

unittest
{
    Struct a = Struct(1);

    Struct b = Struct(2);

    a /= b;

    assert(a == Struct(5, 0, -1));
}
```

The unittest completely ignores the `a /= b`.

Unittests also ignore swapping `a` for `a/b` in the `assert` 
statement, and even if I force the operator overloads to give me 
the correct answers, the `assert` still fails.

Any function other than an operator overload seems to work fine.
Aug 04 2022
parent reply Ruby The Roobster <rubytheroobster yandex.com> writes:
On Friday, 5 August 2022 at 01:23:40 UTC, Ruby The Roobster wrote:
 [SNIP]
 Any function other than an operator overload seems to work fine.
Also, this isn't mentioned in the spec. Additional Information: Fails for both DMD and LDC on Windows x86_64 for dmd v2.100.1
Aug 04 2022
parent reply jfondren <julian.fondren gmail.com> writes:
On Friday, 5 August 2022 at 01:25:50 UTC, Ruby The Roobster wrote:
 On Friday, 5 August 2022 at 01:23:40 UTC, Ruby The Roobster 
 wrote:
 [SNIP]
 Any function other than an operator overload seems to work 
 fine.
Also, this isn't mentioned in the spec. Additional Information: Fails for both DMD and LDC on Windows x86_64 for dmd v2.100.1
unittest blocks are just anonymous functions. They're not a special environment and there's nothing you can do here that wouldn't be necessary in any other user of your code. You have some other problem (which could very well be a compiler bug.)
Aug 04 2022
parent reply jfondren <julian.fondren gmail.com> writes:
On Friday, 5 August 2022 at 01:38:48 UTC, jfondren wrote:

Here's a complete example that passes tests:

```d
struct S {
     int n;
     void opOpAssign(string op)(S rhs) if (op == "/") {
         n++;
     }
}

unittest {
     auto a = S(1), b = S(2);
     a /= b;
     b /= a;
     assert(a.n == 2);
     assert(b.n == 3);
}
```
Aug 04 2022
parent reply Ruby The Roobster <rubytheroobster yandex.com> writes:
On Friday, 5 August 2022 at 01:42:23 UTC, jfondren wrote:
 On Friday, 5 August 2022 at 01:38:48 UTC, jfondren wrote:

 Here's a complete example that passes tests:

 ```d
 struct S {
     int n;
     void opOpAssign(string op)(S rhs) if (op == "/") {
         n++;
     }
 }

 unittest {
     auto a = S(1), b = S(2);
     a /= b;
     b /= a;
     assert(a.n == 2);
     assert(b.n == 3);
 }
 ```
I found the issue: opOpAssign isn't getting called at all. I have no idea why, though.
Aug 04 2022
next sibling parent reply Ruby The Roobster <rubytheroobster yandex.com> writes:
On Friday, 5 August 2022 at 01:47:07 UTC, Ruby The Roobster wrote:
 On Friday, 5 August 2022 at 01:42:23 UTC, jfondren wrote:
 On Friday, 5 August 2022 at 01:38:48 UTC, jfondren wrote:

 Here's a complete example that passes tests:

 ```d
 struct S {
     int n;
     void opOpAssign(string op)(S rhs) if (op == "/") {
         n++;
     }
 }

 unittest {
     auto a = S(1), b = S(2);
     a /= b;
     b /= a;
     assert(a.n == 2);
     assert(b.n == 3);
 }
 ```
I found the issue: opOpAssign isn't getting called at all. I have no idea why, though.
Even then, I still have a bug, but that is completely unrelated to this issue.
Aug 04 2022
parent reply Ruby The Roobster <rubytheroobster yandex.com> writes:
On Friday, 5 August 2022 at 01:51:21 UTC, Ruby The Roobster wrote:
 On Friday, 5 August 2022 at 01:47:07 UTC, Ruby The Roobster 
 wrote:
 On Friday, 5 August 2022 at 01:42:23 UTC, jfondren wrote:
 On Friday, 5 August 2022 at 01:38:48 UTC, jfondren wrote:

 Here's a complete example that passes tests:

 ```d
 struct S {
     int n;
     void opOpAssign(string op)(S rhs) if (op == "/") {
         n++;
     }
 }

 unittest {
     auto a = S(1), b = S(2);
     a /= b;
     b /= a;
     assert(a.n == 2);
     assert(b.n == 3);
 }
 ```
I found the issue: opOpAssign isn't getting called at all. I have no idea why, though.
Even then, I still have a bug, but that is completely unrelated to this issue.
Nevermind. I have to remove the trailing equals sign.
Aug 04 2022
parent jfondren <julian.fondren gmail.com> writes:
On Friday, 5 August 2022 at 01:53:42 UTC, Ruby The Roobster wrote:
 On Friday, 5 August 2022 at 01:38:48 UTC, jfondren wrote:

 Here's a complete example that passes tests:

 ```d
 struct S {
     int n;
     void opOpAssign(string op)(S rhs) if (op == "/") {
         n++;
     }
 }
Nevermind. I have to remove the trailing equals sign.
ah, so you had `(op == "/=")` instead? I think this is a problem actually, maybe arguably not a bug, but not desirable. Consider: ```d struct S { int n; void opOpAssign(string op)(S rhs) if (op == "/=") { n++; } void opOpAssign(string op)(S rhs) if (op == "/") { // do nothing } } unittest { auto a = S(1), b = S(2); a /= b; b /= a; assert(a.n == 2); assert(b.n == 3); } ``` The test fails because the second definition is used (preventing a "none of the overlords are callable" error) and the first definition never matches because it's wrong. I say this arguably isn't a bug because it can still be used: ```d a.opOpAssign!"/="(b); b.opOpAssign!"/="(a); ``` but what's actually wanted is operator overloading and a mistake at the definition is silently allowed.
Aug 04 2022
prev sibling parent reply Paul Backus <snarwin gmail.com> writes:
On Friday, 5 August 2022 at 01:47:07 UTC, Ruby The Roobster wrote:
 I found the issue:  opOpAssign isn't getting called at all.  I 
 have no idea why, though.
Given that the example works, the problem must be in some other part of your code that you haven't posted. If you can post a more complete example that reproduces the problem, we can probably help you fix it.
Aug 04 2022
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 8/4/22 9:51 PM, Paul Backus wrote:
 On Friday, 5 August 2022 at 01:47:07 UTC, Ruby The Roobster wrote:
 I found the issue:  opOpAssign isn't getting called at all.  I have no 
 idea why, though.
Given that the example works, the problem must be in some other part of your code that you haven't posted. If you can post a more complete example that reproduces the problem, we can probably help you fix it.
Another option: use -vcg-ast, and have the compiler tell you what it's actually calling. It's not ignoring that line, it's just not doing what you think it's doing. -Steve
Aug 04 2022
parent reply jfondren <julian.fondren gmail.com> writes:
On Friday, 5 August 2022 at 02:20:31 UTC, Steven Schveighoffer 
wrote:
 On 8/4/22 9:51 PM, Paul Backus wrote:
 Another option: use -vcg-ast, and have the compiler tell you 
 what it's actually calling. It's not ignoring that line, it's 
 just not doing what you think it's doing.
The output's not that useful... ```d import object; struct S { int n; void opOpAssign(string op)(S rhs) if (op == "/=") { n++; } void opOpAssign(string op)(S rhs) if (op == "/") { } } unittest { S a = S(1); S b = S(2); a.opOpAssign(b); b.opOpAssign(a); assert(a.n == 2); assert(b.n == 3); } ... ``` With this tiny example code it's clear by the end when there's only this one template instantiation: ```d opOpAssign!"/" { pure nothrow nogc safe void opOpAssign(S rhs) { } } ```
Aug 04 2022
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 8/4/22 10:27 PM, jfondren wrote:
 
 The output's not that useful...
 
 ```d
 import object;
 struct S
 {
      int n;
      void opOpAssign(string op)(S rhs) if (op == "/=")
      {
          n++;
      }
      void opOpAssign(string op)(S rhs) if (op == "/")
      {
      }
 }
 unittest
 {
      S a = S(1);
      S b = S(2);
      a.opOpAssign(b);
      b.opOpAssign(a);
oof, I expected this to include the template parameters! I believe it normally does? This is a bug that should be filed. -Steve
Aug 05 2022
next sibling parent Steven Schveighoffer <schveiguy gmail.com> writes:
On 8/5/22 11:24 AM, Steven Schveighoffer wrote:
 On 8/4/22 10:27 PM, jfondren wrote:
      a.opOpAssign(b);
      b.opOpAssign(a);
oof, I expected this to include the template parameters! I believe it normally does?
It does not! I'm genuinely shocked. ```d void foo(string s, T)(T t) {} void main() { foo!"hi"(1); } ``` outputs: ```d void foo(string s, T)(T t) { } void main() { foo(1); return 0; } ```
 This is a bug that should be filed.
Make that an enhancement request -Steve
Aug 05 2022
prev sibling parent reply frame <frame86 live.com> writes:
On Friday, 5 August 2022 at 15:24:16 UTC, Steven Schveighoffer 
wrote:

 oof, I expected this to include the template parameters! I 
 believe it normally does?
 This is a bug that should be filed.

 -Steve
Sorry, I don't get what you takling about? The docs says: The expression: `a op= b` is rewritten as: `a.opOpAssign!("op")(b)` There must no "=" to be applied. The compiler creates this: ```d opOpAssign!"/" { pure nothrow nogc safe void opOpAssign(S rhs) { } } ``` as only valid call left. Fine for me since the other template is unused. Same for your example: ```d foo!("hi", int) { pure nothrow nogc safe void foo(int t) { } } ```
Aug 05 2022
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 8/5/22 3:53 PM, frame wrote:
 On Friday, 5 August 2022 at 15:24:16 UTC, Steven Schveighoffer wrote:
 
 oof, I expected this to include the template parameters! I believe it 
 normally does?
 This is a bug that should be filed.

 -Steve
Sorry, I don't get what you takling about? The docs says: The expression: `a op= b` is rewritten as: `a.opOpAssign!("op")(b)` There must no "=" to be applied.
That's not what I was talking about here. I'm talking about `-vcg-ast` not telling you how it's calling the function.
 
 The compiler creates this:
 ```d
 opOpAssign!"/"
 {
      pure nothrow  nogc  safe void opOpAssign(S rhs)
      {
      }
 }
 ```
 as only valid call left. Fine for me since the other template is unused.
And what if the other template is used? This is the AST from code that instantiates foo twice, and calls it 3 times. Tell me which calls are which? ```d import object; void foo(string s, T)(T t) { } void main() { foo(1); foo(1); foo(1); return 0; } mixin _d_cmain!(); // omitted for brevity foo!("hi", int) { pure nothrow nogc safe void foo(int t) { } } foo!("bar", int) { pure nothrow nogc safe void foo(int t) { } } ``` -Steve
Aug 05 2022
parent frame <frame86 live.com> writes:
On Friday, 5 August 2022 at 21:24:13 UTC, Steven Schveighoffer 
wrote:

 That's not what I was talking about here. I'm talking about 
 `-vcg-ast` not telling you how it's calling the function.
Thanks for clarification. I had that in mind but wasn't sure. I first thought it just get optimized away and in case of name/type collision would create another name for the function but after testing with `static if` it really shows that this assumption was wrong :D
Aug 05 2022