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.57k stars 1.47k forks source link

template parameter type checking broken #11794

Open timotheecour opened 5 years ago

timotheecour commented 5 years ago

type checking for templates doesn't work the same as for procs, causing bugs

Example0

can anyone spot the bug in swapEndian ?

template swapEndian*(outp, inp: var uint64) =
    ## copies `inp` to `outp` swapping bytes
    var i = cast[cstring](addr inp)
    var o = cast[cstring](addr outp)
    o[0] = i[7]
    o[1] = i[6]
    o[2] = i[5]
    o[3] = i[4]
    o[4] = i[3]
    o[5] = i[2]
    o[6] = i[1]
    o[7] = i[0]

despite looking type safe, it isn't so:

  type Foo = object
    output, input: uint16
    z: uint16
  var x = Foo(input: 100, z: 1000)
  echo x # (output: 0, input: 100, z: 1000)
  swapEndian(x.output, x.input)
  echo x # (output: 0, input: 0, z: 771) # z was modified under the hood! the compiler didn't complain about implicit conversion from var uint16 to var uint64

If we used a proc instead of a template, the compiler would (correctly) complain: Error: for a 'var' type a variable needs to be passed; but 'uint64(x.output)' is immutable

Example1

  template runTest[T](a, b: T) =
    static:
      const s = $T
      echo ($T, $type(a), $type(b))
  proc main()=
    var a: int
    var b: int32
    static: echo "trying (int,int32):"
    runTest(a, b)

    var c: float
    static: echo "trying float:"
    runTest(c, c)
  main()

Current Output

trying (int,int32):
("9223372036854775807", "int", "int32")
trying float:
/Users/timothee/git_clone/nim/Nim_devel/compiler/lineinfos.nim(229) raiseRecoverableError
Error: unhandled exception: cannot extract number from invalid AST node [ERecoverableError]

there's a few bugs here:

Example2

in this example the signature matching itself should fail. Contrary to previous example, the bug happens during signature matching, which should fail (ie, not implicit conversion possible bc of var), but doesn't

  # proc runTest[T](a: var T, b: var T) = # this would (correctly) give Error: ... 'float64(b)' is immutable
  template runTest[T](a: var T, b: var T) =
    echo (cast[int](addr a), cast[int](addr b))
    b = a
  proc main()=
    var a: float64 = 3.14
    var b: float32 = 1.42
    runTest(a, b)
    echo (a,b)
  main()

output:

# should give CT error instead of compiling/running
(140732816759008, 140732816759004)
(3.14, 3.140000104904175)  # doesn't actually matter, just that it shouldn't compile

[EDIT]

related issues

Additional Information

Araq commented 5 years ago

The problem here is that var T parameter passing makes no sense for template, template can always mutate due to its inlining semantics. IMO var T for parameters in templates should be deprecated.

zah commented 5 years ago

You can still use var as a discriminator in overloading. The template can do one thing if the location is mutable and another if it isn't