new feature: package level objects
This commit is contained in:
parent
7889692523
commit
c17f6c7837
9 changed files with 93 additions and 11 deletions
|
|
@ -16,3 +16,7 @@
|
||||||
module.
|
module.
|
||||||
- The overloading rules changed slightly so that constrained generics are
|
- The overloading rules changed slightly so that constrained generics are
|
||||||
preferred over unconstrained generics. (Bug #6526)
|
preferred over unconstrained generics. (Bug #6526)
|
||||||
|
- It is now possible to forward declare object types so that mutually
|
||||||
|
recursive types can be created across module boundaries. See
|
||||||
|
[package level objects](https://nim-lang.org/docs/manual.html#package-level-objects)
|
||||||
|
for more information.
|
||||||
|
|
|
||||||
|
|
@ -981,6 +981,9 @@ proc genTypeInfoAux(m: BModule, typ, origType: PType, name: Rope;
|
||||||
if sonsLen(typ) > 0 and typ.lastSon != nil:
|
if sonsLen(typ) > 0 and typ.lastSon != nil:
|
||||||
var x = typ.lastSon
|
var x = typ.lastSon
|
||||||
if typ.kind == tyObject: x = x.skipTypes(skipPtrs)
|
if typ.kind == tyObject: x = x.skipTypes(skipPtrs)
|
||||||
|
if typ.kind == tyPtr and x.kind == tyObject and incompleteType(x):
|
||||||
|
base = rope("0")
|
||||||
|
else:
|
||||||
base = genTypeInfo(m, x, info)
|
base = genTypeInfo(m, x, info)
|
||||||
else:
|
else:
|
||||||
base = rope("0")
|
base = rope("0")
|
||||||
|
|
|
||||||
|
|
@ -796,12 +796,6 @@ proc typeSectionLeftSidePass(c: PContext, n: PNode) =
|
||||||
else:
|
else:
|
||||||
localError(name.info, typsym.name.s & " is not a type that can be forwarded")
|
localError(name.info, typsym.name.s & " is not a type that can be forwarded")
|
||||||
s = typsym
|
s = typsym
|
||||||
when false:
|
|
||||||
s = qualifiedLookUp(c, name, {checkUndeclared, checkModule})
|
|
||||||
if s.kind != skType or
|
|
||||||
s.typ.skipTypes(abstractPtrs).kind != tyObject or
|
|
||||||
tfPartial notin s.typ.skipTypes(abstractPtrs).flags:
|
|
||||||
localError(name.info, "only .partial objects can be extended")
|
|
||||||
else:
|
else:
|
||||||
s = semIdentDef(c, name, skType)
|
s = semIdentDef(c, name, skType)
|
||||||
s.typ = newTypeS(tyForward, c)
|
s.typ = newTypeS(tyForward, c)
|
||||||
|
|
@ -812,11 +806,16 @@ proc typeSectionLeftSidePass(c: PContext, n: PNode) =
|
||||||
# check if the symbol already exists:
|
# check if the symbol already exists:
|
||||||
let pkg = c.module.owner
|
let pkg = c.module.owner
|
||||||
if not isTopLevel(c) or pkg.isNil:
|
if not isTopLevel(c) or pkg.isNil:
|
||||||
localError(name.info, "only top level types in a package can be 'forward'")
|
localError(name.info, "only top level types in a package can be 'package'")
|
||||||
else:
|
else:
|
||||||
let typsym = pkg.tab.strTableGet(s.name)
|
let typsym = pkg.tab.strTableGet(s.name)
|
||||||
if typsym != nil:
|
if typsym != nil:
|
||||||
|
if sfForward notin typsym.flags or sfNoForward notin typsym.flags:
|
||||||
typeCompleted(typsym)
|
typeCompleted(typsym)
|
||||||
|
typsym.info = s.info
|
||||||
|
else:
|
||||||
|
localError(name.info, "cannot complete type '" & s.name.s & "' twice; " &
|
||||||
|
"previous type completion was here: " & $typsym.info)
|
||||||
s = typsym
|
s = typsym
|
||||||
# add it here, so that recursive types are possible:
|
# add it here, so that recursive types are possible:
|
||||||
if sfGenSym notin s.flags: addInterfaceDecl(c, s)
|
if sfGenSym notin s.flags: addInterfaceDecl(c, s)
|
||||||
|
|
|
||||||
|
|
@ -698,6 +698,44 @@ branch switch ``system.reset`` has to be used. Also, when the fields of a
|
||||||
particular branch are specified during object construction, the correct value
|
particular branch are specified during object construction, the correct value
|
||||||
for the discriminator must be supplied at compile-time.
|
for the discriminator must be supplied at compile-time.
|
||||||
|
|
||||||
|
Package level objects
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
Every Nim module resides in a (nimble) package. An object type can be attached
|
||||||
|
to the package it resides in. If that is done, the type can be referenced from
|
||||||
|
other modules as an `incomplete`:idx: object type. This features allows to
|
||||||
|
break up recursive type dependencies accross module boundaries. Incomplete
|
||||||
|
object types are always passed ``byref`` and can only be used in pointer like
|
||||||
|
contexts (``var/ref/ptr IncompleteObject``) in general since the compiler does
|
||||||
|
not yet know the size of the object. To complete an incomplete object
|
||||||
|
the ``package`` pragma has to be used. ``package`` implies ``byref``.
|
||||||
|
|
||||||
|
As long as a type ``T`` is incomplete ``sizeof(T)`` or "runtime type
|
||||||
|
information" for ``T`` is not available.
|
||||||
|
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
.. code-block:: nim
|
||||||
|
|
||||||
|
# module A (in an arbitrary package)
|
||||||
|
type
|
||||||
|
Pack.SomeObject = object ## declare as incomplete object of package 'Pack'
|
||||||
|
Triple = object
|
||||||
|
a, b, c: ref SomeObject ## pointers to incomplete objects are allowed
|
||||||
|
|
||||||
|
## Incomplete objects can be used as parameters:
|
||||||
|
proc myproc(x: SomeObject) = discard
|
||||||
|
|
||||||
|
|
||||||
|
.. code-block:: nim
|
||||||
|
|
||||||
|
# module B (in package "Pack")
|
||||||
|
type
|
||||||
|
SomeObject* {.package.} = object ## Use 'package' to complete the object
|
||||||
|
s, t: string
|
||||||
|
x, y: int
|
||||||
|
|
||||||
|
|
||||||
Set type
|
Set type
|
||||||
--------
|
--------
|
||||||
|
|
|
||||||
3
tests/package_level_objects/definefoo.nim
Normal file
3
tests/package_level_objects/definefoo.nim
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
type
|
||||||
|
Foo* {.package.} = object
|
||||||
|
x, y: int
|
||||||
0
tests/package_level_objects/mypackage.nimble
Normal file
0
tests/package_level_objects/mypackage.nimble
Normal file
16
tests/package_level_objects/tusefoo.nim
Normal file
16
tests/package_level_objects/tusefoo.nim
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
discard """
|
||||||
|
output: '''@[(x: 3, y: 4)]'''
|
||||||
|
"""
|
||||||
|
|
||||||
|
type
|
||||||
|
mypackage.Foo = object
|
||||||
|
Other = proc (inp: Foo)
|
||||||
|
|
||||||
|
import definefoo
|
||||||
|
|
||||||
|
# after this import, Foo is a completely resolved type, so
|
||||||
|
# we can create a sequence of it:
|
||||||
|
var s: seq[Foo] = @[]
|
||||||
|
|
||||||
|
s.add Foo(x: 3, y: 4)
|
||||||
|
echo s
|
||||||
19
tests/package_level_objects/tusefoo2.nim
Normal file
19
tests/package_level_objects/tusefoo2.nim
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
discard """
|
||||||
|
output: '''compiles'''
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Test that the object type does not need to be resolved at all:
|
||||||
|
|
||||||
|
type
|
||||||
|
mypackage.Foo = object
|
||||||
|
Other = proc (inp: Foo)
|
||||||
|
|
||||||
|
Node = ref object
|
||||||
|
external: ptr Foo
|
||||||
|
data: string
|
||||||
|
|
||||||
|
var x: Node
|
||||||
|
new(x)
|
||||||
|
x.data = "compiles"
|
||||||
|
|
||||||
|
echo x.data
|
||||||
4
todo.txt
4
todo.txt
|
|
@ -1,8 +1,8 @@
|
||||||
version 1.0 battle plan
|
version 1.0 battle plan
|
||||||
=======================
|
=======================
|
||||||
|
|
||||||
- implement a way to forward object type declarations across module
|
- make nimresolve part of the Nim compiler and add support for
|
||||||
boundaries; C++ style
|
'import staticExec()'
|
||||||
- deprecate unary '<'
|
- deprecate unary '<'
|
||||||
- remove 'mod x' type rule
|
- remove 'mod x' type rule
|
||||||
- implement x[^1] differently, no compiler magic
|
- implement x[^1] differently, no compiler magic
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue