Thread overview
LDC2 Clobbering Local Variableswith std.typecons' nullable.
Aug 14, 2021
LeqxLeqx
Aug 14, 2021
kinke
Aug 14, 2021
kinke
Aug 14, 2021
kinke
Aug 14, 2021
LeqxLeqx
Aug 14, 2021
kinke
Aug 14, 2021
LeqxLeqx
Aug 14, 2021
kinke
August 14, 2021

I have encountered a very strange (apparent) error related to the use of std.typecons' nullable and unions. I appear to be able to clobber the immediately preceding stack variable to a function call if that function makes use of an implicit cast from a nullable wrapped struct to the unwrapped struct where the struct contains a union type which contains an array. Sorry for how strange these requirements are, but I cannot seem to replicate the issue without all of these. See this code:

// Arbitrary union with at least one array member.
union MyUnion
{
  int[] _array;
}

// Struct with the above union and at least one other member.
struct MyStruct
{
  private int _int; // required (for some reason)
  private MyUnion _MyUnion;
}

void main()
{
  import std.stdio : writefln;

  // setting local `myLocal' to something not null and then invoking
  // my method (defined below) `clobber' appears to set the value to
  // null.

  string myLocal = "NOT NULL";
  writefln("myLocal (before clobber): `%s'.", myLocal);
  clobber();
  writefln("myLocal (after clobber):  `%s'.", myLocal);
}

MyStruct clobber()
{
  import std.typecons : nullable;

  // create a new struct and return it.
  // Must use the implicit cast. Using `.get' doesn't induce the bug

  auto ret = nullable(MyStruct());
  return ret;
}

The output of this program when compiled under ldc2 is:

myLocal (before clobber): `NOT NULL'.
myLocal (after clobber):  `'.

Oddly enough, for -O1 and above, the output is the more correct:

myLocal (before clobber): `NOT NULL'.
myLocal (after clobber):  `NOT NULL'.

(of note, for -O1 but not -O0 or -O2 and above, the program terminates with a SIGSEV)

Further, when compiled by GDC, the output appears correct.

I'm running all of this on Debian Buster and the below is the output of my ldc2 --version:

LDC - the LLVM D compiler (1.12.0):
  based on DMD v2.082.1 and LLVM 6.0.1
  built with LDC - the LLVM D compiler (1.12.0)
  Default target: x86_64-pc-linux-gnu
  Host CPU: ivybridge
  http://dlang.org - http://wiki.dlang.org/LDC

  Registered Targets:
    aarch64    - AArch64 (little endian)
    aarch64_be - AArch64 (big endian)
    amdgcn     - AMD GCN GPUs
    arm        - ARM
    arm64      - ARM64 (little endian)
    armeb      - ARM (big endian)
    avr        - Atmel AVR Microcontroller
    bpf        - BPF (host endian)
    bpfeb      - BPF (big endian)
    bpfel      - BPF (little endian)
    hexagon    - Hexagon
    lanai      - Lanai
    mips       - Mips
    mips64     - Mips64 [experimental]
    mips64el   - Mips64el [experimental]
    mipsel     - Mipsel
    msp430     - MSP430 [experimental]
    nvptx      - NVIDIA PTX 32-bit
    nvptx64    - NVIDIA PTX 64-bit
    ppc32      - PowerPC 32
    ppc64      - PowerPC 64
    ppc64le    - PowerPC 64 LE
    r600       - AMD GPUs HD2XXX-HD6XXX
    sparc      - Sparc
    sparcel    - Sparc LE
    sparcv9    - Sparc V9
    systemz    - SystemZ
    thumb      - Thumb
    thumbeb    - Thumb (big endian)
    wasm32     - WebAssembly 32-bit
    wasm64     - WebAssembly 64-bit
    x86        - 32-bit X86: Pentium-Pro and above
    x86-64     - 64-bit X86: EM64T and AMD64
    xcore      - XCore

Am I crazy? Or is this a compiler bug?

Any and all assistance would be much appreciated.

August 14, 2021

On Saturday, 14 August 2021 at 03:13:00 UTC, LeqxLeqx wrote:

>

Or is this a compiler bug?

Might be, but you're using a version that's almost 4 years old. With run.dlang.io and its current LDC v1.26 (not the latest version either...), your testcase works with all optimization levels (line 36 needs a .get() since Phobos v2.096).

August 14, 2021

On Saturday, 14 August 2021 at 03:41:33 UTC, kinke wrote:

>

version that's almost 4 years old

2021-2018=3, my bad. :D - Still kind-of stone-age with a new release every ~2 months. ;)

August 14, 2021

On Saturday, 14 August 2021 at 03:41:33 UTC, kinke wrote:

>

line 36 needs a .get() since Phobos v2.096

Since v2.097, it was just a deprecation before. And it makes all the difference, i.e., v1.26.0 still fails (and segfaults with -O1) without the .get(). Interesting, will look into it in the near future.

August 14, 2021

On Saturday, 14 August 2021 at 04:03:02 UTC, kinke wrote:

>

On Saturday, 14 August 2021 at 03:41:33 UTC, kinke wrote:

>

line 36 needs a .get() since Phobos v2.096

Since v2.097, it was just a deprecation before. And it makes all the difference, i.e., v1.26.0 still fails (and segfaults with -O1) without the .get(). Interesting, will look into it in the near future.

Oh, my bad. I didn't realize that apt repo was so far out of date. I'll install a newer version.

Thank you very much!

August 14, 2021

On Saturday, 14 August 2021 at 04:03:02 UTC, kinke wrote:

>

Interesting, will look into it in the near future.

Pretty bad, and not LDC-specific: https://issues.dlang.org/show_bug.cgi?id=22209

August 14, 2021

On Saturday, 14 August 2021 at 04:21:21 UTC, kinke wrote:

>

On Saturday, 14 August 2021 at 04:03:02 UTC, kinke wrote:

>

Interesting, will look into it in the near future.

Pretty bad, and not LDC-specific: https://issues.dlang.org/show_bug.cgi?id=22209

Wow, that's something, eh? Thank you very much for analyzing this and getting to the root of the issue!

August 14, 2021

On Saturday, 14 August 2021 at 18:09:27 UTC, LeqxLeqx wrote:

>

On Saturday, 14 August 2021 at 04:21:21 UTC, kinke wrote:

>

On Saturday, 14 August 2021 at 04:03:02 UTC, kinke wrote:

>

Interesting, will look into it in the near future.

Pretty bad, and not LDC-specific: https://issues.dlang.org/show_bug.cgi?id=22209

Wow, that's something, eh? Thank you very much for analyzing this and getting to the root of the issue!

Yeah, memory corruption caused by the compiler is one of the worst things. Thanks for reporting and providing the testcase! The fix should land in DMD v2.098 / LDC v1.28.