Default attribute blocks/modules
Quirin Schroll
qs.il.paperinik at gmail.com
Thu Jul 4 16:04:36 UTC 2024
I already outlined it
[here](https://forum.dlang.org/post/efluuipmcdcsknlwqyci@forum.dlang.org). In present-day D, for any module, the default function attributes are `@system`, `throw`, [`@gc`](https://forum.dlang.org/thread/ojjsplombwzzjhjymrnw@forum.dlang.org), and impure. The idea is to allow changing the default for a module or in a block of declarations.
**Note:** Changing the default only affects non-inferred types
and declarations. Attribute inference for templates or `auto` and
nested functions are unaffected.
### Syntax
For changing the module default, use this:
```d
default @safe
module m;
```
For changing the block default, use this:
```d
default @safe:
// or
default @safe { … }
```
### Semantics
There is a subtle difference between module defaults and block
defaults:
Block defaults only affect declarations. Module defaults apply to
anything lexically in the module which can carry the attribute,
in particular, function pointer and delegate types.
That means that in a `default @safe` module, every function
declaration and every function pointer or delegate type lexically
in that module will be `@safe` unless marked `@system`. Blocks
can override the module default for declarations.
```d
default @safe
module m;
// Note: module defaults apply to functions and function
pointer/delegate types
// spelled out in the module: `callback` is implicitly `@safe`
int f(int function() callback) => callback();
static assert(is(typeof(&f) == int function(int function() @safe)
@safe));
// Note: `g` is not inferred; default (@safe) applies
void g()
{
int* p;
int x;
p = &x; // Error: address of variable `x` assigned to `p`
with longer lifetime
}
// Note: `h` is inferred @system; defaults are irrelevant
auto h()
{
int* p;
int x;
p = &x; // Okay, makes `h` a `@system` function
}
static assert(!is(typeof(&h) : void function() @safe));
```
Default blocks are similar to normal blocks, except they don’t
directly affect inference:
```d
module m;
default @safe:
// Note: block defaults only apply to declarations (e.g.
functions),
// but not function pointer/delegate types that are parameters or
return types.
// For `callback`, the module default applies, which is unset,
i.e. `@system`.
int f1(int function() callback); // => callback(); // Error
static assert(is(typeof(&f1) == int function(int function()
@system) @safe));
// An alias is a declaration, so the block default applies,
// and `FP` is `int function() @safe`.
alias FP = int function();
int f2(FP callback) => callback();
static assert(is(typeof(&f2) == int function(int function()
@safe) @safe));
// Note: `g` is not inferred; default (@safe) applies
// (same as above)
void g()
{
int* p;
int x;
p = &x; // Error: address of variable `x` assigned to `p`
with longer lifetime
}
// Note: `h` is inferred @system; defaults are irrelevant
// (same as above)
auto h()
{
int* p;
int x;
p = &x; // Okay, makes `h` a `@system` function
}
static assert(!is(typeof(&h) : void function() @safe));
```
The behavior of block defaults is consistent with attribute
blocks, which likewise affect only declarations, but not function
parameters or return types of function pointer or delegate types.
```d
// Note: Not a default, this is current-day D semantics.
@safe:
int f1(int function() callback);
static assert(is(typeof(&f1) == int function(int function()
@system) @safe));
alias FP = int function();
int f2(FP callback) => callback();
static assert(is(typeof(&f2) == int function(int function()
@safe) @safe));
```
### Grammar
```diff
ModuleDeclaration:
- ModuleAttributes? module ModuleFullyQualifiedName ;
+ ModuleAttributes? ModuleDefaultAttributes? module
ModuleFullyQualifiedName ;
+
+ ModuleDefaultAttributes:
+ default DefaultAttributeList
ModuleAttributes:
ModuleAttribute
ModuleAttribute ModuleAttributes
ModuleAttribute:
DeprecatedAttribute
UserDefinedAttribute
```
```diff
DeclDef:
AttributeSpecifier
+ DefaultAttributeSpecifier
…
+ DefaultAttributeSpecifier:
+ default DefaultAttributeList :
+ default DefaultAttributeList { DeclDefs? }
+
+ DefaultAttributeList:
+ DefaultAttribute DefaultAttributeList?
+
+ DefaultAttribute:
+ pure
+ nothrow
+ @ safe
+ @ nogc
Attribute:
…
const
+ default ( DefaultAttributeList )
final
…
```
This grammar allows `pure default @safe @nothrow static`
(followed by `{…}` or `:`) and by maximum munch, would mean that
`default` applies to `@safe` and `@nothrow`, but not `static`.
The language should reject that and require
`pure default(@safe nothrow) static`, or else
`pure{ default @safe nothrow: static: … }` or
`pure: default @safe nothrow: static:`, respectively.
That is, `default` without parentheses is only allowed with
`default` in front and no trailing that can’t be a default.
The downside of `default(@safe)` is that it somewhat suggests
it’s an attribute of its own, but it would only be allowed for
blocks and module declarations, but not on declarations directly,
where it makes little sense:
* On non-inferred declarations, `default(@safe)` is `@safe`.
* On inferred declarations, `default(@safe)` means nothing.
More information about the dip.ideas
mailing list