Skip to content

Commit d6fa7e1

Browse files
committed
Fixed compiler error in lint checker triggered by associated types
When a function argument bound by `Pointer` is an associated type, we only perform substitutions using the parameters from the callsite but don't attempt to normalize since it may not succeed. A simplified version of the scenario that triggered this error was added as a test case. Also fixed `Pointer::fmt` which was being double-counted when called outside of macros and added a test case for this.
1 parent 432ebd5 commit d6fa7e1

File tree

3 files changed

+122
-83
lines changed

3 files changed

+122
-83
lines changed

compiler/rustc_mir/src/transform/function_references.rs

+65-53
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
use rustc_hir::def_id::DefId;
22
use rustc_middle::mir::visit::Visitor;
33
use rustc_middle::mir::*;
4-
use rustc_middle::ty::{self, subst::GenericArgKind, PredicateAtom, Ty, TyCtxt, TyS};
4+
use rustc_middle::ty::{
5+
self,
6+
subst::{GenericArgKind, Subst},
7+
PredicateAtom, Ty, TyCtxt, TyS,
8+
};
59
use rustc_session::lint::builtin::FUNCTION_ITEM_REFERENCES;
610
use rustc_span::{symbol::sym, Span};
711
use rustc_target::spec::abi::Abi;
@@ -33,48 +37,50 @@ impl<'a, 'tcx> Visitor<'tcx> for FunctionItemRefChecker<'a, 'tcx> {
3337
fn_span: _,
3438
} = &terminator.kind
3539
{
36-
let func_ty = func.ty(self.body, self.tcx);
37-
if let ty::FnDef(def_id, substs_ref) = *func_ty.kind() {
38-
//check arguments for `std::mem::transmute`
39-
if self.tcx.is_diagnostic_item(sym::transmute, def_id) {
40-
let arg_ty = args[0].ty(self.body, self.tcx);
41-
for generic_inner_ty in arg_ty.walk() {
42-
if let GenericArgKind::Type(inner_ty) = generic_inner_ty.unpack() {
43-
if let Some(fn_id) = FunctionItemRefChecker::is_fn_ref(inner_ty) {
44-
let ident = self.tcx.item_name(fn_id).to_ident_string();
45-
let source_info = *self.body.source_info(location);
46-
let span = self.nth_arg_span(&args, 0);
47-
self.emit_lint(ident, fn_id, source_info, span);
40+
let source_info = *self.body.source_info(location);
41+
//this handles all function calls outside macros
42+
if !source_info.span.from_expansion() {
43+
let func_ty = func.ty(self.body, self.tcx);
44+
if let ty::FnDef(def_id, substs_ref) = *func_ty.kind() {
45+
//handle `std::mem::transmute`
46+
if self.tcx.is_diagnostic_item(sym::transmute, def_id) {
47+
let arg_ty = args[0].ty(self.body, self.tcx);
48+
for generic_inner_ty in arg_ty.walk() {
49+
if let GenericArgKind::Type(inner_ty) = generic_inner_ty.unpack() {
50+
if let Some(fn_id) = FunctionItemRefChecker::is_fn_ref(inner_ty) {
51+
let ident = self.tcx.item_name(fn_id).to_ident_string();
52+
let span = self.nth_arg_span(&args, 0);
53+
self.emit_lint(ident, fn_id, source_info, span);
54+
}
4855
}
4956
}
50-
}
51-
} else {
52-
//check arguments for any function with `std::fmt::Pointer` as a bound trait
53-
let param_env = self.tcx.param_env(def_id);
54-
let bounds = param_env.caller_bounds();
55-
for bound in bounds {
56-
if let Some(bound_ty) = self.is_pointer_trait(&bound.skip_binders()) {
57-
let arg_defs = self.tcx.fn_sig(def_id).skip_binder().inputs();
58-
for (arg_num, arg_def) in arg_defs.iter().enumerate() {
59-
for generic_inner_ty in arg_def.walk() {
60-
if let GenericArgKind::Type(inner_ty) =
61-
generic_inner_ty.unpack()
62-
{
63-
//if any type reachable from the argument types in the fn sig matches the type bound by `Pointer`
64-
if TyS::same_type(inner_ty, bound_ty) {
65-
//check if this type is a function reference in the function call
66-
let norm_ty =
67-
self.tcx.subst_and_normalize_erasing_regions(
68-
substs_ref, param_env, &inner_ty,
69-
);
70-
if let Some(fn_id) =
71-
FunctionItemRefChecker::is_fn_ref(norm_ty)
72-
{
73-
let ident =
74-
self.tcx.item_name(fn_id).to_ident_string();
75-
let source_info = *self.body.source_info(location);
76-
let span = self.nth_arg_span(&args, arg_num);
77-
self.emit_lint(ident, fn_id, source_info, span);
57+
} else {
58+
//handle any function call with `std::fmt::Pointer` as a bound trait
59+
//this includes calls to `std::fmt::Pointer::fmt` outside of macros
60+
let param_env = self.tcx.param_env(def_id);
61+
let bounds = param_env.caller_bounds();
62+
for bound in bounds {
63+
if let Some(bound_ty) = self.is_pointer_trait(&bound.skip_binders()) {
64+
//get the argument types as they appear in the function signature
65+
let arg_defs = self.tcx.fn_sig(def_id).skip_binder().inputs();
66+
for (arg_num, arg_def) in arg_defs.iter().enumerate() {
67+
//for all types reachable from the argument type in the fn sig
68+
for generic_inner_ty in arg_def.walk() {
69+
if let GenericArgKind::Type(inner_ty) =
70+
generic_inner_ty.unpack()
71+
{
72+
//if the inner type matches the type bound by `Pointer`
73+
if TyS::same_type(inner_ty, bound_ty) {
74+
//do a substitution using the parameters from the callsite
75+
let subst_ty = inner_ty.subst(self.tcx, substs_ref);
76+
if let Some(fn_id) =
77+
FunctionItemRefChecker::is_fn_ref(subst_ty)
78+
{
79+
let ident =
80+
self.tcx.item_name(fn_id).to_ident_string();
81+
let span = self.nth_arg_span(&args, arg_num);
82+
self.emit_lint(ident, fn_id, source_info, span);
83+
}
7884
}
7985
}
8086
}
@@ -87,19 +93,25 @@ impl<'a, 'tcx> Visitor<'tcx> for FunctionItemRefChecker<'a, 'tcx> {
8793
}
8894
self.super_terminator(terminator, location);
8995
}
90-
//check for `std::fmt::Pointer::<T>::fmt` where T is a function reference
91-
//this is used in formatting macros, but doesn't rely on the specific expansion
96+
//This handles `std::fmt::Pointer::fmt` when it's used in the formatting macros.
97+
//It's handled as an operand instead of a Call terminator so it won't depend on
98+
//whether the formatting macros call `fmt` directly, transmute it first or other
99+
//internal fmt details.
92100
fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) {
93-
let op_ty = operand.ty(self.body, self.tcx);
94-
if let ty::FnDef(def_id, substs_ref) = *op_ty.kind() {
95-
if self.tcx.is_diagnostic_item(sym::pointer_trait_fmt, def_id) {
96-
let param_ty = substs_ref.type_at(0);
97-
if let Some(fn_id) = FunctionItemRefChecker::is_fn_ref(param_ty) {
98-
let source_info = *self.body.source_info(location);
99-
let callsite_ctxt = source_info.span.source_callsite().ctxt();
100-
let span = source_info.span.with_ctxt(callsite_ctxt);
101-
let ident = self.tcx.item_name(fn_id).to_ident_string();
102-
self.emit_lint(ident, fn_id, source_info, span);
101+
let source_info = *self.body.source_info(location);
102+
if source_info.span.from_expansion() {
103+
let op_ty = operand.ty(self.body, self.tcx);
104+
if let ty::FnDef(def_id, substs_ref) = *op_ty.kind() {
105+
if self.tcx.is_diagnostic_item(sym::pointer_trait_fmt, def_id) {
106+
let param_ty = substs_ref.type_at(0);
107+
if let Some(fn_id) = FunctionItemRefChecker::is_fn_ref(param_ty) {
108+
//the operand's ctxt wouldn't display the lint since it's inside a macro
109+
//so we have to use the callsite's ctxt
110+
let callsite_ctxt = source_info.span.source_callsite().ctxt();
111+
let span = source_info.span.with_ctxt(callsite_ctxt);
112+
let ident = self.tcx.item_name(fn_id).to_ident_string();
113+
self.emit_lint(ident, fn_id, source_info, span);
114+
}
103115
}
104116
}
105117
}

src/test/ui/lint/function-references.rs

+21
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#![feature(c_variadic)]
33
#![warn(function_item_references)]
44
use std::fmt::Pointer;
5+
use std::fmt::Formatter;
56

67
fn nop() { }
78
fn foo() -> u32 { 42 }
@@ -20,6 +21,25 @@ fn parameterized_call_fn<F: Fn(u32) -> u32>(f: &F, x: u32) { f(x); }
2021
fn print_ptr<F: Pointer>(f: F) { println!("{:p}", f); }
2122
fn bound_by_ptr_trait<F: Pointer>(_f: F) { }
2223
fn bound_by_ptr_trait_tuple<F: Pointer, G: Pointer>(_t: (F, G)) { }
24+
fn implicit_ptr_trait<F>(f: &F) { println!("{:p}", f); }
25+
26+
//case found in tinyvec that triggered a compiler error in an earlier version of the lint checker
27+
trait HasItem {
28+
type Item;
29+
fn assoc_item(&self) -> Self::Item;
30+
}
31+
fn _format_assoc_item<T: HasItem>(data: T, f: &mut Formatter) -> std::fmt::Result
32+
where T::Item: Pointer {
33+
//when the arg type bound by `Pointer` is an associated type, we shouldn't attempt to normalize
34+
Pointer::fmt(&data.assoc_item(), f)
35+
}
36+
37+
//simple test to make sure that calls to `Pointer::fmt` aren't double counted
38+
fn _call_pointer_fmt(f: &mut Formatter) -> std::fmt::Result {
39+
let zst_ref = &foo;
40+
Pointer::fmt(&zst_ref, f)
41+
//~^ WARNING cast `foo` with `as fn() -> _` to obtain a function pointer
42+
}
2343

2444
fn main() {
2545
//`let` bindings with function references shouldn't lint
@@ -126,6 +146,7 @@ fn main() {
126146
bound_by_ptr_trait_tuple((&foo, &bar));
127147
//~^ WARNING cast `foo` with `as fn() -> _` to obtain a function pointer
128148
//~^^ WARNING cast `bar` with `as fn(_) -> _` to obtain a function pointer
149+
implicit_ptr_trait(&bar); // ignore
129150

130151
//correct ways to pass function pointers as arguments bound by std::fmt::Pointer
131152
print_ptr(bar as fn(u32) -> u32);
+36-30
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
warning: cast `foo` with `as fn() -> _` to obtain a function pointer
2-
--> $DIR/function-references.rs:57:22
2+
--> $DIR/function-references.rs:40:18
33
|
4-
LL | println!("{:p}", &foo);
5-
| ^^^^
4+
LL | Pointer::fmt(&zst_ref, f)
5+
| ^^^^^^^^
66
|
77
note: the lint level is defined here
88
--> $DIR/function-references.rs:3:9
@@ -11,160 +11,166 @@ LL | #![warn(function_item_references)]
1111
| ^^^^^^^^^^^^^^^^^^^^^^^^
1212

1313
warning: cast `foo` with `as fn() -> _` to obtain a function pointer
14-
--> $DIR/function-references.rs:59:20
14+
--> $DIR/function-references.rs:77:22
15+
|
16+
LL | println!("{:p}", &foo);
17+
| ^^^^
18+
19+
warning: cast `foo` with `as fn() -> _` to obtain a function pointer
20+
--> $DIR/function-references.rs:79:20
1521
|
1622
LL | print!("{:p}", &foo);
1723
| ^^^^
1824

1925
warning: cast `foo` with `as fn() -> _` to obtain a function pointer
20-
--> $DIR/function-references.rs:61:21
26+
--> $DIR/function-references.rs:81:21
2127
|
2228
LL | format!("{:p}", &foo);
2329
| ^^^^
2430

2531
warning: cast `foo` with `as fn() -> _` to obtain a function pointer
26-
--> $DIR/function-references.rs:64:22
32+
--> $DIR/function-references.rs:84:22
2733
|
2834
LL | println!("{:p}", &foo as *const _);
2935
| ^^^^^^^^^^^^^^^^
3036

3137
warning: cast `foo` with `as fn() -> _` to obtain a function pointer
32-
--> $DIR/function-references.rs:66:22
38+
--> $DIR/function-references.rs:86:22
3339
|
3440
LL | println!("{:p}", zst_ref);
3541
| ^^^^^^^
3642

3743
warning: cast `foo` with `as fn() -> _` to obtain a function pointer
38-
--> $DIR/function-references.rs:68:22
44+
--> $DIR/function-references.rs:88:22
3945
|
4046
LL | println!("{:p}", cast_zst_ptr);
4147
| ^^^^^^^^^^^^
4248

4349
warning: cast `foo` with `as fn() -> _` to obtain a function pointer
44-
--> $DIR/function-references.rs:70:22
50+
--> $DIR/function-references.rs:90:22
4551
|
4652
LL | println!("{:p}", coerced_zst_ptr);
4753
| ^^^^^^^^^^^^^^^
4854

4955
warning: cast `foo` with `as fn() -> _` to obtain a function pointer
50-
--> $DIR/function-references.rs:73:22
56+
--> $DIR/function-references.rs:93:22
5157
|
5258
LL | println!("{:p}", &fn_item);
5359
| ^^^^^^^^
5460

5561
warning: cast `foo` with `as fn() -> _` to obtain a function pointer
56-
--> $DIR/function-references.rs:75:22
62+
--> $DIR/function-references.rs:95:22
5763
|
5864
LL | println!("{:p}", indirect_ref);
5965
| ^^^^^^^^^^^^
6066

6167
warning: cast `nop` with `as fn()` to obtain a function pointer
62-
--> $DIR/function-references.rs:78:22
68+
--> $DIR/function-references.rs:98:22
6369
|
6470
LL | println!("{:p}", &nop);
6571
| ^^^^
6672

6773
warning: cast `bar` with `as fn(_) -> _` to obtain a function pointer
68-
--> $DIR/function-references.rs:80:22
74+
--> $DIR/function-references.rs:100:22
6975
|
7076
LL | println!("{:p}", &bar);
7177
| ^^^^
7278

7379
warning: cast `baz` with `as fn(_, _) -> _` to obtain a function pointer
74-
--> $DIR/function-references.rs:82:22
80+
--> $DIR/function-references.rs:102:22
7581
|
7682
LL | println!("{:p}", &baz);
7783
| ^^^^
7884

7985
warning: cast `unsafe_fn` with `as unsafe fn()` to obtain a function pointer
80-
--> $DIR/function-references.rs:84:22
86+
--> $DIR/function-references.rs:104:22
8187
|
8288
LL | println!("{:p}", &unsafe_fn);
8389
| ^^^^^^^^^^
8490

8591
warning: cast `c_fn` with `as extern "C" fn()` to obtain a function pointer
86-
--> $DIR/function-references.rs:86:22
92+
--> $DIR/function-references.rs:106:22
8793
|
8894
LL | println!("{:p}", &c_fn);
8995
| ^^^^^
9096

9197
warning: cast `unsafe_c_fn` with `as unsafe extern "C" fn()` to obtain a function pointer
92-
--> $DIR/function-references.rs:88:22
98+
--> $DIR/function-references.rs:108:22
9399
|
94100
LL | println!("{:p}", &unsafe_c_fn);
95101
| ^^^^^^^^^^^^
96102

97103
warning: cast `variadic` with `as unsafe extern "C" fn(_, ...)` to obtain a function pointer
98-
--> $DIR/function-references.rs:90:22
104+
--> $DIR/function-references.rs:110:22
99105
|
100106
LL | println!("{:p}", &variadic);
101107
| ^^^^^^^^^
102108

103109
warning: cast `var` with `as fn(_) -> _` to obtain a function pointer
104-
--> $DIR/function-references.rs:92:22
110+
--> $DIR/function-references.rs:112:22
105111
|
106112
LL | println!("{:p}", &std::env::var::<String>);
107113
| ^^^^^^^^^^^^^^^^^^^^^^^^
108114

109115
warning: cast `nop` with `as fn()` to obtain a function pointer
110-
--> $DIR/function-references.rs:95:32
116+
--> $DIR/function-references.rs:115:32
111117
|
112118
LL | println!("{:p} {:p} {:p}", &nop, &foo, &bar);
113119
| ^^^^
114120

115121
warning: cast `foo` with `as fn() -> _` to obtain a function pointer
116-
--> $DIR/function-references.rs:95:38
122+
--> $DIR/function-references.rs:115:38
117123
|
118124
LL | println!("{:p} {:p} {:p}", &nop, &foo, &bar);
119125
| ^^^^
120126

121127
warning: cast `bar` with `as fn(_) -> _` to obtain a function pointer
122-
--> $DIR/function-references.rs:95:44
128+
--> $DIR/function-references.rs:115:44
123129
|
124130
LL | println!("{:p} {:p} {:p}", &nop, &foo, &bar);
125131
| ^^^^
126132

127133
warning: cast `foo` with `as fn() -> _` to obtain a function pointer
128-
--> $DIR/function-references.rs:110:41
134+
--> $DIR/function-references.rs:130:41
129135
|
130136
LL | std::mem::transmute::<_, usize>(&foo);
131137
| ^^^^
132138

133139
warning: cast `foo` with `as fn() -> _` to obtain a function pointer
134-
--> $DIR/function-references.rs:112:50
140+
--> $DIR/function-references.rs:132:50
135141
|
136142
LL | std::mem::transmute::<_, (usize, usize)>((&foo, &bar));
137143
| ^^^^^^^^^^^^
138144

139145
warning: cast `bar` with `as fn(_) -> _` to obtain a function pointer
140-
--> $DIR/function-references.rs:112:50
146+
--> $DIR/function-references.rs:132:50
141147
|
142148
LL | std::mem::transmute::<_, (usize, usize)>((&foo, &bar));
143149
| ^^^^^^^^^^^^
144150

145151
warning: cast `bar` with `as fn(_) -> _` to obtain a function pointer
146-
--> $DIR/function-references.rs:122:15
152+
--> $DIR/function-references.rs:142:15
147153
|
148154
LL | print_ptr(&bar);
149155
| ^^^^
150156

151157
warning: cast `bar` with `as fn(_) -> _` to obtain a function pointer
152-
--> $DIR/function-references.rs:124:24
158+
--> $DIR/function-references.rs:144:24
153159
|
154160
LL | bound_by_ptr_trait(&bar);
155161
| ^^^^
156162

157163
warning: cast `bar` with `as fn(_) -> _` to obtain a function pointer
158-
--> $DIR/function-references.rs:126:30
164+
--> $DIR/function-references.rs:146:30
159165
|
160166
LL | bound_by_ptr_trait_tuple((&foo, &bar));
161167
| ^^^^^^^^^^^^
162168

163169
warning: cast `foo` with `as fn() -> _` to obtain a function pointer
164-
--> $DIR/function-references.rs:126:30
170+
--> $DIR/function-references.rs:146:30
165171
|
166172
LL | bound_by_ptr_trait_tuple((&foo, &bar));
167173
| ^^^^^^^^^^^^
168174

169-
warning: 27 warnings emitted
175+
warning: 28 warnings emitted
170176

0 commit comments

Comments
 (0)