digitalmars.D - CTFE limitation on reinterpreting cast
- Rambutan (23/23) Sep 28 Here's a tiny version of something I ran into working on a
- monkyyy (18/41) Sep 28 initing is at compile time you want static this()
- Rambutan (6/11) Sep 28 I should have said "is there a way to work around this at compile
- monkyyy (5/17) Sep 28 I believe only void* works `void* ptr=cast(void*)&Bobj;`;
- Rambutan (9/13) Sep 28 I have had no trouble doing this with function pointers earlier.
- Nick Treleaven (11/19) Sep 30 I think its a compiler bug. The cast above should be a compile
- user1234 (3/11) Sep 29 I think this is not related to CTFE, that should just work in a
- Rambutan (25/27) Sep 29 Yes, seems like that.
- monkyyy (9/11) Sep 29 Terrible take, that error message is literally labeled "ctfe" and
- Kapendev (4/16) Sep 29 Monkeyy is right and knows how to read. It's a classic CTFE
- Kapendev (8/10) Sep 29 Not what you want, but here:
- Walter Bright (13/19) Sep 29 CTFE does not do anything that is not memory safe. Consider:
- Rambutan (12/26) Sep 29 Thank you for confirming what I speculated earlier in this thread
- Walter Bright (4/15) Sep 30 I understand your point, and it has merit. But I am apprehensive in allo...
- Richard (Rikki) Andrew Cattermole (3/19) Oct 01 Ya know, C has rules for how structs can be merged.
- Walter Bright (2/5) Oct 01 Perhaps. But we'd also need a case for why this capability is needed.
- Richard (Rikki) Andrew Cattermole (4/10) Oct 01 Can and should are two separate questions, we have not shown can, let
- monkyyy (5/16) Oct 01 Can is very much settled; even if it wasnt the current behavior,
- Rambutan (8/13) Oct 02 Thank you. May I suggest that allowing CTFE to potentially make
- Kagamin (2/2) Oct 03 Your case looks too exotic for that. Whether a pointer can be
- Richard (Rikki) Andrew Cattermole (2/4) Oct 03 Nah you can taint the value and then CTFE will error if you try.
Here's a tiny version of something I ran into working on a
project with both C and D source, while moving into D a C global
array of structs that was initialized in place.
```
struct B {}
struct C {}
__gshared B Bobj;
auto ptr = cast(C*)&Bobj;
void main() {}
```
Without the cast the initializer `&Bobj` is fine, but with it
this does not compile.
```
$ gdc ctfecast.d
ctfecast.d:6:20: error: reinterpreting cast from 'B*' to 'C*' is
not supported in CTFE
6 | auto ptr = cast(C*)&Bobj;
| ^
```
Why? What can you tell me about this? Is there a workaround? Can
you point me at connected documentation?
Any information about this will be gratefully received.
R.
Sep 28
On Monday, 28 September 2026 at 22:50:21 UTC, Rambutan wrote:
Here's a tiny version of something I ran into working on a
project with both C and D source, while moving into D a C
global array of structs that was initialized in place.
```
struct B {}
struct C {}
__gshared B Bobj;
auto ptr = cast(C*)&Bobj;
void main() {}
```
Without the cast the initializer `&Bobj` is fine, but with it
this does not compile.
```
$ gdc ctfecast.d
ctfecast.d:6:20: error: reinterpreting cast from 'B*' to 'C*'
is not supported in CTFE
6 | auto ptr = cast(C*)&Bobj;
| ^
```
Why? What can you tell me about this? Is there a workaround?
Can you point me at connected documentation?
Any information about this will be gratefully received.
R.
initing is at compile time you want static this()
```d
struct B {}
struct C {}
__gshared B Bobj;
C* ptr;// = cast(C*)&Bobj;
static this(){
ptr=cast(C*)&Bobj;
}
void main() {}
```
---
Since its ct and therefore ctfe, the error your getting is from
the colored gc of the ctfe, its largely undocumented but its
mostly implemented ~~to avoid work on bit logic~~ when someone
could imagine how it would compile different on different
architectures
Sep 28
On Monday, 28 September 2026 at 22:56:52 UTC, monkyyy wrote:Since its ct and therefore ctfe, the error your getting is from the colored gc of the ctfe, its largely undocumented but its mostly implemented ~~to avoid work on bit logic~~ when someone could imagine how it would compile different on different architecturesI should have said "is there a way to work around this at compile time?" instead of assuming that was implicit. The pointer will be no different with or without the cast. If `ptr` is made `__gshared` the symbol name in the resulting object will be no different. With this change it still doesn't compile.
Sep 28
On Monday, 28 September 2026 at 23:14:10 UTC, Rambutan wrote:On Monday, 28 September 2026 at 22:56:52 UTC, monkyyy wrote:I believe only void* works `void* ptr=cast(void*)&Bobj;`; otherwise all the colored gc will get in your way. Im only aware of work around with type synchronicity and I probably have tried the hardestSince its ct and therefore ctfe, the error your getting is from the colored gc of the ctfe, its largely undocumented but its mostly implemented ~~to avoid work on bit logic~~ when someone could imagine how it would compile different on different architecturesI should have said "is there a way to work around this at compile time?" instead of assuming that was implicit. The pointer will be no different with or without the cast. If `ptr` is made `__gshared` the symbol name in the resulting object will be no different. With this change it still doesn't compile.
Sep 28
On Monday, 28 September 2026 at 23:20:17 UTC, monkyyy wrote:I believe only void* works `void* ptr=cast(void*)&Bobj;`; otherwise all the colored gc will get in your way. Im only aware of work around with type synchronicity and I probably have tried the hardestI have had no trouble doing this with function pointers earlier. Here's a meaningless small example. ``` extern(C) int f1() { return 0; } __gshared auto f2 = cast(int function(int,char*))&f1; void main() {} ``` This compiles just fine.
Sep 28
On Monday, 28 September 2026 at 23:35:24 UTC, Rambutan wrote:
I have had no trouble doing this with function pointers
earlier. Here's a meaningless small example.
```
extern(C) int f1() { return 0; }
__gshared auto f2 = cast(int function(int,char*))&f1;
void main() {}
```
This compiles just fine.
I think its a compiler bug. The cast above should be a compile
error, because the pointer types are not covariant. Now filed:
https://github.com/dlang/dmd/issues/23950.
The compiler was ignoring the parameter types and linkage, but it
did check the return type. The following errors with stable dmd:
```d
int f1() { return 0; }
auto fp = cast(void function()) &f1; // Error: reinterpreting
cast from `int()*` to `void()*` is not supported in CTFE
```
Sep 30
On Monday, 28 September 2026 at 22:50:21 UTC, Rambutan wrote:Here's a tiny version of something I ran into working on a project with both C and D source, while moving into D a C global array of structs that was initialized in place. [...] Why? What can you tell me about this? Is there a workaround? Can you point me at connected documentation? Any information about this will be gratefully received. R.I think this is not related to CTFE, that should just work in a ` system` scope. In other words, congrats you've found a bug.
Sep 29
On Tuesday, 29 September 2026 at 11:19:15 UTC, user1234 wrote:I think this is not related to CTFE, that should just work in a ` system` scope. In other words, congrats you've found a bug.Yes, seems like that. The CTFE error message specifically forbids a narrow kind of cast that doesn't change the underlying pointer, a "reinterpreting cast". To me this seems to be a design choice not an accident. Any information about this design choice will be gratefully received. In the absence of that I will speculate some. Using what a pointer obtained through a "reinterpreting cast" points to is dangerous in general, with some exceptions of course. So maybe forbidding reinterpreting casts in CTFE is a security decision to avoid this dangerous use of reinterpreted pointers during compilation. This security decision is overkill because it prevents harmless reinterpreting casts of pointers. C can initialize arrays of structs at compile time to contain cast pointers to global data but D cannot. Allowing D to do this would be allowing harmless reinterpreting casts at compile time. What makes a reinterpreting cast harmless in CTFE? When the reinterpreted pointer is not used to access what it refers to. When it is not used for indirection in any way at compile time. So allowing "reinterpreting casts" for pointers, and disallowing indirection for reinterpreted pointers is a far better CTFE rule. This would likely enable D to more-or-less copy any C-style compile-time initialization of big global arrays, which would be great in a mixed source project that started as all C.
Sep 29
On Tuesday, 29 September 2026 at 11:19:15 UTC, user1234 wrote:I think this is not related to CTFE,Terrible take, that error message is literally labeled "ctfe" and its the "the ctfe gc is unhappy" error message. Theres a colored gc for ctfe, thats mostly undocumented, trust your eyes over the spec. Error messages tell you that a specific code branch was unhappy. Even if the spec didnt say `Any pointer may be cast to void* and from void* back to its original type.` on its cope list https://dlang.org/spec/function.html#interpretation ; the error message is more real.
Sep 29
On Tuesday, 29 September 2026 at 18:43:04 UTC, monkyyy wrote:On Tuesday, 29 September 2026 at 11:19:15 UTC, user1234 wrote:Monkeyy is right and knows how to read. It's a classic CTFE error. I say classic because it's one of those things you will try one day while doing metaprogramming.I think this is not related to CTFE,Terrible take, that error message is literally labeled "ctfe" and its the "the ctfe gc is unhappy" error message. Theres a colored gc for ctfe, thats mostly undocumented, trust your eyes over the spec. Error messages tell you that a specific code branch was unhappy. Even if the spec didnt say `Any pointer may be cast to void* and from void* back to its original type.` on its cope list https://dlang.org/spec/function.html#interpretation ; the error message is more real.
Sep 29
On Monday, 28 September 2026 at 22:50:21 UTC, Rambutan wrote:Without the cast the initializer &Bobj is fine, but with it this does not compile.Not what you want, but here: ```d C* ptr() => cast(C*) &Bobj; void main() { ptr.thingInC += 69; } ```
Sep 29
On 9/28/2026 3:50 PM, Rambutan wrote:
struct B {}
struct C {}
__gshared B Bobj;
auto ptr = cast(C*)&Bobj;
CTFE does not do anything that is not memory safe. Consider:
```d
struct B { int b; }
struct C { long c; }
__gshared B Bobj;
auto ptr = cast(C*)&Bobj;
```
This fails, because the structs are laid out differently, and `ptr.c` will
produce memory unsafe behavior.
You could make the point that if the layouts of B and C are identical, this
would be memory safe and should work.
Feel free to make the case for it!
Sep 29
On Wednesday, 30 September 2026 at 00:17:25 UTC, Walter Bright wrote:On 9/28/2026 3:50 PM, Rambutan wrote:Thank you for confirming what I speculated earlier in this thread when I said this is dangerous and so may be the motive for banning reinterpreting casts in CTFE. However I also pointed out that there is no problem with such a cast if the new pointer is never indirected in any way at compile time. So a better rule would be to ban any kind of indirection using the result of a reinterpreting cast, and NOT to ban the cast itself. That way placing cast pointers to globals in global struct initializers would be legal, as it is in C.struct B {} struct C {} __gshared B Bobj; auto ptr = cast(C*)&Bobj;CTFE does not do anything that is not memory safe. Consider: ```d struct B { int b; } struct C { long c; } __gshared B Bobj; auto ptr = cast(C*)&Bobj; ```
Sep 29
On 9/29/2026 8:38 PM, Rambutan wrote:Thank you for confirming what I speculated earlier in this thread when I said this is dangerous and so may be the motive for banning reinterpreting casts in CTFE. However I also pointed out that there is no problem with such a cast if the new pointer is never indirected in any way at compile time. So a better rule would be to ban any kind of indirection using the result of a reinterpreting cast, and NOT to ban the cast itself. That way placing cast pointers to globals in global struct initializers would be legal, as it is in C.I understand your point, and it has merit. But I am apprehensive in allowing CTFE to make bad pointers, even if they are not dereferenced. Perhaps consider a union instead?
Sep 30
On 01/10/2026 7:44 PM, Walter Bright wrote:On 9/29/2026 8:38 PM, Rambutan wrote:Ya know, C has rules for how structs can be merged. Perhaps that is a good place to look at.Thank you for confirming what I speculated earlier in this thread when I said this is dangerous and so may be the motive for banning reinterpreting casts in CTFE. However I also pointed out that there is no problem with such a cast if the new pointer is never indirected in any way at compile time. So a better rule would be to ban any kind of indirection using the result of a reinterpreting cast, and NOT to ban the cast itself. That way placing cast pointers to globals in global struct initializers would be legal, as it is in C.I understand your point, and it has merit. But I am apprehensive in allowing CTFE to make bad pointers, even if they are not dereferenced. Perhaps consider a union instead?
Oct 01
On 10/1/2026 12:15 AM, Richard (Rikki) Andrew Cattermole wrote:Ya know, C has rules for how structs can be merged. Perhaps that is a good place to look at.Perhaps. But we'd also need a case for why this capability is needed.
Oct 01
On 02/10/2026 2:22 PM, Walter Bright wrote:On 10/1/2026 12:15 AM, Richard (Rikki) Andrew Cattermole wrote:Can and should are two separate questions, we have not shown can, let alone should. One problem at a time!Ya know, C has rules for how structs can be merged. Perhaps that is a good place to look at.Perhaps. But we'd also need a case for why this capability is needed.
Oct 01
On Friday, 2 October 2026 at 01:33:46 UTC, Richard (Rikki) Andrew Cattermole wrote:On 02/10/2026 2:22 PM, Walter Bright wrote:Can is very much settled; even if it wasnt the current behavior, void* casts are always an option; it took effort to break ctfe casts for safety.On 10/1/2026 12:15 AM, Richard (Rikki) Andrew Cattermole wrote:Can and should are two separate questions, we have not shown can, let alone should. One problem at a time!Ya know, C has rules for how structs can be merged. Perhaps that is a good place to look at.Perhaps. But we'd also need a case for why this capability is needed.
Oct 01
On Thursday, 1 October 2026 at 06:44:55 UTC, Walter Bright wrote:Thank you. May I suggest that allowing CTFE to potentially make bad pointers but not realize their badness is a natural extension of the ability to write system code in D to get the job done when necessary? Perhaps you might consider that allowing reinterpreting casts of pointers in CTFE but disallowing their indirect use in CTFE is effectively a runtime decision about system code.That way placing cast pointers to globals in global struct initializers would be legal, as it is in C.I understand your point, and it has merit. But I am apprehensive in allowing CTFE to make bad pointers, even if they are not dereferenced. Perhaps consider a union instead?
Oct 02
Your case looks too exotic for that. Whether a pointer can be dereferenced is determined by the type system, `void*` does that.
Oct 03
On 03/10/2026 11:19 PM, Kagamin wrote:Your case looks too exotic for that. Whether a pointer can be dereferenced is determined by the type system, `void*` does that.Nah you can taint the value and then CTFE will error if you try.
Oct 03









Nick Treleaven <nick geany.org> 