wip type class reforms (the compiler bootstraps fine)

* replace tfAny and tfAll with tyAnd and tyOr
* integrate matchTypeClass into typeRel
* introduce tyBuiltInTypeClass to handle types such as tuple, object, proc, etc
This commit is contained in:
Zahary Karadjov 2013-12-25 19:25:04 +02:00
commit 1d02f2ea53
8 changed files with 94 additions and 99 deletions

View file

@ -339,6 +339,7 @@ type
tyTypeClass tyTypeClass
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
tyAnd tyAnd
tyOr tyOr
tyNot tyNot
@ -349,7 +350,8 @@ 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, tyParametricTypeClass, tyAnd, tyOr, tyNot, tyAnything} tyTypeClasses* = {tyTypeClass, tyBuiltInTypeClass,
tyParametricTypeClass, tyAnd, tyOr, tyNot, tyAnything}
type type
TTypeKinds* = set[TTypeKind] TTypeKinds* = set[TTypeKind]
@ -384,9 +386,6 @@ type
# proc foo(T: typedesc, list: seq[T]): var T # proc foo(T: typedesc, list: seq[T]): var T
tfRetType, # marks return types in proc (used to detect type classes tfRetType, # marks return types in proc (used to detect type classes
# used as return types for return type inference) # used as return types for return type inference)
tfAll, # type class requires all constraints to be met (default)
tfAny, # type class requires any constraint to be met
tfNot, # type class with a negative check
tfCapturesEnv, # whether proc really captures some environment tfCapturesEnv, # whether proc really captures some environment
tfByCopy, # pass object/tuple by copy (C backend) tfByCopy, # pass object/tuple by copy (C backend)
tfByRef, # pass object/tuple by reference (C backend) tfByRef, # pass object/tuple by reference (C backend)
@ -399,6 +398,7 @@ type
tfHasShared, # type constains a "shared" constraint modifier somewhere tfHasShared, # type constains a "shared" constraint modifier somewhere
tfHasMeta, # type has "typedesc" or "expr" somewhere; or uses '|' tfHasMeta, # type has "typedesc" or "expr" somewhere; or uses '|'
tfHasGCedMem, # type contains GC'ed memory tfHasGCedMem, # type contains GC'ed memory
tfGenericTypeParam
TTypeFlags* = set[TTypeFlag] TTypeFlags* = set[TTypeFlag]

View file

@ -643,6 +643,8 @@ proc toFileLine*(info: TLineInfo): string {.inline.} =
proc toFileLineCol*(info: TLineInfo): string {.inline.} = proc toFileLineCol*(info: TLineInfo): string {.inline.} =
result = info.toFilename & "(" & $info.line & "," & $info.col & ")" result = info.toFilename & "(" & $info.line & "," & $info.col & ")"
template `$`*(info: TLineInfo): expr = toFileLineCol(info)
proc `??`* (info: TLineInfo, filename: string): bool = proc `??`* (info: TLineInfo, filename: string): bool =
# only for debugging purposes # only for debugging purposes
result = filename in info.toFilename result = filename in info.toFilename

View file

@ -213,6 +213,21 @@ proc makeTypeSymNode*(c: PContext, typ: PType, info: TLineInfo): PNode =
let sym = newSym(skType, idAnon, getCurrOwner(), info).linkTo(typedesc) let sym = newSym(skType, idAnon, getCurrOwner(), info).linkTo(typedesc)
return newSymNode(sym, info) return newSymNode(sym, info)
proc makeAndType*(c: PContext, t1, t2: PType): PType =
result = newTypeS(tyAnd, c)
result.sons = @[t1, t2]
result.flags.incl tfHasMeta
proc makeOrType*(c: PContext, t1, t2: PType): PType =
result = newTypeS(tyOr, c)
result.sons = @[t1, t2]
result.flags.incl tfHasMeta
proc makeNotType*(c: PContext, t1: PType): PType =
result = newTypeS(tyNot, c)
result.sons = @[t1]
result.flags.incl tfHasMeta
proc newTypeS(kind: TTypeKind, c: PContext): PType = proc newTypeS(kind: TTypeKind, c: PContext): PType =
result = newType(kind, getCurrOwner()) result = newType(kind, getCurrOwner())

View file

@ -1163,7 +1163,7 @@ proc semAsgn(c: PContext, n: PNode): PNode =
if lhsIsResult: if lhsIsResult:
n.typ = EnforceVoidContext n.typ = EnforceVoidContext
if lhs.sym.typ.kind == tyGenericParam: if lhs.sym.typ.kind == tyGenericParam:
if matchTypeClass(lhs.typ, rhs.typ): if cmpTypes(c, lhs.typ, rhs.typ) == isGeneric:
InternalAssert c.p.resultSym != nil InternalAssert c.p.resultSym != nil
lhs.typ = rhs.typ lhs.typ = rhs.typ
c.p.resultSym.typ = rhs.typ c.p.resultSym.typ = rhs.typ

View file

@ -18,7 +18,7 @@ proc newOrPrevType(kind: TTypeKind, prev: PType, c: PContext): PType =
if result.kind == tyForward: result.kind = kind if result.kind == tyForward: result.kind = kind
proc newConstraint(c: PContext, k: TTypeKind): PType = proc newConstraint(c: PContext, k: TTypeKind): PType =
result = newTypeS(tyTypeClass, c) result = newTypeS(tyBuiltInTypeClass, c)
result.addSonSkipIntLit(newTypeS(k, c)) result.addSonSkipIntLit(newTypeS(k, c))
proc semEnum(c: PContext, n: PNode, prev: PType): PType = proc semEnum(c: PContext, n: PNode, prev: PType): PType =
@ -603,6 +603,10 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
proc addImplicitGenericImpl(typeClass: PType, typId: PIdent): PType = proc addImplicitGenericImpl(typeClass: PType, typId: PIdent): PType =
let finalTypId = if typId != nil: typId let finalTypId = if typId != nil: typId
else: getIdent(paramName & ":type") else: getIdent(paramName & ":type")
if genericParams == nil:
# This happens with anonymous proc types appearing in signatures
# XXX: we need to lift these earlier
return
# is this a bindOnce type class already present in the param list? # is this a bindOnce type class already present in the param list?
for i in countup(0, genericParams.len - 1): for i in countup(0, genericParams.len - 1):
if genericParams.sons[i].sym.name.id == finalTypId.id: if genericParams.sons[i].sym.name.id == finalTypId.id:
@ -674,7 +678,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
paramType.sons[i] = lifted paramType.sons[i] = lifted
result = paramType result = paramType
if paramType.lastSon.kind == tyTypeClass: if paramType.lastSon.kind == tyTypeClass and false:
result = paramType result = paramType
result.kind = tyParametricTypeClass result.kind = tyParametricTypeClass
result = addImplicitGeneric(copyType(result, result = addImplicitGeneric(copyType(result,
@ -682,10 +686,16 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
elif result != nil: elif result != nil:
result.kind = tyGenericInvokation result.kind = tyGenericInvokation
result.sons.setLen(result.sons.len - 1) result.sons.setLen(result.sons.len - 1)
of tyTypeClass: of tyTypeClass, tyBuiltInTypeClass, tyAnd, tyOr, tyNot:
result = addImplicitGeneric(copyType(paramType, getCurrOwner(), false)) result = addImplicitGeneric(copyType(paramType, getCurrOwner(), false))
of tyExpr: of tyExpr:
result = addImplicitGeneric(newTypeS(tyGenericParam, c)) result = addImplicitGeneric(newTypeS(tyGenericParam, c))
of tyGenericParam:
if tfGenericTypeParam in paramType.flags and false:
if paramType.sonsLen > 0:
result = liftingWalk(paramType.lastSon)
else:
result = addImplicitGeneric(newTypeS(tyGenericParam, c))
else: nil else: nil
# result = liftingWalk(paramType) # result = liftingWalk(paramType)
@ -924,19 +934,22 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
LocalError(n.sons[2].info, errTypeExpected) LocalError(n.sons[2].info, errTypeExpected)
result = newOrPrevType(tyError, prev, c) result = newOrPrevType(tyError, prev, c)
else: else:
result = newTypeS(tyTypeClass, c) result = if op.id == ord(wAnd): makeAndType(c, t1, t2)
result.addSonSkipIntLit(t1) else: makeOrType(c, t1, t2)
result.addSonSkipIntLit(t2)
result.flags.incl(if op.id == ord(wAnd): tfAll else: tfAny)
result.flags.incl(tfHasMeta)
elif op.id == ord(wNot): elif op.id == ord(wNot):
checkSonsLen(n, 3) case n.len
of 3:
result = semTypeNode(c, n.sons[1], prev) result = semTypeNode(c, n.sons[1], prev)
if result.kind in NilableTypes and n.sons[2].kind == nkNilLit: if result.kind in NilableTypes and n.sons[2].kind == nkNilLit:
result = freshType(result, prev) result = freshType(result, prev)
result.flags.incl(tfNotNil) result.flags.incl(tfNotNil)
else: else:
LocalError(n.info, errGenerated, "invalid type") LocalError(n.info, errGenerated, "invalid type")
of 2:
let negated = semTypeNode(c, n.sons[1], prev)
result = makeNotType(c, negated)
else:
LocalError(n.info, errGenerated, "invalid type")
else: else:
result = semTypeExpr(c, n) result = semTypeExpr(c, n)
else: else:
@ -1088,10 +1101,6 @@ proc processMagicType(c: PContext, m: PSym) =
else: LocalError(m.info, errTypeExpected) else: LocalError(m.info, errTypeExpected)
proc semGenericConstraints(c: PContext, x: PType): PType = proc semGenericConstraints(c: PContext, x: PType): PType =
if x.kind in StructuralEquivTypes and (
sonsLen(x) == 0 or x.sons[0].kind in {tyGenericParam, tyEmpty}):
result = newConstraint(c, x.kind)
else:
result = newTypeWithSons(c, tyGenericParam, @[x]) result = newTypeWithSons(c, tyGenericParam, @[x])
proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode = proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode =
@ -1128,6 +1137,8 @@ proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode =
if typ == nil: if typ == nil:
typ = newTypeS(tyGenericParam, c) typ = newTypeS(tyGenericParam, c)
typ.flags.incl tfGenericTypeParam
for j in countup(0, L-3): for j in countup(0, L-3):
let finalType = if j == 0: typ let finalType = if j == 0: typ
else: copyType(typ, typ.owner, false) else: copyType(typ, typ.owner, false)

View file

@ -19,7 +19,7 @@ proc checkPartialConstructedType(info: TLineInfo, t: PType) =
proc checkConstructedType*(info: TLineInfo, typ: PType) = proc checkConstructedType*(info: TLineInfo, typ: PType) =
var t = typ.skipTypes({tyDistinct}) var t = typ.skipTypes({tyDistinct})
if t.kind in {tyTypeClass}: nil if t.kind in tyTypeClasses: nil
elif tfAcyclic in t.flags and skipTypes(t, abstractInst).kind != tyObject: elif tfAcyclic in t.flags and skipTypes(t, abstractInst).kind != tyObject:
LocalError(info, errInvalidPragmaX, "acyclic") LocalError(info, errInvalidPragmaX, "acyclic")
elif t.kind == tyVar and t.sons[0].kind == tyVar: elif t.kind == tyVar and t.sons[0].kind == tyVar:

View file

@ -370,10 +370,6 @@ proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
of tyNil: result = f.allowsNil of tyNil: result = f.allowsNil
else: nil else: nil
proc matchTypeClass(c: var TCandidate, f, a: PType): TTypeRelation =
result = if matchTypeClass(c.bindings, f, a): isGeneric
else: isNone
proc typeRangeRel(f, a: PType): TTypeRelation {.noinline.} = proc typeRangeRel(f, a: PType): TTypeRelation {.noinline.} =
let let
a0 = firstOrd(a) a0 = firstOrd(a)
@ -707,6 +703,19 @@ proc typeRel(c: var TCandidate, f, a: PType, doBind = true): TTypeRelation =
else: else:
return typeRel(c, prev, a) return typeRel(c, prev, a)
of tyBuiltInTypeClass:
var prev = PType(idTableGet(c.bindings, f))
if prev == nil:
let targetKind = f.sons[0].kind
if targetKind == a.skipTypes({tyRange}).kind or
(targetKind in {tyProc, tyPointer} and a.kind == tyNil):
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:
@ -727,10 +736,10 @@ proc typeRel(c: var TCandidate, f, a: PType, doBind = true): TTypeRelation =
else: else:
result = isNone result = isNone
else: else:
if a.kind == tyTypeClass: if f.sonsLen > 0:
result = isGeneric result = typeRel(c, f.lastSon, a)
else: else:
result = matchTypeClass(c, f, a) result = isGeneric
if result == isGeneric: if result == isGeneric:
var concrete = concreteType(c, a) var concrete = concreteType(c, a)
@ -751,7 +760,7 @@ proc typeRel(c: var TCandidate, f, a: PType, doBind = true): TTypeRelation =
if f.sonsLen == 0: if f.sonsLen == 0:
result = isGeneric result = isGeneric
else: else:
result = matchTypeClass(c, f, a.sons[0]) result = typeRel(c, f, a.sons[0])
if result == isGeneric: if result == isGeneric:
put(c.bindings, f, a) put(c.bindings, f, a)
else: else:
@ -911,9 +920,8 @@ proc ParamTypesMatchAux(m: var TCandidate, f, argType: PType,
InternalAssert a.len > 0 InternalAssert a.len > 0
r = typeRel(m, f.lastSon, a.lastSon) r = typeRel(m, f.lastSon, a.lastSon)
else: else:
let match = matchTypeClass(m.bindings, fMaybeStatic, a) r = typeRel(m, fMaybeStatic, a)
if not match: r = isNone if r != isNone:
else:
# XXX: Ideally, this should happen much earlier somewhere near # XXX: Ideally, this should happen much earlier somewhere near
# semOpAux, but to do that, we need to be able to query the # semOpAux, but to do that, we need to be able to query the
# overload set to determine whether compile-time value is expected # overload set to determine whether compile-time value is expected

View file

@ -409,18 +409,8 @@ 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", "and", "or", "not", "any", "static"] "ParametricTypeClass", "BuiltInTypeClass",
"and", "or", "not", "any", "static"]
proc consToStr(t: PType): string =
if t.len > 0: result = t.typeToString
else: result = typeToStr[t.kind].strip
proc constraintsToStr(t: PType): string =
let sep = if tfAny in t.flags: " or " else: " and "
result = ""
for i in countup(0, t.len - 1):
if i > 0: result.add(sep)
result.add(t.sons[i].consToStr)
proc TypeToString(typ: PType, prefer: TPreferedDesc = preferName): string = proc TypeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
var t = typ var t = typ
@ -444,19 +434,24 @@ proc TypeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
add(result, ']') add(result, ']')
of tyTypeDesc: of tyTypeDesc:
if t.len == 0: result = "typedesc" if t.len == 0: result = "typedesc"
else: result = "typedesc[" & constraintsToStr(t) & "]" else: result = "typedesc[" & typeToString(t) & "]"
of tyStatic: of tyStatic:
InternalAssert t.len > 0 InternalAssert t.len > 0
result = "static[" & constraintsToStr(t) & "]" result = "static[" & typeToString(t) & "]"
of tyTypeClass: of tyTypeClass:
if t.n != nil: return t.sym.owner.name.s InternalAssert t.sym != nil and t.sym.owner != nil
case t.len return t.sym.owner.name.s
of 0: result = "typeclass[]" of tyBuiltInTypeClass:
of 1: result = "typeclass[" & consToStr(t.sons[0]) & "]" return "TypeClass"
else: result = constraintsToStr(t) of tyAnd:
result = typeToString(t.sons[0]) & " and " & typeToString(t.sons[1])
of tyOr:
result = typeToString(t.sons[0]) & " and " & typeToString(t.sons[1])
of tyNot:
result = "not " & typeToString(t.sons[0])
of tyExpr: of tyExpr:
if t.len == 0: result = "expr" if t.len == 0: result = "expr"
else: result = "expr[" & constraintsToStr(t) & "]" else: result = "expr[" & typeToString(t) & "]"
of tyArray: of tyArray:
if t.sons[0].kind == tyRange: if t.sons[0].kind == tyRange:
result = "array[" & rangeToStr(t.sons[0].n) & ", " & result = "array[" & rangeToStr(t.sons[0].n) & ", " &
@ -978,42 +973,6 @@ proc isGenericAlias*(t: PType): bool =
proc skipGenericAlias*(t: PType): PType = proc skipGenericAlias*(t: PType): PType =
return if t.isGenericAlias: t.lastSon else: t return if t.isGenericAlias: t.lastSon else: t
proc matchTypeClass*(bindings: var TIdTable, typeClass, t: PType): bool =
for i in countup(0, typeClass.sonsLen - 1):
let req = typeClass.sons[i]
var match = req.kind == skipTypes(t, {tyRange, tyGenericInst}).kind
if not match:
case req.kind
of tyGenericBody:
if t.kind == tyGenericInst and t.sons[0] == req:
match = true
IdTablePut(bindings, typeClass, t)
of tyTypeClass:
match = matchTypeClass(bindings, req, t)
elif t.kind == tyTypeClass:
match = matchTypeClass(bindings, t, req)
elif t.kind in {tyObject} and req.len != 0:
# empty 'object' is fine as constraint in a type class
match = sameType(t, req)
if tfAny in typeClass.flags:
if match: return true
else:
if not match: return false
# if the loop finished without returning, either all constraints matched
# or none of them matched.
result = if tfAny in typeClass.flags: false else: true
if result == true:
IdTablePut(bindings, typeClass, t)
proc matchTypeClass*(typeClass, typ: PType): bool =
var bindings: TIdTable
initIdTable(bindings)
result = matchTypeClass(bindings, typeClass, typ)
proc typeAllowedAux(marker: var TIntSet, typ: PType, kind: TSymKind, proc typeAllowedAux(marker: var TIntSet, typ: PType, kind: TSymKind,
flags: TTypeAllowedFlags = {}): bool = flags: TTypeAllowedFlags = {}): bool =
assert(kind in {skVar, skLet, skConst, skParam, skResult}) assert(kind in {skVar, skLet, skConst, skParam, skResult})