digitalmars.D - a design flaw in DIP1035, its remedy, and the implication for system
- Zach Tollen (77/77) Apr 13 2022 [DIP1035](https://github.com/dlang/DIPs/blob/72f41cffe68ff1f2d4c033b5728...
- Dennis (6/12) Apr 13 2022 extern variables should be rare in D, they're usually the result
- Nick Treleaven (7/17) Apr 13 2022 That would encourage people to use trusted even when it is
- Paul Backus (23/37) Apr 13 2022 Worth noting that this is not the *only* solution. One can also
[DIP1035](https://github.com/dlang/DIPs/blob/72f41cffe68ff1f2d4c033b5728ef37e28246 dd/DIPs/DIP1035.md) has a design flaw in its current form. Let us assume it has been implemented as described. Let us assume a naive user has an `extern` variable, which we will use as the example for the whole illustration. ```d extern int* x; ``` According to the DIP, since this variable could possibly hold an unsafe value, it is immediately promoted to a ` system` variable. Our naive user then tries to write to it in a ` safe` setting. ```d void main() safe { *x = 10; // error: system variable x may not be accessed in safe code } ``` Even if the user reassigns the pointer to a safe value, thus making the actual value safe, the variable is forevermore interpreted by the compiler as ` system`. ```d void fn() safe { () trusted { x = new int; }(); // we're safe now, right? *x = 10; // error: system variable x may not be accessed in safe code } ``` Promoting `x` to a ` system` variable has had the effect of basically making it unusable. Eventually, the user realizes that the only way solve the problem is to declare the original variable ` trusted`. ```d trusted extern int* x; ``` That's what he wanted the whole time. Indeed, that's what *everyone* who inadvertently initializes a variable to an unsafe value wants. No one ever wants a variable to become permanently unwieldy, just because it may have begun in an unsafe state. Now let's imagine that a compiler writer realizes this is a common pattern, and wants to improve the error message. So he makes a special flag to mark unsafely initialized variables internally, to distinguish them from explicitly ` system` ones. The semantics don't change; it's just a clearer error. ```d extern int* x; system int y; void fn() safe { x = new int; // error: unsafely initialized variable `x` cannot be accessed // in safe code - try marking its declaration trusted? y++; // error: system variable y may not be accessed in safe code } ``` We're almost there. The distinction between unsafely-initialized, and explicitly-` system` variables is starting to be felt. Finally, it dawns on the compiler writers that they've been abusing the user the whole time. They already knew, from the moment the variable was declared, that there was going to be a problem. They had an unsafely-initialized variable. They should have forced the user then and there to say whether it was ` trusted` or ` system`. ```d extern int* x; // error: unsafely initialized variables must be explicitly // annotated with ` trusted`, or ` system` ``` Boom. We have the real solution to unsafely-initialized global variables. So that fixes the design flaw. And it leaves us with a new interpretation of ` system` variables. Key points: - Implicitly inferring a variable to be ` system` is little more than a form of torture. The user *always* wanted ` trusted`. - Actual ` system` variables are rare, and never accidental. Being intentionally hard to use, they should only be given to people who ask for them directly.
Apr 13 2022
On Wednesday, 13 April 2022 at 12:14:53 UTC, Zach Tollen wrote:Finally, it dawns on the compiler writers that they've been abusing the user the whole time. They already knew, from the moment the variable was declared, that there was going to be a problem. They had an unsafely-initialized variable. They should have forced the user then and there to say whether it was ` trusted` or ` system`.extern variables should be rare in D, they're usually the result of translated C code. Translated C code isn't ` safe`, so has no problem accessing ` system` variables. Introducing forced attributes seems more annoying to me than inferred ` system` variables.
Apr 13 2022
On Wednesday, 13 April 2022 at 12:14:53 UTC, Zach Tollen wrote:```d extern int* x; system int y; void fn() safe { x = new int; // error: unsafely initialized variable `x` cannot be accessed // in safe code - try marking its declaration trusted?That would encourage people to use trusted even when it is actually an unsafe value. ...They should have forced the user then and there to say whether it was ` trusted` or ` system`.That would break existing code. system is a good default for extern (C) unsafe globals and functions, even if we make D safe by default.
Apr 13 2022
On Wednesday, 13 April 2022 at 12:14:53 UTC, Zach Tollen wrote:```d void fn() safe { () trusted { x = new int; }(); // we're safe now, right? *x = 10; // error: system variable x may not be accessed in safe code } ``` Promoting `x` to a ` system` variable has had the effect of basically making it unusable. Eventually, the user realizes that the only way solve the problem is to declare the original variable ` trusted`. ```d trusted extern int* x; ```Worth noting that this is not the *only* solution. One can also provide a ` trusted` interface using getter and setter functions; e.g., ```d extern int* x; // inferred system system bool initializedX = false; trusted void safeX(int* value) { x = value; initializedX = true; } trusted int* safeX() in (initializedX) { return x; } safe void fn() { safeX = new int; *safeX = 10; } ```
Apr 13 2022