Skip to content

Commit ab6c292

Browse files
committed
Add warn-by-default lint against unpredictable fn pointer comparisons
1 parent 5b8bc56 commit ab6c292

File tree

5 files changed

+167
-2
lines changed

5 files changed

+167
-2
lines changed

compiler/rustc_lint/messages.ftl

+4
Original file line numberDiff line numberDiff line change
@@ -538,6 +538,10 @@ lint_unknown_lint =
538538
lint_unknown_tool_in_scoped_lint = unknown tool name `{$tool_name}` found in scoped lint: `{$tool_name}::{$lint_name}`
539539
.help = add `#![register_tool({$tool_name})]` to the crate root
540540
541+
lint_unpredictable_fn_pointer_comparisons = function pointer comparisons do not produce meaningful results since their addresses are not guaranteed to be unique
542+
.note_foreign_fn = even foreign function pointers are not guaranteed to be unique and could vary between different code generation units
543+
.note_deduplicated_fn = furthermore different functions could have the same address after being merged together
544+
541545
lint_unsupported_group = `{$lint_group}` lint group is not supported with ´--force-warn´
542546
543547
lint_untranslatable_diag = diagnostics should be created using translatable messages

compiler/rustc_lint/src/lints.rs

+6
Original file line numberDiff line numberDiff line change
@@ -1644,6 +1644,12 @@ pub enum AmbiguousWidePointerComparisonsAddrSuggestion<'a> {
16441644
},
16451645
}
16461646

1647+
#[derive(LintDiagnostic)]
1648+
#[diag(lint_unpredictable_fn_pointer_comparisons)]
1649+
#[note(lint_note_foreign_fn)]
1650+
#[note(lint_note_deduplicated_fn)]
1651+
pub struct UnpredictableFunctionPointerComparisons;
1652+
16471653
pub struct ImproperCTypes<'a> {
16481654
pub ty: Ty<'a>,
16491655
pub desc: &'a str,

compiler/rustc_lint/src/types.rs

+57-2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ use crate::{
77
InvalidNanComparisonsSuggestion, OnlyCastu8ToChar, OverflowingBinHex,
88
OverflowingBinHexSign, OverflowingBinHexSignBitSub, OverflowingBinHexSub, OverflowingInt,
99
OverflowingIntHelp, OverflowingLiteral, OverflowingUInt, RangeEndpointOutOfRange,
10-
UnusedComparisons, UseInclusiveRange, VariantSizeDifferencesDiag,
10+
UnpredictableFunctionPointerComparisons, UnusedComparisons, UseInclusiveRange,
11+
VariantSizeDifferencesDiag,
1112
},
1213
};
1314
use crate::{LateContext, LateLintPass, LintContext};
@@ -169,6 +170,32 @@ declare_lint! {
169170
"detects ambiguous wide pointer comparisons"
170171
}
171172

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

187215
impl TypeLimits {
@@ -758,6 +786,30 @@ fn lint_wide_pointer<'tcx>(
758786
);
759787
}
760788

789+
fn lint_fn_pointer<'tcx>(
790+
cx: &LateContext<'tcx>,
791+
e: &'tcx hir::Expr<'tcx>,
792+
_binop: hir::BinOpKind,
793+
l: &'tcx hir::Expr<'tcx>,
794+
r: &'tcx hir::Expr<'tcx>,
795+
) {
796+
let Some(l_ty) = cx.typeck_results().expr_ty_opt(l) else { return };
797+
let Some(r_ty) = cx.typeck_results().expr_ty_opt(r) else { return };
798+
799+
let l_ty = l_ty.peel_refs();
800+
let r_ty = r_ty.peel_refs();
801+
802+
if !l_ty.is_fn() || !r_ty.is_fn() {
803+
return;
804+
}
805+
806+
cx.emit_spanned_lint(
807+
UNPREDICTABLE_FUNCTION_POINTER_COMPARISONS,
808+
e.span,
809+
UnpredictableFunctionPointerComparisons,
810+
);
811+
}
812+
761813
impl<'tcx> LateLintPass<'tcx> for TypeLimits {
762814
fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx hir::Expr<'tcx>) {
763815
match e.kind {
@@ -775,6 +827,7 @@ impl<'tcx> LateLintPass<'tcx> for TypeLimits {
775827
} else {
776828
lint_nan(cx, e, binop, l, r);
777829
lint_wide_pointer(cx, e, binop.node, l, r);
830+
lint_fn_pointer(cx, e, binop.node, l, r);
778831
}
779832
}
780833
}
@@ -786,13 +839,15 @@ impl<'tcx> LateLintPass<'tcx> for TypeLimits {
786839
&& let Some(binop) = partialeq_binop(diag_item) =>
787840
{
788841
lint_wide_pointer(cx, e, binop, l, r);
842+
lint_fn_pointer(cx, e, binop, l, r);
789843
}
790844
hir::ExprKind::MethodCall(_, l, [r], _)
791845
if let Some(def_id) = cx.typeck_results().type_dependent_def_id(e.hir_id)
792846
&& let Some(diag_item) = cx.tcx.get_diagnostic_name(def_id)
793847
&& let Some(binop) = partialeq_binop(diag_item) =>
794848
{
795849
lint_wide_pointer(cx, e, binop, l, r);
850+
lint_fn_pointer(cx, e, binop, l, r);
796851
}
797852
_ => {}
798853
};

tests/ui/lint/fn-ptr-comparisons.rs

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// check-pass
2+
3+
extern "C" {
4+
fn test();
5+
}
6+
7+
fn a() {}
8+
9+
extern "C" fn c() {}
10+
11+
fn main() {
12+
type F = fn();
13+
let f: F = a;
14+
let g: F = f;
15+
16+
let _ = f == a;
17+
//~^ WARN function pointer comparisons
18+
let _ = f != a;
19+
//~^ WARN function pointer comparisons
20+
let _ = f == g;
21+
//~^ WARN function pointer comparisons
22+
let _ = f == f;
23+
//~^ WARN function pointer comparisons
24+
let _ = g == g;
25+
//~^ WARN function pointer comparisons
26+
27+
let cfn: extern "C" fn() = c;
28+
let _ = cfn == c;
29+
//~^ WARN function pointer comparisons
30+
31+
let t: unsafe extern "C" fn() = test;
32+
let _ = t == test;
33+
//~^ WARN function pointer comparisons
34+
}
+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
warning: function pointer comparisons do not produce meaningful results since their addresses are not guaranteed to be unique
2+
--> $DIR/fn-ptr-comparisons.rs:16:13
3+
|
4+
LL | let _ = f == a;
5+
| ^^^^^^
6+
|
7+
= note: even foreign function pointers are not guaranteed to be unique and could vary between different code generation units
8+
= note: furthermore different functions could have the same address after being merged together
9+
= note: `#[warn(unpredictable_function_pointer_comparisons)]` on by default
10+
11+
warning: function pointer comparisons do not produce meaningful results since their addresses are not guaranteed to be unique
12+
--> $DIR/fn-ptr-comparisons.rs:18:13
13+
|
14+
LL | let _ = f != a;
15+
| ^^^^^^
16+
|
17+
= note: even foreign function pointers are not guaranteed to be unique and could vary between different code generation units
18+
= note: furthermore different functions could have the same address after being merged together
19+
20+
warning: function pointer comparisons do not produce meaningful results since their addresses are not guaranteed to be unique
21+
--> $DIR/fn-ptr-comparisons.rs:20:13
22+
|
23+
LL | let _ = f == g;
24+
| ^^^^^^
25+
|
26+
= note: even foreign function pointers are not guaranteed to be unique and could vary between different code generation units
27+
= note: furthermore different functions could have the same address after being merged together
28+
29+
warning: function pointer comparisons do not produce meaningful results since their addresses are not guaranteed to be unique
30+
--> $DIR/fn-ptr-comparisons.rs:22:13
31+
|
32+
LL | let _ = f == f;
33+
| ^^^^^^
34+
|
35+
= note: even foreign function pointers are not guaranteed to be unique and could vary between different code generation units
36+
= note: furthermore different functions could have the same address after being merged together
37+
38+
warning: function pointer comparisons do not produce meaningful results since their addresses are not guaranteed to be unique
39+
--> $DIR/fn-ptr-comparisons.rs:24:13
40+
|
41+
LL | let _ = g == g;
42+
| ^^^^^^
43+
|
44+
= note: even foreign function pointers are not guaranteed to be unique and could vary between different code generation units
45+
= note: furthermore different functions could have the same address after being merged together
46+
47+
warning: function pointer comparisons do not produce meaningful results since their addresses are not guaranteed to be unique
48+
--> $DIR/fn-ptr-comparisons.rs:28:13
49+
|
50+
LL | let _ = cfn == c;
51+
| ^^^^^^^^
52+
|
53+
= note: even foreign function pointers are not guaranteed to be unique and could vary between different code generation units
54+
= note: furthermore different functions could have the same address after being merged together
55+
56+
warning: function pointer comparisons do not produce meaningful results since their addresses are not guaranteed to be unique
57+
--> $DIR/fn-ptr-comparisons.rs:32:13
58+
|
59+
LL | let _ = t == test;
60+
| ^^^^^^^^^
61+
|
62+
= note: even foreign function pointers are not guaranteed to be unique and could vary between different code generation units
63+
= note: furthermore different functions could have the same address after being merged together
64+
65+
warning: 7 warnings emitted
66+

0 commit comments

Comments
 (0)