From e9a0c9634e15b0bab5356547ff848d73faf11630 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Wed, 6 May 2026 03:05:17 +0800 Subject: [PATCH] fixes #25784; Object default field initialized with an object constructor (#25785) fixes #25784 This pull request addresses the handling of forward object types during type determination in the Nim compiler and adds new test cases to ensure correct default value initialization for objects with forward references. The main focus is to allow forward object types to remain unresolved during the initial type analysis, deferring their resolution to a later compilation phase. This helps support object constructors with default values involving forward types. **Compiler improvements:** * Updated `semObjConstr` in `compiler/semobjconstr.nim` to allow forward object types (`tyForward`) to remain unresolved during determine-type analysis. This avoids premature errors and ensures that such types are resolved later, supporting delayed field-default resolution. **Testing enhancements:** * Added new test cases in `tests/objects/mobject_default_value.nim` to verify that objects with default fields referencing forward types are correctly initialized, and that their default values are properly set. --------- Co-authored-by: Copilot --- compiler/semobjconstr.nim | 5 ++++ compiler/semtypes.nim | 1 + tests/objects/tobject_default_value.nim | 35 ++++++++++++++++++++++++- 3 files changed, 40 insertions(+), 1 deletion(-) diff --git a/compiler/semobjconstr.nim b/compiler/semobjconstr.nim index d9317c3320..769f88b6f2 100644 --- a/compiler/semobjconstr.nim +++ b/compiler/semobjconstr.nim @@ -486,6 +486,11 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType # we have to watch out, there are also 'owned proc' types that can be used # multiple times as long as they don't have closures. result.typ.incl tfHasOwned + if t.kind == tyForward and efDetermineType in flags: + # a forward object type does not error during determine-type analysis; + # it now stays unresolved long enough for the existing delayed field-default pass to resolve it after the type section finishes. + result.typ = t + return result if t.kind != tyObject: return localErrorNode(c, result, if t.kind != tyGenericBody: "object constructor needs an object type".dup(addTypeNodeDeclaredLoc(c.config, t)) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 4c2d84c29f..8009f7293c 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -370,6 +370,7 @@ proc semFieldDefault(c: PContext; owner, expectedType: PType; field: PNode): PTy propagateToOwner(owner, result) proc semDelayedFieldDefault(c: PContext; owner, expectedType: PType; field: PNode) = + resetSemFlag(field[^1]) fitDefaultNode(c, field[^1], expectedType) propagateToOwner(owner, field[^1].typ.skipIntLit(c.idgen)) diff --git a/tests/objects/tobject_default_value.nim b/tests/objects/tobject_default_value.nim index 5b0a5cd8e6..69e35bf826 100644 --- a/tests/objects/tobject_default_value.nim +++ b/tests/objects/tobject_default_value.nim @@ -833,4 +833,37 @@ proc overloaded[T: object](x: T) = var v: typeof(val) overloaded(v) -overloaded(Thing()) \ No newline at end of file +overloaded(Thing()) + +block: + type + Foo = object + x = Bar() + + Bar = object + x: int + + var f = Foo() + doassert f.x.x == 0 + +block: + type + Foo = object + x = Bar(x: 55) + + Bar = object + x: int + + var f = Foo() + doassert f.x.x == 55 + +block: + type + Bar = object + x: int + + Foo = object + x = Bar() + + var f = Foo() + doassert f.x.x == 0