www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Do string mixins work under -betterC?

reply DLearner <bmqazwsx123 gmail.com> writes:
I have a rather complicated string mixin defined and invoked from 
within the same source file.
Which seems to work.

But when I move the mixin definition to a separate module, and 
import that module into the original source file, the compilation 
collapses complaining that array concatenation requires the GC, 
which is not available with -betterC.

DLearner
Dec 16 2025
next sibling parent reply "Richard (Rikki) Andrew Cattermole" <richard cattermole.co.nz> writes:
On 17/12/2025 9:48 AM, DLearner wrote:
 I have a rather complicated string mixin defined and invoked from within 
 the same source file.
 Which seems to work.
 
 But when I move the mixin definition to a separate module, and import 
 that module into the original source file, the compilation collapses 
 complaining that array concatenation requires the GC, which is not 
 available with -betterC.
 
 DLearner
Yes it does. Are you trying to extract the string mixin argument out into a function? ```d string genStuff(string val) { return val ~ ";"; } void myFunc() { mixin(genStuff("this")); } ``` This won't work without a way to mark `genStuff` as CTFE only. Which doesn't exist currently. Compiler has to know that a given execution context is CTFE only otherwise disallowed.
Dec 16 2025
parent reply DLearner <bmqazwsx123 gmail.com> writes:
On Tuesday, 16 December 2025 at 20:56:06 UTC, Richard (Rikki) 
Andrew Cattermole wrote:
 On 17/12/2025 9:48 AM, DLearner wrote:
 I have a rather complicated string mixin defined and invoked 
 from within the same source file.
 Which seems to work.
 
 But when I move the mixin definition to a separate module, and 
 import that module into the original source file, the 
 compilation collapses complaining that array concatenation 
 requires the GC, which is not available with -betterC.
 
[...]
 string genStuff(string val) {
 	return val ~ ";";
 }

 void myFunc() {
 	mixin(genStuff("this"));
 }
 ```

 This won't work without a way to mark `genStuff` as CTFE only. 
 Which doesn't exist currently.

 Compiler has to know that a given execution context is CTFE 
 only otherwise disallowed.
1. Any proposals to mark a function as CTFE only? 2. Why (and I've now got this down to a cut-and-paste) is there no problem if the mixin definition is in the main function, but problem appears if the definition is moved to a separate module (which is imported into the main function)? DL
Dec 16 2025
next sibling parent reply "Richard (Rikki) Andrew Cattermole" <richard cattermole.co.nz> writes:
On 17/12/2025 12:25 PM, DLearner wrote:
 On Tuesday, 16 December 2025 at 20:56:06 UTC, Richard (Rikki) Andrew 
 Cattermole wrote:
 On 17/12/2025 9:48 AM, DLearner wrote:
 I have a rather complicated string mixin defined and invoked from 
 within the same source file.
 Which seems to work.

 But when I move the mixin definition to a separate module, and import 
 that module into the original source file, the compilation collapses 
 complaining that array concatenation requires the GC, which is not 
 available with -betterC.
[...]
 string genStuff(string val) {
     return val ~ ";";
 }

 void myFunc() {
     mixin(genStuff("this"));
 }
 ```

 This won't work without a way to mark `genStuff` as CTFE only. Which 
 doesn't exist currently.

 Compiler has to know that a given execution context is CTFE only 
 otherwise disallowed.
1. Any proposals to mark a function as CTFE only?
No, this is something that should've been done a while back. The main concern is that you can get a linker error if you don't emit a function that was called.
Dec 16 2025
parent reply DLearner <bmqazwsx123 gmail.com> writes:
On Wednesday, 17 December 2025 at 00:13:42 UTC, Richard (Rikki) 
Andrew Cattermole wrote:
[...]
 string genStuff(string val) {
     return val ~ ";";
 }

 void myFunc() {
     mixin(genStuff("this"));
 }
[...]
 This won't work without a way to mark `genStuff` as CTFE 
 only. Which doesn't exist currently.

 Compiler has to know that a given execution context is CTFE 
 only otherwise disallowed.
1. Any proposals to mark a function as CTFE only?
No, this is something that should've been done a while back. The main concern is that you can get a linker error if you don't emit a function that was called.
Actually, in your example, don't see the problem. 'mixin' operates to provide text to the compiler _prior to_ run time. Therefore, even if 'genStuff()' has a run-time (as well as) compile-time meaning, the use of mixin should force the compile-time meaning to be taken. So no ambiguity? DL
Dec 17 2025
parent reply "Richard (Rikki) Andrew Cattermole" <richard cattermole.co.nz> writes:
On 18/12/2025 3:15 AM, DLearner wrote:
 On Wednesday, 17 December 2025 at 00:13:42 UTC, Richard (Rikki) Andrew 
 Cattermole wrote:
 [...]
 string genStuff(string val) {
     return val ~ ";";
 }

 void myFunc() {
     mixin(genStuff("this"));
 }
[...]
 This won't work without a way to mark `genStuff` as CTFE only. Which 
 doesn't exist currently.

 Compiler has to know that a given execution context is CTFE only 
 otherwise disallowed.
1. Any proposals to mark a function as CTFE only?
No, this is something that should've been done a while back. The main concern is that you can get a linker error if you don't emit a function that was called.
Actually, in your example, don't see the problem. 'mixin' operates to provide text to the compiler _prior to_ run time. Therefore, even if 'genStuff()' has a run-time (as well as) compile-time meaning, the use of mixin should force the compile-time meaning to be taken. So no ambiguity? DL
genStuff will still compile into the binary. The error happens when it tries to do that, not when it tries to run it for the string mixin.
Dec 17 2025
parent DLearner <bmqazwsx123 gmail.com> writes:
On Wednesday, 17 December 2025 at 14:18:22 UTC, Richard (Rikki) 
Andrew Cattermole wrote:
 On 18/12/2025 3:15 AM, DLearner wrote:
 On Wednesday, 17 December 2025 at 00:13:42 UTC, Richard 
 (Rikki) Andrew Cattermole wrote:
 [...]
 string genStuff(string val) {
     return val ~ ";";
 }

 void myFunc() {
     mixin(genStuff("this"));
 }
[...]
 This won't work without a way to mark `genStuff` as CTFE 
 only. Which doesn't exist currently.

 Compiler has to know that a given execution context is CTFE 
 only otherwise disallowed.
1. Any proposals to mark a function as CTFE only?
No, this is something that should've been done a while back. The main concern is that you can get a linker error if you don't emit a function that was called.
Actually, in your example, don't see the problem. 'mixin' operates to provide text to the compiler _prior to_ run time. Therefore, even if 'genStuff()' has a run-time (as well as) compile-time meaning, the use of mixin should force the compile-time meaning to be taken. So no ambiguity? DL
genStuff will still compile into the binary. The error happens when it tries to do that, not when it tries to run it for the string mixin.
And a suitably placed 'if (__ctfe) {...} else {}' within genStuff doesn't help? Idea is to tell the compiler that compilation for execution is entirely redundant. DL
Dec 17 2025
prev sibling next sibling parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Tuesday, 16 December 2025 at 23:25:18 UTC, DLearner wrote:
 1. Any proposals to mark a function as CTFE only?
In OpenD you can ``` string genStuff(string val) { if(__ctfe) { return val ~ ";"; } else { assert(0); } } ``` That is, wrap the whole function in `if(__ctfe) {}` and it passes this test since the gc use is now provably only in ctfe. I don't know about the other dmd release but it might work there too, worth trying it.
Dec 17 2025
parent DLearner <bmqazwsx123 gmail.com> writes:
On Wednesday, 17 December 2025 at 14:29:21 UTC, Adam D. Ruppe 
wrote:
 On Tuesday, 16 December 2025 at 23:25:18 UTC, DLearner wrote:
 1. Any proposals to mark a function as CTFE only?
In OpenD you can ``` string genStuff(string val) { if(__ctfe) { return val ~ ";"; } else { assert(0); } } ``` That is, wrap the whole function in `if(__ctfe) {}` and it passes this test since the gc use is now provably only in ctfe. I don't know about the other dmd release but it might work there too, worth trying it.
This idea worked with DMD64 D Compiler v2.111.0. In particular, it worked even when the equivalent of 'genStuff()' was defined in a different module and imported into main. Thanks!
Dec 17 2025
prev sibling parent abc <a b.c> writes:
On Tuesday, 16 December 2025 at 23:25:18 UTC, DLearner wrote:
 1. Any proposals to mark a function as CTFE only?
If your function is a lambda assigned to an anonymous enum then it will not generate runtime code and it will not error. ``` enum genStuff = (string val){ return val ~ ";"; }; mixin(genStuff("int abc")); ```
Dec 18 2025
prev sibling parent reply Kapendev <alexandroskapretsos gmail.com> writes:
On Tuesday, 16 December 2025 at 20:48:25 UTC, DLearner wrote:
 I have a rather complicated string mixin defined and invoked 
 from within the same source file.
 Which seems to work.

 But when I move the mixin definition to a separate module, and 
 import that module into the original source file, the 
 compilation collapses complaining that array concatenation 
 requires the GC, which is not available with -betterC.

 DLearner
My solution to this is something like: ```d import core.stdc.stdio; string genStuff(string val)() { return val ~ ";\n"; } extern(C) void main() { printf(genStuff!("Hello").ptr); } ``` This is **in my opinion** the best way to do things if you use the `betterC` flag.
Dec 17 2025
parent IchorDev <zxinsworld gmail.com> writes:
On Wednesday, 17 December 2025 at 15:12:59 UTC, Kapendev wrote:
 My solution to this is something like:

 ```d
 string genStuff(string val)() {
     return val ~ ";\n";
 }
 ```

 This is **in my opinion** the best way to do things if you use 
 the `betterC` flag.
There are two better options in terms of compile speed: First, if you will be using the exact same parameters in many places, you can utilise template caching by assigning the result of your function to an enum (so that the function isn't re-run by the CTFE engine in every place that you use it): ```d enum genStuff(string val) = (){ return val ~ ";\n"; }(); mixin(genStuff("int a")); void foo(){ genStuff("int a"); } void bar(){ genStuff("int a")); } ``` Second, if you anticipate that you will use different parameters every time, then you don't want to be generating a template for every set of parameters because it's somewhat expensive. So we can alleviate the template while making the function compile-time only by just assigning it to an enum: ```d enum genStuff = (string val){ return val ~ ";\n"; }; mixin(genStuff("int a") ~ genStuff("string b") ~ genStuff("void[0][size_t] c")); ```
Jan 10