-
Notifications
You must be signed in to change notification settings - Fork 13k
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
Lint against treating c_char
as i8
or u8
, for portability
#79089
Comments
I've tagged this T-lang (because it's a proposed new lint and new attribute), and I've also tagged it for the three common architectures where char differs from the normal signedness. |
Another lint I'd like: treating i32 or i64 as c_long. |
Should such a lint still fire if the code is |
cc #41619 |
I would not expect it to, no. |
As a first pass, it would be acceptable if the lint triggered even in that case. It could be allow by default, for instance. In other words I don't think it should be gated on the portability lint, though once the portability lint is available this should integrate with it. |
Note, as well, that this isn't clear in the libc documentation (which seems to be generated from the x86_64 build). |
@jacobbramley in the standard library this issue has been improved recently with PR #89114 . The libc crate's repository is here: https://github.com/rust-lang/libc |
A motivating example for this problem: use std::os::raw::c_char;
fn main() {
let x: u8 = 0b10000000; // set the last bit. 128 in decimal
let x = x as i8 as c_char;
let x = x >> 1; // a non-portable operation
} Neither Clippy nor the compiler lint on this right shift, but if |
The issue is deeper than we first thought. The fundamental problem is that the Rust type system does not distinguish between a type alias and its aliased type: type-alises are eagerly resolved during type-checking. This means that a lint which only has access to the HIR and the result of type-checking cannot easily detect when an We have found 3 possible solutions, none of which is fully satisfactory:
fn require_types_equal<T>(x: T, y: T) {}
fn main() {
let x: c_char = 0;
let y = 0_u8 as _;
require_types_equal(x, y);
} Here inferring the target type of
#[cfg(target_arch = "x86_64")]
pub type CCharRepr = i8;
#[cfg(target_arch = "aarch64")]
pub type CCharRepr = u8;
#[repr(transparent)]
pub struct CChar {
repr: CCharRepr
}
impl CChar {
pub fn new(repr: CCharRepr) -> Self {
Self { repr }
}
pub unsafe fn get_inner(&self) -> CCharRepr {
self.repr
}
} The intention here is that This has the advantage of only requiring changes to the standard library, not the compiler. Deprecating |
One common source of portability issues is making assumptions about whether
c_char
isi8
oru8
. Code written for one platform may fail to build on another due because of this.I think we could detect this fairly easily: we could add an attribute that means "yes, this is a type alias, but treat it as a slightly distinct type, and lint when matching it against the aliased type if not going through this type alias to do so".
This would help people avoid gratuitous portability issues.
The text was updated successfully, but these errors were encountered: