Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 3 additions & 62 deletions compiler/rustc_hir_typeck/src/cast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
//! expression, `e as U2` is not necessarily so (in fact it will only be valid if
//! `U1` coerces to `U2`).

use rustc_ast::util::parser::ExprPrecedence;
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::codes::*;
use rustc_errors::{Applicability, Diag, ErrorGuaranteed};
Expand Down Expand Up @@ -860,19 +859,14 @@ impl<'a, 'tcx> CastCheck<'tcx> {
(Ptr(m_e), Ptr(m_c)) => self.check_ptr_ptr_cast(fcx, m_e, m_c), // ptr-ptr-cast

// ptr-addr-cast
(Ptr(m_expr), Int(t_c)) => {
self.lossy_provenance_ptr2int_lint(fcx, t_c);
self.check_ptr_addr_cast(fcx, m_expr)
}
(Ptr(m_expr), Int(_)) => self.check_ptr_addr_cast(fcx, m_expr),

(FnPtr, Int(_)) => {
// FIXME(#95489): there should eventually be a lint for these casts
Ok(CastKind::FnPtrAddrCast)
}
// addr-ptr-cast
(Int(_), Ptr(mt)) => {
self.fuzzy_provenance_int2ptr_lint(fcx);
self.check_addr_ptr_cast(fcx, mt)
}
(Int(_), Ptr(mt)) => self.check_addr_ptr_cast(fcx, mt),
// fn-ptr-cast
(FnPtr, Ptr(mt)) => self.check_fptr_ptr_cast(fcx, mt),

Expand Down Expand Up @@ -1133,59 +1127,6 @@ impl<'a, 'tcx> CastCheck<'tcx> {
}
}

fn lossy_provenance_ptr2int_lint(&self, fcx: &FnCtxt<'a, 'tcx>, t_c: ty::cast::IntTy) {
let expr_ty = fcx.resolve_vars_if_possible(self.expr_ty);
let cast_ty = fcx.resolve_vars_if_possible(self.cast_ty);

let sugg = self.span.can_be_used_for_suggestions().then(|| {
let expr_prec = fcx.precedence(self.expr);
let needs_parens = expr_prec < ExprPrecedence::Unambiguous;
let needs_cast = !matches!(t_c, ty::cast::IntTy::U(ty::UintTy::Usize));
let cast_span = self.expr_span.shrink_to_hi().to(self.cast_span);
let expr_span = self.expr_span.shrink_to_lo();
match (needs_parens, needs_cast) {
(true, true) => errors::LossyProvenancePtr2IntSuggestion::NeedsParensCast {
expr_span,
cast_span,
cast_ty,
},
(true, false) => {
errors::LossyProvenancePtr2IntSuggestion::NeedsParens { expr_span, cast_span }
}
(false, true) => {
errors::LossyProvenancePtr2IntSuggestion::NeedsCast { cast_span, cast_ty }
}
(false, false) => errors::LossyProvenancePtr2IntSuggestion::Other { cast_span },
}
});

let lint = errors::LossyProvenancePtr2Int { expr_ty, cast_ty, sugg };
fcx.tcx.emit_node_span_lint(
lint::builtin::LOSSY_PROVENANCE_CASTS,
self.expr.hir_id,
self.span,
lint,
);
}

fn fuzzy_provenance_int2ptr_lint(&self, fcx: &FnCtxt<'a, 'tcx>) {
let sugg = self.span.can_be_used_for_suggestions().then(|| {
errors::LossyProvenanceInt2PtrSuggestion {
lo: self.expr_span.shrink_to_lo(),
hi: self.expr_span.shrink_to_hi().to(self.cast_span),
}
});
let expr_ty = fcx.resolve_vars_if_possible(self.expr_ty);
let cast_ty = fcx.resolve_vars_if_possible(self.cast_ty);
let lint = errors::LossyProvenanceInt2Ptr { expr_ty, cast_ty, sugg };
fcx.tcx.emit_node_span_lint(
lint::builtin::FUZZY_PROVENANCE_CASTS,
self.expr.hir_id,
self.span,
lint,
);
}

/// Attempt to suggest using `.is_empty` when trying to cast from a
/// collection type to a boolean.
fn try_suggest_collection_to_bool(&self, fcx: &FnCtxt<'a, 'tcx>, err: &mut Diag<'_>) {
Expand Down
82 changes: 0 additions & 82 deletions compiler/rustc_hir_typeck/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -377,18 +377,6 @@ impl Subdiagnostic for TypeMismatchFruTypo {
}
}

#[derive(Diagnostic)]
#[diag("strict provenance disallows casting integer `{$expr_ty}` to pointer `{$cast_ty}`")]
#[help(
"if you can't comply with strict provenance and don't have a pointer with the correct provenance you can use `std::ptr::with_exposed_provenance()` instead"
)]
pub(crate) struct LossyProvenanceInt2Ptr<'tcx> {
pub expr_ty: Ty<'tcx>,
pub cast_ty: Ty<'tcx>,
#[subdiagnostic]
pub sugg: Option<LossyProvenanceInt2PtrSuggestion>,
}

#[derive(Diagnostic)]
#[diag("cannot add {$traits_len ->
[1] auto trait {$traits}
Expand All @@ -404,76 +392,6 @@ pub(crate) struct PtrCastAddAutoToObject {
pub traits: DiagSymbolList<String>,
}

#[derive(Subdiagnostic)]
#[multipart_suggestion(
"use `.with_addr()` to adjust a valid pointer in the same allocation, to this address",
applicability = "has-placeholders"
)]
pub(crate) struct LossyProvenanceInt2PtrSuggestion {
#[suggestion_part(code = "(...).with_addr(")]
pub lo: Span,
#[suggestion_part(code = ")")]
pub hi: Span,
}

#[derive(Diagnostic)]
#[diag(
"under strict provenance it is considered bad style to cast pointer `{$expr_ty}` to integer `{$cast_ty}`"
)]
#[help(
"if you can't comply with strict provenance and need to expose the pointer provenance you can use `.expose_provenance()` instead"
)]
pub(crate) struct LossyProvenancePtr2Int<'tcx> {
pub expr_ty: Ty<'tcx>,
pub cast_ty: Ty<'tcx>,
#[subdiagnostic]
pub sugg: Option<LossyProvenancePtr2IntSuggestion<'tcx>>,
}

#[derive(Subdiagnostic)]
pub(crate) enum LossyProvenancePtr2IntSuggestion<'tcx> {
#[multipart_suggestion(
"use `.addr()` to obtain the address of a pointer",
applicability = "maybe-incorrect"
)]
NeedsParensCast {
#[suggestion_part(code = "(")]
expr_span: Span,
#[suggestion_part(code = ").addr() as {cast_ty}")]
cast_span: Span,
cast_ty: Ty<'tcx>,
},
#[multipart_suggestion(
"use `.addr()` to obtain the address of a pointer",
applicability = "maybe-incorrect"
)]
NeedsParens {
#[suggestion_part(code = "(")]
expr_span: Span,
#[suggestion_part(code = ").addr()")]
cast_span: Span,
},
#[suggestion(
"use `.addr()` to obtain the address of a pointer",
code = ".addr() as {cast_ty}",
applicability = "maybe-incorrect"
)]
NeedsCast {
#[primary_span]
cast_span: Span,
cast_ty: Ty<'tcx>,
},
#[suggestion(
"use `.addr()` to obtain the address of a pointer",
code = ".addr()",
applicability = "maybe-incorrect"
)]
Other {
#[primary_span]
cast_span: Span,
},
}

#[derive(Subdiagnostic)]
pub(crate) enum HelpUseLatestEdition {
#[help("set `edition = \"{$edition}\"` in `Cargo.toml`")]
Expand Down
79 changes: 79 additions & 0 deletions compiler/rustc_lint/src/fuzzy_provenance_casts.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
use rustc_hir as hir;
use rustc_session::{declare_lint, declare_lint_pass};

use crate::lints::{LossyProvenanceInt2Ptr, LossyProvenanceInt2PtrSuggestion};
use crate::{LateContext, LateLintPass};

declare_lint! {
/// The `fuzzy_provenance_casts` lint detects an `as` cast between an integer
/// and a pointer.
///
/// ### Example
///
/// ```rust
/// #![feature(strict_provenance_lints)]
/// #![warn(fuzzy_provenance_casts)]
///
/// fn main() {
/// let _dangling = 16_usize as *const u8;
/// }
/// ```
///
/// {{produces}}
///
/// ### Explanation
///
/// This lint is part of the strict provenance effort, see [issue #95228].
/// Casting an integer to a pointer is considered bad style, as a pointer
/// contains, besides the *address* also a *provenance*, indicating what
/// memory the pointer is allowed to read/write. Casting an integer, which
/// doesn't have provenance, to a pointer requires the compiler to assign
/// (guess) provenance. The compiler assigns "all exposed valid" (see the
/// docs of [`ptr::with_exposed_provenance`] for more information about this
/// "exposing"). This penalizes the optimiser and is not well suited for
/// dynamic analysis/dynamic program verification (e.g. Miri or CHERI
/// platforms).
///
/// It is much better to use [`ptr::with_addr`] instead to specify the
/// provenance you want. If using this function is not possible because the
/// code relies on exposed provenance then there is as an escape hatch
/// [`ptr::with_exposed_provenance`].
///
/// [issue #95228]: https://github.com/rust-lang/rust/issues/95228
/// [`ptr::with_addr`]: https://doc.rust-lang.org/core/primitive.pointer.html#method.with_addr
/// [`ptr::with_exposed_provenance`]: https://doc.rust-lang.org/core/ptr/fn.with_exposed_provenance.html
pub FUZZY_PROVENANCE_CASTS,
Allow,
"a fuzzy integer to pointer cast is used",
@feature_gate = strict_provenance_lints;
}

declare_lint_pass!(
/// Lint for `as` casts between an integer and a pointer.
FuzzyProvenanceCasts => [FUZZY_PROVENANCE_CASTS]
);

impl<'tcx> LateLintPass<'tcx> for FuzzyProvenanceCasts {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) {
let hir::ExprKind::Cast(cast_from_expr, cast_to_hir) = expr.kind else { return };

let typeck_results = cx.typeck_results();
// Only lint casts from integer to pointer
let cast_from_ty = typeck_results.expr_ty(cast_from_expr);
if !cast_from_ty.is_integral() {
return;
}
let cast_to_ty = typeck_results.expr_ty(expr);
if !cast_to_ty.is_raw_ptr() {
return;
}

let sugg =
expr.span.can_be_used_for_suggestions().then(|| LossyProvenanceInt2PtrSuggestion {
lo: cast_from_expr.span.shrink_to_lo(),
hi: cast_from_expr.span.shrink_to_hi().to(cast_to_hir.span),
});
let lint = LossyProvenanceInt2Ptr { expr_ty: cast_from_ty, cast_ty: cast_to_ty, sugg };
cx.tcx.emit_node_span_lint(FUZZY_PROVENANCE_CASTS, expr.hir_id, expr.span, lint)
}
}
6 changes: 6 additions & 0 deletions compiler/rustc_lint/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ mod expect;
mod for_loops_over_fallibles;
mod foreign_modules;
mod function_cast_as_integer;
mod fuzzy_provenance_casts;
mod gpukernel_abi;
mod if_let_rescope;
mod impl_trait_overcaptures;
Expand All @@ -56,6 +57,7 @@ mod let_underscore;
mod levels;
pub mod lifetime_syntax;
mod lints;
mod lossy_provenance_casts;
mod macro_expr_fragment_specifier_2024_migration;
mod map_unit_fn;
mod multiple_supertrait_upcastable;
Expand Down Expand Up @@ -92,6 +94,7 @@ use drop_forget_useless::*;
use enum_intrinsics_non_enums::EnumIntrinsicsNonEnums;
use for_loops_over_fallibles::*;
use function_cast_as_integer::*;
use fuzzy_provenance_casts::FuzzyProvenanceCasts;
use gpukernel_abi::*;
use if_let_rescope::IfLetRescope;
use impl_trait_overcaptures::ImplTraitOvercaptures;
Expand All @@ -100,6 +103,7 @@ use internal::*;
use invalid_from_utf8::*;
use let_underscore::*;
use lifetime_syntax::*;
use lossy_provenance_casts::LossyProvenanceCasts;
use macro_expr_fragment_specifier_2024_migration::*;
use map_unit_fn::*;
use multiple_supertrait_upcastable::*;
Expand Down Expand Up @@ -250,6 +254,8 @@ late_lint_methods!(
CheckTransmutes: CheckTransmutes,
LifetimeSyntax: LifetimeSyntax,
InternalEqTraitMethodImpls: InternalEqTraitMethodImpls,
FuzzyProvenanceCasts: FuzzyProvenanceCasts,
LossyProvenanceCasts: LossyProvenanceCasts,
]
]
);
Expand Down
82 changes: 82 additions & 0 deletions compiler/rustc_lint/src/lints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2932,3 +2932,85 @@ impl Subdiagnostic for MismatchedLifetimeSyntaxesSuggestion {
#[diag("`Eq::assert_receiver_is_total_eq` should never be implemented by hand")]
#[note("this method was used to add checks to the `Eq` derive macro")]
pub(crate) struct EqInternalMethodImplemented;

#[derive(Diagnostic)]
#[diag("strict provenance disallows casting integer `{$expr_ty}` to pointer `{$cast_ty}`")]
#[help(
"if you can't comply with strict provenance and don't have a pointer with the correct provenance you can use `std::ptr::with_exposed_provenance()` instead"
)]
pub(crate) struct LossyProvenanceInt2Ptr<'tcx> {
pub expr_ty: Ty<'tcx>,
pub cast_ty: Ty<'tcx>,
#[subdiagnostic]
pub sugg: Option<LossyProvenanceInt2PtrSuggestion>,
}

#[derive(Subdiagnostic)]
#[multipart_suggestion(
"use `.with_addr()` to adjust a valid pointer in the same allocation, to this address",
applicability = "has-placeholders"
)]
pub(crate) struct LossyProvenanceInt2PtrSuggestion {
#[suggestion_part(code = "(...).with_addr(")]
pub lo: Span,
#[suggestion_part(code = ")")]
pub hi: Span,
}

#[derive(Diagnostic)]
#[diag(
"under strict provenance it is considered bad style to cast pointer `{$cast_from_ty}` to integer `{$cast_to_ty}`"
)]
#[help(
"if you can't comply with strict provenance and need to expose the pointer provenance you can use `.expose_provenance()` instead"
)]
pub(crate) struct LossyProvenancePtr2Int<'tcx> {
pub cast_from_ty: Ty<'tcx>,
pub cast_to_ty: Ty<'tcx>,
#[subdiagnostic]
pub sugg: Option<LossyProvenancePtr2IntSuggestion<'tcx>>,
}

#[derive(Subdiagnostic)]
pub(crate) enum LossyProvenancePtr2IntSuggestion<'tcx> {
#[multipart_suggestion(
"use `.addr()` to obtain the address of a pointer",
applicability = "maybe-incorrect"
)]
NeedsParensCast {
#[suggestion_part(code = "(")]
expr_span: Span,
#[suggestion_part(code = ").addr() as {cast_to_ty}")]
cast_span: Span,
cast_to_ty: Ty<'tcx>,
},
#[multipart_suggestion(
"use `.addr()` to obtain the address of a pointer",
applicability = "maybe-incorrect"
)]
NeedsParens {
#[suggestion_part(code = "(")]
expr_span: Span,
#[suggestion_part(code = ").addr()")]
cast_span: Span,
},
#[suggestion(
"use `.addr()` to obtain the address of a pointer",
code = ".addr() as {cast_to_ty}",
applicability = "maybe-incorrect"
)]
NeedsCast {
#[primary_span]
cast_span: Span,
cast_to_ty: Ty<'tcx>,
},
#[suggestion(
"use `.addr()` to obtain the address of a pointer",
code = ".addr()",
applicability = "maybe-incorrect"
)]
Other {
#[primary_span]
cast_span: Span,
},
}
Loading
Loading