summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorZahary Karadjov <zahary@gmail.com>2012-03-21 23:48:03 +0200
committerZahary Karadjov <zahary@gmail.com>2012-03-22 03:27:00 +0200
commit3a5cf3d63a5f31809acf5c5f07d6f6a33c19af3d (patch)
tree4d8b1775a047536d3c01cf78ee842d15c58233cd
parent4f22326b2493642c1edb39ab262f1f7d0e81461c (diff)
downloadNim-3a5cf3d63a5f31809acf5c5f07d6f6a33c19af3d.tar.gz
expr params implemented for procs; paving the way for type classes
-rwxr-xr-xcompiler/seminst.nim44
-rwxr-xr-xcompiler/semstmts.nim4
-rwxr-xr-xcompiler/semtypes.nim24
-rwxr-xr-xcompiler/sigmatch.nim30
-rw-r--r--tests/run/tunittests.nim2
-rw-r--r--tests/run/uexpr.nim13
6 files changed, 88 insertions, 29 deletions
diff --git a/compiler/seminst.nim b/compiler/seminst.nim
index 1da6a671f..9ec78488b 100755
--- a/compiler/seminst.nim
+++ b/compiler/seminst.nim
@@ -8,6 +8,7 @@
 #
 
 # This module implements the instantiation of generic procs.
+# included from sem.nim
 
 proc instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable,
                                  entry: var TInstantiatedSymbol) = 
@@ -106,6 +107,35 @@ proc sideEffectsCheck(c: PContext, s: PSym) =
       s.ast.sons[genericParamsPos].kind == nkEmpty:
     c.threadEntries.add(s)
 
+template nimdbg: expr = c.filename.endsWith"nimdbg.nim"
+
+proc applyConcreteTypesToSig(genericProc: PSym, concTypes: seq[PType]): PType =
+  # XXX: This is intended to replace the use of semParamList in generateInstance.
+  # The results of semParamList's analysis are already encoded in the original
+  # proc type and any concrete types may be aplied directly over it.
+  # Besides being more efficient, it will remove the awkward case of
+  # genericParams == nil in semParamList.
+  # Currenly, it fails in some cases such as:
+  # proc inc2*[T](x: var ordinal[T], y = 1) {.magic: "Inc", noSideEffect.}
+  let sig = genericProc.typ
+  result = copyType(sig, getCurrOwner(), false)
+  result.n = sig.n.shallowCopy
+  
+  for i in countup(0, sig.len - 1):
+    let tOrig = sig.sons[i]
+    if tOrig == nil: continue        
+    let oGenParams = genericProc.ast.sons[genericParamsPos]
+    if skipTypes(tOrig, skipPtrs).kind in {tyGenericParam}:
+      var tConcrete = concTypes[tOrig.sym.position]
+      if i > 0:
+        let param = sig.n.sons[i].sym.copySym
+        param.typ = tConcrete
+        result.n.sons[i] = newSymNode(param)
+      result.sons[i] = tConcrete
+    else:
+      result.sons[i] = tOrig
+      if i > 0: result.n.sons[i] = sig.n.sons[i]
+
 proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, 
                       info: TLineInfo): PSym = 
   # generates an instantiated proc
@@ -133,12 +163,14 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
   instantiateGenericParamList(c, n.sons[genericParamsPos], pt, entry)
   n.sons[genericParamsPos] = ast.emptyNode
   # semantic checking for the parameters:
-  if n.sons[paramsPos].kind != nkEmpty: 
-    removeDefaultParamValues(n.sons[ParamsPos])
-    semParamList(c, n.sons[ParamsPos], nil, result)
-    # XXX: obsoleted - happens in semParamList # 
-    # addParams(c, result.typ.n)
-  else: 
+  if n.sons[paramsPos].kind != nkEmpty:
+    if false and nimdbg:
+      result.typ = applyConcreteTypesToSig(fn, entry.concreteTypes)
+      addParams(c, result.typ.n, fn.kind)
+    else:
+      removeDefaultParamValues(n.sons[ParamsPos])
+      semParamList(c, n.sons[ParamsPos], nil, result)
+  else:
     result.typ = newTypeS(tyProc, c)
     addSon(result.typ, nil)
   result.typ.callConv = fn.typ.callConv
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index e5f10ece1..2de496b9a 100755
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -641,8 +641,6 @@ proc semLambda(c: PContext, n: PNode): PNode =
     illFormedAst(n)           # process parameters:
   if n.sons[paramsPos].kind != nkEmpty: 
     semParamList(c, n.sons[ParamsPos], nil, s)
-    # XXX: obsoleted - happens in semParamList
-    # addParams(c, s.typ.n)
     ParamsTypeCheck(c, s.typ)
   else:
     s.typ = newTypeS(tyProc, c)
@@ -690,7 +688,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
         # we have a list of implicit type parameters:
         n.sons[genericParamsPos] = gp
         # check for semantics again:
-        semParamList(c, n.sons[ParamsPos], nil, s)
+        # semParamList(c, n.sons[ParamsPos], nil, s)
   else: 
     s.typ = newTypeS(tyProc, c)
     addSon(s.typ, nil)
diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim
index fc45de08c..c0ed5b237 100755
--- a/compiler/semtypes.nim
+++ b/compiler/semtypes.nim
@@ -520,6 +520,9 @@ proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind) =
   else:
     addDecl(c, param)
 
+proc isTypeClass(c: PContext, t: PType): bool =
+  return t.kind in {tyExpr}
+
 proc semProcTypeNode(c: PContext, n, genericParams: PNode, 
                      prev: PType, kind: TSymKind): PType = 
   var
@@ -536,7 +539,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
   addSon(result.n, res)
   var check = initIntSet()
   var counter = 0
-  for i in countup(1, sonsLen(n)-1): 
+  for i in countup(1, n.len - 1):
     var a = n.sons[i]
     if a.kind != nkIdentDefs: IllFormedAst(a)
     checkMinSonsLen(a, 3)
@@ -569,6 +572,21 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
     for j in countup(0, length-3): 
       var arg = newSymS(skParam, a.sons[j], c)
       arg.typ = typ
+      if kind notin {skTemplate, skMacro} and isTypeClass(c, typ):
+        let typeClassParamId = getIdent(":tcls_" & $i & "_" & $j)
+        if genericParams == nil:
+          # genericParams is nil when the proc is being instantiated
+          # the resolved type will be in scope then
+          var s = SymtabGet(c.tab, typeClassParamId)
+          arg.typ = s.typ
+        else:
+          var s = newSym(skType, typeClassParamId, getCurrOwner())
+          s.typ = newTypeS(tyGenericParam, c)
+          s.typ.sym = s
+          s.position = genericParams.len
+          genericParams.addSon(newSymNode(s))
+          arg.typ = s.typ
+
       arg.position = counter
       inc(counter)
       if def != nil and def.kind != nkEmpty: arg.ast = copyTree(def)
@@ -831,10 +849,14 @@ proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode =
           s = newSymS(skType, a.sons[j], c)
           s.typ = newTypeS(tyGenericParam, c)
         of tyExpr:
+          echo "GENERIC EXPR ", a.info.toFileLineCol
           # not a type param, but an expression
+          # proc foo[x: expr](bar: int) what is this?
           s = newSymS(skGenericParam, a.sons[j], c)
           s.typ = typ
         else:
+          # This handles cases like proc foo[t: tuple] 
+          # XXX: we want to turn that into a type class
           s = newSymS(skType, a.sons[j], c)
           s.typ = typ
       if def.kind != nkEmpty: s.ast = def
diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim
index ca65c670f..3db88569f 100755
--- a/compiler/sigmatch.nim
+++ b/compiler/sigmatch.nim
@@ -463,14 +463,8 @@ proc typeRel(mapping: var TIdTable, f, a: PType): TTypeRelation =
       result = isGeneric
     else: 
       result = typeRel(mapping, x, a) # check if it fits
-  of tyExpr, tyStmt, tyTypeDesc: 
-    if a.kind == f.kind: 
-      result = isEqual
-    else: 
-      case a.kind
-      of tyExpr, tyStmt, tyTypeDesc: result = isGeneric
-      of tyNil: result = isSubtype
-      else: nil
+  of tyExpr, tyStmt, tyTypeDesc:
+    result = isGeneric
   else: internalError("typeRel(" & $f.kind & ')')
   
 proc cmpTypes*(f, a: PType): TTypeRelation = 
@@ -514,9 +508,6 @@ proc userConvMatch(c: PContext, m: var TCandidate, f, a: PType,
 
 proc ParamTypesMatchAux(c: PContext, m: var TCandidate, f, a: PType, 
                         arg, argOrig: PNode): PNode =
-  if m.calleeSym != nil and m.calleeSym.kind in {skMacro, skTemplate} and 
-     f.kind in {tyExpr, tyStmt, tyTypeDesc}:
-    return argOrig
   var r = typeRel(m.bindings, f, a)
   case r
   of isConvertible: 
@@ -528,14 +519,17 @@ proc ParamTypesMatchAux(c: PContext, m: var TCandidate, f, a: PType,
   of isSubtype: 
     inc(m.subtypeMatches)
     result = implicitConv(nkHiddenSubConv, f, copyTree(arg), m, c)
-  of isGeneric: 
+  of isGeneric:
     inc(m.genericMatches)
-    result = copyTree(arg)
-    result.typ = getInstantiatedType(c, arg, m, f) 
-    # BUG: f may not be the right key!
-    if skipTypes(result.typ, abstractVar).kind in {tyTuple}:
-      result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c) 
-      # BUGFIX: use ``result.typ`` and not `f` here
+    if m.calleeSym != nil and m.calleeSym.kind in {skMacro, skTemplate}:
+      result = argOrig
+    else:
+      result = copyTree(arg)
+      result.typ = getInstantiatedType(c, arg, m, f) 
+      # BUG: f may not be the right key!
+      if skipTypes(result.typ, abstractVar).kind in {tyTuple}:
+        result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c) 
+        # BUGFIX: use ``result.typ`` and not `f` here
   of isEqual: 
     inc(m.exactMatches)
     result = copyTree(arg)
diff --git a/tests/run/tunittests.nim b/tests/run/tunittests.nim
index b2ec10cdc..e64008e0c 100644
--- a/tests/run/tunittests.nim
+++ b/tests/run/tunittests.nim
@@ -1,2 +1,2 @@
-import uclosures, utemplates
+import uclosures, utemplates, uexpr
 
diff --git a/tests/run/uexpr.nim b/tests/run/uexpr.nim
new file mode 100644
index 000000000..06bab375e
--- /dev/null
+++ b/tests/run/uexpr.nim
@@ -0,0 +1,13 @@
+import unittest
+
+proc concat(a, b): string =
+  result = $a & $b
+
+test "if proc param types are not supplied, the params are assumed to be generic":
+  check concat(1, "test") == "1test"
+  check concat(1, 20) == "120"
+  check concat("foo", "bar") == "foobar"
+
+test "explicit param types can still be specified":
+  check concat[cstring, cstring]("x", "y") == "xy"
+