mirror of
https://github.com/nim-lang/Nim.git
synced 2026-04-26 01:04:00 +00:00
Nil type check implementation (#15287)
* Nil checking * Enable current older not nil checking again, run new checking only under flag, skip our test * Enable tests, work on try/except and bugs, fix notnil tests * Enable strictNotNil tests (currently with lowercase category) and add some expected output * Work on try/except/finally: still some things unclear and a lot of code can raise out of try * Fix the notnil build by going back to the old version of a test which I shouldn't have changed * Fix test : use action compile * Work on mutation and aliasing: not finished * Render var parititions graph, try to understand it, fix a nilcheck if bug * Rebase, progress on working with partitions * Improve time logic * Fix some bugs, use graph indices instead of symbol in nil map * Fix bugs, test simpler ident aliasing for now, support two mutation levels * Support ContentMutation and ReAssignment: for now just detect possible re assignment for var parameters of calls * Enable several simple passing tests * Cleanup a bit, fix condition/branch infix-related bug * Remove some files, address some comments by Araq * Use internalError and no quit for now * Separate tests with expected warnings and with expected ok, fix a bug with if with a single branch related to copyMap * Fix new data structures, bugs: make tests pass, disable some for now * Work on fixing errors with non-sym nodes, aliasing: tests fail * Work on alias support: simple set-based logic, todo more tests and ref sets? * Use ref sets: TODO can we think of handle seq-s similar to varpartitions' Araq ones * Handle defers in one place, stop raising in reverse to make an async test compile with strictNotNil, add a commented out test * Dot expressions: call/reassignment. Other refactorings and distinct, SeqOfDistinct support. Checkout an older varpartitions * Work on field tracking * Backup : trying to fix bugs when running some stdlib stuff for running an async test * Start a section about strict not nil checking in experimental manual * Fix experimental strict not nil manual section and move it to another file based on Araq feedback * Fix unstructured flow and double warning problems, fix manual, cleanup * Fix if/elif/else : take in account structure according to Araq feedback * Refactor a bit * Work on bracket expr support, re-enable tests, clarify in manual/tests/implementation static index support for now * Work on compiling stdlib and compiler with strictNotNil * Small fixes to the manual for strictNotNil * Fix idgen for strict check nil rebase * Enable some simple tests, remove old stuff, comment out code/print * Copy the original varpartitions source instead of my changes * Remove some files
This commit is contained in:
@@ -1004,7 +1004,7 @@ const
|
||||
ConstantDataTypes*: TTypeKinds = {tyArray, tySet,
|
||||
tyTuple, tySequence}
|
||||
NilableTypes*: TTypeKinds = {tyPointer, tyCString, tyRef, tyPtr,
|
||||
tyProc, tyError}
|
||||
tyProc, tyError} # TODO
|
||||
PtrLikeKinds*: TTypeKinds = {tyPointer, tyPtr} # for VM
|
||||
ExportableSymKinds* = {skVar, skConst, skProc, skFunc, skMethod, skType,
|
||||
skIterator,
|
||||
@@ -1387,6 +1387,7 @@ proc newType*(kind: TTypeKind, id: ItemId; owner: PSym): PType =
|
||||
echo "KNID ", kind
|
||||
writeStackTrace()
|
||||
|
||||
|
||||
proc mergeLoc(a: var TLoc, b: TLoc) =
|
||||
if a.k == low(typeof(a.k)): a.k = b.k
|
||||
if a.storage == low(typeof(a.storage)): a.storage = b.storage
|
||||
@@ -1952,9 +1953,13 @@ proc canRaise*(fn: PNode): bool =
|
||||
elif fn.kind == nkSym and fn.sym.magic == mEcho:
|
||||
result = true
|
||||
else:
|
||||
result = fn.typ != nil and fn.typ.n != nil and ((fn.typ.n[0].len < effectListLen) or
|
||||
(fn.typ.n[0][exceptionEffects] != nil and
|
||||
fn.typ.n[0][exceptionEffects].safeLen > 0))
|
||||
# TODO check for n having sons? or just return false for now if not
|
||||
if fn.typ != nil and fn.typ.n != nil and fn.typ.n[0].kind == nkSym:
|
||||
result = false
|
||||
else:
|
||||
result = fn.typ != nil and fn.typ.n != nil and ((fn.typ.n[0].len < effectListLen) or
|
||||
(fn.typ.n[0][exceptionEffects] != nil and
|
||||
fn.typ.n[0][exceptionEffects].safeLen > 0))
|
||||
|
||||
proc toHumanStrImpl[T](kind: T, num: static int): string =
|
||||
result = $kind
|
||||
|
||||
@@ -56,7 +56,7 @@ type
|
||||
warnLockLevel = "LockLevel", warnResultShadowed = "ResultShadowed",
|
||||
warnInconsistentSpacing = "Spacing", warnCaseTransition = "CaseTransition",
|
||||
warnCycleCreated = "CycleCreated", warnObservableStores = "ObservableStores",
|
||||
warnUser = "User",
|
||||
warnUser = "User", warnStrictNotNil = "StrictNotNil",
|
||||
|
||||
hintSuccess = "Success", hintSuccessX = "SuccessX", hintCC = "CC",
|
||||
hintLineTooLong = "LineTooLong", hintXDeclaredButNotUsed = "XDeclaredButNotUsed",
|
||||
@@ -129,6 +129,7 @@ const
|
||||
warnCycleCreated: "$1",
|
||||
warnObservableStores: "observable stores to '$1'",
|
||||
warnUser: "$1",
|
||||
warnStrictNotNil: "$1",
|
||||
hintSuccess: "operation successful: $#",
|
||||
# keep in sync with `testament.isSuccess`
|
||||
hintSuccessX: "${loc} lines; ${sec}s; $mem; $build build; proj: $project; out: $output",
|
||||
|
||||
1381
compiler/nilcheck.nim
Normal file
1381
compiler/nilcheck.nim
Normal file
File diff suppressed because it is too large
Load Diff
@@ -176,7 +176,8 @@ type
|
||||
## Note: this feature can't be localized with {.push.}
|
||||
vmopsDanger,
|
||||
strictFuncs,
|
||||
views
|
||||
views,
|
||||
strictNotNil
|
||||
|
||||
LegacyFeature* = enum
|
||||
allowSemcheckedAstModification,
|
||||
|
||||
@@ -153,6 +153,7 @@ proc collectMissingFields(c: PContext, fieldsRecList: PNode,
|
||||
if assignment == nil:
|
||||
constrCtx.missingFields.add r.sym
|
||||
|
||||
|
||||
proc semConstructFields(c: PContext, n: PNode,
|
||||
constrCtx: var ObjConstrContext,
|
||||
flags: TExprFlags): InitStatus =
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
import
|
||||
intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees,
|
||||
wordrecg, strutils, options, guards, lineinfos, semfold, semdata,
|
||||
modulegraphs, varpartitions, typeallowed
|
||||
modulegraphs, varpartitions, typeallowed, nilcheck
|
||||
|
||||
when defined(useDfa):
|
||||
import dfa
|
||||
@@ -1344,7 +1344,10 @@ proc trackProc*(c: PContext; s: PSym, body: PNode) =
|
||||
when defined(useDfa):
|
||||
if s.name.s == "testp":
|
||||
dataflowAnalysis(s, body)
|
||||
|
||||
when false: trackWrites(s, body)
|
||||
if strictNotNil in c.features and s.kind == skProc:
|
||||
checkNil(s, body, g.config, c.idgen)
|
||||
|
||||
proc trackStmt*(c: PContext; module: PSym; n: PNode, isTopLevel: bool) =
|
||||
if n.kind in {nkPragma, nkMacroDef, nkTemplateDef, nkProcDef, nkFuncDef,
|
||||
|
||||
@@ -917,6 +917,7 @@ proc semAnyRef(c: PContext; n: PNode; kind: TTypeKind; prev: PType): PType =
|
||||
# check every except the last is an object:
|
||||
for i in isCall..<n.len-1:
|
||||
let ni = n[i]
|
||||
# echo "semAnyRef ", "n: ", n, "i: ", i, "ni: ", ni
|
||||
if ni.kind == nkNilLit:
|
||||
isNilable = true
|
||||
else:
|
||||
@@ -933,7 +934,7 @@ proc semAnyRef(c: PContext; n: PNode; kind: TTypeKind; prev: PType): PType =
|
||||
addSonSkipIntLit(result, t, c.idgen)
|
||||
if tfPartial in result.flags:
|
||||
if result.lastSon.kind == tyObject: incl(result.lastSon.flags, tfPartial)
|
||||
#if not isNilable: result.flags.incl tfNotNil
|
||||
# if not isNilable: result.flags.incl tfNotNil
|
||||
case wrapperKind
|
||||
of tyOwned:
|
||||
if optOwnedRefs in c.config.globalOptions:
|
||||
@@ -1563,6 +1564,8 @@ proc semTypeClass(c: PContext, n: PNode, prev: PType): PType =
|
||||
if modifier != tyNone:
|
||||
dummyName = param[0]
|
||||
dummyType = c.makeTypeWithModifier(modifier, candidateTypeSlot)
|
||||
# if modifier == tyRef:
|
||||
# dummyType.flags.incl tfNotNil
|
||||
if modifier == tyTypeDesc:
|
||||
dummyType.flags.incl tfConceptMatchedTypeSym
|
||||
dummyType.flags.incl tfCheckedForDestructor
|
||||
@@ -1747,9 +1750,11 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
||||
if n[2].kind != nkNilLit:
|
||||
localError(c.config, n.info,
|
||||
"Invalid syntax. When used with a type, 'not' can be followed only by 'nil'")
|
||||
if notnil notin c.features:
|
||||
if notnil notin c.features and strictNotNil notin c.features:
|
||||
localError(c.config, n.info,
|
||||
"enable the 'not nil' annotation with {.experimental: \"notnil\".}")
|
||||
"enable the 'not nil' annotation with {.experimental: \"notnil\".} or " &
|
||||
" the `strict not nil` annotation with {.experimental: \"strictNotNil\".} " &
|
||||
" the \"notnil\" one is going to be deprecated, so please use \"strictNotNil\"")
|
||||
let resolvedType = result.skipTypes({tyGenericInst, tyAlias, tySink, tyOwned})
|
||||
case resolvedType.kind
|
||||
of tyGenericParam, tyTypeDesc, tyFromExpr:
|
||||
|
||||
@@ -12,8 +12,9 @@
|
||||
import
|
||||
hashes, ast, astalgo, types
|
||||
|
||||
proc hashTree(n: PNode): Hash =
|
||||
if n == nil: return
|
||||
proc hashTree*(n: PNode): Hash =
|
||||
if n.isNil:
|
||||
return
|
||||
result = ord(n.kind)
|
||||
case n.kind
|
||||
of nkEmpty, nkNilLit, nkType:
|
||||
@@ -21,7 +22,7 @@ proc hashTree(n: PNode): Hash =
|
||||
of nkIdent:
|
||||
result = result !& n.ident.h
|
||||
of nkSym:
|
||||
result = result !& n.sym.name.h
|
||||
result = result !& n.sym.id
|
||||
of nkCharLit..nkUInt64Lit:
|
||||
if (n.intVal >= low(int)) and (n.intVal <= high(int)):
|
||||
result = result !& int(n.intVal)
|
||||
@@ -33,6 +34,9 @@ proc hashTree(n: PNode): Hash =
|
||||
else:
|
||||
for i in 0..<n.len:
|
||||
result = result !& hashTree(n[i])
|
||||
result = !$result
|
||||
#echo "hashTree ", result
|
||||
#echo n
|
||||
|
||||
proc treesEquivalent(a, b: PNode): bool =
|
||||
if a == b:
|
||||
|
||||
@@ -904,4 +904,4 @@ proc computeCursors*(s: PSym; n: PNode; config: ConfigRef) =
|
||||
discard "cannot cursor into a graph that is mutated"
|
||||
else:
|
||||
v.sym.flags.incl sfCursor
|
||||
#echo "this is now a cursor ", v.sym, " ", par.s[rid].flags, " ", config $ v.sym.info
|
||||
#echo "this is now a cursor ", v.sym, " ", par.s[rid].flags, " ", config $ v.sym.info
|
||||
|
||||
Reference in New Issue
Block a user