Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Have a lint against usize-to-u64 casts (or, against *all* integer casts) #9231

Open
RalfJung opened this issue Jul 23, 2022 · 18 comments · Fixed by #10038
Open

Have a lint against usize-to-u64 casts (or, against *all* integer casts) #9231

RalfJung opened this issue Jul 23, 2022 · 18 comments · Fixed by #10038
Labels
A-lint Area: New lints good-first-issue These issues are a good way to get started with Clippy

Comments

@RalfJung
Copy link
Member

RalfJung commented Jul 23, 2022

What it does

I would like clippy to lint against all integer casts. So I have set:

#![warn(
    clippy::cast_possible_wrap, // unsigned -> signed
    clippy::cast_sign_loss, // signed -> unsigned
    clippy::cast_lossless,
    clippy::cast_possible_truncation,
)]

However, I just by accident noticed that this does not lint against usize-to-u64 casts. I guess cast_possible_truncation says "this cannot truncate because we don't have more than 64bit pointer size", and "cast_lossless" says "ah this might be lossy on platforms with pointers larger than 64bit", and then neither of them does anything.

I would be happy to either have one of these lints also trigger on usize-to-u64 casts, or to have a new lint against all integer casts.

Lint Name

cast_integer

Category

pedantic

Advantage

Integer casts are subtle and should be done via From/TryFrom, never via as, so I want to rule out all of them in my codebase.

Drawbacks

No response

Example

pub fn foo(x: usize) -> u64 { x as u64 }

Could be written as:

pub fn foo(x: usize) -> u64 { u64::try_from(x).unwrap() }
@RalfJung RalfJung added the A-lint Area: New lints label Jul 23, 2022
@Alexendoo Alexendoo added the good-first-issue These issues are a good way to get started with Clippy label Jul 23, 2022
@jakubkosno
Copy link

@rustbot claim

navh pushed a commit to navh/rust-clippy that referenced this issue Oct 17, 2022
navh pushed a commit to navh/rust-clippy that referenced this issue Oct 20, 2022
navh pushed a commit to navh/rust-clippy that referenced this issue Oct 20, 2022
navh pushed a commit to navh/rust-clippy that referenced this issue Oct 21, 2022
navh pushed a commit to navh/rust-clippy that referenced this issue Oct 21, 2022
navh pushed a commit to navh/rust-clippy that referenced this issue Oct 21, 2022
navh pushed a commit to navh/rust-clippy that referenced this issue Oct 21, 2022
navh pushed a commit to navh/rust-clippy that referenced this issue Oct 21, 2022
navh pushed a commit to navh/rust-clippy that referenced this issue Oct 21, 2022
navh pushed a commit to navh/rust-clippy that referenced this issue Oct 21, 2022
navh pushed a commit to navh/rust-clippy that referenced this issue Oct 21, 2022
@bors bors closed this as completed in 483b7ac Jan 14, 2023
@RalfJung
Copy link
Member Author

RalfJung commented Jan 25, 2023

Can someone please reopen? The bug is not fixed (tested on playground).

@xFrednet xFrednet reopened this Jan 25, 2023
@xFrednet
Copy link
Member

Yup, sorry there has been a misunderstanding, which is why it was closed.

@ctaymor
Copy link

ctaymor commented Feb 8, 2023

@xFrednet I was thinking about claiming this as a first RustClippy issue. it looks like the misunderstanding was whether PR #10038 fixed this issue, and that it actually doesn't. Am I correctly understanding? If so, if this one open to claim?

@ctaymor
Copy link

ctaymor commented Feb 8, 2023

@rustbot claim

@xFrednet
Copy link
Member

xFrednet commented Feb 8, 2023

Hey @ctaymor , welcome to Clippy! You're correct, #10038 updated the output a bit to be cleaner, but didn't address the issue. You're welcome to take it :)

@gernot-ohner
Copy link

Hey @xFrednet, I believe there has been no active development on this issue for the last couple of months, right?
Could I claim this?

Do I understand correctly that the approach would be to create a new lint that warns on integer casts using as?

@xFrednet
Copy link
Member

Hey @gernot-ohner, you should be able to claim this, since there hasn't been any activity to my knowledge.

For the correct approach, it depends a bit on how @RalfJung would like to use the new lint. My understanding, is that they want to restrict all integer casts. Looking at our lint list I noticed cast_lossless which sounds like a lint, that should trigger on this. But it doesn't (See Playground) and it's also interesting, that it doesn't even trigger on the example from the lint description.

So, I think this sounds like a false positive. It probably makes sense to first look at the code of that lint, and see why it doesn't trigger on the example from the lint description and the example from this issue description.

@gernot-ohner
Copy link

@rustbot claim

@rustbot rustbot assigned gernot-ohner and unassigned ctaymor Nov 13, 2023
@Jarcho
Copy link
Contributor

Jarcho commented Nov 13, 2023

cast_lossless shouldn't trigger on usizeu64 casts as there is no corresponding Into implementation. This means there isn't an alternative other than try_into, which is not a lossless conversion.

cast_possible_truncation ignores the case because there aren't any platforms where usize is larger than u64, so triggering on those cases just ends up being noise.

@xFrednet
Copy link
Member

@Jarcho Thank you for the input. So, would you recommend adding a new lint?

@Jarcho
Copy link
Contributor

Jarcho commented Nov 13, 2023

I think a restriction lint for all integer casts would be the best way. It feels strange to make a lint for only the one cast and the catch-all lint could be made to not trigger when any of the other related lints do in order to avoid the duplicate messages.

We could also add the case to cast_possible_truncation since it technically fits there even though it's unlikely to ever truncate.

@RalfJung
Copy link
Member Author

RalfJung commented Nov 13, 2023

So cast_lossless doesn't warn since the cast is not lossless, and cast_possible_truncation doesn't warn since the cast cannot truncate (i.e., it is lossless)? That does not sound very consistent. ;)

@Jarcho
Copy link
Contributor

Jarcho commented Nov 13, 2023

If the lang team wants to limit the maximum size of usize to 64 bits then the cast would really be lossless for reals. It would be nice to be able to at least opt-in to that constraint since anything using an as cast there is already making that assumption.

Bonus points in the far off future when someone who hopefully isn't me gets to curse the idiots who couldn't conceive of the need for more 64 bits worth of byte-addressable storage.

@asquared31415
Copy link
Contributor

If we assume that we keep adding more larger usize, usize as N could possibly truncate for any integer type N. It's not clear how useful cast_possible_truncation firing on every case would be, but also I expect there to be few places using usize as N.

Having an integer type with no static upper bound means that reasoning about it for conversions is difficult.

@briansmith
Copy link

I don't think there should be a separate cast_integer lint. There are cases there an integer cast is still currently the best construct. For example:

#![deny(
    clippy::cast_lossless,
    clippy::cast_possible_truncation,
    clippy::cast_possible_wrap,
    clippy::cast_precision_loss,
    clippy::cast_ptr_alignment,
    clippy::cast_sign_loss,
    clippy::char_lit_as_u8,
    clippy::checked_conversions,
    clippy::fn_to_numeric_cast,
    clippy::fn_to_numeric_cast_with_truncation,
    clippy::ptr_as_ptr,
    clippy::unnecessary_cast,
    clippy::useless_conversion
)]

const CHUNK_LEN: u32 = 1234567;

extern "C" {
    fn foo(p: *mut u8, len: u32);
}

fn foo_wrapper(slice: &mut [u8]) {
    // Clippy should warn that this is potentially truncating, e.g. if `usize` is 16 bits,
    // but it doesn't.
    for chunk in slice.chunks_mut(CHUNK_LEN as usize) {
        #[allow(clippy::cast_possible_truncation)]
        let chunk_len = chunk.len() as u32;
        unsafe { foo(chunk.as_mut_ptr(), chunk_len) }
    }
}

With cast_integer as proposed, get the warning we desire, but the only way we'd be able to turn it off is to write #[allow(cast_integer)]. But really we should be writing #[allow(clippy::cast_possible_truncation)] or some other more specific lint name, e.g. a new lint that warns specifically about truncations that can only occur on 16-bit targets that most people don't care about. More specific allow directives make the correctness assumptions being made much clearer.

In other words, cast_integer should perhaps enable all the individual cast lints, but it shouldn't be a separate lint on its own.

@briansmith
Copy link

If the lang team wants to limit the maximum size of usize to 64 bits then the cast would really be lossless for reals. It would be nice to be able to at least opt-in to that constraint since anything using an as cast there is already making that assumption.

In my code bases, I write two functions that make it clear that we only support targets where usize is 32 or 64 bits:

#[cfg(any(target_pointer_width = "32", target_pointer_width = "64"))]
pub const fn usize_from_u32(x: u32) -> usize {
    x as usize
}

#[cfg(any(target_pointer_width = "32", target_pointer_width = "64"))]
#[inline(always)]
pub const fn u64_from_usize(x: usize) -> u64 {
    x as u64
}

This is the only place where we'd need to add #[allow(...)] directives to disable lints for integer casts. Anybody can decide to do this without the Rust language team deciding it for everybody. So we absolutely should have x as u64 trigger a clippy lint.

Perhaps, though, we could have directives to tell clippy our minimum and maximum usize, something like:

#![allow(cast_size_32, cast_size_u64)]

@Wrench56
Copy link

As a Rust beginner, I wanted to share my experience using Rust for operating system development. While Rust isn't ideal for every aspect of OS development, its tooling, especially Clippy, has been invaluable for catching bugs early. Our OS supports only 64-bit ISAs, so usize is essentially equivalent to u64. Despite their physical equivalence, usize and u64 serve different semantic purposes, which is why we still use usize.

We use pedantic Clippy to ensure our code is as bug-free as possible. However, we've encountered an issue: Clippy generates warnings for usize <-> u64 casts even though we've specified a target pointer width of 64 bits. We don't want to completely disable this lint, as it helps us catch other potentially problematic casts.

Currently, we cannot selectively allow u64 <-> usize casts without disabling all other casting lints. It would be beneficial if there was a way to permit these specific casts while maintaining the other lint checks.

Please let me know if this issue is related enough to be included under the above-mentioned one, or if I should create a separate issue for it.

Cheers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-lint Area: New lints good-first-issue These issues are a good way to get started with Clippy
Projects
None yet
10 participants