Skip to content

Commit f1bacb2

Browse files
vertexcliqueoli-obk
authored andcommitted
Check intrinsics for callability in const fns
1 parent 4787e97 commit f1bacb2

File tree

14 files changed

+212
-130
lines changed

14 files changed

+212
-130
lines changed

src/libcore/intrinsics.rs

+1
Original file line numberDiff line numberDiff line change
@@ -939,6 +939,7 @@ extern "rust-intrinsic" {
939939
/// }
940940
/// ```
941941
#[stable(feature = "rust1", since = "1.0.0")]
942+
#[rustc_const_unstable(feature = "const_transmute")]
942943
pub fn transmute<T, U>(e: T) -> U;
943944

944945
/// Returns `true` if the actual type given as `T` requires drop

src/librustc/ty/constness.rs

+67-3
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ use crate::ty::query::Providers;
22
use crate::hir::def_id::DefId;
33
use crate::hir;
44
use crate::ty::TyCtxt;
5-
use syntax_pos::symbol::Symbol;
5+
use syntax_pos::symbol::{sym, Symbol};
6+
use rustc_target::spec::abi::Abi;
67
use crate::hir::map::blocks::FnLikeNode;
78
use syntax::attr;
89

@@ -63,13 +64,76 @@ impl<'tcx> TyCtxt<'tcx> {
6364

6465

6566
pub fn provide(providers: &mut Providers<'_>) {
66-
/// only checks whether the function has a `const` modifier
67+
/// Const evaluability whitelist is here to check evaluability at the
68+
/// top level beforehand.
69+
fn is_const_intrinsic(tcx: TyCtxt<'_>, def_id: DefId) -> Option<bool> {
70+
match tcx.fn_sig(def_id).abi() {
71+
Abi::RustIntrinsic |
72+
Abi::PlatformIntrinsic => {
73+
match tcx.item_name(def_id) {
74+
// Keep this list in the same order as the match patterns in
75+
// `librustc_mir/interpret/intrinsics.rs`
76+
| sym::caller_location
77+
78+
| sym::min_align_of
79+
| sym::pref_align_of
80+
| sym::needs_drop
81+
| sym::size_of
82+
| sym::type_id
83+
| sym::type_name
84+
85+
| sym::ctpop
86+
| sym::cttz
87+
| sym::cttz_nonzero
88+
| sym::ctlz
89+
| sym::ctlz_nonzero
90+
| sym::bswap
91+
| sym::bitreverse
92+
93+
| sym::wrapping_add
94+
| sym::wrapping_sub
95+
| sym::wrapping_mul
96+
| sym::add_with_overflow
97+
| sym::sub_with_overflow
98+
| sym::mul_with_overflow
99+
100+
| sym::saturating_add
101+
| sym::saturating_sub
102+
103+
| sym::unchecked_shl
104+
| sym::unchecked_shr
105+
106+
| sym::rotate_left
107+
| sym::rotate_right
108+
109+
| sym::ptr_offset_from
110+
111+
| sym::transmute
112+
113+
| sym::simd_insert
114+
115+
| sym::simd_extract
116+
117+
=> Some(true),
118+
119+
_ => Some(false)
120+
}
121+
}
122+
_ => None
123+
}
124+
}
125+
126+
/// Checks whether the function has a `const` modifier or, in case it is an intrinsic, whether
127+
/// said intrinsic is on the whitelist for being const callable.
67128
fn is_const_fn_raw(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
68129
let hir_id = tcx.hir().as_local_hir_id(def_id)
69130
.expect("Non-local call to local provider is_const_fn");
70131

71132
let node = tcx.hir().get(hir_id);
72-
if let Some(fn_like) = FnLikeNode::from_node(node) {
133+
134+
if let Some(whitelisted) = is_const_intrinsic(tcx, def_id) {
135+
whitelisted
136+
} else if let Some(fn_like) = FnLikeNode::from_node(node) {
73137
fn_like.constness() == hir::Constness::Const
74138
} else if let hir::Node::Ctor(_) = node {
75139
true

src/librustc_feature/active.rs

-4
Original file line numberDiff line numberDiff line change
@@ -408,10 +408,6 @@ declare_features! (
408408
/// Allows using `#[doc(keyword = "...")]`.
409409
(active, doc_keyword, "1.28.0", Some(51315), None),
410410

411-
/// Allows reinterpretation of the bits of a value of one type as another
412-
/// type during const eval.
413-
(active, const_transmute, "1.29.0", Some(53605), None),
414-
415411
/// Allows using `try {...}` expressions.
416412
(active, try_blocks, "1.29.0", Some(31436), None),
417413

src/librustc_metadata/rmeta/decoder.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1364,6 +1364,8 @@ impl<'a, 'tcx> CrateMetadata {
13641364
let constness = match self.kind(id) {
13651365
EntryKind::Method(data) => data.decode(self).fn_data.constness,
13661366
EntryKind::Fn(data) => data.decode(self).constness,
1367+
// Some intrinsics can be const fn
1368+
EntryKind::ForeignFn(data) => data.decode(self).constness,
13671369
EntryKind::Variant(..) | EntryKind::Struct(..) => hir::Constness::Const,
13681370
_ => hir::Constness::NotConst,
13691371
};

src/librustc_metadata/rmeta/encoder.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -1525,7 +1525,11 @@ impl EncodeContext<'tcx> {
15251525
hir::ForeignItemKind::Fn(_, ref names, _) => {
15261526
let data = FnData {
15271527
asyncness: hir::IsAsync::NotAsync,
1528-
constness: hir::Constness::NotConst,
1528+
constness: if self.tcx.is_const_fn_raw(def_id) {
1529+
hir::Constness::Const
1530+
} else {
1531+
hir::Constness::NotConst
1532+
},
15291533
param_names: self.encode_fn_param_names(names),
15301534
};
15311535
EntryKind::ForeignFn(self.lazy(data))

src/librustc_mir/interpret/intrinsics.rs

+66-63
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//! looking at their MIR. Intrinsics/functions supported here are shared by CTFE
33
//! and miri.
44
5-
use syntax::symbol::Symbol;
5+
use syntax_pos::symbol::{sym, Symbol};
66
use syntax_pos::Span;
77
use rustc::ty;
88
use rustc::ty::layout::{LayoutOf, Primitive, Size};
@@ -22,7 +22,7 @@ mod caller_location;
2222
mod type_name;
2323

2424
fn numeric_intrinsic<'tcx, Tag>(
25-
name: &str,
25+
name: Symbol,
2626
bits: u128,
2727
kind: Primitive,
2828
) -> InterpResult<'tcx, Scalar<Tag>> {
@@ -32,11 +32,11 @@ fn numeric_intrinsic<'tcx, Tag>(
3232
};
3333
let extra = 128 - size.bits() as u128;
3434
let bits_out = match name {
35-
"ctpop" => bits.count_ones() as u128,
36-
"ctlz" => bits.leading_zeros() as u128 - extra,
37-
"cttz" => (bits << extra).trailing_zeros() as u128 - extra,
38-
"bswap" => (bits << extra).swap_bytes(),
39-
"bitreverse" => (bits << extra).reverse_bits(),
35+
sym::ctpop => bits.count_ones() as u128,
36+
sym::ctlz => bits.leading_zeros() as u128 - extra,
37+
sym::cttz => (bits << extra).trailing_zeros() as u128 - extra,
38+
sym::bswap => (bits << extra).swap_bytes(),
39+
sym::bitreverse => (bits << extra).reverse_bits(),
4040
_ => bug!("not a numeric intrinsic: {}", name),
4141
};
4242
Ok(Scalar::from_uint(bits_out, size))
@@ -51,9 +51,9 @@ crate fn eval_nullary_intrinsic<'tcx>(
5151
substs: SubstsRef<'tcx>,
5252
) -> InterpResult<'tcx, &'tcx ty::Const<'tcx>> {
5353
let tp_ty = substs.type_at(0);
54-
let name = &*tcx.item_name(def_id).as_str();
54+
let name = tcx.item_name(def_id);
5555
Ok(match name {
56-
"type_name" => {
56+
sym::type_name => {
5757
let alloc = type_name::alloc_type_name(tcx, tp_ty);
5858
tcx.mk_const(ty::Const {
5959
val: ty::ConstKind::Value(ConstValue::Slice {
@@ -64,20 +64,20 @@ crate fn eval_nullary_intrinsic<'tcx>(
6464
ty: tcx.mk_static_str(),
6565
})
6666
},
67-
"needs_drop" => ty::Const::from_bool(tcx, tp_ty.needs_drop(tcx, param_env)),
68-
"size_of" |
69-
"min_align_of" |
70-
"pref_align_of" => {
67+
sym::needs_drop => ty::Const::from_bool(tcx, tp_ty.needs_drop(tcx, param_env)),
68+
sym::size_of |
69+
sym::min_align_of |
70+
sym::pref_align_of => {
7171
let layout = tcx.layout_of(param_env.and(tp_ty)).map_err(|e| err_inval!(Layout(e)))?;
7272
let n = match name {
73-
"pref_align_of" => layout.align.pref.bytes(),
74-
"min_align_of" => layout.align.abi.bytes(),
75-
"size_of" => layout.size.bytes(),
73+
sym::pref_align_of => layout.align.pref.bytes(),
74+
sym::min_align_of => layout.align.abi.bytes(),
75+
sym::size_of => layout.size.bytes(),
7676
_ => bug!(),
7777
};
7878
ty::Const::from_usize(tcx, n)
7979
},
80-
"type_id" => ty::Const::from_bits(
80+
sym::type_id => ty::Const::from_bits(
8181
tcx,
8282
tcx.type_id_hash(tp_ty).into(),
8383
param_env.and(tcx.types.u64),
@@ -96,30 +96,32 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
9696
ret: Option<(PlaceTy<'tcx, M::PointerTag>, mir::BasicBlock)>,
9797
) -> InterpResult<'tcx, bool> {
9898
let substs = instance.substs;
99-
let intrinsic_name = &*self.tcx.item_name(instance.def_id()).as_str();
99+
let intrinsic_name = self.tcx.item_name(instance.def_id());
100100

101101
// We currently do not handle any intrinsics that are *allowed* to diverge,
102102
// but `transmute` could lack a return place in case of UB.
103103
let (dest, ret) = match ret {
104104
Some(p) => p,
105105
None => match intrinsic_name {
106-
"transmute" => throw_ub!(Unreachable),
106+
sym::transmute => throw_ub!(Unreachable),
107107
_ => return Ok(false),
108108
}
109109
};
110110

111+
// Keep the patterns in this match ordered the same as the list in
112+
// `src/librustc/ty/constness.rs`
111113
match intrinsic_name {
112-
"caller_location" => {
114+
sym::caller_location => {
113115
let location = self.alloc_caller_location_for_span(span);
114116
self.write_scalar(location.ptr, dest)?;
115117
}
116118

117-
"min_align_of" |
118-
"pref_align_of" |
119-
"needs_drop" |
120-
"size_of" |
121-
"type_id" |
122-
"type_name" => {
119+
sym::min_align_of |
120+
sym::pref_align_of |
121+
sym::needs_drop |
122+
sym::size_of |
123+
sym::type_id |
124+
sym::type_name => {
123125
let gid = GlobalId {
124126
instance,
125127
promoted: None,
@@ -129,13 +131,13 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
129131
self.copy_op(val, dest)?;
130132
}
131133

132-
| "ctpop"
133-
| "cttz"
134-
| "cttz_nonzero"
135-
| "ctlz"
136-
| "ctlz_nonzero"
137-
| "bswap"
138-
| "bitreverse" => {
134+
| sym::ctpop
135+
| sym::cttz
136+
| sym::cttz_nonzero
137+
| sym::ctlz
138+
| sym::ctlz_nonzero
139+
| sym::bswap
140+
| sym::bitreverse => {
139141
let ty = substs.type_at(0);
140142
let layout_of = self.layout_of(ty)?;
141143
let val = self.read_scalar(args[0])?.not_undef()?;
@@ -144,31 +146,32 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
144146
ty::layout::Abi::Scalar(ref scalar) => scalar.value,
145147
_ => throw_unsup!(TypeNotPrimitive(ty)),
146148
};
147-
let out_val = if intrinsic_name.ends_with("_nonzero") {
148-
if bits == 0 {
149-
throw_ub_format!("`{}` called on 0", intrinsic_name);
150-
}
151-
numeric_intrinsic(intrinsic_name.trim_end_matches("_nonzero"), bits, kind)?
152-
} else {
153-
numeric_intrinsic(intrinsic_name, bits, kind)?
149+
let (nonzero, intrinsic_name) = match intrinsic_name {
150+
sym::cttz_nonzero => (true, sym::cttz),
151+
sym::ctlz_nonzero => (true, sym::ctlz),
152+
other => (false, other),
154153
};
154+
if nonzero && bits == 0 {
155+
throw_ub_format!("`{}_nonzero` called on 0", intrinsic_name);
156+
}
157+
let out_val = numeric_intrinsic(intrinsic_name, bits, kind)?;
155158
self.write_scalar(out_val, dest)?;
156159
}
157-
| "wrapping_add"
158-
| "wrapping_sub"
159-
| "wrapping_mul"
160-
| "add_with_overflow"
161-
| "sub_with_overflow"
162-
| "mul_with_overflow" => {
160+
| sym::wrapping_add
161+
| sym::wrapping_sub
162+
| sym::wrapping_mul
163+
| sym::add_with_overflow
164+
| sym::sub_with_overflow
165+
| sym::mul_with_overflow => {
163166
let lhs = self.read_immediate(args[0])?;
164167
let rhs = self.read_immediate(args[1])?;
165168
let (bin_op, ignore_overflow) = match intrinsic_name {
166-
"wrapping_add" => (BinOp::Add, true),
167-
"wrapping_sub" => (BinOp::Sub, true),
168-
"wrapping_mul" => (BinOp::Mul, true),
169-
"add_with_overflow" => (BinOp::Add, false),
170-
"sub_with_overflow" => (BinOp::Sub, false),
171-
"mul_with_overflow" => (BinOp::Mul, false),
169+
sym::wrapping_add => (BinOp::Add, true),
170+
sym::wrapping_sub => (BinOp::Sub, true),
171+
sym::wrapping_mul => (BinOp::Mul, true),
172+
sym::add_with_overflow => (BinOp::Add, false),
173+
sym::sub_with_overflow => (BinOp::Sub, false),
174+
sym::mul_with_overflow => (BinOp::Mul, false),
172175
_ => bug!("Already checked for int ops")
173176
};
174177
if ignore_overflow {
@@ -177,10 +180,10 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
177180
self.binop_with_overflow(bin_op, lhs, rhs, dest)?;
178181
}
179182
}
180-
"saturating_add" | "saturating_sub" => {
183+
sym::saturating_add | sym::saturating_sub => {
181184
let l = self.read_immediate(args[0])?;
182185
let r = self.read_immediate(args[1])?;
183-
let is_add = intrinsic_name == "saturating_add";
186+
let is_add = intrinsic_name == sym::saturating_add;
184187
let (val, overflowed, _ty) = self.overflowing_binary_op(if is_add {
185188
BinOp::Add
186189
} else {
@@ -220,12 +223,12 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
220223
};
221224
self.write_scalar(val, dest)?;
222225
}
223-
"unchecked_shl" | "unchecked_shr" => {
226+
sym::unchecked_shl | sym::unchecked_shr => {
224227
let l = self.read_immediate(args[0])?;
225228
let r = self.read_immediate(args[1])?;
226229
let bin_op = match intrinsic_name {
227-
"unchecked_shl" => BinOp::Shl,
228-
"unchecked_shr" => BinOp::Shr,
230+
sym::unchecked_shl => BinOp::Shl,
231+
sym::unchecked_shr => BinOp::Shr,
229232
_ => bug!("Already checked for int ops")
230233
};
231234
let (val, overflowed, _ty) = self.overflowing_binary_op(bin_op, l, r)?;
@@ -236,7 +239,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
236239
}
237240
self.write_scalar(val, dest)?;
238241
}
239-
"rotate_left" | "rotate_right" => {
242+
sym::rotate_left | sym::rotate_right => {
240243
// rotate_left: (X << (S % BW)) | (X >> ((BW - S) % BW))
241244
// rotate_right: (X << ((BW - S) % BW)) | (X >> (S % BW))
242245
let layout = self.layout_of(substs.type_at(0))?;
@@ -247,7 +250,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
247250
let width_bits = layout.size.bits() as u128;
248251
let shift_bits = raw_shift_bits % width_bits;
249252
let inv_shift_bits = (width_bits - shift_bits) % width_bits;
250-
let result_bits = if intrinsic_name == "rotate_left" {
253+
let result_bits = if intrinsic_name == sym::rotate_left {
251254
(val_bits << shift_bits) | (val_bits >> inv_shift_bits)
252255
} else {
253256
(val_bits >> shift_bits) | (val_bits << inv_shift_bits)
@@ -257,7 +260,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
257260
self.write_scalar(result, dest)?;
258261
}
259262

260-
"ptr_offset_from" => {
263+
sym::ptr_offset_from => {
261264
let isize_layout = self.layout_of(self.tcx.types.isize)?;
262265
let a = self.read_immediate(args[0])?.to_scalar()?;
263266
let b = self.read_immediate(args[1])?.to_scalar()?;
@@ -303,10 +306,10 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
303306
}
304307
}
305308

306-
"transmute" => {
309+
sym::transmute => {
307310
self.copy_op_transmute(args[0], dest)?;
308311
}
309-
"simd_insert" => {
312+
sym::simd_insert => {
310313
let index = u64::from(self.read_scalar(args[1])?.to_u32()?);
311314
let elem = args[2];
312315
let input = args[0];
@@ -337,7 +340,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
337340
self.copy_op(value, place)?;
338341
}
339342
}
340-
"simd_extract" => {
343+
sym::simd_extract => {
341344
let index = u64::from(self.read_scalar(args[1])?.to_u32()?);
342345
let (len, e_ty) = args[0].layout.ty.simd_size_and_type(self.tcx.tcx);
343346
assert!(

0 commit comments

Comments
 (0)