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

Macro returning static int in type section: intVal is not accessible for TFullReg #16774

Closed mratsim closed 1 month ago

mratsim commented 3 years ago

When using a macro to compute the static int that parametrize a static type in a type section, we get

.....fatal.nim(53)            sysFatal
Error: unhandled exception: 'intVal' is not accessible using discriminant 'kind' of type 'TFullReg' [FieldDefect]

Test case

import macros

type SecretWord = distinct uint64
const WordBitWidth = 8 * sizeof(uint64)

func wordsRequired*(bits: int): int {.compileTime.} =
  ## Compute the number of limbs required
  # from the **announced** bit length
  (bits + WordBitWidth - 1) div WordBitWidth

type
  Curve = enum
    BLS12_381

  BigInt*[bits: static int] = object
    limbs*: array[bits.wordsRequired, SecretWord]

const BLS12_381_Modulus = default(BigInt[381])

{.experimental: "dynamicBindSym".}
macro Mod*(C: static Curve): untyped =
  ## Get the Modulus associated to a curve
  result = bindSym($C & "_Modulus")

macro getCurveBitwidth*(C: static Curve): untyped =
  result = nnkDotExpr.newTree(
    getAST(Mod(C)),
    ident"bits"
  )

type
  Fp*[C: static Curve] = object
    ## Finite Fields / Modular arithmetic
    ## modulo the curve modulus
    mres*: BigInt[getCurveBitwidth(C)]

var x: Fp[BLS12_381]
timotheecour commented 3 years ago

@mratsim please minimize; note that https://github.com/nim-lang/Nim/pull/17590 fixes https://github.com/nim-lang/Nim/issues/14585 but not this issue, however it could very well be that a similar fix as https://github.com/nim-lang/Nim/pull/17590 would fix this too

mratsim commented 3 years ago

Come on, it's a single file with 37 lines. It's also the minimum translation of my data structure and what I attempted to do.

Given how distinct, static, symbol resolution/bindsym, generics, {.compileTime.} and types interact with each other and the useless error message, I think that's reasonable minimal reproducing example.

can-lehmann commented 2 years ago

I tried to minimize this a bit more and obtained the following code:

import macros

macro makeIntLit(c: static int): untyped =
  result = newLit(c)

type Test*[T: static int] = object
  myArray: array[makeIntLit(T), int]

It turns out, that the compiler passes the NimNode "T" to makeIntLit, instead of an int. Therefore its register is of kind rkNode and the call to newLit fails. In the original example, the same thing happens: Instead of passing the enum value BLS12_381, the node "C" is passed to the Mod macro.

I see two ways to solve this:

  1. defer the macro expansion of macros with typed arguments until after generic instantiation (might break existing code, not sure how feasible this is)
  2. report an error that T is not a static value