summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--compiler/semstmts.nim7
-rw-r--r--compiler/sighashes.nim19
-rw-r--r--tests/ccgbugs/tsamename3.nim111
-rw-r--r--tests/enum/tenum.nim5
4 files changed, 133 insertions, 9 deletions
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index 4c6a745f6..2c257bd3d 100644
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -1354,8 +1354,11 @@ proc typeSectionFinalPass(c: PContext, n: PNode) =
         checkConstructedType(c.config, s.info, s.typ)
         if s.typ.kind in {tyObject, tyTuple} and not s.typ.n.isNil:
           checkForMetaFields(c, s.typ.n)
-          # fix bug #5170: ensure locally scoped object types get a unique name:
-          if s.typ.kind == tyObject and not isTopLevel(c): incl(s.flags, sfGenSym)
+
+        # fix bug #5170, bug #17162, bug #15526: ensure locally scoped types get a unique name:
+        if s.typ.kind in {tyEnum, tyRef, tyObject} and not isTopLevel(c):
+          incl(s.flags, sfGenSym)
+
   #instAllTypeBoundOp(c, n.info)
 
 
diff --git a/compiler/sighashes.nim b/compiler/sighashes.nim
index 156bc66d7..f6df422e6 100644
--- a/compiler/sighashes.nim
+++ b/compiler/sighashes.nim
@@ -151,15 +151,24 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]) =
         c.hashTypeSym(t.sym)
       else:
         c.hashSym(t.sym)
-      if {sfAnon, sfGenSym} * t.sym.flags != {}:
+
+      var symWithFlags: PSym
+      template hasFlag(sym): bool =
+        let ret = {sfAnon, sfGenSym} * sym.flags != {}
+        if ret: symWithFlags = sym
+        ret
+      if hasFlag(t.sym) or (t.kind == tyObject and t.owner.kind == skType and t.owner.typ.kind == tyRef and hasFlag(t.owner)):
+        # for `PFoo:ObjectType`, arising from `type PFoo = ref object`
         # Generated object names can be identical, so we need to
         # disambiguate furthermore by hashing the field types and names.
         if t.n.len > 0:
-          let oldFlags = t.sym.flags
-          # Mild hack to prevent endless recursion.
-          t.sym.flags.excl {sfAnon, sfGenSym}
+          let oldFlags = symWithFlags.flags
+          # Hack to prevent endless recursion
+          # xxx intead, use a hash table to indicate we've already visited a type, which
+          # would also be more efficient.
+          symWithFlags.flags.excl {sfAnon, sfGenSym}
           hashTree(c, t.n, flags + {CoHashTypeInsideNode})
-          t.sym.flags = oldFlags
+          symWithFlags.flags = oldFlags
         else:
           # The object has no fields: we _must_ add something here in order to
           # make the hash different from the one we produce by hashing only the
diff --git a/tests/ccgbugs/tsamename3.nim b/tests/ccgbugs/tsamename3.nim
new file mode 100644
index 000000000..a69391e5c
--- /dev/null
+++ b/tests/ccgbugs/tsamename3.nim
@@ -0,0 +1,111 @@
+block: # bug #15526
+  block:
+    type Foo = ref object
+      x1: int
+    let f1 = Foo(x1: 1)
+  block:
+    type Foo = ref object
+      x2: int
+    let f2 = Foo(x2: 2)
+
+block: # ditto
+  template fn() =
+    block:
+      type Foo = ref object
+        x1: int
+      let f1 = Foo(x1: 1)
+      doAssert f1.x1 == 1
+    block:
+      type Foo = ref object
+        x2: int
+      let f2 = Foo(x2: 2)
+      doAssert f2.x2 == 2
+  static: fn()
+  fn()
+
+block: # bug #17162
+  template fn =
+    var ret: string
+    block:
+      type A = enum a0, a1, a2
+      for ai in A:
+        ret.add $ai
+    block:
+      type A = enum b0, b1, b2, b3
+      for ai in A:
+        ret.add $ai
+    doAssert ret == "a0a1a2b0b1b2b3"
+
+  static: fn() # ok
+  fn() # was bug
+
+block: # ditto
+  proc fn =
+    var ret: string
+    block:
+      type A = enum a0, a1, a2
+      for ai in A:
+        ret.add $ai
+    block:
+      type A = enum b0, b1, b2, b3
+      for ai in A:
+        ret.add $ai
+    doAssert ret == "a0a1a2b0b1b2b3"
+
+  static: fn() # ok
+  fn() # was bug
+
+block: # bug #5170
+  block:
+    type Foo = object
+      x1: int
+    let f1 = Foo(x1: 1)
+  block:
+    type Foo = object
+      x2: int
+    let f2 = Foo(x2: 2)
+
+block: # ditto
+  block:
+    type Foo = object
+      bar: bool
+    var f1: Foo
+
+  block:
+    type Foo = object
+      baz: int
+    var f2: Foo
+    doAssert f2.baz == 0
+
+  block:
+    template fn() =
+      block:
+        type Foo = object
+          x1: int
+        let f1 = Foo(x1: 1)
+        doAssert f1.x1 == 1
+      block:
+        type Foo = object
+          x2: int
+        let f2 = Foo(x2: 2)
+        doAssert f2.x2 == 2
+    static: fn()
+    fn()
+
+when true: # ditto, refs https://github.com/nim-lang/Nim/issues/5170#issuecomment-582712132
+  type Foo1 = object # at top level
+    bar: bool
+  var f1: Foo1
+
+  block:
+    type Foo1 = object
+      baz: int
+    var f2: Foo1
+    doAssert f2.baz == 0
+
+block: # make sure `hashType` doesn't recurse infinitely
+  type
+    PFoo = ref object
+      a, b: PFoo
+      c: int
+  var a: PFoo
diff --git a/tests/enum/tenum.nim b/tests/enum/tenum.nim
index aa338ee2c..97fd4c68c 100644
--- a/tests/enum/tenum.nim
+++ b/tests/enum/tenum.nim
@@ -6,7 +6,7 @@ ABCDC
 foo
 first0second32third64
 my value A1my value Bconc2valueCabc4abc
-my value A0my value Bconc1valueCabc3valueC
+my value A0my value Bconc1valueCabc3abc
 '''
 """
 
@@ -124,7 +124,8 @@ block tnamedfields:
   # trick the optimizer with a variable:
   var x = valueD
   echo valueA, ord(valueA), valueB, ord(valueB), valueC, valueD, ord(valueD), x
-
+  doAssert $x == $valueD, $x
+  doAssert $x == "abc", $x
 
 
 block toptions: