February 10, 2016
On 2/10/16 5:49 PM, Chris Wright wrote:
> On Wed, 10 Feb 2016 21:40:21 +0000, Iakh wrote:
>
>> On Wednesday, 10 February 2016 at 20:14:29 UTC, Chris Wright wrote:
>>> @safe protects you from segmentation faults and reading and writing
>>> outside an allocated segment of memory. With array casts, @safety is
>>> assured
>>
>> Yes, @safe protects from direct cast to/from ref types but there still
>> is a trick with T[] -> void[] -> T2[] cast:
>>
>> So no safety in this world.
>
> Okay, that's a problem.
>
> It should always be safe to cast from void[] to immutable(T)[] where T
> doesn't contain pointers.
>
> I didn't see a bug for this, so I'm filing it.
>

I think casting a mutable array to any array type is a recipe for memory issues, no matter what is in the elements. Remember that you are casting a reference that still has a mutable pointer to it.

@safe should start from a very cautious and overtightened state, and then we loosen it as we find issues.

As it was done, it has holes, and so when we fix things, code breaks.

-Steve
February 11, 2016
On Wed, 10 Feb 2016 22:39:20 -0500, Steven Schveighoffer wrote:

> I think casting a mutable array to any array type is a recipe for memory issues, no matter what is in the elements. Remember that you are casting a reference that still has a mutable pointer to it.
> 
> @safe should start from a very cautious and overtightened state, and then we loosen it as we find issues.
> 
> As it was done, it has holes, and so when we fix things, code breaks.
> 
> -Steve

I agree with the principle, but it's always safe to read a pointer as if it were not a pointer, and that's what a cast to an immutable array would do.
February 12, 2016
On 2/10/16 11:01 PM, Chris Wright wrote:
> On Wed, 10 Feb 2016 22:39:20 -0500, Steven Schveighoffer wrote:
>
>> I think casting a mutable array to any array type is a recipe for memory
>> issues, no matter what is in the elements. Remember that you are casting
>> a reference that still has a mutable pointer to it.
>>
>> @safe should start from a very cautious and overtightened state, and
>> then we loosen it as we find issues.
>>
>> As it was done, it has holes, and so when we fix things, code breaks.
>>
>
> I agree with the principle, but it's always safe to read a pointer as if
> it were not a pointer, and that's what a cast to an immutable array would
> do.
>

A cast to immutable is a guarantee to the compiler that there is no other mutable references that you will use again.

This is not the case here.

A cast to const may be viable. However, I think casting in safe code is probably not something to allow. If you need to make something work outside the compiler's comfort zone, there's always @trusted.

-Steve

February 12, 2016
On Fri, 12 Feb 2016 08:45:54 -0500, Steven Schveighoffer wrote:

> A cast to const may be viable.

Touché.

> However, I think casting in safe code is
> probably not something to allow.

*All* casting?

Casting between primitive value types (eg long -> int) is @safe. You can't get memory errors that way, and the conversions are well-defined.

Casting between object references is @safe (assuming the object references are valid; @safe doesn't protect you from dereferencing an invalid pointer you got from @system code). You can dereference null that way, but that's allowed by design.

If you wanted to restrict casts between array types, that would be more reasonable, but some work has already gone into making those casts safe (eg long[] -> int[]). It would also prevent @safe memory-mapped IO, even if we provided a wrapper that yielded a ubyte[].

If you're just talking about casting from void[] in @safe code, that's reasonable, but a little more restrictive than necessary. Casting *to* void[] in this scenario is safe, just not generally useful -- you wouldn't be able to cast back in @safe code.
February 12, 2016
On 2/12/16 12:15 PM, Chris Wright wrote:

> Casting between primitive value types (eg long -> int) is @safe. You
> can't get memory errors that way, and the conversions are well-defined.
>
> Casting between object references is @safe (assuming the object
> references are valid; @safe doesn't protect you from dereferencing an
> invalid pointer you got from @system code). You can dereference null that
> way, but that's allowed by design.

All good points.

what I'm trying to say safe shouldn't allow is reinterpret casting. i.e.: *cast(T*)(&x)

So casting IMO shouldn't be allowed unless it invokes some kind of handler that ensures the conversion is safe.

I'd include in this list:

a) casting between object types
b) casting builtin types that are not, or do not contain, references (that are defined by the compiler)
c) casting an aggregate that has a matching opCast

> If you wanted to restrict casts between array types, that would be more
> reasonable, but some work has already gone into making those casts safe
> (eg long[] -> int[]). It would also prevent @safe memory-mapped IO, even
> if we provided a wrapper that yielded a ubyte[].

Casting an array involves casting a pointer with a reinterpret style cast. IMO, the language is better off requiring a @trusted escape for such things.

>
> If you're just talking about casting from void[] in @safe code, that's
> reasonable, but a little more restrictive than necessary. Casting *to*
> void[] in this scenario is safe, just not generally useful -- you
> wouldn't be able to cast back in @safe code.

casting to void[] doesn't require a cast. So I think it should be fine in @safe code.

-Steve
February 12, 2016
On Fri, 12 Feb 2016 14:32:32 -0500, Steven Schveighoffer wrote:

> what I'm trying to say safe shouldn't allow is reinterpret casting.
> i.e.: *cast(T*)(&x)
> 
> So casting IMO shouldn't be allowed unless it invokes some kind of handler that ensures the conversion is safe.
> 
> I'd include in this list:
> 
> a) casting between object types
> b) casting builtin types that are not,
> or do not contain, references (that are defined by the compiler)
> c) casting an aggregate that has a matching opCast

Casting an array is basically a backdoor way to make a union, ignoring opCast. One of the cases that should be explicitly disallowed here (and of course it isn't). Observe:

import std.stdio;
struct A {
	void* m;
	size_t i;
}
struct B {
	size_t i;
	A opCast() {
		return A(null, i);
	}
}

void main() @safe {
	A[] aa = [A(new int, 5)];
	auto bb = cast(B[])aa;
	writeln(bb[0].i);  // prints -22192128
}

If this honored opCast, it would print 5. Instead it prints a pointer address. (Also, the length of array bb is 2.)

This corresponds to what the spec says, but that's probably not the desired behavior.
1 2
Next ›   Last »