mirror of
https://github.com/nim-lang/Nim.git
synced 2026-04-06 07:38:24 +00:00
This PR allows passing the defining type to generic types in the right
side in a type definition like this:
```nim
type
Foo = object
x: Option[Foo]
```
I think generic types should be instanciated after all given arguments
are semchecked,
because generic types can access information about them.
(for example, `Option[T]` in std/option checks if `T` is a pointer like
type)
But in this case, need to instanciate `Option[Foo]` before type of
`Foo.x` is determined.
This commit is contained in:
@@ -202,7 +202,11 @@ type
|
||||
tySequence,
|
||||
tyProc,
|
||||
tyPointer, tyOpenArray,
|
||||
tyString, tyCstring, tyForward,
|
||||
tyString, tyCstring,
|
||||
tyForward,
|
||||
# a type not yet semchecked
|
||||
# When semcheck a type section, all types defined in it are initialized to tyForward
|
||||
|
||||
tyInt, tyInt8, tyInt16, tyInt32, tyInt64, # signed integers
|
||||
tyFloat, tyFloat32, tyFloat64, tyFloat128,
|
||||
tyUInt, tyUInt8, tyUInt16, tyUInt32, tyUInt64,
|
||||
|
||||
@@ -1739,6 +1739,7 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
|
||||
var isConcrete = true
|
||||
let rType = m.call[0].typ
|
||||
let mIndex = if rType != nil: rType.len - 1 else: -1
|
||||
var hasForwardTypeParam = false
|
||||
for i in 1..<m.call.len:
|
||||
var typ = m.call[i].typ
|
||||
# is this a 'typedesc' *parameter*? If so, use the typedesc type,
|
||||
@@ -1755,13 +1756,36 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
|
||||
skip = false
|
||||
addToResult(typ, skip)
|
||||
|
||||
if typ.kind == tyForward:
|
||||
hasForwardTypeParam = true
|
||||
|
||||
if isConcrete:
|
||||
if s.ast == nil and s.typ.kind != tyCompositeTypeClass:
|
||||
# XXX: What kind of error is this? is it still relevant?
|
||||
localError(c.config, n.info, errCannotInstantiateX % s.name.s)
|
||||
result = newOrPrevType(tyError, prev, c)
|
||||
elif containsGenericInvocationWithForward(n[0]):
|
||||
elif containsGenericInvocationWithForward(n[0]) or hasForwardTypeParam:
|
||||
# isConcrete == false means this generic type is not instanciated here because it invoked with generic parameters.
|
||||
# Even if isConcrete == true, don't instanciate it now if there are any `tyForward` type params.
|
||||
# Such `tyForward` type params will be semchecked later and we can instanciate this next time.
|
||||
# Some generic types like std/options.Option[T] needs a type kinds of the given type argument.
|
||||
|
||||
# return `tyForward` instead of `tyGenericInvocation` because:
|
||||
# ```nim
|
||||
# type Foo = object
|
||||
# x: Option[Foo]
|
||||
# ```
|
||||
# returning `tyGenericInvocation` makes `Option[Foo]` to `tyGenericInvocation` and
|
||||
# next time `semGeneric` is called with `Option[Foo]`, containsGenericType(typeof(`Foo`)) == true
|
||||
# and `isConcrete == false`.
|
||||
if prev == nil:
|
||||
result = newTypeS(tyForward, c)
|
||||
result.sym = s
|
||||
else:
|
||||
assignType(result, newTypeS(tyForward, c))
|
||||
result.sym = s
|
||||
c.forwardTypeUpdates.add (result, n) #fixes 1500
|
||||
return
|
||||
else:
|
||||
result = instGenericContainer(c, n.info, result,
|
||||
allowMetaTypes = false)
|
||||
|
||||
48
tests/generics/tself_type.nim
Normal file
48
tests/generics/tself_type.nim
Normal file
@@ -0,0 +1,48 @@
|
||||
# issue 16754
|
||||
|
||||
type
|
||||
Opt[T] = object
|
||||
when T is ref:
|
||||
val: T
|
||||
x: int
|
||||
else:
|
||||
val: T
|
||||
x: string
|
||||
|
||||
type
|
||||
Foo = ref object
|
||||
x: Opt[Foo]
|
||||
|
||||
Bar = object
|
||||
x: ref Opt[Bar]
|
||||
|
||||
var f = Foo()
|
||||
assert f.x.x is int
|
||||
var b = Bar()
|
||||
assert b.x.x is string
|
||||
|
||||
type
|
||||
BazG[T] = object
|
||||
x: int
|
||||
|
||||
BazGRef[T] = ref object
|
||||
x: T
|
||||
|
||||
Baz = object
|
||||
x: Opt[BazG[Baz]]
|
||||
y: Opt[BazGRef[Baz]]
|
||||
|
||||
var z = Baz()
|
||||
assert z.x.x is string
|
||||
assert z.y.x is int
|
||||
|
||||
import options
|
||||
|
||||
type
|
||||
Person = ref object
|
||||
parent: Option[Person]
|
||||
|
||||
proc newPerson(parent: Option[Person]): Person =
|
||||
Person(parent: parent)
|
||||
|
||||
var person = newPerson(none(Person))
|
||||
Reference in New Issue
Block a user