Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 0 additions & 42 deletions compiler/rustc_attr_ir/src/data_structures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,42 +70,6 @@ pub enum CguFields {
ExpectedCguReuse { cfg: Symbol, module: Symbol, kind: CguKind },
}

#[derive(Copy, Clone, PartialEq, Debug, PrintAttribute)]
#[derive(StableHash, Encodable, Decodable)]
pub enum DivergingFallbackBehavior {
/// Always fallback to `()` (aka "always spontaneous decay")
ToUnit,
/// Always fallback to `!` (which should be equivalent to never falling back + not making
/// never-to-any coercions unless necessary)
ToNever,
/// Don't fallback at all
NoFallback,
}

#[derive(Copy, Clone, PartialEq, Debug, PrintAttribute, Default)]
#[derive(StableHash, Encodable, Decodable)]
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,
}

#[derive(Copy, Clone, PartialEq, Encodable, Decodable, Debug, StableHash, PrintAttribute)]
pub enum InlineAttr {
None,
Expand Down Expand Up @@ -1367,12 +1331,6 @@ pub enum AttributeKind {
/// Represents `#[rustc_never_returns_null_ptr]`
RustcNeverReturnsNullPtr,

/// Represents `#[rustc_never_type_options]`.
RustcNeverTypeOptions {
fallback: Option<DivergingFallbackBehavior>,
diverging_block_default: Option<DivergingBlockBehavior>,
},

/// Represents `#[rustc_no_implicit_autorefs]`
RustcNoImplicitAutorefs,

Expand Down
1 change: 0 additions & 1 deletion compiler/rustc_attr_ir/src/encode_cross_crate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,6 @@ impl AttributeKind {
RustcMustImplementOneOf { .. } => No,
RustcMustMatchExhaustively(..) => Yes,
RustcNeverReturnsNullPtr => Yes,
RustcNeverTypeOptions { .. } => No,
RustcNoImplicitAutorefs => Yes,
RustcNoImplicitBounds => No,
RustcNoMirInline => Yes,
Expand Down
77 changes: 2 additions & 75 deletions compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ use rustc_ast::{LitIntType, LitKind, MetaItemLit};
use rustc_attr_ir::lang_items::LangItem;
use rustc_attr_ir::target::GenericParamKind;
use rustc_attr_ir::{
BorrowckGraphvizFormatKind, CguFields, CguKind, DivergingBlockBehavior,
DivergingFallbackBehavior, RustcCleanAttribute, RustcCleanQueries, RustcMirKind,
BorrowckGraphvizFormatKind, CguFields, CguKind, RustcCleanAttribute, RustcCleanQueries,
RustcMirKind,
};
use rustc_data_structures::fx::FxHashMap;
use rustc_feature::AttributeStability;
Expand Down Expand Up @@ -397,79 +397,6 @@ impl NoArgsAttributeParser for RustcCaptureAnalysisParser {
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcCaptureAnalysis;
}

pub(crate) struct RustcNeverTypeOptionsParser;

impl SingleAttributeParser for RustcNeverTypeOptionsParser {
const PATH: &[Symbol] = &[sym::rustc_never_type_options];
const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
const TEMPLATE: AttributeTemplate = template!(List: &[
r#"fallback = "unit", "never", "no""#,
r#"diverging_block_default = "unit", "never""#,
]);
const STABILITY: AttributeStability = unstable!(
rustc_attrs,
"`rustc_never_type_options` is used to experiment with never type fallback and work on never type stabilization"
);

fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {
let list = cx.expect_list(args, cx.attr_span)?;

let mut fallback = None::<Ident>;
let mut diverging_block_default = None::<Ident>;

for arg in list.mixed() {
let Some((ident, arg)) = cx.expect_name_value(arg, arg.span(), None) else {
continue;
};

let res = match ident.name {
sym::fallback => &mut fallback,
sym::diverging_block_default => &mut diverging_block_default,
_ => {
cx.adcx().expected_specific_argument(
ident.span,
&[sym::fallback, sym::diverging_block_default],
);
continue;
}
};

let field = cx.expect_string_literal(arg)?;

if res.is_some() {
cx.adcx().duplicate_key(ident.span, ident.name);
continue;
}

*res = Some(Ident { name: field, span: arg.value_span });
}

let fallback = match fallback {
None => None,
Some(Ident { name: sym::unit, .. }) => Some(DivergingFallbackBehavior::ToUnit),
Some(Ident { name: sym::never, .. }) => Some(DivergingFallbackBehavior::ToNever),
Some(Ident { name: sym::no, .. }) => Some(DivergingFallbackBehavior::NoFallback),
Some(Ident { span, .. }) => {
cx.adcx()
.expected_specific_argument_strings(span, &[sym::unit, sym::never, sym::no]);
return None;
}
};

let diverging_block_default = match diverging_block_default {
None => None,
Some(Ident { name: sym::unit, .. }) => Some(DivergingBlockBehavior::Unit),
Some(Ident { name: sym::never, .. }) => Some(DivergingBlockBehavior::Never),
Some(Ident { span, .. }) => {
cx.adcx().expected_specific_argument_strings(span, &[sym::unit, sym::no]);
return None;
}
};

Some(AttributeKind::RustcNeverTypeOptions { fallback, diverging_block_default })
}
}

pub(crate) struct RustcTrivialFieldReadsParser;

impl NoArgsAttributeParser for RustcTrivialFieldReadsParser {
Expand Down
1 change: 0 additions & 1 deletion compiler/rustc_attr_parsing/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,6 @@ attribute_parsers!(
Single<RustcLintOptDenyFieldAccessParser>,
Single<RustcMacroTransparencyParser>,
Single<RustcMustImplementOneOfParser>,
Single<RustcNeverTypeOptionsParser>,
Single<RustcObjcClassParser>,
Single<RustcObjcSelectorParser>,
Single<RustcScalableVectorParser>,
Expand Down
128 changes: 51 additions & 77 deletions compiler/rustc_hir_typeck/src/fallback.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::graph;
use rustc_data_structures::graph::vec_graph::VecGraph;
use rustc_data_structures::unord::{UnordMap, UnordSet};
use rustc_hir::attrs::DivergingFallbackBehavior;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::DefId;
use rustc_hir::intravisit::{InferKind, Visitor};
Expand All @@ -14,7 +13,7 @@ use rustc_lint_defs::builtin::{
};
use rustc_middle::ty::{self, FloatVid, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable};
use rustc_span::def_id::LocalDefId;
use rustc_span::{DUMMY_SP, Span};
use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span};
use rustc_trait_selection::traits::TraitEngine;
use tracing::debug;

Expand Down Expand Up @@ -72,77 +71,63 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
return false;
}

let (diverging_fallback, diverging_fallback_ty) = self.calculate_diverging_fallback();
// If tainted by errors, fallback all unresolved variables to error type,
// in order to prevent unnecessary diagnostics.
if let Some(guar) = self.tainted_by_errors() {
self.fallback_types_to_error(guar, unresolved_ty, unresolved_int, unresolved_float);
return true;
}

let diverging_fallback = self.calculate_diverging_fallback();
let fallback_to_f32 = self.calculate_fallback_to_f32(&unresolved_float);

// We do fallback in two passes, to try to generate
// better error messages.
// The first time, we do *not* replace opaque types.
let mut fallback_occurred = false;

for vid in unresolved_ty {
fallback_occurred |= self.fallback_if_possible(
vid,
|| {
diverging_fallback.contains(&vid).then(|| {
self.diverging_fallback_has_occurred.set(true);
diverging_fallback_ty
})
},
|vid| (Ty::new_var(self.tcx, vid), self.type_var_origin(vid).span),
);
// All unresolved int and float variables always use fallback,
// whereas unresolved type variables might not use fallback.
let mut fallback_occurred = !unresolved_int.is_empty() || !unresolved_float.is_empty();

for &vid in unresolved_ty.iter().filter(|&vid| diverging_fallback.contains(vid)) {
let span = self.type_var_origin(vid).span;
self.demand_eqtype(span, Ty::new_var(self.tcx, vid), self.tcx.types.never);

self.diverging_fallback_has_occurred.set(true);
fallback_occurred = true;
}

for vid in unresolved_int {
fallback_occurred |= self.fallback_if_possible(
vid,
|| Some(self.tcx.types.i32),
// Int variables have no origin?..
|vid| (Ty::new_int_var(self.tcx, vid), DUMMY_SP),
);
self.demand_eqtype(DUMMY_SP, Ty::new_int_var(self.tcx, vid), self.tcx.types.i32);
}

for vid in unresolved_float {
fallback_occurred |= self.fallback_if_possible(
vid,
|| {
Some(if fallback_to_f32.contains(&vid) {
self.tcx.types.f32
} else {
self.tcx.types.f64
})
},
|vid| (Ty::new_float_var(self.tcx, vid), self.float_var_origin(vid).span),
);
let fallback = if fallback_to_f32.contains(&vid) {
self.tcx.types.f32
} else {
self.tcx.types.f64
};
let span = self.float_var_origin(vid).span;
self.demand_eqtype(span, Ty::new_float_var(self.tcx, vid), fallback);
}

fallback_occurred
}

/// Applies fallback to `vid`, if possible.
///
/// - If `self.tainted_by_errors()` unifies the type represented by `vid` with error
/// - Otherwise, if `fallback` returns `Some`, unifies it with the output of `fallback`
/// - Otherwise, does nothing
///
/// Returns whatever fallback has been applied.
fn fallback_if_possible<V>(
fn fallback_types_to_error(
&self,
vid: V,
fallback: impl FnOnce() -> Option<Ty<'tcx>>,
vid_to_ty_and_span: impl FnOnce(V) -> (Ty<'tcx>, Span),
) -> bool {
let fallback = if let Some(e) = self.tainted_by_errors() {
Ty::new_error(self.tcx, e)
} else if let Some(fallback) = fallback() {
fallback
} else {
return false;
};
guar: ErrorGuaranteed,
unresolved_ty: Vec<ty::TyVid>,
unresolved_int: Vec<ty::IntVid>,
unresolved_float: Vec<ty::FloatVid>,
) {
let vars = unresolved_ty
.into_iter()
.map(|vid| Ty::new_var(self.tcx, vid))
.chain(unresolved_int.into_iter().map(|vid| Ty::new_int_var(self.tcx, vid)))
.chain(unresolved_float.into_iter().map(|vid| Ty::new_float_var(self.tcx, vid)));

let (ty, span) = vid_to_ty_and_span(vid);
self.demand_eqtype(span, ty, fallback);
true
let error = Ty::new_error(self.tcx, guar);

for var in vars {
self.demand_eqtype(DUMMY_SP, var, error);
}
}

/// Existing code relies on `f32: From<T>` (usually written as `T: Into<f32>`) resolving `T` to
Expand Down Expand Up @@ -209,34 +194,25 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
fallback_to_f32
}

fn calculate_diverging_fallback(&self) -> (UnordSet<ty::TyVid>, Ty<'tcx>) {
let diverging_fallback_ty = match self.diverging_fallback_behavior {
DivergingFallbackBehavior::ToUnit => self.tcx.types.unit,
DivergingFallbackBehavior::ToNever => self.tcx.types.never,
DivergingFallbackBehavior::NoFallback => {
// the type doesn't matter, since no fallback will occur
return (UnordSet::new(), self.tcx.types.unit);
}
};

fn calculate_diverging_fallback(&self) -> UnordSet<ty::TyVid> {
// Compute the diverging root vids D -- that is, the root vid of
// those type variables that (a) are the target of a coercion from
// a `!` type and (b) have not yet been solved.
//
// These variables are the ones that are targets for fallback to
// either `!` or `()`.
// These variables are the ones that are targets for fallback to `!`.
let diverging_root_vids: Vec<ty::TyVid> = self
.diverging_type_vars
.borrow()
.iter()
.filter_map(|&vid| self.infcx.shallow_resolve_ty_var_or_get_root(vid).err())
.collect();
{
// Construct a coercion graph where an edge `A -> B` indicates
// a type variable is that is coerced
let coercion_graph = self.create_coercion_graph();

{
if !diverging_root_vids.is_empty() {
// Construct a coercion graph where an edge `A -> B` indicates
// a type variable is that is coerced
let coercion_graph = self.create_coercion_graph();

let unsafe_infer_vars = compute_unsafe_infer_vars(self, self.body_def_id);

for &root_vid in &diverging_root_vids {
Expand All @@ -249,9 +225,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
}
}

let diverging_fallback = diverging_root_vids.into_iter().collect::<UnordSet<_>>();

(diverging_fallback, diverging_fallback_ty)
diverging_root_vids.into_iter().collect::<UnordSet<_>>()
}

fn lint_never_type_fallback_flowing_into_unsafe_code(
Expand Down
5 changes: 1 addition & 4 deletions compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ use rustc_data_structures::thin_vec::ThinVec;
use rustc_errors::codes::*;
use rustc_errors::{Applicability, Diag, ErrorGuaranteed, MultiSpan, a_or_an, listify, pluralize};
use rustc_hir as hir;
use rustc_hir::attrs::DivergingBlockBehavior;
use rustc_hir::attrs::lang_items::LangItem;
use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
use rustc_hir::def_id::DefId;
Expand Down Expand Up @@ -1357,9 +1356,7 @@ 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()
|| matches!(self.diverging_block_behavior, DivergingBlockBehavior::Unit)
{
if !self.diverges.get().is_always() {
// #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
Loading
Loading