summary refs log tree commit diff stats
path: root/compiler/semexprs.nim
diff options
context:
space:
mode:
authorZahary Karadjov <zahary@gmail.com>2017-04-06 00:44:46 +0300
committerZahary Karadjov <zahary@gmail.com>2017-04-06 00:46:18 +0300
commit34c34cb49b63b04ddb2b0b2680210da742a4545d (patch)
treef61a91bda15948b4626392ecebd618303bb267c6 /compiler/semexprs.nim
parentf162214d5d509945a36f70b7ea6ffeb33ad17fe7 (diff)
downloadNim-34c34cb49b63b04ddb2b0b2680210da742a4545d.tar.gz
move the object construction logic to a separate file
Diffstat (limited to 'compiler/semexprs.nim')
-rw-r--r--compiler/semexprs.nim278
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