diff options
-rw-r--r-- | changelog.md | 4 | ||||
-rw-r--r-- | compiler/ccgtypes.nim | 5 | ||||
-rw-r--r-- | compiler/semstmts.nim | 15 | ||||
-rw-r--r-- | doc/manual/types.txt | 38 | ||||
-rw-r--r-- | tests/package_level_objects/definefoo.nim | 3 | ||||
-rw-r--r-- | tests/package_level_objects/mypackage.nimble | 0 | ||||
-rw-r--r-- | tests/package_level_objects/tusefoo.nim | 16 | ||||
-rw-r--r-- | tests/package_level_objects/tusefoo2.nim | 19 | ||||
-rw-r--r-- | todo.txt | 4 |
9 files changed, 93 insertions, 11 deletions
diff --git a/changelog.md b/changelog.md index 640cb98c5..ebd454ab7 100644 --- a/changelog.md +++ b/changelog.md @@ -16,3 +16,7 @@ module. - The overloading rules changed slightly so that constrained generics are 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. diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 76d0c0158..c5fc67fb0 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -981,7 +981,10 @@ proc genTypeInfoAux(m: BModule, typ, origType: PType, name: Rope; if sonsLen(typ) > 0 and typ.lastSon != nil: var x = typ.lastSon if typ.kind == tyObject: x = x.skipTypes(skipPtrs) - base = genTypeInfo(m, x, info) + if typ.kind == tyPtr and x.kind == tyObject and incompleteType(x): + base = rope("0") + else: + base = genTypeInfo(m, x, info) else: base = rope("0") genTypeInfoAuxBase(m, typ, origType, name, base, info) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index e2ee9ac67..8ec93cacd 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -796,12 +796,6 @@ proc typeSectionLeftSidePass(c: PContext, n: PNode) = else: localError(name.info, typsym.name.s & " is not a type that can be forwarded") 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: s = semIdentDef(c, name, skType) s.typ = newTypeS(tyForward, c) @@ -812,11 +806,16 @@ proc typeSectionLeftSidePass(c: PContext, n: PNode) = # check if the symbol already exists: let pkg = c.module.owner 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: let typsym = pkg.tab.strTableGet(s.name) if typsym != nil: - typeCompleted(typsym) + if sfForward notin typsym.flags or sfNoForward notin typsym.flags: + 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 # add it here, so that recursive types are possible: if sfGenSym notin s.flags: addInterfaceDecl(c, s) diff --git a/doc/manual/types.txt b/doc/manual/types.txt index 7cb4a8b8a..4d66bf664 100644 --- a/doc/manual/types.txt +++ b/doc/manual/types.txt @@ -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 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 -------- diff --git a/tests/package_level_objects/definefoo.nim b/tests/package_level_objects/definefoo.nim new file mode 100644 index 000000000..36576ab59 --- /dev/null +++ b/tests/package_level_objects/definefoo.nim @@ -0,0 +1,3 @@ +type + Foo* {.package.} = object + x, y: int diff --git a/tests/package_level_objects/mypackage.nimble b/tests/package_level_objects/mypackage.nimble new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/tests/package_level_objects/mypackage.nimble diff --git a/tests/package_level_objects/tusefoo.nim b/tests/package_level_objects/tusefoo.nim new file mode 100644 index 000000000..f9bae9545 --- /dev/null +++ b/tests/package_level_objects/tusefoo.nim @@ -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 diff --git a/tests/package_level_objects/tusefoo2.nim b/tests/package_level_objects/tusefoo2.nim new file mode 100644 index 000000000..be6b3fcda --- /dev/null +++ b/tests/package_level_objects/tusefoo2.nim @@ -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 diff --git a/todo.txt b/todo.txt index b0ea449f4..97e749e9e 100644 --- a/todo.txt +++ b/todo.txt @@ -1,8 +1,8 @@ version 1.0 battle plan ======================= -- implement a way to forward object type declarations across module - boundaries; C++ style +- make nimresolve part of the Nim compiler and add support for + 'import staticExec()' - deprecate unary '<' - remove 'mod x' type rule - implement x[^1] differently, no compiler magic |