Compare commits
15 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b2c1dcbbc9 |
||
|
|
ffadc75afe |
||
|
|
af086b68f2 |
||
|
|
46d2161c23 |
||
|
|
f2dad94902 |
||
|
|
0319824322 |
||
|
|
72e262666b |
||
|
|
8e9fc5e639 |
||
|
|
2f89f1eb78 |
||
|
|
6ef9430153 |
||
|
|
25eef64fe1 |
||
|
|
64a0355f3f |
||
|
|
2d9af2bd55 |
||
|
|
4d76725299 |
||
|
05e8a60bb4 |
26 changed files with 415 additions and 48 deletions
|
|
@ -273,6 +273,7 @@ proc genCLineDir(r: var Rope, filename: string, line: int; conf: ConfigRef) =
|
||||||
[rope(makeSingleLineCString(filename)), rope(line)])
|
[rope(makeSingleLineCString(filename)), rope(line)])
|
||||||
|
|
||||||
proc genCLineDir(r: var Rope, info: TLineInfo; conf: ConfigRef) =
|
proc genCLineDir(r: var Rope, info: TLineInfo; conf: ConfigRef) =
|
||||||
|
if optLineDir in conf.options:
|
||||||
genCLineDir(r, toFullPath(conf, info), info.safeLineNm, conf)
|
genCLineDir(r, toFullPath(conf, info), info.safeLineNm, conf)
|
||||||
|
|
||||||
proc freshLineInfo(p: BProc; info: TLineInfo): bool =
|
proc freshLineInfo(p: BProc; info: TLineInfo): bool =
|
||||||
|
|
@ -287,7 +288,7 @@ proc genLineDir(p: BProc, t: PNode) =
|
||||||
|
|
||||||
if optEmbedOrigSrc in p.config.globalOptions:
|
if optEmbedOrigSrc in p.config.globalOptions:
|
||||||
p.s(cpsStmts).add("//" & sourceLine(p.config, t.info) & "\L")
|
p.s(cpsStmts).add("//" & sourceLine(p.config, t.info) & "\L")
|
||||||
genCLineDir(p.s(cpsStmts), toFullPath(p.config, t.info), line, p.config)
|
genCLineDir(p.s(cpsStmts), t.info, p.config)
|
||||||
if ({optLineTrace, optStackTrace} * p.options == {optLineTrace, optStackTrace}) and
|
if ({optLineTrace, optStackTrace} * p.options == {optLineTrace, optStackTrace}) and
|
||||||
(p.prc == nil or sfPure notin p.prc.flags) and t.info.fileIndex != InvalidFileIdx:
|
(p.prc == nil or sfPure notin p.prc.flags) and t.info.fileIndex != InvalidFileIdx:
|
||||||
if freshLineInfo(p, t.info):
|
if freshLineInfo(p, t.info):
|
||||||
|
|
@ -566,6 +567,9 @@ proc localVarDecl(p: BProc; n: PNode): Rope =
|
||||||
if s.kind == skLet: incl(s.loc.flags, lfNoDeepCopy)
|
if s.kind == skLet: incl(s.loc.flags, lfNoDeepCopy)
|
||||||
if s.kind in {skLet, skVar, skField, skForVar} and s.alignment > 0:
|
if s.kind in {skLet, skVar, skField, skForVar} and s.alignment > 0:
|
||||||
result.addf("NIM_ALIGN($1) ", [rope(s.alignment)])
|
result.addf("NIM_ALIGN($1) ", [rope(s.alignment)])
|
||||||
|
|
||||||
|
genCLineDir(result, n.info, p.config)
|
||||||
|
|
||||||
result.add getTypeDesc(p.module, s.typ, skVar)
|
result.add getTypeDesc(p.module, s.typ, skVar)
|
||||||
if s.constraint.isNil:
|
if s.constraint.isNil:
|
||||||
if sfRegister in s.flags: result.add(" register")
|
if sfRegister in s.flags: result.add(" register")
|
||||||
|
|
|
||||||
|
|
@ -1085,13 +1085,7 @@ proc inst(g: ModuleGraph; c: PContext; t: PType; kind: TTypeAttachedOp; idgen: I
|
||||||
let op = getAttachedOp(g, t, kind)
|
let op = getAttachedOp(g, t, kind)
|
||||||
if op != nil and op.ast != nil and op.ast.isGenericRoutine:
|
if op != nil and op.ast != nil and op.ast.isGenericRoutine:
|
||||||
if t.typeInst != nil:
|
if t.typeInst != nil:
|
||||||
var a: TLiftCtx
|
var a = TLiftCtx(info: info, g: g, kind: kind, c: c, idgen: idgen)
|
||||||
a.info = info
|
|
||||||
a.g = g
|
|
||||||
a.kind = kind
|
|
||||||
a.c = c
|
|
||||||
a.idgen = idgen
|
|
||||||
|
|
||||||
let opInst = instantiateGeneric(a, op, t, t.typeInst)
|
let opInst = instantiateGeneric(a, op, t, t.typeInst)
|
||||||
if opInst.ast != nil:
|
if opInst.ast != nil:
|
||||||
patchBody(g, c, opInst.ast, info, a.idgen)
|
patchBody(g, c, opInst.ast, info, a.idgen)
|
||||||
|
|
|
||||||
|
|
@ -343,7 +343,8 @@ proc addDeclAt*(c: PContext; scope: PScope, sym: PSym, info: TLineInfo) =
|
||||||
if sym.name.s == "_": return
|
if sym.name.s == "_": return
|
||||||
let conflict = scope.addUniqueSym(sym)
|
let conflict = scope.addUniqueSym(sym)
|
||||||
if conflict != nil:
|
if conflict != nil:
|
||||||
if sym.kind == skModule and conflict.kind == skModule and sym.owner == conflict.owner:
|
if sym.kind == skModule and conflict.kind == skModule and
|
||||||
|
sym.position == conflict.position:
|
||||||
# e.g.: import foo; import foo
|
# e.g.: import foo; import foo
|
||||||
# xxx we could refine this by issuing a different hint for the case
|
# xxx we could refine this by issuing a different hint for the case
|
||||||
# where a duplicate import happens inside an include.
|
# where a duplicate import happens inside an include.
|
||||||
|
|
|
||||||
|
|
@ -71,6 +71,13 @@ proc commandGenDepend(graph: ModuleGraph) =
|
||||||
let project = graph.config.projectFull
|
let project = graph.config.projectFull
|
||||||
writeDepsFile(graph)
|
writeDepsFile(graph)
|
||||||
generateDot(graph, project)
|
generateDot(graph, project)
|
||||||
|
|
||||||
|
# dot in graphivz tool kit is required
|
||||||
|
let graphvizDotPath = findExe("dot")
|
||||||
|
if graphvizDotPath.len == 0:
|
||||||
|
quit("gendepend: Graphviz's tool dot is required," &
|
||||||
|
"see https://graphviz.org/download for downloading")
|
||||||
|
|
||||||
execExternalProgram(graph.config, "dot -Tpng -o" &
|
execExternalProgram(graph.config, "dot -Tpng -o" &
|
||||||
changeFileExt(project, "png").string &
|
changeFileExt(project, "png").string &
|
||||||
' ' & changeFileExt(project, "dot").string)
|
' ' & changeFileExt(project, "dot").string)
|
||||||
|
|
|
||||||
|
|
@ -7,4 +7,4 @@ proc findNodeJs*(): string {.inline.} =
|
||||||
result = findExe("node")
|
result = findExe("node")
|
||||||
if result.len == 0:
|
if result.len == 0:
|
||||||
echo "Please install NodeJS first, see https://nodejs.org/en/download"
|
echo "Please install NodeJS first, see https://nodejs.org/en/download"
|
||||||
raise newException(IOError, "NodeJS not found in PATH: " & result)
|
raise newException(IOError, "NodeJS not found in PATH")
|
||||||
|
|
|
||||||
|
|
@ -998,7 +998,7 @@ proc afterCallActions(c: PContext; n, orig: PNode, flags: TExprFlags; expectedTy
|
||||||
fixAbstractType(c, result)
|
fixAbstractType(c, result)
|
||||||
analyseIfAddressTakenInCall(c, result)
|
analyseIfAddressTakenInCall(c, result)
|
||||||
if callee.magic != mNone:
|
if callee.magic != mNone:
|
||||||
result = magicsAfterOverloadResolution(c, result, flags)
|
result = magicsAfterOverloadResolution(c, result, flags, expectedType)
|
||||||
when false:
|
when false:
|
||||||
if result.typ != nil and
|
if result.typ != nil and
|
||||||
not (result.typ.kind == tySequence and result.typ[0].kind == tyEmpty):
|
not (result.typ.kind == tySequence and result.typ[0].kind == tyEmpty):
|
||||||
|
|
@ -2206,7 +2206,7 @@ proc semQuoteAst(c: PContext, n: PNode): PNode =
|
||||||
dummyTemplate[paramsPos].add newTreeI(nkIdentDefs, n.info, ids[0], getSysSym(c.graph, n.info, "typed").newSymNode, c.graph.emptyNode)
|
dummyTemplate[paramsPos].add newTreeI(nkIdentDefs, n.info, ids[0], getSysSym(c.graph, n.info, "typed").newSymNode, c.graph.emptyNode)
|
||||||
for i in 1..<ids.len:
|
for i in 1..<ids.len:
|
||||||
let typ = semExprWithType(c, quotes[i+1], {}).typ
|
let typ = semExprWithType(c, quotes[i+1], {}).typ
|
||||||
if tfTriggersCompileTime notin typ.flags:
|
if tfTriggersCompileTime notin typ.flags and typ.kind != tyTypeDesc:
|
||||||
dummyTemplate[paramsPos].add newTreeI(nkIdentDefs, n.info, ids[i], newNodeIT(nkType, n.info, typ), c.graph.emptyNode)
|
dummyTemplate[paramsPos].add newTreeI(nkIdentDefs, n.info, ids[i], newNodeIT(nkType, n.info, typ), c.graph.emptyNode)
|
||||||
else:
|
else:
|
||||||
dummyTemplate[paramsPos].add newTreeI(nkIdentDefs, n.info, ids[i], getSysSym(c.graph, n.info, "typed").newSymNode, c.graph.emptyNode)
|
dummyTemplate[paramsPos].add newTreeI(nkIdentDefs, n.info, ids[i], getSysSym(c.graph, n.info, "typed").newSymNode, c.graph.emptyNode)
|
||||||
|
|
|
||||||
|
|
@ -131,7 +131,14 @@ proc instantiateBody(c: PContext, n, params: PNode, result, orig: PSym) =
|
||||||
if sfBorrow notin orig.flags:
|
if sfBorrow notin orig.flags:
|
||||||
# We do not want to generate a body for generic borrowed procs.
|
# We do not want to generate a body for generic borrowed procs.
|
||||||
# As body is a sym to the borrowed proc.
|
# As body is a sym to the borrowed proc.
|
||||||
b = semProcBody(c, b)
|
let resultType = # todo probably refactor it into a function
|
||||||
|
if result.kind == skMacro:
|
||||||
|
sysTypeFromName(c.graph, n.info, "NimNode")
|
||||||
|
elif not isInlineIterator(result.typ):
|
||||||
|
result.typ[0]
|
||||||
|
else:
|
||||||
|
nil
|
||||||
|
b = semProcBody(c, b, resultType)
|
||||||
result.ast[bodyPos] = hloBody(c, b)
|
result.ast[bodyPos] = hloBody(c, b)
|
||||||
excl(result.flags, sfForward)
|
excl(result.flags, sfForward)
|
||||||
trackProc(c, result, result.ast[bodyPos])
|
trackProc(c, result, result.ast[bodyPos])
|
||||||
|
|
|
||||||
|
|
@ -526,7 +526,7 @@ proc checkDefault(c: PContext, n: PNode): PNode =
|
||||||
message(c.config, n.info, warnUnsafeDefault, typeToString(constructed))
|
message(c.config, n.info, warnUnsafeDefault, typeToString(constructed))
|
||||||
|
|
||||||
proc magicsAfterOverloadResolution(c: PContext, n: PNode,
|
proc magicsAfterOverloadResolution(c: PContext, n: PNode,
|
||||||
flags: TExprFlags): PNode =
|
flags: TExprFlags; expectedType: PType = nil): PNode =
|
||||||
## This is the preferred code point to implement magics.
|
## This is the preferred code point to implement magics.
|
||||||
## ``c`` the current module, a symbol table to a very good approximation
|
## ``c`` the current module, a symbol table to a very good approximation
|
||||||
## ``n`` the ast like it would be passed to a real macro
|
## ``n`` the ast like it would be passed to a real macro
|
||||||
|
|
@ -635,5 +635,9 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
|
||||||
result = n
|
result = n
|
||||||
of mPrivateAccess:
|
of mPrivateAccess:
|
||||||
result = semPrivateAccess(c, n)
|
result = semPrivateAccess(c, n)
|
||||||
|
of mArrToSeq:
|
||||||
|
result = n
|
||||||
|
if result.typ != nil and expectedType != nil and result.typ.kind == tySequence and expectedType.kind == tySequence and result.typ[0].kind == tyEmpty:
|
||||||
|
result.typ = expectedType # type inference for empty sequence # bug #21377
|
||||||
else:
|
else:
|
||||||
result = n
|
result = n
|
||||||
|
|
|
||||||
|
|
@ -142,15 +142,45 @@ proc fieldsPresentInInitExpr(c: PContext, fieldsRecList, initExpr: PNode): strin
|
||||||
if result.len != 0: result.add ", "
|
if result.len != 0: result.add ", "
|
||||||
result.add field.sym.name.s.quoteStr
|
result.add field.sym.name.s.quoteStr
|
||||||
|
|
||||||
|
proc locateFieldInDefaults(sym: PSym, defaults: seq[PNode]): bool =
|
||||||
|
result = false
|
||||||
|
for d in defaults:
|
||||||
|
if sym.id == d[0].sym.id:
|
||||||
|
return true
|
||||||
|
|
||||||
proc collectMissingFields(c: PContext, fieldsRecList: PNode,
|
proc collectMissingFields(c: PContext, fieldsRecList: PNode,
|
||||||
constrCtx: var ObjConstrContext) =
|
constrCtx: var ObjConstrContext, defaults: seq[PNode]
|
||||||
|
): seq[PSym] =
|
||||||
for r in directFieldsInRecList(fieldsRecList):
|
for r in directFieldsInRecList(fieldsRecList):
|
||||||
|
let assignment = locateFieldInInitExpr(c, r.sym, constrCtx.initExpr)
|
||||||
|
if assignment == nil and not locateFieldInDefaults(r.sym, defaults):
|
||||||
if constrCtx.needsFullInit or
|
if constrCtx.needsFullInit or
|
||||||
sfRequiresInit in r.sym.flags or
|
sfRequiresInit in r.sym.flags or
|
||||||
r.sym.typ.requiresInit:
|
r.sym.typ.requiresInit:
|
||||||
let assignment = locateFieldInInitExpr(c, r.sym, constrCtx.initExpr)
|
|
||||||
if assignment == nil:
|
|
||||||
constrCtx.missingFields.add r.sym
|
constrCtx.missingFields.add r.sym
|
||||||
|
else:
|
||||||
|
result.add r.sym
|
||||||
|
|
||||||
|
proc collectMissingCaseFields(c: PContext, branchNode: PNode,
|
||||||
|
constrCtx: var ObjConstrContext, defaults: seq[PNode]): seq[PSym] =
|
||||||
|
if branchNode != nil:
|
||||||
|
let fieldsRecList = branchNode[^1]
|
||||||
|
result = collectMissingFields(c, fieldsRecList, constrCtx, defaults)
|
||||||
|
|
||||||
|
proc collectOrAddMissingCaseFields(c: PContext, branchNode: PNode,
|
||||||
|
constrCtx: var ObjConstrContext, defaults: var seq[PNode]) =
|
||||||
|
let res = collectMissingCaseFields(c, branchNode, constrCtx, defaults)
|
||||||
|
for sym in res:
|
||||||
|
let asgnType = newType(tyTypeDesc, nextTypeId(c.idgen), sym.typ.owner)
|
||||||
|
let recTyp = sym.typ.skipTypes(defaultFieldsSkipTypes)
|
||||||
|
rawAddSon(asgnType, recTyp)
|
||||||
|
let asgnExpr = newTree(nkCall,
|
||||||
|
newSymNode(getSysMagic(c.graph, constrCtx.initExpr.info, "zeroDefault", mZeroDefault)),
|
||||||
|
newNodeIT(nkType, constrCtx.initExpr.info, asgnType)
|
||||||
|
)
|
||||||
|
asgnExpr.flags.incl nfUseDefaultField
|
||||||
|
asgnExpr.typ = recTyp
|
||||||
|
defaults.add newTree(nkExprColonExpr, newSymNode(sym), asgnExpr)
|
||||||
|
|
||||||
proc semConstructFields(c: PContext, n: PNode, constrCtx: var ObjConstrContext,
|
proc semConstructFields(c: PContext, n: PNode, constrCtx: var ObjConstrContext,
|
||||||
flags: TExprFlags): tuple[status: InitStatus, defaults: seq[PNode]] =
|
flags: TExprFlags): tuple[status: InitStatus, defaults: seq[PNode]] =
|
||||||
|
|
@ -166,11 +196,6 @@ proc semConstructFields(c: PContext, n: PNode, constrCtx: var ObjConstrContext,
|
||||||
let fields = branch[^1]
|
let fields = branch[^1]
|
||||||
fieldsPresentInInitExpr(c, fields, constrCtx.initExpr)
|
fieldsPresentInInitExpr(c, fields, constrCtx.initExpr)
|
||||||
|
|
||||||
template collectMissingFields(branchNode: PNode) =
|
|
||||||
if branchNode != nil:
|
|
||||||
let fields = branchNode[^1]
|
|
||||||
collectMissingFields(c, fields, constrCtx)
|
|
||||||
|
|
||||||
let discriminator = n[0]
|
let discriminator = n[0]
|
||||||
internalAssert c.config, discriminator.kind == nkSym
|
internalAssert c.config, discriminator.kind == nkSym
|
||||||
var selectedBranch = -1
|
var selectedBranch = -1
|
||||||
|
|
@ -288,8 +313,7 @@ proc semConstructFields(c: PContext, n: PNode, constrCtx: var ObjConstrContext,
|
||||||
# When a branch is selected with a partial match, some of the fields
|
# When a branch is selected with a partial match, some of the fields
|
||||||
# that were not initialized may be mandatory. We must check for this:
|
# that were not initialized may be mandatory. We must check for this:
|
||||||
if result.status == initPartial:
|
if result.status == initPartial:
|
||||||
collectMissingFields branchNode
|
collectOrAddMissingCaseFields(c, branchNode, constrCtx, result.defaults)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
result.status = initNone
|
result.status = initNone
|
||||||
let discriminatorVal = semConstrField(c, flags + {efPreferStatic},
|
let discriminatorVal = semConstrField(c, flags + {efPreferStatic},
|
||||||
|
|
@ -302,7 +326,7 @@ proc semConstructFields(c: PContext, n: PNode, constrCtx: var ObjConstrContext,
|
||||||
# a result:
|
# a result:
|
||||||
let defaultValue = newIntLit(c.graph, constrCtx.initExpr.info, 0)
|
let defaultValue = newIntLit(c.graph, constrCtx.initExpr.info, 0)
|
||||||
let matchedBranch = n.pickCaseBranch defaultValue
|
let matchedBranch = n.pickCaseBranch defaultValue
|
||||||
collectMissingFields matchedBranch
|
discard collectMissingCaseFields(c, matchedBranch, constrCtx, @[])
|
||||||
else:
|
else:
|
||||||
result.status = initPartial
|
result.status = initPartial
|
||||||
if discriminatorVal.kind == nkIntLit:
|
if discriminatorVal.kind == nkIntLit:
|
||||||
|
|
@ -312,11 +336,12 @@ proc semConstructFields(c: PContext, n: PNode, constrCtx: var ObjConstrContext,
|
||||||
if matchedBranch != nil:
|
if matchedBranch != nil:
|
||||||
let (_, defaults) = semConstructFields(c, matchedBranch[^1], constrCtx, flags)
|
let (_, defaults) = semConstructFields(c, matchedBranch[^1], constrCtx, flags)
|
||||||
result.defaults.add defaults
|
result.defaults.add defaults
|
||||||
collectMissingFields matchedBranch
|
collectOrAddMissingCaseFields(c, matchedBranch, constrCtx, result.defaults)
|
||||||
else:
|
else:
|
||||||
# All bets are off. If any of the branches has a mandatory
|
# All bets are off. If any of the branches has a mandatory
|
||||||
# fields we must produce an error:
|
# fields we must produce an error:
|
||||||
for i in 1..<n.len: collectMissingFields n[i]
|
for i in 1..<n.len:
|
||||||
|
discard collectMissingCaseFields(c, n[i], constrCtx, @[])
|
||||||
of nkSym:
|
of nkSym:
|
||||||
let field = n.sym
|
let field = n.sym
|
||||||
let e = semConstrField(c, flags, field, constrCtx.initExpr)
|
let e = semConstrField(c, flags, field, constrCtx.initExpr)
|
||||||
|
|
@ -348,7 +373,7 @@ proc semConstructTypeAux(c: PContext,
|
||||||
result.status.mergeInitStatus status
|
result.status.mergeInitStatus status
|
||||||
result.defaults.add defaults
|
result.defaults.add defaults
|
||||||
if status in {initPartial, initNone, initUnknown}:
|
if status in {initPartial, initNone, initUnknown}:
|
||||||
collectMissingFields c, t.n, constrCtx
|
discard collectMissingFields(c, t.n, constrCtx, result.defaults)
|
||||||
let base = t[0]
|
let base = t[0]
|
||||||
if base == nil: break
|
if base == nil: break
|
||||||
t = skipTypes(base, skipPtrs)
|
t = skipTypes(base, skipPtrs)
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,7 @@ type
|
||||||
module: PSym
|
module: PSym
|
||||||
transCon: PTransCon # top of a TransCon stack
|
transCon: PTransCon # top of a TransCon stack
|
||||||
inlining: int # > 0 if we are in inlining context (copy vars)
|
inlining: int # > 0 if we are in inlining context (copy vars)
|
||||||
|
isIntroducingNewLocalVars: bool # true if we are in `introducingNewLocalVars` (don't transform yields)
|
||||||
contSyms, breakSyms: seq[PSym] # to transform 'continue' and 'break'
|
contSyms, breakSyms: seq[PSym] # to transform 'continue' and 'break'
|
||||||
deferDetected, tooEarly: bool
|
deferDetected, tooEarly: bool
|
||||||
graph: ModuleGraph
|
graph: ModuleGraph
|
||||||
|
|
@ -450,7 +451,9 @@ proc transformYield(c: PTransf, n: PNode): PNode =
|
||||||
result.add(c.transCon.forLoopBody)
|
result.add(c.transCon.forLoopBody)
|
||||||
else:
|
else:
|
||||||
# we need to introduce new local variables:
|
# we need to introduce new local variables:
|
||||||
|
c.isIntroducingNewLocalVars = true # don't transform yields when introducing new local vars
|
||||||
result.add(introduceNewLocalVars(c, c.transCon.forLoopBody))
|
result.add(introduceNewLocalVars(c, c.transCon.forLoopBody))
|
||||||
|
c.isIntroducingNewLocalVars = false
|
||||||
|
|
||||||
for idx in 0 ..< result.len:
|
for idx in 0 ..< result.len:
|
||||||
var changeNode = result[idx]
|
var changeNode = result[idx]
|
||||||
|
|
@ -1036,7 +1039,7 @@ proc transform(c: PTransf, n: PNode): PNode =
|
||||||
else:
|
else:
|
||||||
result = transformSons(c, n)
|
result = transformSons(c, n)
|
||||||
of nkYieldStmt:
|
of nkYieldStmt:
|
||||||
if c.inlining > 0:
|
if c.inlining > 0 and not c.isIntroducingNewLocalVars:
|
||||||
result = transformYield(c, n)
|
result = transformYield(c, n)
|
||||||
else:
|
else:
|
||||||
result = transformSons(c, n)
|
result = transformSons(c, n)
|
||||||
|
|
|
||||||
|
|
@ -1909,7 +1909,8 @@ proc genVarSection(c: PCtx; n: PNode) =
|
||||||
c.genAdditionalCopy(a[2], opcWrDeref, tmp, 0, val)
|
c.genAdditionalCopy(a[2], opcWrDeref, tmp, 0, val)
|
||||||
c.freeTemp(val)
|
c.freeTemp(val)
|
||||||
c.freeTemp(tmp)
|
c.freeTemp(tmp)
|
||||||
elif not importcCondVar(s) and not (s.typ.kind == tyProc and s.typ.callConv == ccClosure): # fixes #10938
|
elif not importcCondVar(s) and not (s.typ.kind == tyProc and s.typ.callConv == ccClosure) and
|
||||||
|
sfPure notin s.flags: # fixes #10938
|
||||||
# there is a pre-existing issue with closure types in VM
|
# there is a pre-existing issue with closure types in VM
|
||||||
# if `(var s: proc () = default(proc ()); doAssert s == nil)` works for you;
|
# if `(var s: proc () = default(proc ()); doAssert s == nil)` works for you;
|
||||||
# you might remove the second condition.
|
# you might remove the second condition.
|
||||||
|
|
|
||||||
|
|
@ -172,6 +172,6 @@ Advanced options:
|
||||||
--useVersion:1.0|1.2|1.6 emulate Nim version X of the Nim compiler, for testing
|
--useVersion:1.0|1.2|1.6 emulate Nim version X of the Nim compiler, for testing
|
||||||
--benchmarkVM:on|off turn benchmarking of VM code with cpuTime() on|off
|
--benchmarkVM:on|off turn benchmarking of VM code with cpuTime() on|off
|
||||||
--profileVM:on|off turn compile time VM profiler on|off
|
--profileVM:on|off turn compile time VM profiler on|off
|
||||||
--sinkInference:on|off turn sink parameter inference on|off (default: on)
|
--sinkInference:on|off turn sink parameter inference on|off (default: off)
|
||||||
--panics:on|off turn panics into process terminations (default: off)
|
--panics:on|off turn panics into process terminations (default: off)
|
||||||
--deepcopy:on|off enable 'system.deepCopy' for ``--mm:arc|orc``
|
--deepcopy:on|off enable 'system.deepCopy' for ``--mm:arc|orc``
|
||||||
|
|
|
||||||
|
|
@ -408,6 +408,16 @@ const unitWeights: array[FixedTimeUnit, int64] = [
|
||||||
7 * secondsInDay * 1e9.int64,
|
7 * secondsInDay * 1e9.int64,
|
||||||
]
|
]
|
||||||
|
|
||||||
|
when (NimMajor, NimMinor) >= (1, 4):
|
||||||
|
# Newer versions of Nim don't track defects
|
||||||
|
{.pragma: parseFormatRaises, raises: [TimeParseError, TimeFormatParseError].}
|
||||||
|
{.pragma: parseRaises, raises: [TimeParseError].}
|
||||||
|
else:
|
||||||
|
# Still track when using older versions
|
||||||
|
{.pragma: parseFormatRaises, raises: [TimeParseError, TimeFormatParseError, Defect].}
|
||||||
|
{.pragma: parseRaises, raises: [TimeParseError, Defect].}
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Helper procs
|
# Helper procs
|
||||||
#
|
#
|
||||||
|
|
@ -2134,8 +2144,7 @@ proc format*(time: Time, f: static[string], zone: Timezone = local()): string
|
||||||
result = time.inZone(zone).format(f2)
|
result = time.inZone(zone).format(f2)
|
||||||
|
|
||||||
proc parse*(input: string, f: TimeFormat, zone: Timezone = local(),
|
proc parse*(input: string, f: TimeFormat, zone: Timezone = local(),
|
||||||
loc: DateTimeLocale = DefaultLocale): DateTime
|
loc: DateTimeLocale = DefaultLocale): DateTime {.parseRaises.} =
|
||||||
{.raises: [TimeParseError, Defect].} =
|
|
||||||
## Parses `input` as a `DateTime` using the format specified by `f`.
|
## Parses `input` as a `DateTime` using the format specified by `f`.
|
||||||
## If no UTC offset was parsed, then `input` is assumed to be specified in
|
## If no UTC offset was parsed, then `input` is assumed to be specified in
|
||||||
## the `zone` timezone. If a UTC offset was parsed, the result will be
|
## the `zone` timezone. If a UTC offset was parsed, the result will be
|
||||||
|
|
@ -2178,8 +2187,7 @@ proc parse*(input: string, f: TimeFormat, zone: Timezone = local(),
|
||||||
result = toDateTime(parsed, zone, f, input)
|
result = toDateTime(parsed, zone, f, input)
|
||||||
|
|
||||||
proc parse*(input, f: string, tz: Timezone = local(),
|
proc parse*(input, f: string, tz: Timezone = local(),
|
||||||
loc: DateTimeLocale = DefaultLocale): DateTime
|
loc: DateTimeLocale = DefaultLocale): DateTime {.parseFormatRaises.} =
|
||||||
{.raises: [TimeParseError, TimeFormatParseError, Defect].} =
|
|
||||||
## Shorthand for constructing a `TimeFormat` and using it to parse
|
## Shorthand for constructing a `TimeFormat` and using it to parse
|
||||||
## `input` as a `DateTime`.
|
## `input` as a `DateTime`.
|
||||||
##
|
##
|
||||||
|
|
@ -2192,14 +2200,12 @@ proc parse*(input, f: string, tz: Timezone = local(),
|
||||||
result = input.parse(dtFormat, tz, loc = loc)
|
result = input.parse(dtFormat, tz, loc = loc)
|
||||||
|
|
||||||
proc parse*(input: string, f: static[string], zone: Timezone = local(),
|
proc parse*(input: string, f: static[string], zone: Timezone = local(),
|
||||||
loc: DateTimeLocale = DefaultLocale):
|
loc: DateTimeLocale = DefaultLocale): DateTime {.parseRaises.} =
|
||||||
DateTime {.raises: [TimeParseError, Defect].} =
|
|
||||||
## Overload that validates `f` at compile time.
|
## Overload that validates `f` at compile time.
|
||||||
const f2 = initTimeFormat(f)
|
const f2 = initTimeFormat(f)
|
||||||
result = input.parse(f2, zone, loc = loc)
|
result = input.parse(f2, zone, loc = loc)
|
||||||
|
|
||||||
proc parseTime*(input, f: string, zone: Timezone): Time
|
proc parseTime*(input, f: string, zone: Timezone): Time {.parseFormatRaises.} =
|
||||||
{.raises: [TimeParseError, TimeFormatParseError, Defect].} =
|
|
||||||
## Shorthand for constructing a `TimeFormat` and using it to parse
|
## Shorthand for constructing a `TimeFormat` and using it to parse
|
||||||
## `input` as a `DateTime`, then converting it a `Time`.
|
## `input` as a `DateTime`, then converting it a `Time`.
|
||||||
##
|
##
|
||||||
|
|
@ -2211,7 +2217,7 @@ proc parseTime*(input, f: string, zone: Timezone): Time
|
||||||
parse(input, f, zone).toTime()
|
parse(input, f, zone).toTime()
|
||||||
|
|
||||||
proc parseTime*(input: string, f: static[string], zone: Timezone): Time
|
proc parseTime*(input: string, f: static[string], zone: Timezone): Time
|
||||||
{.raises: [TimeParseError, Defect].} =
|
{.parseRaises.} =
|
||||||
## Overload that validates `format` at compile time.
|
## Overload that validates `format` at compile time.
|
||||||
const f2 = initTimeFormat(f)
|
const f2 = initTimeFormat(f)
|
||||||
result = input.parse(f2, zone).toTime()
|
result = input.parse(f2, zone).toTime()
|
||||||
|
|
|
||||||
|
|
@ -269,3 +269,64 @@ proc bug20305 =
|
||||||
echo x.pChildren
|
echo x.pChildren
|
||||||
|
|
||||||
bug20305()
|
bug20305()
|
||||||
|
|
||||||
|
# bug #21023
|
||||||
|
block:
|
||||||
|
block:
|
||||||
|
type
|
||||||
|
MGErrorKind = enum
|
||||||
|
mgeUnexpected, mgeNotFound
|
||||||
|
|
||||||
|
type Foo = object
|
||||||
|
kind: MGErrorKind
|
||||||
|
ex: Exception
|
||||||
|
|
||||||
|
type Boo = object
|
||||||
|
a: seq[int]
|
||||||
|
|
||||||
|
type
|
||||||
|
Result2 = object
|
||||||
|
case o: bool
|
||||||
|
of false:
|
||||||
|
e: Foo
|
||||||
|
of true:
|
||||||
|
v: Boo
|
||||||
|
|
||||||
|
proc startSessionSync(): Result2 =
|
||||||
|
return Result2(o: true)
|
||||||
|
|
||||||
|
proc mainSync =
|
||||||
|
let ff = startSessionSync()
|
||||||
|
doAssert ff.o == true
|
||||||
|
|
||||||
|
mainSync()
|
||||||
|
|
||||||
|
block:
|
||||||
|
type
|
||||||
|
MGErrorKind = enum
|
||||||
|
mgeUnexpected, mgeNotFound
|
||||||
|
|
||||||
|
type Foo = object
|
||||||
|
kind: MGErrorKind
|
||||||
|
ex: Exception
|
||||||
|
|
||||||
|
type Boo = object
|
||||||
|
a: seq[int]
|
||||||
|
|
||||||
|
type
|
||||||
|
Result2 = object
|
||||||
|
case o: bool
|
||||||
|
of false:
|
||||||
|
e: Foo
|
||||||
|
of true:
|
||||||
|
v: Boo
|
||||||
|
s: int
|
||||||
|
|
||||||
|
proc startSessionSync(): Result2 =
|
||||||
|
return Result2(o: true, s: 12)
|
||||||
|
|
||||||
|
proc mainSync =
|
||||||
|
let ff = startSessionSync()
|
||||||
|
doAssert ff.s == 12
|
||||||
|
|
||||||
|
mainSync()
|
||||||
|
|
|
||||||
12
tests/errmsgs/t16654.nim
Normal file
12
tests/errmsgs/t16654.nim
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
discard """
|
||||||
|
cmd: "nim check $options $file"
|
||||||
|
errormsg: "type mismatch: got <int> but expected 'float'"
|
||||||
|
"""
|
||||||
|
|
||||||
|
when true: # bug #16654
|
||||||
|
func fn[T](a: T, op: proc(a: T): float) = discard
|
||||||
|
proc main() =
|
||||||
|
let v = 1
|
||||||
|
proc bar(r: auto): auto = v
|
||||||
|
fn(1, bar)
|
||||||
|
main()
|
||||||
|
|
@ -153,3 +153,16 @@ proc unzip*[T,U](xs: List[tuple[t: T, u: U]]): (List[T], List[U]) = discard
|
||||||
|
|
||||||
proc unzip2*[T,U](xs: List[(T,U)]): (List[T], List[U]) = discard
|
proc unzip2*[T,U](xs: List[(T,U)]): (List[T], List[U]) = discard
|
||||||
|
|
||||||
|
type
|
||||||
|
AtomicType = pointer|ptr|int
|
||||||
|
|
||||||
|
Atomic[T: AtomicType] = distinct T
|
||||||
|
|
||||||
|
Block[T: AtomicType] = object
|
||||||
|
|
||||||
|
AtomicContainer[T: AtomicType] = object
|
||||||
|
b: Atomic[ptr Block[T]]
|
||||||
|
|
||||||
|
# bug #8295
|
||||||
|
var x = AtomicContainer[int]()
|
||||||
|
doAssert (ptr Block[int])(x.b) == nil
|
||||||
|
|
|
||||||
|
|
@ -262,3 +262,8 @@ proc doSomething[A, B](t: tuple[a: A, b: B]) = discard
|
||||||
|
|
||||||
discard identity((c: 1, d: 2))
|
discard identity((c: 1, d: 2))
|
||||||
doSomething(identity((1, 2)))
|
doSomething(identity((1, 2)))
|
||||||
|
|
||||||
|
# bug #6231
|
||||||
|
proc myProc[T, U](x: T or U) = discard
|
||||||
|
|
||||||
|
myProc[int, string](x = 2)
|
||||||
|
|
|
||||||
1
tests/import/buzz/m21496.nim
Normal file
1
tests/import/buzz/m21496.nim
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
proc fb* = echo "buzz!"
|
||||||
1
tests/import/fizz/m21496.nim
Normal file
1
tests/import/fizz/m21496.nim
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
proc fb* = echo "fizz!"
|
||||||
9
tests/import/t21496.nim
Normal file
9
tests/import/t21496.nim
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
discard """
|
||||||
|
errormsg: "redefinition of 'm21496'; previous declaration here: t21496.nim(5, 12)"
|
||||||
|
"""
|
||||||
|
|
||||||
|
import fizz/m21496, buzz/m21496
|
||||||
|
|
||||||
|
# bug #21496
|
||||||
|
|
||||||
|
m21496.fb()
|
||||||
114
tests/iter/t21306.nim
Normal file
114
tests/iter/t21306.nim
Normal file
|
|
@ -0,0 +1,114 @@
|
||||||
|
# bug #21306
|
||||||
|
type
|
||||||
|
FutureState {.pure.} = enum
|
||||||
|
Pending, Finished, Cancelled, Failed
|
||||||
|
|
||||||
|
FutureBase = ref object of RootObj
|
||||||
|
state: FutureState
|
||||||
|
error: ref CatchableError
|
||||||
|
id: uint
|
||||||
|
|
||||||
|
Future[T] = ref object of FutureBase
|
||||||
|
closure: iterator(f: Future[T]): FutureBase {.raises: [Defect, CatchableError, Exception], gcsafe.}
|
||||||
|
value: T
|
||||||
|
|
||||||
|
template setupFutureBase() =
|
||||||
|
new(result)
|
||||||
|
result.state = FutureState.Pending
|
||||||
|
|
||||||
|
proc newFutureImpl[T](): Future[T] =
|
||||||
|
setupFutureBase()
|
||||||
|
|
||||||
|
template newFuture[T](fromProc: static[string] = ""): Future[T] =
|
||||||
|
newFutureImpl[T]()
|
||||||
|
|
||||||
|
proc internalRead[T](fut: Future[T]): T =
|
||||||
|
when T isnot void:
|
||||||
|
return fut.value
|
||||||
|
|
||||||
|
template await[T](f: Future[T]): untyped =
|
||||||
|
when declared(chronosInternalRetFuture):
|
||||||
|
when not declaredInScope(chronosInternalTmpFuture):
|
||||||
|
var chronosInternalTmpFuture {.inject.}: FutureBase = f
|
||||||
|
else:
|
||||||
|
chronosInternalTmpFuture = f
|
||||||
|
|
||||||
|
yield chronosInternalTmpFuture
|
||||||
|
|
||||||
|
when T isnot void:
|
||||||
|
cast[type(f)](chronosInternalTmpFuture).internalRead()
|
||||||
|
|
||||||
|
type
|
||||||
|
VerifierError {.pure.} = enum
|
||||||
|
Invalid
|
||||||
|
MissingParent
|
||||||
|
UnviableFork
|
||||||
|
Duplicate
|
||||||
|
ProcessingCallback = proc() {.gcsafe, raises: [Defect].}
|
||||||
|
BlockVerifier =
|
||||||
|
proc(signedBlock: int):
|
||||||
|
Future[VerifierError] {.gcsafe, raises: [Defect].}
|
||||||
|
|
||||||
|
SyncQueueKind {.pure.} = enum
|
||||||
|
Forward, Backward
|
||||||
|
|
||||||
|
SyncRequest[T] = object
|
||||||
|
kind: SyncQueueKind
|
||||||
|
index: uint64
|
||||||
|
slot: uint64
|
||||||
|
count: uint64
|
||||||
|
item: T
|
||||||
|
|
||||||
|
SyncResult[T] = object
|
||||||
|
request: SyncRequest[T]
|
||||||
|
data: seq[ref int]
|
||||||
|
|
||||||
|
SyncQueue[T] = ref object
|
||||||
|
kind: SyncQueueKind
|
||||||
|
readyQueue: seq[SyncResult[T]]
|
||||||
|
blockVerifier: BlockVerifier
|
||||||
|
|
||||||
|
iterator blocks[T](sq: SyncQueue[T],
|
||||||
|
sr: SyncResult[T]): ref int =
|
||||||
|
case sq.kind
|
||||||
|
of SyncQueueKind.Forward:
|
||||||
|
for i in countup(0, len(sr.data) - 1):
|
||||||
|
yield sr.data[i]
|
||||||
|
of SyncQueueKind.Backward:
|
||||||
|
for i in countdown(len(sr.data) - 1, 0):
|
||||||
|
yield sr.data[i]
|
||||||
|
|
||||||
|
proc push[T](sq: SyncQueue[T]; sr: SyncRequest[T]; data: seq[ref int];
|
||||||
|
processingCb: ProcessingCallback = nil): Future[void] {.
|
||||||
|
stackTrace: off, gcsafe.} =
|
||||||
|
iterator push_436208182(chronosInternalRetFuture: Future[void]): FutureBase {.
|
||||||
|
closure, gcsafe, raises: [Defect, CatchableError, Exception].} =
|
||||||
|
block:
|
||||||
|
template result(): auto {.used.} =
|
||||||
|
{.fatal: "You should not reference the `result` variable inside" &
|
||||||
|
" a void async proc".}
|
||||||
|
|
||||||
|
let item = default(SyncResult[T])
|
||||||
|
for blk in sq.blocks(item):
|
||||||
|
let res = await sq.blockVerifier(blk[])
|
||||||
|
|
||||||
|
var resultFuture = newFuture[void]("push")
|
||||||
|
resultFuture.closure = push_436208182
|
||||||
|
return resultFuture
|
||||||
|
|
||||||
|
type
|
||||||
|
SomeTPeer = ref object
|
||||||
|
score: int
|
||||||
|
|
||||||
|
proc getSlice(): seq[ref int] =
|
||||||
|
discard
|
||||||
|
|
||||||
|
template smokeTest(kkind: SyncQueueKind, start, finish: uint64,
|
||||||
|
chunkSize: uint64) =
|
||||||
|
var queue: SyncQueue[SomeTPeer]
|
||||||
|
var request: SyncRequest[SomeTPeer]
|
||||||
|
discard queue.push(request, getSlice())
|
||||||
|
|
||||||
|
for k in {SyncQueueKind.Forward}:
|
||||||
|
for item in [(uint64(1181), uint64(1399), 41'u64)]:
|
||||||
|
smokeTest(k, item[0], item[1], item[2])
|
||||||
|
|
@ -559,6 +559,38 @@ template main {.dirty.} =
|
||||||
let x = default(Default)
|
let x = default(Default)
|
||||||
doAssert x.data is DjangoDateTime
|
doAssert x.data is DjangoDateTime
|
||||||
|
|
||||||
|
block:
|
||||||
|
type
|
||||||
|
Result2 = object
|
||||||
|
case o: bool
|
||||||
|
of false:
|
||||||
|
e: float
|
||||||
|
of true:
|
||||||
|
v {.requiresInit.} : int = 1
|
||||||
|
|
||||||
|
proc startSessionSync(): Result2 =
|
||||||
|
return Result2(o: true)
|
||||||
|
|
||||||
|
proc mainSync =
|
||||||
|
let ff = startSessionSync()
|
||||||
|
doAssert ff.v == 1
|
||||||
|
|
||||||
|
mainSync()
|
||||||
|
|
||||||
|
block:
|
||||||
|
type
|
||||||
|
Result2 = object
|
||||||
|
v {.requiresInit.} : int = 1
|
||||||
|
|
||||||
|
proc startSessionSync(): Result2 =
|
||||||
|
return Result2()
|
||||||
|
|
||||||
|
proc mainSync =
|
||||||
|
let ff = startSessionSync()
|
||||||
|
doAssert ff.v == 1
|
||||||
|
|
||||||
|
mainSync()
|
||||||
|
|
||||||
|
|
||||||
static: main()
|
static: main()
|
||||||
main()
|
main()
|
||||||
|
|
|
||||||
|
|
@ -311,3 +311,11 @@ block: # bug #9607
|
||||||
|
|
||||||
doAssert echoL() == "bar"
|
doAssert echoL() == "bar"
|
||||||
doAssert echoM() == "bar"
|
doAssert echoM() == "bar"
|
||||||
|
|
||||||
|
block:
|
||||||
|
macro hello[T](x: T): untyped =
|
||||||
|
result = quote do:
|
||||||
|
let m: `T` = `x`
|
||||||
|
discard m
|
||||||
|
|
||||||
|
hello(12)
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ discard """
|
||||||
x + y = 30
|
x + y = 30
|
||||||
'''
|
'''
|
||||||
"""
|
"""
|
||||||
import std/[sugar, algorithm, random, sets, tables, strutils]
|
import std/[sugar, algorithm, random, sets, tables, strutils, sequtils]
|
||||||
import std/[syncio, assertions]
|
import std/[syncio, assertions]
|
||||||
|
|
||||||
type # for capture test, ref #20679
|
type # for capture test, ref #20679
|
||||||
|
|
@ -287,6 +287,17 @@ proc mainProc() =
|
||||||
doAssertRaises(AssertionDefect): doAssert false
|
doAssertRaises(AssertionDefect): doAssert false
|
||||||
doAssert "except AssertionDefect" in s2
|
doAssert "except AssertionDefect" in s2
|
||||||
|
|
||||||
|
block: # bug #20704
|
||||||
|
proc test() =
|
||||||
|
var xs, ys: seq[int]
|
||||||
|
for i in 0..5:
|
||||||
|
xs.add(i)
|
||||||
|
|
||||||
|
xs.apply(d => ys.add(d))
|
||||||
|
doAssert ys == @[0, 1, 2, 3, 4, 5]
|
||||||
|
|
||||||
|
test()
|
||||||
|
|
||||||
static:
|
static:
|
||||||
main()
|
main()
|
||||||
mainProc()
|
mainProc()
|
||||||
|
|
|
||||||
|
|
@ -245,7 +245,7 @@ block: # bug #11777
|
||||||
var s: S = {1, 2}
|
var s: S = {1, 2}
|
||||||
doAssert 1 in s
|
doAssert 1 in s
|
||||||
|
|
||||||
block: # regression #20807
|
block: # bug #20807
|
||||||
var s: seq[string]
|
var s: seq[string]
|
||||||
template fail =
|
template fail =
|
||||||
s = @[]
|
s = @[]
|
||||||
|
|
@ -255,3 +255,38 @@ block: # regression #20807
|
||||||
test: fail()
|
test: fail()
|
||||||
doAssert not (compiles do:
|
doAssert not (compiles do:
|
||||||
let x: seq[int] = `@`[string]([]))
|
let x: seq[int] = `@`[string]([]))
|
||||||
|
|
||||||
|
block: # bug #21377
|
||||||
|
proc b[T](v: T): seq[int] =
|
||||||
|
let x = 0
|
||||||
|
@[]
|
||||||
|
|
||||||
|
doAssert b(0) == @[]
|
||||||
|
|
||||||
|
block: # bug #21377
|
||||||
|
proc b[T](v: T): seq[T] =
|
||||||
|
let x = 0
|
||||||
|
@[]
|
||||||
|
|
||||||
|
doAssert b(0) == @[]
|
||||||
|
|
||||||
|
block: # bug #21377
|
||||||
|
proc b[T](v: T): set[bool] =
|
||||||
|
let x = 0
|
||||||
|
{}
|
||||||
|
|
||||||
|
doAssert b(0) == {}
|
||||||
|
|
||||||
|
block: # bug #21377
|
||||||
|
proc b[T](v: T): array[0, int] =
|
||||||
|
let x = 0
|
||||||
|
[]
|
||||||
|
|
||||||
|
doAssert b(0) == []
|
||||||
|
|
||||||
|
block: # bug #21377
|
||||||
|
proc b[T](v: T): array[0, (string, string)] =
|
||||||
|
let x = 0
|
||||||
|
{:}
|
||||||
|
|
||||||
|
doAssert b(0) == {:}
|
||||||
|
|
|
||||||
|
|
@ -643,3 +643,16 @@ const b = block:
|
||||||
|
|
||||||
doAssert a == @[@[0, 1, 2], @[0, 1, 2], @[0, 1, 2]]
|
doAssert a == @[@[0, 1, 2], @[0, 1, 2], @[0, 1, 2]]
|
||||||
doAssert b == @[@[0, 1, 2], @[0, 1, 2], @[0, 1, 2]]
|
doAssert b == @[@[0, 1, 2], @[0, 1, 2], @[0, 1, 2]]
|
||||||
|
|
||||||
|
macro m1(s: string): int =
|
||||||
|
var ProcID {.global, compileTime.}: int
|
||||||
|
inc(ProcID)
|
||||||
|
result = newLit(ProcID)
|
||||||
|
|
||||||
|
proc macroGlobal =
|
||||||
|
doAssert m1("Macro argument") == 1
|
||||||
|
doAssert m1("Macro argument") == 2
|
||||||
|
doAssert m1("Macro argument") == 3
|
||||||
|
|
||||||
|
static: macroGlobal()
|
||||||
|
macroGlobal()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue