Files
Nim/tests/objects/tobject_default_value.nim
ringabout e9a0c9634e 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 <copilot@github.com>
2026-05-05 21:05:17 +02:00

870 lines
16 KiB
Nim

discard """
matrix: "-d:nimPreviewRangeDefault --mm:refc; -d:nimPreviewRangeDefault --warningAsError:ProveInit --mm:orc"
targets: "c cpp js"
"""
import std/[times, macros, tables]
type
Guess = object
poi: DateTime
GuessDistinct = distinct Guess
block:
var x: Guess
discard Guess()
var y: GuessDistinct
discard y
discard GuessDistinct(x)
import mobject_default_value
block:
let x = Default()
doAssert x.se == 0'i32
block:
let x = default(Default)
doAssert x.se == 0'i32
# echo Default(poi: 12)
# echo Default(poi: 17)
# echo NonDefault(poi: 77)
block:
var x: Default
doAssert x.se == 0'i32
type
ObjectBase = object of RootObj
value = 12
ObjectBaseDistinct = distinct ObjectBase
DinstinctInObject = object
data: ObjectBaseDistinct
Object = object of ObjectBase
time: float = 1.2
date: int
scale: range[1..10]
Object2 = object
name: Object
Object3 = object
obj: Object2
ObjectTuple = tuple
base: ObjectBase
typ: int
obj: Object
TupleInObject = object
size = 777
data: ObjectTuple
Ref = ref object of ObjectBase
RefInt = ref object of Ref
data = 73
Ref2 = ref object of ObjectBase
RefInt2 = ref object of Ref
data = 73
var t {.threadvar.}: Default
# var m1, m2 {.threadvar.}: Default
block:
doAssert t.se == 0'i32
block: # ARC/ORC cannot bind destructors twice, so it cannot
# be moved into main
block:
var x: Ref
new(x)
doAssert x.value == 12, "Ref.value = " & $x.value
var y: RefInt
new(y)
doAssert y.value == 12
doAssert y.data == 73
block:
var x: Ref2
new(x, proc (x: Ref2) {.nimcall.} = discard "call Ref")
doAssert x.value == 12, "Ref.value = " & $x.value
proc call(x: RefInt2) =
discard "call RefInt"
var y: RefInt2
new(y, call)
doAssert y.value == 12
doAssert y.data == 73
template main {.dirty.} =
block: # bug #16744
type
R = range[1..10]
Obj = object
r: R
var
rVal: R = default(R) # Works fine
objVal = default(Obj)
doAssert rVal == 1
doAssert objVal.r == 1
block: # bug #16744
type
R = range[1..10]
Obj = object
r: R
var
rVal: R = default(R) # Works fine
objVal = Obj()
doAssert rVal == 1 # it should be 1
doAssert objVal.r == 1
block: # bug #3608
type
abc = ref object
w: range[2..100]
proc createABC(): abc =
new(result)
result.w = 20
doAssert createABC().w == 20
block:
var x = new ObjectBase
doAssert x.value == 12
proc hello(): ref ObjectBase =
new result
let z = hello()
doAssert z.value == 12
block:
var base = ObjectBase()
var x: ObjectBaseDistinct = ObjectBaseDistinct(base)
doAssert ObjectBase(x).value == 12
let y = ObjectBaseDistinct(default(ObjectBase))
doAssert ObjectBase(y).value == 12
let m = ObjectBaseDistinct(ObjectBase())
doAssert ObjectBase(m).value == 12
proc hello(): ObjectBaseDistinct =
result = ObjectBaseDistinct(default(ObjectBase))
let z = hello()
doAssert ObjectBase(z).value == 12
block:
var x: DinstinctInObject
x.data = ObjectBaseDistinct(default(ObjectBase))
doAssert ObjectBase(x.data).value == 12
block:
var x = Object()
doAssert x.value == 12
doAssert x.time == 1.2
doAssert x.scale == 1
let y = default(Object)
doAssert y.value == 12
doAssert y.time == 1.2
doAssert y.scale == 1
var x1, x2, x3 = default(Object)
doAssert x1.value == 12
doAssert x1.time == 1.2
doAssert x1.scale == 1
doAssert x2.value == 12
doAssert x2.time == 1.2
doAssert x2.scale == 1
doAssert x3.value == 12
doAssert x3.time == 1.2
doAssert x3.scale == 1
block:
var x = new Object
doAssert x[] == default(Object)
block:
var x = default(Object2)
doAssert x.name.value == 12
doAssert x.name.time == 1.2
doAssert x.name.scale == 1
block:
let x = Object2()
doAssert x.name.value == 12
doAssert x.name.time == 1.2
doAssert x.name.scale == 1
block:
var x: ref Object2
new x
doAssert x[] == default(Object2)
block:
var x = default(Object3)
doAssert x.obj.name.value == 12
doAssert x.obj.name.time == 1.2
doAssert x.obj.name.scale == 1
block:
var x = Object3()
doAssert x.obj.name.value == 12
doAssert x.obj.name.time == 1.2
doAssert x.obj.name.scale == 1
when nimvm:
# todo
discard "fixme"
else:
when defined(gcArc) or defined(gcOrc) or defined(gcYrc):
block: #seq
var x = newSeq[Object](10)
let y = x[0]
doAssert y.value == 12
doAssert y.time == 1.2
doAssert y.scale == 1
block:
var x: seq[Object]
setLen(x, 5)
let y = x[^1]
doAssert y.value == 12
doAssert y.time == 1.2
doAssert y.scale == 1
block:
var my = @[1, 2, 3, 4, 5]
my.setLen(0)
my.setLen(5)
doAssert my == @[0, 0, 0, 0, 0]
block:
var my = "hello"
my.setLen(0)
my.setLen(5)
doAssert $(@my) == """@['\x00', '\x00', '\x00', '\x00', '\x00']"""
block: # array
var x: array[10, Object] = default(array[10, Object])
let y = x[0]
doAssert y.value == 12
doAssert y.time == 1.2
doAssert y.scale == 1
block: # array
var x {.noinit.}: array[10, Object]
discard x
block: # tuple
var x = default(ObjectTuple)
doAssert x.base.value == 12
doAssert x.typ == 0
doAssert x.obj.time == 1.2
doAssert x.obj.date == 0
doAssert x.obj.scale == 1
doAssert x.obj.value == 12
block: # tuple in object
var x = default(TupleInObject)
doAssert x.data.base.value == 12
doAssert x.data.typ == 0
doAssert x.data.obj.time == 1.2
doAssert x.data.obj.date == 0
doAssert x.data.obj.scale == 1
doAssert x.data.obj.value == 12
doAssert x.size == 777
type
ObjectArray = object
data: array[10, Object]
block:
var x = default(ObjectArray)
let y = x.data[0]
doAssert y.value == 12
doAssert y.time == 1.2
doAssert y.scale == 1
block:
var x: PrellDeque[int]
doAssert x.pendingTasks == 0
type
Color = enum
Red, Blue, Yellow
ObjectVarint = object
case kind: Color
of Red:
data: int = 10
of Blue:
fill = "123"
of Yellow:
time = 1.8'f32
ObjectVarint1 = object
case kind: Color = Blue
of Red:
data1: int = 10
of Blue:
fill2 = "123"
cry: float
of Yellow:
time3 = 1.8'f32
him: int
block:
var x = ObjectVarint(kind: Red)
doAssert x.kind == Red
doAssert x.data == 10
block:
var x = ObjectVarint(kind: Blue)
doAssert x.kind == Blue
doAssert x.fill == "123"
block:
var x = ObjectVarint(kind: Yellow)
doAssert x.kind == Yellow
doAssert typeof(x.time) is float32
block:
var x = default(ObjectVarint1)
doAssert x.kind == Blue
doAssert x.fill2 == "123"
x.cry = 326
type
ObjectVarint2 = object
case kind: Color
of Red:
data: int = 10
of Blue:
fill = "123"
of Yellow:
time = 1.8'f32
block:
var x = ObjectVarint2(kind: Blue)
doAssert x.fill == "123"
block:
type
Color = enum
Red, Blue, Yellow
type
ObjectVarint3 = object
case kind: Color = Blue
of Red:
data1: int = 10
of Blue:
case name: Color = Blue
of Blue:
go = 12
else:
temp = 66
fill2 = "123"
cry: float
of Yellow:
time3 = 1.8'f32
him: int
block:
var x = default(ObjectVarint3)
doAssert x.kind == Blue
doAssert x.name == Blue
doAssert x.go == 12
block:
var x = ObjectVarint3(kind: Blue, name: Red, temp: 99)
doAssert x.kind == Blue
doAssert x.name == Red
doAssert x.temp == 99
block:
type
Default = tuple
id: int = 1
obj: ObjectBase
name: string
Class = object
def: Default
Member = object
def: Default = (id: 777, obj: ObjectBase(), name: "fine")
block:
var x = default(Default)
doAssert x.id == 1
doAssert x.obj == default(ObjectBase)
doAssert x.name == ""
block:
var x = default(Class)
doAssert x.def == default(Default)
doAssert x.def.id == 1
doAssert x.def.obj == default(ObjectBase)
doAssert x.def.name == ""
block:
var x = default(Member)
doAssert x.def.id == 777
doAssert x.def.obj == default(ObjectBase)
doAssert x.def.name == "fine"
block:
var x {.noinit.} = 12
doAssert x == 12
type
Pure = object
id: int = 12
var y {.noinit.}: Pure
doAssert y.id == 0
var z {.noinit.}: Pure = Pure(id: 77)
doAssert z.id == 77
block: # bug #20681
type A = object
d: DateTime = DateTime()
let x = default(A)
doAssert $x == "(d: Uninitialized DateTime)"
block: # bug #20715
block:
type
Foo = enum
A
B
Bar = object
case foo: Foo
of A:
t: range[-1..2]
else: discard
var d = default(Bar)
doAssert d.t == -1
block:
type
Foo = enum
A
B
Bar = object
case foo: Foo
of A:
t: range[0..2]
else: discard
var d = default(Bar)
doAssert d.t == 0
block: # bug #20740
block:
proc foo(x: static DateTime = Datetime()) =
discard
foo()
block:
macro foo(x: static DateTime) =
discard x
macro foo2: untyped =
var x = DateTime()
result = quote do:
foo(`x`)
foo2()
block: # issue #20699
type
Either[A,B] = object
case kind:bool
of false:
b: B
of true:
a: A
O = object of RootRef
proc oToEither(o:O):Either[O,void] =
Either[O,void](kind:true,a: o)
discard oToEither(O())
block: # bug #20695
type
Default = object
tabs: Table[string, int] = initTable[string, int]()
let d = default(Default)
doAssert d.tabs.len == 0
block:
type
Default = object
tabs: Table[string, int] = Table[string, int]()
let d = default(Default)
doAssert d.tabs.len == 0
block:
type DjangoDateTime = distinct DateTime
type Default = object
data: DjangoDateTime = DjangoDateTime(DateTime())
let x = default(Default)
doAssert x.data is DjangoDateTime
block:
type DjangoDateTime = distinct DateTime
type Default = object
data = DjangoDateTime(DateTime())
let x = default(Default)
doAssert x.data is DjangoDateTime
block:
type
Result2 = object
case o: bool
of false:
e: float
of true:
v {.requiresInit.} : int = 1
proc startSessionSync(): Result2 =
return Result2(o: true)
proc mainSync =
let ff = startSessionSync()
doAssert ff.v == 1
mainSync()
block:
type
Result2 = object
v {.requiresInit.} : int = 1
proc startSessionSync(): Result2 =
return Result2()
proc mainSync =
let ff = startSessionSync()
doAssert ff.v == 1
mainSync()
block: # bug #21801
func evaluate(i: int): float =
0.0
func evaluate(): float =
0.0
type SearchOptions = object
evaluation: proc(): float = evaluate
block:
func evaluate(): float =
0.0
type SearchOptions = object
evaluation: proc(): float = evaluate
block:
func evaluate(i: int): float =
0.0
type SearchOptions = object
evaluation = evaluate
block:
type
Result[T, E] = object
when T is void:
when E is void:
oResultPrivate: bool
else:
case oResultPrivate: bool
of false:
eResultPrivate: E
of true:
discard
else:
when E is void:
case oResultPrivate: bool
of false:
discard
of true:
vResultPrivate: T
else:
case oResultPrivate: bool
of false:
eResultPrivate: E
of true:
vResultPrivate: T
template `?`[T, E](self: Result[T, E]): auto =
let v = (self)
if not v.oResultPrivate:
when compiles(`assignResult?`(default(typeof(result)))):
when typeof(result) is typeof(v):
`assignResult?`(v)
elif E is void:
`assignResult?`(err(typeof(result)))
else:
`assignResult?`(err(typeof(result), v.eResultPrivate))
return
else:
return
when typeof(result) is typeof(v):
v
elif E is void:
err(typeof(result))
else:
err(typeof(result), v.eResultPrivate)
when not(T is void):
v.vResultPrivate
type R = Result[int, string]
proc testAssignResult() =
var assigned: bool
template `assignResult?`(v: Result) =
assigned = true
result = v
proc failed(): Result[int, string] =
result = default(Result[int, string])
proc calling(): Result[int, string] =
let _ = ? failed()
raiseAssert "false"
let r = calling()
doAssert assigned
when nimvm:
when not defined(js):
testAssignResult()
else:
testAssignResult()
block: # bug #22123
type Thing = object
x: float32 = 1
type ThingWithArray = object
arr: array[256, float32]
n: float32 = 1
type Container = ref object
thing: array[5, Thing]
thing_with_array: array[5, ThingWithArray]
var foo = new Container
doAssert int(foo.thing[0].x) == 1
block: # bug #22613
type
K = enum
A = "a"
B = "b"
T = object
case kind: K = B
of A:
a: int
of B:
b: float
doAssert T().kind == B
block: # bug #22926
type
Direction = enum
North
South
East
West
ArrayObj1 = object
list: array[Direction, int]
ArrayObj2 = object
list: array[Direction, int] = [1, 2, 3, 4]
block:
var a: ArrayObj1
doAssert a.list[West] == 0
var b = default ArrayObj1
doAssert b.list[North] == 0
block:
var a: ArrayObj2
doAssert a.list[West] == 0
var b = default ArrayObj2
doAssert b.list[North] == 1
block:
type limited_float = range[1.2..20.0]
doAssert default(limited_float) == 1.2
block:
type
range1 = range[1..10]
range2 = range[-1..10]
proc foo =
doAssert default(range1) == 1
doAssert default(range2) == -1
let s = default(array[5, range1])
doAssert s == [range1 1, 1, 1, 1, 1]
foo()
block:
type
Object = object
id: range[1.2..29.3]
var s = default(Object)
doAssert s.id == 1.2
block: # bug #23943
type limited_int = range[1..20]
var d: limited_int;
doAssert d == 1
block: # bug #23545
proc evaluate(params: int) =
discard
proc evaluate() =
discard
type SearchInfo = object
evaluation: proc() = evaluate
var a = SearchInfo()
a.evaluation()
block: # bug #23770
type
Enum = enum A, B
Object = object
case a: Enum
of A:
integer: int = 200
of B:
time: string
Simple = object
v = -1
Another = object
o = Object(a: A)
v: Simple
let s1 = Object(a: A)
let s2 = Another()
doAssert s1.integer == 200 and s2.o.integer == 200
static: main()
main()
block:
type
MyTyp = ref object
thing = initTable[string,string]()
var t = MyTyp()
t.thing[""] = ""
type
Thing = object
a: int = 100 # this is fine
b = 100 # this is not
proc overloaded[T: SomeSignedInt](x: T) = discard
proc overloaded[T: SomeUnsignedInt](x: T) = discard
proc overloaded[T: object](x: T) =
for val in fields(x):
var v: typeof(val)
overloaded(v)
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