summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--compiler/semexprs.nim4
-rw-r--r--compiler/semstmts.nim6
-rw-r--r--compiler/sigmatch.nim42
-rw-r--r--tests/concepts/tmapconcept.nim102
-rw-r--r--tests/concepts/tstackconcept.nim2
5 files changed, 145 insertions, 11 deletions
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index b3fe83ed8..d81abe20e 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -319,11 +319,11 @@ proc isOpImpl(c: PContext, n: PNode): PNode =
       result = newIntNode(nkIntLit, 0)
   else:
     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)
+    discard inferTypeClassParam(m, t1, rhsOrigType)
     let match = typeRel(m, t2, t1) >= isSubtype # isNone
     result = newIntNode(nkIntLit, ord(match))
 
@@ -912,8 +912,6 @@ const
   tyTypeParamsHolders = {tyGenericInst, tyUserTypeClassInst, tyCompositeTypeClass}
   tyDotOpTransparent = {tyVar, tyPtr, tyRef, tyAlias}
 
-
-
 proc readTypeParameter(c: PContext, typ: PType,
                        paramName: PIdent, info: TLineInfo): PNode =
   let ty = if typ.kind in {tyGenericInst, tyUserTypeClassInst}: typ.skipGenericAlias
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index 9a1850932..33b684e91 100644
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -1631,8 +1631,12 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
           of nkPragma, nkCommentStmt, nkNilLit, nkEmpty: discard
           else: localError(n.sons[j].info, errStmtInvalidAfterReturn)
       else: discard
-  if result.len == 1 and result.sons[0].kind != nkDefer:
+  
+  if result.len == 1 and
+     c.inTypeClass == 0 and # concept bodies should be preserved as a stmt list
+     result.sons[0].kind != nkDefer:
     result = result.sons[0]
+
   when defined(nimfix):
     if result.kind == nkCommentStmt and not result.comment.isNil and
         not (result.comment[0] == '#' and result.comment[1] == '#'):
diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim
index f819af13d..3dede8b6f 100644
--- a/compiler/sigmatch.nim
+++ b/compiler/sigmatch.nim
@@ -645,7 +645,7 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate,
     put(m.bindings, p[1], p[0].typ)
 
   if ff.kind == tyUserTypeClassInst:
-    result = generateTypeInstance(c, m.bindings, m.call.info, ff)
+    result = generateTypeInstance(c, m.bindings, ff.sym.info, ff)
   else:
     result = copyType(ff, ff.owner, true)
 
@@ -1403,13 +1403,34 @@ proc skipToInferrableParam(tt: PType): PType =
 
   return nil
 
-proc inferTypeClassParam*(c: PContext, f, a: PType): bool =
+proc inferTypeClassParam*(m: var TCandidate, f, a: PType): bool =
+  var c = m.c
   if c.inTypeClass == 0: return false
 
   var inferrableType = a.skipToInferrableParam
   if inferrableType == nil: return false
 
-  inferrableType.assignType f
+  var inferAs = f
+  
+  case f.kind
+  of tyGenericParam:
+    var prev  = PType(idTableGet(m.bindings, f))
+    if prev != nil: inferAs = prev
+  
+  of tyFromExpr:
+    let computedType = tryResolvingStaticExpr(m, f.n).typ
+    case computedType.kind
+    of tyTypeDesc:
+      inferAs = computedType.base
+    of tyStatic:
+      inferAs = computedType
+    else:
+      localError(f.n.info, errTypeExpected)
+  
+  else:
+    discard
+    
+  inferrableType.assignType inferAs
   return true
 
 proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
@@ -1420,7 +1441,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
     argType = argType
     c = m.c
 
-  if inferTypeClassParam(c, f, argType):
+  if inferTypeClassParam(m, f, argType):
     return argSemantized
 
   if tfHasStatic in fMaybeStatic.flags:
@@ -1448,9 +1469,18 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
         arg.typ.n = evaluated
         argType = arg.typ
 
-  var a = argType
-  var r = typeRel(m, f, a)
+  var
+    useTypeLoweringRuleInTypeClass = c.inTypeClass > 0 and
+                                     not m.isNoCall and
+                                     f.kind != tyTypeDesc
 
+    a = if useTypeLoweringRuleInTypeClass:
+          argType.skipTypes({tyTypeDesc, tyFieldAccessor})
+        else:
+          argType
+    
+    r = typeRel(m, f, a)
+  
   if r != isNone and m.calleeSym != nil and
      m.calleeSym.kind in {skMacro, skTemplate}:
     # XXX: duplicating this is ugly, but we cannot (!) move this
diff --git a/tests/concepts/tmapconcept.nim b/tests/concepts/tmapconcept.nim
new file mode 100644
index 000000000..81caed7c6
--- /dev/null
+++ b/tests/concepts/tmapconcept.nim
@@ -0,0 +1,102 @@
+discard """
+output: '''10
+10
+nil
+1'''
+msg: '''
+K=string V=int
+K=int64 V=string
+K=int V=int
+'''
+"""
+
+import tables, typetraits
+
+template ok(check) = assert check
+template no(check) = assert(not check)
+
+type
+  Enumerable[T] = concept e
+    for v in e:
+      v is T
+
+  Map[K, V] = concept m, var mvar
+    m[K] is V
+    mvar[K] = V
+    m.contains(K) is bool
+    m.valuesSeq is Enumerable[V]
+
+  TreeMap[K, V] = object
+    root: int
+
+  SparseSeq = object
+    data: seq[int]
+
+  JudyArray = object
+    data: SparseSeq
+
+static:
+  ok seq[int] is Enumerable[int]
+  ok seq[string] is Enumerable
+  ok seq[int] is Enumerable[SomeNumber]
+  ok SparseSeq.data is Enumerable
+  no seq[string] is Enumerable[int]
+  no int is Enumerable
+  no int is Enumerable[int]
+
+# Complete the map concept implementation for the Table type
+proc valuesSeq[K, V](t: Table[K, V]): seq[V]  =
+  result = @[]
+  for k, v in t:
+    result.add v
+
+# Map concept inplementation for TreeMap
+proc valuesSeq(t: TreeMap): array[1, TreeMap.V] =
+  var v: t.V
+  result = [v]
+
+proc contains[K, V](t: TreeMap[K, V], key: K): bool = true
+
+proc `[]=`[K, V](t: var TreeMap[K, V], key: K, val: V) = discard
+proc `[]`(t: TreeMap, key: t.K): TreeMap.V = discard
+
+# Map concept implementation for the non-generic JudyArray
+proc valuesSeq(j: JudyArray): SparseSeq = j.data
+
+proc contains(t: JudyArray, key: int): bool = true
+
+proc `[]=`(t: var JudyArray, key, val: int) = discard
+proc `[]`(t: JudyArray, key: int): int = discard
+
+iterator items(s: SparseSeq): int =
+  for i in s.data: yield i
+
+# Generic proc defined over map
+proc getFirstValue[K,V](m : Map[K,V]): V =
+  static: echo "K=", K.name, " V=", V.name
+
+  for i in m.valuesSeq:
+    return i
+
+  raise newException(RangeError, "no values")
+
+proc useConceptProcInGeneric[K, V](t: Table[K, V]): V =
+  return t.getFirstValue
+
+var t = initTable[string, int]()
+t["test"] = 10
+
+echo t.getFirstValue
+echo t.useConceptProcInGeneric
+
+var tm = TreeMap[int64, string](root: 0)
+echo getFirstValue(tm)
+
+var j = JudyArray(data: SparseSeq(data: @[1, 2, 3]))
+echo getFirstValue(j)
+
+static:
+  ok Table[int, float] is Map
+  ok Table[int, string] is Map[SomeNumber, string]
+  no JudyArray is Map[string, int]
+
diff --git a/tests/concepts/tstackconcept.nim b/tests/concepts/tstackconcept.nim
index 11d6505cf..b6ead2c2b 100644
--- a/tests/concepts/tstackconcept.nim
+++ b/tests/concepts/tstackconcept.nim
@@ -46,7 +46,7 @@ proc implicitGeneric(s: var Stack): auto =
   static:
     echo "IMPLICIT INFERRED ", s.T.name, " ", Stack.T.name
     echo "IMPLICIT VALUE TYPE ", s.ValueType.name, " ", Stack.ValueType.name
-    echo "IMPLICIT VALUE TYPE NAME ", s.ValueTypeName, " ", s.ValueTypeName
+    echo "IMPLICIT VALUE TYPE NAME ", s.ValueTypeName, " ", Stack.ValueTypeName
 
   return s.pop()