www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Type Inference in safe unittests

reply jmh530 <john.michael.hall gmail.com> writes:
I'm not sure if this is a bug or not.

I was playing around with printing out some member types with 
unittests and I was noticing some strange results when they were 
in  safe unittests rather than normal unittests. The first one 
prints out what I would expect, but the  safe unittest puts  safe 
 nogc nothrow and pure on them, as if it is re-writing the struct 
as a template (or maybe just the functions as templates, I don't 
know).

private enum isPrivate(T, string member) = !__traits(compiles, 
__traits(getMember, T, member));

void printMemberTypes(alias T)()
{
     foreach(memberName; __traits(allMembers, T))
     {
         static if(!isPrivate!(T, memberName)) {
             writeln(typeid(typeof(__traits(getMember, T, 
memberName))));
         }
     }
}

unittest
{
     struct Foo {
         int foo(int i, string s)  safe { return 0; }
         double foo2(string s)  safe { return 0; }
     }

     printMemberTypes!(Foo);
}

 safe unittest
{
     struct Foo {
         int foo(int i, string s)  safe { return 0; }
         double foo2(string s)  safe { return 0; }
     }

     printMemberTypes!(Foo);
}
Aug 22
parent reply Jonathan M Davis via Digitalmars-d-learn writes:
On Tuesday, August 22, 2017 16:11:11 jmh530 via Digitalmars-d-learn wrote:
 I'm not sure if this is a bug or not.

 I was playing around with printing out some member types with
 unittests and I was noticing some strange results when they were
 in  safe unittests rather than normal unittests. The first one
 prints out what I would expect, but the  safe unittest puts  safe
  nogc nothrow and pure on them, as if it is re-writing the struct
 as a template (or maybe just the functions as templates, I don't
 know).

 private enum isPrivate(T, string member) = !__traits(compiles,
 __traits(getMember, T, member));

 void printMemberTypes(alias T)()
 {
      foreach(memberName; __traits(allMembers, T))
      {
          static if(!isPrivate!(T, memberName)) {
              writeln(typeid(typeof(__traits(getMember, T,
 memberName))));
          }
      }
 }

 unittest
 {
      struct Foo {
          int foo(int i, string s)  safe { return 0; }
          double foo2(string s)  safe { return 0; }
      }

      printMemberTypes!(Foo);
 }

  safe unittest
 {
      struct Foo {
          int foo(int i, string s)  safe { return 0; }
          double foo2(string s)  safe { return 0; }
      }

      printMemberTypes!(Foo);
 }
Well, templates aren't the only case where we have attribute inference anymore (e.g. auto return functions have it), and I'm pretty sure that there have been several requests for fixing issues regards to local declarations so that they have inference (in particular, I think that there have been complaints about marking a function as pure having issues with internal declarations then not being treated as pure even though they could be). And for better or worse, the trend has been towards adding inference in cases where it's guaranteed that the code will always be available and will be available to any code using that code - and in the case of a declaration inside of a function like that, it's guaranted that anything referencing it is going to have access to the code. So, it doesn't surprise me at all if attribute inference has been added to local declarations like this. If you want to guarantee that no inference is happening, then you'll probably have to declare it directly in the module where it can't be infered due to the fact that a .di file could redeclare it without any function bodies. - Jonathan M Davis
Aug 22
parent reply jmh530 <john.michael.hall gmail.com> writes:
On Tuesday, 22 August 2017 at 16:27:05 UTC, Jonathan M Davis 
wrote:
 Well, templates aren't the only case where we have attribute 
 inference anymore (e.g. auto return functions have it), and I'm 
 pretty sure that there have been several requests for fixing 
 issues regards to local declarations so that they have 
 inference (in particular, I think that there have been 
 complaints about marking a function as pure having issues with 
 internal declarations then not being treated as pure even 
 though they could be). And for better or worse, the trend has 
 been towards adding inference in cases where it's guaranteed 
 that the code will always be available and will be available to 
 any code using that code - and in the case of a declaration 
 inside of a function like that, it's guaranted that anything 
 referencing it is going to have access to the code. So, it 
 doesn't surprise me at all if attribute inference has been 
 added to local declarations like this. If you want to guarantee 
 that no inference is happening, then you'll probably have to 
 declare it directly in the module where it can't be infered due 
 to the fact that a .di file could redeclare it without any 
 function bodies.

 - Jonathan M Davis
Yeah, this happens with safe main also (below), but not for more regular local blocks. Anyway, I found it very confusing as that's not how I assumed safe applied to unittests or main worked. safe void main() { struct Foo { int foo(int i, string s) safe { return 0; } double foo2(string s) safe { return 0; } } printMemberTypes!(Foo); }
Aug 22
parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 8/22/17 2:19 PM, jmh530 wrote:

 Yeah, this happens with  safe main also (below), but not for more 
 regular local blocks. Anyway, I found it very confusing as that's not 
 how I assumed  safe applied to unittests or main worked.
 
  safe void main()
 {
      struct Foo {
          int foo(int i, string s)  safe { return 0; }
          double foo2(string s)  safe { return 0; }
      }
      printMemberTypes!(Foo);
 }
The surprising part to me is that non- safe main doesn't infer anything. Is that true? -Steve
Aug 22
parent reply jmh530 <john.michael.hall gmail.com> writes:
On Tuesday, 22 August 2017 at 18:25:31 UTC, Steven Schveighoffer 
wrote:
 
  safe void main()
 {
      struct Foo {
          int foo(int i, string s)  safe { return 0; }
          double foo2(string s)  safe { return 0; }
      }
      printMemberTypes!(Foo);
 }
The surprising part to me is that non- safe main doesn't infer anything. Is that true? -Steve
They aren't auto functions or templates, just normal member functions of a struct in main. I thought inferring function attributes was only for auto functions and templates. I never thought it would for member functions of structs in main. But yeah, I'm pretty those functions are safe and not safe nogc nothrow pure.
Aug 22
parent Steven Schveighoffer <schveiguy yahoo.com> writes:
On 8/22/17 5:44 PM, jmh530 wrote:
 On Tuesday, 22 August 2017 at 18:25:31 UTC, Steven Schveighoffer wrote:
  safe void main()
 {
      struct Foo {
          int foo(int i, string s)  safe { return 0; }
          double foo2(string s)  safe { return 0; }
      }
      printMemberTypes!(Foo);
 }
The surprising part to me is that non- safe main doesn't infer anything. Is that true?
They aren't auto functions or templates, just normal member functions of a struct in main. I thought inferring function attributes was only for auto functions and templates. I never thought it would for member functions of structs in main. But yeah, I'm pretty those functions are safe and not safe nogc nothrow pure.
Logically, any internal struct's code is available for examination wherever it could possibly be used. If it's returned, then the function return must be auto (and the function code available). If it's passed as a template parameter, it is available. Inference could happen on any internal function anywhere, not just inside internal structs. I'm not sure why the marking of the enclosing function should make a difference, either they all infer or they all don't infer. To me, this seems like a bug (the inconsistency). -Steve
Aug 22