Skip to content

Commit 27a2bae

Browse files
Rollup merge of rust-lang#96788 - JakobDegen:checked-binop, r=oli-obk
Improve validator around field projections and checked bin ops The two commits are unrelated. In both cases, these rules were already documented in MIR docs.
2 parents a644197 + c4168fd commit 27a2bae

File tree

1 file changed

+96
-4
lines changed

1 file changed

+96
-4
lines changed

compiler/rustc_const_eval/src/transform/validate.rs

+96-4
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use rustc_middle::ty::{self, InstanceDef, ParamEnv, Ty, TyCtxt, TypeFoldable};
1414
use rustc_mir_dataflow::impls::MaybeStorageLive;
1515
use rustc_mir_dataflow::storage::AlwaysLiveLocals;
1616
use rustc_mir_dataflow::{Analysis, ResultsCursor};
17-
use rustc_target::abi::Size;
17+
use rustc_target::abi::{Size, VariantIdx};
1818

1919
#[derive(Copy, Clone, Debug)]
2020
enum EdgeKind {
@@ -244,6 +244,60 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
244244
self.fail(location, format!("bad index ({:?} != usize)", index_ty))
245245
}
246246
}
247+
if let ProjectionElem::Field(f, ty) = elem {
248+
let parent = Place { local, projection: self.tcx.intern_place_elems(proj_base) };
249+
let parent_ty = parent.ty(&self.body.local_decls, self.tcx);
250+
let fail_out_of_bounds = |this: &Self, location| {
251+
this.fail(location, format!("Out of bounds field {:?} for {:?}", f, parent_ty));
252+
};
253+
let check_equal = |this: &Self, location, f_ty| {
254+
if !this.mir_assign_valid_types(ty, f_ty) {
255+
this.fail(
256+
location,
257+
format!(
258+
"Field projection `{:?}.{:?}` specified type `{:?}`, but actual type is {:?}",
259+
parent, f, ty, f_ty
260+
)
261+
)
262+
}
263+
};
264+
match parent_ty.ty.kind() {
265+
ty::Tuple(fields) => {
266+
let Some(f_ty) = fields.get(f.as_usize()) else {
267+
fail_out_of_bounds(self, location);
268+
return;
269+
};
270+
check_equal(self, location, *f_ty);
271+
}
272+
ty::Adt(adt_def, substs) => {
273+
let var = parent_ty.variant_index.unwrap_or(VariantIdx::from_u32(0));
274+
let Some(field) = adt_def.variant(var).fields.get(f.as_usize()) else {
275+
fail_out_of_bounds(self, location);
276+
return;
277+
};
278+
check_equal(self, location, field.ty(self.tcx, substs));
279+
}
280+
ty::Closure(_, substs) => {
281+
let substs = substs.as_closure();
282+
let Some(f_ty) = substs.upvar_tys().nth(f.as_usize()) else {
283+
fail_out_of_bounds(self, location);
284+
return;
285+
};
286+
check_equal(self, location, f_ty);
287+
}
288+
ty::Generator(_, substs, _) => {
289+
let substs = substs.as_generator();
290+
let Some(f_ty) = substs.upvar_tys().nth(f.as_usize()) else {
291+
fail_out_of_bounds(self, location);
292+
return;
293+
};
294+
check_equal(self, location, f_ty);
295+
}
296+
_ => {
297+
self.fail(location, format!("{:?} does not have fields", parent_ty.ty));
298+
}
299+
}
300+
}
247301
self.super_projection_elem(local, proj_base, elem, context, location);
248302
}
249303

@@ -291,7 +345,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
291345
ty::Array(..) | ty::Slice(..)
292346
);
293347
}
294-
Rvalue::BinaryOp(op, vals) | Rvalue::CheckedBinaryOp(op, vals) => {
348+
Rvalue::BinaryOp(op, vals) => {
295349
use BinOp::*;
296350
let a = vals.0.ty(&self.body.local_decls, self.tcx);
297351
let b = vals.1.ty(&self.body.local_decls, self.tcx);
@@ -355,17 +409,55 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
355409
for x in [a, b] {
356410
check_kinds!(
357411
x,
358-
"Cannot perform op on type {:?}",
412+
"Cannot perform arithmetic on type {:?}",
359413
ty::Uint(..) | ty::Int(..) | ty::Float(..)
360414
)
361415
}
362416
if a != b {
363417
self.fail(
364418
location,
365-
format!("Cannot perform op on unequal types {:?} and {:?}", a, b),
419+
format!(
420+
"Cannot perform arithmetic on unequal types {:?} and {:?}",
421+
a, b
422+
),
423+
);
424+
}
425+
}
426+
}
427+
}
428+
Rvalue::CheckedBinaryOp(op, vals) => {
429+
use BinOp::*;
430+
let a = vals.0.ty(&self.body.local_decls, self.tcx);
431+
let b = vals.1.ty(&self.body.local_decls, self.tcx);
432+
match op {
433+
Add | Sub | Mul => {
434+
for x in [a, b] {
435+
check_kinds!(
436+
x,
437+
"Cannot perform checked arithmetic on type {:?}",
438+
ty::Uint(..) | ty::Int(..)
439+
)
440+
}
441+
if a != b {
442+
self.fail(
443+
location,
444+
format!(
445+
"Cannot perform checked arithmetic on unequal types {:?} and {:?}",
446+
a, b
447+
),
366448
);
367449
}
368450
}
451+
Shl | Shr => {
452+
for x in [a, b] {
453+
check_kinds!(
454+
x,
455+
"Cannot perform checked shift on non-integer type {:?}",
456+
ty::Uint(..) | ty::Int(..)
457+
)
458+
}
459+
}
460+
_ => self.fail(location, format!("There is no checked version of {:?}", op)),
369461
}
370462
}
371463
Rvalue::UnaryOp(op, operand) => {

0 commit comments

Comments
 (0)