diff options
Diffstat (limited to 'compiler')
-rw-r--r-- | compiler/ast.nim | 8 | ||||
-rw-r--r-- | compiler/cgen.nim | 2 | ||||
-rw-r--r-- | compiler/charsets.nim | 49 | ||||
-rw-r--r-- | compiler/condsyms.nim | 1 | ||||
-rw-r--r-- | compiler/lowerings.nim | 8 | ||||
-rw-r--r-- | compiler/msgs.nim | 7 | ||||
-rw-r--r-- | compiler/nversion.nim | 6 | ||||
-rw-r--r-- | compiler/options.nim | 2 | ||||
-rw-r--r-- | compiler/parsecfg.nim | 346 | ||||
-rw-r--r-- | compiler/passes.nim | 5 | ||||
-rw-r--r-- | compiler/pendx.nim | 18 | ||||
-rw-r--r-- | compiler/pragmas.nim | 13 | ||||
-rw-r--r-- | compiler/sem.nim | 10 | ||||
-rw-r--r-- | compiler/semdata.nim | 2 | ||||
-rw-r--r-- | compiler/semfold.nim | 2 | ||||
-rw-r--r-- | compiler/seminst.nim | 3 | ||||
-rw-r--r-- | compiler/sempass2.nim | 20 | ||||
-rw-r--r-- | compiler/semthreads.nim | 390 | ||||
-rw-r--r-- | compiler/semtypes.nim | 13 | ||||
-rw-r--r-- | compiler/semtypinst.nim | 7 | ||||
-rw-r--r-- | compiler/sigmatch.nim | 7 | ||||
-rw-r--r-- | compiler/suggest.nim | 1 | ||||
-rw-r--r-- | compiler/syntaxes.nim | 2 | ||||
-rw-r--r-- | compiler/types.nim | 9 | ||||
-rw-r--r-- | compiler/typesrenderer.nim | 8 | ||||
-rw-r--r-- | compiler/vm.nim | 10 | ||||
-rw-r--r-- | compiler/vmgen.nim | 20 |
27 files changed, 92 insertions, 877 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim index e575f317d..97f48b253 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -1311,6 +1311,10 @@ proc skipTypes*(t: PType, kinds: TTypeKinds): PType = result = t while result.kind in kinds: result = lastSon(result) +proc isGCedMem*(t: PType): bool {.inline.} = + result = t.kind in {tyString, tyRef, tySequence} or + t.kind == tyProc and t.callConv == ccClosure + proc propagateToOwner*(owner, elem: PType) = const HaveTheirOwnEmpty = {tySequence, tySet} owner.flags = owner.flags + (elem.flags * {tfHasShared, tfHasMeta}) @@ -1331,9 +1335,7 @@ proc propagateToOwner*(owner, elem: PType) = owner.flags.incl tfHasMeta if owner.kind != tyProc: - if elem.kind in {tyString, tyRef, tySequence} or - elem.kind == tyProc and elem.callConv == ccClosure or - tfHasGCedMem in elem.flags: + if elem.isGCedMem or tfHasGCedMem in elem.flags: owner.flags.incl tfHasGCedMem proc rawAddSon*(father, son: PType) = diff --git a/compiler/cgen.nim b/compiler/cgen.nim index f64ebacfb..8d66d7a3b 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -730,7 +730,7 @@ proc retIsNotVoid(s: PSym): bool = result = (s.typ.sons[0] != nil) and not isInvalidReturnType(s.typ.sons[0]) proc initFrame(p: BProc, procname, filename: PRope): PRope = - discard cgsym(p.module, "pushFrame") + discard cgsym(p.module, "nimFrame") if p.maxFrameLen > 0: discard cgsym(p.module, "TVarSlot") result = rfmt(nil, "\tnimfrs($1, $2, $3, $4)$N", diff --git a/compiler/charsets.nim b/compiler/charsets.nim deleted file mode 100644 index d3d00b687..000000000 --- a/compiler/charsets.nim +++ /dev/null @@ -1,49 +0,0 @@ -# -# -# The Nimrod Compiler -# (c) Copyright 2012 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -const - CharSize* = SizeOf(Char) - Lrz* = ' ' - Apo* = '\'' - Tabulator* = '\x09' - ESC* = '\x1B' - CR* = '\x0D' - FF* = '\x0C' - LF* = '\x0A' - BEL* = '\x07' - BACKSPACE* = '\x08' - VT* = '\x0B' - -when defined(macos): - DirSep == ':' - "\n" == CR & "" - FirstNLchar == CR - PathSep == ';' # XXX: is this correct? -else: - when defined(unix): - DirSep == '/' - "\n" == LF & "" - FirstNLchar == LF - PathSep == ':' - else: - # windows, dos - DirSep == '\\' - "\n" == CR + LF - FirstNLchar == CR - DriveSeparator == ':' - PathSep == ';' -UpLetters == {'A'..'Z', '\xC0'..'\xDE'} -DownLetters == {'a'..'z', '\xDF'..'\xFF'} -Numbers == {'0'..'9'} -Letters == UpLetters + DownLetters -type - TCharSet* = set[Char] - PCharSet* = ref TCharSet - -# implementation diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index 4117fc461..17bb5db55 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -49,6 +49,7 @@ proc initDefines*() = defineSymbol("nimcomputedgoto") defineSymbol("nimunion") defineSymbol("nimnewshared") + defineSymbol("nimrequiresnimframe") # add platform specific symbols: case targetCPU diff --git a/compiler/lowerings.nim b/compiler/lowerings.nim index 0ca07e828..1b9e5fe0f 100644 --- a/compiler/lowerings.nim +++ b/compiler/lowerings.nim @@ -12,7 +12,7 @@ const genPrefix* = ":tmp" # prefix for generated names -import ast, astalgo, types, idents, magicsys, msgs +import ast, astalgo, types, idents, magicsys, msgs, options proc newTupleAccess*(tup: PNode, i: int): PNode = result = newNodeIT(nkBracketExpr, tup.info, tup.typ.skipTypes( @@ -151,8 +151,9 @@ proc wrapProcForSpawn*(owner: PSym; n: PNode): PNode = if n.kind notin nkCallKinds or not n.typ.isEmptyType: localError(n.info, "'spawn' takes a call expression of type void") return - if {tfThread, tfNoSideEffect} * n[0].typ.flags == {}: - localError(n.info, "'spawn' takes a GC safe call expression") + if optThreadAnalysis in gGlobalOptions: + if {tfThread, tfNoSideEffect} * n[0].typ.flags == {}: + localError(n.info, "'spawn' takes a GC safe call expression") var threadParam = newSym(skParam, getIdent"thread", owner, n.info) argsParam = newSym(skParam, getIdent"args", owner, n.info) @@ -162,6 +163,7 @@ proc wrapProcForSpawn*(owner: PSym; n: PNode): PNode = argsParam.typ = ptrType argsParam.position = 1 var objType = createObj(owner, n.info) + incl(objType.flags, tfFinal) let castExpr = createCastExpr(argsParam, objType) var scratchObj = newSym(skVar, getIdent"scratch", owner, n.info) diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 4d471bdac..8374c92a7 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -118,7 +118,7 @@ type warnNilStatement, warnAnalysisLoophole, warnDifferentHeaps, warnWriteToForeignHeap, warnImplicitClosure, warnEachIdentIsTuple, warnShadowIdent, - warnProveInit, warnProveField, warnProveIndex, warnGcUnsafe, + warnProveInit, warnProveField, warnProveIndex, warnGcUnsafe, warnGcUnsafe2, warnUninit, warnGcMem, warnUser, hintSuccess, hintSuccessX, hintLineTooLong, hintXDeclaredButNotUsed, hintConvToBaseNotNeeded, @@ -387,6 +387,7 @@ const warnProveField: "cannot prove that field '$1' is accessible [ProveField]", warnProveIndex: "cannot prove index '$1' is valid [ProveIndex]", warnGcUnsafe: "not GC-safe: '$1' [GcUnsafe]", + warnGcUnsafe2: "cannot prove '$1' is GC-safe. This will become a compile time error in the future.", warnUninit: "'$1' might not have been initialized [Uninit]", warnGcMem: "'$1' uses GC'ed memory [GcMem]", warnUser: "$1 [User]", @@ -408,7 +409,7 @@ const hintUser: "$1 [User]"] const - WarningsToStr*: array[0..25, string] = ["CannotOpenFile", "OctalEscape", + WarningsToStr*: array[0..26, string] = ["CannotOpenFile", "OctalEscape", "XIsNeverRead", "XmightNotBeenInit", "Deprecated", "ConfigDeprecated", "SmallLshouldNotBeUsed", "UnknownMagic", @@ -416,7 +417,7 @@ const "CommentXIgnored", "NilStmt", "AnalysisLoophole", "DifferentHeaps", "WriteToForeignHeap", "ImplicitClosure", "EachIdentIsTuple", "ShadowIdent", - "ProveInit", "ProveField", "ProveIndex", "GcUnsafe", "Uninit", + "ProveInit", "ProveField", "ProveIndex", "GcUnsafe", "GcUnsafe2", "Uninit", "GcMem", "User"] HintsToStr*: array[0..15, string] = ["Success", "SuccessX", "LineTooLong", diff --git a/compiler/nversion.nim b/compiler/nversion.nim index db38354ce..b996d0b9b 100644 --- a/compiler/nversion.nim +++ b/compiler/nversion.nim @@ -1,7 +1,7 @@ # # # The Nimrod Compiler -# (c) Copyright 2012 Andreas Rumpf +# (c) Copyright 2014 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -15,8 +15,8 @@ const defaultAsmMarkerSymbol* = '!' VersionMajor* = 0 VersionMinor* = 9 - VersionPatch* = 3 + VersionPatch* = 4 VersionAsString* = $VersionMajor & "." & $VersionMinor & "." & $VersionPatch - RodFileVersion* = "1214" # modify this if the rod-format changes! + RodFileVersion* = "1215" # modify this if the rod-format changes! diff --git a/compiler/options.nim b/compiler/options.nim index 02719cacc..58a340d21 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -95,7 +95,7 @@ var optBoundsCheck, optOverflowCheck, optAssert, optWarns, optHints, optStackTrace, optLineTrace, optPatterns, optNilCheck} - gGlobalOptions*: TGlobalOptions = {optThreadAnalysis} + gGlobalOptions*: TGlobalOptions = {} gExitcode*: int8 gCmd*: TCommands = cmdNone # the command gSelectedGC* = gcRefc # the selected GC diff --git a/compiler/parsecfg.nim b/compiler/parsecfg.nim deleted file mode 100644 index e0d1afff1..000000000 --- a/compiler/parsecfg.nim +++ /dev/null @@ -1,346 +0,0 @@ -# -# -# Nimrod's Runtime Library -# (c) Copyright 2012 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -# A HIGH-PERFORMANCE configuration file parser; -# the Nimrod version of this file is part of the -# standard library. - -import - llstream, nhashes, strutils, nimlexbase - -type - TCfgEventKind* = enum - cfgEof, # end of file reached - cfgSectionStart, # a ``[section]`` has been parsed - cfgKeyValuePair, # a ``key=value`` pair has been detected - cfgOption, # a ``--key=value`` command line option - cfgError # an error ocurred during parsing; msg contains the - # error message - TCfgEvent* = object of TObject - case kind*: TCfgEventKind - of cfgEof: - nil - - of cfgSectionStart: - section*: string - - of cfgKeyValuePair, cfgOption: - key*, value*: string - - of cfgError: - msg*: string - - - TTokKind* = enum - tkInvalid, tkEof, # order is important here! - tkSymbol, tkEquals, tkColon, tkBracketLe, tkBracketRi, tkDashDash - TToken*{.final.} = object # a token - kind*: TTokKind # the type of the token - literal*: string # the parsed (string) literal - - TParserState* = enum - startState, commaState - TCfgParser* = object of TBaseLexer - tok*: TToken - state*: TParserState - filename*: string - - -proc Open*(c: var TCfgParser, filename: string, inputStream: PLLStream) -proc Close*(c: var TCfgParser) -proc next*(c: var TCfgParser): TCfgEvent -proc getColumn*(c: TCfgParser): int -proc getLine*(c: TCfgParser): int -proc getFilename*(c: TCfgParser): string -proc errorStr*(c: TCfgParser, msg: string): string -# implementation - -const - SymChars: TCharSet = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\x80'..'\xFF'} # - # ---------------------------------------------------------------------------- - -proc rawGetTok(c: var TCfgParser, tok: var TToken) -proc open(c: var TCfgParser, filename: string, inputStream: PLLStream) = - openBaseLexer(c, inputStream) - c.filename = filename - c.state = startState - c.tok.kind = tkInvalid - c.tok.literal = "" - rawGetTok(c, c.tok) - -proc close(c: var TCfgParser) = - closeBaseLexer(c) - -proc getColumn(c: TCfgParser): int = - result = getColNumber(c, c.bufPos) - -proc getLine(c: TCfgParser): int = - result = c.linenumber - -proc getFilename(c: TCfgParser): string = - result = c.filename - -proc handleHexChar(c: var TCfgParser, xi: var int) = - case c.buf[c.bufpos] - of '0'..'9': - xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('0')) - inc(c.bufpos) - of 'a'..'f': - xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('a') + 10) - inc(c.bufpos) - of 'A'..'F': - xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('A') + 10) - inc(c.bufpos) - else: - nil - -proc handleDecChars(c: var TCfgParser, xi: var int) = - while c.buf[c.bufpos] in {'0'..'9'}: - xi = (xi * 10) + (ord(c.buf[c.bufpos]) - ord('0')) - inc(c.bufpos) - -proc getEscapedChar(c: var TCfgParser, tok: var TToken) = - var xi: int - inc(c.bufpos) # skip '\' - case c.buf[c.bufpos] - of 'n', 'N': - tok.literal = tok.literal & "\n" - Inc(c.bufpos) - of 'r', 'R', 'c', 'C': - add(tok.literal, CR) - Inc(c.bufpos) - of 'l', 'L': - add(tok.literal, LF) - Inc(c.bufpos) - of 'f', 'F': - add(tok.literal, FF) - inc(c.bufpos) - of 'e', 'E': - add(tok.literal, ESC) - Inc(c.bufpos) - of 'a', 'A': - add(tok.literal, BEL) - Inc(c.bufpos) - of 'b', 'B': - add(tok.literal, BACKSPACE) - Inc(c.bufpos) - of 'v', 'V': - add(tok.literal, VT) - Inc(c.bufpos) - of 't', 'T': - add(tok.literal, Tabulator) - Inc(c.bufpos) - of '\'', '\"': - add(tok.literal, c.buf[c.bufpos]) - Inc(c.bufpos) - of '\\': - add(tok.literal, '\\') - Inc(c.bufpos) - of 'x', 'X': - inc(c.bufpos) - xi = 0 - handleHexChar(c, xi) - handleHexChar(c, xi) - add(tok.literal, Chr(xi)) - of '0'..'9': - xi = 0 - handleDecChars(c, xi) - if (xi <= 255): add(tok.literal, Chr(xi)) - else: tok.kind = tkInvalid - else: tok.kind = tkInvalid - -proc HandleCRLF(c: var TCfgParser, pos: int): int = - case c.buf[pos] - of CR: result = lexbase.HandleCR(c, pos) - of LF: result = lexbase.HandleLF(c, pos) - else: result = pos - -proc getString(c: var TCfgParser, tok: var TToken, rawMode: bool) = - var - pos: int - ch: Char - buf: cstring - pos = c.bufPos + 1 # skip " - buf = c.buf # put `buf` in a register - tok.kind = tkSymbol - if (buf[pos] == '\"') and (buf[pos + 1] == '\"'): - # long string literal: - inc(pos, 2) # skip "" - # skip leading newline: - pos = HandleCRLF(c, pos) - buf = c.buf - while true: - case buf[pos] - of '\"': - if (buf[pos + 1] == '\"') and (buf[pos + 2] == '\"'): break - add(tok.literal, '\"') - Inc(pos) - of CR, LF: - pos = HandleCRLF(c, pos) - buf = c.buf - tok.literal = tok.literal & "\n" - of lexbase.EndOfFile: - tok.kind = tkInvalid - break - else: - add(tok.literal, buf[pos]) - Inc(pos) - c.bufpos = pos + - 3 # skip the three """ - else: - # ordinary string literal - while true: - ch = buf[pos] - if ch == '\"': - inc(pos) # skip '"' - break - if ch in {CR, LF, lexbase.EndOfFile}: - tok.kind = tkInvalid - break - if (ch == '\\') and not rawMode: - c.bufPos = pos - getEscapedChar(c, tok) - pos = c.bufPos - else: - add(tok.literal, ch) - Inc(pos) - c.bufpos = pos - -proc getSymbol(c: var TCfgParser, tok: var TToken) = - var - pos: int - buf: cstring - pos = c.bufpos - buf = c.buf - while true: - add(tok.literal, buf[pos]) - Inc(pos) - if not (buf[pos] in SymChars): break - c.bufpos = pos - tok.kind = tkSymbol - -proc skip(c: var TCfgParser) = - var - buf: cstring - pos: int - pos = c.bufpos - buf = c.buf - while true: - case buf[pos] - of ' ': - Inc(pos) - of Tabulator: - inc(pos) - of '#', ';': - while not (buf[pos] in {CR, LF, lexbase.EndOfFile}): inc(pos) - of CR, LF: - pos = HandleCRLF(c, pos) - buf = c.buf - else: - break # EndOfFile also leaves the loop - c.bufpos = pos - -proc rawGetTok(c: var TCfgParser, tok: var TToken) = - tok.kind = tkInvalid - setlen(tok.literal, 0) - skip(c) - case c.buf[c.bufpos] - of '=': - tok.kind = tkEquals - inc(c.bufpos) - tok.literal = "=" - of '-': - inc(c.bufPos) - if c.buf[c.bufPos] == '-': inc(c.bufPos) - tok.kind = tkDashDash - tok.literal = "--" - of ':': - tok.kind = tkColon - inc(c.bufpos) - tok.literal = ":" - of 'r', 'R': - if c.buf[c.bufPos + 1] == '\"': - Inc(c.bufPos) - getString(c, tok, true) - else: - getSymbol(c, tok) - of '[': - tok.kind = tkBracketLe - inc(c.bufpos) - tok.literal = "[" - of ']': - tok.kind = tkBracketRi - Inc(c.bufpos) - tok.literal = "]" - of '\"': - getString(c, tok, false) - of lexbase.EndOfFile: - tok.kind = tkEof - else: getSymbol(c, tok) - -proc errorStr(c: TCfgParser, msg: string): string = - result = `%`("$1($2, $3) Error: $4", - [c.filename, $(getLine(c)), $(getColumn(c)), msg]) - -proc getKeyValPair(c: var TCfgParser, kind: TCfgEventKind): TCfgEvent = - if c.tok.kind == tkSymbol: - result.kind = kind - result.key = c.tok.literal - result.value = "" - rawGetTok(c, c.tok) - while c.tok.literal == ".": - add(result.key, '.') - rawGetTok(c, c.tok) - if c.tok.kind == tkSymbol: - add(result.key, c.tok.literal) - rawGetTok(c, c.tok) - else: - result.kind = cfgError - result.msg = errorStr(c, "symbol expected, but found: " & c.tok.literal) - break - if c.tok.kind in {tkEquals, tkColon}: - rawGetTok(c, c.tok) - if c.tok.kind == tkSymbol: - result.value = c.tok.literal - else: - result.kind = cfgError - result.msg = errorStr(c, "symbol expected, but found: " & c.tok.literal) - rawGetTok(c, c.tok) - else: - result.kind = cfgError - result.msg = errorStr(c, "symbol expected, but found: " & c.tok.literal) - rawGetTok(c, c.tok) - -proc next(c: var TCfgParser): TCfgEvent = - case c.tok.kind - of tkEof: - result.kind = cfgEof - of tkDashDash: - rawGetTok(c, c.tok) - result = getKeyValPair(c, cfgOption) - of tkSymbol: - result = getKeyValPair(c, cfgKeyValuePair) - of tkBracketLe: - rawGetTok(c, c.tok) - if c.tok.kind == tkSymbol: - result.kind = cfgSectionStart - result.section = c.tok.literal - else: - result.kind = cfgError - result.msg = errorStr(c, "symbol expected, but found: " & c.tok.literal) - rawGetTok(c, c.tok) - if c.tok.kind == tkBracketRi: - rawGetTok(c, c.tok) - else: - result.kind = cfgError - result.msg = errorStr(c, "\']\' expected, but found: " & c.tok.literal) - of tkInvalid, tkBracketRi, tkEquals, tkColon: - result.kind = cfgError - result.msg = errorStr(c, "invalid token: " & c.tok.literal) - rawGetTok(c, c.tok) diff --git a/compiler/passes.nim b/compiler/passes.nim index 3dc31e7ac..66a1a4954 100644 --- a/compiler/passes.nim +++ b/compiler/passes.nim @@ -13,7 +13,7 @@ import strutils, lists, options, ast, astalgo, llstream, msgs, platform, os, condsyms, idents, renderer, types, extccomp, math, magicsys, nversion, - nimsets, syntaxes, times, rodread, semthreads, idgen + nimsets, syntaxes, times, rodread, idgen type TPassContext* = object of TObject # the pass's context @@ -74,7 +74,8 @@ proc astNeeded*(s: PSym): bool = ({sfCompilerProc, sfCompileTime} * s.flags == {}) and (s.typ.callConv != ccInline) and (s.ast.sons[genericParamsPos].kind == nkEmpty): - result = semthreads.needsGlobalAnalysis() + result = false + # XXX this doesn't really make sense with excessive CTFE else: result = true diff --git a/compiler/pendx.nim b/compiler/pendx.nim deleted file mode 100644 index 2942968a0..000000000 --- a/compiler/pendx.nim +++ /dev/null @@ -1,18 +0,0 @@ -# -# -# The Nimrod Compiler -# (c) Copyright 2012 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -import - llstream, lexer, parser, idents, strutils, ast, msgs - -proc ParseAll*(p: var TParser): PNode = - result = nil - -proc parseTopLevelStmt*(p: var TParser): PNode = - result = nil - diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 88fa516bf..db9fe7cbe 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -1,7 +1,7 @@ # # # The Nimrod Compiler -# (c) Copyright 2012 Andreas Rumpf +# (c) Copyright 2014 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -53,7 +53,7 @@ const wPure, wHeader, wCompilerproc, wFinal, wSize, wExtern, wShallow, wImportCpp, wImportObjC, wError, wIncompleteStruct, wByCopy, wByRef, wInheritable, wGensym, wInject, wRequiresInit, wUnchecked, wUnion, wPacked, - wBorrow} + wBorrow, wGcSafe} fieldPragmas* = {wImportc, wExportc, wDeprecated, wExtern, wImportCpp, wImportObjC, wError} varPragmas* = {wImportc, wExportc, wVolatile, wRegister, wThreadVar, wNodecl, @@ -689,10 +689,11 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int, incl(sym.flags, sfProcvar) if sym.typ != nil: incl(sym.typ.flags, tfThread) of wGcSafe: - noVal(it) - incl(sym.flags, sfThread) - if sym.typ != nil: incl(sym.typ.flags, tfGcSafe) - else: invalidPragma(it) + if optThreadAnalysis in gGlobalOptions: + noVal(it) + if sym.kind != skType: incl(sym.flags, sfThread) + if sym.typ != nil: incl(sym.typ.flags, tfGcSafe) + else: invalidPragma(it) of wPacked: noVal(it) if sym.typ == nil: invalidPragma(it) diff --git a/compiler/sem.nim b/compiler/sem.nim index e7bff0665..7d129caf4 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -14,7 +14,7 @@ import wordrecg, ropes, msgs, os, condsyms, idents, renderer, types, platform, math, magicsys, parser, nversion, nimsets, semfold, importer, procfind, lookups, rodread, pragmas, passes, semdata, semtypinst, sigmatch, - semthreads, intsets, transf, vmdef, vm, idgen, aliases, cgmeth, lambdalifting, + intsets, transf, vmdef, vm, idgen, aliases, cgmeth, lambdalifting, evaltempl, patterns, parampatterns, sempass2, pretty, semmacrosanity # implementation @@ -415,12 +415,7 @@ proc myProcess(context: PPassContext, n: PNode): PNode = if getCurrentException() of ESuggestDone: result = nil else: result = ast.emptyNode #if gCmd == cmdIdeTools: findSuggest(c, n) - -proc checkThreads(c: PContext) = - if not needsGlobalAnalysis(): return - for i in 0 .. c.threadEntries.len-1: - semthreads.analyseThreadProc(c.threadEntries[i]) - + proc myClose(context: PPassContext, n: PNode): PNode = var c = PContext(context) closeScope(c) # close module's scope @@ -431,7 +426,6 @@ proc myClose(context: PPassContext, n: PNode): PNode = addCodeForGenerics(c, result) if c.module.ast != nil: result.add(c.module.ast) - checkThreads(c) popOwner() popProcCon(c) diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 4cb5ad38c..987a70a41 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -57,7 +57,6 @@ type # can access private object fields instCounter*: int # to prevent endless instantiations - threadEntries*: TSymSeq # list of thread entries to check ambiguousSymbols*: TIntSet # ids of all ambiguous symbols (cannot # store this info in the syms themselves!) inTypeClass*: int # > 0 if we are in a user-defined type class @@ -170,7 +169,6 @@ proc newContext(module: PSym): PContext = append(result.optionStack, newOptionEntry()) result.module = module result.friendModule = module - result.threadEntries = @[] result.converters = @[] result.patterns = @[] result.includedFiles = initIntSet() diff --git a/compiler/semfold.nim b/compiler/semfold.nim index caaab2291..79abfaf4d 100644 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -404,7 +404,7 @@ proc evalOp(m: TMagic, n, a, b, c: PNode): PNode = mExit, mInc, ast.mDec, mEcho, mSwap, mAppendStrCh, mAppendStrStr, mAppendSeqElem, mSetLengthStr, mSetLengthSeq, mParseExprToAst, mParseStmtToAst, mExpandToAst, mTypeTrait, - mNLen..mNError, mEqRef, mSlurp, mStaticExec, mNGenSym: + mNLen..mNError, mEqRef, mSlurp, mStaticExec, mNGenSym, mSpawn: discard of mRand: result = newIntNodeT(math.random(a.getInt.int), n) diff --git a/compiler/seminst.nim b/compiler/seminst.nim index a5149a842..f7d5fa6f8 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -127,9 +127,6 @@ proc sideEffectsCheck(c: PContext, s: PSym) = if {sfNoSideEffect, sfSideEffect} * s.flags == {sfNoSideEffect, sfSideEffect}: localError(s.info, errXhasSideEffects, s.name.s) - elif sfThread in s.flags and semthreads.needsGlobalAnalysis() and - s.ast.sons[genericParamsPos].kind == nkEmpty: - c.threadEntries.add(s) proc instGenericContainer(c: PContext, info: TLineInfo, header: PType, allowMetaTypes = false): PType = diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index b2b91490c..6235fb76a 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -71,7 +71,7 @@ type init: seq[int] # list of initialized variables guards: TModel # nested guards locked: seq[PNode] # locked locations - gcUnsafe: bool + gcUnsafe, isRecursive: bool PEffects = var TEffects proc isLocalVar(a: PEffects, s: PSym): bool = @@ -113,7 +113,8 @@ proc useVar(a: PEffects, n: PNode) = if {sfGlobal, sfThread} * s.flags == {sfGlobal} and s.kind == skVar: when trackGlobals: a.addUse(copyNode(n)) - if tfHasGCedMem in s.typ.flags: + if (tfHasGCedMem in s.typ.flags or s.typ.isGCedMem) and + tfGcSafe notin s.typ.flags: message(n.info, warnGcUnsafe, renderTree(n)) a.gcUnsafe = true @@ -502,7 +503,9 @@ proc track(tracked: PEffects, n: PNode) = # are indistinguishable from normal procs (both have tyProc type) and # we can detect them only by checking for attached nkEffectList. if op != nil and op.kind == tyProc and op.n.sons[0].kind == nkEffectList: - if notGcSafe(op) and not importedFromC(a): + if a.kind == nkSym and a.sym == tracked.owner: + tracked.isRecursive = true + elif notGcSafe(op) and not importedFromC(a): message(n.info, warnGcUnsafe, renderTree(n)) tracked.gcUnsafe = true var effectList = op.n.sons[0] @@ -515,6 +518,7 @@ proc track(tracked: PEffects, n: PNode) = addEffect(tracked, createRaise(n)) addTag(tracked, createTag(n)) when trackGlobals: addUse(tracked, createAnyGlobal(n)) + # XXX handle 'gcsafe' properly for callbacks! else: mergeEffects(tracked, effectList.sons[exceptionEffects], n) mergeTags(tracked, effectList.sons[tagEffects], n) @@ -701,10 +705,12 @@ proc trackProc*(s: PSym, body: PNode) = checkRaisesSpec(usesSpec, t.uses, "uses an unlisted global variable: ", hints=on, symbolPredicate) effects.sons[usesEffects] = usesSpec - if sfThread in s.flags and t.gcUnsafe: - localError(s.info, "'$1' is not GC-safe" % s.name.s) - if not t.gcUnsafe: s.typ.flags.incl tfGcSafe - + if optThreadAnalysis in gGlobalOptions: + if sfThread in s.flags and t.gcUnsafe: + localError(s.info, warnGcUnsafe2, s.name.s) + #localError(s.info, "'$1' is not GC-safe" % s.name.s) + if not t.gcUnsafe: s.typ.flags.incl tfGcSafe + proc trackTopLevelStmt*(module: PSym; n: PNode) = if n.kind in {nkPragma, nkMacroDef, nkTemplateDef, nkProcDef, nkTypeSection, nkConverterDef, nkMethodDef, nkIteratorDef}: diff --git a/compiler/semthreads.nim b/compiler/semthreads.nim deleted file mode 100644 index d3426ca3e..000000000 --- a/compiler/semthreads.nim +++ /dev/null @@ -1,390 +0,0 @@ -# -# -# The Nimrod Compiler -# (c) Copyright 2013 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -## Semantic analysis that deals with threads: Possible race conditions should -## be reported some day. -## -## -## ======================== -## No heap sharing analysis -## ======================== -## -## The only crucial operation that can violate the heap invariants is the -## write access. The analysis needs to distinguish between 'unknown', 'mine', -## and 'theirs' memory and pointers. Assignments 'whatever <- unknown' are -## invalid, and so are 'theirs <- whatever' but not 'mine <- theirs'. Since -## strings and sequences are heap allocated they are affected too: -## -## .. code-block:: nimrod -## proc p() = -## global = "alloc this string" # ugh! -## -## Thus the analysis is concerned with any type that contains a GC'ed -## reference... -## If the type system would distinguish between 'ref' and '!ref' and threads -## could not have '!ref' as input parameters the analysis could simply need to -## reject any write access to a global variable which contains GC'ed data. -## Thanks to the write barrier of the GC, this is exactly what needs to be -## done! Every write access to a global that contains GC'ed data needs to -## be prevented! Unfortunately '!ref' is not implemented yet... -## -## The assignment target is essential for the algorithm: only -## write access to heap locations and global variables are critical and need -## to be checked. Access via 'var' parameters is no problem to analyse since -## we need the arguments' locations in the analysis. -## -## However, this is tricky: -## -## var x = globalVar # 'x' points to 'theirs' -## while true: -## globalVar = x # NOT OK: 'theirs <- theirs' invalid due to -## # write barrier! -## x = "new string" # ugh: 'x is toUnknown'! -## -## --> Solution: toUnknown is never allowed anywhere! -## -## -## Beware that the same proc might need to be -## analysed multiple times! Oh and watch out for recursion! Recursion is handled -## by a stack of symbols that we are processing, if we come back to the same -## symbol, we have to skip this check (assume no error in the recursive case). -## However this is wrong. We need to check for the particular combination -## of (procsym, threadOwner(arg1), threadOwner(arg2), ...)! - -import - ast, astalgo, strutils, hashes, options, msgs, idents, types, os, - renderer, tables, rodread - -type - TThreadOwner = enum - toUndefined, # not computed yet - toVoid, # no return type - toNil, # cycle in computation or nil: can be overwritten - toTheirs, # some other heap - toMine # mine heap - - TCall = object {.pure.} - callee: PSym # what if callee is an indirect call? - args: seq[TThreadOwner] - - PProcCtx = ref TProcCtx - TProcCtx = object {.pure.} - nxt: PProcCtx # can be stacked - mapping: tables.TTable[int, TThreadOwner] # int = symbol ID - owner: PSym # current owner - -var - computed = tables.initTable[TCall, TThreadOwner]() - -proc hash(c: TCall): THash = - result = hash(c.callee.id) - for a in items(c.args): result = result !& hash(ord(a)) - result = !$result - -proc `==`(a, b: TCall): bool = - if a.callee != b.callee: return - if a.args.len != b.args.len: return - for i in 0..a.args.len-1: - if a.args[i] != b.args[i]: return - result = true - -proc newProcCtx(owner: PSym): PProcCtx = - assert owner != nil - new(result) - result.mapping = tables.initTable[int, TThreadOwner]() - result.owner = owner - -proc analyse(c: PProcCtx, n: PNode): TThreadOwner - -proc analyseSym(c: PProcCtx, n: PNode): TThreadOwner = - var v = n.sym - result = c.mapping[v.id] - if result != toUndefined: return - case v.kind - of skVar, skForVar, skLet, skResult: - result = toNil - if sfGlobal in v.flags: - if sfThread in v.flags: - result = toMine - elif containsGarbageCollectedRef(v.typ): - result = toTheirs - of skTemp: result = toNil - of skConst: result = toMine - of skParam: - result = c.mapping[v.id] - if result == toUndefined: - internalError(n.info, "param not set: " & v.name.s) - else: - result = toNil - c.mapping[v.id] = result - -proc lvalueSym(n: PNode): PNode = - result = n - while result.kind in {nkDotExpr, nkCheckedFieldExpr, - nkBracketExpr, nkDerefExpr, nkHiddenDeref}: - result = result.sons[0] - -proc writeAccess(c: PProcCtx, n: PNode, owner: TThreadOwner) = - if owner notin {toNil, toMine, toTheirs}: - internalError(n.info, "writeAccess: " & $owner) - var a = lvalueSym(n) - if a.kind == nkSym: - var v = a.sym - var lastOwner = analyseSym(c, a) - case lastOwner - of toNil: - # fine, toNil can be overwritten - var newOwner: TThreadOwner - if sfGlobal in v.flags: - newOwner = owner - elif containsTyRef(v.typ): - # ``var local = gNode`` --> ok, but ``local`` is theirs! - newOwner = owner - else: - # ``var local = gString`` --> string copy: ``local`` is mine! - newOwner = toMine - # XXX BUG what if the tuple contains both ``tyRef`` and ``tyString``? - c.mapping[v.id] = newOwner - of toVoid, toUndefined: internalError(n.info, "writeAccess") - of toTheirs: message(n.info, warnWriteToForeignHeap) - of toMine: - if lastOwner != owner and owner != toNil: - message(n.info, warnDifferentHeaps) - else: - # we could not backtrack to a concrete symbol, but that's fine: - var lastOwner = analyse(c, n) - case lastOwner - of toNil: discard # fine, toNil can be overwritten - of toVoid, toUndefined: internalError(n.info, "writeAccess") - of toTheirs: message(n.info, warnWriteToForeignHeap) - of toMine: - if lastOwner != owner and owner != toNil: - message(n.info, warnDifferentHeaps) - -proc analyseAssign(c: PProcCtx, le, ri: PNode) = - var y = analyse(c, ri) # read access; ok - writeAccess(c, le, y) - -proc analyseAssign(c: PProcCtx, n: PNode) = - analyseAssign(c, n.sons[0], n.sons[1]) - -proc analyseCall(c: PProcCtx, n: PNode): TThreadOwner = - var prc = n[0].sym - var newCtx = newProcCtx(prc) - var call: TCall - call.callee = prc - newSeq(call.args, n.len-1) - for i in 1..n.len-1: - call.args[i-1] = analyse(c, n[i]) - if not computed.hasKey(call): - computed[call] = toUndefined # we are computing it - let prctyp = skipTypes(prc.typ, abstractInst).n - for i in 1.. prctyp.len-1: - var formal = prctyp.sons[i].sym - newCtx.mapping[formal.id] = call.args[i-1] - pushInfoContext(n.info) - result = analyse(newCtx, prc.getBody) - if prc.ast.sons[bodyPos].kind == nkEmpty and - {sfNoSideEffect, sfThread, sfImportc} * prc.flags == {}: - message(n.info, warnAnalysisLoophole, renderTree(n)) - if result == toUndefined: result = toNil - if prc.typ.sons[0] != nil: - if prc.ast.len > resultPos: - result = newCtx.mapping[prc.ast.sons[resultPos].sym.id] - # if the proc body does not set 'result', nor 'return's something - # explicitely, it returns a binary zero, so 'toNil' is correct: - if result == toUndefined: result = toNil - else: - result = toNil - else: - result = toVoid - computed[call] = result - popInfoContext() - else: - result = computed[call] - if result == toUndefined: - # ugh, cycle! We are already computing it but don't know the - # outcome yet... - if prc.typ.sons[0] == nil: result = toVoid - else: result = toNil - -proc analyseVarTuple(c: PProcCtx, n: PNode) = - if n.kind != nkVarTuple: internalError(n.info, "analyseVarTuple") - var L = n.len - for i in countup(0, L-3): analyseAssign(c, n.sons[i], n.sons[L-1]) - -proc analyseSingleVar(c: PProcCtx, a: PNode) = - if a.sons[2].kind != nkEmpty: analyseAssign(c, a.sons[0], a.sons[2]) - -proc analyseVarSection(c: PProcCtx, n: PNode): TThreadOwner = - for i in countup(0, sonsLen(n) - 1): - var a = n.sons[i] - if a.kind == nkCommentStmt: continue - if a.kind == nkIdentDefs: - #assert(a.sons[0].kind == nkSym); also valid for after - # closure transformation: - analyseSingleVar(c, a) - else: - analyseVarTuple(c, a) - result = toVoid - -proc analyseConstSection(c: PProcCtx, t: PNode): TThreadOwner = - for i in countup(0, sonsLen(t) - 1): - var it = t.sons[i] - if it.kind == nkCommentStmt: continue - if it.kind != nkConstDef: internalError(t.info, "analyseConstSection") - if sfFakeConst in it.sons[0].sym.flags: analyseSingleVar(c, it) - result = toVoid - -template aggregateOwner(result, ana: expr) = - var a = ana # eval once - if result != a: - if result == toNil: result = a - elif a != toNil: message(n.info, warnDifferentHeaps) - -proc analyseArgs(c: PProcCtx, n: PNode, start = 1) = - for i in start..n.len-1: discard analyse(c, n[i]) - -proc analyseOp(c: PProcCtx, n: PNode): TThreadOwner = - if n[0].kind != nkSym or n[0].sym.kind != skProc: - if {tfNoSideEffect, tfThread} * n[0].typ.flags == {}: - message(n.info, warnAnalysisLoophole, renderTree(n)) - result = toNil - else: - var prc = n[0].sym - case prc.magic - of mNone: - if sfSystemModule in prc.owner.flags: - # System module proc does no harm :-) - analyseArgs(c, n) - if prc.typ.sons[0] == nil: result = toVoid - else: result = toNil - else: - result = analyseCall(c, n) - of mNew, mNewFinalize, mNewSeq, mSetLengthStr, mSetLengthSeq, - mAppendSeqElem, mReset, mAppendStrCh, mAppendStrStr: - writeAccess(c, n[1], toMine) - result = toVoid - of mSwap: - var a = analyse(c, n[2]) - writeAccess(c, n[1], a) - writeAccess(c, n[2], a) - result = toVoid - of mIntToStr, mInt64ToStr, mFloatToStr, mBoolToStr, mCharToStr, - mCStrToStr, mStrToStr, mEnumToStr, - mConStrStr, mConArrArr, mConArrT, - mConTArr, mConTT, mSlice, - mRepr, mArrToSeq, mCopyStr, mCopyStrLast, - mNewString, mNewStringOfCap: - analyseArgs(c, n) - result = toMine - else: - # don't recurse, but check args: - analyseArgs(c, n) - if prc.typ.sons[0] == nil: result = toVoid - else: result = toNil - -proc analyse(c: PProcCtx, n: PNode): TThreadOwner = - case n.kind - of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, - nkCallStrLit, nkHiddenCallConv: - result = analyseOp(c, n) - of nkAsgn, nkFastAsgn: - analyseAssign(c, n) - result = toVoid - of nkSym: result = analyseSym(c, n) - of nkEmpty, nkNone: result = toVoid - of nkNilLit, nkCharLit..nkFloat64Lit: result = toNil - of nkStrLit..nkTripleStrLit: result = toMine - of nkDotExpr, nkBracketExpr, nkDerefExpr, nkHiddenDeref: - # field access: - # pointer deref or array access: - result = analyse(c, n.sons[0]) - of nkBind: result = analyse(c, n.sons[0]) - of nkPar, nkCurly, nkBracket, nkRange: - # container construction: - result = toNil # nothing until later - for i in 0..n.len-1: aggregateOwner(result, analyse(c, n[i])) - of nkObjConstr: - if n.typ != nil and containsGarbageCollectedRef(n.typ): - result = toMine - else: - result = toNil # nothing until later - for i in 1..n.len-1: aggregateOwner(result, analyse(c, n[i])) - of nkAddr, nkHiddenAddr: - var a = lvalueSym(n) - if a.kind == nkSym: - result = analyseSym(c, a) - assert result in {toNil, toMine, toTheirs} - if result == toNil: - # assume toMine here for consistency: - c.mapping[a.sym.id] = toMine - result = toMine - else: - # should never really happen: - result = analyse(c, n.sons[0]) - of nkIfExpr: - result = toNil - for i in countup(0, sonsLen(n) - 1): - var it = n.sons[i] - if it.len == 2: - discard analyse(c, it.sons[0]) - aggregateOwner(result, analyse(c, it.sons[1])) - else: - aggregateOwner(result, analyse(c, it.sons[0])) - of nkStmtListExpr, nkBlockExpr: - var n = if n.kind == nkBlockExpr: n.sons[1] else: n - var L = sonsLen(n) - for i in countup(0, L-2): discard analyse(c, n.sons[i]) - if L > 0: result = analyse(c, n.sons[L-1]) - else: result = toVoid - of nkHiddenStdConv, nkHiddenSubConv, nkConv, nkCast: - result = analyse(c, n.sons[1]) - of nkStringToCString, nkCStringToString, nkChckRangeF, nkChckRange64, - nkChckRange, nkCheckedFieldExpr, nkObjDownConv, - nkObjUpConv: - result = analyse(c, n.sons[0]) - of nkRaiseStmt: - var a = analyse(c, n.sons[0]) - if a != toMine: message(n.info, warnDifferentHeaps) - result = toVoid - of nkVarSection, nkLetSection: result = analyseVarSection(c, n) - of nkConstSection: result = analyseConstSection(c, n) - of nkTypeSection, nkCommentStmt: result = toVoid - of nkIfStmt, nkWhileStmt, nkTryStmt, nkCaseStmt, nkStmtList, nkBlockStmt, - nkElifBranch, nkElse, nkExceptBranch, nkOfBranch, nkFinally: - for i in 0 .. <n.len: discard analyse(c, n[i]) - result = toVoid - of nkBreakStmt, nkContinueStmt: result = toVoid - of nkReturnStmt, nkDiscardStmt: - if n.sons[0].kind != nkEmpty: result = analyse(c, n.sons[0]) - else: result = toVoid - of nkLambdaKinds, nkClosure: - result = toMine - of nkAsmStmt, nkPragma, nkIteratorDef, nkProcDef, nkMethodDef, - nkConverterDef, nkMacroDef, nkTemplateDef, - nkGotoState, nkState, nkBreakState, nkType, nkIdent: - result = toVoid - of nkExprColonExpr: - result = analyse(c, n.sons[1]) - else: internalError(n.info, "analysis not implemented for: " & $n.kind) - -proc analyseThreadProc*(prc: PSym) = - var c = newProcCtx(prc) - var formals = skipTypes(prc.typ, abstractInst).n - for i in 1 .. formals.len-1: - var formal = formals.sons[i].sym - # the input is copied and belongs to the thread: - c.mapping[formal.id] = toMine - discard analyse(c, prc.getBody) - -proc needsGlobalAnalysis*: bool = - result = gGlobalOptions * {optThreads, optThreadAnalysis} == - {optThreads, optThreadAnalysis} - diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 384bdc8a3..6289ecc85 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -194,10 +194,15 @@ proc semRange(c: PContext, n: PNode, prev: PType): PType = if isRange(n[1]): result = semRangeAux(c, n[1], prev) let n = result.n - if n.sons[0].kind in {nkCharLit..nkUInt64Lit}: - if n.sons[0].intVal > 0 or n.sons[1].intVal < 0: - incl(result.flags, tfNeedsInit) - elif n.sons[0].floatVal > 0.0 or n.sons[1].floatVal < 0.0: + if n.sons[0].kind in {nkCharLit..nkUInt64Lit} and n.sons[0].intVal > 0: + incl(result.flags, tfNeedsInit) + elif n.sons[1].kind in {nkCharLit..nkUInt64Lit} and n.sons[1].intVal < 0: + incl(result.flags, tfNeedsInit) + elif n.sons[0].kind in {nkFloatLit..nkFloat64Lit} and + n.sons[0].floatVal > 0.0: + incl(result.flags, tfNeedsInit) + elif n.sons[1].kind in {nkFloatLit..nkFloat64Lit} and + n.sons[1].floatVal < 0.0: incl(result.flags, tfNeedsInit) else: localError(n.sons[0].info, errRangeExpected) diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index 8b9b7b13b..33de40f34 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -19,7 +19,7 @@ proc sharedPtrCheck(info: TLineInfo, t: PType) = if t.sons[0].sym.magic in {mShared, mGuarded}: incl(t.flags, tfShared) if t.sons[0].sym.magic == mGuarded: incl(t.flags, tfGuarded) - if tfHasGCedMem in t.flags: + if tfHasGCedMem in t.flags or t.isGCedMem: localError(info, errGenerated, "shared memory may not refer to GC'ed thread local memory") @@ -352,8 +352,9 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType = result = handleGenericInvokation(cl, t) of tyGenericBody: - internalError(cl.info, "ReplaceTypeVarsT: tyGenericBody" ) - result = replaceTypeVarsT(cl, lastSon(t)) + localError(cl.info, errCannotInstantiateX, typeToString(t)) + result = t + #result = replaceTypeVarsT(cl, lastSon(t)) of tyFromExpr: if cl.allowMetaTypes: return diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 5d3ed05f7..4b91a067e 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -148,6 +148,9 @@ proc sumGeneric(t: PType): int = result = ord(t.kind == tyGenericInvokation) for i in 0 .. <t.len: result += t.sons[i].sumGeneric break + of tyProc: + # proc matche proc better than 'stmt' to disambiguate 'spawn' + return 1 of tyGenericParam, tyExpr, tyStatic, tyStmt, tyTypeDesc: break else: return 0 @@ -851,7 +854,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = of tyAnd: considerPreviousT: for branch in f.sons: - if typeRel(c, branch, aOrig) == isNone: + if typeRel(c, branch, aOrig) < isSubtype: return isNone bindingRet isGeneric @@ -859,7 +862,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = of tyOr: considerPreviousT: for branch in f.sons: - if typeRel(c, branch, aOrig) != isNone: + if typeRel(c, branch, aOrig) >= isSubtype: bindingRet isGeneric return isNone diff --git a/compiler/suggest.nim b/compiler/suggest.nim index 49611f649..fc6ba2f77 100644 --- a/compiler/suggest.nim +++ b/compiler/suggest.nim @@ -253,6 +253,7 @@ proc findUsages(node: PNode, s: PSym) = lastLineInfo = node.info proc findDefinition(node: PNode, s: PSym) = + if node.isNil or s.isNil: return if isTracked(node.info, s.name.s.len): suggestWriteln(symToStr(s, isLocal=false, sectionDef)) suggestQuit() diff --git a/compiler/syntaxes.nim b/compiler/syntaxes.nim index 478c2a837..d5062544f 100644 --- a/compiler/syntaxes.nim +++ b/compiler/syntaxes.nim @@ -62,7 +62,6 @@ proc parseAll(p: var TParsers): PNode = of skinEndX: internalError("parser to implement") result = ast.emptyNode - # skinEndX: result := pendx.parseAll(p.parser); proc parseTopLevelStmt(p: var TParsers): PNode = case p.skin @@ -73,7 +72,6 @@ proc parseTopLevelStmt(p: var TParsers): PNode = of skinEndX: internalError("parser to implement") result = ast.emptyNode - #skinEndX: result := pendx.parseTopLevelStmt(p.parser); proc utf8Bom(s: string): int = if (s[0] == '\xEF') and (s[1] == '\xBB') and (s[2] == '\xBF'): diff --git a/compiler/types.nim b/compiler/types.nim index 1de0fc103..1f266d64f 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -342,9 +342,10 @@ proc canFormAcycleAux(marker: var TIntSet, typ: PType, startId: int): bool = result = t.id == startId # Inheritance can introduce cyclic types, however this is not relevant # as the type that is passed to 'new' is statically known! - #if t.kind == tyObject and tfFinal notin t.flags: - # # damn inheritance may introduce cycles: - # result = true + # er but we use it also for the write barrier ... + if t.kind == tyObject and tfFinal notin t.flags: + # damn inheritance may introduce cycles: + result = true of tyProc: result = typ.callConv == ccClosure else: discard @@ -537,7 +538,7 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = add(prag, "noSideEffect") if tfThread in t.flags: addSep(prag) - add(prag, "thread") + add(prag, "gcsafe") if len(prag) != 0: add(result, "{." & prag & ".}") of tyVarargs, tyIter: result = typeToStr[t.kind] % typeToString(t.sons[0]) diff --git a/compiler/typesrenderer.nim b/compiler/typesrenderer.nim index ebb9b7e15..790bd1047 100644 --- a/compiler/typesrenderer.nim +++ b/compiler/typesrenderer.nim @@ -69,7 +69,7 @@ proc renderType(n: PNode): string = for i in 1 .. <len(n): result.add(renderType(n[i]) & ',') result[<len(result)] = ']' else: result = "" - assert (not result.isNil) + assert(not result.isNil) proc renderParamTypes(found: var seq[string], n: PNode) = @@ -86,13 +86,11 @@ proc renderParamTypes(found: var seq[string], n: PNode) = let typePos = len(n) - 2 assert typePos > 0 var typeStr = renderType(n[typePos]) - if typeStr.len < 1: + if typeStr.len < 1 and n[typePos+1].kind != nkEmpty: # Try with the last node, maybe its a default value. - assert n[typePos+1].kind != nkEmpty let typ = n[typePos+1].typ if not typ.isNil: typeStr = typeToString(typ, preferExported) - if typeStr.len < 1: - return + if typeStr.len < 1: return for i in 0 .. <typePos: assert n[i].kind == nkIdent found.add(typeStr) diff --git a/compiler/vm.nim b/compiler/vm.nim index fb8749250..836f90967 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -354,6 +354,11 @@ template handleJmpBack() {.dirty.} = globalError(c.debug[pc], errTooManyIterations) dec(c.loopIterations) +proc skipColon(n: PNode): PNode = + result = n + if n.kind == nkExprColonExpr: + result = n.sons[1] + proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = var pc = start var tos = tos @@ -454,7 +459,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = decodeBC(rkNode) let src = regs[rb].node if src.kind notin {nkEmpty..nkNilLit}: - let n = src.sons[rc] + let n = src.sons[rc].skipColon regs[ra].node = n else: stackTrace(c, tos, pc, errNilAccess) @@ -969,7 +974,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = of opcRepr: decodeB(rkNode) createStr regs[ra] - regs[ra].node.strVal = renderTree(regs[rb].node, {renderNoComments}) + regs[ra].node.strVal = renderTree(regs[rb].regToNode, {renderNoComments}) of opcQuit: if c.mode in {emRepl, emStaticExpr, emStaticStmt}: message(c.debug[pc], hintQuitCalled) @@ -1099,6 +1104,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = c.module) of opcGorge: decodeBC(rkNode) + createStr regs[ra] regs[ra].node.strVal = opGorge(regs[rb].node.strVal, regs[rc].node.strVal) of opcNError: diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 7c0c3d4f5..84577bb22 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -70,6 +70,9 @@ proc echoCode*(c: PCtx, start=0) {.deprecated.} = echo buf proc gABC(ctx: PCtx; n: PNode; opc: TOpcode; a, b, c: TRegister = 0) = + ## Takes the registers `b` and `c`, applies the operation `opc` to them, and + ## stores the result into register `a` + ## The node is needed for debug information assert opc.ord < 255 let ins = (opc.uint32 or (a.uint32 shl 8'u32) or (b.uint32 shl 16'u32) or @@ -78,6 +81,10 @@ proc gABC(ctx: PCtx; n: PNode; opc: TOpcode; a, b, c: TRegister = 0) = ctx.debug.add(n.info) proc gABI(c: PCtx; n: PNode; opc: TOpcode; a, b: TRegister; imm: BiggestInt) = + # Takes the `b` register and the immediate `imm`, appies the operation `opc`, + # and stores the output value into `a`. + # `imm` is signed and must be within [-127, 128] + assert(imm >= -127 and imm <= 128) let ins = (opc.uint32 or (a.uint32 shl 8'u32) or (b.uint32 shl 16'u32) or (imm+byteExcess).uint32 shl 24'u32).TInstr @@ -85,6 +92,9 @@ proc gABI(c: PCtx; n: PNode; opc: TOpcode; a, b: TRegister; imm: BiggestInt) = c.debug.add(n.info) proc gABx(c: PCtx; n: PNode; opc: TOpcode; a: TRegister = 0; bx: int) = + # Applies `opc` to `bx` and stores it into register `a` + # `bx` must be signed and in the range [-32767, 32768] + assert(bx >= -32767 and bx <= 32768) let ins = (opc.uint32 or a.uint32 shl 8'u32 or (bx+wordExcess).uint32 shl 16'u32).TInstr c.code.add(ins) @@ -316,15 +326,7 @@ proc genAndOr(c: PCtx; n: PNode; opc: TOpcode; dest: var TDest) = c.patch(L1) proc canonValue*(n: PNode): PNode = - if n.kind == nkExprColonExpr: - result = n.sons[1] - elif n.hasSubnodeWith(nkExprColonExpr): - result = n.copyNode - newSeq(result.sons, n.len) - for i in 0.. <n.len: - result.sons[i] = canonValue(n.sons[i]) - else: - result = n + result = n proc rawGenLiteral(c: PCtx; n: PNode): int = result = c.constants.len |