Zahary Karadjov
2020-03-30 00:49:42 +03:00
committed by Andreas Rumpf
parent 06438ed143
commit e63b673ce2
3 changed files with 78 additions and 5 deletions

View File

@@ -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)

View File

@@ -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:

View File

@@ -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