new annotation or pragma to mark functions that are intended to be only used during compile time

Steven Schveighoffer schveiguy at gmail.com
Thu Feb 20 19:44:37 UTC 2025


On Thursday, 20 February 2025 at 17:42:25 UTC, Ilya wrote:
> On Thursday, 20 February 2025 at 15:50:33 UTC, Steven 
> Schveighoffer wrote:
>> This has been proposed before, and it doesn't require language 
>> changes:
>>
>> ```d
>> void foo() {
>>    assert(__ctfe);
>> }
>> ```
>
> Yes, I've seen that. But did it go anywhere? I think on the 
> thread I've found there are some discussions with no conclusion.

It has not gone anywhere, but if we were to consider a language 
change, I'd want to evaluate whether this is a better option, 
since it's a de-facto standard.

> I do see a value in not requiring any language changes, but:
> 1. I'm concerned about doing compile time things based on an 
> `assert`, which is a run time thing.

At the root this is code generation. Do you want to generate code 
or not? Semantic already has to run for CTFE.

> 2. I think making an attribute is a bit clearer. Like, asserts 
> are supposed to only affect debug builds (well, except for 
> `assert(false)`), but I want this new functionality to work in 
> release builds too. There is nothing that stops me from 
> implementing that, but I don't like that assert's meaning gets 
> another exception.

`assert(__ctfe)` (and `assert(!__ctfe)`) could be in the same 
vein as `assert(0)` -- that is, it always evaluates even in 
release builds.

>> Basically, this will never work at runtime, only at compile 
>> time. It's already used in a lot of D code for things like 
>> this.
>
> I agree with the point that existing code could immediately 
> benefit from it. That's great. But there is also the second 
> side of this medal: that could also render some existing code 
> non-compilable. How come? Asserts are run-time, what I'm 
> proposing is a static check, so compile time. Static checks are 
> usually either unsound or incomplete. I prefer to stay on the 
> sound side, so it's doomed to be incomplete, meaning it will 
> reject some examples that are working fine with run-time 
> asserts. Imagine this:
>
> ```d
> int ctonly(int x) {
>   assert(__ctfe);
>   return x+1;
> }
> int f(int v) {
>   if (v < 10) {
>     return ctonly(v);
>   } else {
>     return smth_runtime(v);
>   }
> }
> void main() {
>   enum x = f(9); // that's fine, CTFE
>   auto y = f(10); // that's also fine, f() doesn't call ctonly()
> }
> ```
>
> This works with runtime assert, but will be rejected by the 
> check.

Yes, this is a problem with the idea to make it a compiler error.

However, the compiler still does not have to generate code for 
`ctonly`, even if we don't make it an error. It can just replace 
the call with an `assert(__ctfe)` (or one that produces a nice 
message).

>> The compiler can take this hint as "do not optimize or 
>> generate object code for this".
>
> That won't work the same way `assert(__ctfe)` works today, see 
> the example above, so you are proposing to change the semantics 
> of `assert(__ctfe)`.

Yes, you are right. Then again, your attribute has the same 
issue, no?

>> The compiler can also decide at code-generation time to have 
>> an error if it has tried to call the function, or maybe the 
>> mark gets spread to the next level up?
>
> I think it's better to require the user to be explicit about 
> that, otherwise we may end up with skipping code generation of 
> `main` :) The only exception is when such function is passed as 
> a template parameter, in this case I believe we need to add 
> some basic inference to make it usable in practice.

I think with replacing any runtime calls to a ctfe-only function 
with an assert, we have basically the best that can be had.

>> It can be very straightforward -- if this is not the first 
>> runtime statement in the function, then it doesn't get the 
>> benefit. It has to be this form.
>
> Yeah, we can take the shortcut in the implementation and only 
> look at the first statement, but I'm more concerned about 
> changing the semantics.
>
> Why does it have to be this form?

In general we should prefer solutions that don't require changing 
syntax/semantics:
- no need to update IDEs/LSP
- no need to change any semantics
- no new attributes to worry about
- if we hook onto a de-facto standard, then existing code is 
upgraded automatically.

-Steve


More information about the dip.ideas mailing list