Skip to content

Commit a93c5d7

Browse files
authored
adapt generic default parameters to recent generics changes (#24065)
fixes #16700, fixes #20916, refs #24010 Fixes the instantiation issues for proc param default values encountered in #24010 by: 1. semchecking generic default param values with `inGenericContext` for #22029 and followups to apply (the bigger change in semtypes), 2. rejecting explicit generic instantiations with unresolved generic types inside `inGenericContext` (sigmatch change), 3. instantiating the default param values using `prepareNode` rather than an insufficient manual method (the bigger change in seminst). This had an important side effect of references to other parameters not working since they would be resolved as a symbol belonging to the uninstantiated original generic proc rather than the later instantiated proc. There is a more radical way to fix this which is generating ident nodes with `tyFromExpr` in specifically this context, but instead we just count them as belonging to the same proc in `hoistParamsUsedInDefault`. Other minor bugfixes: * To make the error message in t20883 make sense, we now give a "cannot instantiate" error when trying to instantiate a proc generic param with `tyFromExpr`. * Object constructors as default param values generated default values of private fields going through `evalConstExpr` more than once, but the VM doesn't mark the object fields as `nfSkipFieldChecking` which gives a "cannot access field" error. So the VM now marks object fields it generates as `nfSkipFieldChecking`. Not sure if this affects VM performance, don't see why it would. * The nkRecWhen changes in #24042 didn't cover the case where all conditions are constantly false correctly, this is fixed with a minor change. This isn't needed for this PR now but I encountered it after forgetting to `dec c.inGenericContext`.
1 parent c10f84b commit a93c5d7

File tree

9 files changed

+56
-28
lines changed

9 files changed

+56
-28
lines changed

compiler/semexprs.nim

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3084,7 +3084,11 @@ proc hoistParamsUsedInDefault(c: PContext, call, letSection, defExpr: var PNode)
30843084
# duty is activated by returning a non-nil value. The caller is responsible
30853085
# for replacing the input to the function with the returned non-nil value.
30863086
# (which is the hoisted symbol)
3087-
if defExpr.kind == nkSym and defExpr.sym.kind == skParam and defExpr.sym.owner == call[0].sym:
3087+
if defExpr.kind == nkSym and defExpr.sym.kind == skParam and
3088+
(defExpr.sym.owner == call[0].sym or
3089+
# symbol was resolved before proc was instantiated:
3090+
(sfFromGeneric in call[0].sym.flags and
3091+
defExpr.sym.owner == call[0].sym.instantiatedFrom)):
30883092
let paramPos = defExpr.sym.position + 1
30893093

30903094
if call[paramPos].skipAddr.kind != nkSym and not (

compiler/seminst.nim

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ iterator instantiateGenericParamList(c: PContext, n: PNode, pt: TypeMapping): PS
5353
if q.typ.kind != tyCompositeTypeClass:
5454
localError(c.config, a.info, errCannotInstantiateX % s.name.s)
5555
t = errorType(c)
56-
elif t.kind in {tyGenericParam, tyConcept}:
56+
elif t.kind in {tyGenericParam, tyConcept, tyFromExpr}:
5757
localError(c.config, a.info, errCannotInstantiateX % q.name.s)
5858
t = errorType(c)
5959
elif isUnresolvedStatic(t) and (q.typ.kind == tyStatic or
@@ -277,9 +277,10 @@ proc instantiateProcType(c: PContext, pt: TypeMapping,
277277
# call head symbol, because this leads to infinite recursion.
278278
if oldParam.ast != nil:
279279
var def = oldParam.ast.copyTree
280-
if def.kind in nkCallKinds:
281-
for i in 1..<def.len:
282-
def[i] = replaceTypeVarsN(cl, def[i], 1)
280+
if def.typ.kind == tyFromExpr:
281+
def.typ.flags.incl tfNonConstExpr
282+
if not isIntLit(def.typ):
283+
def = prepareNode(cl, def)
283284

284285
# allow symchoice since node will be fit later
285286
# although expectedType should cover it

compiler/semtypes.nim

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -829,7 +829,7 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int,
829829
it[idx] = if newf.len == 1: newf[0] else: newf
830830
if branch != nil:
831831
semRecordNodeAux(c, branch, check, pos, father, rectype, hasCaseFields)
832-
elif c.inGenericContext > 0:
832+
elif cannotResolve:
833833
father.add a
834834
elif father.kind in {nkElse, nkOfBranch}:
835835
father.add newNodeI(nkRecList, n.info)
@@ -1365,15 +1365,10 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
13651365
"either use ';' (semicolon) or explicitly write each default value")
13661366
message(c.config, a.info, warnImplicitDefaultValue, msg)
13671367
block determineType:
1368-
var defTyp = typ
1369-
if isCurrentlyGeneric():
1370-
defTyp = nil
1371-
def = semGenericStmt(c, def)
1372-
if hasUnresolvedArgs(c, def):
1373-
def.typ = makeTypeFromExpr(c, def.copyTree)
1374-
break determineType
1375-
1376-
def = semExprWithType(c, def, {efDetermineType, efAllowSymChoice}, defTyp)
1368+
let isGeneric = isCurrentlyGeneric()
1369+
inc c.inGenericContext, ord(isGeneric)
1370+
def = semExprWithType(c, def, {efDetermineType, efAllowSymChoice}, typ)
1371+
dec c.inGenericContext, ord(isGeneric)
13771372
if def.referencesAnotherParam(getCurrOwner(c)):
13781373
def.flags.incl nfDefaultRefsParam
13791374

compiler/semtypinst.nim

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ proc replaceTypeVarsT*(cl: var TReplTypeVars, t: PType): PType =
118118
result = replaceTypeVarsTAux(cl, t)
119119
checkMetaInvariants(cl, result)
120120
121-
proc prepareNode(cl: var TReplTypeVars, n: PNode): PNode =
121+
proc prepareNode*(cl: var TReplTypeVars, n: PNode): PNode =
122122
let t = replaceTypeVarsT(cl, n.typ)
123123
if t != nil and t.kind == tyStatic and t.n != nil:
124124
return if tfUnresolved in t.flags: prepareNode(cl, t.n)

compiler/sigmatch.nim

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,13 @@ proc typeRel*(c: var TCandidate, f, aOrig: PType,
135135

136136
proc matchGenericParam(m: var TCandidate, formal: PType, n: PNode) =
137137
var arg = n.typ
138+
if m.c.inGenericContext > 0:
139+
# don't match yet-unresolved generic instantiations
140+
while arg != nil and arg.kind == tyGenericParam:
141+
arg = idTableGet(m.bindings, arg)
142+
if arg == nil or arg.containsGenericType:
143+
m.state = csNoMatch
144+
return
138145
# fix up the type to get ready to match formal:
139146
var formalBase = formal
140147
while formalBase.kind == tyGenericParam and

compiler/vm.nim

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -894,8 +894,10 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
894894
stackTrace(c, tos, pc, errNilAccess)
895895
elif dest[shiftedRb].kind == nkExprColonExpr:
896896
writeField(dest[shiftedRb][1], regs[rc])
897+
dest[shiftedRb][1].flags.incl nfSkipFieldChecking
897898
else:
898899
writeField(dest[shiftedRb], regs[rc])
900+
dest[shiftedRb].flags.incl nfSkipFieldChecking
899901
of opcWrStrIdx:
900902
decodeBC(rkNode)
901903
let idx = regs[rb].intVal.int

tests/misc/t20883.nim

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
discard """
22
action: reject
3-
errormsg: "type mismatch: got <float64> but expected 'typeof(U(0.000001))'"
4-
line: 8
5-
column: 22
3+
nimout: '''
4+
t20883.nim(13, 4) template/generic instantiation of `foo` from here
5+
t20883.nim(9, 11) Error: cannot instantiate: 'U'
6+
'''
67
"""
78

89
proc foo*[U](x: U = U(1e-6)) =

tests/proc/texplicitgenerics.nim

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -36,14 +36,10 @@ block: # ditto but may be wrong minimization
3636
# minimized from measuremancer
3737
type Foo[T] = object
3838
proc foo[T](): Foo[T] = Foo[T]()
39-
when false:
40-
# this is the actual issue but there are other instantiation problems
41-
proc bar[T](x = foo[T]()) = discard
42-
else:
43-
proc bar[T](x: Foo[T] = foo[T]()) = discard
39+
# this is the actual issue but there are other instantiation problems
40+
proc bar[T](x = foo[T]()) = discard
4441
bar[int](Foo[int]())
4542
bar[int]()
46-
when false:
47-
# alternative version, also causes instantiation issue
48-
proc baz[T](x: typeof(foo[T]())) = discard
49-
baz[int](Foo[int]())
43+
# alternative version, also causes instantiation issue
44+
proc baz[T](x: typeof(foo[T]())) = discard
45+
baz[int](Foo[int]())

tests/proc/tgenericdefaultparam.nim

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
block: # issue #16700
2+
type MyObject[T] = object
3+
x: T
4+
proc initMyObject[T](value = T.default): MyObject[T] =
5+
MyObject[T](x: value)
6+
var obj = initMyObject[int]()
7+
8+
block: # issue #20916
9+
type
10+
SomeX = object
11+
v: int
12+
var val = 0
13+
proc f(_: type int, x: SomeX, v = x.v) =
14+
doAssert v == 42
15+
val = v
16+
proc a(): proc() =
17+
let v = SomeX(v: 42)
18+
var tmp = proc() =
19+
int.f(v)
20+
tmp
21+
a()()
22+
doAssert val == 42

0 commit comments

Comments
 (0)