Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stronger enforcement of not nil and requiresInit #13808

Merged
merged 21 commits into from
Apr 1, 2020
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
48b2e10
First steps, the compiler can boot with enforced requiresInit
zah Mar 27, 2020
d8ab0e1
More sophistication; Allow requiresInit to be specified per-field
zah Mar 27, 2020
b9a3b2b
Don't allow 'var x: T' for objects that require initialization
zah Mar 27, 2020
6fbbf9e
Plug another hole: default(T) forbidden for objects requiring initial…
zah Mar 27, 2020
5b3fe7b
Enable the requiresInit checks only for objects
zah Mar 27, 2020
7beaf46
Fix tests/notnil/tnotnil_in_objconstr.nim
zah Mar 27, 2020
c0b9743
Turn the warning for uninitialized (result) variables into errors
zah Mar 29, 2020
ec03190
not nil types are illegal to construct through default(T)
zah Mar 29, 2020
d995226
Hrm, the new errors highlighted some code that seems to be broken
zah Mar 29, 2020
b1290c6
More precise error messages for uninitialized fields in the presence …
zah Mar 29, 2020
85256ed
Perform nil checks during object construction and within compiles()
zah Mar 29, 2020
3205d4e
Close https://github.com/nim-lang/Nim/issues/11428
zah Mar 29, 2020
eda7525
Fix https://github.com/nim-lang/Nim/issues/4907
zah Mar 29, 2020
4ed360e
Fix a CI failure during koch doc
zah Mar 29, 2020
98ccf47
Fix tests/parallel/tguard2.nim
zah Mar 30, 2020
d0b86dd
Replace tfHasRequiresInit with a more accurate mechanism
zah Mar 30, 2020
88ff797
Turn some of the errors back into warnings
zah Apr 1, 2020
805447b
The raises list can now use expressions referencing the generic params
zah Apr 1, 2020
4f6cdd7
Fix tests/types/tparameterizedparent0
zah Apr 1, 2020
e4054cf
revert stdlib changes which are not required anymore
Araq Apr 1, 2020
9754934
Merge branch 'devel' into fix-requiresInit
Araq Apr 1, 2020
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
18 changes: 9 additions & 9 deletions compiler/ast.nim
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,7 @@ type
# needed for the code generator
sfProcvar, # proc can be passed to a proc var
sfDiscriminant, # field is a discriminant in a record/object
sfRequiresInit, # field must be initialized during construction
sfDeprecated, # symbol is deprecated
sfExplain, # provide more diagnostics when this symbol is used
sfError, # usage of symbol should trigger a compile-time error
Expand Down Expand Up @@ -516,9 +517,11 @@ type
tfIterator, # type is really an iterator, not a tyProc
tfPartial, # type is declared as 'partial'
tfNotNil, # type cannot be 'nil'

tfNeedsInit, # type constains a "not nil" constraint somewhere or some
# other type so that it requires initialization
tfRequiresInit, # type constains a "not nil" constraint somewhere or
# a `requiresInit` field, so the default zero init
# is not appropriate
tfNeedsFullInit, # object type marked with {.requiresInit.}
# all fields must be initialized
tfVarIsPtr, # 'var' type is translated like 'ptr' even in C++ mode
tfHasMeta, # type contains "wildcard" sub-types such as generic params
# or other type classes
Expand Down Expand Up @@ -1393,6 +1396,9 @@ proc copyType*(t: PType, owner: PSym, keepId: bool): PType =

proc exactReplica*(t: PType): PType = copyType(t, t.owner, true)

template requiresInit*(t: PType): bool =
t.flags * {tfRequiresInit, tfNotNil} != {}

proc copySym*(s: PSym): PSym =
result = newSym(s.kind, s.name, s.owner, s.info, s.options)
#result.ast = nil # BUGFIX; was: s.ast which made problems
Expand Down Expand Up @@ -1483,12 +1489,6 @@ proc propagateToOwner*(owner, elem: PType; propagateHasAsgn = true) =
if tfNotNil in elem.flags:
if owner.kind in {tyGenericInst, tyGenericBody, tyGenericInvocation}:
owner.flags.incl tfNotNil
elif owner.kind notin HaveTheirOwnEmpty:
owner.flags.incl tfNeedsInit

if tfNeedsInit in elem.flags:
if owner.kind in HaveTheirOwnEmpty: discard
else: owner.flags.incl tfNeedsInit

if elem.isMetaType:
owner.flags.incl tfHasMeta
Expand Down
7 changes: 7 additions & 0 deletions compiler/astalgo.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1041,3 +1041,10 @@ proc isAddrNode*(n: PNode): bool =
if n[0].kind == nkSym and n[0].sym.magic == mAddr: true
else: false
else: false

proc listSymbolNames*(symbols: openArray[PSym]): string =
for sym in symbols:
if result.len > 0:
result.add ", "
result.add sym.name.s

8 changes: 8 additions & 0 deletions compiler/lineinfos.nim
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ type
errGeneralParseError,
errNewSectionExpected,
errInvalidDirectiveX,
errProveInit,
errGenerated,
errUser,
warnCannotOpenFile,
Expand All @@ -36,6 +37,8 @@ type
warnUnusedImportX,
warnInheritFromException,
warnEachIdentIsTuple,
warnUnsafeSetLen,
warnUnsafeDefault,
warnProveInit, warnProveField, warnProveIndex,
warnStaticIndexCheck, warnGcUnsafe, warnGcUnsafe2,
warnUninit, warnGcMem, warnDestructor, warnLockLevel, warnResultShadowed,
Expand Down Expand Up @@ -63,6 +66,7 @@ const
errGeneralParseError: "general parse error",
errNewSectionExpected: "new section expected",
errInvalidDirectiveX: "invalid directive: '$1'",
errProveInit: "Cannot prove that '$1' is initialized.",
errGenerated: "$1",
errUser: "$1",
warnCannotOpenFile: "cannot open '$1'",
Expand All @@ -85,6 +89,9 @@ const
warnUnusedImportX: "imported and not used: '$1'",
warnInheritFromException: "inherit from a more precise exception type like ValueError, IOError or OSError",
warnEachIdentIsTuple: "each identifier is a tuple",
warnUnsafeSetLen: "setLen can potentially expand the sequence, " &
"but the element type '$1' doesn't have a valid default value",
warnUnsafeDefault: "The '$1' type doesn't have a valid default value",
warnProveInit: "Cannot prove that '$1' is initialized. This will become a compile time error in the future.",
warnProveField: "cannot prove that field '$1' is accessible",
warnProveIndex: "cannot prove index '$1' is valid",
Expand Down Expand Up @@ -144,6 +151,7 @@ const
"TypelessParam", "UseBase", "WriteToForeignHeap",
"UnsafeCode", "UnusedImport", "InheritFromException",
"EachIdentIsTuple",
"UnsafeSetLen", "UnsafeDefault",
"ProveInit", "ProveField", "ProveIndex",
"IndexCheck", "GcUnsafe", "GcUnsafe2", "Uninit",
"GcMem", "Destructor", "LockLevel", "ResultShadowed",
Expand Down
21 changes: 14 additions & 7 deletions compiler/pragmas.nim
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ const
wInheritable, wGensym, wInject, wRequiresInit, wUnchecked, wUnion, wPacked,
wBorrow, wGcSafe, wPartial, wExplain, wPackage}
fieldPragmas* = declPragmas + {
wGuard, wBitsize, wCursor} - {wExportNims, wNodecl} # why exclude these?
wGuard, wBitsize, wCursor, wRequiresInit} - {wExportNims, wNodecl} # why exclude these?
varPragmas* = declPragmas + {wVolatile, wRegister, wThreadVar,
wMagic, wHeader, wCompilerProc, wCore, wDynlib,
wNoInit, wCompileTime, wGlobal,
Expand Down Expand Up @@ -637,10 +637,13 @@ proc processPragma(c: PContext, n: PNode, i: int) =

proc pragmaRaisesOrTags(c: PContext, n: PNode) =
proc processExc(c: PContext, x: PNode) =
var t = skipTypes(c.semTypeNode(c, x, nil), skipPtrs)
if t.kind != tyObject:
localError(c.config, x.info, errGenerated, "invalid type for raises/tags list")
x.typ = t
if c.hasUnresolvedArgs(c, x):
x.typ = makeTypeFromExpr(c, x)
else:
var t = skipTypes(c.semTypeNode(c, x, nil), skipPtrs)
if t.kind != tyObject and not t.isMetaType:
localError(c.config, x.info, errGenerated, "invalid type for raises/tags list")
x.typ = t

if n.kind in nkPragmaCallKinds and n.len == 2:
let it = n[1]
Expand Down Expand Up @@ -1087,8 +1090,12 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
else: incl(sym.typ.flags, tfUnion)
of wRequiresInit:
noVal(c, it)
if sym.typ == nil: invalidPragma(c, it)
else: incl(sym.typ.flags, tfNeedsInit)
if sym.kind == skField:
sym.flags.incl sfRequiresInit
elif sym.typ != nil:
incl(sym.typ.flags, tfNeedsFullInit)
else:
invalidPragma(c, it)
of wByRef:
noVal(c, it)
if sym == nil or sym.typ == nil:
Expand Down
6 changes: 5 additions & 1 deletion compiler/sem.nim
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ proc indexTypesMatch(c: PContext, f, a: PType, arg: PNode): PNode
proc semStaticExpr(c: PContext, n: PNode): PNode
proc semStaticType(c: PContext, childNode: PNode, prev: PType): PType
proc semTypeOf(c: PContext; n: PNode): PNode
proc computeRequiresInit(c: PContext, t: PType): bool
proc defaultConstructionError(c: PContext, t: PType, info: TLineInfo)
proc hasUnresolvedArgs(c: PContext, n: PNode): bool
proc isArrayConstr(n: PNode): bool {.inline.} =
result = n.kind == nkBracket and
Expand Down Expand Up @@ -511,13 +513,15 @@ proc myOpen(graph: ModuleGraph; module: PSym): PPassContext =
c.semExpr = semExpr
c.semTryExpr = tryExpr
c.semTryConstExpr = tryConstExpr
c.computeRequiresInit = computeRequiresInit
c.semOperand = semOperand
c.semConstBoolExpr = semConstBoolExpr
c.semOverloadedCall = semOverloadedCall
c.semInferredLambda = semInferredLambda
c.semGenerateInstance = generateInstance
c.semTypeNode = semTypeNode
c.instTypeBoundOp = sigmatch.instTypeBoundOp
c.hasUnresolvedArgs = hasUnresolvedArgs

pushProcCon(c, module)
pushOwner(c, c.module)
Expand Down Expand Up @@ -583,7 +587,7 @@ proc semStmtAndGenerateGenerics(c: PContext, n: PNode): PNode =
result = buildEchoStmt(c, result)
if c.config.cmd == cmdIdeTools:
appendToModule(c.module, result)
trackTopLevelStmt(c, c.module, result)
trackStmt(c, c.module, result, isTopLevel = true)

proc recoverContext(c: PContext) =
# clean up in case of a semantic error: We clean up the stacks, etc. This is
Expand Down
5 changes: 4 additions & 1 deletion compiler/semdata.nim
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ type
# to the user.
efWantStmt, efAllowStmt, efDetermineType, efExplain,
efAllowDestructor, efWantValue, efOperand, efNoSemCheck,
efNoEvaluateGeneric, efInCall, efFromHlo,
efNoEvaluateGeneric, efInCall, efFromHlo, efNoSem2Check,
efNoUndeclared
# Use this if undeclared identifiers should not raise an error during
# overload resolution.
Expand Down Expand Up @@ -103,6 +103,9 @@ type
semExpr*: proc (c: PContext, n: PNode, flags: TExprFlags = {}): PNode {.nimcall.}
semTryExpr*: proc (c: PContext, n: PNode, flags: TExprFlags = {}): PNode {.nimcall.}
semTryConstExpr*: proc (c: PContext, n: PNode): PNode {.nimcall.}
computeRequiresInit*: proc (c: PContext, t: PType): bool {.nimcall.}
hasUnresolvedArgs*: proc (c: PContext, n: PNode): bool

semOperand*: proc (c: PContext, n: PNode, flags: TExprFlags = {}): PNode {.nimcall.}
semConstBoolExpr*: proc (c: PContext, n: PNode): PNode {.nimcall.} # XXX bite the bullet
semOverloadedCall*: proc (c: PContext, n, nOrig: PNode,
Expand Down
8 changes: 7 additions & 1 deletion compiler/semexprs.nim
Original file line number Diff line number Diff line change
Expand Up @@ -2082,7 +2082,10 @@ proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
var err: string
try:
result = semExpr(c, n, flags)
if c.config.errorCounter != oldErrorCount: result = nil
if result != nil and efNoSem2Check notin flags:
trackStmt(c, c.module, result, isTopLevel = false)
if c.config.errorCounter != oldErrorCount:
result = nil
except ERecoverableError:
discard
# undo symbol table changes (as far as it's possible):
Expand Down Expand Up @@ -2644,6 +2647,9 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
return
var typ = semTypeNode(c, n, nil).skipTypes({tyTypeDesc})
result.typ = makeTypeDesc(c, typ)
of nkStmtListType:
let typ = semTypeNode(c, n, nil)
result.typ = makeTypeDesc(c, typ)
of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit:
# check if it is an expression macro:
checkMinSonsLen(n, 1, c.config)
Expand Down
17 changes: 16 additions & 1 deletion compiler/semmagic.nim
Original file line number Diff line number Diff line change
Expand Up @@ -530,4 +530,19 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
result = semQuantifier(c, n)
of mOld:
result = semOld(c, n)
else: result = n
of mSetLengthSeq:
result = n
let seqType = result[1].typ.skipTypes({tyPtr, tyRef, # in case we had auto-dereferencing
tyVar, tyGenericInst, tyOwned, tySink,
tyAlias, tyUserTypeClassInst})
if seqType.kind == tySequence and seqType.base.requiresInit:
message(c.config, n.info, warnUnsafeSetLen, typeToString(seqType.base))
of mDefault:
result = n
c.config.internalAssert result[1].typ.kind == tyTypeDesc
let constructed = result[1].typ.base
if constructed.requiresInit:
message(c.config, n.info, warnUnsafeDefault, typeToString(constructed))
else:
result = n

Loading