fix #1789 (binding to static params during generic proc sigmatch)

This commit is contained in:
Zahary Karadjov
2014-12-30 11:27:54 +02:00
parent bf8f6a3000
commit da36a847a7
6 changed files with 116 additions and 46 deletions

View File

@@ -219,7 +219,7 @@ proc maybeLiftType(t: var PType, c: PContext, info: TLineInfo) =
# 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)
t, ":anon", info)
closeScope(c)
if lifted != nil: t = lifted

View File

@@ -60,7 +60,13 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
else:
result = symChoice(c, n, s, scOpen)
of skGenericParam:
result = newSymNodeTypeDesc(s, n.info)
if s.typ != nil and s.typ.kind == tyStatic:
if s.typ.n != nil:
result = s.typ.n
else:
result = n
else:
result = newSymNodeTypeDesc(s, n.info)
styleCheckUse(n.info, s)
of skParam:
result = n

View File

@@ -21,7 +21,8 @@ proc instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable,
var q = a.sym
if q.typ.kind notin {tyTypeDesc, tyGenericParam, tyStatic, tyIter}+tyTypeClasses:
continue
var s = newSym(skType, q.name, getCurrOwner(), q.info)
let symKind = if q.typ.kind == tyStatic: skConst else: skType
var s = newSym(symKind, q.name, getCurrOwner(), q.info)
s.flags = s.flags + {sfUsed, sfFromGeneric}
var t = PType(idTableGet(pt, q.typ))
if t == nil:
@@ -40,6 +41,7 @@ proc instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable,
t = generateTypeInstance(c, pt, a, t)
#t = ReplaceTypeVarsT(cl, t)
s.typ = t
if t.kind == tyStatic: s.ast = t.n
addDecl(c, s)
entry.concreteTypes[i] = t

View File

@@ -214,44 +214,48 @@ proc semRange(c: PContext, n: PNode, prev: PType): PType =
localError(n.info, errXExpectsOneTypeParam, "range")
result = newOrPrevType(tyError, prev, c)
proc semArray(c: PContext, n: PNode, prev: PType): PType =
var indx, base: PType
result = newOrPrevType(tyArray, prev, c)
if sonsLen(n) == 3:
# 3 = length(array indx base)
if isRange(n[1]): indx = semRangeAux(c, n[1], nil)
proc semArrayIndex(c: PContext, n: PNode): PType =
if isRange(n): result = semRangeAux(c, n, nil)
else:
let e = semExprWithType(c, n, {efDetermineType})
if e.typ.kind == tyFromExpr:
result = makeRangeWithStaticExpr(c, e.typ.n)
elif e.kind in {nkIntLit..nkUInt64Lit}:
result = makeRangeType(c, 0, e.intVal-1, n.info, e.typ)
elif e.kind == nkSym and e.typ.kind == tyStatic:
if e.sym.ast != nil:
return semArrayIndex(c, e.sym.ast)
internalAssert c.inGenericContext > 0
if not isOrdinalType(e.typ.lastSon):
localError(n[1].info, errOrdinalTypeExpected)
result = makeRangeWithStaticExpr(c, e)
result.flags.incl tfUnresolved
elif e.kind in nkCallKinds and hasGenericArguments(e):
if not isOrdinalType(e.typ):
localError(n[1].info, errOrdinalTypeExpected)
# This is an int returning call, depending on an
# yet unknown generic param (see tgenericshardcases).
# We are going to construct a range type that will be
# properly filled-out in semtypinst (see how tyStaticExpr
# is handled there).
result = makeRangeWithStaticExpr(c, e)
elif e.kind == nkIdent:
result = e.typ.skipTypes({tyTypeDesc})
else:
let e = semExprWithType(c, n.sons[1], {efDetermineType})
if e.typ.kind == tyFromExpr:
indx = makeRangeWithStaticExpr(c, e.typ.n)
elif e.kind in {nkIntLit..nkUInt64Lit}:
indx = makeRangeType(c, 0, e.intVal-1, n.info, e.typ)
elif e.kind == nkSym and e.typ.kind == tyStatic:
if e.sym.ast != nil: return semArray(c, e.sym.ast, nil)
internalAssert c.inGenericContext > 0
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)
# This is an int returning call, depending on an
# yet unknown generic param (see tgenericshardcases).
# We are going to construct a range type that will be
# properly filled-out in semtypinst (see how tyStaticExpr
# is handled there).
indx = makeRangeWithStaticExpr(c, e)
elif e.kind == nkIdent:
indx = e.typ.skipTypes({tyTypeDesc})
let x = semConstExpr(c, e)
if x.kind in {nkIntLit..nkUInt64Lit}:
result = makeRangeType(c, 0, x.intVal-1, n.info,
x.typ.skipTypes({tyTypeDesc}))
else:
let x = semConstExpr(c, e)
if x.kind in {nkIntLit..nkUInt64Lit}:
indx = makeRangeType(c, 0, x.intVal-1, n.info,
x.typ.skipTypes({tyTypeDesc}))
else:
indx = x.typ.skipTypes({tyTypeDesc})
#localError(n[1].info, errConstExprExpected)
result = x.typ.skipTypes({tyTypeDesc})
#localError(n[1].info, errConstExprExpected)
proc semArray(c: PContext, n: PNode, prev: PType): PType =
var base: PType
result = newOrPrevType(tyArray, prev, c)
if sonsLen(n) == 3:
# 3 = length(array indx base)
var indx = semArrayIndex(c, n[1])
addSonSkipIntLit(result, indx)
if indx.kind == tyGenericInst: indx = lastSon(indx)
if indx.kind notin {tyGenericParam, tyStatic, tyFromExpr}:

View File

@@ -517,6 +517,16 @@ proc maybeSkipDistinct(t: PType, callee: PSym): PType =
else:
result = t
proc tryResolvingStaticExpr(c: var TCandidate, n: PNode): PNode =
# Consider this example:
# type Value[N: static[int]] = object
# proc foo[N](a: Value[N], r: range[0..(N-1)])
# Here, N-1 will be initially nkStaticExpr that can be evaluated only after
# N is bound to a concrete value during the matching of the first param.
# This proc is used to evaluate such static expressions.
let instantiated = replaceTypesInBody(c.c, c.bindings, n)
result = c.c.semExpr(c.c, instantiated)
proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
# typeRel can be used to establish various relationships between types:
#
@@ -620,6 +630,11 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
# bugfix: accept integer conversions here
#if result < isGeneric: result = isNone
if result notin {isNone, isGeneric}:
# resolve any late-bound static expressions
# that may appear in the range:
for i in 0..1:
if f.n[i].kind == nkStaticExpr:
f.n.sons[i] = tryResolvingStaticExpr(c, f.n[i])
result = typeRangeRel(f, a)
else:
if skipTypes(f, {tyRange}).kind == a.kind:
@@ -1006,15 +1021,14 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
of tyFromExpr:
# fix the expression, so it contains the already instantiated types
let instantiated = replaceTypesInBody(c.c, c.bindings, f.n)
let reevaluted = c.c.semExpr(c.c, instantiated)
case reevaluted.typ.kind
let reevaluated = tryResolvingStaticExpr(c, f.n)
case reevaluated.typ.kind
of tyTypeDesc:
result = typeRel(c, a, reevaluted.typ.base)
result = typeRel(c, a, reevaluated.typ.base)
of tyStatic:
result = typeRel(c, a, reevaluted.typ.base)
if result != isNone and reevaluted.typ.n != nil:
if not exprStructuralEquivalent(aOrig.n, reevaluted.typ.n):
result = typeRel(c, a, reevaluated.typ.base)
if result != isNone and reevaluated.typ.n != nil:
if not exprStructuralEquivalent(aOrig.n, reevaluated.typ.n):
result = isNone
else:
localError(f.n.info, errTypeExpected)

44
tests/generics/t1789.nim Normal file
View File

@@ -0,0 +1,44 @@
discard """
output: "3\n0"
"""
# https://github.com/Araq/Nim/issues/1789
type
Foo[N: static[int]] = object
proc bindStaticN[N](foo: Foo[N]) =
var ar0: array[3, int]
var ar1: array[N, int]
var ar2: array[1..N, int]
var ar3: array[0..(N+10), float]
echo N
var f: Foo[3]
f.bindStaticN
# case 2
type
ObjectWithStatic[X, Y: static[int], T] = object
bar: array[X * Y, T] # this one works
AliasWithStatic[X, Y: static[int], T] = array[X * Y, T]
var
x: ObjectWithStatic[1, 2, int]
y: AliasWithStatic[2, 3, int]
# case 3
type
Bar[N: static[int], T] = object
bar: array[N, T]
proc `[]`*[N, T](f: Bar[N, T], n: range[0..(N - 1)]): T =
assert high(n) == N-1
result = f.bar[n]
var b: Bar[3, int]
echo b[2]