From 7cbf0ee53c0dd86b1b1c2ec9cff7d8bfb845d407 Mon Sep 17 00:00:00 2001 From: metagn Date: Sun, 18 Aug 2024 01:52:32 +0300 Subject: [PATCH] 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. (cherry picked from commit f7c11a8978a1fc7182ef18c4bdc80e920ce6ad88) --- compiler/semdata.nim | 1 + compiler/semexprs.nim | 6 ++++-- compiler/semmagic.nim | 2 ++ compiler/semtypes.nim | 4 ++++ tests/vm/tgenericcompiletimeproc.nim | 29 ++++++++++++++++++++++++++++ 5 files changed, 40 insertions(+), 2 deletions(-) create mode 100644 tests/vm/tgenericcompiletimeproc.nim diff --git a/compiler/semdata.nim b/compiler/semdata.nim index efde71438585..c2836b04e2c1 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -170,6 +170,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 10057275accb..c0edfa9d3bc1 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -971,7 +971,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 @@ -1117,7 +1118,8 @@ proc afterCallActions(c: PContext; n, orig: PNode, flags: TExprFlags; expectedTy not (result.typ.kind == tySequence and result.typ[0].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 57b57d366825..d3c0a4b83465 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -47,7 +47,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 11a6127f8de9..425100554be1 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -1806,7 +1806,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 @@ -1820,7 +1822,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 000000000000..cb4dbb2d8df8 --- /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)))