Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sem generic proc param types like generic types + static instantiation fixes #24005

Merged
merged 15 commits into from
Aug 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 14 additions & 6 deletions compiler/semexprs.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1482,20 +1482,28 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode =

proc tryReadingGenericParam(c: PContext, n: PNode, i: PIdent, t: PType): PNode =
case t.kind
of tyTypeParamsHolders:
of tyGenericInst:
result = readTypeParameter(c, t, i, n.info)
if result == c.graph.emptyNode:
result = semGenericStmt(c, n)
result.typ = makeTypeFromExpr(c, result.copyTree)
if c.inGenericContext > 0:
result = semGenericStmt(c, n)
result.typ = makeTypeFromExpr(c, result.copyTree)
else:
result = nil
of tyUserTypeClasses:
if t.isResolvedUserTypeClass:
result = readTypeParameter(c, t, i, n.info)
elif c.inGenericContext > 0:
result = semGenericStmt(c, n)
result.typ = makeTypeFromExpr(c, copyTree(result))
else:
result = nil
elif t.containsGenericType:
if c.inGenericContext > 0:
result = semGenericStmt(c, n)
result.typ = makeTypeFromExpr(c, copyTree(result))
of tyFromExpr, tyGenericParam, tyAnything:
result = semGenericStmt(c, n)
result.typ = makeTypeFromExpr(c, copyTree(result))
else:
result = nil
else:
result = nil

Expand Down
21 changes: 14 additions & 7 deletions compiler/semtypes.nim
Original file line number Diff line number Diff line change
Expand Up @@ -1312,6 +1312,9 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
result = newProcType(c, n.info, prev)
var check = initIntSet()
var counter = 0
template isCurrentlyGeneric: bool =
# genericParams might update as implicit generic params are added
genericParams != nil and genericParams.len > 0

for i in 1..<n.len:
var a = n[i]
Expand All @@ -1332,7 +1335,10 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
hasDefault = a[^1].kind != nkEmpty

if hasType:
let isGeneric = isCurrentlyGeneric()
inc c.inGenericContext, ord(isGeneric)
typ = semParamType(c, a[^2], constraint)
dec c.inGenericContext, ord(isGeneric)
# TODO: Disallow typed/untyped in procs in the compiler/stdlib
if kind in {skProc, skFunc} and (typ.kind == tyTyped or typ.kind == tyUntyped):
if not isMagic(getCurrOwner(c)):
Expand All @@ -1353,7 +1359,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
message(c.config, a.info, warnImplicitDefaultValue, msg)
block determineType:
var defTyp = typ
if genericParams != nil and genericParams.len > 0:
if isCurrentlyGeneric():
defTyp = nil
def = semGenericStmt(c, def)
if hasUnresolvedArgs(c, def):
Expand Down Expand Up @@ -1432,11 +1438,12 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
onDef(a[j].info, arg)
a[j] = newSymNode(arg)

var r: PType =
if n[0].kind != nkEmpty:
semTypeNode(c, n[0], nil)
else:
nil
var r: PType = nil
if n[0].kind != nkEmpty:
let isGeneric = isCurrentlyGeneric()
inc c.inGenericContext, ord(isGeneric)
r = semTypeNode(c, n[0], nil)
dec c.inGenericContext, ord(isGeneric)

if r != nil and kind in {skMacro, skTemplate} and r.kind == tyTyped:
# XXX: To implement the proposed change in the warning, just
Expand Down Expand Up @@ -1489,7 +1496,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
result.flags.excl tfHasMeta
result.n.typ = r

if genericParams != nil and genericParams.len > 0:
if isCurrentlyGeneric():
for n in genericParams:
if {sfUsed, sfAnon} * n.sym.flags == {}:
result.flags.incl tfUnresolved
Expand Down
25 changes: 24 additions & 1 deletion compiler/semtypinst.nim
Original file line number Diff line number Diff line change
Expand Up @@ -678,8 +678,31 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
elif t.elementType.kind != tyNone:
result = makeTypeDesc(cl.c, replaceTypeVarsT(cl, t.elementType))

of tyUserTypeClass, tyStatic:
of tyUserTypeClass:
result = t

of tyStatic:
if cl.c.matchedConcept != nil:
# allow concepts to not instantiate statics for now
# they can't always infer them
return
if not containsGenericType(t) and (t.n == nil or t.n.kind in nkLiterals):
# no need to instantiate
return
bailout()
result = instCopyType(cl, t)
cl.localCache[t.itemId] = result
for i in FirstGenericParamAt..<result.kidsLen:
var r = result[i]
if r != nil:
r = replaceTypeVarsT(cl, r)
result[i] = r
propagateToOwner(result, r)
result.n = replaceTypeVarsN(cl, result.n)
if not cl.allowMetaTypes and result.n != nil and
result.base.kind != tyNone:
result.n = cl.c.semConstExpr(cl.c, result.n)
result.n.typ = result.base

of tyGenericInst, tyUserTypeClassInst:
bailout()
Expand Down
46 changes: 31 additions & 15 deletions compiler/sigmatch.nim
Original file line number Diff line number Diff line change
Expand Up @@ -885,6 +885,7 @@ proc maybeSkipDistinct(m: TCandidate; t: PType, callee: PSym): PType =

proc tryResolvingStaticExpr(c: var TCandidate, n: PNode,
allowUnresolved = false,
allowCalls = false,
expectedType: PType = nil): PNode =
# Consider this example:
# type Value[N: static[int]] = object
Expand All @@ -894,7 +895,7 @@ proc tryResolvingStaticExpr(c: var TCandidate, n: PNode,
# This proc is used to evaluate such static expressions.
let instantiated = replaceTypesInBody(c.c, c.bindings, n, nil,
allowMetaTypes = allowUnresolved)
if instantiated.kind in nkCallKinds:
if not allowCalls and instantiated.kind in nkCallKinds:
return nil
result = c.c.semExpr(c.c, instantiated)

Expand Down Expand Up @@ -966,7 +967,8 @@ proc inferStaticParam*(c: var TCandidate, lhs: PNode, rhs: BiggestInt): bool =

else: discard

elif lhs.kind == nkSym and lhs.typ.kind == tyStatic and lhs.typ.n == nil:
elif lhs.kind == nkSym and lhs.typ.kind == tyStatic and
(lhs.typ.n == nil or idTableGet(c.bindings, lhs.typ) == nil):
var inferred = newTypeS(tyStatic, c.c, lhs.typ.elementType)
inferred.n = newIntNode(nkIntLit, rhs)
put(c, lhs.typ, inferred)
Expand Down Expand Up @@ -1877,7 +1879,11 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
elif f.base.kind notin {tyNone, tyGenericParam}:
result = typeRel(c, f.base, a, flags)
if result != isNone and f.n != nil:
if not exprStructuralEquivalent(f.n, aOrig.n):
var r = tryResolvingStaticExpr(c, f.n)
if r == nil: r = f.n
if not exprStructuralEquivalent(r, aOrig.n) and
not (aOrig.n.kind == nkIntLit and
inferStaticParam(c, r, aOrig.n.intVal)):
result = isNone
elif f.base.kind == tyGenericParam:
# Handling things like `type A[T; Y: static T] = object`
Expand Down Expand Up @@ -1963,23 +1969,29 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
of tyFromExpr:
# fix the expression, so it contains the already instantiated types
if f.n == nil or f.n.kind == nkEmpty: return isGeneric
let reevaluated = tryResolvingStaticExpr(c, f.n)
if reevaluated == nil:
if c.c.inGenericContext > 0:
# need to delay until instantiation
# also prevent infinite recursion below
return isNone
inc c.c.inGenericContext # to generate tyFromExpr again if unresolved
let reevaluated = tryResolvingStaticExpr(c, f.n, allowCalls = true).typ
dec c.c.inGenericContext
case reevaluated.kind
of tyFromExpr:
# not resolved
result = isNone
return
case reevaluated.typ.kind
of tyTypeDesc:
result = typeRel(c, a, reevaluated.typ.base, flags)
result = typeRel(c, a, reevaluated.base, flags)
of tyStatic:
result = typeRel(c, a, reevaluated.typ.base, flags)
if result != isNone and reevaluated.typ.n != nil:
if not exprStructuralEquivalent(aOrig.n, reevaluated.typ.n):
result = typeRel(c, a, reevaluated.base, flags)
if result != isNone and reevaluated.n != nil:
if not exprStructuralEquivalent(aOrig.n, reevaluated.n):
result = isNone
else:
# bug #14136: other types are just like 'tyStatic' here:
result = typeRel(c, a, reevaluated.typ, flags)
if result != isNone and reevaluated.typ.n != nil:
if not exprStructuralEquivalent(aOrig.n, reevaluated.typ.n):
result = typeRel(c, a, reevaluated, flags)
if result != isNone and reevaluated.n != nil:
if not exprStructuralEquivalent(aOrig.n, reevaluated.n):
result = isNone
of tyNone:
if a.kind == tyNone: result = isEqual
Expand Down Expand Up @@ -2188,7 +2200,11 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType,
a = typ
else:
if m.callee.kind == tyGenericBody:
if f.kind == tyStatic and typeRel(m, f.base, a) != isNone:
# we can't use `makeStaticExpr` if `arg` has a generic type
# because it generates `tyStatic`, which semtypinst doesn't touch
# not sure if checking for `tyFromExpr` is enough
if f.kind == tyStatic and typeRel(m, f.base, a) != isNone and
a.kind != tyFromExpr:
result = makeStaticExpr(m.c, arg)
result.typ.flags.incl tfUnresolved
result.typ.n = arg
Expand Down
3 changes: 2 additions & 1 deletion tests/generics/t23854.nim
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ template getBits[bits: static int](x: ptr UncheckedArray[BigInt[bits]]): int = b

proc main() =
let ctx = ECFFT_Descriptor[EC_ShortW_Aff[Fp[BLS12_381]]].new()
when false: echo getBits(ctx.rootsOfUnity2) # doesn't work yet?
doAssert getBits(ctx.rootsOfUnity1) == 255
doAssert getBits(ctx.rootsOfUnity2) == 255
doAssert ctx.rootsOfUnity1[0].limbs.len == wordsRequired(255)
doAssert ctx.rootsOfUnity2[0].limbs.len == wordsRequired(255)

Expand Down
2 changes: 1 addition & 1 deletion tests/generics/t23855.nim
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ template getBits[bits: static int](x: ptr UncheckedArray[BigInt[bits]]): int = b

proc main() =
let ctx = ECFFT_Descriptor[EC_ShortW_Aff[Fp[BLS12_381]]].new()
when false: echo getBits(ctx.rootsOfUnity) # doesn't work yet?
doAssert getBits(ctx.rootsOfUnity) == 255
doAssert ctx.rootsOfUnity[0].limbs.len == wordsRequired(255)

main()
62 changes: 62 additions & 0 deletions tests/generics/tuninstantiatedgenericcalls.nim
Original file line number Diff line number Diff line change
Expand Up @@ -239,3 +239,65 @@ block: # version of #23432 with `typed`, don't delay instantiation
proc f(x: X) = discard
var v: Future[void].Raising([ValueError])
f(v)

block: # issue #22647
proc c0(n: static int): int = 8
proc c1(n: static int): int = n div 2
proc c2(n: static int): int = n * 2
proc c3(n: static int, n2: int): int = n * n2
proc `**`(n: static int, n2: int): int = n * n2
proc c4(n: int, n2: int): int = n * n2

type
a[N: static int] = object
f0 : array[N, int]

b[N: static int] = object
f0 : a[c0(N)] # does not work
f1 : a[c1(N)] # does not work
f2 : a[c2(N)] # does not work
f3 : a[N * 2] # does not work
f4 : a[N] # works
f5: a[c3(N, 2)]
f6: a[N ** 2]
f7: a[2 * N]
f8: a[c4(N, 2)]

proc p[N: static int](x : a[N]) = discard x.f0[0]
template check(x, s: untyped) =
p(x)
doAssert x is a[s]
doAssert x.N == s
doAssert typeof(x).N == s
doAssert x.f0 == default(array[s, int])
doAssert x.f0.len == s
proc p2[N: static int](y : a[N]) {.gensym.} =
doAssert y is a[s]
doAssert y.N == s
doAssert typeof(y).N == s
doAssert y.f0 == default(array[s, int])
doAssert y.f0.len == s
p2(x)
proc p3(z: typeof(x)) {.gensym.} = discard
p3(default(a[s]))
proc p[N: static int](x : b[N]) =
x.f0.check(8)
x.f1.check(2)
x.f2.check(8)
x.f3.check(8)
x.f4.check(4)
x.f5.check(8)
x.f6.check(8)
x.f7.check(8)
x.f8.check(8)

var x: b[4]
x.p()

when false: # issue #22342, type section version of #22607
type GenAlias[isInt: static bool] = (
when isInt:
int
else:
float
)
Loading