nim-lang / Nim

Nim is a statically typed compiled systems programming language. It combines successful concepts from mature languages like Python, Ada and Modula. Its design focuses on efficiency, expressiveness, and elegance (in that order of priority).
https://nim-lang.org
Other
16.55k stars 1.47k forks source link

SEGFAULT caused by type coercion of distinct types #14482

Open zah opened 4 years ago

zah commented 4 years ago

Consider the following program:

type
  Bytes = seq[byte]
  RedBytes = distinct Bytes
  GreenBytes[T] = distinct Bytes

proc worksWithGreenBytes[T](data: var GreenBytes[T]) =
  Bytes(data)[0] = byte(0)

proc worksWithRedBytes(data: var RedBytes) =
  # Attempt one, let's tell Nim that our red bytes can
  # be used as green bytes in this special case:
  when false:
    worksWithGreenBytes GreenBytes[int](data)
    # Fails with:
    # /home/zahary/nim/scratch/wrong_coercion.nim(12, 22) Error: type mismatch: got <GreenBytes[system.int]>
    # but expected one of: 
    # proc worksWithGreenBytes[T](data: var GreenBytes[T])
    #   first type mismatch at position: 1
    #   required type for data: var GreenBytes[worksWithGreenBytes.T]
    #   but expression 'GreenBytes[int](data)' is immutable, not 'var'

  # Aha! Let's fix that in a clever way:
  type GreenBytesVar = var GreenBytes[int]
  worksWithGreenBytes GreenBytesVar(data)

  # The program now compiles, but segfaults:
  # SIGSEGV: Illegal storage access. (Attempt to read from nil?)

var x = RedBytes @[1.byte, 2, 3]
worksWithRedBytes x

The when false statement demonstrates a coercion error that happens only when the GreenBytes distinct type is also a generic type.

The alternative way to force the coercion tricks the compiler into generating the following invalid code:

N_LIB_PRIVATE N_NIMCALL(void, worksWithGreenBytes__gm84k5EAEXjiXAGaLs4PxA)(tySequence__6H5Oh5UUvVCLiakt9aTwtUQ** data) {
    if ((NU)(((NI) 0)) >= (NU)((*data) ? (*data)->Sup.len : 0)){ raiseIndexError2(((NI) 0),((*data) ? (*data)->Sup.len : 0)-1); }
    (*data)->data[((NI) 0)] = ((NU8) 0);
}
N_LIB_PRIVATE N_NIMCALL(void, worksWithRedBytes__PvYV6DaJwxv9bcad9bhlyHYw)(tySequence__6H5Oh5UUvVCLiakt9aTwtUQ** data) {
    worksWithGreenBytes__gm84k5EAEXjiXAGaLs4PxA((*data));
}

Notice how data is dereferenced unnecessarily which leads to a segfault. It should be noted that the code works correctly in C++ mode, where the compiler uses reference types.

Nim Compiler Version 1.3.5 [Linux: amd64]
Compiled at 2020-05-28
Copyright (c) 2006-2020 by Andreas Rumpf

git hash: fe7a2d60f90ac48b296b637401da2724dd160954
active boot switches: -d:release
metagn commented 1 month ago

but expression 'GreenBytesint' is immutable, not 'var'

This was fixed in #18837, the remaining problem is converting to a var type which is lower priority, a more simple case is:

type Foo = distinct int

var x = Foo(1)
type IntVar = var int
IntVar(x) += 1
/usercode/nimcache/@min.nim.c: In function 'NimMainModule':
/usercode/nimcache/@min.nim.c:136:48: error: invalid type argument of unary '*' (have 'NI' {aka 'long long int'})
  136 |         TM__JXJjWlLylejo6nWONmO6cA_2 = addInt((*x__EGWKGClUlppqAonIP2rgfg), ((NI) 1));
      |                                                ^~~~~~~~~~~~~~~~~~~~~~~~~~
/usercode/nimcache/@min.nim.c:137:10: error: invalid type argument of unary '*' (have 'NI' {aka 'long long int'})
  137 |         (*x__EGWKGClUlppqAonIP2rgfg) = (NI)(TM__JXJjWlLylejo6nWONmO6cA_2);
      |          ^~~~~~~~~~~~~~~~~~~~~~~~~~