Merge branch 'rust-lang:master' into master
This commit is contained in:
commit
cb1c06fdd8
57 changed files with 934 additions and 258 deletions
|
@ -4,7 +4,7 @@ use crate::type_::Type;
|
|||
use rustc_codegen_ssa::traits::*;
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::ty::layout::{FnAbiOf, LayoutOf, TyAndLayout};
|
||||
use rustc_middle::ty::print::with_no_trimmed_paths;
|
||||
use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths};
|
||||
use rustc_middle::ty::{self, Ty, TypeFoldable};
|
||||
use rustc_target::abi::{Abi, AddressSpace, Align, FieldsShape};
|
||||
use rustc_target::abi::{Int, Pointer, F32, F64};
|
||||
|
@ -43,7 +43,8 @@ fn uncached_llvm_type<'a, 'tcx>(
|
|||
// in problematically distinct types due to HRTB and subtyping (see #47638).
|
||||
// ty::Dynamic(..) |
|
||||
ty::Adt(..) | ty::Closure(..) | ty::Foreign(..) | ty::Generator(..) | ty::Str => {
|
||||
let mut name = with_no_trimmed_paths(|| layout.ty.to_string());
|
||||
let mut name =
|
||||
with_no_visible_paths(|| with_no_trimmed_paths(|| layout.ty.to_string()));
|
||||
if let (&ty::Adt(def, _), &Variants::Single { index }) =
|
||||
(layout.ty.kind(), &layout.variants)
|
||||
{
|
||||
|
|
|
@ -15,7 +15,7 @@ use rustc_index::vec::Idx;
|
|||
use rustc_middle::mir::AssertKind;
|
||||
use rustc_middle::mir::{self, SwitchTargets};
|
||||
use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf};
|
||||
use rustc_middle::ty::print::with_no_trimmed_paths;
|
||||
use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths};
|
||||
use rustc_middle::ty::{self, Instance, Ty, TypeFoldable};
|
||||
use rustc_span::source_map::Span;
|
||||
use rustc_span::{sym, Symbol};
|
||||
|
@ -476,15 +476,20 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
UninitValid => !layout.might_permit_raw_init(bx, /*zero:*/ false),
|
||||
};
|
||||
if do_panic {
|
||||
let msg_str = with_no_trimmed_paths(|| {
|
||||
if layout.abi.is_uninhabited() {
|
||||
// Use this error even for the other intrinsics as it is more precise.
|
||||
format!("attempted to instantiate uninhabited type `{}`", ty)
|
||||
} else if intrinsic == ZeroValid {
|
||||
format!("attempted to zero-initialize type `{}`, which is invalid", ty)
|
||||
} else {
|
||||
format!("attempted to leave type `{}` uninitialized, which is invalid", ty)
|
||||
}
|
||||
let msg_str = with_no_visible_paths(|| {
|
||||
with_no_trimmed_paths(|| {
|
||||
if layout.abi.is_uninhabited() {
|
||||
// Use this error even for the other intrinsics as it is more precise.
|
||||
format!("attempted to instantiate uninhabited type `{}`", ty)
|
||||
} else if intrinsic == ZeroValid {
|
||||
format!("attempted to zero-initialize type `{}`, which is invalid", ty)
|
||||
} else {
|
||||
format!(
|
||||
"attempted to leave type `{}` uninitialized, which is invalid",
|
||||
ty
|
||||
)
|
||||
}
|
||||
})
|
||||
});
|
||||
let msg = bx.const_str(Symbol::intern(&msg_str));
|
||||
let location = self.get_caller_location(bx, source_info).immediate();
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
// is also useful to track which value is the "expected" value in
|
||||
// terms of error reporting.
|
||||
|
||||
use super::equate::Equate;
|
||||
use super::glb::Glb;
|
||||
use super::lub::Lub;
|
||||
use super::sub::Sub;
|
||||
|
@ -29,7 +30,6 @@ use super::type_variable::TypeVariableValue;
|
|||
use super::unify_key::replace_if_possible;
|
||||
use super::unify_key::{ConstVarValue, ConstVariableValue};
|
||||
use super::unify_key::{ConstVariableOrigin, ConstVariableOriginKind};
|
||||
use super::{equate::Equate, type_variable::Diverging};
|
||||
use super::{InferCtxt, MiscVariable, TypeTrace};
|
||||
|
||||
use crate::traits::{Obligation, PredicateObligations};
|
||||
|
@ -645,7 +645,7 @@ impl TypeRelation<'tcx> for Generalizer<'_, 'tcx> {
|
|||
.inner
|
||||
.borrow_mut()
|
||||
.type_variables()
|
||||
.new_var(self.for_universe, Diverging::NotDiverging, origin);
|
||||
.new_var(self.for_universe, origin);
|
||||
let u = self.tcx().mk_ty_var(new_var_id);
|
||||
|
||||
// Record that we replaced `vid` with `new_var_id` as part of a generalization
|
||||
|
@ -885,11 +885,12 @@ impl TypeRelation<'tcx> for ConstInferUnifier<'_, 'tcx> {
|
|||
|
||||
let origin =
|
||||
*self.infcx.inner.borrow_mut().type_variables().var_origin(vid);
|
||||
let new_var_id = self.infcx.inner.borrow_mut().type_variables().new_var(
|
||||
self.for_universe,
|
||||
Diverging::NotDiverging,
|
||||
origin,
|
||||
);
|
||||
let new_var_id = self
|
||||
.infcx
|
||||
.inner
|
||||
.borrow_mut()
|
||||
.type_variables()
|
||||
.new_var(self.for_universe, origin);
|
||||
let u = self.tcx().mk_ty_var(new_var_id);
|
||||
debug!(
|
||||
"ConstInferUnifier: replacing original vid={:?} with new={:?}",
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
//! inference graph arose so that we can explain to the user what gave
|
||||
//! rise to a particular error.
|
||||
//!
|
||||
//! The basis of the system are the "origin" types. An "origin" is the
|
||||
//! The system is based around a set of "origin" types. An "origin" is the
|
||||
//! reason that a constraint or inference variable arose. There are
|
||||
//! different "origin" enums for different kinds of constraints/variables
|
||||
//! (e.g., `TypeOrigin`, `RegionVariableOrigin`). An origin always has
|
||||
|
|
|
@ -46,7 +46,7 @@ use self::region_constraints::{GenericKind, RegionConstraintData, VarInfos, Veri
|
|||
use self::region_constraints::{
|
||||
RegionConstraintCollector, RegionConstraintStorage, RegionSnapshot,
|
||||
};
|
||||
use self::type_variable::{Diverging, TypeVariableOrigin, TypeVariableOriginKind};
|
||||
use self::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
|
||||
|
||||
pub mod at;
|
||||
pub mod canonical;
|
||||
|
@ -702,17 +702,6 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
|
|||
t.fold_with(&mut self.freshener())
|
||||
}
|
||||
|
||||
/// Returns whether `ty` is a diverging type variable or not.
|
||||
/// (If `ty` is not a type variable at all, returns not diverging.)
|
||||
///
|
||||
/// No attempt is made to resolve `ty`.
|
||||
pub fn type_var_diverges(&'a self, ty: Ty<'_>) -> Diverging {
|
||||
match *ty.kind() {
|
||||
ty::Infer(ty::TyVar(vid)) => self.inner.borrow_mut().type_variables().var_diverges(vid),
|
||||
_ => Diverging::NotDiverging,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the origin of the type variable identified by `vid`, or `None`
|
||||
/// if this is not a type variable.
|
||||
///
|
||||
|
@ -1071,12 +1060,17 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn next_ty_var_id(&self, diverging: Diverging, origin: TypeVariableOrigin) -> TyVid {
|
||||
self.inner.borrow_mut().type_variables().new_var(self.universe(), diverging, origin)
|
||||
/// Number of type variables created so far.
|
||||
pub fn num_ty_vars(&self) -> usize {
|
||||
self.inner.borrow_mut().type_variables().num_vars()
|
||||
}
|
||||
|
||||
pub fn next_ty_var_id(&self, origin: TypeVariableOrigin) -> TyVid {
|
||||
self.inner.borrow_mut().type_variables().new_var(self.universe(), origin)
|
||||
}
|
||||
|
||||
pub fn next_ty_var(&self, origin: TypeVariableOrigin) -> Ty<'tcx> {
|
||||
self.tcx.mk_ty_var(self.next_ty_var_id(Diverging::NotDiverging, origin))
|
||||
self.tcx.mk_ty_var(self.next_ty_var_id(origin))
|
||||
}
|
||||
|
||||
pub fn next_ty_var_in_universe(
|
||||
|
@ -1084,18 +1078,10 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
|
|||
origin: TypeVariableOrigin,
|
||||
universe: ty::UniverseIndex,
|
||||
) -> Ty<'tcx> {
|
||||
let vid = self.inner.borrow_mut().type_variables().new_var(
|
||||
universe,
|
||||
Diverging::NotDiverging,
|
||||
origin,
|
||||
);
|
||||
let vid = self.inner.borrow_mut().type_variables().new_var(universe, origin);
|
||||
self.tcx.mk_ty_var(vid)
|
||||
}
|
||||
|
||||
pub fn next_diverging_ty_var(&self, origin: TypeVariableOrigin) -> Ty<'tcx> {
|
||||
self.tcx.mk_ty_var(self.next_ty_var_id(Diverging::Diverges, origin))
|
||||
}
|
||||
|
||||
pub fn next_const_var(
|
||||
&self,
|
||||
ty: Ty<'tcx>,
|
||||
|
@ -1207,7 +1193,6 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
|
|||
// as the substitutions for the default, `(T, U)`.
|
||||
let ty_var_id = self.inner.borrow_mut().type_variables().new_var(
|
||||
self.universe(),
|
||||
Diverging::NotDiverging,
|
||||
TypeVariableOrigin {
|
||||
kind: TypeVariableOriginKind::TypeParameterDefinition(
|
||||
param.name,
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
//! constituents)
|
||||
|
||||
use crate::infer::combine::ConstEquateRelation;
|
||||
use crate::infer::type_variable::Diverging;
|
||||
use crate::infer::InferCtxt;
|
||||
use crate::infer::{ConstVarValue, ConstVariableValue};
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
|
@ -927,8 +926,7 @@ where
|
|||
// Replacing with a new variable in the universe `self.universe`,
|
||||
// it will be unified later with the original type variable in
|
||||
// the universe `_universe`.
|
||||
let new_var_id =
|
||||
variables.new_var(self.universe, Diverging::NotDiverging, origin);
|
||||
let new_var_id = variables.new_var(self.universe, origin);
|
||||
|
||||
let u = self.tcx().mk_ty_var(new_var_id);
|
||||
debug!("generalize: replacing original vid={:?} with new={:?}", vid, u);
|
||||
|
|
|
@ -129,19 +129,16 @@ pub enum TypeVariableOriginKind {
|
|||
SubstitutionPlaceholder,
|
||||
AutoDeref,
|
||||
AdjustmentType,
|
||||
DivergingFn,
|
||||
|
||||
/// In type check, when we are type checking a function that
|
||||
/// returns `-> dyn Foo`, we substitute a type variable for the
|
||||
/// return type for diagnostic purposes.
|
||||
DynReturnFn,
|
||||
LatticeVariable,
|
||||
}
|
||||
|
||||
pub(crate) struct TypeVariableData {
|
||||
origin: TypeVariableOrigin,
|
||||
diverging: Diverging,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum Diverging {
|
||||
NotDiverging,
|
||||
Diverges,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
|
@ -191,14 +188,6 @@ impl<'tcx> TypeVariableStorage<'tcx> {
|
|||
}
|
||||
|
||||
impl<'tcx> TypeVariableTable<'_, 'tcx> {
|
||||
/// Returns the diverges flag given when `vid` was created.
|
||||
///
|
||||
/// Note that this function does not return care whether
|
||||
/// `vid` has been unified with something else or not.
|
||||
pub fn var_diverges(&self, vid: ty::TyVid) -> Diverging {
|
||||
self.storage.values.get(vid.index()).diverging
|
||||
}
|
||||
|
||||
/// Returns the origin that was given when `vid` was created.
|
||||
///
|
||||
/// Note that this function does not return care whether
|
||||
|
@ -260,7 +249,6 @@ impl<'tcx> TypeVariableTable<'_, 'tcx> {
|
|||
pub fn new_var(
|
||||
&mut self,
|
||||
universe: ty::UniverseIndex,
|
||||
diverging: Diverging,
|
||||
origin: TypeVariableOrigin,
|
||||
) -> ty::TyVid {
|
||||
let eq_key = self.eq_relations().new_key(TypeVariableValue::Unknown { universe });
|
||||
|
@ -268,13 +256,10 @@ impl<'tcx> TypeVariableTable<'_, 'tcx> {
|
|||
let sub_key = self.sub_relations().new_key(());
|
||||
assert_eq!(eq_key.vid, sub_key);
|
||||
|
||||
let index = self.values().push(TypeVariableData { origin, diverging });
|
||||
let index = self.values().push(TypeVariableData { origin });
|
||||
assert_eq!(eq_key.vid.as_u32(), index as u32);
|
||||
|
||||
debug!(
|
||||
"new_var(index={:?}, universe={:?}, diverging={:?}, origin={:?}",
|
||||
eq_key.vid, universe, diverging, origin,
|
||||
);
|
||||
debug!("new_var(index={:?}, universe={:?}, origin={:?}", eq_key.vid, universe, origin,);
|
||||
|
||||
eq_key.vid
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use crate::infer::InferCtxt;
|
||||
use crate::traits::Obligation;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_middle::ty::{self, ToPredicate, Ty, WithConstness};
|
||||
|
@ -73,6 +74,8 @@ pub trait TraitEngine<'tcx>: 'tcx {
|
|||
}
|
||||
|
||||
fn pending_obligations(&self) -> Vec<PredicateObligation<'tcx>>;
|
||||
|
||||
fn relationships(&mut self) -> &mut FxHashMap<ty::TyVid, ty::FoundRelationships>;
|
||||
}
|
||||
|
||||
pub trait TraitEngineExt<'tcx> {
|
||||
|
|
|
@ -599,7 +599,7 @@ rustc_queries! {
|
|||
desc { "computing the inferred outlives predicates for items in this crate" }
|
||||
}
|
||||
|
||||
/// Maps from an impl/trait `DefId to a list of the `DefId`s of its items.
|
||||
/// Maps from an impl/trait `DefId` to a list of the `DefId`s of its items.
|
||||
query associated_item_def_ids(key: DefId) -> &'tcx [DefId] {
|
||||
desc { |tcx| "collecting associated items of `{}`", tcx.def_path_str(key) }
|
||||
}
|
||||
|
|
|
@ -2090,3 +2090,16 @@ impl<'tcx> fmt::Debug for SymbolName<'tcx> {
|
|||
fmt::Display::fmt(&self.name, fmt)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Copy, Clone)]
|
||||
pub struct FoundRelationships {
|
||||
/// This is true if we identified that this Ty (`?T`) is found in a `?T: Foo`
|
||||
/// obligation, where:
|
||||
///
|
||||
/// * `Foo` is not `Sized`
|
||||
/// * `(): Foo` may be satisfied
|
||||
pub self_in_trait: bool,
|
||||
/// This is true if we identified that this Ty (`?T`) is found in a `<_ as
|
||||
/// _>::AssocType = ?T`
|
||||
pub output: bool,
|
||||
}
|
||||
|
|
|
@ -59,6 +59,7 @@ thread_local! {
|
|||
static SHOULD_PREFIX_WITH_CRATE: Cell<bool> = const { Cell::new(false) };
|
||||
static NO_TRIMMED_PATH: Cell<bool> = const { Cell::new(false) };
|
||||
static NO_QUERIES: Cell<bool> = const { Cell::new(false) };
|
||||
static NO_VISIBLE_PATH: Cell<bool> = const { Cell::new(false) };
|
||||
}
|
||||
|
||||
/// Avoids running any queries during any prints that occur
|
||||
|
@ -112,6 +113,16 @@ pub fn with_no_trimmed_paths<F: FnOnce() -> R, R>(f: F) -> R {
|
|||
})
|
||||
}
|
||||
|
||||
/// Prevent selection of visible paths. `Display` impl of DefId will prefer visible (public) reexports of types as paths.
|
||||
pub fn with_no_visible_paths<F: FnOnce() -> R, R>(f: F) -> R {
|
||||
NO_VISIBLE_PATH.with(|flag| {
|
||||
let old = flag.replace(true);
|
||||
let result = f();
|
||||
flag.set(old);
|
||||
result
|
||||
})
|
||||
}
|
||||
|
||||
/// The "region highlights" are used to control region printing during
|
||||
/// specific error messages. When a "region highlight" is enabled, it
|
||||
/// gives an alternate way to print specific regions. For now, we
|
||||
|
@ -268,6 +279,10 @@ pub trait PrettyPrinter<'tcx>:
|
|||
/// from at least one local module, and returns `true`. If the crate defining `def_id` is
|
||||
/// declared with an `extern crate`, the path is guaranteed to use the `extern crate`.
|
||||
fn try_print_visible_def_path(self, def_id: DefId) -> Result<(Self, bool), Self::Error> {
|
||||
if NO_VISIBLE_PATH.with(|flag| flag.get()) {
|
||||
return Ok((self, false));
|
||||
}
|
||||
|
||||
let mut callers = Vec::new();
|
||||
self.try_print_visible_def_path_recur(def_id, &mut callers)
|
||||
}
|
||||
|
|
|
@ -1672,6 +1672,14 @@ impl<'tcx> TyS<'tcx> {
|
|||
matches!(self.kind(), Infer(TyVar(_)))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn ty_vid(&self) -> Option<ty::TyVid> {
|
||||
match self.kind() {
|
||||
&Infer(TyVar(vid)) => Some(vid),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_ty_infer(&self) -> bool {
|
||||
matches!(self.kind(), Infer(_))
|
||||
|
|
|
@ -1568,6 +1568,20 @@ impl<'a> Parser<'a> {
|
|||
|
||||
pub(super) fn parse_lit(&mut self) -> PResult<'a, Lit> {
|
||||
self.parse_opt_lit().ok_or_else(|| {
|
||||
if let token::Interpolated(inner) = &self.token.kind {
|
||||
let expr = match inner.as_ref() {
|
||||
token::NtExpr(expr) => Some(expr),
|
||||
token::NtLiteral(expr) => Some(expr),
|
||||
_ => None,
|
||||
};
|
||||
if let Some(expr) = expr {
|
||||
if matches!(expr.kind, ExprKind::Err) {
|
||||
self.diagnostic()
|
||||
.delay_span_bug(self.token.span, &"invalid interpolated expression");
|
||||
return self.diagnostic().struct_dummy();
|
||||
}
|
||||
}
|
||||
}
|
||||
let msg = format!("unexpected token: {}", super::token_descr(&self.token));
|
||||
self.struct_span_err(self.token.span, &msg)
|
||||
})
|
||||
|
|
|
@ -321,10 +321,13 @@ macro_rules! define_queries {
|
|||
pub fn $name<$tcx>(tcx: QueryCtxt<$tcx>, key: query_keys::$name<$tcx>) -> QueryStackFrame {
|
||||
let kind = dep_graph::DepKind::$name;
|
||||
let name = stringify!($name);
|
||||
let description = ty::print::with_forced_impl_filename_line(
|
||||
// Disable visible paths printing for performance reasons.
|
||||
// Showing visible path instead of any path is not that important in production.
|
||||
let description = ty::print::with_no_visible_paths(
|
||||
|| ty::print::with_forced_impl_filename_line(
|
||||
// Force filename-line mode to avoid invoking `type_of` query.
|
||||
|| queries::$name::describe(tcx, key)
|
||||
);
|
||||
));
|
||||
let description = if tcx.sess.verbose() {
|
||||
format!("{} [{}]", description, name)
|
||||
} else {
|
||||
|
|
|
@ -7,16 +7,21 @@ use crate::traits::{
|
|||
ChalkEnvironmentAndGoal, FulfillmentError, FulfillmentErrorCode, ObligationCause,
|
||||
PredicateObligation, SelectionError, TraitEngine,
|
||||
};
|
||||
use rustc_data_structures::fx::FxIndexSet;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxIndexSet};
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
|
||||
pub struct FulfillmentContext<'tcx> {
|
||||
obligations: FxIndexSet<PredicateObligation<'tcx>>,
|
||||
|
||||
relationships: FxHashMap<ty::TyVid, ty::FoundRelationships>,
|
||||
}
|
||||
|
||||
impl FulfillmentContext<'tcx> {
|
||||
crate fn new() -> Self {
|
||||
FulfillmentContext { obligations: FxIndexSet::default() }
|
||||
FulfillmentContext {
|
||||
obligations: FxIndexSet::default(),
|
||||
relationships: FxHashMap::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -39,6 +44,8 @@ impl TraitEngine<'tcx> for FulfillmentContext<'tcx> {
|
|||
assert!(!infcx.is_in_snapshot());
|
||||
let obligation = infcx.resolve_vars_if_possible(obligation);
|
||||
|
||||
super::relationships::update(self, infcx, &obligation);
|
||||
|
||||
self.obligations.insert(obligation);
|
||||
}
|
||||
|
||||
|
@ -146,4 +153,8 @@ impl TraitEngine<'tcx> for FulfillmentContext<'tcx> {
|
|||
fn pending_obligations(&self) -> Vec<PredicateObligation<'tcx>> {
|
||||
self.obligations.iter().cloned().collect()
|
||||
}
|
||||
|
||||
fn relationships(&mut self) -> &mut FxHashMap<ty::TyVid, ty::FoundRelationships> {
|
||||
&mut self.relationships
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use crate::infer::{InferCtxt, TyOrConstInferVar};
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_data_structures::obligation_forest::ProcessResult;
|
||||
use rustc_data_structures::obligation_forest::{Error, ForestObligation, Outcome};
|
||||
use rustc_data_structures::obligation_forest::{ObligationForest, ObligationProcessor};
|
||||
|
@ -53,6 +54,9 @@ pub struct FulfillmentContext<'tcx> {
|
|||
// A list of all obligations that have been registered with this
|
||||
// fulfillment context.
|
||||
predicates: ObligationForest<PendingPredicateObligation<'tcx>>,
|
||||
|
||||
relationships: FxHashMap<ty::TyVid, ty::FoundRelationships>,
|
||||
|
||||
// Should this fulfillment context register type-lives-for-region
|
||||
// obligations on its parent infcx? In some cases, region
|
||||
// obligations are either already known to hold (normalization) or
|
||||
|
@ -97,6 +101,7 @@ impl<'a, 'tcx> FulfillmentContext<'tcx> {
|
|||
pub fn new() -> FulfillmentContext<'tcx> {
|
||||
FulfillmentContext {
|
||||
predicates: ObligationForest::new(),
|
||||
relationships: FxHashMap::default(),
|
||||
register_region_obligations: true,
|
||||
usable_in_snapshot: false,
|
||||
}
|
||||
|
@ -105,6 +110,7 @@ impl<'a, 'tcx> FulfillmentContext<'tcx> {
|
|||
pub fn new_in_snapshot() -> FulfillmentContext<'tcx> {
|
||||
FulfillmentContext {
|
||||
predicates: ObligationForest::new(),
|
||||
relationships: FxHashMap::default(),
|
||||
register_region_obligations: true,
|
||||
usable_in_snapshot: true,
|
||||
}
|
||||
|
@ -113,6 +119,7 @@ impl<'a, 'tcx> FulfillmentContext<'tcx> {
|
|||
pub fn new_ignoring_regions() -> FulfillmentContext<'tcx> {
|
||||
FulfillmentContext {
|
||||
predicates: ObligationForest::new(),
|
||||
relationships: FxHashMap::default(),
|
||||
register_region_obligations: false,
|
||||
usable_in_snapshot: false,
|
||||
}
|
||||
|
@ -210,6 +217,8 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> {
|
|||
|
||||
assert!(!infcx.is_in_snapshot() || self.usable_in_snapshot);
|
||||
|
||||
super::relationships::update(self, infcx, &obligation);
|
||||
|
||||
self.predicates
|
||||
.register_obligation(PendingPredicateObligation { obligation, stalled_on: vec![] });
|
||||
}
|
||||
|
@ -265,6 +274,10 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> {
|
|||
fn pending_obligations(&self) -> Vec<PredicateObligation<'tcx>> {
|
||||
self.predicates.map_pending_obligations(|o| o.obligation.clone())
|
||||
}
|
||||
|
||||
fn relationships(&mut self) -> &mut FxHashMap<ty::TyVid, ty::FoundRelationships> {
|
||||
&mut self.relationships
|
||||
}
|
||||
}
|
||||
|
||||
struct FulfillProcessor<'a, 'b, 'tcx> {
|
||||
|
|
|
@ -15,6 +15,7 @@ mod object_safety;
|
|||
mod on_unimplemented;
|
||||
mod project;
|
||||
pub mod query;
|
||||
pub(crate) mod relationships;
|
||||
mod select;
|
||||
mod specialize;
|
||||
mod structural_match;
|
||||
|
|
69
compiler/rustc_trait_selection/src/traits/relationships.rs
Normal file
69
compiler/rustc_trait_selection/src/traits/relationships.rs
Normal file
|
@ -0,0 +1,69 @@
|
|||
use crate::infer::InferCtxt;
|
||||
use crate::traits::query::evaluate_obligation::InferCtxtExt;
|
||||
use crate::traits::{ObligationCause, PredicateObligation};
|
||||
use rustc_infer::traits::TraitEngine;
|
||||
use rustc_middle::ty::{self, ToPredicate};
|
||||
|
||||
pub(crate) fn update<'tcx, T>(
|
||||
engine: &mut T,
|
||||
infcx: &InferCtxt<'_, 'tcx>,
|
||||
obligation: &PredicateObligation<'tcx>,
|
||||
) where
|
||||
T: TraitEngine<'tcx>,
|
||||
{
|
||||
// (*) binder skipped
|
||||
if let ty::PredicateKind::Trait(predicate) = obligation.predicate.kind().skip_binder() {
|
||||
if let Some(ty) =
|
||||
infcx.shallow_resolve(predicate.self_ty()).ty_vid().map(|t| infcx.root_var(t))
|
||||
{
|
||||
if infcx
|
||||
.tcx
|
||||
.lang_items()
|
||||
.sized_trait()
|
||||
.map_or(false, |st| st != predicate.trait_ref.def_id)
|
||||
{
|
||||
let new_self_ty = infcx.tcx.types.unit;
|
||||
|
||||
let trait_ref = ty::TraitRef {
|
||||
substs: infcx
|
||||
.tcx
|
||||
.mk_substs_trait(new_self_ty, &predicate.trait_ref.substs[1..]),
|
||||
..predicate.trait_ref
|
||||
};
|
||||
|
||||
// Then contstruct a new obligation with Self = () added
|
||||
// to the ParamEnv, and see if it holds.
|
||||
let o = rustc_infer::traits::Obligation::new(
|
||||
ObligationCause::dummy(),
|
||||
obligation.param_env,
|
||||
obligation
|
||||
.predicate
|
||||
.kind()
|
||||
.map_bound(|_| {
|
||||
// (*) binder moved here
|
||||
ty::PredicateKind::Trait(ty::TraitPredicate {
|
||||
trait_ref,
|
||||
constness: predicate.constness,
|
||||
})
|
||||
})
|
||||
.to_predicate(infcx.tcx),
|
||||
);
|
||||
// Don't report overflow errors. Otherwise equivalent to may_hold.
|
||||
if let Ok(result) = infcx.probe(|_| infcx.evaluate_obligation(&o)) {
|
||||
if result.may_apply() {
|
||||
engine.relationships().entry(ty).or_default().self_in_trait = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let ty::PredicateKind::Projection(predicate) = obligation.predicate.kind().skip_binder() {
|
||||
// If the projection predicate (Foo::Bar == X) has X as a non-TyVid,
|
||||
// we need to make it into one.
|
||||
if let Some(vid) = predicate.ty.ty_vid() {
|
||||
debug!("relationship: {:?}.output = true", vid);
|
||||
engine.relationships().entry(vid).or_default().output = true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -241,32 +241,16 @@ pub(super) fn check_fn<'a, 'tcx>(
|
|||
// we saw and assigning it to the expected return type. This isn't
|
||||
// really expected to fail, since the coercions would have failed
|
||||
// earlier when trying to find a LUB.
|
||||
//
|
||||
// However, the behavior around `!` is sort of complex. In the
|
||||
// event that the `actual_return_ty` comes back as `!`, that
|
||||
// indicates that the fn either does not return or "returns" only
|
||||
// values of type `!`. In this case, if there is an expected
|
||||
// return type that is *not* `!`, that should be ok. But if the
|
||||
// return type is being inferred, we want to "fallback" to `!`:
|
||||
//
|
||||
// let x = move || panic!();
|
||||
//
|
||||
// To allow for that, I am creating a type variable with diverging
|
||||
// fallback. This was deemed ever so slightly better than unifying
|
||||
// the return value with `!` because it allows for the caller to
|
||||
// make more assumptions about the return type (e.g., they could do
|
||||
//
|
||||
// let y: Option<u32> = Some(x());
|
||||
//
|
||||
// which would then cause this return type to become `u32`, not
|
||||
// `!`).
|
||||
let coercion = fcx.ret_coercion.take().unwrap().into_inner();
|
||||
let mut actual_return_ty = coercion.complete(&fcx);
|
||||
if actual_return_ty.is_never() {
|
||||
actual_return_ty = fcx.next_diverging_ty_var(TypeVariableOrigin {
|
||||
kind: TypeVariableOriginKind::DivergingFn,
|
||||
span,
|
||||
});
|
||||
debug!("actual_return_ty = {:?}", actual_return_ty);
|
||||
if let ty::Dynamic(..) = declared_ret_ty.kind() {
|
||||
// We have special-cased the case where the function is declared
|
||||
// `-> dyn Foo` and we don't actually relate it to the
|
||||
// `fcx.ret_coercion`, so just substitute a type variable.
|
||||
actual_return_ty =
|
||||
fcx.next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::DynReturnFn, span });
|
||||
debug!("actual_return_ty replaced with {:?}", actual_return_ty);
|
||||
}
|
||||
fcx.demand_suptype(span, revealed_ret_ty, actual_return_ty);
|
||||
|
||||
|
|
|
@ -159,24 +159,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
|
|||
|
||||
// Coercing from `!` to any type is allowed:
|
||||
if a.is_never() {
|
||||
// Subtle: If we are coercing from `!` to `?T`, where `?T` is an unbound
|
||||
// type variable, we want `?T` to fallback to `!` if not
|
||||
// otherwise constrained. An example where this arises:
|
||||
//
|
||||
// let _: Option<?T> = Some({ return; });
|
||||
//
|
||||
// here, we would coerce from `!` to `?T`.
|
||||
return if b.is_ty_var() {
|
||||
// Micro-optimization: no need for this if `b` is
|
||||
// already resolved in some way.
|
||||
let diverging_ty = self.next_diverging_ty_var(TypeVariableOrigin {
|
||||
kind: TypeVariableOriginKind::AdjustmentType,
|
||||
span: self.cause.span,
|
||||
});
|
||||
self.coerce_from_inference_variable(diverging_ty, b, simple(Adjust::NeverToAny))
|
||||
} else {
|
||||
success(simple(Adjust::NeverToAny)(b), b, vec![])
|
||||
};
|
||||
return success(simple(Adjust::NeverToAny)(b), b, vec![]);
|
||||
}
|
||||
|
||||
// Coercing *from* an unresolved inference variable means that
|
||||
|
|
|
@ -77,7 +77,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
!self.typeck_results.borrow().adjustments().contains_key(expr.hir_id),
|
||||
"expression with never type wound up being adjusted"
|
||||
);
|
||||
let adj_ty = self.next_diverging_ty_var(TypeVariableOrigin {
|
||||
let adj_ty = self.next_ty_var(TypeVariableOrigin {
|
||||
kind: TypeVariableOriginKind::AdjustmentType,
|
||||
span: expr.span,
|
||||
});
|
||||
|
|
|
@ -1,29 +1,52 @@
|
|||
use crate::check::FnCtxt;
|
||||
use rustc_infer::infer::type_variable::Diverging;
|
||||
use rustc_data_structures::{
|
||||
fx::FxHashMap,
|
||||
graph::WithSuccessors,
|
||||
graph::{iterate::DepthFirstSearch, vec_graph::VecGraph},
|
||||
stable_set::FxHashSet,
|
||||
};
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
|
||||
impl<'tcx> FnCtxt<'_, 'tcx> {
|
||||
/// Performs type inference fallback, returning true if any fallback
|
||||
/// occurs.
|
||||
pub(super) fn type_inference_fallback(&self) -> bool {
|
||||
debug!(
|
||||
"type-inference-fallback start obligations: {:#?}",
|
||||
self.fulfillment_cx.borrow_mut().pending_obligations()
|
||||
);
|
||||
|
||||
// All type checking constraints were added, try to fallback unsolved variables.
|
||||
self.select_obligations_where_possible(false, |_| {});
|
||||
let mut fallback_has_occurred = false;
|
||||
|
||||
debug!(
|
||||
"type-inference-fallback post selection obligations: {:#?}",
|
||||
self.fulfillment_cx.borrow_mut().pending_obligations()
|
||||
);
|
||||
|
||||
// Check if we have any unsolved varibales. If not, no need for fallback.
|
||||
let unsolved_variables = self.unsolved_variables();
|
||||
if unsolved_variables.is_empty() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let diverging_fallback = self.calculate_diverging_fallback(&unsolved_variables);
|
||||
|
||||
let mut fallback_has_occurred = false;
|
||||
// We do fallback in two passes, to try to generate
|
||||
// better error messages.
|
||||
// The first time, we do *not* replace opaque types.
|
||||
for ty in &self.unsolved_variables() {
|
||||
for ty in unsolved_variables {
|
||||
debug!("unsolved_variable = {:?}", ty);
|
||||
fallback_has_occurred |= self.fallback_if_possible(ty);
|
||||
fallback_has_occurred |= self.fallback_if_possible(ty, &diverging_fallback);
|
||||
}
|
||||
|
||||
// We now see if we can make progress. This might
|
||||
// cause us to unify inference variables for opaque types,
|
||||
// since we may have unified some other type variables
|
||||
// during the first phase of fallback.
|
||||
// This means that we only replace inference variables with their underlying
|
||||
// opaque types as a last resort.
|
||||
// We now see if we can make progress. This might cause us to
|
||||
// unify inference variables for opaque types, since we may
|
||||
// have unified some other type variables during the first
|
||||
// phase of fallback. This means that we only replace
|
||||
// inference variables with their underlying opaque types as a
|
||||
// last resort.
|
||||
//
|
||||
// In code like this:
|
||||
//
|
||||
|
@ -62,36 +85,44 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
|
|||
//
|
||||
// - Unconstrained floats are replaced with with `f64`.
|
||||
//
|
||||
// - Non-numerics get replaced with `!` when `#![feature(never_type_fallback)]`
|
||||
// is enabled. Otherwise, they are replaced with `()`.
|
||||
// - Non-numerics may get replaced with `()` or `!`, depending on
|
||||
// how they were categorized by `calculate_diverging_fallback`
|
||||
// (and the setting of `#![feature(never_type_fallback)]`).
|
||||
//
|
||||
// Fallback becomes very dubious if we have encountered
|
||||
// type-checking errors. In that case, fallback to Error.
|
||||
//
|
||||
// Fallback becomes very dubious if we have encountered type-checking errors.
|
||||
// In that case, fallback to Error.
|
||||
// The return value indicates whether fallback has occurred.
|
||||
fn fallback_if_possible(&self, ty: Ty<'tcx>) -> bool {
|
||||
fn fallback_if_possible(
|
||||
&self,
|
||||
ty: Ty<'tcx>,
|
||||
diverging_fallback: &FxHashMap<Ty<'tcx>, Ty<'tcx>>,
|
||||
) -> bool {
|
||||
// Careful: we do NOT shallow-resolve `ty`. We know that `ty`
|
||||
// is an unsolved variable, and we determine its fallback based
|
||||
// solely on how it was created, not what other type variables
|
||||
// it may have been unified with since then.
|
||||
// is an unsolved variable, and we determine its fallback
|
||||
// based solely on how it was created, not what other type
|
||||
// variables it may have been unified with since then.
|
||||
//
|
||||
// The reason this matters is that other attempts at fallback may
|
||||
// (in principle) conflict with this fallback, and we wish to generate
|
||||
// a type error in that case. (However, this actually isn't true right now,
|
||||
// because we're only using the builtin fallback rules. This would be
|
||||
// true if we were using user-supplied fallbacks. But it's still useful
|
||||
// to write the code to detect bugs.)
|
||||
// The reason this matters is that other attempts at fallback
|
||||
// may (in principle) conflict with this fallback, and we wish
|
||||
// to generate a type error in that case. (However, this
|
||||
// actually isn't true right now, because we're only using the
|
||||
// builtin fallback rules. This would be true if we were using
|
||||
// user-supplied fallbacks. But it's still useful to write the
|
||||
// code to detect bugs.)
|
||||
//
|
||||
// (Note though that if we have a general type variable `?T` that is then unified
|
||||
// with an integer type variable `?I` that ultimately never gets
|
||||
// resolved to a special integral type, `?T` is not considered unsolved,
|
||||
// but `?I` is. The same is true for float variables.)
|
||||
// (Note though that if we have a general type variable `?T`
|
||||
// that is then unified with an integer type variable `?I`
|
||||
// that ultimately never gets resolved to a special integral
|
||||
// type, `?T` is not considered unsolved, but `?I` is. The
|
||||
// same is true for float variables.)
|
||||
let fallback = match ty.kind() {
|
||||
_ if self.is_tainted_by_errors() => self.tcx.ty_error(),
|
||||
ty::Infer(ty::IntVar(_)) => self.tcx.types.i32,
|
||||
ty::Infer(ty::FloatVar(_)) => self.tcx.types.f64,
|
||||
_ => match self.type_var_diverges(ty) {
|
||||
Diverging::Diverges => self.tcx.mk_diverging_default(),
|
||||
Diverging::NotDiverging => return false,
|
||||
_ => match diverging_fallback.get(&ty) {
|
||||
Some(&fallback_ty) => fallback_ty,
|
||||
None => return false,
|
||||
},
|
||||
};
|
||||
debug!("fallback_if_possible(ty={:?}): defaulting to `{:?}`", ty, fallback);
|
||||
|
@ -105,11 +136,10 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
|
|||
true
|
||||
}
|
||||
|
||||
/// Second round of fallback: Unconstrained type variables
|
||||
/// created from the instantiation of an opaque
|
||||
/// type fall back to the opaque type itself. This is a
|
||||
/// somewhat incomplete attempt to manage "identity passthrough"
|
||||
/// for `impl Trait` types.
|
||||
/// Second round of fallback: Unconstrained type variables created
|
||||
/// from the instantiation of an opaque type fall back to the
|
||||
/// opaque type itself. This is a somewhat incomplete attempt to
|
||||
/// manage "identity passthrough" for `impl Trait` types.
|
||||
///
|
||||
/// For example, in this code:
|
||||
///
|
||||
|
@ -158,4 +188,274 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// The "diverging fallback" system is rather complicated. This is
|
||||
/// a result of our need to balance 'do the right thing' with
|
||||
/// backwards compatibility.
|
||||
///
|
||||
/// "Diverging" type variables are variables created when we
|
||||
/// coerce a `!` type into an unbound type variable `?X`. If they
|
||||
/// never wind up being constrained, the "right and natural" thing
|
||||
/// is that `?X` should "fallback" to `!`. This means that e.g. an
|
||||
/// expression like `Some(return)` will ultimately wind up with a
|
||||
/// type like `Option<!>` (presuming it is not assigned or
|
||||
/// constrained to have some other type).
|
||||
///
|
||||
/// However, the fallback used to be `()` (before the `!` type was
|
||||
/// added). Moreover, there are cases where the `!` type 'leaks
|
||||
/// out' from dead code into type variables that affect live
|
||||
/// code. The most common case is something like this:
|
||||
///
|
||||
/// ```rust
|
||||
/// match foo() {
|
||||
/// 22 => Default::default(), // call this type `?D`
|
||||
/// _ => return, // return has type `!`
|
||||
/// } // call the type of this match `?M`
|
||||
/// ```
|
||||
///
|
||||
/// Here, coercing the type `!` into `?M` will create a diverging
|
||||
/// type variable `?X` where `?X <: ?M`. We also have that `?D <:
|
||||
/// ?M`. If `?M` winds up unconstrained, then `?X` will
|
||||
/// fallback. If it falls back to `!`, then all the type variables
|
||||
/// will wind up equal to `!` -- this includes the type `?D`
|
||||
/// (since `!` doesn't implement `Default`, we wind up a "trait
|
||||
/// not implemented" error in code like this). But since the
|
||||
/// original fallback was `()`, this code used to compile with `?D
|
||||
/// = ()`. This is somewhat surprising, since `Default::default()`
|
||||
/// on its own would give an error because the types are
|
||||
/// insufficiently constrained.
|
||||
///
|
||||
/// Our solution to this dilemma is to modify diverging variables
|
||||
/// so that they can *either* fallback to `!` (the default) or to
|
||||
/// `()` (the backwards compatibility case). We decide which
|
||||
/// fallback to use based on whether there is a coercion pattern
|
||||
/// like this:
|
||||
///
|
||||
/// ```
|
||||
/// ?Diverging -> ?V
|
||||
/// ?NonDiverging -> ?V
|
||||
/// ?V != ?NonDiverging
|
||||
/// ```
|
||||
///
|
||||
/// Here `?Diverging` represents some diverging type variable and
|
||||
/// `?NonDiverging` represents some non-diverging type
|
||||
/// variable. `?V` can be any type variable (diverging or not), so
|
||||
/// long as it is not equal to `?NonDiverging`.
|
||||
///
|
||||
/// Intuitively, what we are looking for is a case where a
|
||||
/// "non-diverging" type variable (like `?M` in our example above)
|
||||
/// is coerced *into* some variable `?V` that would otherwise
|
||||
/// fallback to `!`. In that case, we make `?V` fallback to `!`,
|
||||
/// along with anything that would flow into `?V`.
|
||||
///
|
||||
/// The algorithm we use:
|
||||
/// * Identify all variables that are coerced *into* by a
|
||||
/// diverging variable. Do this by iterating over each
|
||||
/// diverging, unsolved variable and finding all variables
|
||||
/// reachable from there. Call that set `D`.
|
||||
/// * Walk over all unsolved, non-diverging variables, and find
|
||||
/// any variable that has an edge into `D`.
|
||||
fn calculate_diverging_fallback(
|
||||
&self,
|
||||
unsolved_variables: &[Ty<'tcx>],
|
||||
) -> FxHashMap<Ty<'tcx>, Ty<'tcx>> {
|
||||
debug!("calculate_diverging_fallback({:?})", unsolved_variables);
|
||||
|
||||
let relationships = self.fulfillment_cx.borrow_mut().relationships().clone();
|
||||
|
||||
// Construct a coercion graph where an edge `A -> B` indicates
|
||||
// a type variable is that is coerced
|
||||
let coercion_graph = self.create_coercion_graph();
|
||||
|
||||
// Extract the unsolved type inference variable vids; note that some
|
||||
// unsolved variables are integer/float variables and are excluded.
|
||||
let unsolved_vids = unsolved_variables.iter().filter_map(|ty| ty.ty_vid());
|
||||
|
||||
// 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 `()`.
|
||||
let diverging_roots: FxHashSet<ty::TyVid> = self
|
||||
.diverging_type_vars
|
||||
.borrow()
|
||||
.iter()
|
||||
.map(|&ty| self.infcx.shallow_resolve(ty))
|
||||
.filter_map(|ty| ty.ty_vid())
|
||||
.map(|vid| self.infcx.root_var(vid))
|
||||
.collect();
|
||||
debug!(
|
||||
"calculate_diverging_fallback: diverging_type_vars={:?}",
|
||||
self.diverging_type_vars.borrow()
|
||||
);
|
||||
debug!("calculate_diverging_fallback: diverging_roots={:?}", diverging_roots);
|
||||
|
||||
// Find all type variables that are reachable from a diverging
|
||||
// type variable. These will typically default to `!`, unless
|
||||
// we find later that they are *also* reachable from some
|
||||
// other type variable outside this set.
|
||||
let mut roots_reachable_from_diverging = DepthFirstSearch::new(&coercion_graph);
|
||||
let mut diverging_vids = vec![];
|
||||
let mut non_diverging_vids = vec![];
|
||||
for unsolved_vid in unsolved_vids {
|
||||
let root_vid = self.infcx.root_var(unsolved_vid);
|
||||
debug!(
|
||||
"calculate_diverging_fallback: unsolved_vid={:?} root_vid={:?} diverges={:?}",
|
||||
unsolved_vid,
|
||||
root_vid,
|
||||
diverging_roots.contains(&root_vid),
|
||||
);
|
||||
if diverging_roots.contains(&root_vid) {
|
||||
diverging_vids.push(unsolved_vid);
|
||||
roots_reachable_from_diverging.push_start_node(root_vid);
|
||||
|
||||
debug!(
|
||||
"calculate_diverging_fallback: root_vid={:?} reaches {:?}",
|
||||
root_vid,
|
||||
coercion_graph.depth_first_search(root_vid).collect::<Vec<_>>()
|
||||
);
|
||||
|
||||
// drain the iterator to visit all nodes reachable from this node
|
||||
roots_reachable_from_diverging.complete_search();
|
||||
} else {
|
||||
non_diverging_vids.push(unsolved_vid);
|
||||
}
|
||||
}
|
||||
|
||||
debug!(
|
||||
"calculate_diverging_fallback: roots_reachable_from_diverging={:?}",
|
||||
roots_reachable_from_diverging,
|
||||
);
|
||||
|
||||
// Find all type variables N0 that are not reachable from a
|
||||
// diverging variable, and then compute the set reachable from
|
||||
// N0, which we call N. These are the *non-diverging* type
|
||||
// variables. (Note that this set consists of "root variables".)
|
||||
let mut roots_reachable_from_non_diverging = DepthFirstSearch::new(&coercion_graph);
|
||||
for &non_diverging_vid in &non_diverging_vids {
|
||||
let root_vid = self.infcx.root_var(non_diverging_vid);
|
||||
if roots_reachable_from_diverging.visited(root_vid) {
|
||||
continue;
|
||||
}
|
||||
roots_reachable_from_non_diverging.push_start_node(root_vid);
|
||||
roots_reachable_from_non_diverging.complete_search();
|
||||
}
|
||||
debug!(
|
||||
"calculate_diverging_fallback: roots_reachable_from_non_diverging={:?}",
|
||||
roots_reachable_from_non_diverging,
|
||||
);
|
||||
|
||||
debug!("inherited: {:#?}", self.inh.fulfillment_cx.borrow_mut().pending_obligations());
|
||||
debug!("obligations: {:#?}", self.fulfillment_cx.borrow_mut().pending_obligations());
|
||||
debug!("relationships: {:#?}", relationships);
|
||||
|
||||
// For each diverging variable, figure out whether it can
|
||||
// reach a member of N. If so, it falls back to `()`. Else
|
||||
// `!`.
|
||||
let mut diverging_fallback = FxHashMap::default();
|
||||
diverging_fallback.reserve(diverging_vids.len());
|
||||
for &diverging_vid in &diverging_vids {
|
||||
let diverging_ty = self.tcx.mk_ty_var(diverging_vid);
|
||||
let root_vid = self.infcx.root_var(diverging_vid);
|
||||
let can_reach_non_diverging = coercion_graph
|
||||
.depth_first_search(root_vid)
|
||||
.any(|n| roots_reachable_from_non_diverging.visited(n));
|
||||
|
||||
let mut relationship = ty::FoundRelationships { self_in_trait: false, output: false };
|
||||
|
||||
for (vid, rel) in relationships.iter() {
|
||||
if self.infcx.root_var(*vid) == root_vid {
|
||||
relationship.self_in_trait |= rel.self_in_trait;
|
||||
relationship.output |= rel.output;
|
||||
}
|
||||
}
|
||||
|
||||
if relationship.self_in_trait && relationship.output {
|
||||
// This case falls back to () to ensure that the code pattern in
|
||||
// src/test/ui/never_type/fallback-closure-ret.rs continues to
|
||||
// compile when never_type_fallback is enabled.
|
||||
//
|
||||
// This rule is not readily explainable from first principles,
|
||||
// but is rather intended as a patchwork fix to ensure code
|
||||
// which compiles before the stabilization of never type
|
||||
// fallback continues to work.
|
||||
//
|
||||
// Typically this pattern is encountered in a function taking a
|
||||
// closure as a parameter, where the return type of that closure
|
||||
// (checked by `relationship.output`) is expected to implement
|
||||
// some trait (checked by `relationship.self_in_trait`). This
|
||||
// can come up in non-closure cases too, so we do not limit this
|
||||
// rule to specifically `FnOnce`.
|
||||
//
|
||||
// When the closure's body is something like `panic!()`, the
|
||||
// return type would normally be inferred to `!`. However, it
|
||||
// needs to fall back to `()` in order to still compile, as the
|
||||
// trait is specifically implemented for `()` but not `!`.
|
||||
//
|
||||
// For details on the requirements for these relationships to be
|
||||
// set, see the relationship finding module in
|
||||
// compiler/rustc_trait_selection/src/traits/relationships.rs.
|
||||
debug!("fallback to () - found trait and projection: {:?}", diverging_vid);
|
||||
diverging_fallback.insert(diverging_ty, self.tcx.types.unit);
|
||||
} else if can_reach_non_diverging {
|
||||
debug!("fallback to () - reached non-diverging: {:?}", diverging_vid);
|
||||
diverging_fallback.insert(diverging_ty, self.tcx.types.unit);
|
||||
} else {
|
||||
debug!("fallback to ! - all diverging: {:?}", diverging_vid);
|
||||
diverging_fallback.insert(diverging_ty, self.tcx.mk_diverging_default());
|
||||
}
|
||||
}
|
||||
|
||||
diverging_fallback
|
||||
}
|
||||
|
||||
/// Returns a graph whose nodes are (unresolved) inference variables and where
|
||||
/// an edge `?A -> ?B` indicates that the variable `?A` is coerced to `?B`.
|
||||
fn create_coercion_graph(&self) -> VecGraph<ty::TyVid> {
|
||||
let pending_obligations = self.fulfillment_cx.borrow_mut().pending_obligations();
|
||||
debug!("create_coercion_graph: pending_obligations={:?}", pending_obligations);
|
||||
let coercion_edges: Vec<(ty::TyVid, ty::TyVid)> = pending_obligations
|
||||
.into_iter()
|
||||
.filter_map(|obligation| {
|
||||
// The predicates we are looking for look like `Coerce(?A -> ?B)`.
|
||||
// They will have no bound variables.
|
||||
obligation.predicate.kind().no_bound_vars()
|
||||
})
|
||||
.filter_map(|atom| {
|
||||
// We consider both subtyping and coercion to imply 'flow' from
|
||||
// some position in the code `a` to a different position `b`.
|
||||
// This is then used to determine which variables interact with
|
||||
// live code, and as such must fall back to `()` to preserve
|
||||
// soundness.
|
||||
//
|
||||
// In practice currently the two ways that this happens is
|
||||
// coercion and subtyping.
|
||||
let (a, b) = if let ty::PredicateKind::Coerce(ty::CoercePredicate { a, b }) = atom {
|
||||
(a, b)
|
||||
} else if let ty::PredicateKind::Subtype(ty::SubtypePredicate {
|
||||
a_is_expected: _,
|
||||
a,
|
||||
b,
|
||||
}) = atom
|
||||
{
|
||||
(a, b)
|
||||
} else {
|
||||
return None;
|
||||
};
|
||||
|
||||
let a_vid = self.root_vid(a)?;
|
||||
let b_vid = self.root_vid(b)?;
|
||||
Some((a_vid, b_vid))
|
||||
})
|
||||
.collect();
|
||||
debug!("create_coercion_graph: coercion_edges={:?}", coercion_edges);
|
||||
let num_ty_vars = self.infcx.num_ty_vars();
|
||||
VecGraph::new(num_ty_vars, coercion_edges)
|
||||
}
|
||||
|
||||
/// If `ty` is an unresolved type variable, returns its root vid.
|
||||
fn root_vid(&self, ty: Ty<'tcx>) -> Option<ty::TyVid> {
|
||||
Some(self.infcx.root_var(self.infcx.shallow_resolve(ty).ty_vid()?))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -286,6 +286,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
return;
|
||||
}
|
||||
|
||||
for a in &adj {
|
||||
if let Adjust::NeverToAny = a.kind {
|
||||
if a.target.is_ty_var() {
|
||||
self.diverging_type_vars.borrow_mut().insert(a.target);
|
||||
debug!("apply_adjustments: adding `{:?}` as diverging type var", a.target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let autoborrow_mut = adj.iter().any(|adj| {
|
||||
matches!(
|
||||
adj,
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use super::callee::DeferredCallResolution;
|
||||
use super::MaybeInProgressTables;
|
||||
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::{DefIdMap, LocalDefId};
|
||||
use rustc_hir::HirIdMap;
|
||||
|
@ -56,6 +57,11 @@ pub struct Inherited<'a, 'tcx> {
|
|||
pub(super) constness: hir::Constness,
|
||||
|
||||
pub(super) body_id: Option<hir::BodyId>,
|
||||
|
||||
/// Whenever we introduce an adjustment from `!` into a type variable,
|
||||
/// we record that type variable here. This is later used to inform
|
||||
/// fallback. See the `fallback` module for details.
|
||||
pub(super) diverging_type_vars: RefCell<FxHashSet<Ty<'tcx>>>,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> Deref for Inherited<'a, 'tcx> {
|
||||
|
@ -121,6 +127,7 @@ impl Inherited<'a, 'tcx> {
|
|||
deferred_call_resolutions: RefCell::new(Default::default()),
|
||||
deferred_cast_checks: RefCell::new(Vec::new()),
|
||||
deferred_generator_interiors: RefCell::new(Vec::new()),
|
||||
diverging_type_vars: RefCell::new(Default::default()),
|
||||
constness,
|
||||
body_id,
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use crate::structured_errors::StructuredDiagnostic;
|
||||
use rustc_errors::{pluralize, Applicability, DiagnosticBuilder, DiagnosticId};
|
||||
use rustc_hir as hir;
|
||||
use rustc_middle::hir::map::fn_sig;
|
||||
use rustc_middle::middle::resolve_lifetime::LifetimeScopeForPath;
|
||||
use rustc_middle::ty::{self as ty, TyCtxt};
|
||||
use rustc_session::Session;
|
||||
|
@ -292,12 +293,30 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
|
|||
&self,
|
||||
num_params_to_take: usize,
|
||||
) -> String {
|
||||
let fn_sig = self.tcx.hir().get_if_local(self.def_id).and_then(|node| fn_sig(node));
|
||||
let is_used_in_input = |def_id| {
|
||||
fn_sig.map_or(false, |fn_sig| {
|
||||
fn_sig.decl.inputs.iter().any(|ty| match ty.kind {
|
||||
hir::TyKind::Path(hir::QPath::Resolved(
|
||||
None,
|
||||
hir::Path { res: hir::def::Res::Def(_, id), .. },
|
||||
)) if *id == def_id => true,
|
||||
_ => false,
|
||||
})
|
||||
})
|
||||
};
|
||||
self.gen_params
|
||||
.params
|
||||
.iter()
|
||||
.skip(self.params_offset + self.num_provided_type_or_const_args())
|
||||
.take(num_params_to_take)
|
||||
.map(|param| param.name.to_string())
|
||||
.map(|param| match param.kind {
|
||||
// This is being infered from the item's inputs, no need to set it.
|
||||
ty::GenericParamDefKind::Type { .. } if is_used_in_input(param.def_id) => {
|
||||
"_".to_string()
|
||||
}
|
||||
_ => param.name.to_string(),
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ")
|
||||
}
|
||||
|
|
|
@ -1849,17 +1849,17 @@ macro_rules! int_impl {
|
|||
#[doc = concat!("let a: ", stringify!($SelfT)," = 8;")]
|
||||
/// let b = 3;
|
||||
///
|
||||
/// assert_eq!(a.div_floor(b), 2);
|
||||
/// assert_eq!(a.div_floor(-b), -3);
|
||||
/// assert_eq!((-a).div_floor(b), -3);
|
||||
/// assert_eq!((-a).div_floor(-b), 2);
|
||||
/// assert_eq!(a.unstable_div_floor(b), 2);
|
||||
/// assert_eq!(a.unstable_div_floor(-b), -3);
|
||||
/// assert_eq!((-a).unstable_div_floor(b), -3);
|
||||
/// assert_eq!((-a).unstable_div_floor(-b), 2);
|
||||
/// ```
|
||||
#[unstable(feature = "int_roundings", issue = "88581")]
|
||||
#[must_use = "this returns the result of the operation, \
|
||||
without modifying the original"]
|
||||
#[inline]
|
||||
#[rustc_inherit_overflow_checks]
|
||||
pub const fn div_floor(self, rhs: Self) -> Self {
|
||||
pub const fn unstable_div_floor(self, rhs: Self) -> Self {
|
||||
let d = self / rhs;
|
||||
let r = self % rhs;
|
||||
if (r > 0 && rhs < 0) || (r < 0 && rhs > 0) {
|
||||
|
@ -1884,17 +1884,17 @@ macro_rules! int_impl {
|
|||
#[doc = concat!("let a: ", stringify!($SelfT)," = 8;")]
|
||||
/// let b = 3;
|
||||
///
|
||||
/// assert_eq!(a.div_ceil(b), 3);
|
||||
/// assert_eq!(a.div_ceil(-b), -2);
|
||||
/// assert_eq!((-a).div_ceil(b), -2);
|
||||
/// assert_eq!((-a).div_ceil(-b), 3);
|
||||
/// assert_eq!(a.unstable_div_ceil(b), 3);
|
||||
/// assert_eq!(a.unstable_div_ceil(-b), -2);
|
||||
/// assert_eq!((-a).unstable_div_ceil(b), -2);
|
||||
/// assert_eq!((-a).unstable_div_ceil(-b), 3);
|
||||
/// ```
|
||||
#[unstable(feature = "int_roundings", issue = "88581")]
|
||||
#[must_use = "this returns the result of the operation, \
|
||||
without modifying the original"]
|
||||
#[inline]
|
||||
#[rustc_inherit_overflow_checks]
|
||||
pub const fn div_ceil(self, rhs: Self) -> Self {
|
||||
pub const fn unstable_div_ceil(self, rhs: Self) -> Self {
|
||||
let d = self / rhs;
|
||||
let r = self % rhs;
|
||||
if (r > 0 && rhs > 0) || (r < 0 && rhs < 0) {
|
||||
|
@ -1919,21 +1919,21 @@ macro_rules! int_impl {
|
|||
///
|
||||
/// ```
|
||||
/// #![feature(int_roundings)]
|
||||
#[doc = concat!("assert_eq!(16_", stringify!($SelfT), ".next_multiple_of(8), 16);")]
|
||||
#[doc = concat!("assert_eq!(23_", stringify!($SelfT), ".next_multiple_of(8), 24);")]
|
||||
#[doc = concat!("assert_eq!(16_", stringify!($SelfT), ".next_multiple_of(-8), 16);")]
|
||||
#[doc = concat!("assert_eq!(23_", stringify!($SelfT), ".next_multiple_of(-8), 16);")]
|
||||
#[doc = concat!("assert_eq!((-16_", stringify!($SelfT), ").next_multiple_of(8), -16);")]
|
||||
#[doc = concat!("assert_eq!((-23_", stringify!($SelfT), ").next_multiple_of(8), -16);")]
|
||||
#[doc = concat!("assert_eq!((-16_", stringify!($SelfT), ").next_multiple_of(-8), -16);")]
|
||||
#[doc = concat!("assert_eq!((-23_", stringify!($SelfT), ").next_multiple_of(-8), -24);")]
|
||||
#[doc = concat!("assert_eq!(16_", stringify!($SelfT), ".unstable_next_multiple_of(8), 16);")]
|
||||
#[doc = concat!("assert_eq!(23_", stringify!($SelfT), ".unstable_next_multiple_of(8), 24);")]
|
||||
#[doc = concat!("assert_eq!(16_", stringify!($SelfT), ".unstable_next_multiple_of(-8), 16);")]
|
||||
#[doc = concat!("assert_eq!(23_", stringify!($SelfT), ".unstable_next_multiple_of(-8), 16);")]
|
||||
#[doc = concat!("assert_eq!((-16_", stringify!($SelfT), ").unstable_next_multiple_of(8), -16);")]
|
||||
#[doc = concat!("assert_eq!((-23_", stringify!($SelfT), ").unstable_next_multiple_of(8), -16);")]
|
||||
#[doc = concat!("assert_eq!((-16_", stringify!($SelfT), ").unstable_next_multiple_of(-8), -16);")]
|
||||
#[doc = concat!("assert_eq!((-23_", stringify!($SelfT), ").unstable_next_multiple_of(-8), -24);")]
|
||||
/// ```
|
||||
#[unstable(feature = "int_roundings", issue = "88581")]
|
||||
#[must_use = "this returns the result of the operation, \
|
||||
without modifying the original"]
|
||||
#[inline]
|
||||
#[rustc_inherit_overflow_checks]
|
||||
pub const fn next_multiple_of(self, rhs: Self) -> Self {
|
||||
pub const fn unstable_next_multiple_of(self, rhs: Self) -> Self {
|
||||
// This would otherwise fail when calculating `r` when self == T::MIN.
|
||||
if rhs == -1 {
|
||||
return self;
|
||||
|
|
|
@ -1859,12 +1859,12 @@ macro_rules! uint_impl {
|
|||
///
|
||||
/// ```
|
||||
/// #![feature(int_roundings)]
|
||||
#[doc = concat!("assert_eq!(7_", stringify!($SelfT), ".div_floor(4), 1);")]
|
||||
#[doc = concat!("assert_eq!(7_", stringify!($SelfT), ".unstable_div_floor(4), 1);")]
|
||||
/// ```
|
||||
#[unstable(feature = "int_roundings", issue = "88581")]
|
||||
#[inline(always)]
|
||||
#[rustc_inherit_overflow_checks]
|
||||
pub const fn div_floor(self, rhs: Self) -> Self {
|
||||
pub const fn unstable_div_floor(self, rhs: Self) -> Self {
|
||||
self / rhs
|
||||
}
|
||||
|
||||
|
@ -1880,12 +1880,12 @@ macro_rules! uint_impl {
|
|||
///
|
||||
/// ```
|
||||
/// #![feature(int_roundings)]
|
||||
#[doc = concat!("assert_eq!(7_", stringify!($SelfT), ".div_ceil(4), 2);")]
|
||||
#[doc = concat!("assert_eq!(7_", stringify!($SelfT), ".unstable_div_ceil(4), 2);")]
|
||||
/// ```
|
||||
#[unstable(feature = "int_roundings", issue = "88581")]
|
||||
#[inline]
|
||||
#[rustc_inherit_overflow_checks]
|
||||
pub const fn div_ceil(self, rhs: Self) -> Self {
|
||||
pub const fn unstable_div_ceil(self, rhs: Self) -> Self {
|
||||
let d = self / rhs;
|
||||
let r = self % rhs;
|
||||
if r > 0 && rhs > 0 {
|
||||
|
@ -1908,15 +1908,15 @@ macro_rules! uint_impl {
|
|||
///
|
||||
/// ```
|
||||
/// #![feature(int_roundings)]
|
||||
#[doc = concat!("assert_eq!(16_", stringify!($SelfT), ".next_multiple_of(8), 16);")]
|
||||
#[doc = concat!("assert_eq!(23_", stringify!($SelfT), ".next_multiple_of(8), 24);")]
|
||||
#[doc = concat!("assert_eq!(16_", stringify!($SelfT), ".unstable_next_multiple_of(8), 16);")]
|
||||
#[doc = concat!("assert_eq!(23_", stringify!($SelfT), ".unstable_next_multiple_of(8), 24);")]
|
||||
/// ```
|
||||
#[unstable(feature = "int_roundings", issue = "88581")]
|
||||
#[must_use = "this returns the result of the operation, \
|
||||
without modifying the original"]
|
||||
#[inline]
|
||||
#[rustc_inherit_overflow_checks]
|
||||
pub const fn next_multiple_of(self, rhs: Self) -> Self {
|
||||
pub const fn unstable_next_multiple_of(self, rhs: Self) -> Self {
|
||||
match self % rhs {
|
||||
0 => self,
|
||||
r => self + (rhs - r)
|
||||
|
|
|
@ -294,33 +294,33 @@ macro_rules! int_module {
|
|||
fn test_div_floor() {
|
||||
let a: $T = 8;
|
||||
let b = 3;
|
||||
assert_eq!(a.div_floor(b), 2);
|
||||
assert_eq!(a.div_floor(-b), -3);
|
||||
assert_eq!((-a).div_floor(b), -3);
|
||||
assert_eq!((-a).div_floor(-b), 2);
|
||||
assert_eq!(a.unstable_div_floor(b), 2);
|
||||
assert_eq!(a.unstable_div_floor(-b), -3);
|
||||
assert_eq!((-a).unstable_div_floor(b), -3);
|
||||
assert_eq!((-a).unstable_div_floor(-b), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_div_ceil() {
|
||||
let a: $T = 8;
|
||||
let b = 3;
|
||||
assert_eq!(a.div_ceil(b), 3);
|
||||
assert_eq!(a.div_ceil(-b), -2);
|
||||
assert_eq!((-a).div_ceil(b), -2);
|
||||
assert_eq!((-a).div_ceil(-b), 3);
|
||||
assert_eq!(a.unstable_div_ceil(b), 3);
|
||||
assert_eq!(a.unstable_div_ceil(-b), -2);
|
||||
assert_eq!((-a).unstable_div_ceil(b), -2);
|
||||
assert_eq!((-a).unstable_div_ceil(-b), 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_next_multiple_of() {
|
||||
assert_eq!((16 as $T).next_multiple_of(8), 16);
|
||||
assert_eq!((23 as $T).next_multiple_of(8), 24);
|
||||
assert_eq!((16 as $T).next_multiple_of(-8), 16);
|
||||
assert_eq!((23 as $T).next_multiple_of(-8), 16);
|
||||
assert_eq!((-16 as $T).next_multiple_of(8), -16);
|
||||
assert_eq!((-23 as $T).next_multiple_of(8), -16);
|
||||
assert_eq!((-16 as $T).next_multiple_of(-8), -16);
|
||||
assert_eq!((-23 as $T).next_multiple_of(-8), -24);
|
||||
assert_eq!(MIN.next_multiple_of(-1), MIN);
|
||||
assert_eq!((16 as $T).unstable_next_multiple_of(8), 16);
|
||||
assert_eq!((23 as $T).unstable_next_multiple_of(8), 24);
|
||||
assert_eq!((16 as $T).unstable_next_multiple_of(-8), 16);
|
||||
assert_eq!((23 as $T).unstable_next_multiple_of(-8), 16);
|
||||
assert_eq!((-16 as $T).unstable_next_multiple_of(8), -16);
|
||||
assert_eq!((-23 as $T).unstable_next_multiple_of(8), -16);
|
||||
assert_eq!((-16 as $T).unstable_next_multiple_of(-8), -16);
|
||||
assert_eq!((-23 as $T).unstable_next_multiple_of(-8), -24);
|
||||
assert_eq!(MIN.unstable_next_multiple_of(-1), MIN);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -208,19 +208,19 @@ macro_rules! uint_module {
|
|||
|
||||
#[test]
|
||||
fn test_div_floor() {
|
||||
assert_eq!((8 as $T).div_floor(3), 2);
|
||||
assert_eq!((8 as $T).unstable_div_floor(3), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_div_ceil() {
|
||||
assert_eq!((8 as $T).div_ceil(3), 3);
|
||||
assert_eq!((8 as $T).unstable_div_ceil(3), 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_next_multiple_of() {
|
||||
assert_eq!((16 as $T).next_multiple_of(8), 16);
|
||||
assert_eq!((23 as $T).next_multiple_of(8), 24);
|
||||
assert_eq!(MAX.next_multiple_of(1), MAX);
|
||||
assert_eq!((16 as $T).unstable_next_multiple_of(8), 16);
|
||||
assert_eq!((23 as $T).unstable_next_multiple_of(8), 24);
|
||||
assert_eq!(MAX.unstable_next_multiple_of(1), MAX);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -248,7 +248,11 @@ impl<T> Packet<T> {
|
|||
// Returns true if blocking should proceed.
|
||||
fn decrement(&self, token: SignalToken) -> StartResult {
|
||||
unsafe {
|
||||
assert_eq!(self.to_wake.load(Ordering::SeqCst), 0);
|
||||
assert_eq!(
|
||||
self.to_wake.load(Ordering::SeqCst),
|
||||
0,
|
||||
"This is a known bug in the Rust standard library. See https://github.com/rust-lang/rust/issues/39364"
|
||||
);
|
||||
let ptr = token.cast_to_usize();
|
||||
self.to_wake.store(ptr, Ordering::SeqCst);
|
||||
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 86c7daa627aefda1371568b948eba21edf720747
|
||||
Subproject commit cba558df777a045b5657d56c29944e9e8fd3a776
|
|
@ -1,6 +1,6 @@
|
|||
// aux-build:primitive-doc.rs
|
||||
// compile-flags: --extern-html-root-url=primitive_doc=../ -Z unstable-options
|
||||
// ignore-windows
|
||||
// only-linux
|
||||
|
||||
#![feature(no_core)]
|
||||
#![no_core]
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// aux-build:my-core.rs
|
||||
// build-aux-docs
|
||||
// ignore-cross-compile
|
||||
// ignore-windows
|
||||
// only-linux
|
||||
|
||||
#![deny(broken_intra_doc_links)]
|
||||
#![feature(no_core, lang_items)]
|
||||
|
|
8
src/test/ui/attributes/extented-attribute-macro-error.rs
Normal file
8
src/test/ui/attributes/extented-attribute-macro-error.rs
Normal file
|
@ -0,0 +1,8 @@
|
|||
// normalize-stderr-test: "couldn't read.*" -> "couldn't read the file"
|
||||
|
||||
#![feature(extended_key_value_attributes)]
|
||||
#![doc = include_str!("../not_existing_file.md")]
|
||||
struct Documented {}
|
||||
//~^^ ERROR couldn't read
|
||||
|
||||
fn main() {}
|
10
src/test/ui/attributes/extented-attribute-macro-error.stderr
Normal file
10
src/test/ui/attributes/extented-attribute-macro-error.stderr
Normal file
|
@ -0,0 +1,10 @@
|
|||
error: couldn't read the file
|
||||
--> $DIR/extented-attribute-macro-error.rs:4:10
|
||||
|
|
||||
LL | #![doc = include_str!("../not_existing_file.md")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: this error originates in the macro `include_str` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: aborting due to previous error
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
error[E0277]: the trait bound `(): std::error::Error` is not satisfied
|
||||
--> $DIR/coerce-issue-49593-box-never.rs:17:53
|
||||
|
|
||||
LL | /* *mut $0 is coerced to Box<dyn Error> here */ Box::<_ /* ! */>::new(x)
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::error::Error` is not implemented for `()`
|
||||
|
|
||||
= note: required for the cast to the object type `dyn std::error::Error`
|
||||
|
||||
error[E0277]: the trait bound `(): std::error::Error` is not satisfied
|
||||
--> $DIR/coerce-issue-49593-box-never.rs:22:49
|
||||
|
|
||||
LL | /* *mut $0 is coerced to *mut Error here */ raw_ptr_box::<_ /* ! */>(x)
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::error::Error` is not implemented for `()`
|
||||
|
|
||||
= note: required for the cast to the object type `(dyn std::error::Error + 'static)`
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0277`.
|
|
@ -1,5 +1,9 @@
|
|||
// check-pass
|
||||
#![feature(never_type, never_type_fallback)]
|
||||
// revisions: nofallback fallback
|
||||
//[fallback] check-pass
|
||||
//[nofallback] check-fail
|
||||
|
||||
#![feature(never_type)]
|
||||
#![cfg_attr(fallback, feature(never_type_fallback))]
|
||||
#![allow(unreachable_code)]
|
||||
|
||||
use std::error::Error;
|
||||
|
@ -11,10 +15,12 @@ fn raw_ptr_box<T>(t: T) -> *mut T {
|
|||
|
||||
fn foo(x: !) -> Box<dyn Error> {
|
||||
/* *mut $0 is coerced to Box<dyn Error> here */ Box::<_ /* ! */>::new(x)
|
||||
//[nofallback]~^ ERROR trait bound `(): std::error::Error` is not satisfied
|
||||
}
|
||||
|
||||
fn foo_raw_ptr(x: !) -> *mut dyn Error {
|
||||
/* *mut $0 is coerced to *mut Error here */ raw_ptr_box::<_ /* ! */>(x)
|
||||
//[nofallback]~^ ERROR trait bound `(): std::error::Error` is not satisfied
|
||||
}
|
||||
|
||||
fn no_coercion(d: *mut dyn Error) -> *mut dyn Error {
|
||||
|
|
|
@ -34,7 +34,7 @@ note: ...which requires type-checking `cycle1`...
|
|||
|
|
||||
LL | send(cycle2().clone());
|
||||
| ^^^^
|
||||
= note: ...which requires evaluating trait selection obligation `impl std::clone::Clone: std::marker::Send`...
|
||||
= note: ...which requires evaluating trait selection obligation `impl core::clone::Clone: core::marker::Send`...
|
||||
note: ...which requires computing type of `cycle2::{opaque#0}`...
|
||||
--> $DIR/auto-trait-leak.rs:19:16
|
||||
|
|
||||
|
@ -70,7 +70,7 @@ note: ...which requires type-checking `cycle2`...
|
|||
|
|
||||
LL | send(cycle1().clone());
|
||||
| ^^^^
|
||||
= note: ...which requires evaluating trait selection obligation `impl std::clone::Clone: std::marker::Send`...
|
||||
= note: ...which requires evaluating trait selection obligation `impl core::clone::Clone: core::marker::Send`...
|
||||
= note: ...which again requires computing type of `cycle1::{opaque#0}`, completing the cycle
|
||||
note: cycle used when checking item types in top-level module
|
||||
--> $DIR/auto-trait-leak.rs:1:1
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
// ignore-wasm32-bare compiled with panic=abort by default
|
||||
// revisions: mir thir
|
||||
// [thir]compile-flags: -Zthir-unsafeck
|
||||
// ignore-tidy-linelength
|
||||
|
||||
// This test checks panic emitted from `mem::{uninitialized,zeroed}`.
|
||||
|
||||
|
@ -114,11 +115,11 @@ fn main() {
|
|||
|
||||
test_panic_msg(
|
||||
|| mem::uninitialized::<*const dyn Send>(),
|
||||
"attempted to leave type `*const dyn std::marker::Send` uninitialized, which is invalid"
|
||||
"attempted to leave type `*const dyn core::marker::Send` uninitialized, which is invalid"
|
||||
);
|
||||
test_panic_msg(
|
||||
|| mem::zeroed::<*const dyn Send>(),
|
||||
"attempted to zero-initialize type `*const dyn std::marker::Send`, which is invalid"
|
||||
"attempted to zero-initialize type `*const dyn core::marker::Send`, which is invalid"
|
||||
);
|
||||
|
||||
/* FIXME(#66151) we conservatively do not error here yet.
|
||||
|
@ -145,12 +146,12 @@ fn main() {
|
|||
|
||||
test_panic_msg(
|
||||
|| mem::uninitialized::<(NonNull<u32>, u32, u32)>(),
|
||||
"attempted to leave type `(std::ptr::NonNull<u32>, u32, u32)` uninitialized, \
|
||||
"attempted to leave type `(core::ptr::non_null::NonNull<u32>, u32, u32)` uninitialized, \
|
||||
which is invalid"
|
||||
);
|
||||
test_panic_msg(
|
||||
|| mem::zeroed::<(NonNull<u32>, u32, u32)>(),
|
||||
"attempted to zero-initialize type `(std::ptr::NonNull<u32>, u32, u32)`, \
|
||||
"attempted to zero-initialize type `(core::ptr::non_null::NonNull<u32>, u32, u32)`, \
|
||||
which is invalid"
|
||||
);
|
||||
|
||||
|
@ -187,7 +188,7 @@ fn main() {
|
|||
);
|
||||
test_panic_msg(
|
||||
|| mem::uninitialized::<ManuallyDrop<LR>>(),
|
||||
"attempted to leave type `std::mem::ManuallyDrop<LR>` uninitialized, which is invalid"
|
||||
"attempted to leave type `core::mem::manually_drop::ManuallyDrop<LR>` uninitialized, which is invalid"
|
||||
);
|
||||
|
||||
// Some things that should work.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
error[E0277]: the trait bound `!: ImplementedForUnitButNotNever` is not satisfied
|
||||
--> $DIR/defaulted-never-note.rs:26:5
|
||||
--> $DIR/defaulted-never-note.rs:30:5
|
||||
|
|
||||
LL | foo(_x);
|
||||
| ^^^ the trait `ImplementedForUnitButNotNever` is not implemented for `!`
|
||||
|
@ -8,7 +8,7 @@ LL | foo(_x);
|
|||
= note: this error might have been caused by changes to Rust's type-inference algorithm (see issue #48950 <https://github.com/rust-lang/rust/issues/48950> for more information).
|
||||
= help: did you intend to use the type `()` here instead?
|
||||
note: required by a bound in `foo`
|
||||
--> $DIR/defaulted-never-note.rs:21:11
|
||||
--> $DIR/defaulted-never-note.rs:25:11
|
||||
|
|
||||
LL | fn foo<T: ImplementedForUnitButNotNever>(_t: T) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `foo`
|
|
@ -1,6 +1,10 @@
|
|||
// revisions: nofallback fallback
|
||||
//[nofallback] run-pass
|
||||
//[fallback] check-fail
|
||||
|
||||
// We need to opt into the `never_type_fallback` feature
|
||||
// to trigger the requirement that this is testing.
|
||||
#![feature(never_type, never_type_fallback)]
|
||||
#![cfg_attr(fallback, feature(never_type, never_type_fallback))]
|
||||
|
||||
#![allow(unused)]
|
||||
|
||||
|
@ -19,16 +23,16 @@ trait ImplementedForUnitButNotNever {}
|
|||
impl ImplementedForUnitButNotNever for () {}
|
||||
|
||||
fn foo<T: ImplementedForUnitButNotNever>(_t: T) {}
|
||||
//~^ NOTE required by this bound in `foo`
|
||||
//~| NOTE required by a bound in `foo`
|
||||
//[fallback]~^ NOTE required by this bound in `foo`
|
||||
//[fallback]~| NOTE required by a bound in `foo`
|
||||
fn smeg() {
|
||||
let _x = return;
|
||||
foo(_x);
|
||||
//~^ ERROR the trait bound
|
||||
//~| NOTE the trait `ImplementedForUnitButNotNever` is not implemented
|
||||
//~| NOTE this trait is implemented for `()`
|
||||
//~| NOTE this error might have been caused
|
||||
//~| HELP did you intend
|
||||
//[fallback]~^ ERROR the trait bound
|
||||
//[fallback]~| NOTE the trait `ImplementedForUnitButNotNever` is not implemented
|
||||
//[fallback]~| NOTE this trait is implemented for `()`
|
||||
//[fallback]~| NOTE this error might have been caused
|
||||
//[fallback]~| HELP did you intend
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
|
|
@ -1,30 +1,28 @@
|
|||
// revisions: nofallback fallback
|
||||
// run-pass
|
||||
|
||||
#![allow(dead_code)]
|
||||
#![allow(unused_assignments)]
|
||||
#![allow(unused_variables)]
|
||||
#![allow(unreachable_code)]
|
||||
|
||||
// Test various cases where we permit an unconstrained variable
|
||||
// to fallback based on control-flow.
|
||||
//
|
||||
// These represent current behavior, but are pretty dubious. I would
|
||||
// like to revisit these and potentially change them. --nmatsakis
|
||||
// to fallback based on control-flow. In all of these cases,
|
||||
// the type variable winds up being the target of both a `!` coercion
|
||||
// and a coercion from a non-`!` variable, and hence falls back to `()`.
|
||||
#![cfg_attr(fallback, feature(never_type, never_type_fallback))]
|
||||
|
||||
#![feature(never_type, never_type_fallback)]
|
||||
|
||||
trait BadDefault {
|
||||
trait UnitDefault {
|
||||
fn default() -> Self;
|
||||
}
|
||||
|
||||
impl BadDefault for u32 {
|
||||
impl UnitDefault for u32 {
|
||||
fn default() -> Self {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
impl BadDefault for ! {
|
||||
fn default() -> ! {
|
||||
impl UnitDefault for () {
|
||||
fn default() -> () {
|
||||
panic!()
|
||||
}
|
||||
}
|
||||
|
@ -33,7 +31,7 @@ fn assignment() {
|
|||
let x;
|
||||
|
||||
if true {
|
||||
x = BadDefault::default();
|
||||
x = UnitDefault::default();
|
||||
} else {
|
||||
x = return;
|
||||
}
|
||||
|
@ -45,13 +43,13 @@ fn assignment_rev() {
|
|||
if true {
|
||||
x = return;
|
||||
} else {
|
||||
x = BadDefault::default();
|
||||
x = UnitDefault::default();
|
||||
}
|
||||
}
|
||||
|
||||
fn if_then_else() {
|
||||
let _x = if true {
|
||||
BadDefault::default()
|
||||
UnitDefault::default()
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
|
@ -61,19 +59,19 @@ fn if_then_else_rev() {
|
|||
let _x = if true {
|
||||
return;
|
||||
} else {
|
||||
BadDefault::default()
|
||||
UnitDefault::default()
|
||||
};
|
||||
}
|
||||
|
||||
fn match_arm() {
|
||||
let _x = match Ok(BadDefault::default()) {
|
||||
let _x = match Ok(UnitDefault::default()) {
|
||||
Ok(v) => v,
|
||||
Err(()) => return,
|
||||
};
|
||||
}
|
||||
|
||||
fn match_arm_rev() {
|
||||
let _x = match Ok(BadDefault::default()) {
|
||||
let _x = match Ok(UnitDefault::default()) {
|
||||
Err(()) => return,
|
||||
Ok(v) => v,
|
||||
};
|
||||
|
@ -84,7 +82,7 @@ fn loop_break() {
|
|||
if false {
|
||||
break return;
|
||||
} else {
|
||||
break BadDefault::default();
|
||||
break UnitDefault::default();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -94,9 +92,9 @@ fn loop_break_rev() {
|
|||
if false {
|
||||
break return;
|
||||
} else {
|
||||
break BadDefault::default();
|
||||
break UnitDefault::default();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn main() { }
|
||||
fn main() {}
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
error[E0277]: the trait bound `!: Test` is not satisfied
|
||||
--> $DIR/diverging-fallback-no-leak.rs:17:5
|
||||
|
|
||||
LL | unconstrained_arg(return);
|
||||
| ^^^^^^^^^^^^^^^^^ the trait `Test` is not implemented for `!`
|
||||
|
|
||||
= note: this trait is implemented for `()`.
|
||||
= note: this error might have been caused by changes to Rust's type-inference algorithm (see issue #48950 <https://github.com/rust-lang/rust/issues/48950> for more information).
|
||||
= help: did you intend to use the type `()` here instead?
|
||||
note: required by a bound in `unconstrained_arg`
|
||||
--> $DIR/diverging-fallback-no-leak.rs:12:25
|
||||
|
|
||||
LL | fn unconstrained_arg<T: Test>(_: T) {}
|
||||
| ^^^^ required by this bound in `unconstrained_arg`
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0277`.
|
19
src/test/ui/never_type/diverging-fallback-no-leak.rs
Normal file
19
src/test/ui/never_type/diverging-fallback-no-leak.rs
Normal file
|
@ -0,0 +1,19 @@
|
|||
// revisions: nofallback fallback
|
||||
//[nofallback] check-pass
|
||||
|
||||
#![cfg_attr(fallback, feature(never_type, never_type_fallback))]
|
||||
|
||||
fn make_unit() {}
|
||||
|
||||
trait Test {}
|
||||
impl Test for i32 {}
|
||||
impl Test for () {}
|
||||
|
||||
fn unconstrained_arg<T: Test>(_: T) {}
|
||||
|
||||
fn main() {
|
||||
// Here the type variable falls back to `!`,
|
||||
// and hence we get a type error.
|
||||
unconstrained_arg(return);
|
||||
//[fallback]~^ ERROR trait bound `!: Test` is not satisfied
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
// Variant of diverging-falllback-control-flow that tests
|
||||
// the specific case of a free function with an unconstrained
|
||||
// return type. This captures the pattern we saw in the wild
|
||||
// in the objc crate, where changing the fallback from `!` to `()`
|
||||
// resulted in unsoundness.
|
||||
//
|
||||
// check-pass
|
||||
|
||||
// revisions: nofallback fallback
|
||||
|
||||
#![cfg_attr(fallback, feature(never_type, never_type_fallback))]
|
||||
|
||||
|
||||
fn make_unit() {}
|
||||
|
||||
trait UnitReturn {}
|
||||
impl UnitReturn for i32 {}
|
||||
impl UnitReturn for () {}
|
||||
|
||||
fn unconstrained_return<T: UnitReturn>() -> T {
|
||||
unsafe {
|
||||
let make_unit_fn: fn() = make_unit;
|
||||
let ffi: fn() -> T = std::mem::transmute(make_unit_fn);
|
||||
ffi()
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// In Ye Olde Days, the `T` parameter of `unconstrained_return`
|
||||
// winds up "entangled" with the `!` type that results from
|
||||
// `panic!`, and hence falls back to `()`. This is kind of unfortunate
|
||||
// and unexpected. When we introduced the `!` type, the original
|
||||
// idea was to change that fallback to `!`, but that would have resulted
|
||||
// in this code no longer compiling (or worse, in some cases it injected
|
||||
// unsound results).
|
||||
let _ = if true { unconstrained_return() } else { panic!() };
|
||||
}
|
23
src/test/ui/never_type/fallback-closure-ret.rs
Normal file
23
src/test/ui/never_type/fallback-closure-ret.rs
Normal file
|
@ -0,0 +1,23 @@
|
|||
// This test verifies that never type fallback preserves the following code in a
|
||||
// compiling state. This pattern is fairly common in the wild, notably seen in
|
||||
// wasmtime v0.16. Typically this is some closure wrapper that expects a
|
||||
// collection of 'known' signatures, and -> ! is not included in that set.
|
||||
//
|
||||
// This test is specifically targeted by the unit type fallback when
|
||||
// encountering a set of obligations like `?T: Foo` and `Trait::Projection =
|
||||
// ?T`. In the code below, these are `R: Bar` and `Fn::Output = R`.
|
||||
//
|
||||
// revisions: nofallback fallback
|
||||
// check-pass
|
||||
|
||||
#![cfg_attr(fallback, feature(never_type_fallback))]
|
||||
|
||||
trait Bar { }
|
||||
impl Bar for () { }
|
||||
impl Bar for u32 { }
|
||||
|
||||
fn foo<R: Bar>(_: impl Fn() -> R) {}
|
||||
|
||||
fn main() {
|
||||
foo(|| panic!());
|
||||
}
|
17
src/test/ui/never_type/fallback-closure-wrap.fallback.stderr
Normal file
17
src/test/ui/never_type/fallback-closure-wrap.fallback.stderr
Normal file
|
@ -0,0 +1,17 @@
|
|||
error[E0271]: type mismatch resolving `<[closure@$DIR/fallback-closure-wrap.rs:18:40: 21:6] as FnOnce<()>>::Output == ()`
|
||||
--> $DIR/fallback-closure-wrap.rs:18:31
|
||||
|
|
||||
LL | let error = Closure::wrap(Box::new(move || {
|
||||
| _______________________________^
|
||||
LL | |
|
||||
LL | | panic!("Can't connect to server.");
|
||||
LL | | }) as Box<dyn FnMut()>);
|
||||
| |______^ expected `()`, found `!`
|
||||
|
|
||||
= note: expected unit type `()`
|
||||
found type `!`
|
||||
= note: required for the cast to the object type `dyn FnMut()`
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0271`.
|
30
src/test/ui/never_type/fallback-closure-wrap.rs
Normal file
30
src/test/ui/never_type/fallback-closure-wrap.rs
Normal file
|
@ -0,0 +1,30 @@
|
|||
// This is a minified example from Crater breakage observed when attempting to
|
||||
// stabilize never type, nstoddard/webgl-gui @ 22f0169f.
|
||||
//
|
||||
// This particular test case currently fails as the inference to `()` rather
|
||||
// than `!` happens as a result of an `as` cast, which is not currently tracked.
|
||||
// Crater did not find many cases of this occuring, but it is included for
|
||||
// awareness.
|
||||
//
|
||||
// revisions: nofallback fallback
|
||||
//[nofallback] check-pass
|
||||
//[fallback] check-fail
|
||||
|
||||
#![cfg_attr(fallback, feature(never_type_fallback))]
|
||||
|
||||
use std::marker::PhantomData;
|
||||
|
||||
fn main() {
|
||||
let error = Closure::wrap(Box::new(move || {
|
||||
//[fallback]~^ ERROR type mismatch resolving
|
||||
panic!("Can't connect to server.");
|
||||
}) as Box<dyn FnMut()>);
|
||||
}
|
||||
|
||||
struct Closure<T: ?Sized>(PhantomData<T>);
|
||||
|
||||
impl<T: ?Sized> Closure<T> {
|
||||
fn wrap(data: Box<T>) -> Closure<T> {
|
||||
todo!()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
error[E0277]: the trait bound `E: From<()>` is not satisfied
|
||||
--> $DIR/never-value-fallback-issue-66757.rs:27:5
|
||||
|
|
||||
LL | <E as From<_>>::from(never);
|
||||
| ^^^^^^^^^^^^^^^^^^^^ the trait `From<()>` is not implemented for `E`
|
||||
|
|
||||
= help: the following implementations were found:
|
||||
<E as From<!>>
|
||||
note: required by `from`
|
||||
--> $SRC_DIR/core/src/convert/mod.rs:LL:COL
|
||||
|
|
||||
LL | fn from(_: T) -> Self;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0277`.
|
|
@ -4,12 +4,13 @@
|
|||
// never) and an uninferred variable (here the argument to `From`) it
|
||||
// doesn't fallback to `()` but rather `!`.
|
||||
//
|
||||
// run-pass
|
||||
// revisions: nofallback fallback
|
||||
//[fallback] run-pass
|
||||
//[nofallback] check-fail
|
||||
|
||||
#![feature(never_type)]
|
||||
|
||||
// FIXME(#67225) -- this should be true even without the fallback gate.
|
||||
#![feature(never_type_fallback)]
|
||||
#![cfg_attr(fallback, feature(never_type_fallback))]
|
||||
|
||||
struct E;
|
||||
|
||||
|
@ -23,7 +24,7 @@ impl From<!> for E {
|
|||
#[allow(dead_code)]
|
||||
fn foo(never: !) {
|
||||
<E as From<!>>::from(never); // Ok
|
||||
<E as From<_>>::from(never); // Inference fails here
|
||||
<E as From<_>>::from(never); //[nofallback]~ ERROR trait bound `E: From<()>` is not satisfied
|
||||
}
|
||||
|
||||
fn main() { }
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
//~ ERROR cycle detected when computing layout of `S`
|
||||
//~| NOTE ...which requires computing layout of `std::option::Option<<S as Mirror>::It>`...
|
||||
//~| NOTE ...which requires computing layout of `std::option::Option<S>`...
|
||||
//~| NOTE ...which requires computing layout of `core::option::Option<<S as Mirror>::It>`...
|
||||
//~| NOTE ...which requires computing layout of `core::option::Option<S>`...
|
||||
//~| NOTE ...which again requires computing layout of `S`, completing the cycle
|
||||
//~| NOTE cycle used when computing layout of `std::option::Option<S>`
|
||||
//~| NOTE cycle used when computing layout of `core::option::Option<S>`
|
||||
|
||||
// build-fail
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
error[E0391]: cycle detected when computing layout of `S`
|
||||
|
|
||||
= note: ...which requires computing layout of `std::option::Option<<S as Mirror>::It>`...
|
||||
= note: ...which requires computing layout of `std::option::Option<S>`...
|
||||
= note: ...which requires computing layout of `core::option::Option<<S as Mirror>::It>`...
|
||||
= note: ...which requires computing layout of `core::option::Option<S>`...
|
||||
= note: ...which again requires computing layout of `S`, completing the cycle
|
||||
= note: cycle used when computing layout of `std::option::Option<S>`
|
||||
= note: cycle used when computing layout of `core::option::Option<S>`
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
// run-rustfix
|
||||
|
||||
fn two_type_params<A, B>(_: B) {}
|
||||
|
||||
fn main() {
|
||||
two_type_params::<String, _>(100); //~ ERROR this function takes 2 generic arguments
|
||||
two_type_params::<String, _>(100);
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
// run-rustfix
|
||||
|
||||
fn two_type_params<A, B>(_: B) {}
|
||||
|
||||
fn main() {
|
||||
two_type_params::<String>(100); //~ ERROR this function takes 2 generic arguments
|
||||
two_type_params::<String, _>(100);
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
error[E0107]: this function takes 2 generic arguments but 1 generic argument was supplied
|
||||
--> $DIR/missing-type-param-used-in-param.rs:6:5
|
||||
|
|
||||
LL | two_type_params::<String>(100);
|
||||
| ^^^^^^^^^^^^^^^ ------ supplied 1 generic argument
|
||||
| |
|
||||
| expected 2 generic arguments
|
||||
|
|
||||
note: function defined here, with 2 generic parameters: `A`, `B`
|
||||
--> $DIR/missing-type-param-used-in-param.rs:3:4
|
||||
|
|
||||
LL | fn two_type_params<A, B>(_: B) {}
|
||||
| ^^^^^^^^^^^^^^^ - -
|
||||
help: add missing generic argument
|
||||
|
|
||||
LL | two_type_params::<String, _>(100);
|
||||
| +++
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0107`.
|
|
@ -9,7 +9,7 @@ note: ...which requires type-checking `m::bar`...
|
|||
|
|
||||
LL | is_send(foo());
|
||||
| ^^^^^^^
|
||||
= note: ...which requires evaluating trait selection obligation `impl std::fmt::Debug: std::marker::Send`...
|
||||
= note: ...which requires evaluating trait selection obligation `impl core::fmt::Debug: core::marker::Send`...
|
||||
= note: ...which again requires computing type of `m::Foo::{opaque#0}`, completing the cycle
|
||||
note: cycle used when checking item types in module `m`
|
||||
--> $DIR/auto-trait-leakage3.rs:6:1
|
||||
|
|
|
@ -9,7 +9,7 @@ note: ...which requires type-checking `m::bar`...
|
|||
|
|
||||
LL | is_send(foo()); // Today: error
|
||||
| ^^^^^^^
|
||||
= note: ...which requires evaluating trait selection obligation `impl std::fmt::Debug: std::marker::Send`...
|
||||
= note: ...which requires evaluating trait selection obligation `impl core::fmt::Debug: core::marker::Send`...
|
||||
= note: ...which again requires computing type of `m::Foo::{opaque#0}`, completing the cycle
|
||||
note: cycle used when checking item types in module `m`
|
||||
--> $DIR/inference-cycle.rs:4:1
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue