summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--compiler/ccgexprs.nim6
-rw-r--r--tests/generics/tgeneric_closure.nim37
-rw-r--r--tests/generics/tunique_type.nim60
3 files changed, 101 insertions, 2 deletions
diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim
index 4372ca524..93bb5dbf5 100644
--- a/compiler/ccgexprs.nim
+++ b/compiler/ccgexprs.nim
@@ -215,6 +215,7 @@ proc asgnComplexity(n: PNode): int =
     else: discard
 
 proc optAsgnLoc(a: TLoc, t: PType, field: PRope): TLoc =
+  assert field != nil
   result.k = locField
   result.s = a.s
   result.t = t
@@ -229,7 +230,7 @@ proc genOptAsgnTuple(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
       flags - {needToCopy}
     else:
       flags
-  let t = skipTypes(dest.t, abstractInst)
+  let t = skipTypes(dest.t, abstractInst).getUniqueType()
   for i in 0 .. <t.len:
     let t = t.sons[i]
     let field = ropef("Field$1", i.toRope)
@@ -336,6 +337,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
     elif needsComplexAssignment(ty):
       if ty.sons[0].isNil and asgnComplexity(ty.n) <= 4:
         discard getTypeDesc(p.module, ty)
+        ty = getUniqueType(ty)
         internalAssert ty.n != nil
         genOptAsgnObject(p, dest, src, flags, ty.n)
       else:
@@ -1131,7 +1133,7 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) =
     var tmp2: TLoc
     tmp2.r = r
     var field: PSym = nil
-    var ty = t
+    var ty = getUniqueType(t)
     while ty != nil:
       field = lookupInRecord(ty.n, it.sons[0].sym.name)
       if field != nil: break
diff --git a/tests/generics/tgeneric_closure.nim b/tests/generics/tgeneric_closure.nim
new file mode 100644
index 000000000..7198dce96
--- /dev/null
+++ b/tests/generics/tgeneric_closure.nim
@@ -0,0 +1,37 @@
+# Test to ensure TEventHandler is '.closure'
+
+# bug #1187
+
+type
+  TEventArgs* = object
+    skip*: bool
+  TEventHandler[T] = proc (e: var TEventArgs, data: T) {.closure.}
+  TEvent*[T] = object
+    #handlers: seq[TEventHandler[T]] # Does not work
+    handlers: seq[proc (e: var TEventArgs, data: T) {.closure.}] # works
+
+  TData = object
+    x: int
+
+  TSomething = object
+    s: TEvent[TData]
+
+proc init*[T](e: var TEvent[T]) =
+  e.handlers.newSeq(0)
+
+#proc add*[T](e: var TEvent[T], h: proc (e: var TEventArgs, data: T) {.closure.}) =
+# this line works
+proc add*[T](e: var TEvent[T], h: TEventHandler[T]) =
+  # this line does not work
+  e.handlers.add(h)
+
+proc main () =
+  var something: TSomething
+  something.s.init()
+  var fromOutside = 4711
+
+  something.s.add() do (e: var TEventArgs, data: TData):
+    var x = data.x
+    x = fromOutside
+
+main()
diff --git a/tests/generics/tunique_type.nim b/tests/generics/tunique_type.nim
new file mode 100644
index 000000000..f8901d3bd
--- /dev/null
+++ b/tests/generics/tunique_type.nim
@@ -0,0 +1,60 @@
+# Bug #2022
+
+## The goal of this snippet is to provide and test a construct for general-
+## purpose, random-access mapping. I use an AST-manipulation-based approach
+## because it's more efficient than using procedure pointers and less
+## verbose than defining a new callable type for every invocation of `map`.
+
+import future
+import macros
+import strutils
+
+#===============================================================================
+# Define a system for storing copies of ASTs as static strings.
+# This serves the same purpose as D's `alias` parameters for types, used heavily
+# in its popular `ranges` and `algorithm` modules.
+
+var exprNodes {.compileTime.} = newSeq[PNimrodNode]()
+
+proc refExpr(exprNode: PNimrodNode): string {.compileTime.} =
+  exprNodes.add exprNode.copy
+  "expr" & $(exprNodes.len - 1)
+
+proc derefExpr(exprRef: string): PNimrodNode {.compileTime.} =
+  exprNodes[parseInt(exprRef[4 .. -1])]
+
+#===============================================================================
+# Define a type that allows a callable expression to be mapped onto elements
+# of an indexable collection.
+
+type Mapped[Input; predicate: static[string]] = object
+  input: Input
+
+macro map(input, predicate: expr): expr =
+  newNimNode(nnkObjConstr).add(
+    newNimNode(nnkBracketExpr).add(
+      ident"Mapped",
+      newNimNode(nnkTypeOfExpr).add(input),
+      newLit(refExpr(predicate))),
+    newNimNode(nnkExprColonExpr).add(
+      ident"input", input))
+
+proc `[]`(m: Mapped, i: int): auto =
+  macro buildResult: expr =
+    newCall(
+      derefExpr(m.predicate),
+      newNimNode(nnkBracketExpr).add(
+        newDotExpr(ident"m", ident"input"),
+        ident"i"))
+  buildResult()
+
+#===============================================================================
+# Test out our generic mapping construct.
+
+let a = "a-string".map(ord)
+let b = @["a", "seq"].map((e: string) => e == "a")
+let c = "another-string".map((e: char) => e == 'o')
+
+echo(@[a[0], a[1]]) # @[97, 45]
+echo(@[b[0], b[1]]) # @[true, false]
+echo(@[c[0], c[1]]) # @[false, false]