From 6edb07091dafd4f0214c4aa2df8fc862d03e0a99 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Sat, 18 Mar 2017 01:44:51 +0200 Subject: fix #4556 This implements a number of new safety checks and error messages when object constructors are used: In case objects: * the compiler will prevent you from initializing fields in conflicting branches * When a field from a particular branch is initialized, the compiler will demand that the discriminator field is also supplied with a maching compile-time value In all objects: * When the "requiresInit" pragma is applied to a type, all fields of the type must be initialized when object construction is used. The code will be simplified in a follow up commit. --- compiler/semdata.nim | 2 +- compiler/semexprs.nim | 219 +++++++++++++++++++++++++++++++++++++------------- 2 files changed, 165 insertions(+), 56 deletions(-) diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 023b85802..5c2427401 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -45,7 +45,7 @@ type inst*: PInstantiation TExprFlag* = enum - efLValue, efWantIterator, efInTypeof, + efLValue, efWantIterator, efWantStatic, efInTypeof, efWantStmt, efAllowStmt, efDetermineType, efExplain, efAllowDestructor, efWantValue, efOperand, efNoSemCheck, efNoProcvarCheck, efNoEvaluateGeneric, efInCall, efFromHlo, diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 5f9263645..032b146d3 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -2097,25 +2097,137 @@ proc isTupleType(n: PNode): bool = return false return true -proc checkInitialized(n: PNode, ids: IntSet, info: TLineInfo) = - case n.kind +type + InitializationResult = 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 + + InitStatus = object + status: InitializationResult + missingFields: seq[int] + conflictingFields: seq[int] + assumptions: seq[(int, PNode)] # field and expected value + +proc mergeInitStatus(existing: var InitializationResult, + newStatus: InitializationResult) = + case newStatus + of initConflict: + existing = initConflict + of initPartial: + if existing in {initUnknown, initFull, initNone}: + existing = initPartial + of initNone: + if existing == initUnknown: + existing = initNone + elif existing == initFull: + existing = initPartial + of initFull: + if existing == initUnknown: + existing = initFull + elif existing == initNone: + existing = initPartial + of initUnknown: + discard + +proc semConstrField(c: PContext, flags: TExprFlags, + field: PSym, initExpr: PNode): PNode = + let fieldId = field.name.id + + for i in 1 .. = 0 and selectedBranch < recNode.len - 1: + let discriminatorVal = semConstrField(c, flags + {efWantStatic}, + discriminator.sym, initExpr) + if discriminatorVal == nil: + localError(initExpr.info, "discrimantor not const") + elif not discriminatorVal.matchesAnyCaseInNkOf(recNode[selectedBranch]): + localError(initExpr.info, "wrong branch taken") + else: + result.status = initNone + let f = semConstrField(c, flags, discriminator.sym, initExpr) + mergeInitStatus(result.status, if f != nil: initFull else: initNone) + of nkSym: - if {tfNotNil, tfNeedsInit} * n.sym.typ.flags != {} and - n.sym.name.id notin ids: - message(info, errGenerated, "field not initialized: " & n.sym.name.s) - else: internalError(info, "checkInitialized") + let e = semConstrField(c, flags, recNode.sym, initExpr) + result.status = if e != nil: initFull else: initNone + + else: + internalAssert false proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = var t = semTypeNode(c, n.sons[0], nil) @@ -2126,46 +2238,43 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = if t.kind != tyObject: localError(n.info, errGenerated, "object constructor needs an object type") return - var objType = t - var ids = initIntSet() - for i in 1..