diff options
Diffstat (limited to 'compiler/semobjconstr.nim')
-rw-r--r-- | compiler/semobjconstr.nim | 510 |
1 files changed, 369 insertions, 141 deletions
diff --git a/compiler/semobjconstr.nim b/compiler/semobjconstr.nim index 8b639806d..048053115 100644 --- a/compiler/semobjconstr.nim +++ b/compiler/semobjconstr.nim @@ -11,14 +11,29 @@ # included from sem.nim +from std/sugar import dup + type - InitStatus = enum + 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 + checkDefault: bool # Checking defaults + + 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 semConstructFields(c: PContext, n: PNode, constrCtx: var ObjConstrContext, + flags: TExprFlags): tuple[status: InitStatus, defaults: seq[PNode]] + proc mergeInitStatus(existing: var InitStatus, newStatus: InitStatus) = case newStatus of initConflict: @@ -47,14 +62,13 @@ proc invalidObjConstr(c: PContext, n: PNode) = proc locateFieldInInitExpr(c: PContext, field: PSym, initExpr: PNode): PNode = # Returns the assignment nkExprColonExpr node or nil + result = nil let fieldId = field.name.id - for i in 1 ..< initExpr.len: + for i in 1..<initExpr.len: let assignment = initExpr[i] if assignment.kind != nkExprColonExpr: invalidObjConstr(c, assignment) - continue - - if fieldId == considerQuotedIdent(c, assignment[0]).id: + elif fieldId == considerQuotedIdent(c, assignment[0]).id: return assignment proc semConstrField(c: PContext, flags: TExprFlags, @@ -62,37 +76,56 @@ proc semConstrField(c: PContext, flags: TExprFlags, let assignment = locateFieldInInitExpr(c, field, initExpr) if assignment != nil: if nfSem in assignment.flags: return assignment[1] - if not fieldVisible(c, field): + if nfSkipFieldChecking in assignment[1].flags: + discard + elif not fieldVisible(c, field): localError(c.config, initExpr.info, "the field '$1' is not accessible." % [field.name.s]) return - var initValue = semExprFlagDispatched(c, assignment[1], flags) + var initValue = semExprFlagDispatched(c, assignment[1], flags, field.typ) if initValue != nil: - initValue = fitNode(c, field.typ, initValue, assignment.info) - assignment.sons[0] = newSymNode(field) - assignment.sons[1] = initValue + initValue = fitNodeConsiderViewType(c, field.typ, initValue, assignment.info) + initValue.flags.incl nfSkipFieldChecking + assignment[0] = newSymNode(field) + assignment[1] = initValue assignment.flags.incl nfSem - return initValue - -proc caseBranchMatchesExpr(branch, matched: PNode): bool = - for i in 0 .. branch.len-2: - if branch[i].kind == nkRange: - if overlap(branch[i], matched): return true - elif exprStructuralEquivalent(branch[i], matched): - return true - - return false + result = initValue + else: + result = nil + +proc branchVals(c: PContext, caseNode: PNode, caseIdx: int, + isStmtBranch: bool): IntSet = + if caseNode[caseIdx].kind == nkOfBranch: + result = initIntSet() + for val in processBranchVals(caseNode[caseIdx]): + result.incl(val) + else: + result = c.getIntSetOfType(caseNode[0].typ) + for i in 1..<caseNode.len-1: + for val in processBranchVals(caseNode[i]): + result.excl(val) + +proc findUsefulCaseContext(c: PContext, discrimator: PNode): (PNode, int) = + result = (nil, 0) + for i in countdown(c.p.caseContext.high, 0): + let + (caseNode, index) = c.p.caseContext[i] + skipped = caseNode[0].skipHidden + if skipped.kind == nkSym and skipped.sym == discrimator.sym: + return (caseNode, index) proc pickCaseBranch(caseExpr, matched: PNode): PNode = # XXX: Perhaps this proc already exists somewhere let endsWithElse = caseExpr[^1].kind == nkElse - for i in 1 .. caseExpr.len - 1 - int(endsWithElse): + for i in 1..<caseExpr.len - int(endsWithElse): if caseExpr[i].caseBranchMatchesExpr(matched): return caseExpr[i] if endsWithElse: - return caseExpr[^1] + result = caseExpr[^1] + else: + result = nil iterator directFieldsInRecList(recList: PNode): PNode = # XXX: We can remove this case by making all nkOfBranch nodes @@ -103,188 +136,371 @@ iterator directFieldsInRecList(recList: PNode): PNode = else: doAssert recList.kind == nkRecList for field in recList: - if field.kind != nkSym: continue - yield field + if field.kind == nkSym: + yield field template quoteStr(s: string): string = "'" & s & "'" proc fieldsPresentInInitExpr(c: PContext, fieldsRecList, initExpr: PNode): string = result = "" for field in directFieldsInRecList(fieldsRecList): - let assignment = locateFieldInInitExpr(c, field.sym, initExpr) - if assignment != nil: + if locateFieldInInitExpr(c, field.sym, initExpr) != nil: if result.len != 0: result.add ", " result.add field.sym.name.s.quoteStr -proc missingMandatoryFields(c: PContext, fieldsRecList, initExpr: PNode): string = - for r in directFieldsInRecList(fieldsRecList): - if {tfNotNil, tfNeedsInit} * r.sym.typ.flags != {}: - let assignment = locateFieldInInitExpr(c, r.sym, initExpr) - if assignment == nil: - if result == nil: - result = r.sym.name.s - else: - result.add ", " - result.add r.sym.name.s - -proc checkForMissingFields(c: PContext, recList, initExpr: PNode) = - let missing = missingMandatoryFields(c, recList, initExpr) - if missing != nil: - localError(c.config, initExpr.info, "fields not initialized: $1.", [missing]) +proc locateFieldInDefaults(sym: PSym, defaults: seq[PNode]): bool = + result = false + for d in defaults: + if sym.id == d[0].sym.id: + return true -proc semConstructFields(c: PContext, recNode: PNode, - initExpr: PNode, flags: TExprFlags): InitStatus = - result = initUnknown +proc collectMissingFields(c: PContext, fieldsRecList: PNode, + constrCtx: var ObjConstrContext, defaults: seq[PNode] + ): seq[PSym] = + result = @[] + for r in directFieldsInRecList(fieldsRecList): + let assignment = locateFieldInInitExpr(c, r.sym, constrCtx.initExpr) + if assignment == nil and not locateFieldInDefaults(r.sym, defaults): + if constrCtx.needsFullInit or + sfRequiresInit in r.sym.flags or + r.sym.typ.requiresInit: + constrCtx.missingFields.add r.sym + else: + result.add r.sym - case recNode.kind +proc collectMissingCaseFields(c: PContext, branchNode: PNode, + constrCtx: var ObjConstrContext, defaults: seq[PNode]): seq[PSym] = + if branchNode != nil: + let fieldsRecList = branchNode[^1] + result = collectMissingFields(c, fieldsRecList, constrCtx, defaults) + else: + result = @[] + +proc collectOrAddMissingCaseFields(c: PContext, branchNode: PNode, + constrCtx: var ObjConstrContext, defaults: var seq[PNode]) = + let res = collectMissingCaseFields(c, branchNode, constrCtx, defaults) + for sym in res: + let asgnType = newType(tyTypeDesc, c.idgen, sym.typ.owner) + let recTyp = sym.typ.skipTypes(defaultFieldsSkipTypes) + rawAddSon(asgnType, recTyp) + let asgnExpr = newTree(nkCall, + newSymNode(getSysMagic(c.graph, constrCtx.initExpr.info, "zeroDefault", mZeroDefault)), + newNodeIT(nkType, constrCtx.initExpr.info, asgnType) + ) + asgnExpr.flags.incl nfSkipFieldChecking + asgnExpr.typ = recTyp + defaults.add newTree(nkExprColonExpr, newSymNode(sym), asgnExpr) + +proc collectBranchFields(c: PContext, n: PNode, discriminatorVal: PNode, + constrCtx: var ObjConstrContext, flags: TExprFlags) = + # All bets are off. If any of the branches has a mandatory + # fields we must produce an error: + for i in 1..<n.len: + let branchNode = n[i] + if branchNode != nil: + let oldCheckDefault = constrCtx.checkDefault + constrCtx.checkDefault = true + let (_, defaults) = semConstructFields(c, branchNode[^1], constrCtx, flags) + constrCtx.checkDefault = oldCheckDefault + if len(defaults) > 0: + localError(c.config, discriminatorVal.info, "branch initialization " & + "with a runtime discriminator is not supported " & + "for a branch whose fields have default values.") + discard collectMissingCaseFields(c, n[i], constrCtx, @[]) + +proc semConstructFields(c: PContext, n: PNode, constrCtx: var ObjConstrContext, + flags: TExprFlags): tuple[status: InitStatus, defaults: seq[PNode]] = + result = (initUnknown, @[]) + case n.kind of nkRecList: - for field in recNode: - let status = semConstructFields(c, field, initExpr, flags) - mergeInitStatus(result, status) - + for field in n: + let (subSt, subDf) = semConstructFields(c, field, constrCtx, flags) + result.status.mergeInitStatus subSt + result.defaults.add subDf of nkRecCase: template fieldsPresentInBranch(branchIdx: int): string = - let branch = recNode[branchIdx] - let fields = branch[branch.len - 1] - fieldsPresentInInitExpr(c, fields, initExpr) + let branch = n[branchIdx] + let fields = branch[^1] + fieldsPresentInInitExpr(c, fields, constrCtx.initExpr) - template checkMissingFields(branchNode: PNode) = - let fields = branchNode[branchNode.len - 1] - checkForMissingFields(c, fields, initExpr) - - let discriminator = recNode.sons[0] + let discriminator = n[0] internalAssert c.config, discriminator.kind == nkSym var selectedBranch = -1 - for i in 1 ..< recNode.len: - let innerRecords = recNode[i][^1] - let status = semConstructFields(c, innerRecords, initExpr, flags) + for i in 1..<n.len: + let innerRecords = n[i][^1] + let (status, _) = semConstructFields(c, innerRecords, constrCtx, flags) # todo if status notin {initNone, initUnknown}: - mergeInitStatus(result, status) + result.status.mergeInitStatus status if selectedBranch != -1: let prevFields = fieldsPresentInBranch(selectedBranch) let currentFields = fieldsPresentInBranch(i) - localError(c.config, initExpr.info, + localError(c.config, constrCtx.initExpr.info, ("The fields '$1' and '$2' cannot be initialized together, " & "because they are from conflicting branches in the case object.") % [prevFields, currentFields]) - result = initConflict + result.status = initConflict else: selectedBranch = i if selectedBranch != -1: - let branchNode = recNode[selectedBranch] - let flags = flags*{efAllowDestructor} + {efNeedStatic, efPreferNilResult} - let discriminatorVal = semConstrField(c, flags, - discriminator.sym, initExpr) - if discriminatorVal == nil: - let fields = fieldsPresentInBranch(selectedBranch) - localError(c.config, initExpr.info, - ("you must provide a compile-time value for the discriminator '$1' " & - "in order to prove that it's safe to initialize $2.") % - [discriminator.sym.name.s, fields]) - mergeInitStatus(result, initNone) - else: - let discriminatorVal = discriminatorVal.skipHidden - - template wrongBranchError(i) = + template badDiscriminatorError = + if c.inUncheckedAssignSection == 0: + let fields = fieldsPresentInBranch(selectedBranch) + localError(c.config, constrCtx.initExpr.info, + ("cannot prove that it's safe to initialize $1 with " & + "the runtime value for the discriminator '$2' ") % + [fields, discriminator.sym.name.s]) + mergeInitStatus(result.status, initNone) + + template wrongBranchError(i) = + if c.inUncheckedAssignSection == 0: let fields = fieldsPresentInBranch(i) - localError(c.config, initExpr.info, - "a case selecting discriminator '$1' with value '$2' " & + localError(c.config, constrCtx.initExpr.info, + ("a case selecting discriminator '$1' with value '$2' " & "appears in the object construction, but the field(s) $3 " & - "are in conflict with this value.", + "are in conflict with this value.") % [discriminator.sym.name.s, discriminatorVal.renderTree, fields]) + template valuesInConflictError(valsDiff) = + localError(c.config, discriminatorVal.info, ("possible values " & + "$2 are in conflict with discriminator values for " & + "selected object branch $1.") % [$selectedBranch, + valsDiff.renderAsType(n[0].typ)]) + + let branchNode = n[selectedBranch] + let flags = {efPreferStatic, efPreferNilResult} + var discriminatorVal = semConstrField(c, flags, + discriminator.sym, + constrCtx.initExpr) + if discriminatorVal != nil: + discriminatorVal = discriminatorVal.skipHidden + if discriminatorVal.kind notin nkLiterals and ( + not isOrdinalType(discriminatorVal.typ, true) or + lengthOrd(c.config, discriminatorVal.typ) > 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): - wrongBranchError(selectedBranch) + failedBranch = selectedBranch else: # With an else clause, check that all other branches don't match: - for i in 1 .. (recNode.len - 2): - if recNode[i].caseBranchMatchesExpr(discriminatorVal): - wrongBranchError(i) + for i in 1..<n.len - 1: + if n[i].caseBranchMatchesExpr(discriminatorVal): + failedBranch = i break + if failedBranch != -1: + 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: + wrongBranchError(failedBranch) + + let (_, defaults) = semConstructFields(c, branchNode[^1], constrCtx, flags) + result.defaults.add defaults # 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 - + if result.status == initPartial: + collectOrAddMissingCaseFields(c, branchNode, constrCtx, result.defaults) else: - result = initNone + result.status = initNone let discriminatorVal = semConstrField(c, flags + {efPreferStatic}, - discriminator.sym, initExpr) + discriminator.sym, + constrCtx.initExpr) if discriminatorVal == nil: - # None of the branches were explicitly selected by the user and no - # value was given to the discrimator. We can assume that it will be - # initialized to zero and this will select a particular branch as - # a result: - let matchedBranch = recNode.pickCaseBranch newIntLit(c.graph, initExpr.info, 0) - checkMissingFields matchedBranch + if discriminator.sym.ast != nil: + # branch is selected by the default field value of discriminator + let discriminatorDefaultVal = discriminator.sym.ast + result.status = initUnknown + result.defaults.add newTree(nkExprColonExpr, n[0], discriminatorDefaultVal) + if discriminatorDefaultVal.kind == nkIntLit: + let matchedBranch = n.pickCaseBranch discriminatorDefaultVal + if matchedBranch != nil: + let (_, defaults) = semConstructFields(c, matchedBranch[^1], constrCtx, flags) + result.defaults.add defaults + collectOrAddMissingCaseFields(c, matchedBranch, constrCtx, result.defaults) + else: + collectBranchFields(c, n, discriminatorDefaultVal, constrCtx, flags) + else: + # None of the branches were explicitly selected by the user and no + # value was given to the discrimator. We can assume that it will be + # initialized to zero and this will select a particular branch as + # a result: + let defaultValue = newIntLit(c.graph, constrCtx.initExpr.info, 0) + let matchedBranch = n.pickCaseBranch defaultValue + discard collectMissingCaseFields(c, matchedBranch, constrCtx, @[]) else: - result = initPartial + result.status = initPartial if discriminatorVal.kind == nkIntLit: # When the discriminator is a compile-time value, we also know - # which brach will be selected: - let matchedBranch = recNode.pickCaseBranch discriminatorVal - if matchedBranch != nil: checkMissingFields matchedBranch + # which branch will be selected: + let matchedBranch = n.pickCaseBranch discriminatorVal + if matchedBranch != nil: + let (_, defaults) = semConstructFields(c, matchedBranch[^1], constrCtx, flags) + result.defaults.add defaults + collectOrAddMissingCaseFields(c, matchedBranch, constrCtx, result.defaults) 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] + collectBranchFields(c, n, discriminatorVal, constrCtx, flags) of nkSym: - let field = recNode.sym - let e = semConstrField(c, flags, field, initExpr) - result = if e != nil: initFull else: initNone - + let field = n.sym + let e = semConstrField(c, flags, field, constrCtx.initExpr) + if e != nil: + result.status = initFull + elif field.ast != nil: + if efIgnoreDefaults notin flags: + result.status = initUnknown + result.defaults.add newTree(nkExprColonExpr, n, field.ast) + else: + result.status = initNone + else: + if {efWantNoDefaults, efIgnoreDefaults} * flags == {}: # cannot compute defaults at the typeRightPass + let defaultExpr = defaultNodeField(c, n, constrCtx.checkDefault) + if defaultExpr != nil: + result.status = initUnknown + result.defaults.add newTree(nkExprColonExpr, n, defaultExpr) + else: + result.status = initNone + else: + result.status = initNone else: internalAssert c.config, false -proc semConstructType(c: PContext, initExpr: PNode, - t: PType, flags: TExprFlags): InitStatus = - var t = t - result = initUnknown +proc semConstructTypeAux(c: PContext, + constrCtx: var ObjConstrContext, + flags: TExprFlags): tuple[status: InitStatus, defaults: seq[PNode]] = + result = (initUnknown, @[]) + var t = constrCtx.typ while true: - let status = semConstructFields(c, t.n, initExpr, flags) - mergeInitStatus(result, status) + let (status, defaults) = semConstructFields(c, t.n, constrCtx, flags) + result.status.mergeInitStatus status + result.defaults.add defaults if status in {initPartial, initNone, initUnknown}: - checkForMissingFields c, t.n, initExpr - let base = t.sons[0] - if base == nil: break + discard collectMissingFields(c, t.n, constrCtx, result.defaults) + let base = t.baseClass + if base == nil or base.id == t.id or + base.kind in {tyRef, tyPtr} and base.elementType.id == t.id: + break t = skipTypes(base, skipPtrs) + if t.kind != tyObject: + # XXX: This is not supposed to happen, but apparently + # there are some issues in semtypinst. Luckily, it + # seems to affect only `computeRequiresInit`. + return + constrCtx.needsFullInit = constrCtx.needsFullInit or + tfNeedsFullInit in t.flags + +proc initConstrContext(t: PType, initExpr: PNode): ObjConstrContext = + ObjConstrContext(typ: t, initExpr: initExpr, + needsFullInit: tfNeedsFullInit in t.flags) + +proc computeRequiresInit(c: PContext, t: PType): bool = + assert t.kind == tyObject + var constrCtx = initConstrContext(t, newNode(nkObjConstr)) + let initResult = semConstructTypeAux(c, constrCtx, {efWantNoDefaults}) + constrCtx.missingFields.len > 0 + +proc defaultConstructionError(c: PContext, t: PType, info: TLineInfo) = + var objType = t + while objType.kind notin {tyObject, tyDistinct}: + objType = objType.last + assert objType != nil + if objType.kind == tyObject: + var constrCtx = initConstrContext(objType, newNodeI(nkObjConstr, info)) + let initResult = semConstructTypeAux(c, constrCtx, {efIgnoreDefaults}) + if 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.sons[0], nil) +proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode = + var t = semTypeNode(c, n[0], nil) result = newNodeIT(nkObjConstr, n.info, t) - for child in n: result.add child + for i in 0..<n.len: + result.add n[i] if t == nil: - localError(c.config, n.info, errGenerated, "object constructor needs an object type") - return - - t = skipTypes(t, {tyGenericInst, tyAlias, tySink}) - if t.kind == tyRef: t = skipTypes(t.sons[0], {tyGenericInst, tyAlias, tySink}) + return localErrorNode(c, result, "object constructor needs an object type") + + if t.skipTypes({tyGenericInst, + tyAlias, tySink, tyOwned, tyRef}).kind != tyObject and + expectedType != nil and expectedType.skipTypes({tyGenericInst, + tyAlias, tySink, tyOwned, tyRef}).kind == tyObject: + t = expectedType + + t = skipTypes(t, {tyGenericInst, tyAlias, tySink, tyOwned}) + if t.kind == tyRef: + t = skipTypes(t.elementType, {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: - localError(c.config, n.info, errGenerated, "object constructor needs an object type") - return + return localErrorNode(c, result, if t.kind != tyGenericBody: + "object constructor needs an object type".dup(addTypeNodeDeclaredLoc(c.config, t)) + else: "cannot instantiate: '" & + typeToString(t, preferDesc) & + "'; the object's generic parameters cannot be inferred and must be explicitly given" + ) # 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): - let initResult = semConstructType(c, result, t, flags) + var constrCtx = initConstrContext(t, result) + let (initResult, defaults) = 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. - # XXX: Turn this into an error in the next release - if tfNeedsInit in t.flags and initResult != initFull: - # XXX: Disable this warning for now, because tfNeedsInit is propagated - # too aggressively from fields to object types (and this is not correct - # in case objects) - when false: message(n.info, warnUser, - "object type uses the 'requiresInit' pragma, but not all fields " & - "have been initialized. future versions of Nim will treat this as " & - "an error") + # 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. @@ -294,15 +510,27 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = if nfSem notin field.flags: if field.kind != nkExprColonExpr: invalidObjConstr(c, field) + hasError = true continue let id = considerQuotedIdent(c, field[0]) # This node was not processed. There are two possible reasons: # 1) It was shadowed by a field with the same name on the left - for j in 1 ..< i: + for j in 1..<i: let prevId = considerQuotedIdent(c, result[j][0]) if prevId.id == id.id: localError(c.config, field.info, errFieldInitTwice % id.s) - return + hasError = true + break # 2) No such field exists in the constructed type - localError(c.config, field.info, errUndeclaredFieldX % id.s) - return + let msg = errUndeclaredField % id.s & " for type " & getProcHeader(c.config, t.sym) + localError(c.config, field.info, msg) + hasError = true + break + + result.sons.add defaults + + if initResult == initFull: + incl result.flags, nfAllFieldsSet + + # wrap in an error see #17437 + if hasError: result = errorNode(c, result) |