summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rwxr-xr-xcompiler/ast.nim2
-rwxr-xr-xcompiler/semexprs.nim16
-rwxr-xr-xcompiler/seminst.nim11
-rwxr-xr-xcompiler/semtypes.nim19
-rwxr-xr-xcompiler/sigmatch.nim7
-rwxr-xr-xcompiler/types.nim4
-rwxr-xr-xdoc/manual.txt27
-rwxr-xr-xlib/system.nim25
-rw-r--r--tests/run/trettypeinference.nim29
9 files changed, 104 insertions, 36 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim
index 814784029..c6e4d8318 100755
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -350,6 +350,8 @@ type
                       # pass of semProcTypeNode performed after instantiation.
                       # this won't be needed if we don't perform this redundant
                       # second pass (stay tuned).
+    tfRetType         # marks return types in proc (used to detect type classes 
+                      # used as return types for return type inference)
     tfAll,            # type class requires all constraints to be met (default)
     tfAny,            # type class requires any constraint to be met
     tfCapturesEnv,    # whether proc really captures some environment
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index 7c147e778..4608d38ef 100755
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -1048,8 +1048,20 @@ proc semAsgn(c: PContext, n: PNode): PNode =
     localError(a.info, errXCannotBeAssignedTo, 
                renderTree(a, {renderNoComments}))
   else:
-    n.sons[1] = semExprWithType(c, n.sons[1])
-    n.sons[1] = fitNode(c, le, n.sons[1])
+    var 
+      rhs = semExprWithType(c, n.sons[1])
+      lhs = n.sons[0]
+    if lhs.kind == nkSym and lhs.sym.kind == skResult and
+       lhs.sym.typ.kind == tyGenericParam:
+      if matchTypeClass(lhs.typ, rhs.typ):
+        InternalAssert c.p.resultSym != nil
+        lhs.typ = rhs.typ
+        c.p.resultSym.typ = rhs.typ
+        c.p.owner.typ.sons[0] = rhs.typ
+      else:
+        typeMismatch(n, lhs.typ, rhs.typ)
+
+    n.sons[1] = fitNode(c, le, rhs)
     fixAbstractType(c, n)
     asgnToResultVar(c, n, n.sons[0], n.sons[1])
   result = n
diff --git a/compiler/seminst.nim b/compiler/seminst.nim
index 61210c0f8..8e164531a 100755
--- a/compiler/seminst.nim
+++ b/compiler/seminst.nim
@@ -25,8 +25,13 @@ proc instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable,
     s.flags = s.flags + {sfUsed, sfFromGeneric}
     var t = PType(IdTableGet(pt, q.typ))
     if t == nil:
-      LocalError(a.info, errCannotInstantiateX, s.name.s)
-      t = errorType(c)
+      if tfRetType in q.typ.flags:
+        # keep the generic type and allow the return type to be bound 
+        # later by semAsgn in return type inference scenario
+        t = q.typ
+      else:
+        LocalError(a.info, errCannotInstantiateX, s.name.s)
+        t = errorType(c)
     elif t.kind == tyGenericParam: 
       InternalError(a.info, "instantiateGenericParamList: " & q.name.s)
     elif t.kind == tyGenericInvokation:
@@ -163,7 +168,6 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
     result.typ = newTypeS(tyProc, c)
     rawAddSon(result.typ, nil)
   result.typ.callConv = fn.typ.callConv
-  ParamsTypeCheck(c, result.typ)
   var oldPrc = GenericCacheGet(c, entry)
   if oldPrc == nil:
     c.generics.generics.add(entry)
@@ -174,6 +178,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
     if fn.kind != skTemplate:
       instantiateBody(c, n, result)
       sideEffectsCheck(c, result)
+    ParamsTypeCheck(c, result.typ)
   else:
     result = oldPrc
   popInfoContext()
diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim
index 5362d6d4a..eeb48a647 100755
--- a/compiler/semtypes.nim
+++ b/compiler/semtypes.nim
@@ -584,22 +584,20 @@ proc paramTypeClass(c: PContext, paramType: PType, procKind: TSymKind):
       result.typ = newTypeS(tyTypeDesc, c)
       result.typ.sons = paramType.sons
   of tyDistinct:
-    # type T1 = distinct expr
-    # type S1 = distinct Sortable
-    # proc x(a, b: T1, c, d: S1)
-    # This forces bindOnce behavior for the type class, equivalent to
-    # proc x[T, S](a, b: T, c, d: S)
     result = paramTypeClass(c, paramType.lastSon, procKind)
-    result.id = paramType.sym.name
+    # disable the bindOnce behavior for the type class
+    result.id = nil
+    return
   of tyGenericBody:
     # type Foo[T] = object
     # proc x(a: Foo, b: Foo) 
     result.typ = newTypeS(tyTypeClass, c)
     result.typ.addSonSkipIntLit(paramType)
-    result.id = paramType.sym.name # bindOnce by default
   of tyTypeClass:
     result.typ = copyType(paramType, getCurrOwner(), false)
   else: nil
+  # bindOnce by default
+  if paramType.sym != nil: result.id = paramType.sym.name
 
 proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
                    paramType: PType, paramName: string,
@@ -619,7 +617,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
       let s = SymtabGet(c.tab, paramTypId)
       # tests/run/tinterf triggers this:
       if s != nil: result = s.typ
-      else: 
+      else:
         LocalError(info, errCannotInstantiateX, paramName)
         result = errorType(c)
     else:
@@ -684,8 +682,8 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
     if skipTypes(typ, {tyGenericInst}).kind == tyEmpty: continue
     for j in countup(0, length-3): 
       var arg = newSymG(skParam, a.sons[j], c)
-      var finalType = liftParamType(c, kind, genericParams, typ, arg.name.s,
-                                    arg.info).skipIntLit
+      var finalType = liftParamType(c, kind, genericParams, typ,
+                                    arg.name.s, arg.info).skipIntLit
       arg.typ = finalType
       arg.position = counter
       inc(counter)
@@ -703,6 +701,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
     if skipTypes(r, {tyGenericInst}).kind != tyEmpty:
       if r.sym == nil or sfAnon notin r.sym.flags:
         r = liftParamType(c, kind, genericParams, r, "result", n.sons[0].info)
+        r.flags.incl tfRetType
       result.sons[0] = skipIntLit(r)
       res.typ = result.sons[0]
 
diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim
index 7e482b3d2..82a8c9399 100755
--- a/compiler/sigmatch.nim
+++ b/compiler/sigmatch.nim
@@ -282,7 +282,12 @@ proc matchTypeClass(c: var TCandidate, typeClass, t: PType): TTypeRelation =
   # if the loop finished without returning, either all constraints matched
   # or none of them matched.
   result = if tfAny in typeClass.flags: isNone else: isGeneric
-  
+
+proc matchTypeClass*(typeClass, typ: PType): bool =
+  var c: TCandidate
+  InitCandidate(c, typeClass)
+  result = matchTypeClass(c, typeClass, typ) == isGeneric
+
 proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
   proc inconsistentVarTypes(f, a: PType): bool {.inline.} =
     result = f.kind != a.kind and (f.kind == tyVar or a.kind == tyVar)
diff --git a/compiler/types.nim b/compiler/types.nim
index d8879f1b4..b650b49b8 100755
--- a/compiler/types.nim
+++ b/compiler/types.nim
@@ -626,9 +626,9 @@ proc SameTypeOrNil*(a, b: PType, flags: TTypeCmpFlags = {}): bool =
       var c = initSameTypeClosure()
       c.flags = flags
       result = SameTypeAux(a, b, c)
-  
+
 proc equalParam(a, b: PSym): TParamsEquality = 
-  if SameTypeOrNil(a.typ, b.typ): 
+  if SameTypeOrNil(a.typ, b.typ, {TypeDescExactMatch}): 
     if a.ast == b.ast: 
       result = paramsEqual
     elif a.ast != nil and b.ast != nil: 
diff --git a/doc/manual.txt b/doc/manual.txt
index 4b7dc1aa5..4d9375f30 100755
--- a/doc/manual.txt
+++ b/doc/manual.txt
@@ -2940,6 +2940,13 @@ from the proc body. This is usually used with the ``auto`` type class:
 .. code-block:: nimrod
   proc makePair(a, b): auto = (first: a, second: b)
 
+The return type will be treated as additional generic param and can be
+explicitly specified at call sites as any other generic param.
+
+Future versions of nimrod may also support overloading based on the return type
+of the overloads. In such settings, the expected result type at call sites may 
+also influence the inferred return type.
+
 Symbol lookup in generics
 ~~~~~~~~~~~~~~~~~~~~~~~~~
 
@@ -3431,7 +3438,7 @@ typedesc
 
 `typedesc` is a special type allowing you to treat types as compile-time values
 (i.e. if types are compile-time values and all values have a type, then 
- typedesc must be their type).
+typedesc must be their type).
 
 When used as a regular proc param, typedesc acts as a type class. The proc
 will be instantiated for each unique type parameter and you can refer to the
@@ -3642,15 +3649,20 @@ proc with no side effects:
 
 destructor pragma
 -----------------
-`RAII`:idx:
-`automatic variables`:idx:
-`destructors`:idx:
+
 The `destructor` pragma is used to mark a proc to act as a type destructor.
-The proc must have a single parameter, having a concrete type. 
+The proc must have a single parameter with a concrete type (the name of a
+generic type is allowed too).
+
 Destructors will be automatically invoked when a local stack variable goes 
-out of scope. If a record type features a field with destructable type and 
+out of scope.
+
+If a record type features a field with destructable type and 
 the user have not provided explicit implementation, Nimrod will automatically
-generate a destructor for the record type.
+generate a destructor for the record type. Nimrod will automatically insert
+calls to any base class destructors in both user-defined and generated
+destructors.
+
 
 procvar pragma
 --------------
@@ -3658,7 +3670,6 @@ The `procvar`:idx: pragma is used to mark a proc that it can be passed to a
 procedural variable.
 
 
-
 compileTime pragma
 ------------------
 The `compileTime`:idx: pragma is used to mark a proc to be used at compile
diff --git a/lib/system.nim b/lib/system.nim
index 575c6abc7..4cfa6ba84 100755
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -51,10 +51,11 @@ type
   `nil` {.magic: "Nil".}
   expr* {.magic: Expr.} ## meta type to denote an expression (for templates)
   stmt* {.magic: Stmt.} ## meta type to denote a statement (for templates)
-  typeDesc* {.magic: TypeDesc.} ## meta type to denote
-                                ## a type description (for templates)
-  void* {.magic: "VoidType".}  ## meta type to denote the absense of any type
-  
+  typeDesc* {.magic: TypeDesc.} ## meta type to denote a type description
+  void* {.magic: "VoidType".}   ## meta type to denote the absense of any type
+  auto* = expr
+  any* = distinct auto
+
   TSignedInt* = int|int8|int16|int32|int64
     ## type class matching all signed integer types
 
@@ -111,6 +112,11 @@ proc new*[T](a: var ref T) {.magic: "New", noSideEffect.}
   ## creates a new object of type ``T`` and returns a safe (traced)
   ## reference to it in ``a``.
 
+proc new(T: typedesc): ref T =
+  ## creates a new object of type ``T`` and returns a safe (traced)
+  ## reference to it as result value
+  new(result)
+  
 proc internalNew*[T](a: var ref T) {.magic: "New", noSideEffect.}
   ## leaked implementation detail. Do not use.
 
@@ -538,7 +544,7 @@ proc abs*(x: int64): int64 {.magic: "AbsI64", noSideEffect.}
   ## checking is turned on).
 
 type
-  IntMax32 = distinct int|int8|int16|int32
+  IntMax32 = int|int8|int16|int32
 
 proc `+%` *(x, y: IntMax32): IntMax32 {.magic: "AddU", noSideEffect.}
 proc `+%` *(x, y: Int64): Int64 {.magic: "AddU", noSideEffect.}
@@ -1315,11 +1321,10 @@ iterator items*(a: cstring): char {.inline.} =
     yield a[i]
     inc(i)
 
-when not defined(booting):
-  iterator items*(E: typedesc[enum]): E =
-    ## iterates over the values of the enum ``E``.
-    for v in low(E)..high(E):
-      yield v
+iterator items*(E: typedesc[enum]): E =
+  ## iterates over the values of the enum ``E``.
+  for v in low(E)..high(E):
+    yield v
 
 iterator pairs*[T](a: openarray[T]): tuple[key: int, val: T] {.inline.} =
   ## iterates over each item of `a`. Yields ``(index, a[index])`` pairs.
diff --git a/tests/run/trettypeinference.nim b/tests/run/trettypeinference.nim
new file mode 100644
index 000000000..eea5b597d
--- /dev/null
+++ b/tests/run/trettypeinference.nim
@@ -0,0 +1,29 @@
+discard """
+  msg:    "instantiated for string\ninstantiated for int\ninstantiated for bool"
+  output: "int\nseq[string]\nA\nB\n100\ntrue"
+"""
+
+import typetraits
+
+proc plus(a, b): auto = a + b
+
+proc `+`(a, b: string): seq[string] = @[a, b]
+
+var i = plus(10, 20)
+var s = plus("A", "B")
+
+echo i.type.name
+echo s.type.name
+
+proc inst(a): auto =
+  static: echo "instantiated for ", a.type.name
+  result = a
+
+echo inst("A")
+echo inst("B")
+echo inst(100)
+echo inst(true)
+
+# XXX: [string, tyGenericParam] is cached instead of [string, string]
+# echo inst[string, string]("C")
+