Skip to content

Commit 07b7dc9

Browse files
committed
Auto merge of rust-lang#144876 - Zalathar:rollup-jhv9rir, r=Zalathar
Rollup of 12 pull requests Successful merges: - rust-lang#142205 (Mark `slice::swap_with_slice` unstably const) - rust-lang#144188 (`available_parallelism`: Add documentation for why we don't look at `ulimit`) - rust-lang#144322 (Add lint against dangling pointers from local variables) - rust-lang#144497 (tests: Add test for basic line-by-line stepping in a debugger) - rust-lang#144559 (Enable extract-insert-dyn.rs test on RISC-V (riscv64)) - rust-lang#144667 (`AlignmentEnum` should just be `repr(usize)` now) - rust-lang#144706 (Do not give function allocations alignment in consteval and Miri.) - rust-lang#144746 (resolve: Cleanups and micro-optimizations to extern prelude) - rust-lang#144785 (Regression test for LLVM error with unsupported expression in static initializer for const pointer in array on macOS.) - rust-lang#144811 (Stylize `*-lynxos178-*` target maintainer handle to make it easier to copy/paste) - rust-lang#144848 (For "stage 1" ui-fulldeps, use the stage 1 compiler to query target info) - rust-lang#144853 (Remove unnecessary `rust_` prefixes) Failed merges: - rust-lang#144794 (Port `#[coroutine]` to the new attribute system) r? `@ghost` `@rustbot` modify labels: rollup
2 parents 383b9c4 + 7fbb303 commit 07b7dc9

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+850
-173
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4519,6 +4519,7 @@ name = "rustc_resolve"
45194519
version = "0.0.0"
45204520
dependencies = [
45214521
"bitflags",
4522+
"indexmap",
45224523
"itertools",
45234524
"pulldown-cmark",
45244525
"rustc_arena",

compiler/rustc_const_eval/src/interpret/memory.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -937,8 +937,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
937937
// (both global from `alloc_map` and local from `extra_fn_ptr_map`)
938938
if let Some(fn_val) = self.get_fn_alloc(id) {
939939
let align = match fn_val {
940-
FnVal::Instance(instance) => {
941-
self.tcx.codegen_instance_attrs(instance.def).alignment.unwrap_or(Align::ONE)
940+
FnVal::Instance(_instance) => {
941+
// FIXME: Until we have a clear design for the effects of align(N) functions
942+
// on the address of function pointers, we don't consider the align(N)
943+
// attribute on functions in the interpreter.
944+
// See <https://github.com/rust-lang/rust/issues/144661> for more context.
945+
Align::ONE
942946
}
943947
// Machine-specific extra functions currently do not support alignment restrictions.
944948
FnVal::Other(_) => Align::ONE,

compiler/rustc_lint/messages.ftl

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,12 @@ lint_confusable_identifier_pair = found both `{$existing_sym}` and `{$sym}` as i
207207
208208
lint_custom_inner_attribute_unstable = custom inner attributes are unstable
209209
210+
lint_dangling_pointers_from_locals = a dangling pointer will be produced because the local variable `{$local_var_name}` will be dropped
211+
.ret_ty = return type of the {$fn_kind} is `{$ret_ty}`
212+
.local_var = `{$local_var_name}` is part the {$fn_kind} and will be dropped at the end of the {$fn_kind}
213+
.created_at = dangling pointer created here
214+
.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
215+
210216
lint_dangling_pointers_from_temporaries = a dangling pointer will be produced because the temporary `{$ty}` will be dropped
211217
.label_ptr = this pointer will immediately be invalid
212218
.label_temporary = this `{$ty}` is deallocated at the end of the statement, bind it to a variable to extend its lifetime

compiler/rustc_lint/src/dangling.rs

Lines changed: 142 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
use rustc_ast::visit::{visit_opt, walk_list};
22
use rustc_hir::attrs::AttributeKind;
3+
use rustc_hir::def::Res;
34
use rustc_hir::def_id::LocalDefId;
45
use rustc_hir::intravisit::{FnKind, Visitor, walk_expr};
5-
use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, LangItem, find_attr};
6-
use rustc_middle::ty::{Ty, TyCtxt};
6+
use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, FnRetTy, LangItem, TyKind, find_attr};
7+
use rustc_middle::ty::{self, Ty, TyCtxt};
78
use rustc_session::{declare_lint, impl_lint_pass};
89
use rustc_span::{Span, sym};
910

10-
use crate::lints::DanglingPointersFromTemporaries;
11+
use crate::lints::{DanglingPointersFromLocals, DanglingPointersFromTemporaries};
1112
use crate::{LateContext, LateLintPass};
1213

1314
declare_lint! {
@@ -42,6 +43,36 @@ declare_lint! {
4243
"detects getting a pointer from a temporary"
4344
}
4445

46+
declare_lint! {
47+
/// The `dangling_pointers_from_locals` lint detects getting a pointer to data
48+
/// of a local that will be dropped at the end of the function.
49+
///
50+
/// ### Example
51+
///
52+
/// ```rust
53+
/// fn f() -> *const u8 {
54+
/// let x = 0;
55+
/// &x // returns a dangling ptr to `x`
56+
/// }
57+
/// ```
58+
///
59+
/// {{produces}}
60+
///
61+
/// ### Explanation
62+
///
63+
/// Returning a pointer from a local value will not prolong its lifetime,
64+
/// which means that the value can be dropped and the allocation freed
65+
/// while the pointer still exists, making the pointer dangling.
66+
/// This is not an error (as far as the type system is concerned)
67+
/// but probably is not what the user intended either.
68+
///
69+
/// If you need stronger guarantees, consider using references instead,
70+
/// as they are statically verified by the borrow-checker to never dangle.
71+
pub DANGLING_POINTERS_FROM_LOCALS,
72+
Warn,
73+
"detects returning a pointer from a local variable"
74+
}
75+
4576
/// FIXME: false negatives (i.e. the lint is not emitted when it should be)
4677
/// 1. Ways to get a temporary that are not recognized:
4778
/// - `owning_temporary.field`
@@ -53,20 +84,123 @@ declare_lint! {
5384
#[derive(Clone, Copy, Default)]
5485
pub(crate) struct DanglingPointers;
5586

56-
impl_lint_pass!(DanglingPointers => [DANGLING_POINTERS_FROM_TEMPORARIES]);
87+
impl_lint_pass!(DanglingPointers => [DANGLING_POINTERS_FROM_TEMPORARIES, DANGLING_POINTERS_FROM_LOCALS]);
5788

5889
// This skips over const blocks, but they cannot use or return a dangling pointer anyways.
5990
impl<'tcx> LateLintPass<'tcx> for DanglingPointers {
6091
fn check_fn(
6192
&mut self,
6293
cx: &LateContext<'tcx>,
63-
_: FnKind<'tcx>,
64-
_: &'tcx FnDecl<'tcx>,
94+
fn_kind: FnKind<'tcx>,
95+
fn_decl: &'tcx FnDecl<'tcx>,
6596
body: &'tcx Body<'tcx>,
6697
_: Span,
67-
_: LocalDefId,
98+
def_id: LocalDefId,
6899
) {
69-
DanglingPointerSearcher { cx, inside_call_args: false }.visit_body(body)
100+
DanglingPointerSearcher { cx, inside_call_args: false }.visit_body(body);
101+
102+
if let FnRetTy::Return(ret_ty) = &fn_decl.output
103+
&& let TyKind::Ptr(_) = ret_ty.kind
104+
{
105+
// get the return type of the function or closure
106+
let ty = match cx.tcx.type_of(def_id).instantiate_identity().kind() {
107+
ty::FnDef(..) => cx.tcx.fn_sig(def_id).instantiate_identity(),
108+
ty::Closure(_, args) => args.as_closure().sig(),
109+
_ => return,
110+
};
111+
let ty = ty.output();
112+
113+
// this type is only used for layout computation and pretty-printing, neither of them rely on regions
114+
let ty = cx.tcx.instantiate_bound_regions_with_erased(ty);
115+
116+
// verify that we have a pointer type
117+
let inner_ty = match ty.kind() {
118+
ty::RawPtr(inner_ty, _) => *inner_ty,
119+
_ => return,
120+
};
121+
122+
if cx
123+
.tcx
124+
.layout_of(cx.typing_env().as_query_input(inner_ty))
125+
.is_ok_and(|layout| !layout.is_1zst())
126+
{
127+
let dcx = &DanglingPointerLocalContext {
128+
body: def_id,
129+
fn_ret: ty,
130+
fn_ret_span: ret_ty.span,
131+
fn_ret_inner: inner_ty,
132+
fn_kind: match fn_kind {
133+
FnKind::ItemFn(..) => "function",
134+
FnKind::Method(..) => "method",
135+
FnKind::Closure => "closure",
136+
},
137+
};
138+
139+
// look for `return`s
140+
DanglingPointerReturnSearcher { cx, dcx }.visit_body(body);
141+
142+
// analyze implicit return expression
143+
if let ExprKind::Block(block, None) = &body.value.kind
144+
&& let innermost_block = block.innermost_block()
145+
&& let Some(expr) = innermost_block.expr
146+
{
147+
lint_addr_of_local(cx, dcx, expr);
148+
}
149+
}
150+
}
151+
}
152+
}
153+
154+
struct DanglingPointerLocalContext<'tcx> {
155+
body: LocalDefId,
156+
fn_ret: Ty<'tcx>,
157+
fn_ret_span: Span,
158+
fn_ret_inner: Ty<'tcx>,
159+
fn_kind: &'static str,
160+
}
161+
162+
struct DanglingPointerReturnSearcher<'lcx, 'tcx> {
163+
cx: &'lcx LateContext<'tcx>,
164+
dcx: &'lcx DanglingPointerLocalContext<'tcx>,
165+
}
166+
167+
impl<'tcx> Visitor<'tcx> for DanglingPointerReturnSearcher<'_, 'tcx> {
168+
fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) -> Self::Result {
169+
if let ExprKind::Ret(Some(expr)) = expr.kind {
170+
lint_addr_of_local(self.cx, self.dcx, expr);
171+
}
172+
walk_expr(self, expr)
173+
}
174+
}
175+
176+
/// Look for `&<path_to_local_in_same_body>` pattern and emit lint for it
177+
fn lint_addr_of_local<'a>(
178+
cx: &LateContext<'a>,
179+
dcx: &DanglingPointerLocalContext<'a>,
180+
expr: &'a Expr<'a>,
181+
) {
182+
// peel casts as they do not interest us here, we want the inner expression.
183+
let (inner, _) = super::utils::peel_casts(cx, expr);
184+
185+
if let ExprKind::AddrOf(_, _, inner_of) = inner.kind
186+
&& let ExprKind::Path(ref qpath) = inner_of.peel_blocks().kind
187+
&& let Res::Local(from) = cx.qpath_res(qpath, inner_of.hir_id)
188+
&& cx.tcx.hir_enclosing_body_owner(from) == dcx.body
189+
{
190+
cx.tcx.emit_node_span_lint(
191+
DANGLING_POINTERS_FROM_LOCALS,
192+
expr.hir_id,
193+
expr.span,
194+
DanglingPointersFromLocals {
195+
ret_ty: dcx.fn_ret,
196+
ret_ty_span: dcx.fn_ret_span,
197+
fn_kind: dcx.fn_kind,
198+
local_var: cx.tcx.hir_span(from),
199+
local_var_name: cx.tcx.hir_ident(from),
200+
local_var_ty: dcx.fn_ret_inner,
201+
created_at: (expr.hir_id != inner.hir_id).then_some(inner.span),
202+
},
203+
);
70204
}
71205
}
72206

compiler/rustc_lint/src/lints.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1188,6 +1188,22 @@ pub(crate) struct DanglingPointersFromTemporaries<'tcx> {
11881188
pub temporary_span: Span,
11891189
}
11901190

1191+
#[derive(LintDiagnostic)]
1192+
#[diag(lint_dangling_pointers_from_locals)]
1193+
#[note]
1194+
pub(crate) struct DanglingPointersFromLocals<'tcx> {
1195+
pub ret_ty: Ty<'tcx>,
1196+
#[label(lint_ret_ty)]
1197+
pub ret_ty_span: Span,
1198+
pub fn_kind: &'static str,
1199+
#[label(lint_local_var)]
1200+
pub local_var: Span,
1201+
pub local_var_name: Ident,
1202+
pub local_var_ty: Ty<'tcx>,
1203+
#[label(lint_created_at)]
1204+
pub created_at: Option<Span>,
1205+
}
1206+
11911207
// multiple_supertrait_upcastable.rs
11921208
#[derive(LintDiagnostic)]
11931209
#[diag(lint_multiple_supertrait_upcastable)]

compiler/rustc_resolve/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ edition = "2024"
66
[dependencies]
77
# tidy-alphabetical-start
88
bitflags = "2.4.1"
9+
indexmap = "2.4.0"
910
itertools = "0.12"
1011
pulldown-cmark = { version = "0.11", features = ["html"], default-features = false }
1112
rustc_arena = { path = "../rustc_arena" }

compiler/rustc_resolve/src/build_reduced_graph.rs

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -968,7 +968,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
968968
}
969969
self.r.potentially_unused_imports.push(import);
970970
let imported_binding = self.r.import(binding, import);
971-
if parent == self.r.graph_root {
971+
if ident.name != kw::Underscore && parent == self.r.graph_root {
972972
let ident = ident.normalize_to_macros_2_0();
973973
if let Some(entry) = self.r.extern_prelude.get(&ident)
974974
&& expansion != LocalExpnId::ROOT
@@ -984,23 +984,29 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
984984
// more details: https://github.com/rust-lang/rust/pull/111761
985985
return;
986986
}
987-
let entry = self.r.extern_prelude.entry(ident).or_insert(ExternPreludeEntry {
988-
binding: Cell::new(None),
989-
introduced_by_item: true,
990-
});
991-
if orig_name.is_some() {
992-
entry.introduced_by_item = true;
993-
}
994-
// Binding from `extern crate` item in source code can replace
995-
// a binding from `--extern` on command line here.
996-
if !entry.is_import() {
997-
entry.binding.set(Some(imported_binding));
998-
} else if ident.name != kw::Underscore {
999-
self.r.dcx().span_delayed_bug(
1000-
item.span,
1001-
format!("it had been define the external module '{ident}' multiple times"),
1002-
);
1003-
}
987+
988+
use indexmap::map::Entry;
989+
match self.r.extern_prelude.entry(ident) {
990+
Entry::Occupied(mut occupied) => {
991+
let entry = occupied.get_mut();
992+
if let Some(old_binding) = entry.binding.get()
993+
&& old_binding.is_import()
994+
{
995+
let msg = format!("extern crate `{ident}` already in extern prelude");
996+
self.r.tcx.dcx().span_delayed_bug(item.span, msg);
997+
} else {
998+
// Binding from `extern crate` item in source code can replace
999+
// a binding from `--extern` on command line here.
1000+
entry.binding.set(Some(imported_binding));
1001+
entry.introduced_by_item = orig_name.is_some();
1002+
}
1003+
entry
1004+
}
1005+
Entry::Vacant(vacant) => vacant.insert(ExternPreludeEntry {
1006+
binding: Cell::new(Some(imported_binding)),
1007+
introduced_by_item: true,
1008+
}),
1009+
};
10041010
}
10051011
self.r.define_binding_local(parent, ident, TypeNS, imported_binding);
10061012
}

compiler/rustc_resolve/src/diagnostics.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1098,7 +1098,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
10981098
}
10991099
}
11001100
Scope::ExternPrelude => {
1101-
suggestions.extend(this.extern_prelude.iter().filter_map(|(ident, _)| {
1101+
suggestions.extend(this.extern_prelude.keys().filter_map(|ident| {
11021102
let res = Res::Def(DefKind::Mod, CRATE_DEF_ID.to_def_id());
11031103
filter_fn(res).then_some(TypoSuggestion::typo_from_ident(*ident, res))
11041104
}));
@@ -1409,7 +1409,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
14091409
);
14101410

14111411
if lookup_ident.span.at_least_rust_2018() {
1412-
for ident in self.extern_prelude.clone().into_keys() {
1412+
for &ident in self.extern_prelude.keys() {
14131413
if ident.span.from_expansion() {
14141414
// Idents are adjusted to the root context before being
14151415
// resolved in the extern prelude, so reporting this to the

compiler/rustc_resolve/src/late/diagnostics.rs

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2476,19 +2476,10 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
24762476
} else {
24772477
// Items from the prelude
24782478
if !module.no_implicit_prelude {
2479-
let extern_prelude = self.r.extern_prelude.clone();
2480-
names.extend(extern_prelude.iter().flat_map(|(ident, _)| {
2481-
self.r
2482-
.cstore_mut()
2483-
.maybe_process_path_extern(self.r.tcx, ident.name)
2484-
.and_then(|crate_id| {
2485-
let crate_mod =
2486-
Res::Def(DefKind::Mod, crate_id.as_def_id());
2487-
2488-
filter_fn(crate_mod).then(|| {
2489-
TypoSuggestion::typo_from_ident(*ident, crate_mod)
2490-
})
2491-
})
2479+
names.extend(self.r.extern_prelude.keys().flat_map(|ident| {
2480+
let res = Res::Def(DefKind::Mod, CRATE_DEF_ID.to_def_id());
2481+
filter_fn(res)
2482+
.then_some(TypoSuggestion::typo_from_ident(*ident, res))
24922483
}));
24932484

24942485
if let Some(prelude) = self.r.prelude {

0 commit comments

Comments
 (0)