[Issue 14162] Erratic inference of @safe for lambdas
via Digitalmars-d-bugs
digitalmars-d-bugs at puremagic.com
Tue Feb 10 04:03:19 PST 2015
https://issues.dlang.org/show_bug.cgi?id=14162
--- Comment #1 from Kenji Hara <k.hara.pg at 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.
--
More information about the Digitalmars-d-bugs
mailing list