www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Why is &array[0] safer than array.ptr?

reply Atila Neves <atila.neves gmail.com> writes:
void main() {
     foo;
}

void foo()  safe {
     int[] array;
     auto ptr = array.ptr;
}


foo.d(7): Deprecation: array.ptr cannot be used in  safe code, 
use &array[0] instead


&array[0] is incredibly ugly and feels like an unnecessary hack, 
and I'm wondering why it's  safe.

Atila
Jan 24
next sibling parent Stefan Koch <uplink.coder googlemail.com> writes:
On Tuesday, 24 January 2017 at 11:28:17 UTC, Atila Neves wrote:
 void main() {
     foo;
 }

 void foo()  safe {
     int[] array;
     auto ptr = array.ptr;
 }


 foo.d(7): Deprecation: array.ptr cannot be used in  safe code, 
 use &array[0] instead


 &array[0] is incredibly ugly and feels like an unnecessary 
 hack, and I'm wondering why it's  safe.

 Atila
the type is still a type with bounds and not an unbounded pointer.
Jan 24
prev sibling next sibling parent reply TheFlyingFiddle <kurtyan student.chalmers.se> writes:
On Tuesday, 24 January 2017 at 11:28:17 UTC, Atila Neves wrote:
 void main() {
     foo;
 }

 void foo()  safe {
     int[] array;
     auto ptr = array.ptr;
 }


 foo.d(7): Deprecation: array.ptr cannot be used in  safe code, 
 use &array[0] instead


 &array[0] is incredibly ugly and feels like an unnecessary 
 hack, and I'm wondering why it's  safe.

 Atila
Just a speculative guess. unittest safe { int[] array; auto ptr = array.ptr; //could be null auto ptr2 = &array[0]; //Does a bounds check? auto ptr3 = &array[5]; //Should do a bounds check. }
Jan 24
parent reply Atila Neves <atila.neves gmail.com> writes:
On Tuesday, 24 January 2017 at 11:32:47 UTC, TheFlyingFiddle 
wrote:
 On Tuesday, 24 January 2017 at 11:28:17 UTC, Atila Neves wrote:
 void main() {
     foo;
 }

 void foo()  safe {
     int[] array;
     auto ptr = array.ptr;
 }


 foo.d(7): Deprecation: array.ptr cannot be used in  safe code, 
 use &array[0] instead


 &array[0] is incredibly ugly and feels like an unnecessary 
 hack, and I'm wondering why it's  safe.

 Atila
Just a speculative guess. unittest safe { int[] array; auto ptr = array.ptr; //could be null auto ptr2 = &array[0]; //Does a bounds check? auto ptr3 = &array[5]; //Should do a bounds check. }
&array[5] makes sense to bounds check, and I guess then the issue is I could instead do `array.ptr + 5` which would be bad. But it's still annoying to have to do &array[0] just to pass it to a C function, since `my_c_func(array.ptr)` isn't going to screw up anything. BTW, in that example above array.ptr is null even though array is null. It doesn't crash. Atila
Jan 24
parent David Nadlinger <code klickverbot.at> writes:
On Tuesday, 24 January 2017 at 11:49:59 UTC, Atila Neves wrote:
 But it's still annoying to have to do &array[0] just to pass it 
 to a C function, since `my_c_func(array.ptr)` isn't going to 
 screw up anything.
How do you know it does not screw up anything? Presumably, the function somehow accesses the object the pointer targets. How would this be safe to call if the pointer were not dereferencable? — David
Jan 25
prev sibling parent reply Jonathan M Davis via Digitalmars-d-learn writes:
On Tuesday, January 24, 2017 11:28:17 Atila Neves via Digitalmars-d-learn 
wrote:
 void main() {
      foo;
 }

 void foo()  safe {
      int[] array;
      auto ptr = array.ptr;
 }


 foo.d(7): Deprecation: array.ptr cannot be used in  safe code,
 use &array[0] instead


 &array[0] is incredibly ugly and feels like an unnecessary hack,
 and I'm wondering why it's  safe.
Likely because it does bounds checking, so you at least know that it's not null. But I don't see why that would really improve much considering that the odds are that you're really going to be accessing far more than just the first element with the pointer. It seems _slightly_ better from a safety perspective but only slightly. So, I don't know what the point is in suggesting it as an alternative. - Jonathan M Davis
Jan 24
next sibling parent reply Rene Zwanenburg <renezwanenburg gmail.com> writes:
On Tuesday, 24 January 2017 at 11:38:16 UTC, Jonathan M Davis 
wrote:
 Likely because it does bounds checking, so you at least know 
 that it's not null. But I don't see why that would really 
 improve much considering that the odds are that you're really 
 going to be accessing far more than just the first element with 
 the pointer. It seems _slightly_ better from a safety 
 perspective but only slightly. So, I don't know what the point 
 is in suggesting it as an alternative.

 - Jonathan M Davis
Pointer arithmetic is forbidden in safe code so that's not a problem. The reason this was introduced was indeed bounds checking. For example: safe: int parse(ref char[] input) { // Pop all numeric characters from the front of the input slice and convert to int } void main() { auto input = "123".dup; parse(input); // Since all numeric chars have been popped, input is now effectively input[$ .. $]. // This means input.ptr is pointing past the end of the array. writeln(input.ptr); // Out of bounds access }
Jan 24
parent reply Jonathan M Davis via Digitalmars-d-learn writes:
On Tuesday, January 24, 2017 11:50:16 Rene Zwanenburg via Digitalmars-d-
learn wrote:
 On Tuesday, 24 January 2017 at 11:38:16 UTC, Jonathan M Davis

 wrote:
 Likely because it does bounds checking, so you at least know
 that it's not null. But I don't see why that would really
 improve much considering that the odds are that you're really
 going to be accessing far more than just the first element with
 the pointer. It seems _slightly_ better from a safety
 perspective but only slightly. So, I don't know what the point
 is in suggesting it as an alternative.

 - Jonathan M Davis
Pointer arithmetic is forbidden in safe code so that's not a problem. The reason this was introduced was indeed bounds checking. For example: safe: int parse(ref char[] input) { // Pop all numeric characters from the front of the input slice and convert to int } void main() { auto input = "123".dup; parse(input); // Since all numeric chars have been popped, input is now effectively input[$ .. $]. // This means input.ptr is pointing past the end of the array. writeln(input.ptr); // Out of bounds access }
Sure, there can be problems with .ptr. It's not necessarily a problem that it's not safe. But doing &arr[0] instead of arr.ptr is almost pointless. All it does is verify that the array isn't null or empty. If you're doing arr.ptr, you're almost certainly passing it to C code, and that code will almost certainly read well past the arr[0]. So, while it makes sense to say that .ptr can't be used in safe code, it really doesn't make sense to suggest &arr[0] as an alternative. - Jonathan M Davis
Jan 24
next sibling parent Dukc <ajieskola gmail.com> writes:
On Tuesday, 24 January 2017 at 12:01:35 UTC, Jonathan M Davis 
wrote:
 So, while it makes sense to say that .ptr can't be used in 
  safe code, it really doesn't make sense to suggest &arr[0] as 
 an alternative.
That may well be. But I believe everything that can provably be safe are made so even when apparently pointeless, because someone may find creative uses for those things. Ones the creators did not think of. And even if there are none now, that may change in the future with the language or libraries.
Jan 24
prev sibling next sibling parent reply Kagamin <spam here.lot> writes:
On Tuesday, 24 January 2017 at 12:01:35 UTC, Jonathan M Davis 
wrote:
 So, while it makes sense to say that .ptr can't be used in 
  safe code, it really doesn't make sense to suggest &arr[0] as 
 an alternative.
When you ensure pointers point to existing data, you can dereference them in safe code, otherwise you can't.
Jan 25
parent reply Jonathan M Davis via Digitalmars-d-learn writes:
On Wednesday, January 25, 2017 10:52:51 Kagamin via Digitalmars-d-learn 
wrote:
 On Tuesday, 24 January 2017 at 12:01:35 UTC, Jonathan M Davis

 wrote:
 So, while it makes sense to say that .ptr can't be used in
  safe code, it really doesn't make sense to suggest &arr[0] as
 an alternative.
When you ensure pointers point to existing data, you can dereference them in safe code, otherwise you can't.
Fine, but in the vast majority of cases, you're calling .ptr, because you're going to be passing the pointer to C code, in which case, doing &arr[0] buys you very little, since the C code is inevitably going to be reading more than that one element, and &arr[0] hasn't verified anything beyond the first element. So, telling the programmer to use &arr[0] instead of arr.ptr is just plain bizarre. Doing &arr[0] makes sense when you're just going to be messing with that one element in D code, but that's pretty much it. Otherwise, you might as well just use arr.ptr, because it's up to the programmer to verify the safety of what's going on at that point anyway. - Jonathan M Davis
Jan 25
parent David Nadlinger <code klickverbot.at> writes:
On Wednesday, 25 January 2017 at 18:12:18 UTC, Jonathan M Davis 
wrote:
 Fine, but in the vast majority of cases, you're calling .ptr, 
 because you're going to be passing the pointer to C code, in 
 which case, doing &arr[0] buys you very little, since the C 
 code is inevitably going to be reading more than that one 
 element,
In that case, calling the C function isn't going to be safe anyway, so you might as well use .ptr.
 So, telling the programmer to use &arr[0] instead of arr.ptr is 
 just plain bizarre.
What you call bizarre is a simple, actionable explanation (which is especially important as the behaviour was necessarily a backwards-incompatible change). If &arr[0] doesn't actually apply to your code, then it was mistakenly safe before. — David
Jan 25
prev sibling parent Jerry <Kickupx gmail.com> writes:
On Tuesday, 24 January 2017 at 12:01:35 UTC, Jonathan M Davis 
wrote:
 So, while it makes sense to say that .ptr can't be used in 
  safe code, it really doesn't make sense to suggest &arr[0] as 
 an alternative.

 - Jonathan M Davis
Sure I see your point. But I feel like deprecations should also list what one can do instead. So in that regard the suggestion makes sense.
Jan 25
prev sibling parent reply David Nadlinger <code klickverbot.at> writes:
On Tuesday, 24 January 2017 at 11:38:16 UTC, Jonathan M Davis 
wrote:
 It seems _slightly_ better from a safety perspective but only 
 slightly.
Wrong – one is correct, the other is not. This is because every pointer in SafeD is dereferencable. Pointer arithmetic is not allowed in SafeD, so your concerns about reading from other memory do not apply. — David
Jan 25
next sibling parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Wednesday, 25 January 2017 at 22:46:10 UTC, David Nadlinger 
wrote:
 This is because every pointer in SafeD is dereferencable.
But null pointers are allowed in SafeD and arr.ptr is either arr[0] or null....
Jan 25
parent reply David Nadlinger <code klickverbot.at> writes:
On Wednesday, 25 January 2017 at 22:54:32 UTC, Adam D. Ruppe 
wrote:
 On Wednesday, 25 January 2017 at 22:46:10 UTC, David Nadlinger 
 wrote:
 This is because every pointer in SafeD is dereferencable.
But null pointers are allowed in SafeD and arr.ptr is either arr[0] or null....
This is a fallacy: --- safe: // Deprecated, though. ubyte oops(ubyte[] b) { return *b.ptr; } void main() { oops(new ubyte[0]); // - or - auto b = new ubyte[42]; oops(b[$ .. $]); } --- — David
Jan 25
parent Adam D. Ruppe <destructionator gmail.com> writes:
On Wednesday, 25 January 2017 at 23:09:11 UTC, David Nadlinger 
wrote:
 This is a fallacy:
Ah, yes indeed, that was mentioned earlier in the thread too, it just slipped my mind again.
Jan 25
prev sibling parent reply Jonathan M Davis via Digitalmars-d-learn writes:
On Wednesday, January 25, 2017 22:46:10 David Nadlinger via Digitalmars-d-
learn wrote:
 On Tuesday, 24 January 2017 at 11:38:16 UTC, Jonathan M Davis

 wrote:
 It seems _slightly_ better from a safety perspective but only
 slightly.
Wrong – one is correct, the other is not. This is because every pointer in SafeD is dereferencable. Pointer arithmetic is not allowed in SafeD, so your concerns about reading from other memory do not apply.
Yes, but my point is that you're normally only going to use .ptr to pass something to a C function, and even if you're doing more with it in D, odds are, you're going to be doing pointer arithmetic. All &arr[0] does over arr.ptr is check the first element. It makes it safe, but then everything else you're going to be doing after that almost certainly won't be safe. And when you combine it with marking C function trusted, this is actually pretty bad. It makes it trivial to do something like extern(C) int cFunc(int* ptr, size_t length) trusted; auto result = cFunc(&arr[0], 12); and have it be considered safe, when it's not safe at all. Now, most code would then do auto result = cFunc(&arr[0], arr.length); so in practice, it won't usally be a problem, but it makes it easy to have code treated like it's completely safe when in fact it isn't. Now, really, the fix there is to not mark the C function as trusted and require that the caller make sure they pass in arguments that are safe, but at least if they were doing auto result = cFunc(arr.ptr, arr.length); the compiler would have caught that it was system even with the C function being marked as trusted, whereas if you do &arr[0], it wouldn't. So, yes, if all you're planning to do is look at the pointer to the first element in the array, then &arr[0] is safer, but odds are quite low that that's actually what you're going to do, and in all of the other cases, you might as well just use .ptr. So, telling folks to go use &arr[0] instead of .ptr doesn't seem very helpful to me. - Jonathan M Davis
Jan 25
next sibling parent David Nadlinger <code klickverbot.at> writes:
On Wednesday, 25 January 2017 at 22:59:55 UTC, Jonathan M Davis 
wrote:
 Yes, but my point is that you're normally only going to use 
 .ptr to pass something to a C function, and even if you're 
 doing more with it in D, odds are, you're going to be doing 
 pointer arithmetic.
Wrong again. If this were the case, we wouldn't have needed to make it a deprecation at all, since all uses would have been mistakes. A non-negligible amount of real-world D code actually uses single-object pointers. Look up the change history if you are interested – and indeed, making sure one understands the topic sufficiently well to meaningfully contribute before typing out a wall-length sermon would collectively save us a good chunk of time.
 And when you combine it with marking C function  trusted, this 
 is actually pretty bad.
Ex falso quodlibet – once you have a piece of code mistakenly marked trusted, all guarantees are out of the window even without suspicious-looking client code. safe-ty is about mechanically verifiable code, not faith-based programming. — David
Jan 25
prev sibling parent Dukc <ajieskola gmail.com> writes:
On Wednesday, 25 January 2017 at 22:59:55 UTC, Jonathan M Davis 
wrote:
 So, yes, if all you're planning to do is look at the pointer to 
 the first element in the array, then &arr[0] is safer, but odds 
 are quite low that that's actually what you're going to do, and 
 in all of the other cases, you might as well just use .ptr. So, 
 telling folks to go use &arr[0] instead of .ptr doesn't seem 
 very helpful to me.
Pehaps it should say something like: arr.ptr is deprecated in safe code. Use &arr[0] for checked access or remove the safe attribute. That would make it clear that .ptr is not an issue, only .ptr in safe.
Jan 26