March 16, 2017
On Thursday, 16 March 2017 at 16:59:40 UTC, Adam D. Ruppe wrote:
>> Yet the documentation says there's a toHexString that returns a string.
>
> Yes, indeed, it returns a string if it is passed a dynamic array.

Ah, that's the distinction, should have noticed.

> Remember though, like I warned on my doc fork, overload resolution NEVER looks at the left hand side of the equation, it is always done on arguments alone.
>
> The stupid auto return stuff Phobos loves so much obscures it, but md5Of returns a ubyte[16].
>
> That calls the first overload, the one that returns char[num*2]

OK, but if I try to do this,

char[2] u;
string s = u;

the compiler will complain: Error: cannot implicitly convert expression (u) of type char[2] to string. So why does it allow the template instantiation  of return type char[num*2] in place of u above?


March 16, 2017
On Thu, Mar 16, 2017 at 04:59:40PM +0000, Adam D. Ruppe via Digitalmars-d-learn wrote:
> On Thursday, 16 March 2017 at 16:47:14 UTC, Carl Sturtivant wrote:
> > Silently <expletive-deleted> cast to immutable without copying!??!! This is so wrong.
> 
> It is the implicit slicing that I really want removed from the language, it serves no real benefit and brings a lot of accidental complexity. Casting the static array to immutable is actually OK in isolation, because it is a value type and thus a unique copy... but once you slice it, that promise is gone.
[...]

I'm not convinced casting static array to immutable is OK. Check this out:

	import std.stdio;

	char[32] func() {
	    char[32] staticArr = "A123456789abcdefB123456789abcdef";
	    return staticArr; // OK, by-value return
	}

	string gunk() {
	    string x = func(); // implicit conversion char[32] -> string
	    writeln(x.ptr);
	    writeln(x);		// prints "A123456789abcdefB123456789abcdef"
	    return x;
	}

	void main() {
	    auto s = gunk();
	    writeln(s.ptr);	// prints same address as in gunk()
	    writeln(s);		// prints corrupted string
	}

Run this code and you'll see that s.ptr has the same address as x.ptr, and that x.ptr is the address of a local variable. This is blatantly wrong.

Filed a new issue for this:

	https://issues.dlang.org/show_bug.cgi?id=17261


T

-- 
I am a consultant. My job is to make your job redundant. -- Mr Tom
March 16, 2017
On Thursday, 16 March 2017 at 17:18:30 UTC, Adam D. Ruppe wrote:
> On Thursday, 16 March 2017 at 17:12:08 UTC, Carl Sturtivant wrote:
>> I did that, and it made no difference. :(
>
> wait a minute i just realized:
>
> toHexString.d(11) in the error message
>
> You named the file toHexString which means the *module* will be named toHexString by default...

OK, right!


March 16, 2017
On Thursday, 16 March 2017 at 17:20:45 UTC, H. S. Teoh wrote:
> I'm not convinced casting static array to immutable is OK. Check this out:
>
> 	import std.stdio;
>
> 	char[32] func() {
> 	    char[32] staticArr = "A123456789abcdefB123456789abcdef";
> 	    return staticArr; // OK, by-value return
> 	}
>
> 	string gunk() {
> 	    string x = func(); // implicit conversion char[32] -> string
> 	    writeln(x.ptr);
> 	    writeln(x);		// prints "A123456789abcdefB123456789abcdef"
> 	    return x;
> 	}
>
> 	void main() {
> 	    auto s = gunk();
> 	    writeln(s.ptr);	// prints same address as in gunk()
> 	    writeln(s);		// prints corrupted string
> 	}
>
> Run this code and you'll see that s.ptr has the same address as x.ptr, and that x.ptr is the address of a local variable. This is blatantly wrong.
>
> Filed a new issue for this:
>
> 	https://issues.dlang.org/show_bug.cgi?id=17261

Exactly, if there was a variable of type char[32] on the right hand side of
    string x = func();
instead of the call of func, then the compiler would complain. So this is a bug.



March 16, 2017
On Thu, Mar 16, 2017 at 05:06:39PM +0000, Adam D. Ruppe via Digitalmars-d-learn wrote: [...]
> In isolation, implicit slicing can make sense.... but not with templates. In isolation, implicit immutable can make sense... but not with implicit slicing.
[...]

Is implicit slicing the culprit in the issue I just filed? (Bug 17261)

In retrospect, perhaps implicit casting to immutable is OK if we don't allow implicit slicing:

	char[32] func() { char[32] s; return s; }
	string gunk() {
		string x = func(); // error, if implicit slicing is not allowed
		return x;	// if allowed, this causes escaping ref to stack data
	}
	immutable char[32] hunk() {
		immutable char[32] x = func(); // should be OK: no implicit slicing
		return x; // OK: return by-value, no escaping refs
	}
	string junk() {
		immutable char[32] x = func(); // should be OK: no implicit slicing
		return x; // NG: implicit slicing causes escaping ref
			// However, compiler is smart enough to catch
			// it, so it produces a compile error
	}

Seems like the real cause of bug 17261 is implicit slicing.


T

-- 
Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it. -- Brian W. Kernighan
March 16, 2017
On Thu, Mar 16, 2017 at 10:40:51AM -0700, H. S. Teoh via Digitalmars-d-learn wrote: [...]
> Is implicit slicing the culprit in the issue I just filed? (Bug 17261)
> 
> In retrospect, perhaps implicit casting to immutable is OK if we don't allow implicit slicing:
> 
> 	char[32] func() { char[32] s; return s; }
> 	string gunk() {
> 		string x = func(); // error, if implicit slicing is not allowed

Actually, the bug still exists even if you explicitly slice it:

	string x = func()[]; // still compiles, but shouldn't

For some reason, slicing a static array return value is somehow OK, while slicing a local variable is rejected.  Seems like the compiler is missing escaping ref checks for return values?


T

-- 
People tell me I'm stubborn, but I refuse to accept it!
March 16, 2017
On Thursday, 16 March 2017 at 17:20:45 UTC, H. S. Teoh wrote:
> I'm not convinced casting static array to immutable is OK. Check this out:


You aren't casting static array to immutable there, it is being implicitly sliced AND cased to immutable.

What I mean is:

char[32] s;
immutable(char)[32] i = s; // that is sane, it copies anyway
immutable(char[32]) i2 = s; // also sane


That's an implicit cast to immutable, but since both sides are still static arrays, it is no different than

int a = 0;
immutable b = a; // ok

Value types can be copied in and out of immutable implicitly without worry.

It is when they become a reference type that things go wrong. And, in isolation, the compiler knows this too:

immutable(char)[] i = s; //  Error: cannot implicitly convert expression (s) of type char[32] to string


But when you put it behind a function like you did, two things happen:

1) it sees it is a return value by value, and thus unique... so it is safe to cast to immutable

char[32] func() {
        char[32] a;
        return a;
}

void main() {
     immutable(char[32]) a = func(); // that's fine! it is copied so it is immutable
}

but 2), it is also willing to slice it:

char[] b = func(); // legal under current rules... but insane

and 3) knowing it is a slice of unique data, it allows it to be casted just like:

pure char[] foo() { return []; }

string implicit = foo(); // fine - pure slice must be unique due to purity rules, and unique mutable can become immutable


Combine all that and we get:

char[32] func() { }

1) char[] implicitlySliced = func(); // stupid, but allowed by language rules
2) immutable(char)[] implicitly immutable = impliciltlySliced; // ok, because it is unique...
3) crashing in bug city.





Of all those rules, preventing the implicit slice is the easiest fix:

string s = func(); // error, CAN implicitly cast from char[32] to immutable(char[32]) but can NOT implicitly cast from static to dynamic

Being a unique value, casting to immutable is perfectly sane.

immutable(char[32]) s = func(); // makes sense
string s2 = s; // nope, if you really meant it, write `s[]`

But the caller should be aware of when a reference is taken.

string s = func()[]; // I'd allow it, at least the user wrote `[]` meaning they realized it was stack data and presumably knows what that means about the slice's lifetime
March 16, 2017
On Thursday, 16 March 2017 at 17:40:51 UTC, H. S. Teoh wrote:
> Seems like the real cause of bug 17261 is implicit slicing.

Yes, sorry, it took me 10 mins to type it up (I like to double check the current behavior before posting) but I think we both see it the same way now.
March 16, 2017
On Thursday, 16 March 2017 at 17:20:10 UTC, Carl Sturtivant wrote:
> OK, but if I try to do this,
>
> char[2] u;
> string s = u;

Yeah, that's because `u` is not necessarily unique like a function return value:

char[2] u;
char[] mutable = u[];
string s = u[];

char before = s[0];

mutable[0] = 'n';

char after = s[0];

assert(before == after); // fails! because mutable[0] changed it.



But, with a function return value, there's no opportunity to insert that sneaky `char[] mutable = u[];` alias, so the compiler is free to assume that it is the only reference and do the immutable thing.

If it returns a value type, the compiler can cast to immutable at any time because it knows there is no chance to insert an alias at all - the function return is the only way to access it.

If it returns a reference type, the compiler can cast to immutable if the function is *strongly* pure, because the purity rules also don't give you an opportunity to sneak a mutable reference out (pure cannot write to stuff outside the return values).

I said *strongly* because the compiler will correctly complain if you try to pass a mutable argument back as the return value. It realizes whomever passed the argument might still have an alias and will not allow the implicit cast:

pure char[] foo(char[] a) { return a; }
char[] lolStillMutable;
string tricky = foo(lolStillMutable); // illegal


March 16, 2017
On Thursday, 16 March 2017 at 17:51:41 UTC, Adam D. Ruppe wrote:
> On Thursday, 16 March 2017 at 17:40:51 UTC, H. S. Teoh wrote:
>> Seems like the real cause of bug 17261 is implicit slicing.
>
> Yes, sorry, it took me 10 mins to type it up (I like to double check the current behavior before posting) but I think we both see it the same way now.

Guys, seriously this bug has been fixed for quite some time:
https://issues.dlang.org/show_bug.cgi?id=8838
https://issues.dlang.org/show_bug.cgi?id=12625
https://github.com/dlang/dmd/pull/5972

Why don't you use -dip1000???

/home/zombinedev/dlang/dmd-2.073.2/linux/bin64/../../src/phobos/std/stdio.d(3439): Error: template std.stdio.File.LockingTextWriter.put cannot deduce function from argument types !()(string), candidates are:
/home/zombinedev/dlang/dmd-2.073.2/linux/bin64/../../src/phobos/std/stdio.d(2643):        std.stdio.File.LockingTextWriter.put(A)(A writeme) if (is(ElementType!A : const(dchar)) && isInputRange!A && !isInfinite!A)
/home/zombinedev/dlang/dmd-2.073.2/linux/bin64/../../src/phobos/std/stdio.d(2672):        std.stdio.File.LockingTextWriter.put(C)(C c) if (is(C : const(dchar)))
scope_test.d(5): Error: template instance std.stdio.writeln!string error instantiating
scope_test.d(11): Error: scope variable ans may not be returned

The only issue is that not enough people (except Walter & Andrei) are interested in pushing -dip1000 as the default. Druntime already compiles with -dip1000, so Phobos is the next frontier.