An unnamed break in a block now gives an UnnamedBreak warning (#20901)
* unnamed break in the block now gives an error * bootstrap * fixes * more fixes * break with label * label again * one moee * Delete test5.txt * it now gives a UnnamedBreak warning * change the URL of bump back to the original one
This commit is contained in:
parent
d149727f34
commit
ef29987781
16 changed files with 85 additions and 20 deletions
|
|
@ -105,6 +105,8 @@
|
||||||
|
|
||||||
- Object fields now support default values, see https://nim-lang.github.io/Nim/manual.html#types-default-values-for-object-fields for details.
|
- Object fields now support default values, see https://nim-lang.github.io/Nim/manual.html#types-default-values-for-object-fields for details.
|
||||||
|
|
||||||
|
- Using an unnamed break in a block is deprecated. This warning will become an error in future versions! Use a named block with a named break instead.
|
||||||
|
|
||||||
## Standard library additions and changes
|
## Standard library additions and changes
|
||||||
|
|
||||||
[//]: # "Changes:"
|
[//]: # "Changes:"
|
||||||
|
|
|
||||||
|
|
@ -148,3 +148,4 @@ proc initDefines*(symbols: StringTableRef) =
|
||||||
|
|
||||||
defineSymbol("nimHasOutParams")
|
defineSymbol("nimHasOutParams")
|
||||||
defineSymbol("nimHasSystemRaisesDefect")
|
defineSymbol("nimHasSystemRaisesDefect")
|
||||||
|
defineSymbol("nimHasWarnUnnamedBreak")
|
||||||
|
|
|
||||||
|
|
@ -84,6 +84,7 @@ type
|
||||||
warnEffect = "Effect",
|
warnEffect = "Effect",
|
||||||
warnCastSizes = "CastSizes"
|
warnCastSizes = "CastSizes"
|
||||||
warnTemplateRedefinition = "TemplateRedefinition",
|
warnTemplateRedefinition = "TemplateRedefinition",
|
||||||
|
warnUnnamedBreak = "UnnamedBreak",
|
||||||
warnUser = "User",
|
warnUser = "User",
|
||||||
# hints
|
# hints
|
||||||
hintSuccess = "Success", hintSuccessX = "SuccessX",
|
hintSuccess = "Success", hintSuccessX = "SuccessX",
|
||||||
|
|
@ -181,6 +182,7 @@ const
|
||||||
warnEffect: "$1",
|
warnEffect: "$1",
|
||||||
warnCastSizes: "$1",
|
warnCastSizes: "$1",
|
||||||
warnTemplateRedefinition: "template '$1' is implicitly redefined, consider adding an explicit .redefine pragma",
|
warnTemplateRedefinition: "template '$1' is implicitly redefined, consider adding an explicit .redefine pragma",
|
||||||
|
warnUnnamedBreak: "Using an unnamed break in a block is deprecated; Use a named block with a named break instead",
|
||||||
warnUser: "$1",
|
warnUser: "$1",
|
||||||
hintSuccess: "operation successful: $#",
|
hintSuccess: "operation successful: $#",
|
||||||
# keep in sync with `testament.isSuccess`
|
# keep in sync with `testament.isSuccess`
|
||||||
|
|
|
||||||
|
|
@ -27,9 +27,13 @@ define:useStdoutAsStdmsg
|
||||||
#gc:markAndSweep
|
#gc:markAndSweep
|
||||||
|
|
||||||
@if nimHasWarningObservableStores:
|
@if nimHasWarningObservableStores:
|
||||||
warning[ObservableStores]: off
|
warning[ObservableStores]:off
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@if nimHasWarningAsError:
|
@if nimHasWarningAsError:
|
||||||
warningAsError:GcUnsafe2:on
|
warningAsError[GcUnsafe2]:on
|
||||||
|
@end
|
||||||
|
|
||||||
|
@if nimHasWarnUnnamedBreak:
|
||||||
|
warningAserror[UnnamedBreak]:on
|
||||||
@end
|
@end
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ type
|
||||||
resultSym*: PSym # the result symbol (if we are in a proc)
|
resultSym*: PSym # the result symbol (if we are in a proc)
|
||||||
nestedLoopCounter*: int # whether we are in a loop or not
|
nestedLoopCounter*: int # whether we are in a loop or not
|
||||||
nestedBlockCounter*: int # whether we are in a block or not
|
nestedBlockCounter*: int # whether we are in a block or not
|
||||||
|
breakInLoop*: bool # whether we are in a loop without block
|
||||||
next*: PProcCon # used for stacking procedure contexts
|
next*: PProcCon # used for stacking procedure contexts
|
||||||
mappingExists*: bool
|
mappingExists*: bool
|
||||||
mapping*: TIdTable
|
mapping*: TIdTable
|
||||||
|
|
|
||||||
|
|
@ -2640,6 +2640,8 @@ include semobjconstr
|
||||||
proc semBlock(c: PContext, n: PNode; flags: TExprFlags; expectedType: PType = nil): PNode =
|
proc semBlock(c: PContext, n: PNode; flags: TExprFlags; expectedType: PType = nil): PNode =
|
||||||
result = n
|
result = n
|
||||||
inc(c.p.nestedBlockCounter)
|
inc(c.p.nestedBlockCounter)
|
||||||
|
let oldBreakInLoop = c.p.breakInLoop
|
||||||
|
c.p.breakInLoop = false
|
||||||
checkSonsLen(n, 2, c.config)
|
checkSonsLen(n, 2, c.config)
|
||||||
openScope(c) # BUGFIX: label is in the scope of block!
|
openScope(c) # BUGFIX: label is in the scope of block!
|
||||||
if n[0].kind != nkEmpty:
|
if n[0].kind != nkEmpty:
|
||||||
|
|
@ -2657,6 +2659,7 @@ proc semBlock(c: PContext, n: PNode; flags: TExprFlags; expectedType: PType = ni
|
||||||
if isEmptyType(n.typ): n.transitionSonsKind(nkBlockStmt)
|
if isEmptyType(n.typ): n.transitionSonsKind(nkBlockStmt)
|
||||||
else: n.transitionSonsKind(nkBlockExpr)
|
else: n.transitionSonsKind(nkBlockExpr)
|
||||||
closeScope(c)
|
closeScope(c)
|
||||||
|
c.p.breakInLoop = oldBreakInLoop
|
||||||
dec(c.p.nestedBlockCounter)
|
dec(c.p.nestedBlockCounter)
|
||||||
|
|
||||||
proc semExportExcept(c: PContext, n: PNode): PNode =
|
proc semExportExcept(c: PContext, n: PNode): PNode =
|
||||||
|
|
|
||||||
|
|
@ -133,6 +133,8 @@ proc semForFields(c: PContext, n: PNode, m: TMagic): PNode =
|
||||||
typeMismatch(c.config, calli.info, tupleTypeA, tupleTypeB, calli)
|
typeMismatch(c.config, calli.info, tupleTypeA, tupleTypeB, calli)
|
||||||
|
|
||||||
inc(c.p.nestedLoopCounter)
|
inc(c.p.nestedLoopCounter)
|
||||||
|
let oldBreakInLoop = c.p.breakInLoop
|
||||||
|
c.p.breakInLoop = true
|
||||||
if tupleTypeA.kind == tyTuple:
|
if tupleTypeA.kind == tyTuple:
|
||||||
var loopBody = n[^1]
|
var loopBody = n[^1]
|
||||||
for i in 0..<tupleTypeA.len:
|
for i in 0..<tupleTypeA.len:
|
||||||
|
|
@ -156,6 +158,7 @@ proc semForFields(c: PContext, n: PNode, m: TMagic): PNode =
|
||||||
semForObjectFields(fc, t.n, n, stmts)
|
semForObjectFields(fc, t.n, n, stmts)
|
||||||
if t[0] == nil: break
|
if t[0] == nil: break
|
||||||
t = skipTypes(t[0], skipPtrs)
|
t = skipTypes(t[0], skipPtrs)
|
||||||
|
c.p.breakInLoop = oldBreakInLoop
|
||||||
dec(c.p.nestedLoopCounter)
|
dec(c.p.nestedLoopCounter)
|
||||||
# for TR macros this 'while true: ...; break' loop is pretty bad, so
|
# for TR macros this 'while true: ...; break' loop is pretty bad, so
|
||||||
# we avoid it now if we can:
|
# we avoid it now if we can:
|
||||||
|
|
|
||||||
|
|
@ -73,6 +73,8 @@ proc semBreakOrContinue(c: PContext, n: PNode): PNode =
|
||||||
localError(c.config, n.info, errInvalidControlFlowX % s.name.s)
|
localError(c.config, n.info, errInvalidControlFlowX % s.name.s)
|
||||||
else:
|
else:
|
||||||
localError(c.config, n.info, errGenerated, "'continue' cannot have a label")
|
localError(c.config, n.info, errGenerated, "'continue' cannot have a label")
|
||||||
|
elif c.p.nestedBlockCounter > 0 and n.kind == nkBreakStmt and not c.p.breakInLoop:
|
||||||
|
localError(c.config, n.info, warnUnnamedBreak)
|
||||||
elif (c.p.nestedLoopCounter <= 0) and ((c.p.nestedBlockCounter <= 0) or n.kind == nkContinueStmt):
|
elif (c.p.nestedLoopCounter <= 0) and ((c.p.nestedBlockCounter <= 0) or n.kind == nkContinueStmt):
|
||||||
localError(c.config, n.info, errInvalidControlFlowX %
|
localError(c.config, n.info, errInvalidControlFlowX %
|
||||||
renderTree(n, {renderNoComments}))
|
renderTree(n, {renderNoComments}))
|
||||||
|
|
@ -89,7 +91,10 @@ proc semWhile(c: PContext, n: PNode; flags: TExprFlags): PNode =
|
||||||
openScope(c)
|
openScope(c)
|
||||||
n[0] = forceBool(c, semExprWithType(c, n[0], expectedType = getSysType(c.graph, n.info, tyBool)))
|
n[0] = forceBool(c, semExprWithType(c, n[0], expectedType = getSysType(c.graph, n.info, tyBool)))
|
||||||
inc(c.p.nestedLoopCounter)
|
inc(c.p.nestedLoopCounter)
|
||||||
|
let oldBreakInLoop = c.p.breakInLoop
|
||||||
|
c.p.breakInLoop = true
|
||||||
n[1] = semStmt(c, n[1], flags)
|
n[1] = semStmt(c, n[1], flags)
|
||||||
|
c.p.breakInLoop = oldBreakInLoop
|
||||||
dec(c.p.nestedLoopCounter)
|
dec(c.p.nestedLoopCounter)
|
||||||
closeScope(c)
|
closeScope(c)
|
||||||
if n[1].typ == c.enforceVoidContext:
|
if n[1].typ == c.enforceVoidContext:
|
||||||
|
|
@ -959,11 +964,14 @@ proc semForVars(c: PContext, n: PNode; flags: TExprFlags): PNode =
|
||||||
if not isDiscardUnderscore(v): addDecl(c, v)
|
if not isDiscardUnderscore(v): addDecl(c, v)
|
||||||
elif v.owner == nil: v.owner = getCurrOwner(c)
|
elif v.owner == nil: v.owner = getCurrOwner(c)
|
||||||
inc(c.p.nestedLoopCounter)
|
inc(c.p.nestedLoopCounter)
|
||||||
|
let oldBreakInLoop = c.p.breakInLoop
|
||||||
|
c.p.breakInLoop = true
|
||||||
openScope(c)
|
openScope(c)
|
||||||
n[^1] = semExprBranch(c, n[^1], flags)
|
n[^1] = semExprBranch(c, n[^1], flags)
|
||||||
if efInTypeof notin flags:
|
if efInTypeof notin flags:
|
||||||
discardCheck(c, n[^1], flags)
|
discardCheck(c, n[^1], flags)
|
||||||
closeScope(c)
|
closeScope(c)
|
||||||
|
c.p.breakInLoop = oldBreakInLoop
|
||||||
dec(c.p.nestedLoopCounter)
|
dec(c.p.nestedLoopCounter)
|
||||||
|
|
||||||
proc implicitIterator(c: PContext, it: string, arg: PNode): PNode =
|
proc implicitIterator(c: PContext, it: string, arg: PNode): PNode =
|
||||||
|
|
|
||||||
|
|
@ -1457,6 +1457,8 @@ proc semStmtListType(c: PContext, n: PNode, prev: PType): PType =
|
||||||
|
|
||||||
proc semBlockType(c: PContext, n: PNode, prev: PType): PType =
|
proc semBlockType(c: PContext, n: PNode, prev: PType): PType =
|
||||||
inc(c.p.nestedBlockCounter)
|
inc(c.p.nestedBlockCounter)
|
||||||
|
let oldBreakInLoop = c.p.breakInLoop
|
||||||
|
c.p.breakInLoop = false
|
||||||
checkSonsLen(n, 2, c.config)
|
checkSonsLen(n, 2, c.config)
|
||||||
openScope(c)
|
openScope(c)
|
||||||
if n[0].kind notin {nkEmpty, nkSym}:
|
if n[0].kind notin {nkEmpty, nkSym}:
|
||||||
|
|
@ -1465,6 +1467,7 @@ proc semBlockType(c: PContext, n: PNode, prev: PType): PType =
|
||||||
n[1].typ = result
|
n[1].typ = result
|
||||||
n.typ = result
|
n.typ = result
|
||||||
closeScope(c)
|
closeScope(c)
|
||||||
|
c.p.breakInLoop = oldBreakInLoop
|
||||||
dec(c.p.nestedBlockCounter)
|
dec(c.p.nestedBlockCounter)
|
||||||
|
|
||||||
proc semGenericParamInInvocation(c: PContext, n: PNode): PType =
|
proc semGenericParamInInvocation(c: PContext, n: PNode): PType =
|
||||||
|
|
|
||||||
|
|
@ -173,11 +173,11 @@ when not defined(nimscript):
|
||||||
|
|
||||||
iterator envPairsImpl(): tuple[key, value: string] {.tags: [ReadEnvEffect].} =
|
iterator envPairsImpl(): tuple[key, value: string] {.tags: [ReadEnvEffect].} =
|
||||||
when defined(windows):
|
when defined(windows):
|
||||||
block:
|
block implBlock:
|
||||||
template impl(get_fun, typ, size, zero, free_fun) =
|
template impl(get_fun, typ, size, zero, free_fun) =
|
||||||
let env = get_fun()
|
let env = get_fun()
|
||||||
var e = env
|
var e = env
|
||||||
if e == nil: break
|
if e == nil: break implBlock
|
||||||
while true:
|
while true:
|
||||||
let eend = strEnd(e)
|
let eend = strEnd(e)
|
||||||
let kv = $e
|
let kv = $e
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ proc finalize(n: NimNode, lhs: NimNode, level: int): NimNode =
|
||||||
else:
|
else:
|
||||||
result = quote: (let `lhs` = `n`)
|
result = quote: (let `lhs` = `n`)
|
||||||
|
|
||||||
proc process(n: NimNode, lhs: NimNode, level: int): NimNode =
|
proc process(n: NimNode, lhs: NimNode, label: NimNode, level: int): NimNode =
|
||||||
var n = n.copyNimTree
|
var n = n.copyNimTree
|
||||||
var it = n
|
var it = n
|
||||||
let addr2 = bindSym"addr"
|
let addr2 = bindSym"addr"
|
||||||
|
|
@ -78,7 +78,7 @@ proc process(n: NimNode, lhs: NimNode, level: int): NimNode =
|
||||||
let okSet = check[1]
|
let okSet = check[1]
|
||||||
let kind1 = check[2]
|
let kind1 = check[2]
|
||||||
let tmp = genSym(nskLet, "tmpCase")
|
let tmp = genSym(nskLet, "tmpCase")
|
||||||
let body = process(objRef, tmp, level + 1)
|
let body = process(objRef, tmp, label, level + 1)
|
||||||
let tmp3 = nnkDerefExpr.newTree(tmp)
|
let tmp3 = nnkDerefExpr.newTree(tmp)
|
||||||
it[0][0] = tmp3
|
it[0][0] = tmp3
|
||||||
let dot2 = nnkDotExpr.newTree(@[tmp, dot[1]])
|
let dot2 = nnkDotExpr.newTree(@[tmp, dot[1]])
|
||||||
|
|
@ -87,17 +87,17 @@ proc process(n: NimNode, lhs: NimNode, level: int): NimNode =
|
||||||
let assgn = finalize(n, lhs, level)
|
let assgn = finalize(n, lhs, level)
|
||||||
result = quote do:
|
result = quote do:
|
||||||
`body`
|
`body`
|
||||||
if `tmp3`.`kind1` notin `okSet`: break
|
if `tmp3`.`kind1` notin `okSet`: break `label`
|
||||||
`assgn`
|
`assgn`
|
||||||
break
|
break
|
||||||
elif it.kind in {nnkHiddenDeref, nnkDerefExpr}:
|
elif it.kind in {nnkHiddenDeref, nnkDerefExpr}:
|
||||||
let tmp = genSym(nskLet, "tmp")
|
let tmp = genSym(nskLet, "tmp")
|
||||||
let body = process(it[0], tmp, level + 1)
|
let body = process(it[0], tmp, label, level + 1)
|
||||||
it[0] = tmp
|
it[0] = tmp
|
||||||
let assgn = finalize(n, lhs, level)
|
let assgn = finalize(n, lhs, level)
|
||||||
result = quote do:
|
result = quote do:
|
||||||
`body`
|
`body`
|
||||||
if `tmp` == nil: break
|
if `tmp` == nil: break `label`
|
||||||
`assgn`
|
`assgn`
|
||||||
break
|
break
|
||||||
elif it.kind == nnkCall: # consider extending to `nnkCallKinds`
|
elif it.kind == nnkCall: # consider extending to `nnkCallKinds`
|
||||||
|
|
@ -113,10 +113,11 @@ macro `?.`*(a: typed): auto =
|
||||||
## presence of intermediate nil pointers/references, in which case a default
|
## presence of intermediate nil pointers/references, in which case a default
|
||||||
## value is produced.
|
## value is produced.
|
||||||
let lhs = genSym(nskVar, "lhs")
|
let lhs = genSym(nskVar, "lhs")
|
||||||
let body = process(a, lhs, 0)
|
let label = genSym(nskLabel, "label")
|
||||||
|
let body = process(a, lhs, label, 0)
|
||||||
result = quote do:
|
result = quote do:
|
||||||
var `lhs`: type(`a`)
|
var `lhs`: type(`a`)
|
||||||
block:
|
block `label`:
|
||||||
`body`
|
`body`
|
||||||
`lhs`
|
`lhs`
|
||||||
|
|
||||||
|
|
@ -144,10 +145,11 @@ macro `??.`*(a: typed): Option =
|
||||||
|
|
||||||
let lhs = genSym(nskVar, "lhs")
|
let lhs = genSym(nskVar, "lhs")
|
||||||
let lhs2 = genSym(nskVar, "lhs")
|
let lhs2 = genSym(nskVar, "lhs")
|
||||||
let body = process(a, lhs2, 0)
|
let label = genSym(nskLabel, "label")
|
||||||
|
let body = process(a, lhs2, label, 0)
|
||||||
result = quote do:
|
result = quote do:
|
||||||
var `lhs`: Option[type(`a`)]
|
var `lhs`: Option[type(`a`)]
|
||||||
block:
|
block `label`:
|
||||||
var `lhs2`: type(`a`)
|
var `lhs2`: type(`a`)
|
||||||
`body`
|
`body`
|
||||||
`lhs` = option(`lhs2`)
|
`lhs` = option(`lhs2`)
|
||||||
|
|
|
||||||
|
|
@ -42,3 +42,5 @@ switch("define", "nimPreviewHashRef")
|
||||||
switch("define", "nimPreviewRangeDefault")
|
switch("define", "nimPreviewRangeDefault")
|
||||||
when defined(windows):
|
when defined(windows):
|
||||||
switch("tlsEmulation", "off")
|
switch("tlsEmulation", "off")
|
||||||
|
|
||||||
|
switch("warningAserror", "UnnamedBreak")
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ block tbreak:
|
||||||
run = false
|
run = false
|
||||||
block myblock:
|
block myblock:
|
||||||
if true:
|
if true:
|
||||||
break
|
break myblock
|
||||||
echo "leaving myblock"
|
echo "leaving myblock"
|
||||||
x = true
|
x = true
|
||||||
doAssert(x)
|
doAssert(x)
|
||||||
|
|
@ -95,3 +95,22 @@ block tnestif:
|
||||||
else:
|
else:
|
||||||
writeLine(stdout, "looks like Python")
|
writeLine(stdout, "looks like Python")
|
||||||
#OUT i == 2
|
#OUT i == 2
|
||||||
|
|
||||||
|
# bug https://github.com/nim-lang/RFCs/issues/451
|
||||||
|
for i in 1..2: # works
|
||||||
|
break
|
||||||
|
|
||||||
|
block: # works
|
||||||
|
for i in 1..2:
|
||||||
|
break
|
||||||
|
|
||||||
|
block: # works
|
||||||
|
block:
|
||||||
|
discard 12 + 3
|
||||||
|
for i in 1..2:
|
||||||
|
break
|
||||||
|
|
||||||
|
block named: # works
|
||||||
|
if true:
|
||||||
|
break named
|
||||||
|
doAssert false, "not reached"
|
||||||
|
|
|
||||||
15
tests/controlflow/tunamedbreak.nim
Normal file
15
tests/controlflow/tunamedbreak.nim
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
|
||||||
|
discard """
|
||||||
|
cmd: "nim check $file"
|
||||||
|
action: "reject"
|
||||||
|
nimout: '''
|
||||||
|
tunamedbreak.nim(12, 5) Error: Using an unnamed break in a block is deprecated; Use a named block with a named break instead [UnnamedBreak]
|
||||||
|
tunamedbreak.nim(15, 3) Error: Using an unnamed break in a block is deprecated; Use a named block with a named break instead [UnnamedBreak]
|
||||||
|
'''
|
||||||
|
"""
|
||||||
|
for i in 1..2: # errors
|
||||||
|
block:
|
||||||
|
break
|
||||||
|
|
||||||
|
block: # errors
|
||||||
|
break
|
||||||
|
|
@ -29,16 +29,16 @@ echo "2"
|
||||||
try:
|
try:
|
||||||
raise newException(OSError, "Problem")
|
raise newException(OSError, "Problem")
|
||||||
except OSError:
|
except OSError:
|
||||||
block:
|
block label:
|
||||||
break
|
break label
|
||||||
|
|
||||||
echo "3"
|
echo "3"
|
||||||
|
|
||||||
# Fourth Variety
|
# Fourth Variety
|
||||||
block:
|
block label:
|
||||||
try:
|
try:
|
||||||
raise newException(OSError, "Problem")
|
raise newException(OSError, "Problem")
|
||||||
except OSError:
|
except OSError:
|
||||||
break
|
break label
|
||||||
|
|
||||||
echo "4"
|
echo "4"
|
||||||
|
|
|
||||||
|
|
@ -369,9 +369,9 @@ block:
|
||||||
|
|
||||||
# bug #18824
|
# bug #18824
|
||||||
iterator poc_iterator: int {.closure.} =
|
iterator poc_iterator: int {.closure.} =
|
||||||
block:
|
block bug18824:
|
||||||
try:
|
try:
|
||||||
break
|
break bug18824
|
||||||
finally:
|
finally:
|
||||||
echo "In defer"
|
echo "In defer"
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue