diff options
Diffstat (limited to 'tests/constructors')
-rw-r--r-- | tests/constructors/a.nim | 2 | ||||
-rw-r--r-- | tests/constructors/b.nim | 2 | ||||
-rw-r--r-- | tests/constructors/t18990.nim | 3 | ||||
-rw-r--r-- | tests/constructors/tconstr1.nim | 28 | ||||
-rw-r--r-- | tests/constructors/tconstr2.nim | 22 | ||||
-rw-r--r-- | tests/constructors/tinvalid_construction.nim | 327 |
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 + |