summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorAndreas Rumpf <rumpf_a@web.de>2015-08-25 22:21:40 +0200
committerAndreas Rumpf <rumpf_a@web.de>2015-08-25 22:21:40 +0200
commit19f58704ee33a85b6c42c38bf5c761f2ecf66af6 (patch)
treef7b0182f8cf871d18ce0c256521343ca4d77a00c
parent999c107c52f69b06e697db72af8c31a61d804193 (diff)
parent6da8c7857e41cc21dd3a0685417ac7e2f0465561 (diff)
downloadNim-19f58704ee33a85b6c42c38bf5c761f2ecf66af6.tar.gz
Merge pull request #3234 from petermora/mapAutoAuto
extending metatype matching in sigmatch.nim
-rw-r--r--compiler/sigmatch.nim65
-rw-r--r--tests/metatype/tprocbothmeta.nim10
2 files changed, 70 insertions, 5 deletions
diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim
index ef1c01b7a..357c6cf32 100644
--- a/compiler/sigmatch.nim
+++ b/compiler/sigmatch.nim
@@ -58,6 +58,10 @@ type
     isSubtype,
     isSubrange,              # subrange of the wanted type; no type conversion
                              # but apart from that counts as ``isSubtype``
+    isBothMetaConvertible    # generic proc parameter was matched against
+                             # generic type, e.g., map(mySeq, x=>x+1),
+                             # maybe recoverable by rerun if the parameter is
+                             # the proc's return value
     isInferred,              # generic proc was matched against a concrete type
     isInferredConvertible,   # same as above, but requiring proc CC conversion
     isGeneric,
@@ -389,9 +393,30 @@ proc inconsistentVarTypes(f, a: PType): bool {.inline.} =
   result = f.kind != a.kind and (f.kind == tyVar or a.kind == tyVar)
 
 proc procParamTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
-  var f = f
+  ## For example we have:
+  ## .. code-block:: nim
+  ##   proc myMap[T,S](sIn: seq[T], f: proc(x: T): S): seq[S] = ...
+  ##   proc innerProc[Q,W](q: Q): W = ...
+  ## And we want to match: myMap(@[1,2,3], innerProc)
+  ## This proc (procParamTypeRel) will do the following steps in
+  ## three different calls:
+  ## - matches f=T to a=Q. Since f is metatype, we resolve it
+  ##    to int (which is already known at this point). So in this case
+  ##    Q=int mapping will be saved to c.bindings.
+  ## - matches f=S to a=W. Both of these metatypes are unknown, so we
+  ##    return with isBothMetaConvertible to ask for rerun.
+  ## - matches f=S to a=W. At this point the return type of innerProc
+  ##    is known (we get it from c.bindings). We can use that value
+  ##    to match with f, and save back to c.bindings.
+  var
+    f = f
+    a = a
 
   if a.isMetaType:
+    let aResolved = PType(idTableGet(c.bindings, a))
+    if aResolved != nil:
+      a = aResolved
+  if a.isMetaType:
     if f.isMetaType:
       # We are matching a generic proc (as proc param)
       # to another generic type appearing in the proc
@@ -401,12 +426,15 @@ proc procParamTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
       f = generateTypeInstance(c.c, c.bindings, c.call.info, f)
       if f == nil or f.isMetaType:
         # no luck resolving the type, so the inference fails
-        return isNone
+        return isBothMetaConvertible
+    # Note that this typeRel call will save a's resolved type into c.bindings
     let reverseRel = typeRel(c, a, f)
     if reverseRel >= isGeneric:
       result = isInferred
       #inc c.genericMatches
   else:
+    # Note that this typeRel call will save f's resolved type into c.bindings
+    # if f is metatype.
     result = typeRel(c, f, a)
 
   if result <= isSubtype or inconsistentVarTypes(f, a):
@@ -450,8 +478,9 @@ proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
     elif f.callConv != a.callConv:
       # valid to pass a 'nimcall' thingie to 'closure':
       if f.callConv == ccClosure and a.callConv == ccDefault:
-        result = if result != isInferred: isConvertible
-                 else: isInferredConvertible
+        result = if result == isInferred: isInferredConvertible
+                 elif result == isBothMetaConvertible: isBothMetaConvertible
+                 else: isConvertible
       else:
         return isNone
     when useEffectSystem:
@@ -1225,7 +1254,7 @@ proc incMatches(m: var TCandidate; r: TTypeRelation; convMatch = 1) =
   case r
   of isConvertible, isIntConv: inc(m.convMatches, convMatch)
   of isSubtype, isSubrange: inc(m.subtypeMatches)
-  of isGeneric, isInferred: inc(m.genericMatches)
+  of isGeneric, isInferred, isBothMetaConvertible: inc(m.genericMatches)
   of isFromIntLit: inc(m.intConvMatches, 256)
   of isInferredConvertible:
     inc(m.convMatches)
@@ -1291,6 +1320,29 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
     put(m.bindings, f, inlined)
     return argSemantized
 
+  # If r == isBothMetaConvertible then we rerun typeRel.
+  # bothMetaCounter is for safety to avoid any infinite loop,
+  #  I don't have any example when it is needed.
+  # lastBindingsLenth is used to check whether m.bindings remains the same,
+  #  because in that case there is no point in continuing.
+  var bothMetaCounter = 0
+  var lastBindingsLength = -1
+  while r == isBothMetaConvertible and 
+      lastBindingsLength != m.bindings.counter and
+      bothMetaCounter < 100:
+    lastBindingsLength = m.bindings.counter
+    inc(bothMetaCounter)
+    if arg.kind in {nkProcDef, nkIteratorDef} + nkLambdaKinds:
+      result = c.semInferredLambda(c, m.bindings, arg)
+    elif arg.kind != nkSym:
+      return nil
+    else:
+      let inferred = c.semGenerateInstance(c, arg.sym, m.bindings, arg.info)
+      result = newSymNode(inferred, arg.info)
+    inc(m.convMatches)
+    arg = result
+    r = typeRel(m, f, arg.typ)
+
   case r
   of isConvertible:
     inc(m.convMatches)
@@ -1336,6 +1388,9 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
       result.typ = getInstantiatedType(c, arg, m, f)
     else:
       result = arg
+  of isBothMetaConvertible:
+    # This is the result for the 101th time.
+    result = nil
   of isFromIntLit:
     # too lazy to introduce another ``*matches`` field, so we conflate
     # ``isIntConv`` and ``isIntLit`` here:
diff --git a/tests/metatype/tprocbothmeta.nim b/tests/metatype/tprocbothmeta.nim
new file mode 100644
index 000000000..ad12c5d26
--- /dev/null
+++ b/tests/metatype/tprocbothmeta.nim
@@ -0,0 +1,10 @@
+
+proc myFun[A,B](x: A): B =
+  result = float(x+10)
+
+proc myMap[T,S](sIn: seq[T], f: proc (q: T): S): seq[S] =
+  result = newSeq[S](sIn.len)
+  for i in 0..<sIn.len:
+    result[i] = f(sIn[i])
+
+assert myMap(@[1,2,3], myFun) == @[11.0, 12.0, 13.0]