Skip to content

Rollup of 4 pull requests #137717

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

Closed
wants to merge 10 commits into from
1 change: 1 addition & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3916,6 +3916,7 @@ dependencies = [
"rustc_target",
"rustc_trait_selection",
"rustc_type_ir",
"smallvec",
"tracing",
"unicode-security",
]
Expand Down
13 changes: 8 additions & 5 deletions compiler/rustc_attr_data_structures/src/attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,13 +138,14 @@ impl Deprecation {
}
}

/// Attributes represent parsed, *built in*, inert attributes. That means,
/// attributes that are not actually ever expanded.
/// For more information on this, see the module docs on the rustc_attr_parsing crate.
/// Represent parsed, *built in*, inert attributes.
///
/// That means attributes that are not actually ever expanded.
/// For more information on this, see the module docs on the [`rustc_attr_parsing`] crate.
/// They're instead used as markers, to guide the compilation process in various way in most every stage of the compiler.
/// These are kept around after the AST, into the HIR and further on.
///
/// The word parsed could be a little misleading here, because the parser already parses
/// The word "parsed" could be a little misleading here, because the parser already parses
/// attributes early on. However, the result, an [`ast::Attribute`]
/// is only parsed at a high level, still containing a token stream in many cases. That is
/// because the structure of the contents varies from attribute to attribute.
Expand All @@ -153,7 +154,9 @@ impl Deprecation {
/// the place where `must_use` is checked) little to no extra parsing or validating needs to
/// happen.
///
/// For more docs, look in [`rustc_attr`](https://doc.rust-lang.org/stable/nightly-rustc/rustc_attr/index.html)
/// For more docs, look in [`rustc_attr_parsing`].
///
/// [`rustc_attr_parsing`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_attr_parsing/index.html
#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)]
pub enum AttributeKind {
// tidy-alphabetical-start
Expand Down
42 changes: 25 additions & 17 deletions compiler/rustc_data_structures/src/flat_map_in_place.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::ptr;
use std::{mem, ptr};

use smallvec::{Array, SmallVec};
use thin_vec::ThinVec;
Expand All @@ -13,39 +13,44 @@ pub trait FlatMapInPlace<T>: Sized {
// The implementation of this method is syntactically identical for all the
// different vector types.
macro_rules! flat_map_in_place {
() => {
($vec:ident $( where T: $bound:path)?) => {
fn flat_map_in_place<F, I>(&mut self, mut f: F)
where
F: FnMut(T) -> I,
I: IntoIterator<Item = T>,
{
struct LeakGuard<'a, T $(: $bound)?>(&'a mut $vec<T>);

impl<'a, T $(: $bound)?> Drop for LeakGuard<'a, T> {
fn drop(&mut self) {
unsafe {
self.0.set_len(0); // make sure we just leak elements in case of panic
}
}
}

let this = LeakGuard(self);

let mut read_i = 0;
let mut write_i = 0;
unsafe {
let mut old_len = self.len();
self.set_len(0); // make sure we just leak elements in case of panic

while read_i < old_len {
while read_i < this.0.len() {
// move the read_i'th item out of the vector and map it
// to an iterator
let e = ptr::read(self.as_ptr().add(read_i));
let e = ptr::read(this.0.as_ptr().add(read_i));
let iter = f(e).into_iter();
read_i += 1;

for e in iter {
if write_i < read_i {
ptr::write(self.as_mut_ptr().add(write_i), e);
ptr::write(this.0.as_mut_ptr().add(write_i), e);
write_i += 1;
} else {
// If this is reached we ran out of space
// in the middle of the vector.
// However, the vector is in a valid state here,
// so we just do a somewhat inefficient insert.
self.set_len(old_len);
self.insert(write_i, e);

old_len = self.len();
self.set_len(0);
this.0.insert(write_i, e);

read_i += 1;
write_i += 1;
Expand All @@ -54,20 +59,23 @@ macro_rules! flat_map_in_place {
}

// write_i tracks the number of actually written new items.
self.set_len(write_i);
this.0.set_len(write_i);

// The ThinVec is in a sane state again. Prevent the LeakGuard from leaking the data.
mem::forget(this);
}
}
};
}

impl<T> FlatMapInPlace<T> for Vec<T> {
flat_map_in_place!();
flat_map_in_place!(Vec);
}

impl<T, A: Array<Item = T>> FlatMapInPlace<T> for SmallVec<A> {
flat_map_in_place!();
flat_map_in_place!(SmallVec where T: Array);
}

impl<T> FlatMapInPlace<T> for ThinVec<T> {
flat_map_in_place!();
flat_map_in_place!(ThinVec);
}
1 change: 1 addition & 0 deletions compiler/rustc_lint/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ rustc_span = { path = "../rustc_span" }
rustc_target = { path = "../rustc_target" }
rustc_trait_selection = { path = "../rustc_trait_selection" }
rustc_type_ir = { path = "../rustc_type_ir" }
smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
tracing = "0.1"
unicode-security = "0.1.0"
# tidy-alphabetical-end
5 changes: 5 additions & 0 deletions compiler/rustc_lint/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,11 @@ lint_identifier_uncommon_codepoints = identifier contains {$codepoints_len ->
*[other] {" "}{$identifier_type}
} Unicode general security profile

lint_if_let_dtor = {$dtor_kind ->
[dyn] value may invoke a custom destructor because it contains a trait object
*[concrete] value invokes this custom destructor
}

lint_if_let_rescope = `if let` assigns a shorter lifetime since Edition 2024
.label = this value has a significant drop implementation which may observe a major change in drop order and requires your discretion
.help = the value is now dropped here in Edition 2024
Expand Down
77 changes: 59 additions & 18 deletions compiler/rustc_lint/src/if_let_rescope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,17 @@ use rustc_errors::{
Applicability, Diag, EmissionGuarantee, SubdiagMessageOp, Subdiagnostic, SuggestionStyle,
};
use rustc_hir::{self as hir, HirIdSet};
use rustc_macros::LintDiagnostic;
use rustc_middle::ty::TyCtxt;
use rustc_macros::{LintDiagnostic, Subdiagnostic};
use rustc_middle::ty::adjustment::Adjust;
use rustc_middle::ty::significant_drop_order::{
extract_component_with_significant_dtor, ty_dtor_span,
};
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_session::lint::{FutureIncompatibilityReason, LintId};
use rustc_session::{declare_lint, impl_lint_pass};
use rustc_span::Span;
use rustc_span::edition::Edition;
use rustc_span::{DUMMY_SP, Span};
use smallvec::SmallVec;

use crate::{LateContext, LateLintPass};

Expand Down Expand Up @@ -130,13 +134,15 @@ impl IfLetRescope {
hir::ExprKind::If(_cond, _conseq, Some(alt)) => alt.span.shrink_to_hi(),
_ => return,
};
let mut seen_dyn = false;
let mut add_bracket_to_match_head = match_head_needs_bracket(tcx, expr);
let mut significant_droppers = vec![];
let mut lifetime_ends = vec![];
let mut closing_brackets = 0;
let mut alt_heads = vec![];
let mut match_heads = vec![];
let mut consequent_heads = vec![];
let mut destructors = vec![];
let mut first_if_to_lint = None;
let mut first_if_to_rewrite = false;
let mut empty_alt = false;
Expand All @@ -160,11 +166,25 @@ impl IfLetRescope {
let before_conseq = conseq.span.shrink_to_lo();
let lifetime_end = source_map.end_point(conseq.span);

if let ControlFlow::Break(significant_dropper) =
if let ControlFlow::Break((drop_span, drop_tys)) =
(FindSignificantDropper { cx }).check_if_let_scrutinee(init)
{
destructors.extend(drop_tys.into_iter().filter_map(|ty| {
if let Some(span) = ty_dtor_span(tcx, ty) {
Some(DestructorLabel { span, dtor_kind: "concrete" })
} else if matches!(ty.kind(), ty::Dynamic(..)) {
if seen_dyn {
None
} else {
seen_dyn = true;
Some(DestructorLabel { span: DUMMY_SP, dtor_kind: "dyn" })
}
} else {
None
}
}));
first_if_to_lint = first_if_to_lint.or_else(|| Some((span, expr.hir_id)));
significant_droppers.push(significant_dropper);
significant_droppers.push(drop_span);
lifetime_ends.push(lifetime_end);
if ty_ascription.is_some()
|| !expr.span.can_be_used_for_suggestions()
Expand Down Expand Up @@ -227,6 +247,7 @@ impl IfLetRescope {
hir_id,
span,
IfLetRescopeLint {
destructors,
significant_droppers,
lifetime_ends,
rewrite: first_if_to_rewrite.then_some(IfLetRescopeRewrite {
Expand Down Expand Up @@ -288,6 +309,8 @@ impl<'tcx> LateLintPass<'tcx> for IfLetRescope {
#[derive(LintDiagnostic)]
#[diag(lint_if_let_rescope)]
struct IfLetRescopeLint {
#[subdiagnostic]
destructors: Vec<DestructorLabel>,
#[label]
significant_droppers: Vec<Span>,
#[help]
Expand Down Expand Up @@ -347,6 +370,14 @@ impl Subdiagnostic for IfLetRescopeRewrite {
}
}

#[derive(Subdiagnostic)]
#[note(lint_if_let_dtor)]
struct DestructorLabel {
#[primary_span]
span: Span,
dtor_kind: &'static str,
}

struct AltHead(Span);

struct ConsequentRewrite {
Expand Down Expand Up @@ -374,7 +405,10 @@ impl<'tcx> FindSignificantDropper<'_, 'tcx> {
/// of the scrutinee itself, and also recurses into the expression to find any ref
/// exprs (or autoref) which would promote temporaries that would be scoped to the
/// end of this `if`.
fn check_if_let_scrutinee(&mut self, init: &'tcx hir::Expr<'tcx>) -> ControlFlow<Span> {
fn check_if_let_scrutinee(
&mut self,
init: &'tcx hir::Expr<'tcx>,
) -> ControlFlow<(Span, SmallVec<[Ty<'tcx>; 4]>)> {
self.check_promoted_temp_with_drop(init)?;
self.visit_expr(init)
}
Expand All @@ -385,28 +419,35 @@ impl<'tcx> FindSignificantDropper<'_, 'tcx> {
/// An expression is a promoted temporary if it has an addr taken (i.e. `&expr` or autoref)
/// or is the scrutinee of the `if let`, *and* the expression is not a place
/// expr, and it has a significant drop.
fn check_promoted_temp_with_drop(&self, expr: &'tcx hir::Expr<'tcx>) -> ControlFlow<Span> {
if !expr.is_place_expr(|base| {
fn check_promoted_temp_with_drop(
&self,
expr: &'tcx hir::Expr<'tcx>,
) -> ControlFlow<(Span, SmallVec<[Ty<'tcx>; 4]>)> {
if expr.is_place_expr(|base| {
self.cx
.typeck_results()
.adjustments()
.get(base.hir_id)
.is_some_and(|x| x.iter().any(|adj| matches!(adj.kind, Adjust::Deref(_))))
}) && self
.cx
.typeck_results()
.expr_ty(expr)
.has_significant_drop(self.cx.tcx, self.cx.typing_env())
{
ControlFlow::Break(expr.span)
} else {
ControlFlow::Continue(())
}) {
return ControlFlow::Continue(());
}

let drop_tys = extract_component_with_significant_dtor(
self.cx.tcx,
self.cx.typing_env(),
self.cx.typeck_results().expr_ty(expr),
);
if drop_tys.is_empty() {
return ControlFlow::Continue(());
}

ControlFlow::Break((expr.span, drop_tys))
}
}

impl<'tcx> Visitor<'tcx> for FindSignificantDropper<'_, 'tcx> {
type Result = ControlFlow<Span>;
type Result = ControlFlow<(Span, SmallVec<[Ty<'tcx>; 4]>)>;

fn visit_block(&mut self, b: &'tcx hir::Block<'tcx>) -> Self::Result {
// Blocks introduce temporary terminating scope for all of its
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_middle/src/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ pub mod normalize_erasing_regions;
pub mod pattern;
pub mod print;
pub mod relate;
pub mod significant_drop_order;
pub mod trait_def;
pub mod util;
pub mod visit;
Expand Down
Loading
Loading