Skip to content

Commit b36666b

Browse files
committed
Remove Option<!> return types.
Several compiler functions have `Option<!>` for their return type. That's odd. The only valid return value is `None`, so why is this type used? Because it lets you write certain patterns slightly more concisely. E.g. if you have these common patterns: ``` let Some(a) = f() else { return }; let Ok(b) = g() else { return }; ``` you can shorten them to these: ``` let a = f()?; let b = g().ok()?; ``` Huh. An `Option` return type typically designates success/failure. How should I interpret the type signature of a function that always returns (i.e. doesn't panic), does useful work (modifying `&mut` arguments), and yet only ever fails? This idiom subverts the type system for a cute syntactic trick. Furthermore, returning `Option<!>` from a function F makes things syntactically more convenient within F, but makes things worse at F's callsites. The callsites can themselves use `?` with F but should not, because they will get an unconditional early return, which is almost certainly not desirable. Instead the return value should be ignored. (Note that some of callsites of `process_operand`, `process_immedate`, `process_assign` actually do use `?`, though the early return doesn't matter in these cases because nothing of significance comes after those calls. Ugh.) When I first saw this pattern I had no idea how to interpret it, and it took me several minutes of close reading to understand everything I've written above. I even started a Zulip thread about it to make sure I understood it properly. "Save a few characters by introducing types so weird that compiler devs have to discuss it on Zulip" feels like a bad trade-off to me. This commit replaces all the `Option<!>` return values and uses `else`/`return` (or something similar) to replace the relevant `?` uses. The result is slightly more verbose but much easier to understand.
1 parent bf662eb commit b36666b

File tree

3 files changed

+71
-64
lines changed

3 files changed

+71
-64
lines changed

compiler/rustc_mir_transform/src/dataflow_const_prop.rs

+7-5
Original file line numberDiff line numberDiff line change
@@ -382,7 +382,7 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> {
382382
place: PlaceIndex,
383383
mut operand: OpTy<'tcx>,
384384
projection: &[PlaceElem<'tcx>],
385-
) -> Option<!> {
385+
) {
386386
for &(mut proj_elem) in projection {
387387
if let PlaceElem::Index(index) = proj_elem {
388388
if let FlatSet::Elem(index) = state.get(index.into(), &self.map)
@@ -391,10 +391,14 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> {
391391
{
392392
proj_elem = PlaceElem::ConstantIndex { offset, min_length, from_end: false };
393393
} else {
394-
return None;
394+
return;
395395
}
396396
}
397-
operand = self.ecx.project(&operand, proj_elem).ok()?;
397+
operand = if let Ok(operand) = self.ecx.project(&operand, proj_elem) {
398+
operand
399+
} else {
400+
return;
401+
}
398402
}
399403

400404
self.map.for_each_projection_value(
@@ -426,8 +430,6 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> {
426430
}
427431
},
428432
);
429-
430-
None
431433
}
432434

433435
fn binary_op(

compiler/rustc_mir_transform/src/jump_threading.rs

+59-52
Original file line numberDiff line numberDiff line change
@@ -191,26 +191,26 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> {
191191

192192
/// Recursion entry point to find threading opportunities.
193193
#[instrument(level = "trace", skip(self))]
194-
fn start_from_switch(&mut self, bb: BasicBlock) -> Option<!> {
194+
fn start_from_switch(&mut self, bb: BasicBlock) {
195195
let bbdata = &self.body[bb];
196196
if bbdata.is_cleanup || self.loop_headers.contains(bb) {
197-
return None;
197+
return;
198198
}
199-
let (discr, targets) = bbdata.terminator().kind.as_switch()?;
200-
let discr = discr.place()?;
199+
let Some((discr, targets)) = bbdata.terminator().kind.as_switch() else { return };
200+
let Some(discr) = discr.place() else { return };
201201
debug!(?discr, ?bb);
202202

203203
let discr_ty = discr.ty(self.body, self.tcx).ty;
204-
let discr_layout = self.ecx.layout_of(discr_ty).ok()?;
204+
let Ok(discr_layout) = self.ecx.layout_of(discr_ty) else { return };
205205

206-
let discr = self.map.find(discr.as_ref())?;
206+
let Some(discr) = self.map.find(discr.as_ref()) else { return };
207207
debug!(?discr);
208208

209209
let cost = CostChecker::new(self.tcx, self.param_env, None, self.body);
210210
let mut state = State::new_reachable();
211211

212212
let conds = if let Some((value, then, else_)) = targets.as_static_if() {
213-
let value = ScalarInt::try_from_uint(value, discr_layout.size)?;
213+
let Some(value) = ScalarInt::try_from_uint(value, discr_layout.size) else { return };
214214
self.arena.alloc_from_iter([
215215
Condition { value, polarity: Polarity::Eq, target: then },
216216
Condition { value, polarity: Polarity::Ne, target: else_ },
@@ -225,7 +225,6 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> {
225225
state.insert_value_idx(discr, conds, self.map);
226226

227227
self.find_opportunity(bb, state, cost, 0);
228-
None
229228
}
230229

231230
/// Recursively walk statements backwards from this bb's terminator to find threading
@@ -364,18 +363,17 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> {
364363
lhs: PlaceIndex,
365364
rhs: ImmTy<'tcx>,
366365
state: &mut State<ConditionSet<'a>>,
367-
) -> Option<!> {
366+
) {
368367
let register_opportunity = |c: Condition| {
369368
debug!(?bb, ?c.target, "register");
370369
self.opportunities.push(ThreadingOpportunity { chain: vec![bb], target: c.target })
371370
};
372371

373-
let conditions = state.try_get_idx(lhs, self.map)?;
374-
if let Immediate::Scalar(Scalar::Int(int)) = *rhs {
372+
if let Some(conditions) = state.try_get_idx(lhs, self.map)
373+
&& let Immediate::Scalar(Scalar::Int(int)) = *rhs
374+
{
375375
conditions.iter_matches(int).for_each(register_opportunity);
376376
}
377-
378-
None
379377
}
380378

381379
/// If we expect `lhs ?= A`, we have an opportunity if we assume `constant == A`.
@@ -428,22 +426,23 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> {
428426
lhs: PlaceIndex,
429427
rhs: &Operand<'tcx>,
430428
state: &mut State<ConditionSet<'a>>,
431-
) -> Option<!> {
429+
) {
432430
match rhs {
433431
// If we expect `lhs ?= A`, we have an opportunity if we assume `constant == A`.
434432
Operand::Constant(constant) => {
435-
let constant =
436-
self.ecx.eval_mir_constant(&constant.const_, constant.span, None).ok()?;
433+
let Ok(constant) =
434+
self.ecx.eval_mir_constant(&constant.const_, constant.span, None)
435+
else {
436+
return;
437+
};
437438
self.process_constant(bb, lhs, constant, state);
438439
}
439440
// Transfer the conditions on the copied rhs.
440441
Operand::Move(rhs) | Operand::Copy(rhs) => {
441-
let rhs = self.map.find(rhs.as_ref())?;
442+
let Some(rhs) = self.map.find(rhs.as_ref()) else { return };
442443
state.insert_place_idx(rhs, lhs, self.map);
443444
}
444445
}
445-
446-
None
447446
}
448447

449448
#[instrument(level = "trace", skip(self))]
@@ -453,32 +452,39 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> {
453452
lhs_place: &Place<'tcx>,
454453
rhs: &Rvalue<'tcx>,
455454
state: &mut State<ConditionSet<'a>>,
456-
) -> Option<!> {
457-
let lhs = self.map.find(lhs_place.as_ref())?;
455+
) {
456+
let Some(lhs) = self.map.find(lhs_place.as_ref()) else { return };
458457
match rhs {
459-
Rvalue::Use(operand) => self.process_operand(bb, lhs, operand, state)?,
458+
// njn: remove braces on these two arms
459+
Rvalue::Use(operand) => {
460+
self.process_operand(bb, lhs, operand, state);
461+
}
460462
// Transfer the conditions on the copy rhs.
461463
Rvalue::CopyForDeref(rhs) => {
462-
self.process_operand(bb, lhs, &Operand::Copy(*rhs), state)?
464+
self.process_operand(bb, lhs, &Operand::Copy(*rhs), state);
463465
}
464466
Rvalue::Discriminant(rhs) => {
465-
let rhs = self.map.find_discr(rhs.as_ref())?;
467+
let Some(rhs) = self.map.find_discr(rhs.as_ref()) else { return };
466468
state.insert_place_idx(rhs, lhs, self.map);
467469
}
468470
// If we expect `lhs ?= A`, we have an opportunity if we assume `constant == A`.
469471
Rvalue::Aggregate(box ref kind, ref operands) => {
470472
let agg_ty = lhs_place.ty(self.body, self.tcx).ty;
471473
let lhs = match kind {
472474
// Do not support unions.
473-
AggregateKind::Adt(.., Some(_)) => return None,
475+
AggregateKind::Adt(.., Some(_)) => return,
474476
AggregateKind::Adt(_, variant_index, ..) if agg_ty.is_enum() => {
475477
if let Some(discr_target) = self.map.apply(lhs, TrackElem::Discriminant)
476478
&& let Ok(discr_value) =
477479
self.ecx.discriminant_for_variant(agg_ty, *variant_index)
478480
{
479481
self.process_immediate(bb, discr_target, discr_value, state);
480482
}
481-
self.map.apply(lhs, TrackElem::Variant(*variant_index))?
483+
if let Some(idx) = self.map.apply(lhs, TrackElem::Variant(*variant_index)) {
484+
idx
485+
} else {
486+
return;
487+
}
482488
}
483489
_ => lhs,
484490
};
@@ -490,8 +496,8 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> {
490496
}
491497
// Transfer the conditions on the copy rhs, after inversing polarity.
492498
Rvalue::UnaryOp(UnOp::Not, Operand::Move(place) | Operand::Copy(place)) => {
493-
let conditions = state.try_get_idx(lhs, self.map)?;
494-
let place = self.map.find(place.as_ref())?;
499+
let Some(conditions) = state.try_get_idx(lhs, self.map) else { return };
500+
let Some(place) = self.map.find(place.as_ref()) else { return };
495501
let conds = conditions.map(self.arena, Condition::inv);
496502
state.insert_value_idx(place, conds, self.map);
497503
}
@@ -502,21 +508,25 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> {
502508
box (Operand::Move(place) | Operand::Copy(place), Operand::Constant(value))
503509
| box (Operand::Constant(value), Operand::Move(place) | Operand::Copy(place)),
504510
) => {
505-
let conditions = state.try_get_idx(lhs, self.map)?;
506-
let place = self.map.find(place.as_ref())?;
511+
let Some(conditions) = state.try_get_idx(lhs, self.map) else { return };
512+
let Some(place) = self.map.find(place.as_ref()) else { return };
507513
let equals = match op {
508514
BinOp::Eq => ScalarInt::TRUE,
509515
BinOp::Ne => ScalarInt::FALSE,
510-
_ => return None,
516+
_ => return,
511517
};
512518
if value.const_.ty().is_floating_point() {
513519
// Floating point equality does not follow bit-patterns.
514520
// -0.0 and NaN both have special rules for equality,
515521
// and therefore we cannot use integer comparisons for them.
516522
// Avoid handling them, though this could be extended in the future.
517-
return None;
523+
return;
518524
}
519-
let value = value.const_.normalize(self.tcx, self.param_env).try_to_scalar_int()?;
525+
let Some(value) =
526+
value.const_.normalize(self.tcx, self.param_env).try_to_scalar_int()
527+
else {
528+
return;
529+
};
520530
let conds = conditions.map(self.arena, |c| Condition {
521531
value,
522532
polarity: if c.matches(equals) { Polarity::Eq } else { Polarity::Ne },
@@ -527,8 +537,6 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> {
527537

528538
_ => {}
529539
}
530-
531-
None
532540
}
533541

534542
#[instrument(level = "trace", skip(self))]
@@ -537,7 +545,7 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> {
537545
bb: BasicBlock,
538546
stmt: &Statement<'tcx>,
539547
state: &mut State<ConditionSet<'a>>,
540-
) -> Option<!> {
548+
) {
541549
let register_opportunity = |c: Condition| {
542550
debug!(?bb, ?c.target, "register");
543551
self.opportunities.push(ThreadingOpportunity { chain: vec![bb], target: c.target })
@@ -550,12 +558,12 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> {
550558
// If we expect `discriminant(place) ?= A`,
551559
// we have an opportunity if `variant_index ?= A`.
552560
StatementKind::SetDiscriminant { box place, variant_index } => {
553-
let discr_target = self.map.find_discr(place.as_ref())?;
561+
let Some(discr_target) = self.map.find_discr(place.as_ref()) else { return };
554562
let enum_ty = place.ty(self.body, self.tcx).ty;
555563
// `SetDiscriminant` may be a no-op if the assigned variant is the untagged variant
556564
// of a niche encoding. If we cannot ensure that we write to the discriminant, do
557565
// nothing.
558-
let enum_layout = self.ecx.layout_of(enum_ty).ok()?;
566+
let Ok(enum_layout) = self.ecx.layout_of(enum_ty) else { return };
559567
let writes_discriminant = match enum_layout.variants {
560568
Variants::Single { index } => {
561569
assert_eq!(index, *variant_index);
@@ -568,24 +576,25 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> {
568576
} => *variant_index != untagged_variant,
569577
};
570578
if writes_discriminant {
571-
let discr = self.ecx.discriminant_for_variant(enum_ty, *variant_index).ok()?;
572-
self.process_immediate(bb, discr_target, discr, state)?;
579+
let Ok(discr) = self.ecx.discriminant_for_variant(enum_ty, *variant_index)
580+
else {
581+
return;
582+
};
583+
self.process_immediate(bb, discr_target, discr, state);
573584
}
574585
}
575586
// If we expect `lhs ?= true`, we have an opportunity if we assume `lhs == true`.
576587
StatementKind::Intrinsic(box NonDivergingIntrinsic::Assume(
577588
Operand::Copy(place) | Operand::Move(place),
578589
)) => {
579-
let conditions = state.try_get(place.as_ref(), self.map)?;
590+
let Some(conditions) = state.try_get(place.as_ref(), self.map) else { return };
580591
conditions.iter_matches(ScalarInt::TRUE).for_each(register_opportunity);
581592
}
582593
StatementKind::Assign(box (lhs_place, rhs)) => {
583-
self.process_assign(bb, lhs_place, rhs, state)?;
594+
self.process_assign(bb, lhs_place, rhs, state);
584595
}
585596
_ => {}
586597
}
587-
588-
None
589598
}
590599

591600
#[instrument(level = "trace", skip(self, state, cost))]
@@ -638,17 +647,17 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> {
638647
targets: &SwitchTargets,
639648
target_bb: BasicBlock,
640649
state: &mut State<ConditionSet<'a>>,
641-
) -> Option<!> {
650+
) {
642651
debug_assert_ne!(target_bb, START_BLOCK);
643652
debug_assert_eq!(self.body.basic_blocks.predecessors()[target_bb].len(), 1);
644653

645-
let discr = discr.place()?;
654+
let Some(discr) = discr.place() else { return };
646655
let discr_ty = discr.ty(self.body, self.tcx).ty;
647-
let discr_layout = self.ecx.layout_of(discr_ty).ok()?;
648-
let conditions = state.try_get(discr.as_ref(), self.map)?;
656+
let Ok(discr_layout) = self.ecx.layout_of(discr_ty) else { return };
657+
let Some(conditions) = state.try_get(discr.as_ref(), self.map) else { return };
649658

650659
if let Some((value, _)) = targets.iter().find(|&(_, target)| target == target_bb) {
651-
let value = ScalarInt::try_from_uint(value, discr_layout.size)?;
660+
let Some(value) = ScalarInt::try_from_uint(value, discr_layout.size) else { return };
652661
debug_assert_eq!(targets.iter().filter(|&(_, target)| target == target_bb).count(), 1);
653662

654663
// We are inside `target_bb`. Since we have a single predecessor, we know we passed
@@ -662,7 +671,7 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> {
662671
} else if let Some((value, _, else_bb)) = targets.as_static_if()
663672
&& target_bb == else_bb
664673
{
665-
let value = ScalarInt::try_from_uint(value, discr_layout.size)?;
674+
let Some(value) = ScalarInt::try_from_uint(value, discr_layout.size) else { return };
666675

667676
// We only know that `discr != value`. That's much weaker information than
668677
// the equality we had in the previous arm. All we can conclude is that
@@ -675,8 +684,6 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> {
675684
}
676685
}
677686
}
678-
679-
None
680687
}
681688
}
682689

compiler/rustc_mir_transform/src/known_panics_lint.rs

+5-7
Original file line numberDiff line numberDiff line change
@@ -469,12 +469,12 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
469469
msg: &AssertKind<Operand<'tcx>>,
470470
cond: &Operand<'tcx>,
471471
location: Location,
472-
) -> Option<!> {
473-
let value = &self.eval_operand(cond)?;
472+
) {
473+
let Some(value) = &self.eval_operand(cond) else { return };
474474
trace!("assertion on {:?} should be {:?}", value, expected);
475475

476476
let expected = Scalar::from_bool(expected);
477-
let value_const = self.use_ecx(|this| this.ecx.read_scalar(value))?;
477+
let Some(value_const) = self.use_ecx(|this| this.ecx.read_scalar(value)) else { return };
478478

479479
if expected != value_const {
480480
// Poison all places this operand references so that further code
@@ -516,14 +516,12 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
516516
AssertKind::BoundsCheck { len, index }
517517
}
518518
// Remaining overflow errors are already covered by checks on the binary operators.
519-
AssertKind::Overflow(..) | AssertKind::OverflowNeg(_) => return None,
519+
AssertKind::Overflow(..) | AssertKind::OverflowNeg(_) => return,
520520
// Need proper const propagator for these.
521-
_ => return None,
521+
_ => return,
522522
};
523523
self.report_assert_as_lint(location, AssertLintKind::UnconditionalPanic, msg);
524524
}
525-
526-
None
527525
}
528526

529527
fn ensure_not_propagated(&self, local: Local) {

0 commit comments

Comments
 (0)