Skip to content

Rollup of 12 pull requests #144876

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

Merged
merged 29 commits into from
Aug 4, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
d9ca835
Mark `slice::swap_with_slice` unstably const
paolobarbolini Jun 8, 2025
41199f3
`available_parallelism`: Add documentation for why we don't look at `…
joshtriplett Jul 19, 2025
534b135
tests: Add test for basic line-by-line stepping in a debugger
Enselic Jul 26, 2025
6c7dc05
Fix tests/codegen-llvm/simd/extract-insert-dyn.rs test failure on ris…
Jul 28, 2025
4220587
`AlignmentEnum` should just be `repr(usize)` now
scottmcm Jul 30, 2025
cde0374
resolve: Clarify extern prelude insertion for `extern crate` items
petrochenkov Jul 31, 2025
2f7a2fa
resolve: Do not add erroneous names to extern prelude
petrochenkov Jul 31, 2025
7309696
resolve: Avoid double table lookup in `extern_prelude_get`
petrochenkov Jul 31, 2025
e58e6f8
resolve: Cleanup some uses of extern prelude in diagnostics
petrochenkov Jul 31, 2025
f554c79
Do not give function allocations alignment in consteval or miri.
zachs18 Jul 30, 2025
8a3a7e6
Add lint against dangling pointers form local variables
Urgau Jul 22, 2025
21ec0d5
Allow `dangling_pointers_from_locals` lint in tests
Urgau Jul 22, 2025
fe72018
Update compiler/rustc_const_eval/src/interpret/memory.rs
zachs18 Aug 1, 2025
1a64684
LLVM error with unsupported expression in static initializer for cons…
lucarlig Aug 1, 2025
fe7730f
Stylize `*-lynxos178-*` target maintainer handle to make it easier to…
jieyouxu Jul 26, 2025
3abbdff
For "stage 1" ui-fulldeps, use the stage 1 compiler to query target info
Zalathar Aug 3, 2025
3aeeae6
remove rust_ prefixes
Kivooeo Aug 3, 2025
625b180
Rollup merge of #142205 - paolobarbolini:const_swap_with_slice-impl, …
Zalathar Aug 4, 2025
e65201c
Rollup merge of #144188 - joshtriplett:available-parallelism, r=Mark-…
Zalathar Aug 4, 2025
2a947a0
Rollup merge of #144322 - Urgau:dangling-ptr-from-locals, r=oli-obk
Zalathar Aug 4, 2025
cc7c63b
Rollup merge of #144497 - Enselic:basic-stepping, r=Mark-Simulacrum
Zalathar Aug 4, 2025
70587eb
Rollup merge of #144559 - CaiWeiran:extract-insert-dyn_test, r=Mark-S…
Zalathar Aug 4, 2025
f6b4e45
Rollup merge of #144667 - scottmcm:alignment-is-usize, r=tgross35
Zalathar Aug 4, 2025
0225f8b
Rollup merge of #144706 - zachs18:fix-144661, r=RalfJung
Zalathar Aug 4, 2025
2105044
Rollup merge of #144746 - petrochenkov:extpreltidy, r=b-naber
Zalathar Aug 4, 2025
6c7ffef
Rollup merge of #144785 - lucarlig:master, r=lqd
Zalathar Aug 4, 2025
79e7e03
Rollup merge of #144811 - jieyouxu:target-maintainer-docs, r=Noratrieb
Zalathar Aug 4, 2025
22653b8
Rollup merge of #144848 - Zalathar:ui-fulldeps, r=clubby789
Zalathar Aug 4, 2025
7fbb303
Rollup merge of #144853 - Kivooeo:rust_-cleanup, r=Mark-Simulacrum
Zalathar Aug 4, 2025
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
1 change: 1 addition & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4519,6 +4519,7 @@ name = "rustc_resolve"
version = "0.0.0"
dependencies = [
"bitflags",
"indexmap",
"itertools",
"pulldown-cmark",
"rustc_arena",
Expand Down
8 changes: 6 additions & 2 deletions compiler/rustc_const_eval/src/interpret/memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -937,8 +937,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
// (both global from `alloc_map` and local from `extra_fn_ptr_map`)
if let Some(fn_val) = self.get_fn_alloc(id) {
let align = match fn_val {
FnVal::Instance(instance) => {
self.tcx.codegen_instance_attrs(instance.def).alignment.unwrap_or(Align::ONE)
FnVal::Instance(_instance) => {
// FIXME: Until we have a clear design for the effects of align(N) functions
// on the address of function pointers, we don't consider the align(N)
// attribute on functions in the interpreter.
// See <https://github.com/rust-lang/rust/issues/144661> for more context.
Align::ONE
}
// Machine-specific extra functions currently do not support alignment restrictions.
FnVal::Other(_) => Align::ONE,
Expand Down
6 changes: 6 additions & 0 deletions compiler/rustc_lint/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,12 @@ lint_confusable_identifier_pair = found both `{$existing_sym}` and `{$sym}` as i
lint_custom_inner_attribute_unstable = custom inner attributes are unstable
lint_dangling_pointers_from_locals = a dangling pointer will be produced because the local variable `{$local_var_name}` will be dropped
.ret_ty = return type of the {$fn_kind} is `{$ret_ty}`
.local_var = `{$local_var_name}` is part the {$fn_kind} and will be dropped at the end of the {$fn_kind}
.created_at = dangling pointer created here
.note = pointers do not have a lifetime; after returning, the `{$local_var_ty}` will be deallocated at the end of the {$fn_kind} because nothing is referencing it as far as the type system is concerned
lint_dangling_pointers_from_temporaries = a dangling pointer will be produced because the temporary `{$ty}` will be dropped
.label_ptr = this pointer will immediately be invalid
.label_temporary = this `{$ty}` is deallocated at the end of the statement, bind it to a variable to extend its lifetime
Expand Down
150 changes: 142 additions & 8 deletions compiler/rustc_lint/src/dangling.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
use rustc_ast::visit::{visit_opt, walk_list};
use rustc_hir::attrs::AttributeKind;
use rustc_hir::def::Res;
use rustc_hir::def_id::LocalDefId;
use rustc_hir::intravisit::{FnKind, Visitor, walk_expr};
use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, LangItem, find_attr};
use rustc_middle::ty::{Ty, TyCtxt};
use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, FnRetTy, LangItem, TyKind, find_attr};
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_session::{declare_lint, impl_lint_pass};
use rustc_span::{Span, sym};

use crate::lints::DanglingPointersFromTemporaries;
use crate::lints::{DanglingPointersFromLocals, DanglingPointersFromTemporaries};
use crate::{LateContext, LateLintPass};

declare_lint! {
Expand Down Expand Up @@ -42,6 +43,36 @@ declare_lint! {
"detects getting a pointer from a temporary"
}

declare_lint! {
/// The `dangling_pointers_from_locals` lint detects getting a pointer to data
/// of a local that will be dropped at the end of the function.
///
/// ### Example
///
/// ```rust
/// fn f() -> *const u8 {
/// let x = 0;
/// &x // returns a dangling ptr to `x`
/// }
/// ```
///
/// {{produces}}
///
/// ### Explanation
///
/// Returning a pointer from a local value will not prolong its lifetime,
/// which means that the value can be dropped and the allocation freed
/// while the pointer still exists, making the pointer dangling.
/// This is not an error (as far as the type system is concerned)
/// but probably is not what the user intended either.
///
/// If you need stronger guarantees, consider using references instead,
/// as they are statically verified by the borrow-checker to never dangle.
pub DANGLING_POINTERS_FROM_LOCALS,
Warn,
"detects returning a pointer from a local variable"
}

/// FIXME: false negatives (i.e. the lint is not emitted when it should be)
/// 1. Ways to get a temporary that are not recognized:
/// - `owning_temporary.field`
Expand All @@ -53,20 +84,123 @@ declare_lint! {
#[derive(Clone, Copy, Default)]
pub(crate) struct DanglingPointers;

impl_lint_pass!(DanglingPointers => [DANGLING_POINTERS_FROM_TEMPORARIES]);
impl_lint_pass!(DanglingPointers => [DANGLING_POINTERS_FROM_TEMPORARIES, DANGLING_POINTERS_FROM_LOCALS]);

// This skips over const blocks, but they cannot use or return a dangling pointer anyways.
impl<'tcx> LateLintPass<'tcx> for DanglingPointers {
fn check_fn(
&mut self,
cx: &LateContext<'tcx>,
_: FnKind<'tcx>,
_: &'tcx FnDecl<'tcx>,
fn_kind: FnKind<'tcx>,
fn_decl: &'tcx FnDecl<'tcx>,
body: &'tcx Body<'tcx>,
_: Span,
_: LocalDefId,
def_id: LocalDefId,
) {
DanglingPointerSearcher { cx, inside_call_args: false }.visit_body(body)
DanglingPointerSearcher { cx, inside_call_args: false }.visit_body(body);

if let FnRetTy::Return(ret_ty) = &fn_decl.output
&& let TyKind::Ptr(_) = ret_ty.kind
{
// get the return type of the function or closure
let ty = match cx.tcx.type_of(def_id).instantiate_identity().kind() {
ty::FnDef(..) => cx.tcx.fn_sig(def_id).instantiate_identity(),
ty::Closure(_, args) => args.as_closure().sig(),
_ => return,
};
let ty = ty.output();

// this type is only used for layout computation and pretty-printing, neither of them rely on regions
let ty = cx.tcx.instantiate_bound_regions_with_erased(ty);

// verify that we have a pointer type
let inner_ty = match ty.kind() {
ty::RawPtr(inner_ty, _) => *inner_ty,
_ => return,
};

if cx
.tcx
.layout_of(cx.typing_env().as_query_input(inner_ty))
.is_ok_and(|layout| !layout.is_1zst())
{
let dcx = &DanglingPointerLocalContext {
body: def_id,
fn_ret: ty,
fn_ret_span: ret_ty.span,
fn_ret_inner: inner_ty,
fn_kind: match fn_kind {
FnKind::ItemFn(..) => "function",
FnKind::Method(..) => "method",
FnKind::Closure => "closure",
},
};

// look for `return`s
DanglingPointerReturnSearcher { cx, dcx }.visit_body(body);

// analyze implicit return expression
if let ExprKind::Block(block, None) = &body.value.kind
&& let innermost_block = block.innermost_block()
&& let Some(expr) = innermost_block.expr
{
lint_addr_of_local(cx, dcx, expr);
}
}
}
}
}

struct DanglingPointerLocalContext<'tcx> {
body: LocalDefId,
fn_ret: Ty<'tcx>,
fn_ret_span: Span,
fn_ret_inner: Ty<'tcx>,
fn_kind: &'static str,
}

struct DanglingPointerReturnSearcher<'lcx, 'tcx> {
cx: &'lcx LateContext<'tcx>,
dcx: &'lcx DanglingPointerLocalContext<'tcx>,
}

impl<'tcx> Visitor<'tcx> for DanglingPointerReturnSearcher<'_, 'tcx> {
fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) -> Self::Result {
if let ExprKind::Ret(Some(expr)) = expr.kind {
lint_addr_of_local(self.cx, self.dcx, expr);
}
walk_expr(self, expr)
}
}

/// Look for `&<path_to_local_in_same_body>` pattern and emit lint for it
fn lint_addr_of_local<'a>(
cx: &LateContext<'a>,
dcx: &DanglingPointerLocalContext<'a>,
expr: &'a Expr<'a>,
) {
// peel casts as they do not interest us here, we want the inner expression.
let (inner, _) = super::utils::peel_casts(cx, expr);

if let ExprKind::AddrOf(_, _, inner_of) = inner.kind
&& let ExprKind::Path(ref qpath) = inner_of.peel_blocks().kind
&& let Res::Local(from) = cx.qpath_res(qpath, inner_of.hir_id)
&& cx.tcx.hir_enclosing_body_owner(from) == dcx.body
{
cx.tcx.emit_node_span_lint(
DANGLING_POINTERS_FROM_LOCALS,
expr.hir_id,
expr.span,
DanglingPointersFromLocals {
ret_ty: dcx.fn_ret,
ret_ty_span: dcx.fn_ret_span,
fn_kind: dcx.fn_kind,
local_var: cx.tcx.hir_span(from),
local_var_name: cx.tcx.hir_ident(from),
local_var_ty: dcx.fn_ret_inner,
created_at: (expr.hir_id != inner.hir_id).then_some(inner.span),
},
);
}
}

Expand Down
16 changes: 16 additions & 0 deletions compiler/rustc_lint/src/lints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1188,6 +1188,22 @@ pub(crate) struct DanglingPointersFromTemporaries<'tcx> {
pub temporary_span: Span,
}

#[derive(LintDiagnostic)]
#[diag(lint_dangling_pointers_from_locals)]
#[note]
pub(crate) struct DanglingPointersFromLocals<'tcx> {
pub ret_ty: Ty<'tcx>,
#[label(lint_ret_ty)]
pub ret_ty_span: Span,
pub fn_kind: &'static str,
#[label(lint_local_var)]
pub local_var: Span,
pub local_var_name: Ident,
pub local_var_ty: Ty<'tcx>,
#[label(lint_created_at)]
pub created_at: Option<Span>,
}

// multiple_supertrait_upcastable.rs
#[derive(LintDiagnostic)]
#[diag(lint_multiple_supertrait_upcastable)]
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_resolve/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ edition = "2024"
[dependencies]
# tidy-alphabetical-start
bitflags = "2.4.1"
indexmap = "2.4.0"
itertools = "0.12"
pulldown-cmark = { version = "0.11", features = ["html"], default-features = false }
rustc_arena = { path = "../rustc_arena" }
Expand Down
42 changes: 24 additions & 18 deletions compiler/rustc_resolve/src/build_reduced_graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -968,7 +968,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
}
self.r.potentially_unused_imports.push(import);
let imported_binding = self.r.import(binding, import);
if parent == self.r.graph_root {
if ident.name != kw::Underscore && parent == self.r.graph_root {
let ident = ident.normalize_to_macros_2_0();
if let Some(entry) = self.r.extern_prelude.get(&ident)
&& expansion != LocalExpnId::ROOT
Expand All @@ -984,23 +984,29 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
// more details: https://github.com/rust-lang/rust/pull/111761
return;
}
let entry = self.r.extern_prelude.entry(ident).or_insert(ExternPreludeEntry {
binding: Cell::new(None),
introduced_by_item: true,
});
if orig_name.is_some() {
entry.introduced_by_item = true;
}
// Binding from `extern crate` item in source code can replace
// a binding from `--extern` on command line here.
if !entry.is_import() {
entry.binding.set(Some(imported_binding));
} else if ident.name != kw::Underscore {
self.r.dcx().span_delayed_bug(
item.span,
format!("it had been define the external module '{ident}' multiple times"),
);
}

use indexmap::map::Entry;
match self.r.extern_prelude.entry(ident) {
Entry::Occupied(mut occupied) => {
let entry = occupied.get_mut();
if let Some(old_binding) = entry.binding.get()
&& old_binding.is_import()
{
let msg = format!("extern crate `{ident}` already in extern prelude");
self.r.tcx.dcx().span_delayed_bug(item.span, msg);
} else {
// Binding from `extern crate` item in source code can replace
// a binding from `--extern` on command line here.
entry.binding.set(Some(imported_binding));
entry.introduced_by_item = orig_name.is_some();
}
entry
}
Entry::Vacant(vacant) => vacant.insert(ExternPreludeEntry {
binding: Cell::new(Some(imported_binding)),
introduced_by_item: true,
}),
};
}
self.r.define_binding_local(parent, ident, TypeNS, imported_binding);
}
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_resolve/src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1098,7 +1098,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
}
}
Scope::ExternPrelude => {
suggestions.extend(this.extern_prelude.iter().filter_map(|(ident, _)| {
suggestions.extend(this.extern_prelude.keys().filter_map(|ident| {
let res = Res::Def(DefKind::Mod, CRATE_DEF_ID.to_def_id());
filter_fn(res).then_some(TypoSuggestion::typo_from_ident(*ident, res))
}));
Expand Down Expand Up @@ -1409,7 +1409,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
);

if lookup_ident.span.at_least_rust_2018() {
for ident in self.extern_prelude.clone().into_keys() {
for &ident in self.extern_prelude.keys() {
if ident.span.from_expansion() {
// Idents are adjusted to the root context before being
// resolved in the extern prelude, so reporting this to the
Expand Down
17 changes: 4 additions & 13 deletions compiler/rustc_resolve/src/late/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2476,19 +2476,10 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
} else {
// Items from the prelude
if !module.no_implicit_prelude {
let extern_prelude = self.r.extern_prelude.clone();
names.extend(extern_prelude.iter().flat_map(|(ident, _)| {
self.r
.cstore_mut()
.maybe_process_path_extern(self.r.tcx, ident.name)
.and_then(|crate_id| {
let crate_mod =
Res::Def(DefKind::Mod, crate_id.as_def_id());

filter_fn(crate_mod).then(|| {
TypoSuggestion::typo_from_ident(*ident, crate_mod)
})
})
names.extend(self.r.extern_prelude.keys().flat_map(|ident| {
let res = Res::Def(DefKind::Mod, CRATE_DEF_ID.to_def_id());
filter_fn(res)
.then_some(TypoSuggestion::typo_from_ident(*ident, res))
}));

if let Some(prelude) = self.r.prelude {
Expand Down
Loading
Loading