Skip to content

Commit db6fb90

Browse files
committed
Auto merge of rust-lang#6091 - ebroto:rustup, r=ebroto
Rustup changelog: none r? `@ghost`
2 parents 949b834 + 8bf27c5 commit db6fb90

File tree

5 files changed

+351
-1
lines changed

5 files changed

+351
-1
lines changed

clippy_lints/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#![feature(concat_idents)]
77
#![feature(crate_visibility_modifier)]
88
#![feature(drain_filter)]
9+
#![feature(in_band_lifetimes)]
910
#![feature(or_patterns)]
1011
#![feature(rustc_private)]
1112
#![feature(stmt_expr_attributes)]

clippy_lints/src/missing_const_for_fn.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1+
use crate::utils::qualify_min_const_fn::is_min_const_fn;
12
use crate::utils::{fn_has_unsatisfiable_preds, has_drop, is_entrypoint_fn, span_lint, trait_ref_of_method};
23
use rustc_hir as hir;
34
use rustc_hir::intravisit::FnKind;
45
use rustc_hir::{Body, Constness, FnDecl, GenericParamKind, HirId};
56
use rustc_lint::{LateContext, LateLintPass};
67
use rustc_middle::lint::in_external_macro;
7-
use rustc_mir::transform::qualify_min_const_fn::is_min_const_fn;
88
use rustc_session::{declare_lint_pass, declare_tool_lint};
99
use rustc_span::Span;
1010
use rustc_typeck::hir_ty_to_ty;

clippy_lints/src/redundant_clone.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone {
8787

8888
let maybe_storage_live_result = MaybeStorageLive
8989
.into_engine(cx.tcx, mir, def_id.to_def_id())
90+
.pass_name("redundant_clone")
9091
.iterate_to_fixpoint()
9192
.into_results_cursor(mir);
9293
let mut possible_borrower = {

clippy_lints/src/utils/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ pub mod internal_lints;
1818
pub mod numeric_literal;
1919
pub mod paths;
2020
pub mod ptr;
21+
pub mod qualify_min_const_fn;
2122
pub mod sugg;
2223
pub mod usage;
2324

Lines changed: 347 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,347 @@
1+
use rustc_hir as hir;
2+
use rustc_hir::def_id::DefId;
3+
use rustc_middle::mir::{
4+
Body, CastKind, NullOp, Operand, Place, ProjectionElem, Rvalue, Statement, StatementKind, Terminator,
5+
TerminatorKind,
6+
};
7+
use rustc_middle::ty::subst::GenericArgKind;
8+
use rustc_middle::ty::{self, adjustment::PointerCast, Ty, TyCtxt};
9+
use rustc_span::symbol::sym;
10+
use rustc_span::Span;
11+
use rustc_target::spec::abi::Abi::RustIntrinsic;
12+
use std::borrow::Cow;
13+
14+
type McfResult = Result<(), (Span, Cow<'static, str>)>;
15+
16+
pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, def_id: DefId, body: &'a Body<'tcx>) -> McfResult {
17+
let mut current = def_id;
18+
loop {
19+
let predicates = tcx.predicates_of(current);
20+
for (predicate, _) in predicates.predicates {
21+
match predicate.skip_binders() {
22+
ty::PredicateAtom::RegionOutlives(_)
23+
| ty::PredicateAtom::TypeOutlives(_)
24+
| ty::PredicateAtom::WellFormed(_)
25+
| ty::PredicateAtom::Projection(_)
26+
| ty::PredicateAtom::ConstEvaluatable(..)
27+
| ty::PredicateAtom::ConstEquate(..)
28+
| ty::PredicateAtom::TypeWellFormedFromEnv(..) => continue,
29+
ty::PredicateAtom::ObjectSafe(_) => panic!("object safe predicate on function: {:#?}", predicate),
30+
ty::PredicateAtom::ClosureKind(..) => panic!("closure kind predicate on function: {:#?}", predicate),
31+
ty::PredicateAtom::Subtype(_) => panic!("subtype predicate on function: {:#?}", predicate),
32+
ty::PredicateAtom::Trait(pred, _) => {
33+
if Some(pred.def_id()) == tcx.lang_items().sized_trait() {
34+
continue;
35+
}
36+
match pred.self_ty().kind() {
37+
ty::Param(ref p) => {
38+
let generics = tcx.generics_of(current);
39+
let def = generics.type_param(p, tcx);
40+
let span = tcx.def_span(def.def_id);
41+
return Err((
42+
span,
43+
"trait bounds other than `Sized` \
44+
on const fn parameters are unstable"
45+
.into(),
46+
));
47+
},
48+
// other kinds of bounds are either tautologies
49+
// or cause errors in other passes
50+
_ => continue,
51+
}
52+
},
53+
}
54+
}
55+
match predicates.parent {
56+
Some(parent) => current = parent,
57+
None => break,
58+
}
59+
}
60+
61+
for local in &body.local_decls {
62+
check_ty(tcx, local.ty, local.source_info.span)?;
63+
}
64+
// impl trait is gone in MIR, so check the return type manually
65+
check_ty(
66+
tcx,
67+
tcx.fn_sig(def_id).output().skip_binder(),
68+
body.local_decls.iter().next().unwrap().source_info.span,
69+
)?;
70+
71+
for bb in body.basic_blocks() {
72+
check_terminator(tcx, body, bb.terminator())?;
73+
for stmt in &bb.statements {
74+
check_statement(tcx, body, def_id, stmt)?;
75+
}
76+
}
77+
Ok(())
78+
}
79+
80+
fn check_ty(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span) -> McfResult {
81+
for arg in ty.walk() {
82+
let ty = match arg.unpack() {
83+
GenericArgKind::Type(ty) => ty,
84+
85+
// No constraints on lifetimes or constants, except potentially
86+
// constants' types, but `walk` will get to them as well.
87+
GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => continue,
88+
};
89+
90+
match ty.kind() {
91+
ty::Ref(_, _, hir::Mutability::Mut) => {
92+
return Err((span, "mutable references in const fn are unstable".into()));
93+
},
94+
ty::Opaque(..) => return Err((span, "`impl Trait` in const fn is unstable".into())),
95+
ty::FnPtr(..) => {
96+
return Err((span, "function pointers in const fn are unstable".into()));
97+
},
98+
ty::Dynamic(preds, _) => {
99+
for pred in preds.iter() {
100+
match pred.skip_binder() {
101+
ty::ExistentialPredicate::AutoTrait(_) | ty::ExistentialPredicate::Projection(_) => {
102+
return Err((
103+
span,
104+
"trait bounds other than `Sized` \
105+
on const fn parameters are unstable"
106+
.into(),
107+
));
108+
},
109+
ty::ExistentialPredicate::Trait(trait_ref) => {
110+
if Some(trait_ref.def_id) != tcx.lang_items().sized_trait() {
111+
return Err((
112+
span,
113+
"trait bounds other than `Sized` \
114+
on const fn parameters are unstable"
115+
.into(),
116+
));
117+
}
118+
},
119+
}
120+
}
121+
},
122+
_ => {},
123+
}
124+
}
125+
Ok(())
126+
}
127+
128+
fn check_rvalue(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, def_id: DefId, rvalue: &Rvalue<'tcx>, span: Span) -> McfResult {
129+
match rvalue {
130+
Rvalue::ThreadLocalRef(_) => Err((span, "cannot access thread local storage in const fn".into())),
131+
Rvalue::Repeat(operand, _) | Rvalue::Use(operand) => check_operand(tcx, operand, span, body),
132+
Rvalue::Len(place) | Rvalue::Discriminant(place) | Rvalue::Ref(_, _, place) | Rvalue::AddressOf(_, place) => {
133+
check_place(tcx, *place, span, body)
134+
},
135+
Rvalue::Cast(CastKind::Misc, operand, cast_ty) => {
136+
use rustc_middle::ty::cast::CastTy;
137+
let cast_in = CastTy::from_ty(operand.ty(body, tcx)).expect("bad input type for cast");
138+
let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
139+
match (cast_in, cast_out) {
140+
(CastTy::Ptr(_) | CastTy::FnPtr, CastTy::Int(_)) => {
141+
Err((span, "casting pointers to ints is unstable in const fn".into()))
142+
},
143+
_ => check_operand(tcx, operand, span, body),
144+
}
145+
},
146+
Rvalue::Cast(CastKind::Pointer(PointerCast::MutToConstPointer | PointerCast::ArrayToPointer), operand, _) => {
147+
check_operand(tcx, operand, span, body)
148+
},
149+
Rvalue::Cast(
150+
CastKind::Pointer(
151+
PointerCast::UnsafeFnPointer | PointerCast::ClosureFnPointer(_) | PointerCast::ReifyFnPointer,
152+
),
153+
_,
154+
_,
155+
) => Err((span, "function pointer casts are not allowed in const fn".into())),
156+
Rvalue::Cast(CastKind::Pointer(PointerCast::Unsize), op, cast_ty) => {
157+
let pointee_ty = if let Some(deref_ty) = cast_ty.builtin_deref(true) {
158+
deref_ty.ty
159+
} else {
160+
// We cannot allow this for now.
161+
return Err((span, "unsizing casts are only allowed for references right now".into()));
162+
};
163+
let unsized_ty = tcx.struct_tail_erasing_lifetimes(pointee_ty, tcx.param_env(def_id));
164+
if let ty::Slice(_) | ty::Str = unsized_ty.kind() {
165+
check_operand(tcx, op, span, body)?;
166+
// Casting/coercing things to slices is fine.
167+
Ok(())
168+
} else {
169+
// We just can't allow trait objects until we have figured out trait method calls.
170+
Err((span, "unsizing casts are not allowed in const fn".into()))
171+
}
172+
},
173+
// binops are fine on integers
174+
Rvalue::BinaryOp(_, lhs, rhs) | Rvalue::CheckedBinaryOp(_, lhs, rhs) => {
175+
check_operand(tcx, lhs, span, body)?;
176+
check_operand(tcx, rhs, span, body)?;
177+
let ty = lhs.ty(body, tcx);
178+
if ty.is_integral() || ty.is_bool() || ty.is_char() {
179+
Ok(())
180+
} else {
181+
Err((
182+
span,
183+
"only int, `bool` and `char` operations are stable in const fn".into(),
184+
))
185+
}
186+
},
187+
Rvalue::NullaryOp(NullOp::SizeOf, _) => Ok(()),
188+
Rvalue::NullaryOp(NullOp::Box, _) => Err((span, "heap allocations are not allowed in const fn".into())),
189+
Rvalue::UnaryOp(_, operand) => {
190+
let ty = operand.ty(body, tcx);
191+
if ty.is_integral() || ty.is_bool() {
192+
check_operand(tcx, operand, span, body)
193+
} else {
194+
Err((span, "only int and `bool` operations are stable in const fn".into()))
195+
}
196+
},
197+
Rvalue::Aggregate(_, operands) => {
198+
for operand in operands {
199+
check_operand(tcx, operand, span, body)?;
200+
}
201+
Ok(())
202+
},
203+
}
204+
}
205+
206+
fn check_statement(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, def_id: DefId, statement: &Statement<'tcx>) -> McfResult {
207+
let span = statement.source_info.span;
208+
match &statement.kind {
209+
StatementKind::Assign(box (place, rval)) => {
210+
check_place(tcx, *place, span, body)?;
211+
check_rvalue(tcx, body, def_id, rval, span)
212+
},
213+
214+
StatementKind::FakeRead(_, place) |
215+
// just an assignment
216+
StatementKind::SetDiscriminant { place, .. } => check_place(tcx, **place, span, body),
217+
218+
StatementKind::LlvmInlineAsm { .. } => Err((span, "cannot use inline assembly in const fn".into())),
219+
220+
// These are all NOPs
221+
StatementKind::StorageLive(_)
222+
| StatementKind::StorageDead(_)
223+
| StatementKind::Retag { .. }
224+
| StatementKind::AscribeUserType(..)
225+
| StatementKind::Coverage(..)
226+
| StatementKind::Nop => Ok(()),
227+
}
228+
}
229+
230+
fn check_operand(tcx: TyCtxt<'tcx>, operand: &Operand<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult {
231+
match operand {
232+
Operand::Move(place) | Operand::Copy(place) => check_place(tcx, *place, span, body),
233+
Operand::Constant(c) => match c.check_static_ptr(tcx) {
234+
Some(_) => Err((span, "cannot access `static` items in const fn".into())),
235+
None => Ok(()),
236+
},
237+
}
238+
}
239+
240+
fn check_place(tcx: TyCtxt<'tcx>, place: Place<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult {
241+
let mut cursor = place.projection.as_ref();
242+
while let [ref proj_base @ .., elem] = *cursor {
243+
cursor = proj_base;
244+
match elem {
245+
ProjectionElem::Field(..) => {
246+
let base_ty = Place::ty_from(place.local, &proj_base, body, tcx).ty;
247+
if let Some(def) = base_ty.ty_adt_def() {
248+
// No union field accesses in `const fn`
249+
if def.is_union() {
250+
return Err((span, "accessing union fields is unstable".into()));
251+
}
252+
}
253+
},
254+
ProjectionElem::ConstantIndex { .. }
255+
| ProjectionElem::Downcast(..)
256+
| ProjectionElem::Subslice { .. }
257+
| ProjectionElem::Deref
258+
| ProjectionElem::Index(_) => {},
259+
}
260+
}
261+
262+
Ok(())
263+
}
264+
265+
fn check_terminator(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, terminator: &Terminator<'tcx>) -> McfResult {
266+
let span = terminator.source_info.span;
267+
match &terminator.kind {
268+
TerminatorKind::FalseEdge { .. }
269+
| TerminatorKind::FalseUnwind { .. }
270+
| TerminatorKind::Goto { .. }
271+
| TerminatorKind::Return
272+
| TerminatorKind::Resume
273+
| TerminatorKind::Unreachable => Ok(()),
274+
275+
TerminatorKind::Drop { place, .. } => check_place(tcx, *place, span, body),
276+
TerminatorKind::DropAndReplace { place, value, .. } => {
277+
check_place(tcx, *place, span, body)?;
278+
check_operand(tcx, value, span, body)
279+
},
280+
281+
TerminatorKind::SwitchInt {
282+
discr,
283+
switch_ty: _,
284+
values: _,
285+
targets: _,
286+
} => check_operand(tcx, discr, span, body),
287+
288+
TerminatorKind::Abort => Err((span, "abort is not stable in const fn".into())),
289+
TerminatorKind::GeneratorDrop | TerminatorKind::Yield { .. } => {
290+
Err((span, "const fn generators are unstable".into()))
291+
},
292+
293+
TerminatorKind::Call {
294+
func,
295+
args,
296+
from_hir_call: _,
297+
destination: _,
298+
cleanup: _,
299+
fn_span: _,
300+
} => {
301+
let fn_ty = func.ty(body, tcx);
302+
if let ty::FnDef(fn_def_id, _) = *fn_ty.kind() {
303+
if !rustc_mir::const_eval::is_min_const_fn(tcx, fn_def_id) {
304+
return Err((
305+
span,
306+
format!(
307+
"can only call other `const fn` within a `const fn`, \
308+
but `{:?}` is not stable as `const fn`",
309+
func,
310+
)
311+
.into(),
312+
));
313+
}
314+
315+
// HACK: This is to "unstabilize" the `transmute` intrinsic
316+
// within const fns. `transmute` is allowed in all other const contexts.
317+
// This won't really scale to more intrinsics or functions. Let's allow const
318+
// transmutes in const fn before we add more hacks to this.
319+
if tcx.fn_sig(fn_def_id).abi() == RustIntrinsic && tcx.item_name(fn_def_id) == sym::transmute {
320+
return Err((
321+
span,
322+
"can only call `transmute` from const items, not `const fn`".into(),
323+
));
324+
}
325+
326+
check_operand(tcx, func, span, body)?;
327+
328+
for arg in args {
329+
check_operand(tcx, arg, span, body)?;
330+
}
331+
Ok(())
332+
} else {
333+
Err((span, "can only call other const fns within const fn".into()))
334+
}
335+
},
336+
337+
TerminatorKind::Assert {
338+
cond,
339+
expected: _,
340+
msg: _,
341+
target: _,
342+
cleanup: _,
343+
} => check_operand(tcx, cond, span, body),
344+
345+
TerminatorKind::InlineAsm { .. } => Err((span, "cannot use inline assembly in const fn".into())),
346+
}
347+
}

0 commit comments

Comments
 (0)