Thread overview
Identifier right after a template Type
Jun 22, 2023
Puneet Goel
Jun 22, 2023
Puneet Goel
Jun 22, 2023
Ali Çehreli
Jun 22, 2023
Puneet Goel
Jun 22, 2023
Nick Treleaven
June 22, 2023

Currently, D allows putting an identifier right after a string which may be part of a template instance. Should there not be a mandatory whitespace between the type and the identifier?

For example, the following code is valid:

class Foo(string str) {
  enum STR = str;
}
class Bar {
  Foo!q{foo}bb;
  Foo!q{foo}cc;
}
void main() {
  Bar p;
  pragma(msg, p.tupleof[0].stringof);
  pragma(msg, p.tupleof[1].stringof);
}

// Output:
// p.bb
// p.c  // q{foo}c is taken as a string

https://issues.dlang.org/show_bug.cgi?id=23999

June 22, 2023

It becomes an issue when the string passed to the template is actually meant to be a pseudo-code (to be parsed at compile-time):

class Foo {
  int bar;

  Predicate! q{
    bar < 42;
  }b_pred;

  Predicate! q{
    bar > 0;
  }c_pred;
}

This becomes confusing since the user's intention is to declare a predicate named c_pred. But the D compiler actually parses that as _pred;

June 22, 2023
On 6/22/23 00:49, Puneet Goel wrote:

>    Foo!q{foo}cc;
[...]
> // p.c  // q{foo}c is taken as a string

In case it's not clear to all: q{foo}c is the equivalent of "foo"c, which means a string consisting of chars (as usual).

This issue is somewhat related to something I've struggled with just today. The following expression that sums up 10,000 values came up 0! What?

import std;

void main() {
    const result = iota(10,000).map!(i => foo(i)).sum;
    writeln(result);    // Prints 0! WAT?
}

auto foo(int i) {
    // Trivial for this demonstration
    return i;
}

After struggling for too long to admit, I realized that 10,000 is not "ten thousand" but "10 and 0". Ouch!

Ali

June 22, 2023

On Thursday, 22 June 2023 at 09:19:39 UTC, Ali Çehreli wrote:

>

On 6/22/23 00:49, Puneet Goel wrote:

>

Foo!q{foo}cc;
[...]
// p.c // q{foo}c is taken as a string

In case it's not clear to all: q{foo}c is the equivalent of "foo"c, which means a string consisting of chars (as usual).

I think it is a bug (or maybe I should call it unexpected compiler behavior) at a more fundamental level. Consider how both clang and gcc treat literal suffix errors differently compared to Dlang:

$ cat /tmp/test.d
ulong test = 44LUNG;
$ ldc2 /tmp/test.d
/tmp/test.d(1): Error: semicolon expected following auto declaration, not `NG`
/tmp/test.d(1): Error: no identifier for declarator `NG`
$ cat /tmp/test.c
int long unsigned test = 44LUNG;
$ gcc /tmp/test.c
/tmp/test.c:1:26: error: invalid suffix "LUNG" on integer constant
    1 | int long unsigned test = 44LUNG;
      |                          ^~~~~~

$ clang /tmp/test.c
/tmp/test.c:1:28: error: invalid suffix 'LUNG' on integer constant
int long unsigned test = 44LUNG;
                           ^
1 error generated.
June 22, 2023

On Thursday, 22 June 2023 at 07:49:31 UTC, Puneet Goel wrote:

>
class Bar {
  Foo!q{foo}bb;
  Foo!q{foo}cc;
}

From bugzilla:

Foo!2LUNGS; // declare variable `NGS`

The PR also catches:

Foo!0xFeedme; // variable `me`
@0xFeedObject var; // has type Object

If we don't error on those, we can't add new type suffixes for literals in future without breaking code. I think that is a strong argument to do it.