summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--compiler/sem.nim39
-rw-r--r--compiler/semmagic.nim3
-rw-r--r--compiler/semobjconstr.nim7
-rw-r--r--tests/objects/tobject_default_value.nim37
4 files changed, 66 insertions, 20 deletions
diff --git a/compiler/sem.nim b/compiler/sem.nim
index 3c8ced6b8..25116f8d6 100644
--- a/compiler/sem.nim
+++ b/compiler/sem.nim
@@ -553,17 +553,18 @@ proc pickCaseBranchIndex(caseExpr, matched: PNode): int =
   if endsWithElse:
     return caseExpr.len - 1
 
-proc defaultFieldsForTheUninitialized(c: PContext, recNode: PNode): seq[PNode]
+proc defaultFieldsForTheUninitialized(c: PContext, recNode: PNode, id: var IntSet): seq[PNode]
+proc defaultNodeField(c: PContext, a: PNode, aTyp: PType, id: var IntSet): PNode
 proc defaultNodeField(c: PContext, a: PNode): PNode
 proc defaultNodeField(c: PContext, a: PNode, aTyp: PType): PNode
 
 const defaultFieldsSkipTypes = {tyGenericInst, tyAlias, tySink}
 
-proc defaultFieldsForTuple(c: PContext, recNode: PNode, hasDefault: var bool): seq[PNode] =
+proc defaultFieldsForTuple(c: PContext, recNode: PNode, id: var IntSet, hasDefault: var bool): seq[PNode] =
   case recNode.kind
   of nkRecList:
     for field in recNode:
-      result.add defaultFieldsForTuple(c, field, hasDefault)
+      result.add defaultFieldsForTuple(c, field, id, hasDefault)
   of nkSym:
     let field = recNode.sym
     let recType = recNode.typ.skipTypes(defaultFieldsSkipTypes)
@@ -572,7 +573,7 @@ proc defaultFieldsForTuple(c: PContext, recNode: PNode, hasDefault: var bool): s
       result.add newTree(nkExprColonExpr, recNode, field.ast)
     else:
       if recType.kind in {tyObject, tyArray, tyTuple}:
-        let asgnExpr = defaultNodeField(c, recNode, recNode.typ)
+        let asgnExpr = defaultNodeField(c, recNode, recNode.typ, id)
         if asgnExpr != nil:
           hasDefault = true
           asgnExpr.flags.incl nfUseDefaultField
@@ -591,11 +592,11 @@ proc defaultFieldsForTuple(c: PContext, recNode: PNode, hasDefault: var bool): s
   else:
     doAssert false
 
-proc defaultFieldsForTheUninitialized(c: PContext, recNode: PNode): seq[PNode] =
+proc defaultFieldsForTheUninitialized(c: PContext, recNode: PNode, id: var IntSet): seq[PNode] =
   case recNode.kind
   of nkRecList:
     for field in recNode:
-      result.add defaultFieldsForTheUninitialized(c, field)
+      result.add defaultFieldsForTheUninitialized(c, field, id)
   of nkRecCase:
     let discriminator = recNode[0]
     var selectedBranch: int
@@ -609,31 +610,34 @@ proc defaultFieldsForTheUninitialized(c: PContext, recNode: PNode): seq[PNode] =
     selectedBranch = recNode.pickCaseBranchIndex defaultValue
     defaultValue.flags.incl nfUseDefaultField
     result.add newTree(nkExprColonExpr, discriminator, defaultValue)
-    result.add defaultFieldsForTheUninitialized(c, recNode[selectedBranch][^1])
+    result.add defaultFieldsForTheUninitialized(c, recNode[selectedBranch][^1], id)
   of nkSym:
     let field = recNode.sym
     let recType = recNode.typ.skipTypes(defaultFieldsSkipTypes)
     if field.ast != nil: #Try to use default value
       result.add newTree(nkExprColonExpr, recNode, field.ast)
     elif recType.kind in {tyObject, tyArray, tyTuple}:
-      let asgnExpr = defaultNodeField(c, recNode, recNode.typ)
+      let asgnExpr = defaultNodeField(c, recNode, recType, id)
       if asgnExpr != nil:
+        asgnExpr.typ = recType
         asgnExpr.flags.incl nfUseDefaultField
         result.add newTree(nkExprColonExpr, recNode, asgnExpr)
   else:
     doAssert false
 
-proc defaultNodeField(c: PContext, a: PNode, aTyp: PType): PNode =
+proc defaultNodeField(c: PContext, a: PNode, aTyp: PType, id: var IntSet): PNode =
   let aTypSkip = aTyp.skipTypes(defaultFieldsSkipTypes)
   if aTypSkip.kind == tyObject:
-    let child = defaultFieldsForTheUninitialized(c, aTyp.skipTypes(defaultFieldsSkipTypes).n)
+    if id.containsOrIncl(aTypSkip.id):
+      return
+    let child = defaultFieldsForTheUninitialized(c, aTypSkip.n, id)
     if child.len > 0:
-      var asgnExpr = newTree(nkObjConstr, newNodeIT(nkType, a.info, aTyp))
-      asgnExpr.typ = aTyp
+      var asgnExpr = newTree(nkObjConstr, newNodeIT(nkType, a.info, aTypSkip))
+      asgnExpr.typ = aTypSkip
       asgnExpr.sons.add child
       result = semExpr(c, asgnExpr)
   elif aTypSkip.kind == tyArray:
-    let child = defaultNodeField(c, a, aTypSkip[1])
+    let child = defaultNodeField(c, a, aTypSkip[1], id)
 
     if child != nil:
       let node = newNode(nkIntLit)
@@ -646,15 +650,20 @@ proc defaultNodeField(c: PContext, a: PNode, aTyp: PType): PNode =
   elif aTypSkip.kind == tyTuple:
     var hasDefault = false
     if aTypSkip.n != nil:
-      let children = defaultFieldsForTuple(c, aTypSkip.n, hasDefault)
+      let children = defaultFieldsForTuple(c, aTypSkip.n, id, hasDefault)
       if hasDefault and children.len > 0:
         result = newNodeI(nkTupleConstr, a.info)
         result.typ = aTyp
         result.sons.add children
         result = semExpr(c, result)
 
+proc defaultNodeField(c: PContext, a: PNode, aTyp: PType): PNode =
+  var s = initIntSet()
+  defaultNodeField(c, a, aTyp, s)
+
 proc defaultNodeField(c: PContext, a: PNode): PNode =
-  result = defaultNodeField(c, a, a.typ)
+  var s = initIntSet()
+  result = defaultNodeField(c, a, a.typ, s)
 
 include semtempl, semgnrc, semstmts, semexprs
 
diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim
index 751ca8fe7..7025dc867 100644
--- a/compiler/semmagic.nim
+++ b/compiler/semmagic.nim
@@ -20,8 +20,9 @@ proc addDefaultFieldForNew(c: PContext, n: PNode): PNode =
     var asgnExpr = newTree(nkObjConstr, newNodeIT(nkType, result[1].info, typ))
     asgnExpr.typ = typ
     var t = typ.skipTypes({tyGenericInst, tyAlias, tySink})[0]
+    var id = initIntSet()
     while true:
-      asgnExpr.sons.add defaultFieldsForTheUninitialized(c, t.n)
+      asgnExpr.sons.add defaultFieldsForTheUninitialized(c, t.n, id)
       let base = t[0]
       if base == nil:
         break
diff --git a/compiler/semobjconstr.nim b/compiler/semobjconstr.nim
index 1463ba833..7f76dbee9 100644
--- a/compiler/semobjconstr.nim
+++ b/compiler/semobjconstr.nim
@@ -326,7 +326,12 @@ proc semConstructFields(c: PContext, n: PNode, constrCtx: var ObjConstrContext,
       result.status = initUnknown
       result.defaults.add newTree(nkExprColonExpr, n, field.ast)
     else:
-      result.status = initNone
+      let defaultExpr = defaultNodeField(c, n)
+      if defaultExpr != nil:
+        result.status = initUnknown
+        result.defaults.add newTree(nkExprColonExpr, n, defaultExpr)
+      else:
+        result.status = initNone
   else:
     internalAssert c.config, false
 
diff --git a/tests/objects/tobject_default_value.nim b/tests/objects/tobject_default_value.nim
index 789571bbf..7ea4b42ae 100644
--- a/tests/objects/tobject_default_value.nim
+++ b/tests/objects/tobject_default_value.nim
@@ -3,7 +3,7 @@ discard """
   targets: "c cpp js"
 """
 
-import std/[times, tables, macros]
+import std/[times, macros]
 
 type
   Guess = object
@@ -27,6 +27,10 @@ import mobject_default_value
 block:
   let x = Default()
   doAssert x.se == 0'i32
+
+block:
+  let x = default(Default)
+  doAssert x.se == 0'i32
 # echo Default(poi: 12)
 # echo Default(poi: 17)
 
@@ -120,6 +124,19 @@ template main {.dirty.} =
     doAssert rVal == 0 # it should be 1
     doAssert objVal.r == 1
 
+  block: # bug #16744
+    type
+      R = range[1..10]
+      Obj = object
+        r: R
+
+    var
+      rVal: R = default(R) # Works fine
+      objVal = Obj()
+
+    doAssert rVal == 0 # it should be 1
+    doAssert objVal.r == 1
+
   block: # bug #3608
     type
       abc = ref object
@@ -148,6 +165,9 @@ template main {.dirty.} =
     let y = ObjectBaseDistinct(default(ObjectBase))
     doAssert ObjectBase(y).value == 12
 
+    let m = ObjectBaseDistinct(ObjectBase())
+    doAssert ObjectBase(m).value == 12
+
     proc hello(): ObjectBaseDistinct =
       result = ObjectBaseDistinct(default(ObjectBase))
 
@@ -193,12 +213,24 @@ template main {.dirty.} =
     doAssert x.name.scale == 1
 
   block:
+    let x = Object2()
+    doAssert x.name.value == 12
+    doAssert x.name.time == 1.2
+    doAssert x.name.scale == 1
+
+  block:
     var x: ref Object2
     new x
     doAssert x[] == default(Object2)
 
   block:
-    var x = default(Object3) # todo Object3() ?
+    var x = default(Object3)
+    doAssert x.obj.name.value == 12
+    doAssert x.obj.name.time == 1.2
+    doAssert x.obj.name.scale == 1
+
+  block:
+    var x = Object3()
     doAssert x.obj.name.value == 12
     doAssert x.obj.name.time == 1.2
     doAssert x.obj.name.scale == 1
@@ -275,7 +307,6 @@ template main {.dirty.} =
     doAssert y.time == 1.2
     doAssert y.scale == 1
 
-
   block:
     var x: PrellDeque[int]
     doAssert x.pendingTasks == 0