Skip to content

Commit 0e98766

Browse files
committed
Auto merge of #133893 - fmease:rollup-11pi6fg, r=fmease
Rollup of 10 pull requests Successful merges: - #118833 (Add lint against function pointer comparisons) - #122161 (Fix suggestion when shorthand `self` has erroneous type) - #133233 (Add context to "const in pattern" errors) - #133761 (Update books) - #133843 (Do not emit empty suggestion) - #133863 (Rename `core_pattern_type` and `core_pattern_types` lib feature gates to `pattern_type_macro`) - #133872 (No need to create placeholders for GAT args in confirm_object_candidate) - #133874 (`fn_sig_for_fn_abi` should return a `ty::FnSig`, no need for a binder) - #133890 (Add a new test ui/incoherent-inherent-impls/no-other-unrelated-errors to check E0116 does not cause unrelated errors) - #133892 (Revert #133817) r? `@ghost` `@rustbot` modify labels: rollup
2 parents 5a0a5e6 + 1f1dfd5 commit 0e98766

File tree

201 files changed

+2293
-1109
lines changed

Some content is hidden

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

201 files changed

+2293
-1109
lines changed

compiler/rustc_ast/src/ast.rs

+12
Original file line numberDiff line numberDiff line change
@@ -2566,6 +2566,18 @@ pub enum SelfKind {
25662566
Explicit(P<Ty>, Mutability),
25672567
}
25682568

2569+
impl SelfKind {
2570+
pub fn to_ref_suggestion(&self) -> String {
2571+
match self {
2572+
SelfKind::Region(None, mutbl) => mutbl.ref_prefix_str().to_string(),
2573+
SelfKind::Region(Some(lt), mutbl) => format!("&{lt} {}", mutbl.prefix_str()),
2574+
SelfKind::Value(_) | SelfKind::Explicit(_, _) => {
2575+
unreachable!("if we had an explicit self, we wouldn't be here")
2576+
}
2577+
}
2578+
}
2579+
}
2580+
25692581
pub type ExplicitSelf = Spanned<SelfKind>;
25702582

25712583
impl Param {

compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -3394,7 +3394,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
33943394
let Some(ty) = self.node_ty_opt(tail_expr.hir_id) else {
33953395
return;
33963396
};
3397-
if self.can_eq(self.param_env, expected_ty, ty) {
3397+
if self.can_eq(self.param_env, expected_ty, ty)
3398+
// FIXME: this happens with macro calls. Need to figure out why the stmt
3399+
// `println!();` doesn't include the `;` in its `Span`. (#133845)
3400+
// We filter these out to avoid ICEs with debug assertions on caused by
3401+
// empty suggestions.
3402+
&& stmt.span.hi() != tail_expr.span.hi()
3403+
{
33983404
err.span_suggestion_short(
33993405
stmt.span.with_lo(tail_expr.span.hi()),
34003406
"remove this semicolon",

compiler/rustc_lint/messages.ftl

+6
Original file line numberDiff line numberDiff line change
@@ -885,6 +885,12 @@ lint_unnameable_test_items = cannot test inner items
885885
lint_unnecessary_qualification = unnecessary qualification
886886
.suggestion = remove the unnecessary path segments
887887
888+
lint_unpredictable_fn_pointer_comparisons = function pointer comparisons do not produce meaningful results since their addresses are not guaranteed to be unique
889+
.note_duplicated_fn = the address of the same function can vary between different codegen units
890+
.note_deduplicated_fn = furthermore, different functions could have the same address after being merged together
891+
.note_visit_fn_addr_eq = for more information visit <https://doc.rust-lang.org/nightly/core/ptr/fn.fn_addr_eq.html>
892+
.fn_addr_eq_suggestion = refactor your code, or use `std::ptr::fn_addr_eq` to suppress the lint
893+
888894
lint_unqualified_local_imports = `use` of a local item without leading `self::`, `super::`, or `crate::`
889895
890896
lint_unsafe_attr_outside_unsafe = unsafe attribute used without unsafe

compiler/rustc_lint/src/lints.rs

+36
Original file line numberDiff line numberDiff line change
@@ -1815,6 +1815,42 @@ pub(crate) enum AmbiguousWidePointerComparisonsAddrSuggestion<'a> {
18151815
},
18161816
}
18171817

1818+
#[derive(LintDiagnostic)]
1819+
pub(crate) enum UnpredictableFunctionPointerComparisons<'a> {
1820+
#[diag(lint_unpredictable_fn_pointer_comparisons)]
1821+
#[note(lint_note_duplicated_fn)]
1822+
#[note(lint_note_deduplicated_fn)]
1823+
#[note(lint_note_visit_fn_addr_eq)]
1824+
Suggestion {
1825+
#[subdiagnostic]
1826+
sugg: UnpredictableFunctionPointerComparisonsSuggestion<'a>,
1827+
},
1828+
#[diag(lint_unpredictable_fn_pointer_comparisons)]
1829+
#[note(lint_note_duplicated_fn)]
1830+
#[note(lint_note_deduplicated_fn)]
1831+
#[note(lint_note_visit_fn_addr_eq)]
1832+
Warn,
1833+
}
1834+
1835+
#[derive(Subdiagnostic)]
1836+
#[multipart_suggestion(
1837+
lint_fn_addr_eq_suggestion,
1838+
style = "verbose",
1839+
applicability = "maybe-incorrect"
1840+
)]
1841+
pub(crate) struct UnpredictableFunctionPointerComparisonsSuggestion<'a> {
1842+
pub ne: &'a str,
1843+
pub cast_right: String,
1844+
pub deref_left: &'a str,
1845+
pub deref_right: &'a str,
1846+
#[suggestion_part(code = "{ne}std::ptr::fn_addr_eq({deref_left}")]
1847+
pub left: Span,
1848+
#[suggestion_part(code = ", {deref_right}")]
1849+
pub middle: Span,
1850+
#[suggestion_part(code = "{cast_right})")]
1851+
pub right: Span,
1852+
}
1853+
18181854
pub(crate) struct ImproperCTypes<'a> {
18191855
pub ty: Ty<'a>,
18201856
pub desc: &'a str,

compiler/rustc_lint/src/types.rs

+134-4
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@ use crate::lints::{
2323
AmbiguousWidePointerComparisons, AmbiguousWidePointerComparisonsAddrMetadataSuggestion,
2424
AmbiguousWidePointerComparisonsAddrSuggestion, AtomicOrderingFence, AtomicOrderingLoad,
2525
AtomicOrderingStore, ImproperCTypes, InvalidAtomicOrderingDiag, InvalidNanComparisons,
26-
InvalidNanComparisonsSuggestion, UnusedComparisons, VariantSizeDifferencesDiag,
26+
InvalidNanComparisonsSuggestion, UnpredictableFunctionPointerComparisons,
27+
UnpredictableFunctionPointerComparisonsSuggestion, UnusedComparisons,
28+
VariantSizeDifferencesDiag,
2729
};
2830
use crate::{LateContext, LateLintPass, LintContext, fluent_generated as fluent};
2931

@@ -166,6 +168,35 @@ declare_lint! {
166168
"detects ambiguous wide pointer comparisons"
167169
}
168170

171+
declare_lint! {
172+
/// The `unpredictable_function_pointer_comparisons` lint checks comparison
173+
/// of function pointer as the operands.
174+
///
175+
/// ### Example
176+
///
177+
/// ```rust
178+
/// fn a() {}
179+
/// fn b() {}
180+
///
181+
/// let f: fn() = a;
182+
/// let g: fn() = b;
183+
///
184+
/// let _ = f == g;
185+
/// ```
186+
///
187+
/// {{produces}}
188+
///
189+
/// ### Explanation
190+
///
191+
/// Function pointers comparisons do not produce meaningful result since
192+
/// they are never guaranteed to be unique and could vary between different
193+
/// code generation units. Furthermore, different functions could have the
194+
/// same address after being merged together.
195+
UNPREDICTABLE_FUNCTION_POINTER_COMPARISONS,
196+
Warn,
197+
"detects unpredictable function pointer comparisons"
198+
}
199+
169200
#[derive(Copy, Clone, Default)]
170201
pub(crate) struct TypeLimits {
171202
/// Id of the last visited negated expression
@@ -178,7 +209,8 @@ impl_lint_pass!(TypeLimits => [
178209
UNUSED_COMPARISONS,
179210
OVERFLOWING_LITERALS,
180211
INVALID_NAN_COMPARISONS,
181-
AMBIGUOUS_WIDE_POINTER_COMPARISONS
212+
AMBIGUOUS_WIDE_POINTER_COMPARISONS,
213+
UNPREDICTABLE_FUNCTION_POINTER_COMPARISONS
182214
]);
183215

184216
impl TypeLimits {
@@ -255,7 +287,7 @@ fn lint_nan<'tcx>(
255287
cx.emit_span_lint(INVALID_NAN_COMPARISONS, e.span, lint);
256288
}
257289

258-
#[derive(Debug, PartialEq)]
290+
#[derive(Debug, PartialEq, Copy, Clone)]
259291
enum ComparisonOp {
260292
BinOp(hir::BinOpKind),
261293
Other,
@@ -383,6 +415,100 @@ fn lint_wide_pointer<'tcx>(
383415
);
384416
}
385417

418+
fn lint_fn_pointer<'tcx>(
419+
cx: &LateContext<'tcx>,
420+
e: &'tcx hir::Expr<'tcx>,
421+
cmpop: ComparisonOp,
422+
l: &'tcx hir::Expr<'tcx>,
423+
r: &'tcx hir::Expr<'tcx>,
424+
) {
425+
let peel_refs = |mut ty: Ty<'tcx>| -> (Ty<'tcx>, usize) {
426+
let mut refs = 0;
427+
428+
while let ty::Ref(_, inner_ty, _) = ty.kind() {
429+
ty = *inner_ty;
430+
refs += 1;
431+
}
432+
433+
(ty, refs)
434+
};
435+
436+
// Left and right operands can have borrows, remove them
437+
let l = l.peel_borrows();
438+
let r = r.peel_borrows();
439+
440+
let Some(l_ty) = cx.typeck_results().expr_ty_opt(l) else { return };
441+
let Some(r_ty) = cx.typeck_results().expr_ty_opt(r) else { return };
442+
443+
// Remove any references as `==` will deref through them (and count the
444+
// number of references removed, for latter).
445+
let (l_ty, l_ty_refs) = peel_refs(l_ty);
446+
let (r_ty, r_ty_refs) = peel_refs(r_ty);
447+
448+
if !l_ty.is_fn() || !r_ty.is_fn() {
449+
return;
450+
}
451+
452+
// Let's try to suggest `ptr::fn_addr_eq` if/when possible.
453+
454+
let is_eq_ne = matches!(cmpop, ComparisonOp::BinOp(hir::BinOpKind::Eq | hir::BinOpKind::Ne));
455+
456+
if !is_eq_ne {
457+
// Neither `==` nor `!=`, we can't suggest `ptr::fn_addr_eq`, just show the warning.
458+
return cx.emit_span_lint(
459+
UNPREDICTABLE_FUNCTION_POINTER_COMPARISONS,
460+
e.span,
461+
UnpredictableFunctionPointerComparisons::Warn,
462+
);
463+
}
464+
465+
let (Some(l_span), Some(r_span)) =
466+
(l.span.find_ancestor_inside(e.span), r.span.find_ancestor_inside(e.span))
467+
else {
468+
// No appropriate spans for the left and right operands, just show the warning.
469+
return cx.emit_span_lint(
470+
UNPREDICTABLE_FUNCTION_POINTER_COMPARISONS,
471+
e.span,
472+
UnpredictableFunctionPointerComparisons::Warn,
473+
);
474+
};
475+
476+
let ne = if cmpop == ComparisonOp::BinOp(hir::BinOpKind::Ne) { "!" } else { "" };
477+
478+
// `ptr::fn_addr_eq` only works with raw pointer, deref any references.
479+
let deref_left = &*"*".repeat(l_ty_refs);
480+
let deref_right = &*"*".repeat(r_ty_refs);
481+
482+
let left = e.span.shrink_to_lo().until(l_span.shrink_to_lo());
483+
let middle = l_span.shrink_to_hi().until(r_span.shrink_to_lo());
484+
let right = r_span.shrink_to_hi().until(e.span.shrink_to_hi());
485+
486+
// We only check for a right cast as `FnDef` == `FnPtr` is not possible,
487+
// only `FnPtr == FnDef` is possible.
488+
let cast_right = if !r_ty.is_fn_ptr() {
489+
let fn_sig = r_ty.fn_sig(cx.tcx);
490+
format!(" as {fn_sig}")
491+
} else {
492+
String::new()
493+
};
494+
495+
cx.emit_span_lint(
496+
UNPREDICTABLE_FUNCTION_POINTER_COMPARISONS,
497+
e.span,
498+
UnpredictableFunctionPointerComparisons::Suggestion {
499+
sugg: UnpredictableFunctionPointerComparisonsSuggestion {
500+
ne,
501+
deref_left,
502+
deref_right,
503+
left,
504+
middle,
505+
right,
506+
cast_right,
507+
},
508+
},
509+
);
510+
}
511+
386512
impl<'tcx> LateLintPass<'tcx> for TypeLimits {
387513
fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx hir::Expr<'tcx>) {
388514
match e.kind {
@@ -399,7 +525,9 @@ impl<'tcx> LateLintPass<'tcx> for TypeLimits {
399525
cx.emit_span_lint(UNUSED_COMPARISONS, e.span, UnusedComparisons);
400526
} else {
401527
lint_nan(cx, e, binop, l, r);
402-
lint_wide_pointer(cx, e, ComparisonOp::BinOp(binop.node), l, r);
528+
let cmpop = ComparisonOp::BinOp(binop.node);
529+
lint_wide_pointer(cx, e, cmpop, l, r);
530+
lint_fn_pointer(cx, e, cmpop, l, r);
403531
}
404532
}
405533
}
@@ -411,13 +539,15 @@ impl<'tcx> LateLintPass<'tcx> for TypeLimits {
411539
&& let Some(cmpop) = diag_item_cmpop(diag_item) =>
412540
{
413541
lint_wide_pointer(cx, e, cmpop, l, r);
542+
lint_fn_pointer(cx, e, cmpop, l, r);
414543
}
415544
hir::ExprKind::MethodCall(_, l, [r], _)
416545
if let Some(def_id) = cx.typeck_results().type_dependent_def_id(e.hir_id)
417546
&& let Some(diag_item) = cx.tcx.get_diagnostic_name(def_id)
418547
&& let Some(cmpop) = diag_item_cmpop(diag_item) =>
419548
{
420549
lint_wide_pointer(cx, e, cmpop, l, r);
550+
lint_fn_pointer(cx, e, cmpop, l, r);
421551
}
422552
_ => {}
423553
};

compiler/rustc_mir_build/messages.ftl

+25-12
Original file line numberDiff line numberDiff line change
@@ -84,12 +84,17 @@ mir_build_call_to_unsafe_fn_requires_unsafe_unsafe_op_in_unsafe_fn_allowed =
8484
8585
mir_build_confused = missing patterns are not covered because `{$variable}` is interpreted as a constant pattern, not a new variable
8686
87-
mir_build_const_param_in_pattern = const parameters cannot be referenced in patterns
87+
mir_build_const_defined_here = constant defined here
8888
89-
mir_build_const_pattern_depends_on_generic_parameter =
90-
constant pattern depends on a generic parameter
89+
mir_build_const_param_in_pattern = constant parameters cannot be referenced in patterns
90+
.label = can't be used in patterns
91+
mir_build_const_param_in_pattern_def = constant defined here
92+
93+
mir_build_const_pattern_depends_on_generic_parameter = constant pattern cannot depend on generic parameters
94+
.label = `const` depends on a generic parameter
9195
9296
mir_build_could_not_eval_const_pattern = could not evaluate constant pattern
97+
.label = could not evaluate constant
9398
9499
mir_build_deref_raw_pointer_requires_unsafe =
95100
dereference of raw pointer is unsafe and requires unsafe block
@@ -147,7 +152,8 @@ mir_build_inline_assembly_requires_unsafe_unsafe_op_in_unsafe_fn_allowed =
147152
148153
mir_build_interpreted_as_const = introduce a variable instead
149154
150-
mir_build_invalid_pattern = `{$non_sm_ty}` cannot be used in patterns
155+
mir_build_invalid_pattern = {$prefix} `{$non_sm_ty}` cannot be used in patterns
156+
.label = {$prefix} can't be used in patterns
151157
152158
mir_build_irrefutable_let_patterns_if_let = irrefutable `if let` {$count ->
153159
[one] pattern
@@ -244,10 +250,12 @@ mir_build_mutation_of_layout_constrained_field_requires_unsafe_unsafe_op_in_unsa
244250
.label = mutation of layout constrained field
245251
246252
mir_build_nan_pattern = cannot use NaN in patterns
253+
.label = evaluates to `NaN`, which is not allowed in patterns
247254
.note = NaNs compare inequal to everything, even themselves, so this pattern would never match
248255
.help = try using the `is_nan` method instead
249256
250257
mir_build_non_const_path = runtime values cannot be referenced in patterns
258+
.label = references a runtime value
251259
252260
mir_build_non_empty_never_pattern =
253261
mismatched types
@@ -265,13 +273,15 @@ mir_build_non_exhaustive_patterns_type_not_empty = non-exhaustive patterns: type
265273
.suggestion = ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown
266274
.help = ensure that all possible cases are being handled by adding a match arm with a wildcard pattern
267275
268-
mir_build_non_partial_eq_match =
269-
to use a constant of type `{$non_peq_ty}` in a pattern, the type must implement `PartialEq`
276+
mir_build_non_partial_eq_match = constant of non-structural type `{$ty}` in a pattern
277+
.label = constant of non-structural type
270278
271279
mir_build_pattern_not_covered = refutable pattern in {$origin}
272280
.pattern_ty = the matched value is of type `{$pattern_ty}`
273281
274-
mir_build_pointer_pattern = function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details.
282+
mir_build_pointer_pattern = function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon
283+
.label = can't be used in patterns
284+
.note = see https://github.com/rust-lang/rust/issues/70861 for details
275285
276286
mir_build_privately_uninhabited = pattern `{$witness_1}` is currently uninhabited, but this variant contains private fields which may become inhabited in the future
277287
@@ -283,6 +293,8 @@ mir_build_rustc_box_attribute_error = `#[rustc_box]` attribute used incorrectly
283293
.missing_box = `#[rustc_box]` requires the `owned_box` lang item
284294
285295
mir_build_static_in_pattern = statics cannot be referenced in patterns
296+
.label = can't be used in patterns
297+
mir_build_static_in_pattern_def = `static` defined here
286298
287299
mir_build_suggest_attempted_int_lit = alternatively, you could prepend the pattern with an underscore to define a new named variable; identifiers cannot begin with digits
288300
@@ -310,12 +322,12 @@ mir_build_trailing_irrefutable_let_patterns = trailing irrefutable {$count ->
310322
*[other] them
311323
} into the body
312324
313-
mir_build_type_not_structural =
314-
to use a constant of type `{$non_sm_ty}` in a pattern, `{$non_sm_ty}` must be annotated with `#[derive(PartialEq)]`
315-
325+
mir_build_type_not_structural = constant of non-structural type `{$ty}` in a pattern
326+
.label = constant of non-structural type
327+
mir_build_type_not_structural_def = `{$ty}` must be annotated with `#[derive(PartialEq)]` to be usable in patterns
316328
mir_build_type_not_structural_more_info = see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details
317-
318-
mir_build_type_not_structural_tip = the traits must be derived, manual `impl`s are not sufficient
329+
mir_build_type_not_structural_tip =
330+
the `PartialEq` trait must be derived, manual `impl`s are not sufficient; see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details
319331
320332
mir_build_unconditional_recursion = function cannot return without recursing
321333
.label = cannot return without recursing
@@ -334,6 +346,7 @@ mir_build_union_field_requires_unsafe_unsafe_op_in_unsafe_fn_allowed =
334346
.label = access to union field
335347
336348
mir_build_union_pattern = cannot use unions in constant patterns
349+
.label = can't use a `union` here
337350
338351
mir_build_unreachable_making_this_unreachable = collectively making this unreachable
339352

0 commit comments

Comments
 (0)