Thread overview
How can I tell D that function args are @nogc etc.
Apr 13
Monkyyy
April 10

Below is a example program that illustrates my issue.

When compiled at run.dlang I get:

onlineapp.d(18): Error: `@safe` function `onlineapp.processSafely!(1, 4).processSafely` cannot call `@system` function pointer `shouldDo`
onlineapp.d(28): Error: template instance `onlineapp.processSafely!(1, 4)` error instantiating

Why isn't this working? How can I get the effect I want?

Cheers,
-- john

bool[7] stagesToProcess = false;

@nogc nothrow @safe bool shouldDoInStages(int index)
{
    return stagesToProcess[index];
}

@nogc nothrow @safe bool shouldDoNever(int index)
{
    return false;
}

template processSafely(int low, int high)
{
    alias ShouldDoFnT = @nogc nothrow @safe bool function(int);

    @nogc nothrow @safe uint processSafely(ShouldDoFnT shouldDo)
    {
        assert(low < high);
        uint stepsProcessed;
        for (int ii = low; ii <= high; ii++)
        {
            if (shouldDo(ii))
            {
                stepsProcessed++;
            }
        }
        return stepsProcessed;
    }
}

void main()
{
    stagesToProcess = [false, false, true, true, false, true, false];
    uint count = processSafely!(1, 4)(&shouldDoInStages);
    assert(count == 2);
}
April 10
Place your attributes on the right hand side of the function, not the left side.

Use the left side for attributes/type qualifiers that go on the return type.

```d
bool[7] stagesToProcess = false;

bool shouldDoInStages(int index) @nogc nothrow @safe
{
     return stagesToProcess[index];
}

bool shouldDoNever(int index) @nogc nothrow @safe
{
     return false;
}

template processSafely(int low, int high)
{
     alias ShouldDoFnT = bool function(int) @nogc nothrow @safe;

     uint processSafely(ShouldDoFnT shouldDo) @nogc nothrow @safe
     {
         assert(low < high);
         uint stepsProcessed;
         for (int ii = low; ii <= high; ii++)
         {
             if (shouldDo(ii))
             {
                 stepsProcessed++;
             }
         }
         return stepsProcessed;
     }
}

void main()
{
     stagesToProcess = [false, false, true, true, false, true,
false];
     uint count = processSafely!(1, 4)(&shouldDoInStages);
     assert(count == 2);
}
```
April 10

On Wednesday, 10 April 2024 at 11:34:06 UTC, Richard (Rikki) Andrew Cattermole wrote:

>

Place your attributes on the right hand side of the function, not the left side.

Use the left side for attributes/type qualifiers that go on the return type.

Just a word of warning, this explanation suggests putting qualifiers on the left side would affect the return type, this is not the case.

Attributes apply to the declaration. In some cases this effectively applies to the return type, in some cases it applies to the function, in some cases it applies to the context pointer.

In order to apply type constructors to the return type, you need to use parentheses:

const int  foo(); // const applies to the context pointer of `foo`, not `int`
const(int) bar(); // const applies to `int` return type
ref int baz(); // ref applies to `baz`, which in turn means "ref returning function"

Where this becomes tricky is return types that are function pointers/delegates. Then using the right side of the function/delegate type is the only way.

@safe void function() foo(); // `foo` is safe, the function pointer it returns is not
void function() @safe bar(); // `bar` is not safe, the function pointer returned is
void function() @safe baz() @safe; // both are safe

-Steve

April 11

Interesting. Thank you to both of you.

On Wednesday, 10 April 2024 at 17:38:21 UTC, Steven Schveighoffer wrote:

>

On Wednesday, 10 April 2024 at 11:34:06 UTC, Richard (Rikki) Andrew Cattermole wrote:

>

Place your attributes on the right hand side of the function, not the left side.

Use the left side for attributes/type qualifiers that go on the return type.

Just a word of warning, this explanation suggests putting qualifiers on the left side would affect the return type, this is not the case.

So in my example, what did I actually tell the compiler with the placement of the attributes? And how was it different between the function type alias declaration, and the actual function declaration?

More specifically, what are the semantic differences below?

alias FnPrefixT = @nogc nothrow @safe bool function(int);
// Versus
alias FnSuffixT = bool function(int) @nogc nothrow @safe;

and

@nogc nothrow @safe bool fnPrefix(int) { stuff }
// Versus
bool fnSuffix(int) @nogc nothrow @safe  { stuff }

Is there a reasonably clear overview of how this works anywhere? What I have seen so far led me to the vague impression that it wasn't significant just like attribute ordering.

-- john

April 11

On Thursday, 11 April 2024 at 03:17:36 UTC, John Dougan wrote:

>

Interesting. Thank you to both of you.

On Wednesday, 10 April 2024 at 17:38:21 UTC, Steven Schveighoffer wrote:

>

On Wednesday, 10 April 2024 at 11:34:06 UTC, Richard (Rikki) Andrew Cattermole wrote:

>

Place your attributes on the right hand side of the function, not the left side.

Use the left side for attributes/type qualifiers that go on the return type.

Just a word of warning, this explanation suggests putting qualifiers on the left side would affect the return type, this is not the case.

So in my example, what did I actually tell the compiler with the placement of the attributes? And how was it different between the function type alias declaration, and the actual function declaration?

More specifically, what are the semantic differences below?

alias FnPrefixT = @nogc nothrow @safe bool function(int);
// Versus
alias FnSuffixT = bool function(int) @nogc nothrow @safe;

So D can provide a nice mechanism to show what is happening -- pragma(msg, ...)

If I do that with the two types above I see something very interesting:

pragma(msg, FnPrefixT);
pragma(msg, FnSuffixT);
bool function(int) nothrow @nogc
bool function(int) nothrow @nogc @safe

That surprises me. nothrow and @nogc go onto the type, but not @safe if put before the declaration? I have no idea why. All I can think of is that it is a bug.

>

and

@nogc nothrow @safe bool fnPrefix(int) { stuff }
// Versus
bool fnSuffix(int) @nogc nothrow @safe  { stuff }
pragma(msg, typeof(fnPrefix));
pragma(msg, typeof(fnSuffix));
nothrow @nogc @safe bool(int)
nothrow @nogc @safe bool(int)

(as expected)

-Steve

April 12

On Thursday, 11 April 2024 at 15:00:49 UTC, Steven Schveighoffer wrote:

>

So D can provide a nice mechanism to show what is happening -- pragma(msg, ...)

If I do that with the two types above I see something very interesting:

pragma(msg, FnPrefixT);
pragma(msg, FnSuffixT);
bool function(int) nothrow @nogc
bool function(int) nothrow @nogc @safe

That surprises me. nothrow and @nogc go onto the type, but not @safe if put before the declaration? I have no idea why. All I can think of is that it is a bug.

pragma(msg,...) is very useful. Thanks.

My general impressions were correct then. It shouldn't matter on which side the attrs get put, except in some ambiguous cases. It's just broken. Not every day you get to blame a compiler bug.

Feeding:

alias FnPrefixT = @safe nothrow @nogc bool function(int);
alias FnSuffixT = bool function(int) @safe nothrow @nogc ;

pragma(msg, FnPrefixT);
pragma(msg, FnSuffixT);

void main() { return; }

into run.dlang and having it compile with all the compilers...gets the same result all the way back to 2.060. It has this issue with gdc 2.076, which is what I'm using normally.

What is the procedure for bug reporting? I'm looking at the issues tracker and have no clue how to drive the search to see if this is already there.

>

-Steve

-- john

April 12

On Friday, 12 April 2024 at 03:57:40 UTC, John Dougan wrote:

>

What is the procedure for bug reporting? I'm looking at the issues tracker and have no clue how to drive the search to see if this is already there.

https://issues.dlang.org

While entering the bug title, it does a fuzzy search for existing open and closed issues.

-Steve

April 13

On Friday, 12 April 2024 at 15:08:50 UTC, Steven Schveighoffer wrote:

>

On Friday, 12 April 2024 at 03:57:40 UTC, John Dougan wrote:

>

What is the procedure for bug reporting? I'm looking at the issues tracker and have no clue how to drive the search to see if this is already there.

https://issues.dlang.org

While entering the bug title, it does a fuzzy search for existing open and closed issues.

The typical problem with issue/bug database searches is you have to know the important discriminating keywords that projects evolve over time. When you are new to a system, as I am with D, you end up looking manually through a lot of possibles. Another barrier to noobs that project long timers may not notice.

Any rate, it appears https://issues.dlang.org/show_bug.cgi?id=22046 is the same issue.

And I'm not sure how to interpret it, as a noob I don't have enough context. It appears to be deliberate and also afflicts var declarations. Since 2014.

From my point of view, either it's still a bug and needs to be written up in a best practices list with all the other long term stuff you need to work around until it can be fixed (eg. "in alias and var function declarations, put attributes as a suffix because...", https://dlang.org/dstyle.html might be a place), or it has aged in to become the effective intended behavior and should be documented other places and have a compiler error or warning ("@safe in prefix position in alias, is ignored"). Or of course, it could get fixed but my experiences have shown me that after 10 years that is low probability with most projects.

I'm not trying to be a dick here. I've managed projects and know what unintentional dumb stuff can happen. But, at the moment, I'm evaluating D for a project (porting 30,000 lines of very old C with strict timing requirements) and I've got some time to build impressions of system language candidates. There appears to be a lot of talk from time to time over in General about luring new people in to work with D, and this kind of issue is relevant.

>

-Steve

--john

April 13

On Friday, 12 April 2024 at 03:57:40 UTC, John Dougan wrote:

>

Not every day you get to blame a compiler bug.

D is uniquely: hacky, expressive and buggy.

Having more metaprograming then c++ without the raw man power comes at a cost, in d you should distrust the spec and instead see what the compiler actually does far more then any other languge.

>

-- john

-- monkyyy