More sophistication; Allow requiresInit to be specified per-field
This commit is contained in:
parent
a8b6222c86
commit
7652aede41
4 changed files with 61 additions and 15 deletions
|
|
@ -259,6 +259,7 @@ type
|
||||||
# needed for the code generator
|
# needed for the code generator
|
||||||
sfProcvar, # proc can be passed to a proc var
|
sfProcvar, # proc can be passed to a proc var
|
||||||
sfDiscriminant, # field is a discriminant in a record/object
|
sfDiscriminant, # field is a discriminant in a record/object
|
||||||
|
sfRequiresInit, # field must be initialized during construction
|
||||||
sfDeprecated, # symbol is deprecated
|
sfDeprecated, # symbol is deprecated
|
||||||
sfExplain, # provide more diagnostics when this symbol is used
|
sfExplain, # provide more diagnostics when this symbol is used
|
||||||
sfError, # usage of symbol should trigger a compile-time error
|
sfError, # usage of symbol should trigger a compile-time error
|
||||||
|
|
@ -1488,7 +1489,7 @@ proc propagateToOwner*(owner, elem: PType; propagateHasAsgn = true) =
|
||||||
elif owner.kind notin HaveTheirOwnEmpty:
|
elif owner.kind notin HaveTheirOwnEmpty:
|
||||||
owner.flags.incl tfHasRequiresInit
|
owner.flags.incl tfHasRequiresInit
|
||||||
|
|
||||||
if tfRequiresInit in elem.flags:
|
if {tfRequiresInit, tfHasRequiresInit} * elem.flags != {}:
|
||||||
if owner.kind in HaveTheirOwnEmpty: discard
|
if owner.kind in HaveTheirOwnEmpty: discard
|
||||||
else: owner.flags.incl tfHasRequiresInit
|
else: owner.flags.incl tfHasRequiresInit
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,7 @@ const
|
||||||
wInheritable, wGensym, wInject, wRequiresInit, wUnchecked, wUnion, wPacked,
|
wInheritable, wGensym, wInject, wRequiresInit, wUnchecked, wUnion, wPacked,
|
||||||
wBorrow, wGcSafe, wPartial, wExplain, wPackage}
|
wBorrow, wGcSafe, wPartial, wExplain, wPackage}
|
||||||
fieldPragmas* = declPragmas + {
|
fieldPragmas* = declPragmas + {
|
||||||
wGuard, wBitsize, wCursor} - {wExportNims, wNodecl} # why exclude these?
|
wGuard, wBitsize, wCursor, wRequiresInit} - {wExportNims, wNodecl} # why exclude these?
|
||||||
varPragmas* = declPragmas + {wVolatile, wRegister, wThreadVar,
|
varPragmas* = declPragmas + {wVolatile, wRegister, wThreadVar,
|
||||||
wMagic, wHeader, wCompilerProc, wCore, wDynlib,
|
wMagic, wHeader, wCompilerProc, wCore, wDynlib,
|
||||||
wNoInit, wCompileTime, wGlobal,
|
wNoInit, wCompileTime, wGlobal,
|
||||||
|
|
@ -1087,8 +1087,12 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
|
||||||
else: incl(sym.typ.flags, tfUnion)
|
else: incl(sym.typ.flags, tfUnion)
|
||||||
of wRequiresInit:
|
of wRequiresInit:
|
||||||
noVal(c, it)
|
noVal(c, it)
|
||||||
if sym.typ == nil: invalidPragma(c, it)
|
if sym.kind == skField:
|
||||||
else: incl(sym.typ.flags, tfRequiresInit)
|
sym.flags.incl sfRequiresInit
|
||||||
|
elif sym.typ != nil:
|
||||||
|
incl(sym.typ.flags, tfRequiresInit)
|
||||||
|
else:
|
||||||
|
invalidPragma(c, it)
|
||||||
of wByRef:
|
of wByRef:
|
||||||
noVal(c, it)
|
noVal(c, it)
|
||||||
if sym == nil or sym.typ == nil:
|
if sym == nil or sym.typ == nil:
|
||||||
|
|
|
||||||
|
|
@ -136,9 +136,11 @@ proc fieldsPresentInInitExpr(c: PContext, fieldsRecList, initExpr: PNode): strin
|
||||||
if result.len != 0: result.add ", "
|
if result.len != 0: result.add ", "
|
||||||
result.add field.sym.name.s.quoteStr
|
result.add field.sym.name.s.quoteStr
|
||||||
|
|
||||||
proc missingMandatoryFields(c: PContext, fieldsRecList, initExpr: PNode): string =
|
proc missingMandatoryFields(c: PContext, fieldsRecList, initExpr: PNode,
|
||||||
|
requiresFullInit = false): string =
|
||||||
for r in directFieldsInRecList(fieldsRecList):
|
for r in directFieldsInRecList(fieldsRecList):
|
||||||
if {tfNotNil, tfRequiresInit} * r.sym.typ.flags != {}:
|
if requiresFullInit or sfRequiresInit in r.sym.flags or
|
||||||
|
{tfNotNil, tfRequiresInit, tfHasRequiresInit} * r.sym.typ.flags != {}:
|
||||||
let assignment = locateFieldInInitExpr(c, r.sym, initExpr)
|
let assignment = locateFieldInInitExpr(c, r.sym, initExpr)
|
||||||
if assignment == nil:
|
if assignment == nil:
|
||||||
if result.len == 0:
|
if result.len == 0:
|
||||||
|
|
@ -147,19 +149,22 @@ proc missingMandatoryFields(c: PContext, fieldsRecList, initExpr: PNode): string
|
||||||
result.add ", "
|
result.add ", "
|
||||||
result.add r.sym.name.s
|
result.add r.sym.name.s
|
||||||
|
|
||||||
proc checkForMissingFields(c: PContext, recList, initExpr: PNode) =
|
proc checkForMissingFields(c: PContext, recList, initExpr: PNode,
|
||||||
let missing = missingMandatoryFields(c, recList, initExpr)
|
requiresFullInit = false) =
|
||||||
|
let missing = missingMandatoryFields(c, recList, initExpr, requiresFullInit)
|
||||||
if missing.len > 0:
|
if missing.len > 0:
|
||||||
localError(c.config, initExpr.info, "fields not initialized: $1.", [missing])
|
localError(c.config, initExpr.info, "fields not initialized: $1.", [missing])
|
||||||
|
|
||||||
proc semConstructFields(c: PContext, recNode: PNode,
|
proc semConstructFields(c: PContext, recNode: PNode,
|
||||||
initExpr: PNode, flags: TExprFlags): InitStatus =
|
initExpr: PNode, flags: TExprFlags,
|
||||||
|
requiresFullInit = false): InitStatus =
|
||||||
result = initUnknown
|
result = initUnknown
|
||||||
|
|
||||||
case recNode.kind
|
case recNode.kind
|
||||||
of nkRecList:
|
of nkRecList:
|
||||||
for field in recNode:
|
for field in recNode:
|
||||||
let status = semConstructFields(c, field, initExpr, flags)
|
let status = semConstructFields(c, field, initExpr,
|
||||||
|
flags, requiresFullInit)
|
||||||
mergeInitStatus(result, status)
|
mergeInitStatus(result, status)
|
||||||
|
|
||||||
of nkRecCase:
|
of nkRecCase:
|
||||||
|
|
@ -171,7 +176,7 @@ proc semConstructFields(c: PContext, recNode: PNode,
|
||||||
template checkMissingFields(branchNode: PNode) =
|
template checkMissingFields(branchNode: PNode) =
|
||||||
if branchNode != nil:
|
if branchNode != nil:
|
||||||
let fields = branchNode[^1]
|
let fields = branchNode[^1]
|
||||||
checkForMissingFields(c, fields, initExpr)
|
checkForMissingFields(c, fields, initExpr, requiresFullInit)
|
||||||
|
|
||||||
let discriminator = recNode[0]
|
let discriminator = recNode[0]
|
||||||
internalAssert c.config, discriminator.kind == nkSym
|
internalAssert c.config, discriminator.kind == nkSym
|
||||||
|
|
@ -179,7 +184,8 @@ proc semConstructFields(c: PContext, recNode: PNode,
|
||||||
|
|
||||||
for i in 1..<recNode.len:
|
for i in 1..<recNode.len:
|
||||||
let innerRecords = recNode[i][^1]
|
let innerRecords = recNode[i][^1]
|
||||||
let status = semConstructFields(c, innerRecords, initExpr, flags)
|
let status = semConstructFields(c, innerRecords, initExpr,
|
||||||
|
flags, requiresFullInit)
|
||||||
if status notin {initNone, initUnknown}:
|
if status notin {initNone, initUnknown}:
|
||||||
mergeInitStatus(result, status)
|
mergeInitStatus(result, status)
|
||||||
if selectedBranch != -1:
|
if selectedBranch != -1:
|
||||||
|
|
@ -319,16 +325,20 @@ proc semConstructFields(c: PContext, recNode: PNode,
|
||||||
|
|
||||||
proc semConstructType(c: PContext, initExpr: PNode,
|
proc semConstructType(c: PContext, initExpr: PNode,
|
||||||
t: PType, flags: TExprFlags): InitStatus =
|
t: PType, flags: TExprFlags): InitStatus =
|
||||||
var t = t
|
|
||||||
result = initUnknown
|
result = initUnknown
|
||||||
|
var
|
||||||
|
t = t
|
||||||
|
requiresFullInit = tfRequiresInit in t.flags
|
||||||
while true:
|
while true:
|
||||||
let status = semConstructFields(c, t.n, initExpr, flags)
|
let status = semConstructFields(c, t.n, initExpr,
|
||||||
|
flags, requiresFullInit)
|
||||||
mergeInitStatus(result, status)
|
mergeInitStatus(result, status)
|
||||||
if status in {initPartial, initNone, initUnknown}:
|
if status in {initPartial, initNone, initUnknown}:
|
||||||
checkForMissingFields c, t.n, initExpr
|
checkForMissingFields c, t.n, initExpr, requiresFullInit
|
||||||
let base = t[0]
|
let base = t[0]
|
||||||
if base == nil: break
|
if base == nil: break
|
||||||
t = skipTypes(base, skipPtrs)
|
t = skipTypes(base, skipPtrs)
|
||||||
|
requiresFullInit = requiresFullInit or tfRequiresInit in t.flags
|
||||||
|
|
||||||
proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||||
var t = semTypeNode(c, n[0], nil)
|
var t = semTypeNode(c, n[0], nil)
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,9 @@ template accept(x) =
|
||||||
|
|
||||||
template reject(x) =
|
template reject(x) =
|
||||||
static: assert(not compiles(x))
|
static: assert(not compiles(x))
|
||||||
|
|
||||||
{.experimental: "notnil".}
|
{.experimental: "notnil".}
|
||||||
|
|
||||||
type
|
type
|
||||||
TRefObj = ref object
|
TRefObj = ref object
|
||||||
x: int
|
x: int
|
||||||
|
|
@ -26,6 +28,18 @@ type
|
||||||
else:
|
else:
|
||||||
discard
|
discard
|
||||||
|
|
||||||
|
PartialRequiresInit = object
|
||||||
|
a {.requiresInit.}: int
|
||||||
|
b: string
|
||||||
|
|
||||||
|
FullRequiresInit {.requiresInit.} = object
|
||||||
|
a: int
|
||||||
|
b: int
|
||||||
|
|
||||||
|
FullRequiresInitWithParent {.requiresInit.} = object of THasNotNils
|
||||||
|
e: int
|
||||||
|
d: int
|
||||||
|
|
||||||
TObj = object
|
TObj = object
|
||||||
case choice: TChoice
|
case choice: TChoice
|
||||||
of A:
|
of A:
|
||||||
|
|
@ -106,6 +120,23 @@ accept TBaseHasNotNils(a: notNilRef, b: notNilRef, choice: B, indirectNotNils: m
|
||||||
reject TBaseHasNotNils(a: notNilRef, b: notNilRef, choice: B, indirectNotNils: THasNotNilsRef())
|
reject TBaseHasNotNils(a: notNilRef, b: notNilRef, choice: B, indirectNotNils: THasNotNilsRef())
|
||||||
accept TBaseHasNotNils(a: notNilRef, b: notNilRef, choice: B, indirectNotNils: THasNotNilsRef(a: notNilRef, b: notNilRef))
|
accept TBaseHasNotNils(a: notNilRef, b: notNilRef, choice: B, indirectNotNils: THasNotNilsRef(a: notNilRef, b: notNilRef))
|
||||||
|
|
||||||
|
# Accept only instances where the `a` field is present
|
||||||
|
accept PartialRequiresInit(a: 10, b: "x")
|
||||||
|
accept PartialRequiresInit(a: 20)
|
||||||
|
reject PartialRequiresInit(b: "x")
|
||||||
|
reject PartialRequiresInit()
|
||||||
|
|
||||||
|
accept FullRequiresInit(a: 10, b: 20)
|
||||||
|
reject FullRequiresInit(a: 10)
|
||||||
|
reject FullRequiresInit(b: 20)
|
||||||
|
|
||||||
|
accept FullRequiresInitWithParent(a: notNilRef, b: notNilRef, c: notNilRef, e: 10, d: 20)
|
||||||
|
accept FullRequiresInitWithParent(a: notNilRef, b: notNilRef, c: nil, e: 10, d: 20)
|
||||||
|
reject FullRequiresInitWithParent(a: notNilRef, b: nil, c: nil, e: 10, d: 20) # b should not be nil
|
||||||
|
reject FullRequiresInitWithParent(a: notNilRef, b: notNilRef, e: 10, d: 20) # c should not be missing
|
||||||
|
reject FullRequiresInitWithParent(a: notNilRef, b: notNilRef, c: nil, e: 10) # d should not be missing
|
||||||
|
reject FullRequiresInitWithParent()
|
||||||
|
|
||||||
# this will be accepted, because the false outer branch will be taken and the inner A branch
|
# this will be accepted, because the false outer branch will be taken and the inner A branch
|
||||||
accept TNestedChoices()
|
accept TNestedChoices()
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue