mirror of
https://github.com/nim-lang/Nim.git
synced 2026-06-06 20:04:18 +00:00
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:
@@ -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
|
||||
|
||||
@@ -143,4 +143,5 @@ proc initDefines*(symbols: StringTableRef) =
|
||||
defineSymbol("nimHasTopDownInference")
|
||||
defineSymbol("nimHasTemplateRedefinitionPragma")
|
||||
defineSymbol("nimHasCstringCase")
|
||||
defineSymbol("nimHasCallsitePragma")
|
||||
defineSymbol("nimHasAmbiguousEnumHint")
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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}
|
||||
|
||||
17
tests/template/tcallsitelineinfo.nim
Normal file
17
tests/template/tcallsitelineinfo.nim
Normal 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()
|
||||
20
tests/template/tcallsitelineinfo2.nim
Normal file
20
tests/template/tcallsitelineinfo2.nim
Normal 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")
|
||||
Reference in New Issue
Block a user