Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/workflows/cbindgen.yml
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ jobs:
python -m pip install --upgrade pip wheel
pip install Cython==3.0.2

- name: Install C23 compiler
run: sudo apt-get update && sudo apt-get install -y gcc-14

- name: Build
run: |
cargo +stable build --verbose
Expand All @@ -102,6 +105,7 @@ jobs:
- name: Test package
env:
CBINDGEN_TEST_VERIFY: 1
CC: gcc-14
run: |
cargo +stable package --verbose
(cd target/package/cbindgen-$(cargo +stable run -- --version | cut -d ' ' -f 2) && cargo +stable test --verbose)
Expand All @@ -114,6 +118,7 @@ jobs:
- name: Test
env:
CBINDGEN_TEST_VERIFY: 1
CC: gcc-14
run: |
cargo +nightly test --verbose

Expand Down
18 changes: 18 additions & 0 deletions docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -1033,6 +1033,24 @@ allow_static_const = true
# default: true
allow_constexpr = false

# Whether supported primitive constants should be typed constexpr objects in C.
# Requires a C23 compiler. Independent of the C++ allow_constexpr and
# allow_static_const settings; does not affect C++ or Cython output.
#
# Supports integer, floating-point, boolean and character constants, using
# the same initializer rendering as C++. Explicit conversions to the declared
# type satisfy C23's exact-representability requirement. The generated
# initializer must be a valid C23 constant expression; compiler diagnostics
# apply to unsupported expressions and overflowing intermediate values.
#
# Pointers, arrays, aggregates and aliases retain their existing macro output.
#
# Unlike macros, constexpr objects cannot be used in preprocessor #if/#ifdef
# expressions. Enable this only when consumers use these constants in C code.
#
# default: false
allow_constexpr_in_c = false

# This rule specifies the order in which constants will be sorted.
#
# "Name": sort by the name of the constant
Expand Down
4 changes: 4 additions & 0 deletions src/bindgen/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -680,6 +680,9 @@ pub struct ConstantConfig {
pub allow_static_const: bool,
/// Whether a generated constant should be constexpr in C++ mode.
pub allow_constexpr: bool,
/// Whether supported primitive constants should be constexpr in C23 mode.
/// This is independent of `allow_constexpr` and defaults to false.
pub allow_constexpr_in_c: bool,
/// Sort key for constants
pub sort_by: Option<SortKey>,
}
Expand All @@ -689,6 +692,7 @@ impl Default for ConstantConfig {
ConstantConfig {
allow_static_const: true,
allow_constexpr: true,
allow_constexpr_in_c: false,
sort_by: None,
}
}
Expand Down
18 changes: 18 additions & 0 deletions src/bindgen/ir/constant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -814,6 +814,14 @@ impl Item for Constant {
}

impl Constant {
fn can_be_c_constexpr(&self) -> bool {
match self.ty {
Type::Primitive(PrimitiveType::Void | PrimitiveType::VaList) => false,
Type::Primitive(_) => self.value.can_be_constexpr(),
_ => false,
}
}

pub fn write_declaration<F: Write, LB: LanguageBackend>(
&self,
config: &Config,
Expand Down Expand Up @@ -920,6 +928,16 @@ impl Constant {

let allow_constexpr = config.constant.allow_constexpr && self.value.can_be_constexpr();
match config.language {
Language::C if config.constant.allow_constexpr_in_c && self.can_be_c_constexpr() => {
out.write("constexpr static const ");
crate::bindgen::cdecl::write_field(language_backend, out, &self.ty, &name, config);
// C23 doesn't do implicit conversions on constexpr constants.
out.write(" = (");
language_backend.write_type(out, &self.ty);
out.write(")(");
language_backend.write_literal(out, value);
out.write(");");
}
Language::Cxx if config.constant.allow_static_const || allow_constexpr => {
if allow_constexpr {
out.write("constexpr ")
Expand Down
3 changes: 3 additions & 0 deletions tests/expectations-symbols/constant_c23.c.sym
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
use_types;
};
53 changes: 53 additions & 0 deletions tests/expectations/constant_c23.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>

constexpr static const uint8_t SMALL = (uint8_t)(255);

constexpr static const uint64_t LARGE = (uint64_t)(18446744073709551615ull);

constexpr static const int32_t NEGATIVE = (int32_t)(-42);

constexpr static const uint32_t MASK = (uint32_t)(~0u);

constexpr static const uintptr_t ARITHMETIC = (uintptr_t)(((4 + 2) * 3));

constexpr static const uint32_t SHIFT = (uint32_t)((1u << 31));

constexpr static const uint32_t BITWISE = (uint32_t)((5 | 2));

constexpr static const bool ENABLED = (bool)(true);

constexpr static const uint32_t CHARACTER = (uint32_t)('x');

constexpr static const float FLOAT = (float)(0.1);

constexpr static const double DOUBLE = (double)(0.3333333333333333);

constexpr static const double FLOAT_DIVISION = (double)((1.0 / 3.0));

constexpr static const int64_t NESTED_NEGATION = (int64_t)(-~42);

constexpr static const uint32_t BOUND = (uint32_t)(UINT32_MAX);

constexpr static const int32_t SIGNED_MIN = (int32_t)(INT32_MIN);

#define POINTER (const uint8_t*)1

typedef struct {
uint32_t value;
} Aggregate;
constexpr static const uint32_t Aggregate_ASSOCIATED = (uint32_t)(6);

typedef uint32_t Alias;

#define ARRAY { 1, 2, }

#define STRING "text"

#define AGGREGATE (Aggregate){ .value = 4 }

#define ALIAS 5

void use_types(Aggregate, Alias);
61 changes: 61 additions & 0 deletions tests/expectations/constant_c23.compat.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>

constexpr static const uint8_t SMALL = (uint8_t)(255);

constexpr static const uint64_t LARGE = (uint64_t)(18446744073709551615ull);

constexpr static const int32_t NEGATIVE = (int32_t)(-42);

constexpr static const uint32_t MASK = (uint32_t)(~0u);

constexpr static const uintptr_t ARITHMETIC = (uintptr_t)(((4 + 2) * 3));

constexpr static const uint32_t SHIFT = (uint32_t)((1u << 31));

constexpr static const uint32_t BITWISE = (uint32_t)((5 | 2));

constexpr static const bool ENABLED = (bool)(true);

constexpr static const uint32_t CHARACTER = (uint32_t)('x');

constexpr static const float FLOAT = (float)(0.1);

constexpr static const double DOUBLE = (double)(0.3333333333333333);

constexpr static const double FLOAT_DIVISION = (double)((1.0 / 3.0));

constexpr static const int64_t NESTED_NEGATION = (int64_t)(-~42);

constexpr static const uint32_t BOUND = (uint32_t)(UINT32_MAX);

constexpr static const int32_t SIGNED_MIN = (int32_t)(INT32_MIN);

#define POINTER (const uint8_t*)1

typedef struct {
uint32_t value;
} Aggregate;
constexpr static const uint32_t Aggregate_ASSOCIATED = (uint32_t)(6);

typedef uint32_t Alias;

#define ARRAY { 1, 2, }

#define STRING "text"

#define AGGREGATE (Aggregate){ .value = 4 }

#define ALIAS 5

#ifdef __cplusplus
extern "C" {
#endif // __cplusplus

void use_types(Aggregate, Alias);

#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
60 changes: 60 additions & 0 deletions tests/expectations/constant_c23.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#include <cstdarg>
#include <cstdint>
#include <cstdlib>
#include <ostream>
#include <new>

constexpr const uint8_t SMALL = 255;

constexpr const uint64_t LARGE = 18446744073709551615ull;

constexpr const int32_t NEGATIVE = -42;

constexpr const uint32_t MASK = ~0u;

constexpr const uintptr_t ARITHMETIC = ((4 + 2) * 3);

constexpr const uint32_t SHIFT = (1u << 31);

constexpr const uint32_t BITWISE = (5 | 2);

constexpr const bool ENABLED = true;

constexpr const uint32_t CHARACTER = 'x';

constexpr const float FLOAT = 0.1;

constexpr const double DOUBLE = 0.3333333333333333;

constexpr const double FLOAT_DIVISION = (1.0 / 3.0);

constexpr const int64_t NESTED_NEGATION = -~42;

constexpr const uint32_t BOUND = UINT32_MAX;

constexpr const int32_t SIGNED_MIN = INT32_MIN;

#define POINTER (const uint8_t*)1

struct Aggregate {
uint32_t value;
};
constexpr const uint32_t Aggregate_ASSOCIATED = 6;

using Alias = uint32_t;

constexpr const uint8_t ARRAY[2] = { 1, 2, };

constexpr const char STRING[] = "text";

constexpr const Aggregate AGGREGATE = Aggregate{
/* .value = */ 4
};

constexpr const Alias ALIAS = 5;

extern "C" {

void use_types(Aggregate, Alias);

} // extern "C"
55 changes: 55 additions & 0 deletions tests/expectations/constant_c23.pyx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
from libc.stdint cimport int8_t, int16_t, int32_t, int64_t, intptr_t
from libc.stdint cimport uint8_t, uint16_t, uint32_t, uint64_t, uintptr_t
cdef extern from *:
ctypedef bint bool
ctypedef struct va_list

cdef extern from *:

const uint8_t SMALL # = 255

const uint64_t LARGE # = 18446744073709551615ull

const int32_t NEGATIVE # = -42

const uint32_t MASK # = ~0u

const uintptr_t ARITHMETIC # = ((4 + 2) * 3)

const uint32_t SHIFT # = (1u << 31)

const uint32_t BITWISE # = (5 | 2)

const bool ENABLED # = True

const uint32_t CHARACTER # = 'x'

const float FLOAT # = 0.1

const double DOUBLE # = 0.3333333333333333

const double FLOAT_DIVISION # = (1.0 / 3.0)

const int64_t NESTED_NEGATION # = -~42

const uint32_t BOUND # = UINT32_MAX

const int32_t SIGNED_MIN # = INT32_MIN

const uint8_t *POINTER # = <const uint8_t*>1

ctypedef struct Aggregate:
uint32_t value;
const uint32_t Aggregate_ASSOCIATED # = 6

ctypedef uint32_t Alias;

const uint8_t ARRAY[2] # = [ 1, 2, ]

const char STRING[] # = "text"

const Aggregate AGGREGATE # = <Aggregate>{ 4 }

const Alias ALIAS # = 5

void use_types(Aggregate, Alias);
53 changes: 53 additions & 0 deletions tests/expectations/constant_c23_both.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>

constexpr static const uint8_t SMALL = (uint8_t)(255);

constexpr static const uint64_t LARGE = (uint64_t)(18446744073709551615ull);

constexpr static const int32_t NEGATIVE = (int32_t)(-42);

constexpr static const uint32_t MASK = (uint32_t)(~0u);

constexpr static const uintptr_t ARITHMETIC = (uintptr_t)(((4 + 2) * 3));

constexpr static const uint32_t SHIFT = (uint32_t)((1u << 31));

constexpr static const uint32_t BITWISE = (uint32_t)((5 | 2));

constexpr static const bool ENABLED = (bool)(true);

constexpr static const uint32_t CHARACTER = (uint32_t)('x');

constexpr static const float FLOAT = (float)(0.1);

constexpr static const double DOUBLE = (double)(0.3333333333333333);

constexpr static const double FLOAT_DIVISION = (double)((1.0 / 3.0));

constexpr static const int64_t NESTED_NEGATION = (int64_t)(-~42);

constexpr static const uint32_t BOUND = (uint32_t)(UINT32_MAX);

constexpr static const int32_t SIGNED_MIN = (int32_t)(INT32_MIN);

#define POINTER (const uint8_t*)1

typedef struct Aggregate {
uint32_t value;
} Aggregate;
constexpr static const uint32_t Aggregate_ASSOCIATED = (uint32_t)(6);

typedef uint32_t Alias;

#define ARRAY { 1, 2, }

#define STRING "text"

#define AGGREGATE (Aggregate){ .value = 4 }

#define ALIAS 5

void use_types(struct Aggregate, Alias);
Loading