From 9c8bc3a244b4bbcdc56fdd255bb4e1a7ed30f781 Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Tue, 2 Oct 2012 16:45:34 +0300 Subject: [PATCH] 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 --- compiler/evals.nim | 26 ++++++++++++++++++++- compiler/semexprs.nim | 31 +++++++++++++++++-------- compiler/semfold.nim | 11 --------- compiler/semgnrc.nim | 20 ++++++++-------- compiler/semtypes.nim | 33 ++++++++++++++------------ compiler/semtypinst.nim | 3 ++- compiler/sigmatch.nim | 37 ++++-------------------------- compiler/types.nim | 33 +++++++++++++++++++++++++- lib/pure/os.nim | 2 +- tests/compile/tisop.nim | 42 ++++++++++++++++++++++++++++++++++ tests/compile/ttypeclasses.nim | 4 ++-- tests/reject/tcaseexpr1.nim | 6 ++--- tests/reject/tenummix.nim | 2 +- 13 files changed, 162 insertions(+), 88 deletions(-) create mode 100644 tests/compile/tisop.nim diff --git a/compiler/evals.nim b/compiler/evals.nim index 9c73a6b78d..0886b1ef50 100755 --- a/compiler/evals.nim +++ b/compiler/evals.nim @@ -521,7 +521,7 @@ proc evalSym(c: PEvalContext, n: PNode, flags: TEvalFlags): PNode = else: result = nil if result == nil or {sfImportc, sfForward} * s.flags != {}: result = raiseCannotEval(c, n.info) - + proc evalIncDec(c: PEvalContext, n: PNode, sign: biggestInt): PNode = result = evalAux(c, n.sons[1], {efLValue}) if isSpecial(result): return @@ -875,6 +875,27 @@ proc evalTypeTrait*(n: PNode, context: PSym): PNode = else: 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) = if n.kind notin {nkStrLit, nkRStrLit, nkTripleStrLit}: GlobalError(n.info, errStringLiteralExpected) @@ -968,6 +989,9 @@ proc evalMagicOrCall(c: PEvalContext, n: PNode): PNode = of mTypeTrait: n.sons[1] = evalAux(c, n.sons[1], {}) 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 mStaticExec: let cmd = evalAux(c, n.sons[1], {}) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index a658f7f44f..51ede16145 100755 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -281,18 +281,29 @@ proc semOf(c: PContext, n: PNode): PNode = n.typ = getSysType(tyBool) result = n -proc semIs(c: PContext, n: PNode): PNode = - 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: +proc semIs(c: PContext, n: PNode): PNode = + if sonsLen(n) != 3: 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) = for i in countup(1, sonsLen(n) - tailToExclude): var a = n.sons[i] diff --git a/compiler/semfold.nim b/compiler/semfold.nim index c2958ca5ef..d0805d9210 100755 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -610,17 +610,6 @@ proc getConstExpr(m: PSym, n: PNode): PNode = result = newIntNodeT(sonsLen(a), n) else: 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: result = newStrNodeT(renderTree(n[1], {renderNoComments}), n) of mConStrStr: diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim index dde799ab34..bffa8ad8ea 100755 --- a/compiler/semgnrc.nim +++ b/compiler/semgnrc.nim @@ -261,16 +261,16 @@ proc semGenericStmt(c: PContext, n: PNode, else: a.sons[2] = semGenericStmt(c, a.sons[2], flags+{withinTypeDesc}, toBind) of nkEnumTy: - checkMinSonsLen(n, 1) - if n.sons[0].kind != nkEmpty: - n.sons[0] = semGenericStmt(c, n.sons[0], flags+{withinTypeDesc}, toBind) - for i in countup(1, sonsLen(n) - 1): - var a: PNode - case n.sons[i].kind - of nkEnumFieldDef: a = n.sons[i].sons[0] - of nkIdent: a = n.sons[i] - else: illFormedAst(n) - addDeclAt(c, newSymS(skUnknown, getIdentNode(a.sons[i]), c), c.tab.tos-1) + if n.sonsLen > 0: + if n.sons[0].kind != nkEmpty: + n.sons[0] = semGenericStmt(c, n.sons[0], flags+{withinTypeDesc}, toBind) + for i in countup(1, sonsLen(n) - 1): + var a: PNode + case n.sons[i].kind + of nkEnumFieldDef: a = n.sons[i].sons[0] + of nkIdent: a = n.sons[i] + else: illFormedAst(n) + addDeclAt(c, newSymS(skUnknown, getIdentNode(a.sons[i]), c), c.tab.tos-1) of nkObjectTy, nkTupleTy: nil of nkFormalParams: diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index eeb48a6478..c41727b106 100755 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -799,22 +799,25 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = LocalError(n.info, errTypeExpected) result = newOrPrevType(tyError, prev, c) of nkCallKinds: - let op = n.sons[0].ident - if op.id in {ord(wAnd), ord(wOr)} or op.s == "|": - var - t1 = semTypeNode(c, n.sons[1], nil) - t2 = semTypeNode(c, n.sons[2], nil) - if t1 == nil: - LocalError(n.sons[1].info, errTypeExpected) - result = newOrPrevType(tyError, prev, c) - elif t2 == nil: - LocalError(n.sons[2].info, errTypeExpected) - result = newOrPrevType(tyError, prev, c) + if n[0].kind == nkIdent: + let op = n.sons[0].ident + if op.id in {ord(wAnd), ord(wOr)} or op.s == "|": + var + t1 = semTypeNode(c, n.sons[1], nil) + t2 = semTypeNode(c, n.sons[2], nil) + if t1 == nil: + LocalError(n.sons[1].info, errTypeExpected) + result = newOrPrevType(tyError, prev, c) + elif t2 == nil: + 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: - result = newTypeS(tyTypeClass, c) - result.addSonSkipIntLit(t1) - result.addSonSkipIntLit(t2) - result.flags.incl(if op.id == ord(wAnd): tfAll else: tfAny) + result = semTypeExpr(c, n) else: result = semTypeExpr(c, n) of nkCurlyExpr: diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index d75594dff2..70453c6dba 100755 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -59,6 +59,7 @@ type proc ReplaceTypeVarsT*(cl: var TReplTypeVars, t: PType): PType proc ReplaceTypeVarsS(cl: var TReplTypeVars, s: PSym): PSym +proc ReplaceTypeVarsN(cl: var TReplTypeVars, n: PNode): PNode proc prepareNode(cl: var TReplTypeVars, n: PNode): PNode = result = copyNode(n) @@ -66,7 +67,7 @@ proc prepareNode(cl: var TReplTypeVars, n: PNode): PNode = for i in 0 .. safeLen(n)-1: # XXX HACK: ``f(a, b)``, avoid to instantiate `f` 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 = if n == nil: return diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 82a8c9399d..8f1ca5f360 100755 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -11,7 +11,7 @@ ## the call to overloaded procs, generic procs and operators. import - intsets, ast, astalgo, semdata, types, msgs, renderer, lookups, semtypinst, + intsets, ast, astalgo, semdata, types, msgs, renderer, lookups, semtypinst, magicsys, condsyms, idents, lexer, options type @@ -257,37 +257,6 @@ proc tupleRel(c: var TCandidate, f, a: PType): TTypeRelation = var y = a.n.sons[i].sym 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 inconsistentVarTypes(f, a: PType): bool {.inline.} = 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 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 = # is a subtype of f? result = isNone diff --git a/compiler/types.nim b/compiler/types.nim index b650b49b83..e02d932334 100755 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -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 a = a.sons[i] 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 = assert(kind in {skVar, skLet, skConst, skParam, skResult}) # if we have already checked the type, return true, because we stop the diff --git a/lib/pure/os.nim b/lib/pure/os.nim index 5a298db5bc..68634e4caf 100755 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -149,7 +149,7 @@ else: # UNIX-like operating system FileSystemCaseSensitive* = true ExeExt* = "" ScriptExt* = "" - DynlibFormat* = "lib$1.so" + DynlibFormat* = when defined(macosx): "lib$1.dylib" else: "lib$1.so" when defined(macosx) or defined(bsd): var diff --git a/tests/compile/tisop.nim b/tests/compile/tisop.nim new file mode 100644 index 0000000000..509cc4e95e --- /dev/null +++ b/tests/compile/tisop.nim @@ -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) + diff --git a/tests/compile/ttypeclasses.nim b/tests/compile/ttypeclasses.nim index e22ede1dc7..5e23e84898 100644 --- a/tests/compile/ttypeclasses.nim +++ b/tests/compile/ttypeclasses.nim @@ -2,8 +2,8 @@ type TFoo[T] = object val: T - T1 = distinct expr - T2 = distinct expr + T1 = expr + T2 = expr proc takesExpr(x, y) = echo x, y diff --git a/tests/reject/tcaseexpr1.nim b/tests/reject/tcaseexpr1.nim index 0c2e7c452d..e5e08e25e5 100644 --- a/tests/reject/tcaseexpr1.nim +++ b/tests/reject/tcaseexpr1.nim @@ -1,11 +1,11 @@ discard """ file: "tcaseexpr1.nim" - line: 23 - errormsg: "not all cases are covered" - line: 29 errormsg: "type mismatch: got (string) but expected 'int'" + + line: 23 + errormsg: "not all cases are covered" """ type diff --git a/tests/reject/tenummix.nim b/tests/reject/tenummix.nim index 55c0c05a95..77b7f44d22 100644 --- a/tests/reject/tenummix.nim +++ b/tests/reject/tenummix.nim @@ -1,6 +1,6 @@ discard """ file: "system.nim" - line: 643 + line: 649 errormsg: "type mismatch" """