From 10328e50a5450174d1226f8f362bd83f93b2ba6d Mon Sep 17 00:00:00 2001 From: Tomohiro Date: Sat, 6 May 2023 19:03:45 +0900 Subject: [PATCH 01/15] Document about size pragma (#21794) * Document about size pragma * Fix typos * Fix manual.md * Update doc/manual.md --------- Co-authored-by: Andreas Rumpf --- doc/manual.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/doc/manual.md b/doc/manual.md index 2b982488f0..cb2509bd28 100644 --- a/doc/manual.md +++ b/doc/manual.md @@ -7485,6 +7485,37 @@ generates: ``` +size pragma +----------- +Nim automatically determines the size of an enum. +But when wrapping a C enum type, it needs to be of a specific size. +The `size pragma` allows specifying the size of the enum type. + + ```Nim + type + EventType* {.size: sizeof(uint32).} = enum + QuitEvent, + AppTerminating, + AppLowMemory + + doAssert sizeof(EventType) == sizeof(uint32) + ``` + +The `size pragma` can also specify the size of an `importc` incomplete object type +so that one can get the size of it at compile time even if it was declared without fields. + + ```Nim + type + AtomicFlag* {.importc: "atomic_flag", header: "", size: 1.} = object + + static: + # if AtomicFlag didn't have the size pragma, this code would result in a compile time error. + echo sizeof(AtomicFlag) + ``` + +The `size pragma` accepts only the values 1, 2, 4 or 8. + + Align pragma ------------ From b74d49c037734079765770426d0f5c79dee6cf87 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sat, 6 May 2023 17:58:00 +0200 Subject: [PATCH 02/15] ORC: make rootsThreshold thread local [backport] (#21799) --- lib/system/orc.nim | 10 +++++----- tests/arc/tasyncleak.nim | 2 +- tests/arc/topt_no_cursor.nim | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/system/orc.nim b/lib/system/orc.nim index a56a0c0574..2addefdcfe 100644 --- a/lib/system/orc.nim +++ b/lib/system/orc.nim @@ -350,7 +350,7 @@ const when defined(nimStressOrc): const rootsThreshold = 10 # broken with -d:nimStressOrc: 10 and for havlak iterations 1..8 else: - var rootsThreshold = defaultThreshold + var rootsThreshold {.threadvar.}: int proc partialCollect(lowMark: int) = when false: @@ -392,13 +392,13 @@ proc collectCycles() = # of the cycle collector's effectiveness: # we're effective when we collected 50% or more of the nodes # we touched. If we're effective, we can reset the threshold: - if j.keepThreshold and rootsThreshold <= defaultThreshold: + if j.keepThreshold and rootsThreshold <= 0: discard elif j.freed * 2 >= j.touched: when not defined(nimFixedOrc): rootsThreshold = max(rootsThreshold div 3 * 2, 16) else: - rootsThreshold = defaultThreshold + rootsThreshold = 0 #cfprintf(cstderr, "[collectCycles] freed %ld, touched %ld new threshold %ld\n", j.freed, j.touched, rootsThreshold) elif rootsThreshold < high(int) div 4: rootsThreshold = rootsThreshold * 3 div 2 @@ -411,7 +411,7 @@ proc registerCycle(s: Cell; desc: PNimTypeV2) = if roots.d == nil: init(roots) add(roots, s, desc) - if roots.len >= rootsThreshold: + if roots.len >= rootsThreshold+defaultThreshold: collectCycles() when logOrc: writeCell("[added root]", s, desc) @@ -427,7 +427,7 @@ proc GC_enableOrc*() = ## Enables the cycle collector subsystem of `--gc:orc`. This is a `--gc:orc` ## specific API. Check with `when defined(gcOrc)` for its existence. when not defined(nimStressOrc): - rootsThreshold = defaultThreshold + rootsThreshold = 0 proc GC_disableOrc*() = ## Disables the cycle collector subsystem of `--gc:orc`. This is a `--gc:orc` diff --git a/tests/arc/tasyncleak.nim b/tests/arc/tasyncleak.nim index eb0c452131..8e3a7b3e7b 100644 --- a/tests/arc/tasyncleak.nim +++ b/tests/arc/tasyncleak.nim @@ -1,5 +1,5 @@ discard """ - outputsub: "(allocCount: 4302, deallocCount: 4300)" + outputsub: "(allocCount: 4050, deallocCount: 4048)" cmd: "nim c --gc:orc -d:nimAllocStats $file" """ diff --git a/tests/arc/topt_no_cursor.nim b/tests/arc/topt_no_cursor.nim index 50dfa26ac0..26dc254475 100644 --- a/tests/arc/topt_no_cursor.nim +++ b/tests/arc/topt_no_cursor.nim @@ -1,6 +1,6 @@ discard """ nimoutFull: true - cmd: '''nim c -r --warnings:off --hints:off --gc:arc --expandArc:newTarget --expandArc:delete --expandArc:p1 --expandArc:tt --hint:Performance:off --assertions:off --expandArc:extractConfig --expandArc:mergeShadowScope --expandArc:check $file''' + cmd: '''nim c -r --warnings:off --hints:off --mm:arc --expandArc:newTarget --expandArc:delete --expandArc:p1 --expandArc:tt --hint:Performance:off --assertions:off --expandArc:extractConfig --expandArc:mergeShadowScope --expandArc:check $file''' nimout: ''' --expandArc: newTarget From 53c15f24e923379f74506949eb49433d232b48ad Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Sun, 7 May 2023 00:04:08 +0800 Subject: [PATCH 03/15] fixes #21704; remove nfIsRef for genLit in VM (#21765) * fixes #21704; remove `nfIsRef` for genLit * remove nfIsRef from the output of macros * make the logic better * try again * act together * excl nfIsRef --- compiler/vmgen.nim | 1 + tests/vm/t21704.nim | 69 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 tests/vm/t21704.nim diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 48792790d8..db3276aada 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -443,6 +443,7 @@ proc rawGenLiteral(c: PCtx; n: PNode): int = result = c.constants.len #assert(n.kind != nkCall) n.flags.incl nfAllConst + n.flags.excl nfIsRef c.constants.add n internalAssert c.config, result < regBxMax diff --git a/tests/vm/t21704.nim b/tests/vm/t21704.nim new file mode 100644 index 0000000000..27f4f5b061 --- /dev/null +++ b/tests/vm/t21704.nim @@ -0,0 +1,69 @@ +discard """ +matrix: "--hints:off" +nimout: ''' +Found 2 tests to run. +Found 3 benches to compile. + + --passC:-Wno-stringop-overflow --passL:-Wno-stringop-overflow + + --passC:-Wno-stringop-overflow --passL:-Wno-stringop-overflow + + --passC:-Wno-stringop-overflow --passL:-Wno-stringop-overflow +''' +""" +# bug #21704 +import std/strformat + +const testDesc: seq[string] = @[ + "tests/t_hash_sha256_vs_openssl.nim", + "tests/t_cipher_chacha20.nim" +] +const benchDesc = [ + "bench_sha256", + "bench_hash_to_curve", + "bench_ethereum_bls_signatures" +] + +proc setupTestCommand(flags, path: string): string = + return "nim c -r " & + flags & + &" --nimcache:nimcache/{path} " & # Commenting this out also solves the issue + path + +proc testBatch(commands: var string, flags, path: string) = + commands &= setupTestCommand(flags, path) & '\n' + +proc setupBench(benchName: string): string = + var runFlags = if false: " -r " + else: " " # taking this branch is needed to trigger the bug + + echo runFlags # Somehow runflags isn't reset in corner cases + runFlags &= " --passC:-Wno-stringop-overflow --passL:-Wno-stringop-overflow " + echo runFlags + + return "nim c " & + runFlags & + &" benchmarks/{benchName}.nim" + +proc buildBenchBatch(commands: var string, benchName: string) = + let command = setupBench(benchName) + commands &= command & '\n' + +proc addTestSet(cmdFile: var string) = + echo "Found " & $testDesc.len & " tests to run." + + for path in testDesc: + var flags = "" # This is important + cmdFile.testBatch(flags, path) + +proc addBenchSet(cmdFile: var string) = + echo "Found " & $benchDesc.len & " benches to compile." + for bd in benchDesc: + cmdFile.buildBenchBatch(bd) + +proc task_bug() = + var cmdFile: string + cmdFile.addTestSet() # Comment this out and there is no bug + cmdFile.addBenchSet() + +static: task_bug() From 365a753eed70f817b43fd8c76bdfaf28ab001561 Mon Sep 17 00:00:00 2001 From: quantimnot <54247259+quantimnot@users.noreply.github.com> Date: Sat, 6 May 2023 13:10:13 -0400 Subject: [PATCH 04/15] Fix some `styleCheck` bugs (#20095) refs #19822 Fixes these bugs: * Style check violations in generics defined in foreign packages are raised. * Builtin pragma usage style check violations in foreign packages are raised. * User pragma definition style check violations are not raised. Co-authored-by: quantimnot --- compiler/linter.nim | 14 +++--- compiler/pragmas.nim | 3 +- .../foreign_package/foreign_package.nim | 1 + .../foreign_package/foreign_package.nimble | 2 + tests/stylecheck/tforeign_package.nim | 16 +++++++ tests/stylecheck/thint.nim | 43 +++++++++++++++++++ 6 files changed, 73 insertions(+), 6 deletions(-) create mode 100644 tests/stylecheck/foreign_package/foreign_package.nim create mode 100644 tests/stylecheck/foreign_package/foreign_package.nimble create mode 100644 tests/stylecheck/tforeign_package.nim create mode 100644 tests/stylecheck/thint.nim diff --git a/compiler/linter.nim b/compiler/linter.nim index 0c2aaef792..f3e2d62071 100644 --- a/compiler/linter.nim +++ b/compiler/linter.nim @@ -95,7 +95,7 @@ template styleCheckDef*(ctx: PContext; info: TLineInfo; sym: PSym; k: TSymKind) if optStyleCheck in ctx.config.options and # ignore if styleChecks are off {optStyleHint, optStyleError} * ctx.config.globalOptions != {} and # check only if hint/error is enabled hintName in ctx.config.notes and # ignore if name checks are not requested - ctx.config.belongsToProjectPackage(ctx.module) and # ignore foreign packages + ctx.config.belongsToProjectPackage(sym) and # ignore foreign packages optStyleUsages notin ctx.config.globalOptions and # ignore if requested to only check name usage sym.kind != skResult and # ignore `result` sym.kind != skTemp and # ignore temporary variables created by the compiler @@ -136,7 +136,7 @@ template styleCheckUse*(ctx: PContext; info: TLineInfo; sym: PSym) = ## Check symbol uses match their definition's style. if {optStyleHint, optStyleError} * ctx.config.globalOptions != {} and # ignore if styleChecks are off hintName in ctx.config.notes and # ignore if name checks are not requested - ctx.config.belongsToProjectPackage(ctx.module) and # ignore foreign packages + ctx.config.belongsToProjectPackage(sym) and # ignore foreign packages sym.kind != skTemp and # ignore temporary variables created by the compiler sym.name.s[0] in Letters and # ignore operators TODO: what about unicode symbols??? sfAnon notin sym.flags: # ignore temporary variables created by the compiler @@ -147,6 +147,10 @@ proc checkPragmaUseImpl(conf: ConfigRef; info: TLineInfo; w: TSpecialWord; pragm if pragmaName != wanted: lintReport(conf, info, wanted, pragmaName) -template checkPragmaUse*(conf: ConfigRef; info: TLineInfo; w: TSpecialWord; pragmaName: string) = - if {optStyleHint, optStyleError} * conf.globalOptions != {}: - checkPragmaUseImpl(conf, info, w, pragmaName) +template checkPragmaUse*(ctx: PContext; info: TLineInfo; w: TSpecialWord; pragmaName: string, sym: PSym) = + ## Check builtin pragma uses match their definition's style. + ## Note: This only applies to builtin pragmas, not user pragmas. + if {optStyleHint, optStyleError} * ctx.config.globalOptions != {} and # ignore if styleChecks are off + hintName in ctx.config.notes and # ignore if name checks are not requested + (sym != nil and ctx.config.belongsToProjectPackage(sym)): # ignore foreign packages + checkPragmaUseImpl(ctx.config, info, w, pragmaName) diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 9f2eeb002f..10d77a17e9 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -676,6 +676,7 @@ proc processPragma(c: PContext, n: PNode, i: int) = invalidPragma(c, n) var userPragma = newSym(skTemplate, it[1].ident, c.idgen, c.module, it.info, c.config.options) + styleCheckDef(c, userPragma) userPragma.ast = newTreeI(nkPragma, n.info, n.sons[i+1..^1]) strTableAdd(c.userPragmas, userPragma) @@ -863,7 +864,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, else: let k = whichKeyword(ident) if k in validPragmas: - checkPragmaUse(c.config, key.info, k, ident.s) + checkPragmaUse(c, key.info, k, ident.s, (if sym != nil: sym else: c.module)) case k of wExportc, wExportCpp: makeExternExport(c, sym, getOptionalStr(c, it, "$1"), it.info) diff --git a/tests/stylecheck/foreign_package/foreign_package.nim b/tests/stylecheck/foreign_package/foreign_package.nim new file mode 100644 index 0000000000..f95be006c6 --- /dev/null +++ b/tests/stylecheck/foreign_package/foreign_package.nim @@ -0,0 +1 @@ +include ../thint \ No newline at end of file diff --git a/tests/stylecheck/foreign_package/foreign_package.nimble b/tests/stylecheck/foreign_package/foreign_package.nimble new file mode 100644 index 0000000000..a2c49e3898 --- /dev/null +++ b/tests/stylecheck/foreign_package/foreign_package.nimble @@ -0,0 +1,2 @@ +# See `tstyleCheck` +# Needed to mark `mstyleCheck` as a foreign package. diff --git a/tests/stylecheck/tforeign_package.nim b/tests/stylecheck/tforeign_package.nim new file mode 100644 index 0000000000..8594ad802e --- /dev/null +++ b/tests/stylecheck/tforeign_package.nim @@ -0,0 +1,16 @@ +discard """ + matrix: "--errorMax:0 --styleCheck:error" + action: compile +""" + +import foreign_package/foreign_package + +# This call tests that: +# - an instantiation of a generic in a foreign package doesn't raise errors +# when the generic body contains: +# - definition and usage violations +# - builtin pragma usage violations +# - user pragma usage violations +# - definition violations in foreign packages are ignored +# - usage violations in foreign packages are ignored +genericProc[int]() diff --git a/tests/stylecheck/thint.nim b/tests/stylecheck/thint.nim new file mode 100644 index 0000000000..c19aac1b89 --- /dev/null +++ b/tests/stylecheck/thint.nim @@ -0,0 +1,43 @@ +discard """ + matrix: "--styleCheck:hint" + action: compile +""" + +# Test violating ident definition: +{.pragma: user_pragma.} #[tt.Hint + ^ 'user_pragma' should be: 'userPragma' [Name] ]# + +# Test violating ident usage style matches definition style: +{.userPragma.} #[tt.Hint + ^ 'userPragma' should be: 'user_pragma' [template declared in thint.nim(7, 9)] [Name] ]# + +# Test violating builtin pragma usage style: +{.no_side_effect.}: #[tt.Hint + ^ 'no_side_effect' should be: 'noSideEffect' [Name] ]# + discard 0 + +# Test: +# - definition style violation +# - user pragma usage style violation +# - builtin pragma usage style violation +proc generic_proc*[T] {.no_destroy, userPragma.} = #[tt.Hint + ^ 'generic_proc' should be: 'genericProc' [Name]; tt.Hint + ^ 'no_destroy' should be: 'nodestroy' [Name]; tt.Hint + ^ 'userPragma' should be: 'user_pragma' [template declared in thint.nim(7, 9)] [Name] ]# + # Test definition style violation: + let snake_case = 0 #[tt.Hint + ^ 'snake_case' should be: 'snakeCase' [Name] ]# + # Test user pragma definition style violation: + {.pragma: another_user_pragma.} #[tt.Hint + ^ 'another_user_pragma' should be: 'anotherUserPragma' [Name] ]# + # Test user pragma usage style violation: + {.anotherUserPragma.} #[tt.Hint + ^ 'anotherUserPragma' should be: 'another_user_pragma' [template declared in thint.nim(31, 11)] [Name] ]# + # Test violating builtin pragma usage style: + {.no_side_effect.}: #[tt.Hint + ^ 'no_side_effect' should be: 'noSideEffect' [Name] ]# + # Test usage style violation: + discard snakeCase #[tt.Hint + ^ 'snakeCase' should be: 'snake_case' [let declared in thint.nim(28, 7)] [Name] ]# + +generic_proc[int]() From d0c62fa169f3970653ce0d5bbd16e123efb24251 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sat, 6 May 2023 21:25:45 +0200 Subject: [PATCH 05/15] fixes #21753 [backport] (#21802) --- compiler/types.nim | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/compiler/types.nim b/compiler/types.nim index d2517127a9..2c2dec639c 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -396,9 +396,9 @@ proc canFormAcycleAux(marker: var IntSet, typ: PType, startId: int): bool = result = true elif not containsOrIncl(marker, t.id): for i in 0.. Date: Sat, 6 May 2023 22:27:28 +0300 Subject: [PATCH 06/15] some Token refactors (#21762) * test some Token refactors * fix CI * showcase for more reductions, will revert * Revert "showcase for more reductions, will revert" This reverts commit 5ba48591f4d53e8d83a27de8b03d26c6178dd3d1. * make line and column int32 * remove int32 change --- compiler/layouter.nim | 6 ++--- compiler/lexer.nim | 34 +++++++++++-------------- compiler/parser.nim | 17 ++++++------- nimpretty/tests/exhaustive.nim | 2 +- nimpretty/tests/expected/exhaustive.nim | 2 +- 5 files changed, 28 insertions(+), 33 deletions(-) diff --git a/compiler/layouter.nim b/compiler/layouter.nim index 6e8280e674..7cff98b11b 100644 --- a/compiler/layouter.nim +++ b/compiler/layouter.nim @@ -510,7 +510,7 @@ proc emitTok*(em: var Emitter; L: Lexer; tok: Token) = rememberSplit(splitComma) wrSpace em of openPars: - if tok.strongSpaceA and not em.endsInWhite and + if tsLeading in tok.spacing and not em.endsInWhite and (not em.wasExportMarker or tok.tokType == tkCurlyDotLe): wrSpace em wr(em, $tok.tokType, ltSomeParLe) @@ -528,7 +528,7 @@ proc emitTok*(em: var Emitter; L: Lexer; tok: Token) = wr(em, $tok.tokType, ltOther) if not em.inquote: wrSpace(em) of tkOpr, tkDotDot: - if em.inquote or (((not tok.strongSpaceA) and tok.strongSpaceB == tsNone) and + if em.inquote or (tok.spacing == {} and tok.ident.s notin ["<", ">", "<=", ">=", "==", "!="]): # bug #9504: remember to not spacify a keyword: lastTokWasTerse = true @@ -538,7 +538,7 @@ proc emitTok*(em: var Emitter; L: Lexer; tok: Token) = if not em.endsInWhite: wrSpace(em) wr(em, tok.ident.s, ltOpr) template isUnary(tok): bool = - tok.strongSpaceB == tsNone and tok.strongSpaceA + tok.spacing == {tsLeading} if not isUnary(tok): rememberSplit(splitBinary) diff --git a/compiler/lexer.nim b/compiler/lexer.nim index a62d40e54c..67dafc59fa 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -94,19 +94,18 @@ type base2, base8, base16 TokenSpacing* = enum - tsNone, tsTrailing, tsEof + tsLeading, tsTrailing, tsEof Token* = object # a Nim token tokType*: TokType # the type of the token + base*: NumericalBase # the numerical base; only valid for int + # or float literals + spacing*: set[TokenSpacing] # spaces around token indent*: int # the indentation; != -1 if the token has been # preceded with indentation ident*: PIdent # the parsed identifier iNumber*: BiggestInt # the parsed integer literal fNumber*: BiggestFloat # the parsed floating point literal - base*: NumericalBase # the numerical base; only valid for int - # or float literals - strongSpaceA*: bool # leading spaces of an operator - strongSpaceB*: TokenSpacing # trailing spaces of an operator literal*: string # the parsed (string) literal; and # documentation comments are here too line*, col*: int @@ -178,7 +177,7 @@ proc initToken*(L: var Token) = L.tokType = tkInvalid L.iNumber = 0 L.indent = 0 - L.strongSpaceA = false + L.spacing = {} L.literal = "" L.fNumber = 0.0 L.base = base10 @@ -191,7 +190,7 @@ proc fillToken(L: var Token) = L.tokType = tkInvalid L.iNumber = 0 L.indent = 0 - L.strongSpaceA = false + L.spacing = {} setLen(L.literal, 0) L.fNumber = 0.0 L.base = base10 @@ -960,13 +959,15 @@ proc getOperator(L: var Lexer, tok: var Token) = tokenEnd(tok, pos-1) # advance pos but don't store it in L.bufpos so the next token (which might # be an operator too) gets the preceding spaces: - tok.strongSpaceB = tsNone + tok.spacing = tok.spacing - {tsTrailing, tsEof} + var trailing = false while L.buf[pos] == ' ': inc pos - if tok.strongSpaceB != tsTrailing: - tok.strongSpaceB = tsTrailing + trailing = true if L.buf[pos] in {CR, LF, nimlexbase.EndOfFile}: - tok.strongSpaceB = tsEof + tok.spacing.incl(tsEof) + elif trailing: + tok.spacing.incl(tsTrailing) proc getPrecedence*(tok: Token): int = ## Calculates the precedence of the given token. @@ -1077,7 +1078,6 @@ proc skipMultiLineComment(L: var Lexer; tok: var Token; start: int; when defined(nimpretty): tok.literal.add "\L" if isDoc: when not defined(nimpretty): tok.literal.add "\n" - inc tok.iNumber var c = toStrip while L.buf[pos] == ' ' and c > 0: inc pos @@ -1096,8 +1096,6 @@ proc skipMultiLineComment(L: var Lexer; tok: var Token; start: int; proc scanComment(L: var Lexer, tok: var Token) = var pos = L.bufpos tok.tokType = tkComment - # iNumber contains the number of '\n' in the token - tok.iNumber = 0 assert L.buf[pos+1] == '#' when defined(nimpretty): tok.commentOffsetA = L.offsetBase + pos @@ -1140,7 +1138,6 @@ proc scanComment(L: var Lexer, tok: var Token) = while L.buf[pos] == ' ' and c > 0: inc pos dec c - inc tok.iNumber else: if L.buf[pos] > ' ': L.indentAhead = indent @@ -1153,7 +1150,7 @@ proc scanComment(L: var Lexer, tok: var Token) = proc skip(L: var Lexer, tok: var Token) = var pos = L.bufpos tokenBegin(tok, pos) - tok.strongSpaceA = false + tok.spacing.excl(tsLeading) when defined(nimpretty): var hasComment = false var commentIndent = L.currLineIndent @@ -1164,8 +1161,7 @@ proc skip(L: var Lexer, tok: var Token) = case L.buf[pos] of ' ': inc(pos) - if not tok.strongSpaceA: - tok.strongSpaceA = true + tok.spacing.incl(tsLeading) of '\t': if not L.allowTabs: lexMessagePos(L, errGenerated, pos, "tabs are not allowed, use spaces instead") inc(pos) @@ -1187,7 +1183,7 @@ proc skip(L: var Lexer, tok: var Token) = pos = L.bufpos else: break - tok.strongSpaceA = false + tok.spacing.excl(tsLeading) when defined(nimpretty): if L.buf[pos] == '#' and tok.line < 0: commentIndent = indent if L.buf[pos] > ' ' and (L.buf[pos] != '#' or L.buf[pos+1] == '#'): diff --git a/compiler/parser.nim b/compiler/parser.nim index 26a442e238..babbb87fd4 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -301,14 +301,13 @@ proc isRightAssociative(tok: Token): bool {.inline.} = proc isUnary(tok: Token): bool = ## Check if the given token is a unary operator tok.tokType in {tkOpr, tkDotDot} and - tok.strongSpaceB == tsNone and - tok.strongSpaceA + tok.spacing == {tsLeading} proc checkBinary(p: Parser) {.inline.} = ## Check if the current parser token is a binary operator. # we don't check '..' here as that's too annoying if p.tok.tokType == tkOpr: - if p.tok.strongSpaceB == tsTrailing and not p.tok.strongSpaceA: + if p.tok.spacing == {tsTrailing}: parMessage(p, warnInconsistentSpacing, prettyTok(p.tok)) #| module = stmt ^* (';' / IND{=}) @@ -516,7 +515,7 @@ proc dotExpr(p: var Parser, a: PNode): PNode = optInd(p, result) result.add(a) result.add(parseSymbol(p, smAfterDot)) - if p.tok.tokType == tkBracketLeColon and not p.tok.strongSpaceA: + if p.tok.tokType == tkBracketLeColon and tsLeading notin p.tok.spacing: var x = newNodeI(nkBracketExpr, p.parLineInfo) # rewrite 'x.y[:z]()' to 'y[z](x)' x.add result[1] @@ -525,7 +524,7 @@ proc dotExpr(p: var Parser, a: PNode): PNode = var y = newNodeI(nkCall, p.parLineInfo) y.add x y.add result[0] - if p.tok.tokType == tkParLe and not p.tok.strongSpaceA: + if p.tok.tokType == tkParLe and tsLeading notin p.tok.spacing: exprColonEqExprListAux(p, tkParRi, y) result = y @@ -883,7 +882,7 @@ proc primarySuffix(p: var Parser, r: PNode, case p.tok.tokType of tkParLe: # progress guaranteed - if p.tok.strongSpaceA: + if tsLeading in p.tok.spacing: result = commandExpr(p, result, mode) break result = namedParams(p, result, nkCall, tkParRi) @@ -895,13 +894,13 @@ proc primarySuffix(p: var Parser, r: PNode, result = parseGStrLit(p, result) of tkBracketLe: # progress guaranteed - if p.tok.strongSpaceA: + if tsLeading in p.tok.spacing: result = commandExpr(p, result, mode) break result = namedParams(p, result, nkBracketExpr, tkBracketRi) of tkCurlyLe: # progress guaranteed - if p.tok.strongSpaceA: + if tsLeading in p.tok.spacing: result = commandExpr(p, result, mode) break result = namedParams(p, result, nkCurlyExpr, tkCurlyRi) @@ -2525,7 +2524,7 @@ proc parseAll(p: var Parser): PNode = setEndInfo() proc checkFirstLineIndentation*(p: var Parser) = - if p.tok.indent != 0 and p.tok.strongSpaceA: + if p.tok.indent != 0 and tsLeading in p.tok.spacing: parMessage(p, errInvalidIndentation) proc parseTopLevelStmt(p: var Parser): PNode = diff --git a/nimpretty/tests/exhaustive.nim b/nimpretty/tests/exhaustive.nim index 53ff0ea4d5..bcf8256656 100644 --- a/nimpretty/tests/exhaustive.nim +++ b/nimpretty/tests/exhaustive.nim @@ -267,7 +267,7 @@ proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) = if not em.endsInWhite: wr(" ") wr(tok.ident.s) template isUnary(tok): bool = - tok.strongSpaceB == tsNone and tok.strongSpaceA + tok.spacing == {tsLeading} if not isUnary(tok) or em.lastTok in {tkOpr, tkDotDot}: wr(" ") diff --git a/nimpretty/tests/expected/exhaustive.nim b/nimpretty/tests/expected/exhaustive.nim index 266bcae06b..50ae92a62b 100644 --- a/nimpretty/tests/expected/exhaustive.nim +++ b/nimpretty/tests/expected/exhaustive.nim @@ -272,7 +272,7 @@ proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) = if not em.endsInWhite: wr(" ") wr(tok.ident.s) template isUnary(tok): bool = - tok.strongSpaceB == tsNone and tok.strongSpaceA + tok.spacing == {tsLeading} if not isUnary(tok) or em.lastTok in {tkOpr, tkDotDot}: wr(" ") From b562e1e6d85d5c64eec1d714257e2f728e60f12f Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Sun, 7 May 2023 03:36:57 +0800 Subject: [PATCH 07/15] implement `=dup` hook eliminating `wasMoved` and `=copy` pairs (#21586) * import `=dup` hook eliminating `wasMoved` and `=copy` pairs * add dup * add a test for dup * fixes documentation * fixes signature * resolve comments * fixes tests * fixes tests * clean up --- compiler/ast.nim | 7 ++-- compiler/ccgexprs.nim | 10 +++++ compiler/condsyms.nim | 3 +- compiler/injectdestructors.nim | 27 ++++++++++--- compiler/jsgen.nim | 9 +++++ compiler/liftdestructors.nim | 26 +++++++++++-- compiler/semstmts.nim | 16 ++++++-- compiler/vmgen.nim | 6 +++ lib/system.nim | 6 +++ lib/system/arc.nim | 4 ++ tests/arc/tdup.nim | 70 ++++++++++++++++++++++++++++++++++ tests/arc/topt_no_cursor.nim | 3 +- 12 files changed, 170 insertions(+), 17 deletions(-) create mode 100644 tests/arc/tdup.nim diff --git a/compiler/ast.nim b/compiler/ast.nim index bf942f7849..815cb00dc4 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -687,7 +687,7 @@ type mIsPartOf, mAstToStr, mParallel, mSwap, mIsNil, mArrToSeq, mOpenArrayToSeq, mNewString, mNewStringOfCap, mParseBiggestFloat, - mMove, mWasMoved, mDestroy, mTrace, + mMove, mWasMoved, mDup, mDestroy, mTrace, mDefault, mUnown, mFinished, mIsolate, mAccessEnv, mAccessTypeField, mReset, mArray, mOpenArray, mRange, mSet, mSeq, mVarargs, mRef, mPtr, mVar, mDistinct, mVoid, mTuple, @@ -944,7 +944,8 @@ type attachedAsgn, attachedSink, attachedTrace, - attachedDeepCopy + attachedDeepCopy, + attachedDup TType* {.acyclic.} = object of TIdObj # \ # types are identical iff they have the @@ -1515,7 +1516,7 @@ proc newProcNode*(kind: TNodeKind, info: TLineInfo, body: PNode, const AttachedOpToStr*: array[TTypeAttachedOp, string] = [ - "=wasMoved", "=destroy", "=copy", "=sink", "=trace", "=deepcopy"] + "=wasMoved", "=destroy", "=copy", "=sink", "=trace", "=deepcopy", "=dup"] proc `$`*(s: PSym): string = if s != nil: diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 265ccb92c7..e351e95b02 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -2346,6 +2346,11 @@ proc genMove(p: BProc; n: PNode; d: var TLoc) = genAssignment(p, d, a, {}) resetLoc(p, a) +proc genDup(p: BProc; src: TLoc; d: var TLoc; n: PNode) = + if d.k == locNone: getTemp(p, n.typ, d) + linefmt(p, cpsStmts, "#nimDupRef((void**)$1, (void*)$2);$n", + [addrLoc(p.config, d), rdLoc(src)]) + proc genDestroy(p: BProc; n: PNode) = if optSeqDestructors in p.config.globalOptions: let arg = n[1].skipAddr @@ -2597,6 +2602,11 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = of mAccessTypeField: genAccessTypeField(p, e, d) of mSlice: genSlice(p, e, d) of mTrace: discard "no code to generate" + of mDup: + var a: TLoc + let x = if e[1].kind in {nkAddr, nkHiddenAddr}: e[1][0] else: e[1] + initLocExpr(p, x, a) + genDup(p, a, d, e) else: when defined(debugMagics): echo p.prc.name.s, " ", p.prc.id, " ", p.prc.flags, " ", p.prc.ast[genericParamsPos].kind diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index fa7f56504d..8e0e2f3004 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -154,5 +154,6 @@ proc initDefines*(symbols: StringTableRef) = defineSymbol("nimHasGenericDefine") defineSymbol("nimHasDefineAliases") defineSymbol("nimHasWarnBareExcept") - + defineSymbol("nimHasDup") defineSymbol("nimHasChecksums") + diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index ccb19720df..d9a2da5a08 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -425,11 +425,28 @@ proc passCopyToSink(n: PNode; c: var Con; s: var Scope): PNode = result = newNodeIT(nkStmtListExpr, n.info, n.typ) let tmp = c.getTemp(s, n.typ, n.info) if hasDestructor(c, n.typ): - result.add c.genWasMoved(tmp) - var m = c.genCopy(tmp, n, {}) - m.add p(n, c, s, normal) - c.finishCopy(m, n, isFromSink = true) - result.add m + let typ = n.typ.skipTypes({tyGenericInst, tyAlias, tySink}) + let op = getAttachedOp(c.graph, typ, attachedDup) + if op != nil: + let src = p(n, c, s, normal) + result.add newTreeI(nkFastAsgn, + src.info, tmp, + genOp(c, op, src) + ) + elif typ.kind == tyRef: + let src = p(n, c, s, normal) + result.add newTreeI(nkFastAsgn, + src.info, tmp, + newTreeIT(nkCall, src.info, src.typ, + newSymNode(createMagic(c.graph, c.idgen, "`=dup`", mDup)), + src) + ) + else: + result.add c.genWasMoved(tmp) + var m = c.genCopy(tmp, n, {}) + m.add p(n, c, s, normal) + c.finishCopy(m, n, isFromSink = true) + result.add m if isLValue(n) and not isCapturedVar(n) and n.typ.skipTypes(abstractInst).kind != tyRef and c.inSpawn == 0: message(c.graph.config, n.info, hintPerformance, ("passing '$1' to a sink parameter introduces an implicit copy; " & diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 6195559699..45b0baec0c 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -2174,6 +2174,13 @@ proc genMove(p: PProc; n: PNode; r: var TCompRes) = genReset(p, n) #lineF(p, "$1 = $2;$n", [dest.rdLoc, src.rdLoc]) +proc genDup(p: PProc; n: PNode; r: var TCompRes) = + var a: TCompRes + r.kind = resVal + r.res = p.getTemp() + gen(p, n[1], a) + lineF(p, "$1 = $2;$n", [r.rdLoc, a.rdLoc]) + proc genJSArrayConstr(p: PProc, n: PNode, r: var TCompRes) = var a: TCompRes r.res = rope("[") @@ -2368,6 +2375,8 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) = r.kind = resExpr of mMove: genMove(p, n, r) + of mDup: + genDup(p, n, r) else: genCall(p, n, r) #else internalError(p.config, e.info, 'genMagic: ' + magicToStr[op]); diff --git a/compiler/liftdestructors.nim b/compiler/liftdestructors.nim index 3fd3c5f378..3a997af82b 100644 --- a/compiler/liftdestructors.nim +++ b/compiler/liftdestructors.nim @@ -463,6 +463,9 @@ proc considerUserDefinedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool = body.add genWasMovedCall(c, op, x) result = true + of attachedDup: + assert false, "cannot happen" + proc declareCounter(c: var TLiftCtx; body: PNode; first: BiggestInt): PNode = var temp = newSym(skTemp, getIdent(c.g.cache, lowerings.genPrefix), c.idgen, c.fn, c.info) temp.typ = getSysType(c.g, body.info, tyInt) @@ -546,6 +549,8 @@ proc fillSeqOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = # follow all elements: forallElements(c, t, body, x, y) of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x) + of attachedDup: + assert false, "cannot happen" proc useSeqOrStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = createTypeBoundOps(c.g, c.c, t, body.info, c.idgen) @@ -584,6 +589,8 @@ proc useSeqOrStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = return # protect from recursion body.add newHookCall(c, op, x, y) of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x) + of attachedDup: + assert false, "cannot happen" proc fillStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = case c.kind @@ -600,6 +607,8 @@ proc fillStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = of attachedTrace: discard "strings are atomic and have no inner elements that are to trace" of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x) + of attachedDup: + assert false, "cannot happen" proc cyclicType*(t: PType): bool = case t.kind @@ -699,7 +708,8 @@ proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = body.add callCodegenProc(c.g, "nimTraceRefDyn", c.info, genAddrOf(x, c.idgen), y) #echo "can follow ", elemType, " static ", isFinal(elemType) of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x) - + of attachedDup: + assert false, "cannot happen" proc atomicClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = ## Closures are really like refs except they always use a virtual destructor @@ -749,6 +759,8 @@ proc atomicClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = of attachedTrace: body.add callCodegenProc(c.g, "nimTraceRefDyn", c.info, genAddrOf(xenv, c.idgen), y) of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x) + of attachedDup: + assert false, "cannot happen" proc weakrefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = case c.kind @@ -774,6 +786,8 @@ proc weakrefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = of attachedDeepCopy: assert(false, "cannot happen") of attachedTrace: discard of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x) + of attachedDup: + assert false, "cannot happen" proc ownedRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = var actions = newNodeI(nkStmtList, c.info) @@ -800,6 +814,8 @@ proc ownedRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = of attachedDeepCopy: assert(false, "cannot happen") of attachedTrace: discard of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x) + of attachedDup: + assert false, "cannot happen" proc closureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = if c.kind == attachedDeepCopy: @@ -835,6 +851,8 @@ proc closureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = of attachedDeepCopy: assert(false, "cannot happen") of attachedTrace: discard of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x) + of attachedDup: + assert false, "cannot happen" proc ownedClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = let xx = genBuiltin(c, mAccessEnv, "accessEnv", x) @@ -851,6 +869,8 @@ proc ownedClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = of attachedDeepCopy: assert(false, "cannot happen") of attachedTrace: discard of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x) + of attachedDup: + assert false, "cannot happen" proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) = case t.kind @@ -966,7 +986,7 @@ proc symPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttachedOp result.typ = newProcType(info, nextTypeId(idgen), owner) result.typ.addParam dest - if kind notin {attachedDestructor, attachedWasMoved}: + if kind notin {attachedDestructor, attachedWasMoved, attachedDup}: result.typ.addParam src if kind == attachedAsgn and g.config.selectedGC == gcOrc and @@ -1006,7 +1026,7 @@ proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp; let dest = result.typ.n[1].sym let d = newDeref(newSymNode(dest)) - let src = if kind in {attachedDestructor, attachedWasMoved}: newNodeIT(nkSym, info, getSysType(g, info, tyPointer)) + let src = if kind in {attachedDestructor, attachedWasMoved, attachedDup}: newNodeIT(nkSym, info, getSysType(g, info, tyPointer)) else: newSymNode(result.typ.n[2].sym) # register this operation already: diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index e6fade5285..126d1aa654 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -1815,9 +1815,12 @@ proc whereToBindTypeHook(c: PContext; t: PType): PType = proc bindTypeHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp) = let t = s.typ var noError = false - let cond = if op in {attachedDestructor, attachedWasMoved}: + let cond = case op + of {attachedDestructor, attachedWasMoved}: t.len == 2 and t[0] == nil and t[1].kind == tyVar - elif op == attachedTrace: + of attachedDup: + t.len == 2 and t[0] != nil and t[1].kind == tyVar + of attachedTrace: t.len == 3 and t[0] == nil and t[1].kind == tyVar and t[2].kind == tyPointer else: t.len >= 2 and t[0] == nil @@ -1843,9 +1846,13 @@ proc bindTypeHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp) = localError(c.config, n.info, errGenerated, "type bound operation `" & s.name.s & "` can be defined only in the same module with its type (" & obj.typeToString() & ")") if not noError and sfSystemModule notin s.owner.flags: - if op == attachedTrace: + case op + of attachedTrace: localError(c.config, n.info, errGenerated, "signature for '=trace' must be proc[T: object](x: var T; env: pointer)") + of attachedDup: + localError(c.config, n.info, errGenerated, + "signature for '=dup' must be proc[T: object](x: var T): T") else: localError(c.config, n.info, errGenerated, "signature for '" & s.name.s & "' must be proc[T: object](x: var T)") @@ -1938,6 +1945,9 @@ proc semOverride(c: PContext, s: PSym, n: PNode) = of "=wasmoved": if s.magic != mWasMoved: bindTypeHook(c, s, n, attachedWasMoved) + of "=dup": + if s.magic != mDup: + bindTypeHook(c, s, n, attachedDup) else: if sfOverriden in s.flags: localError(c.config, n.info, errGenerated, diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index db3276aada..25ff62bcc7 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -1401,6 +1401,12 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = # c.gABx(n, opcNodeToReg, a, a) # c.genAsgnPatch(arg, a) c.freeTemp(a) + of mDup: + let arg = n[1] + let a = c.genx(arg) + if dest < 0: dest = c.getTemp(arg.typ) + gABC(c, arg, whichAsgnOpc(arg, requiresCopy=false), dest, a) + c.freeTemp(a) of mNodeId: c.genUnaryABC(n, dest, opcNodeId) else: diff --git a/lib/system.nim b/lib/system.nim index f232423152..8bb50ba5d4 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -347,6 +347,12 @@ proc arrPut[I: Ordinal;T,S](a: T; i: I; proc `=destroy`*[T](x: var T) {.inline, magic: "Destroy".} = ## Generic `destructor`:idx: implementation that can be overridden. discard + +when defined(nimHasDup): + proc `=dup`*[T](x: ref T): ref T {.inline, magic: "Dup".} = + ## Generic `dup` implementation that can be overridden. + discard + proc `=sink`*[T](x: var T; y: T) {.inline, nodestroy, magic: "Asgn".} = ## Generic `sink`:idx: implementation that can be overridden. when defined(gcArc) or defined(gcOrc): diff --git a/lib/system/arc.nim b/lib/system/arc.nim index d8527e1e45..55c4c412af 100644 --- a/lib/system/arc.nim +++ b/lib/system/arc.nim @@ -185,6 +185,10 @@ proc nimDecRefIsLast(p: pointer): bool {.compilerRtl, inl.} = when traceCollector: cprintf("[DeCREF] %p\n", cell) +proc nimDupRef(dest: ptr pointer, src: pointer) {.compilerRtl, inl.} = + dest[] = src + if src != nil: nimIncRef src + proc GC_unref*[T](x: ref T) = ## New runtime only supports this operation for 'ref T'. var y {.cursor.} = x diff --git a/tests/arc/tdup.nim b/tests/arc/tdup.nim new file mode 100644 index 0000000000..3f64061fbe --- /dev/null +++ b/tests/arc/tdup.nim @@ -0,0 +1,70 @@ +discard """ + cmd: "nim c --mm:arc --expandArc:foo --hints:off $file" + nimout: ''' +--expandArc: foo + +var + x + :tmpD + s + :tmpD_1 +x = Ref(id: 8) +inc: + :tmpD = `=dup`(x) + :tmpD +inc: + let blitTmp = x + blitTmp +var id_1 = 777 +s = RefCustom(id_2: addr(id_1)) +inc_1 : + :tmpD_1 = `=dup`(s) + :tmpD_1 +inc_1 : + let blitTmp_1 = s + blitTmp_1 +-- end of expandArc ------------------------ +''' +""" + +type + Ref = ref object + id: int + + RefCustom = object + id: ptr int + +proc inc(x: sink Ref) = + inc x.id + +proc inc(x: sink RefCustom) = + inc x.id[] + +proc `=dup`(x: var RefCustom): RefCustom = + result.id = x.id + +proc foo = + var x = Ref(id: 8) + inc(x) + inc(x) + var id = 777 + var s = RefCustom(id: addr id) + inc s + inc s + +foo() + +proc foo2 = + var x = Ref(id: 8) + inc(x) + doAssert x.id == 9 + inc(x) + doAssert x.id == 10 + var id = 777 + var s = RefCustom(id: addr id) + inc s + doAssert s.id[] == 778 + inc s + doAssert s.id[] == 779 + +foo2() diff --git a/tests/arc/topt_no_cursor.nim b/tests/arc/topt_no_cursor.nim index 26dc254475..32652b60a5 100644 --- a/tests/arc/topt_no_cursor.nim +++ b/tests/arc/topt_no_cursor.nim @@ -113,8 +113,7 @@ block :tmp: var :tmpD sym = shadowScope.symbols[i] addInterfaceDecl(c): - `=wasMoved`(:tmpD) - `=copy_1`(:tmpD, sym) + :tmpD = `=dup`(sym) :tmpD inc(i, 1) `=destroy`(shadowScope) From 8cf5643621600aaa869935721227fc3b7ee5f881 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Sun, 7 May 2023 03:38:17 +0800 Subject: [PATCH 08/15] fixes #21280; Enum with int64.high() value crashes compiler (#21285) * fixes #21280; Enum with int64.high() value crashes compiler * Update tests/enum/tenum.nim * Update tests/enum/tenum.nim * fixes tests * Update tests/enum/tenum.nim --------- Co-authored-by: Andreas Rumpf --- compiler/semtypes.nim | 7 ++++++- tests/enum/tenum.nim | 8 ++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index e54de80a82..38d040946f 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -16,6 +16,7 @@ const errIntLiteralExpected = "integer literal expected" errWrongNumberOfVariables = "wrong number of variables" errInvalidOrderInEnumX = "invalid order in enum '$1'" + errOverflowInEnumX = "The enum '$1' exceeds its maximum value ($2)" errOrdinalTypeExpected = "ordinal type expected; given: $1" errSetTooBig = "set is too large; use `std/sets` for ordinal types with more than 2^16 elements" errBaseTypeMustBeOrdinal = "base type of a set must be an ordinal" @@ -147,7 +148,11 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType = declarePureEnumField(c, e) if (let conflict = strTableInclReportConflict(symbols, e); conflict != nil): wrongRedefinition(c, e.info, e.name.s, conflict.info) - inc(counter) + if counter == high(typeof(counter)): + if i > 1 and result.n[i-2].sym.position == high(int): + localError(c.config, n[i].info, errOverflowInEnumX % [e.name.s, $high(typeof(counter))]) + else: + inc(counter) if isPure and sfExported in result.sym.flags: addPureEnum(c, LazySym(sym: result.sym)) if tfNotNil in e.typ.flags and not hasNull: diff --git a/tests/enum/tenum.nim b/tests/enum/tenum.nim index 88d85ddcc4..8046c65890 100644 --- a/tests/enum/tenum.nim +++ b/tests/enum/tenum.nim @@ -176,3 +176,11 @@ block: # bug #12589 when not defined(gcRefc): doAssert $typ() == "wkbPoint25D" + + block: # bug #21280 + type + Test = enum + B = 19 + A = int64.high() + + doAssert ord(A) == int64.high() From 4a94f3606e2e3c47cf416755c4b3d2cd7eddef9c Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Sun, 7 May 2023 15:22:42 +0800 Subject: [PATCH 09/15] revert #21799 and #21802 which don't pass the tests (#21804) * Revert "ORC: make rootsThreshold thread local [backport] (#21799)" This reverts commit b74d49c037734079765770426d0f5c79dee6cf87. * Revert "fixes #21752 [backport] (#21802)" This reverts commit d0c62fa169f3970653ce0d5bbd16e123efb24251. --- compiler/types.nim | 9 +++------ lib/system/orc.nim | 10 +++++----- tests/arc/tasyncleak.nim | 2 +- tests/arc/topt_no_cursor.nim | 2 +- 4 files changed, 10 insertions(+), 13 deletions(-) diff --git a/compiler/types.nim b/compiler/types.nim index 2c2dec639c..d2517127a9 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -396,9 +396,9 @@ proc canFormAcycleAux(marker: var IntSet, typ: PType, startId: int): bool = result = true elif not containsOrIncl(marker, t.id): for i in 0..= j.touched: when not defined(nimFixedOrc): rootsThreshold = max(rootsThreshold div 3 * 2, 16) else: - rootsThreshold = 0 + rootsThreshold = defaultThreshold #cfprintf(cstderr, "[collectCycles] freed %ld, touched %ld new threshold %ld\n", j.freed, j.touched, rootsThreshold) elif rootsThreshold < high(int) div 4: rootsThreshold = rootsThreshold * 3 div 2 @@ -411,7 +411,7 @@ proc registerCycle(s: Cell; desc: PNimTypeV2) = if roots.d == nil: init(roots) add(roots, s, desc) - if roots.len >= rootsThreshold+defaultThreshold: + if roots.len >= rootsThreshold: collectCycles() when logOrc: writeCell("[added root]", s, desc) @@ -427,7 +427,7 @@ proc GC_enableOrc*() = ## Enables the cycle collector subsystem of `--gc:orc`. This is a `--gc:orc` ## specific API. Check with `when defined(gcOrc)` for its existence. when not defined(nimStressOrc): - rootsThreshold = 0 + rootsThreshold = defaultThreshold proc GC_disableOrc*() = ## Disables the cycle collector subsystem of `--gc:orc`. This is a `--gc:orc` diff --git a/tests/arc/tasyncleak.nim b/tests/arc/tasyncleak.nim index 8e3a7b3e7b..eb0c452131 100644 --- a/tests/arc/tasyncleak.nim +++ b/tests/arc/tasyncleak.nim @@ -1,5 +1,5 @@ discard """ - outputsub: "(allocCount: 4050, deallocCount: 4048)" + outputsub: "(allocCount: 4302, deallocCount: 4300)" cmd: "nim c --gc:orc -d:nimAllocStats $file" """ diff --git a/tests/arc/topt_no_cursor.nim b/tests/arc/topt_no_cursor.nim index 32652b60a5..ddcc549d1f 100644 --- a/tests/arc/topt_no_cursor.nim +++ b/tests/arc/topt_no_cursor.nim @@ -1,6 +1,6 @@ discard """ nimoutFull: true - cmd: '''nim c -r --warnings:off --hints:off --mm:arc --expandArc:newTarget --expandArc:delete --expandArc:p1 --expandArc:tt --hint:Performance:off --assertions:off --expandArc:extractConfig --expandArc:mergeShadowScope --expandArc:check $file''' + cmd: '''nim c -r --warnings:off --hints:off --gc:arc --expandArc:newTarget --expandArc:delete --expandArc:p1 --expandArc:tt --hint:Performance:off --assertions:off --expandArc:extractConfig --expandArc:mergeShadowScope --expandArc:check $file''' nimout: ''' --expandArc: newTarget From 71f2e1a502ad231e3356217398e2d7fcd6137967 Mon Sep 17 00:00:00 2001 From: Jordan Gillard Date: Sun, 7 May 2023 03:25:25 -0400 Subject: [PATCH 10/15] =?UTF-8?q?=F0=9F=9A=80=20Enhancing=20CellSeq=20for?= =?UTF-8?q?=20Better=20Readability=20and=20Maintainability=20(#21797)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactor and improve readability of CellSeq in system directory * Use half-open range in the contains procedure for better readability and to avoid potential off-by-one errors * Extract resizing logic from add procedure into a separate resize procedure for better code readability and separation of concerns --- lib/system/cellseqs_v1.nim | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/lib/system/cellseqs_v1.nim b/lib/system/cellseqs_v1.nim index 1952491b37..1a305aa428 100644 --- a/lib/system/cellseqs_v1.nim +++ b/lib/system/cellseqs_v1.nim @@ -16,18 +16,21 @@ type d: PCellArray proc contains(s: CellSeq, c: PCell): bool {.inline.} = - for i in 0 .. s.len-1: - if s.d[i] == c: return true + for i in 0 ..< s.len: + if s.d[i] == c: + return true return false +proc resize(s: var CellSeq) = + s.cap = s.cap * 3 div 2 + let d = cast[PCellArray](alloc(s.cap * sizeof(PCell))) + copyMem(d, s.d, s.len * sizeof(PCell)) + dealloc(s.d) + s.d = d + proc add(s: var CellSeq, c: PCell) {.inline.} = if s.len >= s.cap: - s.cap = s.cap * 3 div 2 - var d = cast[PCellArray](alloc(s.cap * sizeof(PCell))) - copyMem(d, s.d, s.len * sizeof(PCell)) - dealloc(s.d) - s.d = d - # XXX: realloc? + resize(s) s.d[s.len] = c inc(s.len) From ebdff1c7d36683c13b7b692e7d2f16aa3b13027f Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Mon, 8 May 2023 19:52:28 +0800 Subject: [PATCH 11/15] fixes #21801; object field initialization with overloaded functions (#21805) * fixes #21801; object field initialization with overloaded functions * use the correct type --- compiler/semtypes.nim | 2 +- tests/objects/tobject_default_value.nim | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 38d040946f..750ab2216b 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -227,8 +227,8 @@ proc isRecursiveType(t: PType, cycleDetector: var IntSet): bool = proc fitDefaultNode(c: PContext, n: PNode): PType = let expectedType = if n[^2].kind != nkEmpty: semTypeNode(c, n[^2], nil) else: nil - let oldType = n[^1].typ n[^1] = semConstExpr(c, n[^1], expectedType = expectedType) + let oldType = n[^1].typ n[^1].flags.incl nfSem if n[^2].kind != nkEmpty: if expectedType != nil and oldType != expectedType: diff --git a/tests/objects/tobject_default_value.nim b/tests/objects/tobject_default_value.nim index 59af943e0c..97e3a207d7 100644 --- a/tests/objects/tobject_default_value.nim +++ b/tests/objects/tobject_default_value.nim @@ -591,6 +591,29 @@ template main {.dirty.} = mainSync() + block: # bug #21801 + func evaluate(i: int): float = + 0.0 + + func evaluate(): float = + 0.0 + + type SearchOptions = object + evaluation: proc(): float = evaluate + + block: + func evaluate(): float = + 0.0 + + type SearchOptions = object + evaluation: proc(): float = evaluate + + block: + func evaluate(i: int): float = + 0.0 + + type SearchOptions = object + evaluation = evaluate static: main() main() From 4533e894ad0e113c6057d336290d2c903383e406 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Mon, 8 May 2023 22:25:47 +0800 Subject: [PATCH 12/15] adds an experimental `mm:atomicArc` switch (#21798) --- compiler/btrees.nim | 4 ++-- compiler/ccgcalls.nim | 2 +- compiler/ccgexprs.nim | 6 +++--- compiler/cgen.nim | 8 ++++---- compiler/commands.nim | 24 +++++++++++++----------- compiler/dfa.nim | 2 +- compiler/injectdestructors.nim | 8 ++++---- compiler/liftdestructors.nim | 10 +++++----- compiler/msgs.nim | 4 ++-- compiler/nimfix/prettybase.nim | 4 ++-- compiler/options.nim | 1 + compiler/pragmas.nim | 2 +- compiler/scriptconfig.nim | 2 +- compiler/semexprs.nim | 2 +- compiler/sempass2.nim | 2 +- compiler/semtypes.nim | 2 +- compiler/spawn.nim | 4 ++-- compiler/vm.nim | 4 ++-- lib/system/arc.nim | 33 ++++++++++++++++++++++++--------- 19 files changed, 71 insertions(+), 53 deletions(-) diff --git a/compiler/btrees.nim b/compiler/btrees.nim index c79442249d..92f07f6b09 100644 --- a/compiler/btrees.nim +++ b/compiler/btrees.nim @@ -68,7 +68,7 @@ proc copyHalf[Key, Val](h, result: Node[Key, Val]) = result.links[j] = h.links[Mhalf + j] else: for j in 0..") let initStackBottomCall = - if m.config.target.targetOS == osStandalone or m.config.selectedGC in {gcNone, gcArc, gcOrc}: "".rope + if m.config.target.targetOS == osStandalone or m.config.selectedGC in {gcNone, gcArc, gcAtomicArc, gcOrc}: "".rope else: ropecg(m, "\t#initStackBottomWith((void *)&inner);$N", []) inc(m.labels) - let isVolatile = if m.config.selectedGC notin {gcNone, gcArc, gcOrc}: "1" else: "0" + let isVolatile = if m.config.selectedGC notin {gcNone, gcArc, gcAtomicArc, gcOrc}: "1" else: "0" appcg(m, m.s[cfsProcs], PreMainBody, [m.g.mainDatInit, m.g.otherModsInit, m.config.nimMainPrefix, posixCmdLine, isVolatile]) if m.config.target.targetOS == osWindows and @@ -1725,7 +1725,7 @@ proc registerModuleToMain(g: BModuleList; m: BModule) = if sfSystemModule in m.module.flags: if emulatedThreadVars(m.config) and m.config.target.targetOS != osStandalone: g.mainDatInit.add(ropecg(m, "\t#initThreadVarsEmulation();$N", [])) - if m.config.target.targetOS != osStandalone and m.config.selectedGC notin {gcNone, gcArc, gcOrc}: + if m.config.target.targetOS != osStandalone and m.config.selectedGC notin {gcNone, gcArc, gcAtomicArc, gcOrc}: g.mainDatInit.add(ropecg(m, "\t#initStackBottomWith((void *)&inner);$N", [])) if m.s[cfsInitProc].len > 0: @@ -2177,7 +2177,7 @@ proc finalCodegenActions*(graph: ModuleGraph; m: BModule; n: PNode): PNode = cgsym(m, "rawWrite") # raise dependencies on behalf of genMainProc - if m.config.target.targetOS != osStandalone and m.config.selectedGC notin {gcNone, gcArc, gcOrc}: + if m.config.target.targetOS != osStandalone and m.config.selectedGC notin {gcNone, gcArc, gcAtomicArc, gcOrc}: cgsym(m, "initStackBottomWith") if emulatedThreadVars(m.config) and m.config.target.targetOS != osStandalone: cgsym(m, "initThreadVarsEmulation") diff --git a/compiler/commands.nim b/compiler/commands.nim index c31476b45f..93a36e714b 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -238,7 +238,7 @@ proc processCompile(conf: ConfigRef; filename: string) = extccomp.addExternalFileToCompile(conf, found) const - errNoneBoehmRefcExpectedButXFound = "'arc', 'orc', 'markAndSweep', 'boehm', 'go', 'none', 'regions', or 'refc' expected, but '$1' found" + errNoneBoehmRefcExpectedButXFound = "'arc', 'orc', 'atomicArc', 'markAndSweep', 'boehm', 'go', 'none', 'regions', or 'refc' expected, but '$1' found" errNoneSpeedOrSizeExpectedButXFound = "'none', 'speed' or 'size' expected, but '$1' found" errGuiConsoleOrLibExpectedButXFound = "'gui', 'console' or 'lib' expected, but '$1' found" errInvalidExceptionSystem = "'goto', 'setjmp', 'cpp' or 'quirky' expected, but '$1' found" @@ -262,6 +262,7 @@ proc testCompileOptionArg*(conf: ConfigRef; switch, arg: string, info: TLineInfo of "go": result = conf.selectedGC == gcGo of "none": result = conf.selectedGC == gcNone of "stack", "regions": result = conf.selectedGC == gcRegions + of "atomicarc": result = conf.selectedGC == gcAtomicArc else: localError(conf, info, errNoneBoehmRefcExpectedButXFound % arg) of "opt": case arg.normalize @@ -516,14 +517,7 @@ proc initOrcDefines*(conf: ConfigRef) = if conf.exc == excNone and conf.backend != backendCpp: conf.exc = excGoto -proc registerArcOrc(pass: TCmdLinePass, conf: ConfigRef, isOrc: bool) = - if isOrc: - conf.selectedGC = gcOrc - defineSymbol(conf.symbols, "gcorc") - else: - conf.selectedGC = gcArc - defineSymbol(conf.symbols, "gcarc") - +proc registerArcOrc(pass: TCmdLinePass, conf: ConfigRef) = defineSymbol(conf.symbols, "gcdestructors") incl conf.globalOptions, optSeqDestructors incl conf.globalOptions, optTinyRtti @@ -562,9 +556,17 @@ proc processMemoryManagementOption(switch, arg: string, pass: TCmdLinePass, conf.selectedGC = gcMarkAndSweep defineSymbol(conf.symbols, "gcmarkandsweep") of "destructors", "arc": - registerArcOrc(pass, conf, false) + conf.selectedGC = gcArc + defineSymbol(conf.symbols, "gcarc") + registerArcOrc(pass, conf) of "orc": - registerArcOrc(pass, conf, true) + conf.selectedGC = gcOrc + defineSymbol(conf.symbols, "gcorc") + registerArcOrc(pass, conf) + of "atomicarc": + conf.selectedGC = gcAtomicArc + defineSymbol(conf.symbols, "gcatomicarc") + registerArcOrc(pass, conf) of "hooks": conf.selectedGC = gcHooks defineSymbol(conf.symbols, "gchooks") diff --git a/compiler/dfa.nim b/compiler/dfa.nim index 61a921ba0e..d145e31c33 100644 --- a/compiler/dfa.nim +++ b/compiler/dfa.nim @@ -493,7 +493,7 @@ proc constructCfg*(s: PSym; body: PNode; root: PSym): ControlFlowGraph = gen(c, body) if root.kind == skResult: genImplicitReturn(c) - when defined(gcArc) or defined(gcOrc): + when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc): result = c.code # will move else: shallowCopy(result, c.code) diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index d9a2da5a08..ab0fa02d91 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -66,7 +66,7 @@ proc hasDestructor(c: Con; t: PType): bool {.inline.} = result = ast.hasDestructor(t) when toDebug.len > 0: # for more effective debugging - if not result and c.graph.config.selectedGC in {gcArc, gcOrc}: + if not result and c.graph.config.selectedGC in {gcArc, gcOrc, gcAtomicArc}: assert(not containsGarbageCollectedRef(t)) proc getTemp(c: var Con; s: var Scope; typ: PType; info: TLineInfo): PNode = @@ -452,7 +452,7 @@ proc passCopyToSink(n: PNode; c: var Con; s: var Scope): PNode = ("passing '$1' to a sink parameter introduces an implicit copy; " & "if possible, rearrange your program's control flow to prevent it") % $n) else: - if c.graph.config.selectedGC in {gcArc, gcOrc}: + if c.graph.config.selectedGC in {gcArc, gcOrc, gcAtomicArc}: assert(not containsManagedMemory(n.typ)) if n.typ.skipTypes(abstractInst).kind in {tyOpenArray, tyVarargs}: localError(c.graph.config, n.info, "cannot create an implicit openArray copy to be passed to a sink parameter") @@ -495,7 +495,7 @@ proc ensureDestruction(arg, orig: PNode; c: var Con; s: var Scope): PNode = result = arg proc cycleCheck(n: PNode; c: var Con) = - if c.graph.config.selectedGC != gcArc: return + if c.graph.config.selectedGC notin {gcArc, gcAtomicArc}: return var value = n[1] if value.kind == nkClosure: value = value[1] @@ -838,7 +838,7 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSing if n[0].kind == nkSym and n[0].sym.magic in {mNew, mNewFinalize}: result[0] = copyTree(n[0]) - if c.graph.config.selectedGC in {gcHooks, gcArc, gcOrc}: + if c.graph.config.selectedGC in {gcHooks, gcArc, gcAtomicArc, gcOrc}: let destroyOld = c.genDestroy(result[1]) result = newTree(nkStmtList, destroyOld, result) else: diff --git a/compiler/liftdestructors.nim b/compiler/liftdestructors.nim index 3a997af82b..34ab3cc8e1 100644 --- a/compiler/liftdestructors.nim +++ b/compiler/liftdestructors.nim @@ -159,7 +159,7 @@ proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode; enforceDefaultOp: bool) let f = n.sym let b = if c.kind == attachedTrace: y else: y.dotField(f) if (sfCursor in f.flags and f.typ.skipTypes(abstractInst).kind in {tyRef, tyProc} and - c.g.config.selectedGC in {gcArc, gcOrc, gcHooks}) or + c.g.config.selectedGC in {gcArc, gcAtomicArc, gcOrc, gcHooks}) or enforceDefaultOp: defaultOp(c, f.typ, body, x.dotField(f), b) else: @@ -827,7 +827,7 @@ proc closureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = call[1] = y body.add newAsgnStmt(x, call) elif (optOwnedRefs in c.g.config.globalOptions and - optRefCheck in c.g.config.options) or c.g.config.selectedGC in {gcArc, gcOrc}: + optRefCheck in c.g.config.options) or c.g.config.selectedGC in {gcArc, gcAtomicArc, gcOrc}: let xx = genBuiltin(c, mAccessEnv, "accessEnv", x) xx.typ = getSysType(c.g, c.info, tyPointer) case c.kind @@ -879,7 +879,7 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) = tyPtr, tyUncheckedArray, tyVar, tyLent: defaultOp(c, t, body, x, y) of tyRef: - if c.g.config.selectedGC in {gcArc, gcOrc}: + if c.g.config.selectedGC in {gcArc, gcOrc, gcAtomicArc}: atomicRefOp(c, t, body, x, y) elif (optOwnedRefs in c.g.config.globalOptions and optRefCheck in c.g.config.options): @@ -888,7 +888,7 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) = defaultOp(c, t, body, x, y) of tyProc: if t.callConv == ccClosure: - if c.g.config.selectedGC in {gcArc, gcOrc}: + if c.g.config.selectedGC in {gcArc, gcOrc, gcAtomicArc}: atomicClosureOp(c, t, body, x, y) else: closureOp(c, t, body, x, y) @@ -1039,7 +1039,7 @@ proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp; result.ast[bodyPos].add newAsgnStmt(d, src) else: var tk: TTypeKind - if g.config.selectedGC in {gcArc, gcOrc, gcHooks}: + if g.config.selectedGC in {gcArc, gcOrc, gcHooks, gcAtomicArc}: tk = skipTypes(typ, {tyOrdinal, tyRange, tyInferred, tyGenericInst, tyStatic, tyAlias, tySink}).kind else: tk = tyNone # no special casing for strings and seqs diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 8e391f2fb4..05ace315e3 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -226,7 +226,7 @@ proc setDirtyFile*(conf: ConfigRef; fileIdx: FileIndex; filename: AbsoluteFile) proc setHash*(conf: ConfigRef; fileIdx: FileIndex; hash: string) = assert fileIdx.int32 >= 0 - when defined(gcArc) or defined(gcOrc): + when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc): conf.m.fileInfos[fileIdx.int32].hash = hash else: shallowCopy(conf.m.fileInfos[fileIdx.int32].hash, hash) @@ -234,7 +234,7 @@ proc setHash*(conf: ConfigRef; fileIdx: FileIndex; hash: string) = proc getHash*(conf: ConfigRef; fileIdx: FileIndex): string = assert fileIdx.int32 >= 0 - when defined(gcArc) or defined(gcOrc): + when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc): result = conf.m.fileInfos[fileIdx.int32].hash else: shallowCopy(result, conf.m.fileInfos[fileIdx.int32].hash) diff --git a/compiler/nimfix/prettybase.nim b/compiler/nimfix/prettybase.nim index 78c24bae30..b5a7ba42b5 100644 --- a/compiler/nimfix/prettybase.nim +++ b/compiler/nimfix/prettybase.nim @@ -22,7 +22,7 @@ proc replaceDeprecated*(conf: ConfigRef; info: TLineInfo; oldSym, newSym: PIdent let last = first+identLen(line, first)-1 if cmpIgnoreStyle(line[first..last], oldSym.s) == 0: var x = line.substr(0, first-1) & newSym.s & line.substr(last+1) - when defined(gcArc) or defined(gcOrc): + when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc): conf.m.fileInfos[info.fileIndex.int32].lines[info.line.int-1] = move x else: system.shallowCopy(conf.m.fileInfos[info.fileIndex.int32].lines[info.line.int-1], x) @@ -38,7 +38,7 @@ proc replaceComment*(conf: ConfigRef; info: TLineInfo) = if line[first] != '#': inc first var x = line.substr(0, first-1) & "discard " & line.substr(first+1).escape - when defined(gcArc) or defined(gcOrc): + when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc): conf.m.fileInfos[info.fileIndex.int32].lines[info.line.int-1] = move x else: system.shallowCopy(conf.m.fileInfos[info.fileIndex.int32].lines[info.line.int-1], x) diff --git a/compiler/options.nim b/compiler/options.nim index da9c9cbbb9..a9f1e75424 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -184,6 +184,7 @@ type gcRegions = "regions" gcArc = "arc" gcOrc = "orc" + gcAtomicArc = "atomicArc" gcMarkAndSweep = "markAndSweep" gcHooks = "hooks" gcRefc = "refc" diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 10d77a17e9..31414063a5 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -535,7 +535,7 @@ proc processCompile(c: PContext, n: PNode) = n[i] = c.semConstExpr(c, n[i]) case n[i].kind of nkStrLit, nkRStrLit, nkTripleStrLit: - when defined(gcArc) or defined(gcOrc): + when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc): result = n[i].strVal else: shallowCopy(result, n[i].strVal) diff --git a/compiler/scriptconfig.nim b/compiler/scriptconfig.nim index 27ea94aae8..21b0eb1956 100644 --- a/compiler/scriptconfig.nim +++ b/compiler/scriptconfig.nim @@ -227,7 +227,7 @@ proc runNimScript*(cache: IdentCache; scriptName: AbsoluteFile; if optOwnedRefs in oldGlobalOptions: conf.globalOptions.incl {optTinyRtti, optOwnedRefs, optSeqDestructors} defineSymbol(conf.symbols, "nimv2") - if conf.selectedGC in {gcArc, gcOrc}: + if conf.selectedGC in {gcArc, gcOrc, gcAtomicArc}: conf.globalOptions.incl {optTinyRtti, optSeqDestructors} defineSymbol(conf.symbols, "nimv2") diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index a01466868b..4dd7840f14 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -249,7 +249,7 @@ proc isCastable(c: PContext; dst, src: PType, info: TLineInfo): bool = if skipTypes(dst, abstractInst).kind == tyBuiltInTypeClass: return false let conf = c.config - if conf.selectedGC in {gcArc, gcOrc}: + if conf.selectedGC in {gcArc, gcOrc, gcAtomicArc}: let d = skipTypes(dst, abstractInst) let s = skipTypes(src, abstractInst) if d.kind == tyRef and s.kind == tyRef and s[0].isFinal != d[0].isFinal: diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index baa37a45f9..7024c99fe7 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -1480,7 +1480,7 @@ proc trackProc*(c: PContext; s: PSym, body: PNode) = let param = params[i].sym let typ = param.typ if isSinkTypeForParam(typ) or - (t.config.selectedGC in {gcArc, gcOrc} and + (t.config.selectedGC in {gcArc, gcOrc, gcAtomicArc} and (isClosure(typ.skipTypes(abstractInst)) or param.id in t.escapingParams)): createTypeBoundOps(t, typ, param.info) if isOutParam(typ) and param.id notin t.init: diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 750ab2216b..f4b284f7e1 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -968,7 +968,7 @@ proc semAnyRef(c: PContext; n: PNode; kind: TTypeKind; prev: PType): PType = t.rawAddSonNoPropagationOfTypeFlags result result = t else: discard - if result.kind == tyRef and c.config.selectedGC in {gcArc, gcOrc}: + if result.kind == tyRef and c.config.selectedGC in {gcArc, gcOrc, gcAtomicArc}: result.flags.incl tfHasAsgn proc findEnforcedStaticType(t: PType): PType = diff --git a/compiler/spawn.nim b/compiler/spawn.nim index add36759da..7423fdfaa4 100644 --- a/compiler/spawn.nim +++ b/compiler/spawn.nim @@ -37,7 +37,7 @@ proc spawnResult*(t: PType; inParallel: bool): TSpawnResult = else: srFlowVar proc flowVarKind(c: ConfigRef, t: PType): TFlowVarKind = - if c.selectedGC in {gcArc, gcOrc}: fvBlob + if c.selectedGC in {gcArc, gcOrc, gcAtomicArc}: fvBlob elif t.skipTypes(abstractInst).kind in {tyRef, tyString, tySequence}: fvGC elif containsGarbageCollectedRef(t): fvInvalid else: fvBlob @@ -66,7 +66,7 @@ proc addLocalVar(g: ModuleGraph; varSection, varInit: PNode; idgen: IdGenerator; vpart[2] = if varInit.isNil: v else: vpart[1] varSection.add vpart if varInit != nil: - if g.config.selectedGC in {gcArc, gcOrc}: + if g.config.selectedGC in {gcArc, gcOrc, gcAtomicArc}: # inject destructors pass will do its own analysis varInit.add newFastMoveStmt(g, newSymNode(result), v) else: diff --git a/compiler/vm.nim b/compiler/vm.nim index dbb02cffa8..ba3677cf1b 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -121,7 +121,7 @@ template decodeBx(k: untyped) {.dirty.} = ensureKind(k) template move(a, b: untyped) {.dirty.} = - when defined(gcArc) or defined(gcOrc): + when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc): a = move b else: system.shallowCopy(a, b) @@ -550,7 +550,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = # Used to keep track of where the execution is resumed. var savedPC = -1 var savedFrame: PStackFrame - when defined(gcArc) or defined(gcOrc): + when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc): template updateRegsAlias = discard template regs: untyped = tos.slots else: diff --git a/lib/system/arc.nim b/lib/system/arc.nim index 55c4c412af..acb07174b0 100644 --- a/lib/system/arc.nim +++ b/lib/system/arc.nim @@ -57,6 +57,21 @@ elif defined(nimArcIds): const traceId = -1 +when defined(gcAtomicArc) and hasThreadSupport: + template decrement(cell: Cell): untyped = + discard atomicDec(cell.rc, rcIncrement) + template increment(cell: Cell): untyped = + discard atomicInc(cell.rc, rcIncrement) + template count(x: Cell): untyped = + atomicLoadN(x.rc.addr, ATOMIC_ACQUIRE) shr rcShift +else: + template decrement(cell: Cell): untyped = + dec(cell.rc, rcIncrement) + template increment(cell: Cell): untyped = + inc(cell.rc, rcIncrement) + template count(x: Cell): untyped = + x.rc shr rcShift + proc nimNewObj(size, alignment: int): pointer {.compilerRtl.} = let hdrSize = align(sizeof(RefHeader), alignment) let s = size + hdrSize @@ -69,7 +84,7 @@ proc nimNewObj(size, alignment: int): pointer {.compilerRtl.} = atomicInc gRefId if head(result).refId == traceId: writeStackTrace() - cfprintf(cstderr, "[nimNewObj] %p %ld\n", result, head(result).rc shr rcShift) + cfprintf(cstderr, "[nimNewObj] %p %ld\n", result, head(result).count) when traceCollector: cprintf("[Allocated] %p result: %p\n", result -! sizeof(RefHeader), result) @@ -90,21 +105,21 @@ proc nimNewObjUninit(size, alignment: int): pointer {.compilerRtl.} = atomicInc gRefId if head(result).refId == traceId: writeStackTrace() - cfprintf(cstderr, "[nimNewObjUninit] %p %ld\n", result, head(result).rc shr rcShift) + cfprintf(cstderr, "[nimNewObjUninit] %p %ld\n", result, head(result).count) when traceCollector: cprintf("[Allocated] %p result: %p\n", result -! sizeof(RefHeader), result) proc nimDecWeakRef(p: pointer) {.compilerRtl, inl.} = - dec head(p).rc, rcIncrement + decrement head(p) proc nimIncRef(p: pointer) {.compilerRtl, inl.} = when defined(nimArcDebug): if head(p).refId == traceId: writeStackTrace() - cfprintf(cstderr, "[IncRef] %p %ld\n", p, head(p).rc shr rcShift) + cfprintf(cstderr, "[IncRef] %p %ld\n", p, head(p).count) - inc head(p).rc, rcIncrement + increment head(p) when traceCollector: cprintf("[INCREF] %p\n", head(p)) @@ -173,17 +188,17 @@ proc nimDecRefIsLast(p: pointer): bool {.compilerRtl, inl.} = when defined(nimArcDebug): if cell.refId == traceId: writeStackTrace() - cfprintf(cstderr, "[DecRef] %p %ld\n", p, cell.rc shr rcShift) + cfprintf(cstderr, "[DecRef] %p %ld\n", p, cell.count) - if (cell.rc and not rcMask) == 0: + if cell.count == 0: result = true when traceCollector: cprintf("[ABOUT TO DESTROY] %p\n", cell) else: - dec cell.rc, rcIncrement + decrement cell # According to Lins it's correct to do nothing else here. when traceCollector: - cprintf("[DeCREF] %p\n", cell) + cprintf("[DECREF] %p\n", cell) proc nimDupRef(dest: ptr pointer, src: pointer) {.compilerRtl, inl.} = dest[] = src From e45eb39ef7c194ee16a9fd3c2d08997f62c44375 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20M=20G=C3=B3mez?= Date: Mon, 8 May 2023 16:04:27 +0100 Subject: [PATCH 13/15] documents codegendecl for object types (#21811) --- doc/manual.md | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/doc/manual.md b/doc/manual.md index cb2509bd28..93f13f8181 100644 --- a/doc/manual.md +++ b/doc/manual.md @@ -8072,8 +8072,8 @@ CodegenDecl pragma ------------------ The `codegenDecl` pragma can be used to directly influence Nim's code -generator. It receives a format string that determines how the variable -or proc is declared in the generated code. +generator. It receives a format string that determines how the variable, +proc or object type is declared in the generated code. For variables, $1 in the format string represents the type of the variable, $2 is the name of the variable, and each appearance of $# represents $1/$2 @@ -8108,7 +8108,30 @@ will generate this code: ```c __interrupt void myinterrupt() ``` + +For object types, the $1 represents the name of the object type, $2 is the list of +fields and $3 is the base type. +```nim + +const strTemplate = """ + struct $1 { + $2 + }; +""" +type Foo {.codegenDecl:strTemplate.} = object + a, b: int +``` + +will generate this code: + + +```c +struct Foo { + NI a; + NI b; +}; +``` `cppNonPod` pragma ------------------ From ec3bca8fab723563bc9fb99ce9d5161652ce6945 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 8 May 2023 18:52:47 +0200 Subject: [PATCH 14/15] =?UTF-8?q?Windows:=20use=20=5F=5Fdeclspec(thread)?= =?UTF-8?q?=20TLS=20implementation,=20it=20is=20MUCH=20faster=E2=80=A6=20(?= =?UTF-8?q?#21810)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Windows: use __declspec(thread) TLS implementation, it is MUCH faster than _Thread_local [backport] * Update lib/nimbase.h * better fix --- lib/nimbase.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/nimbase.h b/lib/nimbase.h index b9b2695c9a..570b50b081 100644 --- a/lib/nimbase.h +++ b/lib/nimbase.h @@ -125,7 +125,13 @@ __AVR__ NIM_THREADVAR declaration based on http://stackoverflow.com/questions/18298280/how-to-declare-a-variable-as-thread-local-portably */ -#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112 && !defined __STDC_NO_THREADS__ +#if defined _WIN32 +# if defined _MSC_VER || defined __DMC__ || defined __BORLANDC__ +# define NIM_THREADVAR __declspec(thread) +# else +# define NIM_THREADVAR __thread +# endif +#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112 && !defined __STDC_NO_THREADS__ # define NIM_THREADVAR _Thread_local #elif defined _WIN32 && ( \ defined _MSC_VER || \ From 4ee70165f1f0646df34ae35b7c98bd8b7d1d6d5d Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Mon, 8 May 2023 13:53:32 -0300 Subject: [PATCH 15/15] Add build-id=none for GCC when build for Release (#21808) * Add build-id=none to GCC/Clang, unneeded metadata in binaries * Add build-id=none to GCC/Clang, unneeded metadata in binaries * Add build-id=none to Clang * Fix * Fix * Add build-id=none to GCC --- changelogs/changelog_2_0_0.md | 1 + config/nim.cfg | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/changelogs/changelog_2_0_0.md b/changelogs/changelog_2_0_0.md index 1e0b427b68..8852e398fa 100644 --- a/changelogs/changelog_2_0_0.md +++ b/changelogs/changelog_2_0_0.md @@ -454,6 +454,7 @@ static libraries. - When compiling for Release the flag `-fno-math-errno` is used for GCC. +- When compiling for Release the flag `--build-id=none` is used for GCC Linker. ## Docgen diff --git a/config/nim.cfg b/config/nim.cfg index 13665936b6..cc27d5a3d6 100644 --- a/config/nim.cfg +++ b/config/nim.cfg @@ -364,3 +364,9 @@ tcc.options.always = "-w" clang.options.linker %= "${clang.options.linker} -s" clang.cpp.options.linker %= "${clang.cpp.options.linker} -s" @end + +# Linker: Skip "Build-ID metadata strings" in binaries when build for release. +@if release or danger: + gcc.options.linker %= "${gcc.options.linker} -Wl,--build-id=none" + gcc.cpp.options.linker %= "${gcc.cpp.options.linker} -Wl,--build-id=none" +@end