1
Fork 0

remove sub_relations from infcx, recompute in diagnostics

we don't track them when canonicalizing or when freshening,
resulting in instable caching in the old solver, and issues when
instantiating query responses in the new one.
This commit is contained in:
lcnr 2024-01-15 11:44:56 +01:00
parent 1bb3a9f67a
commit 91535ad026
27 changed files with 180 additions and 280 deletions

View file

@ -1522,10 +1522,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
if self.next_trait_solver() if self.next_trait_solver()
&& let ty::Alias(..) = ty.kind() && let ty::Alias(..) = ty.kind()
{ {
match self // We need to use a separate variable here as otherwise the temporary for
// `self.fulfillment_cx.borrow_mut()` is alive in the `Err` branch, resulting
// in a reentrant borrow, causing an ICE.
let result = self
.at(&self.misc(sp), self.param_env) .at(&self.misc(sp), self.param_env)
.structurally_normalize(ty, &mut **self.fulfillment_cx.borrow_mut()) .structurally_normalize(ty, &mut **self.fulfillment_cx.borrow_mut());
{ match result {
Ok(normalized_ty) => normalized_ty, Ok(normalized_ty) => normalized_ty,
Err(errors) => { Err(errors) => {
let guar = self.err_ctxt().report_fulfillment_errors(errors); let guar = self.err_ctxt().report_fulfillment_errors(errors);

View file

@ -11,6 +11,7 @@ use rustc_hir as hir;
use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir_analysis::astconv::AstConv; use rustc_hir_analysis::astconv::AstConv;
use rustc_infer::infer; use rustc_infer::infer;
use rustc_infer::infer::error_reporting::sub_relations::SubRelations;
use rustc_infer::infer::error_reporting::TypeErrCtxt; use rustc_infer::infer::error_reporting::TypeErrCtxt;
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind};
@ -155,8 +156,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
/// ///
/// [`InferCtxt::err_ctxt`]: infer::InferCtxt::err_ctxt /// [`InferCtxt::err_ctxt`]: infer::InferCtxt::err_ctxt
pub fn err_ctxt(&'a self) -> TypeErrCtxt<'a, 'tcx> { pub fn err_ctxt(&'a self) -> TypeErrCtxt<'a, 'tcx> {
let mut sub_relations = SubRelations::default();
sub_relations.add_constraints(
self,
self.fulfillment_cx.borrow_mut().pending_obligations().iter().map(|o| o.predicate),
);
TypeErrCtxt { TypeErrCtxt {
infcx: &self.infcx, infcx: &self.infcx,
sub_relations: RefCell::new(sub_relations),
typeck_results: Some(self.typeck_results.borrow()), typeck_results: Some(self.typeck_results.borrow()),
fallback_has_occurred: self.fallback_has_occurred.get(), fallback_has_occurred: self.fallback_has_occurred.get(),
normalize_fn_sig: Box::new(|fn_sig| { normalize_fn_sig: Box::new(|fn_sig| {

View file

@ -88,6 +88,7 @@ mod note_and_explain;
mod suggest; mod suggest;
pub(crate) mod need_type_info; pub(crate) mod need_type_info;
pub mod sub_relations;
pub use need_type_info::TypeAnnotationNeeded; pub use need_type_info::TypeAnnotationNeeded;
pub mod nice_region_error; pub mod nice_region_error;
@ -123,6 +124,8 @@ fn escape_literal(s: &str) -> String {
/// methods which should not be used during the happy path. /// methods which should not be used during the happy path.
pub struct TypeErrCtxt<'a, 'tcx> { pub struct TypeErrCtxt<'a, 'tcx> {
pub infcx: &'a InferCtxt<'tcx>, pub infcx: &'a InferCtxt<'tcx>,
pub sub_relations: std::cell::RefCell<sub_relations::SubRelations>,
pub typeck_results: Option<std::cell::Ref<'a, ty::TypeckResults<'tcx>>>, pub typeck_results: Option<std::cell::Ref<'a, ty::TypeckResults<'tcx>>>,
pub fallback_has_occurred: bool, pub fallback_has_occurred: bool,

View file

@ -502,7 +502,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
parent_name, parent_name,
}); });
let args = if self.infcx.tcx.get_diagnostic_item(sym::iterator_collect_fn) let args = if self.tcx.get_diagnostic_item(sym::iterator_collect_fn)
== Some(generics_def_id) == Some(generics_def_id)
{ {
"Vec<_>".to_string() "Vec<_>".to_string()
@ -710,7 +710,7 @@ struct InsertableGenericArgs<'tcx> {
/// While doing so, the currently best spot is stored in `infer_source`. /// While doing so, the currently best spot is stored in `infer_source`.
/// For details on how we rank spots, see [Self::source_cost] /// For details on how we rank spots, see [Self::source_cost]
struct FindInferSourceVisitor<'a, 'tcx> { struct FindInferSourceVisitor<'a, 'tcx> {
infcx: &'a InferCtxt<'tcx>, tecx: &'a TypeErrCtxt<'a, 'tcx>,
typeck_results: &'a TypeckResults<'tcx>, typeck_results: &'a TypeckResults<'tcx>,
target: GenericArg<'tcx>, target: GenericArg<'tcx>,
@ -722,12 +722,12 @@ struct FindInferSourceVisitor<'a, 'tcx> {
impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> { impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> {
fn new( fn new(
infcx: &'a InferCtxt<'tcx>, tecx: &'a TypeErrCtxt<'a, 'tcx>,
typeck_results: &'a TypeckResults<'tcx>, typeck_results: &'a TypeckResults<'tcx>,
target: GenericArg<'tcx>, target: GenericArg<'tcx>,
) -> Self { ) -> Self {
FindInferSourceVisitor { FindInferSourceVisitor {
infcx, tecx,
typeck_results, typeck_results,
target, target,
@ -778,7 +778,7 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> {
} }
// The sources are listed in order of preference here. // The sources are listed in order of preference here.
let tcx = self.infcx.tcx; let tcx = self.tecx.tcx;
let ctx = CostCtxt { tcx }; let ctx = CostCtxt { tcx };
match source.kind { match source.kind {
InferSourceKind::LetBinding { ty, .. } => ctx.ty_cost(ty), InferSourceKind::LetBinding { ty, .. } => ctx.ty_cost(ty),
@ -829,12 +829,12 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> {
fn node_args_opt(&self, hir_id: HirId) -> Option<GenericArgsRef<'tcx>> { fn node_args_opt(&self, hir_id: HirId) -> Option<GenericArgsRef<'tcx>> {
let args = self.typeck_results.node_args_opt(hir_id); let args = self.typeck_results.node_args_opt(hir_id);
self.infcx.resolve_vars_if_possible(args) self.tecx.resolve_vars_if_possible(args)
} }
fn opt_node_type(&self, hir_id: HirId) -> Option<Ty<'tcx>> { fn opt_node_type(&self, hir_id: HirId) -> Option<Ty<'tcx>> {
let ty = self.typeck_results.node_type_opt(hir_id); let ty = self.typeck_results.node_type_opt(hir_id);
self.infcx.resolve_vars_if_possible(ty) self.tecx.resolve_vars_if_possible(ty)
} }
// Check whether this generic argument is the inference variable we // Check whether this generic argument is the inference variable we
@ -849,7 +849,7 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> {
use ty::{Infer, TyVar}; use ty::{Infer, TyVar};
match (inner_ty.kind(), target_ty.kind()) { match (inner_ty.kind(), target_ty.kind()) {
(&Infer(TyVar(a_vid)), &Infer(TyVar(b_vid))) => { (&Infer(TyVar(a_vid)), &Infer(TyVar(b_vid))) => {
self.infcx.inner.borrow_mut().type_variables().sub_unified(a_vid, b_vid) self.tecx.sub_relations.borrow_mut().unified(self.tecx, a_vid, b_vid)
} }
_ => false, _ => false,
} }
@ -857,12 +857,9 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> {
(GenericArgKind::Const(inner_ct), GenericArgKind::Const(target_ct)) => { (GenericArgKind::Const(inner_ct), GenericArgKind::Const(target_ct)) => {
use ty::InferConst::*; use ty::InferConst::*;
match (inner_ct.kind(), target_ct.kind()) { match (inner_ct.kind(), target_ct.kind()) {
(ty::ConstKind::Infer(Var(a_vid)), ty::ConstKind::Infer(Var(b_vid))) => self (ty::ConstKind::Infer(Var(a_vid)), ty::ConstKind::Infer(Var(b_vid))) => {
.infcx self.tecx.inner.borrow_mut().const_unification_table().unioned(a_vid, b_vid)
.inner }
.borrow_mut()
.const_unification_table()
.unioned(a_vid, b_vid),
_ => false, _ => false,
} }
} }
@ -917,7 +914,7 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> {
&self, &self,
expr: &'tcx hir::Expr<'tcx>, expr: &'tcx hir::Expr<'tcx>,
) -> Box<dyn Iterator<Item = InsertableGenericArgs<'tcx>> + 'a> { ) -> Box<dyn Iterator<Item = InsertableGenericArgs<'tcx>> + 'a> {
let tcx = self.infcx.tcx; let tcx = self.tecx.tcx;
match expr.kind { match expr.kind {
hir::ExprKind::Path(ref path) => { hir::ExprKind::Path(ref path) => {
if let Some(args) = self.node_args_opt(expr.hir_id) { if let Some(args) = self.node_args_opt(expr.hir_id) {
@ -980,7 +977,7 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> {
path: &'tcx hir::Path<'tcx>, path: &'tcx hir::Path<'tcx>,
args: GenericArgsRef<'tcx>, args: GenericArgsRef<'tcx>,
) -> impl Iterator<Item = InsertableGenericArgs<'tcx>> + 'a { ) -> impl Iterator<Item = InsertableGenericArgs<'tcx>> + 'a {
let tcx = self.infcx.tcx; let tcx = self.tecx.tcx;
let have_turbofish = path.segments.iter().any(|segment| { let have_turbofish = path.segments.iter().any(|segment| {
segment.args.is_some_and(|args| args.args.iter().any(|arg| arg.is_ty_or_const())) segment.args.is_some_and(|args| args.args.iter().any(|arg| arg.is_ty_or_const()))
}); });
@ -1034,7 +1031,7 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> {
args: GenericArgsRef<'tcx>, args: GenericArgsRef<'tcx>,
qpath: &'tcx hir::QPath<'tcx>, qpath: &'tcx hir::QPath<'tcx>,
) -> Box<dyn Iterator<Item = InsertableGenericArgs<'tcx>> + 'a> { ) -> Box<dyn Iterator<Item = InsertableGenericArgs<'tcx>> + 'a> {
let tcx = self.infcx.tcx; let tcx = self.tecx.tcx;
match qpath { match qpath {
hir::QPath::Resolved(_self_ty, path) => { hir::QPath::Resolved(_self_ty, path) => {
Box::new(self.resolved_path_inferred_arg_iter(path, args)) Box::new(self.resolved_path_inferred_arg_iter(path, args))
@ -1107,7 +1104,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FindInferSourceVisitor<'a, 'tcx> {
type NestedFilter = nested_filter::OnlyBodies; type NestedFilter = nested_filter::OnlyBodies;
fn nested_visit_map(&mut self) -> Self::Map { fn nested_visit_map(&mut self) -> Self::Map {
self.infcx.tcx.hir() self.tecx.tcx.hir()
} }
fn visit_local(&mut self, local: &'tcx Local<'tcx>) { fn visit_local(&mut self, local: &'tcx Local<'tcx>) {
@ -1163,7 +1160,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FindInferSourceVisitor<'a, 'tcx> {
#[instrument(level = "debug", skip(self))] #[instrument(level = "debug", skip(self))]
fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
let tcx = self.infcx.tcx; let tcx = self.tecx.tcx;
match expr.kind { match expr.kind {
// When encountering `func(arg)` first look into `arg` and then `func`, // When encountering `func(arg)` first look into `arg` and then `func`,
// as `arg` is "more specific". // as `arg` is "more specific".
@ -1194,7 +1191,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FindInferSourceVisitor<'a, 'tcx> {
if generics.parent.is_none() && generics.has_self { if generics.parent.is_none() && generics.has_self {
argument_index += 1; argument_index += 1;
} }
let args = self.infcx.resolve_vars_if_possible(args); let args = self.tecx.resolve_vars_if_possible(args);
let generic_args = let generic_args =
&generics.own_args_no_defaults(tcx, args)[generics.own_counts().lifetimes..]; &generics.own_args_no_defaults(tcx, args)[generics.own_counts().lifetimes..];
let span = match expr.kind { let span = match expr.kind {
@ -1224,7 +1221,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FindInferSourceVisitor<'a, 'tcx> {
{ {
let output = args.as_closure().sig().output().skip_binder(); let output = args.as_closure().sig().output().skip_binder();
if self.generic_arg_contains_target(output.into()) { if self.generic_arg_contains_target(output.into()) {
let body = self.infcx.tcx.hir().body(body); let body = self.tecx.tcx.hir().body(body);
let should_wrap_expr = if matches!(body.value.kind, ExprKind::Block(..)) { let should_wrap_expr = if matches!(body.value.kind, ExprKind::Block(..)) {
None None
} else { } else {
@ -1252,12 +1249,12 @@ impl<'a, 'tcx> Visitor<'tcx> for FindInferSourceVisitor<'a, 'tcx> {
&& let Some(args) = self.node_args_opt(expr.hir_id) && let Some(args) = self.node_args_opt(expr.hir_id)
&& args.iter().any(|arg| self.generic_arg_contains_target(arg)) && args.iter().any(|arg| self.generic_arg_contains_target(arg))
&& let Some(def_id) = self.typeck_results.type_dependent_def_id(expr.hir_id) && let Some(def_id) = self.typeck_results.type_dependent_def_id(expr.hir_id)
&& self.infcx.tcx.trait_of_item(def_id).is_some() && self.tecx.tcx.trait_of_item(def_id).is_some()
&& !has_impl_trait(def_id) && !has_impl_trait(def_id)
{ {
let successor = let successor =
method_args.get(0).map_or_else(|| (")", span.hi()), |arg| (", ", arg.span.lo())); method_args.get(0).map_or_else(|| (")", span.hi()), |arg| (", ", arg.span.lo()));
let args = self.infcx.resolve_vars_if_possible(args); let args = self.tecx.resolve_vars_if_possible(args);
self.update_infer_source(InferSource { self.update_infer_source(InferSource {
span: path.ident.span, span: path.ident.span,
kind: InferSourceKind::FullyQualifiedMethodCall { kind: InferSourceKind::FullyQualifiedMethodCall {

View file

@ -0,0 +1,81 @@
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::undo_log::NoUndo;
use rustc_data_structures::unify as ut;
use rustc_middle::ty;
use crate::infer::InferCtxt;
#[derive(Debug, Copy, Clone, PartialEq)]
struct SubId(u32);
impl ut::UnifyKey for SubId {
type Value = ();
#[inline]
fn index(&self) -> u32 {
self.0
}
#[inline]
fn from_index(i: u32) -> SubId {
SubId(i)
}
fn tag() -> &'static str {
"SubId"
}
}
/// When reporting ambiguity errors, we sometimes want to
/// treat all inference vars which are subtypes of each
/// others as if they are equal. For this case we compute
/// the transitive closure of our subtype obligations here.
///
/// E.g. when encountering ambiguity errors, we want to suggest
/// specifying some method argument or to add a type annotation
/// to a local variable. Because subtyping cannot change the
/// shape of a type, it's fine if the cause of the ambiguity error
/// is only related to the suggested variable via subtyping.
///
/// Even for something like `let x = returns_arg(); x.method();` the
/// type of `x` is only a supertype of the argument of `returns_arg`. We
/// still want to suggest specifying the type of the argument.
#[derive(Default)]
pub struct SubRelations {
map: FxHashMap<ty::TyVid, SubId>,
table: ut::UnificationTableStorage<SubId>,
}
impl SubRelations {
fn get_id<'tcx>(&mut self, infcx: &InferCtxt<'tcx>, vid: ty::TyVid) -> SubId {
let root_vid = infcx.root_var(vid);
*self.map.entry(root_vid).or_insert_with(|| self.table.with_log(&mut NoUndo).new_key(()))
}
pub fn add_constraints<'tcx>(
&mut self,
infcx: &InferCtxt<'tcx>,
obls: impl IntoIterator<Item = ty::Predicate<'tcx>>,
) {
for p in obls {
let (a, b) = match p.kind().skip_binder() {
ty::PredicateKind::Subtype(ty::SubtypePredicate { a_is_expected: _, a, b }) => {
(a, b)
}
ty::PredicateKind::Coerce(ty::CoercePredicate { a, b }) => (a, b),
_ => continue,
};
match (a.kind(), b.kind()) {
(&ty::Infer(ty::TyVar(a_vid)), &ty::Infer(ty::TyVar(b_vid))) => {
let a = self.get_id(infcx, a_vid);
let b = self.get_id(infcx, b_vid);
self.table.with_log(&mut NoUndo).unify_var_var(a, b).unwrap();
}
_ => continue,
}
}
}
pub fn unified<'tcx>(&mut self, infcx: &InferCtxt<'tcx>, a: ty::TyVid, b: ty::TyVid) -> bool {
let a = self.get_id(infcx, a);
let b = self.get_id(infcx, b);
self.table.with_log(&mut NoUndo).unioned(a, b)
}
}

View file

@ -762,6 +762,7 @@ impl<'tcx> InferCtxt<'tcx> {
pub fn err_ctxt(&self) -> TypeErrCtxt<'_, 'tcx> { pub fn err_ctxt(&self) -> TypeErrCtxt<'_, 'tcx> {
TypeErrCtxt { TypeErrCtxt {
infcx: self, infcx: self,
sub_relations: Default::default(),
typeck_results: None, typeck_results: None,
fallback_has_occurred: false, fallback_has_occurred: false,
normalize_fn_sig: Box::new(|fn_sig| fn_sig), normalize_fn_sig: Box::new(|fn_sig| fn_sig),
@ -1029,7 +1030,6 @@ impl<'tcx> InferCtxt<'tcx> {
let r_b = self.shallow_resolve(predicate.skip_binder().b); let r_b = self.shallow_resolve(predicate.skip_binder().b);
match (r_a.kind(), r_b.kind()) { match (r_a.kind(), r_b.kind()) {
(&ty::Infer(ty::TyVar(a_vid)), &ty::Infer(ty::TyVar(b_vid))) => { (&ty::Infer(ty::TyVar(a_vid)), &ty::Infer(ty::TyVar(b_vid))) => {
self.inner.borrow_mut().type_variables().sub(a_vid, b_vid);
return Err((a_vid, b_vid)); return Err((a_vid, b_vid));
} }
_ => {} _ => {}

View file

@ -217,10 +217,9 @@ impl<'tcx> InferCtxt<'tcx> {
) -> RelateResult<'tcx, Generalization<T>> { ) -> RelateResult<'tcx, Generalization<T>> {
assert!(!source_term.has_escaping_bound_vars()); assert!(!source_term.has_escaping_bound_vars());
let (for_universe, root_vid) = match target_vid.into() { let (for_universe, root_vid) = match target_vid.into() {
ty::TermVid::Ty(ty_vid) => ( ty::TermVid::Ty(ty_vid) => {
self.probe_ty_var(ty_vid).unwrap_err(), (self.probe_ty_var(ty_vid).unwrap_err(), ty::TermVid::Ty(self.root_var(ty_vid)))
ty::TermVid::Ty(self.inner.borrow_mut().type_variables().sub_root_var(ty_vid)), }
),
ty::TermVid::Const(ct_vid) => ( ty::TermVid::Const(ct_vid) => (
self.probe_const_var(ct_vid).unwrap_err(), self.probe_const_var(ct_vid).unwrap_err(),
ty::TermVid::Const( ty::TermVid::Const(
@ -424,9 +423,7 @@ impl<'tcx> TypeRelation<'tcx> for Generalizer<'_, 'tcx> {
ty::Infer(ty::TyVar(vid)) => { ty::Infer(ty::TyVar(vid)) => {
let mut inner = self.infcx.inner.borrow_mut(); let mut inner = self.infcx.inner.borrow_mut();
let vid = inner.type_variables().root_var(vid); let vid = inner.type_variables().root_var(vid);
let sub_vid = inner.type_variables().sub_root_var(vid); if ty::TermVid::Ty(vid) == self.root_vid {
if ty::TermVid::Ty(sub_vid) == self.root_vid {
// If sub-roots are equal, then `root_vid` and // If sub-roots are equal, then `root_vid` and
// `vid` are related via subtyping. // `vid` are related via subtyping.
Err(self.cyclic_term_error()) Err(self.cyclic_term_error())
@ -461,11 +458,6 @@ impl<'tcx> TypeRelation<'tcx> for Generalizer<'_, 'tcx> {
let new_var_id = let new_var_id =
inner.type_variables().new_var(self.for_universe, origin); inner.type_variables().new_var(self.for_universe, origin);
let u = Ty::new_var(self.tcx(), new_var_id); let u = Ty::new_var(self.tcx(), new_var_id);
// Record that we replaced `vid` with `new_var_id` as part of a generalization
// operation. This is needed to detect cyclic types. To see why, see the
// docs in the `type_variables` module.
inner.type_variables().sub(vid, new_var_id);
debug!("replacing original vid={:?} with new={:?}", vid, u); debug!("replacing original vid={:?} with new={:?}", vid, u);
Ok(u) Ok(u)
} }

View file

@ -1,3 +1,4 @@
use rustc_data_structures::undo_log::Rollback;
use rustc_hir::def_id::DefId; use rustc_hir::def_id::DefId;
use rustc_index::IndexVec; use rustc_index::IndexVec;
use rustc_middle::ty::{self, Ty, TyVid}; use rustc_middle::ty::{self, Ty, TyVid};
@ -12,35 +13,9 @@ use std::cmp;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::ops::Range; use std::ops::Range;
use rustc_data_structures::undo_log::Rollback; impl<'tcx> Rollback<sv::UndoLog<ut::Delegate<TyVidEqKey<'tcx>>>> for TypeVariableStorage<'tcx> {
fn reverse(&mut self, undo: sv::UndoLog<ut::Delegate<TyVidEqKey<'tcx>>>) {
/// Represents a single undo-able action that affects a type inference variable. self.eq_relations.reverse(undo)
#[derive(Clone)]
pub(crate) enum UndoLog<'tcx> {
EqRelation(sv::UndoLog<ut::Delegate<TyVidEqKey<'tcx>>>),
SubRelation(sv::UndoLog<ut::Delegate<ty::TyVid>>),
}
/// Convert from a specific kind of undo to the more general UndoLog
impl<'tcx> From<sv::UndoLog<ut::Delegate<TyVidEqKey<'tcx>>>> for UndoLog<'tcx> {
fn from(l: sv::UndoLog<ut::Delegate<TyVidEqKey<'tcx>>>) -> Self {
UndoLog::EqRelation(l)
}
}
/// Convert from a specific kind of undo to the more general UndoLog
impl<'tcx> From<sv::UndoLog<ut::Delegate<ty::TyVid>>> for UndoLog<'tcx> {
fn from(l: sv::UndoLog<ut::Delegate<ty::TyVid>>) -> Self {
UndoLog::SubRelation(l)
}
}
impl<'tcx> Rollback<UndoLog<'tcx>> for TypeVariableStorage<'tcx> {
fn reverse(&mut self, undo: UndoLog<'tcx>) {
match undo {
UndoLog::EqRelation(undo) => self.eq_relations.reverse(undo),
UndoLog::SubRelation(undo) => self.sub_relations.reverse(undo),
}
} }
} }
@ -52,41 +27,6 @@ pub struct TypeVariableStorage<'tcx> {
/// constraint `?X == ?Y`. This table also stores, for each key, /// constraint `?X == ?Y`. This table also stores, for each key,
/// the known value. /// the known value.
eq_relations: ut::UnificationTableStorage<TyVidEqKey<'tcx>>, eq_relations: ut::UnificationTableStorage<TyVidEqKey<'tcx>>,
/// Two variables are unified in `sub_relations` when we have a
/// constraint `?X <: ?Y` *or* a constraint `?Y <: ?X`. This second
/// table exists only to help with the occurs check. In particular,
/// we want to report constraints like these as an occurs check
/// violation:
/// ``` text
/// ?1 <: ?3
/// Box<?3> <: ?1
/// ```
/// Without this second table, what would happen in a case like
/// this is that we would instantiate `?1` with a generalized
/// type like `Box<?6>`. We would then relate `Box<?3> <: Box<?6>`
/// and infer that `?3 <: ?6`. Next, since `?1` was instantiated,
/// we would process `?1 <: ?3`, generalize `?1 = Box<?6>` to `Box<?9>`,
/// and instantiate `?3` with `Box<?9>`. Finally, we would relate
/// `?6 <: ?9`. But now that we instantiated `?3`, we can process
/// `?3 <: ?6`, which gives us `Box<?9> <: ?6`... and the cycle
/// continues. (This is `occurs-check-2.rs`.)
///
/// What prevents this cycle is that when we generalize
/// `Box<?3>` to `Box<?6>`, we also sub-unify `?3` and `?6`
/// (in the generalizer). When we then process `Box<?6> <: ?3`,
/// the occurs check then fails because `?6` and `?3` are sub-unified,
/// and hence generalization fails.
///
/// This is reasonable because, in Rust, subtypes have the same
/// "skeleton" and hence there is no possible type such that
/// (e.g.) `Box<?3> <: ?3` for any `?3`.
///
/// In practice, we sometimes sub-unify variables in other spots, such
/// as when processing subtype predicates. This is not necessary but is
/// done to aid diagnostics, as it allows us to be more effective when
/// we guide the user towards where they should insert type hints.
sub_relations: ut::UnificationTableStorage<ty::TyVid>,
} }
pub struct TypeVariableTable<'a, 'tcx> { pub struct TypeVariableTable<'a, 'tcx> {
@ -158,7 +98,6 @@ impl<'tcx> TypeVariableStorage<'tcx> {
TypeVariableStorage { TypeVariableStorage {
values: Default::default(), values: Default::default(),
eq_relations: ut::UnificationTableStorage::new(), eq_relations: ut::UnificationTableStorage::new(),
sub_relations: ut::UnificationTableStorage::new(),
} }
} }
@ -197,16 +136,6 @@ impl<'tcx> TypeVariableTable<'_, 'tcx> {
debug_assert!(self.probe(a).is_unknown()); debug_assert!(self.probe(a).is_unknown());
debug_assert!(self.probe(b).is_unknown()); debug_assert!(self.probe(b).is_unknown());
self.eq_relations().union(a, b); self.eq_relations().union(a, b);
self.sub_relations().union(a, b);
}
/// Records that `a <: b`, depending on `dir`.
///
/// Precondition: neither `a` nor `b` are known.
pub fn sub(&mut self, a: ty::TyVid, b: ty::TyVid) {
debug_assert!(self.probe(a).is_unknown());
debug_assert!(self.probe(b).is_unknown());
self.sub_relations().union(a, b);
} }
/// Instantiates `vid` with the type `ty`. /// Instantiates `vid` with the type `ty`.
@ -240,10 +169,6 @@ impl<'tcx> TypeVariableTable<'_, 'tcx> {
origin: TypeVariableOrigin, origin: TypeVariableOrigin,
) -> ty::TyVid { ) -> ty::TyVid {
let eq_key = self.eq_relations().new_key(TypeVariableValue::Unknown { universe }); let eq_key = self.eq_relations().new_key(TypeVariableValue::Unknown { universe });
let sub_key = self.sub_relations().new_key(());
debug_assert_eq!(eq_key.vid, sub_key);
let index = self.storage.values.push(TypeVariableData { origin }); let index = self.storage.values.push(TypeVariableData { origin });
debug_assert_eq!(eq_key.vid, index); debug_assert_eq!(eq_key.vid, index);
@ -266,24 +191,6 @@ impl<'tcx> TypeVariableTable<'_, 'tcx> {
self.eq_relations().find(vid).vid self.eq_relations().find(vid).vid
} }
/// Returns the "root" variable of `vid` in the `sub_relations`
/// equivalence table. All type variables that have been are
/// related via equality or subtyping will yield the same root
/// variable (per the union-find algorithm), so `sub_root_var(a)
/// == sub_root_var(b)` implies that:
/// ```text
/// exists X. (a <: X || X <: a) && (b <: X || X <: b)
/// ```
pub fn sub_root_var(&mut self, vid: ty::TyVid) -> ty::TyVid {
self.sub_relations().find(vid)
}
/// Returns `true` if `a` and `b` have same "sub-root" (i.e., exists some
/// type X such that `forall i in {a, b}. (i <: X || X <: i)`.
pub fn sub_unified(&mut self, a: ty::TyVid, b: ty::TyVid) -> bool {
self.sub_root_var(a) == self.sub_root_var(b)
}
/// Retrieves the type to which `vid` has been instantiated, if /// Retrieves the type to which `vid` has been instantiated, if
/// any. /// any.
pub fn probe(&mut self, vid: ty::TyVid) -> TypeVariableValue<'tcx> { pub fn probe(&mut self, vid: ty::TyVid) -> TypeVariableValue<'tcx> {
@ -314,11 +221,6 @@ impl<'tcx> TypeVariableTable<'_, 'tcx> {
self.storage.eq_relations.with_log(self.undo_log) self.storage.eq_relations.with_log(self.undo_log)
} }
#[inline]
fn sub_relations(&mut self) -> super::UnificationTable<'_, 'tcx, ty::TyVid> {
self.storage.sub_relations.with_log(self.undo_log)
}
/// Returns a range of the type variables created during the snapshot. /// Returns a range of the type variables created during the snapshot.
pub fn vars_since_snapshot( pub fn vars_since_snapshot(
&mut self, &mut self,

View file

@ -20,7 +20,7 @@ pub struct Snapshot<'tcx> {
#[derive(Clone)] #[derive(Clone)]
pub(crate) enum UndoLog<'tcx> { pub(crate) enum UndoLog<'tcx> {
OpaqueTypes(OpaqueTypeKey<'tcx>, Option<OpaqueHiddenType<'tcx>>), OpaqueTypes(OpaqueTypeKey<'tcx>, Option<OpaqueHiddenType<'tcx>>),
TypeVariables(type_variable::UndoLog<'tcx>), TypeVariables(sv::UndoLog<ut::Delegate<type_variable::TyVidEqKey<'tcx>>>),
ConstUnificationTable(sv::UndoLog<ut::Delegate<ConstVidKey<'tcx>>>), ConstUnificationTable(sv::UndoLog<ut::Delegate<ConstVidKey<'tcx>>>),
IntUnificationTable(sv::UndoLog<ut::Delegate<ty::IntVid>>), IntUnificationTable(sv::UndoLog<ut::Delegate<ty::IntVid>>),
FloatUnificationTable(sv::UndoLog<ut::Delegate<ty::FloatVid>>), FloatUnificationTable(sv::UndoLog<ut::Delegate<ty::FloatVid>>),
@ -46,17 +46,13 @@ macro_rules! impl_from {
// Upcast from a single kind of "undoable action" to the general enum // Upcast from a single kind of "undoable action" to the general enum
impl_from! { impl_from! {
RegionConstraintCollector(region_constraints::UndoLog<'tcx>), RegionConstraintCollector(region_constraints::UndoLog<'tcx>),
TypeVariables(type_variable::UndoLog<'tcx>),
TypeVariables(sv::UndoLog<ut::Delegate<type_variable::TyVidEqKey<'tcx>>>), TypeVariables(sv::UndoLog<ut::Delegate<type_variable::TyVidEqKey<'tcx>>>),
TypeVariables(sv::UndoLog<ut::Delegate<ty::TyVid>>),
IntUnificationTable(sv::UndoLog<ut::Delegate<ty::IntVid>>), IntUnificationTable(sv::UndoLog<ut::Delegate<ty::IntVid>>),
FloatUnificationTable(sv::UndoLog<ut::Delegate<ty::FloatVid>>), FloatUnificationTable(sv::UndoLog<ut::Delegate<ty::FloatVid>>),
EffectUnificationTable(sv::UndoLog<ut::Delegate<EffectVidKey<'tcx>>>),
ConstUnificationTable(sv::UndoLog<ut::Delegate<ConstVidKey<'tcx>>>), ConstUnificationTable(sv::UndoLog<ut::Delegate<ConstVidKey<'tcx>>>),
EffectUnificationTable(sv::UndoLog<ut::Delegate<EffectVidKey<'tcx>>>),
RegionUnificationTable(sv::UndoLog<ut::Delegate<RegionVidKey<'tcx>>>), RegionUnificationTable(sv::UndoLog<ut::Delegate<RegionVidKey<'tcx>>>),
ProjectionCache(traits::UndoLog<'tcx>), ProjectionCache(traits::UndoLog<'tcx>),

View file

@ -63,6 +63,10 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
&self, &self,
mut errors: Vec<FulfillmentError<'tcx>>, mut errors: Vec<FulfillmentError<'tcx>>,
) -> ErrorGuaranteed { ) -> ErrorGuaranteed {
self.sub_relations
.borrow_mut()
.add_constraints(self, errors.iter().map(|e| e.obligation.predicate));
#[derive(Debug)] #[derive(Debug)]
struct ErrorDescriptor<'tcx> { struct ErrorDescriptor<'tcx> {
predicate: ty::Predicate<'tcx>, predicate: ty::Predicate<'tcx>,

View file

@ -20,9 +20,10 @@ impl<T> Bind<T> for Foo<{ 6 + 1 }> {
fn main() { fn main() {
let (mut t, foo) = Foo::bind(); let (mut t, foo) = Foo::bind();
//~^ ERROR mismatched types
//~| NOTE cyclic type
// `t` is `ty::Infer(TyVar(?1t))` // `t` is `ty::Infer(TyVar(?1t))`
// `foo` contains `ty::Infer(TyVar(?1t))` in its substs // `foo` contains `ty::Infer(TyVar(?1t))` in its substs
t = foo; t = foo;
//~^ ERROR mismatched types
//~| NOTE cyclic type
} }

View file

@ -1,8 +1,8 @@
error[E0308]: mismatched types error[E0308]: mismatched types
--> $DIR/unused-substs-2.rs:25:9 --> $DIR/unused-substs-2.rs:22:24
| |
LL | t = foo; LL | let (mut t, foo) = Foo::bind();
| ^^^ cyclic type of infinite size | ^^^^^^^^^^^ cyclic type of infinite size
error: aborting due to 1 previous error error: aborting due to 1 previous error

View file

@ -1,10 +1,8 @@
error[E0308]: mismatched types error[E0308]: mismatched types
--> $DIR/unused-substs-5.rs:15:9 --> $DIR/unused-substs-5.rs:15:19
| |
LL | x = q::<_, N>(x); LL | x = q::<_, N>(x);
| ^^^^^^^^^^^^- help: try using a conversion method: `.to_vec()` | ^ cyclic type of infinite size
| |
| cyclic type of infinite size
error: aborting due to 1 previous error error: aborting due to 1 previous error

View file

@ -29,5 +29,5 @@ where
} }
fn main() { fn main() {
Race::new(|race| race.when()); //~ ERROR type annotations needed Race::new(|race| race.when()); //~ ERROR overflow evaluating the requirement `_ <: Option<_>`
} }

View file

@ -1,14 +1,9 @@
error[E0282]: type annotations needed for `RaceBuilder<T, Never<T>>` error[E0275]: overflow evaluating the requirement `_ <: Option<_>`
--> $DIR/issue-84073.rs:32:16 --> $DIR/issue-84073.rs:32:22
| |
LL | Race::new(|race| race.when()); LL | Race::new(|race| race.when());
| ^^^^ ---- type must be known at this point | ^^^^
|
help: consider giving this closure parameter an explicit type, where the type for type parameter `T` is specified
|
LL | Race::new(|race: RaceBuilder<T, Never<T>>| race.when());
| ++++++++++++++++++++++++++
error: aborting due to 1 previous error error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0282`. For more information about this error, try `rustc --explain E0275`.

View file

@ -1,6 +1,3 @@
//@ error-pattern: reached the recursion limit while auto-dereferencing
//@ compile-flags: -Zdeduplicate-diagnostics=yes
use std::ops::Deref; use std::ops::Deref;
struct Foo; struct Foo;
@ -17,6 +14,7 @@ pub fn main() {
let mut x; let mut x;
loop { loop {
x = Box::new(x); x = Box::new(x);
//~^ ERROR overflow evaluating the requirement `Box<_> <: _`
x.foo; x.foo;
x.bar(); x.bar();
} }

View file

@ -1,54 +1,9 @@
error[E0308]: mismatched types error[E0275]: overflow evaluating the requirement `Box<_> <: _`
--> $DIR/infinite-autoderef.rs:19:13 --> $DIR/infinite-autoderef.rs:16:13
| |
LL | x = Box::new(x); LL | x = Box::new(x);
| ^^^^^^^^^^^ cyclic type of infinite size | ^^^^^^^^^^^
|
help: consider unboxing the value
|
LL | x = *Box::new(x);
| +
error[E0055]: reached the recursion limit while auto-dereferencing `Foo` error: aborting due to 1 previous error
--> $DIR/infinite-autoderef.rs:24:5
|
LL | Foo.foo;
| ^^^^^^^ deref recursion limit reached
|
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`infinite_autoderef`)
error[E0055]: reached the recursion limit while auto-dereferencing `Foo` For more information about this error, try `rustc --explain E0275`.
--> $DIR/infinite-autoderef.rs:24:9
|
LL | Foo.foo;
| ^^^ deref recursion limit reached
|
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`infinite_autoderef`)
error[E0609]: no field `foo` on type `Foo`
--> $DIR/infinite-autoderef.rs:24:9
|
LL | Foo.foo;
| ^^^ unknown field
error[E0055]: reached the recursion limit while auto-dereferencing `Foo`
--> $DIR/infinite-autoderef.rs:25:9
|
LL | Foo.bar();
| ^^^ deref recursion limit reached
|
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`infinite_autoderef`)
error[E0599]: no method named `bar` found for struct `Foo` in the current scope
--> $DIR/infinite-autoderef.rs:25:9
|
LL | struct Foo;
| ---------- method `bar` not found for this struct
...
LL | Foo.bar();
| ^^^ method not found in `Foo`
error: aborting due to 6 previous errors
Some errors have detailed explanations: E0055, E0308, E0599, E0609.
For more information about an error, try `rustc --explain E0055`.

View file

@ -18,6 +18,6 @@ fn main() {
let f = |(_, _)| {}; let f = |(_, _)| {};
let g = |(a, _)| a; let g = |(a, _)| a;
let t7 = |env| |a| |b| t7p(f, g)(((env, a), b)); let t7 = |env| |a| |b| t7p(f, g)(((env, a), b));
//~^ ERROR mismatched types
let t8 = t8n(t7, t7p(f, g)); let t8 = t8n(t7, t7p(f, g));
//~^ ERROR: expected a `Fn(_)` closure, found `impl Fn(((_, _), _))` [E0277]
} }

View file

@ -1,18 +1,9 @@
error[E0277]: expected a `Fn(_)` closure, found `impl Fn(((_, _), _))` error[E0308]: mismatched types
--> $DIR/issue-59494.rs:21:22 --> $DIR/issue-59494.rs:20:40
| |
LL | let t8 = t8n(t7, t7p(f, g)); LL | let t7 = |env| |a| |b| t7p(f, g)(((env, a), b));
| --- ^^^^^^^^^ expected an `Fn(_)` closure, found `impl Fn(((_, _), _))` | ^^^ cyclic type of infinite size
| |
| required by a bound introduced by this call
|
= help: the trait `Fn<(_,)>` is not implemented for `impl Fn(((_, _), _))`
note: required by a bound in `t8n`
--> $DIR/issue-59494.rs:5:45
|
LL | fn t8n<A, B, C>(f: impl Fn(A) -> B, g: impl Fn(A) -> C) -> impl Fn(A) -> (B, C)
| ^^^^^^^^^^ required by this bound in `t8n`
error: aborting due to 1 previous error error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0277`. For more information about this error, try `rustc --explain E0308`.

View file

@ -5,6 +5,5 @@ fn main() {
g = f; g = f;
f = Box::new(g); f = Box::new(g);
//~^ ERROR mismatched types //~^ ERROR overflow evaluating the requirement `Box<_> <: _`
//~| cyclic type of infinite size
} }

View file

@ -1,14 +1,9 @@
error[E0308]: mismatched types error[E0275]: overflow evaluating the requirement `Box<_> <: _`
--> $DIR/occurs-check-2.rs:7:9 --> $DIR/occurs-check-2.rs:7:9
| |
LL | f = Box::new(g); LL | f = Box::new(g);
| ^^^^^^^^^^^ cyclic type of infinite size | ^^^^^^^^^^^
|
help: consider unboxing the value
|
LL | f = *Box::new(g);
| +
error: aborting due to 1 previous error error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0308`. For more information about this error, try `rustc --explain E0275`.

View file

@ -1,5 +1,11 @@
// From Issue #778 // From Issue #778
enum Clam<T> { A(T) } enum Clam<T> { A(T) }
fn main() { let c; c = Clam::A(c); match c { Clam::A::<isize>(_) => { } } } fn main() {
//~^ ERROR mismatched types let c;
c = Clam::A(c);
//~^ ERROR overflow evaluating the requirement `Clam<_> <: _`
match c {
Clam::A::<isize>(_) => { }
}
}

View file

@ -1,9 +1,9 @@
error[E0308]: mismatched types error[E0275]: overflow evaluating the requirement `Clam<_> <: _`
--> $DIR/occurs-check-3.rs:4:24 --> $DIR/occurs-check-3.rs:6:9
| |
LL | fn main() { let c; c = Clam::A(c); match c { Clam::A::<isize>(_) => { } } } LL | c = Clam::A(c);
| ^^^^^^^^^^ cyclic type of infinite size | ^^^^^^^^^^
error: aborting due to 1 previous error error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0308`. For more information about this error, try `rustc --explain E0275`.

View file

@ -1,8 +1,5 @@
fn main() { fn main() {
let f; let f;
f = Box::new(f); f = Box::new(f);
//~^ ERROR mismatched types //~^ ERROR overflow evaluating the requirement `Box<_> <: _`
//~| cyclic type of infinite size
} }

View file

@ -1,14 +1,9 @@
error[E0308]: mismatched types error[E0275]: overflow evaluating the requirement `Box<_> <: _`
--> $DIR/occurs-check.rs:5:9 --> $DIR/occurs-check.rs:3:9
| |
LL | f = Box::new(f); LL | f = Box::new(f);
| ^^^^^^^^^^^ cyclic type of infinite size | ^^^^^^^^^^^
|
help: consider unboxing the value
|
LL | f = *Box::new(f);
| +
error: aborting due to 1 previous error error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0308`. For more information about this error, try `rustc --explain E0275`.

View file

@ -13,10 +13,6 @@ fn main() {
//~^ ERROR E0308 //~^ ERROR E0308
test2(&y); test2(&y);
//~^ ERROR E0308 //~^ ERROR E0308
let f;
f = Box::new(f);
//~^ ERROR E0308
let s = &mut String::new(); let s = &mut String::new();
s = format!("foo"); s = format!("foo");
//~^ ERROR E0308 //~^ ERROR E0308

View file

@ -54,22 +54,11 @@ LL | fn test2(_x: &mut i32) {}
error[E0308]: mismatched types error[E0308]: mismatched types
--> $DIR/coerce-suggestions.rs:17:9 --> $DIR/coerce-suggestions.rs:17:9
| |
LL | f = Box::new(f);
| ^^^^^^^^^^^ cyclic type of infinite size
|
help: consider unboxing the value
|
LL | f = *Box::new(f);
| +
error[E0308]: mismatched types
--> $DIR/coerce-suggestions.rs:21:9
|
LL | s = format!("foo"); LL | s = format!("foo");
| ^^^^^^^^^^^^^^ expected `&mut String`, found `String` | ^^^^^^^^^^^^^^ expected `&mut String`, found `String`
| |
= note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info) = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info)
error: aborting due to 6 previous errors error: aborting due to 5 previous errors
For more information about this error, try `rustc --explain E0308`. For more information about this error, try `rustc --explain E0308`.