new minor language feature: .noSideEffect blocks like .gcsafe blocks

This commit is contained in:
Andreas Rumpf 2018-11-26 23:28:02 +01:00
commit 413580bc04
7 changed files with 30 additions and 12 deletions

View file

@ -89,7 +89,8 @@ proc enumToString*(enums: openArray[enum]): string =
### Language additions ### Language additions
- Vm suport for float32<->int32 and float64<->int64 casts was added. - Vm suport for float32<->int32 and float64<->int64 casts was added.
- There is a new pragma block `noSideEffect` that works like
the `gcsafe` pragma block.
### Language changes ### Language changes

View file

@ -38,7 +38,7 @@ const
wImportc, wExportc, wNodecl, wMagic, wDeprecated, wBorrow, wExtern, wImportc, wExportc, wNodecl, wMagic, wDeprecated, wBorrow, wExtern,
wImportCpp, wImportObjC, wError, wDiscardable, wGensym, wInject, wRaises, wImportCpp, wImportObjC, wError, wDiscardable, wGensym, wInject, wRaises,
wTags, wLocks, wGcSafe, wExportNims, wUsed} wTags, wLocks, wGcSafe, wExportNims, wUsed}
exprPragmas* = {wLine, wLocks, wNoRewrite, wGcSafe} exprPragmas* = {wLine, wLocks, wNoRewrite, wGcSafe, wNosideeffect}
stmtPragmas* = {wChecks, wObjChecks, wFieldChecks, wRangechecks, stmtPragmas* = {wChecks, wObjChecks, wFieldChecks, wRangechecks,
wBoundchecks, wOverflowchecks, wNilchecks, wMovechecks, wAssertions, wBoundchecks, wOverflowchecks, wNilchecks, wMovechecks, wAssertions,
wWarnings, wHints, wWarnings, wHints,
@ -855,8 +855,9 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
sym.flags.incl sfOverriden sym.flags.incl sfOverriden
of wNosideeffect: of wNosideeffect:
noVal(c, it) noVal(c, it)
incl(sym.flags, sfNoSideEffect) if sym != nil:
if sym.typ != nil: incl(sym.typ.flags, tfNoSideEffect) incl(sym.flags, sfNoSideEffect)
if sym.typ != nil: incl(sym.typ.flags, tfNoSideEffect)
of wSideeffect: of wSideeffect:
noVal(c, it) noVal(c, it)
incl(sym.flags, sfSideEffect) incl(sym.flags, sfSideEffect)

View file

@ -56,6 +56,7 @@ type
guards: TModel # nested guards guards: TModel # nested guards
locked: seq[PNode] # locked locations locked: seq[PNode] # locked locations
gcUnsafe, isRecursive, isToplevel, hasSideEffect, inEnforcedGcSafe: bool gcUnsafe, isRecursive, isToplevel, hasSideEffect, inEnforcedGcSafe: bool
inEnforcedNoSideEffects: bool
maxLockLevel, currLockLevel: TLockLevel maxLockLevel, currLockLevel: TLockLevel
config: ConfigRef config: ConfigRef
graph: ModuleGraph graph: ModuleGraph
@ -194,10 +195,10 @@ proc markGcUnsafe(a: PEffects; reason: PNode) =
when true: when true:
template markSideEffect(a: PEffects; reason: typed) = template markSideEffect(a: PEffects; reason: typed) =
a.hasSideEffect = true if not a.inEnforcedNoSideEffects: a.hasSideEffect = true
else: else:
template markSideEffect(a: PEffects; reason: typed) = template markSideEffect(a: PEffects; reason: typed) =
a.hasSideEffect = true if not a.inEnforcedNoSideEffects: a.hasSideEffect = true
markGcUnsafe(a, reason) markGcUnsafe(a, reason)
proc listGcUnsafety(s: PSym; onlyWarning: bool; cycleCheck: var IntSet; conf: ConfigRef) = proc listGcUnsafety(s: PSym; onlyWarning: bool; cycleCheck: var IntSet; conf: ConfigRef) =
@ -846,15 +847,20 @@ proc track(tracked: PEffects, n: PNode) =
let oldLocked = tracked.locked.len let oldLocked = tracked.locked.len
let oldLockLevel = tracked.currLockLevel let oldLockLevel = tracked.currLockLevel
var enforcedGcSafety = false var enforcedGcSafety = false
var enforceNoSideEffects = false
for i in 0 ..< pragmaList.len: for i in 0 ..< pragmaList.len:
let pragma = whichPragma(pragmaList.sons[i]) let pragma = whichPragma(pragmaList.sons[i])
if pragma == wLocks: if pragma == wLocks:
lockLocations(tracked, pragmaList.sons[i]) lockLocations(tracked, pragmaList.sons[i])
elif pragma == wGcSafe: elif pragma == wGcSafe:
enforcedGcSafety = true enforcedGcSafety = true
elif pragma == wNosideeffect:
enforceNoSideEffects = true
if enforcedGcSafety: tracked.inEnforcedGcSafe = true if enforcedGcSafety: tracked.inEnforcedGcSafe = true
if enforceNoSideEffects: tracked.inEnforcedNoSideEffects = true
track(tracked, n.lastSon) track(tracked, n.lastSon)
if enforcedGcSafety: tracked.inEnforcedGcSafe = false if enforcedGcSafety: tracked.inEnforcedGcSafe = false
if enforceNoSideEffects: tracked.inEnforcedNoSideEffects = false
setLen(tracked.locked, oldLocked) setLen(tracked.locked, oldLocked)
tracked.currLockLevel = oldLockLevel tracked.currLockLevel = oldLockLevel
of nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef, nkIteratorDef, of nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef, nkIteratorDef,

View file

@ -1860,7 +1860,7 @@ proc semPragmaBlock(c: PContext, n: PNode): PNode =
for i in 0 ..< pragmaList.len: for i in 0 ..< pragmaList.len:
case whichPragma(pragmaList.sons[i]) case whichPragma(pragmaList.sons[i])
of wLine: setLine(result, pragmaList.sons[i].info) of wLine: setLine(result, pragmaList.sons[i].info)
of wLocks, wGcSafe: of wLocks, wGcSafe, wNosideeffect:
result = n result = n
result.typ = n.sons[1].typ result.typ = n.sons[1].typ
of wNoRewrite: of wNoRewrite:

View file

@ -1239,8 +1239,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
if dest < 0: dest = c.getTemp(n.typ) if dest < 0: dest = c.getTemp(n.typ)
c.gABC(n, opcCallSite, dest) c.gABC(n, opcCallSite, dest)
of mNGenSym: genBinaryABC(c, n, dest, opcGenSym) of mNGenSym: genBinaryABC(c, n, dest, opcGenSym)
of mMinI, mMaxI, mAbsF64, mMinF64, mMaxF64, mAbsI, of mMinI, mMaxI, mAbsF64, mMinF64, mMaxF64, mAbsI, mDotDot:
mDotDot:
c.genCall(n, dest) c.genCall(n, dest)
of mExpandToAst: of mExpandToAst:
if n.len != 2: if n.len != 2:

View file

@ -6755,6 +6755,16 @@ routines marked as ``noSideEffect``.
func `+` (x, y: int): int func `+` (x, y: int): int
To override the compiler's side effect analysis a ``{.noSideEffect.}``
pragma block can be used:
.. code-block:: nim
func f() =
{.noSideEffect.}:
echo "test"
compileTime pragma compileTime pragma
------------------ ------------------
The ``compileTime`` pragma is used to mark a proc or variable to be used at The ``compileTime`` pragma is used to mark a proc or variable to be used at

View file

@ -82,7 +82,7 @@ type
cap: int cap: int
region: Allocator region: Allocator
proc newSeqPayload(cap, elemSize: int): pointer {.compilerRtl.} = proc newSeqPayload(cap, elemSize: int): pointer {.compilerRtl, noSideEffect.} =
# we have to use type erasure here as Nim does not support generic # we have to use type erasure here as Nim does not support generic
# compilerProcs. Oh well, this will all be inlined anyway. # compilerProcs. Oh well, this will all be inlined anyway.
if cap <= 0: if cap <= 0:
@ -94,7 +94,8 @@ proc newSeqPayload(cap, elemSize: int): pointer {.compilerRtl.} =
else: else:
result = nil result = nil
proc prepareSeqAdd(len: int; p: pointer; addlen, elemSize: int): pointer {.compilerRtl.} = proc prepareSeqAdd(len: int; p: pointer; addlen, elemSize: int): pointer {.
compilerRtl, noSideEffect.} =
if len+addlen <= len: if len+addlen <= len:
result = p result = p
elif p == nil: elif p == nil:
@ -128,7 +129,7 @@ proc grow*[T](x: var seq[T]; newLen: Natural; value: T) =
xu.p = cast[typeof(xu.p)](prepareSeqAdd(oldLen, xu.p, newLen - oldLen, sizeof(T))) xu.p = cast[typeof(xu.p)](prepareSeqAdd(oldLen, xu.p, newLen - oldLen, sizeof(T)))
xu.len = newLen xu.len = newLen
for i in oldLen .. newLen-1: for i in oldLen .. newLen-1:
x.data[i] = value xu.p.data[i] = value
proc setLen[T](s: var seq[T], newlen: Natural) = proc setLen[T](s: var seq[T], newlen: Natural) =
if newlen < s.len: if newlen < s.len: