Thread overview
opUnary overloading
Aug 26, 2012
cal
Aug 26, 2012
cal
Aug 26, 2012
Timon Gehr
Aug 26, 2012
cal
Aug 26, 2012
Ali Çehreli
August 26, 2012
I have a struct wrapping a union of an int and a float, and want to overload opUnary for this struct:

struct Value
{
    enum Type {
        INT, FLOAT
    }

    Type type;

    union {
        int i;
        float f;
    }

    this(int _i) {
        i = _i;
        type = Type.INT;
    }

    this(float _f) {
        f = _f;
        type = Type.FLOAT;
    }

    auto opUnary(string s)() if (s == "-") {
        if (type == Type.INT)
            return -i;
        if (type == Type.FLOAT) // If i comment this out, I get an int back
            return -f;          // as expected
        assert(0);
    }
}

Value v1 = Value(25);
assert(v1.type == Value.Type.INT);
auto neg = -v1;
writeln(typeof(neg).stringof); // this returns float, when it should return int

The part I don't understand is this: in the opUnary method, if I comment out the second if conditional (if (type == Type.FLOAT)) then when doing the negation on v1 I get an INT as expected. If I don't comment this code out, I get a float.

When commented out, I get back -25 as an int, which i expect. When not commented out, I get back -nan, which suggests to me that it is returning the (uninitialized) float from the union, not simply giving me back the int as a float.

Could someone explain this?



August 26, 2012
I should clarify that I realize the auto cannot be used at runtime to select the return type - I just don't understand why the float part of the union is returned at runtime, instead of the int converted to whatever the compiler determines the return-type of opUnary to be.

August 26, 2012
On 08/26/2012 10:33 PM, cal wrote:
>
> I have a struct wrapping a union of an int and a float, and want to
> overload opUnary for this struct:
>
> struct Value
> {
>      enum Type {
>          INT, FLOAT
>      }
>
>      Type type;
>
>      union {
>          int i;
>          float f;
>      }
>
>      this(int _i) {
>          i = _i;
>          type = Type.INT;
>      }
>
>      this(float _f) {
>          f = _f;
>          type = Type.FLOAT;
>      }
>
>      auto opUnary(string s)() if (s == "-") {
>          if (type == Type.INT)
>              return -i;
>          if (type == Type.FLOAT) // If i comment this out, I get an int
> back
>              return -f;          // as expected
>          assert(0);
>      }
> }
>
> Value v1 = Value(25);
> assert(v1.type == Value.Type.INT);
> auto neg = -v1;
> writeln(typeof(neg).stringof); // this returns float, when it should
> return int
>
> The part I don't understand is this: in the opUnary method, if I comment
> out the second if conditional (if (type == Type.FLOAT)) then when doing
> the negation on v1 I get an INT as expected. If I don't comment this
> code out, I get a float.
>
> When commented out, I get back -25 as an int, which i expect. When not
> commented out, I get back -nan, which suggests to me that it is
> returning the (uninitialized) float from the union, not simply giving me
> back the int as a float.
>
> Could someone explain this?
>
>
>

Compiler bug.

http://d.puremagic.com/issues/

Also see:
http://d.puremagic.com/issues/show_bug.cgi?id=8307

I assume the compiler fails to add an implicit conversion node into the
AST because at the point where -i is returned, the return type is still
assumed to be 'int'. What you are observing is not type coercion, but
an x87 FPU stack underflow.


August 26, 2012
On 08/26/2012 01:33 PM, cal wrote:
>
> I have a struct wrapping a union of an int and a float, and want to
> overload opUnary for this struct:
>
> struct Value
> {
> enum Type {
> INT, FLOAT
> }
>
> Type type;
>
> union {
> int i;
> float f;
> }
>
> this(int _i) {
> i = _i;
> type = Type.INT;
> }
>
> this(float _f) {
> f = _f;
> type = Type.FLOAT;
> }
>
> auto opUnary(string s)() if (s == "-") {
> if (type == Type.INT)
> return -i;
> if (type == Type.FLOAT) // If i comment this out, I get an int back
> return -f; // as expected
> assert(0);
> }
> }
>
> Value v1 = Value(25);
> assert(v1.type == Value.Type.INT);
> auto neg = -v1;
> writeln(typeof(neg).stringof); // this returns float, when it should
> return int
>
> The part I don't understand is this: in the opUnary method, if I comment
> out the second if conditional (if (type == Type.FLOAT)) then when doing
> the negation on v1 I get an INT as expected. If I don't comment this
> code out, I get a float.
>
> When commented out, I get back -25 as an int, which i expect. When not
> commented out, I get back -nan, which suggests to me that it is
> returning the (uninitialized) float from the union, not simply giving me
> back the int as a float.
>
> Could someone explain this?

opUnary should return Value:

    Value opUnary(string s)() if (s == "-") {
        final switch (type) with (Type) {
        case INT:
            return Value(-i);

        case FLOAT:
            return Value(-f);
        }
    }

I have also taken advantage of

- 'final switch' to obviate the assert(0)

- 'with' to simplify syntax (no need to be explicit as in 'Type.INT' anymore)

Ali

-- 
D Programming Language Tutorial: http://ddili.org/ders/d.en/index.html
August 26, 2012
On Sunday, 26 August 2012 at 20:54:35 UTC, Timon Gehr wrote:
> Also see:
> http://d.puremagic.com/issues/show_bug.cgi?id=8307
>
> I assume the compiler fails to add an implicit conversion node into the
> AST because at the point where -i is returned, the return type is still
> assumed to be 'int'. What you are observing is not type coercion, but
> an x87 FPU stack underflow.

"If there are multiple ReturnStatements, the types of them must match exactly."

Understood, I wasn't doing this.