support for parametric user-defined type classes

This commit is contained in:
Zahary Karadjov 2014-01-24 14:13:32 +02:00
commit a6a18be089
6 changed files with 102 additions and 71 deletions

View file

@ -337,12 +337,20 @@ type
tyIter, # unused tyIter, # unused
tyProxy # used as errornous type (for idetools) tyProxy # used as errornous type (for idetools)
tyTypeClass tyTypeClass
tyParametricTypeClass # structured similarly to tyGenericInst tyBuiltInTypeClass # Type such as the catch-all object, tuple, seq, etc
# lastSon is the body of the type class tyUserTypeClass
tyUserTypeClassInst # \
# Instance of a parametric user-defined type class.
# Structured similarly to tyGenericInst.
# tyGenericInst represents concrete types, while
# this is still a "generic param" that will bind types
# and resolves them during sigmatch and instantiation.
tyBuiltInTypeClass # Type such as the catch-all object, tuple, seq, etc tyCompositeTypeClass # Type such as seq[Number]
# The notes for tyUserTypeClassInst apply here as well
tyCompositeTypeClass # # sons[0]: the original expression used by the user.
# sons[1]: fully expanded and instantiated meta type
# (potentially following aliases)
tyAnd, tyOr, tyNot # boolean type classes such as `string|int`,`not seq`, tyAnd, tyOr, tyNot # boolean type classes such as `string|int`,`not seq`,
# `Sortable and Enumable`, etc # `Sortable and Enumable`, etc
@ -365,7 +373,8 @@ const
tyUnknownTypes* = {tyError, tyFromExpr} tyUnknownTypes* = {tyError, tyFromExpr}
tyTypeClasses* = {tyTypeClass, tyBuiltInTypeClass, tyCompositeTypeClass, tyTypeClasses* = {tyTypeClass, tyBuiltInTypeClass, tyCompositeTypeClass,
tyParametricTypeClass, tyAnd, tyOr, tyNot, tyAnything} tyUserTypeClass, tyUserTypeClassInst,
tyAnd, tyOr, tyNot, tyAnything}
tyMetaTypes* = {tyGenericParam, tyTypeDesc, tyStatic, tyExpr} + tyTypeClasses tyMetaTypes* = {tyGenericParam, tyTypeDesc, tyStatic, tyExpr} + tyTypeClasses

View file

@ -322,12 +322,12 @@ proc isOpImpl(c: PContext, n: PNode): PNode =
tfIterator in t.flags)) tfIterator in t.flags))
else: else:
var match: bool var match: bool
let t2 = n[2].typ let t2 = n[2].typ.skipTypes({tyTypeDesc})
case t2.kind case t2.kind
of tyTypeClasses: of tyTypeClasses:
var m: TCandidate var m: TCandidate
initCandidate(c, m, t2) initCandidate(c, m, t2)
match = matchUserTypeClass(c, m, emptyNode, t2, t1) != nil match = typeRel(m, t2, t1) != isNone
of tyOrdinal: of tyOrdinal:
var m: TCandidate var m: TCandidate
initCandidate(c, m, t2) initCandidate(c, m, t2)

View file

@ -710,6 +710,11 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
result = addImplicitGeneric(result) result = addImplicitGeneric(result)
of tyGenericInst: of tyGenericInst:
if paramType.lastSon.kind == tyUserTypeClass:
var cp = copyType(paramType, getCurrOwner(), false)
cp.kind = tyUserTypeClassInst
return addImplicitGeneric(cp)
for i in 1 .. (paramType.sons.len - 2): for i in 1 .. (paramType.sons.len - 2):
var lifted = liftingWalk(paramType.sons[i]) var lifted = liftingWalk(paramType.sons[i])
if lifted != nil: if lifted != nil:
@ -731,7 +736,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
allowMetaTypes = true) allowMetaTypes = true)
result = liftingWalk(expanded) result = liftingWalk(expanded)
of tyTypeClass, tyBuiltInTypeClass, tyAnd, tyOr, tyNot: of tyUserTypeClass, tyTypeClass, tyBuiltInTypeClass, tyAnd, tyOr, tyNot:
result = addImplicitGeneric(copyType(paramType, getCurrOwner(), true)) result = addImplicitGeneric(copyType(paramType, getCurrOwner(), true))
of tyExpr: of tyExpr:
@ -923,7 +928,7 @@ proc freshType(res, prev: PType): PType {.inline.} =
proc semTypeClass(c: PContext, n: PNode, prev: PType): PType = proc semTypeClass(c: PContext, n: PNode, prev: PType): PType =
# if n.sonsLen == 0: return newConstraint(c, tyTypeClass) # if n.sonsLen == 0: return newConstraint(c, tyTypeClass)
result = newOrPrevType(tyTypeClass, prev, c) result = newOrPrevType(tyUserTypeClass, prev, c)
result.n = n result.n = n
let let

View file

@ -360,7 +360,10 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
if tfUnresolved in t.flags: result = result.base if tfUnresolved in t.flags: result = result.base
elif t.sonsLen > 0: elif t.sonsLen > 0:
result = makeTypeDesc(cl.c, replaceTypeVarsT(cl, t.sons[0])) result = makeTypeDesc(cl.c, replaceTypeVarsT(cl, t.sons[0]))
of tyUserTypeClass:
result = t
of tyGenericInst: of tyGenericInst:
result = instCopyType(cl, t) result = instCopyType(cl, t)
for i in 1 .. <result.sonsLen: for i in 1 .. <result.sonsLen:

View file

@ -399,6 +399,69 @@ proc typeRangeRel(f, a: PType): TTypeRelation {.noinline.} =
else: else:
result = isNone result = isNone
proc matchUserTypeClass*(c: PContext, m: var TCandidate,
ff, a: PType): TTypeRelation =
#if f.n == nil:
# let r = typeRel(m, f, a)
# return if r == isGeneric: arg else: nil
var body = ff.skipTypes({tyUserTypeClassInst})
# var prev = PType(idTableGet(m.bindings, f))
# if prev != nil:
# if sameType(prev, a): return arg
# else: return nil
# pushInfoContext(arg.info)
openScope(c)
inc c.inTypeClass
finally:
dec c.inTypeClass
closeScope(c)
if ff.kind == tyUserTypeClassInst:
for i in 1 .. <(ff.len - 1):
var
typeParamName = ff.base.sons[i-1].sym.name
typ = ff.sons[i]
param = newSym(skType, typeParamName, body.sym, body.sym.info)
param.typ = makeTypeDesc(c, typ)
addDecl(c, param)
for param in body.n[0]:
var
dummyName: PNode
dummyType: PType
if param.kind == nkVarTy:
dummyName = param[0]
dummyType = makeVarType(c, a)
else:
dummyName = param
dummyType = a
internalAssert dummyName.kind == nkIdent
var dummyParam = newSym(skType, dummyName.ident, body.sym, body.sym.info)
dummyParam.typ = dummyType
addDecl(c, dummyParam)
for stmt in body.n[3]:
var e = c.semTryExpr(c, copyTree(stmt), bufferErrors = false)
m.errors = bufferedMsgs
clearBufferedMsgs()
if e == nil: return isNone
case e.kind
of nkReturnStmt: discard
of nkTypeSection: discard
of nkConstDef: discard
else: discard
return isGeneric
# put(m.bindings, f, a)
proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
# typeRel can be used to establish various relationships between types: # typeRel can be used to establish various relationships between types:
# #
@ -751,6 +814,12 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
else: else:
return isNone return isNone
of tyUserTypeClassInst:
considerPreviousT:
result = matchUserTypeClass(c.c, c, f, a)
if result == isGeneric:
put(c.bindings, f, a)
of tyCompositeTypeClass: of tyCompositeTypeClass:
considerPreviousT: considerPreviousT:
if typeRel(c, f.sons[1], a) != isNone: if typeRel(c, f.sons[1], a) != isNone:
@ -904,57 +973,6 @@ proc localConvMatch(c: PContext, m: var TCandidate, f, a: PType,
result.typ = getInstantiatedType(c, arg, m, base(f)) result.typ = getInstantiatedType(c, arg, m, base(f))
m.baseTypeMatch = true m.baseTypeMatch = true
proc matchUserTypeClass*(c: PContext, m: var TCandidate,
arg: PNode, f, a: PType): PNode =
if f.n == nil:
let r = typeRel(m, f, a)
return if r == isGeneric: arg else: nil
var prev = PType(idTableGet(m.bindings, f))
if prev != nil:
if sameType(prev, a): return arg
else: return nil
# pushInfoContext(arg.info)
openScope(c)
inc c.inTypeClass
finally:
dec c.inTypeClass
closeScope(c)
for param in f.n[0]:
var
dummyName: PNode
dummyType: PType
if param.kind == nkVarTy:
dummyName = param[0]
dummyType = makeVarType(c, a)
else:
dummyName = param
dummyType = a
internalAssert dummyName.kind == nkIdent
var dummyParam = newSym(skType, dummyName.ident, f.sym, f.sym.info)
dummyParam.typ = dummyType
addDecl(c, dummyParam)
for stmt in f.n[3]:
var e = c.semTryExpr(c, copyTree(stmt), bufferErrors = false)
m.errors = bufferedMsgs
clearBufferedMsgs()
if e == nil: return nil
case e.kind
of nkReturnStmt: discard
of nkTypeSection: discard
of nkConstDef: discard
else: discard
result = arg
put(m.bindings, f, a)
proc paramTypesMatchAux(m: var TCandidate, f, argType: PType, proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
argSemantized, argOrig: PNode): PNode = argSemantized, argOrig: PNode): PNode =
var var
@ -980,14 +998,9 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
else: argType else: argType
case fMaybeStatic.kind case fMaybeStatic.kind
of tyTypeClass, tyParametricTypeClass: of tyTypeClass:
if fMaybeStatic.n != nil: if fMaybeStatic.n != nil:
let match = matchUserTypeClass(c, m, arg, fMaybeStatic, a) r = matchUserTypeClass(c, m, fMaybeStatic, a)
if match != nil:
r = isGeneric
arg = match
else:
r = isNone
else: else:
r = typeRel(m, f, a) r = typeRel(m, f, a)
of tyExpr: of tyExpr:

View file

@ -404,8 +404,9 @@ const
"float", "float32", "float64", "float128", "float", "float32", "float64", "float128",
"uint", "uint8", "uint16", "uint32", "uint64", "uint", "uint8", "uint16", "uint32", "uint64",
"bignum", "const ", "bignum", "const ",
"!", "varargs[$1]", "iter[$1]", "Error Type", "TypeClass", "!", "varargs[$1]", "iter[$1]", "Error Type",
"ParametricTypeClass", "BuiltInTypeClass", "CompositeTypeClass", "TypeClass", "BuiltInTypeClass", "UserTypeClass",
"UserTypeClassInst", "CompositeTypeClass",
"and", "or", "not", "any", "static", "TypeFromExpr"] "and", "or", "not", "any", "static", "TypeFromExpr"]
proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =