summary refs log tree commit diff stats
path: root/compiler/semobjconstr.nim
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/semobjconstr.nim')
-rw-r--r--compiler/semobjconstr.nim510
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)