123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382 |
- discard """
- action: compile
- """
- import tables
- {.experimental: "strictNotNil".}
- type
- Nilable* = ref object
- a*: int
- field*: Nilable
-
- NonNilable* = Nilable not nil
- Nilable2* = nil NonNilable
- # proc `[]`(a: Nilable, b: int): Nilable =
- # nil
- # Nilable tests
- # test deref
- proc testDeref(a: Nilable) =
- echo a.a > 0 #[tt.Warning
- ^ can't deref a, it might be nil
- ]#
- # # # test if else
- proc testIfElse(a: Nilable) =
- if a.isNil:
- echo a.a #[tt.Warning
- ^ can't deref a, it is nil
- ]#
- else:
- echo a.a # ok
- proc testIfNoElse(a: Nilable) =
- if a.isNil:
- echo a.a #[tt.Warning
- ^ can't deref a, it is nil
- ]#
- echo a.a #[tt.Warning
- ^ can't deref a, it might be nil
- ]#
- proc testIfReturn(a: Nilable) =
- if not a.isNil:
- return
- echo a.a #[tt.Warning
- ^ can't deref a, it is nil
- ]#
- proc testIfBreak(a: seq[Nilable]) =
- for b in a:
- if not b.isNil:
- break
- echo b.a #[tt.Warning
- ^ can't deref b, it is nil
- ]#
- proc testIfContinue(a: seq[Nilable]) =
- for b in a:
- if not b.isNil:
- continue
- echo b.a #[tt.Warning
- ^ can't deref b, it is nil
- ]#
- proc testIfRaise(a: Nilable) =
- if not a.isNil:
- raise newException(ValueError, "")
- echo a.a #[tt.Warning
- ^ can't deref a, it is nil
- ]#
- proc testIfElif(a: Nilable) =
- var c = 0
- if c == 0:
- echo a.a #[tt.Warning
- ^ can't deref a, it might be nil
- ]#
- elif c == 1:
- echo a.a #[tt.Warning
- ^ can't deref a, it might be nil
- ]#
- elif not a.isNil:
- echo a.a # ok
- elif c == 2:
- echo 0
- else:
- echo a.a #[tt.Warning
- ^ can't deref a, it is nil
- ]#
- proc testAssignUnify(a: Nilable, b: int) =
- var a2 = a
- if b == 0:
- a2 = Nilable()
- echo a2.a #[tt.Warning
- ^ can't deref a2, it might be nil
- ]#
- # # test assign in branch and unifiying that with the main block after end of branch
- proc testAssignUnifyNil(a: Nilable, b: int) =
- var a2 = a
- if b == 0:
- a2 = nil
- echo a2.a #[tt.Warning
- ^ can't deref a2, it might be nil
- ]#
- # test loop
- proc testForLoop(a: Nilable) =
- var b = Nilable()
- for i in 0 .. 5:
- echo b.a #[tt.Warning
- ^ can't deref b, it might be nil
- ]#
- if i == 2:
- b = a
- echo b.a #[tt.Warning
- ^ can't deref b, it might be nil
- ]#
- # # TODO implement this after discussion
- # # proc testResultCompoundNonNilableElement(a: Nilable): (NonNilable, NonNilable) = #[t t.Warning
- # # ^ result might be not initialized, so it or an element might be nil
- # # ]#
- # # if not a.isNil:
- # # result[0] = a #[t t.Warning
- # # ^ can't assign nilable to non nilable: it might be nil
- # # #]
- # # proc testNonNilDeref(a: NonNilable) =
- # # echo a.a # ok
- # # # not only calls: we can use partitions for dependencies for field aliases
- # # # so we can detect on change what does this affect or was this mutated between us and the original field
- # # proc testRootAliasField(a: Nilable) =
- # # var aliasA = a
- # # if not a.isNil and not a.field.isNil:
- # # aliasA.field = nil
- # # # a.field = nil
- # # # aliasA = nil
- # # echo a.field.a # [tt.Warning
- # # ^ can't deref a.field, it might be nil
- # # ]#
- proc testAliasChanging(a: Nilable) =
- var b = a
- var aliasA = b
- b = Nilable()
- if not b.isNil:
- echo aliasA.a #[tt.Warning
- ^ can't deref aliasA, it might be nil
- ]#
- # # TODO
- # # proc testAliasUnion(a: Nilable) =
- # # var a2 = a
- # # var b = a2
- # # if a.isNil:
- # # b = Nilable()
- # # a2 = nil
- # # else:
- # # a2 = Nilable()
- # # b = a2
- # # if not b.isNil:
- # # echo a2.a #[ tt.Warning
- # # ^ can't deref a2, it might be nil
- # # ]#
- # # TODO after alias support
- # #proc callVar(a: var Nilable) =
- # # a.field = nil
- # # TODO ptr support
- # # proc testPtrAlias(a: Nilable) =
- # # # pointer to a: hm.
- # # # alias to a?
- # # var ptrA = a.addr # {0, 1}
- # # if not a.isNil: # {0, 1}
- # # ptrA[] = nil # {0, 1} 0: MaybeNil 1: MaybeNil
- # # echo a.a #[ tt.Warning
- # # ^ can't deref a, it might be nil
- # # ]#
- # # TODO field stuff
- # # currently it just doesnt support dot, so accidentally it shows a warning but because that
- # # not alias i think
- # # proc testFieldAlias(a: Nilable) =
- # # var b = a # {0, 1} {2}
- # # if not a.isNil and not a.field.isNil: # {0, 1} {2}
- # # callVar(b) # {0, 1} {2} 0: Safe 1: Safe
- # # echo a.field.a #[ tt.Warning
- # # ^ can't deref a.field, it might be nil
- # # ]#
- # #
- # # proc testUniqueHashTree(a: Nilable): Nilable =
- # # # TODO what would be a clash
- # # var field = 0
- # # if not a.isNil and not a.field.isNil:
- # # # echo a.field.a
- # # echo a[field].a
- # # result = Nilable()
-
- # # proc testSeparateShadowingResult(a: Nilable): Nilable =
- # # result = Nilable()
- # # if not a.isNil:
- # # var result: Nilable = nil
- # # echo result.a
- proc testCStringDeref(a: cstring) =
- echo a[0] #[tt.Warning
- ^ can't deref a, it might be nil
- ]#
- proc testNilablePtr(a: ptr int) =
- if not a.isNil:
- echo a[] # ok
- echo a[] #[tt.Warning
- ^ can't deref a, it might be nil
- ]#
- # # proc testNonNilPtr(a: ptr int not nil) =
- # # echo a[] # ok
- proc raiseCall: NonNilable = #[tt.Warning
- ^ return value is nil
- ]#
- raise newException(ValueError, "raise for test")
- # proc testTryCatch(a: Nilable) =
- # var other = a
- # try:
- # other = raiseCall()
- # except:
- # discard
- # echo other.a #[ tt.Warning
- # ^ can't deref other, it might be nil
- # ]#
- # # proc testTryCatchDetectNoRaise(a: Nilable) =
- # # var other = Nilable()
- # # try:
- # # other = nil
- # # other = a
- # # other = Nilable()
- # # except:
- # # other = nil
- # # echo other.a # ok
- # # proc testTryCatchDetectFinally =
- # # var other = Nilable()
- # # try:
- # # other = nil
- # # other = Nilable()
- # # except:
- # # other = Nilable()
- # # finally:
- # # other = nil
- # # echo other.a # can't deref other: it is nil
- # # proc testTryCatchDetectNilableWithRaise(b: bool) =
- # # var other = Nilable()
- # # try:
- # # if b:
- # # other = nil
- # # else:
- # # other = Nilable()
- # # var other2 = raiseCall()
- # # except:
- # # echo other.a # ok
- # # echo other.a # can't deref a: it might be nil
- # # proc testRaise(a: Nilable) =
- # # if a.isNil:
- # # raise newException(ValueError, "a == nil")
- # # echo a.a # ok
- # # proc testBlockScope(a: Nilable) =
- # # var other = a
- # # block:
- # # var other = Nilable()
- # # echo other.a # ok
- # # echo other.a # can't deref other: it might be nil
- # # ok we can't really get the nil value from here, so should be ok
- # # proc testDirectRaiseCall: NonNilable =
- # # var a = raiseCall()
- # # result = NonNilable()
- # # proc testStmtList =
- # # var a = Nilable()
- # # block:
- # # a = nil
- # # a = Nilable()
- # # echo a.a # ok
- proc callChange(a: Nilable) =
- if not a.isNil:
- a.field = nil
- proc testCallChangeField =
- var a = Nilable()
- a.field = Nilable()
- callChange(a)
- echo a.field.a #[ tt.Warning
- ^ can't deref a.field, it might be nil
- ]#
- proc testReassignVarWithField =
- var a = Nilable()
- a.field = Nilable()
- echo a.field.a # ok
- a = Nilable()
- echo a.field.a #[ tt.Warning
- ^ can't deref a.field, it might be nil
- ]#
- proc testItemDeref(a: var seq[Nilable]) =
- echo a[0].a #[tt.Warning
- ^ can't deref a[0], it might be nil
- ]#
- a[0] = Nilable() # good: now .. if we dont track, how do we know
- echo a[0].a # ok
- echo a[1].a #[tt.Warning
- ^ can't deref a[1], it might be nil
- ]#
- var b = 1
- if a[b].isNil:
- echo a[1].a #[tt.Warning
- ^ can't deref a[1], it might be nil
- ]#
- var c = 0
- echo a[c].a #[tt.Warning
- ^ can't deref a[c], it might be nil
- ]#
- # known false positive
- if not a[b].isNil:
- echo a[b].a #[tt.Warning
- ^ can't deref a[b], it might be nil
- ]#
- const c = 0
- if a[c].isNil:
- echo a[0].a #[tt.Warning
- ^ can't deref a[0], it is nil
- ]#
- a[c] = Nilable()
- echo a[0].a # ok
- # # # proc test10(a: Nilable) =
- # # # if not a.isNil and not a.b.isNil:
- # # # c_memset(globalA.addr, 0, globalA.sizeOf.csize_t)
- # # # globalA = nil
- # # # echo a.a # can't deref a: it might be nil
|