diff options
-rw-r--r-- | compiler/astalgo.nim | 7 | ||||
-rw-r--r-- | compiler/semobjconstr.nim | 60 | ||||
-rw-r--r-- | tests/notnil/tnotnil_in_objconstr.nim | 10 |
3 files changed, 39 insertions, 38 deletions
diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim index 968918ba9..bd60ddd7d 100644 --- a/compiler/astalgo.nim +++ b/compiler/astalgo.nim @@ -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 + diff --git a/compiler/semobjconstr.nim b/compiler/semobjconstr.nim index 7222433b3..9d39dd5f8 100644 --- a/compiler/semobjconstr.nim +++ b/compiler/semobjconstr.nim @@ -13,11 +13,12 @@ type ObjConstrContext = object - typ: PType # The constructed type - initExpr: PNode # The init expression (nkObjConstr) - requiresFullInit: bool # A `requiresInit` derived type will - # set this to true while visiting - # parent types. + typ: PType # The constructed type + initExpr: PNode # The init expression (nkObjConstr) + requiresFullInit: bool # A `requiresInit` derived type will + # set this to true while visiting + # parent types. + missingFields: seq[PSym] # Fields that the user failed to specify InitStatus = enum # This indicates the result of object construction initUnknown @@ -143,30 +144,18 @@ 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: PNode, - constrCtx: ObjConstrContext): string = +proc collectMissingFields(c: PContext, fieldsRecList: PNode, + constrCtx: var ObjConstrContext) = for r in directFieldsInRecList(fieldsRecList): if constrCtx.requiresFullInit or sfRequiresInit in r.sym.flags or r.sym.typ.requiresInit: let assignment = locateFieldInInitExpr(c, r.sym, constrCtx.initExpr) if assignment == nil: - if result.len == 0: - result = r.sym.name.s - else: - result.add ", " - result.add r.sym.name.s - -proc checkForMissingFields(c: PContext, recList: PNode, - constrCtx: ObjConstrContext) = - let missing = missingMandatoryFields(c, recList, constrCtx) - if missing.len > 0: - localError(c.config, constrCtx.initExpr.info, - "The $1 type requires the following fields to be initialized: $2.", - [constrCtx.typ.sym.name.s, missing]) + constrCtx.missingFields.add r.sym proc semConstructFields(c: PContext, recNode: PNode, - constrCtx: ObjConstrContext, + constrCtx: var ObjConstrContext, flags: TExprFlags): InitStatus = result = initUnknown @@ -182,10 +171,10 @@ proc semConstructFields(c: PContext, recNode: PNode, let fields = branch[^1] fieldsPresentInInitExpr(c, fields, constrCtx.initExpr) - template checkMissingFields(branchNode: PNode) = + template collectMissingFields(branchNode: PNode) = if branchNode != nil: let fields = branchNode[^1] - checkForMissingFields(c, fields, constrCtx) + collectMissingFields(c, fields, constrCtx) let discriminator = recNode[0] internalAssert c.config, discriminator.kind == nkSym @@ -299,7 +288,7 @@ proc semConstructFields(c: PContext, recNode: PNode, # When a branch is selected with a partial match, some of the fields # that were not initialized may be mandatory. We must check for this: if result == initPartial: - checkMissingFields branchNode + collectMissingFields branchNode else: result = initNone @@ -313,18 +302,18 @@ proc semConstructFields(c: PContext, recNode: PNode, # a result: let defaultValue = newIntLit(c.graph, constrCtx.initExpr.info, 0) let matchedBranch = recNode.pickCaseBranch defaultValue - checkMissingFields matchedBranch + collectMissingFields matchedBranch else: result = initPartial if discriminatorVal.kind == nkIntLit: # When the discriminator is a compile-time value, we also know # which branch will be selected: let matchedBranch = recNode.pickCaseBranch discriminatorVal - if matchedBranch != nil: checkMissingFields matchedBranch + if matchedBranch != nil: collectMissingFields matchedBranch else: # All bets are off. If any of the branches has a mandatory # fields we must produce an error: - for i in 1..<recNode.len: checkMissingFields recNode[i] + for i in 1..<recNode.len: collectMissingFields recNode[i] of nkSym: let field = recNode.sym @@ -337,6 +326,7 @@ proc semConstructFields(c: PContext, recNode: PNode, proc semConstructType(c: PContext, initExpr: PNode, t: PType, flags: TExprFlags): InitStatus = result = initUnknown + var t = t constrCtx = ObjConstrContext(typ: t, initExpr: initExpr, @@ -345,13 +335,20 @@ proc semConstructType(c: PContext, initExpr: PNode, let status = semConstructFields(c, t.n, constrCtx, flags) mergeInitStatus(result, status) if status in {initPartial, initNone, initUnknown}: - checkForMissingFields c, t.n, constrCtx + collectMissingFields c, t.n, constrCtx let base = t[0] if base == nil: break t = skipTypes(base, skipPtrs) constrCtx.requiresFullInit = constrCtx.requiresFullInit or tfRequiresInit in t.flags + # It's possible that the object was not fully initialized while + # specifying a .requiresInit. pragma: + if constrCtx.missingFields.len > 0: + localError(c.config, constrCtx.initExpr.info, + "The $1 type requires the following fields to be initialized: $2.", + [constrCtx.typ.sym.name.s, listSymbolNames(constrCtx.missingFields)]) + proc checkDefaultConstruction(c: PContext, typ: PType, info: TLineInfo) = discard semConstructType(c, newNodeI(nkObjConstr, info), typ, {}) @@ -381,13 +378,6 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = # branches will be reported as an error): let initResult = semConstructType(c, result, t, flags) - # It's possible that the object was not fully initialized while - # specifying a .requiresInit. pragma. - if tfRequiresInit in t.flags and initResult != initFull: - localError(c.config, n.info, - "object type uses the 'requiresInit' pragma, but not all fields " & - "have been initialized.") - # Since we were traversing the object fields, it's possible that # not all of the fields specified in the constructor was visited. # We'll check for such fields here: diff --git a/tests/notnil/tnotnil_in_objconstr.nim b/tests/notnil/tnotnil_in_objconstr.nim index 030c40d32..471150f44 100644 --- a/tests/notnil/tnotnil_in_objconstr.nim +++ b/tests/notnil/tnotnil_in_objconstr.nim @@ -1,13 +1,17 @@ discard """ - errormsg: "The Foo type requires the following fields to be initialized: bar" - line: "13" + errormsg: "The Foo type requires the following fields to be initialized: bar, baz" + line: "17" """ {.experimental: "notnil".} # bug #2355 type - Foo = object + Base = object of RootObj + baz: ref int not nil + + Foo = object of Base foo: ref int bar: ref int not nil + var x: ref int = new(int) # Create instance without initializing the `bar` field var f = Foo(foo: x) |