summary refs log tree commit diff stats
path: root/tests/constructors/tinvalid_construction.nim
blob: aaf06d4f2263488acbe902304ee2a65a437edf7e (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
template accept(x) =
  static: assert compiles(x)

template reject(x) =
  static: assert(not compiles(x))

{.experimental: "notnil".}

type
  TRefObj = ref object
    x: int

  THasNotNils = object of RootObj
    a: TRefObj not nil
    b: TRefObj not nil
    c: TRefObj

  THasNotNilsRef = ref THasNotNils

  TChoice = enum A, B, C, D, E, F

  TBaseHasNotNils = object of THasNotNils
    case choice: TChoice
    of A:
      moreNotNils: THasNotNils
    of B:
      indirectNotNils: ref THasNotNils
    else:
      discard

  PartialRequiresInit = object
    a {.requiresInit.}: int
    b: string

  FullRequiresInit {.requiresInit.} = object
    a: int
    b: int

  FullRequiresInitWithParent {.requiresInit.} = object of THasNotNils
    e: int
    d: int

  TObj = object
    case choice: TChoice
    of A:
      a: int
    of B, C:
      bc: int
    of D:
      d: TRefObj
    of E:
      e1: TRefObj
      e2: int
    else:
      f: string

  TNestedChoices = object
    case outerChoice: bool
    of true:
      truthy: int
    else:
      case innerChoice: TChoice
      of A:
        a: int
      of B:
        b: int
      else:
        notnil: TRefObj not nil

var x = D
var nilRef: TRefObj
var notNilRef = TRefObj(x: 20)

proc makeHasNotNils: ref THasNotNils =
  result.a = TRefObj(x: 10)
  result.b = TRefObj(x: 20)

accept TObj()
accept TObj(choice: A)
reject TObj(choice: A, bc: 10)  # bc is in the wrong branch
accept TObj(choice: B, bc: 20)
reject TObj(a: 10)              # branch selected without providing discriminator
reject TObj(choice: x, a: 10)   # the discrimantor must be a compile-time value when a branch is selected
accept TObj(choice: x)          # it's OK to use run-time value when a branch is not selected
accept TObj(choice: F, f: "")   # match an else clause
reject TObj(f: "")              # the discriminator must still be provided for an else clause
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(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

# missing not nils in base
reject TBaseHasNotNils()

# once you take care of them, it's ok
accept TBaseHasNotNils(a: notNilRef, b: notNilRef, choice: D)

# this one is tricky!
# it has to be rejected, because choice gets value A by default (0) and this means
# that the THasNotNils field will be active (and it will demand more initialized fields).
reject TBaseHasNotNils(a: notNilRef, b: notNilRef)

# you can select a branch without mandatory fields
accept TBaseHasNotNils(a: notNilRef, b: notNilRef, choice: B)
accept TBaseHasNotNils(a: notNilRef, b: notNilRef, choice: B, indirectNotNils: nil)

# but once you select a branch with mandatory fields, you must specify them
reject TBaseHasNotNils(a: notNilRef, b: notNilRef, choice: A)
reject TBaseHasNotNils(a: notNilRef, b: notNilRef, choice: A, indirectNotNils: nil)
reject TBaseHasNotNils(a: notNilRef, b: notNilRef, choice: A, moreNotNils: THasNotNils())
accept TBaseHasNotNils(a: notNilRef, b: notNilRef, choice: A, moreNotNils: THasNotNils(a: notNilRef, b: notNilRef))

# all rules apply to sub-objects as well
accept TBaseHasNotNils(a: notNilRef, b: notNilRef, choice: B, indirectNotNils: makeHasNotNils())
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 FullRequiresInit(a: 10, b: 20)
reject FullRequiresInit(a: 10)
reject FullRequiresInit(b: 20)

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()

# this will be accepted, because the false outer branch will be taken and the inner A branch
accept TNestedChoices()

# 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
reject TNestedChoices(outerChoice: false, innerChoice: x) # XXX: The error message is not very good here
reject TNestedChoices(outerChoice: true,  innerChoice: A) # XXX: The error message is not very good here

accept TNestedChoices(outerChoice: false, innerChoice: B)

reject TNestedChoices(outerChoice: false, innerChoice: C)
accept TNestedChoices(outerChoice: false, innerChoice: C, notnil: notNilRef)
reject TNestedChoices(outerChoice: false, innerChoice: C, notnil: nil)