CT sizeof(+friends) for {.importc, completeStruct.} types, enable ABI static checks (#13926)
* -d:checkabi obsolete (ABI check now enforced); add `addTypeHeader` helper
* cleanups
* import sizeof at CT for {.completeType.}
* address comments; revert default enabling of -d:checkAbi for now
* mimportc_size_check.nim => msizeof5.nim; merge mabi_check.nim into msizeof5.nim; refactor
* all pragmas in errmsgs should be written: '.importc' (un-ambiguous and less verbose than {.importc.})
This commit is contained in:
parent
5c534b2943
commit
66db9de714
14 changed files with 221 additions and 39 deletions
131
tests/misc/msizeof5.nim
Normal file
131
tests/misc/msizeof5.nim
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
## tests for -d:checkAbi used by addAbiCheck via NIM_STATIC_ASSERT
|
||||
|
||||
{.emit:"""/*TYPESECTION*/
|
||||
struct Foo1{
|
||||
int a;
|
||||
};
|
||||
struct Foo2{
|
||||
int a;
|
||||
};
|
||||
enum Foo3{k1, k2};
|
||||
typedef enum Foo3 Foo3b;
|
||||
typedef enum Foo4{k3, k4} Foo4;
|
||||
|
||||
typedef int Foo5[3];
|
||||
|
||||
typedef struct Foo6{
|
||||
int a1;
|
||||
bool a2;
|
||||
double a3;
|
||||
struct Foo6* a4;
|
||||
} Foo6;
|
||||
""".}
|
||||
|
||||
template ensureCgen(T: typedesc) =
|
||||
## ensures cgen
|
||||
var a {.volatile.}: T
|
||||
|
||||
block:
|
||||
type Foo1Alias{.importc: "struct Foo1", size: sizeof(cint).} = object
|
||||
a: cint
|
||||
ensureCgen Foo1Alias
|
||||
|
||||
block:
|
||||
type Foo3Alias{.importc: "enum Foo3", size: sizeof(cint).} = enum
|
||||
k1, k2
|
||||
ensureCgen Foo3Alias
|
||||
|
||||
block:
|
||||
type Foo3bAlias{.importc: "Foo3b", size: sizeof(cint).} = enum
|
||||
k1, k2
|
||||
ensureCgen Foo3bAlias
|
||||
|
||||
block:
|
||||
type Foo3b{.importc, size: sizeof(cint).} = enum
|
||||
k1, k2
|
||||
ensureCgen Foo3b
|
||||
static:
|
||||
doAssert Foo3b.sizeof == cint.sizeof
|
||||
|
||||
block:
|
||||
type Foo4{.importc, size: sizeof(cint).} = enum
|
||||
k3, k4
|
||||
# adding entries should not yield duplicate ABI checks, as enforced by
|
||||
# `typeABICache`.
|
||||
# Currently the test doesn't check for this but you can inspect the cgen'd file
|
||||
ensureCgen Foo4
|
||||
ensureCgen Foo4
|
||||
ensureCgen Foo4
|
||||
|
||||
block:
|
||||
type Foo5{.importc.} = array[3, cint]
|
||||
ensureCgen Foo5
|
||||
|
||||
block:
|
||||
type Foo5{.importc.} = array[3, cint]
|
||||
ensureCgen Foo5
|
||||
|
||||
block: # CT sizeof
|
||||
type Foo6GT = object # grountruth
|
||||
a1: cint
|
||||
a2: bool
|
||||
a3: cfloat
|
||||
a4: ptr Foo6GT
|
||||
|
||||
type Foo6{.importc, completeStruct.} = object
|
||||
a1: cint
|
||||
a2: bool
|
||||
a3: cfloat
|
||||
a4: ptr Foo6
|
||||
|
||||
static: doAssert compiles(static(Foo6.sizeof))
|
||||
static: doAssert Foo6.sizeof == Foo6GT.sizeof
|
||||
static: doAssert (Foo6, int, array[2, Foo6]).sizeof ==
|
||||
(Foo6GT, int, array[2, Foo6GT]).sizeof
|
||||
|
||||
block:
|
||||
type GoodImportcType {.importc: "signed char", nodecl.} = char
|
||||
# "good" in sense the sizeof will match
|
||||
ensureCgen GoodImportcType
|
||||
|
||||
block:
|
||||
type Foo6{.importc.} = object
|
||||
a1: cint
|
||||
doAssert compiles(Foo6.sizeof)
|
||||
static: doAssert not compiles(static(Foo6.sizeof))
|
||||
|
||||
when defined caseBad:
|
||||
# Each case below should give a static cgen assert fail message
|
||||
|
||||
block:
|
||||
type BadImportcType {.importc: "unsigned char", nodecl.} = uint64
|
||||
# "sizeof" check will fail
|
||||
ensureCgen BadImportcType
|
||||
|
||||
block:
|
||||
type Foo2AliasBad{.importc: "struct Foo2", size: 1.} = object
|
||||
a: cint
|
||||
ensureCgen Foo2AliasBad
|
||||
|
||||
block:
|
||||
type Foo5{.importc.} = array[4, cint]
|
||||
ensureCgen Foo5
|
||||
|
||||
block:
|
||||
type Foo5{.importc.} = array[3, bool]
|
||||
ensureCgen Foo5
|
||||
|
||||
block:
|
||||
type Foo6{.importc, completeStruct.} = object
|
||||
a1: cint
|
||||
# a2: bool # missing this should trigger assert fail
|
||||
a3: cfloat
|
||||
a4: ptr Foo6
|
||||
ensureCgen Foo6
|
||||
|
||||
when false:
|
||||
block:
|
||||
# pre-existing BUG: this should give a CT error in semcheck because `size`
|
||||
# disagrees with `array[3, cint]`
|
||||
type Foo5{.importc, size: 1.} = array[3, cint]
|
||||
ensureCgen Foo5
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
discard """
|
||||
errormsg: "cannot evaluate 'sizeof' because its type is not defined completely"
|
||||
errormsg: "'sizeof' requires '.importc' types to be '.completeStruct'"
|
||||
line: 9
|
||||
"""
|
||||
|
||||
|
|
|
|||
|
|
@ -19,14 +19,10 @@ proc runCmd(file, options = ""): auto =
|
|||
echo result[0]
|
||||
echo result[1]
|
||||
|
||||
proc testCodegenStaticAssert() =
|
||||
let (output, exitCode) = runCmd("ccgbugs/mstatic_assert.nim")
|
||||
doAssert "sizeof(bool) == 2" in output
|
||||
doAssert exitCode != 0
|
||||
|
||||
proc testCTFFI() =
|
||||
let (output, exitCode) = runCmd("vm/mevalffi.nim", "--experimental:compiletimeFFI")
|
||||
let expected = """
|
||||
when defined(nimHasLibFFIEnabled):
|
||||
block: # mevalffi
|
||||
let (output, exitCode) = runCmd("vm/mevalffi.nim", "--experimental:compiletimeFFI")
|
||||
let expected = """
|
||||
hello world stderr
|
||||
hi stderr
|
||||
foo
|
||||
|
|
@ -37,10 +33,30 @@ foo:102:103:104
|
|||
foo:0.03:asdf:103:105
|
||||
ret={s1:foobar s2:foobar age:25 pi:3.14}
|
||||
"""
|
||||
doAssert output == expected, output
|
||||
doAssert exitCode == 0
|
||||
doAssert output == expected, output
|
||||
doAssert exitCode == 0
|
||||
|
||||
when defined(nimHasLibFFIEnabled):
|
||||
testCTFFI()
|
||||
else: # don't run twice the same test
|
||||
testCodegenStaticAssert()
|
||||
template check(msg) = doAssert msg in output, output
|
||||
|
||||
block: # mstatic_assert
|
||||
let (output, exitCode) = runCmd("ccgbugs/mstatic_assert.nim", "-d:caseBad")
|
||||
check "sizeof(bool) == 2"
|
||||
doAssert exitCode != 0
|
||||
|
||||
block: # ABI checks
|
||||
let file = "misc/msizeof5.nim"
|
||||
block:
|
||||
let (output, exitCode) = runCmd(file, "-d:checkAbi")
|
||||
doAssert exitCode == 0, output
|
||||
block:
|
||||
let (output, exitCode) = runCmd(file, "-d:checkAbi -d:caseBad")
|
||||
# on platforms that support _StaticAssert natively, errors will show full context, eg:
|
||||
# error: static_assert failed due to requirement 'sizeof(unsigned char) == 8'
|
||||
# "backend & Nim disagree on size for: BadImportcType{int64} [declared in mabi_check.nim(1, 6)]"
|
||||
check "sizeof(unsigned char) == 8"
|
||||
check "sizeof(struct Foo2) == 1"
|
||||
check "sizeof(Foo5) == 16"
|
||||
check "sizeof(Foo5) == 3"
|
||||
check "sizeof(struct Foo6) == "
|
||||
doAssert exitCode != 0
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue