Thread overview
[Issue 14162] Erratic inference of @safe for lambdas
Feb 10, 2015
Kenji Hara
Jun 07, 2016
Walter Bright
Jun 23, 2016
Walter Bright
Jun 23, 2016
Walter Bright
February 10, 2015
https://issues.dlang.org/show_bug.cgi?id=14162

--- Comment #1 from Kenji Hara <k.hara.pg@gmail.com> ---
I also noticed this difference recently. But I think the safe violation error in the first case is incorrect.

(In reply to Walter Bright from comment #0)
> Consider the code:
> ---------
> @trusted auto trusted(alias fun)() { return fun(); }
> 
> @safe void func()
> {
>     char[3] s = "abc";
>     string t = trusted!(() => cast(string)(s[]));
> //test.d(6): Error: cast from char[] to string not allowed in safe code
>     assert(t == "abc");
> }
> 
> @safe void test() { func(); }
> ----------
> The error is correct because the lambda is evaluated in the context of @safe
> func(), not in the context of @trusted trusted(). But turn func() into a
> template function:

Normally a nested function inherits @safe attribute from the enclosing function. But lambda function attribute should inferred from the body statement. Currently the inherited @safe is preferred, but as I'll explain later, it's not good behavior.

> ---------
> @trusted auto trusted(alias fun)() { return fun(); }
> 
> @safe void func()() // only change is add () to make it a template
> {
>     char[3] s = "abc";
>     string t = trusted!(() => cast(string)(s[]));
>     assert(t == "abc");
> }
> 
> @safe void test() { func(); }
> ----------
> And it now incorrectly compiles without error.

Inside template function, the attribute inference result is priority than the inherited @safe on the lambda. Then the lambda is marked as @system.

========

The latter case is at least intended behavior. See FuncDeclaration::semantic()
in func.c:

    if (sc->func)
    {
        /* If the parent is @safe, then this function defaults to safe too.
         */
        if (tf->trust == TRUSTdefault)
        {
            FuncDeclaration *fd = sc->func;

            /* If the parent's @safe-ty is inferred, then this function's
@safe-ty needs
             * to be inferred first.
             * If this function's @safe-ty is inferred, then it needs to be
infeerd first.
             * (local template function inside @safe function can be inferred
to @system).
             */
            if (fd->isSafeBypassingInference() && !isInstantiated())
                tf->trust = TRUSTsafe;              // default to @safe
        }

The behavior is necessary to support a lambda idiom. For example:

struct S { this(this) {} }
import std.traits: isSafe;
void foo(T)() @safe
{
    static if (isSafe!((ref T t){ T t2 = t; }))
    {
        pragma(msg, true);
    }
    else
    {
        pragma(msg, false);
    }
}
void main() { foo!S(); }

Lambda is used to check whether the T's copy operation is really safe. If the lambda inherits @safe attribute from the enclosing foo, unsafe T copy will cause safe violation error so the check won't work.

And more, D allows to declare @system function inside @safe function.

void foo() @safe
{
    static void bar() @system
    {
    }
}

>From the fact, even if a lambda inside @safe function is deduced to @system, it
won't cause safety violation. Only when the lambda is actually called in the @safe function, it should be an error.

void foo() @safe
{
    auto dg = { return systemCall() };   // should be OK
    auto ret = { return systemCall() }();  // system cannot call in safe
function
}

As a conclusion, I think the former case should be fixed, and the behavior should be same with the latter.

--
June 07, 2016
https://issues.dlang.org/show_bug.cgi?id=14162

Walter Bright <bugzilla@digitalmars.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
           Keywords|                            |safe

--
June 23, 2016
https://issues.dlang.org/show_bug.cgi?id=14162

--- Comment #2 from Walter Bright <bugzilla@digitalmars.com> ---
Inference of safety for lambdas should always occur if not explicitly set, because the source is always available.

--
June 23, 2016
https://issues.dlang.org/show_bug.cgi?id=14162

Walter Bright <bugzilla@digitalmars.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
           Keywords|                            |pull

--- Comment #3 from Walter Bright <bugzilla@digitalmars.com> ---
https://github.com/dlang/dmd/pull/5881

--
June 28, 2016
https://issues.dlang.org/show_bug.cgi?id=14162

--- Comment #4 from github-bugzilla@puremagic.com ---
Commits pushed to master at https://github.com/dlang/dmd

https://github.com/dlang/dmd/commit/366378a5b606ee2093eb6625101e88573a7b2960 fix Issue 14162 - Erratic inference of @safe for lambdas

https://github.com/dlang/dmd/commit/11165915a80835d0098f719fe0b4629b4837e583 Merge pull request #5881 from WalterBright/fix14162

fix Issue 14162 - Erratic inference of @safe for lambdas

--
October 01, 2016
https://issues.dlang.org/show_bug.cgi?id=14162

--- Comment #5 from github-bugzilla@puremagic.com ---
Commits pushed to stable at https://github.com/dlang/dmd

https://github.com/dlang/dmd/commit/366378a5b606ee2093eb6625101e88573a7b2960 fix Issue 14162 - Erratic inference of @safe for lambdas

https://github.com/dlang/dmd/commit/11165915a80835d0098f719fe0b4629b4837e583 Merge pull request #5881 from WalterBright/fix14162

--