Recap
“D primary type syntax” is this recurrence:
Type
→ BasicType
BasicType
→ TypeCtor
? (
Type
)
(The novelty is that TypeCtor
is optional.)
This allows to add these additional grammar rules:
Type
→ ref
TypeCtors
? BasicType
CallableSuffix TypeSuffixes
?
CallableSuffix → delegate
Parameters
MemberFunctionAttributes
?
CallableSuffix → function
Parameters
FunctionAttributes
?
The goal was to make e.g. (ref const int function() @safe)[]
parse as a type. As of today, this is possible.
I borrowed the nomenclature of “primary types” from “primary expressions”:
- Primary expressions are something like fundamental or parenthesized expressions.
- Primary types (in D’s grammar: basic types) are something like fundamental types or parenthesized types.
Basic Intro
Parameters
// `a` takes its parameter by value; the parameter returns by reference.
// `b` takes its parameter by reference; the parameter returns by value.
void a( ref (int function()) ) { }
void b((ref int function()) ) { }
// Note: The parameter storage class has priority
// over the value category of the parameter’s return type.
// `c` is `a` without clarifiying parentheses.
void c( ref int function() ) { }
static assert(!is( typeof(&a) == typeof(&b) ));
static assert( is( typeof(&a) == typeof(&c) ));
Return types
// `x` returns by reference; the returned function ptr returns an `int` by value.
// `y` returns by vale; the returned function ptr returns an `int` by reference.
ref (int function()) x() { static typeof(return) fp = null; return fp; }
(ref int function()) y() => null;
// Note: The value category of the declared function has priority
// over the value category of the return type.
// `z` is `x` without clarifiying parentheses.
ref int function() z() => x();
static assert(!is( typeof(&x) == typeof(&y) ));
static assert( is( typeof(&x) == typeof(&z) ));
Satisfying the Inner Monk
Especially pleasing is the congruence between function declaration and function type:
static int i = 0;
ref int funcName() @safe => i;
(ref int delegate() @safe) fptr = &funcName;
The parentheses are necessary. Not only is that an artifact of the implementation, but also the way I think it’s clearer. If we ever add ref
local variables, they would change meaning.
Error Messages vs. Source Files
I also changed the pretty-print so that ref
appears at the beginning of a function pointer or delegate type. This is where it should be according to my eye and the D grammar, but pretty-print gave us ref
among pure
and @safe
. This meant that if you copied the type from the error message to your source file, it would not parse. Now, it will parse. (It might still not compile for other reasons, such as visibility.)
With pragma(msg)
:
pragma(msg, typeof( function ref int { static x = 0; return x; } ));
// Old: int function() nothrow @nogc ref @safe
// New: (ref int function() nothrow @nogc @safe)
With .stringof
:
// Old:
alias FP = ref int function();
static assert(FP.stringof == "int function() ref");
// New:
static assert((ref int function()).stringof == "(ref int function())" );
// No alias required!
The parentheses on the right are somewhat intentional. I didn’t even try to make the pretty-print use parentheses only when necessary because it costs time and is not the main issue. As of now, it mindlessly inserts them. The pretty-print can be improved later.
All the examples are tested and work as intended on my local machine. The code is here. (I know the branch has a stupid name.)
I hope you like it.