Using optional 1.3.0.
The following code works with no problems.
import optional;
class X {
int foo() const pure nothrow @safe @nogc {
return 5;
}
}
class Y {
Optional!X x;
int bar() pure @safe {
return x.oc.foo.frontOr(3);
}
}
This compiles fine.
The problem arises when we want to annotate bar() not only as pure @safe, but as const pure nothrow @safe @nogc in the same way as we did with foo(). If we add all those annotations to bar(), we get these errors:
- We can't call
.oc.foo on a const Optional!X.
- We can't call
frontOr within nothrow or @nogc, or on a const OptionalChain!int. The exact static assert is: "Unable to call frontOr on type const(OptionalChain!int). It has to either be an input range, a Nullable!T, or an Optional!T".
In theory, i.e., without looking at the implementation of the optional library, these extra annotations comply with D's type system:
- We don't modify
x by maybe calling the const method foo() on (presumably) a const(X) through the const(Optional!X).
- We don't modify
x.oc.foo by calling .frontOr(3) because we merely have to examine the range x.oc.foo for emptiness and possibly take its front element. We never have to advance this range. Only if we were to call .popFront on a range, the range struct would have to be mutable, but .frontOr(3) should never call .popFront on any range.
- We provide a default value for
frontOr without calling anything that throws, therefore frontOr will never throw.
- Since
frontOr never throws, it will never allocate with the GC.
We can argue that this report should be split into two separate issues: 1. oc should work on a const(Optional!X) and 2. frontOr should infer that it will neither modify, throw, nor allocate, given its usage with a default value like in our example. But I filed them together because they're so closely related, and because above example code is more meaningful when it shows both.
Do you see conceptual difficulties in allowing x.oc.foo.frontOr(3) from within a method annotated const pure nothrow @safe @nogc?
For now, I've rewritten the example as:
int bar() const pure nothrow @safe @nogc {
return x.empty ? 3 : x.front.foo;
}
Using optional 1.3.0.
The following code works with no problems.
This compiles fine.
The problem arises when we want to annotate
bar()not only aspure @safe, but asconst pure nothrow @safe @nogcin the same way as we did withfoo(). If we add all those annotations tobar(), we get these errors:.oc.fooon aconst Optional!X.frontOrwithinnothrowor@nogc, or on aconst OptionalChain!int. The exact static assert is: "Unable to call frontOr on type const(OptionalChain!int). It has to either be an input range, a Nullable!T, or an Optional!T".In theory, i.e., without looking at the implementation of the optional library, these extra annotations comply with D's type system:
xby maybe calling theconstmethodfoo()on (presumably) aconst(X)through theconst(Optional!X).x.oc.fooby calling.frontOr(3)because we merely have to examine the rangex.oc.foofor emptiness and possibly take its front element. We never have to advance this range. Only if we were to call.popFronton a range, the range struct would have to be mutable, but.frontOr(3)should never call.popFronton any range.frontOrwithout calling anything that throws, thereforefrontOrwill never throw.frontOrnever throws, it will never allocate with the GC.We can argue that this report should be split into two separate issues: 1.
ocshould work on aconst(Optional!X)and 2.frontOrshould infer that it will neither modify, throw, nor allocate, given its usage with a default value like in our example. But I filed them together because they're so closely related, and because above example code is more meaningful when it shows both.Do you see conceptual difficulties in allowing
x.oc.foo.frontOr(3)from within a method annotatedconst pure nothrow @safe @nogc?For now, I've rewritten the example as: