diff options
author | Zahary Karadjov <zahary@gmail.com> | 2017-04-06 00:44:46 +0300 |
---|---|---|
committer | Zahary Karadjov <zahary@gmail.com> | 2017-04-06 00:46:18 +0300 |
commit | 34c34cb49b63b04ddb2b0b2680210da742a4545d (patch) | |
tree | f61a91bda15948b4626392ecebd618303bb267c6 /compiler/semexprs.nim | |
parent | f162214d5d509945a36f70b7ea6ffeb33ad17fe7 (diff) | |
download | Nim-34c34cb49b63b04ddb2b0b2680210da742a4545d.tar.gz |
move the object construction logic to a separate file
Diffstat (limited to 'compiler/semexprs.nim')
-rw-r--r-- | compiler/semexprs.nim | 278 |
1 files changed, 1 insertions, 277 deletions
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 5a71896ef..316cf55c8 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -2097,283 +2097,7 @@ proc isTupleType(n: PNode): bool = return false return true -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 locateFieldInInitExpr(field: PSym, initExpr: PNode): PNode = - # Returns the assignment nkExprColonExpr node or nil - let fieldId = field.name.id - for i in 1 .. <initExpr.len: - let assignment = initExpr[i] - internalAssert assignment.kind == nkExprColonExpr - - if fieldId == considerQuotedIdent(assignment[0]).id: - return assignment - -proc semConstrField(c: PContext, flags: TExprFlags, - field: PSym, initExpr: PNode): PNode = - let assignment = locateFieldInInitExpr(field, initExpr) - if assignment != nil: - if nfSem in assignment.flags: return assignment[1] - if not fieldVisible(c, field): - localError(initExpr.info, - "the field '$1' is not accessible.", [field.name.s]) - return - - var initValue = semExprFlagDispatched(c, assignment[1], flags) - if initValue != nil: - initValue = fitNode(c, field.typ, initValue, assignment.info) - assignment.sons[0] = newSymNode(field) - assignment.sons[1] = initValue - assignment.flags.incl nfSem - return initValue - -proc caseBranchMatchesExpr(branch, matched: PNode): bool = - for i in 0 .. (branch.len - 2): - if exprStructuralEquivalent(branch[i], matched): - return true - - return false - -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): - if caseExpr[i].caseBranchMatchesExpr(matched): - return caseExpr[i] - - if endsWithElse: - return caseExpr{-1} - -iterator directFieldsInRecList(recList: PNode): PNode = - # XXX: We can remove this case by making all nkOfBranch nodes - # regular. Currently, they try to avoid using nkRecList if they - # include only a single field - if recList.kind == nkSym: - yield recList - else: - internalAssert recList.kind == nkRecList - for field in recList: - if field.kind != nkSym: continue - yield field - -template quoeStr(s: string): string = "'" & s & "'" - -proc fieldsPresentInInitExpr(fieldsRecList, initExpr: PNode): string = - result = "" - for field in directFieldsInRecList(fieldsRecList): - let assignment = locateFieldInInitExpr(field.sym, initExpr) - if assignment != nil: - if result.len != 0: result.add ", " - result.add field.sym.name.s.quoeStr - -proc missingMandatoryFields(fieldsRecList, initExpr: PNode): string = - for r in directFieldsInRecList(fieldsRecList): - if {tfNotNil, tfNeedsInit} * r.sym.typ.flags != {}: - let assignment = locateFieldInInitExpr(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(recList, initExpr: PNode) = - let missing = missingMandatoryFields(recList, initExpr) - if missing != nil: - localError(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 = - fieldsPresentInInitExpr(recNode[branchIdx]{-1}, initExpr) - - template checkMissingFields(branchNode: PNode) = - checkForMissingFields(branchNode{-1}, initExpr) - - let discriminator = recNode.sons[0]; - internalAssert discriminator.kind == nkSym - var selectedBranch = -1 - - for i in 1 .. <recNode.len: - let innerRecords = recNode[i]{-1} - let status = semConstructFields(c, innerRecords, initExpr, flags) - if status notin {initNone, initUnknown}: - mergeInitStatus(result, status) - if selectedBranch != -1: - let prevFields = fieldsPresentInBranch(selectedBranch) - let currentFields = fieldsPresentInBranch(i) - localError(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 - 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(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) = - let fields = fieldsPresentInBranch(i) - localError(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.", - [discriminator.sym.name.s, discriminatorVal.renderTree, fields]) - - if branchNode.kind != nkElse: - if not branchNode.caseBranchMatchesExpr(discriminatorVal): - wrongBranchError(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) - break - - # 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 - - else: - result = initNone - let discriminatorVal = semConstrField(c, flags + {efPreferStatic}, - discriminator.sym, 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(0) - checkMissingFields matchedBranch - else: - result = 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 - 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] - - of nkSym: - let field = recNode.sym - let e = semConstrField(c, flags, field, initExpr) - result = if e != nil: initFull else: initNone - - else: - internalAssert false - -proc semContructType(c: PContext, t: PType, - initExpr: PNode, flags: TExprFlags): InitStatus = - var t = t - result = initUnknown - - while true: - let status = semConstrFields(c, t.n, initExpr, flags) - mergeInitStatus(result, status) - - if status in {initPartial, initNone, initUnknown}: - checkForMissingFields t.n, initExpr - - let base = t.sons[0] - if base == nil: break - t = skipTypes(base, skipPtrs) - -proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = - var t = semTypeNode(c, n.sons[0], nil) - result = newNodeIT(nkObjConstr, n.info, t) - result.add n.sons[0] - t = skipTypes(t, {tyGenericInst, tyAlias}) - if t.kind == tyRef: t = skipTypes(t.sons[0], {tyGenericInst, tyAlias}) - if t.kind != tyObject: - localError(n.info, errGenerated, "object constructor needs an object type") - return - - # 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 = semContructType(c, t, n, flags) - - # 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") - - # 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.. <n.len: - let field = n[i] - if nfSem notin field.flags: - let id = considerQuotedIdent(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: - let prevId = considerQuotedIdent(n[j][0]) - if prevId.id == id.id: - localError(field.info, errFieldInitTwice, id.s) - return - # 2) No such field exists in the constructed type - localError(field.info, errUndeclaredFieldX, id.s) - return +include semobjconstr proc semBlock(c: PContext, n: PNode): PNode = result = n |