Jump to page: 1 2
Thread overview
@safe question
Jan 09, 2022
forkit
Jan 09, 2022
Salih Dincer
Jan 10, 2022
forkit
Jan 10, 2022
Paul Backus
Jan 11, 2022
forkit
Jan 11, 2022
Paul Backus
Jan 11, 2022
forkit
Jan 11, 2022
Paul Backus
Jan 12, 2022
forkit
Jan 12, 2022
H. S. Teoh
Jan 12, 2022
Paul Backus
January 09, 2022
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++;
    }
}

//-----
January 09, 2022

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:

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

}
January 10, 2022
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

January 10, 2022
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.
January 11, 2022
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??

January 11, 2022
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.
January 11, 2022
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' :-(

January 11, 2022
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/
January 12, 2022
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.


January 11, 2022
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.
« First   ‹ Prev
1 2