# # # The Nim Compiler # (c) Copyright 2015 Nim Contributors # # See the file "copying.txt", included in this # distribution, for details about the copyright. # ## This module implements Nim's object construction rules. # 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: existing = newStatus 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 invalidObjConstr(c: PContext, n: PNode) = if n.kind == nkInfix and n[0].kind == nkIdent and n[0].ident.s[0] == ':': localError(c.config, n.info, "incorrect object construction syntax; use a space after the colon") else: localError(c.config, n.info, "incorrect object construction syntax") proc locateFieldInInitExpr(c: PContext, field: PSym, initExpr: PNode): PNode = # Returns the assignment nkExprColonExpr node or nil let fieldId = field.name.id for i in 1.. 0: localError(c.config, initExpr.info, "fields not initialized: $1.", [missing]) proc semConstructFields(c: PContext, recNode: PNode, initExpr: PNode, flags: TExprFlags): InitStatus = result = initUnknown case recNode.kind of nkRecList: for field in recNode: let status = semConstructFields(c, field, initExpr, flags) mergeInitStatus(result, status) of nkRecCase: template fieldsPresentInBranch(branchIdx: int): string = let branch = recNode[branchIdx] let fields = branch[^1] fieldsPresentInInitExpr(c, fields, initExpr) template checkMissingFields(branchNode: PNode) = if branchNode != nil: let fields = branchNode[^1] checkForMissingFields(c, fields, initExpr) let discriminator = recNode[0] internalAssert c.config, discriminator.kind == nkSym var selectedBranch = -1 for i in 1.. MaxSetElements or lengthOrd(c.config, recNode[0].typ) > MaxSetElements): localError(c.config, discriminatorVal.info, "branch initialization with a runtime discriminator only " & "supports ordinal types with 2^16 elements or less.") if discriminatorVal == nil: badDiscriminatorError() elif discriminatorVal.kind == nkSym: let (ctorCase, ctorIdx) = findUsefulCaseContext(c, discriminatorVal) if ctorCase == nil: if discriminatorVal.typ.kind == tyRange: let rangeVals = c.getIntSetOfType(discriminatorVal.typ) let recBranchVals = branchVals(c, recNode, selectedBranch, false) let diff = rangeVals - recBranchVals if diff.len != 0: valuesInConflictError(diff) else: badDiscriminatorError() elif discriminatorVal.sym.kind notin {skLet, skParam} or discriminatorVal.sym.typ.kind == tyVar: localError(c.config, discriminatorVal.info, "runtime discriminator must be immutable if branch fields are " & "initialized, a 'let' binding is required.") elif ctorCase[ctorIdx].kind == nkElifBranch: localError(c.config, discriminatorVal.info, "branch initialization " & "with a runtime discriminator is not supported inside of an " & "`elif` branch.") else: var ctorBranchVals = branchVals(c, ctorCase, ctorIdx, true) recBranchVals = branchVals(c, recNode, selectedBranch, false) branchValsDiff = ctorBranchVals - recBranchVals if branchValsDiff.len != 0: valuesInConflictError(branchValsDiff) else: var failedBranch = -1 if branchNode.kind != nkElse: if not branchNode.caseBranchMatchesExpr(discriminatorVal): failedBranch = selectedBranch else: # With an else clause, check that all other branches don't match: for i in 1..