summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorAraq <rumpf_a@web.de>2013-01-16 08:42:30 +0100
committerAraq <rumpf_a@web.de>2013-01-16 08:42:30 +0100
commitc43697b59a08459fdfe41129b9ed0f96cf0150fa (patch)
tree8c1a9c2e3f4f915791922e45fcbaede72aa9c0b3
parentc9690864d4cf9945cb5eb9497f7621e56f9bebd0 (diff)
downloadNim-c43697b59a08459fdfe41129b9ed0f96cf0150fa.tar.gz
implemented generic multi methods
-rwxr-xr-xcompiler/cgmeth.nim19
-rwxr-xr-xcompiler/sem.nim3
-rwxr-xr-xcompiler/semcall.nim20
-rwxr-xr-xcompiler/seminst.nim1
-rwxr-xr-xcompiler/semstmts.nim29
-rwxr-xr-xcompiler/types.nim8
-rw-r--r--tests/run/tmultim6.nim30
-rwxr-xr-xtodo.txt1
-rwxr-xr-xweb/news.txt1
9 files changed, 81 insertions, 31 deletions
diff --git a/compiler/cgmeth.nim b/compiler/cgmeth.nim
index 687653aaf..e7bd54ef0 100755
--- a/compiler/cgmeth.nim
+++ b/compiler/cgmeth.nim
@@ -1,7 +1,7 @@
 #
 #
 #           The Nimrod Compiler
-#        (c) Copyright 2012 Andreas Rumpf
+#        (c) Copyright 2013 Andreas Rumpf
 #
 #    See the file "copying.txt", included in this
 #    distribution, for details about the copyright.
@@ -65,10 +65,10 @@ proc sameMethodBucket(a, b: PSym): bool =
         break 
     if sameType(aa, bb) or
         (aa.kind == tyObject) and (bb.kind == tyObject) and
-        (inheritanceDiff(bb, aa) < 0): 
+        (inheritanceDiff(bb, aa) < 0):
       nil
-    else: 
-      return 
+    else:
+      return
   result = true
 
 proc attachDispatcher(s: PSym, dispatcher: PNode) =
@@ -106,17 +106,16 @@ proc methodDef*(s: PSym, fromCache: bool) =
     # attach to itself to prevent bugs:
     attachDispatcher(disp, newSymNode(disp))
 
-proc relevantCol(methods: TSymSeq, col: int): bool = 
+proc relevantCol(methods: TSymSeq, col: int): bool =
   # returns true iff the position is relevant
   var t = methods[0].typ.sons[col]
-  result = false
-  if skipTypes(t, skipPtrs).kind == tyObject: 
-    for i in countup(1, high(methods)): 
-      if not SameType(methods[i].typ.sons[col], t): 
+  if skipTypes(t, skipPtrs).kind == tyObject:
+    for i in countup(1, high(methods)):
+      let t2 = skipTypes(methods[i].typ.sons[col], skipPtrs)
+      if not SameType(t2, t):
         return true
   
 proc cmpSignatures(a, b: PSym, relevantCols: TIntSet): int = 
-  result = 0
   for col in countup(1, sonsLen(a.typ) - 1): 
     if Contains(relevantCols, col): 
       var aa = skipTypes(a.typ.sons[col], skipPtrs)
diff --git a/compiler/sem.nim b/compiler/sem.nim
index c70cbf61e..77e93a066 100755
--- a/compiler/sem.nim
+++ b/compiler/sem.nim
@@ -1,7 +1,7 @@
 #
 #
 #           The Nimrod Compiler
-#        (c) Copyright 2012 Andreas Rumpf
+#        (c) Copyright 2013 Andreas Rumpf
 #
 #    See the file "copying.txt", included in this
 #    distribution, for details about the copyright.
@@ -45,6 +45,7 @@ proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode
 proc fixImmediateParams(n: PNode): PNode
 proc activate(c: PContext, n: PNode)
 proc semQuoteAst(c: PContext, n: PNode): PNode
+proc finishMethod(c: PContext, s: PSym)
 
 proc IndexTypesMatch(c: PContext, f, a: PType, arg: PNode): PNode
 
diff --git a/compiler/semcall.nim b/compiler/semcall.nim
index 0a5f19822..67d157261 100755
--- a/compiler/semcall.nim
+++ b/compiler/semcall.nim
@@ -1,7 +1,7 @@
 #
 #
 #           The Nimrod Compiler
-#        (c) Copyright 2012 Andreas Rumpf
+#        (c) Copyright 2013 Andreas Rumpf
 #
 #    See the file "copying.txt", included in this
 #    distribution, for details about the copyright.
@@ -15,8 +15,22 @@ proc sameMethodDispatcher(a, b: PSym): bool =
   if a.kind == skMethod and b.kind == skMethod: 
     var aa = lastSon(a.ast)
     var bb = lastSon(b.ast)
-    if aa.kind == nkSym and bb.kind == nkSym and aa.sym == bb.sym: 
-      result = true
+    if aa.kind == nkSym and bb.kind == nkSym:
+      if aa.sym == bb.sym: 
+        result = true
+    else:
+      nil
+      # generics have no dispatcher yet, so we need to compare the method
+      # names; however, the names are equal anyway because otherwise we
+      # wouldn't even consider them to be overloaded. But even this does
+      # not work reliably! See tmultim6 for an example:
+      # method collide[T](a: TThing, b: TUnit[T]) is instantiated and not
+      # method collide[T](a: TUnit[T], b: TThing)! This means we need to
+      # *instantiate* every candidate! However, we don't keep more than 2-3
+      # candidated around so we cannot implement that for now. So in order
+      # to avoid subtle problems, the call remains ambiguous and needs to
+      # be disambiguated by the programmer; this way the right generic is
+      # instantiated.
   
 proc resolveOverloads(c: PContext, n, orig: PNode, 
                       filter: TSymKinds): TCandidate =
diff --git a/compiler/seminst.nim b/compiler/seminst.nim
index 0a1a17f72..b54170435 100755
--- a/compiler/seminst.nim
+++ b/compiler/seminst.nim
@@ -186,6 +186,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
   popOwner()
   c.friendModule = oldFriend
   dec(c.InstCounter)
+  if result.kind == skMethod: finishMethod(c, result)
   
 proc instGenericContainer(c: PContext, n: PNode, header: PType): PType = 
   var cl: TReplTypeVars
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index c86e3eb91..363cff89a 100755
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -867,25 +867,26 @@ proc semIterator(c: PContext, n: PNode): PNode =
 proc semProc(c: PContext, n: PNode): PNode = 
   result = semProcAux(c, n, skProc, procPragmas)
 
+proc hasObjParam(s: PSym): bool =
+  var t = s.typ
+  for col in countup(1, sonsLen(t)-1):
+    if skipTypes(t.sons[col], skipPtrs).kind == tyObject:
+      return true
+
+proc finishMethod(c: PContext, s: PSym) =
+  if hasObjParam(s):
+    methodDef(s, false)
+
 proc semMethod(c: PContext, n: PNode): PNode = 
   if not isTopLevel(c): LocalError(n.info, errXOnlyAtModuleScope, "method")
   result = semProcAux(c, n, skMethod, methodPragmas)
   
   var s = result.sons[namePos].sym
-  var t = s.typ
-  var hasObjParam = false
-  
-  for col in countup(1, sonsLen(t)-1): 
-    if skipTypes(t.sons[col], skipPtrs).kind == tyObject: 
-      hasObjParam = true
-      break
-  
-  # XXX this not really correct way to do it: Perhaps it should be done after
-  # generic instantiation. Well it's good enough for now: 
-  if hasObjParam:
-    methodDef(s, false)
-  else:
-    LocalError(n.info, errXNeedsParamObjectType, "method")
+  if not isGenericRoutine(s):
+    if hasObjParam(s):
+      methodDef(s, false)
+    else:
+      LocalError(n.info, errXNeedsParamObjectType, "method")
 
 proc semConverterDef(c: PContext, n: PNode): PNode = 
   if not isTopLevel(c): LocalError(n.info, errXOnlyAtModuleScope, "converter")
diff --git a/compiler/types.nim b/compiler/types.nim
index 23d202fdf..998ba43d2 100755
--- a/compiler/types.nim
+++ b/compiler/types.nim
@@ -1,7 +1,7 @@
 #
 #
 #           The Nimrod Compiler
-#        (c) Copyright 2012 Andreas Rumpf
+#        (c) Copyright 2013 Andreas Rumpf
 #
 #    See the file "copying.txt", included in this
 #    distribution, for details about the copyright.
@@ -876,20 +876,24 @@ proc inheritanceDiff*(a, b: PType): int =
   # | returns: -x iff `a` is the x'th direct superclass of `b`
   # | returns: +x iff `a` is the x'th direct subclass of `b`
   # | returns: `maxint` iff `a` and `b` are not compatible at all
+  assert a.kind == tyObject
+  assert b.kind == tyObject
   var x = a
   result = 0
   while x != nil:
+    x = skipTypes(x, skipPtrs)
     if sameObjectTypes(x, b): return 
     x = x.sons[0]
     dec(result)
   var y = b
   result = 0
   while y != nil:
+    y = skipTypes(y, skipPtrs)
     if sameObjectTypes(y, a): return 
     y = y.sons[0]
     inc(result)
   result = high(int)
-    
+
 proc typeAllowedAux(marker: var TIntSet, typ: PType, kind: TSymKind): bool
 proc typeAllowedNode(marker: var TIntSet, n: PNode, kind: TSymKind): bool = 
   result = true
diff --git a/tests/run/tmultim6.nim b/tests/run/tmultim6.nim
new file mode 100644
index 000000000..a55b69e37
--- /dev/null
+++ b/tests/run/tmultim6.nim
@@ -0,0 +1,30 @@
+discard """
+  output: "collide: unit, thing | collide: unit, thing | collide: thing, unit"
+"""
+# Test multi methods
+
+type
+  TThing = object {.inheritable.}
+  TUnit[T] = object of TThing
+    x: T
+  TParticle = object of TThing
+    a, b: int
+    
+method collide(a, b: TThing) {.inline.} =
+  quit "to override!"
+  
+method collide[T](a: TThing, b: TUnit[T]) {.inline.} =
+  write stdout, "collide: thing, unit | "
+
+method collide[T](a: TUnit[T], b: TThing) {.inline.} =
+  write stdout, "collide: unit, thing | "
+
+proc test(a, b: TThing) {.inline.} =
+  collide(a, b)
+
+var
+  a: TThing
+  b, c: TUnit[string]
+collide(b, TThing(c))
+test(b, c)
+collide(a, b)
diff --git a/todo.txt b/todo.txt
index dd3290117..ab7c4f2f7 100755
--- a/todo.txt
+++ b/todo.txt
@@ -15,7 +15,6 @@ version 0.9.X
 - test&finish first class iterators:
   * nested iterators
 - implement the missing features wrt inheritance
-- implement generic methods
 - improve the compiler as a service
 - ``=`` should be overloadable; requires specialization for ``=``
 - implement constructors + full 'not nil' checking
diff --git a/web/news.txt b/web/news.txt
index 824e0c3a3..e4e9108e0 100755
--- a/web/news.txt
+++ b/web/news.txt
@@ -57,6 +57,7 @@ Language Additions
   symbol forwarding so client modules don't have to import a module's 
   dependencies explicitly.
 - Overloading based on ASTs has been implemented.
+- Generics are now supported for multi methods.
 
 
 2012-09-23 Version 0.9.0 released