www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - "Error: `TypeInfo` cannot be used with -betterC" on a CTFE function

reply Liam McGillivray <yoshi.pit.link.mario gmail.com> writes:
I'm making a modification to a D binding for a C library. I made 
a CTFE function which takes a function declaration with one or 
more `const(char)*` or `char*` parameters and makes an overload 
that accepts D strings. While this function is only used during 
compile time, unfortunately, I have found that it results in the 
library no longer supporting `-betterC`. The compiler gives the 
following error.

```
/usr/include/dlang/dmd/core/lifetime.d(2760,42): Error: 
`TypeInfo` cannot be used with -betterC
/usr/include/dlang/dmd/std/utf.d(1556,24):        instantiated 
from here: `_d_newclassT!(UTFException)`
/usr/include/dlang/dmd/std/utf.d(1563,32):        instantiated 
from here: `exception!(const(char)[])`
/usr/include/dlang/dmd/std/utf.d(1186,54):        instantiated 
from here: `decodeImpl!(true, Flag.no, const(char)[])`
/usr/include/dlang/dmd/std/range/primitives.d(2551,18):        
instantiated from here: `decode!(Flag.no, const(char)[])`
/usr/include/dlang/dmd/std/range/primitives.d(178,40):        
instantiated from here: `front!char`
Error /usr/bin/dmd failed with exit code 1.
```

I don't know what part of the function uses `TypeInfo` (as I 
don't really understand what that is), but I know that it is this 
CTFE function causing the error, because commenting out the 
mixins that use it results in the `-betterC` program compiling 
properly. Here is the CTFE function I wrote:

```
string MakeStringOverload(alias func)() {
     string def = ReturnType!func.stringof ~" "~ 
__traits(identifier, func) ~ "(";

     auto paramNames = ParameterIdentifierTuple!func;

     import std.algorithm.searching;
     foreach(i, paramType; Parameters!func) {
         if (paramType.stringof.canFind("char")) def ~= "ref 
string"; // Made a reference so that D string literals know to 
use the base version of the function and not this one.
         else def ~= paramType.stringof;
         def ~= " "~paramNames[i] ~ ", ";
     }
     def.length -= 2;
     def ~= ") { ";
     if (ReturnType!func.stringof != "void") def ~= "return ";
     def ~= __traits(identifier, func) ~ "(";
     foreach(i, argument; paramNames) {
         if (Parameters!func[i].stringof.canFind("char")) def ~= 
"cast(char*)";
         def ~= argument ~ ", ";
     }
     def.length -= 2;
     def ~= "); }";

     return def;
}
```

In the module where the base versions of functions are declared, 
I do the following to generate overloads:
```
mixin(MakeStringOverload!SetWindowTitle);
mixin(MakeStringOverload!LoadShader);
mixin(MakeStringOverload!LoadShaderFromMemory);
mixin(MakeStringOverload!GetShaderLocation);
```

So what part of my function is using "TypeInfo"? Can I somehow 
label my function as compile-time only so that it stops 
complaining? If not what else can I do to get it to stop 
complaining and compile with `betterC`?
Apr 06
next sibling parent reply "Richard (Rikki) Andrew Cattermole" <richard cattermole.co.nz> writes:
Unfortunately runtime and CTFE are the same target in the compiler.

So that function is being used for both, and hence uses GC (appending).

```d
template Foo(Args) {
	enum Foo = () {
		return Args.init;
	}();
}
```

Something like that should work instead.
Apr 07
next sibling parent reply Liam McGillivray <yoshi.pit.link.mario gmail.com> writes:
On Sunday, 7 April 2024 at 08:59:55 UTC, Richard (Rikki) Andrew 
Cattermole wrote:
 Unfortunately runtime and CTFE are the same target in the 
 compiler.
:-( Will this ever be changed?
 ```d
 template Foo(Args) {
 	enum Foo = () {
 		return Args.init;
 	}();
 }
 ```

 Something like that should work instead.
I'm sorry, but I can't comprehend any of your example. What would be fed into `Args`? I don't understand how this works, or how I would use it for what I want.
Apr 07
parent reply "Richard (Rikki) Andrew Cattermole" <richard cattermole.co.nz> writes:
On 08/04/2024 10:45 AM, Liam McGillivray wrote:
 On Sunday, 7 April 2024 at 08:59:55 UTC, Richard (Rikki) Andrew 
 Cattermole wrote:
 Unfortunately runtime and CTFE are the same target in the compiler.
:-( Will this ever be changed?
A tad unlikely, it would be a rather large change architecturally to dmd.
 ```d
 template Foo(Args) {
     enum Foo = () {
         return Args.init;
     }();
 }
 ```

 Something like that should work instead.
I'm sorry, but I can't comprehend any of your example. What would be fed into `Args`? I don't understand how this works, or how I would use it for what I want.
You would replace it with whatever template parameters you want (including nothing). It's there as a place holder. Same for the return on the closure. But the main thing to understand is that the closure that gives the enum a value, that'll be CTFE only, no runtime target.
Apr 08
parent reply Liam McGillivray <yoshi.pit.link.mario gmail.com> writes:
On Monday, 8 April 2024 at 08:12:22 UTC, Richard (Rikki) Andrew 
Cattermole wrote:
 ```d
 template Foo(Args) {
     enum Foo = () {
         return Args.init;
     }();
 }
 ```

 Something like that should work instead.
I'm sorry, but I can't comprehend any of your example. What would be fed into `Args`? I don't understand how this works, or how I would use it for what I want.
You would replace it with whatever template parameters you want (including nothing). It's there as a place holder. Same for the return on the closure. But the main thing to understand is that the closure that gives the enum a value, that'll be CTFE only, no runtime target.
Are you saying that this is a way to guarantee that the code is compile-time only? I still understand very little of this code. I'm not experienced in D metaprogramming; just the function I posted above was a major achievement for me. I don't understand how I would use the code you gave in place of the function I have written and posted above. When you say that "You would replace it with whatever template parameters you want", are you saying that instead of doing `mixin(MakeStringOverload!SetWindowTitle); mixin(MakeStringOverload!LoadShader);` as posted above, I would write `mixin(Foo!(SetWindowTitle, LoadShader));`? What does the `return Args.init;` line mean in your example? Am I supposed to replace this with a call to the CTFE function I had already written? If so, it didn't work. Making such a replacement resulted in the same "TypeInfo" error that I had already.
Apr 08
parent reply "Richard (Rikki) Andrew Cattermole" <richard cattermole.co.nz> writes:
On 09/04/2024 11:42 AM, Liam McGillivray wrote:
 On Monday, 8 April 2024 at 08:12:22 UTC, Richard (Rikki) Andrew 
 Cattermole wrote:
 ```d
 template Foo(Args) {
     enum Foo = () {
         return Args.init;
     }();
 }
 ```

 Something like that should work instead.
I'm sorry, but I can't comprehend any of your example. What would be fed into `Args`? I don't understand how this works, or how I would use it for what I want.
You would replace it with whatever template parameters you want (including nothing). It's there as a place holder. Same for the return on the closure. But the main thing to understand is that the closure that gives the enum a value, that'll be CTFE only, no runtime target.
Are you saying that this is a way to guarantee that the code is compile-time only?
More or less.
 I still understand very little of this code. I'm not experienced in D 
 metaprogramming; just the function I posted above was a major 
 achievement for me. I don't understand how I would use the code you gave 
 in place of the function I have written and posted above.
Let's break it down. The expression that initializes the ``func`` variable, this is a closure. The ``a`` and ``b`` are function parameter names (types do not need to be provided). ```d alias FuncType = int function(int, int); FuncType func = (a, b) { return a + b; }; int value = func(1, 2); ``` The alias is used to give a name to the function pointer type. Next, let's combine the function pointer storage variable with the result with the call. ```d int value = (a, b) { return a + b; }(1, 2); ``` We can swap the type ``int`` for the ``enum`` keyword, this produces a compile time constant that is an ``int`` that has the value 3. ```d enum Value = (a, b) { return a + b; }(1, 2); ``` This alone should be a CTFE only function. But if we want template parameters, we'd need to wrap it with the template. ```d template Value(int a, int b) { enum Value = () { return a + b; }(); } int value = Value!(1, 2); ``` Does that help?
Apr 08
parent reply Liam McGillivray <yoshi.pit.link.mario gmail.com> writes:
On Tuesday, 9 April 2024 at 00:02:02 UTC, Richard (Rikki) Andrew 
Cattermole wrote:
 ```d
 enum Value = (a, b) {
 	return a + b;
 }(1, 2);
 ```

 This alone should be a CTFE only function.

 But if we want template parameters, we'd need to wrap it with 
 the template.

 ```d
 template Value(int a, int b) {
 	enum Value = () {
 		return a + b;
 	}();
 }

 int value = Value!(1, 2);
 ```

 Does that help?
I had to reread this a few times to get a sense of what this is. I might have just got it. This is effectively a CTFE function for generating a constant based on the sum of two numbers, right? Doing `int value = Value!(1, 2);` would set `value` to 3, right? I suppose this was a good new thing to learn, though I'm still quite far from being able to construct a function from another function using a template. I suppose that if I wanted it to make a function from another function, I may be able to do it in a template using some `static foreach` to make arrays of function parameters, and then combine them together without the use of strings, instead using placeholders (aliases or whatever they'd be called) and maybe the `tupleof` function. Am I headed in the right direction (if you can understand my weak attempt to describe the direction I'm thinking of going in)?
Apr 08
parent reply "Richard (Rikki) Andrew Cattermole" <richard cattermole.co.nz> writes:
On 09/04/2024 12:48 PM, Liam McGillivray wrote:
 On Tuesday, 9 April 2024 at 00:02:02 UTC, Richard (Rikki) Andrew 
 Cattermole wrote:
 ```d
 enum Value = (a, b) {
     return a + b;
 }(1, 2);
 ```

 This alone should be a CTFE only function.

 But if we want template parameters, we'd need to wrap it with the 
 template.

 ```d
 template Value(int a, int b) {
     enum Value = () {
         return a + b;
     }();
 }

 int value = Value!(1, 2);
 ```

 Does that help?
I had to reread this a few times to get a sense of what this is. I might have just got it. This is effectively a CTFE function for generating a constant based on the sum of two numbers, right? Doing `int value = Value!(1, 2);` would set `value` to 3, right?
Yes.
 I suppose this was a good new thing to learn, though I'm still quite far 
 from being able to construct a function from another function using a 
 template.
 
 I suppose that if I wanted it to make a function from another function, 
 I may be able to do it in a template using some `static foreach` to make 
 arrays of function parameters, and then combine them together without 
 the use of strings, instead using placeholders (aliases or whatever 
 they'd be called) and maybe the `tupleof` function. Am I headed in the 
 right direction (if you can understand my weak attempt to describe the 
 direction I'm thinking of going in)?
``tupleof`` isn't a function, its a property to get a "tuple" a sequence of fields for a struct/class. However most likely you'd have to resort to string mixins if you're messing about with parameters like I think? you are asking for. I'm not entirely sure what you're wanting there.
Apr 09
parent reply Liam McGillivray <yoshi.pit.link.mario gmail.com> writes:
On Tuesday, 9 April 2024 at 12:45:55 UTC, Richard (Rikki) Andrew 
Cattermole wrote:
 On 09/04/2024 12:48 PM, Liam McGillivray wrote:

 I suppose this was a good new thing to learn, though I'm still 
 quite far from being able to construct a function from another 
 function using a template.
 
 I suppose that if I wanted it to make a function from another 
 function, I may be able to do it in a template using some 
 `static foreach` to make arrays of function parameters, and 
 then combine them together without the use of strings, instead 
 using placeholders (aliases or whatever they'd be called) and 
 maybe the `tupleof` function. Am I headed in the right 
 direction (if you can understand my weak attempt to describe 
 the direction I'm thinking of going in)?
``tupleof`` isn't a function, its a property to get a "tuple" a sequence of fields for a struct/class. However most likely you'd have to resort to string mixins if you're messing about with parameters like I think? you are asking for. I'm not entirely sure what you're wanting there.
Here's what I wanted to do. In the library I'm working on, there are various declarations for functions defined in an external C library following the line `extern (C) nogc nothrow:`. Here are some examples of such declarations which have a `const(char)*` parameter: ``` void InitWindow(int width, int height, const(char)* title); void SetWindowTitle(const(char)* title); Shader LoadShader(const(char)* vsFileName, const(char)* fsFileName); ``` I wanted to generate definitions of overloads of these functions using strings as parameters instead of `const(char)*`. For the `InitWindow` function shown above, the overload should be defined like this: ``` void InitWindow(int width, int height, ref string title) { InitWindow(width, height, cast(const(char)*)title); } ``` or alternatively, like the following: ``` void InitWindow(int width, int height, string title) { InitWindow(width, height, title.toStringz); } ``` I'm not sure which of these is better, thought the latter one would need to be modified to not accept string literals. I found that the former one has the advantage that making the `title` parameter `ref string` means that string literals use the existing version of the function. I know that the former can be ` nogc`, unlike the latter, though I don't know if there is any advantage offered by `toStringz` over `cast(const(char)*)`. But anyway, my goal was to generate function overloads like either of the above. I have already posted a version of a CTFE function that does this, though I put them under `version (D_TypeInfo)` so that they aren't available in `betterC` builds, since the function I wrote doesn't build with `betterC`.
Apr 11
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On Friday, 12 April 2024 at 00:04:48 UTC, Liam McGillivray wrote:
 Here's what I wanted to do.

 In the library I'm working on, there are various declarations 
 for functions defined in an external C library following the 
 line `extern (C)  nogc nothrow:`. Here are some examples of 
 such declarations which have a `const(char)*` parameter:
 
 ```d
 void InitWindow(int width, int height, const(char)* title);
 void SetWindowTitle(const(char)* title);
 Shader LoadShader(const(char)* vsFileName, const(char)* 
 fsFileName);
 ```

 I wanted to generate definitions of overloads of these 
 functions using strings as parameters instead of 
 `const(char)*`. For the `InitWindow` function shown above, the 
 overload should be defined like this:

 ```d
 void InitWindow(int width, int height, ref string title) {
     InitWindow(width, height, cast(const(char)*)title);
 }
 ```
This is invalid, a string may not be zero-terminated. You can't just cast.
 or alternatively, like the following:
 ```d
 void InitWindow(int width, int height, string title) {
     InitWindow(width, height, title.toStringz);
 }
 ```
This will allocate from the GC. So there are a few things to consider: 1. Is the string *transiently used*. That is, does the function just quickly use the string and never refers to it again? Given that this is raylib, the source is pretty readable, so you should be able to figure this out. 2. If 1 is false, will it be saved in memory that is scannable by the GC? This is one of the most pernicious issues with using C libraries from D. In this case, you will need to either allocate the memory with C `malloc` or pin the GC memory. For transiently used strings, I would point you at the function [`tempCString`](https://github.com/dlang/phobos/blob/0663564600edb3cce6e0925599ebe8a6da8c20fd/std/inte nal/cstring.d#L77), which allocates a temporary C string using malloc or a stack buffer, and then frees it when done with it. The obvious problem in all this is to avoid accepting string literals (which are magic and automatically convert to const char *). This is currently impossible with function overloading, and so you need a separate function name, or put them in a different module. -Steve
Apr 12
parent reply Liam McGillivray <yoshi.pit.link.mario gmail.com> writes:
On Friday, 12 April 2024 at 15:24:38 UTC, Steven Schveighoffer 
wrote:
 ```d
 void InitWindow(int width, int height, ref string title) {
     InitWindow(width, height, cast(const(char)*)title);
 }
 ```
This is invalid, a string may not be zero-terminated. You can't just cast.
Well, it did work when I tried it (using a string variable, not a literal of course). It displayed as it is supposed to. But from the information I can find on the web it looks like strings are sometimes but not `always` zero-terminated. Not a great look for the language. Are there any rules to determine when it is and when it isn't (for string variables)?
 So there are a few things to consider:

 1. Is the string *transiently used*. That is, does the function 
 just quickly use the string and never refers to it again? Given 
 that this is raylib, the source is pretty readable, so you 
 should be able to figure this out.
I suppose. But if it turns out that the string is used continuously (as I assume to be the case with `InitWindow` and `SetWindowTitle`) and it doesn't make a copy of it, I imagine it would be difficult to design the function overload, as it would need to store a copy of the string somewhere. In that case, the only clean solution would be to have a global array of strings to store everything that's been passed to such functions, but that doesn't feel like a very satisfying solution. I may take a look inside some Raylib functions if I get back to this task.
 2. If 1 is false, will it be saved in memory that is scannable 
 by the GC? This is one of the most pernicious issues with using 
 C libraries from D. In this case, you will need to either 
 allocate the memory with C `malloc` or pin the GC memory.
You mean that the GC can destroy objects that still have references from the C code?
 For transiently used strings, I would point you at the function 
 [`tempCString`](https://github.com/dlang/phobos/blob/0663564600edb3cce6e0925599ebe8a6da8c20fd/std/inte
nal/cstring.d#L77), which allocates a temporary C string using malloc or a
stack buffer, and then frees it when done with it.
Thank you. In a previous thread, someone told me that having to do many deallocations slows down the program, and the GC is more efficient because it deallocates many objects simultaneously. Is this something worth considering here, or is the overhead going to be tiny even when it's called a few times per frame?
 The obvious problem in all this is to avoid accepting string 
 literals (which are magic and automatically convert to const 
 char *). This is currently impossible with function 
 overloading, and so you need a separate function name, or put 
 them in a different module.
Aren't there any compile-time conditions for this?
Apr 14
next sibling parent Steven Schveighoffer <schveiguy gmail.com> writes:
On Sunday, 14 April 2024 at 22:36:18 UTC, Liam McGillivray wrote:
 On Friday, 12 April 2024 at 15:24:38 UTC, Steven Schveighoffer 
 wrote:
 ```d
 void InitWindow(int width, int height, ref string title) {
     InitWindow(width, height, cast(const(char)*)title);
 }
 ```
This is invalid, a string may not be zero-terminated. You can't just cast.
Well, it did work when I tried it (using a string variable, not a literal of course). It displayed as it is supposed to.
A cast "working" isn't enough. It could work in certain cases, with certain environmental conditions, etc., but fail horribly with memory corruption in other cases. It could even happen on different runs of the program. It could happen that it works 99.999% of the time. The risk is not worth it.
 But from the information I can find on the web it looks like 
 strings are sometimes but not `always` zero-terminated. Not a 
 great look for the language. Are there any rules to determine 
 when it is and when it isn't (for string variables)?
string literals are zero-terminated. All other strings are not. If you have a string generated at compile time, the chances are good it has zero termination. However, the implicit conversion to `char *` is the clue that it is zero terminated. If that doesn't happen automatically, it's not guaranteed to be zero terminated. A string generated at runtime only has zero termination if you add a 0. You should not cast to a pointer assuming the zero is going to be there. Casting is a blunt instrument, which does not validate what you are doing is sound. A cast says "compiler, I know what I'm doing here, let me do this even though it's outside the language rules".
 So there are a few things to consider:

 1. Is the string *transiently used*. That is, does the 
 function just quickly use the string and never refers to it 
 again? Given that this is raylib, the source is pretty 
 readable, so you should be able to figure this out.
I suppose. But if it turns out that the string is used continuously (as I assume to be the case with `InitWindow` and `SetWindowTitle`) and it doesn't make a copy of it, I imagine it would be difficult to design the function overload, as it would need to store a copy of the string somewhere. In that case, the only clean solution would be to have a global array of strings to store everything that's been passed to such functions, but that doesn't feel like a very satisfying solution. I may take a look inside some Raylib functions if I get back to this task.
You can pin memory in the GC to ensure it's not collected by using `core.memory.GC.addRoot`, which is effectively "storing in a global array".
 2. If 1 is false, will it be saved in memory that is scannable 
 by the GC? This is one of the most pernicious issues with 
 using C libraries from D. In this case, you will need to 
 either allocate the memory with C `malloc` or pin the GC 
 memory.
You mean that the GC can destroy objects that still have references from the C code?
Yes. If the GC is unaware of the memory that is being used by the C code, it can't scan that code for pointers. It may collect these strings early.
 For transiently used strings, I would point you at the 
 function 
 [`tempCString`](https://github.com/dlang/phobos/blob/0663564600edb3cce6e0925599ebe8a6da8c20fd/std/inte
nal/cstring.d#L77), which allocates a temporary C string using malloc or a
stack buffer, and then frees it when done with it.
Thank you. In a previous thread, someone told me that having to do many deallocations slows down the program, and the GC is more efficient because it deallocates many objects simultaneously. Is this something worth considering here, or is the overhead going to be tiny even when it's called a few times per frame?
In an *application*, I would recommend not worrying about the allocation performance until it becomes an issue. I'm writing a simple game, and never have worried about GC performance. When you do need to worry, you can employ strategies like preallocating all things that need allocation (still with the GC). In a *general library*, you do have to worry about the requirements of your users. If you can allocate locally (on the stack), this is the most efficient option. This is what `tempCString` does (with a fallback to `malloc` when the string gets to be large).
 The obvious problem in all this is to avoid accepting string 
 literals (which are magic and automatically convert to const 
 char *). This is currently impossible with function 
 overloading, and so you need a separate function name, or put 
 them in a different module.
Aren't there any compile-time conditions for this?
Unfortunately no. `string` does not implicitly convert to `char *` unless it is a string literal, and string literals bind to `string` before `char *`. So you can't rely on the overload working. -Steve
Apr 14
prev sibling parent "Richard (Rikki) Andrew Cattermole" <richard cattermole.co.nz> writes:
On 15/04/2024 10:36 AM, Liam McGillivray wrote:
 Well, it did work when I tried it (using a string variable, not a 
 literal of course). It displayed as it is supposed to. But from the 
 information I can find on the web it looks like strings are sometimes 
 but not |always| zero-terminated. Not a great look for the language. Are 
 there any rules to determine when it is and when it isn't (for string 
 variables)?
String literals, which are constants that the compiler puts into ROM of the object file, are zero terminated because it doesn't cost anything to do this. At runtime, unless you explicitly append the null terminator, no string contains it. D's strings are slices, pointer + length. These are superior in both performance (not having to strlen all the time) and are safer (bounds checked access). Null terminated strings are a technical debt due to legacy constraints. We would all be better off if C supported slices. Plenty of notable exploits couldn't of happened if they were used instead.
Apr 14
prev sibling parent reply Liam McGillivray <yoshi.pit.link.mario gmail.com> writes:
On Sunday, 7 April 2024 at 08:59:55 UTC, Richard (Rikki) Andrew 
Cattermole wrote:
 Unfortunately runtime and CTFE are the same target in the 
 compiler.

 So that function is being used for both, and hence uses GC 
 (appending).
Are you sure that string appending was really the problem that caused the "TypeInfo" build error? I forgot about this, but I had already had a working CTFE function with string appending before adding the new one that lead to this error. The symbols that it generates could be used in the program compiled with `betterC`. ``` string EnumPrefixes(T)(string oldName, string prefix) { string result = "enum " ~ oldName ~ " {\n"; static foreach(member; __traits(allMembers, T)) { result ~= " " ~ prefix ~ member ~ " = " ~ __traits(getMember, T, member).to!int.to!string ~ ",\n"; } return result ~ "}\n"; } ``` The purpose of this was that the enums used by the C library were too verbose. I had changed them from things like `KeyboardKey.KEY_C` to `Key.C`. I wanted to leave the new enums written directly in the module since these were recommended for use, but then generate the old ones with CTFE for backwards compatibility. The function above was used like `mixin(EnumPrefixes!Key("KeyboardKey", "KEY_"));`, and the compiler would allow it even when building with `betterC`.
Apr 09
parent reply "Richard (Rikki) Andrew Cattermole" <richard cattermole.co.nz> writes:
On 10/04/2024 11:21 AM, Liam McGillivray wrote:
 On Sunday, 7 April 2024 at 08:59:55 UTC, Richard (Rikki) Andrew 
 Cattermole wrote:
 Unfortunately runtime and CTFE are the same target in the compiler.

 So that function is being used for both, and hence uses GC (appending).
Are you sure that string appending was really the problem that caused the "TypeInfo" build error? I forgot about this, but I had already had a working CTFE function with string appending before adding the new one that lead to this error. The symbols that it generates could be used in the program compiled with `betterC`.
No, for a string it shouldn't trigger the need for TypeInfo. But that wouldn't have worked regardless.
 ```
 string EnumPrefixes(T)(string oldName, string prefix) {
      string result = "enum " ~ oldName ~ " {\n";
      static foreach(member; __traits(allMembers, T)) {
          result ~= "    " ~ prefix ~ member ~ " = " ~ 
 __traits(getMember, T, member).to!int.to!string ~ ",\n";
      }
      return result ~ "}\n";
 }
 ```
 
 The purpose of this was that the enums used by the C library were too 
 verbose. I had changed them from things like `KeyboardKey.KEY_C` to 
 `Key.C`. I wanted to leave the new enums written directly in the module 
 since these were recommended for use, but then generate the old ones 
 with CTFE for backwards compatibility. The function above was used like 
 `mixin(EnumPrefixes!Key("KeyboardKey", "KEY_"));`, and the compiler 
 would allow it even when building with `betterC`.
The string mixin triggers CTFE, if ``EnumPrefixes`` wasn't templated, that would cause codegen and hence error. If you called it in a context that wasn't CTFE only, it would codegen even with template and would error. For quite a long time we emitted -betterC errors during semantic, we learned that this was all around a bad idea and moved (hopefully all but I doubt it) into the glue code. So only if it codegens will it error.
Apr 09
next sibling parent reply Liam McGillivray <yoshi.pit.link.mario gmail.com> writes:
On Tuesday, 9 April 2024 at 23:50:36 UTC, Richard (Rikki) Andrew 
Cattermole wrote:
 The string mixin triggers CTFE, if ``EnumPrefixes`` wasn't 
 templated, that would cause codegen and hence error. If you 
 called it in a context that wasn't CTFE only, it would codegen 
 even with template and would error.

 For quite a long time we emitted -betterC errors during 
 semantic, we learned that this was all around a bad idea and 
 moved (hopefully all but I doubt it) into the glue code. So 
 only if it codegens will it error.
Well then, perhaps this is a bug (though a useful bug in my case). If you want to investigate, you can download [this commit](https://github.com/LiamM32/raylib-d/blob/49a8f2a2e4285fc85b9db 4ae9a49cafe8b1a5ed) of Raylib-D. The DUB package in the `rayguiexample` directory has `betterC` as a build option, yet doesn't have this error. This is despite the inclusion of a CTFE function with string appending. It's in [`source/raylib/package.d`](https://github.com/LiamM32/raylib-d/blob/49a8f2a2e4285fc85b9db54ae9a49cafe8b1a5ed/sour e/raylib/package.d) where `mixin(EnumPrefixes!Key("KeyboardKey", "KEY_"));` appears. The definition of `EnumPrefixes` is in [`source/templates.d`](https://github.com/LiamM32/raylib-d/blob/49a8f2a2e4285fc85b9db54ae9a49cafe8b1a5ed/source/templates.d).
Apr 09
parent "Richard (Rikki) Andrew Cattermole" <richard cattermole.co.nz> writes:
On 10/04/2024 2:50 PM, Liam McGillivray wrote:
 On Tuesday, 9 April 2024 at 23:50:36 UTC, Richard (Rikki) Andrew 
 Cattermole wrote:
 The string mixin triggers CTFE, if ``EnumPrefixes`` wasn't templated, 
 that would cause codegen and hence error. If you called it in a 
 context that wasn't CTFE only, it would codegen even with template and 
 would error.

 For quite a long time we emitted -betterC errors during semantic, we 
 learned that this was all around a bad idea and moved (hopefully all 
 but I doubt it) into the glue code. So only if it codegens will it error.
Well then, perhaps this is a bug (though a useful bug in my case).
Not a bug, this took us quite a while to get reliable.
Apr 10
prev sibling parent Steven Schveighoffer <schveiguy gmail.com> writes:
On Tuesday, 9 April 2024 at 23:50:36 UTC, Richard (Rikki) Andrew 
Cattermole wrote:
 On 10/04/2024 11:21 AM, Liam McGillivray wrote:
 On Sunday, 7 April 2024 at 08:59:55 UTC, Richard (Rikki) 
 Andrew Cattermole wrote:
 Unfortunately runtime and CTFE are the same target in the 
 compiler.

 So that function is being used for both, and hence uses GC 
 (appending).
Are you sure that string appending was really the problem that caused the "TypeInfo" build error? I forgot about this, but I had already had a working CTFE function with string appending before adding the new one that lead to this error. The symbols that it generates could be used in the program compiled with `betterC`.
No, for a string it shouldn't trigger the need for TypeInfo. But that wouldn't have worked regardless.
Array appending calls a runtime function which accepts `TypeInfo` (In this case, `TypeInfoGeneric!char`). So this does indeed involve `TypeInfo`. But also, even if `TypeInfo` weren't involved, it also needs the GC which is unavailable with betterC. It's just that the `TypeInfo` error happens first. The move to use templates instead of `TypeInfo` is slowly happening. -Steve
Apr 10
prev sibling parent Kagamin <spam here.lot> writes:
On Sunday, 7 April 2024 at 06:46:39 UTC, Liam McGillivray wrote:
 instantiated from here: `front!char`
Looks like autodecoding, try to comment `canFind`.
Apr 09