Skip to content

Add opt-in C23 constexpr scalar constants - #1190

Draft
GtechGovind wants to merge 2 commits into
mozilla:mainfrom
GtechGovind:feat/c23-constexpr-constants
Draft

GtechGovind wants to merge 2 commits into
mozilla:mainfrom
GtechGovind:feat/c23-constexpr-constants

Conversation

@GtechGovind

@GtechGovind GtechGovind commented Oct 9, 2026 •

Copy link
Copy Markdown

I added an opt-in [const] allow_constexpr_in_c setting for typed primitive C23 constants. It defaults to false, so existing C headers stay unchanged, and it is independent of the C++ settings.

I reused the existing constexpr eligibility check and initializer renderer instead of inspecting literal spellings or restricting arithmetic operators. Primitive constants receive an explicit conversion to the declared type; pointers, arrays, aggregates and aliases keep their macro output. The documentation covers the C23 compiler requirement, initializer diagnostics and preprocessor compatibility implications.

The C23 tests now use the existing fixture harness and checked-in expectations across all three C declaration styles, C++ compatibility, C++ and Cython. The fixture covers scalar types, bounds, arithmetic, shifts, compound negation, associated constants and macro fallbacks. The harness selects -std=c23 for fixtures that enable the setting, and CI installs GCC 14 for the compiler checks.

Validation:

  • CBINDGEN_TEST_VERIFY=1 CC=/opt/homebrew/bin/gcc-16 cargo +nightly test with Cython 3.0.2: 176 passed, zero failed; one existing doc test ignored. All 161 pre-existing fixtures passed unchanged.
  • Focused fixture verification with CC=/usr/bin/clang: passed with Apple Clang 21, including C++ and Cython variants.
  • cargo +stable fmt --check, cargo +stable clippy --workspace -- -D warnings, cargo +stable build --no-default-features and git diff --check: passed.
  • cargo +1.74.0 test nonexistent-test --verbose: passed the repository's MSRV compilation check (no tests selected).
  • cargo +stable package --allow-dirty: packaging and verification build passed. Full nightly tests from the packaged crate: 148 passed, zero failed; one existing doc test ignored.

Shared literal lowering is unchanged. Generated initializers still need to be valid C23 constant expressions; this change does not infer Rust intermediate types or evaluate constants. The additional cargo +stable clippy --all-targets -- -D warnings check reports the pre-existing eight-argument run_cbindgen harness function; the prescribed workspace check passes. Linux/ARM CI has not been reproduced locally.

Fixes #1095.

Comment thread tests/constant_c23.rs Outdated
String::from_utf8(output).unwrap()
}

const ENABLED: &str = "[const]\nallow_constexpr_in_c = true\n";

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use the existing test infrastructure please. If we need to update the compiler in CI that's fine.

Comment thread src/bindgen/ir/constant.rs Outdated
out.write(")(");
// C23 requires exact representability. The explicit conversion
// performs the narrowing normally applied by initialization,
// e.g. for f32 literals or integer-promoted bitwise expressions.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment looks like it should be above the cast. Also could be less verbose:

// C23 doesn't do implicit conversions on constexpr constants.

Or so.

Comment thread src/bindgen/ir/constant.rs Outdated
Literal::BinOp { left, op, right } => {
// Unsuffixed Rust literals inherit their integer type, whereas
// C evaluates them as int before our final conversion. Arithmetic
// and shifts can therefore overflow even for valid Rust constants.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this is an issue for C++ as well, remove the matches!() check. The common case is that it won't overflow, and if it does the compiler should warn.

Comment thread src/bindgen/ir/constant.rs Outdated
}
Literal::PostfixUnaryOp { op, value } => {
// Negating an inferred C int intermediate can overflow even
// when the wider Rust destination makes the expression valid.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same, what are these hacks about?

Comment thread src/bindgen/ir/constant.rs Outdated
fn can_be_c_constexpr(&self, allow_float: bool) -> bool {
match self {
Literal::Expr(value) => {
(value.starts_with('\'') && !value.contains("\\u{"))

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a massive hack, I don't think this is acceptable.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature request: Allow constexpr in C

2 participants