Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@
- Implicit conversions for `const` behave correctly now, meaning that code like `const SOMECONST = 0.int; procThatTakesInt32(SOMECONST)` will be illegal now.
Simply write `const SOMECONST = 0` instead.

- A bug that automatically lifts nodes of kind `stmtList` into lambda
expressions has been fixed.

- Code blocks that start with a `do` are now consistent of type
`nkDo`.

## Library additions

Expand Down
2 changes: 1 addition & 1 deletion compiler/ast.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1052,7 +1052,7 @@ template `[]`*(n: Indexable, i: BackwardsIndex): Indexable = n[n.len - i.int]
template `[]=`*(n: Indexable, i: BackwardsIndex; x: Indexable) = n[n.len - i.int] = x

when defined(useNodeIds):
const nodeIdToDebug* = -1 # 2322968
const nodeIdToDebug* = -1
var gNodeId: int

proc newNode*(kind: TNodeKind): PNode =
Expand Down
5 changes: 5 additions & 0 deletions compiler/options.nim
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,11 @@ type
## Allows to modify a NimNode where the type has already been
## flagged with nfSem. If you actually do this, it will cause
## bugs.
optOldDoNode
## Do blocks without arguments magically become nkStmtList;
## Nodes of kind nkStmtList automatically convert to lambda
## expressions without arguments.


SymbolFilesOption* = enum
disabledSf, writeOnlySf, readOnlySf, v2Sf
Expand Down
32 changes: 20 additions & 12 deletions compiler/parser.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1090,9 +1090,6 @@ proc parseParamList(p: var TParser, retColon = true): PNode =
getTok(p)
optInd(p, result)
result.sons[0] = parseTypeDesc(p)
elif not retColon and not hasParLe:
# Mark as "not there" in order to mark for deprecation in the semantic pass:
result = p.emptyNode
when defined(nimpretty):
dec p.em.doIndentMore
dec p.em.keepIndents
Expand All @@ -1108,10 +1105,13 @@ proc parseDoBlock(p: var TParser; info: TLineInfo): PNode =
let params = parseParamList(p, retColon=false)
let pragmas = optPragmas(p)
colcom(p, result)
result = parseStmt(p)
if params.kind != nkEmpty:
let body = parseStmt(p)
let legacyMode = optOldDoNode in p.lex.config.legacyFeatures
if legacyMode and params.len == 1 and params[0].kind == nkEmpty:
result = body
else:
result = newProcNode(nkDo, info,
body = result, params = params, name = p.emptyNode, pattern = p.emptyNode,
body = body, params = params, name = p.emptyNode, pattern = p.emptyNode,
genericParams = p.emptyNode, pragmas = pragmas, exceptions = p.emptyNode)

proc parseProcExpr(p: var TParser; isExpr: bool; kind: TNodeKind): PNode =
Expand Down Expand Up @@ -1351,14 +1351,17 @@ proc postExprBlocks(p: var TParser, x: PNode): PNode =
result = x
if p.tok.indent >= 0: return

var
openingParams = p.emptyNode
openingPragmas = p.emptyNode
var openingParams, openingPragmas: PNode

if p.tok.tokType == tkDo:
let doToken = p.tok.tokType == tkDo
if doToken:
getTok(p)
openingParams = parseParamList(p, retColon=false)
openingPragmas = optPragmas(p)
else:
openingParams = newNodeP(nkFormalParams, p)
openingParams.add p.emptyNode
openingPragmas = p.emptyNode

if p.tok.tokType == tkColon:
result = makeCall(result)
Expand All @@ -1371,7 +1374,12 @@ proc postExprBlocks(p: var TParser, x: PNode): PNode =
if stmtList[0].kind == nkStmtList: stmtList = stmtList[0]

stmtList.flags.incl nfBlockArg
if openingParams.kind != nkEmpty:
let legacyMode = optOldDoNode in p.lex.config.legacyFeatures
if (legacyMode and openingParams.len > 1) or (not(legacyMode) and doToken):
# in legacy mode, a nkDo node is generated depending on the
# amount of parameters of the do node `do(a,b,c): ...` vs
# `do:`. This has changed, without legacy mode an `nkDo` node
# is generated always when also a doToken got parsed.
result.add newProcNode(nkDo, stmtList.info, body = stmtList,
params = openingParams,
name = p.emptyNode, pattern = p.emptyNode,
Expand Down Expand Up @@ -1413,7 +1421,7 @@ proc postExprBlocks(p: var TParser, x: PNode): PNode =

if nextBlock.kind == nkElse: break
else:
if openingParams.kind != nkEmpty:
if openingParams.len > 1:
parMessage(p, "expected ':'")

proc parseExprStmt(p: var TParser): PNode =
Expand Down
3 changes: 2 additions & 1 deletion compiler/semexprs.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1994,7 +1994,8 @@ proc semQuoteAst(c: PContext, n: PNode): PNode =
ids = newSeq[PNode](1)
# this will store the generated param names
# leave some room for the result symbol

if quotedBlock.kind == nkDo:
quotedBlock = quotedBlock[^1]
if quotedBlock.kind != nkStmtList:
localError(c.config, n.info, errXExpected, "block")

Expand Down
9 changes: 5 additions & 4 deletions compiler/sigmatch.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1964,9 +1964,10 @@ proc incMatches(m: var TCandidate; r: TTypeRelation; convMatch = 1) =
of isEqual: inc(m.exactMatches)
of isNone: discard

template matchesVoidProc(t: PType): bool =
proc matchesVoidProc(t: PType): bool =
# used onley for legacy optOldDoProc.
(t.kind == tyProc and t.len == 1 and t.sons[0] == nil) or
(t.kind == tyBuiltInTypeClass and t.sons[0].kind == tyProc)
(t.kind == tyBuiltInTypeClass and t.sons[0].kind == tyProc)

proc paramTypesMatchAux(m: var TCandidate, f, a: PType,
argSemantized, argOrig: PNode): PNode =
Expand Down Expand Up @@ -2122,8 +2123,8 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType,
inc(m.genericMatches)
m.fauxMatch = a.kind
return arg
elif a.kind == tyVoid and f.matchesVoidProc and argOrig.kind == nkStmtList:
# lift do blocks without params to lambdas
elif optOldDoNode in c.config.legacyFeatures and a.kind == tyVoid and f.matchesVoidProc and argOrig.kind == nkStmtList:
# lift stmtList nodes to lambdas.
let p = c.graph
let lifted = c.semExpr(c, newProcNode(nkDo, argOrig.info, body = argOrig,
params = nkFormalParams.newTree(p.emptyNode), name = p.emptyNode, pattern = p.emptyNode,
Expand Down
12 changes: 12 additions & 0 deletions lib/core/macros.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1630,6 +1630,18 @@ proc getProjectPath*(): string = discard
## be confused with ``system.currentSourcePath`` which returns
## the path of the current module.

macro stripDoNode*(arg: untyped): untyped =
## for templates that expect multiple blocks of code with the do
## notation, this macro will strip the do lambda node to inline the
## body.
if arg.kind == nnkDo:
expectLen(arg[3], 1)
expectKind(arg[3][0], nnkEmpty)
result = arg[6]
else:
result = arg


when defined(nimMacrosSizealignof):
proc getSize*(arg: NimNode): int {.magic: "NSizeOf", noSideEffect.} =
## Returns the same result as ``system.sizeof`` if the size is
Expand Down
2 changes: 1 addition & 1 deletion lib/pure/asyncdispatch.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1191,7 +1191,7 @@ else:
var curList: seq[Callback]

let selector = getGlobalDispatcher().selector
withData(selector, fd.int, fdData):
withData(selector, fd.int, fdData) do:
case event
of Event.Read:
shallowCopy(curList, fdData.readList)
Expand Down
6 changes: 3 additions & 3 deletions lib/pure/collections/sharedtables.nim
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ template withValue*[A, B](t: var SharedTable[A, B], key: A,
let hasKey = index >= 0
if hasKey:
var value {.inject.} = addr(t.data[index].val)
body
stripDoNode(body)
finally:
release(t.lock)

Expand All @@ -107,9 +107,9 @@ template withValue*[A, B](t: var SharedTable[A, B], key: A,
let hasKey = index >= 0
if hasKey:
var value {.inject.} = addr(t.data[index].val)
body1
stripDoNode(body1)
else:
body2
stripDoNode(body2)
finally:
release(t.lock)

Expand Down
8 changes: 4 additions & 4 deletions lib/pure/collections/tables.nim
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@
## * `hashes module<hashes.html>`_ for helper functions for hashing


import hashes, math, algorithm
import hashes, math, algorithm, macros

include "system/inclrtl"

Expand Down Expand Up @@ -610,7 +610,7 @@ template withValue*[A, B](t: var Table[A, B], key: A, value, body: untyped) =
let hasKey = index >= 0
if hasKey:
var value {.inject.} = addr(t.data[index].val)
body
stripDoNode(body)

template withValue*[A, B](t: var Table[A, B], key: A,
value, body1, body2: untyped) =
Expand All @@ -634,9 +634,9 @@ template withValue*[A, B](t: var Table[A, B], key: A,
let hasKey = index >= 0
if hasKey:
var value {.inject.} = addr(t.data[index].val)
body1
stripDoNode(body1)
else:
body2
stripDoNode(body2)


iterator pairs*[A, B](t: Table[A, B]): (A, B) =
Expand Down
8 changes: 4 additions & 4 deletions lib/pure/ioselects/ioselectors_epoll.nim
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

# This module implements Linux epoll().

import posix, times, epoll
import posix, times, epoll, macros

# Maximum number of events that can be returned
const MAX_EPOLL_EVENTS = 64
Expand Down Expand Up @@ -519,7 +519,7 @@ template withData*[T](s: Selector[T], fd: SocketHandle|int, value,
s.checkFd(fdi)
if fdi in s:
var value = addr(s.getData(fdi))
body
stripDoNode(body)

template withData*[T](s: Selector[T], fd: SocketHandle|int, value, body1,
body2: untyped) =
Expand All @@ -528,9 +528,9 @@ template withData*[T](s: Selector[T], fd: SocketHandle|int, value, body1,
s.checkFd(fdi)
if fdi in s:
var value = addr(s.getData(fdi))
body1
stripDoNode(body1)
else:
body2
stripDoNode(body2)

proc getFd*[T](s: Selector[T]): int =
return s.epollFd.int
8 changes: 4 additions & 4 deletions lib/pure/ioselects/ioselectors_kqueue.nim
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

# This module implements BSD kqueue().

import posix, times, kqueue
import posix, times, kqueue, macros

const
# Maximum number of events that can be returned.
Expand Down Expand Up @@ -617,7 +617,7 @@ template withData*[T](s: Selector[T], fd: SocketHandle|int, value,
s.checkFd(fdi)
if fdi in s:
var value = addr(s.getData(fdi))
body
stripDoNode(body)

template withData*[T](s: Selector[T], fd: SocketHandle|int, value, body1,
body2: untyped) =
Expand All @@ -626,9 +626,9 @@ template withData*[T](s: Selector[T], fd: SocketHandle|int, value, body1,
s.checkFd(fdi)
if fdi in s:
var value = addr(s.getData(fdi))
body1
stripDoNode(body1)
else:
body2
stripDoNode(body2)


proc getFd*[T](s: Selector[T]): int =
Expand Down
9 changes: 4 additions & 5 deletions lib/pure/ioselects/ioselectors_poll.nim
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

# This module implements Posix poll().

import posix, times
import posix, times, macros

# Maximum number of events that can be returned
const MAX_POLL_EVENTS = 64
Expand Down Expand Up @@ -292,7 +292,7 @@ template withData*[T](s: Selector[T], fd: SocketHandle|int, value,
s.checkFd(fdi)
if fdi in s:
var value = addr(s.getData(fdi))
body
stripDoNode(body)

template withData*[T](s: Selector[T], fd: SocketHandle|int, value, body1,
body2: untyped) =
Expand All @@ -301,10 +301,9 @@ template withData*[T](s: Selector[T], fd: SocketHandle|int, value, body1,
s.checkFd(fdi)
if fdi in s:
var value = addr(s.getData(fdi))
body1
stripDoNode(body1)
else:
body2

stripDoNode(body2)

proc getFd*[T](s: Selector[T]): int =
return -1
9 changes: 4 additions & 5 deletions lib/pure/ioselects/ioselectors_select.nim
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

# This module implements Posix and Windows select().

import times, nativesockets
import times, nativesockets, macros

when defined(windows):
import winlean
Expand Down Expand Up @@ -440,7 +440,7 @@ template withData*[T](s: Selector[T], fd: SocketHandle|int, value,
break
inc(i)
if i != FD_SETSIZE:
body
stripDoNode(body)

template withData*[T](s: Selector[T], fd: SocketHandle|int, value,
body1, body2: untyped) =
Expand All @@ -456,10 +456,9 @@ template withData*[T](s: Selector[T], fd: SocketHandle|int, value,
break
inc(i)
if i != FD_SETSIZE:
body1
stripDoNode(body1)
else:
body2

stripDoNode(body2)

proc getFd*[T](s: Selector[T]): int =
return -1
6 changes: 3 additions & 3 deletions tests/closure/tclosure.nim
Original file line number Diff line number Diff line change
Expand Up @@ -453,7 +453,7 @@ block doNotation:
b.onClick do (e: Event):
echo "click at ", e.x, ",", e.y

b.onFocusLost:
b.onFocusLost do:
echo "lost focus 1"

b.onFocusLost do:
Expand All @@ -462,10 +462,10 @@ block doNotation:
b.onUserEvent("UserEvent 1") do:
discard

b.onUserEvent "UserEvent 2":
b.onUserEvent("UserEvent 2") do:
discard

b.onUserEvent("UserEvent 3"):
b.onUserEvent("UserEvent 3") do:
discard

b.onUserEvent("UserEvent 4", () => echo "event 4")
Expand Down
20 changes: 11 additions & 9 deletions tests/closure/tclosure_issues.nim
Original file line number Diff line number Diff line change
Expand Up @@ -67,16 +67,18 @@ proc foo(): proc =
echo foo()()


# issue #7104

block tissue7104:
proc sp(cb: proc())=
cb()
cb()

sp:
var i = 0
sp do:
var i = 0
echo "ok ", i
sp do:
inc i
echo "ok ", i
sp():
inc i
echo "ok ", i
sp do:
inc i
echo "ok ", i
sp do:
inc i
echo "ok ", i
2 changes: 1 addition & 1 deletion tests/misc/tlambdadonotation.nim
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ proc foo(x: proc {.closure.}) =
proc bar =
var x = 123
# foo proc = echo x #[ ok ]#
foo: echo x #[ SIGSEGV: Illegal storage access. (Attempt to read from nil?) ]#
foo do: echo x #[ SIGSEGV: Illegal storage access. (Attempt to read from nil?) ]#

bar()

Expand Down
Loading