summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorringabout <43030857+ringabout@users.noreply.github.com>2023-07-25 18:08:32 +0800
committerGitHub <noreply@github.com>2023-07-25 12:08:32 +0200
commit1c2ccfad08191e936fadd52450b53dfea105a34d (patch)
treed848422b5cd391db4efb14bf2a5b7db2307266df
parentdce714b2598c41e36113a4339fb9fb14655bc090 (diff)
downloadNim-1c2ccfad08191e936fadd52450b53dfea105a34d.tar.gz
fixes #22301; fixes #22324; rejects branch initialization with a runtime discriminator with defaults (#22303)
* fixes #22301; rejects branch initialization with a runtime discriminator with defaults

* undefault nimPreviewRangeDefault

* fixes tests

* use oldCheckDefault
-rw-r--r--compiler/sem.nim34
-rw-r--r--compiler/semmagic.nim4
-rw-r--r--compiler/semobjconstr.nim13
-rw-r--r--config/nim.cfg1
-rw-r--r--tests/objects/t22301.nim17
-rw-r--r--tests/system/tfielditerator.nim16
6 files changed, 64 insertions, 21 deletions
diff --git a/compiler/sem.nim b/compiler/sem.nim
index f92853d9e..3324da55c 100644
--- a/compiler/sem.nim
+++ b/compiler/sem.nim
@@ -553,17 +553,17 @@ proc pickCaseBranchIndex(caseExpr, matched: PNode): int =
   if endsWithElse:
     return caseExpr.len - 1
 
-proc defaultFieldsForTheUninitialized(c: PContext, recNode: PNode): seq[PNode]
-proc defaultNodeField(c: PContext, a: PNode, aTyp: PType): PNode
-proc defaultNodeField(c: PContext, a: PNode): PNode
+proc defaultFieldsForTheUninitialized(c: PContext, recNode: PNode, checkDefault: bool): seq[PNode]
+proc defaultNodeField(c: PContext, a: PNode, aTyp: PType, checkDefault: bool): PNode
+proc defaultNodeField(c: PContext, a: PNode, checkDefault: bool): PNode
 
 const defaultFieldsSkipTypes = {tyGenericInst, tyAlias, tySink}
 
-proc defaultFieldsForTuple(c: PContext, recNode: PNode, hasDefault: var bool): seq[PNode] =
+proc defaultFieldsForTuple(c: PContext, recNode: PNode, hasDefault: var bool, checkDefault: bool): seq[PNode] =
   case recNode.kind
   of nkRecList:
     for field in recNode:
-      result.add defaultFieldsForTuple(c, field, hasDefault)
+      result.add defaultFieldsForTuple(c, field, hasDefault, checkDefault)
   of nkSym:
     let field = recNode.sym
     let recType = recNode.typ.skipTypes(defaultFieldsSkipTypes)
@@ -572,7 +572,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, checkDefault)
         if asgnExpr != nil:
           hasDefault = true
           asgnExpr.flags.incl nfSkipFieldChecking
@@ -591,11 +591,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, checkDefault: bool): seq[PNode] =
   case recNode.kind
   of nkRecList:
     for field in recNode:
-      result.add defaultFieldsForTheUninitialized(c, field)
+      result.add defaultFieldsForTheUninitialized(c, field, checkDefault)
   of nkRecCase:
     let discriminator = recNode[0]
     var selectedBranch: int
@@ -604,19 +604,21 @@ proc defaultFieldsForTheUninitialized(c: PContext, recNode: PNode): seq[PNode] =
       # 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:
+      if checkDefault: # don't add defaults when checking whether a case branch has default fields
+        return
       defaultValue = newIntNode(nkIntLit#[c.graph]#, 0)
       defaultValue.typ = discriminator.typ
     selectedBranch = recNode.pickCaseBranchIndex defaultValue
     defaultValue.flags.incl nfSkipFieldChecking
     result.add newTree(nkExprColonExpr, discriminator, defaultValue)
-    result.add defaultFieldsForTheUninitialized(c, recNode[selectedBranch][^1])
+    result.add defaultFieldsForTheUninitialized(c, recNode[selectedBranch][^1], checkDefault)
   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, recNode.typ, checkDefault)
       if asgnExpr != nil:
         asgnExpr.typ = recNode.typ
         asgnExpr.flags.incl nfSkipFieldChecking
@@ -624,17 +626,17 @@ proc defaultFieldsForTheUninitialized(c: PContext, recNode: PNode): seq[PNode] =
   else:
     doAssert false
 
-proc defaultNodeField(c: PContext, a: PNode, aTyp: PType): PNode =
+proc defaultNodeField(c: PContext, a: PNode, aTyp: PType, checkDefault: bool): PNode =
   let aTypSkip = aTyp.skipTypes(defaultFieldsSkipTypes)
   if aTypSkip.kind == tyObject:
-    let child = defaultFieldsForTheUninitialized(c, aTypSkip.n)
+    let child = defaultFieldsForTheUninitialized(c, aTypSkip.n, checkDefault)
     if child.len > 0:
       var asgnExpr = newTree(nkObjConstr, newNodeIT(nkType, a.info, aTyp))
       asgnExpr.typ = aTyp
       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], checkDefault)
 
     if child != nil:
       let node = newNode(nkIntLit)
@@ -647,15 +649,15 @@ 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, hasDefault, checkDefault)
       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): PNode =
-  result = defaultNodeField(c, a, a.typ)
+proc defaultNodeField(c: PContext, a: PNode, checkDefault: bool): PNode =
+  result = defaultNodeField(c, a, a.typ, checkDefault)
 
 include semtempl, semgnrc, semstmts, semexprs
 
diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim
index f94d8dc33..ad7e9821b 100644
--- a/compiler/semmagic.nim
+++ b/compiler/semmagic.nim
@@ -21,7 +21,7 @@ proc addDefaultFieldForNew(c: PContext, n: PNode): PNode =
     asgnExpr.typ = typ
     var t = typ.skipTypes({tyGenericInst, tyAlias, tySink})[0]
     while true:
-      asgnExpr.sons.add defaultFieldsForTheUninitialized(c, t.n)
+      asgnExpr.sons.add defaultFieldsForTheUninitialized(c, t.n, false)
       let base = t[0]
       if base == nil:
         break
@@ -647,7 +647,7 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
   of mDefault:
     result = checkDefault(c, n)
     let typ = result[^1].typ.skipTypes({tyTypeDesc})
-    let defaultExpr = defaultNodeField(c, result[^1], typ)
+    let defaultExpr = defaultNodeField(c, result[^1], typ, false)
     if defaultExpr != nil:
       result = defaultExpr
   of mZeroDefault:
diff --git a/compiler/semobjconstr.nim b/compiler/semobjconstr.nim
index d4eba2112..9b17676ee 100644
--- a/compiler/semobjconstr.nim
+++ b/compiler/semobjconstr.nim
@@ -21,6 +21,7 @@ type
                              # 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
@@ -342,6 +343,16 @@ proc semConstructFields(c: PContext, n: PNode, constrCtx: var ObjConstrContext,
           # 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, @[])
   of nkSym:
     let field = n.sym
@@ -353,7 +364,7 @@ proc semConstructFields(c: PContext, n: PNode, constrCtx: var ObjConstrContext,
       result.defaults.add newTree(nkExprColonExpr, n, field.ast)
     else:
       if efWantNoDefaults notin flags: # cannot compute defaults at the typeRightPass
-        let defaultExpr = defaultNodeField(c, n)
+        let defaultExpr = defaultNodeField(c, n, constrCtx.checkDefault)
         if defaultExpr != nil:
           result.status = initUnknown
           result.defaults.add newTree(nkExprColonExpr, n, defaultExpr)
diff --git a/config/nim.cfg b/config/nim.cfg
index efb058121..1470de780 100644
--- a/config/nim.cfg
+++ b/config/nim.cfg
@@ -21,7 +21,6 @@ cc = gcc
 #hint[XDeclaredButNotUsed]=off
 
 threads:on
-define:nimPreviewRangeDefault
 
 # Examples of how to setup a cross-compiler:
 # Nim can target architectures and OSes different than the local host
diff --git a/tests/objects/t22301.nim b/tests/objects/t22301.nim
new file mode 100644
index 000000000..8746bf584
--- /dev/null
+++ b/tests/objects/t22301.nim
@@ -0,0 +1,17 @@
+discard """
+  errormsg: "branch initialization with a runtime discriminator is not supported for a branch whose fields have default values."
+"""
+
+# bug #22301
+type
+  Enum = enum A, B
+  Object = object
+    case a: Enum
+    of A:
+      integer: int = 200
+    of B:
+      time: string
+
+let x = A
+let s = Object(a: x)
+echo s
\ No newline at end of file
diff --git a/tests/system/tfielditerator.nim b/tests/system/tfielditerator.nim
index d1fbf02f9..7e063c6cf 100644
--- a/tests/system/tfielditerator.nim
+++ b/tests/system/tfielditerator.nim
@@ -109,4 +109,18 @@ block titerator2:
     echo key, ": ", val
 
   for val in fields(co):
-    echo val
\ No newline at end of file
+    echo val
+
+block:
+  type
+    Enum = enum A, B
+    Object = object
+      case a: Enum
+      of A:
+        integer: int
+      of B:
+        time: string
+
+  let x = A
+  let s = Object(a: x)
+  doAssert s.integer == 0