nicer error messages (untested)
This commit is contained in:
parent
fdf996925b
commit
7a48942719
9 changed files with 82 additions and 50 deletions
|
|
@ -40,8 +40,7 @@ proc semParamList(c: PContext, n, genericParams: PNode, s: PSym)
|
||||||
proc addParams(c: PContext, n: PNode, kind: TSymKind)
|
proc addParams(c: PContext, n: PNode, kind: TSymKind)
|
||||||
proc maybeAddResult(c: PContext, s: PSym, n: PNode)
|
proc maybeAddResult(c: PContext, s: PSym, n: PNode)
|
||||||
proc instGenericContainer(c: PContext, n: PNode, header: PType): PType
|
proc instGenericContainer(c: PContext, n: PNode, header: PType): PType
|
||||||
proc tryExpr(c: PContext, n: PNode,
|
proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode
|
||||||
flags: TExprFlags = {}, bufferErrors = false): PNode
|
|
||||||
proc fixImmediateParams(n: PNode): PNode
|
proc fixImmediateParams(n: PNode): PNode
|
||||||
proc activate(c: PContext, n: PNode)
|
proc activate(c: PContext, n: PNode)
|
||||||
proc semQuoteAst(c: PContext, n: PNode): PNode
|
proc semQuoteAst(c: PContext, n: PNode): PNode
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
|
||||||
initialBinding: PNode,
|
initialBinding: PNode,
|
||||||
filter: TSymKinds,
|
filter: TSymKinds,
|
||||||
best, alt: var TCandidate,
|
best, alt: var TCandidate,
|
||||||
errors: var seq[string]) =
|
errors: var CandidateErrors) =
|
||||||
var o: TOverloadIter
|
var o: TOverloadIter
|
||||||
var sym = initOverloadIter(o, c, headSymbol)
|
var sym = initOverloadIter(o, c, headSymbol)
|
||||||
var symScope = o.lastOverloadScope
|
var symScope = o.lastOverloadScope
|
||||||
|
|
@ -58,10 +58,10 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
|
||||||
z.calleeSym = sym
|
z.calleeSym = sym
|
||||||
matches(c, n, orig, z)
|
matches(c, n, orig, z)
|
||||||
if errors != nil:
|
if errors != nil:
|
||||||
errors.safeAdd(getProcHeader(sym))
|
errors.safeAdd(sym)
|
||||||
if z.errors != nil:
|
if z.errors != nil:
|
||||||
for err in z.errors:
|
for err in z.errors:
|
||||||
errors[errors.len - 1].add("\n " & err)
|
errors.add(err)
|
||||||
if z.state == csMatch:
|
if z.state == csMatch:
|
||||||
# little hack so that iterators are preferred over everything else:
|
# little hack so that iterators are preferred over everything else:
|
||||||
if sym.kind in skIterators: inc(z.exactMatches, 200)
|
if sym.kind in skIterators: inc(z.exactMatches, 200)
|
||||||
|
|
@ -74,7 +74,7 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
|
||||||
else: discard
|
else: discard
|
||||||
sym = nextOverloadIter(o, c, headSymbol)
|
sym = nextOverloadIter(o, c, headSymbol)
|
||||||
|
|
||||||
proc notFoundError*(c: PContext, n: PNode, errors: seq[string]) =
|
proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) =
|
||||||
# Gives a detailed error message; this is separated from semOverloadedCall,
|
# Gives a detailed error message; this is separated from semOverloadedCall,
|
||||||
# as semOverlodedCall is already pretty slow (and we need this information
|
# as semOverlodedCall is already pretty slow (and we need this information
|
||||||
# only in case of an error).
|
# only in case of an error).
|
||||||
|
|
@ -83,18 +83,39 @@ proc notFoundError*(c: PContext, n: PNode, errors: seq[string]) =
|
||||||
globalError(n.info, errTypeMismatch, "")
|
globalError(n.info, errTypeMismatch, "")
|
||||||
if errors.len == 0:
|
if errors.len == 0:
|
||||||
localError(n.info, errExprXCannotBeCalled, n[0].renderTree)
|
localError(n.info, errExprXCannotBeCalled, n[0].renderTree)
|
||||||
var result = msgKindToString(errTypeMismatch)
|
|
||||||
add(result, describeArgs(c, n, 1))
|
|
||||||
add(result, ')')
|
|
||||||
|
|
||||||
|
# to avoid confusing errors like:
|
||||||
|
# got (SslPtr, SocketHandle)
|
||||||
|
# but expected one of:
|
||||||
|
# openssl.SSL_set_fd(ssl: SslPtr, fd: SocketHandle): cint
|
||||||
|
# we do a pre-analysis. If all types produce the same string, we will add
|
||||||
|
# module information.
|
||||||
|
let proto = describeArgs(c, n, 1, preferName)
|
||||||
|
|
||||||
|
var prefer = preferName
|
||||||
|
for err in errors:
|
||||||
|
var errProto = "("
|
||||||
|
let n = err.typ.n
|
||||||
|
for i in countup(1, n.len - 1):
|
||||||
|
var p = n.sons[i]
|
||||||
|
if p.kind == nkSym:
|
||||||
|
add(errProto, typeToString(p.sym.typ, prefer))
|
||||||
|
if i != n.len-1: add(errProto, ", ")
|
||||||
|
# else: ignore internal error as we're already in error handling mode
|
||||||
|
add(errProto, ')')
|
||||||
|
if errProto == proto:
|
||||||
|
prefer = preferModuleInfo
|
||||||
|
break
|
||||||
|
# now use the information stored in 'prefer' to produce a nice error message:
|
||||||
|
var result = msgKindToString(errTypeMismatch)
|
||||||
|
add(result, describeArgs(c, n, 1, prefer))
|
||||||
|
add(result, ')')
|
||||||
var candidates = ""
|
var candidates = ""
|
||||||
for err in errors:
|
for err in errors:
|
||||||
add(candidates, err)
|
add(candidates, err.getProcHeader(prefer))
|
||||||
add(candidates, "\n")
|
add(candidates, "\n")
|
||||||
|
|
||||||
if candidates != "":
|
if candidates != "":
|
||||||
add(result, "\n" & msgKindToString(errButExpected) & "\n" & candidates)
|
add(result, "\n" & msgKindToString(errButExpected) & "\n" & candidates)
|
||||||
|
|
||||||
localError(n.info, errGenerated, result)
|
localError(n.info, errGenerated, result)
|
||||||
|
|
||||||
proc gatherUsedSyms(c: PContext, usedSyms: var seq[PNode]) =
|
proc gatherUsedSyms(c: PContext, usedSyms: var seq[PNode]) =
|
||||||
|
|
@ -114,7 +135,7 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
|
||||||
else:
|
else:
|
||||||
initialBinding = nil
|
initialBinding = nil
|
||||||
|
|
||||||
var errors: seq[string]
|
var errors: CandidateErrors
|
||||||
var usedSyms: seq[PNode]
|
var usedSyms: seq[PNode]
|
||||||
|
|
||||||
template pickBest(headSymbol: expr) =
|
template pickBest(headSymbol: expr) =
|
||||||
|
|
|
||||||
|
|
@ -72,8 +72,7 @@ type
|
||||||
libs*: TLinkedList # all libs used by this module
|
libs*: TLinkedList # all libs used by this module
|
||||||
semConstExpr*: proc (c: PContext, n: PNode): PNode {.nimcall.} # for the pragmas
|
semConstExpr*: proc (c: PContext, n: PNode): PNode {.nimcall.} # for the pragmas
|
||||||
semExpr*: proc (c: PContext, n: PNode, flags: TExprFlags = {}): PNode {.nimcall.}
|
semExpr*: proc (c: PContext, n: PNode, flags: TExprFlags = {}): PNode {.nimcall.}
|
||||||
semTryExpr*: proc (c: PContext, n: PNode,flags: TExprFlags = {},
|
semTryExpr*: proc (c: PContext, n: PNode,flags: TExprFlags = {}): PNode {.nimcall.}
|
||||||
bufferErrors = false): PNode {.nimcall.}
|
|
||||||
semTryConstExpr*: proc (c: PContext, n: PNode): PNode {.nimcall.}
|
semTryConstExpr*: proc (c: PContext, n: PNode): PNode {.nimcall.}
|
||||||
semOperand*: proc (c: PContext, n: PNode, flags: TExprFlags = {}): PNode {.nimcall.}
|
semOperand*: proc (c: PContext, n: PNode, flags: TExprFlags = {}): PNode {.nimcall.}
|
||||||
semConstBoolExpr*: proc (c: PContext, n: PNode): PNode {.nimcall.} # XXX bite the bullet
|
semConstBoolExpr*: proc (c: PContext, n: PNode): PNode {.nimcall.} # XXX bite the bullet
|
||||||
|
|
|
||||||
|
|
@ -1551,8 +1551,7 @@ proc semQuoteAst(c: PContext, n: PNode): PNode =
|
||||||
newNode(nkCall, n.info, quotes)])
|
newNode(nkCall, n.info, quotes)])
|
||||||
result = semExpandToAst(c, result)
|
result = semExpandToAst(c, result)
|
||||||
|
|
||||||
proc tryExpr(c: PContext, n: PNode,
|
proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
||||||
flags: TExprFlags = {}, bufferErrors = false): PNode =
|
|
||||||
# watch out, hacks ahead:
|
# watch out, hacks ahead:
|
||||||
let oldErrorCount = msgs.gErrorCounter
|
let oldErrorCount = msgs.gErrorCounter
|
||||||
let oldErrorMax = msgs.gErrorMax
|
let oldErrorMax = msgs.gErrorMax
|
||||||
|
|
@ -1566,7 +1565,7 @@ proc tryExpr(c: PContext, n: PNode,
|
||||||
let oldOwnerLen = len(gOwners)
|
let oldOwnerLen = len(gOwners)
|
||||||
let oldGenerics = c.generics
|
let oldGenerics = c.generics
|
||||||
let oldErrorOutputs = errorOutputs
|
let oldErrorOutputs = errorOutputs
|
||||||
errorOutputs = if bufferErrors: {eInMemory} else: {}
|
#errorOutputs = if bufferErrors: {eInMemory} else: {}
|
||||||
let oldContextLen = msgs.getInfoContextLen()
|
let oldContextLen = msgs.getInfoContextLen()
|
||||||
|
|
||||||
let oldInGenericContext = c.inGenericContext
|
let oldInGenericContext = c.inGenericContext
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ type
|
||||||
TCandidateState* = enum
|
TCandidateState* = enum
|
||||||
csEmpty, csMatch, csNoMatch
|
csEmpty, csMatch, csNoMatch
|
||||||
|
|
||||||
|
CandidateErrors* = seq[PSym]
|
||||||
TCandidate* {.final.} = object
|
TCandidate* {.final.} = object
|
||||||
c*: PContext
|
c*: PContext
|
||||||
exactMatches*: int # also misused to prefer iters over procs
|
exactMatches*: int # also misused to prefer iters over procs
|
||||||
|
|
@ -45,7 +46,7 @@ type
|
||||||
# a distrinct type
|
# a distrinct type
|
||||||
typedescMatched: bool
|
typedescMatched: bool
|
||||||
inheritancePenalty: int # to prefer closest father object type
|
inheritancePenalty: int # to prefer closest father object type
|
||||||
errors*: seq[string] # additional clarifications to be displayed to the
|
errors*: CandidateErrors # additional clarifications to be displayed to the
|
||||||
# user if overload resolution fails
|
# user if overload resolution fails
|
||||||
|
|
||||||
TTypeRelation* = enum # order is important!
|
TTypeRelation* = enum # order is important!
|
||||||
|
|
@ -202,16 +203,17 @@ proc writeMatches*(c: TCandidate) =
|
||||||
writeln(stdout, "intconv matches: " & $c.intConvMatches)
|
writeln(stdout, "intconv matches: " & $c.intConvMatches)
|
||||||
writeln(stdout, "generic matches: " & $c.genericMatches)
|
writeln(stdout, "generic matches: " & $c.genericMatches)
|
||||||
|
|
||||||
proc argTypeToString(arg: PNode): string =
|
proc argTypeToString(arg: PNode; prefer: TPreferedDesc): string =
|
||||||
if arg.kind in nkSymChoices:
|
if arg.kind in nkSymChoices:
|
||||||
result = typeToString(arg[0].typ)
|
result = typeToString(arg[0].typ, prefer)
|
||||||
for i in 1 .. <arg.len:
|
for i in 1 .. <arg.len:
|
||||||
result.add(" | ")
|
result.add(" | ")
|
||||||
result.add typeToString(arg[i].typ)
|
result.add typeToString(arg[i].typ, prefer)
|
||||||
else:
|
else:
|
||||||
result = arg.typ.typeToString
|
result = arg.typ.typeToString(prefer)
|
||||||
|
|
||||||
proc describeArgs*(c: PContext, n: PNode, startIdx = 1): string =
|
proc describeArgs*(c: PContext, n: PNode, startIdx = 1;
|
||||||
|
prefer: TPreferedDesc = preferName): string =
|
||||||
result = ""
|
result = ""
|
||||||
for i in countup(startIdx, n.len - 1):
|
for i in countup(startIdx, n.len - 1):
|
||||||
var arg = n.sons[i]
|
var arg = n.sons[i]
|
||||||
|
|
@ -227,7 +229,7 @@ proc describeArgs*(c: PContext, n: PNode, startIdx = 1): string =
|
||||||
arg = c.semOperand(c, n.sons[i])
|
arg = c.semOperand(c, n.sons[i])
|
||||||
n.sons[i] = arg
|
n.sons[i] = arg
|
||||||
if arg.typ.kind == tyError: return
|
if arg.typ.kind == tyError: return
|
||||||
add(result, argTypeToString(arg))
|
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 typeRel*(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation
|
||||||
|
|
@ -480,8 +482,8 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate,
|
||||||
dummyParam.typ = dummyType
|
dummyParam.typ = dummyType
|
||||||
addDecl(c, dummyParam)
|
addDecl(c, dummyParam)
|
||||||
|
|
||||||
var checkedBody = c.semTryExpr(c, body.n[3].copyTree, bufferErrors = false)
|
var checkedBody = c.semTryExpr(c, body.n[3].copyTree)
|
||||||
m.errors = bufferedMsgs
|
#m.errors = bufferedMsgs
|
||||||
clearBufferedMsgs()
|
clearBufferedMsgs()
|
||||||
if checkedBody == nil: return isNone
|
if checkedBody == nil: return isNone
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,10 +17,9 @@ proc lastOrd*(t: PType): BiggestInt
|
||||||
proc lengthOrd*(t: PType): BiggestInt
|
proc lengthOrd*(t: PType): BiggestInt
|
||||||
type
|
type
|
||||||
TPreferedDesc* = enum
|
TPreferedDesc* = enum
|
||||||
preferName, preferDesc, preferExported
|
preferName, preferDesc, preferExported, preferModuleInfo
|
||||||
|
|
||||||
proc typeToString*(typ: PType, prefer: TPreferedDesc = preferName): string
|
proc typeToString*(typ: PType; prefer: TPreferedDesc = preferName): string
|
||||||
proc getProcHeader*(sym: PSym): string
|
|
||||||
proc base*(t: PType): PType
|
proc base*(t: PType): PType
|
||||||
# ------------------- type iterator: ----------------------------------------
|
# ------------------- type iterator: ----------------------------------------
|
||||||
type
|
type
|
||||||
|
|
@ -121,7 +120,7 @@ proc isCompatibleToCString(a: PType): bool =
|
||||||
(a.sons[1].kind == tyChar):
|
(a.sons[1].kind == tyChar):
|
||||||
result = true
|
result = true
|
||||||
|
|
||||||
proc getProcHeader(sym: PSym): string =
|
proc getProcHeader*(sym: PSym; prefer: TPreferedDesc = preferName): string =
|
||||||
result = sym.owner.name.s & '.' & sym.name.s & '('
|
result = sym.owner.name.s & '.' & sym.name.s & '('
|
||||||
var n = sym.typ.n
|
var n = sym.typ.n
|
||||||
for i in countup(1, sonsLen(n) - 1):
|
for i in countup(1, sonsLen(n) - 1):
|
||||||
|
|
@ -129,12 +128,13 @@ proc getProcHeader(sym: PSym): string =
|
||||||
if p.kind == nkSym:
|
if p.kind == nkSym:
|
||||||
add(result, p.sym.name.s)
|
add(result, p.sym.name.s)
|
||||||
add(result, ": ")
|
add(result, ": ")
|
||||||
add(result, typeToString(p.sym.typ))
|
add(result, typeToString(p.sym.typ, prefer))
|
||||||
if i != sonsLen(n)-1: add(result, ", ")
|
if i != sonsLen(n)-1: add(result, ", ")
|
||||||
else:
|
else:
|
||||||
internalError("getProcHeader")
|
internalError("getProcHeader")
|
||||||
add(result, ')')
|
add(result, ')')
|
||||||
if n.sons[0].typ != nil: result.add(": " & typeToString(n.sons[0].typ))
|
if n.sons[0].typ != nil:
|
||||||
|
result.add(": " & typeToString(n.sons[0].typ, prefer))
|
||||||
|
|
||||||
proc elemType*(t: PType): PType =
|
proc elemType*(t: PType): PType =
|
||||||
assert(t != nil)
|
assert(t != nil)
|
||||||
|
|
@ -415,10 +415,14 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
|
||||||
var t = typ
|
var t = typ
|
||||||
result = ""
|
result = ""
|
||||||
if t == nil: return
|
if t == nil: return
|
||||||
if prefer == preferName and t.sym != nil and sfAnon notin t.sym.flags:
|
if prefer in {preferName, preferModuleInfo} and t.sym != nil and
|
||||||
|
sfAnon notin t.sym.flags:
|
||||||
if t.kind == tyInt and isIntLit(t):
|
if t.kind == tyInt and isIntLit(t):
|
||||||
return t.sym.name.s & " literal(" & $t.n.intVal & ")"
|
return t.sym.name.s & " literal(" & $t.n.intVal & ")"
|
||||||
|
if prefer == preferName:
|
||||||
return t.sym.name.s
|
return t.sym.name.s
|
||||||
|
else:
|
||||||
|
return t.sym.skipGenericOwner.name.s & '.' & t.sym.name.s
|
||||||
case t.kind
|
case t.kind
|
||||||
of tyInt:
|
of tyInt:
|
||||||
if not isIntLit(t) or prefer == preferExported:
|
if not isIntLit(t) or prefer == preferExported:
|
||||||
|
|
@ -493,7 +497,8 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
|
||||||
of tyOpenArray:
|
of tyOpenArray:
|
||||||
result = "openarray[" & typeToString(t.sons[0]) & ']'
|
result = "openarray[" & typeToString(t.sons[0]) & ']'
|
||||||
of tyDistinct:
|
of tyDistinct:
|
||||||
result = "distinct " & typeToString(t.sons[0], preferName)
|
result = "distinct " & typeToString(t.sons[0],
|
||||||
|
if prefer == preferModuleInfo: preferModuleInfo else: preferName)
|
||||||
of tyTuple:
|
of tyTuple:
|
||||||
# we iterate over t.sons here, because t.n may be nil
|
# we iterate over t.sons here, because t.n may be nil
|
||||||
result = "tuple["
|
result = "tuple["
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@
|
||||||
|
|
||||||
{.push hints: off.}
|
{.push hints: off.}
|
||||||
|
|
||||||
|
include "system/inclrtl.nim"
|
||||||
include "system/hti.nim"
|
include "system/hti.nim"
|
||||||
|
|
||||||
{.pop.}
|
{.pop.}
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,7 @@ proc addFile*(z: var TZipArchive, dest, src: string) =
|
||||||
## may contain a path that will be created.
|
## may contain a path that will be created.
|
||||||
assert(z.mode != fmRead)
|
assert(z.mode != fmRead)
|
||||||
if not fileExists(src):
|
if not fileExists(src):
|
||||||
raise newException(EIO, "File '" & src & "' does not exist")
|
raise newException(IOError, "File '" & src & "' does not exist")
|
||||||
var zipsrc = zip_source_file(z.w, src, 0, -1)
|
var zipsrc = zip_source_file(z.w, src, 0, -1)
|
||||||
if zipsrc == nil:
|
if zipsrc == nil:
|
||||||
#echo("Dest: " & dest)
|
#echo("Dest: " & dest)
|
||||||
|
|
|
||||||
|
|
@ -2030,16 +2030,17 @@ when not defined(nimrodVM) and hostOS != "standalone":
|
||||||
## returns an informative string about the GC's activity. This may be useful
|
## returns an informative string about the GC's activity. This may be useful
|
||||||
## for tweaking.
|
## for tweaking.
|
||||||
|
|
||||||
proc GC_ref*[T](x: ref T) {.magic: "GCref", benign.}
|
# XXX mark these as 'locks: 0' once 0.10.0 has been released
|
||||||
proc GC_ref*[T](x: seq[T]) {.magic: "GCref", benign.}
|
proc GC_ref*[T](x: ref T) {.magic: "GCref", gcsafe.}
|
||||||
proc GC_ref*(x: string) {.magic: "GCref", benign.}
|
proc GC_ref*[T](x: seq[T]) {.magic: "GCref", gcsafe.}
|
||||||
|
proc GC_ref*(x: string) {.magic: "GCref", gcsafe.}
|
||||||
## marks the object `x` as referenced, so that it will not be freed until
|
## 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`,
|
## 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`.
|
## 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: ref T) {.magic: "GCunref", gcsafe.}
|
||||||
proc GC_unref*[T](x: seq[T]) {.magic: "GCunref", benign.}
|
proc GC_unref*[T](x: seq[T]) {.magic: "GCunref", gcsafe.}
|
||||||
proc GC_unref*(x: string) {.magic: "GCunref", benign.}
|
proc GC_unref*(x: string) {.magic: "GCunref", gcsafe.}
|
||||||
## see the documentation of `GC_ref`.
|
## see the documentation of `GC_ref`.
|
||||||
|
|
||||||
template accumulateResult*(iter: expr) =
|
template accumulateResult*(iter: expr) =
|
||||||
|
|
@ -2178,7 +2179,12 @@ when not declared(sysFatal):
|
||||||
e.msg = message & arg
|
e.msg = message & arg
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
proc getTypeInfo*[T](x: T): pointer {.magic: "GetTypeInfo", benign.}
|
when defined(nimlocks):
|
||||||
|
proc getTypeInfo*[T](x: T): pointer {.magic: "GetTypeInfo", gcsafe, locks: 0.}
|
||||||
|
## get type information for `x`. Ordinary code should not use this, but
|
||||||
|
## the `typeinfo` module instead.
|
||||||
|
else:
|
||||||
|
proc getTypeInfo*[T](x: T): pointer {.magic: "GetTypeInfo", gcsafe.}
|
||||||
## get type information for `x`. Ordinary code should not use this, but
|
## get type information for `x`. Ordinary code should not use this, but
|
||||||
## the `typeinfo` module instead.
|
## the `typeinfo` module instead.
|
||||||
|
|
||||||
|
|
@ -2379,7 +2385,7 @@ when not defined(JS): #and not defined(NimrodVM):
|
||||||
|
|
||||||
when not defined(booting):
|
when not defined(booting):
|
||||||
proc writeln*[Ty](f: File, x: varargs[Ty, `$`]) {.inline,
|
proc writeln*[Ty](f: File, x: varargs[Ty, `$`]) {.inline,
|
||||||
tags: [WriteIOEffect], benign.}
|
tags: [WriteIOEffect], gcsafe, locks: 0.}
|
||||||
## writes the values `x` to `f` and then writes "\n".
|
## writes the values `x` to `f` and then writes "\n".
|
||||||
## May throw an IO exception.
|
## May throw an IO exception.
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue