pragma for sfCallsite instead of name check + better semantics, test (#20464)

* pragma for sfCallsite instead of name check at every template definition

Not documented because it seems to be for internal use?

Should also make it possible to make comparisons and setops imports, but this doesn't have to be done.

I can reuse a name like `cursor` for the pragma as well, added a new name just to be safe.

* make sfCallsite recursive, add tests
This commit is contained in:
metagn
2022-10-03 07:07:55 +03:00
committed by GitHub
parent 81e7811e01
commit 2cca38d33c
13 changed files with 73 additions and 25 deletions

View File

@@ -1254,6 +1254,12 @@ proc skipPragmaExpr*(n: PNode): PNode =
else:
result = n
proc setInfoRecursive*(n: PNode, info: TLineInfo) =
## set line info recursively
if n != nil:
for i in 0..<n.safeLen: setInfoRecursive(n[i], info)
n.info = info
when defined(useNodeIds):
const nodeIdToDebug* = -1 # 2322968
var gNodeId: int

View File

@@ -143,4 +143,5 @@ proc initDefines*(symbols: StringTableRef) =
defineSymbol("nimHasTopDownInference")
defineSymbol("nimHasTemplateRedefinitionPragma")
defineSymbol("nimHasCstringCase")
defineSymbol("nimHasCallsitePragma")
defineSymbol("nimHasAmbiguousEnumHint")

View File

@@ -26,7 +26,7 @@ type
proc copyNode(ctx: TemplCtx, a, b: PNode): PNode =
result = copyNode(a)
if ctx.instLines: result.info = b.info
if ctx.instLines: setInfoRecursive(result, b.info)
proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) =
template handleParam(param) =
@@ -204,7 +204,7 @@ proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym;
result = copyNode(body)
ctx.instLines = sfCallsite in tmpl.flags
if ctx.instLines:
result.info = n.info
setInfoRecursive(result, n.info)
for i in 0..<body.safeLen:
evalTemplateAux(body[i], args, ctx, result)
result.flags.incl nfFromTemplate

View File

@@ -38,7 +38,7 @@ const
converterPragmas* = procPragmas
methodPragmas* = procPragmas+{wBase}-{wImportCpp}
templatePragmas* = {wDeprecated, wError, wGensym, wInject, wDirty,
wDelegator, wExportNims, wUsed, wPragma, wRedefine}
wDelegator, wExportNims, wUsed, wPragma, wRedefine, wCallsite}
macroPragmas* = declPragmas + {FirstCallConv..LastCallConv,
wMagic, wNoSideEffect, wCompilerProc, wNonReloadable, wCore,
wDiscardable, wGensym, wInject, wDelegator}
@@ -873,6 +873,9 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
of wRedefine:
if sym.kind == skTemplate: incl(sym.flags, sfTemplateRedefinition)
else: invalidPragma(c, it)
of wCallsite:
if sym.kind == skTemplate: incl(sym.flags, sfCallsite)
else: invalidPragma(c, it)
of wImportCpp:
processImportCpp(c, sym, getOptionalStr(c, it, "$1"), it.info)
of wCppNonPod:

View File

@@ -2338,11 +2338,6 @@ proc evalInclude(c: PContext, n: PNode): PNode =
else:
incMod(c, n, it, result)
proc setLine(n: PNode, info: TLineInfo) =
if n != nil:
for i in 0..<n.safeLen: setLine(n[i], info)
n.info = info
proc recursiveSetFlag(n: PNode, flag: TNodeFlag) =
if n != nil:
for i in 0..<n.safeLen: recursiveSetFlag(n[i], flag)
@@ -2371,7 +2366,7 @@ proc semPragmaBlock(c: PContext, n: PNode; expectedType: PType = nil): PNode =
result.typ = n[1].typ
for i in 0..<pragmaList.len:
case whichPragma(pragmaList[i])
of wLine: setLine(result, pragmaList[i].info)
of wLine: setInfoRecursive(result, pragmaList[i].info)
of wNoRewrite: recursiveSetFlag(result, nfNoRewrite)
else: discard

View File

@@ -620,12 +620,6 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
s = semIdentVis(c, skTemplate, n[namePos], {})
assert s.kind == skTemplate
if s.owner != nil:
const names = ["!=", ">=", ">", "incl", "excl", "in", "notin", "isnot"]
if sfSystemModule in s.owner.flags and s.name.s in names or
s.owner.name.s == "vm" and s.name.s == "stackTrace":
incl(s.flags, sfCallsite)
styleCheckDef(c, s)
onDef(n[namePos].info, s)
# check parameter list:

View File

@@ -75,8 +75,11 @@ proc stackTraceImpl(c: PCtx, tos: PStackFrame, pc: int,
let lineInfo = if lineInfo == TLineInfo.default: c.debug[pc] else: lineInfo
liMessage(c.config, lineInfo, errGenerated, msg, action, infoOrigin)
when not defined(nimHasCallsitePragma):
{.pragma: callsite.}
template stackTrace(c: PCtx, tos: PStackFrame, pc: int,
msg: string, lineInfo: TLineInfo = TLineInfo.default) =
msg: string, lineInfo: TLineInfo = TLineInfo.default) {.callsite.} =
stackTraceImpl(c, tos, pc, msg, lineInfo, instantiationInfo(-2, fullPaths = true))
return

View File

@@ -87,7 +87,7 @@ type
wGlobal = "global", wCodegenDecl = "codegenDecl", wUnchecked = "unchecked",
wGuard = "guard", wLocks = "locks", wPartial = "partial", wExplain = "explain",
wLiftLocals = "liftlocals", wEnforceNoRaises = "enforceNoRaises",
wRedefine = "redefine",
wRedefine = "redefine", wCallsite = "callsite",
wAuto = "auto", wBool = "bool", wCatch = "catch", wChar = "char",
wClass = "class", wCompl = "compl", wConstCast = "const_cast", wDefault = "default",

View File

@@ -731,13 +731,16 @@ proc contains*[U, V, W](s: HSlice[U, V], value: W): bool {.noSideEffect, inline.
## ```
result = s.a <= value and value <= s.b
template `in`*(x, y: untyped): untyped {.dirty.} = contains(y, x)
when not defined(nimHasCallsitePragma):
{.pragma: callsite.}
template `in`*(x, y: untyped): untyped {.dirty, callsite.} = contains(y, x)
## Sugar for `contains`.
## ```
## assert(1 in (1..3) == true)
## assert(5 in (1..3) == false)
## ```
template `notin`*(x, y: untyped): untyped {.dirty.} = not contains(y, x)
template `notin`*(x, y: untyped): untyped {.dirty, callsite.} = not contains(y, x)
## Sugar for `not contains`.
## ```
## assert(1 notin (1..3) == false)
@@ -762,7 +765,7 @@ proc `is`*[T, S](x: T, y: S): bool {.magic: "Is", noSideEffect.}
## assert(test[int](3) == 3)
## assert(test[string]("xyz") == 0)
## ```
template `isnot`*(x, y: untyped): untyped = not (x is y)
template `isnot`*(x, y: untyped): untyped {.callsite.} = not (x is y)
## Negated version of `is <#is,T,S>`_. Equivalent to `not(x is y)`.
## ```
## assert 42 isnot float

View File

@@ -125,15 +125,18 @@ proc `<`*[T](x, y: ref T): bool {.magic: "LtPtr", noSideEffect.}
proc `<`*[T](x, y: ptr T): bool {.magic: "LtPtr", noSideEffect.}
proc `<`*(x, y: pointer): bool {.magic: "LtPtr", noSideEffect.}
template `!=`*(x, y: untyped): untyped =
when not defined(nimHasCallsitePragma):
{.pragma: callsite.}
template `!=`*(x, y: untyped): untyped {.callsite.} =
## Unequals operator. This is a shorthand for `not (x == y)`.
not (x == y)
template `>=`*(x, y: untyped): untyped =
template `>=`*(x, y: untyped): untyped {.callsite.} =
## "is greater or equals" operator. This is the same as `y <= x`.
y <= x
template `>`*(x, y: untyped): untyped =
template `>`*(x, y: untyped): untyped {.callsite.} =
## "is greater" operator. This is the same as `y < x`.
y < x

View File

@@ -9,7 +9,10 @@ func incl*[T](x: var set[T], y: T) {.magic: "Incl".} =
a.incl(4)
assert a == {1, 2, 3, 4, 5}
template incl*[T](x: var set[T], y: set[T]) =
when not defined(nimHasCallsitePragma):
{.pragma: callsite.}
template incl*[T](x: var set[T], y: set[T]) {.callsite.} =
## Includes the set `y` in the set `x`.
runnableExamples:
var a = {1, 3, 5, 7}
@@ -27,7 +30,7 @@ func excl*[T](x: var set[T], y: T) {.magic: "Excl".} =
b.excl(5)
assert b == {2, 3, 6, 12, 545}
template excl*[T](x: var set[T], y: set[T]) =
template excl*[T](x: var set[T], y: set[T]) {.callsite.} =
## Excludes the set `y` from the set `x`.
runnableExamples:
var a = {1, 3, 5, 7}

View File

@@ -0,0 +1,17 @@
discard """
nimout: '''
tcallsitelineinfo.nim(17, 4) Warning: abc [User]
'''
exitcode: 1
outputsub: '''
tcallsitelineinfo.nim(17) tcallsitelineinfo
Error: unhandled exception: def [ValueError]
'''
"""
template foo(): untyped {.callsite.} =
{.warning: "abc".}
raise newException(ValueError, "def")
echo "hello"
foo()

View File

@@ -0,0 +1,20 @@
discard """
nimout: '''
tcallsitelineinfo2.nim(18, 1) Warning: abc [User]
tcallsitelineinfo2.nim(19, 12) Warning: def [User]
'''
exitcode: 1
outputsub: '''
tcallsitelineinfo2.nim(20) tcallsitelineinfo2
Error: unhandled exception: ghi [ValueError]
'''
"""
template foo(a: untyped): untyped {.callsite.} =
{.warning: "abc".}
a
echo "hello"
foo: # with `{.line.}:`, the following do not keep their line information:
{.warning: "def".}
raise newException(ValueError, "ghi")