www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - auto limitation?

reply "Namespace" <rswhite4 googlemail.com> writes:
I have this code, but it works not as expected:
http://dpaste.dzfl.pl/6ce5b4dd

I get 0 instead of 42 if my type is Int.
My value is correct (as you can see) but "writeln" prints still 0 
instead of 42.
I think "auto" compiles first to float and cannot handle then 
integers.
Am I right? And could you explain me how this could work?
Sep 11 2012
next sibling parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 09/11/2012 11:10 AM, Namespace wrote:
 I have this code, but it works not as expected:
 http://dpaste.dzfl.pl/6ce5b4dd

 I get 0 instead of 42 if my type is Int.
 My value is correct (as you can see) but "writeln" prints still 0
 instead of 42.
 I think "auto" compiles first to float and cannot handle then integers.
 Am I right? And could you explain me how this could work?

Here is a reduced code: import std.stdio; enum Type { Int, Float } auto foo(Type t) { final switch (t) { case Type.Int: return 42; case Type.Float: return 1.5; } } void main() { writeln(foo(Type.Int)); writeln(foo(Type.Float)); } The return type of foo() is double. (It's float in your code but it doesn't matter.) I think this is a bug. I guess that 'return 42' is still placing an int onto the program stack instead of a float. A workarounds are returning to!float(this._num.ivalue). But I think this is a compiler bug. Ali
Sep 11 2012
next sibling parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 09/11/2012 11:40 AM, Namespace wrote:
 Here is a reduced code:

 import std.stdio;

 enum Type { Int, Float }

 auto foo(Type t)
 {
 final switch (t) {
 case Type.Int:
 return 42;
 case Type.Float:
 return 1.5;
 }
 }

 void main()
 {
 writeln(foo(Type.Int));
 writeln(foo(Type.Float));
 }

 The return type of foo() is double. (It's float in your code but it
 doesn't matter.)

 I think this is a bug. I guess that 'return 42' is still placing an
 int onto the program stack instead of a float. A workarounds are
 returning to!float(this._num.ivalue).

 But I think this is a compiler bug.

 Ali

I should begin to count the bugs I find with stuff like this. :)

Please create a bug about this one: http://d.puremagic.com/issues/ But wait until someone else confirms that it really is a bug. :)
 So no correct workaround, hm?

You have to cast the types to the largest one. The following does not work because foo() is not defined yet: import std.traits; // ... auto foo(Type t) { final switch (t) { case Type.Int: return to!(ReturnType!(typeof(&foo)))(42); case Type.Float: return 1.5; } } Error: forward reference to foo Explicitly casting is a way: return to!double(42); Or you can write or find a template that produces the largest type among the members of a union. Ali
Sep 11 2012
parent Denis Shelomovskij <verylonglogin.reg gmail.com> writes:
11.09.2012 22:48, Ali Çehreli пишет:
          return to!(ReturnType!(typeof(&foo)))(42);

typeof(return) See http://dlang.org/declaration.html#typeof Anyway don't use it as a type of the first return. You will get: --- Error: cannot use typeof(return) inside function foo with inferred return type --- -- Денис В. Шеломовский Denis V. Shelomovskij
Sep 11 2012
prev sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 09/11/2012 08:57 PM, Maxim Fomin wrote:
 I think it is UB rather than a bug.

No, this is a bug.
 The spec says that return types must match exactly.

Exactly. If it was UB, the spec would say the behaviour is undefined if they don't match exactly.
 AFAIK auto is a feature to infer return type, not to
 magically adjust to multiple incompatible types.

Sep 11 2012
parent Timon Gehr <timon.gehr gmx.ch> writes:
On 09/11/2012 09:29 PM, Namespace wrote:
 On Tuesday, 11 September 2012 at 19:27:40 UTC, Timon Gehr wrote:
 On 09/11/2012 08:57 PM, Maxim Fomin wrote:
 I think it is UB rather than a bug.

No, this is a bug.
 The spec says that return types must match exactly.

Exactly. If it was UB, the spec would say the behaviour is undefined if they don't match exactly.
 AFAIK auto is a feature to infer return type, not to
 magically adjust to multiple incompatible types.


So what? Should my code work or not?

According to the spec it should fail compilation.
Sep 11 2012
prev sibling next sibling parent "Namespace" <rswhite4 googlemail.com> writes:
 Here is a reduced code:

 import std.stdio;

 enum Type { Int, Float }

 auto foo(Type t)
 {
     final switch (t) {
     case Type.Int:
         return 42;
     case Type.Float:
         return 1.5;
     }
 }

 void main()
 {
     writeln(foo(Type.Int));
     writeln(foo(Type.Float));
 }

 The return type of foo() is double. (It's float in your code 
 but it doesn't matter.)

 I think this is a bug. I guess that 'return 42' is still 
 placing an int onto the program stack instead of a float. A 
 workarounds are returning to!float(this._num.ivalue).

 But I think this is a compiler bug.

 Ali

I should begin to count the bugs I find with stuff like this. :) So no correct workaround, hm? I tried to use a Variant and in the getter method "get" and even "coerce" but then i get the error, that the type of "get" cannot be detected.
Sep 11 2012
prev sibling next sibling parent "Namespace" <rswhite4 googlemail.com> writes:
That's not what i want. I would avoid casting if it's possible.
I hope it is a bug and will fixed in 2.061.
Sep 11 2012
prev sibling next sibling parent "Maxim Fomin" <maxim maxim-fomin.ru> writes:
On Tuesday, 11 September 2012 at 18:32:47 UTC, Ali Çehreli wrote:
 Here is a reduced code:

I guess it may be reduced to: auto foo(bool val) { if (val) return 42; else return 1.5; } void main() { assert(foo(true) == 42); // assertion failure assert(foo(false) == 1.5); }
 The return type of foo() is double. (It's float in your code 
 but it doesn't matter.)

 I think this is a bug. I guess that 'return 42' is still 
 placing an int onto the program stack instead of a float. A 
 workarounds are returning to!float(this._num.ivalue).

 But I think this is a compiler bug.

 Ali

I think it is UB rather than a bug. The spec says that return types must match exactly. AFAIK auto is a feature to infer return type, not to magically adjust to multiple incompatible types.
Sep 11 2012
prev sibling next sibling parent "Namespace" <rswhite4 googlemail.com> writes:
 I think it is UB rather than a bug. The spec says that return 
 types must match exactly. AFAIK auto is a feature to infer 
 return type, not to magically adjust to multiple incompatible 
 types.

But i thought Variant is one. ;)
Sep 11 2012
prev sibling next sibling parent "Simen Kjaeraas" <simen.kjaras gmail.com> writes:
On Tue, 11 Sep 2012 20:48:25 +0200, Ali =C3=87ehreli <acehreli yahoo.com=
 wrote:

 Or you can write or find a template that produces the largest type amo=

 the members of a union.

std.traits.CommonType. -- = Simen
Sep 11 2012
prev sibling next sibling parent "Simen Kjaeraas" <simen.kjaras gmail.com> writes:
On Tue, 11 Sep 2012 20:57:07 +0200, Maxim Fomin <maxim maxim-fomin.ru>  
wrote:

 I think it is UB rather than a bug. The spec says that return types must  
 match exactly. AFAIK auto is a feature to infer return type, not to  
 magically adjust to multiple incompatible types.

I'd be surprised if this were not a bug. My expectation would be that the types be combined as with the ?: operator, just like std.traits.CommonType. If this is not a bug, it's certainly worth filing as an enhancement request. -- Simen
Sep 11 2012
prev sibling next sibling parent "Maxim Fomin" <maxim maxim-fomin.ru> writes:
On Tuesday, 11 September 2012 at 19:03:56 UTC, Namespace wrote:
 I think it is UB rather than a bug. The spec says that return 
 types must match exactly. AFAIK auto is a feature to infer 
 return type, not to magically adjust to multiple incompatible 
 types.

But i thought Variant is one. ;)

I guess (never used Variant) that it uses templated getter which correctly returns value. Float values are returned in %xmm registers and integer values are returned in %eax. Without knowing return type a caller doesn't know where to take return value. For example in this case foo function correctly places return values, but main takes in both cases return value from %xmm.
Sep 11 2012
prev sibling next sibling parent "Maxim Fomin" <maxim maxim-fomin.ru> writes:
On Tuesday, 11 September 2012 at 19:07:52 UTC, Simen Kjaeraas 
wrote:
 On Tue, 11 Sep 2012 20:57:07 +0200, Maxim Fomin 
 <maxim maxim-fomin.ru> wrote:

 I think it is UB rather than a bug. The spec says that return 
 types must match exactly. AFAIK auto is a feature to infer 
 return type, not to magically adjust to multiple incompatible 
 types.

I'd be surprised if this were not a bug. My expectation would be that the types be combined as with the ?: operator, just like std.traits.CommonType. If this is not a bug, it's certainly worth filing as an enhancement request.

It can do, but in compile time. At runtime it cannot know what type is returned (I suppose, but I am not expert in such things). Variant works because it does it in compile time, but buggy examples published so far do it at runtime.
Sep 11 2012
prev sibling next sibling parent "Namespace" <rswhite4 googlemail.com> writes:
With multiple alias this this should work.

int getInt() const { return this.ivalue; }
alias getInt this;
float getFloat() const { return this.fvalue; }
alias getFloat this;

Or with a class method for implicit conversion.
Sep 11 2012
prev sibling next sibling parent "Namespace" <rswhite4 googlemail.com> writes:
On Tuesday, 11 September 2012 at 19:27:40 UTC, Timon Gehr wrote:
 On 09/11/2012 08:57 PM, Maxim Fomin wrote:
 I think it is UB rather than a bug.

No, this is a bug.
 The spec says that return types must match exactly.

Exactly. If it was UB, the spec would say the behaviour is undefined if they don't match exactly.
 AFAIK auto is a feature to infer return type, not to
 magically adjust to multiple incompatible types.


So what? Should my code work or not?
Sep 11 2012
prev sibling next sibling parent "Namespace" <rswhite4 googlemail.com> writes:
Ah ok. That's sad.
Sep 11 2012
prev sibling next sibling parent "Maxim Fomin" <maxim maxim-fomin.ru> writes:
On Tuesday, 11 September 2012 at 19:35:58 UTC, Timon Gehr wrote:
 According to the spec it should fail compilation.

After rethinking it I agree that bug is in faulty compilation (assuming spec is correct), not in returning zero at runtime, which was thought previously.
Sep 11 2012
prev sibling next sibling parent "Namespace" <rswhite4 googlemail.com> writes:
My final implementation:
http://dpaste.dzfl.pl/4d2a045a

Any suggestions?
Sep 11 2012
prev sibling next sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
Namespace:

 I have this code, but it works not as expected:
 http://dpaste.dzfl.pl/6ce5b4dd

I suggest to file a bug: auto foo(bool b) { final switch (b) { case true: return 10; case false: return 20.0; } } void main() { import std.stdio: writeln; writeln(foo(true)); writeln(foo(false)); } The acceptable results are a compile-time error for type mismatch, or a conversion of both literals to double. Probably the second is better. But a silent bit-level cast is not acceptable. Bye, bearophile
Sep 11 2012
prev sibling next sibling parent "Namespace" <rswhite4 googlemail.com> writes:
On Tuesday, 11 September 2012 at 21:13:02 UTC, bearophile wrote:
 Namespace:

 I have this code, but it works not as expected:
 http://dpaste.dzfl.pl/6ce5b4dd

I suggest to file a bug: auto foo(bool b) { final switch (b) { case true: return 10; case false: return 20.0; } } void main() { import std.stdio: writeln; writeln(foo(true)); writeln(foo(false)); } The acceptable results are a compile-time error for type mismatch, or a conversion of both literals to double. Probably the second is better. But a silent bit-level cast is not acceptable. Bye, bearophile

Sure that is the first i will do tomorrow. But so far no suggestions?
Sep 11 2012
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Tuesday, September 11, 2012 23:31:43 Namespace wrote:
 Sure that is the first i will do tomorrow.
 But so far no suggestions?

Use a cast. auto _cannot_ be different types depending on what you return. It must always resolve to the same type. So, in this case, either it needs to resolve to double (in which case the compiler should case) or fail to compile. If you really want the type of the return type to be different depending on what you return, you need to use Variant (in which case it _still_ won't really be different, it'll just always be Variant, but Variant will hold a different value in its internal union, and you'll be able to get that value out as its original type later, because the Variant knows what it was). But D is statically typed, so all types must be known at compile time, and you can't have types changing based on the path of execution. - Jonathan M Davis
Sep 11 2012
prev sibling next sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
Namespace:

 But so far no suggestions?

This seems to work, but solutions that use cast() are sometimes fragile (and dangerous): auto foo(bool b) { final switch (b) { case false: return 20.0; case true: return cast(typeof(return))10; } } void main() { import std.stdio: writeln; writeln(foo(false)); writeln(foo(true)); } Bye, bearophile
Sep 11 2012
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Tuesday, September 11, 2012 23:55:40 bearophile wrote:
 Namespace:
 But so far no suggestions?

This seems to work, but solutions that use cast() are sometimes fragile (and dangerous): auto foo(bool b) { final switch (b) { case false: return 20.0; case true: return cast(typeof(return))10; } } void main() { import std.stdio: writeln; writeln(foo(false)); writeln(foo(true)); }

If you're going to use a cast, then use one where you give the type explicitly. Using typeof(return) is just asking for it when the compiler is clearly already having issues with the return type. - Jonathan M Davis
Sep 11 2012
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Tuesday, September 11, 2012 15:14:43 Jonathan M Davis wrote:
 If you're going to use a cast, then use one where you give the type
 explicitly. Using typeof(return) is just asking for it when the compiler is
 clearly already having issues with the return type.

And actually, in all honesty, I see no reason to use auto here. If you know what the return type is supposed to be (e.g. double), then just use double. Casting where it's unnecessary is just begging for bugs. Based on Namespace's posts in this thread, I get the impression that what he wanted was that the return type would be either int or double depending on the value returned - i.e. he was trying to get dynamic typing - which doesn't work, because D is a static language. If that's what he wants, he needs to use Variant, and casting and the like is going to do him no good. auto was the wrong choice in the first place. - Jonathan M Davis
Sep 11 2012
prev sibling next sibling parent "Namespace" <rswhite4 googlemail.com> writes:
You mean so?
ref Variant get() {
     return this._num; // with Variant _num;
}

alias get this;

and then:
int i = zahl.get!int?
No. Then I use my solution as you can see on DPaste. If Variant 
works only this way I can also set float to the return type an 
cast if I need to int.
And with my solution I have in calculation a numeric value and 
doesn't have to cast with Variant's get method.
Sep 11 2012
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Wednesday, September 12, 2012 00:29:32 Namespace wrote:
 You mean so?
 ref Variant get() {
      return this._num; // with Variant _num;
 }
 
 alias get this;
 
 and then:
 int i = zahl.get!int?
 No. Then I use my solution as you can see on DPaste. If Variant
 works only this way I can also set float to the return type an
 cast if I need to int.
 And with my solution I have in calculation a numeric value and
 doesn't have to cast with Variant's get method.

If want the result to always be the same type, then either use the explicit type or auto (though clearly auto is having some issues right now, so it might be best to avoid it if you're returning stuff that differs in type and needs to be converted). If you want the result to differ in type depending on the value returned, then you need Variant in order to simulate dynamic typing. I'm not quite sure what you're trying to do with your code, but if you use Variant, you'll need to use get or coerce to get its value from it, which it doesn't sound like what you want to do. If that's the case, then your get function needs to always return the same type - be it int or float or double or whatever, and you might as well just use the explicit type rather than auto, since it's a primitive type, not a complicated, templated one (which is the main reason to use auto for the return type of a function). - Jonathan M Davis
Sep 11 2012
prev sibling next sibling parent "Graham Fawcett" <fawcett uwindsor.ca> writes:
On Tuesday, 11 September 2012 at 21:31:17 UTC, Namespace wrote:
 On Tuesday, 11 September 2012 at 21:13:02 UTC, bearophile wrote:
 Namespace:

 I have this code, but it works not as expected:
 http://dpaste.dzfl.pl/6ce5b4dd

I suggest to file a bug: auto foo(bool b) { final switch (b) { case true: return 10; case false: return 20.0; } } void main() { import std.stdio: writeln; writeln(foo(true)); writeln(foo(false)); } The acceptable results are a compile-time error for type mismatch, or a conversion of both literals to double. Probably the second is better. But a silent bit-level cast is not acceptable. Bye, bearophile

Sure that is the first i will do tomorrow. But so far no suggestions?

Just this one. You can use an anonymous union in your Num struct, so you can write "obj.ivalue" rather than obj._num.ivalue": struct Num { private: final enum Type { None, Float, Int } union { float fvalue; int ivalue; } Type _type; .... Graham
Sep 11 2012
prev sibling parent "Namespace" <rswhite4 googlemail.com> writes:
Done, thank you. :)
Sep 12 2012