www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - safe question

reply forkit <forkit gmail.com> writes:
Do not understand why one line is not considered  safe, but the 
other is.

//----

module test;

import std;

 safe void main()
{
     immutable string[] strings = ["one", "one", "two"];

     immutable(string)*[] pointers = null;

     foreach(size_t i, ref str; strings)
     {
         if(str == "one")
         {
             //pointers ~= &str; // not allowed in  safe ??

             pointers ~= &strings[i]; // for  safe, I have to 
revert to using an index into strings.
         }
         i++;
     }
}

//-----
Jan 09 2022
parent reply Salih Dincer <salihdb hotmail.com> writes:
On Sunday, 9 January 2022 at 20:58:05 UTC, forkit wrote:
 Do not understand why one line is not considered  safe, but the 
 other is.

 //----

 module test;

 import std;

  safe void main()
 {
     immutable string[] strings = ["one", "one", "two"];

     immutable(string)*[] pointers = null;

     foreach(size_t i, ref str; strings)
     {
         if(str == "one")
         {
             //pointers ~= &str; // not allowed in  safe ??

             pointers ~= &strings[i]; // for  safe, I have to 
 revert to using an index into strings.
         }
         i++;
     }
 }

 //-----
Try the trusted and in/out: ```d auto pro(in immutable string[] strings, out immutable(string)*[] pointers) trusted { foreach(i, ref str; strings) { if(str == "one") { //pointers ~= &strings[i]/* ok pointers ~= &str;//*/ } /* unnecessary: i++;//*/ } } safe void main() { immutable string[] strings = ["one", "one", "two"]; immutable(string)*[] pointers = null; strings.pro(pointers); assert(pointers[0] == &strings[0]); // ok assert(pointers[1] == &strings[1]); // ok } ```
Jan 09 2022
parent reply forkit <forkit gmail.com> writes:
On Sunday, 9 January 2022 at 21:56:05 UTC, Salih Dincer wrote:
 Try the  trusted and in/out:
 ...
 ..
 .
thanks for introducing me to the in/out feature of D :-) I'll certainly look into that feature more. But my question still remains: //pointers ~= &str; // why is this *not* allowed in safe pointers ~= &strings[i]; // while this *is* allowed in safe
Jan 09 2022
parent reply Paul Backus <snarwin gmail.com> writes:
On Monday, 10 January 2022 at 01:16:31 UTC, forkit wrote:
 On Sunday, 9 January 2022 at 21:56:05 UTC, Salih Dincer wrote:
 Try the  trusted and in/out:
 ...
 ..
 .
thanks for introducing me to the in/out feature of D :-) I'll certainly look into that feature more. But my question still remains: //pointers ~= &str; // why is this *not* allowed in safe pointers ~= &strings[i]; // while this *is* allowed in safe
Taking the address of a local variable is forbidden in safe code. Even though str is a ref variable that points to a heap-allocated string, it is still considered a local variable because it is declared inside the body of a function.
Jan 09 2022
parent reply forkit <forkit gmail.com> writes:
On Monday, 10 January 2022 at 03:21:46 UTC, Paul Backus wrote:
 Taking the address of a local variable is forbidden in  safe 
 code. Even though str is a ref variable that points to a 
 heap-allocated string, it is still considered a local variable 
 because it is declared inside the body of a function.
but strings[] is also a local variable declared in the body of the same function, and yet within the foreach statement, safe lets me do: pointers ~= &strings[i]; // safe ...but not this below, where str is just a reference to the exact same memory as the statement above... is it not? How is this below any more or less safe than the above statement. pointers ~= &str; // not safe - ok, but why??
Jan 11 2022
parent reply Paul Backus <snarwin gmail.com> writes:
On Tuesday, 11 January 2022 at 10:57:28 UTC, forkit wrote:
 On Monday, 10 January 2022 at 03:21:46 UTC, Paul Backus wrote:
 Taking the address of a local variable is forbidden in  safe 
 code. Even though str is a ref variable that points to a 
 heap-allocated string, it is still considered a local variable 
 because it is declared inside the body of a function.
but strings[] is also a local variable declared in the body of the same function, and yet within the foreach statement, safe lets me do: pointers ~= &strings[i]; // safe ...but not this below, where str is just a reference to the exact same memory as the statement above... is it not? How is this below any more or less safe than the above statement. pointers ~= &str; // not safe - ok, but why??
Because the compiler doesn't look at that much context, and it's possible to write code where `str` points to memory that's on the stack; for example: string[3] strings = ["foo", "bar", "baz"]; foreach (ref str; strings) { // ... } If you compile with -preview=dip1000, the compiler will actually keep track of which pointers point to stack memory, and will allow your original code. But -preview=dip1000 is still somewhat experimental, and the documentation for it is pretty sparse, so you may have an easier time just working around the limitations of the default safety checks.
Jan 11 2022
parent reply forkit <forkit gmail.com> writes:
On Tuesday, 11 January 2022 at 14:54:51 UTC, Paul Backus wrote:
 ..
 If you compile with -preview=dip1000, the compiler will 
 actually keep track of which pointers point to stack memory, 
 and will allow your original code. But -preview=dip1000 is 
 still somewhat experimental, and the documentation for it is 
 pretty sparse, so you may have an easier time just working 
 around the limitations of the default safety checks.
Thanks. Appreciate the explanation :-) In the end though, correct code should just compile. I shouldn't need a 'work around' :-(
Jan 11 2022
parent reply Paul Backus <snarwin gmail.com> writes:
On Tuesday, 11 January 2022 at 21:38:58 UTC, forkit wrote:
 On Tuesday, 11 January 2022 at 14:54:51 UTC, Paul Backus wrote:
 ..
 If you compile with -preview=dip1000, the compiler will 
 actually keep track of which pointers point to stack memory, 
 and will allow your original code. But -preview=dip1000 is 
 still somewhat experimental, and the documentation for it is 
 pretty sparse, so you may have an easier time just working 
 around the limitations of the default safety checks.
Thanks. Appreciate the explanation :-) In the end though, correct code should just compile. I shouldn't need a 'work around' :-(
In any statically typed language, there is always going to be code which you, the programmer, know is correct, but which the compiler can't automatically prove is correct. The same is true for safety. If you know a particular bit of code is memory safe, but the compiler can't prove it, you can mark that code as trusted. For example: () trusted { pointers ~= &str; )(); This example uses an immediately-invoked function literal [1] (also known as a "lambda") to apply the trusted attribute to a single statement. Of course, when you write trusted code, you must be *very* sure that what you are doing cannot possibly lead to undefined behavior, no matter what happens in other parts of the program. There's a post on the official D blog, "How to Write trusted Code in D," [2] that talks about some of the most common pitfalls, and gives advice for avoiding them. [1] https://dlang.org/spec/expression.html#function_literals [2] https://dlang.org/blog/2016/09/28/how-to-write-trusted-code-in-d/
Jan 11 2022
parent reply forkit <forkit gmail.com> writes:
On Tuesday, 11 January 2022 at 21:50:00 UTC, Paul Backus wrote:
 ..
 If you know a particular bit of code is memory safe, but the 
 compiler can't prove it, you can mark that code as  trusted. 
 For example:

     ()  trusted { pointers ~= &str; )();

 This example uses an immediately-invoked function literal [1] 
 (also known as a "lambda") to apply the  trusted attribute to a 
 single statement.
 ...
Thanks again. Really useful information. The more I use D, the more I feel that I'm falling into a deep, deep, deep....rabbit hole.
Jan 11 2022
parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Wed, Jan 12, 2022 at 12:24:14AM +0000, forkit via Digitalmars-d-learn wrote:
 On Tuesday, 11 January 2022 at 21:50:00 UTC, Paul Backus wrote:
 ..
 If you know a particular bit of code is memory safe, but the compiler
 can't prove it, you can mark that code as  trusted. For example:
 
     ()  trusted { pointers ~= &str; )();
 
 This example uses an immediately-invoked function literal [1] (also
 known as a "lambda") to apply the  trusted attribute to a single
 statement.
 ...
Thanks again. Really useful information. The more I use D, the more I feel that I'm falling into a deep, deep, deep....rabbit hole.
IMNSHO, that trusted lambda thing is an anti-pattern that should be avoided, needless to say already promoted. It's papering over a problem that ought to be fixed instead of being pushed under the rug. If it takes -dip1000 to compile the OP's code, then I say, by all means, use -dip1000. It's not *that* hard to add a compile switch to your build. I know dip1000 isn't quite there yet, but how is it supposed to "get there" if everyone is avoiding to use it? We should rather be pushing more people to use it so that more flaws are discovered and fixed, rather than avoiding it and letting it languish, and 5 years later the same old flaws continue to sit unfixed. T -- People walk. Computers run.
Jan 11 2022
parent Paul Backus <snarwin gmail.com> writes:
On Wednesday, 12 January 2022 at 00:45:23 UTC, H. S. Teoh wrote:
 IMNSHO, that  trusted lambda thing is an anti-pattern that 
 should be avoided, needless to say already promoted.  It's 
 papering over a problem that ought to be fixed instead of being 
 pushed under the rug.
There's nothing wrong with trusted lambdas. The recent FUD around them is almost entirely unjustified.
 If it takes -dip1000 to compile the OP's code, then I say, by 
 all means, use -dip1000.  It's not *that* hard to add a compile 
 switch to your build.  I know dip1000 isn't quite there yet, 
 but how is it supposed to "get there" if everyone is avoiding 
 to use it?
The reason I hesitate to recommend -preview=dip1000 to a beginning D programmer is that because the documentation is incomplete, it can be very difficult to *debug* DIP 1000 errors, and because the implementation has several known bugs, you cannot simply rely on the compiler to tell you when you've gotten things right.
Jan 11 2022