Skip to content

Commit 93297bf

Browse files
committed
Add a never type option to make diverging blocks ()
```rust #![allow(internal_features)] #![feature(never_type, rustc_attrs)] #![rustc_never_type_options(diverging_block_default = "unit")] fn main() { let _: u8 = { //~ error: expected `u8`, found `()` return; }; } ```
1 parent 6f2c6ef commit 93297bf

File tree

3 files changed

+51
-4
lines changed

3 files changed

+51
-4
lines changed

compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs

+26-1
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,29 @@ use rustc_trait_selection::traits::{self, ObligationCauseCode, SelectionContext}
4545
use std::iter;
4646
use std::mem;
4747

48+
#[derive(Clone, Copy, Default)]
49+
pub enum DivergingBlockBehavior {
50+
/// This is the current stable behavior:
51+
///
52+
/// ```rust
53+
/// {
54+
/// return;
55+
/// } // block has type = !, even though we are supposedly dropping it with `;`
56+
/// ```
57+
#[default]
58+
Never,
59+
60+
/// Alternative behavior:
61+
///
62+
/// ```ignore (very-unstable-new-attribute)
63+
/// #![rustc_never_type_options(diverging_block_default = "unit")]
64+
/// {
65+
/// return;
66+
/// } // block has type = (), since we are dropping `!` from `return` with `;`
67+
/// ```
68+
Unit,
69+
}
70+
4871
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
4972
pub(in super::super) fn check_casts(&mut self) {
5073
// don't hold the borrow to deferred_cast_checks while checking to avoid borrow checker errors
@@ -1710,7 +1733,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
17101733
//
17111734
// #41425 -- label the implicit `()` as being the
17121735
// "found type" here, rather than the "expected type".
1713-
if !self.diverges.get().is_always() {
1736+
if !self.diverges.get().is_always()
1737+
|| matches!(self.diverging_block_behavior, DivergingBlockBehavior::Unit)
1738+
{
17141739
// #50009 -- Do not point at the entire fn block span, point at the return type
17151740
// span, as it is the cause of the requirement, and
17161741
// `consider_hint_about_removing_semicolon` will point at the last expression

compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs

+24-3
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ mod suggestions;
66

77
use crate::coercion::DynamicCoerceMany;
88
use crate::fallback::DivergingFallbackBehavior;
9+
use crate::fn_ctxt::checks::DivergingBlockBehavior;
910
use crate::{CoroutineTypes, Diverges, EnclosingBreakables, Inherited};
1011
use hir::def_id::CRATE_DEF_ID;
1112
use rustc_errors::{DiagCtxt, ErrorGuaranteed};
@@ -112,6 +113,7 @@ pub struct FnCtxt<'a, 'tcx> {
112113
pub(super) fallback_has_occurred: Cell<bool>,
113114

114115
pub(super) diverging_fallback_behavior: DivergingFallbackBehavior,
116+
pub(super) diverging_block_behavior: DivergingBlockBehavior,
115117
}
116118

117119
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
@@ -120,7 +122,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
120122
param_env: ty::ParamEnv<'tcx>,
121123
body_id: LocalDefId,
122124
) -> FnCtxt<'a, 'tcx> {
123-
let diverging_fallback_behavior = parse_never_type_options_attr(inh.tcx);
125+
let (diverging_fallback_behavior, diverging_block_behavior) =
126+
parse_never_type_options_attr(inh.tcx);
124127
FnCtxt {
125128
body_id,
126129
param_env,
@@ -137,6 +140,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
137140
inh,
138141
fallback_has_occurred: Cell::new(false),
139142
diverging_fallback_behavior,
143+
diverging_block_behavior,
140144
}
141145
}
142146

@@ -381,13 +385,16 @@ impl<'tcx> LoweredTy<'tcx> {
381385
}
382386
}
383387

384-
fn parse_never_type_options_attr(tcx: TyCtxt<'_>) -> DivergingFallbackBehavior {
388+
fn parse_never_type_options_attr(
389+
tcx: TyCtxt<'_>,
390+
) -> (DivergingFallbackBehavior, DivergingBlockBehavior) {
385391
use DivergingFallbackBehavior::*;
386392

387393
// Error handling is dubious here (unwraps), but that's probably fine for an internal attribute.
388394
// Just don't write incorrect attributes <3
389395

390396
let mut fallback = None;
397+
let mut block = None;
391398

392399
let items = tcx
393400
.get_attr(CRATE_DEF_ID, sym::rustc_never_type_options)
@@ -409,6 +416,18 @@ fn parse_never_type_options_attr(tcx: TyCtxt<'_>) -> DivergingFallbackBehavior {
409416
continue;
410417
}
411418

419+
if item.has_name(sym::diverging_block_default) && fallback.is_none() {
420+
let mode = item.value_str().unwrap();
421+
match mode {
422+
sym::unit => block = Some(DivergingBlockBehavior::Unit),
423+
sym::never => block = Some(DivergingBlockBehavior::Never),
424+
_ => {
425+
tcx.dcx().span_err(item.span(), format!("unknown diverging block default: `{mode}` (supported: `unit` and `never`)"));
426+
}
427+
};
428+
continue;
429+
}
430+
412431
tcx.dcx().span_err(
413432
item.span(),
414433
format!(
@@ -422,5 +441,7 @@ fn parse_never_type_options_attr(tcx: TyCtxt<'_>) -> DivergingFallbackBehavior {
422441
if tcx.features().never_type_fallback { FallbackToNiko } else { FallbackToUnit }
423442
});
424443

425-
fallback
444+
let block = block.unwrap_or_default();
445+
446+
(fallback, block)
426447
}

compiler/rustc_span/src/symbol.rs

+1
Original file line numberDiff line numberDiff line change
@@ -689,6 +689,7 @@ symbols! {
689689
dispatch_from_dyn,
690690
div,
691691
div_assign,
692+
diverging_block_default,
692693
do_not_recommend,
693694
doc,
694695
doc_alias,

0 commit comments

Comments
 (0)