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
This commit is contained in:
Zahary Karadjov 2014-03-19 02:52:48 +02:00
commit 4b7655fd10
12 changed files with 225 additions and 58 deletions

View file

@ -435,6 +435,8 @@ type
tfUnresolved, # marks unresolved typedesc/static params: e.g. tfUnresolved, # marks unresolved typedesc/static params: e.g.
# proc foo(T: typedesc, list: seq[T]): var T # proc foo(T: typedesc, list: seq[T]): var T
# proc foo(L: static[int]): array[L, int] # 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 tfRetType, # marks return types in proc (used to detect type classes
# used as return types for return type inference) # used as return types for return type inference)
tfCapturesEnv, # whether proc really captures some environment tfCapturesEnv, # whether proc really captures some environment

View file

@ -66,6 +66,9 @@ proc fitNode(c: PContext, formal: PType, arg: PNode): PNode =
result = copyTree(arg) result = copyTree(arg)
result.typ = formal result.typ = formal
proc inferWithMetatype(c: PContext, formal: PType,
arg: PNode, coerceDistincts = false): PNode
var commonTypeBegin = PType(kind: tyExpr) var commonTypeBegin = PType(kind: tyExpr)
proc commonType*(x, y: PType): PType = proc commonType*(x, y: PType): PType =

View file

@ -228,12 +228,25 @@ proc indexTypesMatch(c: PContext, f, a: PType, arg: PNode): PNode =
if m.genericConverter and result != nil: if m.genericConverter and result != nil:
instGenericConvertersArg(c, result, m) 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 var m: TCandidate
initCandidate(c, m, f) initCandidate(c, m, formal)
result = paramTypesMatch(m, f, n.typ, n, nil) m.coerceDistincts = coerceDistincts
result = paramTypesMatch(m, formal, arg.typ, arg, nil)
if m.genericConverter and result != nil: if m.genericConverter and result != nil:
instGenericConvertersArg(c, result, m) 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 = proc semResolvedCall(c: PContext, n: PNode, x: TCandidate): PNode =
assert x.state == csMatch assert x.state == csMatch

View file

@ -251,6 +251,27 @@ proc makeNotType*(c: PContext, t1: PType): PType =
propagateToOwner(result, t1) propagateToOwner(result, t1)
result.flags.incl(t1.flags * {tfHasStatic}) 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 = proc newTypeS(kind: TTypeKind, c: PContext): PType =
result = newType(kind, getCurrOwner()) result = newType(kind, getCurrOwner())

View file

@ -121,6 +121,8 @@ proc semSym(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
return n return n
of skType: of skType:
markUsed(n, s) markUsed(n, s)
if s.typ.kind == tyStatic and s.typ.n != nil:
return s.typ.n
result = newSymNode(s, n.info) result = newSymNode(s, n.info)
result.typ = makeTypeDesc(c, s.typ) result.typ = makeTypeDesc(c, s.typ)
else: else:
@ -191,15 +193,35 @@ proc isCastable(dst, src: PType): bool =
proc isSymChoice(n: PNode): bool {.inline.} = proc isSymChoice(n: PNode): bool {.inline.} =
result = n.kind in nkSymChoices 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 = proc semConv(c: PContext, n: PNode): PNode =
if sonsLen(n) != 2: if sonsLen(n) != 2:
localError(n.info, errConvNeedsOneArg) localError(n.info, errConvNeedsOneArg)
return n return n
result = newNodeI(nkConv, n.info) result = newNodeI(nkConv, n.info)
result.typ = semTypeNode(c, n.sons[0], nil).skipTypes({tyGenericInst}) var targetType = semTypeNode(c, n.sons[0], nil)
addSon(result, copyTree(n.sons[0])) maybeLiftType(targetType, c, n[0].info)
addSon(result, semExprWithType(c, n.sons[1])) result.addSon copyTree(n.sons[0])
var op = result.sons[1] 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): if not isSymChoice(op):
let status = checkConvertible(c, result.typ, op.typ) 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): for i in countup(0, sonsLen(op) - 1):
let it = op.sons[i] let it = op.sons[i]
let status = checkConvertible(c, result.typ, it.typ) let status = checkConvertible(c, result.typ, it.typ)
if status == convOK: if status in {convOK, convNotNeedeed}:
markUsed(n, it.sym) markUsed(n, it.sym)
markIndirect(c, it.sym) markIndirect(c, it.sym)
return it return it
@ -325,14 +347,7 @@ proc isOpImpl(c: PContext, n: PNode): PNode =
tfIterator notin t.flags)) tfIterator notin t.flags))
else: else:
var t2 = n[2].typ.skipTypes({tyTypeDesc}) var t2 = n[2].typ.skipTypes({tyTypeDesc})
# XXX: liftParamType started to perform addDecl maybeLiftType(t2, c, n.info)
# 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
var m: TCandidate var m: TCandidate
initCandidate(c, m, t2) initCandidate(c, m, t2)
let match = typeRel(m, t2, t1) != isNone let match = typeRel(m, t2, t1) != isNone
@ -1202,7 +1217,7 @@ proc semAsgn(c: PContext, n: PNode): PNode =
if lhsIsResult: {efAllowDestructor} else: {}) if lhsIsResult: {efAllowDestructor} else: {})
if lhsIsResult: if lhsIsResult:
n.typ = enforceVoidContext 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: if cmpTypes(c, lhs.typ, rhs.typ) == isGeneric:
internalAssert c.p.resultSym != nil internalAssert c.p.resultSym != nil
lhs.typ = rhs.typ lhs.typ = rhs.typ

View file

@ -12,9 +12,6 @@
var enforceVoidContext = PType(kind: tyStmt) var enforceVoidContext = PType(kind: tyStmt)
proc semCommand(c: PContext, n: PNode): PNode =
result = semExprNoType(c, n)
proc semDiscard(c: PContext, n: PNode): PNode = proc semDiscard(c: PContext, n: PNode): PNode =
result = n result = n
checkSonsLen(n, 1) checkSonsLen(n, 1)
@ -133,6 +130,7 @@ proc fixNilType(n: PNode) =
n.typ = nil n.typ = nil
proc discardCheck(c: PContext, result: PNode) = proc discardCheck(c: PContext, result: PNode) =
if c.inTypeClass > 0: return
if result.typ != nil and result.typ.kind notin {tyStmt, tyEmpty}: if result.typ != nil and result.typ.kind notin {tyStmt, tyEmpty}:
if result.kind == nkNilLit: if result.kind == nkNilLit:
result.typ = nil result.typ = nil
@ -143,11 +141,6 @@ proc discardCheck(c: PContext, result: PNode) =
while n.kind in skipForDiscardable: while n.kind in skipForDiscardable:
n = n.lastSon n = n.lastSon
n.typ = nil 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: elif result.typ.kind != tyError and gCmd != cmdInteractive:
if result.typ.kind == tyNil: if result.typ.kind == tyNil:
fixNilType(result) fixNilType(result)
@ -1324,13 +1317,17 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
return return
else: else:
n.sons[i] = semExpr(c, n.sons[i]) 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]): if n.sons[i].typ == enforceVoidContext or usesResult(n.sons[i]):
voidContext = true voidContext = true
n.typ = enforceVoidContext n.typ = enforceVoidContext
if i == last and (length == 1 or efWantValue in flags): if i == last and (length == 1 or efWantValue in flags):
n.typ = n.sons[i].typ n.typ = n.sons[i].typ
if not isEmptyType(n.typ): n.kind = nkStmtListExpr 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]) discardCheck(c, n.sons[i])
else: else:
n.typ = n.sons[i].typ n.typ = n.sons[i].typ

View file

@ -198,19 +198,6 @@ proc semRange(c: PContext, n: PNode, prev: PType): PType =
localError(n.info, errXExpectsOneTypeParam, "range") localError(n.info, errXExpectsOneTypeParam, "range")
result = newOrPrevType(tyError, prev, c) 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 = proc semArray(c: PContext, n: PNode, prev: PType): PType =
var indx, base: PType var indx, base: PType
result = newOrPrevType(tyArray, prev, c) result = newOrPrevType(tyArray, prev, c)
@ -229,6 +216,7 @@ proc semArray(c: PContext, n: PNode, prev: PType): PType =
if not isOrdinalType(e.typ.lastSon): if not isOrdinalType(e.typ.lastSon):
localError(n[1].info, errOrdinalTypeExpected) localError(n[1].info, errOrdinalTypeExpected)
indx = makeRangeWithStaticExpr(c, e) indx = makeRangeWithStaticExpr(c, e)
indx.flags.incl tfUnresolved
elif e.kind in nkCallKinds and hasGenericArguments(e): elif e.kind in nkCallKinds and hasGenericArguments(e):
if not isOrdinalType(e.typ): if not isOrdinalType(e.typ):
localError(n[1].info, errOrdinalTypeExpected) 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: if base == nil and tfInheritable notin result.flags:
incl(result.flags, tfFinal) 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) = proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind) =
template addDecl(x) = template addDecl(x) =
if sfGenSym notin x.flags: addDecl(c, x) if sfGenSym notin x.flags: addDecl(c, x)
if kind == skMacro: if kind == skMacro:
if param.typ.kind == tyTypeDesc: let staticType = findEnforcedStaticType(param.typ)
addDecl(param) if staticType != nil:
elif param.typ.kind == tyStatic:
var a = copySym(param) var a = copySym(param)
a.typ = param.typ.base a.typ = staticType.base
addDecl(a) addDecl(a)
elif param.typ.kind == tyTypeDesc:
addDecl(param)
else: else:
# within a macro, every param has the type PNimrodNode! # within a macro, every param has the type PNimrodNode!
let nn = getSysSym"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 = proc semGenericParamInInvokation(c: PContext, n: PNode): PType =
result = semTypeNode(c, n, nil) 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: if s.typ == nil:
localError(n.info, "cannot instantiate the '$1' $2" % localError(n.info, "cannot instantiate the '$1' $2" %
[s.name.s, ($s.kind).substr(2).toLower]) [s.name.s, ($s.kind).substr(2).toLower])
return newOrPrevType(tyError, prev, c) 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) result = newOrPrevType(tyGenericInvokation, prev, c)
addSonSkipIntLit(result, s.typ) addSonSkipIntLit(result, t)
template addToResult(typ) = template addToResult(typ) =
if typ.isNil: if typ.isNil:
@ -959,23 +962,24 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
rawAddSon(result, typ) rawAddSon(result, typ)
else: addSonSkipIntLit(result, typ) else: addSonSkipIntLit(result, typ)
if s.typ.kind == tyForward: if t.kind == tyForward:
for i in countup(1, sonsLen(n)-1): for i in countup(1, sonsLen(n)-1):
var elem = semGenericParamInInvokation(c, n.sons[i]) var elem = semGenericParamInInvokation(c, n.sons[i])
addToResult(elem) addToResult(elem)
elif s.typ.kind != tyGenericBody: return
elif t.kind != tyGenericBody:
#we likely got code of the form TypeA[TypeB] where TypeA is #we likely got code of the form TypeA[TypeB] where TypeA is
#not generic. #not generic.
localError(n.info, errNoGenericParamsAllowedForX, s.name.s) localError(n.info, errNoGenericParamsAllowedForX, s.name.s)
return newOrPrevType(tyError, prev, c) return newOrPrevType(tyError, prev, c)
else: else:
var m = newCandidate(c, s, n) var m = newCandidate(c, t)
matches(c, n, copyTree(n), m) matches(c, n, copyTree(n), m)
if m.state != csMatch: if m.state != csMatch:
var err = "cannot instantiate " & typeToString(s.typ) & "\n" & var err = "cannot instantiate " & typeToString(t) & "\n" &
"got: (" & describeArgs(c, n) & ")\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) localError(n.info, errGenerated, err)
return newOrPrevType(tyError, prev, c) return newOrPrevType(tyError, prev, c)
@ -987,7 +991,8 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
addToResult(typ) addToResult(typ)
if isConcrete: 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) localError(n.info, errCannotInstantiateX, s.name.s)
result = newOrPrevType(tyError, prev, c) result = newOrPrevType(tyError, prev, c)
else: else:

View file

@ -40,6 +40,8 @@ type
proxyMatch*: bool # to prevent instantiations proxyMatch*: bool # to prevent instantiations
genericConverter*: bool # true if a generic converter needs to genericConverter*: bool # true if a generic converter needs to
# be instantiated # be instantiated
coerceDistincts*: bool # this is an explicit coercion that can strip away
# a distrinct type
typedescMatched: bool typedescMatched: bool
inheritancePenalty: int # to prefer closest father object type inheritancePenalty: int # to prefer closest father object type
errors*: seq[string] # additional clarifications to be displayed to the 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 = binding: PNode, calleeScope = -1): TCandidate =
initCandidate(ctx, result, callee, binding, calleeScope) initCandidate(ctx, result, callee, binding, calleeScope)
proc newCandidate*(ctx: PContext, callee: PType): TCandidate =
initCandidate(ctx, result, callee)
proc copyCandidate(a: var TCandidate, b: TCandidate) = proc copyCandidate(a: var TCandidate, b: TCandidate) =
a.c = b.c a.c = b.c
a.exactMatches = b.exactMatches a.exactMatches = b.exactMatches
@ -460,7 +465,8 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate,
if param.kind == nkVarTy: if param.kind == nkVarTy:
dummyName = param[0] dummyName = param[0]
dummyType = makeVarType(c, a) dummyType = if a.kind != tyVar: makeVarType(c, a)
else: a
else: else:
dummyName = param dummyName = param
dummyType = a dummyType = a
@ -470,7 +476,7 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate,
dummyParam.typ = dummyType dummyParam.typ = dummyType
addDecl(c, dummyParam) 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 m.errors = bufferedMsgs
clearBufferedMsgs() clearBufferedMsgs()
if checkedBody == nil: return isNone 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]) result = typeRel(c, f.sons[1], a.sons[1])
if result < isGeneric: if result < isGeneric:
result = isNone 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): elif lengthOrd(fRange) != lengthOrd(a):
result = isNone result = isNone
else: discard else: discard
@ -686,6 +706,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
result = isSubtype result = isSubtype
of tyDistinct: of tyDistinct:
if (a.kind == tyDistinct) and sameDistinctTypes(f, a): result = isEqual if (a.kind == tyDistinct) and sameDistinctTypes(f, a): result = isEqual
elif c.coerceDistincts: result = typeRel(c, f.base, a)
of tySet: of tySet:
if a.kind == tySet: if a.kind == tySet:
if (f.sons[0].kind != tyGenericParam) and (a.sons[0].kind == tyEmpty): 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: of tyUserTypeClass, tyUserTypeClassInst:
considerPreviousT: considerPreviousT:
result = matchUserTypeClass(c.c, c, f, a) result = matchUserTypeClass(c.c, c, f, aOrig)
if result == isGeneric: if result == isGeneric:
put(c.bindings, f, a) put(c.bindings, f, a)
@ -863,7 +884,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
of tyGenericParam: of tyGenericParam:
var x = PType(idTableGet(c.bindings, f)) var x = PType(idTableGet(c.bindings, f))
if x == nil: 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: f.kind == tyGenericParam and not c.typedescMatched:
# XXX: The fact that generic types currently use tyGenericParam for # XXX: The fact that generic types currently use tyGenericParam for
# their parameters is really a misnomer. tyGenericParam means "match # 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 # XXX: When implicit statics are the default
# this will be done earlier - we just have to # this will be done earlier - we just have to
# make sure that static types enter here # make sure that static types enter here
# XXX: weaken tyGenericParam and call it tyGenericPlaceholder # XXX: weaken tyGenericParam and call it tyGenericPlaceholder
# and finally start using tyTypedesc for generic types properly. # and finally start using tyTypedesc for generic types properly.
if argType.kind == tyGenericParam and tfWildcard in argType.flags: if argType.kind == tyGenericParam and tfWildcard in argType.flags:
@ -1060,7 +1081,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
return argSemantized return argSemantized
if argType.kind == tyStatic: if argType.kind == tyStatic:
if m.calleeSym.kind == skType: if m.callee.kind == tyGenericBody:
result = newNodeI(nkType, argOrig.info) result = newNodeI(nkType, argOrig.info)
result.typ = makeTypeFromExpr(c, arg) result.typ = makeTypeFromExpr(c, arg)
return return

View file

@ -436,6 +436,7 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
of tyStatic: of tyStatic:
internalAssert t.len > 0 internalAssert t.len > 0
result = "static[" & typeToString(t.sons[0]) & "]" result = "static[" & typeToString(t.sons[0]) & "]"
if t.n != nil: result.add "(" & renderTree(t.n) & ")"
of tyUserTypeClass: of tyUserTypeClass:
internalAssert t.sym != nil and t.sym.owner != nil internalAssert t.sym != nil and t.sym.owner != nil
return t.sym.owner.name.s return t.sym.owner.name.s

View file

@ -1,5 +1,5 @@
discard """ discard """
line: 11 line: 10
errormsg: "type mismatch: got (typedesc[float], string)" errormsg: "type mismatch: got (typedesc[float], string)"
""" """

View file

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

View file

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