www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - D Primary Type Syntax is Essentially Implemented!

reply Quirin Schroll <qs.il.paperinik gmail.com> writes:


“D primary type syntax” is this recurrence:

 [`Type`](https://dlang.org/spec/type.html#Type) → 
[`BasicType`](https://dlang.org/spec/type.html#BasicType)
 [`BasicType`](https://dlang.org/spec/type.html#BasicType) → 
[`TypeCtor`](https://dlang.org/spec/type.html#TypeCtor)? 
**`(`**[`Type`](https://dlang.org/spec/type.html#Type)**`)`** 
(The novelty is that `TypeCtor` is optional.)

This allows to add these additional grammar rules:

 [`Type`](https://dlang.org/spec/type.html#Type) → **`ref`** 
[`TypeCtors`](https://dlang.org/spec/type.html#TypeCtor)? 
[`BasicType`](https://dlang.org/spec/type.html#BasicType) 
*CallableSuffix* 
[`TypeSuffixes`](https://dlang.org/spec/type.html#TypeSuffix)?
 *CallableSuffix* → **`delegate`** 
[`Parameters`](https://dlang.org/spec/function.html#Parameters) 
[`MemberFunctionAttributes`](https://dlang.org/spec/function.html#MemberFunctionAttributes)?
 *CallableSuffix* → **`function`** 
[`Parameters`](https://dlang.org/spec/function.html#Parameters) 
[`FunctionAttributes`](https://dlang.org/spec/function.html#FunctionAttributes)?

The goal was to make e.g. `(ref const int function()  safe)[]` 
parse as a type. As of today, [this is 
possible](https://github.com/dlang/dmd/pull/15245).

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.



```d
// `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) ));
```

```d
// `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) ));
```



Especially pleasing is the congruence between function 
declaration and function type:
```d
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.



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)`:
```d
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`:
```d
// 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](https://github.com/Bolpat/dmd/tree/patch-3). (I know the 
branch has a stupid name.)

I hope you like it.
May 23 2023
next sibling parent reply "Richard (Rikki) Andrew Cattermole" <richard cattermole.co.nz> writes:
Good work cleaning this up.

On 24/05/2023 10:18 AM, Quirin Schroll wrote:
 |// `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()) ) { }|
Are you sure that this is correct? I read this as `a` takes its parameter by reference; the argument returns by value. If this isn't what you intended its a little worrying because this is going to cause problems.
May 23 2023
next sibling parent reply Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On Tuesday, 23 May 2023 at 22:39:44 UTC, Richard (Rikki) Andrew 
Cattermole wrote:
 Good work cleaning this up.

 On 24/05/2023 10:18 AM, Quirin Schroll wrote:
 |// `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()) ) { }|
Are you sure that this is correct? I read this as `a` takes its parameter by reference; the argument returns by value. If this isn't what you intended its a little worrying because this is going to cause problems.
Same, this is completely counter-intuitive to me.
May 23 2023
parent reply Olivier Pisano <olivier.pisano laposte.net> writes:
On Wednesday, 24 May 2023 at 03:48:38 UTC, Andrej Mitrovic wrote:
 On Tuesday, 23 May 2023 at 22:39:44 UTC, Richard (Rikki) Andrew 
 Cattermole wrote:
 Good work cleaning this up.

 On 24/05/2023 10:18 AM, Quirin Schroll wrote:
 |// `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()) ) { }|
Are you sure that this is correct? I read this as `a` takes its parameter by reference; the argument returns by value. If this isn't what you intended its a little worrying because this is going to cause problems.
Same, this is completely counter-intuitive to me.
Same here.
May 23 2023
parent Quirin Schroll <qs.il.paperinik gmail.com> writes:
On Wednesday, 24 May 2023 at 05:15:27 UTC, Olivier Pisano wrote:
 On Wednesday, 24 May 2023 at 03:48:38 UTC, Andrej Mitrovic 
 wrote:
 On Tuesday, 23 May 2023 at 22:39:44 UTC, Richard (Rikki) 
 Andrew Cattermole wrote:
 Good work cleaning this up.

 On 24/05/2023 10:18 AM, Quirin Schroll wrote:
 |// `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()) ) { }|
Are you sure that this is correct? I read this as `a` takes its parameter by reference; the argument returns by value. If this isn't what you intended its a little worrying because this is going to cause problems.
Same, this is completely counter-intuitive to me.
Same here.
It's only the comments for `a` and `b` that are reversed. Still `a` and `c` are the same, which should have cleared things up. I'm sorry for the confusion. This is so embarrassing. The implementation is sane in your and the other commenters opinion. Unfortunately, the compiler has no AI to check if comments make sense. All the code is tested.
May 24 2023
prev sibling parent Quirin Schroll <qs.il.paperinik gmail.com> writes:
On Tuesday, 23 May 2023 at 22:39:44 UTC, Richard (Rikki) Andrew 
Cattermole wrote:
 Good work cleaning this up.

 On 24/05/2023 10:18 AM, Quirin Schroll wrote:
 |// `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()) ) { }|
Are you sure that this is correct? I read this as `a` takes its parameter by reference; the argument returns by value. If this isn't what you intended its a little worrying because this is going to cause problems.
You're 100% right, I mixed it up. It's exactly the other way around.
May 24 2023
prev sibling next sibling parent reply Basile B. <b2.temp gmx.com> writes:
On Tuesday, 23 May 2023 at 22:18:35 UTC, Quirin Schroll wrote:
 [...]
 I hope you like it.
I like this solution more than the [alterantivey proposed](https://github.com/dlang/dmd/pull/15245). Both work but yours try to be a bit more faithful to the language. The alternative can look more seducing because it's implemented by someone who knows his shits, so yeah it looks nicer, the checks are greens but it is based on a special case. I'm very "pro" your way, problem is that currently the implementation is not perfect (red checks).
May 23 2023
parent Quirin Schroll <qs.il.paperinik gmail.com> writes:
On Wednesday, 24 May 2023 at 00:24:38 UTC, Basile B. wrote:
 On Tuesday, 23 May 2023 at 22:18:35 UTC, Quirin Schroll wrote:
 [...]
 I hope you like it.
I like this solution more than the [alterantivey proposed](https://github.com/dlang/dmd/pull/15245).
Did you link the right PR? It seems you linked my PR calling it the alternative to the post.
 Both work but yours try to be a bit more faithful to the 
 language. The alternative can look more seducing because it's 
 implemented by someone who knows his shits, so yeah it looks 
 nicer, the checks are greens but it is based on a special case.
This is why I took every minute of time I had to draft an implementation. This was stupidly hard! The compiler (parser at least) isn’t exactly well-documented. I did that because there’s a tendency of quick and dirty solutions to stick. *Nichts ist zählebiger als ein Provisorium.* (German proverb; “Nothing’s more tenacious than a makeshift.”)
 I'm very "pro" your way, problem is that currently the 
 implementation is not perfect
 (red checks).
This was my first DMD PR that could be called a meaningful attempt. I definitely need help by “someone who knows his shits.” I tried to fix things so that the problems would be solved. --- If you (Basile B. or anyone reading this) would like to help, here’s a summary of the failed tests: 1. *CyberShadow/DAutoTest* checks that all the examples on the dlang.org website work. The one that fails is an intended breaking: `static assert(__traits(isSame, (e), 3))` (where `e` is an `enum` with value `3`) used to work but now fails. 2. *ci/circleci: build* has errors because “template instance `core.stdc.config._Complex!float` cannot resolve forward reference”. 3. I worked on the `fail_compilation`, but they still fail. (This accounts for a lot of failed tests, maybe even all except 1. and 2.) Comments: 1. This due to how `isSame` works: In essence, the reason is that, with the new grammar, `(e)` is not (necessarily) a primary expression anymore, therefore just putting parentheses around it is unsuited to force the evaluation as an expression; `(e)` becomes exactly equivalent to `e`, and `e` is a symbol, and `isSame` is always false if you compare a symbol with a literal. There’s two ways to tackle this: Change the example (e.g. use a cast) or change `isSame`. I’d prefer the former, but I don’t care much. 2. I don’t know what this means and I cannot reproduce this on my machine. The DMD files I changed I only touch the parser, but for all that I can tell this is a semantic problem. It even says that happened when instantiating the template, which has nothing to do with parsing. 3. The logs do not show the discrepancies of the expected and actual error messages, so it’s hard to know what is wrong. Maybe someone here knows how to get them.
May 24 2023
prev sibling parent reply Nick Treleaven <nick geany.org> writes:
On Tuesday, 23 May 2023 at 22:18:35 UTC, Quirin Schroll wrote:

 ```d
 // `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) ));
 ```

 ```d
 // `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) ));
 ```
The solution seems to be more than what is required to solve the `ref` problem. It also means we introduce multiple ways of spelling the same type. And as you found, it can break code: https://forum.dlang.org/post/eguzeiayudwqcvaencdf forum.dlang.org Instead, why not implement just the subset of this proposal that is actually needed - add this rule: ```md BasicType: `(` `ref` Type `)` ``` This rule doesn't allow `a` or `x`, yet allows `b` and `y`, and works for variable declarations. It doesn't conflict with any expression AFAIK. And if the full proposal is wanted later, the subset can be extended without breakage.
Dec 21 2023
parent Nick Treleaven <nick geany.org> writes:
On Thursday, 21 December 2023 at 15:34:11 UTC, Nick Treleaven 
wrote:
 ```
 BasicType:
     `(` `ref` Type `)`
 ```
Sorry, that allows invalid types. Should be: ``` BasicType: ( ref Type delegate Parameters MemberFunctionAttributes? ) ( ref Type function Parameters FunctionAttributes? ) ```
Dec 21 2023