the is operator now works with type classes and type variables
bugfixes: the DLL tests were failing on Mac OS X, due to an incorrect DynlibFormat
This commit is contained in:
parent
770d4a997e
commit
9c8bc3a244
13 changed files with 162 additions and 88 deletions
|
|
@ -875,6 +875,27 @@ proc evalTypeTrait*(n: PNode, context: PSym): PNode =
|
||||||
else:
|
else:
|
||||||
internalAssert false
|
internalAssert false
|
||||||
|
|
||||||
|
proc evalIsOp*(n: PNode): PNode =
|
||||||
|
InternalAssert n.sonsLen == 3 and
|
||||||
|
n[1].kind == nkSym and n[1].sym.kind == skType and
|
||||||
|
n[2].kind in {nkStrLit..nkTripleStrLit, nkType}
|
||||||
|
|
||||||
|
let t1 = n[1].sym.typ
|
||||||
|
|
||||||
|
if n[2].kind in {nkStrLit..nkTripleStrLit}:
|
||||||
|
case n[2].strVal.normalize
|
||||||
|
of "closure":
|
||||||
|
let t = skipTypes(t1, abstractRange)
|
||||||
|
result = newIntNode(nkIntLit, ord(t.kind == tyProc and
|
||||||
|
t.callConv == ccClosure))
|
||||||
|
else:
|
||||||
|
let t2 = n[2].typ
|
||||||
|
var match = if t2.kind == tyTypeClass: matchTypeClass(t2, t1)
|
||||||
|
else: sameType(t1, t2)
|
||||||
|
result = newIntNode(nkIntLit, ord(match))
|
||||||
|
|
||||||
|
result.typ = n.typ
|
||||||
|
|
||||||
proc expectString(n: PNode) =
|
proc expectString(n: PNode) =
|
||||||
if n.kind notin {nkStrLit, nkRStrLit, nkTripleStrLit}:
|
if n.kind notin {nkStrLit, nkRStrLit, nkTripleStrLit}:
|
||||||
GlobalError(n.info, errStringLiteralExpected)
|
GlobalError(n.info, errStringLiteralExpected)
|
||||||
|
|
@ -968,6 +989,9 @@ proc evalMagicOrCall(c: PEvalContext, n: PNode): PNode =
|
||||||
of mTypeTrait:
|
of mTypeTrait:
|
||||||
n.sons[1] = evalAux(c, n.sons[1], {})
|
n.sons[1] = evalAux(c, n.sons[1], {})
|
||||||
result = evalTypeTrait(n, c.module)
|
result = evalTypeTrait(n, c.module)
|
||||||
|
of mIs:
|
||||||
|
n.sons[1] = evalAux(c, n.sons[1], {})
|
||||||
|
result = evalIsOp(n)
|
||||||
of mSlurp: result = evalSlurp(evalAux(c, n.sons[1], {}), c.module)
|
of mSlurp: result = evalSlurp(evalAux(c, n.sons[1], {}), c.module)
|
||||||
of mStaticExec:
|
of mStaticExec:
|
||||||
let cmd = evalAux(c, n.sons[1], {})
|
let cmd = evalAux(c, n.sons[1], {})
|
||||||
|
|
|
||||||
|
|
@ -282,16 +282,27 @@ proc semOf(c: PContext, n: PNode): PNode =
|
||||||
result = n
|
result = n
|
||||||
|
|
||||||
proc semIs(c: PContext, n: PNode): PNode =
|
proc semIs(c: PContext, n: PNode): PNode =
|
||||||
if sonsLen(n) == 3:
|
if sonsLen(n) != 3:
|
||||||
n.typ = getSysType(tyBool)
|
|
||||||
let a = semTypeNode(c, n[1], nil)
|
|
||||||
n.sons[1] = newNodeIT(nkType, n[1].info, a)
|
|
||||||
if n[2].kind notin {nkStrLit..nkTripleStrLit}:
|
|
||||||
let b = semTypeNode(c, n[2], nil)
|
|
||||||
n.sons[2] = newNodeIT(nkType, n[2].info, b)
|
|
||||||
else:
|
|
||||||
LocalError(n.info, errXExpectsTwoArguments, "is")
|
LocalError(n.info, errXExpectsTwoArguments, "is")
|
||||||
|
|
||||||
result = n
|
result = n
|
||||||
|
n.typ = getSysType(tyBool)
|
||||||
|
|
||||||
|
n.sons[1] = semExprWithType(c, n[1])
|
||||||
|
if n[1].typ.kind != tyTypeDesc:
|
||||||
|
LocalError(n[0].info, errTypeExpected)
|
||||||
|
|
||||||
|
if n[2].kind notin {nkStrLit..nkTripleStrLit}:
|
||||||
|
let t2 = semTypeNode(c, n[2], nil)
|
||||||
|
n.sons[2] = newNodeIT(nkType, n[2].info, t2)
|
||||||
|
|
||||||
|
if n[1].typ.sonsLen == 0:
|
||||||
|
# this is a typedesc variable, leave for evals
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
let t1 = n[1].typ.sons[0]
|
||||||
|
# BUGFIX: don't evaluate this too early: ``T is void``
|
||||||
|
if not containsGenericType(t1): result = evalIsOp(n)
|
||||||
|
|
||||||
proc semOpAux(c: PContext, n: PNode, tailToExclude = 1) =
|
proc semOpAux(c: PContext, n: PNode, tailToExclude = 1) =
|
||||||
for i in countup(1, sonsLen(n) - tailToExclude):
|
for i in countup(1, sonsLen(n) - tailToExclude):
|
||||||
|
|
|
||||||
|
|
@ -610,17 +610,6 @@ proc getConstExpr(m: PSym, n: PNode): PNode =
|
||||||
result = newIntNodeT(sonsLen(a), n)
|
result = newIntNodeT(sonsLen(a), n)
|
||||||
else:
|
else:
|
||||||
result = magicCall(m, n)
|
result = magicCall(m, n)
|
||||||
of mIs:
|
|
||||||
# BUGFIX: don't evaluate this too early: ``T is void``
|
|
||||||
if not containsGenericType(n[1].typ):
|
|
||||||
if n[2].kind in {nkStrLit..nkTripleStrLit}:
|
|
||||||
case n[2].strVal.normalize
|
|
||||||
of "closure":
|
|
||||||
let t = skipTypes(n[1].typ, abstractRange)
|
|
||||||
result = newIntNodeT(ord(t.kind == tyProc and
|
|
||||||
t.callConv == ccClosure), n)
|
|
||||||
elif not containsGenericType(n[2].typ):
|
|
||||||
result = newIntNodeT(ord(sameType(n[1].typ, n[2].typ)), n)
|
|
||||||
of mAstToStr:
|
of mAstToStr:
|
||||||
result = newStrNodeT(renderTree(n[1], {renderNoComments}), n)
|
result = newStrNodeT(renderTree(n[1], {renderNoComments}), n)
|
||||||
of mConStrStr:
|
of mConStrStr:
|
||||||
|
|
|
||||||
|
|
@ -261,16 +261,16 @@ proc semGenericStmt(c: PContext, n: PNode,
|
||||||
else:
|
else:
|
||||||
a.sons[2] = semGenericStmt(c, a.sons[2], flags+{withinTypeDesc}, toBind)
|
a.sons[2] = semGenericStmt(c, a.sons[2], flags+{withinTypeDesc}, toBind)
|
||||||
of nkEnumTy:
|
of nkEnumTy:
|
||||||
checkMinSonsLen(n, 1)
|
if n.sonsLen > 0:
|
||||||
if n.sons[0].kind != nkEmpty:
|
if n.sons[0].kind != nkEmpty:
|
||||||
n.sons[0] = semGenericStmt(c, n.sons[0], flags+{withinTypeDesc}, toBind)
|
n.sons[0] = semGenericStmt(c, n.sons[0], flags+{withinTypeDesc}, toBind)
|
||||||
for i in countup(1, sonsLen(n) - 1):
|
for i in countup(1, sonsLen(n) - 1):
|
||||||
var a: PNode
|
var a: PNode
|
||||||
case n.sons[i].kind
|
case n.sons[i].kind
|
||||||
of nkEnumFieldDef: a = n.sons[i].sons[0]
|
of nkEnumFieldDef: a = n.sons[i].sons[0]
|
||||||
of nkIdent: a = n.sons[i]
|
of nkIdent: a = n.sons[i]
|
||||||
else: illFormedAst(n)
|
else: illFormedAst(n)
|
||||||
addDeclAt(c, newSymS(skUnknown, getIdentNode(a.sons[i]), c), c.tab.tos-1)
|
addDeclAt(c, newSymS(skUnknown, getIdentNode(a.sons[i]), c), c.tab.tos-1)
|
||||||
of nkObjectTy, nkTupleTy:
|
of nkObjectTy, nkTupleTy:
|
||||||
nil
|
nil
|
||||||
of nkFormalParams:
|
of nkFormalParams:
|
||||||
|
|
|
||||||
|
|
@ -799,22 +799,25 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
||||||
LocalError(n.info, errTypeExpected)
|
LocalError(n.info, errTypeExpected)
|
||||||
result = newOrPrevType(tyError, prev, c)
|
result = newOrPrevType(tyError, prev, c)
|
||||||
of nkCallKinds:
|
of nkCallKinds:
|
||||||
let op = n.sons[0].ident
|
if n[0].kind == nkIdent:
|
||||||
if op.id in {ord(wAnd), ord(wOr)} or op.s == "|":
|
let op = n.sons[0].ident
|
||||||
var
|
if op.id in {ord(wAnd), ord(wOr)} or op.s == "|":
|
||||||
t1 = semTypeNode(c, n.sons[1], nil)
|
var
|
||||||
t2 = semTypeNode(c, n.sons[2], nil)
|
t1 = semTypeNode(c, n.sons[1], nil)
|
||||||
if t1 == nil:
|
t2 = semTypeNode(c, n.sons[2], nil)
|
||||||
LocalError(n.sons[1].info, errTypeExpected)
|
if t1 == nil:
|
||||||
result = newOrPrevType(tyError, prev, c)
|
LocalError(n.sons[1].info, errTypeExpected)
|
||||||
elif t2 == nil:
|
result = newOrPrevType(tyError, prev, c)
|
||||||
LocalError(n.sons[2].info, errTypeExpected)
|
elif t2 == nil:
|
||||||
result = newOrPrevType(tyError, prev, c)
|
LocalError(n.sons[2].info, errTypeExpected)
|
||||||
|
result = newOrPrevType(tyError, prev, c)
|
||||||
|
else:
|
||||||
|
result = newTypeS(tyTypeClass, c)
|
||||||
|
result.addSonSkipIntLit(t1)
|
||||||
|
result.addSonSkipIntLit(t2)
|
||||||
|
result.flags.incl(if op.id == ord(wAnd): tfAll else: tfAny)
|
||||||
else:
|
else:
|
||||||
result = newTypeS(tyTypeClass, c)
|
result = semTypeExpr(c, n)
|
||||||
result.addSonSkipIntLit(t1)
|
|
||||||
result.addSonSkipIntLit(t2)
|
|
||||||
result.flags.incl(if op.id == ord(wAnd): tfAll else: tfAny)
|
|
||||||
else:
|
else:
|
||||||
result = semTypeExpr(c, n)
|
result = semTypeExpr(c, n)
|
||||||
of nkCurlyExpr:
|
of nkCurlyExpr:
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,7 @@ type
|
||||||
|
|
||||||
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
|
||||||
|
proc ReplaceTypeVarsN(cl: var TReplTypeVars, n: PNode): PNode
|
||||||
|
|
||||||
proc prepareNode(cl: var TReplTypeVars, n: PNode): PNode =
|
proc prepareNode(cl: var TReplTypeVars, n: PNode): PNode =
|
||||||
result = copyNode(n)
|
result = copyNode(n)
|
||||||
|
|
@ -66,7 +67,7 @@ proc prepareNode(cl: var TReplTypeVars, n: PNode): PNode =
|
||||||
for i in 0 .. safeLen(n)-1:
|
for i in 0 .. safeLen(n)-1:
|
||||||
# XXX HACK: ``f(a, b)``, avoid to instantiate `f`
|
# XXX HACK: ``f(a, b)``, avoid to instantiate `f`
|
||||||
if i == 0: result.add(n[i])
|
if i == 0: result.add(n[i])
|
||||||
else: result.add(prepareNode(cl, n[i]))
|
else: result.add(ReplaceTypeVarsN(cl, n[i]))
|
||||||
|
|
||||||
proc ReplaceTypeVarsN(cl: var TReplTypeVars, n: PNode): PNode =
|
proc ReplaceTypeVarsN(cl: var TReplTypeVars, n: PNode): PNode =
|
||||||
if n == nil: return
|
if n == nil: return
|
||||||
|
|
|
||||||
|
|
@ -257,37 +257,6 @@ proc tupleRel(c: var TCandidate, f, a: PType): TTypeRelation =
|
||||||
var y = a.n.sons[i].sym
|
var y = a.n.sons[i].sym
|
||||||
if x.name.id != y.name.id: return isNone
|
if x.name.id != y.name.id: return isNone
|
||||||
|
|
||||||
proc matchTypeClass(c: var TCandidate, typeClass, t: PType): TTypeRelation =
|
|
||||||
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
|
|
||||||
put(c.bindings, typeClass, t)
|
|
||||||
of tyTypeClass:
|
|
||||||
match = matchTypeClass(c, req, t) == isGeneric
|
|
||||||
else: nil
|
|
||||||
elif t.kind in {tyObject}:
|
|
||||||
match = sameType(t, req)
|
|
||||||
|
|
||||||
if tfAny in typeClass.flags:
|
|
||||||
if match: return isGeneric
|
|
||||||
else:
|
|
||||||
if not match: return isNone
|
|
||||||
|
|
||||||
# if the loop finished without returning, either all constraints matched
|
|
||||||
# or none of them matched.
|
|
||||||
result = if tfAny in typeClass.flags: isNone else: isGeneric
|
|
||||||
|
|
||||||
proc matchTypeClass*(typeClass, typ: PType): bool =
|
|
||||||
var c: TCandidate
|
|
||||||
InitCandidate(c, typeClass)
|
|
||||||
result = matchTypeClass(c, typeClass, typ) == isGeneric
|
|
||||||
|
|
||||||
proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
|
proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
|
||||||
proc inconsistentVarTypes(f, a: PType): bool {.inline.} =
|
proc inconsistentVarTypes(f, a: PType): bool {.inline.} =
|
||||||
result = f.kind != a.kind and (f.kind == tyVar or a.kind == tyVar)
|
result = f.kind != a.kind and (f.kind == tyVar or a.kind == tyVar)
|
||||||
|
|
@ -330,6 +299,10 @@ proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
|
||||||
result = isNone
|
result = isNone
|
||||||
else: nil
|
else: nil
|
||||||
|
|
||||||
|
proc matchTypeClass(c: var TCandidate, f, a: PType): TTypeRelation =
|
||||||
|
result = if matchTypeClass(c.bindings, f, a): isGeneric
|
||||||
|
else: isNone
|
||||||
|
|
||||||
proc typeRel(c: var TCandidate, f, a: PType): TTypeRelation =
|
proc typeRel(c: var TCandidate, f, a: PType): TTypeRelation =
|
||||||
# is a subtype of f?
|
# is a subtype of f?
|
||||||
result = isNone
|
result = isNone
|
||||||
|
|
|
||||||
|
|
@ -905,6 +905,37 @@ proc matchType*(a: PType, pattern: openArray[tuple[k:TTypeKind, i:int]],
|
||||||
a = a.sons[i]
|
a = a.sons[i]
|
||||||
result = a.kind == last
|
result = a.kind == last
|
||||||
|
|
||||||
|
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)
|
||||||
|
else: nil
|
||||||
|
elif t.kind in {tyObject}:
|
||||||
|
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
|
||||||
|
|
||||||
|
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): bool =
|
proc typeAllowedAux(marker: var TIntSet, typ: PType, kind: TSymKind): bool =
|
||||||
assert(kind in {skVar, skLet, skConst, skParam, skResult})
|
assert(kind in {skVar, skLet, skConst, skParam, skResult})
|
||||||
# if we have already checked the type, return true, because we stop the
|
# if we have already checked the type, return true, because we stop the
|
||||||
|
|
|
||||||
|
|
@ -149,7 +149,7 @@ else: # UNIX-like operating system
|
||||||
FileSystemCaseSensitive* = true
|
FileSystemCaseSensitive* = true
|
||||||
ExeExt* = ""
|
ExeExt* = ""
|
||||||
ScriptExt* = ""
|
ScriptExt* = ""
|
||||||
DynlibFormat* = "lib$1.so"
|
DynlibFormat* = when defined(macosx): "lib$1.dylib" else: "lib$1.so"
|
||||||
|
|
||||||
when defined(macosx) or defined(bsd):
|
when defined(macosx) or defined(bsd):
|
||||||
var
|
var
|
||||||
|
|
|
||||||
42
tests/compile/tisop.nim
Normal file
42
tests/compile/tisop.nim
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
import typetraits
|
||||||
|
|
||||||
|
type
|
||||||
|
TRecord = (tuple) or (object)
|
||||||
|
|
||||||
|
TFoo[T, U] = object
|
||||||
|
x: int
|
||||||
|
|
||||||
|
when T is string:
|
||||||
|
y: float
|
||||||
|
else:
|
||||||
|
y: string
|
||||||
|
|
||||||
|
when U is TRecord:
|
||||||
|
z: float
|
||||||
|
|
||||||
|
E = enum A, B, C
|
||||||
|
|
||||||
|
macro m(t: typedesc): typedesc =
|
||||||
|
if t is enum:
|
||||||
|
result = string
|
||||||
|
else:
|
||||||
|
result = int
|
||||||
|
|
||||||
|
var f: TFoo[int, int]
|
||||||
|
static: assert(f.y.type.name == "string")
|
||||||
|
|
||||||
|
when compiles(f.z):
|
||||||
|
{.error: "Foo should not have a `z` field".}
|
||||||
|
|
||||||
|
proc p(a, b) =
|
||||||
|
when a.type is int:
|
||||||
|
static: assert false
|
||||||
|
|
||||||
|
var f: TFoo[m(a.type), b.type]
|
||||||
|
static:
|
||||||
|
assert f.x.type.name == "int"
|
||||||
|
assert f.y.type.name == "float"
|
||||||
|
assert f.z.type.name == "float"
|
||||||
|
|
||||||
|
p(A, f)
|
||||||
|
|
||||||
|
|
@ -2,8 +2,8 @@ type
|
||||||
TFoo[T] = object
|
TFoo[T] = object
|
||||||
val: T
|
val: T
|
||||||
|
|
||||||
T1 = distinct expr
|
T1 = expr
|
||||||
T2 = distinct expr
|
T2 = expr
|
||||||
|
|
||||||
proc takesExpr(x, y) =
|
proc takesExpr(x, y) =
|
||||||
echo x, y
|
echo x, y
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
discard """
|
discard """
|
||||||
file: "tcaseexpr1.nim"
|
file: "tcaseexpr1.nim"
|
||||||
|
|
||||||
line: 23
|
|
||||||
errormsg: "not all cases are covered"
|
|
||||||
|
|
||||||
line: 29
|
line: 29
|
||||||
errormsg: "type mismatch: got (string) but expected 'int'"
|
errormsg: "type mismatch: got (string) but expected 'int'"
|
||||||
|
|
||||||
|
line: 23
|
||||||
|
errormsg: "not all cases are covered"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
type
|
type
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
discard """
|
discard """
|
||||||
file: "system.nim"
|
file: "system.nim"
|
||||||
line: 643
|
line: 649
|
||||||
errormsg: "type mismatch"
|
errormsg: "type mismatch"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue