diff options
author | Zahary Karadjov <zahary@gmail.com> | 2014-03-19 02:52:48 +0200 |
---|---|---|
committer | Zahary Karadjov <zahary@gmail.com> | 2014-03-20 01:16:50 +0200 |
commit | 4b7655fd10d81ea50d0af41152df07ec21b05232 (patch) | |
tree | a437c41d515806717de7737d065098a91e13a3b7 | |
parent | a66d059accc9db4c376fdb1220ee2c5ca8daf311 (diff) | |
download | Nim-4b7655fd10d81ea50d0af41152df07ec21b05232.tar.gz |
reference implementation of a vector swizzle library
This also provides the initial steps towards support for type class "filtered" type inference fixes an "ordinal type expected" ICE, related to the use of static params
-rw-r--r-- | compiler/ast.nim | 2 | ||||
-rw-r--r-- | compiler/sem.nim | 3 | ||||
-rw-r--r-- | compiler/semcall.nim | 19 | ||||
-rw-r--r-- | compiler/semdata.nim | 21 | ||||
-rw-r--r-- | compiler/semexprs.nim | 43 | ||||
-rw-r--r-- | compiler/semstmts.nim | 15 | ||||
-rw-r--r-- | compiler/semtypes.nim | 55 | ||||
-rw-r--r-- | compiler/sigmatch.nim | 33 | ||||
-rw-r--r-- | compiler/types.nim | 1 | ||||
-rw-r--r-- | tests/bind/tinvalidbindtypedesc.nim | 2 | ||||
-rw-r--r-- | tests/metatype/swizzle.nim | 79 | ||||
-rw-r--r-- | tests/metatype/typeclassinference.nim | 10 |
12 files changed, 225 insertions, 58 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim index 00fe3b246..ae1b34fd9 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -435,6 +435,8 @@ type tfUnresolved, # marks unresolved typedesc/static params: e.g. # proc foo(T: typedesc, list: seq[T]): var T # proc foo(L: static[int]): array[L, int] + # can be attached to ranges to indicate that the range + # depends on unresolved static params. tfRetType, # marks return types in proc (used to detect type classes # used as return types for return type inference) tfCapturesEnv, # whether proc really captures some environment diff --git a/compiler/sem.nim b/compiler/sem.nim index 093fc9452..c35cff027 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -66,6 +66,9 @@ proc fitNode(c: PContext, formal: PType, arg: PNode): PNode = result = copyTree(arg) result.typ = formal +proc inferWithMetatype(c: PContext, formal: PType, + arg: PNode, coerceDistincts = false): PNode + var commonTypeBegin = PType(kind: tyExpr) proc commonType*(x, y: PType): PType = diff --git a/compiler/semcall.nim b/compiler/semcall.nim index 5d480bc98..b994a94d7 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -228,12 +228,25 @@ proc indexTypesMatch(c: PContext, f, a: PType, arg: PNode): PNode = if m.genericConverter and result != nil: instGenericConvertersArg(c, result, m) -proc convertTo*(c: PContext, f: PType, n: PNode): PNode = +proc inferWithMetatype(c: PContext, formal: PType, + arg: PNode, coerceDistincts = false): PNode = var m: TCandidate - initCandidate(c, m, f) - result = paramTypesMatch(m, f, n.typ, n, nil) + initCandidate(c, m, formal) + m.coerceDistincts = coerceDistincts + result = paramTypesMatch(m, formal, arg.typ, arg, nil) if m.genericConverter and result != nil: instGenericConvertersArg(c, result, m) + if result != nil: + # This almost exactly replicates the steps taken by the compiler during + # param matching. It performs an embarassing ammount of back-and-forth + # type jugling, but it's the price to pay for consistency and correctness + result.typ = generateTypeInstance(c, m.bindings, arg.info, + formal.skipTypes({tyCompositeTypeClass})) + else: + typeMismatch(arg, formal, arg.typ) + # error correction: + result = copyTree(arg) + result.typ = formal proc semResolvedCall(c: PContext, n: PNode, x: TCandidate): PNode = assert x.state == csMatch diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 088b93fae..5ec66b51c 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -251,6 +251,27 @@ proc makeNotType*(c: PContext, t1: PType): PType = propagateToOwner(result, t1) result.flags.incl(t1.flags * {tfHasStatic}) +proc nMinusOne*(n: PNode): PNode = + result = newNode(nkCall, n.info, @[ + newSymNode(getSysMagic("<", mUnaryLt)), + n]) + +# Remember to fix the procs below this one when you make changes! +proc makeRangeWithStaticExpr*(c: PContext, n: PNode): PType = + let intType = getSysType tyInt + result = newTypeS(tyRange, c) + result.sons = @[intType] + result.n = newNode(nkRange, n.info, @[ + newIntTypeNode(nkIntLit, 0, intType), + makeStaticExpr(c, n.nMinusOne)]) + +template rangeHasStaticIf*(t: PType): bool = + # this accepts the ranges's node + t.n[1].kind == nkStaticExpr + +template getStaticTypeFromRange*(t: PType): PType = + t.n[1][0][1].typ + proc newTypeS(kind: TTypeKind, c: PContext): PType = result = newType(kind, getCurrOwner()) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index a6e2eaa23..b8c4f3297 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -121,6 +121,8 @@ proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = return n of skType: markUsed(n, s) + if s.typ.kind == tyStatic and s.typ.n != nil: + return s.typ.n result = newSymNode(s, n.info) result.typ = makeTypeDesc(c, s.typ) else: @@ -191,15 +193,35 @@ proc isCastable(dst, src: PType): bool = proc isSymChoice(n: PNode): bool {.inline.} = result = n.kind in nkSymChoices +proc maybeLiftType(t: var PType, c: PContext, info: TLineInfo) = + # XXX: liftParamType started to perform addDecl + # we could do that instead in semTypeNode by snooping for added + # gnrc. params, then it won't be necessary to open a new scope here + openScope(c) + var lifted = liftParamType(c, skType, newNodeI(nkArgList, info), + t, ":anon", info) + closeScope(c) + if lifted != nil: t = lifted + proc semConv(c: PContext, n: PNode): PNode = if sonsLen(n) != 2: localError(n.info, errConvNeedsOneArg) return n + result = newNodeI(nkConv, n.info) - result.typ = semTypeNode(c, n.sons[0], nil).skipTypes({tyGenericInst}) - addSon(result, copyTree(n.sons[0])) - addSon(result, semExprWithType(c, n.sons[1])) - var op = result.sons[1] + var targetType = semTypeNode(c, n.sons[0], nil) + maybeLiftType(targetType, c, n[0].info) + result.addSon copyTree(n.sons[0]) + var op = semExprWithType(c, n.sons[1]) + + if targetType.isMetaType: + let final = inferWithMetatype(c, targetType, op, true) + result.addSon final + result.typ = final.typ + return + + result.typ = targetType + addSon(result, op) if not isSymChoice(op): let status = checkConvertible(c, result.typ, op.typ) @@ -221,7 +243,7 @@ proc semConv(c: PContext, n: PNode): PNode = for i in countup(0, sonsLen(op) - 1): let it = op.sons[i] let status = checkConvertible(c, result.typ, it.typ) - if status == convOK: + if status in {convOK, convNotNeedeed}: markUsed(n, it.sym) markIndirect(c, it.sym) return it @@ -325,14 +347,7 @@ proc isOpImpl(c: PContext, n: PNode): PNode = tfIterator notin t.flags)) else: var t2 = n[2].typ.skipTypes({tyTypeDesc}) - # XXX: liftParamType started to perform addDecl - # we could do that instead in semTypeNode by snooping for added - # gnrc. params, then it won't be necessary to open a new scope here - openScope(c) - let lifted = liftParamType(c, skType, newNodeI(nkArgList, n.info), - t2, ":anon", n.info) - closeScope(c) - if lifted != nil: t2 = lifted + maybeLiftType(t2, c, n.info) var m: TCandidate initCandidate(c, m, t2) let match = typeRel(m, t2, t1) != isNone @@ -1202,7 +1217,7 @@ proc semAsgn(c: PContext, n: PNode): PNode = if lhsIsResult: {efAllowDestructor} else: {}) if lhsIsResult: n.typ = enforceVoidContext - if resultTypeIsInferrable(lhs.sym.typ): + if c.p.owner.kind != skMacro and resultTypeIsInferrable(lhs.sym.typ): if cmpTypes(c, lhs.typ, rhs.typ) == isGeneric: internalAssert c.p.resultSym != nil lhs.typ = rhs.typ diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 15bfaab10..38854bc8a 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -12,9 +12,6 @@ var enforceVoidContext = PType(kind: tyStmt) -proc semCommand(c: PContext, n: PNode): PNode = - result = semExprNoType(c, n) - proc semDiscard(c: PContext, n: PNode): PNode = result = n checkSonsLen(n, 1) @@ -133,6 +130,7 @@ proc fixNilType(n: PNode) = n.typ = nil proc discardCheck(c: PContext, result: PNode) = + if c.inTypeClass > 0: return if result.typ != nil and result.typ.kind notin {tyStmt, tyEmpty}: if result.kind == nkNilLit: result.typ = nil @@ -143,11 +141,6 @@ proc discardCheck(c: PContext, result: PNode) = while n.kind in skipForDiscardable: n = n.lastSon n.typ = nil - elif c.inTypeClass > 0: - if result.typ.kind == tyBool: - let verdict = semConstExpr(c, result) - if verdict.intVal == 0: - localError(result.info, "type class predicate failed") elif result.typ.kind != tyError and gCmd != cmdInteractive: if result.typ.kind == tyNil: fixNilType(result) @@ -1324,13 +1317,17 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode = return else: n.sons[i] = semExpr(c, n.sons[i]) + if c.inTypeClass > 0 and n[i].typ != nil and n[i].typ.kind == tyBool: + let verdict = semConstExpr(c, n[i]) + if verdict.intVal == 0: + localError(result.info, "type class predicate failed") if n.sons[i].typ == enforceVoidContext or usesResult(n.sons[i]): voidContext = true n.typ = enforceVoidContext if i == last and (length == 1 or efWantValue in flags): n.typ = n.sons[i].typ if not isEmptyType(n.typ): n.kind = nkStmtListExpr - elif i != last or voidContext or c.inTypeClass > 0: + elif i != last or voidContext: discardCheck(c, n.sons[i]) else: n.typ = n.sons[i].typ diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index f0ef43be4..269676624 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -198,19 +198,6 @@ proc semRange(c: PContext, n: PNode, prev: PType): PType = localError(n.info, errXExpectsOneTypeParam, "range") result = newOrPrevType(tyError, prev, c) -proc nMinusOne(n: PNode): PNode = - result = newNode(nkCall, n.info, @[ - newSymNode(getSysMagic("<", mUnaryLt)), - n]) - -proc makeRangeWithStaticExpr(c: PContext, n: PNode): PType = - let intType = getSysType(tyInt) - result = newTypeS(tyRange, c) - result.sons = @[intType] - result.n = newNode(nkRange, n.info, @[ - newIntTypeNode(nkIntLit, 0, intType), - makeStaticExpr(c, n.nMinusOne)]) - proc semArray(c: PContext, n: PNode, prev: PType): PType = var indx, base: PType result = newOrPrevType(tyArray, prev, c) @@ -229,6 +216,7 @@ proc semArray(c: PContext, n: PNode, prev: PType): PType = if not isOrdinalType(e.typ.lastSon): localError(n[1].info, errOrdinalTypeExpected) indx = makeRangeWithStaticExpr(c, e) + indx.flags.incl tfUnresolved elif e.kind in nkCallKinds and hasGenericArguments(e): if not isOrdinalType(e.typ): localError(n[1].info, errOrdinalTypeExpected) @@ -628,17 +616,28 @@ proc semObjectNode(c: PContext, n: PNode, prev: PType): PType = if base == nil and tfInheritable notin result.flags: incl(result.flags, tfFinal) +proc findEnforcedStaticType(t: PType): PType = + # This handles types such as `static[T] and Foo`, + # which are subset of `static[T]`, hence they could + # be treated in the same way + if t.kind == tyStatic: return t + if t.kind == tyAnd: + for s in t.sons: + let t = findEnforcedStaticType(s) + if t != nil: return t + proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind) = template addDecl(x) = if sfGenSym notin x.flags: addDecl(c, x) if kind == skMacro: - if param.typ.kind == tyTypeDesc: - addDecl(param) - elif param.typ.kind == tyStatic: + let staticType = findEnforcedStaticType(param.typ) + if staticType != nil: var a = copySym(param) - a.typ = param.typ.base + a.typ = staticType.base addDecl(a) + elif param.typ.kind == tyTypeDesc: + addDecl(param) else: # within a macro, every param has the type PNimrodNode! let nn = getSysSym"PNimrodNode" @@ -944,14 +943,18 @@ proc semBlockType(c: PContext, n: PNode, prev: PType): PType = proc semGenericParamInInvokation(c: PContext, n: PNode): PType = result = semTypeNode(c, n, nil) -proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType = +proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType = if s.typ == nil: localError(n.info, "cannot instantiate the '$1' $2" % [s.name.s, ($s.kind).substr(2).toLower]) return newOrPrevType(tyError, prev, c) + var t = s.typ + if t.kind == tyCompositeTypeClass and t.base.kind == tyGenericBody: + t = t.base + result = newOrPrevType(tyGenericInvokation, prev, c) - addSonSkipIntLit(result, s.typ) + addSonSkipIntLit(result, t) template addToResult(typ) = if typ.isNil: @@ -959,23 +962,24 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType = rawAddSon(result, typ) else: addSonSkipIntLit(result, typ) - if s.typ.kind == tyForward: + if t.kind == tyForward: for i in countup(1, sonsLen(n)-1): var elem = semGenericParamInInvokation(c, n.sons[i]) addToResult(elem) - elif s.typ.kind != tyGenericBody: + return + elif t.kind != tyGenericBody: #we likely got code of the form TypeA[TypeB] where TypeA is #not generic. localError(n.info, errNoGenericParamsAllowedForX, s.name.s) return newOrPrevType(tyError, prev, c) else: - var m = newCandidate(c, s, n) + var m = newCandidate(c, t) matches(c, n, copyTree(n), m) if m.state != csMatch: - var err = "cannot instantiate " & typeToString(s.typ) & "\n" & + var err = "cannot instantiate " & typeToString(t) & "\n" & "got: (" & describeArgs(c, n) & ")\n" & - "but expected: (" & describeArgs(c, s.typ.n, 0) & ")" + "but expected: (" & describeArgs(c, t.n, 0) & ")" localError(n.info, errGenerated, err) return newOrPrevType(tyError, prev, c) @@ -987,7 +991,8 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType = addToResult(typ) if isConcrete: - if s.ast == nil: + if s.ast == nil and s.typ.kind != tyCompositeTypeClass: + # XXX: What kind of error is this? is it still relevant? localError(n.info, errCannotInstantiateX, s.name.s) result = newOrPrevType(tyError, prev, c) else: diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 662268380..56490dfe3 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -40,6 +40,8 @@ type proxyMatch*: bool # to prevent instantiations genericConverter*: bool # true if a generic converter needs to # be instantiated + coerceDistincts*: bool # this is an explicit coercion that can strip away + # a distrinct type typedescMatched: bool inheritancePenalty: int # to prefer closest father object type errors*: seq[string] # additional clarifications to be displayed to the @@ -114,6 +116,9 @@ proc newCandidate*(ctx: PContext, callee: PSym, binding: PNode, calleeScope = -1): TCandidate = initCandidate(ctx, result, callee, binding, calleeScope) +proc newCandidate*(ctx: PContext, callee: PType): TCandidate = + initCandidate(ctx, result, callee) + proc copyCandidate(a: var TCandidate, b: TCandidate) = a.c = b.c a.exactMatches = b.exactMatches @@ -460,7 +465,8 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate, if param.kind == nkVarTy: dummyName = param[0] - dummyType = makeVarType(c, a) + dummyType = if a.kind != tyVar: makeVarType(c, a) + else: a else: dummyName = param dummyType = a @@ -470,7 +476,7 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate, dummyParam.typ = dummyType addDecl(c, dummyParam) - var checkedBody = c.semTryExpr(c, copyTree(body.n[3]), bufferErrors = false) + var checkedBody = c.semTryExpr(c, body.n[3].copyTree, bufferErrors = false) m.errors = bufferedMsgs clearBufferedMsgs() if checkedBody == nil: return isNone @@ -623,6 +629,20 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = result = typeRel(c, f.sons[1], a.sons[1]) if result < isGeneric: result = isNone + elif tfUnresolved in fRange.flags and + rangeHasStaticIf(fRange): + # This is a range from an array instantiated with a generic + # static param. We must extract the static param here and bind + # it to the size of the currently supplied array. + var + rangeStaticT = fRange.getStaticTypeFromRange + replacementT = newTypeWithSons(c.c, tyStatic, @[tyInt.getSysType]) + inputUpperBound = a.sons[0].n[1].intVal + # we must correct for the off-by-one discrepancy between + # ranges and static params: + replacementT.n = newIntNode(nkIntLit, inputUpperBound + 1) + put(c.bindings, rangeStaticT, replacementT) + result = isGeneric elif lengthOrd(fRange) != lengthOrd(a): result = isNone else: discard @@ -686,6 +706,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = result = isSubtype of tyDistinct: if (a.kind == tyDistinct) and sameDistinctTypes(f, a): result = isEqual + elif c.coerceDistincts: result = typeRel(c, f.base, a) of tySet: if a.kind == tySet: if (f.sons[0].kind != tyGenericParam) and (a.sons[0].kind == tyEmpty): @@ -848,7 +869,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = of tyUserTypeClass, tyUserTypeClassInst: considerPreviousT: - result = matchUserTypeClass(c.c, c, f, a) + result = matchUserTypeClass(c.c, c, f, aOrig) if result == isGeneric: put(c.bindings, f, a) @@ -863,7 +884,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation = of tyGenericParam: var x = PType(idTableGet(c.bindings, f)) if x == nil: - if c.calleeSym != nil and c.calleeSym.kind == skType and + if c.calleeSym != nil and c.callee.kind == tyGenericBody and f.kind == tyGenericParam and not c.typedescMatched: # XXX: The fact that generic types currently use tyGenericParam for # their parameters is really a misnomer. tyGenericParam means "match @@ -1051,7 +1072,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType, # XXX: When implicit statics are the default # this will be done earlier - we just have to # make sure that static types enter here - + # XXX: weaken tyGenericParam and call it tyGenericPlaceholder # and finally start using tyTypedesc for generic types properly. if argType.kind == tyGenericParam and tfWildcard in argType.flags: @@ -1060,7 +1081,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType, return argSemantized if argType.kind == tyStatic: - if m.calleeSym.kind == skType: + if m.callee.kind == tyGenericBody: result = newNodeI(nkType, argOrig.info) result.typ = makeTypeFromExpr(c, arg) return diff --git a/compiler/types.nim b/compiler/types.nim index 89b15c4a8..d7410c67a 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -436,6 +436,7 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = of tyStatic: internalAssert t.len > 0 result = "static[" & typeToString(t.sons[0]) & "]" + if t.n != nil: result.add "(" & renderTree(t.n) & ")" of tyUserTypeClass: internalAssert t.sym != nil and t.sym.owner != nil return t.sym.owner.name.s diff --git a/tests/bind/tinvalidbindtypedesc.nim b/tests/bind/tinvalidbindtypedesc.nim index 7d97d2e0d..5b2f51110 100644 --- a/tests/bind/tinvalidbindtypedesc.nim +++ b/tests/bind/tinvalidbindtypedesc.nim @@ -1,5 +1,5 @@ discard """ - line: 11 + line: 10 errormsg: "type mismatch: got (typedesc[float], string)" """ diff --git a/tests/metatype/swizzle.nim b/tests/metatype/swizzle.nim new file mode 100644 index 000000000..ce18fa234 --- /dev/null +++ b/tests/metatype/swizzle.nim @@ -0,0 +1,79 @@ +discard """ + output: '''3 +[1, 3] +[2, 1, 2] +''' +""" + +import macros, strutils + +template accept(e: expr) = + static: assert(compiles(e)) + +template reject(e: expr) = + static: assert(not compiles(e)) + +proc swizzleIdx(c: char): int = + return case c + of 'x': 0 + of 'y': 1 + of 'z': 2 + of 'w': 3 + of 'r': 0 + of 'g': 1 + of 'b': 2 + of 'a': 3 + else: 0 + +proc isSwizzle(s: string): bool = + template trySet(name, set) = + block search: + for c in s: + if c notin set: + break search + return true + + trySet coords, {'x', 'y', 'z', 'w'} + trySet colors, {'r', 'g', 'b', 'a'} + + return false + +type + StringIsSwizzle = generic value + value.isSwizzle + + SwizzleStr = static[string] and StringIsSwizzle + +proc foo(x: SwizzleStr) = + echo "sw" + +accept foo("xx") +reject foo("xe") + +type + Vec[N: static[int]; T] = array[N, T] + + +proc card(x: Vec): int = x.N +proc `$`(x: Vec): string = x.repr.strip + +macro `.`(x: Vec, swizzle: SwizzleStr): expr = + var + cardinality = swizzle.len + values = newNimNode(nnkBracket) + v = genSym() + + for c in swizzle: + values.add newNimNode(nnkBracketExpr).add( + v, c.swizzleIdx.newIntLitNode) + + return quote do: + let `v` = `x` + Vec[`cardinality`, `v`.T](`values`) + +var z = Vec([1, 2, 3]) + +echo z.card +echo z.xz +echo z.yxy + diff --git a/tests/metatype/typeclassinference.nim b/tests/metatype/typeclassinference.nim new file mode 100644 index 000000000..72b5aca96 --- /dev/null +++ b/tests/metatype/typeclassinference.nim @@ -0,0 +1,10 @@ +import typetraits + +type + Vec[N: static[int]; T] = distinct array[N, T] + +var x = Vec([1, 2, 3]) + +static: + assert x.type.name == "Vec[static[int](3), int]" + |