Jump to page: 1 2
Thread overview
nothrow/@nogc inference troubles with emplace/move?
Aug 27
Manu
Aug 27
Dennis
Aug 28
Manu
Aug 28
Dennis
Aug 28
Manu
Aug 28
Dennis
Aug 28
Manu
Aug 28
Dennis
Aug 29
Manu
August 27
I'm getting a lot of nothrow/@nogc errors where I would have expected the
attributed to be inferred...
In particular; core.lifetime stuff, I can't live without that, but I'm
finding that move and emplace are both often not inferring the nothrow and
@nogc attributes correctly, and so they break my code.

Is this a known issue? Should I expect attribute inference to work comprehensively?

I've copy-pasted core.lifetime into my own library, stripped it bare and marked everything `nothrow @nogc`, and that works... but I'm not really keen to do this.


August 27

On Tuesday, 27 August 2024 at 05:17:18 UTC, Manu wrote:

>

Is this a known issue? Should I expect attribute inference to work comprehensively?s.

The error message for calling a function that failed attribute inference should include the reason the attribute failed to be inferred. If it doesn't, you're likely hitting this classic:

Issue 7205 - Function attribute inference fails in case of mutual dependencies

August 28
On Tue, 27 Aug 2024 at 21:10, Dennis via Digitalmars-d < digitalmars-d@puremagic.com> wrote:

> On Tuesday, 27 August 2024 at 05:17:18 UTC, Manu wrote:
> > Is this a known issue? Should I expect attribute inference to work comprehensively?s.
>
> The error message for calling a function that failed attribute inference should include the reason the attribute failed to be inferred. If it doesn't, you're likely hitting this classic:
>
> [Issue 7205 - Function attribute inference fails in case of mutual dependencies ](https://issues.dlang.org/show_bug.cgi?id=7205)
>

Oh wow... that's bad. Is there any plan to fix this?


August 28

On Wednesday, 28 August 2024 at 08:59:49 UTC, Manu wrote:

>

Oh wow... that's bad. Is there any plan to fix this?

I want to, but with dmd's current architecture, I don't know how. The tricky case is:

// example of @safe inference, but the same applies to pure nothrow and @nogc

void systemFunc() @system;

void fun1()()
{
    fun2();
    systemFunc();
}

void fun2()()
{
    fun1();
}

void main0() @system
{
    fun1();
}

void main1() @safe
{
    fun2(); // should error
}

fun1 gets analyzed first, which gets interrupted when it sees the call to fun2.
Then fun2 gets analyzed, but that sees a call to fun1, which at that point is still in the process of inferring attributes. The current implementation gives up here and infers fun2 as @system.

So I tried replacing that pessimistic assumption with an optimistic assumption, but in this case, fun1 will turn out to be @system because of the systemFunc() call. But at the time fun2 ends its analysis, this is completely unknown. Until fun1 ends its analysis, the needed information isn't there.

I could start by inferring fun2 as @safe and then retract that once fun1 finishes analysis, but currently, the compiler assumes a function type to be final after its body was analyzed, so mutating the type later is going to mess up things.

So without re-architecturing dmd's semantic analysis, I don't see a way out.

August 28
On Wed, 28 Aug 2024 at 20:11, Dennis via Digitalmars-d < digitalmars-d@puremagic.com> wrote:

> On Wednesday, 28 August 2024 at 08:59:49 UTC, Manu wrote:
> > Oh wow... that's bad. Is there any plan to fix this?
>
> I want to, but with dmd's current architecture, I don't know how. The tricky case is:
>
> ```D
> // example of @safe inference, but the same applies to pure
> nothrow and @nogc
>
> void systemFunc() @system;
>
> void fun1()()
> {
>      fun2();
>      systemFunc();
> }
>
> void fun2()()
> {
>      fun1();
> }
>
> void main0() @system
> {
>      fun1();
> }
>
> void main1() @safe
> {
>      fun2(); // should error
> }
> ```
>
> fun1 gets analyzed first, which gets interrupted when it sees the
> call to fun2.
> Then fun2 gets analyzed, but that sees a call to fun1, which at
> that point is still in the process of inferring attributes. The
> current implementation gives up here and infers fun2 as `@system`.
>
> So I tried replacing that pessimistic assumption with an optimistic assumption, but in this case, `fun1` will turn out to be `@system` because of the `systemFunc()` call. But at the time `fun2` ends its analysis, this is completely unknown. Until `fun1` ends its analysis, the needed information isn't there.
>
> I could start by inferring `fun2` as `@safe` and then retract that once `fun1` finishes analysis, but currently, the compiler assumes a function type to be final after its body was analyzed, so mutating the type later is going to mess up things.
>
> So without re-architecturing dmd's semantic analysis, I don't see a way out.
>

I reckon while parsing a function X, you could gather any evidence
available that invalidates the inference, and in lieu of any invalidation
when you encounter Y that's not itself resolved, place a token on X that
it's waiting on Y (and another token that it's waiting on Z, etc), then
also place a token on Y and Z that says when it's finished it's own
resolution it should poke the result back to X.
What will happen then is Y may run inference and be invalidated, in which
case it reports the invalidation result back to X, and that may invalidate
a cascade of pending inferences... or it may itself not be finalised
waiting optimistically on the inference of X (or some other cycle).
At the end when everything's had a go, all outstanding tokens must be
involved in optimistic cycles since there was nothing in any of those
functions that invalidated their inferences; and since they're all
outstanding on an optimistic cycle, then I think they naturally all satisfy
eachother in the optimistic case. So just close out all outstanding tokens
in the optimistic case... does that sound right?


August 28

On Wednesday, 28 August 2024 at 11:20:43 UTC, Manu wrote:

>

At the end when everything's had a go
(...)
So just close out all outstanding tokens
in the optimistic case... does that sound right?

That's what I tried to implement, with each function keeping an array of callers of that function, so it could propagate an attribute violation to functions that had 'outstanding tokens' from calling that function.

But we don't have the luxury to wait, because D code may inspect function types and demand an answer. Here's a (contrived) example:

import std.traits;

@system void systemFunc();

void fun1()()
{
    alias T = typeof(fun2()); // is fun2 @safe? need an answer for T now!

    // let's make a contradiction!
    static if (hasFunctionAttributes!(fun2!(), "@safe"))
        systemFunc();
}

void fun2()()
{
    fun1();
}

void main0() @system
{
    fun1();
}

void main() @safe
{
    fun2(); // not system!
}

We don't need to support such contrived examples of course, but even for normal code, there are many places where the compiler inspects the type of a function. Making it account everywhere for the possibility that the function's attributes can still change, even after semantic analysis has finished, is a complex task.

August 28
On Wed, 28 Aug 2024 at 22:11, Dennis via Digitalmars-d < digitalmars-d@puremagic.com> wrote:

> On Wednesday, 28 August 2024 at 11:20:43 UTC, Manu wrote:
> > At the end when everything's had a go
> > (...)
> > So just close out all outstanding tokens
> > in the optimistic case... does that sound right?
>
> That's what I tried to implement, with each function keeping an array of callers of that function, so it could propagate an attribute violation to functions that had 'outstanding tokens' from calling that function.
>
> But we don't have the luxury to wait, because D code may inspect function types and demand an answer. Here's a (contrived) example:
>
> ```D
> import std.traits;
>
> @system void systemFunc();
>
> void fun1()()
> {
>      alias T = typeof(fun2()); // is fun2 @safe? need an answer
> for T now!
>
>      // let's make a contradiction!
>      static if (hasFunctionAttributes!(fun2!(), "@safe"))
>          systemFunc();
> }
>
> void fun2()()
> {
>      fun1();
> }
>
> void main0() @system
> {
>      fun1();
> }
>
> void main() @safe
> {
>      fun2(); // not system!
> }
> ```
>
> We don't need to support such contrived examples of course, but even for normal code, there are many places where the compiler inspects the type of a function. Making it account everywhere for the possibility that the function's attributes can still change, even after semantic analysis has finished, is a complex task.
>

Yeah I get that... it's complex because there's not enough passes; a lot is done in one or a few passes, so there's no way to be confident that you can establish some compile-time facts before you expect they could be used...

I notice that all of your inferred functions are templates, and that
reminded me a thought I had the other night... I don't see any reason
non-templates couldn't be subject to attribute inferrence too; just that
they shouldn't have it mangled into the name. If it carries a silent
attribute that it inferred, it could be super convenient since there are so
many attributes to juggle these days.
It would also mean people who didn't write code aggressively pursuing
attributes may inadvertently write libraries that still CAN be used by more
context sensitive users. I've noticed on my current project that I have to
throw basically every library every written out the window... including
druntime, etc. Heaps of druntime is compatible with my code, but because
someone didn't put the attribute on it, I can't call it. It would be good
if it inferred the full set of attributes silently, and then I could
actually use the libs.


August 28

On Wednesday, 28 August 2024 at 12:57:24 UTC, Manu wrote:

>

Yeah I get that... it's complex because there's not enough passes; a lot is done in one or a few passes, so there's no way to be confident that you can establish some compile-time facts before you expect they could be used...

I wish attribute checking were a separate pass that was completely subtractive. I.e. it rejects programs with violations, but doesn't affect program semantics otherwise. But that's not the case with D's current feature set.

>

I don't see any reason
non-templates couldn't be subject to attribute inferrence too;

Inference for all came up in the last DLF monthly as something we want to pursue.

>

just that
they shouldn't have it mangled into the name.

That's an interesting aspect that didn't come up. It could be confusing when the listed function type differs from the de-facto type with respect to attributes, but worth considering.

>

druntime, etc. Heaps of druntime is compatible with my code, but because
someone didn't put the attribute on it, I can't call it.

druntime and Phobos should be relatively well-annotated, but I've stumbled on missing nothrow and @nogc in Windows bindings before. If you have any specifics, please post them on bugzilla.

August 28
Excellent explanatory cases like this should be added to the bug report.
August 28
On 8/28/2024 5:57 AM, Manu wrote:
> Heaps of druntime is compatible with my code, but because someone didn't put the attribute on it, I can't call it.

You can add the attribute and submit a PR for it! Or submit a bugzilla issue. Or email Dennis or myself. Or post in the n.g.

But we can't fix any of these because you don't report them!

There's no reason to suffer from this.

« First   ‹ Prev
1 2