summary refs log tree commit diff stats
diff options
context:
space:
mode:
authormetagn <metagngn@gmail.com>2024-08-18 01:52:32 +0300
committerGitHub <noreply@github.com>2024-08-18 00:52:32 +0200
commitf7c11a8978a1fc7182ef18c4bdc80e920ce6ad88 (patch)
tree084c92138cf165be22b69eb5096fc3208eb58b2a
parenta354b18fe189b61ab1ff29039f8cef9502fce934 (diff)
downloadNim-f7c11a8978a1fc7182ef18c4bdc80e920ce6ad88.tar.gz
allow generic compileTime proc folding (#22022)
fixes #10753, fixes #22021, refs #19365 (was fixed by #22029, but more
faithful test added)

For whatever reason `compileTime` proc calls did not fold if the proc
was generic ([since this folding was
introduced](https://github.com/nim-lang/Nim/commit/c25ffbf2622a197c15a4a3bd790b1bc788db2c7f#diff-539da3a63df08fa987f1b0c67d26cdc690753843d110b6bf0805a685eeaffd40)).
I'm guessing the intention was for *unresolved* generic procs to not
fold, which is now the logic.

Non-magic `compileTime` procs also now don't fold at compile time in
`typeof` contexts to avoid possible runtime errors (only the important)
and prevent double/needless evaluation.
-rw-r--r--compiler/semdata.nim1
-rw-r--r--compiler/semexprs.nim6
-rw-r--r--compiler/semmagic.nim2
-rw-r--r--compiler/semtypes.nim4
-rw-r--r--tests/vm/tgenericcompiletimeproc.nim29
5 files changed, 40 insertions, 2 deletions
diff --git a/compiler/semdata.nim b/compiler/semdata.nim
index 12930feca..6065e9845 100644
--- a/compiler/semdata.nim
+++ b/compiler/semdata.nim
@@ -168,6 +168,7 @@ type
     inUncheckedAssignSection*: int
     importModuleLookup*: Table[int, seq[int]] # (module.ident.id, [module.id])
     skipTypes*: seq[PNode] # used to skip types between passes in type section. So far only used for inheritance, sets and generic bodies.
+    inTypeofContext*: int
   TBorrowState* = enum
     bsNone, bsReturnNotMatch, bsNoDistinct, bsGeneric, bsNotSupported, bsMatch
 
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index 731bd14dc..0147245af 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -970,7 +970,8 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode =
 
     if callee.magic notin ctfeWhitelist: return
 
-    if callee.kind notin {skProc, skFunc, skConverter, skConst} or callee.isGenericRoutine:
+    if callee.kind notin {skProc, skFunc, skConverter, skConst} or
+        callee.isGenericRoutineStrict:
       return
 
     if n.typ != nil and typeAllowed(n.typ, skConst, c) != nil: return
@@ -1118,7 +1119,8 @@ proc afterCallActions(c: PContext; n, orig: PNode, flags: TExprFlags; expectedTy
           not (result.typ.kind == tySequence and result.elementType.kind == tyEmpty):
         liftTypeBoundOps(c, result.typ, n.info)
     #result = patchResolvedTypeBoundOp(c, result)
-  if c.matchedConcept == nil:
+  if c.matchedConcept == nil and (c.inTypeofContext == 0 or callee.magic != mNone):
+    # don't fold calls in concepts and typeof
     result = evalAtCompileTime(c, result)
 
 proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode =
diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim
index c3c032147..1e579a959 100644
--- a/compiler/semmagic.nim
+++ b/compiler/semmagic.nim
@@ -49,7 +49,9 @@ proc semTypeOf(c: PContext; n: PNode): PNode =
     else:
       m = mode.intVal
   result = newNodeI(nkTypeOfExpr, n.info)
+  inc c.inTypeofContext
   let typExpr = semExprWithType(c, n[1], if m == 1: {efInTypeof} else: {})
+  dec c.inTypeofContext
   result.add typExpr
   result.typ = makeTypeDesc(c, typExpr.typ)
 
diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim
index 48d4f2c8d..1219d6ed8 100644
--- a/compiler/semtypes.nim
+++ b/compiler/semtypes.nim
@@ -1841,7 +1841,9 @@ proc semStaticType(c: PContext, childNode: PNode, prev: PType): PType =
 
 proc semTypeOf(c: PContext; n: PNode; prev: PType): PType =
   openScope(c)
+  inc c.inTypeofContext
   let t = semExprWithType(c, n, {efInTypeof})
+  dec c.inTypeofContext
   closeScope(c)
   fixupTypeOf(c, prev, t)
   result = t.typ
@@ -1855,7 +1857,9 @@ proc semTypeOf2(c: PContext; n: PNode; prev: PType): PType =
       localError(c.config, n.info, "typeof: cannot evaluate 'mode' parameter at compile-time")
     else:
       m = mode.intVal
+  inc c.inTypeofContext
   let t = semExprWithType(c, n[1], if m == 1: {efInTypeof} else: {})
+  dec c.inTypeofContext
   closeScope(c)
   fixupTypeOf(c, prev, t)
   result = t.typ
diff --git a/tests/vm/tgenericcompiletimeproc.nim b/tests/vm/tgenericcompiletimeproc.nim
new file mode 100644
index 000000000..cb4dbb2d8
--- /dev/null
+++ b/tests/vm/tgenericcompiletimeproc.nim
@@ -0,0 +1,29 @@
+block: # issue #10753
+  proc foo(x: int): int {.compileTime.} = x
+  const a = foo(123)
+  doAssert foo(123) == a
+
+  proc bar[T](x: T): T {.compileTime.} = x
+  const b = bar(123)
+  doAssert bar(123) == b
+  const c = bar("abc")
+  doAssert bar("abc") == c
+
+block: # issue #22021
+  proc foo(x: static int): int {.compileTime.} = x + 1
+  doAssert foo(123) == 124
+
+block: # issue #19365
+  proc f[T](x: static T): T {.compileTime.} = x + x
+  doAssert f(123) == 246
+  doAssert f(1.0) == 2.0
+
+block:
+  # don't fold compile time procs in typeof
+  proc fail[T](x: T): T {.compileTime.} =
+    doAssert false
+    x
+  doAssert typeof(fail(123)) is typeof(123)
+  proc p(x: int): int = x
+
+  type Foo = typeof(p(fail(123)))