diff options
33 files changed, 265 insertions, 83 deletions
diff --git a/changelog.md b/changelog.md index 6844d3791..77a01b9c7 100644 --- a/changelog.md +++ b/changelog.md @@ -27,6 +27,10 @@ ### Compiler changes +- The VM's instruction count limit was raised to 1 billion instructions in order + to support more complex computations at compile-time. + + ### Bugfixes - The `importcpp` pragma now allows importing the listed fields of generic diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index ba0820e93..060a5fdbd 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -1364,6 +1364,7 @@ proc genOf(p: BProc, x: PNode, typ: PType, d: var TLoc) = if t.kind notin {tyVar, tyLent} or not p.module.compileToCpp: r = rfmt(nil, "(*$1)", r) t = skipTypes(t.lastSon, typedescInst) + discard getTypeDesc(p.module, t) if not p.module.compileToCpp: while t.kind == tyObject and t.sons[0] != nil: add(r, ~".Sup") @@ -1910,7 +1911,8 @@ proc genSetConstr(p: BProc, e: PNode, d: var TLoc) = if getSize(e.typ) > 8: # big set: useStringh(p.module) - lineF(p, cpsStmts, "memset($1, 0, sizeof($1));$n", [rdLoc(d)]) + lineF(p, cpsStmts, "memset($1, 0, sizeof($2));$n", + [rdLoc(d), getTypeDesc(p.module, e.typ)]) for i in countup(0, sonsLen(e) - 1): if e.sons[i].kind == nkRange: getTemp(p, getSysType(tyInt), idx) # our counter @@ -2061,6 +2063,7 @@ proc upConv(p: BProc, n: PNode, d: var TLoc) = proc downConv(p: BProc, n: PNode, d: var TLoc) = if p.module.compileToCpp: + discard getTypeDesc(p.module, skipTypes(n[0].typ, abstractPtrs)) expr(p, n.sons[0], d) # downcast does C++ for us else: var dest = skipTypes(n.typ, abstractPtrs) @@ -2069,6 +2072,7 @@ proc downConv(p: BProc, n: PNode, d: var TLoc) = while arg.kind == nkObjDownConv: arg = arg.sons[0] var src = skipTypes(arg.typ, abstractPtrs) + discard getTypeDesc(p.module, src) var a: TLoc initLocExpr(p, arg, a) var r = rdLoc(a) diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index bdba34e36..4fc029116 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -368,6 +368,8 @@ proc getTypeForward(m: BModule, typ: PType; sig: SigHash): Rope = if not isImportedType(concrete): addf(m.s[cfsForwardTypes], getForwardStructFormat(m), [structOrUnion(typ), result]) + else: + pushType(m, concrete) doAssert m.forwTypeCache[sig] == result else: internalError("getTypeForward(" & $typ.kind & ')') @@ -665,7 +667,6 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope = let name = getTypeForward(m, et, hashType et) result = name & star m.typeCache[sig] = result - pushType(m, et) of tySequence: # no restriction! We have a forward declaration for structs let name = getTypeForward(m, et, hashType et) diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 8b3da223f..01610010b 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -317,8 +317,10 @@ proc resetLoc(p: BProc, loc: var TLoc) = genObjectInit(p, cpsStmts, loc.t, loc, true) else: useStringh(p.module) + # array passed as argument decayed into pointer, bug #7332 + # so we use getTypeDesc here rather than rdLoc(loc) linefmt(p, cpsStmts, "memset((void*)$1, 0, sizeof($2));$n", - addrLoc(loc), rdLoc(loc)) + addrLoc(loc), getTypeDesc(p.module, loc.t)) # XXX: We can be extra clever here and call memset only # on the bytes following the m_type field? genObjectInit(p, cpsStmts, loc.t, loc, true) @@ -335,7 +337,7 @@ proc constructLoc(p: BProc, loc: TLoc, isTemp = false) = if not isImportedCppType(typ): useStringh(p.module) linefmt(p, cpsStmts, "memset((void*)$1, 0, sizeof($2));$n", - addrLoc(loc), rdLoc(loc)) + addrLoc(loc), getTypeDesc(p.module, typ)) genObjectInit(p, cpsStmts, loc.t, loc, true) proc initLocalVar(p: BProc, v: PSym, immediateAsgn: bool) = diff --git a/compiler/msgs.nim b/compiler/msgs.nim index ac4242e67..09413128b 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -357,7 +357,9 @@ const errXExpectsTwoArguments: "\'$1\' expects two arguments", errXExpectsObjectTypes: "\'$1\' expects object types", errXcanNeverBeOfThisSubtype: "\'$1\' can never be of this subtype", - errTooManyIterations: "interpretation requires too many iterations", + errTooManyIterations: "interpretation requires too many iterations; " & + "if you are sure this is not a bug in your code edit " & + "compiler/vmdef.MaxLoopIterations and rebuild the compiler", errCannotInterpretNodeX: "cannot evaluate \'$1\'", errFieldXNotFound: "field \'$1\' cannot be found", errInvalidConversionFromTypeX: "invalid conversion from type \'$1\'", diff --git a/compiler/parser.nim b/compiler/parser.nim index 4974abcc3..debb0b34d 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -17,6 +17,8 @@ # In fact the grammar is generated from this file: when isMainModule: + # Leave a note in grammar.txt that it is generated: + #| # This file is generated by compiler/parser.nim. import pegs var outp = open("doc/grammar.txt", fmWrite) for line in lines("compiler/parser.nim"): @@ -818,7 +820,6 @@ proc parseIfExpr(p: var TParser, kind: TNodeKind): PNode = if realInd(p): p.currInd = p.tok.indent wasIndented = true - echo result.info, " yes ", p.currInd addSon(branch, parseExpr(p)) result.add branch while sameInd(p) or not wasIndented: @@ -962,6 +963,8 @@ proc parseTuple(p: var TParser, indentAllowed = false): PNode = parMessage(p, errIdentifierExpected, p.tok) break if not sameInd(p): break + elif p.tok.tokType == tkParLe: + parMessage(p, errGenerated, "the syntax for tuple types is 'tuple[...]', not 'tuple(...)'") else: result = newNodeP(nkTupleClassTy, p) @@ -983,6 +986,9 @@ proc parseParamList(p: var TParser, retColon = true): PNode = a = parseIdentColonEquals(p, {withBothOptional, withPragma}) of tkParRi: break + of tkVar: + parMessage(p, errGenerated, "the syntax is 'parameter: var T', not 'var parameter: T'") + break else: parMessage(p, errTokenExpected, ")") break @@ -1675,7 +1681,7 @@ proc parseSection(p: var TParser, kind: TNodeKind, parMessage(p, errIdentifierExpected, p.tok) proc parseConstant(p: var TParser): PNode = - #| constant = identWithPragma (colon typedesc)? '=' optInd expr indAndComment + #| constant = identWithPragma (colon typeDesc)? '=' optInd expr indAndComment result = newNodeP(nkConstDef, p) addSon(result, identWithPragma(p)) if p.tok.tokType == tkColon: @@ -2130,7 +2136,12 @@ proc parseTopLevelStmt(p: var TParser): PNode = if p.tok.indent != 0: if p.firstTok and p.tok.indent < 0: discard elif p.tok.tokType != tkSemiColon: - parMessage(p, errInvalidIndentation) + # special casing for better error messages: + if p.tok.tokType == tkOpr and p.tok.ident.s == "*": + parMessage(p, errGenerated, + "invalid indentation; an export marker '*' follows the declared identifier") + else: + parMessage(p, errInvalidIndentation) p.firstTok = false case p.tok.tokType of tkSemiColon: diff --git a/compiler/semcall.nim b/compiler/semcall.nim index ba38ed215..0fc12f164 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -190,12 +190,12 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors): if wanted != nil and got != nil: effectProblem(wanted, got, candidates) if cond: candidates.add "\n" - elif err.unmatchedVarParam != 0 and err.unmatchedVarParam < n.len: - add(candidates, "for a 'var' type a variable needs to be passed, but '" & + if err.unmatchedVarParam != 0 and err.unmatchedVarParam < n.len: + candidates.add(" for a 'var' type a variable needs to be passed, but '" & renderNotLValue(n[err.unmatchedVarParam]) & "' is immutable\n") for diag in err.diagnostics: - add(candidates, diag & "\n") + candidates.add(diag & "\n") result = (prefer, candidates) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 8e3aeffbe..49beadc8b 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -192,6 +192,10 @@ proc semConv(c: PContext, n: PNode): PNode = result.addSon copyTree(n.sons[0]) + # special case to make MyObject(x = 3) produce a nicer error message: + if n[1].kind == nkExprEqExpr and + targetType.skipTypes(abstractPtrs).kind == tyObject: + localError(n.info, "object contruction uses ':', not '='") var op = semExprWithType(c, n.sons[1]) if targetType.isMetaType: let final = inferWithMetatype(c, targetType, op, true) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index e9f4e1a98..be6eac052 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -257,7 +257,8 @@ proc semArrayIndex(c: PContext, n: PNode): PType = if e.sym.ast != nil: return semArrayIndex(c, e.sym.ast) if not isOrdinalType(e.typ.lastSon): - localError(n[1].info, errOrdinalTypeExpected) + let info = if n.safeLen > 1: n[1].info else: n.info + localError(info, errOrdinalTypeExpected) result = makeRangeWithStaticExpr(c, e) if c.inGenericContext > 0: result.flags.incl tfUnresolved elif e.kind in nkCallKinds and hasGenericArguments(e): @@ -719,7 +720,8 @@ proc semObjectNode(c: PContext, n: PNode, prev: PType): PType = addInheritedFields(c, check, pos, concreteBase) else: if concreteBase.kind != tyError: - localError(n.sons[1].info, errInheritanceOnlyWithNonFinalObjects) + localError(n.sons[1].info, "inheritance only works with non-final objects; " & + "to enable inheritance write '" & typeToString(realBase) & " of RootObj'") base = nil realBase = nil if n.kind != nkObjectTy: internalError(n.info, "semObjectNode") diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 9f802a10e..e4509ff91 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -1242,7 +1242,9 @@ proc typeRelImpl(c: var TCandidate, f, aOrig: PType, if result < isGeneric: result = isNone elif a.kind == tyGenericParam: result = isGeneric - of tyForward: internalError("forward type in typeRel()") + of tyForward: + #internalError("forward type in typeRel()") + result = isNone of tyNil: if a.kind == f.kind: result = isEqual of tyTuple: diff --git a/compiler/vm.nim b/compiler/vm.nim index 23216f0a3..6ea6f1492 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -1490,7 +1490,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = c.debug[pc]) x.flags.incl nfIsRef # prevent crashes in the compiler resulting from wrong macros: - if x.kind == nkIdent: x.ident = getIdent"" + if x.kind == nkIdent: x.ident = c.cache.emptyIdent regs[ra].node = x of opcNCopyNimNode: decodeB(rkNode) diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim index 5395d4bad..dd4bc5060 100644 --- a/compiler/vmdef.nim +++ b/compiler/vmdef.nim @@ -16,7 +16,7 @@ const byteExcess* = 128 # we use excess-K for immediates wordExcess* = 32768 - MaxLoopIterations* = 1500_000 # max iterations of all loops + MaxLoopIterations* = 1_000_000_000 # max iterations of all loops type diff --git a/doc/grammar.txt b/doc/grammar.txt index eae3694a0..f25b079f4 100644 --- a/doc/grammar.txt +++ b/doc/grammar.txt @@ -1,3 +1,4 @@ +# This file is generated by compiler/parser.nim. module = stmt ^* (';' / IND{=}) comma = ',' COMMENT? semicolon = ';' COMMENT? @@ -155,7 +156,7 @@ routine = optInd identVis pattern? genericParamList? paramListColon pragma? ('=' COMMENT? stmt)? indAndComment commentStmt = COMMENT section(p) = COMMENT? p / (IND{>} (p / COMMENT)^+IND{=} DED) -constant = identWithPragma (colon typedesc)? '=' optInd expr indAndComment +constant = identWithPragma (colon typeDesc)? '=' optInd expr indAndComment enum = 'enum' optInd (symbol optInd ('=' optInd expr COMMENT?)? comma?)+ objectWhen = 'when' expr colcom objectPart COMMENT? ('elif' expr colcom objectPart COMMENT?)* diff --git a/doc/manual/lexing.txt b/doc/manual/lexing.txt index 915e87971..0328ffd55 100644 --- a/doc/manual/lexing.txt +++ b/doc/manual/lexing.txt @@ -344,9 +344,11 @@ prefix), binary (prefix ``0b``), octal (prefix ``0o`` or ``0c``) and hexadecimal There exists a literal for each numerical type that is defined. The suffix starting with an apostrophe ('\'') is called a -`type suffix`:idx:. Literals without a type suffix are of the type ``int``, +`type suffix`:idx:. Literals without a type suffix are of an integer type, unless the literal contains a dot or ``E|e`` in which case it is of -type ``float``. For notational convenience the apostrophe of a type suffix +type ``float``. This integer type is ``int`` if the literal is in the range +``low(i32)..high(i32)``, otherwise it is ``int64``. +For notational convenience the apostrophe of a type suffix is optional if it is not ambiguous (only hexadecimal floating point literals with a type suffix can be ambiguous). diff --git a/lib/core/macros.nim b/lib/core/macros.nim index ed9c304fe..d09f5f933 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -678,7 +678,7 @@ proc astGenRepr*(n: NimNode): string {.compileTime, benign.} = ## See also `repr`, `treeRepr`, and `lispRepr`. const - NodeKinds = {nnkEmpty, nnkNilLit, nnkIdent, nnkSym, nnkNone} + NodeKinds = {nnkEmpty, nnkNilLit, nnkIdent, nnkSym, nnkNone, nnkCommentStmt} LitKinds = {nnkCharLit..nnkInt64Lit, nnkFloatLit..nnkFloat64Lit, nnkStrLit..nnkTripleStrLit} proc escape(s: string, prefix = "\"", suffix = "\""): string {.noSideEffect.} = @@ -723,7 +723,7 @@ proc astGenRepr*(n: NimNode): string {.compileTime, benign.} = of nnkCharLit: res.add("'" & $chr(n.intVal) & "'") of nnkIntLit..nnkInt64Lit: res.add($n.intVal) of nnkFloatLit..nnkFloat64Lit: res.add($n.floatVal) - of nnkStrLit..nnkTripleStrLit: res.add($n.strVal.escape()) + of nnkStrLit..nnkTripleStrLit, nnkCommentStmt: res.add($n.strVal.escape()) of nnkIdent: res.add(($n.ident).escape()) of nnkSym: res.add(($n.symbol).escape()) of nnkNone: assert false diff --git a/lib/pure/algorithm.nim b/lib/pure/algorithm.nim index 23a0cdb02..2f7b44b31 100644 --- a/lib/pure/algorithm.nim +++ b/lib/pure/algorithm.nim @@ -84,7 +84,7 @@ proc smartBinarySearch*[T](a: openArray[T], key: T): int = const onlySafeCode = true -proc lowerBound*[T](a: openArray[T], key: T, cmp: proc(x,y: T): int {.closure.}): int = +proc lowerBound*[T, K](a: openArray[T], key: K, cmp: proc(x: T, k: K): int {.closure.}): int = ## same as binarySearch except that if key is not in `a` then this ## returns the location where `key` would be if it were. In other ## words if you have a sorted sequence and you call diff --git a/lib/pure/asyncfile.nim b/lib/pure/asyncfile.nim index 97bec2815..1df7c3fc0 100644 --- a/lib/pure/asyncfile.nim +++ b/lib/pure/asyncfile.nim @@ -78,7 +78,10 @@ proc getFileSize*(f: AsyncFile): int64 = raiseOSError(osLastError()) result = (high shl 32) or low else: + let curPos = lseek(f.fd.cint, 0, SEEK_CUR) result = lseek(f.fd.cint, 0, SEEK_END) + f.offset = lseek(f.fd.cint, curPos, SEEK_SET) + assert(f.offset == curPos) proc newAsyncFile*(fd: AsyncFd): AsyncFile = ## Creates `AsyncFile` with a previously opened file descriptor `fd`. @@ -281,6 +284,7 @@ proc read*(f: AsyncFile, size: int): Future[string] = result = false # We still want this callback to be called. elif res == 0: # EOF + f.offset = lseek(fd.cint, 0, SEEK_CUR) retFuture.complete("") else: readBuffer.setLen(res) diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim index 648481ca5..568e453bd 100644 --- a/lib/pure/httpclient.nim +++ b/lib/pure/httpclient.nim @@ -622,7 +622,7 @@ proc get*(url: string, extraHeaders = "", maxRedirects = 5, ## | An optional timeout can be specified in milliseconds, if reading from the ## server takes longer than specified an ETimeout exception will be raised. ## - ## ## **Deprecated since version 0.15.0**: use ``HttpClient.get`` instead. + ## **Deprecated since version 0.15.0**: use ``HttpClient.get`` instead. result = request(url, httpGET, extraHeaders, "", sslContext, timeout, userAgent, proxy) var lastURL = url diff --git a/lib/pure/json.nim b/lib/pure/json.nim index bbde4db5f..2b58d154e 100644 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -734,7 +734,7 @@ proc getFields*(n: JsonNode, else: return n.fields proc getElems*(n: JsonNode, default: seq[JsonNode] = @[]): seq[JsonNode] = - ## Retrieves the int value of a `JArray JsonNode`. + ## Retrieves the array of a `JArray JsonNode`. ## ## Returns ``default`` if ``n`` is not a ``JArray``, or if ``n`` is nil. if n.isNil or n.kind != JArray: return default diff --git a/lib/pure/xmlparser.nim b/lib/pure/xmlparser.nim index 22bd259b7..9159b4bfc 100644 --- a/lib/pure/xmlparser.nim +++ b/lib/pure/xmlparser.nim @@ -12,9 +12,9 @@ import streams, parsexml, strtabs, xmltree type - XmlError* = object of ValueError ## exception that is raised - ## for invalid XML - errors*: seq[string] ## all detected parsing errors + XmlError* = object of ValueError ## Exception that is raised + ## for invalid XML. + errors*: seq[string] ## All detected parsing errors. {.deprecated: [EInvalidXml: XmlError].} @@ -102,8 +102,8 @@ proc parse(x: var XmlParser, errors: var seq[string]): XmlNode = proc parseXml*(s: Stream, filename: string, errors: var seq[string]): XmlNode = - ## parses the XML from stream `s` and returns a ``PXmlNode``. Every - ## occurred parsing error is added to the `errors` sequence. + ## Parses the XML from stream ``s`` and returns a ``XmlNode``. Every + ## occurred parsing error is added to the ``errors`` sequence. var x: XmlParser open(x, s, filename, {reportComments}) while true: @@ -121,15 +121,20 @@ proc parseXml*(s: Stream, filename: string, close(x) proc parseXml*(s: Stream): XmlNode = - ## parses the XTML from stream `s` and returns a ``PXmlNode``. All parsing - ## errors are turned into an ``EInvalidXML`` exception. + ## Parses the XML from stream ``s`` and returns a ``XmlNode``. All parsing + ## errors are turned into an ``XmlError`` exception. var errors: seq[string] = @[] - result = parseXml(s, "unknown_html_doc", errors) + result = parseXml(s, "unknown_xml_doc", errors) if errors.len > 0: raiseInvalidXml(errors) +proc parseXml*(str: string): XmlNode = + ## Parses the XML from string ``str`` and returns a ``XmlNode``. All parsing + ## errors are turned into an ``XmlError`` exception. + parseXml(newStringStream(str)) + proc loadXml*(path: string, errors: var seq[string]): XmlNode = ## Loads and parses XML from file specified by ``path``, and returns - ## a ``PXmlNode``. Every occurred parsing error is added to the `errors` + ## a ``XmlNode``. Every occurred parsing error is added to the ``errors`` ## sequence. var s = newFileStream(path, fmRead) if s == nil: raise newException(IOError, "Unable to read file: " & path) @@ -137,7 +142,7 @@ proc loadXml*(path: string, errors: var seq[string]): XmlNode = proc loadXml*(path: string): XmlNode = ## Loads and parses XML from file specified by ``path``, and returns - ## a ``PXmlNode``. All parsing errors are turned into an ``EInvalidXML`` + ## a ``XmlNode``. All parsing errors are turned into an ``XmlError`` ## exception. var errors: seq[string] = @[] result = loadXml(path, errors) diff --git a/lib/system.nim b/lib/system.nim index b71a3c73c..7a8c81666 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1774,7 +1774,7 @@ when not defined(nimscript): proc createU*(T: typedesc, size = 1.Positive): ptr T {.inline, benign.} = ## allocates a new memory block with at least ``T.sizeof * size`` ## bytes. The block has to be freed with ``resize(block, 0)`` or - ## ``free(block)``. The block is not initialized, so reading + ## ``dealloc(block)``. The block is not initialized, so reading ## from it before writing to it is undefined behaviour! ## The allocated memory belongs to its allocating thread! ## Use `createSharedU` to allocate from a shared heap. @@ -1789,7 +1789,7 @@ when not defined(nimscript): proc create*(T: typedesc, size = 1.Positive): ptr T {.inline, benign.} = ## allocates a new memory block with at least ``T.sizeof * size`` ## bytes. The block has to be freed with ``resize(block, 0)`` or - ## ``free(block)``. The block is initialized with all bytes + ## ``dealloc(block)``. The block is initialized with all bytes ## containing zero, so it is somewhat safer than ``createU``. ## The allocated memory belongs to its allocating thread! ## Use `createShared` to allocate from a shared heap. @@ -1807,7 +1807,7 @@ when not defined(nimscript): ## grows or shrinks a given memory block. If p is **nil** then a new ## memory block is returned. In either way the block has at least ## ``T.sizeof * newSize`` bytes. If ``newSize == 0`` and p is not - ## **nil** ``resize`` calls ``free(p)``. In other cases the block + ## **nil** ``resize`` calls ``dealloc(p)``. In other cases the block ## has to be freed with ``free``. The allocated memory belongs to ## its allocating thread! ## Use `resizeShared` to reallocate from a shared heap. diff --git a/lib/system/alloc.nim b/lib/system/alloc.nim index 4291013a2..6aef4f411 100644 --- a/lib/system/alloc.nim +++ b/lib/system/alloc.nim @@ -29,7 +29,9 @@ const FliOffset = 6 RealFli = MaxFli - FliOffset - HugeChunkSize = int high(int32) - 1 # 2 GB, depends on TLSF's impl + # size of chunks in last matrix bin + MaxBigChunkSize = 1 shl MaxFli - 1 shl (MaxFli-MaxLog2Sli-1) + HugeChunkSize = MaxBigChunkSize + 1 type PTrunk = ptr Trunk @@ -154,10 +156,11 @@ proc mappingSearch(r, fl, sl: var int) {.inline.} = # PageSize alignment: let t = roundup((1 shl (msbit(uint32 r) - MaxLog2Sli)), PageSize) - 1 r = r + t + r = r and not t + r = min(r, MaxBigChunkSize) fl = msbit(uint32 r) sl = (r shr (fl - MaxLog2Sli)) - MaxSli dec fl, FliOffset - r = r and not t sysAssert((r and PageMask) == 0, "mappingSearch: still not aligned") # See http://www.gii.upv.es/tlsf/files/papers/tlsf_desc.pdf for details of @@ -518,55 +521,61 @@ proc updatePrevSize(a: var MemRegion, c: PBigChunk, if isAccessible(a, ri): ri.prevSize = prevSize or (ri.prevSize and 1) +proc splitChunk2(a: var MemRegion, c: PBigChunk, size: int): PBigChunk = + result = cast[PBigChunk](cast[ByteAddress](c) +% size) + result.size = c.size - size + track("result.origSize", addr result.origSize, sizeof(int)) + # XXX check if these two nil assignments are dead code given + # addChunkToMatrix's implementation: + result.next = nil + result.prev = nil + # size and not used: + result.prevSize = size + sysAssert((size and 1) == 0, "splitChunk 2") + sysAssert((size and PageMask) == 0, + "splitChunk: size is not a multiple of the PageSize") + updatePrevSize(a, c, result.size) + c.size = size + incl(a, a.chunkStarts, pageIndex(result)) + +proc splitChunk(a: var MemRegion, c: PBigChunk, size: int) = + let rest = splitChunk2(a, c, size) + addChunkToMatrix(a, rest) + proc freeBigChunk(a: var MemRegion, c: PBigChunk) = var c = c sysAssert(c.size >= PageSize, "freeBigChunk") inc(a.freeMem, c.size) - when coalescRight: - var ri = cast[PChunk](cast[ByteAddress](c) +% c.size) - sysAssert((cast[ByteAddress](ri) and PageMask) == 0, "freeBigChunk 2") - if isAccessible(a, ri) and chunkUnused(ri): - sysAssert(not isSmallChunk(ri), "freeBigChunk 3") - if not isSmallChunk(ri): - removeChunkFromMatrix(a, cast[PBigChunk](ri)) - inc(c.size, ri.size) - excl(a.chunkStarts, pageIndex(ri)) + c.prevSize = c.prevSize and not 1 # set 'used' to false when coalescLeft: - let prevSize = c.prevSize and not 1 + let prevSize = c.prevSize if prevSize != 0: var le = cast[PChunk](cast[ByteAddress](c) -% prevSize) sysAssert((cast[ByteAddress](le) and PageMask) == 0, "freeBigChunk 4") if isAccessible(a, le) and chunkUnused(le): sysAssert(not isSmallChunk(le), "freeBigChunk 5") - if not isSmallChunk(le): + if not isSmallChunk(le) and le.size < MaxBigChunkSize: removeChunkFromMatrix(a, cast[PBigChunk](le)) inc(le.size, c.size) excl(a.chunkStarts, pageIndex(c)) c = cast[PBigChunk](le) - - incl(a, a.chunkStarts, pageIndex(c)) - updatePrevSize(a, c, c.size) + if c.size > MaxBigChunkSize: + let rest = splitChunk2(a, c, MaxBigChunkSize) + addChunkToMatrix(a, c) + c = rest + when coalescRight: + var ri = cast[PChunk](cast[ByteAddress](c) +% c.size) + sysAssert((cast[ByteAddress](ri) and PageMask) == 0, "freeBigChunk 2") + if isAccessible(a, ri) and chunkUnused(ri): + sysAssert(not isSmallChunk(ri), "freeBigChunk 3") + if not isSmallChunk(ri) and c.size < MaxBigChunkSize: + removeChunkFromMatrix(a, cast[PBigChunk](ri)) + inc(c.size, ri.size) + excl(a.chunkStarts, pageIndex(ri)) + if c.size > MaxBigChunkSize: + let rest = splitChunk2(a, c, MaxBigChunkSize) + addChunkToMatrix(a, rest) addChunkToMatrix(a, c) - # set 'used' to false: - c.prevSize = c.prevSize and not 1 - -proc splitChunk(a: var MemRegion, c: PBigChunk, size: int) = - var rest = cast[PBigChunk](cast[ByteAddress](c) +% size) - rest.size = c.size - size - track("rest.origSize", addr rest.origSize, sizeof(int)) - # XXX check if these two nil assignments are dead code given - # addChunkToMatrix's implementation: - rest.next = nil - rest.prev = nil - # size and not used: - rest.prevSize = size - sysAssert((size and 1) == 0, "splitChunk 2") - sysAssert((size and PageMask) == 0, - "splitChunk: size is not a multiple of the PageSize") - updatePrevSize(a, c, rest.size) - c.size = size - incl(a, a.chunkStarts, pageIndex(rest)) - addChunkToMatrix(a, rest) proc getBigChunk(a: var MemRegion, size: int): PBigChunk = sysAssert(size > 0, "getBigChunk 2") diff --git a/tests/async/hello.txt b/tests/async/hello.txt new file mode 100644 index 000000000..854d6c20a --- /dev/null +++ b/tests/async/hello.txt @@ -0,0 +1 @@ +hello humans! \ No newline at end of file diff --git a/tests/async/tasyncfile.nim b/tests/async/tasyncfile.nim index aa7f03ab1..c7b71a2f7 100644 --- a/tests/async/tasyncfile.nim +++ b/tests/async/tasyncfile.nim @@ -1,4 +1,8 @@ discard """ + output: '''13 +hello humans! +13 +''' file: "tasyncfile.nim" exitcode: 0 """ @@ -48,5 +52,12 @@ proc main() {.async.} = doAssert data == "t3" file.close() + # Issue #7347 + block: + let appDir = getAppDir() + var file = openAsync(appDir & DirSep & "hello.txt") + echo file.getFileSize() + echo await file.readAll() + echo file.getFilePos() waitFor main() diff --git a/tests/ccgbugs/mymodule.nim b/tests/ccgbugs/mymodule.nim new file mode 100644 index 000000000..d3306ec49 --- /dev/null +++ b/tests/ccgbugs/mymodule.nim @@ -0,0 +1,12 @@ +type + MyRefObject* = ref object + s: string + + +proc newMyRefObject*(s: string): MyRefObject = + new(result) + result.s = s + +proc `$`*(o: MyRefObject): string = + o.s + \ No newline at end of file diff --git a/tests/ccgbugs/tforward_decl_only.nim b/tests/ccgbugs/tforward_decl_only.nim new file mode 100644 index 000000000..dcd74eaf4 --- /dev/null +++ b/tests/ccgbugs/tforward_decl_only.nim @@ -0,0 +1,15 @@ +discard """ +ccodecheck: "\\i !@('struct tyObject_MyRefObject'[0-z]+' {')" +output: "hello" +""" + +# issue #7339 +# Test that MyRefObject is only forward declared as it used only by reference + +import mymodule +type AnotherType = object + f: MyRefObject + +let x = AnotherType(f: newMyRefObject("hello")) +echo $x.f + diff --git a/tests/ccgbugs/tresult_of_array.nim b/tests/ccgbugs/tresult_of_array.nim new file mode 100644 index 000000000..fb5abf18a --- /dev/null +++ b/tests/ccgbugs/tresult_of_array.nim @@ -0,0 +1,29 @@ +discard """ + output: '''false +true +false +[false, false, false] +''' +""" + +# bug #7332 +# resetLoc generate incorrect memset code +# because of array passed as argument decaying into a pointer + +import tables +const tableOfArray = { + "one": [true, false, false], + "two": [false, true, false], + "three": [false, false, true] +}.toTable() +for i in 0..2: + echo tableOfArray["two"][i] + +var seqOfArray = @[ + [true, false, false], + [false, true, false], + [false, false, true] +] +proc crashingProc*[B](t: seq[B], index: Natural): B = + discard +echo seqOfArray.crashingProc(0) diff --git a/tests/macros/tdumpastgen.nim b/tests/macros/tdumpastgen.nim index faed77225..0a1836886 100644 --- a/tests/macros/tdumpastgen.nim +++ b/tests/macros/tdumpastgen.nim @@ -2,16 +2,33 @@ discard """ msg: '''nnkStmtList.newTree( nnkVarSection.newTree( nnkIdentDefs.newTree( - newIdentNode(!"x"), + newIdentNode("x"), newEmptyNode(), nnkCall.newTree( nnkDotExpr.newTree( - newIdentNode(!"foo"), - newIdentNode(!"create") + newIdentNode("baz"), + newIdentNode("create") ), newLit(56) ) ) + ), + nnkProcDef.newTree( + newIdentNode("foo"), + newEmptyNode(), + newEmptyNode(), + nnkFormalParams.newTree( + newEmptyNode() + ), + newEmptyNode(), + newEmptyNode(), + nnkStmtList.newTree( + newCommentStmtNode("This is a docstring"), + nnkCommand.newTree( + newIdentNode("echo"), + newLit("bar") + ) + ) ) )''' """ @@ -21,5 +38,8 @@ msg: '''nnkStmtList.newTree( import macros dumpAstGen: - var x = foo.create(56) + var x = baz.create(56) + proc foo() = + ## This is a docstring + echo "bar" diff --git a/tests/system/alloc.nim b/tests/system/talloc.nim index 7abefec2a..18396041d 100644 --- a/tests/system/alloc.nim +++ b/tests/system/talloc.nim @@ -8,7 +8,7 @@ x.dealloc() x = createU(int, 3) assert x != nil -x.free() +x.dealloc() x = create(int, 4) assert cast[ptr array[4, int]](x)[0] == 0 @@ -18,7 +18,7 @@ assert cast[ptr array[4, int]](x)[3] == 0 x = x.resize(4) assert x != nil -x.free() +x.dealloc() x = cast[ptr int](allocShared(100)) assert x != nil @@ -26,7 +26,7 @@ deallocShared(x) x = createSharedU(int, 3) assert x != nil -x.freeShared() +x.deallocShared() x = createShared(int, 3) assert x != nil @@ -37,7 +37,7 @@ assert cast[ptr array[3, int]](x)[2] == 0 assert x != nil x = cast[ptr int](x.resizeShared(2)) assert x != nil -x.freeShared() +x.deallocShared() x = create(int, 10) assert x != nil @@ -49,4 +49,9 @@ x = createShared(int, 1) assert x != nil x = x.resizeShared(1) assert x != nil -x.freeShared() +x.deallocShared() + +x = cast[ptr int](alloc0(125 shl 23)) +dealloc(x) +x = cast[ptr int](alloc0(126 shl 23)) +dealloc(x) diff --git a/tests/system/talloc2.nim b/tests/system/talloc2.nim new file mode 100644 index 000000000..c8cab78a1 --- /dev/null +++ b/tests/system/talloc2.nim @@ -0,0 +1,37 @@ +const + nmax = 2*1024*1024*1024 + +proc test(n: int) = + var a = alloc0(9999) + var t = cast[ptr UncheckedArray[int8]](alloc(n)) + var b = alloc0(9999) + t[0] = 1 + t[1] = 2 + t[n-2] = 3 + t[n-1] = 4 + dealloc(a) + dealloc(t) + dealloc(b) + +# allocator adds 48 bytes to BigChunk +# BigChunk allocator edges at 2^n * (1 - s) for s = [1..32]/64 +proc test2(n: int) = + let d = n div 256 # cover edges and more + for i in countdown(128,1): + for j in [-4096, -64, -49, -48, -47, -32, 0, 4096]: + let b = n + j - i*d + if b>0 and b<=nmax: + test(b) + #echo b, ": ", getTotalMem(), " ", getOccupiedMem(), " ", getFreeMem() + +proc test3 = + var n = 1 + while n <= nmax: + test2(n) + n *= 2 + n = nmax + while n >= 1: + test2(n) + n = n div 2 + +test3() diff --git a/tests/system/io.nim b/tests/system/tio.nim index 3d4df806b..3d4df806b 100644 --- a/tests/system/io.nim +++ b/tests/system/tio.nim diff --git a/tests/system/params.nim b/tests/system/tparams.nim index 1358212f2..1358212f2 100644 --- a/tests/system/params.nim +++ b/tests/system/tparams.nim diff --git a/todo.txt b/todo.txt index e06ddf555..32bc32630 100644 --- a/todo.txt +++ b/todo.txt @@ -45,7 +45,6 @@ Bugs - VM: Pegs do not work at compile-time - blocks can "export" an identifier but the CCG generates {} for them ... -- ConcreteTypes in a 'case' means we don't check for duplicated case branches GC |