Skip to content

Commit

Permalink
Merge branch 'devel' into ringabout-patch-5
Browse files Browse the repository at this point in the history
  • Loading branch information
ringabout committed Sep 4, 2023
2 parents d3114f9 + 8f7aedb commit 725884c
Show file tree
Hide file tree
Showing 12 changed files with 253 additions and 27 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/bisects.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4

# nimrun-action requires Nim installed.
- uses: jiro4989/setup-nim-action@v1
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/ci_bench.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
timeout-minutes: 60 # refs bug #18178
steps:
- name: 'Checkout'
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
fetch-depth: 2

Expand Down Expand Up @@ -60,7 +60,7 @@ jobs:
run: nim c -r -d:release ci/action.nim

- name: 'Checkout minimize'
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
repository: 'nim-lang/ci_bench'
path: minimize
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci_docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ jobs:

steps:
- name: 'Checkout'
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
fetch-depth: 2

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci_packages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ jobs:
NIM_TESTAMENT_BATCH: ${{ matrix.batch }}
steps:
- name: 'Checkout'
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
fetch-depth: 2

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci_publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- name: 'Checkout'
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
fetch-depth: 2

Expand Down
3 changes: 2 additions & 1 deletion changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@

[//]: # "Additions:"

- Adds `newStringUninit` to system, which creates a new string of length `len` like `newString` but with uninitialized content.
- Added `newStringUninit` to system, which creates a new string of length `len` like `newString` but with uninitialized content.
- Added `hasDefaultValue` to `std/typetraits` to check if a type has a valid default value.

[//]: # "Deprecations:"

Expand Down
2 changes: 1 addition & 1 deletion compiler/lookups.nim
Original file line number Diff line number Diff line change
Expand Up @@ -596,7 +596,7 @@ proc lookUp*(c: PContext, n: PNode): PSym =
if result == nil: result = errorUndeclaredIdentifierHint(c, n, ident)
else:
internalError(c.config, n.info, "lookUp")
return
return nil
if amb:
#contains(c.ambiguousSymbols, result.id):
result = errorUseQualifier(c, n.info, result, amb)
Expand Down
2 changes: 2 additions & 0 deletions compiler/semmagic.nim
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,8 @@ proc evalTypeTrait(c: PContext; traitCall: PNode, operand: PType, context: PSym)
let complexObj = containsGarbageCollectedRef(t) or
hasDestructor(t)
result = newIntNodeT(toInt128(ord(not complexObj)), traitCall, c.idgen, c.graph)
of "hasDefaultValue":
result = newIntNodeT(toInt128(ord(not operand.requiresInit)), traitCall, c.idgen, c.graph)
of "isNamedTuple":
var operand = operand.skipTypes({tyGenericInst})
let cond = operand.kind == tyTuple and operand.n != nil
Expand Down
87 changes: 68 additions & 19 deletions compiler/sempass2.nim
Original file line number Diff line number Diff line change
Expand Up @@ -352,20 +352,25 @@ proc useVar(a: PEffects, n: PNode) =
a.init.add s.id
useVarNoInitCheck(a, n, s)

type
BreakState = enum
bsNone
bsBreakOrReturn
bsNoReturn

type
TIntersection = seq[tuple[id, count: int]] # a simple count table

proc addToIntersection(inter: var TIntersection, s: int, initOnly: bool) =
proc addToIntersection(inter: var TIntersection, s: int, state: BreakState) =
for j in 0..<inter.len:
if s == inter[j].id:
if not initOnly:
if state == bsNone:
inc inter[j].count
return
if initOnly:
inter.add((id: s, count: 0))
else:
if state == bsNone:
inter.add((id: s, count: 1))
else:
inter.add((id: s, count: 0))

proc throws(tracked, n, orig: PNode) =
if n.typ == nil or n.typ.kind != tyError:
Expand Down Expand Up @@ -469,7 +474,7 @@ proc trackTryStmt(tracked: PEffects, n: PNode) =
track(tracked, n[0])
dec tracked.inTryStmt
for i in oldState..<tracked.init.len:
addToIntersection(inter, tracked.init[i], false)
addToIntersection(inter, tracked.init[i], bsNone)

var branches = 1
var hasFinally = false
Expand Down Expand Up @@ -504,7 +509,7 @@ proc trackTryStmt(tracked: PEffects, n: PNode) =
tracked.init.add b[j][2].sym.id
track(tracked, b[^1])
for i in oldState..<tracked.init.len:
addToIntersection(inter, tracked.init[i], false)
addToIntersection(inter, tracked.init[i], bsNone)
else:
setLen(tracked.init, oldState)
track(tracked, b[^1])
Expand Down Expand Up @@ -673,15 +678,50 @@ proc trackOperandForIndirectCall(tracked: PEffects, n: PNode, formals: PType; ar
localError(tracked.config, n.info, $n & " is not GC safe")
notNilCheck(tracked, n, paramType)

proc breaksBlock(n: PNode): bool =

proc breaksBlock(n: PNode): BreakState =
# semantic check doesn't allow statements after raise, break, return or
# call to noreturn proc, so it is safe to check just the last statements
var it = n
while it.kind in {nkStmtList, nkStmtListExpr} and it.len > 0:
it = it.lastSon

result = it.kind in {nkBreakStmt, nkReturnStmt, nkRaiseStmt} or
it.kind in nkCallKinds and it[0].kind == nkSym and sfNoReturn in it[0].sym.flags
case it.kind
of nkBreakStmt, nkReturnStmt:
result = bsBreakOrReturn
of nkRaiseStmt:
result = bsNoReturn
of nkCallKinds:
if it[0].kind == nkSym and sfNoReturn in it[0].sym.flags:
result = bsNoReturn
else:
result = bsNone
else:
result = bsNone

proc addIdToIntersection(tracked: PEffects, inter: var TIntersection, resCounter: var int,
hasBreaksBlock: BreakState, oldState: int, resSym: PSym, hasResult: bool) =
if hasResult:
var alreadySatisfy = false

if hasBreaksBlock == bsNoReturn:
alreadySatisfy = true
inc resCounter

for i in oldState..<tracked.init.len:
if tracked.init[i] == resSym.id:
if not alreadySatisfy:
inc resCounter
alreadySatisfy = true
else:
addToIntersection(inter, tracked.init[i], hasBreaksBlock)
else:
for i in oldState..<tracked.init.len:
addToIntersection(inter, tracked.init[i], hasBreaksBlock)

template hasResultSym(s: PSym): bool =
s != nil and s.kind in {skProc, skFunc, skConverter, skMethod} and
not isEmptyType(s.typ[0])

proc trackCase(tracked: PEffects, n: PNode) =
track(tracked, n[0])
Expand All @@ -694,6 +734,10 @@ proc trackCase(tracked: PEffects, n: PNode) =
(tracked.config.hasWarn(warnProveField) or strictCaseObjects in tracked.c.features)
var inter: TIntersection = @[]
var toCover = 0
let hasResult = hasResultSym(tracked.owner)
let resSym = if hasResult: tracked.owner.ast[resultPos].sym else: nil
var resCounter = 0

for i in 1..<n.len:
let branch = n[i]
setLen(tracked.init, oldState)
Expand All @@ -703,13 +747,14 @@ proc trackCase(tracked: PEffects, n: PNode) =
for i in 0..<branch.len:
track(tracked, branch[i])
let hasBreaksBlock = breaksBlock(branch.lastSon)
if not hasBreaksBlock:
if hasBreaksBlock == bsNone:
inc toCover
for i in oldState..<tracked.init.len:
addToIntersection(inter, tracked.init[i], hasBreaksBlock)
addIdToIntersection(tracked, inter, resCounter, hasBreaksBlock, oldState, resSym, hasResult)

setLen(tracked.init, oldState)
if not stringCase or lastSon(n).kind == nkElse:
if hasResult and resCounter == n.len-1:
tracked.init.add resSym.id
for id, count in items(inter):
if count >= toCover: tracked.init.add id
# else we can't merge
Expand All @@ -723,14 +768,17 @@ proc trackIf(tracked: PEffects, n: PNode) =
addFact(tracked.guards, n[0][0])
let oldState = tracked.init.len

let hasResult = hasResultSym(tracked.owner)
let resSym = if hasResult: tracked.owner.ast[resultPos].sym else: nil
var resCounter = 0

var inter: TIntersection = @[]
var toCover = 0
track(tracked, n[0][1])
let hasBreaksBlock = breaksBlock(n[0][1])
if not hasBreaksBlock:
if hasBreaksBlock == bsNone:
inc toCover
for i in oldState..<tracked.init.len:
addToIntersection(inter, tracked.init[i], hasBreaksBlock)
addIdToIntersection(tracked, inter, resCounter, hasBreaksBlock, oldState, resSym, hasResult)

for i in 1..<n.len:
let branch = n[i]
Expand All @@ -743,13 +791,14 @@ proc trackIf(tracked: PEffects, n: PNode) =
for i in 0..<branch.len:
track(tracked, branch[i])
let hasBreaksBlock = breaksBlock(branch.lastSon)
if not hasBreaksBlock:
if hasBreaksBlock == bsNone:
inc toCover
for i in oldState..<tracked.init.len:
addToIntersection(inter, tracked.init[i], hasBreaksBlock)
addIdToIntersection(tracked, inter, resCounter, hasBreaksBlock, oldState, resSym, hasResult)

setLen(tracked.init, oldState)
if lastSon(n).len == 1:
if hasResult and resCounter == n.len:
tracked.init.add resSym.id
for id, count in items(inter):
if count >= toCover: tracked.init.add id
# else we can't merge as it is not exhaustive
Expand Down
17 changes: 17 additions & 0 deletions lib/pure/typetraits.nim
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,23 @@ proc supportsCopyMem*(t: typedesc): bool {.magic: "TypeTrait".}
##
## Other languages name a type like these `blob`:idx:.

proc hasDefaultValue*(t: typedesc): bool {.magic: "TypeTrait".} =
## Returns true if `t` has a valid default value.
runnableExamples:
{.experimental: "strictNotNil".}
type
NilableObject = ref object
a: int
Object = NilableObject not nil
RequiresInit[T] = object
a {.requiresInit.}: T

assert hasDefaultValue(NilableObject)
assert not hasDefaultValue(Object)
assert hasDefaultValue(string)
assert not hasDefaultValue(var string)
assert not hasDefaultValue(RequiresInit[int])

proc isNamedTuple*(T: typedesc): bool {.magic: "TypeTrait".} =
## Returns true for named tuples, false for any other type.
runnableExamples:
Expand Down
64 changes: 64 additions & 0 deletions tests/init/tcompiles.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
discard """
matrix: "--warningAsError:ProveInit --warningAsError:Uninit"
"""

{.experimental: "strictdefs".}

type Test = object
id: int

proc foo {.noreturn.} = discard

block:
proc test(x: bool): Test =
if x:
foo()
else:
foo()

block:
proc test(x: bool): Test =
if x:
result = Test()
else:
foo()

discard test(true)

block:
proc test(x: bool): Test =
if x:
result = Test()
else:
return Test()

discard test(true)

block:
proc test(x: bool): Test =
if x:
return Test()
else:
return Test()

discard test(true)

block:
proc test(x: bool): Test =
if x:
result = Test()
else:
result = Test()
return

discard test(true)

block:
proc test(x: bool): Test =
if x:
result = Test()
return
else:
raise newException(ValueError, "unreachable")

discard test(true)
Loading

0 comments on commit 725884c

Please sign in to comment.