From 242f761627c5b02581fc75b0c83920c2dca8702d Mon Sep 17 00:00:00 2001 From: Ryan McConnell Date: Sun, 12 Apr 2026 02:56:31 -0400 Subject: [PATCH] RE: fix #25627 (#25736) Follow up PR to #25700 @demotomohiro This doesn't seem to mirror your suggested approach completely. I still went with a recursive walk. Could probably add some kind of "clean types" and "dirty types" cache through this to minimize the recursions, but that seems like a little much. --- compiler/semdata.nim | 3 ++ compiler/semstmts.nim | 3 ++ compiler/semtypes.nim | 95 +++++++++++++++++++++++++--------------- tests/objects/t25627.nim | 48 ++++++++++++++++++++ 4 files changed, 114 insertions(+), 35 deletions(-) diff --git a/compiler/semdata.nim b/compiler/semdata.nim index d133f2fee9..c561f6690e 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -183,6 +183,9 @@ type forwardTypeUpdates*: seq[(PType, PNode)] # types that need to be updated in a type section # due to containing forward types, and their corresponding nodes + forwardFieldUpdates*: seq[(PType, PNode, PType)] + # object/tuple field definitions whose default values mention forward + # types and need delayed const checking inTypeofContext*: int semAsgnOpr*: proc (c: PContext; n: PNode; k: TNodeKind): PNode {.nimcall.} diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 5a04b2b592..380c50ce51 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -1817,6 +1817,9 @@ proc typeSectionFinalPass(c: PContext, n: PNode) = assignType(typ, reified) typ.itemId = reified.itemId # same id c.forwardTypeUpdates = @[] + for (owner, field, expectedType) in c.forwardFieldUpdates: + semDelayedFieldDefault(c, owner, expectedType, field) + c.forwardFieldUpdates = @[] for i in 0.. 0: - a[^1] = semExprWithType(c, a[^1], {efDetermineType, efAllowSymChoice}, typ) - if typ == nil: - typ = a[^1].typ - else: - fitDefaultNode(c, a[^1], typ) - typ = a[^1].typ.skipIntLit(c.idgen) + typ = semFieldDefault(c, result, typ, a) elif a[^2].kind != nkEmpty: typ = semTypeNode(c, a[^2], nil) if c.graph.config.isDefined("nimPreviewRangeDefault") and typ.skipTypes(abstractInst).kind == tyRange: @@ -922,14 +971,7 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int, var hasDefaultField = n[^1].kind != nkEmpty if hasDefaultField: typ = if n[^2].kind != nkEmpty: semTypeNode(c, n[^2], nil) else: nil - if c.inGenericContext > 0: - n[^1] = semExprWithType(c, n[^1], {efDetermineType, efAllowSymChoice}, typ) - if typ == nil: - typ = n[^1].typ - else: - fitDefaultNode(c, n[^1], typ) - typ = n[^1].typ.skipIntLit(c.idgen) - propagateToOwner(rectype, typ) + typ = semFieldDefault(c, rectype, typ, n) elif n[^2].kind == nkEmpty: localError(c.config, n.info, errTypeExpected) typ = errorType(c) @@ -1693,22 +1735,6 @@ proc containsGenericInvocationWithForward(n: PNode): bool = return true return false -proc containsRecWhen(n: PNode): bool = - if n == nil: - return false - case n.kind - of nkRecWhen: - return true - else: - for i in 0.. 2 and - containsRecWhen(n.sym.ast[2]) - proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType = if s.typ == nil: localError(c.config, n.info, "cannot instantiate the '$1' $2" % @@ -1788,12 +1814,11 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType = # XXX: What kind of error is this? is it still relevant? localError(c.config, n.info, errCannotInstantiateX % s.name.s) result = newOrPrevType(tyError, prev, c) - elif containsGenericInvocationWithForward(n[0]) or - (hasForwardTypeParam and requiresForwardTypeDelay(n[0])): + elif containsGenericInvocationWithForward(n[0]) or hasForwardTypeParam: # isConcrete == false means this generic type is not instanciated here because # it invoked with generic parameters. - # Even if isConcrete == true, don't instanciate it now if the type - # shape depends on unresolved `tyForward` type params. + # Even if isConcrete == true, don't instanciate it now if there are + # unresolved `tyForward` type params. # Such `tyForward` type params will be semchecked later and we can # instanciate this next time. # Some generic types like std/options.Option[T] need the kind of the diff --git a/tests/objects/t25627.nim b/tests/objects/t25627.nim index 57fa0ceb2c..7ba1e296c9 100644 --- a/tests/objects/t25627.nim +++ b/tests/objects/t25627.nim @@ -20,3 +20,51 @@ let dir = DirStruct() doAssert dir.root.kind == fsoDir doAssert dir.root.dirName == "/" doAssert dir.root.files.len == 0 + +block: + type + Opt[T] = object + when T is ref: + val: T + x: int + else: + val: T + x: string + + DefaultOpt = ref object + files: Opt[DefaultOpt] + + OptDirStruct = object + root = DefaultOpt() + + let dir = OptDirStruct() + doAssert dir.root.files.x is int + +block: + type + Opt[T] = object + when T is ref: + x: int + else: + x: string + + Foo[T] = object + x: Opt[T] + + Nested = ref object + files: Foo[Nested] + + let nested = Nested() + doAssert nested.files.x.x is int + +block: + type + Foo[T] = object + x = sizeof(T) + + Sized = ref object + files: Foo[Sized] + + let sized = Sized() + doAssert sized.files.x == sizeof(Sized) +