Skip to content

Conversation

fw-immunant
Copy link
Contributor

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

Copy link
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
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
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
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
Contributor

kkysen commented Aug 8, 2025

@fw-immunant, can I rebase this?

kkysen added 2 commits August 17, 2025 17:30
…sion_test}` into `IndexMap`s instead of `HashMap`s

Previously, `macro_invocations` was a `HashMap`, and thus iterating through it was unordered,
which populated the `Vec<CExprId>` of `macro_expansions` non-deterministically,
which then resulted in non-deterministic output from `--translate-const-macros conservative`.

I also changed the other `macro_*` maps to `IndexMap`,
as many other maps in `TypedAstContext` are already `IndexMap`s, too,
and it's likely that we want these stably ordered and deterministic.
…ranslate-const-macros conservative`

Some operations, such as ptr arithmetic, are `unsafe` and can be done in const macros.
So as an initially overly conservative implementation,
just make all `const`s use `unsafe` blocks in case `unsafe` operations are used.
This is what we already do for `fn`s, for example,
even if all of the operations in a `fn` are safe.
@kkysen kkysen changed the base branch from master to kkysen/expanded-translate-const-macros-conservative August 18, 2025 19:19
kkysen and others added 5 commits August 18, 2025 12:21
…re `CExprKind`s

We do this by recursively checking whether an expression is `const`.
This is generally done in a conservative manner,
modulo the `ExplicitCast` bug (#853), non-`const` `sizeof(VLA)`s,
and `f128`'s non-`const` methods (#1262).

Statements are not handled yet.
resolving the type ignores all casts, but some casts are nontrivial computations that we cannot elide, e.g. truncating larger types to smaller ones

as far as I'm aware we can simply not resolve the expression type here; we'll emit more casts in these cases, but some of those may be load-bearing
@kkysen kkysen force-pushed the kkysen/expanded-translate-const-macros-conservative branch from 1255ca4 to d0c85d0 Compare August 18, 2025 19:21
.resolve_expr_type_id(id)
.unwrap_or((id, ty));
let expr = self.convert_expr(ctx, expr_id, None)?;
let ty = self.ast_context[id].kind.get_type().unwrap_or(ty);
Copy link
Contributor

Choose a reason for hiding this comment

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

What's the difference between this and the above

                let ty = self.ast_context[id]
                    .kind
                    .get_type()
                    .ok_or_else(|| format_err!("Invalid expression type"))?;

?

Copy link
Contributor Author

@fw-immunant fw-immunant Aug 18, 2025

Choose a reason for hiding this comment

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

Oh, good catch--there really isn't one. I guess this can be dropped; all we needed to do was remove the unnecessary resolve_expr_type_id.

Copy link
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
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
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.

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