summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--compiler/semexprs.nim3
-rw-r--r--compiler/semtypes.nim39
-rw-r--r--tests/constructors/tinvalid_construction.nim41
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
+