diff options
author | Zahary Karadjov <zahary@gmail.com> | 2020-03-27 18:43:23 +0200 |
---|---|---|
committer | Andreas Rumpf <rumpf_a@web.de> | 2020-04-01 19:38:44 +0200 |
commit | 7652aede41d81b4e7db2c70ffe462c6fb675d078 (patch) | |
tree | d44427bafa4bbdf19adbfa629aafcd0dc932a2ba | |
parent | a8b6222c862d207a32c104699fc4f0f0a8bced17 (diff) | |
download | Nim-7652aede41d81b4e7db2c70ffe462c6fb675d078.tar.gz |
More sophistication; Allow requiresInit to be specified per-field
-rw-r--r-- | compiler/ast.nim | 3 | ||||
-rw-r--r-- | compiler/pragmas.nim | 10 | ||||
-rw-r--r-- | compiler/semobjconstr.nim | 32 | ||||
-rw-r--r-- | tests/constructors/tinvalid_construction.nim | 31 |
4 files changed, 61 insertions, 15 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim index a4630732b..a7eb2cac5 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -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 diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index e4b72bed7..604ba063f 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -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: diff --git a/compiler/semobjconstr.nim b/compiler/semobjconstr.nim index bc1e85425..ee79f1a67 100644 --- a/compiler/semobjconstr.nim +++ b/compiler/semobjconstr.nim @@ -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) diff --git a/tests/constructors/tinvalid_construction.nim b/tests/constructors/tinvalid_construction.nim index b3e56eec6..aaf06d4f2 100644 --- a/tests/constructors/tinvalid_construction.nim +++ b/tests/constructors/tinvalid_construction.nim @@ -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() |