diff options
author | Zahary Karadjov <zahary@gmail.com> | 2020-03-30 00:49:42 +0300 |
---|---|---|
committer | Andreas Rumpf <rumpf_a@web.de> | 2020-04-01 19:38:44 +0200 |
commit | e63b673ce2557bcc3189bd5917b35327253b4c1b (patch) | |
tree | 6f6e3b5fc812a9cede2226e78ed9eb7adeaea080 | |
parent | 06438ed1431ee8ad3ae2eda9dcb7fbe64739329a (diff) | |
download | Nim-e63b673ce2557bcc3189bd5917b35327253b4c1b.tar.gz |
Fix https://github.com/nim-lang/Nim/issues/4907
-rw-r--r-- | compiler/semexprs.nim | 3 | ||||
-rw-r--r-- | compiler/semtypes.nim | 39 | ||||
-rw-r--r-- | tests/constructors/tinvalid_construction.nim | 41 |
3 files changed, 78 insertions, 5 deletions
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 5484f4594..e405ec0ef 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -2655,6 +2655,9 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = return var typ = semTypeNode(c, n, nil).skipTypes({tyTypeDesc}) result.typ = makeTypeDesc(c, typ) + of nkStmtListType: + let typ = semTypeNode(c, n, nil) + result.typ = makeTypeDesc(c, typ) of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit: # check if it is an expression macro: checkMinSonsLen(n, 1, c.config) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 1de181dbe..39df93b62 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -1720,12 +1720,43 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = case n.len of 3: result = semTypeNode(c, n[1], prev) - if result.skipTypes({tyGenericInst, tyAlias, tySink, tyOwned}).kind in NilableTypes+GenericTypes+{tyForward} and - n[2].kind == nkNilLit: + if result.kind == tyTypeDesc and tfUnresolved notin result.flags: + result = result.base + if n[2].kind != nkNilLit: + localError(c.config, n.info, + "Invalid syntax. When used with a type, 'not' can be followed only by 'nil'") + if notnil notin c.features: + localError(c.config, n.info, + "enable the 'not nil' annotation with {.experimental: \"notnil\".}") + let resolvedType = result.skipTypes({tyGenericInst, tyAlias, tySink, tyOwned}) + case resolvedType.kind + of tyGenericParam, tyTypeDesc, tyFromExpr: + # XXX: This is a really inappropraite hack, but it solves + # https://github.com/nim-lang/Nim/issues/4907 for now. + # + # A proper solution is to introduce a new type kind such + # as `tyNotNil[tyRef[SomeGenericParam]]`. This will allow + # semtypinst to replace the generic param correctly in + # situations like the following: + # + # type Foo[T] = object + # bar: ref T not nil + # baz: ref T + # + # The root of the problem is that `T` here must have a specific + # ID that is bound to a concrete type during instantiation. + # The use of `freshType` below breaks this. Another hack would + # be to reuse the same ID for the not nil type, but this will + # fail if the `T` parameter is referenced multiple times as in + # the example above. + # + # I suggest revisiting this once the language decides on whether + # `not nil` should be the default. We can then map nilable refs + # to other types such as `Option[T]`. + result = makeTypeFromExpr(c, newTree(nkStmtListType, n.copyTree)) + of NilableTypes + {tyGenericInvocation, tyForward}: result = freshType(result, prev) result.flags.incl(tfNotNil) - if notnil notin c.features: - localError(c.config, n.info, "enable the 'not nil' annotation with {.experimental: \"notnil\".}") else: localError(c.config, n.info, errGenerated, "invalid type") of 2: diff --git a/tests/constructors/tinvalid_construction.nim b/tests/constructors/tinvalid_construction.nim index c17e2123c..6716bfb45 100644 --- a/tests/constructors/tinvalid_construction.nim +++ b/tests/constructors/tinvalid_construction.nim @@ -267,4 +267,43 @@ block: of C: r: range[1..1] # DateTime # Fine to not initialize 'r' because this is implicitly initialized and known to be branch 'A'. - let someThing = Thing() + 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 + |