Skip to content

importc types: CT sizeof, $, {.sizeof.}, const CVAR {.importc.}: int #205

@timotheecour

Description

@timotheecour

proposal 1 would allow CT sizeof for specified importc types
proposal 2 is just cleanup (related to proposal 1)
proposal 4 would fix $ and repr for importc types
proposal 5 would avoid having to hardcode macro constants defined in C headers (which is brittle and not cross platform), and make them available at CT in a reliable way

proposal 3 is just sugar

proposal 1: introduce {.sizeof.} for importc types

  • introduce {.sizeof.} pragma for importc types, with following meaning:
type Foo1 {.importc.} = object
  x1: cint
const s1 = sizeof(Foo1) # CT error: Foo1 is importc and either incomplete or needs to be annotated with sizeof

type Foo2 {.importc, sizeof.} = object
  x1: cint # all fields are specicied
  c2: cstring
const s2 = sizeof(Foo2) # works

type Foo3 {.importc, sizeof: s2 .} = object
  x1: cint  # some fields may be missing but sizeof is specified
static: doAssert sizeof(Foo3) == s2
  • Note: there should be zero additional code to compute CT sizeof for importc types, it's just the regular CT sizeof except that we remove a CT error depending on presence of {.sizeof.}

  • we add a CT check in codegen to check for errors (eg if some header is updated and fields are added), using NIM_STATIC_ASSERT C/C++ macro introduced in fix some codegen bugs: NIM_BOOL, NIM_STATIC_ASSERT, --passc:-std=... (etc) Nim#13798:
    codegen inserts NIM_STATIC_ASSERT(sizeof(Foo1) == computedSizeofFoo1) right after the header where it's define (or right after its declaration if no header).

proposal 2: deprecate incompleteStruct

[EDIT: this needs to be double checked]
deprecate {.incompleteStruct.} (introduced in 2012), it's the wrong default, it's not observed (AFAIK) as evidenced by face only few structs are annotated with it. It's also un-safe as a lack of annotation would mean a struct is complete, which is often wrong.

proposal 3: {.importc.} for ref/ptr object types

introduce type Foo {.importc.} = ref object, with the meaning that the importc applies to the object, not the ref, to avoid the common boilerplate:

type TFoo {.importc.} = object
  x1: cint
  c2: cstring
type Foo = ref TFoo

where TFoo doesn't need to be declared.
ditto with ptr.

I don't think there's any use case otherwise for {.importc.} applying to the ref or ptr directly, since a pointer is just a pointer.

proposal 4: {.hidden.} for importc fields that shouldn't be referenced by name in codegen

(previous version of this proposal called it {.importc:"?".})
this is an alternative approach to nim-lang/Nim#13783 for cases where some field names (eg pad, reserved etc) are un-reliable because they're OS-specific, and not part of stable ABI. eg:

# /usr/include/x86_64-linux-gnu/bits/stat.h
struct stat
  {
...
#ifdef __x86_64__
    __syscall_slong_t __glibc_reserved[3];
#else
# ifndef __USE_FILE_OFFSET64
    unsigned long int __glibc_reserved4;
    unsigned long int __glibc_reserved5;
# else
    __ino64_t st_ino;                   /* File serial number.  */
# endif
#endif
  };
type stat {.importc: "stat", header = "<stat.h>".} =
  foo1: cint
  reserved {.hidden}: array[4, byte]
  foo2: cstring

proposal 5: const CVAR {.importc.}: int

the idea is to replace hardcoded values obtained from reading header files (which may be unreliable, not cross platform, and even wrong if user passes some other --passc option affecting some symbols) by simply calling preprocessor:

const
  DARWIN_MAXPATHLEN = 1024

by:

const
  DARWIN_MAXPATHLEN {.importc: "__DARWIN_MAXPATHLEN", header: "<dirent.h>".}: int
static: doAssert DARWIN_MAXPATHLEN == 1024 # this would pass

I've implemented this in a POC and it seems to work, but no PR yet.
It is efficient since I'm caching header files and preprocessor output.
EDIT: yet another example use case: nim-lang/Nim#16459 (comment)

Note: regardless of whether we use const CVAR {.importc.}: int, codegen could validate the value we get via NIM_STATIC_ASSERT

Note: this doesn't require libffi

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions