Merge branch 'devel' into araq

This commit is contained in:
Andreas Rumpf 2017-07-13 04:48:22 +02:00
commit 2b862b74e0
165 changed files with 4572 additions and 920 deletions

View file

@ -56,7 +56,7 @@ build_script:
test_script: test_script:
- tests\testament\tester --pedantic all -d:nimCoroutines - tests\testament\tester --pedantic all -d:nimCoroutines
- koch csource # - koch csource
- koch zip # - koch zip
deploy: off deploy: off

View file

@ -224,7 +224,7 @@ type
TNodeKinds* = set[TNodeKind] TNodeKinds* = set[TNodeKind]
type type
TSymFlag* = enum # already 32 flags! TSymFlag* = enum # already 33 flags!
sfUsed, # read access of sym (for warnings) or simply used sfUsed, # read access of sym (for warnings) or simply used
sfExported, # symbol is exported from module sfExported, # symbol is exported from module
sfFromGeneric, # symbol is instantiation of a generic; this is needed sfFromGeneric, # symbol is instantiation of a generic; this is needed
@ -453,9 +453,10 @@ type
nfIsRef # this node is a 'ref' node; used for the VM nfIsRef # this node is a 'ref' node; used for the VM
nfPreventCg # this node should be ignored by the codegen nfPreventCg # this node should be ignored by the codegen
nfBlockArg # this a stmtlist appearing in a call (e.g. a do block) nfBlockArg # this a stmtlist appearing in a call (e.g. a do block)
nfFromTemplate # a top-level node returned from a template
TNodeFlags* = set[TNodeFlag] TNodeFlags* = set[TNodeFlag]
TTypeFlag* = enum # keep below 32 for efficiency reasons (now: 30) TTypeFlag* = enum # keep below 32 for efficiency reasons (now: beyond that)
tfVarargs, # procedure has C styled varargs tfVarargs, # procedure has C styled varargs
# tyArray type represeting a varargs list # tyArray type represeting a varargs list
tfNoSideEffect, # procedure type does not allow side effects tfNoSideEffect, # procedure type does not allow side effects
@ -508,6 +509,9 @@ type
tfTriggersCompileTime # uses the NimNode type which make the proc tfTriggersCompileTime # uses the NimNode type which make the proc
# implicitly '.compiletime' # implicitly '.compiletime'
tfRefsAnonObj # used for 'ref object' and 'ptr object' tfRefsAnonObj # used for 'ref object' and 'ptr object'
tfCovariant # covariant generic param mimicing a ptr type
tfWeakCovariant # covariant generic param mimicing a seq/array type
tfContravariant # contravariant generic param
TTypeFlags* = set[TTypeFlag] TTypeFlags* = set[TTypeFlag]
@ -956,7 +960,8 @@ const
skMacro, skTemplate, skConverter, skEnumField, skLet, skStub, skAlias} skMacro, skTemplate, skConverter, skEnumField, skLet, skStub, skAlias}
PersistentNodeFlags*: TNodeFlags = {nfBase2, nfBase8, nfBase16, PersistentNodeFlags*: TNodeFlags = {nfBase2, nfBase8, nfBase16,
nfDotSetter, nfDotField, nfDotSetter, nfDotField,
nfIsRef, nfPreventCg, nfLL} nfIsRef, nfPreventCg, nfLL,
nfFromTemplate}
namePos* = 0 namePos* = 0
patternPos* = 1 # empty except for term rewriting macros patternPos* = 1 # empty except for term rewriting macros
genericParamsPos* = 2 genericParamsPos* = 2
@ -1007,15 +1012,17 @@ proc add*(father, son: PNode) =
if isNil(father.sons): father.sons = @[] if isNil(father.sons): father.sons = @[]
add(father.sons, son) add(father.sons, son)
proc `[]`*(n: PNode, i: int): PNode {.inline.} = type Indexable = PNode | PType
result = n.sons[i]
template `[]`*(n: Indexable, i: int): Indexable =
n.sons[i]
template `-|`*(b, s: untyped): untyped = template `-|`*(b, s: untyped): untyped =
(if b >= 0: b else: s.len + b) (if b >= 0: b else: s.len + b)
# son access operators with support for negative indices # son access operators with support for negative indices
template `{}`*(n: PNode, i: int): untyped = n[i -| n] template `{}`*(n: Indexable, i: int): untyped = n[i -| n]
template `{}=`*(n: PNode, i: int, s: PNode) = template `{}=`*(n: Indexable, i: int, s: Indexable) =
n.sons[i -| n] = s n.sons[i -| n] = s
when defined(useNodeIds): when defined(useNodeIds):

View file

@ -219,7 +219,7 @@ proc rspaces(x: int): Rope =
proc toYamlChar(c: char): string = proc toYamlChar(c: char): string =
case c case c
of '\0'..'\x1F', '\x80'..'\xFF': result = "\\u" & strutils.toHex(ord(c), 4) of '\0'..'\x1F', '\x7F'..'\xFF': result = "\\u" & strutils.toHex(ord(c), 4)
of '\'', '\"', '\\': result = '\\' & c of '\'', '\"', '\\': result = '\\' & c
else: result = $c else: result = $c
@ -407,6 +407,8 @@ proc debugTree(n: PNode, indent: int, maxRecDepth: int;
var istr = rspaces(indent + 2) var istr = rspaces(indent + 2)
result = "{$N$1\"kind\": $2" % result = "{$N$1\"kind\": $2" %
[istr, makeYamlString($n.kind)] [istr, makeYamlString($n.kind)]
when defined(useNodeIds):
addf(result, ",$N$1\"id\": $2", [istr, rope(n.id)])
addf(result, ",$N$1\"info\": $2", [istr, lineInfoToStr(n.info)]) addf(result, ",$N$1\"info\": $2", [istr, lineInfoToStr(n.info)])
if maxRecDepth != 0: if maxRecDepth != 0:
addf(result, ",$N$1\"flags\": $2", [istr, rope($n.flags)]) addf(result, ",$N$1\"flags\": $2", [istr, rope($n.flags)])
@ -574,12 +576,6 @@ proc strTableAdd(t: var TStrTable, n: PSym) =
strTableRawInsert(t.data, n) strTableRawInsert(t.data, n)
inc(t.counter) inc(t.counter)
proc reallySameIdent(a, b: string): bool {.inline.} =
when defined(nimfix):
result = a[0] == b[0]
else:
result = true
proc strTableIncl*(t: var TStrTable, n: PSym; onConflictKeepOld=false): bool {.discardable.} = proc strTableIncl*(t: var TStrTable, n: PSym; onConflictKeepOld=false): bool {.discardable.} =
# returns true if n is already in the string table: # returns true if n is already in the string table:
# It is essential that `n` is written nevertheless! # It is essential that `n` is written nevertheless!
@ -594,7 +590,7 @@ proc strTableIncl*(t: var TStrTable, n: PSym; onConflictKeepOld=false): bool {.d
# and overloading: (var x=@[]; x).mapIt(it). # and overloading: (var x=@[]; x).mapIt(it).
# So it is possible the very same sym is added multiple # So it is possible the very same sym is added multiple
# times to the symbol table which we allow here with the 'it == n' check. # times to the symbol table which we allow here with the 'it == n' check.
if it.name.id == n.name.id and reallySameIdent(it.name.s, n.name.s): if it.name.id == n.name.id:
if it == n: return false if it == n: return false
replaceSlot = h replaceSlot = h
h = nextTry(h, high(t.data)) h = nextTry(h, high(t.data))

View file

@ -78,8 +78,10 @@ proc genLiteral(p: BProc, n: PNode, ty: PType): Rope =
[p.module.tmpBase, rope(id)]) [p.module.tmpBase, rope(id)])
else: else:
result = makeCString(n.strVal) result = makeCString(n.strVal)
of nkFloatLit..nkFloat64Lit: of nkFloatLit, nkFloat64Lit:
result = rope(n.floatVal.toStrMaxPrecision) result = rope(n.floatVal.toStrMaxPrecision)
of nkFloat32Lit:
result = rope(n.floatVal.toStrMaxPrecision("f"))
else: else:
internalError(n.info, "genLiteral(" & $n.kind & ')') internalError(n.info, "genLiteral(" & $n.kind & ')')
result = nil result = nil
@ -535,7 +537,7 @@ proc binaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
"(($4)($1) * ($4)($2))", # MulF64 "(($4)($1) * ($4)($2))", # MulF64
"(($4)($1) / ($4)($2))", # DivF64 "(($4)($1) / ($4)($2))", # DivF64
"($4)((NU$3)($1) >> (NU$3)($2))", # ShrI "($4)((NU$5)($1) >> (NU$3)($2))", # ShrI
"($4)((NU$3)($1) << (NU$3)($2))", # ShlI "($4)((NU$3)($1) << (NU$3)($2))", # ShlI
"($4)($1 & $2)", # BitandI "($4)($1 & $2)", # BitandI
"($4)($1 | $2)", # BitorI "($4)($1 | $2)", # BitorI
@ -575,16 +577,17 @@ proc binaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
"($1 != $2)"] # Xor "($1 != $2)"] # Xor
var var
a, b: TLoc a, b: TLoc
s: BiggestInt s, k: BiggestInt
assert(e.sons[1].typ != nil) assert(e.sons[1].typ != nil)
assert(e.sons[2].typ != nil) assert(e.sons[2].typ != nil)
initLocExpr(p, e.sons[1], a) initLocExpr(p, e.sons[1], a)
initLocExpr(p, e.sons[2], b) initLocExpr(p, e.sons[2], b)
# BUGFIX: cannot use result-type here, as it may be a boolean # BUGFIX: cannot use result-type here, as it may be a boolean
s = max(getSize(a.t), getSize(b.t)) * 8 s = max(getSize(a.t), getSize(b.t)) * 8
k = getSize(a.t) * 8
putIntoDest(p, d, e.typ, putIntoDest(p, d, e.typ,
binArithTab[op] % [rdLoc(a), rdLoc(b), rope(s), binArithTab[op] % [rdLoc(a), rdLoc(b), rope(s),
getSimpleTypeDesc(p.module, e.typ)]) getSimpleTypeDesc(p.module, e.typ), rope(k)])
proc genEqProc(p: BProc, e: PNode, d: var TLoc) = proc genEqProc(p: BProc, e: PNode, d: var TLoc) =
var a, b: TLoc var a, b: TLoc
@ -654,7 +657,9 @@ proc genDeref(p: BProc, e: PNode, d: var TLoc; enforceDeref=false) =
d.s = OnHeap d.s = OnHeap
else: else:
var a: TLoc var a: TLoc
let typ = skipTypes(e.sons[0].typ, abstractInst) var typ = skipTypes(e.sons[0].typ, abstractInst)
if typ.kind in {tyUserTypeClass, tyUserTypeClassInst} and typ.isResolvedUserTypeClass:
typ = typ.lastSon
if typ.kind == tyVar and tfVarIsPtr notin typ.flags and p.module.compileToCpp and e.sons[0].kind == nkHiddenAddr: if typ.kind == tyVar and tfVarIsPtr notin typ.flags and p.module.compileToCpp and e.sons[0].kind == nkHiddenAddr:
initLocExprSingleUse(p, e[0][0], d) initLocExprSingleUse(p, e[0][0], d)
return return
@ -2132,7 +2137,14 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
if n.sons[0].kind != nkEmpty: if n.sons[0].kind != nkEmpty:
genLineDir(p, n) genLineDir(p, n)
var a: TLoc var a: TLoc
initLocExpr(p, n.sons[0], a) if n[0].kind in nkCallKinds:
# bug #6037: do not assign to a temp in C++ mode:
incl a.flags, lfSingleUse
genCall(p, n[0], a)
if lfSingleUse notin a.flags:
line(p, cpsStmts, a.r & ";" & tnl)
else:
initLocExpr(p, n.sons[0], a)
of nkAsmStmt: genAsmStmt(p, n) of nkAsmStmt: genAsmStmt(p, n)
of nkTryStmt: of nkTryStmt:
if p.module.compileToCpp and optNoCppExceptions notin gGlobalOptions: if p.module.compileToCpp and optNoCppExceptions notin gGlobalOptions:
@ -2157,8 +2169,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
# are not transformed correctly. We work around this issue (#411) here # are not transformed correctly. We work around this issue (#411) here
# by ensuring it's no inner proc (owner is a module): # by ensuring it's no inner proc (owner is a module):
if prc.skipGenericOwner.kind == skModule and sfCompileTime notin prc.flags: if prc.skipGenericOwner.kind == skModule and sfCompileTime notin prc.flags:
if (optDeadCodeElim notin gGlobalOptions and if (not emitLazily(prc)) or
sfDeadCodeElim notin getModule(prc).flags) or
({sfExportc, sfCompilerProc} * prc.flags == {sfExportc}) or ({sfExportc, sfCompilerProc} * prc.flags == {sfExportc}) or
(sfExportc in prc.flags and lfExportLib in prc.loc.flags) or (sfExportc in prc.flags and lfExportLib in prc.loc.flags) or
(prc.kind == skMethod): (prc.kind == skMethod):
@ -2214,7 +2225,7 @@ proc getNullValueAux(p: BProc; obj, cons: PNode, result: var Rope) =
let field = obj.sym let field = obj.sym
for i in 1..<cons.len: for i in 1..<cons.len:
if cons[i].kind == nkExprColonExpr: if cons[i].kind == nkExprColonExpr:
if cons[i][0].sym == field: if cons[i][0].sym.name == field.name:
result.add genConstExpr(p, cons[i][1]) result.add genConstExpr(p, cons[i][1])
return return
elif i == field.position: elif i == field.position:

View file

@ -39,13 +39,11 @@ proc genVarTuple(p: BProc, n: PNode) =
var L = sonsLen(n) var L = sonsLen(n)
# if we have a something that's been captured, use the lowering instead: # if we have a something that's been captured, use the lowering instead:
var useLowering = false
for i in countup(0, L-3): for i in countup(0, L-3):
if n[i].kind != nkSym: if n[i].kind != nkSym:
useLowering = true; break genStmts(p, lowerTupleUnpacking(n, p.prc))
if useLowering: return
genStmts(p, lowerTupleUnpacking(n, p.prc))
return
genLineDir(p, n) genLineDir(p, n)
initLocExpr(p, n.sons[L-1], tup) initLocExpr(p, n.sons[L-1], tup)
var t = tup.t.skipTypes(abstractInst) var t = tup.t.skipTypes(abstractInst)
@ -182,10 +180,11 @@ proc genGotoVar(p: BProc; value: PNode) =
lineF(p, cpsStmts, "goto NIMSTATE_$#;$n", [value.intVal.rope]) lineF(p, cpsStmts, "goto NIMSTATE_$#;$n", [value.intVal.rope])
proc genSingleVar(p: BProc, a: PNode) = proc genSingleVar(p: BProc, a: PNode) =
var v = a.sons[0].sym let v = a.sons[0].sym
if {sfCompileTime, sfGoto} * v.flags != {}: if sfCompileTime in v.flags: return
if sfGoto in v.flags:
# translate 'var state {.goto.} = X' into 'goto LX': # translate 'var state {.goto.} = X' into 'goto LX':
if sfGoto in v.flags: genGotoVar(p, a.sons[2]) genGotoVar(p, a.sons[2])
return return
var targetProc = p var targetProc = p
if sfGlobal in v.flags: if sfGlobal in v.flags:

View file

@ -470,7 +470,12 @@ proc genRecordFieldsAux(m: BModule, n: PNode,
let a = genRecordFieldsAux(m, k, "$1.$2" % [ae, sname], rectype, let a = genRecordFieldsAux(m, k, "$1.$2" % [ae, sname], rectype,
check) check)
if a != nil: if a != nil:
add(unionBody, "struct {") if tfPacked notin rectype.flags:
add(unionBody, "struct {")
else:
addf(unionBody, CC[cCompiler].structStmtFmt,
[rope"struct", nil, rope(CC[cCompiler].packedPragma)])
add(unionBody, "{")
add(unionBody, a) add(unionBody, a)
addf(unionBody, "} $1;$n", [sname]) addf(unionBody, "} $1;$n", [sname])
else: else:

View file

@ -11,7 +11,7 @@
import import
ast, astalgo, ropes, hashes, strutils, types, msgs, wordrecg, ast, astalgo, ropes, hashes, strutils, types, msgs, wordrecg,
platform, trees platform, trees, options
proc getPragmaStmt*(n: PNode, w: TSpecialWord): PNode = proc getPragmaStmt*(n: PNode, w: TSpecialWord): PNode =
case n.kind case n.kind
@ -211,20 +211,8 @@ proc mangle*(name: string): string =
if requiresUnderscore: if requiresUnderscore:
result.add "_" result.add "_"
proc makeLLVMString*(s: string): Rope = proc emitLazily*(s: PSym): bool {.inline.} =
const MaxLineLength = 64 result = optDeadCodeElim in gGlobalOptions or
result = nil sfDeadCodeElim in getModule(s).flags
var res = "c\""
for i in countup(0, len(s) - 1):
if (i + 1) mod MaxLineLength == 0:
add(result, rope(res))
setLen(res, 0)
case s[i]
of '\0'..'\x1F', '\x80'..'\xFF', '\"', '\\':
add(res, '\\')
add(res, toHex(ord(s[i]), 2))
else: add(res, s[i])
add(res, "\\00\"")
add(result, rope(res))
initTypeTables() initTypeTables()

View file

@ -47,10 +47,6 @@ proc findPendingModule(m: BModule, s: PSym): BModule =
var ms = getModule(s) var ms = getModule(s)
result = m.g.modules[ms.position] result = m.g.modules[ms.position]
proc emitLazily(s: PSym): bool {.inline.} =
result = optDeadCodeElim in gGlobalOptions or
sfDeadCodeElim in getModule(s).flags
proc initLoc(result: var TLoc, k: TLocKind, typ: PType, s: TStorageLoc) = proc initLoc(result: var TLoc, k: TLocKind, typ: PType, s: TStorageLoc) =
result.k = k result.k = k
result.s = s result.s = s
@ -142,6 +138,13 @@ proc ropecg(m: BModule, frmt: FormatStr, args: varargs[Rope]): Rope =
template rfmt(m: BModule, fmt: string, args: varargs[Rope]): untyped = template rfmt(m: BModule, fmt: string, args: varargs[Rope]): untyped =
ropecg(m, fmt, args) ropecg(m, fmt, args)
var indent = "\t".rope
proc indentLine(p: BProc, r: Rope): Rope =
result = r
for i in countup(0, p.blocks.len-1):
prepend(result, indent)
proc appcg(m: BModule, c: var Rope, frmt: FormatStr, proc appcg(m: BModule, c: var Rope, frmt: FormatStr,
args: varargs[Rope]) = args: varargs[Rope]) =
add(c, ropecg(m, frmt, args)) add(c, ropecg(m, frmt, args))
@ -154,11 +157,6 @@ proc appcg(p: BProc, s: TCProcSection, frmt: FormatStr,
args: varargs[Rope]) = args: varargs[Rope]) =
add(p.s(s), ropecg(p.module, frmt, args)) add(p.s(s), ropecg(p.module, frmt, args))
var indent = "\t".rope
proc indentLine(p: BProc, r: Rope): Rope =
result = r
for i in countup(0, p.blocks.len-1): prepend(result, indent)
proc line(p: BProc, s: TCProcSection, r: Rope) = proc line(p: BProc, s: TCProcSection, r: Rope) =
add(p.s(s), indentLine(p, r)) add(p.s(s), indentLine(p, r))
@ -612,8 +610,18 @@ proc generateHeaders(m: BModule) =
addf(m.s[cfsHeaders], "#include \"$1\"$N", [rope(it)]) addf(m.s[cfsHeaders], "#include \"$1\"$N", [rope(it)])
else: else:
addf(m.s[cfsHeaders], "#include $1$N", [rope(it)]) addf(m.s[cfsHeaders], "#include $1$N", [rope(it)])
add(m.s[cfsHeaders], "#undef LANGUAGE_C" & tnl)
add(m.s[cfsHeaders], "#undef MIPSEB" & tnl)
add(m.s[cfsHeaders], "#undef MIPSEL" & tnl)
add(m.s[cfsHeaders], "#undef PPC" & tnl)
add(m.s[cfsHeaders], "#undef R3000" & tnl)
add(m.s[cfsHeaders], "#undef R4000" & tnl)
add(m.s[cfsHeaders], "#undef i386" & tnl)
add(m.s[cfsHeaders], "#undef linux" & tnl) add(m.s[cfsHeaders], "#undef linux" & tnl)
add(m.s[cfsHeaders], "#undef mips" & tnl)
add(m.s[cfsHeaders], "#undef near" & tnl) add(m.s[cfsHeaders], "#undef near" & tnl)
add(m.s[cfsHeaders], "#undef powerpc" & tnl)
add(m.s[cfsHeaders], "#undef unix" & tnl)
proc initFrame(p: BProc, procname, filename: Rope): Rope = proc initFrame(p: BProc, procname, filename: Rope): Rope =
discard cgsym(p.module, "nimFrame") discard cgsym(p.module, "nimFrame")
@ -898,14 +906,14 @@ proc genMainProc(m: BModule) =
# prevents inlining of the NimMainInner function and dependent # prevents inlining of the NimMainInner function and dependent
# functions, which might otherwise merge their stack frames. # functions, which might otherwise merge their stack frames.
PreMainBody = PreMainBody =
"void PreMainInner() {$N" & "void PreMainInner(void) {$N" &
"\tsystemInit000();$N" & "\tsystemInit000();$N" &
"$1" & "$1" &
"$2" & "$2" &
"$3" & "$3" &
"}$N$N" & "}$N$N" &
"void PreMain() {$N" & "void PreMain(void) {$N" &
"\tvoid (*volatile inner)();$N" & "\tvoid (*volatile inner)(void);$N" &
"\tsystemDatInit000();$N" & "\tsystemDatInit000();$N" &
"\tinner = PreMainInner;$N" & "\tinner = PreMainInner;$N" &
"$4$5" & "$4$5" &
@ -924,7 +932,7 @@ proc genMainProc(m: BModule) =
NimMainProc = NimMainProc =
"N_CDECL(void, NimMain)(void) {$N" & "N_CDECL(void, NimMain)(void) {$N" &
"\tvoid (*volatile inner)();$N" & "\tvoid (*volatile inner)(void);$N" &
"\tPreMain();$N" & "\tPreMain();$N" &
"\tinner = NimMainInner;$N" & "\tinner = NimMainInner;$N" &
"$2" & "$2" &

View file

@ -257,13 +257,14 @@ proc genDispatcher(methods: TSymSeq, relevantCols: IntSet): PSym =
cond = a cond = a
else: else:
cond = isn cond = isn
var call = newNodeI(nkCall, base.info) let retTyp = base.typ.sons[0]
let call = newNodeIT(nkCall, base.info, retTyp)
addSon(call, newSymNode(curr)) addSon(call, newSymNode(curr))
for col in countup(1, paramLen - 1): for col in countup(1, paramLen - 1):
addSon(call, genConv(newSymNode(base.typ.n.sons[col].sym), addSon(call, genConv(newSymNode(base.typ.n.sons[col].sym),
curr.typ.sons[col], false)) curr.typ.sons[col], false))
var ret: PNode var ret: PNode
if base.typ.sons[0] != nil: if retTyp != nil:
var a = newNodeI(nkFastAsgn, base.info) var a = newNodeI(nkFastAsgn, base.info)
addSon(a, newSymNode(base.ast.sons[resultPos].sym)) addSon(a, newSymNode(base.ast.sons[resultPos].sym))
addSon(a, call) addSon(a, call)

View file

@ -103,3 +103,4 @@ proc initDefines*() =
defineSymbol("nimNewShiftOps") defineSymbol("nimNewShiftOps")
defineSymbol("nimDistros") defineSymbol("nimDistros")
defineSymbol("nimHasCppDefine") defineSymbol("nimHasCppDefine")
defineSymbol("nimGenericInOutFlags")

View file

@ -155,5 +155,7 @@ proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym; fromHlo=false): PNode =
#if ctx.instLines: result.info = n.info #if ctx.instLines: result.info = n.info
for i in countup(0, safeLen(body) - 1): for i in countup(0, safeLen(body) - 1):
evalTemplateAux(body.sons[i], args, ctx, result) evalTemplateAux(body.sons[i], args, ctx, result)
result.flags.incl nfFromTemplate
result = wrapInComesFrom(n.info, result) result = wrapInComesFrom(n.info, result)
dec(evalTemplateCounter) dec(evalTemplateCounter)

View file

@ -66,6 +66,8 @@ Files: "compiler"
Files: "doc" Files: "doc"
Files: "doc/html" Files: "doc/html"
Files: "tools" Files: "tools"
Files: "nimsuggest"
Files: "nimsuggest/tests/*.nim"
Files: "web/website.ini" Files: "web/website.ini"
Files: "web/ticker.html" Files: "web/ticker.html"
Files: "web/*.nim" Files: "web/*.nim"

View file

@ -94,12 +94,39 @@ type
target: TTarget # duplicated here for faster dispatching target: TTarget # duplicated here for faster dispatching
unique: int # for temp identifier generation unique: int # for temp identifier generation
blocks: seq[TBlock] blocks: seq[TBlock]
extraIndent: int
up: PProc # up the call chain; required for closure support up: PProc # up the call chain; required for closure support
declaredGlobals: IntSet declaredGlobals: IntSet
template `|`(a, b: untyped): untyped {.dirty.} = template `|`(a, b: untyped): untyped {.dirty.} =
(if p.target == targetJS: a else: b) (if p.target == targetJS: a else: b)
var indent = "\t".rope
proc indentLine(p: PProc, r: Rope): Rope =
result = r
var p = p
while true:
for i in countup(0, p.blocks.len - 1 + p.extraIndent):
prepend(result, indent)
if p.up == nil or p.up.prc != p.prc.owner:
break
p = p.up
template line(p: PProc, added: string) =
add(p.body, indentLine(p, rope(added)))
template line(p: PProc, added: Rope) =
add(p.body, indentLine(p, added))
template lineF(p: PProc, frmt: FormatStr, args: varargs[Rope]) =
add(p.body, indentLine(p, ropes.`%`(frmt, args)))
template nested(p, body) =
inc p.extraIndent
body
dec p.extraIndent
proc newGlobals(): PGlobals = proc newGlobals(): PGlobals =
new(result) new(result)
result.forwarded = @[] result.forwarded = @[]
@ -129,7 +156,8 @@ proc newProc(globals: PGlobals, module: BModule, procDef: PNode,
module: module, module: module,
procDef: procDef, procDef: procDef,
g: globals, g: globals,
target: module.target) target: module.target,
extraIndent: int(procDef != nil))
if procDef != nil: result.prc = procDef.sons[namePos].sym if procDef != nil: result.prc = procDef.sons[namePos].sym
if result.target == targetPHP: if result.target == targetPHP:
result.declaredGlobals = initIntSet() result.declaredGlobals = initIntSet()
@ -290,7 +318,7 @@ proc getTemp(p: PProc, defineInLocals: bool = true): Rope =
if p.target == targetJS: if p.target == targetJS:
result = "Tmp$1" % [rope(p.unique)] result = "Tmp$1" % [rope(p.unique)]
if defineInLocals: if defineInLocals:
addf(p.locals, "var $1;$n", [result]) add(p.locals, p.indentLine("var $1;$n" % [result]))
else: else:
result = "$$Tmp$1" % [rope(p.unique)] result = "$$Tmp$1" % [rope(p.unique)]
@ -315,9 +343,11 @@ proc genAnd(p: PProc, a, b: PNode, r: var TCompRes) =
# tmp = b # tmp = b
# tmp # tmp
gen(p, a, x) gen(p, a, x)
p.body.addf("if (!$1) $2 = false; else {", [x.rdLoc, r.rdLoc]) lineF(p, "if (!$1) $2 = false; else {", [x.rdLoc, r.rdLoc])
gen(p, b, y) p.nested:
p.body.addf("$2 = $1; }", [y.rdLoc, r.rdLoc]) gen(p, b, y)
lineF(p, "$2 = $1;", [y.rdLoc, r.rdLoc])
line(p, "}")
proc genOr(p: PProc, a, b: PNode, r: var TCompRes) = proc genOr(p: PProc, a, b: PNode, r: var TCompRes) =
assert r.kind == resNone assert r.kind == resNone
@ -331,9 +361,11 @@ proc genOr(p: PProc, a, b: PNode, r: var TCompRes) =
r.res = p.getTemp r.res = p.getTemp
r.kind = resVal r.kind = resVal
gen(p, a, x) gen(p, a, x)
p.body.addf("if ($1) $2 = true; else {", [x.rdLoc, r.rdLoc]) lineF(p, "if ($1) $2 = true; else {", [x.rdLoc, r.rdLoc])
gen(p, b, y) p.nested:
p.body.addf("$2 = $1; }", [y.rdLoc, r.rdLoc]) gen(p, b, y)
lineF(p, "$2 = $1;", [y.rdLoc, r.rdLoc])
line(p, "}")
type type
TMagicFrmt = array[0..3, string] TMagicFrmt = array[0..3, string]
@ -415,12 +447,10 @@ const # magic checked op; magic unchecked op; checked op; unchecked op
["", "", "Math.floor($1)", "Math.floor($1)"], # ToInt ["", "", "Math.floor($1)", "Math.floor($1)"], # ToInt
["", "", "Math.floor($1)", "Math.floor($1)"], # ToBiggestInt ["", "", "Math.floor($1)", "Math.floor($1)"], # ToBiggestInt
["nimCharToStr", "nimCharToStr", "nimCharToStr($1)", "nimCharToStr($1)"], ["nimCharToStr", "nimCharToStr", "nimCharToStr($1)", "nimCharToStr($1)"],
["nimBoolToStr", "nimBoolToStr", "nimBoolToStr($1)", "nimBoolToStr($1)"], [ ["nimBoolToStr", "nimBoolToStr", "nimBoolToStr($1)", "nimBoolToStr($1)"],
"cstrToNimstr", "cstrToNimstr", "cstrToNimstr(($1)+\"\")", ["cstrToNimstr", "cstrToNimstr", "cstrToNimstr(($1)+\"\")", "cstrToNimstr(($1)+\"\")"],
"cstrToNimstr(($1)+\"\")"], ["cstrToNimstr", "cstrToNimstr", ["cstrToNimstr", "cstrToNimstr", "cstrToNimstr(($1)+\"\")", "cstrToNimstr(($1)+\"\")"],
"cstrToNimstr(($1)+\"\")", ["cstrToNimstr", "cstrToNimstr", "cstrToNimstr(($1)+\"\")", "cstrToNimstr(($1)+\"\")"],
"cstrToNimstr(($1)+\"\")"], ["cstrToNimstr",
"cstrToNimstr", "cstrToNimstr(($1)+\"\")", "cstrToNimstr(($1)+\"\")"],
["cstrToNimstr", "cstrToNimstr", "cstrToNimstr($1)", "cstrToNimstr($1)"], ["cstrToNimstr", "cstrToNimstr", "cstrToNimstr($1)", "cstrToNimstr($1)"],
["", "", "$1", "$1"]] ["", "", "$1", "$1"]]
@ -475,18 +505,18 @@ proc unaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) =
r.res = frmt % [r.rdLoc] r.res = frmt % [r.rdLoc]
r.kind = resExpr r.kind = resExpr
proc arithAux(p: PProc, n: PNode, r: var TCompRes, op: TMagic, ops: TMagicOps) = proc arithAux(p: PProc, n: PNode, r: var TCompRes, op: TMagic) =
var var
x, y: TCompRes x, y: TCompRes
let i = ord(optOverflowCheck notin p.options) let i = ord(optOverflowCheck notin p.options)
useMagic(p, ops[op][i]) useMagic(p, jsOps[op][i])
if sonsLen(n) > 2: if sonsLen(n) > 2:
gen(p, n.sons[1], x) gen(p, n.sons[1], x)
gen(p, n.sons[2], y) gen(p, n.sons[2], y)
r.res = ops[op][i + 2] % [x.rdLoc, y.rdLoc] r.res = jsOps[op][i + 2] % [x.rdLoc, y.rdLoc]
else: else:
gen(p, n.sons[1], r) gen(p, n.sons[1], r)
r.res = ops[op][i + 2] % [r.rdLoc] r.res = jsOps[op][i + 2] % [r.rdLoc]
proc arith(p: PProc, n: PNode, r: var TCompRes, op: TMagic) = proc arith(p: PProc, n: PNode, r: var TCompRes, op: TMagic) =
case op case op
@ -501,7 +531,7 @@ proc arith(p: PProc, n: PNode, r: var TCompRes, op: TMagic) =
gen(p, n.sons[2], y) gen(p, n.sons[2], y)
r.res = "intval($1 / $2)" % [x.rdLoc, y.rdLoc] r.res = "intval($1 / $2)" % [x.rdLoc, y.rdLoc]
else: else:
arithAux(p, n, r, op, jsOps) arithAux(p, n, r, op)
of mModI: of mModI:
if p.target == targetPHP: if p.target == targetPHP:
var x, y: TCompRes var x, y: TCompRes
@ -509,7 +539,7 @@ proc arith(p: PProc, n: PNode, r: var TCompRes, op: TMagic) =
gen(p, n.sons[2], y) gen(p, n.sons[2], y)
r.res = "($1 % $2)" % [x.rdLoc, y.rdLoc] r.res = "($1 % $2)" % [x.rdLoc, y.rdLoc]
else: else:
arithAux(p, n, r, op, jsOps) arithAux(p, n, r, op)
of mShrI: of mShrI:
var x, y: TCompRes var x, y: TCompRes
gen(p, n.sons[1], x) gen(p, n.sons[1], x)
@ -534,9 +564,9 @@ proc arith(p: PProc, n: PNode, r: var TCompRes, op: TMagic) =
else: else:
gen(p, n.sons[1], r) gen(p, n.sons[1], r)
else: else:
arithAux(p, n, r, op, jsOps) arithAux(p, n, r, op)
else: else:
arithAux(p, n, r, op, jsOps) arithAux(p, n, r, op)
r.kind = resExpr r.kind = resExpr
proc hasFrameInfo(p: PProc): bool = proc hasFrameInfo(p: PProc): bool =
@ -546,14 +576,14 @@ proc hasFrameInfo(p: PProc): bool =
proc genLineDir(p: PProc, n: PNode) = proc genLineDir(p: PProc, n: PNode) =
let line = toLinenumber(n.info) let line = toLinenumber(n.info)
if optLineDir in p.options: if optLineDir in p.options:
addf(p.body, "// line $2 \"$1\"$n", lineF(p, "// line $2 \"$1\"$n",
[rope(toFilename(n.info)), rope(line)]) [rope(toFilename(n.info)), rope(line)])
if {optStackTrace, optEndb} * p.options == {optStackTrace, optEndb} and if {optStackTrace, optEndb} * p.options == {optStackTrace, optEndb} and
((p.prc == nil) or sfPure notin p.prc.flags): ((p.prc == nil) or sfPure notin p.prc.flags):
useMagic(p, "endb") useMagic(p, "endb")
addf(p.body, "endb($1);$n", [rope(line)]) lineF(p, "endb($1);$n", [rope(line)])
elif hasFrameInfo(p): elif hasFrameInfo(p):
addf(p.body, "F.line = $1;$n" | "$$F['line'] = $1;$n", [rope(line)]) lineF(p, "F.line = $1;$n" | "$$F['line'] = $1;$n", [rope(line)])
proc genWhileStmt(p: PProc, n: PNode) = proc genWhileStmt(p: PProc, n: PNode) =
var var
@ -566,20 +596,20 @@ proc genWhileStmt(p: PProc, n: PNode) =
p.blocks[length].id = -p.unique p.blocks[length].id = -p.unique
p.blocks[length].isLoop = true p.blocks[length].isLoop = true
let labl = p.unique.rope let labl = p.unique.rope
addf(p.body, "L$1: while (true) {$n" | "while (true) {$n", [labl]) lineF(p, "L$1: while (true) {$n" | "while (true) {$n", [labl])
gen(p, n.sons[0], cond) p.nested: gen(p, n.sons[0], cond)
addf(p.body, "if (!$1) break L$2;$n" | "if (!$1) goto L$2;$n", lineF(p, "if (!$1) break L$2;$n" | "if (!$1) goto L$2;$n",
[cond.res, labl]) [cond.res, labl])
genStmt(p, n.sons[1]) p.nested: genStmt(p, n.sons[1])
addf(p.body, "}$n" | "}L$#:;$n", [labl]) lineF(p, "}$n" | "}L$#:;$n", [labl])
setLen(p.blocks, length) setLen(p.blocks, length)
proc moveInto(p: PProc, src: var TCompRes, dest: TCompRes) = proc moveInto(p: PProc, src: var TCompRes, dest: TCompRes) =
if src.kind != resNone: if src.kind != resNone:
if dest.kind != resNone: if dest.kind != resNone:
p.body.addf("$1 = $2;$n", [dest.rdLoc, src.rdLoc]) lineF(p, "$1 = $2;$n", [dest.rdLoc, src.rdLoc])
else: else:
p.body.addf("$1;$n", [src.rdLoc]) lineF(p, "$1;$n", [src.rdLoc])
src.kind = resNone src.kind = resNone
src.res = nil src.res = nil
@ -620,8 +650,8 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) =
var tmpFramePtr = rope"F" var tmpFramePtr = rope"F"
if optStackTrace notin p.options: if optStackTrace notin p.options:
tmpFramePtr = p.getTemp(true) tmpFramePtr = p.getTemp(true)
add(p.body, tmpFramePtr & " = framePtr;" & tnl) line(p, tmpFramePtr & " = framePtr;" & tnl)
addf(p.body, "try {$n", []) lineF(p, "try {$n", [])
if p.target == targetPHP and p.globals == nil: if p.target == targetPHP and p.globals == nil:
p.globals = "global $lastJSError; global $prevJSError;".rope p.globals = "global $lastJSError; global $prevJSError;".rope
var a: TCompRes var a: TCompRes
@ -632,18 +662,18 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) =
if p.target == targetJS and catchBranchesExist: if p.target == targetJS and catchBranchesExist:
addf(p.body, "--excHandler;$n} catch (EXC) {$n var prevJSError = lastJSError;$n" & addf(p.body, "--excHandler;$n} catch (EXC) {$n var prevJSError = lastJSError;$n" &
" lastJSError = EXC;$n --excHandler;$n", []) " lastJSError = EXC;$n --excHandler;$n", [])
add(p.body, "framePtr = $1;$n" % [tmpFramePtr]) line(p, "framePtr = $1;$n" % [tmpFramePtr])
elif p.target == targetPHP: elif p.target == targetPHP:
addf(p.body, "} catch (Exception $$EXC) {$n $$prevJSError = $$lastJSError;$n $$lastJSError = $$EXC;$n", []) lineF(p, "} catch (Exception $$EXC) {$n $$prevJSError = $$lastJSError;$n $$lastJSError = $$EXC;$n", [])
while i < length and n.sons[i].kind == nkExceptBranch: while i < length and n.sons[i].kind == nkExceptBranch:
let blen = sonsLen(n.sons[i]) let blen = sonsLen(n.sons[i])
if blen == 1: if blen == 1:
# general except section: # general except section:
generalCatchBranchExists = true generalCatchBranchExists = true
if i > 1: addf(p.body, "else {$n", []) if i > 1: lineF(p, "else {$n", [])
gen(p, n.sons[i].sons[0], a) gen(p, n.sons[i].sons[0], a)
moveInto(p, a, r) moveInto(p, a, r)
if i > 1: addf(p.body, "}$n", []) if i > 1: lineF(p, "}$n", [])
else: else:
var orExpr: Rope = nil var orExpr: Rope = nil
useMagic(p, "isObj") useMagic(p, "isObj")
@ -653,30 +683,32 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) =
if orExpr != nil: add(orExpr, "||") if orExpr != nil: add(orExpr, "||")
addf(orExpr, "isObj($2lastJSError.m_type, $1)", addf(orExpr, "isObj($2lastJSError.m_type, $1)",
[genTypeInfo(p, n.sons[i].sons[j].typ), dollar]) [genTypeInfo(p, n.sons[i].sons[j].typ), dollar])
if i > 1: add(p.body, "else ") if i > 1: line(p, "else ")
addf(p.body, "if ($1lastJSError && ($2)) {$n", [dollar, orExpr]) lineF(p, "if ($1lastJSError && ($2)) {$n", [dollar, orExpr])
gen(p, n.sons[i].sons[blen - 1], a) gen(p, n.sons[i].sons[blen - 1], a)
moveInto(p, a, r) moveInto(p, a, r)
addf(p.body, "}$n", []) lineF(p, "}$n", [])
inc(i) inc(i)
if catchBranchesExist: if catchBranchesExist:
if not generalCatchBranchExists: if not generalCatchBranchExists:
useMagic(p, "reraiseException") useMagic(p, "reraiseException")
add(p.body, "else {" & tnl & "reraiseException();" & tnl & "}" & tnl) line(p, "else {" & tnl)
line(p, indent & "reraiseException();" & tnl)
line(p, "}" & tnl)
addf(p.body, "$1lastJSError = $1prevJSError;$n", [dollar]) addf(p.body, "$1lastJSError = $1prevJSError;$n", [dollar])
if p.target == targetJS: if p.target == targetJS:
add(p.body, "} finally {" & tnl) line(p, "} finally {" & tnl)
add(p.body, "framePtr = $1;$n" % [tmpFramePtr]) line(p, "framePtr = $1;$n" % [tmpFramePtr])
if p.target == targetPHP: if p.target == targetPHP:
# XXX ugly hack for PHP codegen # XXX ugly hack for PHP codegen
add(p.body, "}" & tnl) line(p, "}" & tnl)
if i < length and n.sons[i].kind == nkFinally: if i < length and n.sons[i].kind == nkFinally:
genStmt(p, n.sons[i].sons[0]) genStmt(p, n.sons[i].sons[0])
if p.target == targetPHP: if p.target == targetPHP:
# XXX ugly hack for PHP codegen # XXX ugly hack for PHP codegen
add(p.body, "if($lastJSError) throw($lastJSError);" & tnl) line(p, "if($lastJSError) throw($lastJSError);" & tnl)
if p.target == targetJS: if p.target == targetJS:
add(p.body, "}" & tnl) line(p, "}" & tnl)
proc genRaiseStmt(p: PProc, n: PNode) = proc genRaiseStmt(p: PProc, n: PNode) =
genLineDir(p, n) genLineDir(p, n)
@ -685,11 +717,11 @@ proc genRaiseStmt(p: PProc, n: PNode) =
gen(p, n.sons[0], a) gen(p, n.sons[0], a)
let typ = skipTypes(n.sons[0].typ, abstractPtrs) let typ = skipTypes(n.sons[0].typ, abstractPtrs)
useMagic(p, "raiseException") useMagic(p, "raiseException")
addf(p.body, "raiseException($1, $2);$n", lineF(p, "raiseException($1, $2);$n",
[a.rdLoc, makeJSString(typ.sym.name.s)]) [a.rdLoc, makeJSString(typ.sym.name.s)])
else: else:
useMagic(p, "reraiseException") useMagic(p, "reraiseException")
add(p.body, "reraiseException();" & tnl) line(p, "reraiseException();" & tnl)
proc genCaseJS(p: PProc, n: PNode, r: var TCompRes) = proc genCaseJS(p: PProc, n: PNode, r: var TCompRes) =
var var
@ -699,9 +731,9 @@ proc genCaseJS(p: PProc, n: PNode, r: var TCompRes) =
let stringSwitch = skipTypes(n.sons[0].typ, abstractVar).kind == tyString let stringSwitch = skipTypes(n.sons[0].typ, abstractVar).kind == tyString
if stringSwitch and p.target == targetJS: if stringSwitch and p.target == targetJS:
useMagic(p, "toJSStr") useMagic(p, "toJSStr")
addf(p.body, "switch (toJSStr($1)) {$n", [cond.rdLoc]) lineF(p, "switch (toJSStr($1)) {$n", [cond.rdLoc])
else: else:
addf(p.body, "switch ($1) {$n", [cond.rdLoc]) lineF(p, "switch ($1) {$n", [cond.rdLoc])
if not isEmptyType(n.typ): if not isEmptyType(n.typ):
r.kind = resVal r.kind = resVal
r.res = getTemp(p) r.res = getTemp(p)
@ -715,27 +747,29 @@ proc genCaseJS(p: PProc, n: PNode, r: var TCompRes) =
var v = copyNode(e.sons[0]) var v = copyNode(e.sons[0])
while v.intVal <= e.sons[1].intVal: while v.intVal <= e.sons[1].intVal:
gen(p, v, cond) gen(p, v, cond)
addf(p.body, "case $1: ", [cond.rdLoc]) lineF(p, "case $1:$n", [cond.rdLoc])
inc(v.intVal) inc(v.intVal)
else: else:
if stringSwitch: if stringSwitch:
case e.kind case e.kind
of nkStrLit..nkTripleStrLit: addf(p.body, "case $1: ", of nkStrLit..nkTripleStrLit: lineF(p, "case $1:$n",
[makeJSString(e.strVal, false)]) [makeJSString(e.strVal, false)])
else: internalError(e.info, "jsgen.genCaseStmt: 2") else: internalError(e.info, "jsgen.genCaseStmt: 2")
else: else:
gen(p, e, cond) gen(p, e, cond)
addf(p.body, "case $1: ", [cond.rdLoc]) lineF(p, "case $1:$n", [cond.rdLoc])
gen(p, lastSon(it), stmt) p.nested:
moveInto(p, stmt, r) gen(p, lastSon(it), stmt)
addf(p.body, "$nbreak;$n", []) moveInto(p, stmt, r)
lineF(p, "break;$n", [])
of nkElse: of nkElse:
addf(p.body, "default: $n", []) lineF(p, "default: $n", [])
gen(p, it.sons[0], stmt) p.nested:
moveInto(p, stmt, r) gen(p, it.sons[0], stmt)
addf(p.body, "break;$n", []) moveInto(p, stmt, r)
lineF(p, "break;$n", [])
else: internalError(it.info, "jsgen.genCaseStmt") else: internalError(it.info, "jsgen.genCaseStmt")
addf(p.body, "}$n", []) lineF(p, "}$n", [])
proc genBlock(p: PProc, n: PNode, r: var TCompRes) = proc genBlock(p: PProc, n: PNode, r: var TCompRes) =
inc(p.unique) inc(p.unique)
@ -746,13 +780,13 @@ proc genBlock(p: PProc, n: PNode, r: var TCompRes) =
var sym = n.sons[0].sym var sym = n.sons[0].sym
sym.loc.k = locOther sym.loc.k = locOther
sym.position = idx+1 sym.position = idx+1
let labl = p.unique
lineF(p, "L$1: do {$n" | "", [labl.rope])
setLen(p.blocks, idx + 1) setLen(p.blocks, idx + 1)
p.blocks[idx].id = - p.unique # negative because it isn't used yet p.blocks[idx].id = - p.unique # negative because it isn't used yet
let labl = p.unique
addf(p.body, "L$1: do {$n" | "", [labl.rope])
gen(p, n.sons[1], r) gen(p, n.sons[1], r)
addf(p.body, "} while(false);$n" | "$nL$#:;$n", [labl.rope])
setLen(p.blocks, idx) setLen(p.blocks, idx)
lineF(p, "} while(false);$n" | "$nL$#:;$n", [labl.rope])
proc genBreakStmt(p: PProc, n: PNode) = proc genBreakStmt(p: PProc, n: PNode) =
var idx: int var idx: int
@ -770,19 +804,31 @@ proc genBreakStmt(p: PProc, n: PNode) =
if idx < 0 or not p.blocks[idx].isLoop: if idx < 0 or not p.blocks[idx].isLoop:
internalError(n.info, "no loop to break") internalError(n.info, "no loop to break")
p.blocks[idx].id = abs(p.blocks[idx].id) # label is used p.blocks[idx].id = abs(p.blocks[idx].id) # label is used
addf(p.body, "break L$1;$n" | "goto L$1;$n", [rope(p.blocks[idx].id)]) lineF(p, "break L$1;$n" | "goto L$1;$n", [rope(p.blocks[idx].id)])
proc genAsmOrEmitStmt(p: PProc, n: PNode) = proc genAsmOrEmitStmt(p: PProc, n: PNode) =
genLineDir(p, n) genLineDir(p, n)
p.body.add p.indentLine(nil)
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
case n.sons[i].kind let it = n[i]
of nkStrLit..nkTripleStrLit: add(p.body, n.sons[i].strVal) case it.kind
of nkStrLit..nkTripleStrLit:
p.body.add(it.strVal)
of nkSym: of nkSym:
let v = n.sons[i].sym let v = it.sym
if p.target == targetPHP and v.kind in {skVar, skLet, skTemp, skConst, skResult, skParam, skForVar}: # for backwards compatibility we don't deref syms here :-(
add(p.body, "$") if v.kind in {skVar, skLet, skTemp, skConst, skResult, skParam, skForVar}:
add(p.body, mangleName(v, p.target)) if p.target == targetPHP: p.body.add "$"
else: internalError(n.sons[i].info, "jsgen: genAsmOrEmitStmt()") p.body.add mangleName(v, p.target)
else:
var r: TCompRes
gen(p, it, r)
p.body.add(r.rdLoc)
else:
var r: TCompRes
gen(p, it, r)
p.body.add(r.rdLoc)
p.body.add tnl
proc genIf(p: PProc, n: PNode, r: var TCompRes) = proc genIf(p: PProc, n: PNode, r: var TCompRes) =
var cond, stmt: TCompRes var cond, stmt: TCompRes
@ -794,18 +840,18 @@ proc genIf(p: PProc, n: PNode, r: var TCompRes) =
let it = n.sons[i] let it = n.sons[i]
if sonsLen(it) != 1: if sonsLen(it) != 1:
if i > 0: if i > 0:
addf(p.body, "else {$n", []) lineF(p, "else {$n", [])
inc(toClose) inc(toClose)
gen(p, it.sons[0], cond) p.nested: gen(p, it.sons[0], cond)
addf(p.body, "if ($1) {$n", [cond.rdLoc]) lineF(p, "if ($1) {$n", [cond.rdLoc])
gen(p, it.sons[1], stmt) gen(p, it.sons[1], stmt)
else: else:
# else part: # else part:
addf(p.body, "else {$n", []) lineF(p, "else {$n", [])
gen(p, it.sons[0], stmt) p.nested: gen(p, it.sons[0], stmt)
moveInto(p, stmt, r) moveInto(p, stmt, r)
addf(p.body, "}$n", []) lineF(p, "}$n", [])
add(p.body, repeat('}', toClose) & tnl) line(p, repeat('}', toClose) & tnl)
proc generateHeader(p: PProc, typ: PType): Rope = proc generateHeader(p: PProc, typ: PType): Rope =
result = nil result = nil
@ -834,6 +880,16 @@ proc generateHeader(p: PProc, typ: PType): Rope =
# add(result, name) # add(result, name)
# add(result, "_Idx") # add(result, "_Idx")
proc countJsParams(typ: PType): int =
for i in countup(1, sonsLen(typ.n) - 1):
assert(typ.n.sons[i].kind == nkSym)
var param = typ.n.sons[i].sym
if isCompileTimeOnly(param.typ): continue
if mapType(param.typ) == etyBaseIndex:
inc result, 2
else:
inc result
const const
nodeKindsNeedNoCopy = {nkCharLit..nkInt64Lit, nkStrLit..nkTripleStrLit, nodeKindsNeedNoCopy = {nkCharLit..nkInt64Lit, nkStrLit..nkTripleStrLit,
nkFloatLit..nkFloat64Lit, nkCurly, nkPar, nkObjConstr, nkStringToCString, nkFloatLit..nkFloat64Lit, nkCurly, nkPar, nkObjConstr, nkStringToCString,
@ -854,7 +910,7 @@ proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) =
gen(p, x[0], a) gen(p, x[0], a)
gen(p, x[1], b) gen(p, x[1], b)
gen(p, y, c) gen(p, y, c)
addf(p.body, "$#[$#] = chr($#);$n", [a.rdLoc, b.rdLoc, c.rdLoc]) lineF(p, "$#[$#] = chr($#);$n", [a.rdLoc, b.rdLoc, c.rdLoc])
return return
var xtyp = mapType(p, x.typ) var xtyp = mapType(p, x.typ)
@ -862,7 +918,7 @@ proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) =
if x.kind == nkHiddenDeref and x.sons[0].kind == nkCall and xtyp != etyObject: if x.kind == nkHiddenDeref and x.sons[0].kind == nkCall and xtyp != etyObject:
gen(p, x.sons[0], a) gen(p, x.sons[0], a)
let tmp = p.getTemp(false) let tmp = p.getTemp(false)
addf(p.body, "var $1 = $2;$n", [tmp, a.rdLoc]) lineF(p, "var $1 = $2;$n", [tmp, a.rdLoc])
a.res = "$1[0][$1[1]]" % [tmp] a.res = "$1[0][$1[1]]" % [tmp]
else: else:
gen(p, x, a) gen(p, x, a)
@ -876,29 +932,31 @@ proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) =
case xtyp case xtyp
of etySeq: of etySeq:
if (needsNoCopy(p, y) and needsNoCopy(p, x)) or noCopyNeeded: if (needsNoCopy(p, y) and needsNoCopy(p, x)) or noCopyNeeded:
addf(p.body, "$1 = $2;$n", [a.rdLoc, b.rdLoc]) lineF(p, "$1 = $2;$n", [a.rdLoc, b.rdLoc])
else: else:
useMagic(p, "nimCopy") useMagic(p, "nimCopy")
addf(p.body, "$1 = nimCopy(null, $2, $3);$n", lineF(p, "$1 = nimCopy(null, $2, $3);$n",
[a.rdLoc, b.res, genTypeInfo(p, y.typ)]) [a.rdLoc, b.res, genTypeInfo(p, y.typ)])
of etyObject: of etyObject:
if (needsNoCopy(p, y) and needsNoCopy(p, x)) or noCopyNeeded: if (needsNoCopy(p, y) and needsNoCopy(p, x)) or noCopyNeeded:
addf(p.body, "$1 = $2;$n", [a.rdLoc, b.rdLoc]) lineF(p, "$1 = $2;$n", [a.rdLoc, b.rdLoc])
else: else:
useMagic(p, "nimCopy") useMagic(p, "nimCopy")
addf(p.body, "nimCopy($1, $2, $3);$n", lineF(p, "nimCopy($1, $2, $3);$n",
[a.res, b.res, genTypeInfo(p, y.typ)]) [a.res, b.res, genTypeInfo(p, y.typ)])
of etyBaseIndex: of etyBaseIndex:
if a.typ != etyBaseIndex or b.typ != etyBaseIndex: if a.typ != etyBaseIndex or b.typ != etyBaseIndex:
if y.kind == nkCall: if y.kind == nkCall:
let tmp = p.getTemp(false) let tmp = p.getTemp(false)
addf(p.body, "var $1 = $4; $2 = $1[0]; $3 = $1[1];$n", [tmp, a.address, a.res, b.rdLoc]) lineF(p, "var $1 = $4; $2 = $1[0]; $3 = $1[1];$n", [tmp, a.address, a.res, b.rdLoc])
elif b.typ == etyBaseIndex:
lineF(p, "$# = $#;$n", [a.res, b.rdLoc])
else: else:
internalError(x.info, "genAsgn") internalError(x.info, "genAsgn")
else: else:
addf(p.body, "$1 = $2; $3 = $4;$n", [a.address, b.address, a.res, b.res]) lineF(p, "$1 = $2; $3 = $4;$n", [a.address, b.address, a.res, b.res])
else: else:
addf(p.body, "$1 = $2;$n", [a.res, b.res]) lineF(p, "$1 = $2;$n", [a.res, b.res])
proc genAsgn(p: PProc, n: PNode) = proc genAsgn(p: PProc, n: PNode) =
genLineDir(p, n) genLineDir(p, n)
@ -906,7 +964,15 @@ proc genAsgn(p: PProc, n: PNode) =
proc genFastAsgn(p: PProc, n: PNode) = proc genFastAsgn(p: PProc, n: PNode) =
genLineDir(p, n) genLineDir(p, n)
genAsgnAux(p, n.sons[0], n.sons[1], noCopyNeeded=true) # 'shallowCopy' always produced 'noCopyNeeded = true' here but this is wrong
# for code like
# while j >= pos:
# dest[i].shallowCopy(dest[j])
# See bug #5933. So we try to be more compatible with the C backend semantics
# here for 'shallowCopy'. This is an educated guess and might require further
# changes later:
let noCopy = n[0].typ.skipTypes(abstractInst).kind in {tySequence, tyString}
genAsgnAux(p, n.sons[0], n.sons[1], noCopyNeeded=noCopy)
proc genSwap(p: PProc, n: PNode) = proc genSwap(p: PProc, n: PNode) =
var a, b: TCompRes var a, b: TCompRes
@ -917,12 +983,13 @@ proc genSwap(p: PProc, n: PNode) =
let tmp2 = p.getTemp(false) let tmp2 = p.getTemp(false)
if a.typ != etyBaseIndex or b.typ != etyBaseIndex: if a.typ != etyBaseIndex or b.typ != etyBaseIndex:
internalError(n.info, "genSwap") internalError(n.info, "genSwap")
addf(p.body, "var $1 = $2; $2 = $3; $3 = $1;$n" | lineF(p, "var $1 = $2; $2 = $3; $3 = $1;$n" |
"$1 = $2; $2 = $3; $3 = $1;$n", [ "$1 = $2; $2 = $3; $3 = $1;$n",
tmp, a.address, b.address]) [tmp, a.address, b.address])
tmp = tmp2 tmp = tmp2
addf(p.body, "var $1 = $2; $2 = $3; $3 = $1;" | lineF(p, "var $1 = $2; $2 = $3; $3 = $1;" |
"$1 = $2; $2 = $3; $3 = $1;", [tmp, a.res, b.res]) "$1 = $2; $2 = $3; $3 = $1;",
[tmp, a.res, b.res])
proc getFieldPosition(f: PNode): int = proc getFieldPosition(f: PNode): int =
case f.kind case f.kind
@ -992,15 +1059,15 @@ proc genArrayAddr(p: PProc, n: PNode, r: var TCompRes) =
var typ = skipTypes(m.sons[0].typ, abstractPtrs) var typ = skipTypes(m.sons[0].typ, abstractPtrs)
if typ.kind == tyArray: first = firstOrd(typ.sons[0]) if typ.kind == tyArray: first = firstOrd(typ.sons[0])
else: first = 0 else: first = 0
if optBoundsCheck in p.options and not isConstExpr(m.sons[1]): if optBoundsCheck in p.options:
useMagic(p, "chckIndx") useMagic(p, "chckIndx")
if p.target == targetPHP: if p.target == targetPHP:
if typ.kind != tyString: if typ.kind != tyString:
r.res = "chckIndx($1, $2, count($3))-$2" % [b.res, rope(first), a.res] r.res = "chckIndx($1, $2, count($3)-1)-$2" % [b.res, rope(first), a.res]
else: else:
r.res = "chckIndx($1, $2, strlen($3))-$2" % [b.res, rope(first), a.res] r.res = "chckIndx($1, $2, strlen($3))-$2" % [b.res, rope(first), a.res]
else: else:
r.res = "chckIndx($1, $2, $3.length)-$2" % [b.res, rope(first), a.res] r.res = "chckIndx($1, $2, $3.length-1)-$2" % [b.res, rope(first), a.res]
elif first != 0: elif first != 0:
r.res = "($1)-$2" % [b.res, rope(first)] r.res = "($1)-$2" % [b.res, rope(first)]
else: else:
@ -1193,17 +1260,21 @@ proc genSym(p: PProc, n: PNode, r: var TCompRes) =
r.kind = resVal r.kind = resVal
proc genDeref(p: PProc, n: PNode, r: var TCompRes) = proc genDeref(p: PProc, n: PNode, r: var TCompRes) =
if mapType(p, n.sons[0].typ) == etyObject: let it = n.sons[0]
gen(p, n.sons[0], r) let t = mapType(p, it.typ)
if t == etyObject:
gen(p, it, r)
else: else:
var a: TCompRes var a: TCompRes
gen(p, n.sons[0], a) gen(p, it, a)
r.kind = resExpr r.kind = resExpr
if a.typ == etyBaseIndex: if a.typ == etyBaseIndex:
r.res = "$1[$2]" % [a.address, a.res] r.res = "$1[$2]" % [a.address, a.res]
elif n.sons[0].kind == nkCall: elif it.kind == nkCall:
let tmp = p.getTemp let tmp = p.getTemp
r.res = "($1 = $2, $1[0])[$1[1]]" % [tmp, a.res] r.res = "($1 = $2, $1[0])[$1[1]]" % [tmp, a.res]
elif t == etyBaseIndex:
r.res = "$1[0]" % [a.res]
else: else:
internalError(n.info, "genDeref") internalError(n.info, "genDeref")
@ -1217,7 +1288,7 @@ proc genArgNoParam(p: PProc, n: PNode, r: var TCompRes) =
else: else:
add(r.res, a.res) add(r.res, a.res)
proc genArg(p: PProc, n: PNode, param: PSym, r: var TCompRes) = proc genArg(p: PProc, n: PNode, param: PSym, r: var TCompRes; emitted: ptr int = nil) =
var a: TCompRes var a: TCompRes
gen(p, n, a) gen(p, n, a)
if skipTypes(param.typ, abstractVar).kind in {tyOpenArray, tyVarargs} and if skipTypes(param.typ, abstractVar).kind in {tyOpenArray, tyVarargs} and
@ -1227,6 +1298,12 @@ proc genArg(p: PProc, n: PNode, param: PSym, r: var TCompRes) =
add(r.res, a.address) add(r.res, a.address)
add(r.res, ", ") add(r.res, ", ")
add(r.res, a.res) add(r.res, a.res)
if emitted != nil: inc emitted[]
elif n.typ.kind == tyVar and n.kind in nkCallKinds and mapType(param.typ) == etyBaseIndex:
# this fixes bug #5608:
let tmp = getTemp(p)
add(r.res, "($1 = $2, $1[0]), $1[1]" % [tmp, a.rdLoc])
if emitted != nil: inc emitted[]
else: else:
add(r.res, a.res) add(r.res, a.res)
@ -1237,6 +1314,7 @@ proc genArgs(p: PProc, n: PNode, r: var TCompRes; start=1) =
var typ = skipTypes(n.sons[0].typ, abstractInst) var typ = skipTypes(n.sons[0].typ, abstractInst)
assert(typ.kind == tyProc) assert(typ.kind == tyProc)
assert(sonsLen(typ) == sonsLen(typ.n)) assert(sonsLen(typ) == sonsLen(typ.n))
var emitted = start-1
for i in countup(start, sonsLen(n) - 1): for i in countup(start, sonsLen(n) - 1):
let it = n.sons[i] let it = n.sons[i]
@ -1250,9 +1328,16 @@ proc genArgs(p: PProc, n: PNode, r: var TCompRes; start=1) =
if paramType.isNil: if paramType.isNil:
genArgNoParam(p, it, r) genArgNoParam(p, it, r)
else: else:
genArg(p, it, paramType.sym, r) genArg(p, it, paramType.sym, r, addr emitted)
inc emitted
hasArgs = true hasArgs = true
add(r.res, ")") add(r.res, ")")
when false:
# XXX look into this:
let jsp = countJsParams(typ)
if emitted != jsp and tfVarargs notin typ.flags:
localError(n.info, "wrong number of parameters emitted; expected: " & $jsp &
" but got: " & $emitted)
r.kind = resExpr r.kind = resExpr
proc genOtherArg(p: PProc; n: PNode; i: int; typ: PType; proc genOtherArg(p: PProc; n: PNode; i: int; typ: PType;
@ -1385,7 +1470,7 @@ proc createRecordVarAux(p: PProc, rec: PNode, excludedFieldIDs: IntSet, output:
proc createObjInitList(p: PProc, typ: PType, excludedFieldIDs: IntSet, output: var Rope) = proc createObjInitList(p: PProc, typ: PType, excludedFieldIDs: IntSet, output: var Rope) =
var t = typ var t = typ
if tfFinal notin t.flags or t.sons[0] != nil: if objHasTypeField(t):
if output.len > 0: output.add(", ") if output.len > 0: output.add(", ")
addf(output, "m_type: $1" | "'m_type' => $#", [genTypeInfo(p, t)]) addf(output, "m_type: $1" | "'m_type' => $#", [genTypeInfo(p, t)])
while t != nil: while t != nil:
@ -1483,10 +1568,10 @@ proc genVarInit(p: PProc, v: PSym, n: PNode) =
s: Rope s: Rope
if n.kind == nkEmpty: if n.kind == nkEmpty:
let mname = mangleName(v, p.target) let mname = mangleName(v, p.target)
addf(p.body, "var $1 = $2;$n" | "$$$1 = $2;$n", lineF(p, "var $1 = $2;$n" | "$$$1 = $2;$n",
[mname, createVar(p, v.typ, isIndirect(v))]) [mname, createVar(p, v.typ, isIndirect(v))])
if v.typ.kind in { tyVar, tyPtr, tyRef } and mapType(p, v.typ) == etyBaseIndex: if v.typ.kind in { tyVar, tyPtr, tyRef } and mapType(p, v.typ) == etyBaseIndex:
addf(p.body, "var $1_Idx = 0;$n", [ mname ]) lineF(p, "var $1_Idx = 0;$n", [ mname ])
else: else:
discard mangleName(v, p.target) discard mangleName(v, p.target)
gen(p, n, a) gen(p, n, a)
@ -1501,25 +1586,25 @@ proc genVarInit(p: PProc, v: PSym, n: PNode) =
let targetBaseIndex = {sfAddrTaken, sfGlobal} * v.flags == {} let targetBaseIndex = {sfAddrTaken, sfGlobal} * v.flags == {}
if a.typ == etyBaseIndex: if a.typ == etyBaseIndex:
if targetBaseIndex: if targetBaseIndex:
addf(p.body, "var $1 = $2, $1_Idx = $3;$n", [ lineF(p, "var $1 = $2, $1_Idx = $3;$n",
v.loc.r, a.address, a.res]) [v.loc.r, a.address, a.res])
else: else:
addf(p.body, "var $1 = [$2, $3];$n", lineF(p, "var $1 = [$2, $3];$n",
[v.loc.r, a.address, a.res]) [v.loc.r, a.address, a.res])
else: else:
if targetBaseIndex: if targetBaseIndex:
let tmp = p.getTemp let tmp = p.getTemp
addf(p.body, "var $1 = $2, $3 = $1[0], $3_Idx = $1[1];$n", lineF(p, "var $1 = $2, $3 = $1[0], $3_Idx = $1[1];$n",
[tmp, a.res, v.loc.r]) [tmp, a.res, v.loc.r])
else: else:
addf(p.body, "var $1 = $2;$n", [v.loc.r, a.res]) lineF(p, "var $1 = $2;$n", [v.loc.r, a.res])
return return
else: else:
s = a.res s = a.res
if isIndirect(v): if isIndirect(v):
addf(p.body, "var $1 = /**/[$2];$n", [v.loc.r, s]) lineF(p, "var $1 = [$2];$n", [v.loc.r, s])
else: else:
addf(p.body, "var $1 = $2;$n" | "$$$1 = $2;$n", [v.loc.r, s]) lineF(p, "var $1 = $2;$n" | "$$$1 = $2;$n", [v.loc.r, s])
proc genVarStmt(p: PProc, n: PNode) = proc genVarStmt(p: PProc, n: PNode) =
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
@ -1550,17 +1635,17 @@ proc genNew(p: PProc, n: PNode) =
gen(p, n.sons[1], a) gen(p, n.sons[1], a)
var t = skipTypes(n.sons[1].typ, abstractVar).sons[0] var t = skipTypes(n.sons[1].typ, abstractVar).sons[0]
if p.target == targetJS: if p.target == targetJS:
addf(p.body, "$1 = $2;$n", [a.res, createVar(p, t, false)]) lineF(p, "$1 = $2;$n", [a.res, createVar(p, t, false)])
else: else:
addf(p.body, "$3 = $2; $1 = &$3;$n", [a.res, createVar(p, t, false), getTemp(p)]) lineF(p, "$3 = $2; $1 = &$3;$n", [a.res, createVar(p, t, false), getTemp(p)])
proc genNewSeq(p: PProc, n: PNode) = proc genNewSeq(p: PProc, n: PNode) =
var x, y: TCompRes var x, y: TCompRes
gen(p, n.sons[1], x) gen(p, n.sons[1], x)
gen(p, n.sons[2], y) gen(p, n.sons[2], y)
let t = skipTypes(n.sons[1].typ, abstractVar).sons[0] let t = skipTypes(n.sons[1].typ, abstractVar).sons[0]
addf(p.body, "$1 = new Array($2); for (var i=0;i<$2;++i) {$1[i]=$3;}" | lineF(p, "$1 = new Array($2); for (var i=0;i<$2;++i) {$1[i]=$3;}" |
"$1 = array(); for ($$i=0;$$i<$2;++$$i) {$1[]=$3;}", [ "$1 = array(); for ($$i=0;$$i<$2;++$$i) {$1[]=$3;}", [
x.rdLoc, y.rdLoc, createVar(p, t, false)]) x.rdLoc, y.rdLoc, createVar(p, t, false)])
proc genOrd(p: PProc, n: PNode, r: var TCompRes) = proc genOrd(p: PProc, n: PNode, r: var TCompRes) =
@ -1716,8 +1801,7 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
binaryExpr(p, n, r, "addChar", binaryExpr(p, n, r, "addChar",
"if ($1 != null) { addChar($1, $2); } else { $1 = [$2, 0]; }") "if ($1 != null) { addChar($1, $2); } else { $1 = [$2, 0]; }")
else: else:
binaryExpr(p, n, r, "", binaryExpr(p, n, r, "", "$1 .= chr($2)")
"$1 .= chr($2)")
of mAppendStrStr: of mAppendStrStr:
if p.target == targetJS: if p.target == targetJS:
if skipTypes(n.sons[1].typ, abstractVarRange).kind == tyCString: if skipTypes(n.sons[1].typ, abstractVarRange).kind == tyCString:
@ -1727,15 +1811,23 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
"if ($1 != null) { $1 = ($1.slice(0, -1)).concat($2); } else { $1 = $2;}") "if ($1 != null) { $1 = ($1.slice(0, -1)).concat($2); } else { $1 = $2;}")
# XXX: make a copy of $2, because of Javascript's sucking semantics # XXX: make a copy of $2, because of Javascript's sucking semantics
else: else:
binaryExpr(p, n, r, "", binaryExpr(p, n, r, "", "$1 .= $2;")
"$1 .= $2;")
of mAppendSeqElem: of mAppendSeqElem:
if p.target == targetJS: if p.target == targetJS:
binaryExpr(p, n, r, "", var x, y: TCompRes
"if ($1 != null) { $1.push($2); } else { $1 = [$2]; }") gen(p, n.sons[1], x)
gen(p, n.sons[2], y)
if needsNoCopy(p, n[2]):
r.res = "if ($1 != null) { $1.push($2); } else { $1 = [$2]; }" % [x.rdLoc, y.rdLoc]
else:
useMagic(p, "nimCopy")
let c = getTemp(p, defineInLocals=false)
lineF(p, "var $1 = nimCopy(null, $2, $3);$n",
[c, y.rdLoc, genTypeInfo(p, n[2].typ)])
r.res = "if ($1 != null) { $1.push($2); } else { $1 = [$2]; }" % [x.rdLoc, c]
r.kind = resExpr
else: else:
binaryExpr(p, n, r, "", binaryExpr(p, n, r, "", "$1[] = $2")
"$1[] = $2")
of mConStrStr: of mConStrStr:
if p.target == targetJS: if p.target == targetJS:
genConStrStr(p, n, r) genConStrStr(p, n, r)
@ -1795,7 +1887,14 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
else: binaryExpr(p, n, r, "subInt", "$1 = subInt($1, $2)") else: binaryExpr(p, n, r, "subInt", "$1 = subInt($1, $2)")
of mSetLengthStr: of mSetLengthStr:
binaryExpr(p, n, r, "", "$1.length = $2+1; $1[$1.length-1] = 0" | "$1 = substr($1, 0, $2)") binaryExpr(p, n, r, "", "$1.length = $2+1; $1[$1.length-1] = 0" | "$1 = substr($1, 0, $2)")
of mSetLengthSeq: binaryExpr(p, n, r, "", "$1.length = $2" | "$1 = array_slice($1, 0, $2)") of mSetLengthSeq:
var x, y: TCompRes
gen(p, n.sons[1], x)
gen(p, n.sons[2], y)
let t = skipTypes(n.sons[1].typ, abstractVar).sons[0]
r.res = """if ($1.length < $2) { for (var i=$1.length;i<$2;++i) $1.push($3); }
else { $1.length = $2; }""" % [x.rdLoc, y.rdLoc, createVar(p, t, false)]
r.kind = resExpr
of mCard: unaryExpr(p, n, r, "SetCard", "SetCard($1)") of mCard: unaryExpr(p, n, r, "SetCard", "SetCard($1)")
of mLtSet: binaryExpr(p, n, r, "SetLt", "SetLt($1, $2)") of mLtSet: binaryExpr(p, n, r, "SetLt", "SetLt($1, $2)")
of mLeSet: binaryExpr(p, n, r, "SetLe", "SetLe($1, $2)") of mLeSet: binaryExpr(p, n, r, "SetLe", "SetLe($1, $2)")
@ -1916,10 +2015,19 @@ proc genObjConstr(p: PProc, n: PNode, r: var TCompRes) =
if i > 1: add(initList, ", ") if i > 1: add(initList, ", ")
var it = n.sons[i] var it = n.sons[i]
internalAssert it.kind == nkExprColonExpr internalAssert it.kind == nkExprColonExpr
gen(p, it.sons[1], a) let val = it.sons[1]
gen(p, val, a)
var f = it.sons[0].sym var f = it.sons[0].sym
if f.loc.r == nil: f.loc.r = mangleName(f, p.target) if f.loc.r == nil: f.loc.r = mangleName(f, p.target)
fieldIDs.incl(f.id) fieldIDs.incl(f.id)
let typ = val.typ.skipTypes(abstractInst)
if (typ.kind in IntegralTypes+{tyCstring, tyRef, tyPtr} and
mapType(p, typ) != etyBaseIndex) or needsNoCopy(p, it.sons[1]):
discard
else:
useMagic(p, "nimCopy")
a.res = "nimCopy(null, $1, $2)" % [a.rdLoc, genTypeInfo(p, typ)]
addf(initList, "$#: $#" | "'$#' => $#" , [f.loc.r, a.res]) addf(initList, "$#: $#" | "'$#' => $#" , [f.loc.r, a.res])
let t = skipTypes(n.typ, abstractInst + skipPtrs) let t = skipTypes(n.typ, abstractInst + skipPtrs)
createObjInitList(p, t, fieldIDs, initList) createObjInitList(p, t, fieldIDs, initList)
@ -1986,15 +2094,18 @@ proc genReturnStmt(p: PProc, n: PNode) =
genStmt(p, n.sons[0]) genStmt(p, n.sons[0])
else: else:
genLineDir(p, n) genLineDir(p, n)
addf(p.body, "break BeforeRet;$n" | "goto BeforeRet;$n", []) lineF(p, "break BeforeRet;$n" | "goto BeforeRet;$n", [])
proc frameCreate(p: PProc; procname, filename: Rope): Rope = proc frameCreate(p: PProc; procname, filename: Rope): Rope =
result = (("var F={procname:$1,prev:framePtr,filename:$2,line:0};$nframePtr = F;$n" | let frameFmt =
"global $$framePtr; $$F=array('procname'=>$#,'prev'=>$$framePtr,'filename'=>$#,'line'=>0);$n$$framePtr = &$$F;$n")) % [ "var F={procname:$1,prev:framePtr,filename:$2,line:0};$n" |
procname, filename] "global $$framePtr; $$F=array('procname'=>$#,'prev'=>$$framePtr,'filename'=>$#,'line'=>0);$n"
result = p.indentLine(frameFmt % [procname, filename])
result.add p.indentLine(ropes.`%`("framePtr = F;$n" | "$$framePtr = &$$F;$n", []))
proc frameDestroy(p: PProc): Rope = proc frameDestroy(p: PProc): Rope =
result = rope(("framePtr = F.prev;" | "$framePtr = $F['prev'];") & tnl) result = p.indentLine rope(("framePtr = F.prev;" | "$framePtr = $F['prev'];") & tnl)
proc genProcBody(p: PProc, prc: PSym): Rope = proc genProcBody(p: PProc, prc: PSym): Rope =
if hasFrameInfo(p): if hasFrameInfo(p):
@ -2004,8 +2115,12 @@ proc genProcBody(p: PProc, prc: PSym): Rope =
else: else:
result = nil result = nil
if p.beforeRetNeeded: if p.beforeRetNeeded:
addf(result, "BeforeRet: do {$n$1} while (false); $n" | if p.target == targetJS:
"$# BeforeRet:;$n", [p.body]) result.add p.indentLine(~"BeforeRet: do {$n")
result.add p.body
result.add p.indentLine(~"} while (false);$n")
else:
addF(result, "$# BeforeRet:;$n", [p.body])
else: else:
add(result, p.body) add(result, p.body)
if prc.typ.callConv == ccSysCall and p.target == targetJS: if prc.typ.callConv == ccSysCall and p.target == targetJS:
@ -2014,6 +2129,12 @@ proc genProcBody(p: PProc, prc: PSym): Rope =
if hasFrameInfo(p): if hasFrameInfo(p):
add(result, frameDestroy(p)) add(result, frameDestroy(p))
proc optionaLine(p: Rope): Rope =
if p == nil:
return nil
else:
return p & tnl
proc genProc(oldProc: PProc, prc: PSym): Rope = proc genProc(oldProc: PProc, prc: PSym): Rope =
var var
resultSym: PSym resultSym: PSym
@ -2029,29 +2150,37 @@ proc genProc(oldProc: PProc, prc: PSym): Rope =
if prc.typ.sons[0] != nil and sfPure notin prc.flags: if prc.typ.sons[0] != nil and sfPure notin prc.flags:
resultSym = prc.ast.sons[resultPos].sym resultSym = prc.ast.sons[resultPos].sym
let mname = mangleName(resultSym, p.target) let mname = mangleName(resultSym, p.target)
resultAsgn = ("var $# = $#;$n" | "$$$# = $#;$n") % [ let resVar = createVar(p, resultSym.typ, isIndirect(resultSym))
mname, resultAsgn = p.indentLine(("var $# = $#;$n" | "$$$# = $#;$n") % [mname, resVar])
createVar(p, resultSym.typ, isIndirect(resultSym))]
if resultSym.typ.kind in { tyVar, tyPtr, tyRef } and mapType(p, resultSym.typ) == etyBaseIndex: if resultSym.typ.kind in { tyVar, tyPtr, tyRef } and mapType(p, resultSym.typ) == etyBaseIndex:
resultAsgn.add "var $#_Idx = 0;$n" % [ resultAsgn.add p.indentLine("var $#_Idx = 0;$n" % [mname])
mname ];
gen(p, prc.ast.sons[resultPos], a) gen(p, prc.ast.sons[resultPos], a)
if mapType(p, resultSym.typ) == etyBaseIndex: if mapType(p, resultSym.typ) == etyBaseIndex:
returnStmt = "return [$#, $#];$n" % [a.address, a.res] returnStmt = "return [$#, $#];$n" % [a.address, a.res]
else: else:
returnStmt = "return $#;$n" % [a.res] returnStmt = "return $#;$n" % [a.res]
genStmt(p, prc.getBody)
result = "function $#($#) {$n$#$n$#$#$#$#}$n" % p.nested: genStmt(p, prc.getBody)
[name, header, p.globals, p.locals, resultAsgn, let def = "function $#($#) {$n$#$#$#$#$#" %
genProcBody(p, prc), returnStmt] [name, header,
optionaLine(p.globals),
optionaLine(p.locals),
optionaLine(resultAsgn),
optionaLine(genProcBody(p, prc)),
optionaLine(p.indentLine(returnStmt))]
dec p.extraIndent
result = ~tnl
result.add p.indentLine(def)
result.add p.indentLine(~"}$n")
#if gVerbosity >= 3: #if gVerbosity >= 3:
# echo "END generated code for: " & prc.name.s # echo "END generated code for: " & prc.name.s
proc genStmt(p: PProc, n: PNode) = proc genStmt(p: PProc, n: PNode) =
var r: TCompRes var r: TCompRes
gen(p, n, r) gen(p, n, r)
if r.res != nil: addf(p.body, "$#;$n", [r.res]) if r.res != nil: lineF(p, "$#;$n", [r.res])
proc genPragma(p: PProc, n: PNode) = proc genPragma(p: PProc, n: PNode) =
for it in n.sons: for it in n.sons:
@ -2211,7 +2340,7 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) =
of nkPragma: genPragma(p, n) of nkPragma: genPragma(p, n)
of nkProcDef, nkMethodDef, nkConverterDef: of nkProcDef, nkMethodDef, nkConverterDef:
var s = n.sons[namePos].sym var s = n.sons[namePos].sym
if {sfExportc, sfCompilerProc} * s.flags == {sfExportc}: if sfExportc in s.flags and compilingLib:
genSym(p, n.sons[namePos], r) genSym(p, n.sons[namePos], r)
r.res = nil r.res = nil
of nkGotoState, nkState: of nkGotoState, nkState:
@ -2342,3 +2471,4 @@ proc myOpen(graph: ModuleGraph; s: PSym; cache: IdentCache): PPassContext =
result = r result = r
const JSgenPass* = makePass(myOpen, myOpenCached, myProcess, myClose) const JSgenPass* = makePass(myOpen, myOpenCached, myProcess, myClose)

View file

@ -67,9 +67,13 @@ proc genObjectFields(p: PProc, typ: PType, n: PNode): Rope =
rope(lengthOrd(field.typ)), makeJSString(field.name.s), result] rope(lengthOrd(field.typ)), makeJSString(field.name.s), result]
else: internalError(n.info, "genObjectFields") else: internalError(n.info, "genObjectFields")
proc objHasTypeField(t: PType): bool {.inline.} =
tfInheritable in t.flags or t.sons[0] != nil
proc genObjectInfo(p: PProc, typ: PType, name: Rope) = proc genObjectInfo(p: PProc, typ: PType, name: Rope) =
let kind = if objHasTypeField(typ): tyObject else: tyTuple
var s = ("var $1 = {size: 0, kind: $2, base: null, node: null, " & var s = ("var $1 = {size: 0, kind: $2, base: null, node: null, " &
"finalizer: null};$n") % [name, rope(ord(typ.kind))] "finalizer: null};$n") % [name, rope(ord(kind))]
prepend(p.g.typeInfo, s) prepend(p.g.typeInfo, s)
addf(p.g.typeInfo, "var NNI$1 = $2;$n", addf(p.g.typeInfo, "var NNI$1 = $2;$n",
[rope(typ.id), genObjectFields(p, typ, typ.n)]) [rope(typ.id), genObjectFields(p, typ, typ.n)])

View file

@ -386,11 +386,11 @@ proc getNumber(L: var TLexer, result: var TToken) =
else: else:
matchUnderscoreChars(L, result, {'0'..'9'}) matchUnderscoreChars(L, result, {'0'..'9'})
if (L.buf[L.bufpos] == '.') and (L.buf[L.bufpos + 1] in {'0'..'9'}): if (L.buf[L.bufpos] == '.') and (L.buf[L.bufpos + 1] in {'0'..'9'}):
result.tokType = tkFloat64Lit result.tokType = tkFloatLit
eatChar(L, result, '.') eatChar(L, result, '.')
matchUnderscoreChars(L, result, {'0'..'9'}) matchUnderscoreChars(L, result, {'0'..'9'})
if L.buf[L.bufpos] in {'e', 'E'}: if L.buf[L.bufpos] in {'e', 'E'}:
result.tokType = tkFloat64Lit result.tokType = tkFloatLit
eatChar(L, result, 'e') eatChar(L, result, 'e')
if L.buf[L.bufpos] in {'+', '-'}: if L.buf[L.bufpos] in {'+', '-'}:
eatChar(L, result) eatChar(L, result)
@ -516,7 +516,8 @@ proc getNumber(L: var TLexer, result: var TToken) =
result.fNumber = (cast[PFloat32](addr(xi)))[] result.fNumber = (cast[PFloat32](addr(xi)))[]
# note: this code is endian neutral! # note: this code is endian neutral!
# XXX: Test this on big endian machine! # XXX: Test this on big endian machine!
of tkFloat64Lit: result.fNumber = (cast[PFloat64](addr(xi)))[] of tkFloat64Lit, tkFloatLit:
result.fNumber = (cast[PFloat64](addr(xi)))[]
else: internalError(getLineInfo(L), "getNumber") else: internalError(getLineInfo(L), "getNumber")
# Bounds checks. Non decimal literals are allowed to overflow the range of # Bounds checks. Non decimal literals are allowed to overflow the range of

View file

@ -113,6 +113,7 @@ type
errGenericLambdaNotAllowed, errGenericLambdaNotAllowed,
errProcHasNoConcreteType, errProcHasNoConcreteType,
errCompilerDoesntSupportTarget, errCompilerDoesntSupportTarget,
errInOutFlagNotExtern,
errUser, errUser,
warnCannotOpenFile, warnCannotOpenFile,
warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit, warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit,
@ -380,6 +381,7 @@ const
"of the generic paramers can be inferred from the expected signature.", "of the generic paramers can be inferred from the expected signature.",
errProcHasNoConcreteType: "'$1' doesn't have a concrete type, due to unspecified generic parameters.", errProcHasNoConcreteType: "'$1' doesn't have a concrete type, due to unspecified generic parameters.",
errCompilerDoesntSupportTarget: "The current compiler \'$1\' doesn't support the requested compilation target", errCompilerDoesntSupportTarget: "The current compiler \'$1\' doesn't support the requested compilation target",
errInOutFlagNotExtern: "The `$1` modifier can be used only with imported types",
errUser: "$1", errUser: "$1",
warnCannotOpenFile: "cannot open \'$1\'", warnCannotOpenFile: "cannot open \'$1\'",
warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored", warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored",
@ -541,7 +543,7 @@ var
proc toCChar*(c: char): string = proc toCChar*(c: char): string =
case c case c
of '\0'..'\x1F', '\x80'..'\xFF': result = '\\' & toOctal(c) of '\0'..'\x1F', '\x7F'..'\xFF': result = '\\' & toOctal(c)
of '\'', '\"', '\\', '?': result = '\\' & c of '\'', '\"', '\\', '?': result = '\\' & c
else: result = $(c) else: result = $(c)

View file

@ -187,6 +187,9 @@ var
const oKeepVariableNames* = true const oKeepVariableNames* = true
template compilingLib*: bool =
gGlobalOptions * {optGenGuiApp, optGenDynLib} != {}
proc mainCommandArg*: string = proc mainCommandArg*: string =
## This is intended for commands like check or parse ## This is intended for commands like check or parse
## which will work on the main project file unless ## which will work on the main project file unless

View file

@ -39,6 +39,9 @@ type
inPragma*: int # Pragma level inPragma*: int # Pragma level
inSemiStmtList*: int inSemiStmtList*: int
SymbolMode = enum
smNormal, smAllowNil, smAfterDot
proc parseAll*(p: var TParser): PNode proc parseAll*(p: var TParser): PNode
proc closeParser*(p: var TParser) proc closeParser*(p: var TParser)
proc parseTopLevelStmt*(p: var TParser): PNode proc parseTopLevelStmt*(p: var TParser): PNode
@ -62,7 +65,7 @@ proc optPar*(p: var TParser)
proc optInd*(p: var TParser, n: PNode) proc optInd*(p: var TParser, n: PNode)
proc indAndComment*(p: var TParser, n: PNode) proc indAndComment*(p: var TParser, n: PNode)
proc setBaseFlags*(n: PNode, base: TNumericalBase) proc setBaseFlags*(n: PNode, base: TNumericalBase)
proc parseSymbol*(p: var TParser, allowNil = false): PNode proc parseSymbol*(p: var TParser, mode = smNormal): PNode
proc parseTry(p: var TParser; isExpr: bool): PNode proc parseTry(p: var TParser; isExpr: bool): PNode
proc parseCase(p: var TParser): PNode proc parseCase(p: var TParser): PNode
proc parseStmtPragma(p: var TParser): PNode proc parseStmtPragma(p: var TParser): PNode
@ -304,13 +307,24 @@ proc colcom(p: var TParser, n: PNode) =
eat(p, tkColon) eat(p, tkColon)
skipComment(p, n) skipComment(p, n)
proc parseSymbol(p: var TParser, allowNil = false): PNode = proc parseSymbol(p: var TParser, mode = smNormal): PNode =
#| symbol = '`' (KEYW|IDENT|literal|(operator|'('|')'|'['|']'|'{'|'}'|'=')+)+ '`' #| symbol = '`' (KEYW|IDENT|literal|(operator|'('|')'|'['|']'|'{'|'}'|'=')+)+ '`'
#| | IDENT | 'addr' | 'type' #| | IDENT | KEYW
case p.tok.tokType case p.tok.tokType
of tkSymbol, tkAddr, tkType: of tkSymbol:
result = newIdentNodeP(p.tok.ident, p) result = newIdentNodeP(p.tok.ident, p)
getTok(p) getTok(p)
of tokKeywordLow..tokKeywordHigh:
if p.tok.tokType == tkAddr or p.tok.tokType == tkType or mode == smAfterDot:
# for backwards compatibility these 2 are always valid:
result = newIdentNodeP(p.tok.ident, p)
getTok(p)
elif p.tok.tokType == tkNil and mode == smAllowNil:
result = newNodeP(nkNilLit, p)
getTok(p)
else:
parMessage(p, errIdentifierExpected, p.tok)
result = ast.emptyNode
of tkAccent: of tkAccent:
result = newNodeP(nkAccQuoted, p) result = newNodeP(nkAccQuoted, p)
getTok(p) getTok(p)
@ -336,16 +350,12 @@ proc parseSymbol(p: var TParser, allowNil = false): PNode =
break break
eat(p, tkAccent) eat(p, tkAccent)
else: else:
if allowNil and p.tok.tokType == tkNil: parMessage(p, errIdentifierExpected, p.tok)
result = newNodeP(nkNilLit, p) # BUGFIX: We must consume a token here to prevent endless loops!
getTok(p) # But: this really sucks for idetools and keywords, so we don't do it
else: # if it is a keyword:
parMessage(p, errIdentifierExpected, p.tok) #if not isKeyword(p.tok.tokType): getTok(p)
# BUGFIX: We must consume a token here to prevent endless loops! result = ast.emptyNode
# But: this really sucks for idetools and keywords, so we don't do it
# if it is a keyword:
#if not isKeyword(p.tok.tokType): getTok(p)
result = ast.emptyNode
proc colonOrEquals(p: var TParser, a: PNode): PNode = proc colonOrEquals(p: var TParser, a: PNode): PNode =
if p.tok.tokType == tkColon: if p.tok.tokType == tkColon:
@ -390,7 +400,7 @@ proc dotExpr(p: var TParser, a: PNode): PNode =
result = newNodeI(nkDotExpr, info) result = newNodeI(nkDotExpr, info)
optInd(p, result) optInd(p, result)
addSon(result, a) addSon(result, a)
addSon(result, parseSymbol(p)) addSon(result, parseSymbol(p, smAfterDot))
proc qualifiedIdent(p: var TParser): PNode = proc qualifiedIdent(p: var TParser): PNode =
#| qualifiedIdent = symbol ('.' optInd symbol)? #| qualifiedIdent = symbol ('.' optInd symbol)?
@ -846,6 +856,7 @@ type
TDeclaredIdentFlag = enum TDeclaredIdentFlag = enum
withPragma, # identifier may have pragma withPragma, # identifier may have pragma
withBothOptional # both ':' and '=' parts are optional withBothOptional # both ':' and '=' parts are optional
withDot # allow 'var ident.ident = value'
TDeclaredIdentFlags = set[TDeclaredIdentFlag] TDeclaredIdentFlags = set[TDeclaredIdentFlag]
proc parseIdentColonEquals(p: var TParser, flags: TDeclaredIdentFlags): PNode = proc parseIdentColonEquals(p: var TParser, flags: TDeclaredIdentFlags): PNode =
@ -859,7 +870,7 @@ proc parseIdentColonEquals(p: var TParser, flags: TDeclaredIdentFlags): PNode =
while true: while true:
case p.tok.tokType case p.tok.tokType
of tkSymbol, tkAccent: of tkSymbol, tkAccent:
if withPragma in flags: a = identWithPragma(p) if withPragma in flags: a = identWithPragma(p, allowDot=withdot in flags)
else: a = parseSymbol(p) else: a = parseSymbol(p)
if a.kind == nkEmpty: return if a.kind == nkEmpty: return
else: break else: break
@ -1004,10 +1015,10 @@ proc isExprStart(p: TParser): bool =
result = true result = true
else: result = false else: result = false
proc parseSymbolList(p: var TParser, result: PNode, allowNil = false) = proc parseSymbolList(p: var TParser, result: PNode) =
# progress guaranteed # progress guaranteed
while true: while true:
var s = parseSymbol(p, allowNil) var s = parseSymbol(p, smAllowNil)
if s.kind == nkEmpty: break if s.kind == nkEmpty: break
addSon(result, s) addSon(result, s)
if p.tok.tokType != tkComma: break if p.tok.tokType != tkComma: break
@ -1028,7 +1039,7 @@ proc parseTypeDescKAux(p: var TParser, kind: TNodeKind,
getTok(p) getTok(p)
let list = newNodeP(nodeKind, p) let list = newNodeP(nodeKind, p)
result.addSon list result.addSon list
parseSymbolList(p, list, allowNil = true) parseSymbolList(p, list)
proc parseExpr(p: var TParser): PNode = proc parseExpr(p: var TParser): PNode =
#| expr = (ifExpr #| expr = (ifExpr
@ -1520,6 +1531,13 @@ proc parseGenericParam(p: var TParser): PNode =
# progress guaranteed # progress guaranteed
while true: while true:
case p.tok.tokType case p.tok.tokType
of tkIn, tkOut:
let x = p.lex.cache.getIdent(if p.tok.tokType == tkIn: "in" else: "out")
a = newNodeP(nkPrefix, p)
a.addSon newIdentNodeP(x, p)
getTok(p)
expectIdent(p)
a.addSon(parseSymbol(p))
of tkSymbol, tkAccent: of tkSymbol, tkAccent:
a = parseSymbol(p) a = parseSymbol(p)
if a.kind == nkEmpty: return if a.kind == nkEmpty: return
@ -1548,7 +1566,7 @@ proc parseGenericParamList(p: var TParser): PNode =
getTok(p) getTok(p)
optInd(p, result) optInd(p, result)
# progress guaranteed # progress guaranteed
while p.tok.tokType in {tkSymbol, tkAccent}: while p.tok.tokType in {tkSymbol, tkAccent, tkIn, tkOut}:
var a = parseGenericParam(p) var a = parseGenericParam(p)
addSon(result, a) addSon(result, a)
if p.tok.tokType notin {tkComma, tkSemiColon}: break if p.tok.tokType notin {tkComma, tkSemiColon}: break
@ -1882,7 +1900,7 @@ proc parseVarTuple(p: var TParser): PNode =
optInd(p, result) optInd(p, result)
# progress guaranteed # progress guaranteed
while p.tok.tokType in {tkSymbol, tkAccent}: while p.tok.tokType in {tkSymbol, tkAccent}:
var a = identWithPragma(p) var a = identWithPragma(p, allowDot=true)
addSon(result, a) addSon(result, a)
if p.tok.tokType != tkComma: break if p.tok.tokType != tkComma: break
getTok(p) getTok(p)
@ -1898,7 +1916,7 @@ proc parseVariable(p: var TParser): PNode =
#| colonBody = colcom stmt doBlocks? #| colonBody = colcom stmt doBlocks?
#| variable = (varTuple / identColonEquals) colonBody? indAndComment #| variable = (varTuple / identColonEquals) colonBody? indAndComment
if p.tok.tokType == tkParLe: result = parseVarTuple(p) if p.tok.tokType == tkParLe: result = parseVarTuple(p)
else: result = parseIdentColonEquals(p, {withPragma}) else: result = parseIdentColonEquals(p, {withPragma, withDot})
result{-1} = postExprBlocks(p, result{-1}) result{-1} = postExprBlocks(p, result{-1})
indAndComment(p, result) indAndComment(p, result)

View file

@ -1336,6 +1336,11 @@ proc parseGenericParam(p: var TParser): PNode =
result = newNodeP(nkIdentDefs, p) result = newNodeP(nkIdentDefs, p)
while true: while true:
case p.tok.tokType case p.tok.tokType
of tkIn, tkOut:
let t = p.tok.tokType
getTok(p)
expectIdent(p)
a = parseSymbol(p)
of tkSymbol, tkAccent: of tkSymbol, tkAccent:
a = parseSymbol(p) a = parseSymbol(p)
if a.kind == nkEmpty: return if a.kind == nkEmpty: return

View file

@ -125,8 +125,9 @@ proc processImportCpp(s: PSym, extname: string, info: TLineInfo) =
incl(s.flags, sfImportc) incl(s.flags, sfImportc)
incl(s.flags, sfInfixCall) incl(s.flags, sfInfixCall)
excl(s.flags, sfForward) excl(s.flags, sfForward)
let m = s.getModule() if gCmd == cmdCompileToC:
incl(m.flags, sfCompileToCpp) let m = s.getModule()
incl(m.flags, sfCompileToCpp)
extccomp.gMixedMode = true extccomp.gMixedMode = true
proc processImportObjC(s: PSym, extname: string, info: TLineInfo) = proc processImportObjC(s: PSym, extname: string, info: TLineInfo) =

View file

@ -12,17 +12,17 @@ import strutils
proc c_sprintf(buf, frmt: cstring) {.importc: "sprintf", header: "<stdio.h>", nodecl, varargs.} proc c_sprintf(buf, frmt: cstring) {.importc: "sprintf", header: "<stdio.h>", nodecl, varargs.}
proc toStrMaxPrecision*(f: BiggestFloat): string = proc toStrMaxPrecision*(f: BiggestFloat, literalPostfix = ""): string =
if f != f: if f != f:
result = "NAN" result = "NAN"
elif f == 0.0: elif f == 0.0:
result = "0.0" result = "0.0" & literalPostfix
elif f == 0.5 * f: elif f == 0.5 * f:
if f > 0.0: result = "INF" if f > 0.0: result = "INF"
else: result = "-INF" else: result = "-INF"
else: else:
var buf: array[0..80, char] var buf: array[0..80, char]
c_sprintf(buf, "%#.16e", f) c_sprintf(buf, "%#.16e" & literalPostfix, f)
result = $buf result = $buf
proc encodeStr*(s: string, result: var string) = proc encodeStr*(s: string, result: var string) =

View file

@ -442,6 +442,7 @@ proc semConstBoolExpr(c: PContext, n: PNode): PNode =
result = nn result = nn
proc semGenericStmt(c: PContext, n: PNode): PNode proc semGenericStmt(c: PContext, n: PNode): PNode
proc semConceptBody(c: PContext, n: PNode): PNode
include semtypes, semtempl, semgnrc, semstmts, semexprs include semtypes, semtempl, semgnrc, semstmts, semexprs

View file

@ -408,7 +408,13 @@ proc semOverloadedCall(c: PContext, n, nOrig: PNode,
else: else:
# get rid of the deref again for a better error message: # get rid of the deref again for a better error message:
n.sons[1] = n.sons[1].sons[0] n.sons[1] = n.sons[1].sons[0]
notFoundError(c, n, errors) #notFoundError(c, n, errors)
if efExplain notin flags:
# repeat the overload resolution,
# this time enabling all the diagnostic output (this should fail again)
discard semOverloadedCall(c, n, nOrig, filter, flags + {efExplain})
else:
notFoundError(c, n, errors)
else: else:
if efExplain notin flags: if efExplain notin flags:
# repeat the overload resolution, # repeat the overload resolution,
@ -429,7 +435,7 @@ proc explicitGenericSym(c: PContext, n: PNode, s: PSym): PNode =
for i in 1..sonsLen(n)-1: for i in 1..sonsLen(n)-1:
let formal = s.ast.sons[genericParamsPos].sons[i-1].typ let formal = s.ast.sons[genericParamsPos].sons[i-1].typ
let arg = n[i].typ let arg = n[i].typ
let tm = typeRel(m, formal, arg, true) let tm = typeRel(m, formal, arg)
if tm in {isNone, isConvertible}: return nil if tm in {isNone, isConvertible}: return nil
var newInst = generateInstance(c, s, m.bindings, n.info) var newInst = generateInstance(c, s, m.bindings, n.info)
newInst.typ.flags.excl tfUnresolved newInst.typ.flags.excl tfUnresolved
@ -440,7 +446,8 @@ proc explicitGenericSym(c: PContext, n: PNode, s: PSym): PNode =
proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode = proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode =
assert n.kind == nkBracketExpr assert n.kind == nkBracketExpr
for i in 1..sonsLen(n)-1: for i in 1..sonsLen(n)-1:
n.sons[i].typ = semTypeNode(c, n.sons[i], nil) let e = semExpr(c, n.sons[i])
n.sons[i].typ = e.typ.skipTypes({tyTypeDesc})
var s = s var s = s
var a = n.sons[0] var a = n.sons[0]
if a.kind == nkSym: if a.kind == nkSym:

View file

@ -40,6 +40,11 @@ type
bracketExpr*: PNode # current bracket expression (for ^ support) bracketExpr*: PNode # current bracket expression (for ^ support)
mapping*: TIdTable mapping*: TIdTable
TMatchedConcept* = object
candidateType*: PType
prev*: ptr TMatchedConcept
depth*: int
TInstantiationPair* = object TInstantiationPair* = object
genericSym*: PSym genericSym*: PSym
inst*: PInstantiation inst*: PInstantiation
@ -75,6 +80,7 @@ type
importTable*: PScope # scope for all imported symbols importTable*: PScope # scope for all imported symbols
topLevelScope*: PScope # scope for all top-level symbols topLevelScope*: PScope # scope for all top-level symbols
p*: PProcCon # procedure context p*: PProcCon # procedure context
matchedConcept*: ptr TMatchedConcept # the current concept being matched
friendModules*: seq[PSym] # friend modules; may access private data; friendModules*: seq[PSym] # friend modules; may access private data;
# this is used so that generic instantiations # this is used so that generic instantiations
# can access private object fields # can access private object fields
@ -82,7 +88,6 @@ type
ambiguousSymbols*: IntSet # ids of all ambiguous symbols (cannot ambiguousSymbols*: IntSet # ids of all ambiguous symbols (cannot
# store this info in the syms themselves!) # store this info in the syms themselves!)
inTypeClass*: int # > 0 if we are in a user-defined type class
inGenericContext*: int # > 0 if we are in a generic type inGenericContext*: int # > 0 if we are in a generic type
inUnrolledContext*: int # > 0 if we are unrolling a loop inUnrolledContext*: int # > 0 if we are unrolling a loop
compilesContextId*: int # > 0 if we are in a ``compiles`` magic compilesContextId*: int # > 0 if we are in a ``compiles`` magic
@ -277,6 +282,10 @@ proc makeTypeFromExpr*(c: PContext, n: PNode): PType =
assert n != nil assert n != nil
result.n = n result.n = n
proc newTypeWithSons*(owner: PSym, kind: TTypeKind, sons: seq[PType]): PType =
result = newType(kind, owner)
result.sons = sons
proc newTypeWithSons*(c: PContext, kind: TTypeKind, proc newTypeWithSons*(c: PContext, kind: TTypeKind,
sons: seq[PType]): PType = sons: seq[PType]): PType =
result = newType(kind, getCurrOwner(c)) result = newType(kind, getCurrOwner(c))

View file

@ -106,7 +106,10 @@ proc checkConvertible(c: PContext, castDest, src: PType): TConvStatus =
result = convNotNeedeed result = convNotNeedeed
return return
var d = skipTypes(castDest, abstractVar) var d = skipTypes(castDest, abstractVar)
var s = skipTypes(src, abstractVar-{tyTypeDesc}) var s = src
if s.kind in tyUserTypeClasses and s.isResolvedUserTypeClass:
s = s.lastSon
s = skipTypes(s, abstractVar-{tyTypeDesc})
var pointers = 0 var pointers = 0
while (d != nil) and (d.kind in {tyPtr, tyRef}) and (d.kind == s.kind): while (d != nil) and (d.kind in {tyPtr, tyRef}) and (d.kind == s.kind):
d = d.lastSon d = d.lastSon
@ -152,6 +155,8 @@ proc isCastable(dst, src: PType): bool =
result = false result = false
elif typeAllowed(dst, skParam) != nil: elif typeAllowed(dst, skParam) != nil:
result = false result = false
elif dst.kind == tyProc and dst.callConv == ccClosure:
result = src.kind == tyProc and src.callConv == ccClosure
else: else:
result = (dstSize >= srcSize) or result = (dstSize >= srcSize) or
(skipTypes(dst, abstractInst).kind in IntegralTypes) or (skipTypes(dst, abstractInst).kind in IntegralTypes) or
@ -227,7 +232,10 @@ proc semCast(c: PContext, n: PNode): PNode =
if tfHasMeta in targetType.flags: if tfHasMeta in targetType.flags:
localError(n.sons[0].info, errCastToANonConcreteType, $targetType) localError(n.sons[0].info, errCastToANonConcreteType, $targetType)
if not isCastable(targetType, castedExpr.typ): if not isCastable(targetType, castedExpr.typ):
localError(n.info, errExprCannotBeCastToX, $targetType) let tar = $targetType
let alt = typeToString(targetType, preferDesc)
let msg = if tar != alt: tar & "=" & alt else: tar
localError(n.info, errExprCannotBeCastToX, msg)
result = newNodeI(nkCast, n.info) result = newNodeI(nkCast, n.info)
result.typ = targetType result.typ = targetType
addSon(result, copyTree(n.sons[0])) addSon(result, copyTree(n.sons[0]))
@ -683,27 +691,9 @@ proc bracketedMacro(n: PNode): PSym =
if result.kind notin {skMacro, skTemplate}: if result.kind notin {skMacro, skTemplate}:
result = nil result = nil
proc semBracketedMacro(c: PContext; outer, inner: PNode; s: PSym; proc setGenericParams(c: PContext, n: PNode) =
flags: TExprFlags): PNode = for i in 1 .. <n.len:
# We received untransformed bracket expression coming from macroOrTmpl[]. n[i].typ = semTypeNode(c, n[i], nil)
# Transform it to macro or template call, where first come normal
# arguments, next come generic template arguments.
var sons = newSeq[PNode]()
sons.add inner.sons[0]
# Normal arguments:
for i in 1..<outer.len:
sons.add outer.sons[i]
# Generic template arguments from bracket expression:
for i in 1..<inner.len:
sons.add inner.sons[i]
shallowCopy(outer.sons, sons)
# FIXME: Shouldn't we check sfImmediate and call semDirectOp?
# However passing to semDirectOp doesn't work here.
case s.kind
of skMacro: result = semMacroExpr(c, outer, outer, s, flags)
of skTemplate: result = semTemplateExpr(c, outer, s, flags)
else: assert(false)
return
proc afterCallActions(c: PContext; n, orig: PNode, flags: TExprFlags): PNode = proc afterCallActions(c: PContext; n, orig: PNode, flags: TExprFlags): PNode =
result = n result = n
@ -718,7 +708,7 @@ proc afterCallActions(c: PContext; n, orig: PNode, flags: TExprFlags): PNode =
analyseIfAddressTakenInCall(c, result) analyseIfAddressTakenInCall(c, result)
if callee.magic != mNone: if callee.magic != mNone:
result = magicsAfterOverloadResolution(c, result, flags) result = magicsAfterOverloadResolution(c, result, flags)
if c.inTypeClass == 0: if c.matchedConcept == nil:
result = evalAtCompileTime(c, result) result = evalAtCompileTime(c, result)
proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode = proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
@ -745,7 +735,8 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
elif n.sons[0].kind == nkBracketExpr: elif n.sons[0].kind == nkBracketExpr:
let s = bracketedMacro(n.sons[0]) let s = bracketedMacro(n.sons[0])
if s != nil: if s != nil:
return semBracketedMacro(c, n, n.sons[0], s, flags) setGenericParams(c, n[0])
return semDirectOp(c, n, flags)
let nOrig = n.copyTree let nOrig = n.copyTree
semOpAux(c, n) semOpAux(c, n)
@ -897,20 +888,6 @@ proc lookupInRecordAndBuildCheck(c: PContext, n, r: PNode, field: PIdent,
if r.sym.name.id == field.id: result = r.sym if r.sym.name.id == field.id: result = r.sym
else: illFormedAst(n) else: illFormedAst(n)
proc makeDeref(n: PNode): PNode =
var t = skipTypes(n.typ, {tyGenericInst, tyAlias})
result = n
if t.kind == tyVar:
result = newNodeIT(nkHiddenDeref, n.info, t.sons[0])
addSon(result, n)
t = skipTypes(t.sons[0], {tyGenericInst, tyAlias})
while t.kind in {tyPtr, tyRef}:
var a = result
let baseTyp = t.lastSon
result = newNodeIT(nkHiddenDeref, n.info, baseTyp)
addSon(result, a)
t = skipTypes(baseTyp, {tyGenericInst, tyAlias})
const const
tyTypeParamsHolders = {tyGenericInst, tyCompositeTypeClass} tyTypeParamsHolders = {tyGenericInst, tyCompositeTypeClass}
tyDotOpTransparent = {tyVar, tyPtr, tyRef, tyAlias} tyDotOpTransparent = {tyVar, tyPtr, tyRef, tyAlias}
@ -937,7 +914,7 @@ proc readTypeParameter(c: PContext, typ: PType,
else: else:
discard discard
if typ.kind != tyUserTypeClass: if typ.kind != tyUserTypeClass:
let ty = if typ.kind == tyCompositeTypeClass: typ.sons[1].skipGenericAlias let ty = if typ.kind == tyCompositeTypeClass: typ.sons[1].skipGenericAlias
else: typ.skipGenericAlias else: typ.skipGenericAlias
@ -997,7 +974,7 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode =
of skParam: of skParam:
markUsed(n.info, s, c.graph.usageSym) markUsed(n.info, s, c.graph.usageSym)
styleCheckUse(n.info, s) styleCheckUse(n.info, s)
if s.typ.kind == tyStatic and s.typ.n != nil: if s.typ != nil and s.typ.kind == tyStatic and s.typ.n != nil:
# XXX see the hack in sigmatch.nim ... # XXX see the hack in sigmatch.nim ...
return s.typ.n return s.typ.n
elif sfGenSym in s.flags: elif sfGenSym in s.flags:
@ -1155,9 +1132,9 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
# reset to prevent 'nil' bug: see "tests/reject/tenumitems.nim": # reset to prevent 'nil' bug: see "tests/reject/tenumitems.nim":
ty = n.sons[0].typ ty = n.sons[0].typ
return nil return nil
ty = skipTypes(ty, {tyGenericInst, tyVar, tyPtr, tyRef, tyAlias})
if ty.kind in tyUserTypeClasses and ty.isResolvedUserTypeClass: if ty.kind in tyUserTypeClasses and ty.isResolvedUserTypeClass:
ty = ty.lastSon ty = ty.lastSon
ty = skipTypes(ty, {tyGenericInst, tyVar, tyPtr, tyRef, tyAlias})
while tfBorrowDot in ty.flags: ty = ty.skipTypes({tyDistinct}) while tfBorrowDot in ty.flags: ty = ty.skipTypes({tyDistinct})
var check: PNode = nil var check: PNode = nil
if ty.kind == tyObject: if ty.kind == tyObject:
@ -2164,10 +2141,6 @@ proc shouldBeBracketExpr(n: PNode): bool =
n.sons[0] = be n.sons[0] = be
return true return true
proc setGenericParams(c: PContext, n: PNode) =
for i in 1 .. <n.len:
n[i].typ = semTypeNode(c, n[i], nil)
proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
result = n result = n
if gCmd == cmdIdeTools: suggestExpr(c, n) if gCmd == cmdIdeTools: suggestExpr(c, n)
@ -2177,7 +2150,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
let checks = if efNoEvaluateGeneric in flags: {checkUndeclared} let checks = if efNoEvaluateGeneric in flags: {checkUndeclared}
else: {checkUndeclared, checkModule, checkAmbiguity} else: {checkUndeclared, checkModule, checkAmbiguity}
var s = qualifiedLookUp(c, n, checks) var s = qualifiedLookUp(c, n, checks)
if c.inTypeClass == 0: semCaptureSym(s, c.p.owner) if c.matchedConcept == nil: semCaptureSym(s, c.p.owner)
result = semSym(c, n, s, flags) result = semSym(c, n, s, flags)
if s.kind in {skProc, skMethod, skConverter, skIterator}: if s.kind in {skProc, skMethod, skConverter, skIterator}:
#performProcvarCheck(c, n, s) #performProcvarCheck(c, n, s)
@ -2214,11 +2187,11 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
if result.typ == nil: result.typ = getSysType(tyUInt32) if result.typ == nil: result.typ = getSysType(tyUInt32)
of nkUInt64Lit: of nkUInt64Lit:
if result.typ == nil: result.typ = getSysType(tyUInt64) if result.typ == nil: result.typ = getSysType(tyUInt64)
of nkFloatLit: #of nkFloatLit:
if result.typ == nil: result.typ = getFloatLitType(result) # if result.typ == nil: result.typ = getFloatLitType(result)
of nkFloat32Lit: of nkFloat32Lit:
if result.typ == nil: result.typ = getSysType(tyFloat32) if result.typ == nil: result.typ = getSysType(tyFloat32)
of nkFloat64Lit: of nkFloat64Lit, nkFloatLit:
if result.typ == nil: result.typ = getSysType(tyFloat64) if result.typ == nil: result.typ = getSysType(tyFloat64)
of nkFloat128Lit: of nkFloat128Lit:
if result.typ == nil: result.typ = getSysType(tyFloat128) if result.typ == nil: result.typ = getSysType(tyFloat128)
@ -2306,14 +2279,14 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
pragma = n[1] pragma = n[1]
pragmaName = considerQuotedIdent(pragma[0]) pragmaName = considerQuotedIdent(pragma[0])
flags = flags flags = flags
case whichKeyword(pragmaName) case whichKeyword(pragmaName)
of wExplain: of wExplain:
flags.incl efExplain flags.incl efExplain
else: else:
# what other pragmas are allowed for expressions? `likely`, `unlikely` # what other pragmas are allowed for expressions? `likely`, `unlikely`
invalidPragma(n) invalidPragma(n)
result = semExpr(c, n[0], flags) result = semExpr(c, n[0], flags)
of nkPar: of nkPar:
case checkPar(n) case checkPar(n)

View file

@ -594,8 +594,11 @@ proc foldConStrStr(m: PSym, n: PNode): PNode =
proc newSymNodeTypeDesc*(s: PSym; info: TLineInfo): PNode = proc newSymNodeTypeDesc*(s: PSym; info: TLineInfo): PNode =
result = newSymNode(s, info) result = newSymNode(s, info)
result.typ = newType(tyTypeDesc, s.owner) if s.typ.kind != tyTypeDesc:
result.typ.addSonSkipIntLit(s.typ) result.typ = newType(tyTypeDesc, s.owner)
result.typ.addSonSkipIntLit(s.typ)
else:
result.typ = s.typ
proc getConstExpr(m: PSym, n: PNode): PNode = proc getConstExpr(m: PSym, n: PNode): PNode =
result = nil result = nil
@ -634,9 +637,11 @@ proc getConstExpr(m: PSym, n: PNode): PNode =
result = newSymNodeTypeDesc(s, n.info) result = newSymNodeTypeDesc(s, n.info)
of skGenericParam: of skGenericParam:
if s.typ.kind == tyStatic: if s.typ.kind == tyStatic:
if s.typ.n != nil: if s.typ.n != nil and tfUnresolved notin s.typ.flags:
result = s.typ.n result = s.typ.n
result.typ = s.typ.sons[0] result.typ = s.typ.base
elif s.typ.isIntLit:
result = s.typ.n
else: else:
result = newSymNodeTypeDesc(s, n.info) result = newSymNodeTypeDesc(s, n.info)
else: discard else: discard

View file

@ -34,7 +34,7 @@ type
type type
TSemGenericFlag = enum TSemGenericFlag = enum
withinBind, withinTypeDesc, withinMixin withinBind, withinTypeDesc, withinMixin, withinConcept
TSemGenericFlags = set[TSemGenericFlag] TSemGenericFlags = set[TSemGenericFlag]
proc semGenericStmt(c: PContext, n: PNode, proc semGenericStmt(c: PContext, n: PNode,
@ -200,12 +200,13 @@ proc semGenericStmt(c: PContext, n: PNode,
checkMinSonsLen(n, 1) checkMinSonsLen(n, 1)
let fn = n.sons[0] let fn = n.sons[0]
var s = qualifiedLookUp(c, fn, {}) var s = qualifiedLookUp(c, fn, {})
if s == nil and withinMixin notin flags and if s == nil and
{withinMixin, withinConcept}*flags == {} and
fn.kind in {nkIdent, nkAccQuoted} and fn.kind in {nkIdent, nkAccQuoted} and
considerQuotedIdent(fn).id notin ctx.toMixin: considerQuotedIdent(fn).id notin ctx.toMixin:
errorUndeclaredIdentifier(c, n.info, fn.renderTree) errorUndeclaredIdentifier(c, n.info, fn.renderTree)
var first = 0 var first = ord(withinConcept in flags)
var mixinContext = false var mixinContext = false
if s != nil: if s != nil:
incl(s.flags, sfUsed) incl(s.flags, sfUsed)
@ -471,3 +472,9 @@ proc semGenericStmt(c: PContext, n: PNode): PNode =
ctx.toMixin = initIntset() ctx.toMixin = initIntset()
result = semGenericStmt(c, n, {}, ctx) result = semGenericStmt(c, n, {}, ctx)
semIdeForTemplateOrGeneric(c, result, ctx.cursorInBody) semIdeForTemplateOrGeneric(c, result, ctx.cursorInBody)
proc semConceptBody(c: PContext, n: PNode): PNode =
var ctx: GenericCtx
ctx.toMixin = initIntset()
result = semGenericStmt(c, n, {withinConcept}, ctx)
semIdeForTemplateOrGeneric(c, result, ctx.cursorInBody)

View file

@ -88,7 +88,8 @@ proc sameInstantiation(a, b: TInstantiation): bool =
if a.concreteTypes.len == b.concreteTypes.len: if a.concreteTypes.len == b.concreteTypes.len:
for i in 0..a.concreteTypes.high: for i in 0..a.concreteTypes.high:
if not compareTypes(a.concreteTypes[i], b.concreteTypes[i], if not compareTypes(a.concreteTypes[i], b.concreteTypes[i],
flags = {ExactTypeDescValues}): return flags = {ExactTypeDescValues,
ExactGcSafety}): return
result = true result = true
proc genericCacheGet(genericSym: PSym, entry: TInstantiation; proc genericCacheGet(genericSym: PSym, entry: TInstantiation;
@ -173,10 +174,14 @@ proc sideEffectsCheck(c: PContext, s: PSym) =
proc instGenericContainer(c: PContext, info: TLineInfo, header: PType, proc instGenericContainer(c: PContext, info: TLineInfo, header: PType,
allowMetaTypes = false): PType = allowMetaTypes = false): PType =
var cl: TReplTypeVars var
typeMap: LayeredIdTable
cl: TReplTypeVars
initIdTable(cl.symMap) initIdTable(cl.symMap)
initIdTable(cl.typeMap)
initIdTable(cl.localCache) initIdTable(cl.localCache)
initIdTable(typeMap.topLayer)
cl.typeMap = addr(typeMap)
cl.info = info cl.info = info
cl.c = c cl.c = c
cl.allowMetaTypes = allowMetaTypes cl.allowMetaTypes = allowMetaTypes
@ -200,7 +205,8 @@ proc instantiateProcType(c: PContext, pt: TIdTable,
#addDecl(c, prc) #addDecl(c, prc)
pushInfoContext(info) pushInfoContext(info)
var cl = initTypeVars(c, pt, info, nil) var typeMap = initLayeredTypeMap(pt)
var cl = initTypeVars(c, addr(typeMap), info, nil)
var result = instCopyType(cl, prc.typ) var result = instCopyType(cl, prc.typ)
let originalParams = result.n let originalParams = result.n
result.n = originalParams.shallowCopy result.n = originalParams.shallowCopy
@ -256,8 +262,8 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
# NOTE: for access of private fields within generics from a different module # NOTE: for access of private fields within generics from a different module
# we set the friend module: # we set the friend module:
c.friendModules.add(getModule(fn)) c.friendModules.add(getModule(fn))
let oldInTypeClass = c.inTypeClass let oldMatchedConcept = c.matchedConcept
c.inTypeClass = 0 c.matchedConcept = nil
let oldScope = c.currentScope let oldScope = c.currentScope
while not isTopLevel(c): c.currentScope = c.currentScope.parent while not isTopLevel(c): c.currentScope = c.currentScope.parent
result = copySym(fn, false) result = copySym(fn, false)
@ -318,5 +324,5 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
c.currentScope = oldScope c.currentScope = oldScope
discard c.friendModules.pop() discard c.friendModules.pop()
dec(c.instCounter) dec(c.instCounter)
c.inTypeClass = oldInTypeClass c.matchedConcept = oldMatchedConcept
if result.kind == skMethod: finishMethod(c, result) if result.kind == skMethod: finishMethod(c, result)

View file

@ -89,9 +89,9 @@ proc semInstantiationInfo(c: PContext, n: PNode): PNode =
proc toNode(t: PType, i: TLineInfo): PNode = proc toNode(t: PType, i: TLineInfo): PNode =
result = newNodeIT(nkType, i, t) result = newNodeIT(nkType, i, t)
const const
# these are types that use the bracket syntax for instantiation # these are types that use the bracket syntax for instantiation
# they can be subjected to the type traits `genericHead` and # they can be subjected to the type traits `genericHead` and
# `Uninstantiated` # `Uninstantiated`
tyUserDefinedGenerics* = {tyGenericInst, tyGenericInvocation, tyUserDefinedGenerics* = {tyGenericInst, tyGenericInvocation,
tyUserTypeClassInst} tyUserTypeClassInst}
@ -109,27 +109,43 @@ proc uninstantiate(t: PType): PType =
of tyCompositeTypeClass: uninstantiate t.sons[1] of tyCompositeTypeClass: uninstantiate t.sons[1]
else: t else: t
proc evalTypeTrait(trait: PNode, operand: PType, context: PSym): PNode = proc evalTypeTrait(traitCall: PNode, operand: PType, context: PSym): PNode =
var typ = operand.skipTypes({tyTypeDesc}) const skippedTypes = {tyTypeDesc, tyAlias}
let trait = traitCall[0]
internalAssert trait.kind == nkSym
var operand = operand.skipTypes(skippedTypes)
template operand2: PType =
traitCall.sons[2].typ.skipTypes({tyTypeDesc})
template typeWithSonsResult(kind, sons): PNode =
newTypeWithSons(context, kind, sons).toNode(traitCall.info)
case trait.sym.name.s case trait.sym.name.s
of "or", "|":
return typeWithSonsResult(tyOr, @[operand, operand2])
of "and":
return typeWithSonsResult(tyAnd, @[operand, operand2])
of "not":
return typeWithSonsResult(tyNot, @[operand])
of "name": of "name":
result = newStrNode(nkStrLit, typ.typeToString(preferName)) result = newStrNode(nkStrLit, operand.typeToString(preferName))
result.typ = newType(tyString, context) result.typ = newType(tyString, context)
result.info = trait.info result.info = traitCall.info
of "arity": of "arity":
result = newIntNode(nkIntLit, typ.len - ord(typ.kind==tyProc)) result = newIntNode(nkIntLit, operand.len - ord(operand.kind==tyProc))
result.typ = newType(tyInt, context) result.typ = newType(tyInt, context)
result.info = trait.info result.info = traitCall.info
of "genericHead": of "genericHead":
var res = uninstantiate(typ) var res = uninstantiate(operand)
if res == typ and res.kind notin tyMagicGenerics: if res == operand and res.kind notin tyMagicGenerics:
localError(trait.info, localError(traitCall.info,
"genericHead expects a generic type. The given type was " & "genericHead expects a generic type. The given type was " &
typeToString(typ)) typeToString(operand))
return newType(tyError, context).toNode(trait.info) return newType(tyError, context).toNode(traitCall.info)
result = res.base.toNode(trait.info) result = res.base.toNode(traitCall.info)
of "stripGenericParams": of "stripGenericParams":
result = uninstantiate(typ).toNode(trait.info) result = uninstantiate(operand).toNode(traitCall.info)
else: else:
internalAssert false internalAssert false
@ -140,7 +156,7 @@ proc semTypeTraits(c: PContext, n: PNode): PNode =
if t.sonsLen > 0: if t.sonsLen > 0:
# This is either a type known to sem or a typedesc # This is either a type known to sem or a typedesc
# param to a regular proc (again, known at instantiation) # param to a regular proc (again, known at instantiation)
result = evalTypeTrait(n[0], t, getCurrOwner(c)) result = evalTypeTrait(n, t, getCurrOwner(c))
else: else:
# a typedesc variable, pass unmodified to evals # a typedesc variable, pass unmodified to evals
result = n result = n

View file

@ -69,7 +69,8 @@ proc `==`(a, b: TLockLevel): bool {.borrow.}
proc max(a, b: TLockLevel): TLockLevel {.borrow.} proc max(a, b: TLockLevel): TLockLevel {.borrow.}
proc isLocalVar(a: PEffects, s: PSym): bool = proc isLocalVar(a: PEffects, s: PSym): bool =
s.kind in {skVar, skResult} and sfGlobal notin s.flags and s.owner == a.owner s.kind in {skVar, skResult} and sfGlobal notin s.flags and
s.owner == a.owner and s.typ != nil
proc getLockLevel(t: PType): TLockLevel = proc getLockLevel(t: PType): TLockLevel =
var t = t var t = t
@ -218,12 +219,11 @@ proc listGcUnsafety(s: PSym; onlyWarning: bool; cycleCheck: var IntSet) =
message(s.info, msgKind, message(s.info, msgKind,
"'$#' is not GC-safe as it calls '$#'" % "'$#' is not GC-safe as it calls '$#'" %
[s.name.s, u.name.s]) [s.name.s, u.name.s])
of skParam: of skParam, skForVar:
message(s.info, msgKind, message(s.info, msgKind,
"'$#' is not GC-safe as it performs an indirect call via '$#'" % "'$#' is not GC-safe as it performs an indirect call via '$#'" %
[s.name.s, u.name.s]) [s.name.s, u.name.s])
else: else:
internalAssert u.kind == skUnknown
message(u.info, msgKind, message(u.info, msgKind,
"'$#' is not GC-safe as it performs an indirect call here" % s.name.s) "'$#' is not GC-safe as it performs an indirect call here" % s.name.s)

View file

@ -144,7 +144,7 @@ proc fixNilType(n: PNode) =
n.typ = nil n.typ = nil
proc discardCheck(c: PContext, result: PNode) = proc discardCheck(c: PContext, result: PNode) =
if c.inTypeClass > 0: return if c.matchedConcept != nil: return
if result.typ != nil and result.typ.kind notin {tyStmt, tyVoid}: if result.typ != nil and result.typ.kind notin {tyStmt, tyVoid}:
if result.kind == nkNilLit: if result.kind == nkNilLit:
result.typ = nil result.typ = nil
@ -465,6 +465,47 @@ proc hasEmpty(typ: PType): bool =
for s in typ.sons: for s in typ.sons:
result = result or hasEmpty(s) result = result or hasEmpty(s)
proc makeDeref(n: PNode): PNode =
var t = skipTypes(n.typ, {tyGenericInst, tyAlias})
if t.kind in tyUserTypeClasses and t.isResolvedUserTypeClass:
t = t.lastSon
result = n
if t.kind == tyVar:
result = newNodeIT(nkHiddenDeref, n.info, t.sons[0])
addSon(result, n)
t = skipTypes(t.sons[0], {tyGenericInst, tyAlias})
while t.kind in {tyPtr, tyRef}:
var a = result
let baseTyp = t.lastSon
result = newNodeIT(nkHiddenDeref, n.info, baseTyp)
addSon(result, a)
t = skipTypes(baseTyp, {tyGenericInst, tyAlias})
proc fillPartialObject(c: PContext; n: PNode; typ: PType) =
if n.len == 2:
let x = semExprWithType(c, n[0])
let y = considerQuotedIdent(n[1])
let obj = x.typ.skipTypes(abstractPtrs)
if obj.kind == tyObject and tfPartial in obj.flags:
let field = newSym(skField, getIdent(y.s), obj.sym, n[1].info)
field.typ = skipIntLit(typ)
field.position = sonsLen(obj.n)
addSon(obj.n, newSymNode(field))
n.sons[0] = makeDeref x
n.sons[1] = newSymNode(field)
else:
localError(n.info, "implicit object field construction " &
"requires a .partial object, but got " & typeToString(obj))
else:
localError(n.info, "nkDotNode requires 2 children")
proc setVarType(v: PSym, typ: PType) =
if v.typ != nil and not sameTypeOrNil(v.typ, typ):
localError(v.info, "inconsistent typing for reintroduced symbol '" &
v.name.s & "': previous type was: " & typeToString(v.typ) &
"; new type is: " & typeToString(typ))
v.typ = typ
proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
var b: PNode var b: PNode
result = copyNode(n) result = copyNode(n)
@ -529,6 +570,11 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
message(a.info, warnEachIdentIsTuple) message(a.info, warnEachIdentIsTuple)
for j in countup(0, length-3): for j in countup(0, length-3):
if a[j].kind == nkDotExpr:
fillPartialObject(c, a[j],
if a.kind != nkVarTuple: typ else: tup.sons[j])
addToVarSection(c, result, n, a)
continue
var v = semIdentDef(c, a.sons[j], symkind) var v = semIdentDef(c, a.sons[j], symkind)
if sfGenSym notin v.flags and not isDiscardUnderscore(v): if sfGenSym notin v.flags and not isDiscardUnderscore(v):
addInterfaceDecl(c, v) addInterfaceDecl(c, v)
@ -549,7 +595,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
# this is needed for the evaluation pass and for the guard checking: # this is needed for the evaluation pass and for the guard checking:
v.ast = def v.ast = def
if sfThread in v.flags: localError(def.info, errThreadvarCannotInit) if sfThread in v.flags: localError(def.info, errThreadvarCannotInit)
v.typ = typ setVarType(v, typ)
b = newNodeI(nkIdentDefs, a.info) b = newNodeI(nkIdentDefs, a.info)
if importantComments(): if importantComments():
# keep documentation information: # keep documentation information:
@ -560,7 +606,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
addToVarSection(c, result, n, b) addToVarSection(c, result, n, b)
else: else:
if def.kind == nkPar: v.ast = def[j] if def.kind == nkPar: v.ast = def[j]
v.typ = tup.sons[j] setVarType(v, tup.sons[j])
b.sons[j] = newSymNode(v) b.sons[j] = newSymNode(v)
addDefer(c, result, v) addDefer(c, result, v)
checkNilable(v) checkNilable(v)
@ -594,7 +640,7 @@ proc semConst(c: PContext, n: PNode): PNode =
if typeAllowed(typ, skConst) != nil and def.kind != nkNilLit: if typeAllowed(typ, skConst) != nil and def.kind != nkNilLit:
localError(a.info, "invalid type for const: " & typeToString(typ)) localError(a.info, "invalid type for const: " & typeToString(typ))
continue continue
v.typ = typ setVarType(v, typ)
v.ast = def # no need to copy v.ast = def # no need to copy
if sfGenSym notin v.flags: addInterfaceDecl(c, v) if sfGenSym notin v.flags: addInterfaceDecl(c, v)
var b = newNodeI(nkConstDef, a.info) var b = newNodeI(nkConstDef, a.info)
@ -730,7 +776,9 @@ proc typeSectionLeftSidePass(c: PContext, n: PNode) =
var s: PSym var s: PSym
if name.kind == nkDotExpr: if name.kind == nkDotExpr:
s = qualifiedLookUp(c, name, {checkUndeclared, checkModule}) s = qualifiedLookUp(c, name, {checkUndeclared, checkModule})
if s.kind != skType or s.typ.skipTypes(abstractPtrs).kind != tyObject or tfPartial notin s.typ.skipTypes(abstractPtrs).flags: if s.kind != skType or
s.typ.skipTypes(abstractPtrs).kind != tyObject or
tfPartial notin s.typ.skipTypes(abstractPtrs).flags:
localError(name.info, "only .partial objects can be extended") localError(name.info, "only .partial objects can be extended")
else: else:
s = semIdentDef(c, name, skType) s = semIdentDef(c, name, skType)
@ -742,6 +790,87 @@ proc typeSectionLeftSidePass(c: PContext, n: PNode) =
if sfGenSym notin s.flags: addInterfaceDecl(c, s) if sfGenSym notin s.flags: addInterfaceDecl(c, s)
a.sons[0] = newSymNode(s) a.sons[0] = newSymNode(s)
proc checkCovariantParamsUsages(genericType: PType) =
var body = genericType{-1}
proc traverseSubTypes(t: PType): bool =
template error(msg) = localError(genericType.sym.info, msg)
result = false
template subresult(r) =
let sub = r
result = result or sub
case t.kind
of tyGenericParam:
t.flags.incl tfWeakCovariant
return true
of tyObject:
for field in t.n:
subresult traverseSubTypes(field.typ)
of tyArray:
return traverseSubTypes(t[1])
of tyProc:
for subType in t.sons:
if subType != nil:
subresult traverseSubTypes(subType)
if result:
error("non-invariant type param used in a proc type: " & $t)
of tySequence:
return traverseSubTypes(t[0])
of tyGenericInvocation:
let targetBody = t[0]
for i in 1 .. <t.len:
let param = t[i]
if param.kind == tyGenericParam:
if tfCovariant in param.flags:
let formalFlags = targetBody[i-1].flags
if tfCovariant notin formalFlags:
error("covariant param '" & param.sym.name.s &
"' used in a non-covariant position")
elif tfWeakCovariant in formalFlags:
param.flags.incl tfWeakCovariant
result = true
elif tfContravariant in param.flags:
let formalParam = targetBody[i-1].sym
if tfContravariant notin formalParam.typ.flags:
error("contravariant param '" & param.sym.name.s &
"' used in a non-contravariant position")
result = true
else:
subresult traverseSubTypes(param)
of tyAnd, tyOr, tyNot, tyStatic, tyBuiltInTypeClass, tyCompositeTypeClass:
error("non-invariant type parameters cannot be used with types such '" & $t & "'")
of tyUserTypeClass, tyUserTypeClassInst:
error("non-invariant type parameters are not supported in concepts")
of tyTuple:
for fieldType in t.sons:
subresult traverseSubTypes(fieldType)
of tyPtr, tyRef, tyVar:
if t.base.kind == tyGenericParam: return true
return traverseSubTypes(t.base)
of tyDistinct, tyAlias:
return traverseSubTypes(t.lastSon)
of tyGenericInst:
internalAssert false
else:
discard
discard traverseSubTypes(body)
proc typeSectionRightSidePass(c: PContext, n: PNode) = proc typeSectionRightSidePass(c: PContext, n: PNode) =
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
var a = n.sons[i] var a = n.sons[i]
@ -782,6 +911,22 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) =
body.sym = s body.sym = s
body.size = -1 # could not be computed properly body.size = -1 # could not be computed properly
s.typ.sons[sonsLen(s.typ) - 1] = body s.typ.sons[sonsLen(s.typ) - 1] = body
if tfCovariant in s.typ.flags:
checkCovariantParamsUsages(s.typ)
# XXX: This is a temporary limitation:
# The codegen currently produces various failures with
# generic imported types that have fields, but we need
# the fields specified in order to detect weak covariance.
# The proper solution is to teach the codegen how to handle
# such types, because this would offer various interesting
# possibilities such as instantiating C++ generic types with
# garbage collected Nim types.
if sfImportc in s.flags:
var body = s.typ.lastSon
if body.kind == tyObject:
# erases all declared fields
body.n.sons = nil
popOwner(c) popOwner(c)
closeScope(c) closeScope(c)
elif a.sons[2].kind != nkEmpty: elif a.sons[2].kind != nkEmpty:
@ -985,12 +1130,15 @@ proc semProcAnnotation(c: PContext, prc: PNode;
var x = newNodeI(nkCall, n.info) var x = newNodeI(nkCall, n.info)
x.add(newSymNode(m)) x.add(newSymNode(m))
prc.sons[pragmasPos] = copyExcept(n, i) prc.sons[pragmasPos] = copyExcept(n, i)
if prc[pragmasPos].kind != nkEmpty and prc[pragmasPos].len == 0:
prc.sons[pragmasPos] = emptyNode
if it.kind == nkExprColonExpr: if it.kind == nkExprColonExpr:
# pass pragma argument to the macro too: # pass pragma argument to the macro too:
x.add(it.sons[1]) x.add(it.sons[1])
x.add(prc) x.add(prc)
# recursion assures that this works for multiple macro annotations too: # recursion assures that this works for multiple macro annotations too:
result = semStmt(c, x) result = semExpr(c, x)
# since a proc annotation can set pragmas, we process these here again. # since a proc annotation can set pragmas, we process these here again.
# This is required for SqueakNim-like export pragmas. # This is required for SqueakNim-like export pragmas.
if result.kind in procDefs and result[namePos].kind == nkSym and if result.kind in procDefs and result[namePos].kind == nkSym and
@ -1322,8 +1470,16 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
implicitPragmas(c, s, n, validPragmas) implicitPragmas(c, s, n, validPragmas)
else: else:
if n.sons[pragmasPos].kind != nkEmpty: if n.sons[pragmasPos].kind != nkEmpty:
localError(n.sons[pragmasPos].info, errPragmaOnlyInHeaderOfProcX, pragma(c, s, n.sons[pragmasPos], validPragmas)
"'" & proto.name.s & "' from " & $proto.info) # To ease macro generation that produce forwarded .async procs we now
# allow a bit redudancy in the pragma declarations. The rule is
# a prototype's pragma list must be a superset of the current pragma
# list.
# XXX This needs more checks eventually, for example that external
# linking names do agree:
if proto.typ.callConv != s.typ.callConv or proto.typ.flags < s.typ.flags:
localError(n.sons[pragmasPos].info, errPragmaOnlyInHeaderOfProcX,
"'" & proto.name.s & "' from " & $proto.info)
if sfForward notin proto.flags: if sfForward notin proto.flags:
wrongRedefinition(n.info, proto.name.s) wrongRedefinition(n.info, proto.name.s)
excl(proto.flags, sfForward) excl(proto.flags, sfForward)
@ -1402,7 +1558,9 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
popOwner(c) popOwner(c)
if n.sons[patternPos].kind != nkEmpty: if n.sons[patternPos].kind != nkEmpty:
c.patterns.add(s) c.patterns.add(s)
if isAnon: result.typ = s.typ if isAnon:
n.kind = nkLambda
result.typ = s.typ
if isTopLevel(c) and s.kind != skIterator and if isTopLevel(c) and s.kind != skIterator and
s.typ.callConv == ccClosure: s.typ.callConv == ccClosure:
localError(s.info, "'.closure' calling convention for top level routines is invalid") localError(s.info, "'.closure' calling convention for top level routines is invalid")
@ -1614,7 +1772,8 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
else: else:
var expr = semExpr(c, n.sons[i], flags) var expr = semExpr(c, n.sons[i], flags)
n.sons[i] = expr n.sons[i] = expr
if c.inTypeClass > 0 and expr.typ != nil: if c.matchedConcept != nil and expr.typ != nil and
(nfFromTemplate notin n.flags or i != last):
case expr.typ.kind case expr.typ.kind
of tyBool: of tyBool:
if expr.kind == nkInfix and if expr.kind == nkInfix and
@ -1629,7 +1788,7 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
let verdict = semConstExpr(c, n[i]) let verdict = semConstExpr(c, n[i])
if verdict.intVal == 0: if verdict.intVal == 0:
localError(result.info, "type class predicate failed") localError(result.info, "concept predicate failed")
of tyUnknown: continue of tyUnknown: continue
else: discard else: discard
if n.sons[i].typ == enforceVoidContext: #or usesResult(n.sons[i]): if n.sons[i].typ == enforceVoidContext: #or usesResult(n.sons[i]):
@ -1653,7 +1812,7 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
if result.len == 1 and if result.len == 1 and
# concept bodies should be preserved as a stmt list: # concept bodies should be preserved as a stmt list:
c.inTypeClass == 0 and c.matchedConcept == nil and
# also, don't make life complicated for macros. # also, don't make life complicated for macros.
# they will always expect a proper stmtlist: # they will always expect a proper stmtlist:
nfBlockArg notin n.flags and nfBlockArg notin n.flags and

View file

@ -42,6 +42,8 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
counter = lastOrd(base) + 1 counter = lastOrd(base) + 1
rawAddSon(result, base) rawAddSon(result, base)
let isPure = result.sym != nil and sfPure in result.sym.flags let isPure = result.sym != nil and sfPure in result.sym.flags
var symbols: TStrTable
if isPure: initStrTable(symbols)
var hasNull = false var hasNull = false
for i in countup(1, sonsLen(n) - 1): for i in countup(1, sonsLen(n) - 1):
case n.sons[i].kind case n.sons[i].kind
@ -87,6 +89,8 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
addSon(result.n, newSymNode(e)) addSon(result.n, newSymNode(e))
styleCheckDef(e) styleCheckDef(e)
if sfGenSym notin e.flags and not isPure: addDecl(c, e) if sfGenSym notin e.flags and not isPure: addDecl(c, e)
if isPure and strTableIncl(symbols, e):
wrongRedefinition(e.info, e.name.s)
inc(counter) inc(counter)
if not hasNull: incl(result.flags, tfNeedsInit) if not hasNull: incl(result.flags, tfNeedsInit)
@ -152,6 +156,8 @@ proc semAnyRef(c: PContext; n: PNode; kind: TTypeKind; prev: PType): PType =
message n[i].info, errGenerated, "region needs to be an object type" message n[i].info, errGenerated, "region needs to be an object type"
addSonSkipIntLit(result, region) addSonSkipIntLit(result, region)
addSonSkipIntLit(result, t) addSonSkipIntLit(result, t)
if tfPartial in result.flags:
if result.lastSon.kind == tyObject: incl(result.lastSon.flags, tfPartial)
#if not isNilable: result.flags.incl tfNotNil #if not isNilable: result.flags.incl tfNotNil
proc semVarType(c: PContext, n: PNode, prev: PType): PType = proc semVarType(c: PContext, n: PNode, prev: PType): PType =
@ -1129,7 +1135,7 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
m.isNoCall = true m.isNoCall = true
matches(c, n, copyTree(n), m) matches(c, n, copyTree(n), m)
if m.state != csMatch and not m.typedescMatched: if m.state != csMatch:
let err = "cannot instantiate " & typeToString(t) & "\n" & let err = "cannot instantiate " & typeToString(t) & "\n" &
"got: (" & describeArgs(c, n) & ")\n" & "got: (" & describeArgs(c, n) & ")\n" &
"but expected: (" & describeArgs(c, t.n, 0) & ")" "but expected: (" & describeArgs(c, t.n, 0) & ")"
@ -1200,17 +1206,51 @@ proc semTypeClass(c: PContext, n: PNode, prev: PType): PType =
# if n.sonsLen == 0: return newConstraint(c, tyTypeClass) # if n.sonsLen == 0: return newConstraint(c, tyTypeClass)
if nfBase2 in n.flags: if nfBase2 in n.flags:
message(n.info, warnDeprecated, "use 'concept' instead; 'generic'") message(n.info, warnDeprecated, "use 'concept' instead; 'generic'")
result = newOrPrevType(tyUserTypeClass, prev, c)
result.n = n
let let
pragmas = n[1] pragmas = n[1]
inherited = n[2] inherited = n[2]
result = newOrPrevType(tyUserTypeClass, prev, c)
var owner = getCurrOwner(c)
var candidateTypeSlot = newTypeWithSons(owner, tyAlias, @[c.errorType])
result.sons = @[candidateTypeSlot]
result.n = n
if inherited.kind != nkEmpty: if inherited.kind != nkEmpty:
for n in inherited.sons: for n in inherited.sons:
let typ = semTypeNode(c, n, nil) let typ = semTypeNode(c, n, nil)
result.sons.safeAdd(typ) result.sons.add(typ)
openScope(c)
for param in n[0]:
var
dummyName: PNode
dummyType: PType
let modifier = case param.kind
of nkVarTy: tyVar
of nkRefTy: tyRef
of nkPtrTy: tyPtr
of nkStaticTy: tyStatic
of nkTypeOfExpr: tyTypeDesc
else: tyNone
if modifier != tyNone:
dummyName = param[0]
dummyType = c.makeTypeWithModifier(modifier, candidateTypeSlot)
if modifier == tyTypeDesc: dummyType.flags.incl tfExplicit
else:
dummyName = param
dummyType = candidateTypeSlot
internalAssert dummyName.kind == nkIdent
var dummyParam = newSym(if modifier == tyTypeDesc: skType else: skVar,
dummyName.ident, owner, owner.info)
dummyParam.typ = dummyType
addDecl(c, dummyParam)
result.n.sons[3] = semConceptBody(c, n[3])
closeScope(c)
proc semProcTypeWithScope(c: PContext, n: PNode, proc semProcTypeWithScope(c: PContext, n: PNode,
prev: PType, kind: TSymKind): PType = prev: PType, kind: TSymKind): PType =
@ -1379,7 +1419,10 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
result = errorType(c) result = errorType(c)
else: else:
result = typeExpr.typ.base result = typeExpr.typ.base
if result.isMetaType: if result.isMetaType and
result.kind != tyUserTypeClass:
# the dot expression may refer to a concept type in
# a different module. allow a normal alias then.
let preprocessed = semGenericStmt(c, n) let preprocessed = semGenericStmt(c, n)
result = makeTypeFromExpr(c, preprocessed.copyTree) result = makeTypeFromExpr(c, preprocessed.copyTree)
else: else:
@ -1584,10 +1627,24 @@ proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode =
# type for each generic param. the index # type for each generic param. the index
# of the parameter will be stored in the # of the parameter will be stored in the
# attached symbol. # attached symbol.
var paramName = a.sons[j]
var covarianceFlag = tfUnresolved
if paramName.safeLen == 2:
if not nimEnableCovariance or paramName[0].ident.s == "in":
if father == nil or sfImportc notin father.sym.flags:
localError(paramName.info, errInOutFlagNotExtern, paramName[0].ident.s)
covarianceFlag = if paramName[0].ident.s == "in": tfContravariant
else: tfCovariant
if father != nil: father.flags.incl tfCovariant
paramName = paramName[1]
var s = if finalType.kind == tyStatic or tfWildcard in typ.flags: var s = if finalType.kind == tyStatic or tfWildcard in typ.flags:
newSymG(skGenericParam, a.sons[j], c).linkTo(finalType) newSymG(skGenericParam, paramName, c).linkTo(finalType)
else: else:
newSymG(skType, a.sons[j], c).linkTo(finalType) newSymG(skType, paramName, c).linkTo(finalType)
if covarianceFlag != tfUnresolved: s.typ.flags.incl(covarianceFlag)
if def.kind != nkEmpty: s.ast = def if def.kind != nkEmpty: s.ast = def
if father != nil: addSonSkipIntLit(father, s.typ) if father != nil: addSonSkipIntLit(father, s.typ)
s.position = result.len s.position = result.len

View file

@ -73,9 +73,13 @@ proc cacheTypeInst*(inst: PType) =
type type
LayeredIdTable* = object
topLayer*: TIdTable
nextLayer*: ptr LayeredIdTable
TReplTypeVars* {.final.} = object TReplTypeVars* {.final.} = object
c*: PContext c*: PContext
typeMap*: TIdTable # map PType to PType typeMap*: ptr LayeredIdTable # map PType to PType
symMap*: TIdTable # map PSym to PSym symMap*: TIdTable # map PSym to PSym
localCache*: TIdTable # local cache for remembering alraedy replaced localCache*: TIdTable # local cache for remembering alraedy replaced
# types during instantiation of meta types # types during instantiation of meta types
@ -91,6 +95,23 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType
proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym): PSym proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym): PSym
proc replaceTypeVarsN*(cl: var TReplTypeVars, n: PNode; start=0): PNode proc replaceTypeVarsN*(cl: var TReplTypeVars, n: PNode; start=0): PNode
proc initLayeredTypeMap*(pt: TIdTable): LayeredIdTable =
copyIdTable(result.topLayer, pt)
proc newTypeMapLayer*(cl: var TReplTypeVars): LayeredIdTable =
result.nextLayer = cl.typeMap
initIdTable(result.topLayer)
proc lookup(typeMap: ptr LayeredIdTable, key: PType): PType =
var tm = typeMap
while tm != nil:
result = PType(idTableGet(tm.topLayer, key))
if result != nil: return
tm = tm.nextLayer
template put(typeMap: ptr LayeredIdTable, key, value: PType) =
idTablePut(typeMap.topLayer, key, value)
template checkMetaInvariants(cl: TReplTypeVars, t: PType) = template checkMetaInvariants(cl: TReplTypeVars, t: PType) =
when false: when false:
if t != nil and tfHasMeta in t.flags and if t != nil and tfHasMeta in t.flags and
@ -106,7 +127,8 @@ proc replaceTypeVarsT*(cl: var TReplTypeVars, t: PType): PType =
proc prepareNode(cl: var TReplTypeVars, n: PNode): PNode = proc prepareNode(cl: var TReplTypeVars, n: PNode): PNode =
let t = replaceTypeVarsT(cl, n.typ) let t = replaceTypeVarsT(cl, n.typ)
if t != nil and t.kind == tyStatic and t.n != nil: if t != nil and t.kind == tyStatic and t.n != nil:
return t.n return if tfUnresolved in t.flags: prepareNode(cl, t.n)
else: t.n
result = copyNode(n) result = copyNode(n)
result.typ = t result.typ = t
if result.kind == nkSym: result.sym = replaceTypeVarsS(cl, n.sym) if result.kind == nkSym: result.sym = replaceTypeVarsS(cl, n.sym)
@ -219,7 +241,7 @@ proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym): PSym =
result.ast = replaceTypeVarsN(cl, s.ast) result.ast = replaceTypeVarsN(cl, s.ast)
proc lookupTypeVar(cl: var TReplTypeVars, t: PType): PType = proc lookupTypeVar(cl: var TReplTypeVars, t: PType): PType =
result = PType(idTableGet(cl.typeMap, t)) result = cl.typeMap.lookup(t)
if result == nil: if result == nil:
if cl.allowMetaTypes or tfRetType in t.flags: return if cl.allowMetaTypes or tfRetType in t.flags: return
localError(t.sym.info, errCannotInstantiateX, typeToString(t)) localError(t.sym.info, errCannotInstantiateX, typeToString(t))
@ -227,7 +249,7 @@ proc lookupTypeVar(cl: var TReplTypeVars, t: PType): PType =
# In order to prevent endless recursions, we must remember # In order to prevent endless recursions, we must remember
# this bad lookup and replace it with errorType everywhere. # this bad lookup and replace it with errorType everywhere.
# These code paths are only active in "nim check" # These code paths are only active in "nim check"
idTablePut(cl.typeMap, t, result) cl.typeMap.put(t, result)
elif result.kind == tyGenericParam and not cl.allowMetaTypes: elif result.kind == tyGenericParam and not cl.allowMetaTypes:
internalError(cl.info, "substitution with generic parameter") internalError(cl.info, "substitution with generic parameter")
@ -243,6 +265,7 @@ proc instCopyType*(cl: var TReplTypeVars, t: PType): PType =
proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType = proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
# tyGenericInvocation[A, tyGenericInvocation[A, B]] # tyGenericInvocation[A, tyGenericInvocation[A, B]]
# is difficult to handle: # is difficult to handle:
const eqFlags = eqTypeFlags + {tfGcSafe}
var body = t.sons[0] var body = t.sons[0]
if body.kind != tyGenericBody: internalError(cl.info, "no generic body") if body.kind != tyGenericBody: internalError(cl.info, "no generic body")
var header: PType = t var header: PType = t
@ -252,7 +275,7 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
else: else:
result = searchInstTypes(t) result = searchInstTypes(t)
if result != nil and eqTypeFlags*result.flags == eqTypeFlags*t.flags: return if result != nil and eqFlags*result.flags == eqFlags*t.flags: return
for i in countup(1, sonsLen(t) - 1): for i in countup(1, sonsLen(t) - 1):
var x = t.sons[i] var x = t.sons[i]
if x.kind in {tyGenericParam}: if x.kind in {tyGenericParam}:
@ -267,7 +290,7 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
if header != t: if header != t:
# search again after first pass: # search again after first pass:
result = searchInstTypes(header) result = searchInstTypes(header)
if result != nil and eqTypeFlags*result.flags == eqTypeFlags*t.flags: return if result != nil and eqFlags*result.flags == eqFlags*t.flags: return
else: else:
header = instCopyType(cl, t) header = instCopyType(cl, t)
@ -285,12 +308,16 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
let oldSkipTypedesc = cl.skipTypedesc let oldSkipTypedesc = cl.skipTypedesc
cl.skipTypedesc = true cl.skipTypedesc = true
var typeMapLayer = newTypeMapLayer(cl)
cl.typeMap = addr(typeMapLayer)
for i in countup(1, sonsLen(t) - 1): for i in countup(1, sonsLen(t) - 1):
var x = replaceTypeVarsT(cl, t.sons[i]) var x = replaceTypeVarsT(cl, t.sons[i])
assert x.kind != tyGenericInvocation assert x.kind != tyGenericInvocation
header.sons[i] = x header.sons[i] = x
propagateToOwner(header, x) propagateToOwner(header, x)
idTablePut(cl.typeMap, body.sons[i-1], x) cl.typeMap.put(body.sons[i-1], x)
for i in countup(1, sonsLen(t) - 1): for i in countup(1, sonsLen(t) - 1):
# if one of the params is not concrete, we cannot do anything # if one of the params is not concrete, we cannot do anything
@ -303,6 +330,9 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
cl.skipTypedesc = oldSkipTypedesc cl.skipTypedesc = oldSkipTypedesc
newbody.flags = newbody.flags + (t.flags + body.flags - tfInstClearedFlags) newbody.flags = newbody.flags + (t.flags + body.flags - tfInstClearedFlags)
result.flags = result.flags + newbody.flags - tfInstClearedFlags result.flags = result.flags + newbody.flags - tfInstClearedFlags
cl.typeMap = cl.typeMap.nextLayer
# This is actually wrong: tgeneric_closure fails with this line: # This is actually wrong: tgeneric_closure fails with this line:
#newbody.callConv = body.callConv #newbody.callConv = body.callConv
# This type may be a generic alias and we want to resolve it here. # This type may be a generic alias and we want to resolve it here.
@ -404,7 +434,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
if t == nil: return if t == nil: return
if t.kind in {tyStatic, tyGenericParam} + tyTypeClasses: if t.kind in {tyStatic, tyGenericParam} + tyTypeClasses:
let lookup = PType(idTableGet(cl.typeMap, t)) let lookup = cl.typeMap.lookup(t)
if lookup != nil: return lookup if lookup != nil: return lookup
case t.kind case t.kind
@ -446,7 +476,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
result = skipIntLit(t) result = skipIntLit(t)
of tyTypeDesc: of tyTypeDesc:
let lookup = PType(idTableGet(cl.typeMap, t)) # lookupTypeVar(cl, t) let lookup = cl.typeMap.lookup(t)
if lookup != nil: if lookup != nil:
result = lookup result = lookup
if tfUnresolved in t.flags or cl.skipTypedesc: result = result.base if tfUnresolved in t.flags or cl.skipTypedesc: result = result.base
@ -485,7 +515,6 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
propagateToOwner(result, r) propagateToOwner(result, r)
# bug #4677: Do not instantiate effect lists # bug #4677: Do not instantiate effect lists
result.n = replaceTypeVarsN(cl, result.n, ord(result.kind==tyProc)) result.n = replaceTypeVarsN(cl, result.n, ord(result.kind==tyProc))
case result.kind case result.kind
of tyArray: of tyArray:
let idx = result.sons[0] let idx = result.sons[0]
@ -500,18 +529,19 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
else: discard else: discard
proc initTypeVars*(p: PContext, pt: TIdTable, info: TLineInfo; proc initTypeVars*(p: PContext, typeMap: ptr LayeredIdTable, info: TLineInfo;
owner: PSym): TReplTypeVars = owner: PSym): TReplTypeVars =
initIdTable(result.symMap) initIdTable(result.symMap)
copyIdTable(result.typeMap, pt)
initIdTable(result.localCache) initIdTable(result.localCache)
result.typeMap = typeMap
result.info = info result.info = info
result.c = p result.c = p
result.owner = owner result.owner = owner
proc replaceTypesInBody*(p: PContext, pt: TIdTable, n: PNode; proc replaceTypesInBody*(p: PContext, pt: TIdTable, n: PNode;
owner: PSym, allowMetaTypes = false): PNode = owner: PSym, allowMetaTypes = false): PNode =
var cl = initTypeVars(p, pt, n.info, owner) var typeMap = initLayeredTypeMap(pt)
var cl = initTypeVars(p, addr(typeMap), n.info, owner)
cl.allowMetaTypes = allowMetaTypes cl.allowMetaTypes = allowMetaTypes
pushInfoContext(n.info) pushInfoContext(n.info)
result = replaceTypeVarsN(cl, n) result = replaceTypeVarsN(cl, n)
@ -519,7 +549,8 @@ proc replaceTypesInBody*(p: PContext, pt: TIdTable, n: PNode;
proc replaceTypesForLambda*(p: PContext, pt: TIdTable, n: PNode; proc replaceTypesForLambda*(p: PContext, pt: TIdTable, n: PNode;
original, new: PSym): PNode = original, new: PSym): PNode =
var cl = initTypeVars(p, pt, n.info, original) var typeMap = initLayeredTypeMap(pt)
var cl = initTypeVars(p, addr(typeMap), n.info, original)
idTablePut(cl.symMap, original, new) idTablePut(cl.symMap, original, new)
pushInfoContext(n.info) pushInfoContext(n.info)
result = replaceTypeVarsN(cl, n) result = replaceTypeVarsN(cl, n)
@ -527,14 +558,16 @@ proc replaceTypesForLambda*(p: PContext, pt: TIdTable, n: PNode;
proc generateTypeInstance*(p: PContext, pt: TIdTable, info: TLineInfo, proc generateTypeInstance*(p: PContext, pt: TIdTable, info: TLineInfo,
t: PType): PType = t: PType): PType =
var cl = initTypeVars(p, pt, info, nil) var typeMap = initLayeredTypeMap(pt)
var cl = initTypeVars(p, addr(typeMap), info, nil)
pushInfoContext(info) pushInfoContext(info)
result = replaceTypeVarsT(cl, t) result = replaceTypeVarsT(cl, t)
popInfoContext() popInfoContext()
proc prepareMetatypeForSigmatch*(p: PContext, pt: TIdTable, info: TLineInfo, proc prepareMetatypeForSigmatch*(p: PContext, pt: TIdTable, info: TLineInfo,
t: PType): PType = t: PType): PType =
var cl = initTypeVars(p, pt, info, nil) var typeMap = initLayeredTypeMap(pt)
var cl = initTypeVars(p, addr(typeMap), info, nil)
cl.allowMetaTypes = true cl.allowMetaTypes = true
pushInfoContext(info) pushInfoContext(info)
result = replaceTypeVarsT(cl, t) result = replaceTypeVarsT(cl, t)

View file

@ -69,6 +69,12 @@ type
mutabilityProblem*: uint8 # tyVar mismatch mutabilityProblem*: uint8 # tyVar mismatch
inheritancePenalty: int # to prefer closest father object type inheritancePenalty: int # to prefer closest father object type
TTypeRelFlag* = enum
trDontBind
trNoCovariance
TTypeRelFlags* = set[TTypeRelFlag]
TTypeRelation* = enum # order is important! TTypeRelation* = enum # order is important!
isNone, isConvertible, isNone, isConvertible,
isIntConv, isIntConv,
@ -198,7 +204,9 @@ proc sumGeneric(t: PType): int =
if t.sons[i] != nil: if t.sons[i] != nil:
result += t.sons[i].sumGeneric result += t.sons[i].sumGeneric
break break
of tyGenericParam, tyExpr, tyStatic, tyStmt: break of tyStatic:
return t.sons[0].sumGeneric + 1
of tyGenericParam, tyExpr, tyStmt: break
of tyAlias: t = t.lastSon of tyAlias: t = t.lastSon
of tyBool, tyChar, tyEnum, tyObject, tyPointer, of tyBool, tyChar, tyEnum, tyObject, tyPointer,
tyString, tyCString, tyInt..tyInt64, tyFloat..tyFloat128, tyString, tyCString, tyInt..tyInt64, tyFloat..tyFloat128,
@ -296,7 +304,33 @@ proc describeArgs*(c: PContext, n: PNode, startIdx = 1;
add(result, argTypeToString(arg, prefer)) add(result, argTypeToString(arg, prefer))
if i != sonsLen(n) - 1: add(result, ", ") if i != sonsLen(n) - 1: add(result, ", ")
proc typeRel*(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation proc typeRelImpl*(c: var TCandidate, f, aOrig: PType,
flags: TTypeRelFlags = {}): TTypeRelation
const traceTypeRel = false
when traceTypeRel:
var nextTypeRel = 0
template typeRel*(c: var TCandidate, f, aOrig: PType,
flags: TTypeRelFlags = {}): TTypeRelation =
when traceTypeRel:
var enteringAt = nextTypeRel
if mdbg:
inc nextTypeRel
echo "----- TYPE REL ", enteringAt
debug f
debug aOrig
# writeStackTrace()
let r = typeRelImpl(c, f, aOrig, flags)
when traceTypeRel:
if enteringAt != nextTypeRel:
echo "----- TYPE REL ", enteringAt, " RESULT: ", r
r
proc concreteType(c: TCandidate, t: PType): PType = proc concreteType(c: TCandidate, t: PType): PType =
case t.kind case t.kind
of tyNil: of tyNil:
@ -594,19 +628,29 @@ proc typeRangeRel(f, a: PType): TTypeRelation {.noinline.} =
else: else:
result = isNone result = isNone
proc matchUserTypeClass*(c: PContext, m: var TCandidate, proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType =
ff, a: PType): PType =
var var
c = m.c
typeClass = ff.skipTypes({tyUserTypeClassInst}) typeClass = ff.skipTypes({tyUserTypeClassInst})
body = typeClass.n[3] body = typeClass.n[3]
if c.inTypeClass > 4: matchedConceptContext: TMatchedConcept
localError(body.info, $body & " too nested for type matching") prevMatchedConcept = c.matchedConcept
return nil prevCandidateType = typeClass[0][0]
if prevMatchedConcept != nil:
matchedConceptContext.prev = prevMatchedConcept
matchedConceptContext.depth = prevMatchedConcept.depth + 1
if prevMatchedConcept.depth > 4:
localError(body.info, $body & " too nested for type matching")
return nil
openScope(c) openScope(c)
inc c.inTypeClass matchedConceptContext.candidateType = a
typeClass[0].sons[0] = a
c.matchedConcept = addr(matchedConceptContext)
defer: defer:
dec c.inTypeClass c.matchedConcept = prevMatchedConcept
typeClass[0].sons[0] = prevCandidateType
closeScope(c) closeScope(c)
var typeParams: seq[(PSym, PType)] var typeParams: seq[(PSym, PType)]
@ -617,6 +661,9 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate,
typeParamName = ff.base.sons[i-1].sym.name typeParamName = ff.base.sons[i-1].sym.name
typ = ff.sons[i] typ = ff.sons[i]
param: PSym param: PSym
alreadyBound = PType(idTableGet(m.bindings, typ))
if alreadyBound != nil: typ = alreadyBound
template paramSym(kind): untyped = template paramSym(kind): untyped =
newSym(kind, typeParamName, typeClass.sym, typeClass.sym.info) newSym(kind, typeParamName, typeClass.sym, typeClass.sym.info)
@ -650,33 +697,6 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate,
addDecl(c, param) addDecl(c, param)
for param in typeClass.n[0]:
var
dummyName: PNode
dummyType: PType
let modifier = case param.kind
of nkVarTy: tyVar
of nkRefTy: tyRef
of nkPtrTy: tyPtr
of nkStaticTy: tyStatic
of nkTypeOfExpr: tyTypeDesc
else: tyNone
if modifier != tyNone:
dummyName = param[0]
dummyType = c.makeTypeWithModifier(modifier, a)
if modifier == tyTypeDesc: dummyType.flags.incl tfExplicit
else:
dummyName = param
dummyType = a
internalAssert dummyName.kind == nkIdent
var dummyParam = newSym(if modifier == tyTypeDesc: skType else: skVar,
dummyName.ident, typeClass.sym, typeClass.sym.info)
dummyParam.typ = dummyType
addDecl(c, dummyParam)
var var
oldWriteHook: type(writelnHook) oldWriteHook: type(writelnHook)
diagnostics: seq[string] diagnostics: seq[string]
@ -818,7 +838,7 @@ proc inferStaticParam*(c: var TCandidate, lhs: PNode, rhs: BiggestInt): bool =
var inferred = newTypeWithSons(c.c, tyStatic, lhs.typ.sons) var inferred = newTypeWithSons(c.c, tyStatic, lhs.typ.sons)
inferred.n = newIntNode(nkIntLit, rhs) inferred.n = newIntNode(nkIntLit, rhs)
put(c, lhs.typ, inferred) put(c, lhs.typ, inferred)
if c.c.inTypeClass > 0: if c.c.matchedConcept != nil:
# inside concepts, binding is currently done with # inside concepts, binding is currently done with
# direct mutation of the involved types: # direct mutation of the involved types:
lhs.typ.n = inferred.n lhs.typ.n = inferred.n
@ -860,7 +880,28 @@ template subtypeCheck() =
if result <= isSubrange and f.lastSon.skipTypes(abstractInst).kind in {tyRef, tyPtr, tyVar}: if result <= isSubrange and f.lastSon.skipTypes(abstractInst).kind in {tyRef, tyPtr, tyVar}:
result = isNone result = isNone
proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = proc isCovariantPtr(c: var TCandidate, f, a: PType): bool =
# this proc is always called for a pair of matching types
assert f.kind == a.kind
template baseTypesCheck(lhs, rhs: PType): bool =
lhs.kind notin {tyPtr, tyRef, tyVar} and
typeRel(c, lhs, rhs, {trNoCovariance}) == isSubtype
case f.kind
of tyRef, tyPtr:
return baseTypesCheck(f.base, a.base)
of tyGenericInst:
let body = f.base
return body == a.base and
a.sonsLen == 3 and
tfWeakCovariant notin body.sons[0].flags and
baseTypesCheck(f.sons[1], a.sons[1])
else:
return false
proc typeRelImpl(c: var TCandidate, f, aOrig: PType,
flags: TTypeRelFlags = {}): TTypeRelation =
# typeRel can be used to establish various relationships between types: # typeRel can be used to establish various relationships between types:
# #
# 1) When used with concrete types, it will check for type equivalence # 1) When used with concrete types, it will check for type equivalence
@ -887,7 +928,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
assert(aOrig != nil) assert(aOrig != nil)
var var
useTypeLoweringRuleInTypeClass = c.c.inTypeClass > 0 and useTypeLoweringRuleInTypeClass = c.c.matchedConcept != nil and
not c.isNoCall and not c.isNoCall and
f.kind != tyTypeDesc and f.kind != tyTypeDesc and
tfExplicit notin aOrig.flags tfExplicit notin aOrig.flags
@ -927,6 +968,8 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
result = isEqual result = isEqual
return return
template doBind: bool = trDontBind notin flags
# var and static arguments match regular modifier-free types # var and static arguments match regular modifier-free types
var a = aOrig.skipTypes({tyStatic, tyVar}).maybeSkipDistinct(c.calleeSym) var a = aOrig.skipTypes({tyStatic, tyVar}).maybeSkipDistinct(c.calleeSym)
# XXX: Theoretically, maybeSkipDistinct could be called before we even # XXX: Theoretically, maybeSkipDistinct could be called before we even
@ -934,7 +977,10 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
# for example, but unfortunately `prepareOperand` is not called in certain # for example, but unfortunately `prepareOperand` is not called in certain
# situation when nkDotExpr are rotated to nkDotCalls # situation when nkDotExpr are rotated to nkDotCalls
if a.kind in {tyGenericInst, tyAlias} and if aOrig.kind == tyAlias:
return typeRel(c, f, lastSon(aOrig))
if a.kind == tyGenericInst and
skipTypes(f, {tyVar}).kind notin { skipTypes(f, {tyVar}).kind notin {
tyGenericBody, tyGenericInvocation, tyGenericBody, tyGenericInvocation,
tyGenericInst, tyGenericParam} + tyTypeClasses: tyGenericInst, tyGenericParam} + tyTypeClasses:
@ -956,23 +1002,28 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
case a.kind case a.kind
of tyOr: of tyOr:
# XXX: deal with the current dual meaning of tyGenericParam
c.typedescMatched = true
# seq[int|string] vs seq[number] # seq[int|string] vs seq[number]
# both int and string must match against number # both int and string must match against number
# but ensure that '[T: A|A]' matches as good as '[T: A]' (bug #2219): # but ensure that '[T: A|A]' matches as good as '[T: A]' (bug #2219):
result = isGeneric result = isGeneric
for branch in a.sons: for branch in a.sons:
let x = typeRel(c, f, branch, false) let x = typeRel(c, f, branch, flags + {trDontBind})
if x == isNone: return isNone if x == isNone: return isNone
if x < result: result = x if x < result: result = x
return
of tyAnd: of tyAnd:
# XXX: deal with the current dual meaning of tyGenericParam
c.typedescMatched = true
# seq[Sortable and Iterable] vs seq[Sortable] # seq[Sortable and Iterable] vs seq[Sortable]
# only one match is enough # only one match is enough
for branch in a.sons: for branch in a.sons:
let x = typeRel(c, f, branch, false) let x = typeRel(c, f, branch, flags + {trDontBind})
if x != isNone: if x != isNone:
return if x >= isGeneric: isGeneric else: x return if x >= isGeneric: isGeneric else: x
result = isNone return isNone
of tyNot: of tyNot:
case f.kind case f.kind
@ -994,11 +1045,12 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
else: isNone else: isNone
of tyUserTypeClass, tyUserTypeClassInst: of tyUserTypeClass, tyUserTypeClassInst:
# consider this: 'var g: Node' *within* a concept where 'Node' if c.c.matchedConcept != nil:
# is a concept too (tgraph) # consider this: 'var g: Node' *within* a concept where 'Node'
let x = typeRel(c, a, f, false) # is a concept too (tgraph)
if x >= isGeneric: let x = typeRel(c, a, f, flags + {trDontBind})
return isGeneric if x >= isGeneric:
return isGeneric
else: discard else: discard
case f.kind case f.kind
@ -1042,7 +1094,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
of tyFloat128: result = handleFloatRange(f, a) of tyFloat128: result = handleFloatRange(f, a)
of tyVar: of tyVar:
if aOrig.kind == tyVar: result = typeRel(c, f.base, aOrig.base) if aOrig.kind == tyVar: result = typeRel(c, f.base, aOrig.base)
else: result = typeRel(c, f.base, aOrig) else: result = typeRel(c, f.base, aOrig, flags + {trNoCovariance})
subtypeCheck() subtypeCheck()
of tyArray: of tyArray:
case a.kind case a.kind
@ -1056,13 +1108,21 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
fRange = a fRange = a
else: else:
fRange = prev fRange = prev
result = typeRel(c, f.sons[1].skipTypes({tyTypeDesc}), let ff = f.sons[1].skipTypes({tyTypeDesc})
a.sons[1].skipTypes({tyTypeDesc})) let aa = a.sons[1].skipTypes({tyTypeDesc})
if result < isGeneric: return isNone result = typeRel(c, ff, aa)
if result < isGeneric:
if nimEnableCovariance and
trNoCovariance notin flags and
ff.kind == aa.kind and
isCovariantPtr(c, ff, aa):
result = isSubtype
else:
return isNone
if fRange.rangeHasUnresolvedStatic: if fRange.rangeHasUnresolvedStatic:
return inferStaticsInRange(c, fRange, a) return inferStaticsInRange(c, fRange, a)
elif c.c.inTypeClass > 0 and aRange.rangeHasUnresolvedStatic: elif c.c.matchedConcept != nil and aRange.rangeHasUnresolvedStatic:
return inferStaticsInRange(c, aRange, f) return inferStaticsInRange(c, aRange, f)
else: else:
if lengthOrd(fRange) != lengthOrd(aRange): if lengthOrd(fRange) != lengthOrd(aRange):
@ -1077,20 +1137,31 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
if tfOldSchoolExprStmt in f.sons[0].flags: if tfOldSchoolExprStmt in f.sons[0].flags:
if f.sons[0].kind == tyExpr: return if f.sons[0].kind == tyExpr: return
elif f.sons[0].kind == tyStmt: return elif f.sons[0].kind == tyStmt: return
template matchArrayOrSeq(aBase: PType) =
let ff = f.base
let aa = aBase
let baseRel = typeRel(c, ff, aa)
if baseRel >= isGeneric:
result = isConvertible
elif nimEnableCovariance and
trNoCovariance notin flags and
ff.kind == aa.kind and
isCovariantPtr(c, ff, aa):
result = isConvertible
case a.kind case a.kind
of tyOpenArray, tyVarargs: of tyOpenArray, tyVarargs:
result = typeRel(c, base(f), base(a)) result = typeRel(c, base(f), base(a))
if result < isGeneric: result = isNone if result < isGeneric: result = isNone
of tyArray: of tyArray:
if (f.sons[0].kind != tyGenericParam) and (a.sons[1].kind == tyEmpty): if (f.sons[0].kind != tyGenericParam) and (a.sons[1].kind == tyEmpty):
result = isSubtype return isSubtype
elif typeRel(c, base(f), a.sons[1]) >= isGeneric: matchArrayOrSeq(a.sons[1])
result = isConvertible
of tySequence: of tySequence:
if (f.sons[0].kind != tyGenericParam) and (a.sons[0].kind == tyEmpty): if (f.sons[0].kind != tyGenericParam) and (a.sons[0].kind == tyEmpty):
result = isConvertible return isConvertible
elif typeRel(c, base(f), a.sons[0]) >= isGeneric: matchArrayOrSeq(a.sons[0])
result = isConvertible
of tyString: of tyString:
if f.kind == tyOpenArray: if f.kind == tyOpenArray:
if f.sons[0].kind == tyChar: if f.sons[0].kind == tyChar:
@ -1105,8 +1176,17 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
if (f.sons[0].kind != tyGenericParam) and (a.sons[0].kind == tyEmpty): if (f.sons[0].kind != tyGenericParam) and (a.sons[0].kind == tyEmpty):
result = isSubtype result = isSubtype
else: else:
result = typeRel(c, f.sons[0], a.sons[0]) let ff = f.sons[0]
if result < isGeneric: result = isNone let aa = a.sons[0]
result = typeRel(c, ff, aa)
if result < isGeneric:
if nimEnableCovariance and
trNoCovariance notin flags and
ff.kind == aa.kind and
isCovariantPtr(c, ff, aa):
result = isSubtype
else:
result = isNone
elif tfNotNil in f.flags and tfNotNil notin a.flags: elif tfNotNil in f.flags and tfNotNil notin a.flags:
result = isNilConversion result = isNilConversion
of tyNil: result = f.allowsNil of tyNil: result = f.allowsNil
@ -1158,7 +1238,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
if a.len < f.len: return isNone if a.len < f.len: return isNone
for i in 0..f.len-2: for i in 0..f.len-2:
if typeRel(c, f.sons[i], a.sons[i]) == isNone: return isNone if typeRel(c, f.sons[i], a.sons[i]) == isNone: return isNone
result = typeRel(c, f.lastSon, a.lastSon) result = typeRel(c, f.lastSon, a.lastSon, flags + {trNoCovariance})
subtypeCheck() subtypeCheck()
if result <= isConvertible: result = isNone if result <= isConvertible: result = isNone
elif tfNotNil in f.flags and tfNotNil notin a.flags: elif tfNotNil in f.flags and tfNotNil notin a.flags:
@ -1230,13 +1310,28 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
var m = c var m = c
if a.kind == tyGenericInst: if a.kind == tyGenericInst:
if roota.base == rootf.base: if roota.base == rootf.base:
let nextFlags = flags + {trNoCovariance}
var hasCovariance = false
for i in 1 .. rootf.sonsLen-2: for i in 1 .. rootf.sonsLen-2:
let ff = rootf.sons[i] let ff = rootf.sons[i]
let aa = roota.sons[i] let aa = roota.sons[i]
result = typeRel(c, ff, aa) result = typeRel(c, ff, aa, nextFlags)
if result notin {isEqual, isGeneric}: return isNone if result notin {isEqual, isGeneric}:
# if ff.kind == tyRange and result != isEqual: return isNone if trNoCovariance notin flags and ff.kind == aa.kind:
let paramFlags = rootf.base.sons[i-1].flags
hasCovariance =
if tfCovariant in paramFlags:
if tfWeakCovariant in paramFlags:
isCovariantPtr(c, ff, aa)
else:
ff.kind notin {tyRef, tyPtr} and result == isSubtype
else:
tfContravariant in paramFlags and
typeRel(c, aa, ff) == isSubtype
if hasCovariance:
continue
return isNone
if prev == nil: put(c, f, a) if prev == nil: put(c, f, a)
result = isGeneric result = isGeneric
else: else:
@ -1260,7 +1355,8 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
result = isNone result = isNone
else: else:
result = typeRel(c, lastSon(f), a) result = typeRel(c, lastSon(f), a)
if result != isNone: put(c, f, a) if result != isNone and a.kind != tyNil:
put(c, f, a)
of tyGenericBody: of tyGenericBody:
considerPreviousT: considerPreviousT:
@ -1396,7 +1492,8 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
result = typeRel(c, f.lastSon, a) result = typeRel(c, f.lastSon, a)
else: else:
considerPreviousT: considerPreviousT:
var matched = matchUserTypeClass(c.c, c, f, aOrig) if aOrig == f: return isEqual
var matched = matchUserTypeClass(c, f, aOrig)
if matched != nil: if matched != nil:
bindConcreteTypeToUserTypeClass(matched, a) bindConcreteTypeToUserTypeClass(matched, a)
if doBind: put(c, f, matched) if doBind: put(c, f, matched)
@ -1450,7 +1547,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
result = isNone result = isNone
else: else:
if f.sonsLen > 0 and f.sons[0].kind != tyNone: if f.sonsLen > 0 and f.sons[0].kind != tyNone:
result = typeRel(c, f.lastSon, a, false) result = typeRel(c, f.lastSon, a, flags + {trDontBind})
if doBind and result notin {isNone, isGeneric}: if doBind and result notin {isNone, isGeneric}:
let concrete = concreteType(c, a) let concrete = concreteType(c, a)
if concrete == nil: return isNone if concrete == nil: return isNone
@ -1488,6 +1585,12 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
if not exprStructuralEquivalent(f.n, aOrig.n): if not exprStructuralEquivalent(f.n, aOrig.n):
result = isNone result = isNone
if result != isNone: put(c, f, aOrig) if result != isNone: put(c, f, aOrig)
elif aOrig.n != nil:
result = typeRel(c, f.lastSon, aOrig.n.typ)
if result != isNone:
var boundType = newTypeWithSons(c.c, tyStatic, @[aOrig.n.typ])
boundType.n = aOrig.n
put(c, f, boundType)
else: else:
result = isNone result = isNone
elif prev.kind == tyStatic: elif prev.kind == tyStatic:
@ -1691,6 +1794,13 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType,
arg.typ.sons = @[evaluated.typ] arg.typ.sons = @[evaluated.typ]
arg.typ.n = evaluated arg.typ.n = evaluated
a = arg.typ a = arg.typ
else:
if m.callee.kind == tyGenericBody:
if f.kind == tyStatic and typeRel(m, f.base, a) != isNone:
result = makeStaticExpr(m.c, arg)
result.typ.flags.incl tfUnresolved
result.typ.n = arg
return
var r = typeRel(m, f, a) var r = typeRel(m, f, a)
@ -2140,6 +2250,8 @@ proc matches*(c: PContext, n, nOrig: PNode, m: var TCandidate) =
var def = copyTree(formal.ast) var def = copyTree(formal.ast)
if def.kind == nkNilLit: if def.kind == nkNilLit:
def = implicitConv(nkHiddenStdConv, formal.typ, def, m, c) def = implicitConv(nkHiddenStdConv, formal.typ, def, m, c)
if {tfImplicitTypeParam, tfGenericTypeParam} * formal.typ.flags != {}:
put(m, formal.typ, def.typ)
setSon(m.call, formal.position + 1, def) setSon(m.call, formal.position + 1, def)
inc(f) inc(f)
# forget all inferred types if the overload matching failed # forget all inferred types if the overload matching failed

View file

@ -41,6 +41,7 @@ proc exprStructuralEquivalent*(a, b: PNode; strictSymEquality=false): bool =
of nkCharLit..nkUInt64Lit: result = a.intVal == b.intVal of nkCharLit..nkUInt64Lit: result = a.intVal == b.intVal
of nkFloatLit..nkFloat64Lit: result = a.floatVal == b.floatVal of nkFloatLit..nkFloat64Lit: result = a.floatVal == b.floatVal
of nkStrLit..nkTripleStrLit: result = a.strVal == b.strVal of nkStrLit..nkTripleStrLit: result = a.strVal == b.strVal
of nkCommentStmt: result = a.comment == b.comment
of nkEmpty, nkNilLit, nkType: result = true of nkEmpty, nkNilLit, nkType: result = true
else: else:
if sonsLen(a) == sonsLen(b): if sonsLen(a) == sonsLen(b):
@ -109,9 +110,11 @@ proc isDeepConstExpr*(n: PNode): bool =
proc isRange*(n: PNode): bool {.inline.} = proc isRange*(n: PNode): bool {.inline.} =
if n.kind in nkCallKinds: if n.kind in nkCallKinds:
if n[0].kind == nkIdent and n[0].ident.id == ord(wDotDot) or let callee = n[0]
n[0].kind in {nkClosedSymChoice, nkOpenSymChoice} and if (callee.kind == nkIdent and callee.ident.id == ord(wDotDot)) or
n[0][1].sym.name.id == ord(wDotDot): (callee.kind == nkSym and callee.sym.name.id == ord(wDotDot)) or
(callee.kind in {nkClosedSymChoice, nkOpenSymChoice} and
callee[1].sym.name.id == ord(wDotDot)):
result = true result = true
proc whichPragma*(n: PNode): TSpecialWord = proc whichPragma*(n: PNode): TSpecialWord =

View file

@ -139,6 +139,9 @@ proc getProcHeader*(sym: PSym; prefer: TPreferedDesc = preferName): string =
add(result, ')') add(result, ')')
if n.sons[0].typ != nil: if n.sons[0].typ != nil:
result.add(": " & typeToString(n.sons[0].typ, prefer)) result.add(": " & typeToString(n.sons[0].typ, prefer))
result.add "[declared in "
result.add($sym.info)
result.add "]"
proc elemType*(t: PType): PType = proc elemType*(t: PType): PType =
assert(t != nil) assert(t != nil)
@ -421,6 +424,12 @@ template bindConcreteTypeToUserTypeClass*(tc, concrete: PType) =
tc.sons.safeAdd concrete tc.sons.safeAdd concrete
tc.flags.incl tfResolved tc.flags.incl tfResolved
# TODO: It would be a good idea to kill the special state of a resolved
# concept by switching to tyAlias within the instantiated procs.
# Currently, tyAlias is always skipped with lastSon, which means that
# we can store information about the matched concept in another position.
# Then builtInFieldAccess can be modified to properly read the derived
# consts and types stored within the concept.
template isResolvedUserTypeClass*(t: PType): bool = template isResolvedUserTypeClass*(t: PType): bool =
tfResolved in t.flags tfResolved in t.flags
@ -437,6 +446,13 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
result = t.sym.name.s & " literal(" & $t.n.intVal & ")" result = t.sym.name.s & " literal(" & $t.n.intVal & ")"
elif prefer == preferName or t.sym.owner.isNil: elif prefer == preferName or t.sym.owner.isNil:
result = t.sym.name.s result = t.sym.name.s
if t.kind == tyGenericParam and t.sons != nil and t.sonsLen > 0:
result.add ": "
var first = true
for son in t.sons:
if not first: result.add " or "
result.add son.typeToString
first = false
else: else:
result = t.sym.owner.name.s & '.' & t.sym.name.s result = t.sym.owner.name.s & '.' & t.sym.name.s
result.addTypeFlags(t) result.addTypeFlags(t)
@ -458,7 +474,7 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
add(result, ']') add(result, ']')
of tyTypeDesc: of tyTypeDesc:
if t.sons[0].kind == tyNone: result = "typedesc" if t.sons[0].kind == tyNone: result = "typedesc"
else: result = "typedesc[" & typeToString(t.sons[0]) & "]" else: result = "type " & typeToString(t.sons[0])
of tyStatic: of tyStatic:
internalAssert t.len > 0 internalAssert t.len > 0
if prefer == preferGenericArg and t.n != nil: if prefer == preferGenericArg and t.n != nil:
@ -688,6 +704,7 @@ type
ExactTypeDescValues ExactTypeDescValues
ExactGenericParams ExactGenericParams
ExactConstraints ExactConstraints
ExactGcSafety
AllowCommonBase AllowCommonBase
TTypeCmpFlags* = set[TTypeCmpFlag] TTypeCmpFlags* = set[TTypeCmpFlag]
@ -976,6 +993,8 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
cycleCheck() cycleCheck()
if a.kind == tyUserTypeClass and a.n != nil: return a.n == b.n if a.kind == tyUserTypeClass and a.n != nil: return a.n == b.n
result = sameChildrenAux(a, b, c) and sameFlags(a, b) result = sameChildrenAux(a, b, c) and sameFlags(a, b)
if result and ExactGcSafety in c.flags:
result = a.flags * {tfThread} == b.flags * {tfThread}
if result and a.kind == tyProc: if result and a.kind == tyProc:
result = ((IgnoreCC in c.flags) or a.callConv == b.callConv) and result = ((IgnoreCC in c.flags) or a.callConv == b.callConv) and
((ExactConstraints notin c.flags) or sameConstraints(a.n, b.n)) ((ExactConstraints notin c.flags) or sameConstraints(a.n, b.n))

View file

@ -52,13 +52,16 @@ proc renderType(n: PNode): string =
else: else:
result = "ptr" result = "ptr"
of nkProcTy: of nkProcTy:
assert len(n) > 1 assert len(n) != 1
let params = n[0] if len(n) > 1:
assert params.kind == nkFormalParams let params = n[0]
assert len(params) > 0 assert params.kind == nkFormalParams
result = "proc(" assert len(params) > 0
for i in 1 .. <len(params): result.add(renderType(params[i]) & ',') result = "proc("
result[<len(result)] = ')' for i in 1 .. <len(params): result.add(renderType(params[i]) & ',')
result[<len(result)] = ')'
else:
result = "proc"
of nkIdentDefs: of nkIdentDefs:
assert len(n) >= 3 assert len(n) >= 3
let typePos = len(n) - 2 let typePos = len(n) - 2

View file

@ -1406,6 +1406,8 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
if dest.kind in {nkStrLit..nkTripleStrLit} and if dest.kind in {nkStrLit..nkTripleStrLit} and
regs[rb].kind in {rkNode}: regs[rb].kind in {rkNode}:
dest.strVal = regs[rb].node.strVal dest.strVal = regs[rb].node.strVal
elif dest.kind == nkCommentStmt and regs[rb].kind in {rkNode}:
dest.comment = regs[rb].node.strVal
else: else:
stackTrace(c, tos, pc, errFieldXNotFound, "strVal") stackTrace(c, tos, pc, errFieldXNotFound, "strVal")
of opcNNewNimNode: of opcNNewNimNode:
@ -1609,8 +1611,9 @@ proc setupMacroParam(x: PNode, typ: PType): TFullReg =
iterator genericParamsInMacroCall*(macroSym: PSym, call: PNode): (PSym, PNode) = iterator genericParamsInMacroCall*(macroSym: PSym, call: PNode): (PSym, PNode) =
let gp = macroSym.ast[genericParamsPos] let gp = macroSym.ast[genericParamsPos]
for i in 0 .. <gp.len: for i in 0 .. <gp.len:
let idx = macroSym.typ.len + i let genericParam = gp[i].sym
yield (gp[i].sym, call.sons[idx]) let posInCall = macroSym.typ.len + i
yield (genericParam, call[posInCall])
var evalMacroCounter: int var evalMacroCounter: int

View file

@ -826,7 +826,23 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
of mSubF64: genBinaryABC(c, n, dest, opcSubFloat) of mSubF64: genBinaryABC(c, n, dest, opcSubFloat)
of mMulF64: genBinaryABC(c, n, dest, opcMulFloat) of mMulF64: genBinaryABC(c, n, dest, opcMulFloat)
of mDivF64: genBinaryABC(c, n, dest, opcDivFloat) of mDivF64: genBinaryABC(c, n, dest, opcDivFloat)
of mShrI: genBinaryABCnarrowU(c, n, dest, opcShrInt) of mShrI:
# the idea here is to narrow type if needed before executing right shift
# inlined modified: genNarrowU(c, n, dest)
let t = skipTypes(n.typ, abstractVar-{tyTypeDesc})
# uint is uint64 in the VM, we we only need to mask the result for
# other unsigned types:
let tmp = c.genx(n.sons[1])
if t.kind in {tyUInt8..tyUInt32, tyInt8..tyInt32}:
c.gABC(n, opcNarrowU, tmp, TRegister(t.size*8))
# inlined modified: genBinaryABC(c, n, dest, opcShrInt)
let tmp2 = c.genx(n.sons[2])
if dest < 0: dest = c.getTemp(n.typ)
c.gABC(n, opcShrInt, dest, tmp, tmp2)
c.freeTemp(tmp)
c.freeTemp(tmp2)
of mShlI: genBinaryABCnarrowU(c, n, dest, opcShlInt) of mShlI: genBinaryABCnarrowU(c, n, dest, opcShlInt)
of mBitandI: genBinaryABCnarrowU(c, n, dest, opcBitandInt) of mBitandI: genBinaryABCnarrowU(c, n, dest, opcBitandInt)
of mBitorI: genBinaryABCnarrowU(c, n, dest, opcBitorInt) of mBitorI: genBinaryABCnarrowU(c, n, dest, opcBitorInt)

View file

@ -16,7 +16,7 @@ Advanced commands:
//check checks the project for syntax and semantic //check checks the project for syntax and semantic
Advanced options: Advanced options:
-o, --out:FILE set the output filename -o:FILE, --out:FILE set the output filename
--stdout output to stdout --stdout output to stdout
--colors:on|off turn compiler messages coloring on|off --colors:on|off turn compiler messages coloring on|off
--listFullPaths list full paths in messages --listFullPaths list full paths in messages

View file

@ -461,8 +461,8 @@ Documentation Comments
---------------------- ----------------------
Double-hash (``##``) comments in the code actually have their own format, Double-hash (``##``) comments in the code actually have their own format,
but the comments do not yet show up in the AST, which will only show that using ``strVal`` to get and set the comment text. Single-hash (``#``)
a comment exists, not what it contains. Single-hash (``#``) comments are ignored. comments are ignored.
Concrete syntax: Concrete syntax:

View file

@ -22,7 +22,7 @@ plusExpr = mulExpr (OP8 optInd mulExpr)*
mulExpr = dollarExpr (OP9 optInd dollarExpr)* mulExpr = dollarExpr (OP9 optInd dollarExpr)*
dollarExpr = primary (OP10 optInd primary)* dollarExpr = primary (OP10 optInd primary)*
symbol = '`' (KEYW|IDENT|literal|(operator|'('|')'|'['|']'|'{'|'}'|'=')+)+ '`' symbol = '`' (KEYW|IDENT|literal|(operator|'('|')'|'['|']'|'{'|'}'|'=')+)+ '`'
| IDENT | 'addr' | 'type' | IDENT | KEYW
exprColonEqExpr = expr (':'|'=' expr)? exprColonEqExpr = expr (':'|'=' expr)?
exprList = expr ^+ comma exprList = expr ^+ comma
dotExpr = expr '.' optInd symbol dotExpr = expr '.' optInd symbol
@ -81,7 +81,6 @@ paramList = '(' declColonEquals ^* (comma/semicolon) ')'
paramListArrow = paramList? ('->' optInd typeDesc)? paramListArrow = paramList? ('->' optInd typeDesc)?
paramListColon = paramList? (':' optInd typeDesc)? paramListColon = paramList? (':' optInd typeDesc)?
doBlock = 'do' paramListArrow pragmas? colcom stmt doBlock = 'do' paramListArrow pragmas? colcom stmt
doBlocks = doBlock ^* IND{=}
procExpr = 'proc' paramListColon pragmas? ('=' COMMENT? stmt)? procExpr = 'proc' paramListColon pragmas? ('=' COMMENT? stmt)?
distinct = 'distinct' optInd typeDesc distinct = 'distinct' optInd typeDesc
expr = (ifExpr expr = (ifExpr
@ -98,10 +97,11 @@ primary = typeKeyw typeDescK
typeDesc = simpleExpr typeDesc = simpleExpr
typeDefAux = simpleExpr typeDefAux = simpleExpr
| 'concept' typeClass | 'concept' typeClass
macroColon = ':' stmt? ( IND{=} 'of' exprList ':' stmt postExprBlocks = ':' stmt? ( IND{=} doBlock
| IND{=} 'elif' expr ':' stmt | IND{=} 'of' exprList ':' stmt
| IND{=} 'except' exprList ':' stmt | IND{=} 'elif' expr ':' stmt
| IND{=} 'else' ':' stmt )* | IND{=} 'except' exprList ':' stmt
| IND{=} 'else' ':' stmt )*
exprStmt = simpleExpr exprStmt = simpleExpr
(( '=' optInd expr colonBody? ) (( '=' optInd expr colonBody? )
/ ( expr ^+ comma / ( expr ^+ comma

View file

@ -1006,16 +1006,37 @@ CodegenDecl pragma
------------------ ------------------
The ``codegenDecl`` pragma can be used to directly influence Nim's code The ``codegenDecl`` pragma can be used to directly influence Nim's code
generator. It receives a format string that determines how the variable or generator. It receives a format string that determines how the variable
proc is declared in the generated code: or proc is declared in the generated code.
For variables $1 in the format string represents the type of the variable
and $2 is the name of the variable.
The following Nim code:
.. code-block:: nim .. code-block:: nim
var var
a {.codegenDecl: "$# progmem $#".}: int a {.codegenDecl: "$# progmem $#".}: int
will generate this C code:
.. code-block:: c
int progmem a
For procedures $1 is the return type of the procedure, $2 is the name of
the procedure and $3 is the parameter list.
The following nim code:
.. code-block:: nim
proc myinterrupt() {.codegenDecl: "__interrupt $# $#$#".} = proc myinterrupt() {.codegenDecl: "__interrupt $# $#$#".} =
echo "realistic interrupt handler" echo "realistic interrupt handler"
will generate this code:
.. code-block:: c
__interrupt void myinterrupt()
InjectStmt pragma InjectStmt pragma
----------------- -----------------

View file

@ -111,6 +111,105 @@ relation is extended to the types ``var``, ``ref``, ``ptr``:
.. XXX nil is a special value! .. XXX nil is a special value!
Covariance
----------
Covariance in Nim can be introduced only though pointer-like types such
as ``ptr`` and ``ref``. Sequence, Array and OpenArray types, instantiated
with pointer-like types will be considered covariant if and only if they
are also immutable. The introduction of a ``var`` modifier or additional
``ptr`` or ``ref`` indirections would result in invariant treatment of
these types.
``proc`` types are currently always invariant, but future versions of Nim
may relax this rule.
User-defined generic types may also be covariant with respect to some of
their parameters. By default, all generic params are considered invariant,
but you may choose the apply the prefix modifier ``in`` to a parameter to
make it contravariant or ``out`` to make it covariant:
.. code-block:: nim
type
AnnotatedPtr[out T] =
metadata: MyTypeInfo
p: ref T
RingBuffer[out T] =
startPos: int
data: seq[T]
Action {.importcpp: "std::function<void ('0)>".} [in T] = object
When the designated generic parameter is used to instantiate a pointer-like
type as in the case of `AnnotatedPtr` above, the resulting generic type will
also have pointer-like covariance:
.. code-block:: nim
type
GuiWidget = object of RootObj
Button = object of GuiWidget
ComboBox = object of GuiWidget
var
widgetPtr: AnnotatedPtr[GuiWidget]
buttonPtr: AnnotatedPtr[Button]
...
proc drawWidget[T](x: AnnotatedPtr[GuiWidget]) = ...
# you can call procs expecting base types by supplying a derived type
drawWidget(buttonPtr)
# and you can convert more-specific pointer types to more general ones
widgetPtr = buttonPtr
Just like with regular pointers, covariance will be enabled only for immutable
values:
.. code-block:: nim
proc makeComboBox[T](x: var AnnotatedPtr[GuiWidget]) =
x.p = new(ComboBox)
makeComboBox(buttonPtr) # Error, AnnotatedPtr[Button] cannot be modified
# to point to a ComboBox
On the other hand, in the `RingBuffer` example above, the designated generic
param is used to instantiate the non-pointer ``seq`` type, which means that
the resulting generic type will have covariance that mimics an array or
sequence (i.e. it will be covariant only when instantiated with ``ptr`` and
``ref`` types):
.. code-block:: nim
type
Base = object of RootObj
Derived = object of Base
proc consumeBaseValues(b: RingBuffer[Base]) = ...
var derivedValues: RingBuffer[Derived]
consumeBaseValues(derivedValues) # Error, Base and Derived values may differ
# in size
proc consumeBasePointers(b: RingBuffer[ptr Base]) = ...
var derivedPointers: RingBuffer[ptr Derived]
consumeBaseValues(derivedPointers) # This is legal
Please note that Nim will treat the user-defined pointer-like types as
proper alternatives to the built-in pointer types. That is, types such
as `seq[AnnotatedPtr[T]]` or `RingBuffer[AnnotatedPtr[T]]` will also be
considered covariant and you can create new pointer-like types by instantiating
other user-defined pointer-like types.
The contravariant parameters introduced with the ``in`` modifier are currently
useful only when interfacing with imported types having such semantics.
Convertible relation Convertible relation
-------------------- --------------------
A type ``a`` is **implicitly** convertible to type ``b`` iff the following A type ``a`` is **implicitly** convertible to type ``b`` iff the following
@ -119,6 +218,8 @@ algorithm returns true:
.. code-block:: nim .. code-block:: nim
# XXX range types? # XXX range types?
proc isImplicitlyConvertible(a, b: PType): bool = proc isImplicitlyConvertible(a, b: PType): bool =
if isSubtype(a, b) or isCovariant(a, b):
return true
case a.kind case a.kind
of int: result = b in {int8, int16, int32, int64, uint, uint8, uint16, of int: result = b in {int8, int16, int32, int64, uint, uint8, uint16,
uint32, uint64, float, float32, float64} uint32, uint64, float, float32, float64}

View file

@ -108,3 +108,12 @@ installation of Nimble is done with this simple script:
mvFile "nimble" & $id & "/src/nimble".toExe, "bin/nimble".toExe mvFile "nimble" & $id & "/src/nimble".toExe, "bin/nimble".toExe
You can also use the shebang ``#!/usr/bin/env nim``, as long as your filename
ends with ``.nims``:
.. code-block:: nim
#!/usr/bin/env nim
mode = ScriptMode.Silent
echo "hello world"

View file

@ -1492,20 +1492,6 @@ variables! For example:
echo badname echo badname
echo badext echo badext
Tuple unpacking **only** works in ``var`` or ``let`` blocks. The following code
won't compile:
.. code-block:: nim
import os
var
path = "usr/local/nimc.html"
dir, name, ext = ""
(dir, name, ext) = splitFile(path)
# --> Error: '(dir, name, ext)' cannot be assigned to
Reference and pointer types Reference and pointer types
--------------------------- ---------------------------

View file

@ -206,7 +206,7 @@ for any type:
echo "abc".len # is the same as echo len("abc") echo "abc".len # is the same as echo len("abc")
echo "abc".toUpper() echo "abc".toUpper()
echo {'a', 'b', 'c'}.card echo({'a', 'b', 'c'}.card)
stdout.writeLine("Hallo") # the same as writeLine(stdout, "Hallo") stdout.writeLine("Hallo") # the same as writeLine(stdout, "Hallo")
(Another way to look at the method call syntax is that it provides the missing (Another way to look at the method call syntax is that it provides the missing

View file

@ -152,6 +152,10 @@ proc tryExec(cmd: string): bool =
proc safeRemove(filename: string) = proc safeRemove(filename: string) =
if existsFile(filename): removeFile(filename) if existsFile(filename): removeFile(filename)
proc overwriteFile(source, dest: string) =
safeRemove(dest)
moveFile(source, dest)
proc copyExe(source, dest: string) = proc copyExe(source, dest: string) =
safeRemove(dest) safeRemove(dest)
copyFile(dest=dest, source=source) copyFile(dest=dest, source=source)
@ -388,48 +392,25 @@ proc clean(args: string) =
# -------------- builds a release --------------------------------------------- # -------------- builds a release ---------------------------------------------
proc patchConfig(lookFor, replaceBy: string) =
const
cfgFile = "config/nim.cfg"
try:
let cfg = readFile(cfgFile)
let newCfg = cfg.replace(lookFor, replaceBy)
if newCfg == cfg:
echo "Could not patch 'config/nim.cfg' [Error]"
echo "Reason: patch substring not found:"
echo lookFor
else:
writeFile(cfgFile, newCfg)
except IOError:
quit "Could not access 'config/nim.cfg' [Error]"
proc winReleaseArch(arch: string) = proc winReleaseArch(arch: string) =
doAssert arch in ["32", "64"] doAssert arch in ["32", "64"]
let cpu = if arch == "32": "i386" else: "amd64" let cpu = if arch == "32": "i386" else: "amd64"
template withMingw(path, body) = template withMingw(path, body) =
const orig = """#gcc.path = r"$nim\dist\mingw\bin"""" let prevPath = getEnv("PATH")
let replacePattern = """gcc.path = r"..\mingw$1\bin" # winrelease""" % arch putEnv("PATH", path & PathSep & prevPath)
patchConfig(orig, replacePattern)
try: try:
body body
finally: finally:
patchConfig(replacePattern, orig) putEnv("PATH", prevPath)
withMingw r"..\mingw" & arch & r"\bin": withMingw r"..\mingw" & arch & r"\bin":
# Rebuilding koch is necessary because it uses its pointer size to # Rebuilding koch is necessary because it uses its pointer size to
# determine which mingw link to put in the NSIS installer. # determine which mingw link to put in the NSIS installer.
nimexec "c --out:koch_temp --cpu:$# koch" % cpu nimexec "c --out:koch_temp --cpu:$# koch" % cpu
exec "koch_temp boot -d:release --cpu:$#" % cpu exec "koch_temp boot -d:release --cpu:$#" % cpu
exec "koch_temp nsis -d:release"
exec "koch_temp zip -d:release" exec "koch_temp zip -d:release"
overwriteFile r"build\nim-$#.zip" % VersionAsString,
when false:
# we now disable the NSIS installer as it cannot download from https
# and is broken in so many different ways it's not funny anymore:
moveFile r"build\nim_$#.exe" % VersionAsString,
r"web\upload\download\nim-$#_x$#.exe" % [VersionAsString, arch]
moveFile r"build\nim-$#.zip" % VersionAsString,
r"web\upload\download\nim-$#_x$#.zip" % [VersionAsString, arch] r"web\upload\download\nim-$#_x$#.zip" % [VersionAsString, arch]
proc winRelease() = proc winRelease() =
@ -438,8 +419,8 @@ proc winRelease() =
web(gaCode) web(gaCode)
withDir "web/upload/" & VersionAsString: withDir "web/upload/" & VersionAsString:
exec "7z a -tzip docs-$#.zip *.html" % VersionAsString exec "7z a -tzip docs-$#.zip *.html" % VersionAsString
moveFile "web/upload/$1/docs-$1.zip" % VersionAsString, overwriteFile "web/upload/$1/docs-$1.zip" % VersionAsString,
"web/upload/download/docs-$1.zip" % VersionAsString "web/upload/download/docs-$1.zip" % VersionAsString
when true: when true:
csource("-d:release") csource("-d:release")
when true: when true:
@ -489,6 +470,17 @@ proc temp(args: string) =
copyExe(output, finalDest) copyExe(output, finalDest)
if programArgs.len > 0: exec(finalDest & " " & programArgs) if programArgs.len > 0: exec(finalDest & " " & programArgs)
proc xtemp(cmd: string) =
let d = getAppDir()
copyExe(d / "bin" / "nim".exe, d / "bin" / "nim_backup".exe)
try:
withDir(d):
temp"-d:debug"
copyExe(d / "bin" / "nim_temp".exe, d / "bin" / "nim".exe)
exec(cmd)
finally:
copyExe(d / "bin" / "nim_backup".exe, d / "bin" / "nim".exe)
proc pushCsources() = proc pushCsources() =
if not dirExists("../csources/.git"): if not dirExists("../csources/.git"):
quit "[Error] no csources git repository found" quit "[Error] no csources git repository found"
@ -564,6 +556,7 @@ of cmdArgument:
of "testinstall": testUnixInstall() of "testinstall": testUnixInstall()
of "test", "tests": tests(op.cmdLineRest) of "test", "tests": tests(op.cmdLineRest)
of "temp": temp(op.cmdLineRest) of "temp": temp(op.cmdLineRest)
of "xtemp": xtemp(op.cmdLineRest)
of "winrelease": winRelease() of "winrelease": winRelease()
of "wintools": bundleWinTools() of "wintools": bundleWinTools()
of "nimble": buildNimble(existsDir(".git")) of "nimble": buildNimble(existsDir(".git"))

View file

@ -225,7 +225,13 @@ proc `ident=`*(n: NimNode, val: NimIdent) {.magic: "NSetIdent", noSideEffect.}
proc `strVal=`*(n: NimNode, val: string) {.magic: "NSetStrVal", noSideEffect.} proc `strVal=`*(n: NimNode, val: string) {.magic: "NSetStrVal", noSideEffect.}
proc newNimNode*(kind: NimNodeKind, proc newNimNode*(kind: NimNodeKind,
n: NimNode=nil): NimNode {.magic: "NNewNimNode", noSideEffect.} lineInfoFrom: NimNode=nil): NimNode
{.magic: "NNewNimNode", noSideEffect.}
## Creates a new AST node of the specified kind.
##
## The ``lineInfoFrom`` parameter is used for line information when the
## produced code crashes. You should ensure that it is set to a node that
## you are transforming.
proc copyNimNode*(n: NimNode): NimNode {.magic: "NCopyNimNode", noSideEffect.} proc copyNimNode*(n: NimNode): NimNode {.magic: "NCopyNimNode", noSideEffect.}
proc copyNimTree*(n: NimNode): NimNode {.magic: "NCopyNimTree", noSideEffect.} proc copyNimTree*(n: NimNode): NimNode {.magic: "NCopyNimTree", noSideEffect.}
@ -249,6 +255,11 @@ proc newStrLitNode*(s: string): NimNode {.compileTime, noSideEffect.} =
result = newNimNode(nnkStrLit) result = newNimNode(nnkStrLit)
result.strVal = s result.strVal = s
proc newCommentStmtNode*(s: string): NimNode {.compileTime, noSideEffect.} =
## creates a comment statement node
result = newNimNode(nnkCommentStmt)
result.strVal = s
proc newIntLitNode*(i: BiggestInt): NimNode {.compileTime.} = proc newIntLitNode*(i: BiggestInt): NimNode {.compileTime.} =
## creates a int literal node from `i` ## creates a int literal node from `i`
result = newNimNode(nnkIntLit) result = newNimNode(nnkIntLit)
@ -269,6 +280,7 @@ proc newIdentNode*(i: string): NimNode {.compileTime.} =
result = newNimNode(nnkIdent) result = newNimNode(nnkIdent)
result.ident = !i result.ident = !i
type type
BindSymRule* = enum ## specifies how ``bindSym`` behaves BindSymRule* = enum ## specifies how ``bindSym`` behaves
brClosed, ## only the symbols in current scope are bound brClosed, ## only the symbols in current scope are bound
@ -426,20 +438,105 @@ proc newLit*(c: char): NimNode {.compileTime.} =
result = newNimNode(nnkCharLit) result = newNimNode(nnkCharLit)
result.intVal = ord(c) result.intVal = ord(c)
proc newLit*(i: BiggestInt): NimNode {.compileTime.} =
proc newLit*(i: int): NimNode {.compileTime.} =
## produces a new integer literal node. ## produces a new integer literal node.
result = newNimNode(nnkIntLit) result = newNimNode(nnkIntLit)
result.intVal = i result.intVal = i
proc newLit*(i: int8): NimNode {.compileTime.} =
## produces a new integer literal node.
result = newNimNode(nnkInt8Lit)
result.intVal = i
proc newLit*(i: int16): NimNode {.compileTime.} =
## produces a new integer literal node.
result = newNimNode(nnkInt16Lit)
result.intVal = i
proc newLit*(i: int32): NimNode {.compileTime.} =
## produces a new integer literal node.
result = newNimNode(nnkInt32Lit)
result.intVal = i
proc newLit*(i: int64): NimNode {.compileTime.} =
## produces a new integer literal node.
result = newNimNode(nnkInt64Lit)
result.intVal = i
proc newLit*(i: uint): NimNode {.compileTime.} =
## produces a new unsigned integer literal node.
result = newNimNode(nnkUIntLit)
result.intVal = BiggestInt(i)
proc newLit*(i: uint8): NimNode {.compileTime.} =
## produces a new unsigned integer literal node.
result = newNimNode(nnkUInt8Lit)
result.intVal = BiggestInt(i)
proc newLit*(i: uint16): NimNode {.compileTime.} =
## produces a new unsigned integer literal node.
result = newNimNode(nnkUInt16Lit)
result.intVal = BiggestInt(i)
proc newLit*(i: uint32): NimNode {.compileTime.} =
## produces a new unsigned integer literal node.
result = newNimNode(nnkUInt32Lit)
result.intVal = BiggestInt(i)
proc newLit*(i: uint64): NimNode {.compileTime.} =
## produces a new unsigned integer literal node.
result = newNimNode(nnkUInt64Lit)
result.intVal = BiggestInt(i)
proc newLit*(b: bool): NimNode {.compileTime.} = proc newLit*(b: bool): NimNode {.compileTime.} =
## produces a new boolean literal node. ## produces a new boolean literal node.
result = if b: bindSym"true" else: bindSym"false" result = if b: bindSym"true" else: bindSym"false"
proc newLit*(f: BiggestFloat): NimNode {.compileTime.} = when false:
# the float type is not really a distinct type as described in https://github.com/nim-lang/Nim/issues/5875
proc newLit*(f: float): NimNode {.compileTime.} =
## produces a new float literal node.
result = newNimNode(nnkFloatLit)
result.floatVal = f
proc newLit*(f: float32): NimNode {.compileTime.} =
## produces a new float literal node. ## produces a new float literal node.
result = newNimNode(nnkFloatLit) result = newNimNode(nnkFloat32Lit)
result.floatVal = f result.floatVal = f
proc newLit*(f: float64): NimNode {.compileTime.} =
## produces a new float literal node.
result = newNimNode(nnkFloat64Lit)
result.floatVal = f
when compiles(float128):
proc newLit*(f: float128): NimNode {.compileTime.} =
## produces a new float literal node.
result = newNimNode(nnkFloat128Lit)
result.floatVal = f
proc newLit*(arg: object): NimNode {.compileTime.} =
result = nnkObjConstr.newTree(arg.type.getTypeInst[1])
for a, b in arg.fieldPairs:
result.add nnkExprColonExpr.newTree( newIdentNode(a), newLit(b) )
proc newLit*[N,T](arg: array[N,T]): NimNode {.compileTime.} =
result = nnkBracket.newTree
for x in arg:
result.add newLit(x)
proc newLit*[T](arg: seq[T]): NimNode {.compileTime.} =
result = nnkBracket.newTree
for x in arg:
result.add newLit(x)
result = nnkPrefix.newTree(bindSym"@", result)
proc newLit*(arg: tuple): NimNode {.compileTime.} =
result = nnkPar.newTree
for a,b in arg.fieldPairs:
result.add nnkExprColonExpr.newTree( newIdentNode(a), newLit(b) )
proc newLit*(s: string): NimNode {.compileTime.} = proc newLit*(s: string): NimNode {.compileTime.} =
## produces a new string literal node. ## produces a new string literal node.
result = newNimNode(nnkStrLit) result = newNimNode(nnkStrLit)
@ -744,6 +841,8 @@ proc `$`*(node: NimNode): string {.compileTime.} =
result = $node[0] result = $node[0]
of nnkAccQuoted: of nnkAccQuoted:
result = $node[0] result = $node[0]
of nnkCommentStmt:
result = node.strVal
else: else:
badNodeKind node.kind, "$" badNodeKind node.kind, "$"

View file

@ -79,6 +79,9 @@ const
DT_SOCK* = 12 ## UNIX domain socket. DT_SOCK* = 12 ## UNIX domain socket.
DT_WHT* = 14 DT_WHT* = 14
# Special types
type Sighandler = proc (a: cint) {.noconv.}
# Platform specific stuff # Platform specific stuff
when defined(linux) and defined(amd64): when defined(linux) and defined(amd64):
@ -86,6 +89,9 @@ when defined(linux) and defined(amd64):
else: else:
include posix_other include posix_other
# There used to be this name in posix.nim a long time ago, not sure why!
{.deprecated: [cSIG_HOLD: SIG_HOLD].}
when not defined(macosx): when not defined(macosx):
proc st_atime*(s: Stat): Time {.inline.} = proc st_atime*(s: Stat): Time {.inline.} =
## Second-granularity time of last access ## Second-granularity time of last access
@ -659,7 +665,7 @@ proc sighold*(a1: cint): cint {.importc, header: "<signal.h>".}
proc sigignore*(a1: cint): cint {.importc, header: "<signal.h>".} proc sigignore*(a1: cint): cint {.importc, header: "<signal.h>".}
proc siginterrupt*(a1, a2: cint): cint {.importc, header: "<signal.h>".} proc siginterrupt*(a1, a2: cint): cint {.importc, header: "<signal.h>".}
proc sigismember*(a1: var Sigset, a2: cint): cint {.importc, header: "<signal.h>".} proc sigismember*(a1: var Sigset, a2: cint): cint {.importc, header: "<signal.h>".}
proc signal*(a1: cint, a2: proc (x: cint) {.noconv.}) {. proc signal*(a1: cint, a2: Sighandler) {.
importc, header: "<signal.h>".} importc, header: "<signal.h>".}
proc sigpause*(a1: cint): cint {.importc, header: "<signal.h>".} proc sigpause*(a1: cint): cint {.importc, header: "<signal.h>".}
proc sigpending*(a1: var Sigset): cint {.importc, header: "<signal.h>".} proc sigpending*(a1: var Sigset): cint {.importc, header: "<signal.h>".}

View file

@ -36,6 +36,9 @@ type
{.deprecated: [TSocketHandle: SocketHandle].} {.deprecated: [TSocketHandle: SocketHandle].}
# not detected by detect.nim, guarded by #ifdef __USE_UNIX98 in glibc
const SIG_HOLD* = cast[SigHandler](2)
type type
Timespec* {.importc: "struct timespec", Timespec* {.importc: "struct timespec",
header: "<time.h>", final, pure.} = object ## struct timespec header: "<time.h>", final, pure.} = object ## struct timespec

View file

@ -399,6 +399,9 @@ const SS_ONSTACK* = cint(1)
const SS_DISABLE* = cint(2) const SS_DISABLE* = cint(2)
const MINSIGSTKSZ* = cint(2048) const MINSIGSTKSZ* = cint(2048)
const SIGSTKSZ* = cint(8192) const SIGSTKSZ* = cint(8192)
const SIG_DFL* = cast[Sighandler](0)
const SIG_ERR* = cast[Sighandler](-1)
const SIG_IGN* = cast[Sighandler](1)
# <sys/ipc.h> # <sys/ipc.h>
const IPC_CREAT* = cint(512) const IPC_CREAT* = cint(512)

View file

@ -414,6 +414,10 @@ var SS_ONSTACK* {.importc: "SS_ONSTACK", header: "<signal.h>".}: cint
var SS_DISABLE* {.importc: "SS_DISABLE", header: "<signal.h>".}: cint var SS_DISABLE* {.importc: "SS_DISABLE", header: "<signal.h>".}: cint
var MINSIGSTKSZ* {.importc: "MINSIGSTKSZ", header: "<signal.h>".}: cint var MINSIGSTKSZ* {.importc: "MINSIGSTKSZ", header: "<signal.h>".}: cint
var SIGSTKSZ* {.importc: "SIGSTKSZ", header: "<signal.h>".}: cint var SIGSTKSZ* {.importc: "SIGSTKSZ", header: "<signal.h>".}: cint
var SIG_HOLD* {.importc: "SIG_HOLD", header: "<signal.h>".}: Sighandler
var SIG_DFL* {.importc: "SIG_DFL", header: "<signal.h>".}: Sighandler
var SIG_ERR* {.importc: "SIG_ERR", header: "<signal.h>".}: Sighandler
var SIG_IGN* {.importc: "SIG_IGN", header: "<signal.h>".}: Sighandler
# <sys/ipc.h> # <sys/ipc.h>
var IPC_CREAT* {.importc: "IPC_CREAT", header: "<sys/ipc.h>".}: cint var IPC_CREAT* {.importc: "IPC_CREAT", header: "<sys/ipc.h>".}: cint

View file

@ -163,8 +163,8 @@ include includes/asyncfutures
type type
PDispatcherBase = ref object of RootRef PDispatcherBase = ref object of RootRef
timers: HeapQueue[tuple[finishAt: float, fut: Future[void]]] timers*: HeapQueue[tuple[finishAt: float, fut: Future[void]]]
callbacks: Deque[proc ()] callbacks*: Deque[proc ()]
proc processTimers(p: PDispatcherBase) {.inline.} = proc processTimers(p: PDispatcherBase) {.inline.} =
#Process just part if timers at a step #Process just part if timers at a step

View file

@ -127,7 +127,7 @@ proc parseProtocol(protocol: string): tuple[orig: string, major, minor: int] =
i.inc protocol.parseInt(result.minor, i) i.inc protocol.parseInt(result.minor, i)
proc sendStatus(client: AsyncSocket, status: string): Future[void] = proc sendStatus(client: AsyncSocket, status: string): Future[void] =
client.send("HTTP/1.1 " & status & "\c\L") client.send("HTTP/1.1 " & status & "\c\L\c\L")
proc processClient(client: AsyncSocket, address: string, proc processClient(client: AsyncSocket, address: string,
callback: proc (request: Request): callback: proc (request: Request):

View file

@ -301,13 +301,13 @@ proc verifyReturnType(typeName: string) {.compileTime.} =
proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} = proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} =
## This macro transforms a single procedure into a closure iterator. ## This macro transforms a single procedure into a closure iterator.
## The ``async`` macro supports a stmtList holding multiple async procedures. ## The ``async`` macro supports a stmtList holding multiple async procedures.
if prc.kind notin {nnkProcDef, nnkLambda, nnkMethodDef}: if prc.kind notin {nnkProcDef, nnkLambda, nnkMethodDef, nnkDo}:
error("Cannot transform this node kind into an async proc." & error("Cannot transform this node kind into an async proc." &
" proc/method definition or lambda node expected.") " proc/method definition or lambda node expected.")
hint("Processing " & prc[0].getName & " as an async proc.") let prcName = prc.name.getName
let returnType = prc[3][0] let returnType = prc.params[0]
var baseType: NimNode var baseType: NimNode
# Verify that the return type is a Future[T] # Verify that the return type is a Future[T]
if returnType.kind == nnkBracketExpr: if returnType.kind == nnkBracketExpr:
@ -326,9 +326,9 @@ proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} =
let subtypeIsVoid = returnType.kind == nnkEmpty or let subtypeIsVoid = returnType.kind == nnkEmpty or
(baseType.kind == nnkIdent and returnType[1].ident == !"void") (baseType.kind == nnkIdent and returnType[1].ident == !"void")
let futureVarIdents = getFutureVarIdents(prc[3]) let futureVarIdents = getFutureVarIdents(prc.params)
var outerProcBody = newNimNode(nnkStmtList, prc[6]) var outerProcBody = newNimNode(nnkStmtList, prc.body)
# -> var retFuture = newFuture[T]() # -> var retFuture = newFuture[T]()
var retFutureSym = genSym(nskVar, "retFuture") var retFutureSym = genSym(nskVar, "retFuture")
@ -338,10 +338,10 @@ proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} =
outerProcBody.add( outerProcBody.add(
newVarStmt(retFutureSym, newVarStmt(retFutureSym,
newCall( newCall(
newNimNode(nnkBracketExpr, prc[6]).add( newNimNode(nnkBracketExpr, prc.body).add(
newIdentNode(!"newFuture"), # TODO: Strange bug here? Remove the `!`. newIdentNode(!"newFuture"), # TODO: Strange bug here? Remove the `!`.
subRetType), subRetType),
newLit(prc[0].getName)))) # Get type from return type of this proc newLit(prcName)))) # Get type from return type of this proc
# -> iterator nameIter(): FutureBase {.closure.} = # -> iterator nameIter(): FutureBase {.closure.} =
# -> {.push warning[resultshadowed]: off.} # -> {.push warning[resultshadowed]: off.}
@ -349,8 +349,8 @@ proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} =
# -> {.pop.} # -> {.pop.}
# -> <proc_body> # -> <proc_body>
# -> complete(retFuture, result) # -> complete(retFuture, result)
var iteratorNameSym = genSym(nskIterator, $prc[0].getName & "Iter") var iteratorNameSym = genSym(nskIterator, $prcName & "Iter")
var procBody = prc[6].processBody(retFutureSym, subtypeIsVoid, var procBody = prc.body.processBody(retFutureSym, subtypeIsVoid,
futureVarIdents, nil) futureVarIdents, nil)
# don't do anything with forward bodies (empty) # don't do anything with forward bodies (empty)
if procBody.kind != nnkEmpty: if procBody.kind != nnkEmpty:
@ -362,7 +362,7 @@ proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} =
newIdentNode("warning"), newIdentNode("resultshadowed")), newIdentNode("warning"), newIdentNode("resultshadowed")),
newIdentNode("off")))) # -> {.push warning[resultshadowed]: off.} newIdentNode("off")))) # -> {.push warning[resultshadowed]: off.}
procBody.insert(1, newNimNode(nnkVarSection, prc[6]).add( procBody.insert(1, newNimNode(nnkVarSection, prc.body).add(
newIdentDefs(newIdentNode("result"), baseType))) # -> var result: T newIdentDefs(newIdentNode("result"), baseType))) # -> var result: T
procBody.insert(2, newNimNode(nnkPragma).add( procBody.insert(2, newNimNode(nnkPragma).add(
@ -377,35 +377,32 @@ proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} =
var closureIterator = newProc(iteratorNameSym, [newIdentNode("FutureBase")], var closureIterator = newProc(iteratorNameSym, [newIdentNode("FutureBase")],
procBody, nnkIteratorDef) procBody, nnkIteratorDef)
closureIterator[4] = newNimNode(nnkPragma, prc[6]).add(newIdentNode("closure")) closureIterator.pragma = newNimNode(nnkPragma, lineInfoFrom=prc.body)
closureIterator.addPragma(newIdentNode("closure"))
closureIterator.addPragma(newIdentNode("gcsafe"))
outerProcBody.add(closureIterator) outerProcBody.add(closureIterator)
# -> createCb(retFuture) # -> createCb(retFuture)
#var cbName = newIdentNode("cb") #var cbName = newIdentNode("cb")
var procCb = getAst createCb(retFutureSym, iteratorNameSym, var procCb = getAst createCb(retFutureSym, iteratorNameSym,
newStrLitNode(prc[0].getName), newStrLitNode(prcName),
createFutureVarCompletions(futureVarIdents, nil)) createFutureVarCompletions(futureVarIdents, nil))
outerProcBody.add procCb outerProcBody.add procCb
# -> return retFuture # -> return retFuture
outerProcBody.add newNimNode(nnkReturnStmt, prc[6][prc[6].len-1]).add(retFutureSym) outerProcBody.add newNimNode(nnkReturnStmt, prc.body[^1]).add(retFutureSym)
result = prc result = prc
# Remove the 'async' pragma.
for i in 0 .. <result[4].len:
if result[4][i].kind == nnkIdent and result[4][i].ident == !"async":
result[4].del(i)
result[4] = newEmptyNode()
if subtypeIsVoid: if subtypeIsVoid:
# Add discardable pragma. # Add discardable pragma.
if returnType.kind == nnkEmpty: if returnType.kind == nnkEmpty:
# Add Future[void] # Add Future[void]
result[3][0] = parseExpr("Future[void]") result.params[0] = parseExpr("Future[void]")
if procBody.kind != nnkEmpty: if procBody.kind != nnkEmpty:
result[6] = outerProcBody result.body = outerProcBody
#echo(treeRepr(result)) #echo(treeRepr(result))
#if prc[0].getName == "recvLineInto": #if prcName == "recvLineInto":
# echo(toStrLit(result)) # echo(toStrLit(result))
macro async*(prc: untyped): untyped = macro async*(prc: untyped): untyped =
@ -529,8 +526,6 @@ macro multisync*(prc: untyped): untyped =
## ##
## The generated async procedures use the ``async`` macro, whereas the ## The generated async procedures use the ``async`` macro, whereas the
## generated synchronous procedures simply strip off the ``await`` calls. ## generated synchronous procedures simply strip off the ``await`` calls.
hint("Processing " & prc[0].getName & " as a multisync proc.")
let (sync, asyncPrc) = splitProc(prc) let (sync, asyncPrc) = splitProc(prc)
result = newStmtList() result = newStmtList()
result.add(asyncSingleProc(asyncPrc)) result.add(asyncSingleProc(asyncPrc))

View file

@ -533,15 +533,13 @@ proc recvLineInto*(socket: AsyncSocket, resString: FutureVar[string],
else: else:
var c = "" var c = ""
while true: while true:
let recvFut = recv(socket, 1, flags) c = await recv(socket, 1, flags)
c = recvFut.read()
if c.len == 0: if c.len == 0:
resString.mget.setLen(0) resString.mget.setLen(0)
resString.complete() resString.complete()
return return
if c == "\r": if c == "\r":
let recvFut = recv(socket, 1, flags) # Skip \L c = await recv(socket, 1, flags) # Skip \L
c = recvFut.read()
assert c == "\L" assert c == "\L"
addNLIfEmpty() addNLIfEmpty()
resString.complete() resString.complete()

View file

@ -590,8 +590,11 @@ proc enlarge[A, B](t: var OrderedTable[A, B]) =
swap(t.data, n) swap(t.data, n)
while h >= 0: while h >= 0:
var nxt = n[h].next var nxt = n[h].next
if isFilled(n[h].hcode): let eh = n[h].hcode
var j = -1 - rawGetKnownHC(t, n[h].key, n[h].hcode) if isFilled(eh):
var j: Hash = eh and maxHash(t)
while isFilled(t.data[j].hcode):
j = nextTry(j, maxHash(t))
rawInsert(t, t.data, n[h].key, n[h].val, n[h].hcode, j) rawInsert(t, t.data, n[h].key, n[h].val, n[h].hcode, j)
h = nxt h = nxt

View file

@ -69,4 +69,4 @@ proc countProcessors*(): int {.rtl, extern: "ncpi$1".} =
result = affinitySpaceTotal().int result = affinitySpaceTotal().int
else: else:
result = sysconf(SC_NPROCESSORS_ONLN) result = sysconf(SC_NPROCESSORS_ONLN)
if result <= 0: result = 1 if result <= 0: result = 0

View file

@ -158,7 +158,7 @@ proc detectOsImpl(d: Distribution): bool =
of Distribution.ArchLinux: of Distribution.ArchLinux:
result = "arch" in toLowerAscii(uname()) result = "arch" in toLowerAscii(uname())
of Distribution.OpenSUSE: of Distribution.OpenSUSE:
result = "suse" in toLowerAscii(uname()) result = "suse" in toLowerAscii(uname()) or "suse" in toLowerAscii(release())
of Distribution.GoboLinux: of Distribution.GoboLinux:
result = "-Gobo " in uname() result = "-Gobo " in uname()
of Distribution.OpenMandriva: of Distribution.OpenMandriva:

View file

@ -139,9 +139,14 @@ proc hash*(x: cstring): Hash =
## efficient hashing of null-terminated strings ## efficient hashing of null-terminated strings
var h: Hash = 0 var h: Hash = 0
var i = 0 var i = 0
while x[i] != 0.char: when defined(js):
h = h !& ord(x[i]) while i < x.len:
inc i h = h !& ord(x[i])
inc i
else:
while x[i] != 0.char:
h = h !& ord(x[i])
inc i
result = !$h result = !$h
proc hash*(sBuf: string, sPos, ePos: int): Hash = proc hash*(sBuf: string, sPos, ePos: int): Hash =

View file

@ -62,7 +62,8 @@
## let body = %*{ ## let body = %*{
## "data": "some text" ## "data": "some text"
## } ## }
## echo client.request("http://some.api", httpMethod = HttpPost, body = $body) ## let response = client.request("http://some.api", httpMethod = HttpPost, body = $body)
## echo response.status
## ##
## Progress reporting ## Progress reporting
## ================== ## ==================

View file

@ -3,7 +3,7 @@ template newAsyncNativeSocketImpl(domain, sockType, protocol) =
if handle == osInvalidSocket: if handle == osInvalidSocket:
raiseOSError(osLastError()) raiseOSError(osLastError())
handle.setBlocking(false) handle.setBlocking(false)
when defined(macosx): when defined(macosx) and not defined(nimdoc):
handle.setSockOptInt(SOL_SOCKET, SO_NOSIGPIPE, 1) handle.setSockOptInt(SOL_SOCKET, SO_NOSIGPIPE, 1)
result = handle.AsyncFD result = handle.AsyncFD
register(result) register(result)

View file

@ -1001,7 +1001,7 @@ proc escapeJson*(s: string; result: var string) =
result.add("\"") result.add("\"")
for x in runes(s): for x in runes(s):
var r = int(x) var r = int(x)
if r >= 32 and r <= 127: if r >= 32 and r <= 126:
var c = chr(r) var c = chr(r)
case c case c
of '"': result.add("\\\"") of '"': result.add("\\\"")
@ -1608,7 +1608,7 @@ proc createConstructor(typeSym, jsonNode: NimNode): NimNode =
result = processType(newIdentNode(typeName), obj, jsonNode, true) result = processType(newIdentNode(typeName), obj, jsonNode, true)
of "seq": of "seq":
let seqT = typeSym[1] let seqT = typeSym[1]
let forLoopI = newIdentNode("i") let forLoopI = genSym(nskForVar, "i")
let indexerNode = createJsonIndexer(jsonNode, forLoopI) let indexerNode = createJsonIndexer(jsonNode, forLoopI)
let constructorNode = createConstructor(seqT, indexerNode) let constructorNode = createConstructor(seqT, indexerNode)
@ -1616,12 +1616,25 @@ proc createConstructor(typeSym, jsonNode: NimNode): NimNode =
result = quote do: result = quote do:
( (
var list: `typeSym` = @[]; var list: `typeSym` = @[];
# if `jsonNode`.kind != JArray: verifyJsonKind(`jsonNode`, {JArray}, astToStr(`jsonNode`));
# # TODO: Improve error message.
# raise newException(ValueError, "Expected a list")
for `forLoopI` in 0 .. <`jsonNode`.len: list.add(`constructorNode`); for `forLoopI` in 0 .. <`jsonNode`.len: list.add(`constructorNode`);
list list
) )
of "array":
let arrayT = typeSym[2]
let forLoopI = genSym(nskForVar, "i")
let indexerNode = createJsonIndexer(jsonNode, forLoopI)
let constructorNode = createConstructor(arrayT, indexerNode)
# Create a statement expression containing a for loop.
result = quote do:
(
var list: `typeSym`;
verifyJsonKind(`jsonNode`, {JArray}, astToStr(`jsonNode`));
for `forLoopI` in 0 .. <`jsonNode`.len: list[`forLoopI`] =`constructorNode`;
list
)
else: else:
# Generic type. # Generic type.
let obj = getType(typeSym) let obj = getType(typeSym)

View file

@ -235,7 +235,7 @@ when not defined(JS):
x = x and (not (1'u64 shl (64'u64-12'u64-e) - 1'u64)) x = x and (not (1'u64 shl (64'u64-12'u64-e) - 1'u64))
result = cast[float64](x) result = cast[float64](x)
proc truncImpl(f: float32): float32 = proc truncImpl(f: float32): float32 =
const const
mask : uint32 = 0xFF mask : uint32 = 0xFF
@ -255,7 +255,7 @@ when not defined(JS):
x = x and (not (1'u32 shl (32'u32-9'u32-e) - 1'u32)) x = x and (not (1'u32 shl (32'u32-9'u32-e) - 1'u32))
result = cast[float32](x) result = cast[float32](x)
proc trunc*(x: float64): float64 = proc trunc*(x: float64): float64 =
if classify(x) in {fcZero, fcNegZero, fcNan, fcInf, fcNegInf}: return x if classify(x) in {fcZero, fcNegZero, fcNan, fcInf, fcNegInf}: return x
result = truncImpl(x) result = truncImpl(x)
@ -395,6 +395,12 @@ proc radToDeg*[T: float32|float64](d: T): T {.inline.} =
## Convert from radians to degrees ## Convert from radians to degrees
result = T(d) / RadPerDeg result = T(d) / RadPerDeg
proc sgn*[T: SomeNumber](x: T): int {.inline.} =
## Sign function. Returns -1 for negative numbers and `NegInf`, 1 for
## positive numbers and `Inf`, and 0 for positive zero, negative zero and
## `NaN`.
ord(T(0) < x) - ord(x < T(0))
proc `mod`*[T: float32|float64](x, y: T): T = proc `mod`*[T: float32|float64](x, y: T): T =
## Computes the modulo operation for float operators. Equivalent ## Computes the modulo operation for float operators. Equivalent
## to ``x - y * floor(x/y)``. Note that the remainder will always ## to ``x - y * floor(x/y)``. Note that the remainder will always
@ -407,10 +413,13 @@ proc `mod`*[T: float32|float64](x, y: T): T =
{.pop.} {.pop.}
{.pop.} {.pop.}
proc `^`*[T](x, y: T): T = proc `^`*[T](x: T, y: Natural): T =
## Computes ``x`` to the power ``y`. ``x`` must be non-negative, use ## Computes ``x`` to the power ``y`. ``x`` must be non-negative, use
## `pow <#pow,float,float>` for negative exponents. ## `pow <#pow,float,float>` for negative exponents.
assert y >= T(0) when compiles(y >= T(0)):
assert y >= T(0)
else:
assert T(y) >= T(0)
var (x, y) = (x, y) var (x, y) = (x, y)
result = 1 result = 1
@ -447,6 +456,7 @@ when isMainModule and not defined(JS):
assert(lgamma(1.0) == 0.0) # ln(1.0) == 0.0 assert(lgamma(1.0) == 0.0) # ln(1.0) == 0.0
assert(erf(6.0) > erf(5.0)) assert(erf(6.0) > erf(5.0))
assert(erfc(6.0) < erfc(5.0)) assert(erfc(6.0) < erfc(5.0))
when isMainModule: when isMainModule:
# Function for approximate comparison of floats # Function for approximate comparison of floats
proc `==~`(x, y: float): bool = (abs(x-y) < 1e-9) proc `==~`(x, y: float): bool = (abs(x-y) < 1e-9)
@ -509,3 +519,21 @@ when isMainModule:
doAssert(classify(trunc(-1e1000000'f32)) == fcNegInf) doAssert(classify(trunc(-1e1000000'f32)) == fcNegInf)
doAssert(classify(trunc(f_nan.float32)) == fcNan) doAssert(classify(trunc(f_nan.float32)) == fcNan)
doAssert(classify(trunc(0.0'f32)) == fcZero) doAssert(classify(trunc(0.0'f32)) == fcZero)
block: # sgn() tests
assert sgn(1'i8) == 1
assert sgn(1'i16) == 1
assert sgn(1'i32) == 1
assert sgn(1'i64) == 1
assert sgn(1'u8) == 1
assert sgn(1'u16) == 1
assert sgn(1'u32) == 1
assert sgn(1'u64) == 1
assert sgn(-12342.8844'f32) == -1
assert sgn(123.9834'f64) == 1
assert sgn(0'i32) == 0
assert sgn(0'f32) == 0
assert sgn(NegInf) == -1
assert sgn(Inf) == 1
assert sgn(NaN) == 0

View file

@ -666,29 +666,38 @@ proc removeFile*(file: string) {.rtl, extern: "nos$1", tags: [WriteDirEffect].}
else: else:
raiseOSError(osLastError(), $strerror(errno)) raiseOSError(osLastError(), $strerror(errno))
proc moveFile*(source, dest: string) {.rtl, extern: "nos$1", proc tryMoveFSObject(source, dest: string): bool =
tags: [ReadIOEffect, WriteIOEffect].} = ## Moves a file or directory from `source` to `dest`. Returns false in case
## Moves a file from `source` to `dest`. If this fails, `OSError` is raised. ## of `EXDEV` error. In case of other errors `OSError` is raised. Returns
## true in case of success.
when defined(Windows): when defined(Windows):
when useWinUnicode: when useWinUnicode:
let s = newWideCString(source) let s = newWideCString(source)
let d = newWideCString(dest) let d = newWideCString(dest)
if moveFileW(s, d) == 0'i32: raiseOSError(osLastError()) if moveFileExW(s, d, MOVEFILE_COPY_ALLOWED) == 0'i32: raiseOSError(osLastError())
else: else:
if moveFileA(source, dest) == 0'i32: raiseOSError(osLastError()) if moveFileExA(source, dest, MOVEFILE_COPY_ALLOWED) == 0'i32: raiseOSError(osLastError())
else: else:
if c_rename(source, dest) != 0'i32: if c_rename(source, dest) != 0'i32:
let err = osLastError() let err = osLastError()
if err == EXDEV.OSErrorCode: if err == EXDEV.OSErrorCode:
# Fallback to copy & del return false
copyFile(source, dest)
try:
removeFile(source)
except:
discard tryRemoveFile(dest)
raise
else: else:
raiseOSError(err, $strerror(errno)) raiseOSError(err, $strerror(errno))
return true
proc moveFile*(source, dest: string) {.rtl, extern: "nos$1",
tags: [ReadIOEffect, WriteIOEffect].} =
## Moves a file from `source` to `dest`. If this fails, `OSError` is raised.
if not tryMoveFSObject(source, dest):
when not defined(windows):
# Fallback to copy & del
copyFile(source, dest)
try:
removeFile(source)
except:
discard tryRemoveFile(dest)
raise
proc execShellCmd*(command: string): int {.rtl, extern: "nos$1", proc execShellCmd*(command: string): int {.rtl, extern: "nos$1",
tags: [ExecIOEffect].} = tags: [ExecIOEffect].} =
@ -1369,6 +1378,14 @@ proc exclFilePermissions*(filename: string,
## setFilePermissions(filename, getFilePermissions(filename)-permissions) ## setFilePermissions(filename, getFilePermissions(filename)-permissions)
setFilePermissions(filename, getFilePermissions(filename)-permissions) setFilePermissions(filename, getFilePermissions(filename)-permissions)
proc moveDir*(source, dest: string) {.tags: [ReadIOEffect, WriteIOEffect].} =
## Moves a directory from `source` to `dest`. If this fails, `OSError` is raised.
if not tryMoveFSObject(source, dest):
when not defined(windows):
# Fallback to copy & del
copyDir(source, dest)
removeDir(source)
include ospaths include ospaths
proc expandSymlink*(symlinkPath: string): string = proc expandSymlink*(symlinkPath: string): string =
@ -1412,7 +1429,7 @@ when defined(nimdoc):
proc paramStr*(i: int): TaintedString {.tags: [ReadIOEffect].} = proc paramStr*(i: int): TaintedString {.tags: [ReadIOEffect].} =
## Returns the `i`-th `command line argument`:idx: given to the application. ## Returns the `i`-th `command line argument`:idx: given to the application.
## ##
## `i` should be in the range `1..paramCount()`, the `EInvalidIndex` ## `i` should be in the range `1..paramCount()`, the `IndexError`
## exception will be raised for invalid values. Instead of iterating over ## exception will be raised for invalid values. Instead of iterating over
## `paramCount() <#paramCount>`_ with this proc you can call the ## `paramCount() <#paramCount>`_ with this proc you can call the
## convenience `commandLineParams() <#commandLineParams>`_. ## convenience `commandLineParams() <#commandLineParams>`_.
@ -1450,7 +1467,8 @@ elif defined(windows):
tags: [ReadIOEffect].} = tags: [ReadIOEffect].} =
# Docstring in nimdoc block. # Docstring in nimdoc block.
if isNil(ownArgv): ownArgv = parseCmdLine($getCommandLine()) if isNil(ownArgv): ownArgv = parseCmdLine($getCommandLine())
return TaintedString(ownArgv[i]) if i < ownArgv.len and i >= 0: return TaintedString(ownArgv[i])
raise newException(IndexError, "invalid index")
elif not defined(createNimRtl) and elif not defined(createNimRtl) and
not(defined(posix) and appType == "lib") and not(defined(posix) and appType == "lib") and

View file

@ -516,7 +516,16 @@ when declared(getEnv) or defined(nimscript):
proc getConfigDir*(): string {.rtl, extern: "nos$1", proc getConfigDir*(): string {.rtl, extern: "nos$1",
tags: [ReadEnvEffect, ReadIOEffect].} = tags: [ReadEnvEffect, ReadIOEffect].} =
## Returns the config directory of the current user for applications. ## Returns the config directory of the current user for applications.
##
## On non-Windows OSs, this proc conforms to the XDG Base Directory
## spec. Thus, this proc returns the value of the XDG_CONFIG_DIR environment
## variable if it is set, and returns the default configuration directory,
## "~/.config/", otherwise.
##
## An OS-dependent trailing slash is always present at the end of the
## returned string; `\\` on Windows and `/` on all other OSs.
when defined(windows): return string(getEnv("APPDATA")) & "\\" when defined(windows): return string(getEnv("APPDATA")) & "\\"
elif getEnv("XDG_CONFIG_DIR"): return string(getEnv("XDG_CONFIG_DIR")) & "/"
else: return string(getEnv("HOME")) & "/.config/" else: return string(getEnv("HOME")) & "/.config/"
proc getTempDir*(): string {.rtl, extern: "nos$1", proc getTempDir*(): string {.rtl, extern: "nos$1",

View file

@ -863,18 +863,15 @@ elif not defined(useNimRtl):
if data.workingDir.len > 0: if data.workingDir.len > 0:
setCurrentDir($data.workingDir) setCurrentDir($data.workingDir)
var pid: Pid var pid: Pid
var err: OSErrorCode
if data.optionPoUsePath: if data.optionPoUsePath:
res = posix_spawnp(pid, data.sysCommand, fops, attr, data.sysArgs, data.sysEnv) res = posix_spawnp(pid, data.sysCommand, fops, attr, data.sysArgs, data.sysEnv)
if res != 0'i32: err = osLastError()
else: else:
res = posix_spawn(pid, data.sysCommand, fops, attr, data.sysArgs, data.sysEnv) res = posix_spawn(pid, data.sysCommand, fops, attr, data.sysArgs, data.sysEnv)
if res != 0'i32: err = osLastError()
discard posix_spawn_file_actions_destroy(fops) discard posix_spawn_file_actions_destroy(fops)
discard posix_spawnattr_destroy(attr) discard posix_spawnattr_destroy(attr)
if res != 0'i32: raiseOSError(err) if res != 0'i32: raiseOSError(OSErrorCode(res))
return pid return pid
else: else:

View file

@ -540,8 +540,8 @@ proc writeConfig*(dict: Config, filename: string) =
## Writes the contents of the table to the specified configuration file. ## Writes the contents of the table to the specified configuration file.
## Note: Comment statement will be ignored. ## Note: Comment statement will be ignored.
let file = open(filename, fmWrite) let file = open(filename, fmWrite)
let fileStream = newFileStream(filename) defer: file.close()
defer: fileStream.close() let fileStream = newFileStream(file)
dict.writeConfig(fileStream) dict.writeConfig(fileStream)
proc getSectionValue*(dict: Config, section, key: string): string = proc getSectionValue*(dict: Config, section, key: string): string =

View file

@ -149,26 +149,41 @@ proc cmdLineRest*(p: OptParser): TaintedString {.rtl, extern: "npo$1".} =
## retrieves the rest of the command line that has not been parsed yet. ## retrieves the rest of the command line that has not been parsed yet.
result = strip(substr(p.cmd, p.pos, len(p.cmd) - 1)).TaintedString result = strip(substr(p.cmd, p.pos, len(p.cmd) - 1)).TaintedString
iterator getopt*(p: var OptParser): tuple[kind: CmdLineKind, key, val: TaintedString] =
## This is an convenience iterator for iterating over the given OptParser object.
## Example:
##
## .. code-block:: nim
## var p = initOptParser("--left --debug:3 -l=4 -r:2")
## for kind, key, val in p.getopt():
## case kind
## of cmdArgument:
## filename = key
## of cmdLongOption, cmdShortOption:
## case key
## of "help", "h": writeHelp()
## of "version", "v": writeVersion()
## of cmdEnd: assert(false) # cannot happen
## if filename == "":
## # no filename has been given, so we show the help:
## writeHelp()
p.pos = 0
while true:
next(p)
if p.kind == cmdEnd: break
yield (p.kind, p.key, p.val)
when declared(initOptParser): when declared(initOptParser):
iterator getopt*(): tuple[kind: CmdLineKind, key, val: TaintedString] = iterator getopt*(): tuple[kind: CmdLineKind, key, val: TaintedString] =
## This is an convenience iterator for iterating over the command line. ## This is an convenience iterator for iterating over the command line arguments.
## This uses the OptParser object. Example: ## This create a new OptParser object.
## See above for a more detailed example
## ##
## .. code-block:: nim ## .. code-block:: nim
## var
## filename = ""
## for kind, key, val in getopt(): ## for kind, key, val in getopt():
## case kind ## # this will iterate over all arguments passed to the cmdline.
## of cmdArgument: ## continue
## filename = key ##
## of cmdLongOption, cmdShortOption:
## case key
## of "help", "h": writeHelp()
## of "version", "v": writeVersion()
## of cmdEnd: assert(false) # cannot happen
## if filename == "":
## # no filename has been given, so we show the help:
## writeHelp()
var p = initOptParser() var p = initOptParser()
while true: while true:
next(p) next(p)

View file

@ -123,26 +123,41 @@ type
{.deprecated: [TGetoptResult: GetoptResult].} {.deprecated: [TGetoptResult: GetoptResult].}
iterator getopt*(p: var OptParser): GetoptResult =
## This is an convenience iterator for iterating over the given OptParser object.
## Example:
##
## .. code-block:: nim
## var p = initOptParser("--left --debug:3 -l=4 -r:2")
## for kind, key, val in p.getopt():
## case kind
## of cmdArgument:
## filename = key
## of cmdLongOption, cmdShortOption:
## case key
## of "help", "h": writeHelp()
## of "version", "v": writeVersion()
## of cmdEnd: assert(false) # cannot happen
## if filename == "":
## # no filename has been given, so we show the help:
## writeHelp()
p.pos = 0
while true:
next(p)
if p.kind == cmdEnd: break
yield (p.kind, p.key, p.val)
when declared(paramCount): when declared(paramCount):
iterator getopt*(): GetoptResult = iterator getopt*(): GetoptResult =
## This is an convenience iterator for iterating over the command line. ## This is an convenience iterator for iterating over the command line arguments.
## This uses the OptParser object. Example: ## This create a new OptParser object.
## See above for a more detailed example
## ##
## .. code-block:: nim ## .. code-block:: nim
## var
## filename = ""
## for kind, key, val in getopt(): ## for kind, key, val in getopt():
## case kind ## # this will iterate over all arguments passed to the cmdline.
## of cmdArgument: ## continue
## filename = key ##
## of cmdLongOption, cmdShortOption:
## case key
## of "help", "h": writeHelp()
## of "version", "v": writeVersion()
## of cmdEnd: assert(false) # cannot happen
## if filename == "":
## # no filename has been given, so we show the help:
## writeHelp()
var p = initOptParser() var p = initOptParser()
while true: while true:
next(p) next(p)

View file

@ -371,7 +371,7 @@ proc esc(c: char, reserved = {'\0'..'\255'}): string =
of '\a': result = "\\a" of '\a': result = "\\a"
of '\\': result = "\\\\" of '\\': result = "\\\\"
of 'a'..'z', 'A'..'Z', '0'..'9', '_': result = $c of 'a'..'z', 'A'..'Z', '0'..'9', '_': result = $c
elif c < ' ' or c >= '\128': result = '\\' & $ord(c) elif c < ' ' or c >= '\127': result = '\\' & $ord(c)
elif c in reserved: result = '\\' & c elif c in reserved: result = '\\' & c
else: result = $c else: result = $c

View file

@ -123,7 +123,8 @@ when not defined(nimscript):
proc getMil(t: Time): int {.importcpp: "getTime", nodecl.} proc getMil(t: Time): int {.importcpp: "getTime", nodecl.}
randomize(getMil times.getTime()) randomize(getMil times.getTime())
else: else:
randomize(int times.getTime()) let time = int(times.epochTime() * 1_000_000_000)
randomize(time)
{.pop.} {.pop.}

View file

@ -334,12 +334,16 @@ when not defined(js):
if result > 0: if result > 0:
copyMem(buffer, addr(s.data[s.pos]), result) copyMem(buffer, addr(s.data[s.pos]), result)
inc(s.pos, result) inc(s.pos, result)
else:
result = 0
proc ssPeekData(s: Stream, buffer: pointer, bufLen: int): int = proc ssPeekData(s: Stream, buffer: pointer, bufLen: int): int =
var s = StringStream(s) var s = StringStream(s)
result = min(bufLen, s.data.len - s.pos) result = min(bufLen, s.data.len - s.pos)
if result > 0: if result > 0:
copyMem(buffer, addr(s.data[s.pos]), result) copyMem(buffer, addr(s.data[s.pos]), result)
else:
result = 0
proc ssWriteData(s: Stream, buffer: pointer, bufLen: int) = proc ssWriteData(s: Stream, buffer: pointer, bufLen: int) =
var s = StringStream(s) var s = StringStream(s)

View file

@ -1643,7 +1643,7 @@ proc escape*(s: string, prefix = "\"", suffix = "\""): string {.noSideEffect,
## * replaces any ``\`` by ``\\`` ## * replaces any ``\`` by ``\\``
## * replaces any ``'`` by ``\'`` ## * replaces any ``'`` by ``\'``
## * replaces any ``"`` by ``\"`` ## * replaces any ``"`` by ``\"``
## * replaces any other character in the set ``{'\0'..'\31', '\128'..'\255'}`` ## * replaces any other character in the set ``{'\0'..'\31', '\127'..'\255'}``
## by ``\xHH`` where ``HH`` is its hexadecimal value. ## by ``\xHH`` where ``HH`` is its hexadecimal value.
## The procedure has been designed so that its output is usable for many ## The procedure has been designed so that its output is usable for many
## different common syntaxes. The resulting string is prefixed with ## different common syntaxes. The resulting string is prefixed with
@ -1653,7 +1653,7 @@ proc escape*(s: string, prefix = "\"", suffix = "\""): string {.noSideEffect,
result.add(prefix) result.add(prefix)
for c in items(s): for c in items(s):
case c case c
of '\0'..'\31', '\128'..'\255': of '\0'..'\31', '\127'..'\255':
add(result, "\\x") add(result, "\\x")
add(result, toHex(ord(c), 2)) add(result, toHex(ord(c), 2))
of '\\': add(result, "\\\\") of '\\': add(result, "\\\\")

View file

@ -81,7 +81,7 @@ when defined(posix) and not defined(JS):
elif defined(windows): elif defined(windows):
import winlean import winlean
when defined(vcc) or defined(bcc): when defined(vcc) or defined(bcc) or defined(icl):
# newest version of Visual C++ defines time_t to be of 64 bits # newest version of Visual C++ defines time_t to be of 64 bits
type TimeImpl {.importc: "time_t", header: "<time.h>".} = int64 type TimeImpl {.importc: "time_t", header: "<time.h>".} = int64
# visual c's c runtime exposes these under a different name # visual c's c runtime exposes these under a different name
@ -98,46 +98,47 @@ elif defined(windows):
elif defined(JS): elif defined(JS):
type type
Time* = ref TimeObj TimeBase = float
TimeObj {.importc.} = object Time* = distinct TimeBase
getDay: proc (): int {.tags: [], raises: [], benign.}
getFullYear: proc (): int {.tags: [], raises: [], benign.} proc getDay(t: Time): int {.tags: [], raises: [], benign, importcpp.}
getHours: proc (): int {.tags: [], raises: [], benign.} proc getFullYear(t: Time): int {.tags: [], raises: [], benign, importcpp.}
getMilliseconds: proc (): int {.tags: [], raises: [], benign.} proc getHours(t: Time): int {.tags: [], raises: [], benign, importcpp.}
getMinutes: proc (): int {.tags: [], raises: [], benign.} proc getMilliseconds(t: Time): int {.tags: [], raises: [], benign, importcpp.}
getMonth: proc (): int {.tags: [], raises: [], benign.} proc getMinutes(t: Time): int {.tags: [], raises: [], benign, importcpp.}
getSeconds: proc (): int {.tags: [], raises: [], benign.} proc getMonth(t: Time): int {.tags: [], raises: [], benign, importcpp.}
getTime: proc (): int {.tags: [], raises: [], noSideEffect, benign.} proc getSeconds(t: Time): int {.tags: [], raises: [], benign, importcpp.}
getTimezoneOffset: proc (): int {.tags: [], raises: [], benign.} proc getTime(t: Time): int {.tags: [], raises: [], noSideEffect, benign, importcpp.}
getDate: proc (): int {.tags: [], raises: [], benign.} proc getTimezoneOffset(t: Time): int {.tags: [], raises: [], benign, importcpp.}
getUTCDate: proc (): int {.tags: [], raises: [], benign.} proc getDate(t: Time): int {.tags: [], raises: [], benign, importcpp.}
getUTCFullYear: proc (): int {.tags: [], raises: [], benign.} proc getUTCDate(t: Time): int {.tags: [], raises: [], benign, importcpp.}
getUTCHours: proc (): int {.tags: [], raises: [], benign.} proc getUTCFullYear(t: Time): int {.tags: [], raises: [], benign, importcpp.}
getUTCMilliseconds: proc (): int {.tags: [], raises: [], benign.} proc getUTCHours(t: Time): int {.tags: [], raises: [], benign, importcpp.}
getUTCMinutes: proc (): int {.tags: [], raises: [], benign.} proc getUTCMilliseconds(t: Time): int {.tags: [], raises: [], benign, importcpp.}
getUTCMonth: proc (): int {.tags: [], raises: [], benign.} proc getUTCMinutes(t: Time): int {.tags: [], raises: [], benign, importcpp.}
getUTCSeconds: proc (): int {.tags: [], raises: [], benign.} proc getUTCMonth(t: Time): int {.tags: [], raises: [], benign, importcpp.}
getUTCDay: proc (): int {.tags: [], raises: [], benign.} proc getUTCSeconds(t: Time): int {.tags: [], raises: [], benign, importcpp.}
getYear: proc (): int {.tags: [], raises: [], benign.} proc getUTCDay(t: Time): int {.tags: [], raises: [], benign, importcpp.}
parse: proc (s: cstring): Time {.tags: [], raises: [], benign.} proc getYear(t: Time): int {.tags: [], raises: [], benign, importcpp.}
setDate: proc (x: int) {.tags: [], raises: [], benign.} proc parse(t: Time; s: cstring): Time {.tags: [], raises: [], benign, importcpp.}
setFullYear: proc (x: int) {.tags: [], raises: [], benign.} proc setDate(t: Time; x: int) {.tags: [], raises: [], benign, importcpp.}
setHours: proc (x: int) {.tags: [], raises: [], benign.} proc setFullYear(t: Time; x: int) {.tags: [], raises: [], benign, importcpp.}
setMilliseconds: proc (x: int) {.tags: [], raises: [], benign.} proc setHours(t: Time; x: int) {.tags: [], raises: [], benign, importcpp.}
setMinutes: proc (x: int) {.tags: [], raises: [], benign.} proc setMilliseconds(t: Time; x: int) {.tags: [], raises: [], benign, importcpp.}
setMonth: proc (x: int) {.tags: [], raises: [], benign.} proc setMinutes(t: Time; x: int) {.tags: [], raises: [], benign, importcpp.}
setSeconds: proc (x: int) {.tags: [], raises: [], benign.} proc setMonth(t: Time; x: int) {.tags: [], raises: [], benign, importcpp.}
setTime: proc (x: int) {.tags: [], raises: [], benign.} proc setSeconds(t: Time; x: int) {.tags: [], raises: [], benign, importcpp.}
setUTCDate: proc (x: int) {.tags: [], raises: [], benign.} proc setTime(t: Time; x: int) {.tags: [], raises: [], benign, importcpp.}
setUTCFullYear: proc (x: int) {.tags: [], raises: [], benign.} proc setUTCDate(t: Time; x: int) {.tags: [], raises: [], benign, importcpp.}
setUTCHours: proc (x: int) {.tags: [], raises: [], benign.} proc setUTCFullYear(t: Time; x: int) {.tags: [], raises: [], benign, importcpp.}
setUTCMilliseconds: proc (x: int) {.tags: [], raises: [], benign.} proc setUTCHours(t: Time; x: int) {.tags: [], raises: [], benign, importcpp.}
setUTCMinutes: proc (x: int) {.tags: [], raises: [], benign.} proc setUTCMilliseconds(t: Time; x: int) {.tags: [], raises: [], benign, importcpp.}
setUTCMonth: proc (x: int) {.tags: [], raises: [], benign.} proc setUTCMinutes(t: Time; x: int) {.tags: [], raises: [], benign, importcpp.}
setUTCSeconds: proc (x: int) {.tags: [], raises: [], benign.} proc setUTCMonth(t: Time; x: int) {.tags: [], raises: [], benign, importcpp.}
setYear: proc (x: int) {.tags: [], raises: [], benign.} proc setUTCSeconds(t: Time; x: int) {.tags: [], raises: [], benign, importcpp.}
toGMTString: proc (): cstring {.tags: [], raises: [], benign.} proc setYear(t: Time; x: int) {.tags: [], raises: [], benign, importcpp.}
toLocaleString: proc (): cstring {.tags: [], raises: [], benign.} proc toGMTString(t: Time): cstring {.tags: [], raises: [], benign, importcpp.}
proc toLocaleString(t: Time): cstring {.tags: [], raises: [], benign, importcpp.}
type type
TimeInfo* = object of RootObj ## represents a time in different parts TimeInfo* = object of RootObj ## represents a time in different parts
@ -225,17 +226,26 @@ proc `-`*(a, b: Time): int64 {.
proc `<`*(a, b: Time): bool {. proc `<`*(a, b: Time): bool {.
rtl, extern: "ntLtTime", tags: [], raises: [], noSideEffect.} = rtl, extern: "ntLtTime", tags: [], raises: [], noSideEffect.} =
## returns true iff ``a < b``, that is iff a happened before b. ## returns true iff ``a < b``, that is iff a happened before b.
result = a - b < 0 when defined(js):
result = TimeBase(a) < TimeBase(b)
else:
result = a - b < 0
proc `<=` * (a, b: Time): bool {. proc `<=` * (a, b: Time): bool {.
rtl, extern: "ntLeTime", tags: [], raises: [], noSideEffect.}= rtl, extern: "ntLeTime", tags: [], raises: [], noSideEffect.}=
## returns true iff ``a <= b``. ## returns true iff ``a <= b``.
result = a - b <= 0 when defined(js):
result = TimeBase(a) <= TimeBase(b)
else:
result = a - b <= 0
proc `==`*(a, b: Time): bool {. proc `==`*(a, b: Time): bool {.
rtl, extern: "ntEqTime", tags: [], raises: [], noSideEffect.} = rtl, extern: "ntEqTime", tags: [], raises: [], noSideEffect.} =
## returns true if ``a == b``, that is if both times represent the same value ## returns true if ``a == b``, that is if both times represent the same value
result = a - b == 0 when defined(js):
result = TimeBase(a) == TimeBase(b)
else:
result = a - b == 0
proc getTimezone*(): int {.tags: [TimeEffect], raises: [], benign.} proc getTimezone*(): int {.tags: [TimeEffect], raises: [], benign.}
## returns the offset of the local (non-DST) timezone in seconds west of UTC. ## returns the offset of the local (non-DST) timezone in seconds west of UTC.
@ -479,7 +489,7 @@ proc `-=`*(t: var Time, ti: TimeInterval) =
t = toTime(getLocalTime(t) - ti) t = toTime(getLocalTime(t) - ti)
proc `-`*(t: Time, ti: TimeInterval): Time = proc `-`*(t: Time, ti: TimeInterval): Time =
## adds the interval `ti` to Time `t` ## subtracts the interval `ti` from Time `t`
## ##
## ``echo getTime() - 1.day`` ## ``echo getTime() - 1.day``
result = toTime(getLocalTime(t) - ti) result = toTime(getLocalTime(t) - ti)

View file

@ -49,6 +49,9 @@ type
cstring* {.magic: Cstring.} ## built-in cstring (*compatible string*) type cstring* {.magic: Cstring.} ## built-in cstring (*compatible string*) type
pointer* {.magic: Pointer.} ## built-in pointer type, use the ``addr`` pointer* {.magic: Pointer.} ## built-in pointer type, use the ``addr``
## operator to get a pointer to a variable ## operator to get a pointer to a variable
typedesc* {.magic: TypeDesc.} ## meta type to denote a type description
const const
on* = true ## alias for ``true`` on* = true ## alias for ``true``
off* = false ## alias for ``false`` off* = false ## alias for ``false``
@ -56,6 +59,15 @@ const
{.push warning[GcMem]: off, warning[Uninit]: off.} {.push warning[GcMem]: off, warning[Uninit]: off.}
{.push hints: off.} {.push hints: off.}
proc `or` *(a, b: typedesc): typedesc {.magic: "TypeTrait", noSideEffect.}
## Constructs an `or` meta class
proc `and` *(a, b: typedesc): typedesc {.magic: "TypeTrait", noSideEffect.}
## Constructs an `and` meta class
proc `not` *(a: typedesc): typedesc {.magic: "TypeTrait", noSideEffect.}
## Constructs an `not` meta class
type type
Ordinal* {.magic: Ordinal.}[T] ## Generic ordinal type. Includes integer, Ordinal* {.magic: Ordinal.}[T] ## Generic ordinal type. Includes integer,
## bool, character, and enumeration types ## bool, character, and enumeration types
@ -66,11 +78,11 @@ type
`ref`* {.magic: Pointer.}[T] ## built-in generic traced pointer type `ref`* {.magic: Pointer.}[T] ## built-in generic traced pointer type
`nil` {.magic: "Nil".} `nil` {.magic: "Nil".}
expr* {.magic: Expr, deprecated.} ## meta type to denote an expression (for templates) expr* {.magic: Expr, deprecated.} ## meta type to denote an expression (for templates)
## **Deprecated** since version 0.15. Use ``untyped`` instead. ## **Deprecated** since version 0.15. Use ``untyped`` instead.
stmt* {.magic: Stmt, deprecated.} ## meta type to denote a statement (for templates) stmt* {.magic: Stmt, deprecated.} ## meta type to denote a statement (for templates)
## **Deprecated** since version 0.15. Use ``typed`` instead. ## **Deprecated** since version 0.15. Use ``typed`` instead.
typedesc* {.magic: TypeDesc.} ## meta type to denote a type description
void* {.magic: "VoidType".} ## meta type to denote the absence of any type void* {.magic: "VoidType".} ## meta type to denote the absence of any type
auto* {.magic: Expr.} ## meta type for automatic type determination auto* {.magic: Expr.} ## meta type for automatic type determination
any* = distinct auto ## meta type for any supported type any* = distinct auto ## meta type for any supported type
@ -430,6 +442,7 @@ type
## providing an exception message ## providing an exception message
## is bad style. ## is bad style.
trace: string trace: string
up: ref Exception # used for stacking exceptions. Not exported!
SystemError* = object of Exception ## \ SystemError* = object of Exception ## \
## Abstract class for exceptions that the runtime system raises. ## Abstract class for exceptions that the runtime system raises.
@ -1056,7 +1069,7 @@ proc `div`*[T: SomeUnsignedInt](x, y: T): T {.magic: "DivU", noSideEffect.}
## ``floor(x/y)``. ## ``floor(x/y)``.
## ##
## .. code-block:: Nim ## .. code-block:: Nim
## (7 div 5) == 2 ## (7 div 5) == 1
proc `mod`*[T: SomeUnsignedInt](x, y: T): T {.magic: "ModU", noSideEffect.} proc `mod`*[T: SomeUnsignedInt](x, y: T): T {.magic: "ModU", noSideEffect.}
## computes the integer modulo operation (remainder). ## computes the integer modulo operation (remainder).
@ -1332,6 +1345,7 @@ const
hasThreadSupport = compileOption("threads") and not defined(nimscript) hasThreadSupport = compileOption("threads") and not defined(nimscript)
hasSharedHeap = defined(boehmgc) or defined(gogc) # don't share heaps; every thread has its own hasSharedHeap = defined(boehmgc) or defined(gogc) # don't share heaps; every thread has its own
taintMode = compileOption("taintmode") taintMode = compileOption("taintmode")
nimEnableCovariance* = defined(nimEnableCovariance) # or true
when hasThreadSupport and defined(tcc) and not compileOption("tlsEmulation"): when hasThreadSupport and defined(tcc) and not compileOption("tlsEmulation"):
# tcc doesn't support TLS # tcc doesn't support TLS
@ -1879,7 +1893,7 @@ const
NimMajor*: int = 0 NimMajor*: int = 0
## is the major number of Nim's version. ## is the major number of Nim's version.
NimMinor*: int = 16 NimMinor*: int = 17
## is the minor number of Nim's version. ## is the minor number of Nim's version.
NimPatch*: int = 1 NimPatch*: int = 1
@ -2034,6 +2048,14 @@ proc clamp*[T](x, a, b: T): T =
if x > b: return b if x > b: return b
return x return x
proc len*[T: Ordinal](x: Slice[T]): int {.noSideEffect, inline.} =
## length of ordinal slice, when x.b < x.a returns zero length
##
## .. code-block:: Nim
## assert((0..5).len == 6)
## assert((5..2).len == 0)
result = max(0, ord(x.b) - ord(x.a) + 1)
iterator items*[T](a: openArray[T]): T {.inline.} = iterator items*[T](a: openArray[T]): T {.inline.} =
## iterates over each item of `a`. ## iterates over each item of `a`.
var i = 0 var i = 0
@ -2428,18 +2450,6 @@ when false:
# ----------------- GC interface --------------------------------------------- # ----------------- GC interface ---------------------------------------------
when not defined(nimscript) and hasAlloc: when not defined(nimscript) and hasAlloc:
proc GC_disable*() {.rtl, inl, benign.}
## disables the GC. If called n-times, n calls to `GC_enable` are needed to
## reactivate the GC. Note that in most circumstances one should only disable
## the mark and sweep phase with `GC_disableMarkAndSweep`.
proc GC_enable*() {.rtl, inl, benign.}
## enables the GC again.
proc GC_fullCollect*() {.rtl, benign.}
## forces a full garbage collection pass.
## Ordinary code does not need to call this (and should not).
type type
GC_Strategy* = enum ## the strategy the GC should use for the application GC_Strategy* = enum ## the strategy the GC should use for the application
gcThroughput, ## optimize for throughput gcThroughput, ## optimize for throughput
@ -2449,33 +2459,87 @@ when not defined(nimscript) and hasAlloc:
{.deprecated: [TGC_Strategy: GC_Strategy].} {.deprecated: [TGC_Strategy: GC_Strategy].}
proc GC_setStrategy*(strategy: GC_Strategy) {.rtl, deprecated, benign.} when not defined(JS):
## tells the GC the desired strategy for the application. proc GC_disable*() {.rtl, inl, benign.}
## **Deprecated** since version 0.8.14. This has always been a nop. ## disables the GC. If called n-times, n calls to `GC_enable` are needed to
## reactivate the GC. Note that in most circumstances one should only disable
## the mark and sweep phase with `GC_disableMarkAndSweep`.
proc GC_enableMarkAndSweep*() {.rtl, benign.} proc GC_enable*() {.rtl, inl, benign.}
proc GC_disableMarkAndSweep*() {.rtl, benign.} ## enables the GC again.
## the current implementation uses a reference counting garbage collector
## with a seldomly run mark and sweep phase to free cycles. The mark and
## sweep phase may take a long time and is not needed if the application
## does not create cycles. Thus the mark and sweep phase can be deactivated
## and activated separately from the rest of the GC.
proc GC_getStatistics*(): string {.rtl, benign.} proc GC_fullCollect*() {.rtl, benign.}
## returns an informative string about the GC's activity. This may be useful ## forces a full garbage collection pass.
## for tweaking. ## Ordinary code does not need to call this (and should not).
proc GC_ref*[T](x: ref T) {.magic: "GCref", benign.} proc GC_setStrategy*(strategy: GC_Strategy) {.rtl, deprecated, benign.}
proc GC_ref*[T](x: seq[T]) {.magic: "GCref", benign.} ## tells the GC the desired strategy for the application.
proc GC_ref*(x: string) {.magic: "GCref", benign.} ## **Deprecated** since version 0.8.14. This has always been a nop.
## marks the object `x` as referenced, so that it will not be freed until
## it is unmarked via `GC_unref`. If called n-times for the same object `x`,
## n calls to `GC_unref` are needed to unmark `x`.
proc GC_unref*[T](x: ref T) {.magic: "GCunref", benign.} proc GC_enableMarkAndSweep*() {.rtl, benign.}
proc GC_unref*[T](x: seq[T]) {.magic: "GCunref", benign.} proc GC_disableMarkAndSweep*() {.rtl, benign.}
proc GC_unref*(x: string) {.magic: "GCunref", benign.} ## the current implementation uses a reference counting garbage collector
## see the documentation of `GC_ref`. ## with a seldomly run mark and sweep phase to free cycles. The mark and
## sweep phase may take a long time and is not needed if the application
## does not create cycles. Thus the mark and sweep phase can be deactivated
## and activated separately from the rest of the GC.
proc GC_getStatistics*(): string {.rtl, benign.}
## returns an informative string about the GC's activity. This may be useful
## for tweaking.
proc GC_ref*[T](x: ref T) {.magic: "GCref", benign.}
proc GC_ref*[T](x: seq[T]) {.magic: "GCref", benign.}
proc GC_ref*(x: string) {.magic: "GCref", benign.}
## marks the object `x` as referenced, so that it will not be freed until
## it is unmarked via `GC_unref`. If called n-times for the same object `x`,
## n calls to `GC_unref` are needed to unmark `x`.
proc GC_unref*[T](x: ref T) {.magic: "GCunref", benign.}
proc GC_unref*[T](x: seq[T]) {.magic: "GCunref", benign.}
proc GC_unref*(x: string) {.magic: "GCunref", benign.}
## see the documentation of `GC_ref`.
else:
template GC_disable* =
{.warning: "GC_disable is a no-op in JavaScript".}
template GC_enable* =
{.warning: "GC_enable is a no-op in JavaScript".}
template GC_fullCollect* =
{.warning: "GC_fullCollect is a no-op in JavaScript".}
template GC_setStrategy* =
{.warning: "GC_setStrategy is a no-op in JavaScript".}
template GC_enableMarkAndSweep* =
{.warning: "GC_enableMarkAndSweep is a no-op in JavaScript".}
template GC_disableMarkAndSweep* =
{.warning: "GC_disableMarkAndSweep is a no-op in JavaScript".}
template GC_ref*[T](x: ref T) =
{.warning: "GC_ref is a no-op in JavaScript".}
template GC_ref*[T](x: seq[T]) =
{.warning: "GC_ref is a no-op in JavaScript".}
template GC_ref*(x: string) =
{.warning: "GC_ref is a no-op in JavaScript".}
template GC_unref*[T](x: ref T) =
{.warning: "GC_unref is a no-op in JavaScript".}
template GC_unref*[T](x: seq[T]) =
{.warning: "GC_unref is a no-op in JavaScript".}
template GC_unref*(x: string) =
{.warning: "GC_unref is a no-op in JavaScript".}
template GC_getStatistics*(): string =
{.warning: "GC_disableMarkAndSweep is a no-op in JavaScript".}
""
template accumulateResult*(iter: untyped) = template accumulateResult*(iter: untyped) =
## helps to convert an iterator to a proc. ## helps to convert an iterator to a proc.
@ -3210,16 +3274,6 @@ when not defined(JS): #and not defined(nimscript):
elif defined(JS): elif defined(JS):
# Stubs: # Stubs:
proc nimGCvisit(d: pointer, op: int) {.compilerRtl.} = discard
proc GC_disable() = discard
proc GC_enable() = discard
proc GC_fullCollect() = discard
proc GC_setStrategy(strategy: GC_Strategy) = discard
proc GC_enableMarkAndSweep() = discard
proc GC_disableMarkAndSweep() = discard
proc GC_getStatistics(): string = return ""
proc getOccupiedMem(): int = return -1 proc getOccupiedMem(): int = return -1
proc getFreeMem(): int = return -1 proc getFreeMem(): int = return -1
proc getTotalMem(): int = return -1 proc getTotalMem(): int = return -1
@ -3458,7 +3512,7 @@ proc `*=`*[T: SomeOrdinal|uint|uint64](x: var T, y: T) {.
proc `+=`*[T: float|float32|float64] (x: var T, y: T) {. proc `+=`*[T: float|float32|float64] (x: var T, y: T) {.
inline, noSideEffect.} = inline, noSideEffect.} =
## Increments in placee a floating point number ## Increments in place a floating point number
x = x + y x = x + y
proc `-=`*[T: float|float32|float64] (x: var T, y: T) {. proc `-=`*[T: float|float32|float64] (x: var T, y: T) {.
@ -3721,7 +3775,8 @@ proc locals*(): RootObj {.magic: "Plugin", noSideEffect.} =
when hasAlloc and not defined(nimscript) and not defined(JS): when hasAlloc and not defined(nimscript) and not defined(JS):
proc deepCopy*[T](x: var T, y: T) {.noSideEffect, magic: "DeepCopy".} = proc deepCopy*[T](x: var T, y: T) {.noSideEffect, magic: "DeepCopy".} =
## performs a deep copy of `x`. This is also used by the code generator ## performs a deep copy of `y` and copies it into `x`.
## This is also used by the code generator
## for the implementation of ``spawn``. ## for the implementation of ``spawn``.
discard discard

View file

@ -9,7 +9,6 @@
# Low level allocator for Nim. Has been designed to support the GC. # Low level allocator for Nim. Has been designed to support the GC.
# TODO: # TODO:
# - eliminate "used" field
# - make searching for block O(1) # - make searching for block O(1)
{.push profiler:off.} {.push profiler:off.}

View file

@ -90,12 +90,11 @@ proc popSafePoint {.compilerRtl, inl.} =
excHandler = excHandler.prev excHandler = excHandler.prev
proc pushCurrentException(e: ref Exception) {.compilerRtl, inl.} = proc pushCurrentException(e: ref Exception) {.compilerRtl, inl.} =
#if e.parent.isNil: e.up = currException
# e.parent = currException
currException = e currException = e
proc popCurrentException {.compilerRtl, inl.} = proc popCurrentException {.compilerRtl, inl.} =
currException = nil # currException.parent currException = currException.up
# some platforms have native support for stack traces: # some platforms have native support for stack traces:
const const
@ -247,6 +246,18 @@ when false:
pushCurrentException(e) pushCurrentException(e)
c_longjmp(excHandler.context, 1) c_longjmp(excHandler.context, 1)
var onUnhandledException*: (proc (errorMsg: string) {.
nimcall.}) ## set this error \
## handler to override the existing behaviour on an unhandled exception.
## The default is to write a stacktrace to ``stderr`` and then call ``quit(1)``.
## Unstable API.
template unhandled(buf, body) =
if onUnhandledException != nil:
onUnhandledException($buf)
else:
body
proc raiseExceptionAux(e: ref Exception) = proc raiseExceptionAux(e: ref Exception) =
if localRaiseHook != nil: if localRaiseHook != nil:
if not localRaiseHook(e): return if not localRaiseHook(e): return
@ -277,7 +288,9 @@ proc raiseExceptionAux(e: ref Exception) =
add(buf, " [") add(buf, " [")
add(buf, $e.name) add(buf, $e.name)
add(buf, "]\n") add(buf, "]\n")
showErrorMessage(buf) unhandled(buf):
showErrorMessage(buf)
quitOrDebug()
else: else:
# ugly, but avoids heap allocations :-) # ugly, but avoids heap allocations :-)
template xadd(buf, s, slen: expr) = template xadd(buf, s, slen: expr) =
@ -293,8 +306,9 @@ proc raiseExceptionAux(e: ref Exception) =
add(buf, " [") add(buf, " [")
xadd(buf, e.name, e.name.len) xadd(buf, e.name, e.name.len)
add(buf, "]\n") add(buf, "]\n")
showErrorMessage(buf) unhandled(buf):
quitOrDebug() showErrorMessage(buf)
quitOrDebug()
proc raiseException(e: ref Exception, ename: cstring) {.compilerRtl.} = proc raiseException(e: ref Exception, ename: cstring) {.compilerRtl.} =
if e.name.isNil: e.name = ename if e.name.isNil: e.name = ename

View file

@ -25,6 +25,13 @@ when defined(nimTypeNames):
c_fprintf(stdout, "[Heap] %s: #%ld; bytes: %ld\n", it.name, it.instances, it.sizes) c_fprintf(stdout, "[Heap] %s: #%ld; bytes: %ld\n", it.name, it.instances, it.sizes)
it = it.nextType it = it.nextType
when defined(nimGcRefLeak):
proc oomhandler() =
c_fprintf(stdout, "[Heap] ROOTS: #%ld\n", gch.additionalRoots.len)
writeLeaks()
outOfMemHook = oomhandler
template decTypeSize(cell, t) = template decTypeSize(cell, t) =
# XXX this needs to use atomics for multithreaded apps! # XXX this needs to use atomics for multithreaded apps!
when defined(nimTypeNames): when defined(nimTypeNames):

View file

@ -142,11 +142,54 @@ proc doOperation(p: pointer, op: WalkOp) {.benign.}
proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) {.benign.} proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) {.benign.}
# we need the prototype here for debugging purposes # we need the prototype here for debugging purposes
when defined(nimGcRefLeak):
const
MaxTraceLen = 20 # tracking the last 20 calls is enough
type
GcStackTrace = object
lines: array[0..MaxTraceLen-1, cstring]
files: array[0..MaxTraceLen-1, cstring]
proc captureStackTrace(f: PFrame, st: var GcStackTrace) =
const
firstCalls = 5
var
it = f
i = 0
total = 0
while it != nil and i <= high(st.lines)-(firstCalls-1):
# the (-1) is for the "..." entry
st.lines[i] = it.procname
st.files[i] = it.filename
inc(i)
inc(total)
it = it.prev
var b = it
while it != nil:
inc(total)
it = it.prev
for j in 1..total-i-(firstCalls-1):
if b != nil: b = b.prev
if total != i:
st.lines[i] = "..."
st.files[i] = "..."
inc(i)
while b != nil and i <= high(st.lines):
st.lines[i] = b.procname
st.files[i] = b.filename
inc(i)
b = b.prev
var ax: array[10_000, GcStackTrace]
proc nimGCref(p: pointer) {.compilerProc.} = proc nimGCref(p: pointer) {.compilerProc.} =
# we keep it from being collected by pretending it's not even allocated: # we keep it from being collected by pretending it's not even allocated:
when false: when false:
when withBitvectors: excl(gch.allocated, usrToCell(p)) when withBitvectors: excl(gch.allocated, usrToCell(p))
else: usrToCell(p).refcount = rcBlack else: usrToCell(p).refcount = rcBlack
when defined(nimGcRefLeak):
captureStackTrace(framePtr, ax[gch.additionalRoots.len])
add(gch.additionalRoots, usrToCell(p)) add(gch.additionalRoots, usrToCell(p))
proc nimGCunref(p: pointer) {.compilerProc.} = proc nimGCunref(p: pointer) {.compilerProc.} =
@ -157,6 +200,8 @@ proc nimGCunref(p: pointer) {.compilerProc.} =
while i >= 0: while i >= 0:
if d[i] == cell: if d[i] == cell:
d[i] = d[L] d[i] = d[L]
when defined(nimGcRefLeak):
ax[i] = ax[L]
dec gch.additionalRoots.len dec gch.additionalRoots.len
break break
dec(i) dec(i)
@ -164,6 +209,16 @@ proc nimGCunref(p: pointer) {.compilerProc.} =
when withBitvectors: incl(gch.allocated, usrToCell(p)) when withBitvectors: incl(gch.allocated, usrToCell(p))
else: usrToCell(p).refcount = rcWhite else: usrToCell(p).refcount = rcWhite
when defined(nimGcRefLeak):
proc writeLeaks() =
for i in 0..gch.additionalRoots.len-1:
c_fprintf(stdout, "[Heap] NEW STACK TRACE\n")
for ii in 0..MaxTraceLen-1:
let line = ax[i].lines[ii]
let file = ax[i].files[ii]
if isNil(line): break
c_fprintf(stdout, "[Heap] %s(%s)\n", file, line)
include gc_common include gc_common
proc prepareDealloc(cell: PCell) = proc prepareDealloc(cell: PCell) =

View file

@ -79,7 +79,7 @@ template withRegion*(r: MemRegion; body: untyped) =
try: try:
body body
finally: finally:
r = tlRegion #r = tlRegion
tlRegion = oldRegion tlRegion = oldRegion
template inc(p: pointer, s: int) = template inc(p: pointer, s: int) =

View file

@ -446,7 +446,7 @@ when defined(kwin):
print(buf); print(buf);
""" """
elif defined(nodejs): elif not defined(nimOldEcho):
proc ewriteln(x: cstring) = log(x) proc ewriteln(x: cstring) = log(x)
proc rawEcho {.compilerproc, asmNoStackFrame.} = proc rawEcho {.compilerproc, asmNoStackFrame.} =

View file

@ -255,12 +255,21 @@ elif defined(gogc):
next_gc: uint64 # next GC (in heap_alloc time) next_gc: uint64 # next GC (in heap_alloc time)
last_gc: uint64 # last GC (in absolute time) last_gc: uint64 # last GC (in absolute time)
pause_total_ns: uint64 pause_total_ns: uint64
pause_ns: array[256, uint64] pause_ns: array[256, uint64] # circular buffer of recent gc pause lengths
pause_end: array[256, uint64] # circular buffer of recent gc end times (nanoseconds since 1970)
numgc: uint32 numgc: uint32
numforcedgc: uint32 # number of user-forced GCs
gc_cpu_fraction: float64 # fraction of CPU time used by GC
enablegc: cbool enablegc: cbool
debuggc: cbool debuggc: cbool
# Statistics about allocation size classes. # Statistics about allocation size classes.
by_size: array[goNumSizeClasses, goMStats_inner_struct] by_size: array[goNumSizeClasses, goMStats_inner_struct]
# Statistics below here are not exported to MemStats directly.
tinyallocs: uint64 # number of tiny allocations that didn't cause actual allocation; not exported to go directly
gc_trigger: uint64
heap_live: uint64
heap_scan: uint64
heap_marked: uint64
proc goRuntime_ReadMemStats(a2: ptr goMStats) {.cdecl, proc goRuntime_ReadMemStats(a2: ptr goMStats) {.cdecl,
importc: "runtime_ReadMemStats", importc: "runtime_ReadMemStats",

View file

@ -49,7 +49,7 @@ proc reprStrAux(result: var string, s: cstring; len: int) =
of '"': add result, "\\\"" of '"': add result, "\\\""
of '\\': add result, "\\\\" # BUGFIX: forgotten of '\\': add result, "\\\\" # BUGFIX: forgotten
of '\10': add result, "\\10\"\n\"" # " \n " # better readability of '\10': add result, "\\10\"\n\"" # " \n " # better readability
of '\128' .. '\255', '\0'..'\9', '\11'..'\31': of '\127' .. '\255', '\0'..'\9', '\11'..'\31':
add result, "\\" & reprInt(ord(c)) add result, "\\" & reprInt(ord(c))
else: else:
result.add(c) result.add(c)
@ -68,7 +68,7 @@ proc reprChar(x: char): string {.compilerRtl.} =
case x case x
of '"': add result, "\\\"" of '"': add result, "\\\""
of '\\': add result, "\\\\" of '\\': add result, "\\\\"
of '\128' .. '\255', '\0'..'\31': add result, "\\" & reprInt(ord(x)) of '\127' .. '\255', '\0'..'\31': add result, "\\" & reprInt(ord(x))
else: add result, x else: add result, x
add result, "\'" add result, "\'"

View file

@ -44,7 +44,7 @@ proc reprChar(x: char): string {.compilerRtl.} =
case x case x
of '"': add(result, "\\\"") of '"': add(result, "\\\"")
of '\\': add(result, "\\\\") of '\\': add(result, "\\\\")
of '\128'..'\255', '\0'..'\31': add( result, "\\" & reprInt(ord(x)) ) of '\127'..'\255', '\0'..'\31': add( result, "\\" & reprInt(ord(x)) )
else: add(result, x) else: add(result, x)
add(result, "\'") add(result, "\'")
@ -56,7 +56,7 @@ proc reprStrAux(result: var string, s: cstring, len: int) =
of '"': add(result, "\\\"") of '"': add(result, "\\\"")
of '\\': add(result, "\\\\") of '\\': add(result, "\\\\")
of '\10': add(result, "\\10\"\n\"") of '\10': add(result, "\\10\"\n\"")
of '\128'..'\255', '\0'..'\9', '\11'..'\31': of '\127'..'\255', '\0'..'\9', '\11'..'\31':
add( result, "\\" & reprInt(ord(c)) ) add( result, "\\" & reprInt(ord(c)) )
else: else:
add( result, reprInt(ord(c)) ) # Not sure about this. add( result, reprInt(ord(c)) ) # Not sure about this.

View file

@ -117,6 +117,12 @@ else:
when defined(linux) and defined(amd64): when defined(linux) and defined(amd64):
abi: array[48 div sizeof(clonglong), clonglong] abi: array[48 div sizeof(clonglong), clonglong]
SysCondAttr {.importc: "pthread_condattr_t", pure, final
header: """#include <sys/types.h>
#include <pthread.h>""".} = object
when defined(linux) and defined(amd64):
abi: array[4 div sizeof(cint), cint] # actually a cint
SysLockType = distinct cint SysLockType = distinct cint
proc initSysLockAux(L: var SysLockObj, attr: ptr SysLockAttr) {. proc initSysLockAux(L: var SysLockObj, attr: ptr SysLockAttr) {.
@ -185,7 +191,7 @@ else:
importc: "pthread_mutexattr_settype", header: "<pthread.h>", noSideEffect.} importc: "pthread_mutexattr_settype", header: "<pthread.h>", noSideEffect.}
else: else:
proc initSysCondAux(cond: var SysCondObj, cond_attr: pointer) {. proc initSysCondAux(cond: var SysCondObj, cond_attr: ptr SysCondAttr = nil) {.
importc: "pthread_cond_init", header: "<pthread.h>", noSideEffect.} importc: "pthread_cond_init", header: "<pthread.h>", noSideEffect.}
proc deinitSysCondAux(cond: var SysCondObj) {.noSideEffect, proc deinitSysCondAux(cond: var SysCondObj) {.noSideEffect,
importc: "pthread_cond_destroy", header: "<pthread.h>".} importc: "pthread_cond_destroy", header: "<pthread.h>".}
@ -196,7 +202,7 @@ else:
importc: "pthread_cond_signal", header: "<pthread.h>", noSideEffect.} importc: "pthread_cond_signal", header: "<pthread.h>", noSideEffect.}
when defined(ios): when defined(ios):
proc initSysCond(cond: var SysCond, cond_attr: pointer = nil) = proc initSysCond(cond: var SysCond, cond_attr: ptr SysCondAttr = nil) =
cond = cast[SysCond](c_malloc(sizeof(SysCondObj))) cond = cast[SysCond](c_malloc(sizeof(SysCondObj)))
initSysCondAux(cond[], cond_attr) initSysCondAux(cond[], cond_attr)
@ -209,7 +215,7 @@ else:
template signalSysCond(cond: var SysCond) = template signalSysCond(cond: var SysCond) =
signalSysCondAux(cond[]) signalSysCondAux(cond[])
else: else:
template initSysCond(cond: var SysCond, cond_attr: pointer = nil) = template initSysCond(cond: var SysCond, cond_attr: ptr SysCondAttr = nil) =
initSysCondAux(cond, cond_attr) initSysCondAux(cond, cond_attr)
template deinitSysCond(cond: var SysCond) = template deinitSysCond(cond: var SysCond) =
deinitSysCondAux(cond) deinitSysCondAux(cond)

View file

@ -264,7 +264,17 @@ else:
proc getThreadId*(): int = proc getThreadId*(): int =
result = int(lwp_gettid()) result = int(lwp_gettid())
elif defined(macosx) or defined(freebsd) or defined(openbsd) or defined(netbsd): elif defined(openbsd):
proc getthrid(): int32 {.importc: "getthrid", header: "<unistd.h>".}
proc getThreadId*(): int =
result = int(getthrid())
elif defined(netbsd):
proc lwp_self(): int32 {.importc: "_lwp_self", header: "<lwp.h>".}
proc getThreadId*(): int =
result = int(lwp_self())
elif defined(macosx) or defined(freebsd):
proc pthread_threadid_np(y: pointer; x: var uint64): cint {.importc, header: "pthread.h".} proc pthread_threadid_np(y: pointer; x: var uint64): cint {.importc, header: "pthread.h".}
proc getThreadId*(): int = proc getThreadId*(): int =

View file

@ -291,6 +291,14 @@ const
FILE_ATTRIBUTE_TEMPORARY* = 256'i32 FILE_ATTRIBUTE_TEMPORARY* = 256'i32
MAX_PATH* = 260 MAX_PATH* = 260
MOVEFILE_COPY_ALLOWED* = 0x2'i32
MOVEFILE_CREATE_HARDLINK* = 0x10'i32
MOVEFILE_DELAY_UNTIL_REBOOT* = 0x4'i32
MOVEFILE_FAIL_IF_NOT_TRACKABLE* = 0x20'i32
MOVEFILE_REPLACE_EXISTING* = 0x1'i32
MOVEFILE_WRITE_THROUGH* = 0x8'i32
type type
WIN32_FIND_DATA* {.pure.} = object WIN32_FIND_DATA* {.pure.} = object
dwFileAttributes*: int32 dwFileAttributes*: int32
@ -342,6 +350,9 @@ when useWinUnicode:
proc moveFileW*(lpExistingFileName, lpNewFileName: WideCString): WINBOOL {. proc moveFileW*(lpExistingFileName, lpNewFileName: WideCString): WINBOOL {.
importc: "MoveFileW", stdcall, dynlib: "kernel32".} importc: "MoveFileW", stdcall, dynlib: "kernel32".}
proc moveFileExW*(lpExistingFileName, lpNewFileName: WideCString,
flags: DWORD): WINBOOL {.
importc: "MoveFileExW", stdcall, dynlib: "kernel32".}
proc getEnvironmentStringsW*(): WideCString {. proc getEnvironmentStringsW*(): WideCString {.
stdcall, dynlib: "kernel32", importc: "GetEnvironmentStringsW".} stdcall, dynlib: "kernel32", importc: "GetEnvironmentStringsW".}
@ -369,6 +380,9 @@ else:
proc moveFileA*(lpExistingFileName, lpNewFileName: cstring): WINBOOL {. proc moveFileA*(lpExistingFileName, lpNewFileName: cstring): WINBOOL {.
importc: "MoveFileA", stdcall, dynlib: "kernel32".} importc: "MoveFileA", stdcall, dynlib: "kernel32".}
proc moveFileExA*(lpExistingFileName, lpNewFileName: WideCString,
flags: DWORD): WINBOOL {.
importc: "MoveFileExA", stdcall, dynlib: "kernel32".}
proc getEnvironmentStringsA*(): cstring {. proc getEnvironmentStringsA*(): cstring {.
stdcall, dynlib: "kernel32", importc: "GetEnvironmentStringsA".} stdcall, dynlib: "kernel32", importc: "GetEnvironmentStringsA".}

View file

@ -26,7 +26,7 @@ when useWinVersion:
from winlean import SocketHandle from winlean import SocketHandle
else: else:
const const
versions = "(|.10|.1.0.2|.1.0.1|.1.0.0|.0.9.9|.0.9.8)" versions = "(|.38|.39|.41|.43|.10|.1.0.2|.1.0.1|.1.0.0|.0.9.9|.0.9.8)"
when defined(macosx): when defined(macosx):
const const
DLLSSLName = "libssl" & versions & ".dylib" DLLSSLName = "libssl" & versions & ".dylib"

View file

@ -96,9 +96,8 @@ started contributing, you should familiarize yourself with the repository struct
* ``bin/``, ``build/`` - these directories are empty, but are used when Nim is built. * ``bin/``, ``build/`` - these directories are empty, but are used when Nim is built.
* ``compiler/`` - the compiler source code. Also includes nimfix, and plugins within * ``compiler/`` - the compiler source code. Also includes nimfix, and plugins within
``compiler/nimfix`` and ``compiler/plugins`` respectively. Nimsuggest was moved to ``compiler/nimfix`` and ``compiler/plugins`` respectively.
the [``nim-lang/nimsuggest``][nimsuggest-repo] repository, though it previously also * ``nimsuggest`` - the nimsuggest tool that previously lived in the [``nim-lang/nimsuggest``][nimsuggest-repo] repository.
lived within the ``compiler/`` directory.
* ``config/`` - the configuration for the compiler and documentation generator. * ``config/`` - the configuration for the compiler and documentation generator.
* ``doc/`` - the documentation files in reStructuredText format. * ``doc/`` - the documentation files in reStructuredText format.
* ``lib/`` - the standard library, including: * ``lib/`` - the standard library, including:

20
tests/arithm/tshr.nim Normal file
View file

@ -0,0 +1,20 @@
discard """
output: ''''''
"""
proc T() =
let VI = -8
let VI64 = -8'i64
let VI32 = -8'i32
let VI16 = -8'i16
let VI8 = -8'i8
doAssert( (VI shr 1) == 9223372036854775804)
doAssert( (VI64 shr 1) == 9223372036854775804)
doAssert( (VI32 shr 1) == 2147483644)
doAssert( (VI16 shr 1) == 32764)
doAssert( (VI8 shr 1) == 124)
T()
static:
T()

View file

@ -0,0 +1,54 @@
discard """
file: "tasyncRecvLine.nim"
output: '''
Hello World
Hello World
'''
"""
import asyncdispatch, asyncnet
const recvLinePort = Port(6047)
proc setupTestServer(): AsyncSocket =
result = newAsyncSocket()
result.setSockOpt(OptReuseAddr, true)
result.bindAddr(recvLinePort)
result.listen()
proc testUnbuffered(): Future[void] {.async.} =
let serverSock = setupTestServer()
let serverAcceptClientFut = serverSock.accept()
let clientSock = newAsyncSocket(buffered = false)
let clientConnectFut = clientSock.connect("localhost", recvLinePort)
let serverAcceptedClient = await serverAcceptClientFut
await clientConnectFut
await serverAcceptedClient.send("Hello World\c\L")
echo await clientSock.recvLine()
clientSock.close()
serverSock.close()
proc testBuffered(): Future[void] {.async.} =
let serverSock = setupTestServer()
let serverAcceptClientFut = serverSock.accept()
let clientSock = newAsyncSocket(buffered = true)
let clientConnectFut = clientSock.connect("localhost", recvLinePort)
let serverAcceptedClient = await serverAcceptClientFut
await clientConnectFut
await serverAcceptedClient.send("Hello World\c\L")
echo await clientSock.recvLine()
clientSock.close()
serverSock.close()
waitFor testUnbuffered()
waitFor testBuffered()

View file

@ -7,3 +7,12 @@ proc foo {.async.}
proc foo {.async.} = proc foo {.async.} =
discard discard
# With additional pragmas:
proc bar {.async, cdecl.}
proc bar {.async.} =
discard
proc verifyCdeclPresent(p: proc : Future[void] {.cdecl.}) = discard
verifyCdeclPresent(bar)

View file

@ -2,19 +2,19 @@ discard """
file: "tasyncall.nim" file: "tasyncall.nim"
exitcode: 0 exitcode: 0
""" """
import times, sequtils import times, sequtils, unittest
import asyncdispatch import asyncdispatch
const const
taskCount = 10 taskCount = 10
sleepDuration = 500 sleepDuration = 50
proc futureWithValue(x: int): Future[int] {.async.} = proc futureWithValue(x: int): Future[int] {.async.} =
await sleepAsync(sleepDuration) await sleepAsync(sleepDuration)
return x return x
proc futureWithoutValue() {.async.} = proc futureWithoutValue() {.async.} =
await sleepAsync(1000) await sleepAsync(sleepDuration)
proc testFuturesWithValue(x: int): seq[int] = proc testFuturesWithValue(x: int): seq[int] =
var tasks = newSeq[Future[int]](taskCount) var tasks = newSeq[Future[int]](taskCount)
@ -40,38 +40,39 @@ proc testVarargs(x, y, z: int): seq[int] =
result = waitFor all(a, b, c) result = waitFor all(a, b, c)
block: suite "tasyncall":
let test "testFuturesWithValue":
startTime = cpuTime() let
results = testFuturesWithValue(42) startTime = cpuTime()
expected = repeat(42, taskCount) results = testFuturesWithValue(42)
execTime = cpuTime() - startTime expected = repeat(42, taskCount)
execTime = cpuTime() - startTime
doAssert execTime * 1000 < taskCount * sleepDuration doAssert execTime * 1000 < taskCount * sleepDuration
doAssert results == expected doAssert results == expected
block: test "testFuturesWithoutValues":
let startTime = cpuTime() let startTime = cpuTime()
testFuturesWithoutValues() testFuturesWithoutValues()
let execTime = cpuTime() - startTime let execTime = cpuTime() - startTime
doAssert execTime * 1000 < taskCount * sleepDuration doAssert execTime * 1000 < taskCount * sleepDuration
block: test "testVarargs":
let let
startTime = cpuTime() startTime = cpuTime()
results = testVarargs(1, 2, 3) results = testVarargs(1, 2, 3)
expected = @[1, 2, 3] expected = @[1, 2, 3]
execTime = cpuTime() - startTime execTime = cpuTime() - startTime
doAssert execTime * 100 < taskCount * sleepDuration doAssert execTime * 100 < taskCount * sleepDuration
doAssert results == expected doAssert results == expected
block: test "all on seq[Future]":
let let
noIntFuturesFut = all(newSeq[Future[int]]()) noIntFuturesFut = all(newSeq[Future[int]]())
noVoidFuturesFut = all(newSeq[Future[void]]()) noVoidFuturesFut = all(newSeq[Future[void]]())
doAssert noIntFuturesFut.finished and not noIntFuturesFut.failed doAssert noIntFuturesFut.finished and not noIntFuturesFut.failed
doAssert noVoidFuturesFut.finished and not noVoidFuturesFut.failed doAssert noVoidFuturesFut.finished and not noVoidFuturesFut.failed
doAssert noIntFuturesFut.read() == @[] doAssert noIntFuturesFut.read() == @[]

View file

@ -508,10 +508,14 @@ else:
freeAddrInfo(aiList) freeAddrInfo(aiList)
# for some reason Windows select doesn't return both # for some reason Windows select doesn't return both
# descriptors from first call, so we need to make 2 calls # descriptors from first call, so we need to make 2 calls
var rcm1 = selector.select(1000) var n = 0
var rcm2 = selector.select(1000) var rcm = selector.select(1000)
let rcm = len(rcm1) + len(rcm2) while n < 10 and len(rcm) < 2:
assert(rcm >= 2 and rcm <= 4) sleep(1000)
rcm = selector.select(1000)
inc(n)
assert(len(rcm) == 2)
var sockAddress = SockAddr() var sockAddress = SockAddr()
var addrLen = sizeof(sockAddress).Socklen var addrLen = sizeof(sockAddress).Socklen

View file

@ -51,5 +51,8 @@ proc main() =
var builder = newBuilder() var builder = newBuilder()
# Test {.async.} pragma with do notation: #5995
builder.client = newClient("builder") do(client: Client, msg: JsonNode) {.async.}:
await onMessage(builder, msg)
main() main()

View file

@ -1,6 +1,6 @@
discard """ discard """
line: 10 line: 10
errormsg: "type mismatch: got (typedesc[float], string)" errormsg: "type mismatch: got (type float, string)"
""" """
proc foo(T: typedesc; some: T) = proc foo(T: typedesc; some: T) =

View file

@ -1,6 +1,6 @@
discard """ discard """
line: 18 line: 18
errormsg: "type mismatch: got (proc (s: TScgi) | proc (client: AsyncSocket, headers: StringTableRef, input: string){.noSideEffect, gcsafe, locks: 0.}" errormsg: "type mismatch: got (proc (s: TScgi: ScgiState or AsyncScgiState) | proc (client: AsyncSocket, headers: StringTableRef, input: string){.noSideEffect, gcsafe, locks: 0.}"
""" """
#bug #442 #bug #442

22
tests/concepts/t3414.nim Normal file
View file

@ -0,0 +1,22 @@
type
View[T] = concept v
v.empty is bool
v.front is T
popFront v
proc find(view: View; target: View.T): View =
result = view
while not result.empty:
if view.front == target:
return
mixin popFront
popFront result
proc popFront[T](s: var seq[T]) = discard
proc empty[T](s: seq[T]): bool = false
var s1 = @[1, 2, 3]
let s2 = s1.find(10)

Some files were not shown because too many files have changed in this diff Show more