# # # 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 from sugar import dup type ObjConstrContext = object typ: PType # The constructed type initExpr: PNode # The init expression (nkObjConstr) needsFullInit: 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 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.. MaxSetElements or lengthOrd(c.config, n[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, n, 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 in {tyVar}: if c.inUncheckedAssignSection == 0: 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, n, 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.. 0 proc defaultConstructionError(c: PContext, t: PType, info: TLineInfo) = var objType = t while objType.kind notin {tyObject, tyDistinct}: objType = objType.lastSon assert objType != nil if objType.kind == tyObject: var constrCtx = initConstrContext(objType, newNodeI(nkObjConstr, info)) let initResult = semConstructTypeAux(c, constrCtx, {}) assert constrCtx.missingFields.len > 0 localError(c.config, info, "The $1 type doesn't have a default value. The following fields must " & "be initialized: $2." % [typeToString(t), listSymbolNames(constrCtx.missingFields)]) elif objType.kind == tyDistinct: localError(c.config, info, "The $1 distinct type doesn't have a default value." % typeToString(t)) else: assert false, "Must not enter here." proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = var t = semTypeNode(c, n[0], nil) result = newNodeIT(nkObjConstr, n.info, t) for child in n: result.add child if t == nil: return localErrorNode(c, result, "object constructor needs an object type") t = skipTypes(t, {tyGenericInst, tyAlias, tySink, tyOwned}) if t.kind == tyRef: t = skipTypes(t[0], {tyGenericInst, tyAlias, tySink, tyOwned}) if optOwnedRefs in c.config.globalOptions: result.typ = makeVarType(c, result.typ, tyOwned) # we have to watch out, there are also 'owned proc' types that can be used # multiple times as long as they don't have closures. result.typ.flags.incl tfHasOwned if t.kind != tyObject: return localErrorNode(c, result, "object constructor needs an object type".dup(addDeclaredLoc(c.config, t))) # Check if the object is fully initialized by recursively testing each # field (if this is a case object, initialized fields in two different # branches will be reported as an error): var constrCtx = initConstrContext(t, result) let initResult = semConstructTypeAux(c, constrCtx, flags) var hasError = false # needed to split error detect/report for better msgs # It's possible that the object was not fully initialized while # specifying a .requiresInit. pragma: if constrCtx.missingFields.len > 0: hasError = true localError(c.config, result.info, "The $1 type requires the following fields to be initialized: $2." % [t.sym.name.s, listSymbolNames(constrCtx.missingFields)]) # 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: for i in 1..