tinvalid_construction.nim 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. template accept(x) =
  2. static: assert compiles(x)
  3. template reject(x) =
  4. static: assert(not compiles(x))
  5. {.experimental: "notnil".}
  6. type
  7. TRefObj = ref object
  8. x: int
  9. THasNotNils = object of RootObj
  10. a: TRefObj not nil
  11. b: TRefObj not nil
  12. c: TRefObj
  13. THasNotNilsRef = ref THasNotNils
  14. TChoice = enum A, B, C, D, E, F
  15. TBaseHasNotNils = object of THasNotNils
  16. case choice: TChoice
  17. of A:
  18. moreNotNils: THasNotNils
  19. of B:
  20. indirectNotNils: ref THasNotNils
  21. else:
  22. discard
  23. TObj = object
  24. case choice: TChoice
  25. of A:
  26. a: int
  27. of B, C:
  28. bc: int
  29. of D:
  30. d: TRefObj
  31. of E:
  32. e1: TRefObj
  33. e2: int
  34. else:
  35. f: string
  36. TNestedChoices = object
  37. case outerChoice: bool
  38. of true:
  39. truthy: int
  40. else:
  41. case innerChoice: TChoice
  42. of A:
  43. a: int
  44. of B:
  45. b: int
  46. else:
  47. notnil: TRefObj not nil
  48. var x = D
  49. var nilRef: TRefObj
  50. var notNilRef = TRefObj(x: 20)
  51. proc makeHasNotNils: ref THasNotNils =
  52. result.a = TRefObj(x: 10)
  53. result.b = TRefObj(x: 20)
  54. accept TObj()
  55. accept TObj(choice: A)
  56. reject TObj(choice: A, bc: 10) # bc is in the wrong branch
  57. accept TObj(choice: B, bc: 20)
  58. reject TObj(a: 10) # branch selected without providing discriminator
  59. reject TObj(choice: x, a: 10) # the discrimantor must be a compile-time value when a branch is selected
  60. accept TObj(choice: x) # it's OK to use run-time value when a branch is not selected
  61. accept TObj(choice: F, f: "") # match an else clause
  62. reject TObj(f: "") # the discriminator must still be provided for an else clause
  63. reject TObj(a: 10, f: "") # conflicting fields
  64. accept TObj(choice: E, e1: TRefObj(x: 10), e2: 10)
  65. accept THasNotNils(a: notNilRef, b: notNilRef, c: nilRef)
  66. # XXX: the "not nil" logic in the compiler is not strong enough to catch this one yet:
  67. # reject THasNotNils(a: notNilRef, b: nilRef, c: nilRef)
  68. reject THasNotNils(b: notNilRef, c: notNilRef) # there is a missing not nil field
  69. reject THasNotNils() # again, missing fields
  70. accept THasNotNils(a: notNilRef, b: notNilRef) # it's OK to omit a non-mandatory field
  71. # missing not nils in base
  72. reject TBaseHasNotNils()
  73. # once you take care of them, it's ok
  74. accept TBaseHasNotNils(a: notNilRef, b: notNilRef, choice: D)
  75. # this one is tricky!
  76. # it has to be rejected, because choice gets value A by default (0) and this means
  77. # that the THasNotNils field will be active (and it will demand more initialized fields).
  78. reject TBaseHasNotNils(a: notNilRef, b: notNilRef)
  79. # you can select a branch without mandatory fields
  80. accept TBaseHasNotNils(a: notNilRef, b: notNilRef, choice: B)
  81. accept TBaseHasNotNils(a: notNilRef, b: notNilRef, choice: B, indirectNotNils: nil)
  82. # but once you select a branch with mandatory fields, you must specify them
  83. reject TBaseHasNotNils(a: notNilRef, b: notNilRef, choice: A)
  84. reject TBaseHasNotNils(a: notNilRef, b: notNilRef, choice: A, indirectNotNils: nil)
  85. reject TBaseHasNotNils(a: notNilRef, b: notNilRef, choice: A, moreNotNils: THasNotNils())
  86. accept TBaseHasNotNils(a: notNilRef, b: notNilRef, choice: A, moreNotNils: THasNotNils(a: notNilRef, b: notNilRef))
  87. # all rules apply to sub-objects as well
  88. accept TBaseHasNotNils(a: notNilRef, b: notNilRef, choice: B, indirectNotNils: makeHasNotNils())
  89. reject TBaseHasNotNils(a: notNilRef, b: notNilRef, choice: B, indirectNotNils: THasNotNilsRef())
  90. accept TBaseHasNotNils(a: notNilRef, b: notNilRef, choice: B, indirectNotNils: THasNotNilsRef(a: notNilRef, b: notNilRef))
  91. # this will be accepted, because the false outer branch will be taken and the inner A branch
  92. accept TNestedChoices()
  93. # but if we supply a run-time value for the inner branch, the compiler won't be able to prove
  94. # that the notnil field was initialized
  95. reject TNestedChoices(outerChoice: false, innerChoice: x) # XXX: The error message is not very good here
  96. reject TNestedChoices(outerChoice: true, innerChoice: A) # XXX: The error message is not very good here
  97. accept TNestedChoices(outerChoice: false, innerChoice: B)
  98. reject TNestedChoices(outerChoice: false, innerChoice: C)
  99. accept TNestedChoices(outerChoice: false, innerChoice: C, notnil: notNilRef)
  100. reject TNestedChoices(outerChoice: false, innerChoice: C, notnil: nil)