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

Crash on call result of getCustomPragmaVal if result is generic proc #16108

Open gabbhack opened 3 years ago

gabbhack commented 3 years ago

Example

import macros

template skipSerializeIf(condition: typed{`proc`}) {.pragma.}

proc alwaysTrue[T](x: T): bool = true
# also crush on
# proc alwaysTrue(x: any): bool = true
# proc alwaysTrue(x: auto): bool = true
# proc alwaysTrue(x: string | int): bool = true

type
  Test = object
    id {.skipSerializeIf(alwaysTrue).}: string

let t = Test()
echo t.id.getCustomPragmaVal(skipSerializeIf).type  # proc (x: T): bool
echo t.id.getCustomPragmaVal(skipSerializeIf)(t.id)  # crush on this line

https://play.nim-lang.org/#ix=2Fdq

Current Output

Error: internal error: getTypeDescAux(tyGenericParam)
No stack traceback available
To create a stacktrace, rerun compilation with './koch temp c <file>', see https://nim-lang.github.io/Nim/intern.html#debugging-the-compiler for details

Expected Output

proc (x: T): bool
true
$ nim -v
Nim Compiler Version 1.4.0 [Windows: amd64]
Compiled at 2020-10-16
Copyright (c) 2006-2020 by Andreas Rumpf

active boot switches: -d:release
gabbhack commented 3 years ago

Hack:

import macros

template skipSerializeIf(condition: typed{`proc`}) {.pragma.}

proc alwaysTrue[T](x: T): bool = true
# also crush on
# proc alwaysTrue(x: any): bool = true
# proc alwaysTrue(x: auto): bool = true
# proc alwaysTrue(x: string | int): bool = true

template hack[T](x: T): T = x

type
  Test = object
    id {.skipSerializeIf(alwaysTrue).}: string

let t = Test()
echo t.id.getCustomPragmaVal(skipSerializeIf).type  # proc (x: T): bool
echo hack(t.id.getCustomPragmaVal(skipSerializeIf)(t.id))

https://play.nim-lang.org/#ix=2FgG

cooldome commented 3 years ago

Error message could have been better but I don't think this would ever compile. alwaysTrue proc instantiation needs to happen on skipSerializeIf invocation but it is no possible at this stage and there is no mechanism to delay it.

Here is better design:

import macros

template skipSerializeIf(cond: bool) {.pragma.}

macro toString(x: untyped): string = 
  # used just to test
  newLit(repr(x))

type
  Test = object
    id {.skipSerializeIf(true).}: string
    id2 {.skipSerializeIf(toString(id2) == "id2").}: string
    id3 {.skipSerializeIf(toString(id3) == "id2").}: string

let t = Test()
echo t.id.getCustomPragmaVal(skipSerializeIf).type
echo t.id.getCustomPragmaVal(skipSerializeIf)
echo t.id2.getCustomPragmaVal(skipSerializeIf).type
echo t.id2.getCustomPragmaVal(skipSerializeIf)
echo t.id3.getCustomPragmaVal(skipSerializeIf).type
echo t.id3.getCustomPragmaVal(skipSerializeIf)
gabbhack commented 3 years ago

@cooldome I may not understand you, but with your design I won't be able to work with values at runtime.

I need something like this:

import options

type
  Foo = object
      id: int
      someOption {.skipSerializeIf(isNone).}: Option[string]

let f = Foo(id: 123)
# in ser isNone is called with the value of someOption field
assert ser(f) == """{"id": 123}"""

I can do this using the hack posted above.

gabbhack commented 3 years ago

Strange behavior, static does not crash and gives the wrong result

import macros

template skipSerializeIf(condition: typed{`proc`}) {.pragma.}

proc alwaysTrue[T](x: T): bool = true
# also crush on
# proc alwaysTrue(x: any): bool = true
# proc alwaysTrue(x: auto): bool = true
# proc alwaysTrue(x: string | int): bool = true
template hack[T](x: T): T = x

type
  Test = object
    id {.skipSerializeIf(alwaysTrue).}: string

const t = Test()
static:
  echo t.id.getCustomPragmaVal(skipSerializeIf)(t.id)  # false
  echo hack(t.id.getCustomPragmaVal(skipSerializeIf)(t.id))  # true

https://play.nim-lang.org/#ix=2GnS