support for inferring static[T] concept params
This commit is contained in:
parent
8cd5f1f8f5
commit
76c663f692
8 changed files with 144 additions and 28 deletions
|
|
@ -490,6 +490,7 @@ type
|
|||
tfHasStatic
|
||||
tfGenericTypeParam
|
||||
tfImplicitTypeParam
|
||||
tfInferrableStatic
|
||||
tfWildcard # consider a proc like foo[T, I](x: Type[T, I])
|
||||
# T and I here can bind to both typedesc and static types
|
||||
# before this is determined, we'll consider them to be a
|
||||
|
|
@ -1073,6 +1074,9 @@ proc isMetaType*(t: PType): bool =
|
|||
(t.kind == tyStatic and t.n == nil) or
|
||||
tfHasMeta in t.flags
|
||||
|
||||
proc isUnresolvedStatic*(t: PType): bool =
|
||||
return t.kind == tyStatic and t.n == nil
|
||||
|
||||
proc linkTo*(t: PType, s: PSym): PType {.discardable.} =
|
||||
t.sym = s
|
||||
s.typ = t
|
||||
|
|
|
|||
|
|
@ -164,6 +164,9 @@ proc mapType(typ: PType): TCTypeKind =
|
|||
of tySet: result = mapSetType(typ)
|
||||
of tyOpenArray, tyArray, tyVarargs: result = ctArray
|
||||
of tyObject, tyTuple: result = ctStruct
|
||||
of tyUserTypeClass, tyUserTypeClassInst:
|
||||
internalAssert typ.isResolvedUserTypeClass
|
||||
return mapType(typ.lastSon)
|
||||
of tyGenericBody, tyGenericInst, tyGenericParam, tyDistinct, tyOrdinal,
|
||||
tyTypeDesc, tyAlias, tyInferred:
|
||||
result = mapType(lastSon(typ))
|
||||
|
|
|
|||
|
|
@ -273,7 +273,8 @@ proc newTypeWithSons*(c: PContext, kind: TTypeKind,
|
|||
proc makeStaticExpr*(c: PContext, n: PNode): PNode =
|
||||
result = newNodeI(nkStaticExpr, n.info)
|
||||
result.sons = @[n]
|
||||
result.typ = newTypeWithSons(c, tyStatic, @[n.typ])
|
||||
result.typ = if n.typ != nil and n.typ.kind == tyStatic: n.typ
|
||||
else: newTypeWithSons(c, tyStatic, @[n.typ])
|
||||
|
||||
proc makeAndType*(c: PContext, t1, t2: PType): PType =
|
||||
result = newTypeS(tyAnd, c)
|
||||
|
|
@ -317,16 +318,23 @@ proc makeRangeWithStaticExpr*(c: PContext, n: PNode): PType =
|
|||
let intType = getSysType(tyInt)
|
||||
result = newTypeS(tyRange, c)
|
||||
result.sons = @[intType]
|
||||
if n.typ.n == nil: result.flags.incl tfUnresolved
|
||||
result.n = newNode(nkRange, n.info, @[
|
||||
newIntTypeNode(nkIntLit, 0, intType),
|
||||
makeStaticExpr(c, n.nMinusOne)])
|
||||
|
||||
template rangeHasStaticIf*(t: PType): bool =
|
||||
template rangeHasUnresolvedStatic*(t: PType): bool =
|
||||
# this accepts the ranges's node
|
||||
t.n != nil and t.n.len > 1 and t.n[1].kind == nkStaticExpr
|
||||
|
||||
template getStaticTypeFromRange*(t: PType): PType =
|
||||
t.n[1][0][1].typ
|
||||
proc findUnresolvedStaticInRange*(t: PType): (PType, int) =
|
||||
assert t.kind == tyRange
|
||||
# XXX: This really needs to become more sophisticated
|
||||
let upperBound = t.n[1]
|
||||
if upperBound[0].kind == nkCall:
|
||||
return (upperBound[0][1].typ, 1)
|
||||
else:
|
||||
return (upperBound.typ, 0)
|
||||
|
||||
proc errorType*(c: PContext): PType =
|
||||
## creates a type representing an error state
|
||||
|
|
|
|||
|
|
@ -1553,6 +1553,11 @@ proc usesResult(n: PNode): bool =
|
|||
for c in n:
|
||||
if usesResult(c): return true
|
||||
|
||||
proc inferConceptStaticParam(c: PContext, typ: PType, n: PNode) =
|
||||
let res = semConstExpr(c, n)
|
||||
if not sameType(res.typ, typ.base): localError(n.info, "")
|
||||
typ.n = res
|
||||
|
||||
proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||
# these must be last statements in a block:
|
||||
const
|
||||
|
|
@ -1604,10 +1609,19 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
|||
n.typ = n.sons[i].typ
|
||||
return
|
||||
else:
|
||||
n.sons[i] = semExpr(c, n.sons[i])
|
||||
if c.inTypeClass > 0 and n[i].typ != nil:
|
||||
case n[i].typ.kind
|
||||
var expr = semExpr(c, n.sons[i])
|
||||
n.sons[i] = expr
|
||||
if c.inTypeClass > 0 and expr.typ != nil:
|
||||
case expr.typ.kind
|
||||
of tyBool:
|
||||
if expr.kind == nkInfix and expr[0].sym.name.s == "==":
|
||||
if expr[1].typ.isUnresolvedStatic:
|
||||
inferConceptStaticParam(c, expr[1].typ, expr[2])
|
||||
continue
|
||||
elif expr[2].typ.isUnresolvedStatic:
|
||||
inferConceptStaticParam(c, expr[2].typ, expr[1])
|
||||
continue
|
||||
|
||||
let verdict = semConstExpr(c, n[i])
|
||||
if verdict.intVal == 0:
|
||||
localError(result.info, "type class predicate failed")
|
||||
|
|
|
|||
|
|
@ -122,6 +122,7 @@ proc isTypeParam(n: PNode): bool =
|
|||
proc hasGenericArguments*(n: PNode): bool =
|
||||
if n.kind == nkSym:
|
||||
return n.sym.kind == skGenericParam or
|
||||
tfInferrableStatic in n.sym.typ.flags or
|
||||
(n.sym.kind == skType and
|
||||
n.sym.typ.flags * {tfGenericTypeParam, tfImplicitTypeParam} != {})
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -605,7 +605,10 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate,
|
|||
of tyStatic:
|
||||
param = paramSym skConst
|
||||
param.typ = typ.exactReplica
|
||||
param.ast = typ.n
|
||||
if typ.n == nil:
|
||||
param.typ.flags.incl tfInferrableStatic
|
||||
else:
|
||||
param.ast = typ.n
|
||||
of tyUnknown:
|
||||
param = paramSym skVar
|
||||
param.typ = typ.exactReplica
|
||||
|
|
@ -619,8 +622,6 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate,
|
|||
addDecl(c, param)
|
||||
typeParams.safeAdd((param, typ))
|
||||
|
||||
#echo "A ", param.name.s, " ", typeToString(param.typ), " ", param.kind
|
||||
|
||||
for param in body.n[0]:
|
||||
var
|
||||
dummyName: PNode
|
||||
|
|
@ -647,8 +648,6 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate,
|
|||
dummyParam.typ = dummyType
|
||||
addDecl(c, dummyParam)
|
||||
|
||||
#echo "B ", dummyName.ident.s, " ", typeToString(dummyType), " ", dummyparam.kind
|
||||
|
||||
var checkedBody = c.semTryExpr(c, body.n[3].copyTree)
|
||||
if checkedBody == nil: return nil
|
||||
|
||||
|
|
@ -733,9 +732,6 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
|
|||
aOrig
|
||||
|
||||
if aOrig.kind == tyInferred:
|
||||
# echo "INFER A"
|
||||
# debug f
|
||||
# debug aOrig
|
||||
let prev = aOrig.previouslyInferred
|
||||
if prev != nil:
|
||||
return typeRel(c, f, prev)
|
||||
|
|
@ -886,6 +882,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
|
|||
case a.kind
|
||||
of tyArray:
|
||||
var fRange = f.sons[0]
|
||||
var aRange = a.sons[0]
|
||||
if fRange.kind == tyGenericParam:
|
||||
var prev = PType(idTableGet(c.bindings, fRange))
|
||||
if prev == nil:
|
||||
|
|
@ -893,21 +890,28 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
|
|||
fRange = a
|
||||
else:
|
||||
fRange = prev
|
||||
result = typeRel(c, f.sons[1], a.sons[1])
|
||||
result = typeRel(c, f.sons[1].skipTypes({tyTypeDesc}),
|
||||
a.sons[1].skipTypes({tyTypeDesc}))
|
||||
if result < isGeneric: return isNone
|
||||
if rangeHasStaticIf(fRange):
|
||||
|
||||
proc inferStaticRange(c: var TCandidate, inferred, concrete: PType) =
|
||||
var (staticT, offset) = inferred.findUnresolvedStaticInRange
|
||||
var
|
||||
replacementT = newTypeWithSons(c.c, tyStatic, @[tyInt.getSysType])
|
||||
concreteUpperBound = concrete.n[1].intVal
|
||||
# we must correct for the off-by-one discrepancy between
|
||||
# ranges and static params:
|
||||
replacementT.n = newIntNode(nkIntLit, concreteUpperBound + offset)
|
||||
if tfInferrableStatic in staticT.flags:
|
||||
staticT.n = replacementT.n
|
||||
put(c.bindings, staticT, replacementT)
|
||||
|
||||
if rangeHasUnresolvedStatic(fRange):
|
||||
if tfUnresolved in fRange.flags:
|
||||
# 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, rangeStaticT, replacementT)
|
||||
inferStaticRange(c, fRange, aRange)
|
||||
return isGeneric
|
||||
|
||||
let len = tryResolvingStaticExpr(c, fRange.n[1])
|
||||
|
|
@ -915,6 +919,9 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
|
|||
return # if we get this far, the result is already good
|
||||
else:
|
||||
return isNone
|
||||
elif c.c.inTypeClass > 0 and aRange.rangeHasUnresolvedStatic:
|
||||
inferStaticRange(c, aRange, fRange)
|
||||
return isGeneric
|
||||
elif lengthOrd(fRange) != lengthOrd(a):
|
||||
result = isNone
|
||||
else: discard
|
||||
|
|
@ -1307,9 +1314,6 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
|
|||
result = isNone
|
||||
|
||||
of tyInferred:
|
||||
# echo "INFER F"
|
||||
# debug f
|
||||
# debug a
|
||||
let prev = f.previouslyInferred
|
||||
if prev != nil:
|
||||
result = typeRel(c, prev, a)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue