mirror of
https://github.com/nim-lang/Nim.git
synced 2025-12-28 17:04:41 +00:00
More sophistication; Allow requiresInit to be specified per-field
This commit is contained in:
committed by
Andreas Rumpf
parent
a8b6222c86
commit
7652aede41
@@ -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
|
||||
@@ -1488,7 +1489,7 @@ proc propagateToOwner*(owner, elem: PType; propagateHasAsgn = true) =
|
||||
elif owner.kind notin HaveTheirOwnEmpty:
|
||||
owner.flags.incl tfHasRequiresInit
|
||||
|
||||
if tfRequiresInit in elem.flags:
|
||||
if {tfRequiresInit, tfHasRequiresInit} * elem.flags != {}:
|
||||
if owner.kind in HaveTheirOwnEmpty: discard
|
||||
else: owner.flags.incl tfHasRequiresInit
|
||||
|
||||
|
||||
@@ -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,
|
||||
@@ -1087,8 +1087,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, tfRequiresInit)
|
||||
if sym.kind == skField:
|
||||
sym.flags.incl sfRequiresInit
|
||||
elif sym.typ != nil:
|
||||
incl(sym.typ.flags, tfRequiresInit)
|
||||
else:
|
||||
invalidPragma(c, it)
|
||||
of wByRef:
|
||||
noVal(c, it)
|
||||
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 ", "
|
||||
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):
|
||||
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)
|
||||
if assignment == nil:
|
||||
if result.len == 0:
|
||||
@@ -147,19 +149,22 @@ proc missingMandatoryFields(c: PContext, fieldsRecList, initExpr: PNode): string
|
||||
result.add ", "
|
||||
result.add r.sym.name.s
|
||||
|
||||
proc checkForMissingFields(c: PContext, recList, initExpr: PNode) =
|
||||
let missing = missingMandatoryFields(c, recList, initExpr)
|
||||
proc checkForMissingFields(c: PContext, recList, initExpr: PNode,
|
||||
requiresFullInit = false) =
|
||||
let missing = missingMandatoryFields(c, recList, initExpr, requiresFullInit)
|
||||
if missing.len > 0:
|
||||
localError(c.config, initExpr.info, "fields not initialized: $1.", [missing])
|
||||
|
||||
proc semConstructFields(c: PContext, recNode: PNode,
|
||||
initExpr: PNode, flags: TExprFlags): InitStatus =
|
||||
initExpr: PNode, flags: TExprFlags,
|
||||
requiresFullInit = false): InitStatus =
|
||||
result = initUnknown
|
||||
|
||||
case recNode.kind
|
||||
of nkRecList:
|
||||
for field in recNode:
|
||||
let status = semConstructFields(c, field, initExpr, flags)
|
||||
let status = semConstructFields(c, field, initExpr,
|
||||
flags, requiresFullInit)
|
||||
mergeInitStatus(result, status)
|
||||
|
||||
of nkRecCase:
|
||||
@@ -171,7 +176,7 @@ proc semConstructFields(c: PContext, recNode: PNode,
|
||||
template checkMissingFields(branchNode: PNode) =
|
||||
if branchNode != nil:
|
||||
let fields = branchNode[^1]
|
||||
checkForMissingFields(c, fields, initExpr)
|
||||
checkForMissingFields(c, fields, initExpr, requiresFullInit)
|
||||
|
||||
let discriminator = recNode[0]
|
||||
internalAssert c.config, discriminator.kind == nkSym
|
||||
@@ -179,7 +184,8 @@ proc semConstructFields(c: PContext, recNode: PNode,
|
||||
|
||||
for i in 1..<recNode.len:
|
||||
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}:
|
||||
mergeInitStatus(result, status)
|
||||
if selectedBranch != -1:
|
||||
@@ -319,16 +325,20 @@ proc semConstructFields(c: PContext, recNode: PNode,
|
||||
|
||||
proc semConstructType(c: PContext, initExpr: PNode,
|
||||
t: PType, flags: TExprFlags): InitStatus =
|
||||
var t = t
|
||||
result = initUnknown
|
||||
var
|
||||
t = t
|
||||
requiresFullInit = tfRequiresInit in t.flags
|
||||
while true:
|
||||
let status = semConstructFields(c, t.n, initExpr, flags)
|
||||
let status = semConstructFields(c, t.n, initExpr,
|
||||
flags, requiresFullInit)
|
||||
mergeInitStatus(result, status)
|
||||
if status in {initPartial, initNone, initUnknown}:
|
||||
checkForMissingFields c, t.n, initExpr
|
||||
checkForMissingFields c, t.n, initExpr, requiresFullInit
|
||||
let base = t[0]
|
||||
if base == nil: break
|
||||
t = skipTypes(base, skipPtrs)
|
||||
requiresFullInit = requiresFullInit or tfRequiresInit in t.flags
|
||||
|
||||
proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||
var t = semTypeNode(c, n[0], nil)
|
||||
|
||||
@@ -3,7 +3,9 @@ template accept(x) =
|
||||
|
||||
template reject(x) =
|
||||
static: assert(not compiles(x))
|
||||
|
||||
{.experimental: "notnil".}
|
||||
|
||||
type
|
||||
TRefObj = ref object
|
||||
x: int
|
||||
@@ -26,6 +28,18 @@ type
|
||||
else:
|
||||
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
|
||||
case choice: TChoice
|
||||
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())
|
||||
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
|
||||
accept TNestedChoices()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user