www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - CTFE limitation on reinterpreting cast

reply Rambutan <s56l6zk0w mozmail.com> writes:
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
next sibling parent reply monkyyy <crazymonkyyy gmail.com> writes:
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
parent reply Rambutan <s56l6zk0w mozmail.com> writes:
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 
 architectures
I 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
parent reply monkyyy <crazymonkyyy gmail.com> writes:
On Monday, 28 September 2026 at 23:14:10 UTC, Rambutan wrote:
 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 architectures
I 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.
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 hardest
Sep 28
parent reply Rambutan <s56l6zk0w mozmail.com> writes:
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 hardest
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.
Sep 28
parent Nick Treleaven <nick geany.org> writes:
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
prev sibling next sibling parent reply user1234 <user1234 12.de> writes:
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
next sibling parent Rambutan <s56l6zk0w mozmail.com> writes:
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
prev sibling parent reply monkyyy <crazymonkyyy gmail.com> writes:
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
parent Kapendev <alexandroskapretsos gmail.com> writes:
On Tuesday, 29 September 2026 at 18:43:04 UTC, monkyyy wrote:
 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.
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.
Sep 29
prev sibling next sibling parent Kapendev <alexandroskapretsos gmail.com> writes:
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
prev sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
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
parent reply Rambutan <s56l6zk0w mozmail.com> writes:
On Wednesday, 30 September 2026 at 00:17:25 UTC, Walter Bright 
wrote:
 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; ```
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.
Sep 29
parent reply Walter Bright <newshound2 digitalmars.com> writes:
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
next sibling parent reply "Richard (Rikki) Andrew Cattermole" <richard cattermole.co.nz> writes:
On 01/10/2026 7:44 PM, Walter Bright wrote:
 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?
Ya know, C has rules for how structs can be merged. Perhaps that is a good place to look at.
Oct 01
parent reply Walter Bright <newshound2 digitalmars.com> writes:
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
parent reply "Richard (Rikki) Andrew Cattermole" <richard cattermole.co.nz> writes:
On 02/10/2026 2:22 PM, Walter Bright wrote:
 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.
Can and should are two separate questions, we have not shown can, let alone should. One problem at a time!
Oct 01
parent monkyyy <crazymonkyyy gmail.com> writes:
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:
 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.
Can and should are two separate questions, we have not shown can, let alone should. One problem at a time!
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.
Oct 01
prev sibling parent reply Rambutan <s56l6zk0w mozmail.com> writes:
On Thursday, 1 October 2026 at 06:44:55 UTC, Walter Bright wrote:
 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?
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.
Oct 02
parent reply Kagamin <spam here.lot> writes:
Your case looks too exotic for that. Whether a pointer can be 
dereferenced is determined by the type system, `void*` does that.
Oct 03
parent "Richard (Rikki) Andrew Cattermole" <richard cattermole.co.nz> writes:
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