generic types can be used like type classes. distinct can be applied to type classes.

This commit is contained in:
Zahary Karadjov
2012-03-25 20:55:21 +03:00
parent 296ef07955
commit bc2eb0ea9b
11 changed files with 147 additions and 79 deletions

View File

@@ -310,6 +310,8 @@ type
tfFromGeneric # type is an instantiation of a generic; this is needed
# because for instantiations of objects, structural
# type equality has to be used
tfAll # type class requires all constraints to be met (default)
tfAny # type class requires any constraint to be met
TTypeFlags* = set[TTypeFlag]

View File

@@ -662,3 +662,8 @@ proc InternalError*(info: TLineInfo, errMsg: string) =
proc InternalError*(errMsg: string) =
writeContext(UnknownLineInfo())
rawMessage(errInternal, errMsg)
template AssertNotNull*(e: expr): expr =
if(e == nil): InternalError($InstantiationInfo())
e

View File

@@ -219,3 +219,6 @@ proc binaryStrSearch*(x: openarray[string], y: string): int =
return mid
result = - 1
# Can we keep this? I'm using it all the time
template nimdbg*: expr = c.filename.endsWith"nimdbg.nim"

View File

@@ -20,7 +20,7 @@ proc instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable,
if a.kind != nkSym:
InternalError(a.info, "instantiateGenericParamList; no symbol")
var q = a.sym
if q.typ.kind notin {tyTypeDesc, tyGenericParam}: continue
if q.typ.kind notin {tyTypeDesc, tyGenericParam, tyTypeClass}: continue
var s = newSym(skType, q.name, getCurrOwner())
s.info = q.info
s.flags = s.flags + {sfUsed, sfFromGeneric}
@@ -107,8 +107,6 @@ proc sideEffectsCheck(c: PContext, s: PSym) =
s.ast.sons[genericParamsPos].kind == nkEmpty:
c.threadEntries.add(s)
template nimdbg: expr = c.filename.endsWith"nimdbg.nim"
proc applyConcreteTypesToSig(genericProc: PSym, concTypes: seq[PType]): PType =
# XXX: This is intended to replace the use of semParamList in generateInstance.
# The results of semParamList's analysis are already encoded in the original

View File

@@ -475,42 +475,6 @@ proc semObjectNode(c: PContext, n: PNode, prev: PType): PType =
result.n = newNodeI(nkRecList, n.info)
semRecordNodeAux(c, n.sons[2], check, pos, result.n, result.sym)
proc addTypeVarsOfGenericBody(c: PContext, t: PType, genericParams: PNode,
cl: var TIntSet): PType =
result = t
if t == nil: return
if ContainsOrIncl(cl, t.id): return
case t.kind
of tyGenericBody:
result = newTypeS(tyGenericInvokation, c)
addSon(result, t)
for i in countup(0, sonsLen(t) - 2):
if t.sons[i].kind != tyGenericParam:
InternalError("addTypeVarsOfGenericBody")
# do not declare ``TKey`` twice:
#if not ContainsOrIncl(cl, t.sons[i].sym.ident.id):
var s = copySym(t.sons[i].sym)
s.position = sonsLen(genericParams)
if s.typ == nil or s.typ.kind != tyGenericParam:
InternalError("addTypeVarsOfGenericBody 2")
addDecl(c, s)
addSon(genericParams, newSymNode(s))
addSon(result, t.sons[i])
of tyGenericInst:
var L = sonsLen(t) - 1
t.sons[L] = addTypeVarsOfGenericBody(c, t.sons[L], genericParams, cl)
of tyGenericInvokation:
for i in countup(1, sonsLen(t) - 1):
t.sons[i] = addTypeVarsOfGenericBody(c, t.sons[i], genericParams, cl)
else:
for i in countup(0, sonsLen(t) - 1):
t.sons[i] = addTypeVarsOfGenericBody(c, t.sons[i], genericParams, cl)
proc paramType(c: PContext, n, genericParams: PNode, cl: var TIntSet): PType =
result = semTypeNode(c, n, nil)
if genericParams != nil and sonsLen(genericParams) == 0:
result = addTypeVarsOfGenericBody(c, result, genericParams, cl)
proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind) =
if kind == skMacro and param.typ.kind in {tyTypeDesc, tyExpr, tyStmt}:
let nn = getSysSym"PNimrodNode"
@@ -520,13 +484,33 @@ proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind) =
else:
addDecl(c, param)
proc paramTypeClass(c: PContext, paramType: PType, procKind: TSymKind): PType =
proc paramTypeClass(c: PContext, paramType: PType, procKind: TSymKind):
tuple[typ: PType, id: PIdent] =
# if typ is not-nil, the param should be turned into a generic param
# if id is not nil, the generic param will bind just once (see below)
case paramType.kind:
of tyExpr:
# proc(a, b: expr)
if procKind notin {skTemplate, skMacro}:
result = newTypeS(tyGenericParam, c)
result.typ = newTypeS(tyGenericParam, c)
of tyDistinct:
# type T1 = distinct expr
# type S1 = distinct Sortable
# proc x(a, b: T1, c, d: S1)
# This forces bindOnce behavior for the type class, equivalent to
# proc x[T, S](a, b: T, c, d: S)
result = paramTypeClass(c, paramType.lastSon, procKind)
result.id = paramType.sym.name
of tyGenericBody:
# type Foo[T] = object
# proc x(a: Foo, b: Foo)
result.typ = newTypeS(tyTypeClass, c)
result.typ.addSon(paramType)
result.id = paramType.sym.name # bindOnce by default
of tyTypeClass:
result.typ = copyType(paramType, getCurrOwner(), false)
else: nil
proc semProcTypeNode(c: PContext, n, genericParams: PNode,
prev: PType, kind: TSymKind): PType =
var
@@ -555,14 +539,8 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
hasDefault = a.sons[length-1].kind != nkEmpty
if hasType:
typ = paramType(c, a.sons[length-2], genericParams, cl)
if c.filename.endsWith"nimdbg.nim" and typ != nil:
echo("PARAM TYPE ", typ.kind, " ")
if genericParams != nil:
echo genericParams.info.toFileLineCol
debug typ
#if matchType(typ, [(tyVar, 0)], tyGenericInvokation):
# debug a.sons[length-2][0][1]
typ = semTypeNode(c, a.sons[length-2], nil)
if hasDefault:
def = semExprWithType(c, a.sons[length-1])
# check type compability between def.typ and typ:
@@ -580,42 +558,46 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
if skipTypes(typ, {tyGenericInst}).kind == tyEmpty: continue
for j in countup(0, length-3):
var arg = newSymS(skParam, a.sons[j], c)
let typeClass = paramTypeClass(c, typ, kind)
var endingType = typ
var (typeClass, paramTypId) = paramTypeClass(c, typ, kind)
if typeClass != nil:
let typeClassParamId = getIdent(arg.name.s & ":type")
if paramTypId == nil: paramTypId = getIdent(arg.name.s & ":type")
if genericParams == nil:
# genericParams is nil when the proc is being instantiated
# the resolved type will be in scope then
var s = SymtabGet(c.tab, typeClassParamId)
arg.typ = s.typ
endingType = SymtabGet(c.tab, paramTypId).AssertNotNull.typ
else:
var s = newSym(skType, typeClassParamId, getCurrOwner())
s.typ = typeClass
s.typ.sym = s
s.position = genericParams.len
genericParams.addSon(newSymNode(s))
arg.typ = s.typ
else:
arg.typ = typ
block addImplicitGeneric:
# is this a bindOnce type class already present in the param list?
for i in countup(0, genericParams.len - 1):
if genericParams.sons[i].sym.name == paramTypId:
endingType = genericParams.sons[i].typ
break addImplicitGeneric
var s = newSym(skType, paramTypId, getCurrOwner())
s.typ = typeClass
s.typ.sym = s
s.position = genericParams.len
genericParams.addSon(newSymNode(s))
endingType = typeClass
arg.typ = endingType
arg.position = counter
inc(counter)
if def != nil and def.kind != nkEmpty: arg.ast = copyTree(def)
if ContainsOrIncl(check, arg.name.id):
LocalError(a.sons[j].info, errAttemptToRedefine, arg.name.s)
addSon(result.n, newSymNode(arg))
addSon(result, typ)
addSon(result, endingType)
addParamOrResult(c, arg, kind)
if n.sons[0].kind != nkEmpty:
var r = paramType(c, n.sons[0], genericParams, cl)
if n.sons[0].kind != nkEmpty:
var r = semTypeNode(c, n.sons[0], nil)
# turn explicit 'void' return type into 'nil' because the rest of the
# compiler only checks for 'nil':
if skipTypes(r, {tyGenericInst}).kind != tyEmpty:
result.sons[0] = r
res.typ = result.sons[0]
#if matchType(result, [(tyProc, 1), (tyVar, 0)], tyGenericInvokation):
# debug result
proc semStmtListType(c: PContext, n: PNode, prev: PType): PType =
checkMinSonsLen(n, 1)
@@ -707,9 +689,23 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
if sonsLen(n) == 1: result = semTypeNode(c, n.sons[0], prev)
else: GlobalError(n.info, errTypeExpected)
of nkCallKinds:
# expand macros and templates
var expandedSym = expectMacroOrTemplateCall(c, n)
result = semExpandToType(c, n, expandedSym)
let op = n.sons[0].ident.id
if op in {ord(wAnd), ord(wOr)}:
var
t1 = semTypeNode(c, n.sons[1], nil)
t2 = semTypeNode(c, n.sons[2], nil)
if t1 == nil: GlobalError(n.sons[1].info, errTypeExpected)
elif t2 == nil: GlobalError(n.sons[2].info, errTypeExpected)
else:
result = newTypeS(tyTypeClass, c)
result.addSon(t1)
result.addSon(t2)
result.flags.incl(if op == ord(wAnd): tfAll else: tfAny)
else:
# expand macros and templates
var expandedSym = expectMacroOrTemplateCall(c, n)
result = semExpandToType(c, n, expandedSym)
of nkWhenStmt:
var whenResult = semWhen(c, n, false)
if whenResult.kind == nkStmtList: whenResult.kind = nkStmtListType

View File

@@ -202,6 +202,7 @@ proc ReplaceTypeVarsT*(cl: var TReplTypeVars, t: PType): PType =
result = t
if t == nil: return
case t.kind
of tyTypeClass: nil
of tyGenericParam:
result = lookupTypeVar(cl, t)
if result.kind == tyGenericInvokation:

View File

@@ -208,9 +208,15 @@ proc tupleRel(mapping: var TIdTable, f, a: PType): TTypeRelation =
var y = a.n.sons[i].sym
if x.name.id != y.name.id: return isNone
proc constraintRel(mapping: var TIdTable, f, a: PType): TTypeRelation =
proc matchTypeClass(mapping: var TIdTable, f, a: PType): TTypeRelation =
result = isNone
if f.kind == a.kind: result = isGeneric
let son = f.sons[0]
if son.kind == a.kind:
result = isGeneric
elif son.kind == tyGenericBody:
if a.kind == tyGenericInst and a.sons[0] == son:
result = isGeneric
put(mapping, f, a)
proc procTypeRel(mapping: var TIdTable, f, a: PType): TTypeRelation =
proc inconsistentVarTypes(f, a: PType): bool {.inline.} =
@@ -261,7 +267,8 @@ proc typeRel(mapping: var TIdTable, f, a: PType): TTypeRelation =
assert(a != nil)
if a.kind == tyGenericInst and
skipTypes(f, {tyVar}).kind notin {
tyGenericBody, tyGenericInvokation, tyGenericParam}:
tyGenericBody, tyGenericInvokation,
tyGenericParam, tyTypeClass }:
return typeRel(mapping, f, lastSon(a))
if a.kind == tyVar and f.kind != tyVar:
return typeRel(mapping, f, a.sons[0])
@@ -341,7 +348,7 @@ proc typeRel(mapping: var TIdTable, f, a: PType): TTypeRelation =
if result < isGeneric: result = isNone
else: nil
of tyTypeClass:
result = constraintRel(mapping, f.sons[0], a)
result = matchTypeClass(mapping, f, a)
of tyOrdinal:
if isOrdinalType(a):
var x = if a.kind == tyOrdinal: a.sons[0] else: a
@@ -414,7 +421,7 @@ proc typeRel(mapping: var TIdTable, f, a: PType): TTypeRelation =
of tyGenericBody:
let ff = lastSon(f)
if ff != nil: result = typeRel(mapping, ff, a)
of tyGenericInvokation:
of tyGenericInvokation:
assert(f.sons[0].kind == tyGenericBody)
if a.kind == tyGenericInvokation:
#InternalError("typeRel: tyGenericInvokation -> tyGenericInvokation")

View File

@@ -409,7 +409,7 @@ proc TypeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
of tySequence:
result = "seq[" & typeToString(t.sons[0]) & ']'
of tyOrdinal:
result = "ordinal"
result = "ordinal[" & typeToString(t.sons[0]) & ']'
of tySet:
result = "set[" & typeToString(t.sons[0]) & ']'
of tyOpenArray:

View File

@@ -2173,7 +2173,7 @@ proc InstantiationInfo*(index = -1): tuple[filename: string, line: int] {.
## This is only useful for advanced meta programming. See the implementation
## of `assert` for an example.
proc raiseAssert(msg: string) {.noinline.} =
proc raiseAssert*(msg: string) {.noinline.} =
raise newException(EAssertionFailed, msg)
template assert*(cond: bool, msg = "") =
@@ -2182,7 +2182,7 @@ template assert*(cond: bool, msg = "") =
## raises an ``EAssertionFailure`` exception. However, the compiler may
## not generate any code at all for ``assert`` if it is advised to do so.
## Use ``assert`` for debugging purposes only.
bind raiseAssert, InstantiationInfo
bind InstantiationInfo
when compileOption("assertions"):
{.line.}:
if not cond:
@@ -2191,11 +2191,33 @@ template assert*(cond: bool, msg = "") =
template doAssert*(cond: bool, msg = "") =
## same as `assert` but is always turned on and not affected by the
## ``--assertions`` command line switch.
bind raiseAssert, InstantiationInfo
bind InstantiationInfo
{.line: InstantiationInfo().}:
if not cond:
raiseAssert(astToStr(cond) & ' ' & msg)
template onFailedAssert*(msg: expr, code: stmt): stmt =
## Sets an assertion failure handler that will intercept any assert statements
## following `onFailedAssert` in the current lexical scope.
## Can be defined multiple times in a single function.
##
## .. code-block:: nimrod
##
## proc example(x: int): TErrorCode =
## onFailedAssert(msg):
## log msg
## return E_FAIL
##
## assert(...)
##
## onFailedAssert(msg):
## raise newException(EMyException, msg)
##
## assert(...)
##
template raiseAssert(msgIMPL: string): stmt =
let `msg` = msgIMPL
code
proc shallow*[T](s: var seq[T]) {.noSideEffect, inline.} =
## marks a sequence `s` as `shallow`:idx:. Subsequent assignments will not

View File

@@ -0,0 +1,34 @@
type
TFoo[T] = object
val: T
T1 = distinct expr
T2 = distinct expr
proc takesExpr(x, y) =
echo x, y
proc same(x, y: T1) =
echo x, y
proc takesFoo(x, y: TFoo) =
echo x.val, y.val
proc takes2Types(x,y: T1, z: T2) =
echo x, y, z
takesExpr(1, 2)
takesExpr(1, "xxx")
takesExpr[bool, int](true, 0)
same(1, 2)
same("test", "test")
var f: TFoo[int]
f.val = 10
takesFoo(f, f)
takes2Types(1, 1, "string")
takes2Types[string, int]("test", "test", 1)