Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

{.emit.} for types is too late: Nim uses them before their declaration #16664

Closed
mratsim opened this issue Jan 10, 2021 · 5 comments
Closed

{.emit.} for types is too late: Nim uses them before their declaration #16664

mratsim opened this issue Jan 10, 2021 · 5 comments

Comments

@mratsim
Copy link
Collaborator

mratsim commented Jan 10, 2021

I'm trying to wrap a C++ library and I need to resort to {.emit.} to workaround the absence of default field value for Nim and the C++ backend (#4687)

Unfortunately Nim generates types before they were emitted.

Test case:

{.push header: "<memory>".}
type
  CppSharedPtr* {.importcpp: "std::shared_ptr", bycopy.} [T] = object

func make_shared*(T: typedesc): CppSharedPtr[T] {.importcpp: "std::make_shared<'*0>()".}
func deref*[T](p: CppSharedPtr[T]): var T {.importcpp: "(* #)".}
{.pop.}

{.passC: "-Wfatal-errors".} # Only block at the first issue


{.emit: ["""
#include <memory>

struct Base {
  Base() = delete;
};

struct Derived: public Base {
  std::shared_ptr<Base> f1{nullptr};
  std::shared_ptr<Base> f2{nullptr};
};

"""].}

type
  Base {.pure, inheritable, importcpp.} = object
  Derived {.inheritable, importcpp.} = object of Base
    f1: CppSharedPtr[Base]
    f2: CppSharedPtr[Base]

proc main() =
  let derived = make_shared(Derived)

main()

The code generated (with danger) is:

/* section: NIM_merge_HEADERS */

#include "nimbase.h"
#include <memory>
#undef LANGUAGE_C
#undef MIPSEB
#undef MIPSEL
#undef PPC
#undef R3000
#undef R4000
#undef i386
#undef linux
#undef mips
#undef near
#undef far
#undef powerpc
#undef unix

/* section: NIM_merge_FRAME_DEFINES */
#define nimfr_(x, y)
#define nimln_(x, y)

/* section: NIM_merge_TYPES */
typedef std::shared_ptr<Derived>  TY__r4mQCt9bhZT6EjBvshZiuCw; // <-------- Generated before the emitted type !!!

/* section: NIM_merge_PROC_HEADERS */
#include <memory>

struct Base {
  Base() = delete;
};

struct Derived: public Base {
  std::shared_ptr<Base> f1{nullptr};
  std::shared_ptr<Base> f2{nullptr};
};


N_LIB_PRIVATE N_NIMCALL(void, main__8FadbF9aXJUyuO9aYZ4NjXHQ)(void);
static N_INLINE(void, initStackBottomWith)(void* locals);
N_LIB_PRIVATE N_NOINLINE(void, nimGC_setStackBottom)(void* theStackBottom);
N_LIB_PRIVATE N_NIMCALL(void, systemDatInit000)(void);
N_LIB_PRIVATE N_NIMCALL(void, systemInit000)(void);
N_LIB_PRIVATE N_NIMCALL(void, NimMainModule)(void);

Note that if you remove the importcpp (in the same code), a forward declaration section appears NIM_merge_FORWARD_TYPES

/* section: NIM_merge_HEADERS */

#include "nimbase.h"
#include <memory>
#undef LANGUAGE_C
#undef MIPSEB
#undef MIPSEL
#undef PPC
#undef R3000
#undef R4000
#undef i386
#undef linux
#undef mips
#undef near
#undef far
#undef powerpc
#undef unix

/* section: NIM_merge_FRAME_DEFINES */
#define nimfr_(x, y)
#define nimln_(x, y)

/* section: NIM_merge_FORWARD_TYPES */ # <--------------- This is new !
struct tyObject_Derived__Xjo9b4GYxjCDKcoIkW13VSg;
struct tyObject_Base__lHJ509cStVLqAdHwmmNfOsw;

/* section: NIM_merge_TYPES */
struct tyObject_Base__lHJ509cStVLqAdHwmmNfOsw {
char dummy;
};
typedef std::shared_ptr<tyObject_Base__lHJ509cStVLqAdHwmmNfOsw>  TY__Ib9bHVVFSEeJD7L58LFE9aDw;
struct tyObject_Derived__Xjo9b4GYxjCDKcoIkW13VSg : public tyObject_Base__lHJ509cStVLqAdHwmmNfOsw {
TY__Ib9bHVVFSEeJD7L58LFE9aDw f1;
TY__Ib9bHVVFSEeJD7L58LFE9aDw f2;
};
typedef std::shared_ptr<tyObject_Derived__Xjo9b4GYxjCDKcoIkW13VSg>  TY__r4mQCt9bhZT6EjBvshZiuCw;

/* section: NIM_merge_PROC_HEADERS */
#include <memory>

struct Base {
  Base() = delete;
};

struct Derived: public Base {
  std::shared_ptr<Base> f1{nullptr};
  std::shared_ptr<Base> f2{nullptr};
};

Is there a way to notify importcpp that a type is defined in the same module and so need forward declaration?

@alaviss
Copy link
Collaborator

alaviss commented Jan 10, 2021

Isn't there a special emit comment /** TYPESECTION **/ to do that or something?

@alaviss
Copy link
Collaborator

alaviss commented Jan 10, 2021

See this for an example in the testsuite:

{.emit: """/*TYPESECTION*/
template <int N, class T>
struct GenericIntType {
T data[N];
};
template <class T>
struct GenericTType {
T field;
};
struct SimpleStruct {
int field;
};
""" .}

@mratsim
Copy link
Collaborator Author

mratsim commented Jan 10, 2021

Indeed, apparently it hooks there https://github.com/nim-lang/Nim/blob/acf3715/compiler/ccgstmts.nim#L1489-L1495
so there is also includes and var.

Is there a plan to document that or maybe have specialised emitVar, emitInclude, emitType templates?

@cooldome
Copy link
Member

@timotheecour
Copy link
Member

see also #13953

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants