Comment #0 by ilyayaroshenko — 2018-09-26T12:50:56Z
dmd -betterC
mixin(`void foo(){}`.idup);
---
pass in DMD 2.78
fails in DMD >=2.79
/opt/compiler-explorer/dmd-2.079.0/dmd2/linux/bin64/../../src/druntime/import/object.d(4259): Error: `TypeInfo` cannot be used with -betterC
This cause BetterC regression in mir.bitmanip and mir-cpuid. The workaround for mir.bitmanip was added to mir-algorithm v2.0.6.
Comment #1 by mail — 2018-09-26T15:51:34Z
I have similar issues. Most things from phobos don't work in CTFE when compiling with betterC (same error in both dmd and ldc).
---
void main() {
import std.uni : toLower;
pragma(msg,"asdfBsdf".toLower);
}
---
This particular error is happening because the definition of idup() in object.d is versioned out when BetterC is on, so ctfe cannot find it.
There's not an obvious solution to this.
Comment #4 by destructionator — 2020-02-05T02:46:16Z
idup is found in betterC, just the compiler refuses to run it.
The betterC restrictions should not apply while executing in a compile time context at all.
Comment #5 by petar.p.kirov — 2020-02-26T17:04:41Z
I guess the trickiest problem to solve is what to do about is(typeof({ code; })) and __traits(compiles, { code; }). Allowing those constructs to yield true while containing non-betterc code would lead to logical contradictions. Disallowing betterc code in such speculative contexts, while allowing it in CTFE context would be strange.
Comment #6 by destructionator — 2020-02-26T17:36:32Z
I think my proposal would be that *specifically* the `if(__ctfe)` construct overrides the betterC checks, since that can be easily factored out.
Just like how we can write
---
int* do_something() {
if(__ctfe) {
return new int;
} else {
return cast(int*) malloc(int.sizeof);
}
}
enum e = *do_something();
---
today (which works in real D, including without linking druntime btw), which bypasses the normal "malloc cannot be evaluated at compile time), we could conceivably use it in the other direction to allow something in a CT context that isn't allowed in a RT context.
I'm not sure how to write that in spec language... it might be able to just say specifically that betterC's restrictions do not apply inside that specific ast node and like specifically hack the hack that is betterC.
I think that would be fine with `__traits(compiles)` as well. The `if(__ctfe)` switch keeps everything compiling the same way - it is specifically a runtime branch in terms of formal semantics. So the compiles thing passes because that if branch is encapsulated still.
Comment #7 by schveiguy — 2020-02-26T20:44:31Z
(In reply to ZombineDev from comment #5)
> I guess the trickiest problem to solve is what to do about is(typeof({ code;
> })) and __traits(compiles, { code; }). Allowing those constructs to yield
> true while containing non-betterc code would lead to logical contradictions.
> Disallowing betterc code in such speculative contexts, while allowing it in
> CTFE context would be strange.
This is a good point. CTFE is hard because it still needs to compile, even though in this case you won't actually run that code.
I would say that functions that are not going into the object symbol table can be compiled and run for CTFE only. If you do try to refer to that symbol during runtime, then throw the error.
I don't know how hard this is to do, but it seems possible.
possibly another answer is to revert the binding to druntime idup in betterC mode (obviously this worked before).
Comment #8 by destructionator — 2020-02-26T21:02:05Z
My view is that the code compiles just fine. Just without druntime linked in or generated typeinfo, or whatever it depends on at run time, it will not link.
The -betterC switch tries to detect these these would-be linker errors and report them ahead of time, in the compile step, for more consistent and user-friendly errors.
Since an `if(__ctfe)` branch (or `mixin` or `pragma(msg)` or any other unambiguously* compile-time only area) is never actually involved in code generation, that linker error will never actually happen... and the compiler *should* know that and not cause the ahead-of-time compile error eiter.
* __traits(compiles) (and is(typeof())) is a grey area. Technically it isn't generating code at all and thus should not generate a linker error, and thus not generate the betterC error.... but given how it is used in practice I think it should assume the stuff inside WILL be used for codegen and thus return false in these cases.
But when in doubt, I say `-betterC` should aim to give the same end result as `-defaultlib=` just with compile errors instead of linker undefined symbol errors.
Comment #9 by schveiguy — 2020-02-26T23:05:47Z
(In reply to Adam D. Ruppe from comment #8)
> My view is that the code compiles just fine. Just without druntime linked in
> or generated typeinfo, or whatever it depends on at run time, it will not
> link.
>
> The -betterC switch tries to detect these these would-be linker errors and
> report them ahead of time, in the compile step, for more consistent and
> user-friendly errors.
Right, this is my view too.
> Since an `if(__ctfe)` branch (or `mixin` or `pragma(msg)` or any other
> unambiguously* compile-time only area) is never actually involved in code
> generation, that linker error will never actually happen... and the compiler
> *should* know that and not cause the ahead-of-time compile error eiter.
Except this is not the problem. The CTFE part DOES compile. It's the part that's outside that doesn't (in fact typeid(x) does not compile during CTFE). So how do you "compile" that, and only allow it to run at compile time? That's why this is hard.
Any solution for detecting druntime usage is not going to be perfect. For example, this doesn't have compile errors but linker errors (with -betterC):
import core.thread;
import core.time;
extern(C) void main()
{
Thread.sleep(1.seconds);
}
And of course, __traits(compiles) is going to say YES to this. So it is indeed a gray area.
Comment #10 by schveiguy — 2020-02-26T23:08:55Z
Ooooh, I just had an idea. Consider the template that is causing issues _dup. What if we did this:
version(D_BetterC) extern(C) void _dup_UNAVAILABLE_IN_BETTERC();
private U[] _dup(T, U)(T[] a) // pure nothrow depends on postblit
{
if (__ctfe)
{ /* ctfe code */ }
version(D_BetterC) {
_dup_UNAVAILABLE_IN_BETTERC();
return U[].init;
}
else:
// normal implementation that uses typeid
}
This defers the error to the linker, but with the message identifying which function is causing the problem. We can probably make an easy mixin to do this wherever needed.
In essence this is a compiler error, with a "funky" but albeit legible message.
But this does change idup to return true for __traits(compiles). Is that a problem? I'd say no. Use the version(D_BetterC) instead of __traits(compiles).
Aaand, it doesn't work. Using a template at compile time for some reason makes the compiler want to link in the symbol. I think there is no way to make this work unless the compiler is smarter about what it used at compile time vs. runtime.
Comment #11 by schveiguy — 2020-02-26T23:13:38Z
(In reply to ZombineDev from comment #5)
> I guess the trickiest problem to solve is what to do about is(typeof({ code;
> })) and __traits(compiles, { code; }). Allowing those constructs to yield
> true while containing non-betterc code would lead to logical contradictions.
> Disallowing betterc code in such speculative contexts, while allowing it in
> CTFE context would be strange.
When trying to test stuff surrounding this, I'll note that a betterC error seems to kill the compilation when used inside __traits(compiles).
This prints nothing and produces no binary:
extern(C) void main()
{
int[] arr;
pragma(msg, __traits(compiles, arr.idup));
}
Comment #12 by destructionator — 2020-02-26T23:17:20Z
(In reply to Steven Schveighoffer from comment #9)
> Except this is not the problem. The CTFE part DOES compile.
eeeeh, it is the problem that brought me to this bug.
private string makefoo() {
if(__ctfe) {
string a = "b";
a ~= "c\0";
return a;
}
assert(0);
}
enum foo = makefoo();
extern(C) int main() {
import core.stdc.stdio;
printf("%s", foo.ptr);
return 0;
}
Here that private function is obviously intended to be CTFE only - the `private` and `if(__ctfe)` make that extremely clear to a human reader, but the compiler doesn't get the hint.
We could also do a like `pragma(ct_only)` maybe just I was hoping this pattern would work since it is in the existing language. But nope :(
Comment #13 by snarwin+bugzilla — 2021-10-18T16:04:20Z
*** Issue 20086 has been marked as a duplicate of this issue. ***
Comment #14 by dlang-bot — 2023-01-15T02:45:38Z
@WalterBright created dlang/dmd pull request #14819 "fix Issue 19268 - BetterC turns off .dup for CTFE" fixing this issue:
- fix Issue 19268 - BetterC turns off .dup for CTFE
https://github.com/dlang/dmd/pull/14819
Comment #15 by dlang-bot — 2023-01-15T23:07:45Z
dlang/dmd pull request #14819 "fix Issue 19268 - BetterC turns off .dup for CTFE" was merged into stable:
- 3a90f628026f97fcfa46265921ac8ef85c373ced by Walter Bright:
fix Issue 19268 - BetterC turns off .dup for CTFE
https://github.com/dlang/dmd/pull/14819
Comment #16 by dlang-bot — 2023-02-02T07:33:24Z
dlang/dmd pull request #14857 "merge stable" was merged into master:
- d1819884d5ddcdddb25c369f41dd0b0cde230b45 by Walter Bright:
fix Issue 19268 - BetterC turns off .dup for CTFE
https://github.com/dlang/dmd/pull/14857