From 05a0ec4adb5fc5ce1b92ca8693a688586c5b7371 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Fri, 27 Mar 2020 20:22:37 +0200 Subject: Don't allow 'var x: T' for objects that require initialization --- compiler/sem.nim | 18 ++++++++ compiler/semobjconstr.nim | 68 +++++++++++++--------------- compiler/semstmts.nim | 6 ++- tests/constructors/tinvalid_construction.nim | 8 ++++ 4 files changed, 63 insertions(+), 37 deletions(-) diff --git a/compiler/sem.nim b/compiler/sem.nim index 48f767af7..f4d992e25 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -58,6 +58,24 @@ proc isArrayConstr(n: PNode): bool {.inline.} = result = n.kind == nkBracket and n.typ.skipTypes(abstractInst).kind == tyArray +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. + + InitStatus = enum # This indicates the result of object construction + initUnknown + initFull # All of the fields have been initialized + initPartial # Some of the fields have been initialized + initNone # None of the fields have been initialized + initConflict # Fields from different branches have been initialized + +proc semConstructType(c: PContext, initExpr: PNode, + t: PType, flags: TExprFlags): InitStatus + template semIdeForTemplateOrGenericCheck(conf, n, requiresCheck) = # we check quickly if the node is where the cursor is when defined(nimsuggest): diff --git a/compiler/semobjconstr.nim b/compiler/semobjconstr.nim index ee79f1a67..2d226aa33 100644 --- a/compiler/semobjconstr.nim +++ b/compiler/semobjconstr.nim @@ -11,14 +11,6 @@ # included from sem.nim -type - InitStatus = enum - initUnknown - initFull # All of the fields have been initialized - initPartial # Some of the fields have been initialized - initNone # None of the fields have been initialized - initConflict # Fields from different branches have been initialized - proc mergeInitStatus(existing: var InitStatus, newStatus: InitStatus) = case newStatus of initConflict: @@ -136,12 +128,12 @@ 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, - requiresFullInit = false): string = +proc missingMandatoryFields(c: PContext, fieldsRecList: PNode, + constrCtx: ObjConstrContext): string = for r in directFieldsInRecList(fieldsRecList): - if requiresFullInit or sfRequiresInit in r.sym.flags or + if constrCtx.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, constrCtx.initExpr) if assignment == nil: if result.len == 0: result = r.sym.name.s @@ -149,34 +141,35 @@ proc missingMandatoryFields(c: PContext, fieldsRecList, initExpr: PNode, result.add ", " result.add r.sym.name.s -proc checkForMissingFields(c: PContext, recList, initExpr: PNode, - requiresFullInit = false) = - let missing = missingMandatoryFields(c, recList, initExpr, requiresFullInit) +proc checkForMissingFields(c: PContext, recList: PNode, + constrCtx: ObjConstrContext) = + let missing = missingMandatoryFields(c, recList, constrCtx) if missing.len > 0: - localError(c.config, initExpr.info, "fields not initialized: $1.", [missing]) + localError(c.config, constrCtx.initExpr.info, + "The $1 type requires the following fields to be initialized: $2.", + [constrCtx.typ.sym.name.s, missing]) proc semConstructFields(c: PContext, recNode: PNode, - initExpr: PNode, flags: TExprFlags, - requiresFullInit = false): InitStatus = + constrCtx: ObjConstrContext, + flags: TExprFlags): InitStatus = result = initUnknown case recNode.kind of nkRecList: for field in recNode: - let status = semConstructFields(c, field, initExpr, - flags, requiresFullInit) + let status = semConstructFields(c, field, constrCtx, flags) mergeInitStatus(result, status) of nkRecCase: template fieldsPresentInBranch(branchIdx: int): string = let branch = recNode[branchIdx] let fields = branch[^1] - fieldsPresentInInitExpr(c, fields, initExpr) + fieldsPresentInInitExpr(c, fields, constrCtx.initExpr) template checkMissingFields(branchNode: PNode) = if branchNode != nil: let fields = branchNode[^1] - checkForMissingFields(c, fields, initExpr, requiresFullInit) + checkForMissingFields(c, fields, constrCtx) let discriminator = recNode[0] internalAssert c.config, discriminator.kind == nkSym @@ -184,14 +177,13 @@ proc semConstructFields(c: PContext, recNode: PNode, for i in 1..