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

template with sink parameter wrongfully compiles #16745

Open n5m opened 3 years ago

n5m commented 3 years ago

When a parameter to a template is a sink parameter, the compiler does not compile appropriately.

Setup

import json

type
  Decree = object
    common: int

type
  Command1 = object
    common: int
  Command2 = object
    uncommon: bool
  Command = Command1 | Command2

template convertCommandToDecree[T: typedesc[Command], R](
  input: sink T
): R =
  parseJson(json.pretty(%input)).to(typedesc[R])

This example is written with the following in mind:

  1. The return type will always be Decree because of how the template is invoked
  2. Only Commands should be allowed for input, namely either Command1 or Command2
  3. Decree and Command1 share a variable name and type in common, while Decree and Command2 do not

Current output with sink

OK: sink example 1

The following code should compile and runs successfully. Currently, it does compile and run.

echo $convertCommandToDecree[Command1, Decree](Command1())
user@user:~$ nim c --hints:off --gc:arc --run jj.nim 
(common: 0)

OK: sink example 2

The following code should not compile. Currently, it does not compile.

echo $convertCommandToDecree[Command1, Decree](Decree())
user@user:~$ nim c --hints:off --gc:arc --run jj.nim 
/home/user/jj.nim(19, 47) Error: type mismatch: got <Decree>
but expected one of: 
template convertCommandToDecree[T; R; ](input: sink T): R
  first type mismatch at position: 1
  required type for input: Command
  but expression 'Decree()' is of type: Decree

expression: convertCommandToDecree[Command1, Decree](Decree())

ERROR: sink example 3

The following code should not compile. Currently, it compiles but fails at runtime.

echo $convertCommandToDecree[Command1, Decree](Command2())
                             |                 |
                             |                 |
 note this is still Command1 ^                 |
                                               ^ but this is Command2
user@user:~$ nim c --hints:off --gc:arc --run jj.nim 
/home/user/jj.nim(19) jj
/home/user/nim/lib/pure/json.nim(1301) to
/home/user/nim/lib/pure/json.nim(1197) initFromJson
/home/user/nim/lib/pure/json.nim(1013) initFromJson
Error: unhandled exception: key not found: .common [KeyError]
Error: execution of an external program failed: '/home/user/jj '

Current output without sink

If the parameter to the template is changed to be a non-sink parameter, like so:

template convertCommandToDecree[T: typedesc[Command], R](
  input: T # no longer a sink parameter
): R =
  parseJson(json.pretty(%input)).to(typedesc[R])

then none of examples 1, 2, or 3 compile.

ERROR: non-sink example 1

echo $convertCommandToDecree[Command1, Decree](Command1())
user@user:~$ nim c --hints:off --gc:arc --run jj.nim 
/home/user/jj.nim(19, 47) Error: type mismatch: got <Command1>
but expected one of: 
template convertCommandToDecree[T; R](input: T): R
  first type mismatch at position: 1
  required type for input: T
  but expression 'Command1()' is of type: Command1

expression: convertCommandToDecree[Command1, Decree](Command1())

OK: non-sink example 2

echo $convertCommandToDecree[Command1, Decree](Decree())
user@user:~$ nim c --hints:off --gc:arc --run jj.nim 
/home/user/jj.nim(19, 47) Error: type mismatch: got <Decree>
but expected one of: 
template convertCommandToDecree[T; R](input: T): R
  first type mismatch at position: 1
  required type for input: T
  but expression 'Decree()' is of type: Decree

expression: convertCommandToDecree[Command1, Decree](Decree())

OK: non-sink example 3

echo $convertCommandToDecree[Command1, Decree](Command2())
user@user:~$ nim c --hints:off --gc:arc --run jj.nim 
/home/user/jj.nim(19, 47) Error: type mismatch: got <Command2>
but expected one of: 
template convertCommandToDecree[T; R](input: T): R
  first type mismatch at position: 1
  required type for input: T
  but expression 'Command2()' is of type: Command2

expression: convertCommandToDecree[Command1, Decree](Command2())

Summary

sink non-sink
example 1 OK error
example 2 OK OK
example 3 error OK

I don't know which behavior exactly is undesirable, but I think there is evidence of at least one bug here when all 6 examples are taken in unison.

Additional Information

Git hash 6c07b0a1f8daf96078ae68b83ead1d48675969d7

user@user:~$ nim --version
Nim Compiler Version 1.5.1 [Linux: amd64]
Compiled at 2021-01-18
Copyright (c) 2006-2021 by Andreas Rumpf

git hash: 6c07b0a1f8daf96078ae68b83ead1d48675969d7
active boot switches: -d:release
bung87 commented 1 year ago

@ringabout it's related to type relation and template, please remove the mm label