Thread overview
Old code no longer working on any DMD compilers
Sep 06, 2019
Jamie
Sep 06, 2019
Jonathan M Davis
Sep 06, 2019
Jamie
Sep 06, 2019
Mike Parker
Sep 06, 2019
Jonathan M Davis
September 06, 2019
I just picked up some of my old code that was working when I last used it (approximately 6 months ago) but now is no longer working. I thought it was due to using an updated compiler, so I have installed many old compilers in and attempt to make it work, but no luck. Using DVM I have tried 2.077.0, 2.078.0, 2.079.0, 2.087.0, 2.088.0, and I think I was using 2.079.0 at the time, but none of these have worked. The actual issue is:

/home/jamie/.dvm/compilers/dmd-2.077.0/linux/bin/../../src/phobos/std/math.d(3702): Error: fmodl cannot be interpreted at compile time, because it has no available source code
called from here: called from here: fmod(cast(real)to(n), cast(real)to(2))

and

/home/jamie/.dvm/compilers/dmd-2.077.0/linux/bin/../../src/druntime/import/object.d(3067): Error: _d_arraysetcapacity cannot be interpreted at compile time, because it has no available source code
called from here: reserve(i_n, 1LU << cast(int)((p + 1LU) * 1LU))

I've looked in the directory it provides and as far as I can tell the code does exist. I.e. in std.math:
real fmod(real x, real y) @trusted nothrow @nogc
{
    version (CRuntime_Microsoft)
    {
        return x % y;
    }
    else
        return core.stdc.math.fmodl(x, y);
}

and in core.stdc.math:
   ///
    double  fmod(double x, double y);
    ///
    float   fmodf(float x, float y);
    ///
    extern(D) real fmodl()(real x, real y) { return fmod(cast(double) x, cast(double) y); }

Thanks
September 05, 2019
On Thursday, September 5, 2019 6:24:07 PM MDT Jamie via Digitalmars-d-learn wrote:
> I just picked up some of my old code that was working when I last used it (approximately 6 months ago) but now is no longer working. I thought it was due to using an updated compiler, so I have installed many old compilers in and attempt to make it work, but no luck. Using DVM I have tried 2.077.0, 2.078.0, 2.079.0, 2.087.0, 2.088.0, and I think I was using 2.079.0 at the time, but none of these have worked. The actual issue is:
>
> /home/jamie/.dvm/compilers/dmd-2.077.0/linux/bin/../../src/phobos/std/math
> .d(3702): Error: fmodl cannot be interpreted at compile time, because it
> has no available source code called from here: called from here:
> fmod(cast(real)to(n),
> cast(real)to(2))
>
> and
>
> /home/jamie/.dvm/compilers/dmd-2.077.0/linux/bin/../../src/druntime/import
> /object.d(3067): Error: _d_arraysetcapacity cannot be interpreted at
> compile time, because it has no available source code called from here:
> reserve(i_n, 1LU << cast(int)((p + 1LU) * 1LU))
>
> I've looked in the directory it provides and as far as I can tell
> the code does exist. I.e. in std.math:
> real fmod(real x, real y) @trusted nothrow @nogc
> {
>      version (CRuntime_Microsoft)
>      {
>          return x % y;
>      }
>      else
>          return core.stdc.math.fmodl(x, y);
> }
>
> and in core.stdc.math:
>     ///
>      double  fmod(double x, double y);
>      ///
>      float   fmodf(float x, float y);
>      ///
>      extern(D) real fmodl()(real x, real y) { return
> fmod(cast(double) x, cast(double) y); }
>
> Thanks

Based on that implementation, fmod would work at compile time if you compile on Windows and use Microsoft's runtime instead of Digital Mars runtime (so, if it was compiled as 64-bit or it used whatever the flag is to force the COFF format instead of OMF for 32-bit). It won't work on any other platform at compile time, because core.stdc.math.fmodl is a C function, and C functions cannot be called at compile time.

By any chance were you compiling this on Windows previously rather than Linux? If so, that's likely why the code worked before and doesn't now. If you were always compiling on Linux, then I don't know why you were able to compile your code before. A quick glance at the repo history shows that fmod has had that same implementation since 2012, so there should be no way that it was callable on any Linux system even 6 years ago, let alone 6 months ago.

- Jonathan M Davis



September 06, 2019
On Friday, 6 September 2019 at 00:41:12 UTC, Jonathan M Davis wrote:
> On Thursday, September 5, 2019 6:24:07 PM MDT Jamie via Digitalmars-d-learn wrote:
>>
>> /home/jamie/.dvm/compilers/dmd-2.077.0/linux/bin/../../src/phobos/std/math
>> .d(3702): Error: fmodl cannot be interpreted at compile time, because it
>> has no available source code called from here: called from here:
>> fmod(cast(real)to(n),
>> cast(real)to(2))
>>
>> Thanks
>
> Based on that implementation, fmod would work at compile time if you compile on Windows and use Microsoft's runtime instead of Digital Mars runtime (so, if it was compiled as 64-bit or it used whatever the flag is to force the COFF format instead of OMF for 32-bit). It won't work on any other platform at compile time, because core.stdc.math.fmodl is a C function, and C functions cannot be called at compile time.
>
> By any chance were you compiling this on Windows previously rather than Linux? If so, that's likely why the code worked before and doesn't now. If you were always compiling on Linux, then I don't know why you were able to compile your code before. A quick glance at the repo history shows that fmod has had that same implementation since 2012, so there should be no way that it was callable on any Linux system even 6 years ago, let alone 6 months ago.
>
> - Jonathan M Davis

Thanks for the reply. I haven't used Windows in the past two years so that isn't the issue in my case. After your comments I looked deeper, and I think my issue is related to compile time values, and me misunderstanding default arguments for structs.

import std.stdio;
import std.conv: to;
import std.math: fmod;

void main()
{
    /// without default arguments
    S s = S(64, 64);            // case 1: works
    // static S s = S(64, 64);     // case 2: doesn't work

    /// with default arguments
    // static S s = S();           // case 3: works
}

struct S
{
    this(size_t N, size_t M) {
    // this(size_t N=64, size_t M=64) {
        f(N, M);
    }
}

void f(size_t N, size_t M)
{
    int kj(int n)
    {
        return to!int(fmod(n, 2));
    }
    int k = kj(2);
}

If I'm understanding it correctly now, in case 1 the struct constructor isn't called at compile time, so the underlying C function (fmod) is not called. In case 2 because the struct is called in a static region, the constructor is called at compile time and fmod is called so it breaks. In case 3, with default struct arguments, I thought that the constructor I have defined was being called, however the default constructor was being called (this()) so fmod wasn't being called.

The reason why my old code worked was because it used the default arguments and I wasn't actually calling the constructor I defined. When I removed the default arguments in the constructor and tried case 2 it obviously didn't work.

Am I understanding correctly? Thanks


September 06, 2019
On Friday, 6 September 2019 at 05:59:30 UTC, Jamie wrote:

> time and fmod is called so it breaks. In case 3, with default struct arguments, I thought that the constructor I have defined was being called, however the default constructor was being called (this()) so fmod wasn't being called.
>
> The reason why my old code worked was because it used the default arguments and I wasn't actually calling the constructor I defined. When I removed the default arguments in the constructor and tried case 2 it obviously didn't work.
>
> Am I understanding correctly? Thanks

You're right about case 1 and case 2, and partially correct about case 3. Structs don't actually have default constructors. They have default *initializers*. In other words, s = S() is the same as s = S.init. See item #4 in the documentation for struct destructors [1]:

"If the ParameterList is empty, the struct instance is default initialized."

So yes, your constructor is not being called.

1. https://dlang.org/spec/struct.html#struct-constructor


September 06, 2019
On Thursday, September 5, 2019 11:59:30 PM MDT Jamie via Digitalmars-d-learn wrote:
> On Friday, 6 September 2019 at 00:41:12 UTC, Jonathan M Davis
>
> wrote:
> > On Thursday, September 5, 2019 6:24:07 PM MDT Jamie via
> >
> > Digitalmars-d-learn wrote:
> >> /home/jamie/.dvm/compilers/dmd-2.077.0/linux/bin/../../src/phobos/std/m
> >> ath .d(3702): Error: fmodl cannot be interpreted at compile time,
> >> because it
> >> has no available source code called from here: called from
> >> here:
> >> fmod(cast(real)to(n),
> >> cast(real)to(2))
> >>
> >> Thanks
> >
> > Based on that implementation, fmod would work at compile time if you compile on Windows and use Microsoft's runtime instead of Digital Mars runtime (so, if it was compiled as 64-bit or it used whatever the flag is to force the COFF format instead of OMF for 32-bit). It won't work on any other platform at compile time, because core.stdc.math.fmodl is a C function, and C functions cannot be called at compile time.
> >
> > By any chance were you compiling this on Windows previously rather than Linux? If so, that's likely why the code worked before and doesn't now. If you were always compiling on Linux, then I don't know why you were able to compile your code before. A quick glance at the repo history shows that fmod has had that same implementation since 2012, so there should be no way that it was callable on any Linux system even 6 years ago, let alone 6 months ago.
> >
> > - Jonathan M Davis
>
> Thanks for the reply. I haven't used Windows in the past two years so that isn't the issue in my case. After your comments I looked deeper, and I think my issue is related to compile time values, and me misunderstanding default arguments for structs.
>
> import std.stdio;
> import std.conv: to;
> import std.math: fmod;
>
> void main()
> {
>      /// without default arguments
>      S s = S(64, 64);            // case 1: works
>      // static S s = S(64, 64);     // case 2: doesn't work
>
>      /// with default arguments
>      // static S s = S();           // case 3: works
> }
>
> struct S
> {
>      this(size_t N, size_t M) {
>      // this(size_t N=64, size_t M=64) {
>          f(N, M);
>      }
> }
>
> void f(size_t N, size_t M)
> {
>      int kj(int n)
>      {
>          return to!int(fmod(n, 2));
>      }
>      int k = kj(2);
> }
>
> If I'm understanding it correctly now, in case 1 the struct constructor isn't called at compile time, so the underlying C function (fmod) is not called. In case 2 because the struct is called in a static region, the constructor is called at compile time and fmod is called so it breaks. In case 3, with default struct arguments, I thought that the constructor I have defined was being called, however the default constructor was being called (this()) so fmod wasn't being called.
>
> The reason why my old code worked was because it used the default arguments and I wasn't actually calling the constructor I defined. When I removed the default arguments in the constructor and tried case 2 it obviously didn't work.
>
> Am I understanding correctly? Thanks

Structs in D do not have default constructors. They have init values, where the value is what their members are directly initialized to. e.g.

struct S
{
    int i = 5;
    bool b;
}

void main()
{
    assert(S.init.i == 5);
    assert(S.init.b == false);
    S s;
    assert(s.i == 5);
    assert(s.b == false);
}

The init value must be known at compile time, so if you directly initialize any members with anything, that expression must be evaluated at compile time (meaning that you can't do something like use a C function to directly initialize a member variable).

If you declare a constructor for a struct with no parameters, it will _never_ be called. Similarly, a constructor with all default arguments will never be called unless it's explicitly called with at least one argument. And in fact, at least with the version of the compiler I have locally, the compiler gives an error if I try to declare a struct constructor with either no parameters or where all of its parameters have default arguments.

Under normal circumstances, in D, all variables are default initialized if they're not explicitly initialized. So, when you have something like

void main()
{
    int i;
    static int j;
}

both i and j are default initialized to the init value of int (which is 0). However, local variables are different from other variables in that if you explicitly initialize them, that's done at runtime. So, if you have

void main()
{
    int i = foo();
    static int j = bar();
}

then foo will be called at runtime to initialize i, whereas because j is static, its initial value must be known at compile time, and so bar is called at compile time just like happens with member variables. Similarly, enums must have their value known at compile time, so the code to determine their value is run at compile time (though since an enum has no address, its value essentially gets copy-pasted everywhere the enum is used).

If a struct's constructor is called to initialize a variable, then it's called whenever is appropriate based on the variable being initialized. e.g. in

void main()
{
    MyStruct a = MyStruct(42);
    static MyStruct b = MyStruct(42);
}

for a, the constructor is called at runtime to initialize the variable, whereas for b, it's called at compile time. Similarly, if a member variable is a struct, and it's directly initialized with a constructor call, then that constructor call is done at compile time, whereas if it were done in the constructor of the enclosing type, then it would be done whenever that constructor was called. So, whether a constructor is called at compile time or runtime depends entirely on the context in which it's used - but because structs don't have default constructors, you'll never get a constructor being called implicitly.

Unlike D structs, D classes _can_ have constructors with no parameters, but that's because they're reference types, and their init value is null (and even then, the constructor isn't a default constructor, because it still has te be called explicitly - a class variable with no constructor call is null). The reason that structs don't have default constructors is because D requires that the init value of a type has to be known at compile time, and having a piece of code run when a struct isn't explicitly initialized is incompatible with that.

- Jonathan M Davis