mirror of
https://github.com/nim-lang/Nim.git
synced 2025-12-29 01:14:41 +00:00
generic types can be used like type classes. distinct can be applied to type classes.
This commit is contained in:
@@ -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]
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
34
tests/compile/ttypeclasses.nim
Normal file
34
tests/compile/ttypeclasses.nim
Normal 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)
|
||||
|
||||
Reference in New Issue
Block a user