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

@ -521,7 +521,7 @@ proc evalSym(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode =
else: result = nil else: result = nil
if result == nil or {sfImportc, sfForward} * s.flags != {}: if result == nil or {sfImportc, sfForward} * s.flags != {}:
result = raiseCannotEval(c, n.info) result = raiseCannotEval(c, n.info)
proc evalIncDec(c: PEvalContext, n: PNode, sign: biggestInt): PNode = proc evalIncDec(c: PEvalContext, n: PNode, sign: biggestInt): PNode =
result = evalAux(c, n.sons[1], {efLValue}) result = evalAux(c, n.sons[1], {efLValue})
if isSpecial(result): return if isSpecial(result): return
@ -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

@ -281,18 +281,29 @@ proc semOf(c: PContext, n: PNode): PNode =
n.typ = getSysType(tyBool) n.typ = getSysType(tyBool)
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):
var a = n.sons[i] var a = n.sons[i]

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

View file

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

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

@ -11,7 +11,7 @@
## the call to overloaded procs, generic procs and operators. ## the call to overloaded procs, generic procs and operators.
import import
intsets, ast, astalgo, semdata, types, msgs, renderer, lookups, semtypinst, intsets, ast, astalgo, semdata, types, msgs, renderer, lookups, semtypinst,
magicsys, condsyms, idents, lexer, options magicsys, condsyms, idents, lexer, options
type type
@ -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

@ -904,7 +904,38 @@ proc matchType*(a: PType, pattern: openArray[tuple[k:TTypeKind, i:int]],
if i >= a.sonslen or a.sons[i] == nil: return false if i >= a.sonslen or a.sons[i] == nil: return false
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"
""" """