summary refs log tree commit diff stats
path: root/tests/constructors
diff options
context:
space:
mode:
Diffstat (limited to 'tests/constructors')
-rw-r--r--tests/constructors/a.nim2
-rw-r--r--tests/constructors/b.nim2
-rw-r--r--tests/constructors/t18990.nim3
-rw-r--r--tests/constructors/tconstr1.nim28
-rw-r--r--tests/constructors/tconstr2.nim22
-rw-r--r--tests/constructors/tinvalid_construction.nim327
6 files changed, 379 insertions, 5 deletions
diff --git a/tests/constructors/a.nim b/tests/constructors/a.nim
new file mode 100644
index 000000000..03788fc57
--- /dev/null
+++ b/tests/constructors/a.nim
@@ -0,0 +1,2 @@
+type A* = object
+  a: uint8
\ No newline at end of file
diff --git a/tests/constructors/b.nim b/tests/constructors/b.nim
new file mode 100644
index 000000000..437dd0550
--- /dev/null
+++ b/tests/constructors/b.nim
@@ -0,0 +1,2 @@
+type B* = object
+proc A*(a, b: float): B = discard
\ No newline at end of file
diff --git a/tests/constructors/t18990.nim b/tests/constructors/t18990.nim
new file mode 100644
index 000000000..2f60f3c2c
--- /dev/null
+++ b/tests/constructors/t18990.nim
@@ -0,0 +1,3 @@
+import a, b
+discard A(1f, 1f) # works
+proc x(b = A(1f, 1f)) = discard # doesn't work
\ No newline at end of file
diff --git a/tests/constructors/tconstr1.nim b/tests/constructors/tconstr1.nim
new file mode 100644
index 000000000..a169bf453
--- /dev/null
+++ b/tests/constructors/tconstr1.nim
@@ -0,0 +1,28 @@
+discard """
+  errormsg: "type mismatch"
+  file: "tconstr1.nim"
+  line: 25
+"""
+# Test array, record constructors
+
+type
+  TComplexRecord = tuple[
+    s: string,
+    x, y: int,
+    z: float,
+    chars: set[char]]
+
+proc testSem =
+  var
+    things: array[0..1, TComplexRecord] = [
+      (s: "hi", x: 69, y: 45, z: 0.0, chars: {'a', 'b', 'c'}),
+      (s: "hi", x: 69, y: 45, z: 1.0, chars: {'a', 'b', 'c'})]
+  write(stdout, things[0].x)
+
+const
+  things: array[0..1, TComplexRecord] = [
+    (s: "hi", x: 69, y: 45, z: 0.0, chars: {'a', 'b', 'c'}),
+    (s: "hi", x: 69, y: 45, z: 1.0)] #ERROR
+  otherThings = [  # the same
+    (s: "hi", x: 69, y: 45, z: 0.0, chars: {'a', 'b', 'c'}),
+    (s: "hi", x: 69, y: 45, z: 1.0, chars: {'a'})]
diff --git a/tests/constructors/tconstr2.nim b/tests/constructors/tconstr2.nim
new file mode 100644
index 000000000..2557d7db9
--- /dev/null
+++ b/tests/constructors/tconstr2.nim
@@ -0,0 +1,22 @@
+discard """
+  output: "69"
+"""
+# Test array, record constructors
+
+type
+  TComplexRecord = tuple[
+    s: string,
+    x, y: int,
+    z: float,
+    chars: set[char]]
+
+const
+  things: array[0..1, TComplexRecord] = [
+    (s: "hi", x: 69, y: 45, z: 0.0, chars: {'a', 'b', 'c'}),
+    (s: "hi", x: 69, y: 45, z: 1.0, chars: {})]
+  otherThings = [  # the same
+    (s: "hi", x: 69, y: 45, z: 0.0, chars: {'a', 'b', 'c'}),
+    (s: "hi", x: 69, y: 45, z: 1.0, chars: {'a'})]
+
+writeLine(stdout, things[0].x)
+#OUT 69
diff --git a/tests/constructors/tinvalid_construction.nim b/tests/constructors/tinvalid_construction.nim
index b3e56eec6..4b372d68a 100644
--- a/tests/constructors/tinvalid_construction.nim
+++ b/tests/constructors/tinvalid_construction.nim
@@ -3,11 +3,16 @@ template accept(x) =
 
 template reject(x) =
   static: assert(not compiles(x))
+
 {.experimental: "notnil".}
+
 type
   TRefObj = ref object
     x: int
 
+  IllegalToConstruct = object
+    x: cstring not nil
+
   THasNotNils = object of RootObj
     a: TRefObj not nil
     b: TRefObj not nil
@@ -15,6 +20,8 @@ type
 
   THasNotNilsRef = ref THasNotNils
 
+  TRefObjNotNil = TRefObj not nil
+
   TChoice = enum A, B, C, D, E, F
 
   TBaseHasNotNils = object of THasNotNils
@@ -26,6 +33,22 @@ type
     else:
       discard
 
+  PartialRequiresInit = object
+    a {.requiresInit.}: int
+    b: string
+
+  PartialRequiresInitRef = ref PartialRequiresInit
+
+  FullRequiresInit {.requiresInit.} = object
+    a: int
+    b: int
+
+  FullRequiresInitRef = ref FullRequiresInit
+
+  FullRequiresInitWithParent {.requiresInit.} = object of THasNotNils
+    e: int
+    d: int
+
   TObj = object
     case choice: TChoice
     of A:
@@ -55,11 +78,23 @@ type
 
 var x = D
 var nilRef: TRefObj
-var notNilRef = TRefObj(x: 20)
+let notNilRef = TRefObjNotNil(x: 20)
 
 proc makeHasNotNils: ref THasNotNils =
-  result.a = TRefObj(x: 10)
-  result.b = TRefObj(x: 20)
+  (ref THasNotNils)(a: TRefObj(x: 10),
+                    b: TRefObj(x: 20))
+
+proc userDefinedDefault(T: typedesc): T =
+  # We'll use that to make sure the user cannot cheat
+  # with constructing requiresInit types
+  discard
+
+proc genericDefault(T: typedesc): T =
+  result = default(T)
+
+reject IllegalToConstruct()
+reject:
+  var x: IllegalToConstruct
 
 accept TObj()
 accept TObj(choice: A)
@@ -74,14 +109,22 @@ reject TObj(a: 10, f: "")       # conflicting fields
 accept TObj(choice: E, e1: TRefObj(x: 10), e2: 10)
 
 accept THasNotNils(a: notNilRef, b: notNilRef, c: nilRef)
-# XXX: the "not nil" logic in the compiler is not strong enough to catch this one yet:
-# reject THasNotNils(a: notNilRef, b: nilRef, c: nilRef)
+reject THasNotNils(a: notNilRef, b: nilRef, c: nilRef)      # `b` shouldn't be nil
 reject THasNotNils(b: notNilRef, c: notNilRef)              # there is a missing not nil field
 reject THasNotNils()                                        # again, missing fields
 accept THasNotNils(a: notNilRef, b: notNilRef)              # it's OK to omit a non-mandatory field
+# produces only warning: reject default(THasNotNils)
+# produces only warning: reject userDefinedDefault(THasNotNils)
+
+# produces only warning: reject default(TRefObjNotNil)
+# produces only warning: reject userDefinedDefault(TRefObjNotNil)
+# produces only warning: reject genericDefault(TRefObjNotNil)
 
 # missing not nils in base
 reject TBaseHasNotNils()
+# produces only warning: reject default(TBaseHasNotNils)
+# produces only warning: reject userDefinedDefault(TBaseHasNotNils)
+# produces only warning: reject genericDefault(TBaseHasNotNils)
 
 # once you take care of them, it's ok
 accept TBaseHasNotNils(a: notNilRef, b: notNilRef, choice: D)
@@ -106,8 +149,118 @@ accept TBaseHasNotNils(a: notNilRef, b: notNilRef, choice: B, indirectNotNils: m
 reject TBaseHasNotNils(a: notNilRef, b: notNilRef, choice: B, indirectNotNils: THasNotNilsRef())
 accept TBaseHasNotNils(a: notNilRef, b: notNilRef, choice: B, indirectNotNils: THasNotNilsRef(a: notNilRef, b: notNilRef))
 
+# Accept only instances where the `a` field is present
+accept PartialRequiresInit(a: 10, b: "x")
+accept PartialRequiresInit(a: 20)
+reject PartialRequiresInit(b: "x")
+reject PartialRequiresInit()
+accept PartialRequiresInitRef(a: 10, b: "x")
+accept PartialRequiresInitRef(a: 20)
+reject PartialRequiresInitRef(b: "x")
+reject PartialRequiresInitRef()
+accept((ref PartialRequiresInit)(a: 10, b: "x"))
+accept((ref PartialRequiresInit)(a: 20))
+reject((ref PartialRequiresInit)(b: "x"))
+reject((ref PartialRequiresInit)())
+
+# produces only warning: reject default(PartialRequiresInit)
+# produces only warning: reject userDefinedDefault(PartialRequiresInit)
+reject:
+  var obj: PartialRequiresInit
+
+accept FullRequiresInit(a: 10, b: 20)
+reject FullRequiresInit(a: 10)
+reject FullRequiresInit(b: 20)
+reject FullRequiresInit()
+accept FullRequiresInitRef(a: 10, b: 20)
+reject FullRequiresInitRef(a: 10)
+reject FullRequiresInitRef(b: 20)
+reject FullRequiresInitRef()
+accept((ref FullRequiresInit)(a: 10, b: 20))
+reject((ref FullRequiresInit)(a: 10))
+reject((ref FullRequiresInit)(b: 20))
+reject((ref FullRequiresInit)())
+
+# produces only warning: reject default(FullRequiresInit)
+# produces only warning: reject userDefinedDefault(FullRequiresInit)
+reject:
+  var obj: FullRequiresInit
+
+accept FullRequiresInitWithParent(a: notNilRef, b: notNilRef, c: notNilRef, e: 10, d: 20)
+accept FullRequiresInitWithParent(a: notNilRef, b: notNilRef, c: nil, e: 10, d: 20)
+reject FullRequiresInitWithParent(a: notNilRef, b: nil, c: nil, e: 10, d: 20) # b should not be nil
+reject FullRequiresInitWithParent(a: notNilRef, b: notNilRef, e: 10, d: 20)   # c should not be missing
+reject FullRequiresInitWithParent(a: notNilRef, b: notNilRef, c: nil, e: 10)  # d should not be missing
+reject FullRequiresInitWithParent()
+# produces only warning: reject default(FullRequiresInitWithParent)
+# produces only warning: reject userDefinedDefault(FullRequiresInitWithParent)
+reject:
+  var obj: FullRequiresInitWithParent
+
 # this will be accepted, because the false outer branch will be taken and the inner A branch
 accept TNestedChoices()
+accept default(TNestedChoices)
+accept:
+  var obj: TNestedChoices
+
+#[# produces only warning:
+reject:
+  # This proc is illegal, because it tries to produce
+  # a default object of a type that requires initialization:
+  proc defaultHasNotNils: THasNotNils =
+    discard
+#]#
+
+#[# produces only warning:
+reject:
+  # You cannot cheat by using the result variable to specify
+  # only some of the fields
+  proc invalidPartialTHasNotNils: THasNotNils =
+    result.c = nilRef
+#]#
+
+#[# produces only warning:
+reject:
+  # The same applies for requiresInit types
+  proc invalidPartialRequiersInit: PartialRequiresInit =
+    result.b = "x"
+#]#
+
+#[# produces only warning:
+# All code paths must return a value when the result requires initialization:
+reject:
+  proc ifWithoutAnElse: THasNotNils =
+    if stdin.readLine == "":
+      return THasNotNils(a: notNilRef, b: notNilRef, c: nilRef)
+#]#
+
+accept:
+  # All code paths must return a value when the result requires initialization:
+  proc wellFormedIf: THasNotNils =
+    if stdin.readLine == "":
+      return THasNotNils(a: notNilRef, b: notNilRef, c: nilRef)
+    else:
+      return THasNotNIls(a: notNilRef, b: notNilRef)
+
+#[# produces only warning:
+reject:
+  proc caseWithoutAllCasesCovered: FullRequiresInit =
+    # Please note that these is no else branch here:
+    case stdin.readLine
+    of "x":
+      return FullRequiresInit(a: 10, b: 20)
+    of "y":
+      return FullRequiresInit(a: 30, b: 40)
+#]#
+
+accept:
+  proc wellFormedCase: FullRequiresInit =
+    case stdin.readLine
+    of "x":
+      result = FullRequiresInit(a: 10, b: 20)
+    else:
+      # Mixing result and return is fine:
+      return FullRequiresInit(a: 30, b: 40)
 
 # but if we supply a run-time value for the inner branch, the compiler won't be able to prove
 # that the notnil field was initialized
@@ -120,3 +273,167 @@ reject TNestedChoices(outerChoice: false, innerChoice: C)
 accept TNestedChoices(outerChoice: false, innerChoice: C, notnil: notNilRef)
 reject TNestedChoices(outerChoice: false, innerChoice: C, notnil: nil)
 
+# Tests involving generics and sequences:
+#
+block:
+  # This test aims to show that it's possible to instantiate and
+  # use a sequence with a requiresInit type:
+
+  var legalSeq: seq[IllegalToConstruct]
+  legalSeq.add IllegalToConstruct(x: "one")
+  var two = IllegalToConstruct(x: "two")
+  legalSeq.add two
+  var one = legalSeq[0]
+  var twoAgain = legalSeq.pop
+
+  #[# produces only warning:
+  # It's not possible to tell the sequence to create elements
+  # for us though:
+  reject:
+    var illegalSeq = newSeq[IllegalToConstruct](10)
+  #]#
+
+  #[# produces only warning:
+  reject:
+    var illegalSeq: seq[IllegalToConstruct]
+    newSeq(illegalSeq, 10)
+  #]#
+
+  #[# produces only warning:
+  reject:
+    var illegalSeq: seq[IllegalToConstruct]
+    illegalSeq.setLen 10
+  #]#
+
+  # You can still use newSeqOfCap to write efficient code:
+  var anotherLegalSequence = newSeqOfCap[IllegalToConstruct](10)
+  for i in 0..9:
+    anotherLegalSequence.add IllegalToConstruct(x: "x")
+
+type DefaultConstructible[yesOrNo: static[bool]] = object
+  when yesOrNo:
+    x: string
+  else:
+    x: cstring not nil
+
+block:
+  # Constructability may also depend on the generic parameters of the type:
+  accept:
+    var a: DefaultConstructible[true]
+    var b = DefaultConstructible[true]()
+    var c = DefaultConstructible[true](x: "test")
+    var d = DefaultConstructible[false](x: "test")
+
+  reject:
+    var y: DefaultConstructible[false]
+
+  reject:
+    var y = DefaultConstructible[false]()
+
+block:
+  type
+    Hash = int
+
+    HashTableSlotType = enum
+      Free    = Hash(0)
+      Deleted = Hash(1)
+      HasKey  = Hash(2)
+
+    KeyValuePair[A, B] = object
+      key: A
+      case hash: HashTableSlotType
+      of Free, Deleted:
+        discard
+      else:
+        value: B
+
+  # The above KeyValuePair is an interesting type because it
+  # may become unconstructible depending on the generic parameters:
+  accept KeyValuePair[int, string](hash: Deleted)
+  accept KeyValuePair[int, IllegalToConstruct](hash: Deleted)
+
+  accept KeyValuePair[int, string](hash: HasKey)
+  reject KeyValuePair[int, IllegalToConstruct](hash: HasKey)
+
+  # Since all the above variations don't have a non-constructible
+  # field in the default branch of the case object, we can construct
+  # such values:
+  accept KeyValuePair[int, string]()
+  accept KeyValuePair[int, IllegalToConstruct]()
+  accept KeyValuePair[DefaultConstructible[true], string]()
+  accept KeyValuePair[DefaultConstructible[true], IllegalToConstruct]()
+
+  var a: KeyValuePair[int, string]
+  var b: KeyValuePair[int, IllegalToConstruct]
+  var c: KeyValuePair[DefaultConstructible[true], string]
+  var d: KeyValuePair[DefaultConstructible[true], IllegalToConstruct]
+  var s1 = newSeq[KeyValuePair[int, IllegalToConstruct]](10)
+  var s2 = newSeq[KeyValuePair[DefaultConstructible[true], IllegalToConstruct]](10)
+
+  # But let's put the non-constructible values as keys:
+  reject KeyValuePair[IllegalToConstruct, int](hash: Deleted)
+  reject KeyValuePair[IllegalToConstruct, int]()
+
+  type IllegalPair = KeyValuePair[DefaultConstructible[false], string]
+
+  reject:
+    var x: IllegalPair
+
+  #[# produces only warning:
+  reject:
+    var s = newSeq[IllegalPair](10)
+  #]#
+
+# Specific issues:
+#
+block:
+  # https://github.com/nim-lang/Nim/issues/11428
+  type
+    Enum = enum A, B, C
+    Thing = object
+      case kind: Enum
+      of A: discard
+      of B: s: string
+      of C: r: range[1..1] # DateTime
+
+  # Fine to not initialize 'r' because this is implicitly initialized and known to be branch 'A'.
+  var x = Thing()
+  discard x
+
+block:
+  # https://github.com/nim-lang/Nim/issues/4907
+  type
+    Foo = ref object
+    Bar = object
+
+    Thing[A, B] = ref object
+      a: A not nil
+      b: ref B
+      c: ref B not nil
+
+  proc allocNotNil(T: typedesc): T not nil =
+    new result
+
+  proc mutateThing(t: var Thing[Foo, Bar]) =
+    let fooNotNil = allocNotNil(Foo)
+    var foo: Foo
+
+    let barNotNil = allocNotNil(ref Bar)
+    var bar: ref Bar
+
+    t.a = fooNotNil
+    t.b = bar
+    t.b = barNotNil
+    t.c = barNotNil
+
+    reject:
+      t.a = foo
+
+    reject:
+      t.c = bar
+
+  var thing = Thing[Foo, Bar](a: allocNotNil(Foo),
+                              b: allocNotNil(ref Bar),
+                              c: allocNotNil(ref Bar))
+  mutateThing thing
+