summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--compiler/ast.nim1
-rw-r--r--compiler/semexprs.nim4
-rw-r--r--compiler/semtypes.nim3
-rw-r--r--compiler/sigmatch.nim38
-rw-r--r--tests/concepts/tstackconcept.nim49
5 files changed, 93 insertions, 2 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim
index 26305cf3b..4a25e0336 100644
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -482,6 +482,7 @@ type
     tfHasStatic
     tfGenericTypeParam
     tfImplicitTypeParam
+    tfInferrableTypeClassTypeParam
     tfWildcard        # consider a proc like foo[T, I](x: Type[T, I])
                       # T and I here can bind to both typedesc and static types
                       # before this is determined, we'll consider them to be a
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index 4d698dbfc..cb16bf406 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -318,7 +318,9 @@ proc isOpImpl(c: PContext, n: PNode): PNode =
     else:
       result = newIntNode(nkIntLit, 0)
   else:
-    var t2 = n[2].typ.skipTypes({tyTypeDesc})
+    var rhsOrigType = n[2].typ
+    discard inferTypeClassParam(c, t1, rhsOrigType)
+    var t2 = rhsOrigType.skipTypes({tyTypeDesc})
     maybeLiftType(t2, c, n.info)
     var m: TCandidate
     initCandidate(c, m, t2)
diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim
index e86b527d6..e3c3123f9 100644
--- a/compiler/semtypes.nim
+++ b/compiler/semtypes.nim
@@ -883,7 +883,8 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
     for i in 1 .. <paramType.len:
       let lifted = liftingWalk(paramType.sons[i])
       if lifted != nil: paramType.sons[i] = lifted
-    when false:
+
+    if paramType.base.lastSon.kind == tyUserTypeClass:
       let expanded = instGenericContainer(c, info, paramType,
                                           allowMetaTypes = true)
       result = liftingWalk(expanded, true)
diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim
index 164fd0999..b279e7d2d 100644
--- a/compiler/sigmatch.nim
+++ b/compiler/sigmatch.nim
@@ -585,6 +585,8 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate,
     dec c.inTypeClass
     closeScope(c)
 
+  var typeParams: seq[(PSym, PType)]
+
   if ff.kind == tyUserTypeClassInst:
     for i in 1 .. <(ff.len - 1):
       var
@@ -607,7 +609,12 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate,
         param = paramSym skType
         param.typ = makeTypeDesc(c, typ)
 
+      if typ.isMetaType:
+        param.typ.flags.incl tfInferrableTypeClassTypeParam
+
       addDecl(c, param)
+      typeParams.safeAdd((param, typ))
+
       #echo "A ", param.name.s, " ", typeToString(param.typ), " ", param.kind
 
   for param in body.n[0]:
@@ -630,6 +637,13 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate,
 
   var checkedBody = c.semTryExpr(c, body.n[3].copyTree)
   if checkedBody == nil: return isNone
+
+  # The inferrable type params have been identified during the semTryExpr above.
+  # We need to put them in the current sigmatch's binding table in order for them
+  # to be resolvable while matching the rest of the parameters
+  for p in typeParams:
+    put(m.bindings, p[0].typ, p[1])
+
   return isGeneric
 
 proc shouldSkipDistinct(rules: PNode, callIdent: PIdent): bool =
@@ -1355,6 +1369,27 @@ proc incMatches(m: var TCandidate; r: TTypeRelation; convMatch = 1) =
   of isEqual: inc(m.exactMatches)
   of isNone: discard
 
+proc skipToInferrableParam(tt: PType): PType =
+  var t = tt
+  while t != nil:
+    if tfInferrableTypeClassTypeParam in t.flags:
+      return t
+    if t.sonsLen > 0 and t.kind == tyTypeDesc:
+      t = t.base
+    else:
+      return nil
+
+  return nil
+
+proc inferTypeClassParam*(c: PContext, f, a: PType): bool =
+  if c.inTypeClass == 0: return false
+
+  var inferrableType = a.skipToInferrableParam
+  if inferrableType == nil: return false
+
+  inferrableType.assignType f
+  return true
+
 proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
                         argSemantized, argOrig: PNode): PNode =
   var
@@ -1363,6 +1398,9 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
     argType = argType
     c = m.c
 
+  if inferTypeClassParam(c, f, argType):
+    return argSemantized
+
   if tfHasStatic in fMaybeStatic.flags:
     # XXX: When implicit statics are the default
     # this will be done earlier - we just have to
diff --git a/tests/concepts/tstackconcept.nim b/tests/concepts/tstackconcept.nim
new file mode 100644
index 000000000..3993ca534
--- /dev/null
+++ b/tests/concepts/tstackconcept.nim
@@ -0,0 +1,49 @@
+discard """
+output: "20\n10"
+msg: '''
+INFERRED int
+'''
+"""
+
+import typetraits
+
+template reject(e: expr) =
+  static: assert(not compiles(e))
+
+type
+  ArrayStack = object
+    data: seq[int]
+
+proc push(s: var ArrayStack, item: int) =
+  s.data.add item
+
+proc pop(s: var ArrayStack): int =
+  return s.data.pop()
+
+type
+  Stack[T] = concept var s
+    s.push(T)
+    s.pop() is T
+
+proc genericAlgorithm[T](s: var Stack[T], y: T) =
+  static: echo "INFERRED ", T.name
+
+  s.push(y)
+  echo s.pop
+
+proc implicitGeneric(s: var Stack): auto =
+  # static: echo "IMPLICIT INFERRED ", s.T.name, " ", Stack.T.name
+
+  return s.pop()
+
+var s = ArrayStack(data: @[])
+
+s.push 10
+s.genericAlgorithm 20
+echo s.implicitGeneric
+
+reject s.genericAlgorithm "x"
+reject s.genericAlgorithm 1.0
+reject "str".implicitGeneric
+reject implicitGeneric(10)
+