summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--compiler/semcall.nim30
-rw-r--r--compiler/semstmts.nim17
-rw-r--r--compiler/semtypes.nim2
-rw-r--r--compiler/sigmatch.nim24
-rw-r--r--doc/manual/generics.txt31
-rw-r--r--lib/system.nim11
6 files changed, 66 insertions, 49 deletions
diff --git a/compiler/semcall.nim b/compiler/semcall.nim
index e1f09c3d5..f707fc844 100644
--- a/compiler/semcall.nim
+++ b/compiler/semcall.nim
@@ -99,7 +99,10 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
           if cmp < 0: best = z   # x is better than the best so far
           elif cmp == 0: alt = z # x is as good as the best so far
       elif errors != nil or z.diagnostics != nil:
-        errors.safeAdd((sym, int z.mutabilityProblem, z.diagnostics))
+        errors.safeAdd(CandidateError(
+          sym: sym,
+          unmatchedVarParam: int z.mutabilityProblem,
+          diagnostics: z.diagnostics))
     else:
       # Symbol table has been modified. Restart and pre-calculate all syms
       # before any further candidate init and compare. SLOW, but rare case.
@@ -126,9 +129,9 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors):
   # we do a pre-analysis. If all types produce the same string, we will add
   # module information.
   let proto = describeArgs(c, n, 1, preferName)
-  for err, mut, diagnostics in items(errors):
+  for err in errors:
     var errProto = ""
-    let n = err.typ.n
+    let n = err.sym.typ.n
     for i in countup(1, n.len - 1):
       var p = n.sons[i]
       if p.kind == nkSym:
@@ -140,16 +143,17 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors):
       break
 
   var candidates = ""
-  for err, mut, diagnostics in items(errors):
-    if err.kind in routineKinds and err.ast != nil:
-      add(candidates, renderTree(err.ast,
+  for err in errors:
+    if err.sym.kind in routineKinds and err.sym.ast != nil:
+      add(candidates, renderTree(err.sym.ast,
             {renderNoBody, renderNoComments, renderNoPragmas}))
     else:
-      add(candidates, err.getProcHeader(prefer))
+      add(candidates, err.sym.getProcHeader(prefer))
     add(candidates, "\n")
-    if mut != 0 and mut < n.len:
-      add(candidates, "for a 'var' type a variable needs to be passed, but '" & renderTree(n[mut]) & "' is immutable\n")
-    for diag in diagnostics:
+    if err.unmatchedVarParam != 0 and err.unmatchedVarParam < n.len:
+      add(candidates, "for a 'var' type a variable needs to be passed, but '" &
+                      renderTree(n[err.unmatchedVarParam]) & "' is immutable\n")
+    for diag in err.diagnostics:
       add(candidates, diag & "\n")
   
   result = (prefer, candidates)
@@ -180,7 +184,9 @@ proc bracketNotFoundError(c: PContext; n: PNode) =
   var symx = initOverloadIter(o, c, headSymbol)
   while symx != nil:
     if symx.kind in routineKinds:
-      errors.add((symx, 0, nil))
+      errors.add(CandidateError(sym: symx,
+                                unmatchedVarParam: 0,
+                                diagnostics: nil))
     symx = nextOverloadIter(o, c, headSymbol)
   if errors.len == 0:
     localError(n.info, "could not resolve: " & $n)
@@ -405,7 +411,7 @@ proc semOverloadedCall(c: PContext, n, nOrig: PNode,
       n.sons[1] = n.sons[1].sons[0]
       notFoundError(c, n, errors)
   else:
-    if efExplain notin flags:
+    if efExplain notin flags and c.compilesContextId == 0:
       # repeat the overload resolution,
       # this time enabling all the diagnostic output (this should fail again)
       discard semOverloadedCall(c, n, nOrig, filter, flags + {efExplain})
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index 12391a9cd..a678311bf 100644
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -1555,9 +1555,14 @@ proc usesResult(n: PNode): bool =
       for c in n:
         if usesResult(c): return true
 
-proc inferConceptStaticParam(c: PContext, typ: PType, n: PNode) =
+proc inferConceptStaticParam(c: PContext, inferred, n: PNode) =
+  var typ = inferred.typ
   let res = semConstExpr(c, n)
-  if not sameType(res.typ, typ.base): localError(n.info, "")
+  if not sameType(res.typ, typ.base):
+    localError(n.info,
+      "cannot infer the concept parameter '%s', due to a type mismatch. " &
+      "attempt to equate '%s' and '%s'.",
+      [inferred.renderTree, $res.typ, $typ.base])
   typ.n = res
 
 proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
@@ -1616,12 +1621,14 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
       if c.inTypeClass > 0 and expr.typ != nil:
         case expr.typ.kind
         of tyBool:
-          if expr.kind == nkInfix and expr[0].sym.name.s == "==":
+          if expr.kind == nkInfix and
+             expr[0].kind == nkSym and
+             expr[0].sym.name.s == "==":
             if expr[1].typ.isUnresolvedStatic:
-              inferConceptStaticParam(c, expr[1].typ, expr[2])
+              inferConceptStaticParam(c, expr[1], expr[2])
               continue
             elif expr[2].typ.isUnresolvedStatic:
-              inferConceptStaticParam(c, expr[2].typ, expr[1])
+              inferConceptStaticParam(c, expr[2], expr[1])
               continue
             
           let verdict = semConstExpr(c, n[i])
diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim
index 02166f0aa..422d2f0fa 100644
--- a/compiler/semtypes.nim
+++ b/compiler/semtypes.nim
@@ -1235,7 +1235,7 @@ proc symFromExpectedTypeNode(c: PContext, n: PNode): PSym =
   if n.kind == nkType:
     result = symFromType(n.typ, n.info)
   else:
-    localError(n.info, "xx")
+    localError(n.info, errTypeExpected)
     result = errorSym(c, n)
 
 proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim
index bbcf25903..ff7b0ae72 100644
--- a/compiler/sigmatch.nim
+++ b/compiler/sigmatch.nim
@@ -22,10 +22,10 @@ type
   TCandidateState* = enum
     csEmpty, csMatch, csNoMatch
 
-  CandidateError = tuple
-    sym: PSym
-    unmatchedVarParam: int
-    diagnostics: seq[string]
+  CandidateError* = object
+    sym*: PSym
+    unmatchedVarParam*: int
+    diagnostics*: seq[string]
   
   CandidateErrors* = seq[CandidateError]
 
@@ -589,8 +589,8 @@ proc typeRangeRel(f, a: PType): TTypeRelation {.noinline.} =
 proc matchUserTypeClass*(c: PContext, m: var TCandidate,
                          ff, a: PType): PType =
   var
-    Concept = ff.skipTypes({tyUserTypeClassInst})
-    body = Concept.n[3]
+    typeClass = ff.skipTypes({tyUserTypeClassInst})
+    body = typeClass.n[3]
   if c.inTypeClass > 4:
     localError(body.info, $body & " too nested for type matching")
     return nil
@@ -611,7 +611,7 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate,
         param: PSym
 
       template paramSym(kind): untyped =
-        newSym(kind, typeParamName, Concept.sym, Concept.sym.info)
+        newSym(kind, typeParamName, typeClass.sym, typeClass.sym.info)
 
       block addTypeParam:
         for prev in typeParams:
@@ -642,7 +642,7 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate,
       
       addDecl(c, param)
 
-  for param in Concept.n[0]:
+  for param in typeClass.n[0]:
     var
       dummyName: PNode
       dummyType: PType
@@ -665,7 +665,7 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate,
 
     internalAssert dummyName.kind == nkIdent
     var dummyParam = newSym(if modifier == tyTypeDesc: skType else: skVar,
-                            dummyName.ident, Concept.sym, Concept.sym.info)
+                            dummyName.ident, typeClass.sym, typeClass.sym.info)
     dummyParam.typ = dummyType
     addDecl(c, dummyParam)
 
@@ -675,7 +675,7 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate,
     errorPrefix: string
     flags: TExprFlags = {}
     collectDiagnostics = m.diagnostics != nil or
-                         sfExplain in Concept.sym.flags
+                         sfExplain in typeClass.sym.flags
   
   if collectDiagnostics:
     oldWriteHook = writelnHook
@@ -684,7 +684,7 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate,
     diagnostics = @[]
     flags = {efExplain}
     writelnHook = proc (s: string) =
-      if errorPrefix == nil: errorPrefix = Concept.sym.name.s & ":"
+      if errorPrefix == nil: errorPrefix = typeClass.sym.name.s & ":"
       let msg = s.replace("Error:", errorPrefix)
       if oldWriteHook != nil: oldWriteHook msg
       diagnostics.add msg
@@ -704,7 +704,7 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate,
     put(m, p[1], p[0].typ)
 
   if ff.kind == tyUserTypeClassInst:
-    result = generateTypeInstance(c, m.bindings, Concept.sym.info, ff)
+    result = generateTypeInstance(c, m.bindings, typeClass.sym.info, ff)
   else:
     result = copyType(ff, ff.owner, true)
 
diff --git a/doc/manual/generics.txt b/doc/manual/generics.txt
index bb5d0ab2c..87fcb7828 100644
--- a/doc/manual/generics.txt
+++ b/doc/manual/generics.txt
@@ -197,9 +197,6 @@ supply all type parameters of the generic type, because any missing ones will
 be inferred to have the equivalent of the `any` type class and thus they will
 match anything without discrimination.
 
-To help you write more concise implicitly generic procs, the Nim's system
-module includes the named types `T1` through `T9` which are bind once aliases
-of the `auto` type.
 
 Concepts
 --------
@@ -436,17 +433,35 @@ in the place of each missing generic param.
 Please note that generic concepts such as `Enumerable[T]` can be matched
 against concrete types such as `string`. Nim doesn't require the concept
 type to have the same number of parameters as the type being matched.
-In order to express such a requirement, you'll need to rely on a type
-mapping operator such a `genericHead` or `stripGenericParams` within the
-concept body:
+If you wish to express a requirement towards the generic parameters of
+the matched type, you can use a type mapping operator such as `genericHead`
+or `stripGenericParams` within the body of the concept to obtain the
+uninstantiated version of the type, which you can then try to instantiate
+in any required way. For example, here is how one might define the classic
+`Functor` concept from Haskell and then demonstrate that Nim's `Option[T]`
+type is an instance of it:
 
 .. code-block:: nim
   import future, typetraits
 
   type
     Functor[A] = concept f
-      f.value is A
-      map(f, A -> T1) is genericHead(f.type)[T1]
+      type MatchedGenericType = genericHead(f.type)
+        # `f` will be a value of a type such as `Option[T]`
+        # `MatchedGenericType` will become the `Option` type
+
+      f.val is A
+        # The Functor should provide a way to obtain
+        # a value stored inside it
+
+      type T = auto
+      map(f, A -> T) is MatchedGenericType[T]
+        # And it should provide a way to map one instance of
+        # the Functor to a instance of a different type, given
+        # a suitable `map` operation for the enclosed values
+
+  import options
+  echo Option[int] is Functor # prints true
 
 
 Concept derived values
diff --git a/lib/system.nim b/lib/system.nim
index 0e777b707..94e10d7df 100644
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -98,17 +98,6 @@ type
   SomeNumber* = SomeInteger|SomeReal
     ## type class matching all number types
 
-  T1* = auto
-  T2* = auto
-  T3* = auto
-  T4* = auto
-  T5* = auto
-  T6* = auto
-  T7* = auto
-  T8* = auto
-  T9* = auto
-    ## Helper types for writing implicitly generic procs
-
 proc defined*(x: untyped): bool {.magic: "Defined", noSideEffect, compileTime.}
   ## Special compile-time procedure that checks whether `x` is
   ## defined.