summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--compiler/semfold.nim2
-rw-r--r--tests/macros/t23784.nim157
2 files changed, 158 insertions, 1 deletions
diff --git a/compiler/semfold.nim b/compiler/semfold.nim
index 466df2e4e..10361eb79 100644
--- a/compiler/semfold.nim
+++ b/compiler/semfold.nim
@@ -703,7 +703,7 @@ proc getConstExpr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode
   of nkAddr:
     var a = getConstExpr(m, n[0], idgen, g)
     if a != nil:
-      result = n
+      result = nil # don't fold paths containing nkAddr
       n[0] = a
   of nkBracket, nkCurly:
     result = copyNode(n)
diff --git a/tests/macros/t23784.nim b/tests/macros/t23784.nim
new file mode 100644
index 000000000..31b4544c6
--- /dev/null
+++ b/tests/macros/t23784.nim
@@ -0,0 +1,157 @@
+discard """
+  joinable: false
+"""
+
+
+# debug ICE: genCheckedRecordField
+# apparently after https://github.com/nim-lang/Nim/pull/23477
+
+# bug #23784
+
+import std/bitops, std/macros
+
+# --------------------------------------------------------------
+
+type Algebra = enum
+  BN254_Snarks
+
+type SecretWord* = distinct uint64
+const WordBitWidth* = sizeof(SecretWord) * 8
+
+func wordsRequired*(bits: int): int {.inline.} =
+  const divShiftor = fastLog2(WordBitWidth)
+  result = (bits + WordBitWidth - 1) shr divShiftor
+
+type
+  BigInt*[bits: static int] = object
+    limbs*: array[bits.wordsRequired, SecretWord]  # <--- crash points to here
+
+# --------------------------------------------------------------
+
+const CurveBitWidth = [
+  BN254_Snarks: 254
+]
+
+const BN254_Snarks_Modulus = BigInt[254](limbs: [SecretWord 0x1, SecretWord 0x2, SecretWord 0x3, SecretWord 0x4])
+const BN254_Snarks_Order = BigInt[254](limbs: [SecretWord 0x1, SecretWord 0x1, SecretWord 0x2, SecretWord 0x2])
+
+func montyOne*(M: BigInt[254]): BigInt[254] =
+  ## Returns "1 (mod M)" in the Montgomery domain.
+  ## This is equivalent to R (mod M) in the natural domain
+  BigInt[254](limbs: [SecretWord 0x1, SecretWord 0x1, SecretWord 0x1, SecretWord 0x1])
+
+
+{.experimental: "dynamicBindSym".}
+
+type
+  DerivedConstantMode* = enum
+    kModulus
+    kOrder
+
+macro genDerivedConstants*(mode: static DerivedConstantMode): untyped =
+  ## Generate constants derived from the main constants
+  ##
+  ## For example
+  ## - the Montgomery magic constant "R^2 mod N" in ROM
+  ##   For each curve under the private symbol "MyCurve_R2modP"
+  ## - the Montgomery magic constant -1/P mod 2^Wordbitwidth
+  ##   For each curve under the private symbol "MyCurve_NegInvModWord
+  ## - ...
+
+  # Now typedesc are NimNode and there is no way to translate
+  # NimNode -> typedesc easily so we can't
+  # "for curve in low(Curve) .. high(Curve):"
+  # As an ugly workaround, we count
+  # The item at position 0 is a pragma
+  result = newStmtList()
+
+  template used(name: string): NimNode =
+    nnkPragmaExpr.newTree(
+      ident(name),
+      nnkPragma.newTree(ident"used")
+    )
+
+  let ff = if mode == kModulus: "_Fp" else: "_Fr"
+
+  for curveSym in low(Algebra) .. high(Algebra):
+    let curve = $curveSym
+    let M = if mode == kModulus: bindSym(curve & "_Modulus")
+            else: bindSym(curve & "_Order")
+
+    # const MyCurve_montyOne = montyOne(MyCurve_Modulus)
+    result.add newConstStmt(
+      used(curve & ff & "_MontyOne"), newCall(
+        bindSym"montyOne",
+        M
+      )
+    )
+
+# --------------------------------------------------------------
+
+{.experimental: "dynamicBindSym".}
+
+genDerivedConstants(kModulus)
+genDerivedConstants(kOrder)
+
+proc bindConstant(ff: NimNode, property: string): NimNode =
+  # Need to workaround https://github.com/nim-lang/Nim/issues/14021
+  # which prevents checking if a type FF[Name] = Fp[Name] or Fr[Name]
+  # was instantiated with Fp or Fr.
+  # getTypeInst only returns FF and sameType doesn't work.
+  # so quote do + when checks.
+  let T = getTypeInst(ff)
+  T.expectKind(nnkBracketExpr)
+  doAssert T[0].eqIdent("typedesc")
+
+  let curve =
+    if T[1].kind == nnkBracketExpr: # typedesc[Fp[BLS12_381]] as used internally
+      # doAssert T[1][0].eqIdent"Fp" or T[1][0].eqIdent"Fr", "Found ident: '" & $T[1][0] & "' instead of 'Fp' or 'Fr'"
+      T[1][1].expectKind(nnkIntLit) # static enum are ints in the VM
+      $Algebra(T[1][1].intVal)
+    else: # typedesc[bls12381_fp] alias as used for C exports
+      let T1 = getTypeInst(T[1].getImpl()[2])
+      if T1.kind != nnkBracketExpr or
+         T1[1].kind != nnkIntLit:
+        echo T.repr()
+        echo T1.repr()
+        echo getTypeInst(T1).treerepr()
+        error "getTypeInst didn't return the full instantiation." &
+          " Dealing with types in macros is hard, complain at https://github.com/nim-lang/RFCs/issues/44"
+      $Algebra(T1[1].intVal)
+
+  let curve_fp = bindSym(curve & "_Fp_" & property)
+  let curve_fr = bindSym(curve & "_Fr_" & property)
+  result = quote do:
+    when `ff` is Fp:
+      `curve_fp`
+    elif `ff` is Fr:
+      `curve_fr`
+    else:
+      {.error: "Unreachable, received type: " & $`ff`.}
+
+# --------------------------------------------------------------
+
+template matchingBigInt*(Name: static Algebra): untyped =
+  ## BigInt type necessary to store the prime field Fp
+  # Workaround: https://github.com/nim-lang/Nim/issues/16774
+  # as we cannot do array accesses in type section.
+  # Due to generic sandwiches, it must be exported.
+  BigInt[CurveBitWidth[Name]]
+
+type
+  Fp*[Name: static Algebra] = object
+    mres*: matchingBigInt(Name)
+
+macro getMontyOne*(ff: type Fp): untyped =
+  ## Get one in Montgomery representation (i.e. R mod P)
+  result = bindConstant(ff, "MontyOne")
+
+func getOne*(T: type Fp): T {.noInit, inline.} =
+  result = cast[ptr T](unsafeAddr getMontyOne(T))[]
+
+# --------------------------------------------------------------
+proc foo(T: Fp) =
+  discard T
+
+let a = Fp[BN254_Snarks].getOne()
+foo(a) # oops this was a leftover that broke the bisect.