Skip to content

Commit

Permalink
Add a never type option to make diverging blocks ()
Browse files Browse the repository at this point in the history
```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;
    };
}
```
  • Loading branch information
WaffleLapkin committed Mar 21, 2024
1 parent 6f2c6ef commit 93297bf
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 4 deletions.
27 changes: 26 additions & 1 deletion compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,29 @@ use rustc_trait_selection::traits::{self, ObligationCauseCode, SelectionContext}
use std::iter;
use std::mem;

#[derive(Clone, Copy, Default)]
pub enum DivergingBlockBehavior {
/// This is the current stable behavior:
///
/// ```rust
/// {
/// return;
/// } // block has type = !, even though we are supposedly dropping it with `;`
/// ```
#[default]
Never,

/// Alternative behavior:
///
/// ```ignore (very-unstable-new-attribute)
/// #![rustc_never_type_options(diverging_block_default = "unit")]
/// {
/// return;
/// } // block has type = (), since we are dropping `!` from `return` with `;`
/// ```
Unit,
}

impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
pub(in super::super) fn check_casts(&mut self) {
// don't hold the borrow to deferred_cast_checks while checking to avoid borrow checker errors
Expand Down Expand Up @@ -1710,7 +1733,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
//
// #41425 -- label the implicit `()` as being the
// "found type" here, rather than the "expected type".
if !self.diverges.get().is_always() {
if !self.diverges.get().is_always()
|| matches!(self.diverging_block_behavior, DivergingBlockBehavior::Unit)
{
// #50009 -- Do not point at the entire fn block span, point at the return type
// span, as it is the cause of the requirement, and
// `consider_hint_about_removing_semicolon` will point at the last expression
Expand Down
27 changes: 24 additions & 3 deletions compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ mod suggestions;

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

pub(super) diverging_fallback_behavior: DivergingFallbackBehavior,
pub(super) diverging_block_behavior: DivergingBlockBehavior,
}

impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
Expand All @@ -120,7 +122,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
param_env: ty::ParamEnv<'tcx>,
body_id: LocalDefId,
) -> FnCtxt<'a, 'tcx> {
let diverging_fallback_behavior = parse_never_type_options_attr(inh.tcx);
let (diverging_fallback_behavior, diverging_block_behavior) =
parse_never_type_options_attr(inh.tcx);
FnCtxt {
body_id,
param_env,
Expand All @@ -137,6 +140,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
inh,
fallback_has_occurred: Cell::new(false),
diverging_fallback_behavior,
diverging_block_behavior,
}
}

Expand Down Expand Up @@ -381,13 +385,16 @@ impl<'tcx> LoweredTy<'tcx> {
}
}

fn parse_never_type_options_attr(tcx: TyCtxt<'_>) -> DivergingFallbackBehavior {
fn parse_never_type_options_attr(
tcx: TyCtxt<'_>,
) -> (DivergingFallbackBehavior, DivergingBlockBehavior) {
use DivergingFallbackBehavior::*;

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

let mut fallback = None;
let mut block = None;

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

if item.has_name(sym::diverging_block_default) && fallback.is_none() {
let mode = item.value_str().unwrap();
match mode {
sym::unit => block = Some(DivergingBlockBehavior::Unit),
sym::never => block = Some(DivergingBlockBehavior::Never),
_ => {
tcx.dcx().span_err(item.span(), format!("unknown diverging block default: `{mode}` (supported: `unit` and `never`)"));
}
};
continue;
}

tcx.dcx().span_err(
item.span(),
format!(
Expand All @@ -422,5 +441,7 @@ fn parse_never_type_options_attr(tcx: TyCtxt<'_>) -> DivergingFallbackBehavior {
if tcx.features().never_type_fallback { FallbackToNiko } else { FallbackToUnit }
});

fallback
let block = block.unwrap_or_default();

(fallback, block)
}
1 change: 1 addition & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -689,6 +689,7 @@ symbols! {
dispatch_from_dyn,
div,
div_assign,
diverging_block_default,
do_not_recommend,
doc,
doc_alias,
Expand Down

0 comments on commit 93297bf

Please sign in to comment.