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/t5965_1.nim | 10 | ||||
-rw-r--r-- | tests/constructors/t5965_2.nim | 10 | ||||
-rw-r--r-- | tests/constructors/tconstr1.nim | 28 | ||||
-rw-r--r-- | tests/constructors/tconstr2.nim | 22 | ||||
-rw-r--r-- | tests/constructors/tinvalid_construction.nim | 439 |
8 files changed, 516 insertions, 0 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/t5965_1.nim b/tests/constructors/t5965_1.nim new file mode 100644 index 000000000..abf07b21c --- /dev/null +++ b/tests/constructors/t5965_1.nim @@ -0,0 +1,10 @@ +discard """ + errormsg: "incorrect object construction syntax" + file: "t5965_1.nim" + line: 10 +""" + +type Foo = object + a, b, c: int + +discard Foo(a: 1, 2) diff --git a/tests/constructors/t5965_2.nim b/tests/constructors/t5965_2.nim new file mode 100644 index 000000000..e04f1b715 --- /dev/null +++ b/tests/constructors/t5965_2.nim @@ -0,0 +1,10 @@ +discard """ + errormsg: "incorrect object construction syntax" + file: "t5965_2.nim" + line: 10 +""" + +type Foo = object + a: int + +discard Foo(a: 1, 2) 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 new file mode 100644 index 000000000..4b372d68a --- /dev/null +++ b/tests/constructors/tinvalid_construction.nim @@ -0,0 +1,439 @@ +template accept(x) = + static: assert compiles(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 + c: TRefObj + + THasNotNilsRef = ref THasNotNils + + TRefObjNotNil = TRefObj not nil + + 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 + + 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: + 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 +let notNilRef = TRefObjNotNil(x: 20) + +proc makeHasNotNils: ref THasNotNils = + (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) +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) +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) + +# 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 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 +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) + +# 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 + |