Thread overview
what are the rules for @nogc and @safe attributes inference?
Nov 15, 2018
ikod
Nov 15, 2018
Adam D. Ruppe
Nov 16, 2018
ikod
Nov 16, 2018
Alex
Nov 16, 2018
ikod
Nov 30, 2018
ikod
Nov 30, 2018
Neia Neutuladh
Nov 30, 2018
ikod
Nov 30, 2018
Neia Neutuladh
November 15, 2018
Hello,

what are the rules for @nogc inference? It looks like sometimes compiler can infer @nogc, but not in this simplest case:

int x()
{
    return 1;
}
void main() @nogc
{
    x();
}

Thanks!
November 15, 2018
On Thursday, 15 November 2018 at 21:00:48 UTC, ikod wrote:
> what are the rules for @nogc inference?

It attempts it if and only if it is a template.


November 15, 2018
On 11/15/18 4:09 PM, Adam D. Ruppe wrote:
> On Thursday, 15 November 2018 at 21:00:48 UTC, ikod wrote:
>> what are the rules for @nogc inference?
> 
> It attempts it if and only if it is a template.

Well, the general "rule" is, if it's code that must be available to the compiler when it's called, then it will be inferred.

Examples of code that must be processed every time it's used:

1. Template functions
2. auto-returning functions
3. functions inside templates (like member functions of a templated struct)
4. Inner functions

There may be others I didn't think of.

Everything else must be manually attributed. The reasoning is that the function may be stubbed in a .di file, and in that case, attribute inference wouldn't be possible.

-Steve
November 16, 2018
On Thursday, 15 November 2018 at 21:55:18 UTC, Steven Schveighoffer wrote:
> On 11/15/18 4:09 PM, Adam D. Ruppe wrote:
>> On Thursday, 15 November 2018 at 21:00:48 UTC, ikod wrote:
>>> what are the rules for @nogc inference?
>> 
>> It attempts it if and only if it is a template.
>
> Well, the general "rule" is, if it's code that must be available to the compiler when it's called, then it will be inferred.
>
> Examples of code that must be processed every time it's used:
>
> 1. Template functions
> 2. auto-returning functions
> 3. functions inside templates (like member functions of a templated struct)
> 4. Inner functions
>
> There may be others I didn't think of.
>
> Everything else must be manually attributed. The reasoning is that the function may be stubbed in a .di file, and in that case, attribute inference wouldn't be possible.
>
> -Steve

Thanks for clarifications, Adam and Steven!

My problem is next code:
=================================================
import std.traits;

T library_func(T)(/*lazy*/ T i)
{
    // this fuction can be @nogc or not depending on T properties
    static if (isArray!T) {
        return i ~ i;
    }
    else
    {
    	return i;
    }
}
void user_function_nogc() @nogc
{
    int x = 1;
    library_func(x+1);
}
void user_function_gc()
{
    library_func([1]);
}
void main()
{
}
=====================================================
This code compiles as long as `lazy` is commented out. But I'd like to have
both lazy parameter and @nogc inferrence for `library_func` so that user is not locked to code @nogc or not.

November 16, 2018
On Friday, 16 November 2018 at 10:25:26 UTC, ikod wrote:
> On Thursday, 15 November 2018 at 21:55:18 UTC, Steven Schveighoffer wrote:
>> On 11/15/18 4:09 PM, Adam D. Ruppe wrote:
>>> On Thursday, 15 November 2018 at 21:00:48 UTC, ikod wrote:
>>>> what are the rules for @nogc inference?
>>> 
>>> It attempts it if and only if it is a template.
>>
>> Well, the general "rule" is, if it's code that must be available to the compiler when it's called, then it will be inferred.
>>
>> Examples of code that must be processed every time it's used:
>>
>> 1. Template functions
>> 2. auto-returning functions
>> 3. functions inside templates (like member functions of a templated struct)
>> 4. Inner functions
>>
>> There may be others I didn't think of.
>>
>> Everything else must be manually attributed. The reasoning is that the function may be stubbed in a .di file, and in that case, attribute inference wouldn't be possible.
>>
>> -Steve
>
> Thanks for clarifications, Adam and Steven!
>
> My problem is next code:
> =================================================
> import std.traits;
>
> T library_func(T)(/*lazy*/ T i)
> {
>     // this fuction can be @nogc or not depending on T properties
>     static if (isArray!T) {
>         return i ~ i;
>     }
>     else
>     {
>     	return i;
>     }
> }
> void user_function_nogc() @nogc
> {
>     int x = 1;
>     library_func(x+1);
> }
> void user_function_gc()
> {
>     library_func([1]);
> }
> void main()
> {
> }
> =====================================================
> This code compiles as long as `lazy` is commented out. But I'd like to have
> both lazy parameter and @nogc inferrence for `library_func` so that user is not locked to code @nogc or not.

Aha, aha... Interesting!
I had a similar problem with passing a delegate. And it was solved:
https://forum.dlang.org/thread/erznqknpyxzxqivawnix@forum.dlang.org

For your problem, there is a bug report:
https://forum.dlang.org/post/wedwfooqdxbwxttpmzim@forum.dlang.org
https://issues.dlang.org/show_bug.cgi?id=12664
https://issues.dlang.org/show_bug.cgi?id=12647
November 16, 2018
On Friday, 16 November 2018 at 12:12:12 UTC, Alex wrote:
>> =====================================================
>> This code compiles as long as `lazy` is commented out. But I'd like to have
>> both lazy parameter and @nogc inferrence for `library_func` so that user is not locked to code @nogc or not.
>
> Aha, aha... Interesting!
> I had a similar problem with passing a delegate. And it was solved:
> https://forum.dlang.org/thread/erznqknpyxzxqivawnix@forum.dlang.org
>
> For your problem, there is a bug report:
> https://forum.dlang.org/post/wedwfooqdxbwxttpmzim@forum.dlang.org
> https://issues.dlang.org/show_bug.cgi?id=12664
> https://issues.dlang.org/show_bug.cgi?id=12647

Thanks for these links.
And yes, I have workaround with handmade delegate. It compiles and it is lazy, but also ugly.

import std.traits;

T library_func(T)(/*not lazy*/ T i) if (!isCallable!T)
{
    return i;
}
ReturnType!T library_func(T)(/*lazy*/ T i) if (isCallable!T)
{
    return i();
}
void user_function_nogc() @nogc
{
    int x = 1;
    library_func(x+1);
}
void user_function_gc()
{
    library_func(()=>[1]);
}
void main()
{
}
November 30, 2018
On Thursday, 15 November 2018 at 21:55:18 UTC, Steven Schveighoffer wrote:
> On 11/15/18 4:09 PM, Adam D. Ruppe wrote:
>> On Thursday, 15 November 2018 at 21:00:48 UTC, ikod wrote:
>>> what are the rules for @nogc inference?
>> 
>> It attempts it if and only if it is a template.
>
> Well, the general "rule" is, if it's code that must be available to the compiler when it's called, then it will be inferred.
>
> Examples of code that must be processed every time it's used:
>
> 1. Template functions
> 2. auto-returning functions
> 3. functions inside templates (like member functions of a templated struct)
> 4. Inner functions
>
> There may be others I didn't think of.
>
> Everything else must be manually attributed. The reasoning is that the function may be stubbed in a .di file, and in that case, attribute inference wouldn't be possible.
>
> -Steve

Hello, Steve!

I can't find the reason why nogc/nothrow can't be inferred in this case:

class S(K,V)
{
    auto get/*()*/(K a)
    {
        return 0;
    }
}
void main() @nogc nothrow
{
    S!(int, string) sia;
    auto v = sia.get(1);
}

But everything is ok if you uncomment parentheses after get. get is already a member of templated class, what can be wrong with this code?

Thanks!
November 30, 2018
On Fri, 30 Nov 2018 20:41:03 +0000, ikod wrote:
> I can't find the reason why nogc/nothrow can't be inferred in this case:
> 
> class S(K,V)
> {
>      auto get/*()*/(K a) {
>          return 0;
>      }
> }
> void main() @nogc nothrow {
>      S!(int, string) sia;
>      auto v = sia.get(1);
> }

class Nefarious : S!(int, string)
{
  override int get(int a)
  {
    // Whoops, I used the GC
    return new char[a].length;
  }
}

The compiler can't prove that a variable of type S!(int, string) will not be of type Nefarious, which uses the GC, so it can't infer @nogc for S.get.

However, if you make the function final, then the compiler can infer it to be pure nothrow @nogc @safe.

Or if you use a struct instead of a class, structs don't do inheritance, so the compiler can infer attributes without worrying about nefarious inheritance.

> But everything is ok if you uncomment parentheses after get.

Templated functions are implicitly final.
November 30, 2018
On Friday, 30 November 2018 at 21:03:06 UTC, Neia Neutuladh wrote:
> On Fri, 30 Nov 2018 20:41:03 +0000, ikod wrote:
>> I can't find the reason why nogc/nothrow can't be inferred in this case:
>> 
>> class S(K,V)
>> {
>>      auto get/*()*/(K a) {
>>          return 0;
>>      }
>> }
>> void main() @nogc nothrow {
>>      S!(int, string) sia;
>>      auto v = sia.get(1);
>> }
>
> class Nefarious : S!(int, string)
> {
>   override int get(int a)
>   {
>     // Whoops, I used the GC
>     return new char[a].length;
>   }
> }
>
> The compiler can't prove that a variable of type S!(int, string) will not be of type Nefarious, which uses the GC, so it can't infer @nogc for S.get.
>
> However, if you make the function final, then the compiler can infer it to be pure nothrow @nogc @safe.
>
> Or if you use a struct instead of a class, structs don't do inheritance, so the compiler can infer attributes without worrying about nefarious inheritance.
>
>> But everything is ok if you uncomment parentheses after get.
>
> Templated functions are implicitly final.

Thanks for explanation, got it.

My case is actually

interface I(K,V)
{
    int get()(K);
}
class S(K,V) : I!(K, V)
{
    int v;
    int get()(K a)
    {
        return v;
    }
}
void main() nothrow
{
    S!(int, string) s = new S!(int, string);
    s.get(1);
}

My goal is to allow compiler to infer all properties of s.get without adding nothrow/nogc anywhere.  And these templated functions is only way it works for me. Is it ok? Or there is better solution?

Thanks!
November 30, 2018
On Fri, 30 Nov 2018 22:10:11 +0000, ikod wrote:
> Thanks for explanation, got it.
> 
> My case is actually
> 
> interface I(K,V)
> {
>      int get()(K);
> }

Interface functions must be abstract. Templated functions are implicitly final. Final things can't be abstract.

If there's something about types K and V that determine whether you should be able to use the GC or not, you'll have to encode that explicitly.