www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Error "Outer Function Context is Needed" when class declared in

reply Vijay Nayar <madric gmail.com> writes:
I've run into an unexpected problem that only seems to happen in 
unittests, but not outside of them. Consider the following 
example:

```
unittest {
   class Ab {
     int a;
     string b;

     static class Builder {
       int _a;
       string _b;
       Builder a(int a) {
         _a = a;
         return this;
       }
       Builder b(string b) {
         _b = b;
         return this;
       }
       Ab build() {
         Ab t = new Ab();
         t.a = _a;
         t.b = _b;
         return t;
       }
     }
   }

   Ab ab = new Ab.Builder()
       .a(1)
       .b("ham")
       .build();
   assert(ab.a == 1);
   assert(ab.b == "ham");
}
```

This fails to compile with the following error:
```
Generating test runner configuration 'builder-test-library' for 
'library' (library).
     Starting Performing "unittest" build using /usr/bin/dmd for 
x86_64.
     Building builder ~master: building configuration 
[builder-test-library]
source/builder.d(58,16): Error: outer function context of 
`builder.__unittest_L41_C1` is needed to `new` nested class 
`builder.__unittest_L41_C1.Ab`
Error /usr/bin/dmd failed with exit code 1.
```

However, if I move the class definition outside of the unittest 
block, then everything works fine:
```
class Ab {
   int a;
   string b;

   static class Builder {
     int _a;
     string _b;
     Builder a(int a) {
       _a = a;
       return this;
     }
     Builder b(string b) {
       _b = b;
       return this;
     }
     Ab build() {
       Ab t = new Ab();
       t.a = _a;
       t.b = _b;
       return t;
     }
   }
}

unittest {
   Ab ab = new Ab.Builder()
       .a(1)
       .b("ham")
       .build();
   assert(ab.a == 1);
   assert(ab.b == "ham");
}
```

```
              Generating test runner configuration 
'builder-test-library' for 'library' (library).
     Starting Performing "unittest" build using /usr/bin/dmd for 
x86_64.
     Building builder ~master: building configuration 
[builder-test-library]
      Linking builder-test-library
      Running builder-test-library
2 modules passed unittests
```

Why is this error only found when declaring a class in the 
unittest?
Jan 05 2023
parent reply Adam D Ruppe <destructionator gmail.com> writes:
On Thursday, 5 January 2023 at 13:27:23 UTC, Vijay Nayar wrote:
 Why is this error only found when declaring a class in the 
 unittest?
A unittest is just a special function, it can run code and have local variables. classes and structs declared inside it have access to those local contexts, which it calls the outer function context. Make the outer class `static` too to lift it out of this and your error should go away.
Jan 05 2023
next sibling parent reply Vijay Nayar <madric gmail.com> writes:
On Thursday, 5 January 2023 at 13:47:24 UTC, Adam D Ruppe wrote:
 On Thursday, 5 January 2023 at 13:27:23 UTC, Vijay Nayar wrote:
 Why is this error only found when declaring a class in the 
 unittest?
A unittest is just a special function, it can run code and have local variables. classes and structs declared inside it have access to those local contexts, which it calls the outer function context. Make the outer class `static` too to lift it out of this and your error should go away.
That's very informative, I didn't realize that `unittest` is actually a function. It raises another question one step deeper, what does it mean to define a non-static class within a function? Does that class inherit the scope of the function it is inside, similar to how an inner class does with an outer class?
Jan 05 2023
parent reply Adam D Ruppe <destructionator gmail.com> writes:
On Thursday, 5 January 2023 at 16:38:49 UTC, Vijay Nayar wrote:
 Does that class inherit the scope of the function it is inside, 
 similar to how an inner class does with an outer class?
yup. They can see the local variables from the function.
Jan 05 2023
parent Vijay Nayar <madric gmail.com> writes:
On Thursday, 5 January 2023 at 16:41:32 UTC, Adam D Ruppe wrote:
 On Thursday, 5 January 2023 at 16:38:49 UTC, Vijay Nayar wrote:
 Does that class inherit the scope of the function it is 
 inside, similar to how an inner class does with an outer class?
yup. They can see the local variables from the function.
Glad to learn that. Having worked many years in the Java world, where basically "class" and "scope" are nearly synonymous, I just assumed that classes could only get the scope of other classes, it never occurred to me that it could get a scope from a function. Thanks for the explanation!
Jan 05 2023
prev sibling parent reply Jim Balter <Jim Balter.name> writes:
On Thursday, 5 January 2023 at 13:47:24 UTC, Adam D Ruppe wrote:
 On Thursday, 5 January 2023 at 13:27:23 UTC, Vijay Nayar wrote:
 Why is this error only found when declaring a class in the 
 unittest?
A unittest is just a special function, it can run code and have local variables. classes and structs declared inside it have access to those local contexts, which it calls the outer function context. Make the outer class `static` too to lift it out of this and your error should go away.
Why doesn't the compiler just create the instance with the correct context pointer? And if it won't, how does one provide it? I have a template function which creates new instances of a type that is an argument to the template, and it fails miserably when the type is a nested class. How do I create such instances? Dlang is supposed to be "turtles all the way down", but I keep finding that to be far from reality.
Jan 21
parent Kagamin <spam here.lot> writes:
Looks like the context is currently passed for nested functions, 
not for nested classes.
Jan 25