Skip to content

Commit 894a19c

Browse files
authored
fix calls in generic bodies, delay typecheck when no overloads match (#22029)
* sacrifice "tgenericshardcases" for working statics * legacy switch for CI, maybe experimental later * convert to experimental * apparently untyped needs the experimental switch * try special case call semcheck * try fix * fix compilation * final cleanup, not experimental, make `when` work * remove last needed use of untyped * fix unused warning in test * remove untyped feature
1 parent 606e994 commit 894a19c

File tree

11 files changed

+239
-68
lines changed

11 files changed

+239
-68
lines changed

compiler/semcall.nim

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -627,7 +627,10 @@ proc semOverloadedCall(c: PContext, n, nOrig: PNode,
627627
candidates)
628628
result = semResolvedCall(c, r, n, flags)
629629
else:
630-
if efExplain notin flags:
630+
if efDetermineType in flags and c.inGenericContext > 0 and c.matchedConcept == nil:
631+
result = n
632+
result.typ = makeTypeFromExpr(c, result.copyTree)
633+
elif efExplain notin flags:
631634
# repeat the overload resolution,
632635
# this time enabling all the diagnostic output (this should fail again)
633636
result = semOverloadedCall(c, n, nOrig, filter, flags + {efExplain})

compiler/semexprs.nim

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -971,7 +971,8 @@ proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode,
971971

972972
if result != nil:
973973
if result[0].kind != nkSym:
974-
internalError(c.config, "semOverloadedCallAnalyseEffects")
974+
if not (efDetermineType in flags and c.inGenericContext > 0):
975+
internalError(c.config, "semOverloadedCallAnalyseEffects")
975976
return
976977
let callee = result[0].sym
977978
case callee.kind
@@ -1007,6 +1008,8 @@ proc setGenericParams(c: PContext, n: PNode) =
10071008
proc afterCallActions(c: PContext; n, orig: PNode, flags: TExprFlags; expectedType: PType = nil): PNode =
10081009
if efNoSemCheck notin flags and n.typ != nil and n.typ.kind == tyError:
10091010
return errorNode(c, n)
1011+
if n.typ != nil and n.typ.kind == tyFromExpr and c.inGenericContext > 0:
1012+
return n
10101013

10111014
result = n
10121015

compiler/seminst.nim

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -388,8 +388,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
388388
pragma(c, result, n[pragmasPos], allRoutinePragmas)
389389
if isNil(n[bodyPos]):
390390
n[bodyPos] = copyTree(getBody(c.graph, fn))
391-
if c.inGenericContext == 0:
392-
instantiateBody(c, n, fn.typ.n, result, fn)
391+
instantiateBody(c, n, fn.typ.n, result, fn)
393392
sideEffectsCheck(c, result)
394393
if result.magic notin {mSlice, mTypeOf}:
395394
# 'toOpenArray' is special and it is allowed to return 'openArray':

compiler/semtypes.nim

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,8 @@ proc semRangeAux(c: PContext, n: PNode, prev: PType): PType =
269269
localError(c.config, n.info, "range is empty")
270270

271271
var range: array[2, PNode]
272+
# XXX this is still a hard compilation in a generic context, this can
273+
# result in unresolved generic parameters being treated like real types
272274
range[0] = semExprWithType(c, n[1], {efDetermineType})
273275
range[1] = semExprWithType(c, n[2], {efDetermineType})
274276

@@ -277,7 +279,7 @@ proc semRangeAux(c: PContext, n: PNode, prev: PType): PType =
277279
rangeT[i] = range[i].typ.skipTypes({tyStatic}).skipIntLit(c.idgen)
278280

279281
let hasUnknownTypes = c.inGenericContext > 0 and
280-
rangeT[0].kind == tyFromExpr or rangeT[1].kind == tyFromExpr
282+
(rangeT[0].kind == tyFromExpr or rangeT[1].kind == tyFromExpr)
281283

282284
if not hasUnknownTypes:
283285
if not sameType(rangeT[0].skipTypes({tyRange}), rangeT[1].skipTypes({tyRange})):
@@ -337,6 +339,8 @@ proc semArrayIndex(c: PContext, n: PNode): PType =
337339
elif n.kind == nkInfix and n[0].kind == nkIdent and n[0].ident.s == "..<":
338340
result = errorType(c)
339341
else:
342+
# XXX this is still a hard compilation in a generic context, this can
343+
# result in unresolved generic parameters being treated like real types
340344
let e = semExprWithType(c, n, {efDetermineType})
341345
if e.typ.kind == tyFromExpr:
342346
result = makeRangeWithStaticExpr(c, e.typ.n)
@@ -357,7 +361,7 @@ proc semArrayIndex(c: PContext, n: PNode): PType =
357361
if not isOrdinalType(e.typ.skipTypes({tyStatic, tyAlias, tyGenericInst, tySink})):
358362
localError(c.config, n[1].info, errOrdinalTypeExpected % typeToString(e.typ, preferDesc))
359363
# This is an int returning call, depending on an
360-
# yet unknown generic param (see tgenericshardcases).
364+
# yet unknown generic param (see tuninstantiatedgenericcalls).
361365
# We are going to construct a range type that will be
362366
# properly filled-out in semtypinst (see how tyStaticExpr
363367
# is handled there).
@@ -380,7 +384,8 @@ proc semArray(c: PContext, n: PNode, prev: PType): PType =
380384
let indx = semArrayIndex(c, n[1])
381385
var indxB = indx
382386
if indxB.kind in {tyGenericInst, tyAlias, tySink}: indxB = lastSon(indxB)
383-
if indxB.kind notin {tyGenericParam, tyStatic, tyFromExpr}:
387+
if indxB.kind notin {tyGenericParam, tyStatic, tyFromExpr} and
388+
tfUnresolved notin indxB.flags:
384389
if indxB.skipTypes({tyRange}).kind in {tyUInt, tyUInt64}:
385390
discard
386391
elif not isOrdinalType(indxB):
@@ -737,7 +742,13 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int,
737742
if e.kind != nkIntLit: discard "don't report followup error"
738743
elif e.intVal != 0 and branch == nil: branch = it[1]
739744
else:
740-
it[0] = forceBool(c, semExprWithType(c, it[0]))
745+
# XXX this is still a hard compilation in a generic context, this can
746+
# result in unresolved generic parameters being treated like real types
747+
let e = semExprWithType(c, it[0], {efDetermineType})
748+
if e.typ.kind == tyFromExpr:
749+
it[0] = makeStaticExpr(c, e)
750+
else:
751+
it[0] = forceBool(c, e)
741752
of nkElse:
742753
checkSonsLen(it, 1, c.config)
743754
if branch == nil: branch = it[0]

compiler/semtypinst.nim

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -141,27 +141,28 @@ proc isTypeParam(n: PNode): bool =
141141
(n.sym.kind == skGenericParam or
142142
(n.sym.kind == skType and sfFromGeneric in n.sym.flags))
143143

144-
proc reResolveCallsWithTypedescParams(cl: var TReplTypeVars, n: PNode): PNode =
145-
# This is needed for tgenericshardcases
146-
# It's possible that a generic param will be used in a proc call to a
147-
# typedesc accepting proc. After generic param substitution, such procs
148-
# should be optionally instantiated with the correct type. In order to
149-
# perform this instantiation, we need to re-run the generateInstance path
150-
# in the compiler, but it's quite complicated to do so at the moment so we
151-
# resort to a mild hack; the head symbol of the call is temporary reset and
152-
# overload resolution is executed again (which may trigger generateInstance).
153-
if n.kind in nkCallKinds and sfFromGeneric in n[0].sym.flags:
154-
var needsFixing = false
155-
for i in 1..<n.safeLen:
156-
if isTypeParam(n[i]): needsFixing = true
157-
if needsFixing:
158-
n[0] = newSymNode(n[0].sym.owner)
159-
return cl.c.semOverloadedCall(cl.c, n, n, {skProc, skFunc}, {})
160-
161-
for i in 0..<n.safeLen:
162-
n[i] = reResolveCallsWithTypedescParams(cl, n[i])
163-
164-
return n
144+
when false: # old workaround
145+
proc reResolveCallsWithTypedescParams(cl: var TReplTypeVars, n: PNode): PNode =
146+
# This is needed for tuninstantiatedgenericcalls
147+
# It's possible that a generic param will be used in a proc call to a
148+
# typedesc accepting proc. After generic param substitution, such procs
149+
# should be optionally instantiated with the correct type. In order to
150+
# perform this instantiation, we need to re-run the generateInstance path
151+
# in the compiler, but it's quite complicated to do so at the moment so we
152+
# resort to a mild hack; the head symbol of the call is temporary reset and
153+
# overload resolution is executed again (which may trigger generateInstance).
154+
if n.kind in nkCallKinds and sfFromGeneric in n[0].sym.flags:
155+
var needsFixing = false
156+
for i in 1..<n.safeLen:
157+
if isTypeParam(n[i]): needsFixing = true
158+
if needsFixing:
159+
n[0] = newSymNode(n[0].sym.owner)
160+
return cl.c.semOverloadedCall(cl.c, n, n, {skProc, skFunc}, {})
161+
162+
for i in 0..<n.safeLen:
163+
n[i] = reResolveCallsWithTypedescParams(cl, n[i])
164+
165+
return n
165166

166167
proc replaceObjBranches(cl: TReplTypeVars, n: PNode): PNode =
167168
result = n
@@ -250,7 +251,8 @@ proc replaceTypeVarsN(cl: var TReplTypeVars, n: PNode; start=0): PNode =
250251
result = newNodeI(nkRecList, n.info)
251252
of nkStaticExpr:
252253
var n = prepareNode(cl, n)
253-
n = reResolveCallsWithTypedescParams(cl, n)
254+
when false:
255+
n = reResolveCallsWithTypedescParams(cl, n)
254256
result = if cl.allowMetaTypes: n
255257
else: cl.c.semExpr(cl.c, n)
256258
if not cl.allowMetaTypes:

compiler/sigmatch.nim

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1837,7 +1837,13 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
18371837
# proc foo(T: typedesc, x: T)
18381838
# when `f` is an unresolved typedesc, `a` could be any
18391839
# type, so we should not perform this check earlier
1840-
if a.kind != tyTypeDesc:
1840+
if c.c.inGenericContext > 0 and
1841+
a.skipTypes({tyTypeDesc}).kind == tyGenericParam:
1842+
# generic type bodies can sometimes compile call expressions
1843+
# prevent unresolved generic parameters from being passed to procs as
1844+
# typedesc parameters
1845+
result = isNone
1846+
elif a.kind != tyTypeDesc:
18411847
if a.kind == tyGenericParam and tfWildcard in a.flags:
18421848
# TODO: prevent `a` from matching as a wildcard again
18431849
result = isGeneric

compiler/types.nim

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,8 @@ proc iterOverNode(marker: var IntSet, n: PNode, iter: TTypeIter,
209209
# a leaf
210210
result = iterOverTypeAux(marker, n.typ, iter, closure)
211211
else:
212+
result = iterOverTypeAux(marker, n.typ, iter, closure)
213+
if result: return
212214
for i in 0..<n.len:
213215
result = iterOverNode(marker, n[i], iter, closure)
214216
if result: return
@@ -480,6 +482,7 @@ proc valueToString(a: PNode): string =
480482
result = $a.intVal
481483
of nkFloatLit..nkFloat128Lit: result = $a.floatVal
482484
of nkStrLit..nkTripleStrLit: result = a.strVal
485+
of nkStaticExpr: result = "static(" & a[0].renderTree & ")"
483486
else: result = "<invalid value>"
484487

485488
proc rangeToStr(n: PNode): string =
@@ -1513,7 +1516,7 @@ proc compatibleEffects*(formal, actual: PType): EffectsCompat =
15131516

15141517

15151518
proc isCompileTimeOnly*(t: PType): bool {.inline.} =
1516-
result = t.kind in {tyTypeDesc, tyStatic}
1519+
result = t.kind in {tyTypeDesc, tyStatic, tyGenericParam}
15171520

15181521
proc containsCompileTimeOnly*(t: PType): bool =
15191522
if isCompileTimeOnly(t): return true

tests/generics/tgenerics_various.nim

Lines changed: 0 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -127,42 +127,6 @@ block trefs:
127127

128128

129129

130-
block tsharedcases:
131-
proc typeNameLen(x: typedesc): int {.compileTime.} =
132-
result = x.name.len
133-
macro selectType(a, b: typedesc): typedesc =
134-
result = a
135-
136-
type
137-
Foo[T] = object
138-
data1: array[T.high, int]
139-
data2: array[typeNameLen(T), float]
140-
data3: array[0..T.typeNameLen, selectType(float, int)]
141-
MyEnum = enum A, B, C, D
142-
143-
var f1: Foo[MyEnum]
144-
var f2: Foo[int8]
145-
146-
doAssert high(f1.data1) == 2 # (D = 3) - 1 == 2
147-
doAssert high(f1.data2) == 5 # (MyEnum.len = 6) - 1 == 5
148-
149-
doAssert high(f2.data1) == 126 # 127 - 1 == 126
150-
doAssert high(f2.data2) == 3 # int8.len - 1 == 3
151-
152-
static:
153-
doAssert high(f1.data1) == ord(C)
154-
doAssert high(f1.data2) == 5 # length of MyEnum minus one, because we used T.high
155-
156-
doAssert high(f2.data1) == 126
157-
doAssert high(f2.data2) == 3
158-
159-
doAssert high(f1.data3) == 6 # length of MyEnum
160-
doAssert high(f2.data3) == 4 # length of int8
161-
162-
doAssert f2.data3[0] is float
163-
164-
165-
166130
block tmap_auto:
167131
let x = map(@[1, 2, 3], x => x+10)
168132
doAssert x == @[11, 12, 13]
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# Cases that used to only work due to weird workarounds in the compiler
2+
# involving not instantiating calls in generic bodies which are removed
3+
# due to breaking statics.
4+
# The issue was that these calls are compiled as regular expressions at
5+
# the generic declaration with unresolved generic parameter types,
6+
# which are special cased in some places in the compiler, but sometimes
7+
# treated like real types.
8+
9+
block:
10+
type Base10 = object
11+
12+
func maxLen(T: typedesc[Base10], I: type): int8 =
13+
when I is uint8:
14+
3
15+
elif I is uint16:
16+
5
17+
elif I is uint32:
18+
10
19+
elif I is uint64:
20+
20
21+
else:
22+
when sizeof(uint) == 4:
23+
10
24+
else:
25+
20
26+
27+
type
28+
Base10Buf[T: SomeUnsignedInt] = object
29+
data: array[maxLen(Base10, T), byte]
30+
len: int8
31+
32+
var x: Base10Buf[uint32]
33+
doAssert x.data.len == 10
34+
var y: Base10Buf[uint16]
35+
doAssert y.data.len == 5
36+
37+
import typetraits
38+
39+
block thardcases:
40+
proc typeNameLen(x: typedesc): int {.compileTime.} =
41+
result = x.name.len
42+
macro selectType(a, b: typedesc): typedesc =
43+
result = a
44+
45+
type
46+
Foo[T] = object
47+
data1: array[T.high, int]
48+
data2: array[typeNameLen(T), float]
49+
data3: array[0..T.typeNameLen, selectType(float, int)]
50+
51+
type MyEnum = enum A, B, C, D
52+
53+
var f1: Foo[MyEnum]
54+
var f2: Foo[int8]
55+
56+
doAssert high(f1.data1) == 2 # (D = 3) - 1 == 2
57+
doAssert high(f1.data2) == 5 # (MyEnum.len = 6) - 1 == 5
58+
59+
doAssert high(f2.data1) == 126 # 127 - 1 == 126
60+
doAssert high(f2.data2) == 3 # int8.len - 1 == 3
61+
62+
static:
63+
doAssert high(f1.data1) == ord(C)
64+
doAssert high(f1.data2) == 5 # length of MyEnum minus one, because we used T.high
65+
66+
doAssert high(f2.data1) == 126
67+
doAssert high(f2.data2) == 3
68+
69+
doAssert high(f1.data3) == 6 # length of MyEnum
70+
doAssert high(f2.data3) == 4 # length of int8
71+
72+
doAssert f2.data3[0] is float

0 commit comments

Comments
 (0)