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:
Zahary Karadjov 2012-10-02 16:45:34 +03:00
commit 9c8bc3a244
13 changed files with 162 additions and 88 deletions

View file

@ -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], {})

View file

@ -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):

View file

@ -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:

View file

@ -261,7 +261,7 @@ 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):

View file

@ -799,6 +799,7 @@ 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:
if n[0].kind == nkIdent:
let op = n.sons[0].ident let op = n.sons[0].ident
if op.id in {ord(wAnd), ord(wOr)} or op.s == "|": if op.id in {ord(wAnd), ord(wOr)} or op.s == "|":
var var
@ -817,6 +818,8 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
result.flags.incl(if op.id == ord(wAnd): tfAll else: tfAny) result.flags.incl(if op.id == ord(wAnd): tfAll else: tfAny)
else: else:
result = semTypeExpr(c, n) result = semTypeExpr(c, n)
else:
result = semTypeExpr(c, n)
of nkCurlyExpr: of nkCurlyExpr:
result = semTypeNode(c, n.sons[0], nil) result = semTypeNode(c, n.sons[0], nil)
if result != nil: if result != nil:

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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
View 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)

View file

@ -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

View file

@ -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

View file

@ -1,6 +1,6 @@
discard """ discard """
file: "system.nim" file: "system.nim"
line: 643 line: 649
errormsg: "type mismatch" errormsg: "type mismatch"
""" """