debug a reserved keyword (even for enums?)
Quirin Schroll
qs.il.paperinik at gmail.com
Mon Apr 13 10:58:50 UTC 2026
On Saturday, 11 April 2026 at 09:33:34 UTC, libxmoc wrote:
> On Saturday, 11 April 2026 at 01:38:30 UTC, Jonathan M Davis
> wrote:
>> On Friday, April 10, 2026 4:37:54 PM Mountain Daylight Time
>> Katastic via Digitalmars-d wrote:
>>> Hello all, it's been awhile. I have a question. Does it make
>>> 'sense' that debug is a reserved keyword even when its inside
>>> an enum?
>>>
>>> ```D
>>> enum loggingLevel {
>>> none = 0,
>>> debug, // Error: found `debug` when expecting `identifier`
>>> info,
>>> warning,
>>> error
>>> }
>>> ```
>>>
>>> Is there anywhere where that would actually clash?
>>> ```D
>>> void myFunction(loggingLevel l);
>>> myFunction(loggingLevel.debug);
>>>
>>> with(loggingLevel){
>>> myFunction(debug);
>>> }
>>> ```
>>>
>>> Is `debug` used anywhere outside of `version()` statements?
>>> Would
>>> it explode templates or something?
>>
>> As a general rule, keywords in D are keywords no matter where
>> they are. They are not contextual. As such, you cannot use
>> them anywhere other than where the language uses that word.
>> There are of course downsides to that approach, but it's
>> simpler to process than contextual keywords, and it tends to
>> work better with stuff like error messages (since the keyword
>> then is essentially an anchor point in the grammar instead of
>> the compiler needing to figure which context you're trying to
>> use the keyword in when the context isn't well-formed due to
>> typos or whatnot).
>>
>> Obviously, that's annoying when you have a situation where a
>> keyword is the best identfier, but you can't use it, because
>> it's a keyword. However, the solution that's generally used in
>> D (and given in the official style guide) is that when you
>> need to use a keyword as an identifier, you apppend an
>> underscore to it:
>>
>> https://dlang.org/dstyle.html#naming_keywords
>>
>> So, you'd have `LoggingLevel.debug_` rather than
>> `LoggingLevel.debug`.
>>
>> A prime example of where this comes up in the standard library
>> is with `std.traits.FunctionAttribute`:
>>
>> https://dlang.org/phobos/std_traits.html#.FunctionAttribute
>>
>> The enum literally needs to list a bunch of keywords as its
>> values, and that's not legal, so trailing underscores have
>> been added to those keywords.
>
> This is exactly the kind of friction that D editions should
> address.
>
> I've been bitten by this twice recently: once designing a UI
> system where `Align align;` was illegal, and again a few months
> ago with SemVer version. Both times, the trailing underscore
> workaround felt like unnecessary concessions for perfectly
> unambiguous code.
>
> The fundamental issue isn't whether non-contextual keywords are
> easier to parse, it's that D already has a mechanism to
> disambiguate: the dot operator. `loggingLevel.debug`,
> `Align.align`, and `SemVer.version` are all unambiguous to both
> the parser and the reader. The token following a dot is already
> in a different lexical context.
>
> There's precedent for this in other languages. Rust allows
> `self.as` and `match.as` because keywords after `.` are
> unambiguous. Even C#—hardly a radical language—has contextual
> keywords in specific positions.
That is quite an understatement. I’d go as far and say *most*
keywords in C# are contextual these days. Linq has many keywords
and they’re all contextual.
> The "trailing underscore" convention is a concession that keeps
> accumulating.
On the other hand, C# has a way to mark keywords as identifiers
when they’d otherwise lead to errors: `@keyword`. Maybe D can use
a `#` suffix or something. While `#` is reserved for things like
`#line`, those need to be the first non-whitespace character in
their line, which cannot clash with a suffix. If you need such a
variable, use `debug#`. There are countless other options, such
as `debug$`.
> debug_, version_, align_, function_, immutable_... at what
> point do we acknowledge that the cure (universal keyword
> reservation) is worse than the disease (contextual parsing
> complexity)?
>
> Editions exist precisely to make breaking improvements like
> this possible without fragmenting the ecosystem.
>
> Let's do it.
The way to do this, IMO, is going over the list of keywords and
identifying those that are both
* common English words, especially nouns, and
* rarely used.
For each:
1. Figure out if a common keyword would do. Rare cases may hit
this, e.g. `body` → `do` is such a success story.
2. The remaining ones would be replaced by `__keyword`.
Here’s my take:
Obvious candidates
```
abstract → @abstract
align → __align and kill `align` without parentheses
debug → __debug
deprecated → __deprecated
final → @final
invariant → __invariant
pure → @pure
version → __version
```
Those are English words that are used as keywords that are used
in very specific contexts.
The most obvious candidates are `align`, `debug`, `export`, and
`version`. `debug` especially warrants `__` because it breaks
attributes. The average programmer very rarely uses `align` or
`deprecated` and your code won’t become that more ugly when those
need underscores. And while `version` is more common, it’s
possibly also more common as a desired identifier. Similar is
maybe `invariant`. You don’t use it that often that `__` would be
too annoying, but in certain domains, having `invariant` as an
identifier might be nice.
`abstract` and `final` simply don’t need to be keywords. Moving
them to compiler-recognized UDAs allows them as identifiers and
isn’t a big change.
I’m torn on:
```
export → __export
macro → remove it
package → __package
```
I’m not sure if `macro` is a highly desired identifier. `package`
and `export` might be, but they’re visibility attributes and if
we’re not targeting `private` and `public` maybe we should let
them be, especially `package` because it takes an optional a
parameter.
More information about the Digitalmars-d
mailing list