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: --------- @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.
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.
Inference of safety for lambdas should always occur if not explicitly set, because the source is always available.
https://github.com/dlang/dmd/pull/5881
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
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