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:
parent
a66d059acc
commit
4b7655fd10
12 changed files with 225 additions and 58 deletions
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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 =
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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())
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
@ -950,8 +949,12 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
|
||||||
[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:
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
discard """
|
discard """
|
||||||
line: 11
|
line: 10
|
||||||
errormsg: "type mismatch: got (typedesc[float], string)"
|
errormsg: "type mismatch: got (typedesc[float], string)"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
|
||||||
79
tests/metatype/swizzle.nim
Normal file
79
tests/metatype/swizzle.nim
Normal 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
|
||||||
|
|
||||||
10
tests/metatype/typeclassinference.nim
Normal file
10
tests/metatype/typeclassinference.nim
Normal 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]"
|
||||||
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue