summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorZahary Karadjov <zahary@gmail.com>2014-03-19 02:52:48 +0200
committerZahary Karadjov <zahary@gmail.com>2014-03-20 01:16:50 +0200
commit4b7655fd10d81ea50d0af41152df07ec21b05232 (patch)
treea437c41d515806717de7737d065098a91e13a3b7
parenta66d059accc9db4c376fdb1220ee2c5ca8daf311 (diff)
downloadNim-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.nim2
-rw-r--r--compiler/sem.nim3
-rw-r--r--compiler/semcall.nim19
-rw-r--r--compiler/semdata.nim21
-rw-r--r--compiler/semexprs.nim43
-rw-r--r--compiler/semstmts.nim15
-rw-r--r--compiler/semtypes.nim55
-rw-r--r--compiler/sigmatch.nim33
-rw-r--r--compiler/types.nim1
-rw-r--r--tests/bind/tinvalidbindtypedesc.nim2
-rw-r--r--tests/metatype/swizzle.nim79
-rw-r--r--tests/metatype/typeclassinference.nim10
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]"
+