Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a never type option to make diverging blocks () #122843

Merged
merged 2 commits into from
Mar 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 5 additions & 2 deletions compiler/rustc_feature/src/builtin_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -597,9 +597,12 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
),

rustc_attr!(
rustc_never_type_mode, Normal, template!(NameValueStr: "fallback_to_unit|fallback_to_niko|fallback_to_never|no_fallback"), ErrorFollowing,
rustc_never_type_options,
Normal,
template!(List: r#"/*opt*/ fallback = "unit|niko|never|no""#),
ErrorFollowing,
EncodeCrossCrate::No,
"`rustc_never_type_fallback` is used to experiment with never type fallback and work on \
"`rustc_never_type_options` is used to experiment with never type fallback and work on \
never type stabilization, and will never be stable"
),

Expand Down
36 changes: 4 additions & 32 deletions compiler/rustc_hir_typeck/src/fallback.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,11 @@ use rustc_data_structures::{
graph::{iterate::DepthFirstSearch, vec_graph::VecGraph},
unord::{UnordBag, UnordMap, UnordSet},
};
use rustc_hir::def_id::CRATE_DEF_ID;
use rustc_infer::infer::{DefineOpaqueTypes, InferOk};
use rustc_middle::ty::{self, Ty};
use rustc_span::sym;

enum DivergingFallbackBehavior {
#[derive(Copy, Clone)]
pub enum DivergingFallbackBehavior {
/// Always fallback to `()` (aka "always spontaneous decay")
FallbackToUnit,
/// Sometimes fallback to `!`, but mainly fallback to `()` so that most of the crates are not broken.
Expand Down Expand Up @@ -78,9 +77,8 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
return false;
}

let diverging_behavior = self.diverging_fallback_behavior();
let diverging_fallback =
self.calculate_diverging_fallback(&unresolved_variables, diverging_behavior);
let diverging_fallback = self
.calculate_diverging_fallback(&unresolved_variables, self.diverging_fallback_behavior);

// We do fallback in two passes, to try to generate
// better error messages.
Expand All @@ -94,32 +92,6 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
fallback_occurred
}

fn diverging_fallback_behavior(&self) -> DivergingFallbackBehavior {
let Some((mode, span)) = self
.tcx
.get_attr(CRATE_DEF_ID, sym::rustc_never_type_mode)
.map(|attr| (attr.value_str().unwrap(), attr.span))
else {
if self.tcx.features().never_type_fallback {
return DivergingFallbackBehavior::FallbackToNiko;
}

return DivergingFallbackBehavior::FallbackToUnit;
};

match mode {
sym::fallback_to_unit => DivergingFallbackBehavior::FallbackToUnit,
sym::fallback_to_niko => DivergingFallbackBehavior::FallbackToNiko,
sym::fallback_to_never => DivergingFallbackBehavior::FallbackToNever,
sym::no_fallback => DivergingFallbackBehavior::NoFallback,
_ => {
self.tcx.dcx().span_err(span, format!("unknown never type mode: `{mode}` (supported: `fallback_to_unit`, `fallback_to_niko`, `fallback_to_never` and `no_fallback`)"));

DivergingFallbackBehavior::FallbackToUnit
}
}
}

fn fallback_effects(&self) -> bool {
let unsolved_effects = self.unsolved_effects();

Expand Down
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
73 changes: 72 additions & 1 deletion compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ mod checks;
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};
use rustc_hir as hir;
use rustc_hir::def_id::{DefId, LocalDefId};
Expand All @@ -18,7 +21,7 @@ use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKin
use rustc_middle::ty::{self, Const, Ty, TyCtxt, TypeVisitableExt};
use rustc_session::Session;
use rustc_span::symbol::Ident;
use rustc_span::{self, Span, DUMMY_SP};
use rustc_span::{self, sym, Span, DUMMY_SP};
use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode, ObligationCtxt};

use std::cell::{Cell, RefCell};
Expand Down Expand Up @@ -108,6 +111,9 @@ pub struct FnCtxt<'a, 'tcx> {
pub(super) inh: &'a Inherited<'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 @@ -116,6 +122,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
param_env: ty::ParamEnv<'tcx>,
body_id: LocalDefId,
) -> FnCtxt<'a, 'tcx> {
let (diverging_fallback_behavior, diverging_block_behavior) =
parse_never_type_options_attr(inh.tcx);
FnCtxt {
body_id,
param_env,
Expand All @@ -131,6 +139,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}),
inh,
fallback_has_occurred: Cell::new(false),
diverging_fallback_behavior,
diverging_block_behavior,
}
}

Expand Down Expand Up @@ -374,3 +384,64 @@ impl<'tcx> LoweredTy<'tcx> {
LoweredTy { raw, normalized }
}
}

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)
.map(|attr| attr.meta_item_list().unwrap())
.unwrap_or_default();

for item in items {
if item.has_name(sym::fallback) && fallback.is_none() {
let mode = item.value_str().unwrap();
match mode {
sym::unit => fallback = Some(FallbackToUnit),
sym::niko => fallback = Some(FallbackToNiko),
sym::never => fallback = Some(FallbackToNever),
sym::no => fallback = Some(NoFallback),
_ => {
tcx.dcx().span_err(item.span(), format!("unknown never type fallback mode: `{mode}` (supported: `unit`, `niko`, `never` and `no`)"));
}
};
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!(
"unknown never type option: `{}` (supported: `fallback`)",
item.name_or_empty()
),
);
}

let fallback = fallback.unwrap_or_else(|| {
if tcx.features().never_type_fallback { FallbackToNiko } else { FallbackToUnit }
});

let block = block.unwrap_or_default();

(fallback, block)
}
9 changes: 4 additions & 5 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 Expand Up @@ -814,9 +815,7 @@ symbols! {
fadd_algebraic,
fadd_fast,
fake_variadic,
fallback_to_never,
fallback_to_niko,
fallback_to_unit,
fallback,
fdiv_algebraic,
fdiv_fast,
feature,
Expand Down Expand Up @@ -1227,6 +1226,7 @@ symbols! {
new_v1,
new_v1_formatted,
next,
niko,
nll,
no,
no_builtins,
Expand All @@ -1235,7 +1235,6 @@ symbols! {
no_crate_inject,
no_debug,
no_default_passes,
no_fallback,
no_implicit_prelude,
no_inline,
no_link,
Expand Down Expand Up @@ -1553,7 +1552,7 @@ symbols! {
rustc_mir,
rustc_must_implement_one_of,
rustc_never_returns_null_ptr,
rustc_never_type_mode,
rustc_never_type_options,
rustc_no_mir_inline,
rustc_nonnull_optimization_guaranteed,
rustc_nounwind,
Expand Down