This commit is contained in:
parent
06438ed143
commit
e63b673ce2
3 changed files with 78 additions and 5 deletions
|
|
@ -2655,6 +2655,9 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
|
||||||
return
|
return
|
||||||
var typ = semTypeNode(c, n, nil).skipTypes({tyTypeDesc})
|
var typ = semTypeNode(c, n, nil).skipTypes({tyTypeDesc})
|
||||||
result.typ = makeTypeDesc(c, typ)
|
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:
|
of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit:
|
||||||
# check if it is an expression macro:
|
# check if it is an expression macro:
|
||||||
checkMinSonsLen(n, 1, c.config)
|
checkMinSonsLen(n, 1, c.config)
|
||||||
|
|
|
||||||
|
|
@ -1720,12 +1720,43 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
||||||
case n.len
|
case n.len
|
||||||
of 3:
|
of 3:
|
||||||
result = semTypeNode(c, n[1], prev)
|
result = semTypeNode(c, n[1], prev)
|
||||||
if result.skipTypes({tyGenericInst, tyAlias, tySink, tyOwned}).kind in NilableTypes+GenericTypes+{tyForward} and
|
if result.kind == tyTypeDesc and tfUnresolved notin result.flags:
|
||||||
n[2].kind == nkNilLit:
|
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 = freshType(result, prev)
|
||||||
result.flags.incl(tfNotNil)
|
result.flags.incl(tfNotNil)
|
||||||
if notnil notin c.features:
|
|
||||||
localError(c.config, n.info, "enable the 'not nil' annotation with {.experimental: \"notnil\".}")
|
|
||||||
else:
|
else:
|
||||||
localError(c.config, n.info, errGenerated, "invalid type")
|
localError(c.config, n.info, errGenerated, "invalid type")
|
||||||
of 2:
|
of 2:
|
||||||
|
|
|
||||||
|
|
@ -267,4 +267,43 @@ block:
|
||||||
of C: r: range[1..1] # DateTime
|
of C: r: range[1..1] # DateTime
|
||||||
|
|
||||||
# Fine to not initialize 'r' because this is implicitly initialized and known to be branch 'A'.
|
# 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
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue