towards support for composite type classes such as seq[Number] and SquareMatrix[T]

This commit is contained in:
Zahary Karadjov 2013-12-27 18:34:28 +02:00
commit a27eb51535
8 changed files with 87 additions and 26 deletions

View file

@ -340,6 +340,7 @@ type
tyParametricTypeClass # structured similarly to tyGenericInst tyParametricTypeClass # structured similarly to tyGenericInst
# lastSon is the body of the type class # lastSon is the body of the type class
tyBuiltInTypeClass tyBuiltInTypeClass
tyCompositeTypeClass
tyAnd tyAnd
tyOr tyOr
tyNot tyNot
@ -350,7 +351,7 @@ const
tyPureObject* = tyTuple tyPureObject* = tyTuple
GcTypeKinds* = {tyRef, tySequence, tyString} GcTypeKinds* = {tyRef, tySequence, tyString}
tyError* = tyProxy # as an errornous node should match everything tyError* = tyProxy # as an errornous node should match everything
tyTypeClasses* = {tyTypeClass, tyBuiltInTypeClass, tyTypeClasses* = {tyTypeClass, tyBuiltInTypeClass, tyCompositeTypeClass,
tyParametricTypeClass, tyAnd, tyOr, tyNot, tyAnything} tyParametricTypeClass, tyAnd, tyOr, tyNot, tyAnything}
type type

View file

@ -25,7 +25,7 @@ proc equalGenericParams(procA, procB: PNode): bool =
let a = procA.sons[i].sym let a = procA.sons[i].sym
let b = procB.sons[i].sym let b = procB.sons[i].sym
if a.name.id != b.name.id or if a.name.id != b.name.id or
not sameTypeOrNil(a.typ, b.typ, {TypeDescExactMatch}): return not sameTypeOrNil(a.typ, b.typ, {ExactTypeDescValues}): return
if a.ast != nil and b.ast != nil: if a.ast != nil and b.ast != nil:
if not ExprStructuralEquivalent(a.ast, b.ast): return if not ExprStructuralEquivalent(a.ast, b.ast): return
result = true result = true

View file

@ -47,7 +47,7 @@ proc sameInstantiation(a, b: TInstantiation): bool =
if a.concreteTypes.len == b.concreteTypes.len: if a.concreteTypes.len == b.concreteTypes.len:
for i in 0..a.concreteTypes.high: for i in 0..a.concreteTypes.high:
if not compareTypes(a.concreteTypes[i], b.concreteTypes[i], if not compareTypes(a.concreteTypes[i], b.concreteTypes[i],
flags = {TypeDescExactMatch}): return flags = {ExactTypeDescValues}): return
result = true result = true
proc GenericCacheGet(genericSym: Psym, entry: TInstantiation): PSym = proc GenericCacheGet(genericSym: Psym, entry: TInstantiation): PSym =
@ -165,7 +165,8 @@ proc lateInstantiateGeneric(c: PContext, invocation: PType, info: TLineInfo): PT
result.sons.add instantiated result.sons.add instantiated
cacheTypeInst result cacheTypeInst result
proc instGenericContainer(c: PContext, info: TLineInfo, header: PType): PType = proc instGenericContainer(c: PContext, info: TLineInfo, header: PType,
allowMetaTypes = false): PType =
when oUseLateInstantiation: when oUseLateInstantiation:
lateInstantiateGeneric(c, header, info) lateInstantiateGeneric(c, header, info)
else: else:
@ -174,6 +175,7 @@ proc instGenericContainer(c: PContext, info: TLineInfo, header: PType): PType =
InitIdTable(cl.typeMap) InitIdTable(cl.typeMap)
cl.info = info cl.info = info
cl.c = c cl.c = c
cl.allowMetaTypes = allowMetaTypes
result = ReplaceTypeVarsT(cl, header) result = ReplaceTypeVarsT(cl, header)
proc instGenericContainer(c: PContext, n: PNode, header: PType): PType = proc instGenericContainer(c: PContext, n: PNode, header: PType): PType =

View file

@ -666,11 +666,14 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
paramType.sons[i] = lifted paramType.sons[i] = lifted
result = paramType result = paramType
of tyGenericBody: of tyGenericBody:
# type Foo[T] = object result = newTypeS(tyGenericInvokation, c)
# proc x(a: Foo, b: Foo) result.rawAddSon(paramType)
var typ = newTypeS(tyTypeClass, c) for i in 0 .. paramType.sonsLen - 2:
typ.addSonSkipIntLit(paramType) result.rawAddSon(copyType(paramType.sons[i], getCurrOwner(), true))
result = addImplicitGeneric(typ) result = instGenericContainer(c, paramType.sym.info, result,
allowMetaTypes = true)
result = newTypeWithSons(c, tyCompositeTypeClass, @[paramType, result])
result = addImplicitGeneric(result)
of tyGenericInst: of tyGenericInst:
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])

View file

@ -50,7 +50,8 @@ proc searchInstTypes*(key: PType): PType =
block MatchType: block MatchType:
for j in 1 .. high(key.sons): for j in 1 .. high(key.sons):
# XXX sameType is not really correct for nested generics? # XXX sameType is not really correct for nested generics?
if not sameType(inst.sons[j], key.sons[j]): if not compareTypes(inst.sons[j], key.sons[j],
flags = {ExactGenericParams}):
break MatchType break MatchType
return inst return inst
@ -67,6 +68,8 @@ type
typeMap*: TIdTable # map PType to PType typeMap*: TIdTable # map PType to PType
symMap*: TIdTable # map PSym to PSym symMap*: TIdTable # map PSym to PSym
info*: TLineInfo info*: TLineInfo
allowMetaTypes*: bool # allow types such as seq[Number]
# i.e. the result contains unresolved generics
proc ReplaceTypeVarsT*(cl: var TReplTypeVars, t: PType): PType proc ReplaceTypeVarsT*(cl: var TReplTypeVars, t: PType): PType
proc ReplaceTypeVarsS(cl: var TReplTypeVars, s: PSym): PSym proc ReplaceTypeVarsS(cl: var TReplTypeVars, s: PSym): PSym
@ -132,9 +135,10 @@ proc ReplaceTypeVarsS(cl: var TReplTypeVars, s: PSym): PSym =
proc lookupTypeVar(cl: TReplTypeVars, t: PType): PType = proc lookupTypeVar(cl: TReplTypeVars, t: PType): PType =
result = PType(idTableGet(cl.typeMap, t)) result = PType(idTableGet(cl.typeMap, t))
if result == nil: if result == nil:
if cl.allowMetaTypes: return
LocalError(t.sym.info, errCannotInstantiateX, typeToString(t)) LocalError(t.sym.info, errCannotInstantiateX, typeToString(t))
result = errorType(cl.c) result = errorType(cl.c)
elif result.kind == tyGenericParam: elif result.kind == tyGenericParam and not cl.allowMetaTypes:
InternalError(cl.info, "substitution with generic parameter") InternalError(cl.info, "substitution with generic parameter")
proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType = proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType =
@ -150,10 +154,10 @@ proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType =
var x = t.sons[i] var x = t.sons[i]
if x.kind == tyGenericParam: if x.kind == tyGenericParam:
x = lookupTypeVar(cl, x) x = lookupTypeVar(cl, x)
if header == nil: header = copyType(t, t.owner, false) if x != nil:
header.sons[i] = x if header == nil: header = copyType(t, t.owner, false)
propagateToOwner(header, x) header.sons[i] = x
#idTablePut(cl.typeMap, body.sons[i-1], x) propagateToOwner(header, x)
if header != nil: if header != nil:
# search again after first pass: # search again after first pass:
@ -202,6 +206,7 @@ proc ReplaceTypeVarsT*(cl: var TReplTypeVars, t: PType): PType =
of tyTypeClass: nil of tyTypeClass: nil
of tyGenericParam: of tyGenericParam:
result = lookupTypeVar(cl, t) result = lookupTypeVar(cl, t)
if result == nil: return t
if result.kind == tyGenericInvokation: if result.kind == tyGenericInvokation:
result = handleGenericInvokation(cl, result) result = handleGenericInvokation(cl, result)
of tyGenericInvokation: of tyGenericInvokation:

View file

@ -647,6 +647,8 @@ proc typeRel(c: var TCandidate, f, a: PType, doBind = true): TTypeRelation =
result = typeRel(c, lastSon(f), a) result = typeRel(c, lastSon(f), a)
of tyGenericBody: of tyGenericBody:
if a.kind == tyGenericInst and a.sons[0] == f:
return isGeneric
let ff = lastSon(f) let ff = lastSon(f)
if ff != nil: result = typeRel(c, ff, a) if ff != nil: result = typeRel(c, ff, a)
@ -718,6 +720,17 @@ proc typeRel(c: var TCandidate, f, a: PType, doBind = true): TTypeRelation =
else: else:
result = typeRel(c, prev, a) result = typeRel(c, prev, a)
of tyCompositeTypeClass:
var prev = PType(idTableGet(c.bindings, f))
if prev == nil:
if typeRel(c, f.sons[1], a) != isNone:
put(c.bindings, f, a)
return isGeneric
else:
return isNone
else:
result = typeRel(c, prev, a)
of tyGenericParam, tyTypeClass: of tyGenericParam, tyTypeClass:
var x = PType(idTableGet(c.bindings, f)) var x = PType(idTableGet(c.bindings, f))
if x == nil: if x == nil:
@ -780,10 +793,13 @@ proc typeRel(c: var TCandidate, f, a: PType, doBind = true): TTypeRelation =
let toMatch = if tfUnresolved in f.flags: a let toMatch = if tfUnresolved in f.flags: a
else: a.sons[0] else: a.sons[0]
result = typeRel(c, prev.sons[0], toMatch) result = typeRel(c, prev.sons[0], toMatch)
of tyExpr, tyStmt: of tyExpr, tyStmt:
result = isGeneric result = isGeneric
of tyProxy: of tyProxy:
result = isEqual result = isEqual
else: internalError("typeRel: " & $f.kind) else: internalError("typeRel: " & $f.kind)
proc cmpTypes*(c: PContext, f, a: PType): TTypeRelation = proc cmpTypes*(c: PContext, f, a: PType): TTypeRelation =

View file

@ -409,7 +409,7 @@ const
"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", "TypeClass",
"ParametricTypeClass", "BuiltInTypeClass", "ParametricTypeClass", "BuiltInTypeClass", "CompositeTypeClass",
"and", "or", "not", "any", "static"] "and", "or", "not", "any", "static"]
proc TypeToString(typ: PType, prefer: TPreferedDesc = preferName): string = proc TypeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
@ -604,8 +604,9 @@ type
dcEqOrDistinctOf ## a equals b or a is distinct of b dcEqOrDistinctOf ## a equals b or a is distinct of b
TTypeCmpFlag* = enum TTypeCmpFlag* = enum
IgnoreTupleFields, IgnoreTupleFields
TypeDescExactMatch, ExactTypeDescValues
ExactGenericParams
AllowCommonBase AllowCommonBase
TTypeCmpFlags* = set[TTypeCmpFlag] TTypeCmpFlags* = set[TTypeCmpFlag]
@ -646,7 +647,7 @@ proc SameTypeOrNil*(a, b: PType, flags: TTypeCmpFlags = {}): bool =
result = SameTypeAux(a, b, c) result = SameTypeAux(a, b, c)
proc equalParam(a, b: PSym): TParamsEquality = proc equalParam(a, b: PSym): TParamsEquality =
if SameTypeOrNil(a.typ, b.typ, {TypeDescExactMatch}) and if SameTypeOrNil(a.typ, b.typ, {ExactTypeDescValues}) and
ExprStructuralEquivalent(a.constraint, b.constraint): ExprStructuralEquivalent(a.constraint, b.constraint):
if a.ast == b.ast: if a.ast == b.ast:
result = paramsEqual result = paramsEqual
@ -682,7 +683,7 @@ proc equalParams(a, b: PNode): TParamsEquality =
return paramsNotEqual # paramsIncompatible; return paramsNotEqual # paramsIncompatible;
# continue traversal! If not equal, we can return immediately; else # continue traversal! If not equal, we can return immediately; else
# it stays incompatible # it stays incompatible
if not SameTypeOrNil(a.sons[0].typ, b.sons[0].typ, {TypeDescExactMatch}): if not SameTypeOrNil(a.sons[0].typ, b.sons[0].typ, {ExactTypeDescValues}):
if (a.sons[0].typ == nil) or (b.sons[0].typ == nil): if (a.sons[0].typ == nil) or (b.sons[0].typ == nil):
result = paramsNotEqual # one proc has a result, the other not is OK result = paramsNotEqual # one proc has a result, the other not is OK
else: else:
@ -852,12 +853,15 @@ proc SameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
result = sameTypeAux(lastSon(a), lastSon(b), c) result = sameTypeAux(lastSon(a), lastSon(b), c)
of tyTypeDesc: of tyTypeDesc:
if c.cmp == dcEqIgnoreDistinct: result = false if c.cmp == dcEqIgnoreDistinct: result = false
elif TypeDescExactMatch in c.flags: elif ExactTypeDescValues in c.flags:
CycleCheck() CycleCheck()
result = sameChildrenAux(x, y, c) and sameFlags(a, b) result = sameChildrenAux(x, y, c) and sameFlags(a, b)
else: else:
result = sameFlags(a, b) result = sameFlags(a, b)
of tyGenericParam, tyGenericInvokation, tyGenericBody, tySequence, of tyGenericParam:
result = if ExactGenericParams in c.flags: a.id == b.id
else: sameChildrenAux(a, b, c) and sameFlags(a, b)
of tyGenericInvokation, tyGenericBody, tySequence,
tyOpenArray, tySet, tyRef, tyPtr, tyVar, tyArrayConstr, tyOpenArray, tySet, tyRef, tyPtr, tyVar, tyArrayConstr,
tyArray, tyProc, tyConst, tyMutable, tyVarargs, tyIter, tyArray, tyProc, tyConst, tyMutable, tyVarargs, tyIter,
tyOrdinal, tyTypeClasses: tyOrdinal, tyTypeClasses:

View file

@ -0,0 +1,30 @@
template accept(e) =
static: assert(compiles(e))
template reject(e) =
static: assert(not compiles(e))
type
TFoo[T, U] = tuple
x: T
y: U
TBar[K] = TFoo[K, K]
TUserClass = int|string
# TBaz = TBar[TUserClass]
var
vfoo: TFoo[int, string]
vbar: TFoo[string, string]
proc foo(x: TFoo) = echo "foo"
proc bar(x: TBar) = echo "bar"
# proc baz(x: TBaz) = echo "baz"
accept(foo(vfoo))
accept(bar(vbar))
# baz vbar
reject(bar(vfoo))