Thread overview
Error "Outer Function Context is Needed" when class declared in unittest
Jan 05, 2023
Vijay Nayar
Jan 05, 2023
Adam D Ruppe
Jan 05, 2023
Vijay Nayar
Jan 05, 2023
Adam D Ruppe
Jan 05, 2023
Vijay Nayar
Jan 25
Kagamin
January 05, 2023

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?

January 05, 2023
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.
January 05, 2023
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?
January 05, 2023
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.


January 05, 2023
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!
January 21
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.
January 25
Looks like the context is currently passed for nested functions, not for nested classes.