Skip to content

transpile: const macros: strip implicit casts from macro expansion sites#1311

Merged
fw-immunant merged 9 commits intomasterfrom
fw/const-types
Sep 27, 2025
Merged

transpile: const macros: strip implicit casts from macro expansion sites#1311
fw-immunant merged 9 commits intomasterfrom
fw/const-types

Conversation

@fw-immunant
Copy link
Copy Markdown
Contributor

@fw-immunant fw-immunant commented Aug 7, 2025

Copy link
Copy Markdown
Contributor

@kkysen kkysen left a comment

Choose a reason for hiding this comment

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

Oh, I didn't realize you were working on this, too (I was as well). It's okay, though; the fix looks simple. As for the tests, I have some already in #1304.

Copy link
Copy Markdown
Contributor

@kkysen kkysen left a comment

Choose a reason for hiding this comment

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

I don't think this works. It doesn't fix test_silk_int16_MIN, the test I added for the buggy case from #853.

@fw-immunant
Copy link
Copy Markdown
Contributor Author

After cherry-picking:
d002704
6e4ebd4
498f130
f8d0e9e

onto this branch and running c2rust-transpile --overwrite-existing --translate-const-macros c2rust-transpile/tests/snapshots/macros.c I see that test translated as follows:

pub const silk_int16_MIN: std::ffi::c_short = 0x8000 as std::ffi::c_int
    as std::ffi::c_short;
#[no_mangle]
pub unsafe extern "C" fn test_silk_int16_MIN() -> std::ffi::c_int {
    let mut _null: std::ffi::c_char = (*::core::mem::transmute::<
        &[u8; 1],
        &[std::ffi::c_char; 1],
    >(b"\0"))[(silk_int16_MIN as std::ffi::c_int + 0x8000 as std::ffi::c_int) as usize];
    return silk_int16_MIN as std::ffi::c_int;
}

That looks sane to me (the const is -32768). Am I missing something?

@kkysen
Copy link
Copy Markdown
Contributor

kkysen commented Aug 8, 2025

Hmm, that is correct. I just wasn't seeing that in the snapshot. If you rebase onto that branch and check in the updated snapshot, it should be all good. It just wasn't working for me for some reason.

@kkysen
Copy link
Copy Markdown
Contributor

kkysen commented Aug 8, 2025

@fw-immunant, can I rebase this?

@kkysen kkysen changed the base branch from master to kkysen/expanded-translate-const-macros-conservative August 18, 2025 19:19
@kkysen kkysen force-pushed the kkysen/expanded-translate-const-macros-conservative branch from 1255ca4 to d0c85d0 Compare August 18, 2025 19:21
Copy link
Copy Markdown
Contributor

@kkysen kkysen left a comment

Choose a reason for hiding this comment

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

@fw-immunant, I'm not sure this is the right approach. It makes the translation much worse (see the snapshot diff).

@kkysen kkysen force-pushed the kkysen/expanded-translate-const-macros-conservative branch 4 times, most recently from 604c58a to cdb7bd0 Compare August 21, 2025 19:18
@fw-immunant
Copy link
Copy Markdown
Contributor Author

Here's my reasoning around this change:

resolve_expr_type_id is documented to "Resolve true expression type, iterating through any casts and variable references.". From what I can tell, it does this correctly, though one has to consider any outer casts not to be real parts of the expression. So for an expression like (double)(char)500 it will give back the expression and type of 500.

We call this in recreate_const_macro_from_expansions, presumably to have a better chance of looking at not the surrounding casts in context but instead at the type of the macro expansion itself. But the expression pointed at by this is not necessarily going to have the same value as the original one; 500 != (char)500. So it isn't valid to rewrite all uses of a macro with the expression that resolve_expr_type_id gives back.

If we avoid calling resolve_expr_type_id, we stick with an expression that has the correct value, but it may have a type unduly influenced by implicit casts in its context, e.g. what we see in the changed snapshot where the true macro is translated as a double due to its usage in a floating-point arithmetic expression in MIXED_ARITHMETIC LITERAL_INT.

I'll look into whether there's some way to determine which casts are caused by the expansion context as opposed to being part of the macro itself, as the former as the only ones we should strip off to generate the translation for the macro body.

Copy link
Copy Markdown
Contributor

@kkysen kkysen left a comment

Choose a reason for hiding this comment

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

I'll look into whether there's some way to determine which casts are caused by the expansion context as opposed to being part of the macro itself, as the former as the only ones we should strip off to generate the translation for the macro body.

If we're able to do that, that sounds like it would be good. Pulling in all the contextual casts not written in the macro itself, though, like this does, just seems like it results in a much worse translation, although I understand the reasoning for it.

@kkysen kkysen force-pushed the kkysen/expanded-translate-const-macros-conservative branch 2 times, most recently from 81551c0 to ecddc95 Compare September 2, 2025 06:00
Base automatically changed from kkysen/expanded-translate-const-macros-conservative to master September 2, 2025 06:28
@fw-immunant fw-immunant force-pushed the fw/const-types branch 3 times, most recently from ea88c30 to 033afab Compare September 4, 2025 21:30
@fw-immunant fw-immunant force-pushed the fw/const-types branch 2 times, most recently from 103cc62 to 642de3c Compare September 12, 2025 22:17
Comment on lines +2534 to +2545
if ctx.is_const {
return Err(format_translation_err!(
None,
"cannot check nullity of pointer in `const` context",
));
}
let is_null = mk().method_call_expr(e, "is_null", vec![]);
if negated {
mk().unary_expr(UnOp::Not(Default::default()), is_null)
} else {
is_null
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

How come we can't use fn match_bool here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I think we plausibly could; I wasn't looking for refactorings here, and the code is slightly different (here we only handle pointer types).

Comment on lines +439 to +441
use CExprKind::*;
match expr {
ImplicitCast(_, subexpr, _, _, _) => {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

If it's only one match arm and not a ton of them, a glob import probably isn't worth it.

Suggested change
use CExprKind::*;
match expr {
ImplicitCast(_, subexpr, _, _, _) => {
match expr {
CExprKind::ImplicitCast(_, subexpr, _, _, _) => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Could also be an if let. This likely won't change, right, as it's pretty specific to implicit casts?

@fw-immunant fw-immunant force-pushed the fw/const-types branch 3 times, most recently from a00692a to ce3c96d Compare September 17, 2025 12:53
Comment on lines +4521 to +4522
// name of the const macro only occurs if we process the expr_id with a direct call
// to `convert_expr`.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

When/where does this happen? That might've been part of what was tripping me up in adding override_tys to const macros.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

At the beginning of convert_expr:

    pub fn convert_expr(
        &self,
        mut ctx: ExprContext,
        expr_id: CExprId,
        override_ty: Option<CQualTypeId>,
    ) -> TranslationResult<WithStmts<Box<Expr>>> {
        let Located {
            loc: src_loc,
            kind: expr_kind,
        } = &self.ast_context[expr_id];

        trace!(
            "Converting expr {:?}: {:?}",
            expr_id,
            self.ast_context[expr_id]
        );

        if let Some(converted) = self.convert_const_macro_expansion(ctx, expr_id, override_ty)? {
            return Ok(converted);
        }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Oh, sorry, I meant the opposite. When do we not go through fn convert_expr to process an expr_id?

Copy link
Copy Markdown
Contributor Author

@fw-immunant fw-immunant Sep 26, 2025

Choose a reason for hiding this comment

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

Whenever we look extra levels down in the expression tree and short-circuit instead of recursing in convert_expr; there isn't a concise answer here, but it mostly happens toward leaves of the tree.

@fw-immunant
Copy link
Copy Markdown
Contributor Author

@fw-immunant fw-immunant requested a review from kkysen September 26, 2025 18:18
Copy link
Copy Markdown
Contributor

@kkysen kkysen left a comment

Choose a reason for hiding this comment

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

I think it LGTM now. I'm not sure why SIG_IGN isn't being translated as a const now (I saw it was in one commit), though.

@fw-immunant fw-immunant merged commit 48caa16 into master Sep 27, 2025
7 of 8 checks passed
@kkysen kkysen deleted the fw/const-types branch September 29, 2025 14:04
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.

Missing casts in const macro translation

2 participants