mirror of
https://github.com/nim-lang/Nim.git
synced 2026-06-06 03:44:14 +00:00
towards support for composite type classes such as seq[Number] and SquareMatrix[T]
This commit is contained in:
@@ -340,6 +340,7 @@ type
|
||||
tyParametricTypeClass # structured similarly to tyGenericInst
|
||||
# lastSon is the body of the type class
|
||||
tyBuiltInTypeClass
|
||||
tyCompositeTypeClass
|
||||
tyAnd
|
||||
tyOr
|
||||
tyNot
|
||||
@@ -350,7 +351,7 @@ const
|
||||
tyPureObject* = tyTuple
|
||||
GcTypeKinds* = {tyRef, tySequence, tyString}
|
||||
tyError* = tyProxy # as an errornous node should match everything
|
||||
tyTypeClasses* = {tyTypeClass, tyBuiltInTypeClass,
|
||||
tyTypeClasses* = {tyTypeClass, tyBuiltInTypeClass, tyCompositeTypeClass,
|
||||
tyParametricTypeClass, tyAnd, tyOr, tyNot, tyAnything}
|
||||
|
||||
type
|
||||
|
||||
@@ -25,7 +25,7 @@ proc equalGenericParams(procA, procB: PNode): bool =
|
||||
let a = procA.sons[i].sym
|
||||
let b = procB.sons[i].sym
|
||||
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 not ExprStructuralEquivalent(a.ast, b.ast): return
|
||||
result = true
|
||||
|
||||
@@ -47,7 +47,7 @@ proc sameInstantiation(a, b: TInstantiation): bool =
|
||||
if a.concreteTypes.len == b.concreteTypes.len:
|
||||
for i in 0..a.concreteTypes.high:
|
||||
if not compareTypes(a.concreteTypes[i], b.concreteTypes[i],
|
||||
flags = {TypeDescExactMatch}): return
|
||||
flags = {ExactTypeDescValues}): return
|
||||
result = true
|
||||
|
||||
proc GenericCacheGet(genericSym: Psym, entry: TInstantiation): PSym =
|
||||
@@ -165,7 +165,8 @@ proc lateInstantiateGeneric(c: PContext, invocation: PType, info: TLineInfo): PT
|
||||
result.sons.add instantiated
|
||||
cacheTypeInst result
|
||||
|
||||
proc instGenericContainer(c: PContext, info: TLineInfo, header: PType): PType =
|
||||
proc instGenericContainer(c: PContext, info: TLineInfo, header: PType,
|
||||
allowMetaTypes = false): PType =
|
||||
when oUseLateInstantiation:
|
||||
lateInstantiateGeneric(c, header, info)
|
||||
else:
|
||||
@@ -174,6 +175,7 @@ proc instGenericContainer(c: PContext, info: TLineInfo, header: PType): PType =
|
||||
InitIdTable(cl.typeMap)
|
||||
cl.info = info
|
||||
cl.c = c
|
||||
cl.allowMetaTypes = allowMetaTypes
|
||||
result = ReplaceTypeVarsT(cl, header)
|
||||
|
||||
proc instGenericContainer(c: PContext, n: PNode, header: PType): PType =
|
||||
|
||||
@@ -666,11 +666,14 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
|
||||
paramType.sons[i] = lifted
|
||||
result = paramType
|
||||
of tyGenericBody:
|
||||
# type Foo[T] = object
|
||||
# proc x(a: Foo, b: Foo)
|
||||
var typ = newTypeS(tyTypeClass, c)
|
||||
typ.addSonSkipIntLit(paramType)
|
||||
result = addImplicitGeneric(typ)
|
||||
result = newTypeS(tyGenericInvokation, c)
|
||||
result.rawAddSon(paramType)
|
||||
for i in 0 .. paramType.sonsLen - 2:
|
||||
result.rawAddSon(copyType(paramType.sons[i], getCurrOwner(), true))
|
||||
result = instGenericContainer(c, paramType.sym.info, result,
|
||||
allowMetaTypes = true)
|
||||
result = newTypeWithSons(c, tyCompositeTypeClass, @[paramType, result])
|
||||
result = addImplicitGeneric(result)
|
||||
of tyGenericInst:
|
||||
for i in 1 .. (paramType.sons.len - 2):
|
||||
var lifted = liftingWalk(paramType.sons[i])
|
||||
|
||||
@@ -50,9 +50,10 @@ proc searchInstTypes*(key: PType): PType =
|
||||
block MatchType:
|
||||
for j in 1 .. high(key.sons):
|
||||
# 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
|
||||
|
||||
|
||||
return inst
|
||||
|
||||
proc cacheTypeInst*(inst: PType) =
|
||||
@@ -67,6 +68,8 @@ type
|
||||
typeMap*: TIdTable # map PType to PType
|
||||
symMap*: TIdTable # map PSym to PSym
|
||||
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 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 =
|
||||
result = PType(idTableGet(cl.typeMap, t))
|
||||
if result == nil:
|
||||
if cl.allowMetaTypes: return
|
||||
LocalError(t.sym.info, errCannotInstantiateX, typeToString(t))
|
||||
result = errorType(cl.c)
|
||||
elif result.kind == tyGenericParam:
|
||||
elif result.kind == tyGenericParam and not cl.allowMetaTypes:
|
||||
InternalError(cl.info, "substitution with generic parameter")
|
||||
|
||||
proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType =
|
||||
@@ -150,11 +154,11 @@ proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType =
|
||||
var x = t.sons[i]
|
||||
if x.kind == tyGenericParam:
|
||||
x = lookupTypeVar(cl, x)
|
||||
if header == nil: header = copyType(t, t.owner, false)
|
||||
header.sons[i] = x
|
||||
propagateToOwner(header, x)
|
||||
#idTablePut(cl.typeMap, body.sons[i-1], x)
|
||||
|
||||
if x != nil:
|
||||
if header == nil: header = copyType(t, t.owner, false)
|
||||
header.sons[i] = x
|
||||
propagateToOwner(header, x)
|
||||
|
||||
if header != nil:
|
||||
# search again after first pass:
|
||||
result = searchInstTypes(header)
|
||||
@@ -202,6 +206,7 @@ proc ReplaceTypeVarsT*(cl: var TReplTypeVars, t: PType): PType =
|
||||
of tyTypeClass: nil
|
||||
of tyGenericParam:
|
||||
result = lookupTypeVar(cl, t)
|
||||
if result == nil: return t
|
||||
if result.kind == tyGenericInvokation:
|
||||
result = handleGenericInvokation(cl, result)
|
||||
of tyGenericInvokation:
|
||||
|
||||
@@ -647,6 +647,8 @@ proc typeRel(c: var TCandidate, f, a: PType, doBind = true): TTypeRelation =
|
||||
result = typeRel(c, lastSon(f), a)
|
||||
|
||||
of tyGenericBody:
|
||||
if a.kind == tyGenericInst and a.sons[0] == f:
|
||||
return isGeneric
|
||||
let ff = lastSon(f)
|
||||
if ff != nil: result = typeRel(c, ff, a)
|
||||
|
||||
@@ -718,6 +720,17 @@ proc typeRel(c: var TCandidate, f, a: PType, doBind = true): TTypeRelation =
|
||||
else:
|
||||
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:
|
||||
var x = PType(idTableGet(c.bindings, f))
|
||||
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
|
||||
else: a.sons[0]
|
||||
result = typeRel(c, prev.sons[0], toMatch)
|
||||
|
||||
of tyExpr, tyStmt:
|
||||
result = isGeneric
|
||||
|
||||
of tyProxy:
|
||||
result = isEqual
|
||||
|
||||
else: internalError("typeRel: " & $f.kind)
|
||||
|
||||
proc cmpTypes*(c: PContext, f, a: PType): TTypeRelation =
|
||||
|
||||
@@ -409,7 +409,7 @@ const
|
||||
"uint", "uint8", "uint16", "uint32", "uint64",
|
||||
"bignum", "const ",
|
||||
"!", "varargs[$1]", "iter[$1]", "Error Type", "TypeClass",
|
||||
"ParametricTypeClass", "BuiltInTypeClass",
|
||||
"ParametricTypeClass", "BuiltInTypeClass", "CompositeTypeClass",
|
||||
"and", "or", "not", "any", "static"]
|
||||
|
||||
proc TypeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
|
||||
@@ -604,8 +604,9 @@ type
|
||||
dcEqOrDistinctOf ## a equals b or a is distinct of b
|
||||
|
||||
TTypeCmpFlag* = enum
|
||||
IgnoreTupleFields,
|
||||
TypeDescExactMatch,
|
||||
IgnoreTupleFields
|
||||
ExactTypeDescValues
|
||||
ExactGenericParams
|
||||
AllowCommonBase
|
||||
|
||||
TTypeCmpFlags* = set[TTypeCmpFlag]
|
||||
@@ -646,7 +647,7 @@ proc SameTypeOrNil*(a, b: PType, flags: TTypeCmpFlags = {}): bool =
|
||||
result = SameTypeAux(a, b, c)
|
||||
|
||||
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):
|
||||
if a.ast == b.ast:
|
||||
result = paramsEqual
|
||||
@@ -682,7 +683,7 @@ proc equalParams(a, b: PNode): TParamsEquality =
|
||||
return paramsNotEqual # paramsIncompatible;
|
||||
# continue traversal! If not equal, we can return immediately; else
|
||||
# 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):
|
||||
result = paramsNotEqual # one proc has a result, the other not is OK
|
||||
else:
|
||||
@@ -749,9 +750,9 @@ template IfFastObjectTypeCheckFailed(a, b: PType, body: stmt) {.immediate.} =
|
||||
|
||||
proc sameObjectTypes*(a, b: PType): bool =
|
||||
# specialized for efficiency (sigmatch uses it)
|
||||
IfFastObjectTypeCheckFailed(a, b):
|
||||
IfFastObjectTypeCheckFailed(a, b):
|
||||
var c = initSameTypeClosure()
|
||||
result = sameTypeAux(a, b, c)
|
||||
result = sameTypeAux(a, b, c)
|
||||
|
||||
proc sameDistinctTypes*(a, b: PType): bool {.inline.} =
|
||||
result = sameObjectTypes(a, b)
|
||||
@@ -852,12 +853,15 @@ proc SameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
|
||||
result = sameTypeAux(lastSon(a), lastSon(b), c)
|
||||
of tyTypeDesc:
|
||||
if c.cmp == dcEqIgnoreDistinct: result = false
|
||||
elif TypeDescExactMatch in c.flags:
|
||||
elif ExactTypeDescValues in c.flags:
|
||||
CycleCheck()
|
||||
result = sameChildrenAux(x, y, c) and sameFlags(a, b)
|
||||
else:
|
||||
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,
|
||||
tyArray, tyProc, tyConst, tyMutable, tyVarargs, tyIter,
|
||||
tyOrdinal, tyTypeClasses:
|
||||
|
||||
30
tests/compile/tcompositetypeclasses.nim
Normal file
30
tests/compile/tcompositetypeclasses.nim
Normal 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))
|
||||
|
||||
Reference in New Issue
Block a user