support for matching generic types' procs in generic concepts
This commit is contained in:
parent
33f4e69759
commit
0a9a878bd3
5 changed files with 145 additions and 11 deletions
|
|
@ -319,11 +319,11 @@ proc isOpImpl(c: PContext, n: PNode): PNode =
|
||||||
result = newIntNode(nkIntLit, 0)
|
result = newIntNode(nkIntLit, 0)
|
||||||
else:
|
else:
|
||||||
var rhsOrigType = n[2].typ
|
var rhsOrigType = n[2].typ
|
||||||
discard inferTypeClassParam(c, t1, rhsOrigType)
|
|
||||||
var t2 = rhsOrigType.skipTypes({tyTypeDesc})
|
var t2 = rhsOrigType.skipTypes({tyTypeDesc})
|
||||||
maybeLiftType(t2, c, n.info)
|
maybeLiftType(t2, c, n.info)
|
||||||
var m: TCandidate
|
var m: TCandidate
|
||||||
initCandidate(c, m, t2)
|
initCandidate(c, m, t2)
|
||||||
|
discard inferTypeClassParam(m, t1, rhsOrigType)
|
||||||
let match = typeRel(m, t2, t1) >= isSubtype # isNone
|
let match = typeRel(m, t2, t1) >= isSubtype # isNone
|
||||||
result = newIntNode(nkIntLit, ord(match))
|
result = newIntNode(nkIntLit, ord(match))
|
||||||
|
|
||||||
|
|
@ -912,8 +912,6 @@ const
|
||||||
tyTypeParamsHolders = {tyGenericInst, tyUserTypeClassInst, tyCompositeTypeClass}
|
tyTypeParamsHolders = {tyGenericInst, tyUserTypeClassInst, tyCompositeTypeClass}
|
||||||
tyDotOpTransparent = {tyVar, tyPtr, tyRef, tyAlias}
|
tyDotOpTransparent = {tyVar, tyPtr, tyRef, tyAlias}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
proc readTypeParameter(c: PContext, typ: PType,
|
proc readTypeParameter(c: PContext, typ: PType,
|
||||||
paramName: PIdent, info: TLineInfo): PNode =
|
paramName: PIdent, info: TLineInfo): PNode =
|
||||||
let ty = if typ.kind in {tyGenericInst, tyUserTypeClassInst}: typ.skipGenericAlias
|
let ty = if typ.kind in {tyGenericInst, tyUserTypeClassInst}: typ.skipGenericAlias
|
||||||
|
|
|
||||||
|
|
@ -1631,8 +1631,12 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||||
of nkPragma, nkCommentStmt, nkNilLit, nkEmpty: discard
|
of nkPragma, nkCommentStmt, nkNilLit, nkEmpty: discard
|
||||||
else: localError(n.sons[j].info, errStmtInvalidAfterReturn)
|
else: localError(n.sons[j].info, errStmtInvalidAfterReturn)
|
||||||
else: discard
|
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]
|
result = result.sons[0]
|
||||||
|
|
||||||
when defined(nimfix):
|
when defined(nimfix):
|
||||||
if result.kind == nkCommentStmt and not result.comment.isNil and
|
if result.kind == nkCommentStmt and not result.comment.isNil and
|
||||||
not (result.comment[0] == '#' and result.comment[1] == '#'):
|
not (result.comment[0] == '#' and result.comment[1] == '#'):
|
||||||
|
|
|
||||||
|
|
@ -645,7 +645,7 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate,
|
||||||
put(m.bindings, p[1], p[0].typ)
|
put(m.bindings, p[1], p[0].typ)
|
||||||
|
|
||||||
if ff.kind == tyUserTypeClassInst:
|
if ff.kind == tyUserTypeClassInst:
|
||||||
result = generateTypeInstance(c, m.bindings, m.call.info, ff)
|
result = generateTypeInstance(c, m.bindings, ff.sym.info, ff)
|
||||||
else:
|
else:
|
||||||
result = copyType(ff, ff.owner, true)
|
result = copyType(ff, ff.owner, true)
|
||||||
|
|
||||||
|
|
@ -1403,13 +1403,34 @@ proc skipToInferrableParam(tt: PType): PType =
|
||||||
|
|
||||||
return nil
|
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
|
if c.inTypeClass == 0: return false
|
||||||
|
|
||||||
var inferrableType = a.skipToInferrableParam
|
var inferrableType = a.skipToInferrableParam
|
||||||
if inferrableType == nil: return false
|
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
|
return true
|
||||||
|
|
||||||
proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
|
proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
|
||||||
|
|
@ -1420,7 +1441,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
|
||||||
argType = argType
|
argType = argType
|
||||||
c = m.c
|
c = m.c
|
||||||
|
|
||||||
if inferTypeClassParam(c, f, argType):
|
if inferTypeClassParam(m, f, argType):
|
||||||
return argSemantized
|
return argSemantized
|
||||||
|
|
||||||
if tfHasStatic in fMaybeStatic.flags:
|
if tfHasStatic in fMaybeStatic.flags:
|
||||||
|
|
@ -1448,9 +1469,18 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
|
||||||
arg.typ.n = evaluated
|
arg.typ.n = evaluated
|
||||||
argType = arg.typ
|
argType = arg.typ
|
||||||
|
|
||||||
var a = argType
|
var
|
||||||
var r = typeRel(m, f, a)
|
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
|
if r != isNone and m.calleeSym != nil and
|
||||||
m.calleeSym.kind in {skMacro, skTemplate}:
|
m.calleeSym.kind in {skMacro, skTemplate}:
|
||||||
# XXX: duplicating this is ugly, but we cannot (!) move this
|
# XXX: duplicating this is ugly, but we cannot (!) move this
|
||||||
|
|
|
||||||
102
tests/concepts/tmapconcept.nim
Normal file
102
tests/concepts/tmapconcept.nim
Normal file
|
|
@ -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]
|
||||||
|
|
||||||
|
|
@ -46,7 +46,7 @@ proc implicitGeneric(s: var Stack): auto =
|
||||||
static:
|
static:
|
||||||
echo "IMPLICIT INFERRED ", s.T.name, " ", Stack.T.name
|
echo "IMPLICIT INFERRED ", s.T.name, " ", Stack.T.name
|
||||||
echo "IMPLICIT VALUE TYPE ", s.ValueType.name, " ", Stack.ValueType.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()
|
return s.pop()
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue