Auto merge of #140732 - onur-ozkan:use-in-tree-rustfmt, r=Kobzol
make it possible to run in-tree rustfmt with `x run rustfmt`
Currently, there is no way to run in-tree `rustfmt` using `x fmt` or `x test tidy` commands. This PR implements `rustfmt` on `x run`, which allows bootstrap to run the in-tree `rustfmt`.
Fixes #140723
diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs
index 44af1b7..3e075d4 100644
--- a/compiler/rustc_borrowck/src/lib.rs
+++ b/compiler/rustc_borrowck/src/lib.rs
@@ -47,7 +47,7 @@
use rustc_mir_dataflow::move_paths::{
InitIndex, InitLocation, LookupResult, MoveData, MovePathIndex,
};
-use rustc_mir_dataflow::{Analysis, EntryStates, Results, ResultsVisitor, visit_results};
+use rustc_mir_dataflow::{Analysis, Results, ResultsVisitor, visit_results};
use rustc_session::lint::builtin::{TAIL_EXPR_DROP_ORDER, UNUSED_MUT};
use rustc_span::{ErrorGuaranteed, Span, Symbol};
use smallvec::SmallVec;
@@ -127,6 +127,14 @@ fn mir_borrowck(
Ok(tcx.arena.alloc(opaque_types))
} else {
let mut root_cx = BorrowCheckRootCtxt::new(tcx, def);
+ // We need to manually borrowck all nested bodies from the HIR as
+ // we do not generate MIR for dead code. Not doing so causes us to
+ // never check closures in dead code.
+ let nested_bodies = tcx.nested_bodies_within(def);
+ for def_id in nested_bodies {
+ root_cx.get_or_insert_nested(def_id);
+ }
+
let PropagatedBorrowCheckResults { closure_requirements, used_mut_upvars } =
do_mir_borrowck(&mut root_cx, def, None).0;
debug_assert!(closure_requirements.is_none());
@@ -461,11 +469,13 @@ fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) {
// Compute and report region errors, if any.
mbcx.report_region_errors(nll_errors);
- let mut flow_results = get_flow_results(tcx, body, &move_data, &borrow_set, ®ioncx);
+ let (mut flow_analysis, flow_entry_states) =
+ get_flow_results(tcx, body, &move_data, &borrow_set, ®ioncx);
visit_results(
body,
traversal::reverse_postorder(body).map(|(bb, _)| bb),
- &mut flow_results,
+ &mut flow_analysis,
+ &flow_entry_states,
&mut mbcx,
);
@@ -525,7 +535,7 @@ fn get_flow_results<'a, 'tcx>(
move_data: &'a MoveData<'tcx>,
borrow_set: &'a BorrowSet<'tcx>,
regioncx: &RegionInferenceContext<'tcx>,
-) -> Results<'tcx, Borrowck<'a, 'tcx>> {
+) -> (Borrowck<'a, 'tcx>, Results<BorrowckDomain>) {
// We compute these three analyses individually, but them combine them into
// a single results so that `mbcx` can visit them all together.
let borrows = Borrows::new(tcx, body, regioncx, borrow_set).iterate_to_fixpoint(
@@ -550,14 +560,14 @@ fn get_flow_results<'a, 'tcx>(
ever_inits: ever_inits.analysis,
};
- assert_eq!(borrows.entry_states.len(), uninits.entry_states.len());
- assert_eq!(borrows.entry_states.len(), ever_inits.entry_states.len());
- let entry_states: EntryStates<'_, Borrowck<'_, '_>> =
- itertools::izip!(borrows.entry_states, uninits.entry_states, ever_inits.entry_states)
+ assert_eq!(borrows.results.len(), uninits.results.len());
+ assert_eq!(borrows.results.len(), ever_inits.results.len());
+ let results: Results<_> =
+ itertools::izip!(borrows.results, uninits.results, ever_inits.results)
.map(|(borrows, uninits, ever_inits)| BorrowckDomain { borrows, uninits, ever_inits })
.collect();
- Results { analysis, entry_states }
+ (analysis, results)
}
pub(crate) struct BorrowckInferCtxt<'tcx> {
@@ -705,7 +715,7 @@ struct MirBorrowckCtxt<'a, 'infcx, 'tcx> {
impl<'a, 'tcx> ResultsVisitor<'tcx, Borrowck<'a, 'tcx>> for MirBorrowckCtxt<'a, '_, 'tcx> {
fn visit_after_early_statement_effect(
&mut self,
- _results: &mut Results<'tcx, Borrowck<'a, 'tcx>>,
+ _analysis: &mut Borrowck<'a, 'tcx>,
state: &BorrowckDomain,
stmt: &Statement<'tcx>,
location: Location,
@@ -781,7 +791,7 @@ fn visit_after_early_statement_effect(
fn visit_after_early_terminator_effect(
&mut self,
- _results: &mut Results<'tcx, Borrowck<'a, 'tcx>>,
+ _analysis: &mut Borrowck<'a, 'tcx>,
state: &BorrowckDomain,
term: &Terminator<'tcx>,
loc: Location,
@@ -901,7 +911,7 @@ fn visit_after_early_terminator_effect(
fn visit_after_primary_terminator_effect(
&mut self,
- _results: &mut Results<'tcx, Borrowck<'a, 'tcx>>,
+ _analysis: &mut Borrowck<'a, 'tcx>,
state: &BorrowckDomain,
term: &Terminator<'tcx>,
loc: Location,
diff --git a/compiler/rustc_borrowck/src/root_cx.rs b/compiler/rustc_borrowck/src/root_cx.rs
index 13daa5c..66b526f 100644
--- a/compiler/rustc_borrowck/src/root_cx.rs
+++ b/compiler/rustc_borrowck/src/root_cx.rs
@@ -62,7 +62,10 @@ pub(super) fn set_tainted_by_errors(&mut self, guar: ErrorGuaranteed) {
self.tainted_by_errors = Some(guar);
}
- fn get_or_insert_nested(&mut self, def_id: LocalDefId) -> &PropagatedBorrowCheckResults<'tcx> {
+ pub(super) fn get_or_insert_nested(
+ &mut self,
+ def_id: LocalDefId,
+ ) -> &PropagatedBorrowCheckResults<'tcx> {
debug_assert_eq!(
self.tcx.typeck_root_def_id(def_id.to_def_id()),
self.root_def_id.to_def_id()
diff --git a/compiler/rustc_borrowck/src/type_check/opaque_types.rs b/compiler/rustc_borrowck/src/type_check/opaque_types.rs
index d41cbf7..341c50c 100644
--- a/compiler/rustc_borrowck/src/type_check/opaque_types.rs
+++ b/compiler/rustc_borrowck/src/type_check/opaque_types.rs
@@ -266,10 +266,6 @@ impl<'tcx, OP> TypeVisitor<TyCtxt<'tcx>> for ConstrainOpaqueTypeRegionVisitor<'t
where
OP: FnMut(ty::Region<'tcx>),
{
- fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(&mut self, t: &ty::Binder<'tcx, T>) {
- t.super_visit_with(self);
- }
-
fn visit_region(&mut self, r: ty::Region<'tcx>) {
match r.kind() {
// ignore bound regions, keep visiting
diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs
index fdf8053..95cfe22 100644
--- a/compiler/rustc_driver_impl/src/lib.rs
+++ b/compiler/rustc_driver_impl/src/lib.rs
@@ -463,6 +463,7 @@ fn handle_explain(early_dcx: &EarlyDiagCtxt, registry: Registry, code: &str, col
// Allow "E0123" or "0123" form.
let upper_cased_code = code.to_ascii_uppercase();
if let Ok(code) = upper_cased_code.strip_prefix('E').unwrap_or(&upper_cased_code).parse::<u32>()
+ && code <= ErrCode::MAX_AS_U32
&& let Ok(description) = registry.try_find_description(ErrCode::from_u32(code))
{
let mut is_in_code_block = false;
diff --git a/compiler/rustc_hir/src/definitions.rs b/compiler/rustc_hir/src/definitions.rs
index 98b4118..f93b9e5 100644
--- a/compiler/rustc_hir/src/definitions.rs
+++ b/compiler/rustc_hir/src/definitions.rs
@@ -309,6 +309,8 @@ pub enum DefPathData {
/// An existential `impl Trait` type node.
/// Argument position `impl Trait` have a `TypeNs` with their pretty-printed name.
OpaqueTy,
+ /// Used for remapped captured lifetimes in an existential `impl Trait` type node.
+ OpaqueLifetime(Symbol),
/// An anonymous associated type from an RPITIT. The symbol refers to the name of the method
/// that defined the type.
AnonAssocTy(Symbol),
@@ -445,7 +447,8 @@ impl DefPathData {
pub fn get_opt_name(&self) -> Option<Symbol> {
use self::DefPathData::*;
match *self {
- TypeNs(name) | ValueNs(name) | MacroNs(name) | LifetimeNs(name) => Some(name),
+ TypeNs(name) | ValueNs(name) | MacroNs(name) | LifetimeNs(name)
+ | OpaqueLifetime(name) => Some(name),
Impl
| ForeignMod
@@ -465,9 +468,8 @@ pub fn get_opt_name(&self) -> Option<Symbol> {
fn hashed_symbol(&self) -> Option<Symbol> {
use self::DefPathData::*;
match *self {
- TypeNs(name) | ValueNs(name) | MacroNs(name) | LifetimeNs(name) | AnonAssocTy(name) => {
- Some(name)
- }
+ TypeNs(name) | ValueNs(name) | MacroNs(name) | LifetimeNs(name) | AnonAssocTy(name)
+ | OpaqueLifetime(name) => Some(name),
Impl
| ForeignMod
@@ -486,9 +488,8 @@ fn hashed_symbol(&self) -> Option<Symbol> {
pub fn name(&self) -> DefPathDataName {
use self::DefPathData::*;
match *self {
- TypeNs(name) | ValueNs(name) | MacroNs(name) | LifetimeNs(name) => {
- DefPathDataName::Named(name)
- }
+ TypeNs(name) | ValueNs(name) | MacroNs(name) | LifetimeNs(name)
+ | OpaqueLifetime(name) => DefPathDataName::Named(name),
// Note that this does not show up in user print-outs.
CrateRoot => DefPathDataName::Anon { namespace: kw::Crate },
Impl => DefPathDataName::Anon { namespace: kw::Impl },
diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
index bbf36fe..fb67f2f 100644
--- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
+++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
@@ -5,7 +5,7 @@
use hir::def_id::{DefId, DefIdMap, LocalDefId};
use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
use rustc_errors::codes::*;
-use rustc_errors::{Applicability, ErrorGuaranteed, pluralize, struct_span_code_err};
+use rustc_errors::{Applicability, ErrorGuaranteed, MultiSpan, pluralize, struct_span_code_err};
use rustc_hir::def::{DefKind, Res};
use rustc_hir::intravisit::VisitorExt;
use rustc_hir::{self as hir, AmbigArg, GenericParamKind, ImplItemKind, intravisit};
@@ -14,10 +14,10 @@
use rustc_middle::ty::error::{ExpectedFound, TypeError};
use rustc_middle::ty::{
self, BottomUpFolder, GenericArgs, GenericParamDefKind, Ty, TyCtxt, TypeFoldable, TypeFolder,
- TypeSuperFoldable, TypeVisitableExt, TypingMode, Upcast,
+ TypeSuperFoldable, TypeVisitable, TypeVisitableExt, TypeVisitor, TypingMode, Upcast,
};
use rustc_middle::{bug, span_bug};
-use rustc_span::Span;
+use rustc_span::{DUMMY_SP, Span};
use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
use rustc_trait_selection::infer::InferCtxtExt;
use rustc_trait_selection::regions::InferCtxtRegionExt;
@@ -1137,65 +1137,319 @@ fn check_region_bounds_on_impl_item<'tcx>(
// but found 0" it's confusing, because it looks like there
// are zero. Since I don't quite know how to phrase things at
// the moment, give a kind of vague error message.
- if trait_params != impl_params {
- let span = tcx
- .hir_get_generics(impl_m.def_id.expect_local())
- .expect("expected impl item to have generics or else we can't compare them")
- .span;
+ if trait_params == impl_params {
+ return Ok(());
+ }
- let mut generics_span = None;
- let mut bounds_span = vec![];
- let mut where_span = None;
- if let Some(trait_node) = tcx.hir_get_if_local(trait_m.def_id)
- && let Some(trait_generics) = trait_node.generics()
- {
- generics_span = Some(trait_generics.span);
- // FIXME: we could potentially look at the impl's bounds to not point at bounds that
- // *are* present in the impl.
- for p in trait_generics.predicates {
- if let hir::WherePredicateKind::BoundPredicate(pred) = p.kind {
- for b in pred.bounds {
+ if !delay && let Some(guar) = check_region_late_boundedness(tcx, impl_m, trait_m) {
+ return Err(guar);
+ }
+
+ let span = tcx
+ .hir_get_generics(impl_m.def_id.expect_local())
+ .expect("expected impl item to have generics or else we can't compare them")
+ .span;
+
+ let mut generics_span = None;
+ let mut bounds_span = vec![];
+ let mut where_span = None;
+
+ if let Some(trait_node) = tcx.hir_get_if_local(trait_m.def_id)
+ && let Some(trait_generics) = trait_node.generics()
+ {
+ generics_span = Some(trait_generics.span);
+ // FIXME: we could potentially look at the impl's bounds to not point at bounds that
+ // *are* present in the impl.
+ for p in trait_generics.predicates {
+ match p.kind {
+ hir::WherePredicateKind::BoundPredicate(hir::WhereBoundPredicate {
+ bounds,
+ ..
+ })
+ | hir::WherePredicateKind::RegionPredicate(hir::WhereRegionPredicate {
+ bounds,
+ ..
+ }) => {
+ for b in *bounds {
if let hir::GenericBound::Outlives(lt) = b {
bounds_span.push(lt.ident.span);
}
}
}
+ _ => {}
}
- if let Some(impl_node) = tcx.hir_get_if_local(impl_m.def_id)
- && let Some(impl_generics) = impl_node.generics()
- {
- let mut impl_bounds = 0;
- for p in impl_generics.predicates {
- if let hir::WherePredicateKind::BoundPredicate(pred) = p.kind {
- for b in pred.bounds {
+ }
+ if let Some(impl_node) = tcx.hir_get_if_local(impl_m.def_id)
+ && let Some(impl_generics) = impl_node.generics()
+ {
+ let mut impl_bounds = 0;
+ for p in impl_generics.predicates {
+ match p.kind {
+ hir::WherePredicateKind::BoundPredicate(hir::WhereBoundPredicate {
+ bounds,
+ ..
+ })
+ | hir::WherePredicateKind::RegionPredicate(hir::WhereRegionPredicate {
+ bounds,
+ ..
+ }) => {
+ for b in *bounds {
if let hir::GenericBound::Outlives(_) = b {
impl_bounds += 1;
}
}
}
- }
- if impl_bounds == bounds_span.len() {
- bounds_span = vec![];
- } else if impl_generics.has_where_clause_predicates {
- where_span = Some(impl_generics.where_clause_span);
+ _ => {}
}
}
+ if impl_bounds == bounds_span.len() {
+ bounds_span = vec![];
+ } else if impl_generics.has_where_clause_predicates {
+ where_span = Some(impl_generics.where_clause_span);
+ }
}
- let reported = tcx
- .dcx()
- .create_err(LifetimesOrBoundsMismatchOnTrait {
- span,
- item_kind: impl_m.descr(),
- ident: impl_m.ident(tcx),
- generics_span,
- bounds_span,
- where_span,
- })
- .emit_unless(delay);
- return Err(reported);
}
- Ok(())
+ let reported = tcx
+ .dcx()
+ .create_err(LifetimesOrBoundsMismatchOnTrait {
+ span,
+ item_kind: impl_m.descr(),
+ ident: impl_m.ident(tcx),
+ generics_span,
+ bounds_span,
+ where_span,
+ })
+ .emit_unless(delay);
+
+ Err(reported)
+}
+
+#[allow(unused)]
+enum LateEarlyMismatch<'tcx> {
+ EarlyInImpl(DefId, DefId, ty::Region<'tcx>),
+ LateInImpl(DefId, DefId, ty::Region<'tcx>),
+}
+
+fn check_region_late_boundedness<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ impl_m: ty::AssocItem,
+ trait_m: ty::AssocItem,
+) -> Option<ErrorGuaranteed> {
+ if !impl_m.is_fn() {
+ return None;
+ }
+
+ let (infcx, param_env) = tcx
+ .infer_ctxt()
+ .build_with_typing_env(ty::TypingEnv::non_body_analysis(tcx, impl_m.def_id));
+
+ let impl_m_args = infcx.fresh_args_for_item(DUMMY_SP, impl_m.def_id);
+ let impl_m_sig = tcx.fn_sig(impl_m.def_id).instantiate(tcx, impl_m_args);
+ let impl_m_sig = tcx.liberate_late_bound_regions(impl_m.def_id, impl_m_sig);
+
+ let trait_m_args = infcx.fresh_args_for_item(DUMMY_SP, trait_m.def_id);
+ let trait_m_sig = tcx.fn_sig(trait_m.def_id).instantiate(tcx, trait_m_args);
+ let trait_m_sig = tcx.liberate_late_bound_regions(impl_m.def_id, trait_m_sig);
+
+ let ocx = ObligationCtxt::new(&infcx);
+
+ // Equate the signatures so that we can infer whether a late-bound param was present where
+ // an early-bound param was expected, since we replace the late-bound lifetimes with
+ // `ReLateParam`, and early-bound lifetimes with infer vars, so the early-bound args will
+ // resolve to `ReLateParam` if there is a mismatch.
+ let Ok(()) = ocx.eq(
+ &ObligationCause::dummy(),
+ param_env,
+ ty::Binder::dummy(trait_m_sig),
+ ty::Binder::dummy(impl_m_sig),
+ ) else {
+ return None;
+ };
+
+ let errors = ocx.select_where_possible();
+ if !errors.is_empty() {
+ return None;
+ }
+
+ let mut mismatched = vec![];
+
+ let impl_generics = tcx.generics_of(impl_m.def_id);
+ for (id_arg, arg) in
+ std::iter::zip(ty::GenericArgs::identity_for_item(tcx, impl_m.def_id), impl_m_args)
+ {
+ if let ty::GenericArgKind::Lifetime(r) = arg.unpack()
+ && let ty::ReVar(vid) = r.kind()
+ && let r = infcx
+ .inner
+ .borrow_mut()
+ .unwrap_region_constraints()
+ .opportunistic_resolve_var(tcx, vid)
+ && let ty::ReLateParam(ty::LateParamRegion {
+ kind: ty::LateParamRegionKind::Named(trait_param_def_id, _),
+ ..
+ }) = r.kind()
+ && let ty::ReEarlyParam(ebr) = id_arg.expect_region().kind()
+ {
+ mismatched.push(LateEarlyMismatch::EarlyInImpl(
+ impl_generics.region_param(ebr, tcx).def_id,
+ trait_param_def_id,
+ id_arg.expect_region(),
+ ));
+ }
+ }
+
+ let trait_generics = tcx.generics_of(trait_m.def_id);
+ for (id_arg, arg) in
+ std::iter::zip(ty::GenericArgs::identity_for_item(tcx, trait_m.def_id), trait_m_args)
+ {
+ if let ty::GenericArgKind::Lifetime(r) = arg.unpack()
+ && let ty::ReVar(vid) = r.kind()
+ && let r = infcx
+ .inner
+ .borrow_mut()
+ .unwrap_region_constraints()
+ .opportunistic_resolve_var(tcx, vid)
+ && let ty::ReLateParam(ty::LateParamRegion {
+ kind: ty::LateParamRegionKind::Named(impl_param_def_id, _),
+ ..
+ }) = r.kind()
+ && let ty::ReEarlyParam(ebr) = id_arg.expect_region().kind()
+ {
+ mismatched.push(LateEarlyMismatch::LateInImpl(
+ impl_param_def_id,
+ trait_generics.region_param(ebr, tcx).def_id,
+ id_arg.expect_region(),
+ ));
+ }
+ }
+
+ if mismatched.is_empty() {
+ return None;
+ }
+
+ let spans: Vec<_> = mismatched
+ .iter()
+ .map(|param| {
+ let (LateEarlyMismatch::EarlyInImpl(impl_param_def_id, ..)
+ | LateEarlyMismatch::LateInImpl(impl_param_def_id, ..)) = param;
+ tcx.def_span(impl_param_def_id)
+ })
+ .collect();
+
+ let mut diag = tcx
+ .dcx()
+ .struct_span_err(spans, "lifetime parameters do not match the trait definition")
+ .with_note("lifetime parameters differ in whether they are early- or late-bound")
+ .with_code(E0195);
+ for mismatch in mismatched {
+ match mismatch {
+ LateEarlyMismatch::EarlyInImpl(
+ impl_param_def_id,
+ trait_param_def_id,
+ early_bound_region,
+ ) => {
+ let mut multispan = MultiSpan::from_spans(vec![
+ tcx.def_span(impl_param_def_id),
+ tcx.def_span(trait_param_def_id),
+ ]);
+ multispan
+ .push_span_label(tcx.def_span(tcx.parent(impl_m.def_id)), "in this impl...");
+ multispan
+ .push_span_label(tcx.def_span(tcx.parent(trait_m.def_id)), "in this trait...");
+ multispan.push_span_label(
+ tcx.def_span(impl_param_def_id),
+ format!("`{}` is early-bound", tcx.item_name(impl_param_def_id)),
+ );
+ multispan.push_span_label(
+ tcx.def_span(trait_param_def_id),
+ format!("`{}` is late-bound", tcx.item_name(trait_param_def_id)),
+ );
+ if let Some(span) =
+ find_region_in_predicates(tcx, impl_m.def_id, early_bound_region)
+ {
+ multispan.push_span_label(
+ span,
+ format!(
+ "this lifetime bound makes `{}` early-bound",
+ tcx.item_name(impl_param_def_id)
+ ),
+ );
+ }
+ diag.span_note(
+ multispan,
+ format!(
+ "`{}` differs between the trait and impl",
+ tcx.item_name(impl_param_def_id)
+ ),
+ );
+ }
+ LateEarlyMismatch::LateInImpl(
+ impl_param_def_id,
+ trait_param_def_id,
+ early_bound_region,
+ ) => {
+ let mut multispan = MultiSpan::from_spans(vec![
+ tcx.def_span(impl_param_def_id),
+ tcx.def_span(trait_param_def_id),
+ ]);
+ multispan
+ .push_span_label(tcx.def_span(tcx.parent(impl_m.def_id)), "in this impl...");
+ multispan
+ .push_span_label(tcx.def_span(tcx.parent(trait_m.def_id)), "in this trait...");
+ multispan.push_span_label(
+ tcx.def_span(impl_param_def_id),
+ format!("`{}` is late-bound", tcx.item_name(impl_param_def_id)),
+ );
+ multispan.push_span_label(
+ tcx.def_span(trait_param_def_id),
+ format!("`{}` is early-bound", tcx.item_name(trait_param_def_id)),
+ );
+ if let Some(span) =
+ find_region_in_predicates(tcx, trait_m.def_id, early_bound_region)
+ {
+ multispan.push_span_label(
+ span,
+ format!(
+ "this lifetime bound makes `{}` early-bound",
+ tcx.item_name(trait_param_def_id)
+ ),
+ );
+ }
+ diag.span_note(
+ multispan,
+ format!(
+ "`{}` differs between the trait and impl",
+ tcx.item_name(impl_param_def_id)
+ ),
+ );
+ }
+ }
+ }
+
+ Some(diag.emit())
+}
+
+fn find_region_in_predicates<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ def_id: DefId,
+ early_bound_region: ty::Region<'tcx>,
+) -> Option<Span> {
+ for (pred, span) in tcx.explicit_predicates_of(def_id).instantiate_identity(tcx) {
+ if pred.visit_with(&mut FindRegion(early_bound_region)).is_break() {
+ return Some(span);
+ }
+ }
+
+ struct FindRegion<'tcx>(ty::Region<'tcx>);
+ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for FindRegion<'tcx> {
+ type Result = ControlFlow<()>;
+ fn visit_region(&mut self, r: ty::Region<'tcx>) -> Self::Result {
+ if r == self.0 { ControlFlow::Break(()) } else { ControlFlow::Continue(()) }
+ }
+ }
+
+ None
}
#[instrument(level = "debug", skip(infcx))]
diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
index a891607..709446d 100644
--- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
+++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
@@ -14,7 +14,7 @@
use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
use rustc_errors::ErrorGuaranteed;
use rustc_hir::def::{DefKind, Res};
-use rustc_hir::definitions::DisambiguatorState;
+use rustc_hir::definitions::{DefPathData, DisambiguatorState};
use rustc_hir::intravisit::{self, InferKind, Visitor, VisitorExt};
use rustc_hir::{
self as hir, AmbigArg, GenericArg, GenericParam, GenericParamKind, HirId, LifetimeKind, Node,
@@ -1470,14 +1470,14 @@ fn remap_opaque_captures(
let mut captures = captures.borrow_mut();
let remapped = *captures.entry(lifetime).or_insert_with(|| {
// `opaque_def_id` is unique to the `BoundVarContext` pass which is executed once
- // per `resolve_bound_vars` query. This is the only location that creates nested
- // lifetime inside a opaque type. `<opaque_def_id>::LifetimeNs(..)` is thus unique
+ // per `resolve_bound_vars` query. This is the only location that creates
+ // `OpaqueLifetime` paths. `<opaque_def_id>::OpaqueLifetime(..)` is thus unique
// to this query and duplicates within the query are handled by `self.disambiguator`.
let feed = self.tcx.create_def(
opaque_def_id,
- Some(ident.name),
- DefKind::LifetimeParam,
None,
+ DefKind::LifetimeParam,
+ Some(DefPathData::OpaqueLifetime(ident.name)),
&mut self.disambiguator,
);
feed.def_span(ident.span);
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs
index bf91eb1..4419d5d 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs
@@ -9,8 +9,8 @@
use rustc_hir::{AmbigArg, HirId};
use rustc_middle::bug;
use rustc_middle::ty::{
- self as ty, IsSuggestable, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt,
- TypeVisitor, Upcast,
+ self as ty, IsSuggestable, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitable,
+ TypeVisitableExt, TypeVisitor, Upcast,
};
use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol, kw, sym};
use rustc_trait_selection::traits;
@@ -996,7 +996,7 @@ struct GenericParamAndBoundVarCollector<'a, 'tcx> {
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for GenericParamAndBoundVarCollector<'_, 'tcx> {
type Result = ControlFlow<ErrorGuaranteed>;
- fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(
+ fn visit_binder<T: TypeFoldable<TyCtxt<'tcx>>>(
&mut self,
binder: &ty::Binder<'tcx, T>,
) -> Self::Result {
diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs
index 78233a3..60187ab 100644
--- a/compiler/rustc_hir_typeck/src/lib.rs
+++ b/compiler/rustc_hir_typeck/src/lib.rs
@@ -32,6 +32,7 @@
mod intrinsicck;
mod method;
mod op;
+mod opaque_types;
mod pat;
mod place_op;
mod rvalue_scopes;
@@ -245,9 +246,7 @@ fn typeck_with_inspect<'tcx>(
let typeck_results = fcx.resolve_type_vars_in_body(body);
- // We clone the defined opaque types during writeback in the new solver
- // because we have to use them during normalization.
- let _ = fcx.infcx.take_opaque_types();
+ fcx.detect_opaque_types_added_during_writeback();
// Consistency check our TypeckResults instance can hold all ItemLocalIds
// it will need to hold.
diff --git a/compiler/rustc_hir_typeck/src/opaque_types.rs b/compiler/rustc_hir_typeck/src/opaque_types.rs
new file mode 100644
index 0000000..e0224f8
--- /dev/null
+++ b/compiler/rustc_hir_typeck/src/opaque_types.rs
@@ -0,0 +1,26 @@
+use super::FnCtxt;
+impl<'tcx> FnCtxt<'_, 'tcx> {
+ /// We may in theory add further uses of an opaque after cloning the opaque
+ /// types storage during writeback when computing the defining uses.
+ ///
+ /// Silently ignoring them is dangerous and could result in ICE or even in
+ /// unsoundness, so we make sure we catch such cases here. There's currently
+ /// no known code where this actually happens, even with the new solver which
+ /// does normalize types in writeback after cloning the opaque type storage.
+ ///
+ /// FIXME(@lcnr): I believe this should be possible in theory and would like
+ /// an actual test here. After playing around with this for an hour, I wasn't
+ /// able to do anything which didn't already try to normalize the opaque before
+ /// then, either allowing compilation to succeed or causing an ambiguity error.
+ pub(super) fn detect_opaque_types_added_during_writeback(&self) {
+ let num_entries = self.checked_opaque_types_storage_entries.take().unwrap();
+ for (key, hidden_type) in
+ self.inner.borrow_mut().opaque_types().opaque_types_added_since(num_entries)
+ {
+ let opaque_type_string = self.tcx.def_path_str(key.def_id);
+ let msg = format!("unexpected cyclic definition of `{opaque_type_string}`");
+ self.dcx().span_delayed_bug(hidden_type.span, msg);
+ }
+ let _ = self.take_opaque_types();
+ }
+}
diff --git a/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs b/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs
index 56859ee..26be5fc 100644
--- a/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs
+++ b/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs
@@ -1,10 +1,10 @@
-use std::cell::RefCell;
+use std::cell::{Cell, RefCell};
use std::ops::Deref;
use rustc_data_structures::unord::{UnordMap, UnordSet};
use rustc_hir::def_id::LocalDefId;
use rustc_hir::{self as hir, HirId, HirIdMap, LangItem};
-use rustc_infer::infer::{InferCtxt, InferOk, TyCtxtInferExt};
+use rustc_infer::infer::{InferCtxt, InferOk, OpaqueTypeStorageEntries, TyCtxtInferExt};
use rustc_middle::span_bug;
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt, TypingMode};
use rustc_span::Span;
@@ -37,6 +37,11 @@ pub(crate) struct TypeckRootCtxt<'tcx> {
pub(super) fulfillment_cx: RefCell<Box<dyn TraitEngine<'tcx, FulfillmentError<'tcx>>>>,
+ // Used to detect opaque types uses added after we've already checked them.
+ //
+ // See [FnCtxt::detect_opaque_types_added_during_writeback] for more details.
+ pub(super) checked_opaque_types_storage_entries: Cell<Option<OpaqueTypeStorageEntries>>,
+
/// Some additional `Sized` obligations badly affect type inference.
/// These obligations are added in a later stage of typeck.
/// Removing these may also cause additional complications, see #101066.
@@ -85,12 +90,14 @@ pub(crate) fn new(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Self {
let infcx =
tcx.infer_ctxt().ignoring_regions().build(TypingMode::typeck_for_body(tcx, def_id));
let typeck_results = RefCell::new(ty::TypeckResults::new(hir_owner));
+ let fulfillment_cx = RefCell::new(<dyn TraitEngine<'_, _>>::new(&infcx));
TypeckRootCtxt {
- typeck_results,
- fulfillment_cx: RefCell::new(<dyn TraitEngine<'_, _>>::new(&infcx)),
infcx,
+ typeck_results,
locals: RefCell::new(Default::default()),
+ fulfillment_cx,
+ checked_opaque_types_storage_entries: Cell::new(None),
deferred_sized_obligations: RefCell::new(Vec::new()),
deferred_call_resolutions: RefCell::new(Default::default()),
deferred_cast_checks: RefCell::new(Vec::new()),
diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs
index 8e7ce83..9be041f 100644
--- a/compiler/rustc_hir_typeck/src/writeback.rs
+++ b/compiler/rustc_hir_typeck/src/writeback.rs
@@ -535,13 +535,10 @@ fn visit_opaque_types(&mut self) {
let tcx = self.tcx();
// We clone the opaques instead of stealing them here as they are still used for
// normalization in the next generation trait solver.
- //
- // FIXME(-Znext-solver): Opaque types defined after this would simply get dropped
- // at the end of typeck. While this seems unlikely to happen in practice this
- // should still get fixed. Either by preventing writeback from defining new opaque
- // types or by using this function at the end of writeback and running it as a
- // fixpoint.
let opaque_types = self.fcx.infcx.clone_opaque_types();
+ let num_entries = self.fcx.inner.borrow_mut().opaque_types().num_entries();
+ let prev = self.fcx.checked_opaque_types_storage_entries.replace(Some(num_entries));
+ debug_assert_eq!(prev, None);
for (opaque_type_key, hidden_type) in opaque_types {
let hidden_type = self.resolve(hidden_type, &hidden_type.span);
let opaque_type_key = self.resolve(opaque_type_key, &hidden_type.span);
diff --git a/compiler/rustc_infer/src/infer/context.rs b/compiler/rustc_infer/src/infer/context.rs
index 22d7ce7..359b9da 100644
--- a/compiler/rustc_infer/src/infer/context.rs
+++ b/compiler/rustc_infer/src/infer/context.rs
@@ -6,7 +6,10 @@
use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable};
use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span};
-use super::{BoundRegionConversionTime, InferCtxt, RegionVariableOrigin, SubregionOrigin};
+use super::{
+ BoundRegionConversionTime, InferCtxt, OpaqueTypeStorageEntries, RegionVariableOrigin,
+ SubregionOrigin,
+};
impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> {
type Interner = TyCtxt<'tcx>;
@@ -213,4 +216,58 @@ fn equate_regions(&self, a: ty::Region<'tcx>, b: ty::Region<'tcx>, span: Span) {
fn register_ty_outlives(&self, ty: Ty<'tcx>, r: ty::Region<'tcx>, span: Span) {
self.register_region_obligation_with_cause(ty, r, &ObligationCause::dummy_with_span(span));
}
+
+ type OpaqueTypeStorageEntries = OpaqueTypeStorageEntries;
+ fn opaque_types_storage_num_entries(&self) -> OpaqueTypeStorageEntries {
+ self.inner.borrow_mut().opaque_types().num_entries()
+ }
+ fn clone_opaque_types_lookup_table(&self) -> Vec<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)> {
+ self.inner.borrow_mut().opaque_types().iter_lookup_table().map(|(k, h)| (k, h.ty)).collect()
+ }
+ fn clone_duplicate_opaque_types(&self) -> Vec<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)> {
+ self.inner
+ .borrow_mut()
+ .opaque_types()
+ .iter_duplicate_entries()
+ .map(|(k, h)| (k, h.ty))
+ .collect()
+ }
+ fn clone_opaque_types_added_since(
+ &self,
+ prev_entries: OpaqueTypeStorageEntries,
+ ) -> Vec<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)> {
+ self.inner
+ .borrow_mut()
+ .opaque_types()
+ .opaque_types_added_since(prev_entries)
+ .map(|(k, h)| (k, h.ty))
+ .collect()
+ }
+
+ fn register_hidden_type_in_storage(
+ &self,
+ opaque_type_key: ty::OpaqueTypeKey<'tcx>,
+ hidden_ty: Ty<'tcx>,
+ span: Span,
+ ) -> Option<Ty<'tcx>> {
+ self.register_hidden_type_in_storage(
+ opaque_type_key,
+ ty::OpaqueHiddenType { span, ty: hidden_ty },
+ )
+ }
+ fn add_duplicate_opaque_type(
+ &self,
+ opaque_type_key: ty::OpaqueTypeKey<'tcx>,
+ hidden_ty: Ty<'tcx>,
+ span: Span,
+ ) {
+ self.inner
+ .borrow_mut()
+ .opaque_types()
+ .add_duplicate(opaque_type_key, ty::OpaqueHiddenType { span, ty: hidden_ty })
+ }
+
+ fn reset_opaque_types(&self) {
+ let _ = self.take_opaque_types();
+ }
}
diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs
index 070d285..b408d76 100644
--- a/compiler/rustc_infer/src/infer/mod.rs
+++ b/compiler/rustc_infer/src/infer/mod.rs
@@ -9,7 +9,7 @@
pub use freshen::TypeFreshener;
use lexical_region_resolve::LexicalRegionResolutions;
pub use lexical_region_resolve::RegionResolutionError;
-use opaque_types::OpaqueTypeStorage;
+pub use opaque_types::{OpaqueTypeStorage, OpaqueTypeStorageEntries, OpaqueTypeTable};
use region_constraints::{
GenericKind, RegionConstraintCollector, RegionConstraintStorage, VarInfos, VerifyBound,
};
diff --git a/compiler/rustc_infer/src/infer/opaque_types/mod.rs b/compiler/rustc_infer/src/infer/opaque_types/mod.rs
index df7144c..220d5e9 100644
--- a/compiler/rustc_infer/src/infer/opaque_types/mod.rs
+++ b/compiler/rustc_infer/src/infer/opaque_types/mod.rs
@@ -18,7 +18,7 @@
mod table;
-pub(crate) use table::{OpaqueTypeStorage, OpaqueTypeTable};
+pub use table::{OpaqueTypeStorage, OpaqueTypeStorageEntries, OpaqueTypeTable};
impl<'tcx> InferCtxt<'tcx> {
/// This is a backwards compatibility hack to prevent breaking changes from
diff --git a/compiler/rustc_infer/src/infer/opaque_types/table.rs b/compiler/rustc_infer/src/infer/opaque_types/table.rs
index 3c5bf9d..4675284 100644
--- a/compiler/rustc_infer/src/infer/opaque_types/table.rs
+++ b/compiler/rustc_infer/src/infer/opaque_types/table.rs
@@ -14,6 +14,16 @@ pub struct OpaqueTypeStorage<'tcx> {
duplicate_entries: Vec<(OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>)>,
}
+/// The number of entries in the opaque type storage at a given point.
+///
+/// Used to check that we haven't added any new opaque types after checking
+/// the opaque types currently in the storage.
+#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
+pub struct OpaqueTypeStorageEntries {
+ opaque_types: usize,
+ duplicate_entries: usize,
+}
+
impl<'tcx> OpaqueTypeStorage<'tcx> {
#[instrument(level = "debug")]
pub(crate) fn remove(
@@ -49,6 +59,24 @@ pub(crate) fn take_opaque_types(
std::mem::take(opaque_types).into_iter().chain(std::mem::take(duplicate_entries))
}
+ pub fn num_entries(&self) -> OpaqueTypeStorageEntries {
+ OpaqueTypeStorageEntries {
+ opaque_types: self.opaque_types.len(),
+ duplicate_entries: self.duplicate_entries.len(),
+ }
+ }
+
+ pub fn opaque_types_added_since(
+ &self,
+ prev_entries: OpaqueTypeStorageEntries,
+ ) -> impl Iterator<Item = (OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>)> {
+ self.opaque_types
+ .iter()
+ .skip(prev_entries.opaque_types)
+ .map(|(k, v)| (*k, *v))
+ .chain(self.duplicate_entries.iter().skip(prev_entries.duplicate_entries).copied())
+ }
+
/// Only returns the opaque types from the lookup table. These are used
/// when normalizing opaque types and have a unique key.
///
diff --git a/compiler/rustc_infer/src/infer/outlives/for_liveness.rs b/compiler/rustc_infer/src/infer/outlives/for_liveness.rs
index c44d9723..2a4b977 100644
--- a/compiler/rustc_infer/src/infer/outlives/for_liveness.rs
+++ b/compiler/rustc_infer/src/infer/outlives/for_liveness.rs
@@ -24,10 +24,6 @@ impl<'tcx, OP> TypeVisitor<TyCtxt<'tcx>> for FreeRegionsVisitor<'tcx, OP>
where
OP: FnMut(ty::Region<'tcx>),
{
- fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(&mut self, t: &ty::Binder<'tcx, T>) {
- t.super_visit_with(self);
- }
-
fn visit_region(&mut self, r: ty::Region<'tcx>) {
match r.kind() {
// ignore bound regions, keep visiting
diff --git a/compiler/rustc_lint/src/impl_trait_overcaptures.rs b/compiler/rustc_lint/src/impl_trait_overcaptures.rs
index 7f4789a..a8f45d0 100644
--- a/compiler/rustc_lint/src/impl_trait_overcaptures.rs
+++ b/compiler/rustc_lint/src/impl_trait_overcaptures.rs
@@ -15,7 +15,8 @@
Relate, RelateResult, TypeRelation, structurally_relate_consts, structurally_relate_tys,
};
use rustc_middle::ty::{
- self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor,
+ self, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt,
+ TypeVisitor,
};
use rustc_middle::{bug, span_bug};
use rustc_session::lint::FutureIncompatibilityReason;
@@ -209,7 +210,7 @@ impl<'tcx, VarFn, OutlivesFn> TypeVisitor<TyCtxt<'tcx>>
VarFn: FnOnce() -> FxHashMap<DefId, ty::Variance>,
OutlivesFn: FnOnce() -> OutlivesEnvironment<'tcx>,
{
- fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(&mut self, t: &ty::Binder<'tcx, T>) {
+ fn visit_binder<T: TypeFoldable<TyCtxt<'tcx>>>(&mut self, t: &ty::Binder<'tcx, T>) {
// When we get into a binder, we need to add its own bound vars to the scope.
let mut added = vec![];
for arg in t.bound_vars() {
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index eb04c35..b2133fe 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -387,7 +387,7 @@
}
}
- query stalled_generators_within(
+ query nested_bodies_within(
key: LocalDefId
) -> &'tcx ty::List<LocalDefId> {
desc {
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index f9ff5c3..0759fa3 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -691,15 +691,17 @@ fn opaque_types_defined_by(self, defining_anchor: LocalDefId) -> Self::LocalDefI
self.opaque_types_defined_by(defining_anchor)
}
- fn opaque_types_and_generators_defined_by(
+ fn opaque_types_and_coroutines_defined_by(
self,
defining_anchor: Self::LocalDefId,
) -> Self::LocalDefIds {
if self.next_trait_solver_globally() {
+ let coroutines_defined_by = self
+ .nested_bodies_within(defining_anchor)
+ .iter()
+ .filter(|def_id| self.is_coroutine(def_id.to_def_id()));
self.mk_local_def_ids_from_iter(
- self.opaque_types_defined_by(defining_anchor)
- .iter()
- .chain(self.stalled_generators_within(defining_anchor)),
+ self.opaque_types_defined_by(defining_anchor).iter().chain(coroutines_defined_by),
)
} else {
self.opaque_types_defined_by(defining_anchor)
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index c4ddaca..2d69a1c 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -309,7 +309,10 @@ pub fn to_string(self, def_id: LocalDefId, tcx: TyCtxt<'_>) -> String {
} else if restricted_id == tcx.parent_module_from_def_id(def_id).to_local_def_id() {
"pub(self)".to_string()
} else {
- format!("pub({})", tcx.item_name(restricted_id.to_def_id()))
+ format!(
+ "pub(in crate{})",
+ tcx.def_path(restricted_id.to_def_id()).to_string_no_crate_verbose()
+ )
}
}
ty::Visibility::Public => "pub".to_string(),
diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs
index bc1423a..0250c77 100644
--- a/compiler/rustc_middle/src/ty/print/pretty.rs
+++ b/compiler/rustc_middle/src/ty/print/pretty.rs
@@ -2934,7 +2934,7 @@ pub fn pretty_wrap_binder<T, C: FnOnce(&T, &mut Self) -> Result<(), fmt::Error>>
fn prepare_region_info<T>(&mut self, value: &ty::Binder<'tcx, T>)
where
- T: TypeVisitable<TyCtxt<'tcx>>,
+ T: TypeFoldable<TyCtxt<'tcx>>,
{
struct RegionNameCollector<'tcx> {
used_region_names: FxHashSet<Symbol>,
diff --git a/compiler/rustc_middle/src/ty/visit.rs b/compiler/rustc_middle/src/ty/visit.rs
index 3853a80..f8042174 100644
--- a/compiler/rustc_middle/src/ty/visit.rs
+++ b/compiler/rustc_middle/src/ty/visit.rs
@@ -66,7 +66,7 @@ impl<'tcx, F> TypeVisitor<TyCtxt<'tcx>> for RegionVisitor<F>
{
type Result = ControlFlow<()>;
- fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(
+ fn visit_binder<T: TypeFoldable<TyCtxt<'tcx>>>(
&mut self,
t: &Binder<'tcx, T>,
) -> Self::Result {
@@ -168,7 +168,7 @@ fn new(just_constrained: bool) -> Self {
}
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for LateBoundRegionsCollector {
- fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(&mut self, t: &Binder<'tcx, T>) {
+ fn visit_binder<T: TypeFoldable<TyCtxt<'tcx>>>(&mut self, t: &Binder<'tcx, T>) {
self.current_index.shift_in(1);
t.super_visit_with(self);
self.current_index.shift_out(1);
diff --git a/compiler/rustc_mir_dataflow/src/framework/cursor.rs b/compiler/rustc_mir_dataflow/src/framework/cursor.rs
index d500576..3f6e7a0 100644
--- a/compiler/rustc_mir_dataflow/src/framework/cursor.rs
+++ b/compiler/rustc_mir_dataflow/src/framework/cursor.rs
@@ -1,5 +1,6 @@
//! Random access inspection of the results of a dataflow analysis.
+use std::borrow::Cow;
use std::cmp::Ordering;
use std::ops::{Deref, DerefMut};
@@ -9,38 +10,30 @@
use super::{Analysis, Direction, Effect, EffectIndex, Results};
-/// Some `ResultsCursor`s want to own a `Results`, and some want to borrow a `Results`, either
-/// mutable or immutably. This type allows all of the above. It's similar to `Cow`.
-pub enum ResultsHandle<'a, 'tcx, A>
-where
- A: Analysis<'tcx>,
-{
- BorrowedMut(&'a mut Results<'tcx, A>),
- Owned(Results<'tcx, A>),
+/// Some `ResultsCursor`s want to own an `Analysis`, and some want to borrow an `Analysis`, either
+/// mutable or immutably. This type allows all of the above. It's similar to `Cow`, but `Cow`
+/// doesn't allow mutable borrowing.
+enum CowMut<'a, T> {
+ BorrowedMut(&'a mut T),
+ Owned(T),
}
-impl<'tcx, A> Deref for ResultsHandle<'_, 'tcx, A>
-where
- A: Analysis<'tcx>,
-{
- type Target = Results<'tcx, A>;
+impl<T> Deref for CowMut<'_, T> {
+ type Target = T;
- fn deref(&self) -> &Results<'tcx, A> {
+ fn deref(&self) -> &T {
match self {
- ResultsHandle::BorrowedMut(borrowed) => borrowed,
- ResultsHandle::Owned(owned) => owned,
+ CowMut::BorrowedMut(borrowed) => borrowed,
+ CowMut::Owned(owned) => owned,
}
}
}
-impl<'tcx, A> DerefMut for ResultsHandle<'_, 'tcx, A>
-where
- A: Analysis<'tcx>,
-{
- fn deref_mut(&mut self) -> &mut Results<'tcx, A> {
+impl<T> DerefMut for CowMut<'_, T> {
+ fn deref_mut(&mut self) -> &mut T {
match self {
- ResultsHandle::BorrowedMut(borrowed) => borrowed,
- ResultsHandle::Owned(owned) => owned,
+ CowMut::BorrowedMut(borrowed) => borrowed,
+ CowMut::Owned(owned) => owned,
}
}
}
@@ -60,7 +53,8 @@ pub struct ResultsCursor<'mir, 'tcx, A>
A: Analysis<'tcx>,
{
body: &'mir mir::Body<'tcx>,
- results: ResultsHandle<'mir, 'tcx, A>,
+ analysis: CowMut<'mir, A>,
+ results: Cow<'mir, Results<A::Domain>>,
state: A::Domain,
pos: CursorPosition,
@@ -88,11 +82,15 @@ pub fn body(&self) -> &'mir mir::Body<'tcx> {
self.body
}
- /// Returns a new cursor that can inspect `results`.
- pub fn new(body: &'mir mir::Body<'tcx>, results: ResultsHandle<'mir, 'tcx, A>) -> Self {
- let bottom_value = results.analysis.bottom_value(body);
+ fn new(
+ body: &'mir mir::Body<'tcx>,
+ analysis: CowMut<'mir, A>,
+ results: Cow<'mir, Results<A::Domain>>,
+ ) -> Self {
+ let bottom_value = analysis.bottom_value(body);
ResultsCursor {
body,
+ analysis,
results,
// Initialize to the `bottom_value` and set `state_needs_reset` to tell the cursor that
@@ -107,6 +105,24 @@ pub fn new(body: &'mir mir::Body<'tcx>, results: ResultsHandle<'mir, 'tcx, A>) -
}
}
+ /// Returns a new cursor that takes ownership of and inspects analysis results.
+ pub fn new_owning(
+ body: &'mir mir::Body<'tcx>,
+ analysis: A,
+ results: Results<A::Domain>,
+ ) -> Self {
+ Self::new(body, CowMut::Owned(analysis), Cow::Owned(results))
+ }
+
+ /// Returns a new cursor that borrows and inspects analysis results.
+ pub fn new_borrowing(
+ body: &'mir mir::Body<'tcx>,
+ analysis: &'mir mut A,
+ results: &'mir Results<A::Domain>,
+ ) -> Self {
+ Self::new(body, CowMut::BorrowedMut(analysis), Cow::Borrowed(results))
+ }
+
/// Allows inspection of unreachable basic blocks even with `debug_assertions` enabled.
#[cfg(test)]
pub(crate) fn allow_unreachable(&mut self) {
@@ -116,7 +132,7 @@ pub(crate) fn allow_unreachable(&mut self) {
/// Returns the `Analysis` used to generate the underlying `Results`.
pub fn analysis(&self) -> &A {
- &self.results.analysis
+ &self.analysis
}
/// Resets the cursor to hold the entry set for the given basic block.
@@ -128,7 +144,7 @@ pub(super) fn seek_to_block_entry(&mut self, block: BasicBlock) {
#[cfg(debug_assertions)]
assert!(self.reachable_blocks.contains(block));
- self.state.clone_from(self.results.entry_set_for_block(block));
+ self.state.clone_from(&self.results[block]);
self.pos = CursorPosition::block_entry(block);
self.state_needs_reset = false;
}
@@ -220,7 +236,7 @@ fn seek_after(&mut self, target: Location, effect: Effect) {
let target_effect_index = effect.at_index(target.statement_index);
A::Direction::apply_effects_in_range(
- &mut self.results.analysis,
+ &mut *self.analysis,
&mut self.state,
target.block,
block_data,
@@ -236,7 +252,7 @@ fn seek_after(&mut self, target: Location, effect: Effect) {
/// This can be used, e.g., to apply the call return effect directly to the cursor without
/// creating an extra copy of the dataflow state.
pub fn apply_custom_effect(&mut self, f: impl FnOnce(&mut A, &mut A::Domain)) {
- f(&mut self.results.analysis, &mut self.state);
+ f(&mut self.analysis, &mut self.state);
self.state_needs_reset = true;
}
}
diff --git a/compiler/rustc_mir_dataflow/src/framework/direction.rs b/compiler/rustc_mir_dataflow/src/framework/direction.rs
index b8c26da..e955e38 100644
--- a/compiler/rustc_mir_dataflow/src/framework/direction.rs
+++ b/compiler/rustc_mir_dataflow/src/framework/direction.rs
@@ -5,7 +5,7 @@
};
use super::visitor::ResultsVisitor;
-use super::{Analysis, Effect, EffectIndex, Results};
+use super::{Analysis, Effect, EffectIndex};
pub trait Direction {
const IS_FORWARD: bool;
@@ -36,13 +36,13 @@ fn apply_effects_in_range<'tcx, A>(
A: Analysis<'tcx>;
/// Called by `ResultsVisitor` to recompute the analysis domain values for
- /// all locations in a basic block (starting from the entry value stored
- /// in `Results`) and to visit them with `vis`.
+ /// all locations in a basic block (starting from `entry_state` and to
+ /// visit them with `vis`.
fn visit_results_in_block<'mir, 'tcx, A>(
state: &mut A::Domain,
block: BasicBlock,
block_data: &'mir mir::BasicBlockData<'tcx>,
- results: &mut Results<'tcx, A>,
+ analysis: &mut A,
vis: &mut impl ResultsVisitor<'tcx, A>,
) where
A: Analysis<'tcx>;
@@ -211,28 +211,26 @@ fn visit_results_in_block<'mir, 'tcx, A>(
state: &mut A::Domain,
block: BasicBlock,
block_data: &'mir mir::BasicBlockData<'tcx>,
- results: &mut Results<'tcx, A>,
+ analysis: &mut A,
vis: &mut impl ResultsVisitor<'tcx, A>,
) where
A: Analysis<'tcx>,
{
- state.clone_from(results.entry_set_for_block(block));
-
vis.visit_block_end(state);
let loc = Location { block, statement_index: block_data.statements.len() };
let term = block_data.terminator();
- results.analysis.apply_early_terminator_effect(state, term, loc);
- vis.visit_after_early_terminator_effect(results, state, term, loc);
- results.analysis.apply_primary_terminator_effect(state, term, loc);
- vis.visit_after_primary_terminator_effect(results, state, term, loc);
+ analysis.apply_early_terminator_effect(state, term, loc);
+ vis.visit_after_early_terminator_effect(analysis, state, term, loc);
+ analysis.apply_primary_terminator_effect(state, term, loc);
+ vis.visit_after_primary_terminator_effect(analysis, state, term, loc);
for (statement_index, stmt) in block_data.statements.iter().enumerate().rev() {
let loc = Location { block, statement_index };
- results.analysis.apply_early_statement_effect(state, stmt, loc);
- vis.visit_after_early_statement_effect(results, state, stmt, loc);
- results.analysis.apply_primary_statement_effect(state, stmt, loc);
- vis.visit_after_primary_statement_effect(results, state, stmt, loc);
+ analysis.apply_early_statement_effect(state, stmt, loc);
+ vis.visit_after_early_statement_effect(analysis, state, stmt, loc);
+ analysis.apply_primary_statement_effect(state, stmt, loc);
+ vis.visit_after_primary_statement_effect(analysis, state, stmt, loc);
}
vis.visit_block_start(state);
@@ -393,29 +391,27 @@ fn visit_results_in_block<'mir, 'tcx, A>(
state: &mut A::Domain,
block: BasicBlock,
block_data: &'mir mir::BasicBlockData<'tcx>,
- results: &mut Results<'tcx, A>,
+ analysis: &mut A,
vis: &mut impl ResultsVisitor<'tcx, A>,
) where
A: Analysis<'tcx>,
{
- state.clone_from(results.entry_set_for_block(block));
-
vis.visit_block_start(state);
for (statement_index, stmt) in block_data.statements.iter().enumerate() {
let loc = Location { block, statement_index };
- results.analysis.apply_early_statement_effect(state, stmt, loc);
- vis.visit_after_early_statement_effect(results, state, stmt, loc);
- results.analysis.apply_primary_statement_effect(state, stmt, loc);
- vis.visit_after_primary_statement_effect(results, state, stmt, loc);
+ analysis.apply_early_statement_effect(state, stmt, loc);
+ vis.visit_after_early_statement_effect(analysis, state, stmt, loc);
+ analysis.apply_primary_statement_effect(state, stmt, loc);
+ vis.visit_after_primary_statement_effect(analysis, state, stmt, loc);
}
let loc = Location { block, statement_index: block_data.statements.len() };
let term = block_data.terminator();
- results.analysis.apply_early_terminator_effect(state, term, loc);
- vis.visit_after_early_terminator_effect(results, state, term, loc);
- results.analysis.apply_primary_terminator_effect(state, term, loc);
- vis.visit_after_primary_terminator_effect(results, state, term, loc);
+ analysis.apply_early_terminator_effect(state, term, loc);
+ vis.visit_after_early_terminator_effect(analysis, state, term, loc);
+ analysis.apply_primary_terminator_effect(state, term, loc);
+ vis.visit_after_primary_terminator_effect(analysis, state, term, loc);
vis.visit_block_end(state);
}
diff --git a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs
index b5e9a0b..a7d5422 100644
--- a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs
+++ b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs
@@ -21,7 +21,9 @@
use {rustc_ast as ast, rustc_graphviz as dot};
use super::fmt::{DebugDiffWithAdapter, DebugWithAdapter, DebugWithContext};
-use super::{Analysis, CallReturnPlaces, Direction, Results, ResultsCursor, ResultsVisitor};
+use super::{
+ Analysis, CallReturnPlaces, Direction, Results, ResultsCursor, ResultsVisitor, visit_results,
+};
use crate::errors::{
DuplicateValuesFor, PathMustEndInFilename, RequiresAnArgument, UnknownFormatter,
};
@@ -32,7 +34,8 @@
pub(super) fn write_graphviz_results<'tcx, A>(
tcx: TyCtxt<'tcx>,
body: &Body<'tcx>,
- results: &mut Results<'tcx, A>,
+ analysis: &mut A,
+ results: &Results<A::Domain>,
pass_name: Option<&'static str>,
) -> std::io::Result<()>
where
@@ -77,7 +80,7 @@ pub(super) fn write_graphviz_results<'tcx, A>(
let mut buf = Vec::new();
- let graphviz = Formatter::new(body, results, style);
+ let graphviz = Formatter::new(body, analysis, results, style);
let mut render_opts =
vec![dot::RenderOption::Fontname(tcx.sess.opts.unstable_opts.graphviz_font.clone())];
if tcx.sess.opts.unstable_opts.graphviz_dark_mode {
@@ -203,10 +206,11 @@ struct Formatter<'mir, 'tcx, A>
{
body: &'mir Body<'tcx>,
// The `RefCell` is used because `<Formatter as Labeller>::node_label`
- // takes `&self`, but it needs to modify the results. This is also the
+ // takes `&self`, but it needs to modify the analysis. This is also the
// reason for the `Formatter`/`BlockFormatter` split; `BlockFormatter` has
// the operations that involve the mutation, i.e. within the `borrow_mut`.
- results: RefCell<&'mir mut Results<'tcx, A>>,
+ analysis: RefCell<&'mir mut A>,
+ results: &'mir Results<A::Domain>,
style: OutputStyle,
reachable: DenseBitSet<BasicBlock>,
}
@@ -217,11 +221,12 @@ impl<'mir, 'tcx, A> Formatter<'mir, 'tcx, A>
{
fn new(
body: &'mir Body<'tcx>,
- results: &'mir mut Results<'tcx, A>,
+ analysis: &'mir mut A,
+ results: &'mir Results<A::Domain>,
style: OutputStyle,
) -> Self {
let reachable = traversal::reachable_as_bitset(body);
- Formatter { body, results: results.into(), style, reachable }
+ Formatter { body, analysis: analysis.into(), results, style, reachable }
}
}
@@ -259,12 +264,12 @@ fn node_id(&self, n: &Self::Node) -> dot::Id<'_> {
}
fn node_label(&self, block: &Self::Node) -> dot::LabelText<'_> {
- let mut results = self.results.borrow_mut();
+ let analysis = &mut **self.analysis.borrow_mut();
- let diffs = StateDiffCollector::run(self.body, *block, *results, self.style);
+ let diffs = StateDiffCollector::run(self.body, *block, analysis, self.results, self.style);
let mut fmt = BlockFormatter {
- cursor: results.as_results_cursor(self.body),
+ cursor: ResultsCursor::new_borrowing(self.body, analysis, self.results),
style: self.style,
bg: Background::Light,
};
@@ -692,7 +697,8 @@ impl<D> StateDiffCollector<D> {
fn run<'tcx, A>(
body: &Body<'tcx>,
block: BasicBlock,
- results: &mut Results<'tcx, A>,
+ analysis: &mut A,
+ results: &Results<A::Domain>,
style: OutputStyle,
) -> Self
where
@@ -700,12 +706,12 @@ fn run<'tcx, A>(
D: DebugWithContext<A>,
{
let mut collector = StateDiffCollector {
- prev_state: results.analysis.bottom_value(body),
+ prev_state: analysis.bottom_value(body),
after: vec![],
before: (style == OutputStyle::BeforeAndAfter).then_some(vec![]),
};
- results.visit_with(body, std::iter::once(block), &mut collector);
+ visit_results(body, std::iter::once(block), analysis, results, &mut collector);
collector
}
}
@@ -729,49 +735,49 @@ fn visit_block_end(&mut self, state: &A::Domain) {
fn visit_after_early_statement_effect(
&mut self,
- results: &mut Results<'tcx, A>,
+ analysis: &mut A,
state: &A::Domain,
_statement: &mir::Statement<'tcx>,
_location: Location,
) {
if let Some(before) = self.before.as_mut() {
- before.push(diff_pretty(state, &self.prev_state, &results.analysis));
+ before.push(diff_pretty(state, &self.prev_state, analysis));
self.prev_state.clone_from(state)
}
}
fn visit_after_primary_statement_effect(
&mut self,
- results: &mut Results<'tcx, A>,
+ analysis: &mut A,
state: &A::Domain,
_statement: &mir::Statement<'tcx>,
_location: Location,
) {
- self.after.push(diff_pretty(state, &self.prev_state, &results.analysis));
+ self.after.push(diff_pretty(state, &self.prev_state, analysis));
self.prev_state.clone_from(state)
}
fn visit_after_early_terminator_effect(
&mut self,
- results: &mut Results<'tcx, A>,
+ analysis: &mut A,
state: &A::Domain,
_terminator: &mir::Terminator<'tcx>,
_location: Location,
) {
if let Some(before) = self.before.as_mut() {
- before.push(diff_pretty(state, &self.prev_state, &results.analysis));
+ before.push(diff_pretty(state, &self.prev_state, analysis));
self.prev_state.clone_from(state)
}
}
fn visit_after_primary_terminator_effect(
&mut self,
- results: &mut Results<'tcx, A>,
+ analysis: &mut A,
state: &A::Domain,
_terminator: &mir::Terminator<'tcx>,
_location: Location,
) {
- self.after.push(diff_pretty(state, &self.prev_state, &results.analysis));
+ self.after.push(diff_pretty(state, &self.prev_state, analysis));
self.prev_state.clone_from(state)
}
}
diff --git a/compiler/rustc_mir_dataflow/src/framework/mod.rs b/compiler/rustc_mir_dataflow/src/framework/mod.rs
index 09f6cdb..9cadec1 100644
--- a/compiler/rustc_mir_dataflow/src/framework/mod.rs
+++ b/compiler/rustc_mir_dataflow/src/framework/mod.rs
@@ -58,8 +58,9 @@
pub use self::cursor::ResultsCursor;
pub use self::direction::{Backward, Direction, Forward};
pub use self::lattice::{JoinSemiLattice, MaybeReachable};
-pub use self::results::{EntryStates, Results};
-pub use self::visitor::{ResultsVisitor, visit_results};
+pub(crate) use self::results::AnalysisAndResults;
+pub use self::results::Results;
+pub use self::visitor::{ResultsVisitor, visit_reachable_results, visit_results};
/// Analysis domains are all bitsets of various kinds. This trait holds
/// operations needed by all of them.
@@ -247,17 +248,15 @@ fn iterate_to_fixpoint<'mir>(
tcx: TyCtxt<'tcx>,
body: &'mir mir::Body<'tcx>,
pass_name: Option<&'static str>,
- ) -> Results<'tcx, Self>
+ ) -> AnalysisAndResults<'tcx, Self>
where
Self: Sized,
Self::Domain: DebugWithContext<Self>,
{
- let mut entry_states =
- IndexVec::from_fn_n(|_| self.bottom_value(body), body.basic_blocks.len());
- self.initialize_start_block(body, &mut entry_states[mir::START_BLOCK]);
+ let mut results = IndexVec::from_fn_n(|_| self.bottom_value(body), body.basic_blocks.len());
+ self.initialize_start_block(body, &mut results[mir::START_BLOCK]);
- if Self::Direction::IS_BACKWARD && entry_states[mir::START_BLOCK] != self.bottom_value(body)
- {
+ if Self::Direction::IS_BACKWARD && results[mir::START_BLOCK] != self.bottom_value(body) {
bug!("`initialize_start_block` is not yet supported for backward dataflow analyses");
}
@@ -280,10 +279,9 @@ fn iterate_to_fixpoint<'mir>(
// every iteration.
let mut state = self.bottom_value(body);
while let Some(bb) = dirty_queue.pop() {
- // Set the state to the entry state of the block.
- // This is equivalent to `state = entry_states[bb].clone()`,
- // but it saves an allocation, thus improving compile times.
- state.clone_from(&entry_states[bb]);
+ // Set the state to the entry state of the block. This is equivalent to `state =
+ // results[bb].clone()`, but it saves an allocation, thus improving compile times.
+ state.clone_from(&results[bb]);
Self::Direction::apply_effects_in_block(
&mut self,
@@ -292,7 +290,7 @@ fn iterate_to_fixpoint<'mir>(
bb,
&body[bb],
|target: BasicBlock, state: &Self::Domain| {
- let set_changed = entry_states[target].join(state);
+ let set_changed = results[target].join(state);
if set_changed {
dirty_queue.insert(target);
}
@@ -300,16 +298,14 @@ fn iterate_to_fixpoint<'mir>(
);
}
- let mut results = Results { analysis: self, entry_states };
-
if tcx.sess.opts.unstable_opts.dump_mir_dataflow {
- let res = write_graphviz_results(tcx, body, &mut results, pass_name);
+ let res = write_graphviz_results(tcx, body, &mut self, &results, pass_name);
if let Err(e) = res {
error!("Failed to write graphviz dataflow results: {}", e);
}
}
- results
+ AnalysisAndResults { analysis: self, results }
}
}
diff --git a/compiler/rustc_mir_dataflow/src/framework/results.rs b/compiler/rustc_mir_dataflow/src/framework/results.rs
index 93dfc06..7b7e981 100644
--- a/compiler/rustc_mir_dataflow/src/framework/results.rs
+++ b/compiler/rustc_mir_dataflow/src/framework/results.rs
@@ -1,63 +1,30 @@
//! Dataflow analysis results.
use rustc_index::IndexVec;
-use rustc_middle::mir::{BasicBlock, Body, traversal};
+use rustc_middle::mir::{BasicBlock, Body};
-use super::{Analysis, ResultsCursor, ResultsVisitor, visit_results};
-use crate::framework::cursor::ResultsHandle;
+use super::{Analysis, ResultsCursor};
-pub type EntryStates<'tcx, A> = IndexVec<BasicBlock, <A as Analysis<'tcx>>::Domain>;
+/// The results of a dataflow analysis that has converged to fixpoint. It only holds the domain
+/// values at the entry of each basic block. Domain values in other parts of the block are
+/// recomputed on the fly by visitors (i.e. `ResultsCursor`, or `ResultsVisitor` impls).
+pub type Results<D> = IndexVec<BasicBlock, D>;
-/// A dataflow analysis that has converged to fixpoint. It only holds the domain values at the
-/// entry of each basic block. Domain values in other parts of the block are recomputed on the fly
-/// by visitors (i.e. `ResultsCursor`, or `ResultsVisitor` impls).
-#[derive(Clone)]
-pub struct Results<'tcx, A>
+/// Utility type used in a few places where it's convenient to bundle an analysis with its results.
+pub struct AnalysisAndResults<'tcx, A>
where
A: Analysis<'tcx>,
{
pub analysis: A,
- pub entry_states: EntryStates<'tcx, A>,
+ pub results: Results<A::Domain>,
}
-impl<'tcx, A> Results<'tcx, A>
+impl<'tcx, A> AnalysisAndResults<'tcx, A>
where
A: Analysis<'tcx>,
{
- /// Creates a `ResultsCursor` that mutably borrows the `Results`, which is appropriate when the
- /// `Results` is also used outside the cursor.
- pub fn as_results_cursor<'mir>(
- &'mir mut self,
- body: &'mir Body<'tcx>,
- ) -> ResultsCursor<'mir, 'tcx, A> {
- ResultsCursor::new(body, ResultsHandle::BorrowedMut(self))
- }
-
- /// Creates a `ResultsCursor` that takes ownership of the `Results`.
+ /// Creates a `ResultsCursor` that takes ownership of `self`.
pub fn into_results_cursor<'mir>(self, body: &'mir Body<'tcx>) -> ResultsCursor<'mir, 'tcx, A> {
- ResultsCursor::new(body, ResultsHandle::Owned(self))
- }
-
- /// Gets the dataflow state for the given block.
- pub fn entry_set_for_block(&self, block: BasicBlock) -> &A::Domain {
- &self.entry_states[block]
- }
-
- pub fn visit_with<'mir>(
- &mut self,
- body: &'mir Body<'tcx>,
- blocks: impl IntoIterator<Item = BasicBlock>,
- vis: &mut impl ResultsVisitor<'tcx, A>,
- ) {
- visit_results(body, blocks, self, vis)
- }
-
- pub fn visit_reachable_with<'mir>(
- &mut self,
- body: &'mir Body<'tcx>,
- vis: &mut impl ResultsVisitor<'tcx, A>,
- ) {
- let blocks = traversal::reachable(body);
- visit_results(body, blocks.map(|(bb, _)| bb), self, vis)
+ ResultsCursor::new_owning(body, self.analysis, self.results)
}
}
diff --git a/compiler/rustc_mir_dataflow/src/framework/tests.rs b/compiler/rustc_mir_dataflow/src/framework/tests.rs
index ae0f117..8602bb5 100644
--- a/compiler/rustc_mir_dataflow/src/framework/tests.rs
+++ b/compiler/rustc_mir_dataflow/src/framework/tests.rs
@@ -79,7 +79,7 @@ fn mock_body<'tcx>() -> mir::Body<'tcx> {
///
/// The `102` in the block's entry set is derived from the basic block index and ensures that the
/// expected state is unique across all basic blocks. Remember, it is generated by
-/// `mock_entry_states`, not from actually running `MockAnalysis` to fixpoint.
+/// `mock_results`, not from actually running `MockAnalysis` to fixpoint.
struct MockAnalysis<'tcx, D> {
body: &'tcx mir::Body<'tcx>,
dir: PhantomData<D>,
@@ -96,7 +96,7 @@ fn mock_entry_set(&self, bb: BasicBlock) -> DenseBitSet<usize> {
ret
}
- fn mock_entry_states(&self) -> IndexVec<BasicBlock, DenseBitSet<usize>> {
+ fn mock_results(&self) -> IndexVec<BasicBlock, DenseBitSet<usize>> {
let empty = self.bottom_value(self.body);
let mut ret = IndexVec::from_elem(empty, &self.body.basic_blocks);
@@ -255,7 +255,7 @@ fn test_cursor<D: Direction>(analysis: MockAnalysis<'_, D>) {
let body = analysis.body;
let mut cursor =
- Results { entry_states: analysis.mock_entry_states(), analysis }.into_results_cursor(body);
+ AnalysisAndResults { results: analysis.mock_results(), analysis }.into_results_cursor(body);
cursor.allow_unreachable();
diff --git a/compiler/rustc_mir_dataflow/src/framework/visitor.rs b/compiler/rustc_mir_dataflow/src/framework/visitor.rs
index c9fdf46..fbb9e41 100644
--- a/compiler/rustc_mir_dataflow/src/framework/visitor.rs
+++ b/compiler/rustc_mir_dataflow/src/framework/visitor.rs
@@ -1,4 +1,4 @@
-use rustc_middle::mir::{self, BasicBlock, Location};
+use rustc_middle::mir::{self, BasicBlock, Location, traversal};
use super::{Analysis, Direction, Results};
@@ -7,12 +7,13 @@
pub fn visit_results<'mir, 'tcx, A>(
body: &'mir mir::Body<'tcx>,
blocks: impl IntoIterator<Item = BasicBlock>,
- results: &mut Results<'tcx, A>,
+ analysis: &mut A,
+ results: &Results<A::Domain>,
vis: &mut impl ResultsVisitor<'tcx, A>,
) where
A: Analysis<'tcx>,
{
- let mut state = results.analysis.bottom_value(body);
+ let mut state = analysis.bottom_value(body);
#[cfg(debug_assertions)]
let reachable_blocks = mir::traversal::reachable_as_bitset(body);
@@ -22,10 +23,24 @@ pub fn visit_results<'mir, 'tcx, A>(
assert!(reachable_blocks.contains(block));
let block_data = &body[block];
- A::Direction::visit_results_in_block(&mut state, block, block_data, results, vis);
+ state.clone_from(&results[block]);
+ A::Direction::visit_results_in_block(&mut state, block, block_data, analysis, vis);
}
}
+/// Like `visit_results`, but only for reachable blocks.
+pub fn visit_reachable_results<'mir, 'tcx, A>(
+ body: &'mir mir::Body<'tcx>,
+ analysis: &mut A,
+ results: &Results<A::Domain>,
+ vis: &mut impl ResultsVisitor<'tcx, A>,
+) where
+ A: Analysis<'tcx>,
+{
+ let blocks = traversal::reachable(body).map(|(bb, _)| bb);
+ visit_results(body, blocks, analysis, results, vis)
+}
+
/// A visitor over the results of an `Analysis`. Use this when you want to inspect domain values in
/// many or all locations; use `ResultsCursor` if you want to inspect domain values only in certain
/// locations.
@@ -38,7 +53,7 @@ fn visit_block_start(&mut self, _state: &A::Domain) {}
/// Called after the "early" effect of the given statement is applied to `state`.
fn visit_after_early_statement_effect(
&mut self,
- _results: &mut Results<'tcx, A>,
+ _analysis: &mut A,
_state: &A::Domain,
_statement: &mir::Statement<'tcx>,
_location: Location,
@@ -48,7 +63,7 @@ fn visit_after_early_statement_effect(
/// Called after the "primary" effect of the given statement is applied to `state`.
fn visit_after_primary_statement_effect(
&mut self,
- _results: &mut Results<'tcx, A>,
+ _analysis: &mut A,
_state: &A::Domain,
_statement: &mir::Statement<'tcx>,
_location: Location,
@@ -58,7 +73,7 @@ fn visit_after_primary_statement_effect(
/// Called after the "early" effect of the given terminator is applied to `state`.
fn visit_after_early_terminator_effect(
&mut self,
- _results: &mut Results<'tcx, A>,
+ _analysis: &mut A,
_state: &A::Domain,
_terminator: &mir::Terminator<'tcx>,
_location: Location,
@@ -70,7 +85,7 @@ fn visit_after_early_terminator_effect(
/// The `call_return_effect` (if one exists) will *not* be applied to `state`.
fn visit_after_primary_terminator_effect(
&mut self,
- _results: &mut Results<'tcx, A>,
+ _analysis: &mut A,
_state: &A::Domain,
_terminator: &mir::Terminator<'tcx>,
_location: Location,
diff --git a/compiler/rustc_mir_dataflow/src/lib.rs b/compiler/rustc_mir_dataflow/src/lib.rs
index 38f82b1..658fbf5 100644
--- a/compiler/rustc_mir_dataflow/src/lib.rs
+++ b/compiler/rustc_mir_dataflow/src/lib.rs
@@ -18,8 +18,8 @@
move_path_children_matching, on_all_children_bits, on_lookup_result_bits,
};
pub use self::framework::{
- Analysis, Backward, Direction, EntryStates, Forward, GenKill, JoinSemiLattice, MaybeReachable,
- Results, ResultsCursor, ResultsVisitor, fmt, graphviz, lattice, visit_results,
+ Analysis, Backward, Direction, Forward, GenKill, JoinSemiLattice, MaybeReachable, Results,
+ ResultsCursor, ResultsVisitor, fmt, graphviz, lattice, visit_reachable_results, visit_results,
};
use self::move_paths::MoveData;
diff --git a/compiler/rustc_mir_dataflow/src/points.rs b/compiler/rustc_mir_dataflow/src/points.rs
index 21590ff..70d1a34 100644
--- a/compiler/rustc_mir_dataflow/src/points.rs
+++ b/compiler/rustc_mir_dataflow/src/points.rs
@@ -98,7 +98,8 @@ pub struct PointIndex {}
pub fn save_as_intervals<'tcx, N, A>(
elements: &DenseLocationMap,
body: &mir::Body<'tcx>,
- mut results: Results<'tcx, A>,
+ mut analysis: A,
+ results: Results<A::Domain>,
) -> SparseIntervalMatrix<N, PointIndex>
where
N: Idx,
@@ -109,7 +110,8 @@ pub fn save_as_intervals<'tcx, N, A>(
visit_results(
body,
body.basic_blocks.reverse_postorder().iter().copied(),
- &mut results,
+ &mut analysis,
+ &results,
&mut visitor,
);
visitor.values
@@ -127,7 +129,7 @@ impl<'tcx, A, N> ResultsVisitor<'tcx, A> for Visitor<'_, N>
{
fn visit_after_primary_statement_effect<'mir>(
&mut self,
- _results: &mut Results<'tcx, A>,
+ _analysis: &mut A,
state: &A::Domain,
_statement: &'mir mir::Statement<'tcx>,
location: Location,
@@ -141,7 +143,7 @@ fn visit_after_primary_statement_effect<'mir>(
fn visit_after_primary_terminator_effect<'mir>(
&mut self,
- _results: &mut Results<'tcx, A>,
+ _analysis: &mut A,
state: &A::Domain,
_terminator: &'mir mir::Terminator<'tcx>,
location: Location,
diff --git a/compiler/rustc_mir_dataflow/src/rustc_peek.rs b/compiler/rustc_mir_dataflow/src/rustc_peek.rs
index 399141a..303fc76 100644
--- a/compiler/rustc_mir_dataflow/src/rustc_peek.rs
+++ b/compiler/rustc_mir_dataflow/src/rustc_peek.rs
@@ -39,23 +39,23 @@ pub fn sanity_check<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) {
let move_data = MoveData::gather_moves(body, tcx, |_| true);
if has_rustc_mir_with(tcx, def_id, sym::rustc_peek_maybe_init).is_some() {
- let flow_inits =
- MaybeInitializedPlaces::new(tcx, body, &move_data).iterate_to_fixpoint(tcx, body, None);
-
- sanity_check_via_rustc_peek(tcx, flow_inits.into_results_cursor(body));
+ let flow_inits = MaybeInitializedPlaces::new(tcx, body, &move_data)
+ .iterate_to_fixpoint(tcx, body, None)
+ .into_results_cursor(body);
+ sanity_check_via_rustc_peek(tcx, flow_inits);
}
if has_rustc_mir_with(tcx, def_id, sym::rustc_peek_maybe_uninit).is_some() {
let flow_uninits = MaybeUninitializedPlaces::new(tcx, body, &move_data)
- .iterate_to_fixpoint(tcx, body, None);
-
- sanity_check_via_rustc_peek(tcx, flow_uninits.into_results_cursor(body));
+ .iterate_to_fixpoint(tcx, body, None)
+ .into_results_cursor(body);
+ sanity_check_via_rustc_peek(tcx, flow_uninits);
}
if has_rustc_mir_with(tcx, def_id, sym::rustc_peek_liveness).is_some() {
- let flow_liveness = MaybeLiveLocals.iterate_to_fixpoint(tcx, body, None);
-
- sanity_check_via_rustc_peek(tcx, flow_liveness.into_results_cursor(body));
+ let flow_liveness =
+ MaybeLiveLocals.iterate_to_fixpoint(tcx, body, None).into_results_cursor(body);
+ sanity_check_via_rustc_peek(tcx, flow_liveness);
}
if has_rustc_mir_with(tcx, def_id, sym::stop_after_dataflow).is_some() {
diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs
index 66f106b..cddb2f8 100644
--- a/compiler/rustc_mir_transform/src/coroutine.rs
+++ b/compiler/rustc_mir_transform/src/coroutine.rs
@@ -79,7 +79,9 @@
MaybeBorrowedLocals, MaybeLiveLocals, MaybeRequiresStorage, MaybeStorageLive,
always_storage_live_locals,
};
-use rustc_mir_dataflow::{Analysis, Results, ResultsVisitor};
+use rustc_mir_dataflow::{
+ Analysis, Results, ResultsCursor, ResultsVisitor, visit_reachable_results,
+};
use rustc_span::def_id::{DefId, LocalDefId};
use rustc_span::source_map::dummy_spanned;
use rustc_span::symbol::sym;
@@ -680,18 +682,29 @@ fn locals_live_across_suspend_points<'tcx>(
.iterate_to_fixpoint(tcx, body, None)
.into_results_cursor(body);
- // Calculate the MIR locals which have been previously
- // borrowed (even if they are still active).
- let borrowed_locals_results =
- MaybeBorrowedLocals.iterate_to_fixpoint(tcx, body, Some("coroutine"));
-
- let mut borrowed_locals_cursor = borrowed_locals_results.clone().into_results_cursor(body);
+ // Calculate the MIR locals that have been previously borrowed (even if they are still active).
+ let borrowed_locals = MaybeBorrowedLocals.iterate_to_fixpoint(tcx, body, Some("coroutine"));
+ let mut borrowed_locals_analysis1 = borrowed_locals.analysis;
+ let mut borrowed_locals_analysis2 = borrowed_locals_analysis1.clone(); // trivial
+ let borrowed_locals_cursor1 = ResultsCursor::new_borrowing(
+ body,
+ &mut borrowed_locals_analysis1,
+ &borrowed_locals.results,
+ );
+ let mut borrowed_locals_cursor2 = ResultsCursor::new_borrowing(
+ body,
+ &mut borrowed_locals_analysis2,
+ &borrowed_locals.results,
+ );
// Calculate the MIR locals that we need to keep storage around for.
- let mut requires_storage_results =
- MaybeRequiresStorage::new(borrowed_locals_results.into_results_cursor(body))
- .iterate_to_fixpoint(tcx, body, None);
- let mut requires_storage_cursor = requires_storage_results.as_results_cursor(body);
+ let mut requires_storage =
+ MaybeRequiresStorage::new(borrowed_locals_cursor1).iterate_to_fixpoint(tcx, body, None);
+ let mut requires_storage_cursor = ResultsCursor::new_borrowing(
+ body,
+ &mut requires_storage.analysis,
+ &requires_storage.results,
+ );
// Calculate the liveness of MIR locals ignoring borrows.
let mut liveness =
@@ -720,8 +733,8 @@ fn locals_live_across_suspend_points<'tcx>(
// If a borrow is converted to a raw reference, we must also assume that it lives
// forever. Note that the final liveness is still bounded by the storage liveness
// of the local, which happens using the `intersect` operation below.
- borrowed_locals_cursor.seek_before_primary_effect(loc);
- live_locals.union(borrowed_locals_cursor.get());
+ borrowed_locals_cursor2.seek_before_primary_effect(loc);
+ live_locals.union(borrowed_locals_cursor2.get());
}
// Store the storage liveness for later use so we can restore the state
@@ -763,7 +776,8 @@ fn locals_live_across_suspend_points<'tcx>(
body,
&saved_locals,
always_live_locals.clone(),
- requires_storage_results,
+ &mut requires_storage.analysis,
+ &requires_storage.results,
);
LivenessInfo {
@@ -828,7 +842,8 @@ fn compute_storage_conflicts<'mir, 'tcx>(
body: &'mir Body<'tcx>,
saved_locals: &'mir CoroutineSavedLocals,
always_live_locals: DenseBitSet<Local>,
- mut requires_storage: Results<'tcx, MaybeRequiresStorage<'mir, 'tcx>>,
+ analysis: &mut MaybeRequiresStorage<'mir, 'tcx>,
+ results: &Results<DenseBitSet<Local>>,
) -> BitMatrix<CoroutineSavedLocal, CoroutineSavedLocal> {
assert_eq!(body.local_decls.len(), saved_locals.domain_size());
@@ -848,7 +863,7 @@ fn compute_storage_conflicts<'mir, 'tcx>(
eligible_storage_live: DenseBitSet::new_empty(body.local_decls.len()),
};
- requires_storage.visit_reachable_with(body, &mut visitor);
+ visit_reachable_results(body, analysis, results, &mut visitor);
let local_conflicts = visitor.local_conflicts;
@@ -891,7 +906,7 @@ impl<'a, 'tcx> ResultsVisitor<'tcx, MaybeRequiresStorage<'a, 'tcx>>
{
fn visit_after_early_statement_effect(
&mut self,
- _results: &mut Results<'tcx, MaybeRequiresStorage<'a, 'tcx>>,
+ _analysis: &mut MaybeRequiresStorage<'a, 'tcx>,
state: &DenseBitSet<Local>,
_statement: &Statement<'tcx>,
loc: Location,
@@ -901,7 +916,7 @@ fn visit_after_early_statement_effect(
fn visit_after_early_terminator_effect(
&mut self,
- _results: &mut Results<'tcx, MaybeRequiresStorage<'a, 'tcx>>,
+ _analysis: &mut MaybeRequiresStorage<'a, 'tcx>,
state: &DenseBitSet<Local>,
_terminator: &Terminator<'tcx>,
loc: Location,
diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs
index a2103a0..99b95e7 100644
--- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs
+++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs
@@ -23,7 +23,7 @@
use rustc_mir_dataflow::value_analysis::{
Map, PlaceIndex, State, TrackElem, ValueOrPlace, debug_with_context,
};
-use rustc_mir_dataflow::{Analysis, Results, ResultsVisitor};
+use rustc_mir_dataflow::{Analysis, ResultsVisitor, visit_reachable_results};
use rustc_span::DUMMY_SP;
use tracing::{debug, debug_span, instrument};
@@ -61,13 +61,14 @@ fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
let map = Map::new(tcx, body, place_limit);
// Perform the actual dataflow analysis.
- let analysis = ConstAnalysis::new(tcx, body, map);
- let mut results =
- debug_span!("analyze").in_scope(|| analysis.iterate_to_fixpoint(tcx, body, None));
+ let mut const_ = debug_span!("analyze")
+ .in_scope(|| ConstAnalysis::new(tcx, body, map).iterate_to_fixpoint(tcx, body, None));
// Collect results and patch the body afterwards.
let mut visitor = Collector::new(tcx, &body.local_decls);
- debug_span!("collect").in_scope(|| results.visit_reachable_with(body, &mut visitor));
+ debug_span!("collect").in_scope(|| {
+ visit_reachable_results(body, &mut const_.analysis, &const_.results, &mut visitor)
+ });
let mut patch = visitor.patch;
debug_span!("patch").in_scope(|| patch.visit_body_preserves_cfg(body));
}
@@ -959,10 +960,10 @@ fn try_write_constant<'tcx>(
}
impl<'tcx> ResultsVisitor<'tcx, ConstAnalysis<'_, 'tcx>> for Collector<'_, 'tcx> {
- #[instrument(level = "trace", skip(self, results, statement))]
+ #[instrument(level = "trace", skip(self, analysis, statement))]
fn visit_after_early_statement_effect(
&mut self,
- results: &mut Results<'tcx, ConstAnalysis<'_, 'tcx>>,
+ analysis: &mut ConstAnalysis<'_, 'tcx>,
state: &State<FlatSet<Scalar>>,
statement: &Statement<'tcx>,
location: Location,
@@ -972,8 +973,8 @@ fn visit_after_early_statement_effect(
OperandCollector {
state,
visitor: self,
- ecx: &mut results.analysis.ecx,
- map: &results.analysis.map,
+ ecx: &mut analysis.ecx,
+ map: &analysis.map,
}
.visit_rvalue(rvalue, location);
}
@@ -981,10 +982,10 @@ fn visit_after_early_statement_effect(
}
}
- #[instrument(level = "trace", skip(self, results, statement))]
+ #[instrument(level = "trace", skip(self, analysis, statement))]
fn visit_after_primary_statement_effect(
&mut self,
- results: &mut Results<'tcx, ConstAnalysis<'_, 'tcx>>,
+ analysis: &mut ConstAnalysis<'_, 'tcx>,
state: &State<FlatSet<Scalar>>,
statement: &Statement<'tcx>,
location: Location,
@@ -994,12 +995,9 @@ fn visit_after_primary_statement_effect(
// Don't overwrite the assignment if it already uses a constant (to keep the span).
}
StatementKind::Assign(box (place, _)) => {
- if let Some(value) = self.try_make_constant(
- &mut results.analysis.ecx,
- place,
- state,
- &results.analysis.map,
- ) {
+ if let Some(value) =
+ self.try_make_constant(&mut analysis.ecx, place, state, &analysis.map)
+ {
self.patch.assignments.insert(location, value);
}
}
@@ -1009,18 +1007,13 @@ fn visit_after_primary_statement_effect(
fn visit_after_early_terminator_effect(
&mut self,
- results: &mut Results<'tcx, ConstAnalysis<'_, 'tcx>>,
+ analysis: &mut ConstAnalysis<'_, 'tcx>,
state: &State<FlatSet<Scalar>>,
terminator: &Terminator<'tcx>,
location: Location,
) {
- OperandCollector {
- state,
- visitor: self,
- ecx: &mut results.analysis.ecx,
- map: &results.analysis.map,
- }
- .visit_terminator(terminator, location);
+ OperandCollector { state, visitor: self, ecx: &mut analysis.ecx, map: &analysis.map }
+ .visit_terminator(terminator, location);
}
}
diff --git a/compiler/rustc_mir_transform/src/dest_prop.rs b/compiler/rustc_mir_transform/src/dest_prop.rs
index 7395ad4..4c94a6c 100644
--- a/compiler/rustc_mir_transform/src/dest_prop.rs
+++ b/compiler/rustc_mir_transform/src/dest_prop.rs
@@ -171,7 +171,7 @@ fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
let live = MaybeLiveLocals.iterate_to_fixpoint(tcx, body, Some("MaybeLiveLocals-DestProp"));
let points = DenseLocationMap::new(body);
- let mut live = save_as_intervals(&points, body, live);
+ let mut live = save_as_intervals(&points, body, live.analysis, live.results);
// In order to avoid having to collect data for every single pair of locals in the body, we
// do not allow doing more than one merge for places that are derived from the same local at
diff --git a/compiler/rustc_mir_transform/src/lint_tail_expr_drop_order.rs b/compiler/rustc_mir_transform/src/lint_tail_expr_drop_order.rs
index b8cb101..75f351f 100644
--- a/compiler/rustc_mir_transform/src/lint_tail_expr_drop_order.rs
+++ b/compiler/rustc_mir_transform/src/lint_tail_expr_drop_order.rs
@@ -235,8 +235,9 @@ pub(crate) fn run_lint<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, body: &Body<
// When we encounter a DROP of some place P we only care
// about the drop if `P` may be initialized.
let move_data = MoveData::gather_moves(body, tcx, |_| true);
- let maybe_init = MaybeInitializedPlaces::new(tcx, body, &move_data);
- let mut maybe_init = maybe_init.iterate_to_fixpoint(tcx, body, None).into_results_cursor(body);
+ let mut maybe_init = MaybeInitializedPlaces::new(tcx, body, &move_data)
+ .iterate_to_fixpoint(tcx, body, None)
+ .into_results_cursor(body);
let mut block_drop_value_info =
IndexVec::from_elem_n(MovePathIndexAtBlock::Unknown, body.basic_blocks.len());
for (&block, candidates) in &bid_per_block {
diff --git a/compiler/rustc_next_trait_solver/src/delegate.rs b/compiler/rustc_next_trait_solver/src/delegate.rs
index 9e8fbd6..90a7c2e 100644
--- a/compiler/rustc_next_trait_solver/src/delegate.rs
+++ b/compiler/rustc_next_trait_solver/src/delegate.rs
@@ -39,13 +39,6 @@ fn well_formed_goals(
term: <Self::Interner as Interner>::Term,
) -> Option<Vec<Goal<Self::Interner, <Self::Interner as Interner>::Predicate>>>;
- fn clone_opaque_types_lookup_table(
- &self,
- ) -> Vec<(ty::OpaqueTypeKey<Self::Interner>, <Self::Interner as Interner>::Ty)>;
- fn clone_duplicate_opaque_types(
- &self,
- ) -> Vec<(ty::OpaqueTypeKey<Self::Interner>, <Self::Interner as Interner>::Ty)>;
-
fn make_deduplicated_outlives_constraints(
&self,
) -> Vec<ty::OutlivesPredicate<Self::Interner, <Self::Interner as Interner>::GenericArg>>;
@@ -64,20 +57,6 @@ fn instantiate_canonical_var_with_infer(
span: <Self::Interner as Interner>::Span,
universe_map: impl Fn(ty::UniverseIndex) -> ty::UniverseIndex,
) -> <Self::Interner as Interner>::GenericArg;
-
- fn register_hidden_type_in_storage(
- &self,
- opaque_type_key: ty::OpaqueTypeKey<Self::Interner>,
- hidden_ty: <Self::Interner as Interner>::Ty,
- span: <Self::Interner as Interner>::Span,
- ) -> Option<<Self::Interner as Interner>::Ty>;
- fn add_duplicate_opaque_type(
- &self,
- opaque_type_key: ty::OpaqueTypeKey<Self::Interner>,
- hidden_ty: <Self::Interner as Interner>::Ty,
- span: <Self::Interner as Interner>::Span,
- );
-
fn add_item_bounds_for_hidden_type(
&self,
def_id: <Self::Interner as Interner>::DefId,
@@ -86,7 +65,6 @@ fn add_item_bounds_for_hidden_type(
hidden_ty: <Self::Interner as Interner>::Ty,
goals: &mut Vec<Goal<Self::Interner, <Self::Interner as Interner>::Predicate>>,
);
- fn reset_opaque_types(&self);
fn fetch_eligible_assoc_item(
&self,
diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs
index 101129a..345a272 100644
--- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs
@@ -2,21 +2,24 @@
pub(super) mod structural_traits;
+use std::ops::ControlFlow;
+
use derive_where::derive_where;
use rustc_type_ir::inherent::*;
use rustc_type_ir::lang_items::TraitSolverLangItem;
use rustc_type_ir::{
- self as ty, Interner, TypeFoldable, TypeVisitableExt as _, TypingMode, Upcast as _, elaborate,
+ self as ty, Interner, TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt as _,
+ TypeVisitor, TypingMode, Upcast as _, elaborate,
};
use tracing::{debug, instrument};
-use super::has_only_region_constraints;
use super::trait_goals::TraitGoalProvenVia;
+use super::{has_only_region_constraints, inspect};
use crate::delegate::SolverDelegate;
use crate::solve::inspect::ProbeKind;
use crate::solve::{
BuiltinImplSource, CandidateSource, CanonicalResponse, Certainty, EvalCtxt, Goal, GoalSource,
- MaybeCause, NoSolution, QueryResult,
+ MaybeCause, NoSolution, ParamEnvSource, QueryResult,
};
enum AliasBoundKind {
@@ -49,18 +52,6 @@ pub(super) trait GoalKind<D, I = <D as SolverDelegate>::Interner>:
fn trait_def_id(self, cx: I) -> I::DefId;
- /// Try equating an assumption predicate against a goal's predicate. If it
- /// holds, then execute the `then` callback, which should do any additional
- /// work, then produce a response (typically by executing
- /// [`EvalCtxt::evaluate_added_goals_and_make_canonical_response`]).
- fn probe_and_match_goal_against_assumption(
- ecx: &mut EvalCtxt<'_, D>,
- source: CandidateSource<I>,
- goal: Goal<I, Self>,
- assumption: I::Clause,
- then: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResult<I>,
- ) -> Result<Candidate<I>, NoSolution>;
-
/// Consider a clause, which consists of a "assumption" and some "requirements",
/// to satisfy a goal. If the requirements hold, then attempt to satisfy our
/// goal by equating it with the assumption.
@@ -119,6 +110,67 @@ fn consider_additional_alias_assumptions(
alias_ty: ty::AliasTy<I>,
) -> Vec<Candidate<I>>;
+ fn probe_and_consider_param_env_candidate(
+ ecx: &mut EvalCtxt<'_, D>,
+ goal: Goal<I, Self>,
+ assumption: I::Clause,
+ ) -> Result<Candidate<I>, NoSolution> {
+ Self::fast_reject_assumption(ecx, goal, assumption)?;
+
+ ecx.probe(|candidate: &Result<Candidate<I>, NoSolution>| match candidate {
+ Ok(candidate) => inspect::ProbeKind::TraitCandidate {
+ source: candidate.source,
+ result: Ok(candidate.result),
+ },
+ Err(NoSolution) => inspect::ProbeKind::TraitCandidate {
+ source: CandidateSource::ParamEnv(ParamEnvSource::Global),
+ result: Err(NoSolution),
+ },
+ })
+ .enter(|ecx| {
+ Self::match_assumption(ecx, goal, assumption)?;
+ let source = ecx.characterize_param_env_assumption(goal.param_env, assumption)?;
+ Ok(Candidate {
+ source,
+ result: ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)?,
+ })
+ })
+ }
+
+ /// Try equating an assumption predicate against a goal's predicate. If it
+ /// holds, then execute the `then` callback, which should do any additional
+ /// work, then produce a response (typically by executing
+ /// [`EvalCtxt::evaluate_added_goals_and_make_canonical_response`]).
+ fn probe_and_match_goal_against_assumption(
+ ecx: &mut EvalCtxt<'_, D>,
+ source: CandidateSource<I>,
+ goal: Goal<I, Self>,
+ assumption: I::Clause,
+ then: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResult<I>,
+ ) -> Result<Candidate<I>, NoSolution> {
+ Self::fast_reject_assumption(ecx, goal, assumption)?;
+
+ ecx.probe_trait_candidate(source).enter(|ecx| {
+ Self::match_assumption(ecx, goal, assumption)?;
+ then(ecx)
+ })
+ }
+
+ /// Try to reject the assumption based off of simple heuristics, such as [`ty::ClauseKind`]
+ /// and `DefId`.
+ fn fast_reject_assumption(
+ ecx: &mut EvalCtxt<'_, D>,
+ goal: Goal<I, Self>,
+ assumption: I::Clause,
+ ) -> Result<(), NoSolution>;
+
+ /// Relate the goal and assumption.
+ fn match_assumption(
+ ecx: &mut EvalCtxt<'_, D>,
+ goal: Goal<I, Self>,
+ assumption: I::Clause,
+ ) -> Result<(), NoSolution>;
+
fn consider_impl_candidate(
ecx: &mut EvalCtxt<'_, D>,
goal: Goal<I, Self>,
@@ -500,14 +552,8 @@ fn assemble_param_env_candidates<G: GoalKind<D>>(
goal: Goal<I, G>,
candidates: &mut Vec<Candidate<I>>,
) {
- for (i, assumption) in goal.param_env.caller_bounds().iter().enumerate() {
- candidates.extend(G::probe_and_consider_implied_clause(
- self,
- CandidateSource::ParamEnv(i),
- goal,
- assumption,
- [],
- ));
+ for assumption in goal.param_env.caller_bounds().iter() {
+ candidates.extend(G::probe_and_consider_param_env_candidate(self, goal, assumption));
}
}
@@ -943,4 +989,88 @@ pub(super) fn assemble_and_merge_candidates<G: GoalKind<D>>(
}
}
}
+
+ /// Compute whether a param-env assumption is global or non-global after normalizing it.
+ ///
+ /// This is necessary because, for example, given:
+ ///
+ /// ```ignore,rust
+ /// where
+ /// T: Trait<Assoc = u32>,
+ /// i32: From<T::Assoc>,
+ /// ```
+ ///
+ /// The `i32: From<T::Assoc>` bound is non-global before normalization, but is global after.
+ /// Since the old trait solver normalized param-envs eagerly, we want to emulate this
+ /// behavior lazily.
+ fn characterize_param_env_assumption(
+ &mut self,
+ param_env: I::ParamEnv,
+ assumption: I::Clause,
+ ) -> Result<CandidateSource<I>, NoSolution> {
+ // FIXME: This should be fixed, but it also requires changing the behavior
+ // in the old solver which is currently relied on.
+ if assumption.has_bound_vars() {
+ return Ok(CandidateSource::ParamEnv(ParamEnvSource::NonGlobal));
+ }
+
+ match assumption.visit_with(&mut FindParamInClause { ecx: self, param_env }) {
+ ControlFlow::Break(Err(NoSolution)) => Err(NoSolution),
+ ControlFlow::Break(Ok(())) => Ok(CandidateSource::ParamEnv(ParamEnvSource::NonGlobal)),
+ ControlFlow::Continue(()) => Ok(CandidateSource::ParamEnv(ParamEnvSource::Global)),
+ }
+ }
+}
+
+struct FindParamInClause<'a, 'b, D: SolverDelegate<Interner = I>, I: Interner> {
+ ecx: &'a mut EvalCtxt<'b, D>,
+ param_env: I::ParamEnv,
+}
+
+impl<D, I> TypeVisitor<I> for FindParamInClause<'_, '_, D, I>
+where
+ D: SolverDelegate<Interner = I>,
+ I: Interner,
+{
+ type Result = ControlFlow<Result<(), NoSolution>>;
+
+ fn visit_binder<T: TypeFoldable<I>>(&mut self, t: &ty::Binder<I, T>) -> Self::Result {
+ self.ecx.enter_forall(t.clone(), |ecx, v| {
+ v.visit_with(&mut FindParamInClause { ecx, param_env: self.param_env })
+ })
+ }
+
+ fn visit_ty(&mut self, ty: I::Ty) -> Self::Result {
+ let Ok(ty) = self.ecx.structurally_normalize_ty(self.param_env, ty) else {
+ return ControlFlow::Break(Err(NoSolution));
+ };
+
+ if let ty::Placeholder(_) = ty.kind() {
+ ControlFlow::Break(Ok(()))
+ } else {
+ ty.super_visit_with(self)
+ }
+ }
+
+ fn visit_const(&mut self, ct: I::Const) -> Self::Result {
+ let Ok(ct) = self.ecx.structurally_normalize_const(self.param_env, ct) else {
+ return ControlFlow::Break(Err(NoSolution));
+ };
+
+ if let ty::ConstKind::Placeholder(_) = ct.kind() {
+ ControlFlow::Break(Ok(()))
+ } else {
+ ct.super_visit_with(self)
+ }
+ }
+
+ fn visit_region(&mut self, r: I::Region) -> Self::Result {
+ match self.ecx.eager_resolve_region(r).kind() {
+ ty::ReStatic | ty::ReError(_) => ControlFlow::Continue(()),
+ ty::ReVar(_) | ty::RePlaceholder(_) => ControlFlow::Break(Ok(())),
+ ty::ReErased | ty::ReEarlyParam(_) | ty::ReLateParam(_) | ty::ReBound(..) => {
+ unreachable!()
+ }
+ }
+ }
}
diff --git a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs
index 5edc777..84a83d7 100644
--- a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs
@@ -36,39 +36,38 @@ fn trait_def_id(self, _: I) -> I::DefId {
self.def_id()
}
- fn probe_and_match_goal_against_assumption(
+ fn fast_reject_assumption(
ecx: &mut EvalCtxt<'_, D>,
- source: rustc_type_ir::solve::CandidateSource<I>,
goal: Goal<I, Self>,
- assumption: <I as Interner>::Clause,
- then: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResult<I>,
- ) -> Result<Candidate<I>, NoSolution> {
+ assumption: I::Clause,
+ ) -> Result<(), NoSolution> {
if let Some(host_clause) = assumption.as_host_effect_clause() {
if host_clause.def_id() == goal.predicate.def_id()
&& host_clause.constness().satisfies(goal.predicate.constness)
{
- if !DeepRejectCtxt::relate_rigid_rigid(ecx.cx()).args_may_unify(
+ if DeepRejectCtxt::relate_rigid_rigid(ecx.cx()).args_may_unify(
goal.predicate.trait_ref.args,
host_clause.skip_binder().trait_ref.args,
) {
- return Err(NoSolution);
+ return Ok(());
}
-
- ecx.probe_trait_candidate(source).enter(|ecx| {
- let assumption_trait_pred = ecx.instantiate_binder_with_infer(host_clause);
- ecx.eq(
- goal.param_env,
- goal.predicate.trait_ref,
- assumption_trait_pred.trait_ref,
- )?;
- then(ecx)
- })
- } else {
- Err(NoSolution)
}
- } else {
- Err(NoSolution)
}
+
+ Err(NoSolution)
+ }
+
+ fn match_assumption(
+ ecx: &mut EvalCtxt<'_, D>,
+ goal: Goal<I, Self>,
+ assumption: I::Clause,
+ ) -> Result<(), NoSolution> {
+ let host_clause = assumption.as_host_effect_clause().unwrap();
+
+ let assumption_trait_pred = ecx.instantiate_binder_with_infer(host_clause);
+ ecx.eq(goal.param_env, goal.predicate.trait_ref, assumption_trait_pred.trait_ref)?;
+
+ Ok(())
}
/// Register additional assumptions for aliases corresponding to `~const` item bounds.
@@ -124,7 +123,7 @@ fn consider_additional_alias_assumptions(
fn consider_impl_candidate(
ecx: &mut EvalCtxt<'_, D>,
goal: Goal<I, Self>,
- impl_def_id: <I as Interner>::DefId,
+ impl_def_id: I::DefId,
) -> Result<Candidate<I>, NoSolution> {
let cx = ecx.cx();
@@ -178,7 +177,7 @@ fn consider_impl_candidate(
fn consider_error_guaranteed_candidate(
ecx: &mut EvalCtxt<'_, D>,
- _guar: <I as Interner>::ErrorGuaranteed,
+ _guar: I::ErrorGuaranteed,
) -> Result<Candidate<I>, NoSolution> {
ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc)
.enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes))
diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs
index 65b10e4..36f6880 100644
--- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs
@@ -132,12 +132,14 @@ pub(in crate::solve) fn evaluate_added_goals_and_make_canonical_response(
(Certainty::Yes, NestedNormalizationGoals(goals))
}
_ => {
- let certainty = shallow_certainty.unify_with(goals_certainty);
+ let certainty = shallow_certainty.and(goals_certainty);
(certainty, NestedNormalizationGoals::empty())
}
};
- if let Certainty::Maybe(cause @ MaybeCause::Overflow { .. }) = certainty {
+ if let Certainty::Maybe(cause @ MaybeCause::Overflow { keep_constraints: false, .. }) =
+ certainty
+ {
// If we have overflow, it's probable that we're substituting a type
// into itself infinitely and any partial substitutions in the query
// response are probably not useful anyways, so just return an empty
@@ -193,6 +195,7 @@ pub(in crate::solve) fn evaluate_added_goals_and_make_canonical_response(
debug!(?num_non_region_vars, "too many inference variables -> overflow");
return Ok(self.make_ambiguous_response_no_constraints(MaybeCause::Overflow {
suggest_increasing_limit: true,
+ keep_constraints: false,
}));
}
}
@@ -250,13 +253,7 @@ fn compute_external_query_constraints(
// to the `var_values`.
let opaque_types = self
.delegate
- .clone_opaque_types_lookup_table()
- .into_iter()
- .filter(|(a, _)| {
- self.predefined_opaques_in_body.opaque_types.iter().all(|(pa, _)| pa != a)
- })
- .chain(self.delegate.clone_duplicate_opaque_types())
- .collect();
+ .clone_opaque_types_added_since(self.initial_opaque_types_storage_num_entries);
ExternalConstraintsData { region_constraints, opaque_types, normalization_nested_goals }
}
diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs
index c13e730..fc5dad9 100644
--- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs
@@ -23,8 +23,7 @@
use crate::solve::search_graph::SearchGraph;
use crate::solve::{
CanonicalInput, Certainty, FIXPOINT_STEP_LIMIT, Goal, GoalEvaluationKind, GoalSource,
- HasChanged, NestedNormalizationGoals, NoSolution, PredefinedOpaquesData, QueryInput,
- QueryResult,
+ HasChanged, NestedNormalizationGoals, NoSolution, QueryInput, QueryResult,
};
pub(super) mod canonical;
@@ -99,8 +98,6 @@ pub struct EvalCtxt<'a, D, I = <D as SolverDelegate>::Interner>
current_goal_kind: CurrentGoalKind,
pub(super) var_values: CanonicalVarValues<I>,
- predefined_opaques_in_body: I::PredefinedOpaques,
-
/// The highest universe index nameable by the caller.
///
/// When we enter a new binder inside of the query we create new universes
@@ -111,6 +108,10 @@ pub struct EvalCtxt<'a, D, I = <D as SolverDelegate>::Interner>
/// if we have a coinductive cycle and because that's the only way we can return
/// new placeholders to the caller.
pub(super) max_input_universe: ty::UniverseIndex,
+ /// The opaque types from the canonical input. We only need to return opaque types
+ /// which have been added to the storage while evaluating this goal.
+ pub(super) initial_opaque_types_storage_num_entries:
+ <D::Infcx as InferCtxtLike>::OpaqueTypeStorageEntries,
pub(super) search_graph: &'a mut SearchGraph<D>,
@@ -305,10 +306,8 @@ pub(super) fn enter_root<R>(
// Only relevant when canonicalizing the response,
// which we don't do within this evaluation context.
- predefined_opaques_in_body: delegate
- .cx()
- .mk_predefined_opaques_in_body(PredefinedOpaquesData::default()),
max_input_universe: ty::UniverseIndex::ROOT,
+ initial_opaque_types_storage_num_entries: Default::default(),
variables: Default::default(),
var_values: CanonicalVarValues::dummy(),
current_goal_kind: CurrentGoalKind::Misc,
@@ -342,25 +341,10 @@ fn enter_canonical<R>(
canonical_goal_evaluation: &mut ProofTreeBuilder<D>,
f: impl FnOnce(&mut EvalCtxt<'_, D>, Goal<I, I::Predicate>) -> R,
) -> R {
- let (ref delegate, input, var_values) =
- SolverDelegate::build_with_canonical(cx, &canonical_input);
-
- let mut ecx = EvalCtxt {
- delegate,
- variables: canonical_input.canonical.variables,
- var_values,
- current_goal_kind: CurrentGoalKind::from_query_input(cx, input),
- predefined_opaques_in_body: input.predefined_opaques_in_body,
- max_input_universe: canonical_input.canonical.max_universe,
- search_graph,
- nested_goals: Default::default(),
- origin_span: I::Span::dummy(),
- tainted: Ok(()),
- inspect: canonical_goal_evaluation.new_goal_evaluation_step(var_values),
- };
+ let (ref delegate, input, var_values) = D::build_with_canonical(cx, &canonical_input);
for &(key, ty) in &input.predefined_opaques_in_body.opaque_types {
- let prev = ecx.delegate.register_hidden_type_in_storage(key, ty, ecx.origin_span);
+ let prev = delegate.register_hidden_type_in_storage(key, ty, I::Span::dummy());
// It may be possible that two entries in the opaque type storage end up
// with the same key after resolving contained inference variables.
//
@@ -373,13 +357,24 @@ fn enter_canonical<R>(
// the canonical input. This is more annoying to implement and may cause a
// perf regression, so we do it inside of the query for now.
if let Some(prev) = prev {
- debug!(?key, ?ty, ?prev, "ignore duplicate in `opaque_type_storage`");
+ debug!(?key, ?ty, ?prev, "ignore duplicate in `opaque_types_storage`");
}
}
- if !ecx.nested_goals.is_empty() {
- panic!("prepopulating opaque types shouldn't add goals: {:?}", ecx.nested_goals);
- }
+ let initial_opaque_types_storage_num_entries = delegate.opaque_types_storage_num_entries();
+ let mut ecx = EvalCtxt {
+ delegate,
+ variables: canonical_input.canonical.variables,
+ var_values,
+ current_goal_kind: CurrentGoalKind::from_query_input(cx, input),
+ max_input_universe: canonical_input.canonical.max_universe,
+ initial_opaque_types_storage_num_entries,
+ search_graph,
+ nested_goals: Default::default(),
+ origin_span: I::Span::dummy(),
+ tainted: Ok(()),
+ inspect: canonical_goal_evaluation.new_goal_evaluation_step(var_values),
+ };
let result = f(&mut ecx, input.goal);
ecx.inspect.probe_final_state(ecx.delegate, ecx.max_input_universe);
@@ -666,7 +661,7 @@ fn evaluate_added_goals_step(&mut self) -> Result<Option<Certainty>, NoSolution>
Certainty::Yes => {}
Certainty::Maybe(_) => {
self.nested_goals.push((source, with_resolved_vars));
- unchanged_certainty = unchanged_certainty.map(|c| c.unify_with(certainty));
+ unchanged_certainty = unchanged_certainty.map(|c| c.and(certainty));
}
}
} else {
@@ -680,7 +675,7 @@ fn evaluate_added_goals_step(&mut self) -> Result<Option<Certainty>, NoSolution>
Certainty::Yes => {}
Certainty::Maybe(_) => {
self.nested_goals.push((source, goal));
- unchanged_certainty = unchanged_certainty.map(|c| c.unify_with(certainty));
+ unchanged_certainty = unchanged_certainty.map(|c| c.and(certainty));
}
}
}
@@ -1000,6 +995,14 @@ pub(super) fn resolve_vars_if_possible<T>(&self, value: T) -> T
self.delegate.resolve_vars_if_possible(value)
}
+ pub(super) fn eager_resolve_region(&self, r: I::Region) -> I::Region {
+ if let ty::ReVar(vid) = r.kind() {
+ self.delegate.opportunistic_resolve_lt_var(vid)
+ } else {
+ r
+ }
+ }
+
pub(super) fn fresh_args_for_item(&mut self, def_id: I::DefId) -> I::GenericArgs {
let args = self.delegate.fresh_args_for_item(def_id);
for arg in args.iter() {
diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/probe.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/probe.rs
index 0a9e7fa..ed0cedc 100644
--- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/probe.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/probe.rs
@@ -26,32 +26,33 @@ impl<D, I, F, T> ProbeCtxt<'_, '_, D, I, F, T>
I: Interner,
{
pub(in crate::solve) fn enter(self, f: impl FnOnce(&mut EvalCtxt<'_, D>) -> T) -> T {
- let ProbeCtxt { ecx: outer_ecx, probe_kind, _result } = self;
+ let ProbeCtxt { ecx: outer, probe_kind, _result } = self;
- let delegate = outer_ecx.delegate;
- let max_input_universe = outer_ecx.max_input_universe;
- let mut nested_ecx = EvalCtxt {
+ let delegate = outer.delegate;
+ let max_input_universe = outer.max_input_universe;
+ let mut nested = EvalCtxt {
delegate,
- variables: outer_ecx.variables,
- var_values: outer_ecx.var_values,
- current_goal_kind: outer_ecx.current_goal_kind,
- predefined_opaques_in_body: outer_ecx.predefined_opaques_in_body,
+ variables: outer.variables,
+ var_values: outer.var_values,
+ current_goal_kind: outer.current_goal_kind,
max_input_universe,
- search_graph: outer_ecx.search_graph,
- nested_goals: outer_ecx.nested_goals.clone(),
- origin_span: outer_ecx.origin_span,
- tainted: outer_ecx.tainted,
- inspect: outer_ecx.inspect.take_and_enter_probe(),
+ initial_opaque_types_storage_num_entries: outer
+ .initial_opaque_types_storage_num_entries,
+ search_graph: outer.search_graph,
+ nested_goals: outer.nested_goals.clone(),
+ origin_span: outer.origin_span,
+ tainted: outer.tainted,
+ inspect: outer.inspect.take_and_enter_probe(),
};
- let r = nested_ecx.delegate.probe(|| {
- let r = f(&mut nested_ecx);
- nested_ecx.inspect.probe_final_state(delegate, max_input_universe);
+ let r = nested.delegate.probe(|| {
+ let r = f(&mut nested);
+ nested.inspect.probe_final_state(delegate, max_input_universe);
r
});
- if !nested_ecx.inspect.is_noop() {
+ if !nested.inspect.is_noop() {
let probe_kind = probe_kind(&r);
- nested_ecx.inspect.probe_kind(probe_kind);
- outer_ecx.inspect = nested_ecx.inspect.finish_probe();
+ nested.inspect.probe_kind(probe_kind);
+ outer.inspect = nested.inspect.finish_probe();
}
r
}
diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs
index c9f4fc6..8173146 100644
--- a/compiler/rustc_next_trait_solver/src/solve/mod.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs
@@ -253,16 +253,18 @@ fn try_merge_responses(
}
fn bail_with_ambiguity(&mut self, responses: &[CanonicalResponse<I>]) -> CanonicalResponse<I> {
- debug_assert!(!responses.is_empty());
- if let Certainty::Maybe(maybe_cause) =
- responses.iter().fold(Certainty::AMBIGUOUS, |certainty, response| {
- certainty.unify_with(response.value.certainty)
- })
- {
- self.make_ambiguous_response_no_constraints(maybe_cause)
- } else {
- panic!("expected flounder response to be ambiguous")
- }
+ debug_assert!(responses.len() > 1);
+ let maybe_cause = responses.iter().fold(MaybeCause::Ambiguity, |maybe_cause, response| {
+ // Pull down the certainty of `Certainty::Yes` to ambiguity when combining
+ // these responses, b/c we're combining more than one response and this we
+ // don't know which one applies.
+ let candidate = match response.value.certainty {
+ Certainty::Yes => MaybeCause::Ambiguity,
+ Certainty::Maybe(candidate) => candidate,
+ };
+ maybe_cause.or(candidate)
+ });
+ self.make_ambiguous_response_no_constraints(maybe_cause)
}
/// If we fail to merge responses we flounder and return overflow or ambiguity.
diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs
index 400b4ce..b90e34e 100644
--- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs
@@ -106,50 +106,48 @@ fn trait_def_id(self, cx: I) -> I::DefId {
self.trait_def_id(cx)
}
- fn probe_and_match_goal_against_assumption(
+ fn fast_reject_assumption(
ecx: &mut EvalCtxt<'_, D>,
- source: CandidateSource<I>,
goal: Goal<I, Self>,
assumption: I::Clause,
- then: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResult<I>,
- ) -> Result<Candidate<I>, NoSolution> {
+ ) -> Result<(), NoSolution> {
if let Some(projection_pred) = assumption.as_projection_clause() {
if projection_pred.item_def_id() == goal.predicate.def_id() {
- let cx = ecx.cx();
- if !DeepRejectCtxt::relate_rigid_rigid(ecx.cx()).args_may_unify(
+ if DeepRejectCtxt::relate_rigid_rigid(ecx.cx()).args_may_unify(
goal.predicate.alias.args,
projection_pred.skip_binder().projection_term.args,
) {
- return Err(NoSolution);
+ return Ok(());
}
- ecx.probe_trait_candidate(source).enter(|ecx| {
- let assumption_projection_pred =
- ecx.instantiate_binder_with_infer(projection_pred);
- ecx.eq(
- goal.param_env,
- goal.predicate.alias,
- assumption_projection_pred.projection_term,
- )?;
-
- ecx.instantiate_normalizes_to_term(goal, assumption_projection_pred.term);
-
- // Add GAT where clauses from the trait's definition
- // FIXME: We don't need these, since these are the type's own WF obligations.
- ecx.add_goals(
- GoalSource::AliasWellFormed,
- cx.own_predicates_of(goal.predicate.def_id())
- .iter_instantiated(cx, goal.predicate.alias.args)
- .map(|pred| goal.with(cx, pred)),
- );
-
- then(ecx)
- })
- } else {
- Err(NoSolution)
}
- } else {
- Err(NoSolution)
}
+
+ Err(NoSolution)
+ }
+
+ fn match_assumption(
+ ecx: &mut EvalCtxt<'_, D>,
+ goal: Goal<I, Self>,
+ assumption: I::Clause,
+ ) -> Result<(), NoSolution> {
+ let projection_pred = assumption.as_projection_clause().unwrap();
+
+ let assumption_projection_pred = ecx.instantiate_binder_with_infer(projection_pred);
+ ecx.eq(goal.param_env, goal.predicate.alias, assumption_projection_pred.projection_term)?;
+
+ ecx.instantiate_normalizes_to_term(goal, assumption_projection_pred.term);
+
+ // Add GAT where clauses from the trait's definition
+ // FIXME: We don't need these, since these are the type's own WF obligations.
+ let cx = ecx.cx();
+ ecx.add_goals(
+ GoalSource::AliasWellFormed,
+ cx.own_predicates_of(goal.predicate.def_id())
+ .iter_instantiated(cx, goal.predicate.alias.args)
+ .map(|pred| goal.with(cx, pred)),
+ );
+
+ Ok(())
}
fn consider_additional_alias_assumptions(
diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs
index d19249d..e3addf8 100644
--- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs
@@ -17,7 +17,7 @@
use crate::solve::inspect::ProbeKind;
use crate::solve::{
BuiltinImplSource, CandidateSource, Certainty, EvalCtxt, Goal, GoalSource, MaybeCause,
- NoSolution, QueryResult,
+ NoSolution, ParamEnvSource,
};
impl<D, I> assembly::GoalKind<D> for TraitPredicate<I>
@@ -125,39 +125,38 @@ fn consider_error_guaranteed_candidate(
.enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes))
}
- fn probe_and_match_goal_against_assumption(
+ fn fast_reject_assumption(
ecx: &mut EvalCtxt<'_, D>,
- source: CandidateSource<I>,
goal: Goal<I, Self>,
assumption: I::Clause,
- then: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResult<I>,
- ) -> Result<Candidate<I>, NoSolution> {
+ ) -> Result<(), NoSolution> {
if let Some(trait_clause) = assumption.as_trait_clause() {
if trait_clause.def_id() == goal.predicate.def_id()
&& trait_clause.polarity() == goal.predicate.polarity
{
- if !DeepRejectCtxt::relate_rigid_rigid(ecx.cx()).args_may_unify(
+ if DeepRejectCtxt::relate_rigid_rigid(ecx.cx()).args_may_unify(
goal.predicate.trait_ref.args,
trait_clause.skip_binder().trait_ref.args,
) {
- return Err(NoSolution);
+ return Ok(());
}
-
- ecx.probe_trait_candidate(source).enter(|ecx| {
- let assumption_trait_pred = ecx.instantiate_binder_with_infer(trait_clause);
- ecx.eq(
- goal.param_env,
- goal.predicate.trait_ref,
- assumption_trait_pred.trait_ref,
- )?;
- then(ecx)
- })
- } else {
- Err(NoSolution)
}
- } else {
- Err(NoSolution)
}
+
+ Err(NoSolution)
+ }
+
+ fn match_assumption(
+ ecx: &mut EvalCtxt<'_, D>,
+ goal: Goal<I, Self>,
+ assumption: I::Clause,
+ ) -> Result<(), NoSolution> {
+ let trait_clause = assumption.as_trait_clause().unwrap();
+
+ let assumption_trait_pred = ecx.instantiate_binder_with_infer(trait_clause);
+ ecx.eq(goal.param_env, goal.predicate.trait_ref, assumption_trait_pred.trait_ref)?;
+
+ Ok(())
}
fn consider_auto_trait_candidate(
@@ -1253,10 +1252,9 @@ impl<D, I> EvalCtxt<'_, D>
D: SolverDelegate<Interner = I>,
I: Interner,
{
- #[instrument(level = "debug", skip(self, goal), ret)]
+ #[instrument(level = "debug", skip(self), ret)]
pub(super) fn merge_trait_candidates(
&mut self,
- goal: Goal<I, TraitPredicate<I>>,
mut candidates: Vec<Candidate<I>>,
) -> Result<(CanonicalResponse<I>, Option<TraitGoalProvenVia>), NoSolution> {
if let TypingMode::Coherence = self.typing_mode() {
@@ -1284,21 +1282,9 @@ pub(super) fn merge_trait_candidates(
// If there are non-global where-bounds, prefer where-bounds
// (including global ones) over everything else.
- let has_non_global_where_bounds = candidates.iter().any(|c| match c.source {
- CandidateSource::ParamEnv(idx) => {
- let where_bound = goal.param_env.caller_bounds().get(idx).unwrap();
- let ty::ClauseKind::Trait(trait_pred) = where_bound.kind().skip_binder() else {
- unreachable!("expected trait-bound: {where_bound:?}");
- };
-
- if trait_pred.has_bound_vars() || !trait_pred.is_global() {
- return true;
- }
-
- false
- }
- _ => false,
- });
+ let has_non_global_where_bounds = candidates
+ .iter()
+ .any(|c| matches!(c.source, CandidateSource::ParamEnv(ParamEnvSource::NonGlobal)));
if has_non_global_where_bounds {
let where_bounds: Vec<_> = candidates
.iter()
@@ -1331,13 +1317,16 @@ pub(super) fn merge_trait_candidates(
// is still reported as being proven-via the param-env so that rigid projections
// operate correctly. Otherwise, drop all global where-bounds before merging the
// remaining candidates.
- let proven_via =
- if candidates.iter().all(|c| matches!(c.source, CandidateSource::ParamEnv(_))) {
- TraitGoalProvenVia::ParamEnv
- } else {
- candidates.retain(|c| !matches!(c.source, CandidateSource::ParamEnv(_)));
- TraitGoalProvenVia::Misc
- };
+ let proven_via = if candidates
+ .iter()
+ .all(|c| matches!(c.source, CandidateSource::ParamEnv(ParamEnvSource::Global)))
+ {
+ TraitGoalProvenVia::ParamEnv
+ } else {
+ candidates
+ .retain(|c| !matches!(c.source, CandidateSource::ParamEnv(ParamEnvSource::Global)));
+ TraitGoalProvenVia::Misc
+ };
let all_candidates: Vec<_> = candidates.into_iter().map(|c| c.result).collect();
if let Some(response) = self.try_merge_responses(&all_candidates) {
@@ -1353,7 +1342,7 @@ pub(super) fn compute_trait_goal(
goal: Goal<I, TraitPredicate<I>>,
) -> Result<(CanonicalResponse<I>, Option<TraitGoalProvenVia>), NoSolution> {
let candidates = self.assemble_and_evaluate_candidates(goal, AssembleCandidatesFrom::All);
- self.merge_trait_candidates(goal, candidates)
+ self.merge_trait_candidates(candidates)
}
fn try_stall_coroutine_witness(
diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl
index 3e953e6..3d7e0fc 100644
--- a/compiler/rustc_parse/messages.ftl
+++ b/compiler/rustc_parse/messages.ftl
@@ -715,6 +715,16 @@
parse_path_double_colon = path separator must be a double colon
.suggestion = use a double colon instead
+
+parse_path_found_attribute_in_params = `Trait(...)` syntax does not support attributes in parameters
+ .suggestion = remove the attributes
+
+parse_path_found_c_variadic_params = `Trait(...)` syntax does not support c_variadic parameters
+ .suggestion = remove the `...`
+
+parse_path_found_named_params = `Trait(...)` syntax does not support named parameters
+ .suggestion = remove the parameter name
+
parse_pattern_method_param_without_body = patterns aren't allowed in methods without bodies
.suggestion = give this argument a name or use an underscore to ignore it
diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs
index 9e5c81d..766baf6 100644
--- a/compiler/rustc_parse/src/errors.rs
+++ b/compiler/rustc_parse/src/errors.rs
@@ -1647,6 +1647,30 @@ pub(crate) struct ExpectedFnPathFoundFnKeyword {
}
#[derive(Diagnostic)]
+#[diag(parse_path_found_named_params)]
+pub(crate) struct FnPathFoundNamedParams {
+ #[primary_span]
+ #[suggestion(applicability = "machine-applicable", code = "")]
+ pub named_param_span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parse_path_found_c_variadic_params)]
+pub(crate) struct PathFoundCVariadicParams {
+ #[primary_span]
+ #[suggestion(applicability = "machine-applicable", code = "")]
+ pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parse_path_found_attribute_in_params)]
+pub(crate) struct PathFoundAttributeInParams {
+ #[primary_span]
+ #[suggestion(applicability = "machine-applicable", code = "")]
+ pub span: Span,
+}
+
+#[derive(Diagnostic)]
#[diag(parse_path_double_colon)]
pub(crate) struct PathSingleColon {
#[primary_span]
diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs
index 4be8a90..babc55c 100644
--- a/compiler/rustc_parse/src/parser/item.rs
+++ b/compiler/rustc_parse/src/parser/item.rs
@@ -2894,7 +2894,7 @@ pub(super) fn parse_fn_params(&mut self, req_name: ReqName) -> PResult<'a, ThinV
let (mut params, _) = self.parse_paren_comma_seq(|p| {
p.recover_vcs_conflict_marker();
let snapshot = p.create_snapshot_for_diagnostic();
- let param = p.parse_param_general(req_name, first_param).or_else(|e| {
+ let param = p.parse_param_general(req_name, first_param, true).or_else(|e| {
let guar = e.emit();
// When parsing a param failed, we should check to make the span of the param
// not contain '(' before it.
@@ -2922,7 +2922,13 @@ pub(super) fn parse_fn_params(&mut self, req_name: ReqName) -> PResult<'a, ThinV
/// Parses a single function parameter.
///
/// - `self` is syntactically allowed when `first_param` holds.
- fn parse_param_general(&mut self, req_name: ReqName, first_param: bool) -> PResult<'a, Param> {
+ /// - `recover_arg_parse` is used to recover from a failed argument parse.
+ pub(super) fn parse_param_general(
+ &mut self,
+ req_name: ReqName,
+ first_param: bool,
+ recover_arg_parse: bool,
+ ) -> PResult<'a, Param> {
let lo = self.token.span;
let attrs = self.parse_outer_attributes()?;
self.collect_tokens(None, attrs, ForceCollect::No, |this, attrs| {
@@ -2990,12 +2996,13 @@ fn parse_param_general(&mut self, req_name: ReqName, first_param: bool) -> PResu
// If this is a C-variadic argument and we hit an error, return the error.
Err(err) if this.token == token::DotDotDot => return Err(err),
Err(err) if this.unmatched_angle_bracket_count > 0 => return Err(err),
- // Recover from attempting to parse the argument as a type without pattern.
- Err(err) => {
+ Err(err) if recover_arg_parse => {
+ // Recover from attempting to parse the argument as a type without pattern.
err.cancel();
this.restore_snapshot(parser_snapshot_before_ty);
this.recover_arg_parse()?
}
+ Err(err) => return Err(err),
}
};
diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs
index 1093e4f..9bce2fa 100644
--- a/compiler/rustc_parse/src/parser/path.rs
+++ b/compiler/rustc_parse/src/parser/path.rs
@@ -15,7 +15,11 @@
use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign};
use super::{Parser, Restrictions, TokenType};
-use crate::errors::{self, PathSingleColon, PathTripleColon};
+use crate::ast::{PatKind, TyKind};
+use crate::errors::{
+ self, FnPathFoundNamedParams, PathFoundAttributeInParams, PathFoundCVariadicParams,
+ PathSingleColon, PathTripleColon,
+};
use crate::exp;
use crate::parser::{CommaRecoveryMode, RecoverColon, RecoverComma};
@@ -396,7 +400,28 @@ pub(super) fn parse_path_segment(
snapshot = Some(self.create_snapshot_for_diagnostic());
}
- let (inputs, _) = match self.parse_paren_comma_seq(|p| p.parse_ty()) {
+ let dcx = self.dcx();
+ let parse_params_result = self.parse_paren_comma_seq(|p| {
+ let param = p.parse_param_general(|_| false, false, false);
+ param.map(move |param| {
+ if !matches!(param.pat.kind, PatKind::Missing) {
+ dcx.emit_err(FnPathFoundNamedParams {
+ named_param_span: param.pat.span,
+ });
+ }
+ if matches!(param.ty.kind, TyKind::CVarArgs) {
+ dcx.emit_err(PathFoundCVariadicParams { span: param.pat.span });
+ }
+ if !param.attrs.is_empty() {
+ dcx.emit_err(PathFoundAttributeInParams {
+ span: param.attrs[0].span,
+ });
+ }
+ param.ty
+ })
+ });
+
+ let (inputs, _) = match parse_params_result {
Ok(output) => output,
Err(mut error) if prev_token_before_parsing == token::PathSep => {
error.span_label(
diff --git a/compiler/rustc_pattern_analysis/messages.ftl b/compiler/rustc_pattern_analysis/messages.ftl
index 41a1d95..d3a3107 100644
--- a/compiler/rustc_pattern_analysis/messages.ftl
+++ b/compiler/rustc_pattern_analysis/messages.ftl
@@ -6,6 +6,10 @@
.label = this range doesn't match `{$max}` because `..` is an exclusive range
.suggestion = use an inclusive range instead
+pattern_analysis_mixed_deref_pattern_constructors = mix of deref patterns and normal constructors
+ .deref_pattern_label = matches on the result of dereferencing `{$smart_pointer_ty}`
+ .normal_constructor_label = matches directly on `{$smart_pointer_ty}`
+
pattern_analysis_non_exhaustive_omitted_pattern = some variants are not matched explicitly
.help = ensure that all variants are matched explicitly by adding the suggested match arms
.note = the matched value is of type `{$scrut_ty}` and the `non_exhaustive_omitted_patterns` attribute was found
diff --git a/compiler/rustc_pattern_analysis/src/constructor.rs b/compiler/rustc_pattern_analysis/src/constructor.rs
index 4ce868f..f7a4931 100644
--- a/compiler/rustc_pattern_analysis/src/constructor.rs
+++ b/compiler/rustc_pattern_analysis/src/constructor.rs
@@ -696,6 +696,10 @@ pub enum Constructor<Cx: PatCx> {
F128Range(IeeeFloat<QuadS>, IeeeFloat<QuadS>, RangeEnd),
/// String literals. Strings are not quite the same as `&[u8]` so we treat them separately.
Str(Cx::StrLit),
+ /// Deref patterns (enabled by the `deref_patterns` feature) provide a way of matching on a
+ /// smart pointer ADT through its pointee. They don't directly correspond to ADT constructors,
+ /// and currently are not supported alongside them. Carries the type of the pointee.
+ DerefPattern(Cx::Ty),
/// Constants that must not be matched structurally. They are treated as black boxes for the
/// purposes of exhaustiveness: we must not inspect them, and they don't count towards making a
/// match exhaustive.
@@ -740,6 +744,7 @@ fn clone(&self) -> Self {
Constructor::F64Range(lo, hi, end) => Constructor::F64Range(*lo, *hi, *end),
Constructor::F128Range(lo, hi, end) => Constructor::F128Range(*lo, *hi, *end),
Constructor::Str(value) => Constructor::Str(value.clone()),
+ Constructor::DerefPattern(ty) => Constructor::DerefPattern(ty.clone()),
Constructor::Opaque(inner) => Constructor::Opaque(inner.clone()),
Constructor::Or => Constructor::Or,
Constructor::Never => Constructor::Never,
@@ -856,6 +861,10 @@ pub(crate) fn is_covered_by(&self, cx: &Cx, other: &Self) -> Result<bool, Cx::Er
}
(Slice(self_slice), Slice(other_slice)) => self_slice.is_covered_by(*other_slice),
+ // Deref patterns only interact with other deref patterns. Prior to usefulness analysis,
+ // we ensure they don't appear alongside any other non-wild non-opaque constructors.
+ (DerefPattern(_), DerefPattern(_)) => true,
+
// Opaque constructors don't interact with anything unless they come from the
// syntactically identical pattern.
(Opaque(self_id), Opaque(other_id)) => self_id == other_id,
@@ -932,6 +941,7 @@ pub(crate) fn fmt_fields(
F64Range(lo, hi, end) => write!(f, "{lo}{end}{hi}")?,
F128Range(lo, hi, end) => write!(f, "{lo}{end}{hi}")?,
Str(value) => write!(f, "{value:?}")?,
+ DerefPattern(_) => write!(f, "deref!({:?})", fields.next().unwrap())?,
Opaque(..) => write!(f, "<constant pattern>")?,
Or => {
for pat in fields {
@@ -1039,8 +1049,17 @@ pub fn split<'a>(
let mut missing = Vec::new();
// Constructors in `ctors`, except wildcards and opaques.
let mut seen = Vec::new();
+ // If we see a deref pattern, it must be the only non-wildcard non-opaque constructor; we
+ // ensure this prior to analysis.
+ let mut deref_pat_present = false;
for ctor in ctors.cloned() {
match ctor {
+ DerefPattern(..) => {
+ if !deref_pat_present {
+ deref_pat_present = true;
+ present.push(ctor);
+ }
+ }
Opaque(..) => present.push(ctor),
Wildcard => {} // discard wildcards
_ => seen.push(ctor),
@@ -1048,6 +1067,9 @@ pub fn split<'a>(
}
match self {
+ _ if deref_pat_present => {
+ // Deref patterns are the only constructor; nothing is missing.
+ }
ConstructorSet::Struct { empty } => {
if !seen.is_empty() {
present.push(Struct);
diff --git a/compiler/rustc_pattern_analysis/src/errors.rs b/compiler/rustc_pattern_analysis/src/errors.rs
index e60930d..156ba97 100644
--- a/compiler/rustc_pattern_analysis/src/errors.rs
+++ b/compiler/rustc_pattern_analysis/src/errors.rs
@@ -1,5 +1,5 @@
use rustc_errors::{Diag, EmissionGuarantee, Subdiagnostic};
-use rustc_macros::{LintDiagnostic, Subdiagnostic};
+use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
use rustc_middle::ty::Ty;
use rustc_span::Span;
@@ -133,3 +133,15 @@ pub(crate) struct NonExhaustiveOmittedPatternLintOnArm {
pub lint_level: &'static str,
pub lint_name: &'static str,
}
+
+#[derive(Diagnostic)]
+#[diag(pattern_analysis_mixed_deref_pattern_constructors)]
+pub(crate) struct MixedDerefPatternConstructors<'tcx> {
+ #[primary_span]
+ pub spans: Vec<Span>,
+ pub smart_pointer_ty: Ty<'tcx>,
+ #[label(pattern_analysis_deref_pattern_label)]
+ pub deref_pattern_label: Span,
+ #[label(pattern_analysis_normal_constructor_label)]
+ pub normal_constructor_label: Span,
+}
diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs
index 7c12f69..a89d01d 100644
--- a/compiler/rustc_pattern_analysis/src/rustc.rs
+++ b/compiler/rustc_pattern_analysis/src/rustc.rs
@@ -269,6 +269,7 @@ fn reveal_and_alloc<'a, 'tcx>(
}
_ => bug!("bad slice pattern {:?} {:?}", ctor, ty),
},
+ DerefPattern(pointee_ty) => reveal_and_alloc(cx, once(pointee_ty.inner())),
Bool(..) | IntRange(..) | F16Range(..) | F32Range(..) | F64Range(..)
| F128Range(..) | Str(..) | Opaque(..) | Never | NonExhaustive | Hidden | Missing
| PrivateUninhabited | Wildcard => &[],
@@ -296,7 +297,7 @@ pub(crate) fn ctor_arity(&self, ctor: &Constructor<'p, 'tcx>, ty: RevealedTy<'tc
}
_ => bug!("Unexpected type for constructor `{ctor:?}`: {ty:?}"),
},
- Ref => 1,
+ Ref | DerefPattern(_) => 1,
Slice(slice) => slice.arity(),
Bool(..) | IntRange(..) | F16Range(..) | F32Range(..) | F64Range(..)
| F128Range(..) | Str(..) | Opaque(..) | Never | NonExhaustive | Hidden | Missing
@@ -493,11 +494,15 @@ pub fn lower_pat(&self, pat: &'p Pat<'tcx>) -> DeconstructedPat<'p, 'tcx> {
),
};
}
- PatKind::DerefPattern { .. } => {
- // FIXME(deref_patterns): At least detect that `box _` is irrefutable.
- fields = vec![];
- arity = 0;
- ctor = Opaque(OpaqueId::new());
+ PatKind::DerefPattern { subpattern, .. } => {
+ // NB(deref_patterns): This assumes the deref pattern is matching on a trusted
+ // `DerefPure` type. If the `Deref` impl isn't trusted, exhaustiveness must take
+ // into account that multiple calls to deref may return different results. Hence
+ // multiple deref! patterns cannot be exhaustive together unless each is exhaustive
+ // by itself.
+ fields = vec![self.lower_pat(subpattern).at_index(0)];
+ arity = 1;
+ ctor = DerefPattern(cx.reveal_opaque_ty(subpattern.ty));
}
PatKind::Leaf { subpatterns } | PatKind::Variant { subpatterns, .. } => {
match ty.kind() {
@@ -874,6 +879,7 @@ pub fn print_witness_pat(&self, pat: &WitnessPat<'p, 'tcx>) -> String {
print::write_ref_like(&mut s, pat.ty().inner(), &print(&pat.fields[0])).unwrap();
s
}
+ DerefPattern(_) => format!("deref!({})", print(&pat.fields[0])),
Slice(slice) => {
let (prefix_len, has_dot_dot) = match slice.kind {
SliceKind::FixedLen(len) => (len, false),
@@ -1100,6 +1106,14 @@ pub fn analyze_match<'p, 'tcx>(
scrut_ty: Ty<'tcx>,
) -> Result<UsefulnessReport<'p, 'tcx>, ErrorGuaranteed> {
let scrut_ty = tycx.reveal_opaque_ty(scrut_ty);
+
+ // The analysis doesn't support deref patterns mixed with normal constructors; error if present.
+ // FIXME(deref_patterns): This only needs to run when a deref pattern was found during lowering.
+ if tycx.tcx.features().deref_patterns() {
+ let pat_column = PatternColumn::new(arms);
+ detect_mixed_deref_pat_ctors(tycx, &pat_column)?;
+ }
+
let scrut_validity = PlaceValidity::from_bool(tycx.known_valid_scrutinee);
let report = compute_match_usefulness(
tycx,
@@ -1119,6 +1133,51 @@ pub fn analyze_match<'p, 'tcx>(
Ok(report)
}
+// FIXME(deref_patterns): Currently it's the responsibility of the frontend (rustc or rust-analyzer)
+// to ensure that deref patterns don't appear in the same column as normal constructors. Deref
+// patterns aren't currently implemented in rust-analyzer, but should they be, the columnwise check
+// here could be made generic and shared between frontends.
+fn detect_mixed_deref_pat_ctors<'p, 'tcx>(
+ cx: &RustcPatCtxt<'p, 'tcx>,
+ column: &PatternColumn<'p, RustcPatCtxt<'p, 'tcx>>,
+) -> Result<(), ErrorGuaranteed> {
+ let Some(&ty) = column.head_ty() else {
+ return Ok(());
+ };
+
+ // Check for a mix of deref patterns and normal constructors.
+ let mut normal_ctor_span = None;
+ let mut deref_pat_span = None;
+ for pat in column.iter() {
+ match pat.ctor() {
+ // The analysis can handle mixing deref patterns with wildcards and opaque patterns.
+ Wildcard | Opaque(_) => {}
+ DerefPattern(_) => deref_pat_span = Some(pat.data().span),
+ // Nothing else can be compared to deref patterns in `Constructor::is_covered_by`.
+ _ => normal_ctor_span = Some(pat.data().span),
+ }
+ }
+ if let Some(normal_constructor_label) = normal_ctor_span
+ && let Some(deref_pattern_label) = deref_pat_span
+ {
+ return Err(cx.tcx.dcx().emit_err(errors::MixedDerefPatternConstructors {
+ spans: vec![deref_pattern_label, normal_constructor_label],
+ smart_pointer_ty: ty.inner(),
+ deref_pattern_label,
+ normal_constructor_label,
+ }));
+ }
+
+ // Specialize and recurse into the patterns' fields.
+ let set = column.analyze_ctors(cx, &ty)?;
+ for ctor in set.present {
+ for specialized_column in column.specialize(cx, &ty, &ctor).iter() {
+ detect_mixed_deref_pat_ctors(cx, specialized_column)?;
+ }
+ }
+ Ok(())
+}
+
struct RecursiveOpaque {
def_id: DefId,
}
diff --git a/compiler/rustc_pattern_analysis/src/usefulness.rs b/compiler/rustc_pattern_analysis/src/usefulness.rs
index 11ebbea..53638f2 100644
--- a/compiler/rustc_pattern_analysis/src/usefulness.rs
+++ b/compiler/rustc_pattern_analysis/src/usefulness.rs
@@ -702,6 +702,7 @@
//! - `ui/consts/const_in_pattern`
//! - `ui/rfc-2008-non-exhaustive`
//! - `ui/half-open-range-patterns`
+//! - `ui/pattern/deref-patterns`
//! - probably many others
//!
//! I (Nadrieril) prefer to put new tests in `ui/pattern/usefulness` unless there's a specific
@@ -866,7 +867,8 @@ fn is_known_valid(self) -> bool {
/// inside `&` and union fields where validity is reset to `MaybeInvalid`.
fn specialize<Cx: PatCx>(self, ctor: &Constructor<Cx>) -> Self {
// We preserve validity except when we go inside a reference or a union field.
- if matches!(ctor, Constructor::Ref | Constructor::UnionField) {
+ if matches!(ctor, Constructor::Ref | Constructor::DerefPattern(_) | Constructor::UnionField)
+ {
// Validity of `x: &T` does not imply validity of `*x: T`.
MaybeInvalid
} else {
diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs
index f7f354d..47831f2 100644
--- a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs
+++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs
@@ -722,6 +722,7 @@ fn encode_ty_name(tcx: TyCtxt<'_>, def_id: DefId) -> String {
| hir::definitions::DefPathData::Use
| hir::definitions::DefPathData::GlobalAsm
| hir::definitions::DefPathData::MacroNs(..)
+ | hir::definitions::DefPathData::OpaqueLifetime(..)
| hir::definitions::DefPathData::LifetimeNs(..)
| hir::definitions::DefPathData::AnonAssocTy(..) => {
bug!("encode_ty_name: unexpected `{:?}`", disambiguated_data.data);
diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs
index 1f45440..4a99ce0 100644
--- a/compiler/rustc_symbol_mangling/src/v0.rs
+++ b/compiler/rustc_symbol_mangling/src/v0.rs
@@ -890,6 +890,7 @@ fn path_append(
| DefPathData::Impl
| DefPathData::MacroNs(_)
| DefPathData::LifetimeNs(_)
+ | DefPathData::OpaqueLifetime(_)
| DefPathData::AnonAssocTy(..) => {
bug!("symbol_names: unexpected DefPathData: {:?}", disambiguated_data.data)
}
diff --git a/compiler/rustc_trait_selection/src/solve/delegate.rs b/compiler/rustc_trait_selection/src/solve/delegate.rs
index 87b8db5..3601c2c 100644
--- a/compiler/rustc_trait_selection/src/solve/delegate.rs
+++ b/compiler/rustc_trait_selection/src/solve/delegate.rs
@@ -104,25 +104,6 @@ fn well_formed_goals(
.map(|obligations| obligations.into_iter().map(|obligation| obligation.as_goal()).collect())
}
- fn clone_opaque_types_lookup_table(&self) -> Vec<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)> {
- self.0
- .inner
- .borrow_mut()
- .opaque_types()
- .iter_lookup_table()
- .map(|(k, h)| (k, h.ty))
- .collect()
- }
- fn clone_duplicate_opaque_types(&self) -> Vec<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)> {
- self.0
- .inner
- .borrow_mut()
- .opaque_types()
- .iter_duplicate_entries()
- .map(|(k, h)| (k, h.ty))
- .collect()
- }
-
fn make_deduplicated_outlives_constraints(
&self,
) -> Vec<ty::OutlivesPredicate<'tcx, ty::GenericArg<'tcx>>> {
@@ -168,30 +149,6 @@ fn instantiate_canonical_var_with_infer(
self.0.instantiate_canonical_var(span, cv_info, universe_map)
}
- fn register_hidden_type_in_storage(
- &self,
- opaque_type_key: ty::OpaqueTypeKey<'tcx>,
- hidden_ty: Ty<'tcx>,
- span: Span,
- ) -> Option<Ty<'tcx>> {
- self.0.register_hidden_type_in_storage(
- opaque_type_key,
- ty::OpaqueHiddenType { span, ty: hidden_ty },
- )
- }
- fn add_duplicate_opaque_type(
- &self,
- opaque_type_key: ty::OpaqueTypeKey<'tcx>,
- hidden_ty: Ty<'tcx>,
- span: Span,
- ) {
- self.0
- .inner
- .borrow_mut()
- .opaque_types()
- .add_duplicate(opaque_type_key, ty::OpaqueHiddenType { span, ty: hidden_ty })
- }
-
fn add_item_bounds_for_hidden_type(
&self,
def_id: DefId,
@@ -203,10 +160,6 @@ fn add_item_bounds_for_hidden_type(
self.0.add_item_bounds_for_hidden_type(def_id, args, param_env, hidden_ty, goals);
}
- fn reset_opaque_types(&self) {
- let _ = self.take_opaque_types();
- }
-
fn fetch_eligible_assoc_item(
&self,
goal_trait_ref: ty::TraitRef<'tcx>,
diff --git a/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs
index 2d445dd..f64cd5f 100644
--- a/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs
+++ b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs
@@ -99,7 +99,13 @@ pub(super) fn fulfillment_error_for_stalled<'tcx>(
Ok((_, Certainty::Maybe(MaybeCause::Ambiguity))) => {
(FulfillmentErrorCode::Ambiguity { overflow: None }, true)
}
- Ok((_, Certainty::Maybe(MaybeCause::Overflow { suggest_increasing_limit }))) => (
+ Ok((
+ _,
+ Certainty::Maybe(MaybeCause::Overflow {
+ suggest_increasing_limit,
+ keep_constraints: _,
+ }),
+ )) => (
FulfillmentErrorCode::Ambiguity { overflow: Some(suggest_increasing_limit) },
// Don't look into overflows because we treat overflows weirdly anyways.
// We discard the inference constraints from overflowing goals, so
diff --git a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs
index 24b8700..9795655 100644
--- a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs
+++ b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs
@@ -382,7 +382,7 @@ fn new(
if let Some(term_hack) = normalizes_to_term_hack {
infcx
.probe(|_| term_hack.constrain(infcx, DUMMY_SP, uncanonicalized_goal.param_env))
- .map(|certainty| ok.value.certainty.unify_with(certainty))
+ .map(|certainty| ok.value.certainty.and(certainty))
} else {
Ok(ok.value.certainty)
}
diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs
index a54eb80..eb34cb1 100644
--- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs
@@ -9,7 +9,7 @@
pub use rustc_middle::traits::query::NormalizationResult;
use rustc_middle::ty::{
self, FallibleTypeFolder, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable, TypeSuperVisitable,
- TypeVisitable, TypeVisitableExt, TypeVisitor, TypingMode,
+ TypeVisitableExt, TypeVisitor, TypingMode,
};
use rustc_span::DUMMY_SP;
use tracing::{debug, info, instrument};
@@ -127,7 +127,7 @@ struct MaxEscapingBoundVarVisitor {
}
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for MaxEscapingBoundVarVisitor {
- fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(&mut self, t: &ty::Binder<'tcx, T>) {
+ fn visit_binder<T: TypeFoldable<TyCtxt<'tcx>>>(&mut self, t: &ty::Binder<'tcx, T>) {
self.outer_index.shift_in(1);
t.super_visit_with(self);
self.outer_index.shift_out(1);
diff --git a/compiler/rustc_ty_utils/src/lib.rs b/compiler/rustc_ty_utils/src/lib.rs
index ea0f6b8..f79b6d4 100644
--- a/compiler/rustc_ty_utils/src/lib.rs
+++ b/compiler/rustc_ty_utils/src/lib.rs
@@ -29,10 +29,10 @@
mod instance;
mod layout;
mod needs_drop;
+mod nested_bodies;
mod opaque_types;
mod representability;
pub mod sig_types;
-mod stalled_generators;
mod structural_match;
mod ty;
@@ -51,5 +51,5 @@ pub fn provide(providers: &mut Providers) {
ty::provide(providers);
instance::provide(providers);
structural_match::provide(providers);
- stalled_generators::provide(providers);
+ nested_bodies::provide(providers);
}
diff --git a/compiler/rustc_ty_utils/src/nested_bodies.rs b/compiler/rustc_ty_utils/src/nested_bodies.rs
new file mode 100644
index 0000000..7c74d8e
--- /dev/null
+++ b/compiler/rustc_ty_utils/src/nested_bodies.rs
@@ -0,0 +1,34 @@
+use rustc_hir as hir;
+use rustc_hir::def_id::{DefId, LocalDefId};
+use rustc_hir::intravisit::Visitor;
+use rustc_middle::query::Providers;
+use rustc_middle::ty::{self, TyCtxt};
+
+fn nested_bodies_within<'tcx>(tcx: TyCtxt<'tcx>, item: LocalDefId) -> &'tcx ty::List<LocalDefId> {
+ let body = tcx.hir_body_owned_by(item);
+ let mut collector =
+ NestedBodiesVisitor { tcx, root_def_id: item.to_def_id(), nested_bodies: vec![] };
+ collector.visit_body(body);
+ tcx.mk_local_def_ids(&collector.nested_bodies)
+}
+
+struct NestedBodiesVisitor<'tcx> {
+ tcx: TyCtxt<'tcx>,
+ root_def_id: DefId,
+ nested_bodies: Vec<LocalDefId>,
+}
+
+impl<'tcx> Visitor<'tcx> for NestedBodiesVisitor<'tcx> {
+ fn visit_nested_body(&mut self, id: hir::BodyId) {
+ let body_def_id = self.tcx.hir_body_owner_def_id(id);
+ if self.tcx.typeck_root_def_id(body_def_id.to_def_id()) == self.root_def_id {
+ self.nested_bodies.push(body_def_id);
+ let body = self.tcx.hir_body(id);
+ self.visit_body(body);
+ }
+ }
+}
+
+pub(super) fn provide(providers: &mut Providers) {
+ *providers = Providers { nested_bodies_within, ..*providers };
+}
diff --git a/compiler/rustc_ty_utils/src/stalled_generators.rs b/compiler/rustc_ty_utils/src/stalled_generators.rs
deleted file mode 100644
index 8b45e8b..0000000
--- a/compiler/rustc_ty_utils/src/stalled_generators.rs
+++ /dev/null
@@ -1,54 +0,0 @@
-use rustc_hir as hir;
-use rustc_hir::def_id::{DefId, LocalDefId};
-use rustc_hir::intravisit;
-use rustc_hir::intravisit::Visitor;
-use rustc_middle::query::Providers;
-use rustc_middle::ty::{self, TyCtxt};
-
-fn stalled_generators_within<'tcx>(
- tcx: TyCtxt<'tcx>,
- item: LocalDefId,
-) -> &'tcx ty::List<LocalDefId> {
- if !tcx.next_trait_solver_globally() {
- return ty::List::empty();
- }
-
- let body = tcx.hir_body_owned_by(item);
- let mut collector =
- StalledGeneratorVisitor { tcx, root_def_id: item.to_def_id(), stalled_coroutines: vec![] };
- collector.visit_body(body);
- tcx.mk_local_def_ids(&collector.stalled_coroutines)
-}
-
-struct StalledGeneratorVisitor<'tcx> {
- tcx: TyCtxt<'tcx>,
- root_def_id: DefId,
- stalled_coroutines: Vec<LocalDefId>,
-}
-
-impl<'tcx> Visitor<'tcx> for StalledGeneratorVisitor<'tcx> {
- fn visit_nested_body(&mut self, id: hir::BodyId) {
- if self.tcx.typeck_root_def_id(self.tcx.hir_body_owner_def_id(id).to_def_id())
- == self.root_def_id
- {
- let body = self.tcx.hir_body(id);
- self.visit_body(body);
- }
- }
-
- fn visit_expr(&mut self, ex: &'tcx hir::Expr<'tcx>) {
- if let hir::ExprKind::Closure(&hir::Closure {
- def_id,
- kind: hir::ClosureKind::Coroutine(_),
- ..
- }) = ex.kind
- {
- self.stalled_coroutines.push(def_id);
- }
- intravisit::walk_expr(self, ex);
- }
-}
-
-pub(super) fn provide(providers: &mut Providers) {
- *providers = Providers { stalled_generators_within, ..*providers };
-}
diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs
index 31d69ee..0c49ddf 100644
--- a/compiler/rustc_ty_utils/src/ty.rs
+++ b/compiler/rustc_ty_utils/src/ty.rs
@@ -7,7 +7,8 @@
use rustc_middle::bug;
use rustc_middle::query::Providers;
use rustc_middle::ty::{
- self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor, Upcast, fold_regions,
+ self, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitor, Upcast,
+ fold_regions,
};
use rustc_span::DUMMY_SP;
use rustc_span::def_id::{CRATE_DEF_ID, DefId, LocalDefId};
@@ -185,7 +186,7 @@ struct ImplTraitInTraitFinder<'a, 'tcx> {
}
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ImplTraitInTraitFinder<'_, 'tcx> {
- fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(&mut self, binder: &ty::Binder<'tcx, T>) {
+ fn visit_binder<T: TypeFoldable<TyCtxt<'tcx>>>(&mut self, binder: &ty::Binder<'tcx, T>) {
self.depth.shift_in(1);
binder.super_visit_with(self);
self.depth.shift_out(1);
diff --git a/compiler/rustc_type_ir/src/binder.rs b/compiler/rustc_type_ir/src/binder.rs
index 27ea4e2..000cf1e 100644
--- a/compiler/rustc_type_ir/src/binder.rs
+++ b/compiler/rustc_type_ir/src/binder.rs
@@ -128,7 +128,7 @@ fn fold_with<F: TypeFolder<I>>(self, folder: &mut F) -> Self {
}
}
-impl<I: Interner, T: TypeVisitable<I>> TypeVisitable<I> for Binder<I, T> {
+impl<I: Interner, T: TypeFoldable<I>> TypeVisitable<I> for Binder<I, T> {
fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> V::Result {
visitor.visit_binder(self)
}
@@ -147,7 +147,7 @@ fn super_fold_with<F: TypeFolder<I>>(self, folder: &mut F) -> Self {
}
}
-impl<I: Interner, T: TypeVisitable<I>> TypeSuperVisitable<I> for Binder<I, T> {
+impl<I: Interner, T: TypeFoldable<I>> TypeSuperVisitable<I> for Binder<I, T> {
fn super_visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> V::Result {
self.as_ref().skip_binder().visit_with(visitor)
}
@@ -292,7 +292,7 @@ pub fn new(bound_vars: I::BoundVarKinds) -> Self {
impl<I: Interner> TypeVisitor<I> for ValidateBoundVars<I> {
type Result = ControlFlow<()>;
- fn visit_binder<T: TypeVisitable<I>>(&mut self, t: &Binder<I, T>) -> Self::Result {
+ fn visit_binder<T: TypeFoldable<I>>(&mut self, t: &Binder<I, T>) -> Self::Result {
self.binder_index.shift_in(1);
let result = t.super_visit_with(self);
self.binder_index.shift_out(1);
diff --git a/compiler/rustc_type_ir/src/infer_ctxt.rs b/compiler/rustc_type_ir/src/infer_ctxt.rs
index 8fa56c3..c149076 100644
--- a/compiler/rustc_type_ir/src/infer_ctxt.rs
+++ b/compiler/rustc_type_ir/src/infer_ctxt.rs
@@ -1,3 +1,5 @@
+use std::fmt::Debug;
+
use derive_where::derive_where;
#[cfg(feature = "nightly")]
use rustc_macros::{Decodable_NoContext, Encodable_NoContext, HashStable_NoContext};
@@ -100,7 +102,7 @@ pub fn non_body_analysis() -> TypingMode<I> {
pub fn typeck_for_body(cx: I, body_def_id: I::LocalDefId) -> TypingMode<I> {
TypingMode::Analysis {
defining_opaque_types_and_generators: cx
- .opaque_types_and_generators_defined_by(body_def_id),
+ .opaque_types_and_coroutines_defined_by(body_def_id),
}
}
@@ -245,4 +247,32 @@ fn register_ty_outlives(
r: <Self::Interner as Interner>::Region,
span: <Self::Interner as Interner>::Span,
);
+
+ type OpaqueTypeStorageEntries: Debug + Copy + Default;
+ fn opaque_types_storage_num_entries(&self) -> Self::OpaqueTypeStorageEntries;
+ fn clone_opaque_types_lookup_table(
+ &self,
+ ) -> Vec<(ty::OpaqueTypeKey<Self::Interner>, <Self::Interner as Interner>::Ty)>;
+ fn clone_duplicate_opaque_types(
+ &self,
+ ) -> Vec<(ty::OpaqueTypeKey<Self::Interner>, <Self::Interner as Interner>::Ty)>;
+ fn clone_opaque_types_added_since(
+ &self,
+ prev_entries: Self::OpaqueTypeStorageEntries,
+ ) -> Vec<(ty::OpaqueTypeKey<Self::Interner>, <Self::Interner as Interner>::Ty)>;
+
+ fn register_hidden_type_in_storage(
+ &self,
+ opaque_type_key: ty::OpaqueTypeKey<Self::Interner>,
+ hidden_ty: <Self::Interner as Interner>::Ty,
+ span: <Self::Interner as Interner>::Span,
+ ) -> Option<<Self::Interner as Interner>::Ty>;
+ fn add_duplicate_opaque_type(
+ &self,
+ opaque_type_key: ty::OpaqueTypeKey<Self::Interner>,
+ hidden_ty: <Self::Interner as Interner>::Ty,
+ span: <Self::Interner as Interner>::Span,
+ );
+
+ fn reset_opaque_types(&self);
}
diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs
index 6410da1..0fd2d9f 100644
--- a/compiler/rustc_type_ir/src/interner.rs
+++ b/compiler/rustc_type_ir/src/interner.rs
@@ -341,7 +341,7 @@ fn anonymize_bound_vars<T: TypeFoldable<Self>>(
fn opaque_types_defined_by(self, defining_anchor: Self::LocalDefId) -> Self::LocalDefIds;
- fn opaque_types_and_generators_defined_by(
+ fn opaque_types_and_coroutines_defined_by(
self,
defining_anchor: Self::LocalDefId,
) -> Self::LocalDefIds;
diff --git a/compiler/rustc_type_ir/src/solve/mod.rs b/compiler/rustc_type_ir/src/solve/mod.rs
index 4e9b87f..2e05c23 100644
--- a/compiler/rustc_type_ir/src/solve/mod.rs
+++ b/compiler/rustc_type_ir/src/solve/mod.rs
@@ -147,9 +147,8 @@ pub enum CandidateSource<I: Interner> {
/// For a list of all traits with builtin impls, check out the
/// `EvalCtxt::assemble_builtin_impl_candidates` method.
BuiltinImpl(BuiltinImplSource),
- /// An assumption from the environment.
- ///
- /// More precisely we've used the `n-th` assumption in the `param_env`.
+ /// An assumption from the environment. Stores a [`ParamEnvSource`], since we
+ /// prefer non-global param-env candidates in candidate assembly.
///
/// ## Examples
///
@@ -160,7 +159,7 @@ pub enum CandidateSource<I: Interner> {
/// (x.clone(), x)
/// }
/// ```
- ParamEnv(usize),
+ ParamEnv(ParamEnvSource),
/// If the self type is an alias type, e.g. an opaque type or a projection,
/// we know the bounds on that alias to hold even without knowing its concrete
/// underlying type.
@@ -190,6 +189,14 @@ pub enum CandidateSource<I: Interner> {
}
#[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)]
+pub enum ParamEnvSource {
+ /// Preferred eagerly.
+ NonGlobal,
+ // Not considered unless there are non-global param-env candidates too.
+ Global,
+}
+
+#[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)]
#[cfg_attr(
feature = "nightly",
derive(HashStable_NoContext, Encodable_NoContext, Decodable_NoContext)
@@ -266,17 +273,17 @@ impl Certainty {
/// however matter for diagnostics. If `T: Foo` resulted in overflow and `T: Bar`
/// in ambiguity without changing the inference state, we still want to tell the
/// user that `T: Baz` results in overflow.
- pub fn unify_with(self, other: Certainty) -> Certainty {
+ pub fn and(self, other: Certainty) -> Certainty {
match (self, other) {
(Certainty::Yes, Certainty::Yes) => Certainty::Yes,
(Certainty::Yes, Certainty::Maybe(_)) => other,
(Certainty::Maybe(_), Certainty::Yes) => self,
- (Certainty::Maybe(a), Certainty::Maybe(b)) => Certainty::Maybe(a.unify_with(b)),
+ (Certainty::Maybe(a), Certainty::Maybe(b)) => Certainty::Maybe(a.and(b)),
}
}
pub const fn overflow(suggest_increasing_limit: bool) -> Certainty {
- Certainty::Maybe(MaybeCause::Overflow { suggest_increasing_limit })
+ Certainty::Maybe(MaybeCause::Overflow { suggest_increasing_limit, keep_constraints: false })
}
}
@@ -289,19 +296,58 @@ pub enum MaybeCause {
/// or we hit a case where we just don't bother, e.g. `?x: Trait` goals.
Ambiguity,
/// We gave up due to an overflow, most often by hitting the recursion limit.
- Overflow { suggest_increasing_limit: bool },
+ Overflow { suggest_increasing_limit: bool, keep_constraints: bool },
}
impl MaybeCause {
- fn unify_with(self, other: MaybeCause) -> MaybeCause {
+ fn and(self, other: MaybeCause) -> MaybeCause {
match (self, other) {
(MaybeCause::Ambiguity, MaybeCause::Ambiguity) => MaybeCause::Ambiguity,
(MaybeCause::Ambiguity, MaybeCause::Overflow { .. }) => other,
(MaybeCause::Overflow { .. }, MaybeCause::Ambiguity) => self,
(
- MaybeCause::Overflow { suggest_increasing_limit: a },
- MaybeCause::Overflow { suggest_increasing_limit: b },
- ) => MaybeCause::Overflow { suggest_increasing_limit: a || b },
+ MaybeCause::Overflow {
+ suggest_increasing_limit: limit_a,
+ keep_constraints: keep_a,
+ },
+ MaybeCause::Overflow {
+ suggest_increasing_limit: limit_b,
+ keep_constraints: keep_b,
+ },
+ ) => MaybeCause::Overflow {
+ suggest_increasing_limit: limit_a && limit_b,
+ keep_constraints: keep_a && keep_b,
+ },
+ }
+ }
+
+ pub fn or(self, other: MaybeCause) -> MaybeCause {
+ match (self, other) {
+ (MaybeCause::Ambiguity, MaybeCause::Ambiguity) => MaybeCause::Ambiguity,
+
+ // When combining ambiguity + overflow, we can keep constraints.
+ (
+ MaybeCause::Ambiguity,
+ MaybeCause::Overflow { suggest_increasing_limit, keep_constraints: _ },
+ ) => MaybeCause::Overflow { suggest_increasing_limit, keep_constraints: true },
+ (
+ MaybeCause::Overflow { suggest_increasing_limit, keep_constraints: _ },
+ MaybeCause::Ambiguity,
+ ) => MaybeCause::Overflow { suggest_increasing_limit, keep_constraints: true },
+
+ (
+ MaybeCause::Overflow {
+ suggest_increasing_limit: limit_a,
+ keep_constraints: keep_a,
+ },
+ MaybeCause::Overflow {
+ suggest_increasing_limit: limit_b,
+ keep_constraints: keep_b,
+ },
+ ) => MaybeCause::Overflow {
+ suggest_increasing_limit: limit_a || limit_b,
+ keep_constraints: keep_a || keep_b,
+ },
}
}
}
diff --git a/compiler/rustc_type_ir/src/ty_kind/closure.rs b/compiler/rustc_type_ir/src/ty_kind/closure.rs
index d1ca9bd..8ba985d 100644
--- a/compiler/rustc_type_ir/src/ty_kind/closure.rs
+++ b/compiler/rustc_type_ir/src/ty_kind/closure.rs
@@ -342,7 +342,7 @@ struct HasRegionsBoundAt {
// FIXME: Could be optimized to not walk into components with no escaping bound vars.
impl<I: Interner> TypeVisitor<I> for HasRegionsBoundAt {
type Result = ControlFlow<()>;
- fn visit_binder<T: TypeVisitable<I>>(&mut self, t: &ty::Binder<I, T>) -> Self::Result {
+ fn visit_binder<T: TypeFoldable<I>>(&mut self, t: &ty::Binder<I, T>) -> Self::Result {
self.binder.shift_in(1);
t.super_visit_with(self)?;
self.binder.shift_out(1);
diff --git a/compiler/rustc_type_ir/src/visit.rs b/compiler/rustc_type_ir/src/visit.rs
index 2285e0e..ccb84e2 100644
--- a/compiler/rustc_type_ir/src/visit.rs
+++ b/compiler/rustc_type_ir/src/visit.rs
@@ -52,7 +52,7 @@
use thin_vec::ThinVec;
use crate::inherent::*;
-use crate::{self as ty, Interner, TypeFlags};
+use crate::{self as ty, Interner, TypeFlags, TypeFoldable};
/// This trait is implemented for every type that can be visited,
/// providing the skeleton of the traversal.
@@ -94,7 +94,7 @@ pub trait TypeVisitor<I: Interner>: Sized {
#[cfg(not(feature = "nightly"))]
type Result: VisitorResult;
- fn visit_binder<T: TypeVisitable<I>>(&mut self, t: &ty::Binder<I, T>) -> Self::Result {
+ fn visit_binder<T: TypeFoldable<I>>(&mut self, t: &ty::Binder<I, T>) -> Self::Result {
t.super_visit_with(self)
}
@@ -401,7 +401,7 @@ fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
impl<I: Interner> TypeVisitor<I> for HasTypeFlagsVisitor {
type Result = ControlFlow<FoundFlags>;
- fn visit_binder<T: TypeVisitable<I>>(&mut self, t: &ty::Binder<I, T>) -> Self::Result {
+ fn visit_binder<T: TypeFoldable<I>>(&mut self, t: &ty::Binder<I, T>) -> Self::Result {
// If we're looking for the HAS_BINDER_VARS flag, check if the
// binder has vars. This won't be present in the binder's bound
// value, so we need to check here too.
@@ -510,7 +510,7 @@ struct HasEscapingVarsVisitor {
impl<I: Interner> TypeVisitor<I> for HasEscapingVarsVisitor {
type Result = ControlFlow<FoundEscapingVars>;
- fn visit_binder<T: TypeVisitable<I>>(&mut self, t: &ty::Binder<I, T>) -> Self::Result {
+ fn visit_binder<T: TypeFoldable<I>>(&mut self, t: &ty::Binder<I, T>) -> Self::Result {
self.outer_index.shift_in(1);
let result = t.super_visit_with(self);
self.outer_index.shift_out(1);
diff --git a/library/std/src/sys/process/unix/mod.rs b/library/std/src/sys/process/unix/mod.rs
index 2e8b38f..ee8fd8b 100644
--- a/library/std/src/sys/process/unix/mod.rs
+++ b/library/std/src/sys/process/unix/mod.rs
@@ -11,6 +11,7 @@
} else if #[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "vita", target_os = "nuttx"))] {
mod unsupported;
use unsupported as imp;
+ pub use unsupported::output;
} else {
mod unix;
use unix as imp;
diff --git a/library/test/src/lib.rs b/library/test/src/lib.rs
index acaf026..7f56d1e 100644
--- a/library/test/src/lib.rs
+++ b/library/test/src/lib.rs
@@ -98,6 +98,15 @@ pub mod test {
// The default console test runner. It accepts the command line
// arguments and a vector of test_descs.
pub fn test_main(args: &[String], tests: Vec<TestDescAndFn>, options: Option<Options>) {
+ test_main_with_exit_callback(args, tests, options, || {})
+}
+
+pub fn test_main_with_exit_callback<F: FnOnce()>(
+ args: &[String],
+ tests: Vec<TestDescAndFn>,
+ options: Option<Options>,
+ exit_callback: F,
+) {
let mut opts = match cli::parse_opts(args) {
Some(Ok(o)) => o,
Some(Err(msg)) => {
@@ -151,6 +160,7 @@ pub fn test_main(args: &[String], tests: Vec<TestDescAndFn>, options: Option<Opt
let res = console::run_tests_console(&opts, tests);
// Prevent Valgrind from reporting reachable blocks in users' unit tests.
drop(panic::take_hook());
+ exit_callback();
match res {
Ok(true) => {}
Ok(false) => process::exit(ERROR_EXIT_CODE),
diff --git a/src/doc/rustc-dev-guide/src/mir/dataflow.md b/src/doc/rustc-dev-guide/src/mir/dataflow.md
index f31da5c..85e57dd 100644
--- a/src/doc/rustc-dev-guide/src/mir/dataflow.md
+++ b/src/doc/rustc-dev-guide/src/mir/dataflow.md
@@ -148,8 +148,7 @@
```rust,ignore
let mut results = MyAnalysis::new()
- .into_engine(tcx, body, def_id)
- .iterate_to_fixpoint()
+ .iterate_to_fixpoint(tcx, body, None);
.into_results_cursor(body);
// Inspect the fixpoint state immediately before each `Drop` terminator.
diff --git a/src/doc/rustc/src/SUMMARY.md b/src/doc/rustc/src/SUMMARY.md
index cf41f5b8..a3939e5 100644
--- a/src/doc/rustc/src/SUMMARY.md
+++ b/src/doc/rustc/src/SUMMARY.md
@@ -58,6 +58,7 @@
- [thumbv7m-none-eabi](./platform-support/thumbv7m-none-eabi.md)
- [thumbv8m.base-none-eabi](./platform-support/thumbv8m.base-none-eabi.md)
- [thumbv8m.main-none-eabi\*](./platform-support/thumbv8m.main-none-eabi.md)
+ - [armv5te-unknown-linux-gnueabi](platform-support/armv5te-unknown-linux-gnueabi.md)
- [armv6k-nintendo-3ds](platform-support/armv6k-nintendo-3ds.md)
- [armv7-rtems-eabihf](platform-support/armv7-rtems-eabihf.md)
- [armv7-sony-vita-newlibeabihf](platform-support/armv7-sony-vita-newlibeabihf.md)
diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md
index 50c7ae3..60002a5 100644
--- a/src/doc/rustc/src/platform-support.md
+++ b/src/doc/rustc/src/platform-support.md
@@ -156,7 +156,7 @@
[`arm64ec-pc-windows-msvc`](platform-support/arm64ec-pc-windows-msvc.md) | ✓ | Arm64EC Windows MSVC
[`armebv7r-none-eabi`](platform-support/armv7r-none-eabi.md) | * | Bare Armv7-R, Big Endian
[`armebv7r-none-eabihf`](platform-support/armv7r-none-eabi.md) | * | Bare Armv7-R, Big Endian, hardfloat
-`armv5te-unknown-linux-gnueabi` | ✓ | Armv5TE Linux (kernel 4.4, glibc 2.23)
+[`armv5te-unknown-linux-gnueabi`](platform-support/armv5te-unknown-linux-gnueabi.md) | ✓ | Armv5TE Linux (kernel 4.4, glibc 2.23)
`armv5te-unknown-linux-musleabi` | ✓ | Armv5TE Linux with musl 1.2.3
[`armv7-linux-androideabi`](platform-support/android.md) | ✓ | Armv7-A Android
`armv7-unknown-linux-gnueabi` | ✓ | Armv7-A Linux (kernel 4.15, glibc 2.27)
diff --git a/src/doc/rustc/src/platform-support/armv5te-unknown-linux-gnueabi.md b/src/doc/rustc/src/platform-support/armv5te-unknown-linux-gnueabi.md
new file mode 100644
index 0000000..1baf104
--- /dev/null
+++ b/src/doc/rustc/src/platform-support/armv5te-unknown-linux-gnueabi.md
@@ -0,0 +1,29 @@
+# `armv5te-unknown-linux-gnueabi`
+
+**Tier: 2**
+
+This target supports Linux programs with glibc on ARMv5TE CPUs without
+floating-point units.
+
+## Target maintainers
+
+[@koalatux](https://github.com/koalatux)
+
+## Requirements
+
+The target is for cross-compilation only. Host tools are not supported.
+std is fully supported.
+
+## Building the target
+
+Because this target is tier 2, artifacts are available from rustup.
+
+## Building Rust programs
+
+For building rust programs, you might want to specify GCC as linker in
+`.cargo/config.toml` as follows:
+
+```toml
+[target.armv5te-unknown-linux-gnueabi]
+linker = "arm-linux-gnueabi-gcc"
+```
diff --git a/src/doc/style-guide/src/editions.md b/src/doc/style-guide/src/editions.md
index 19e62c4..81b7ff5 100644
--- a/src/doc/style-guide/src/editions.md
+++ b/src/doc/style-guide/src/editions.md
@@ -26,6 +26,10 @@
Not all Rust editions have corresponding changes to the Rust style. For
instance, Rust 2015, Rust 2018, and Rust 2021 all use the same style edition.
+## Rust next style edition
+
+- Never break within a nullary function call `func()` or a unit literal `()`.
+
## Rust 2024 style edition
This style guide describes the Rust 2024 style edition. The Rust 2024 style
diff --git a/src/doc/style-guide/src/expressions.md b/src/doc/style-guide/src/expressions.md
index 031e59d..9df5d7d 100644
--- a/src/doc/style-guide/src/expressions.md
+++ b/src/doc/style-guide/src/expressions.md
@@ -183,6 +183,10 @@
};
```
+## Unit literals
+
+Never break between the opening and closing parentheses of the `()` unit literal.
+
## Tuple literals
Use a single-line form where possible. Do not put spaces between the opening
@@ -377,6 +381,11 @@
Prefer not to break a line in the callee expression.
+For a function call with no arguments (a nullary function call like `func()`),
+never break within the parentheses, and never put a space between the
+parentheses. Always write a nullary function call as a single-line call, never
+a multi-line call.
+
### Single-line calls
Do not put a space between the function name and open paren, between the open
diff --git a/src/doc/unstable-book/src/language-features/deref-patterns.md b/src/doc/unstable-book/src/language-features/deref-patterns.md
index fb6df29..4c3d456 100644
--- a/src/doc/unstable-book/src/language-features/deref-patterns.md
+++ b/src/doc/unstable-book/src/language-features/deref-patterns.md
@@ -60,8 +60,7 @@
# #![feature(deref_patterns)]
# #![allow(incomplete_features)]
struct NoCopy;
-// Match exhaustiveness analysis is not yet implemented.
-let deref!(x) = Box::new(NoCopy) else { unreachable!() };
+let deref!(x) = Box::new(NoCopy);
drop::<NoCopy>(x);
```
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index bbe11bf..15890ff 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -786,7 +786,11 @@ pub(crate) fn attributes(&self, tcx: TyCtxt<'_>, cache: &Cache, is_json: bool) -
// because it isn't public API.
None
}
- _ => Some(rustc_hir_pretty::attribute_to_string(&tcx, attr)),
+ _ => Some({
+ let mut s = rustc_hir_pretty::attribute_to_string(&tcx, attr);
+ assert_eq!(s.pop(), Some('\n'));
+ s
+ }),
}
} else if attr.has_any_name(ALLOWED_ATTRIBUTES) {
Some(
diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs
index 829a9ca..0cdf2f9 100644
--- a/src/librustdoc/doctest.rs
+++ b/src/librustdoc/doctest.rs
@@ -262,11 +262,21 @@ pub(crate) fn run(dcx: DiagCtxtHandle<'_>, input: Input, options: RustdocOptions
Ok(None) => return,
Err(error) => {
eprintln!("{error}");
+ // Since some files in the temporary folder are still owned and alive, we need
+ // to manually remove the folder.
+ let _ = std::fs::remove_dir_all(temp_dir.path());
std::process::exit(1);
}
};
- run_tests(opts, &rustdoc_options, &unused_extern_reports, standalone_tests, mergeable_tests);
+ run_tests(
+ opts,
+ &rustdoc_options,
+ &unused_extern_reports,
+ standalone_tests,
+ mergeable_tests,
+ Some(temp_dir),
+ );
let compiling_test_count = compiling_test_count.load(Ordering::SeqCst);
@@ -316,6 +326,8 @@ pub(crate) fn run_tests(
unused_extern_reports: &Arc<Mutex<Vec<UnusedExterns>>>,
mut standalone_tests: Vec<test::TestDescAndFn>,
mergeable_tests: FxIndexMap<Edition, Vec<(DocTestBuilder, ScrapedDocTest)>>,
+ // We pass this argument so we can drop it manually before using `exit`.
+ mut temp_dir: Option<TempDir>,
) {
let mut test_args = Vec::with_capacity(rustdoc_options.test_args.len() + 1);
test_args.insert(0, "rustdoctest".to_string());
@@ -382,9 +394,14 @@ pub(crate) fn run_tests(
// `running 0 tests...`.
if ran_edition_tests == 0 || !standalone_tests.is_empty() {
standalone_tests.sort_by(|a, b| a.desc.name.as_slice().cmp(b.desc.name.as_slice()));
- test::test_main(&test_args, standalone_tests, None);
+ test::test_main_with_exit_callback(&test_args, standalone_tests, None, || {
+ // We ensure temp dir destructor is called.
+ std::mem::drop(temp_dir.take());
+ });
}
if nb_errors != 0 {
+ // We ensure temp dir destructor is called.
+ std::mem::drop(temp_dir);
// libtest::ERROR_EXIT_CODE is not public but it's the same value.
std::process::exit(101);
}
@@ -450,7 +467,7 @@ enum TestFailure {
}
enum DirState {
- Temp(tempfile::TempDir),
+ Temp(TempDir),
Perm(PathBuf),
}
diff --git a/src/librustdoc/doctest/markdown.rs b/src/librustdoc/doctest/markdown.rs
index 497a8d7..b3a3ce0 100644
--- a/src/librustdoc/doctest/markdown.rs
+++ b/src/librustdoc/doctest/markdown.rs
@@ -116,6 +116,7 @@ pub(crate) fn test(input: &Input, options: Options) -> Result<(), String> {
&Arc::new(Mutex::new(Vec::new())),
standalone_tests,
mergeable_tests,
+ None,
);
Ok(())
}
diff --git a/src/tools/clippy/clippy_utils/src/ty/mod.rs b/src/tools/clippy/clippy_utils/src/ty/mod.rs
index 8db9cd5..da09edd 100644
--- a/src/tools/clippy/clippy_utils/src/ty/mod.rs
+++ b/src/tools/clippy/clippy_utils/src/ty/mod.rs
@@ -20,8 +20,8 @@
use rustc_middle::ty::layout::ValidityRequirement;
use rustc_middle::ty::{
self, AdtDef, AliasTy, AssocItem, AssocTag, Binder, BoundRegion, FnSig, GenericArg, GenericArgKind, GenericArgsRef,
- GenericParamDefKind, IntTy, ParamEnv, Region, RegionKind, TraitRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable,
- TypeVisitableExt, TypeVisitor, UintTy, Upcast, VariantDef, VariantDiscr,
+ GenericParamDefKind, IntTy, ParamEnv, Region, RegionKind, TraitRef, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable,
+ TypeVisitable, TypeVisitableExt, TypeVisitor, UintTy, Upcast, VariantDef, VariantDiscr,
};
use rustc_span::symbol::Ident;
use rustc_span::{DUMMY_SP, Span, Symbol, sym};
@@ -915,7 +915,7 @@ fn visit_region(&mut self, r: Region<'tcx>) -> Self::Result {
ControlFlow::Continue(())
}
}
- fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(&mut self, t: &Binder<'tcx, T>) -> Self::Result {
+ fn visit_binder<T: TypeFoldable<TyCtxt<'tcx>>>(&mut self, t: &Binder<'tcx, T>) -> Self::Result {
self.index += 1;
let res = t.super_visit_with(self);
self.index -= 1;
diff --git a/src/tools/run-make-support/src/external_deps/cargo.rs b/src/tools/run-make-support/src/external_deps/cargo.rs
index e91d101..8da9f00 100644
--- a/src/tools/run-make-support/src/external_deps/cargo.rs
+++ b/src/tools/run-make-support/src/external_deps/cargo.rs
@@ -1,8 +1,11 @@
use crate::command::Command;
use crate::env_var;
+use crate::util::set_host_compiler_dylib_path;
/// Returns a command that can be used to invoke cargo. The cargo is provided by compiletest
/// through the `CARGO` env var.
pub fn cargo() -> Command {
- Command::new(env_var("CARGO"))
+ let mut cmd = Command::new(env_var("CARGO"));
+ set_host_compiler_dylib_path(&mut cmd);
+ cmd
}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs
index 6323d8b..068fc22 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs
@@ -301,6 +301,7 @@ pub(crate) fn hoist_witness_pat(&self, pat: &WitnessPat<'db>) -> Pat {
// ignore this issue.
Ref => PatKind::Deref { subpattern: subpatterns.next().unwrap() },
Slice(_) => unimplemented!(),
+ DerefPattern(_) => unimplemented!(),
&Str(void) => match void {},
Wildcard | NonExhaustive | Hidden | PrivateUninhabited => PatKind::Wild,
Never => PatKind::Never,
@@ -351,6 +352,7 @@ fn ctor_arity(
},
Ref => 1,
Slice(..) => unimplemented!(),
+ DerefPattern(..) => unimplemented!(),
Never | Bool(..) | IntRange(..) | F16Range(..) | F32Range(..) | F64Range(..)
| F128Range(..) | Str(..) | Opaque(..) | NonExhaustive | PrivateUninhabited
| Hidden | Missing | Wildcard => 0,
@@ -411,6 +413,7 @@ fn ctor_sub_tys(
}
},
Slice(_) => unreachable!("Found a `Slice` constructor in match checking"),
+ DerefPattern(_) => unreachable!("Found a `DerefPattern` constructor in match checking"),
Never | Bool(..) | IntRange(..) | F16Range(..) | F32Range(..) | F64Range(..)
| F128Range(..) | Str(..) | Opaque(..) | NonExhaustive | PrivateUninhabited
| Hidden | Missing | Wildcard => {
diff --git a/tests/run-make/rustdoc-tempdir-removal/compile-error.rs b/tests/run-make/rustdoc-tempdir-removal/compile-error.rs
new file mode 100644
index 0000000..66a3b3f
--- /dev/null
+++ b/tests/run-make/rustdoc-tempdir-removal/compile-error.rs
@@ -0,0 +1,5 @@
+#![doc(test(attr(deny(warnings))))]
+
+//! ```
+//! let a = 12;
+//! ```
diff --git a/tests/run-make/rustdoc-tempdir-removal/rmake.rs b/tests/run-make/rustdoc-tempdir-removal/rmake.rs
new file mode 100644
index 0000000..bd87f05
--- /dev/null
+++ b/tests/run-make/rustdoc-tempdir-removal/rmake.rs
@@ -0,0 +1,34 @@
+// This test ensures that no temporary folder is "left behind" when doctests fail for any reason.
+
+//@ only-linux
+
+use std::path::Path;
+
+use run_make_support::{path, rfs, rustdoc};
+
+fn run_doctest_and_check_tmpdir(tmp_dir: &Path, doctest: &str, edition: &str) {
+ let output =
+ rustdoc().input(doctest).env("TMPDIR", tmp_dir).arg("--test").edition(edition).run_fail();
+
+ output.assert_exit_code(101).assert_stdout_contains(
+ "test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out",
+ );
+
+ rfs::read_dir_entries(tmp_dir, |entry| {
+ panic!("Found an item inside the temporary folder: {entry:?}");
+ });
+}
+
+fn run_doctest_and_check_tmpdir_for_edition(tmp_dir: &Path, edition: &str) {
+ run_doctest_and_check_tmpdir(tmp_dir, "compile-error.rs", edition);
+ run_doctest_and_check_tmpdir(tmp_dir, "run-error.rs", edition);
+}
+
+fn main() {
+ let tmp_dir = path("tmp");
+ rfs::create_dir(&tmp_dir);
+
+ run_doctest_and_check_tmpdir_for_edition(&tmp_dir, "2018");
+ // We use the 2024 edition to check that it's also working for merged doctests.
+ run_doctest_and_check_tmpdir_for_edition(&tmp_dir, "2024");
+}
diff --git a/tests/run-make/rustdoc-tempdir-removal/run-error.rs b/tests/run-make/rustdoc-tempdir-removal/run-error.rs
new file mode 100644
index 0000000..4ac95f6
--- /dev/null
+++ b/tests/run-make/rustdoc-tempdir-removal/run-error.rs
@@ -0,0 +1,3 @@
+//! ```
+//! panic!();
+//! ```
diff --git a/tests/rustdoc-json/attrs/automatically_derived.rs b/tests/rustdoc-json/attrs/automatically_derived.rs
index 6c90d63..4e1ab3d 100644
--- a/tests/rustdoc-json/attrs/automatically_derived.rs
+++ b/tests/rustdoc-json/attrs/automatically_derived.rs
@@ -9,5 +9,5 @@ fn default() -> Self {
}
}
-//@ is '$.index[?(@.inner.impl.for.resolved_path.path == "Derive" && @.inner.impl.trait.path == "Default")].attrs' '["#[automatically_derived]\n"]'
+//@ is '$.index[?(@.inner.impl.for.resolved_path.path == "Derive" && @.inner.impl.trait.path == "Default")].attrs' '["#[automatically_derived]"]'
//@ is '$.index[?(@.inner.impl.for.resolved_path.path == "Manual" && @.inner.impl.trait.path == "Default")].attrs' '[]'
diff --git a/tests/rustdoc-json/attrs/export_name_2021.rs b/tests/rustdoc-json/attrs/export_name_2021.rs
index 4e65264..254e9f6 100644
--- a/tests/rustdoc-json/attrs/export_name_2021.rs
+++ b/tests/rustdoc-json/attrs/export_name_2021.rs
@@ -1,6 +1,6 @@
//@ edition: 2021
#![no_std]
-//@ is "$.index[?(@.name=='example')].attrs" '["#[export_name = \"altered\"]\n"]'
+//@ is "$.index[?(@.name=='example')].attrs" '["#[export_name = \"altered\"]"]'
#[export_name = "altered"]
pub extern "C" fn example() {}
diff --git a/tests/rustdoc-json/attrs/export_name_2024.rs b/tests/rustdoc-json/attrs/export_name_2024.rs
index f6a2a92..8129c10 100644
--- a/tests/rustdoc-json/attrs/export_name_2024.rs
+++ b/tests/rustdoc-json/attrs/export_name_2024.rs
@@ -4,6 +4,6 @@
// The representation of `#[unsafe(export_name = ..)]` in rustdoc in edition 2024
// is still `#[export_name = ..]` without the `unsafe` attribute wrapper.
-//@ is "$.index[?(@.name=='example')].attrs" '["#[export_name = \"altered\"]\n"]'
+//@ is "$.index[?(@.name=='example')].attrs" '["#[export_name = \"altered\"]"]'
#[unsafe(export_name = "altered")]
pub extern "C" fn example() {}
diff --git a/tests/rustdoc-json/attrs/must_use.rs b/tests/rustdoc-json/attrs/must_use.rs
index 20696dc..64df8e5 100644
--- a/tests/rustdoc-json/attrs/must_use.rs
+++ b/tests/rustdoc-json/attrs/must_use.rs
@@ -1,9 +1,9 @@
#![no_std]
-//@ is "$.index[?(@.name=='example')].attrs" '["#[must_use]\n"]'
+//@ is "$.index[?(@.name=='example')].attrs" '["#[must_use]"]'
#[must_use]
pub fn example() -> impl Iterator<Item = i64> {}
-//@ is "$.index[?(@.name=='explicit_message')].attrs" '["#[must_use = \"does nothing if you do not use it\"]\n"]'
+//@ is "$.index[?(@.name=='explicit_message')].attrs" '["#[must_use = \"does nothing if you do not use it\"]"]'
#[must_use = "does nothing if you do not use it"]
pub fn explicit_message() -> impl Iterator<Item = i64> {}
diff --git a/tests/rustdoc-json/attrs/no_mangle_2021.rs b/tests/rustdoc-json/attrs/no_mangle_2021.rs
index 10a3725..588be72 100644
--- a/tests/rustdoc-json/attrs/no_mangle_2021.rs
+++ b/tests/rustdoc-json/attrs/no_mangle_2021.rs
@@ -1,6 +1,6 @@
//@ edition: 2021
#![no_std]
-//@ is "$.index[?(@.name=='example')].attrs" '["#[no_mangle]\n"]'
+//@ is "$.index[?(@.name=='example')].attrs" '["#[no_mangle]"]'
#[no_mangle]
pub extern "C" fn example() {}
diff --git a/tests/rustdoc-json/attrs/no_mangle_2024.rs b/tests/rustdoc-json/attrs/no_mangle_2024.rs
index 8f3a14c..0d500e2 100644
--- a/tests/rustdoc-json/attrs/no_mangle_2024.rs
+++ b/tests/rustdoc-json/attrs/no_mangle_2024.rs
@@ -4,6 +4,6 @@
// The representation of `#[unsafe(no_mangle)]` in rustdoc in edition 2024
// is still `#[no_mangle]` without the `unsafe` attribute wrapper.
-//@ is "$.index[?(@.name=='example')].attrs" '["#[no_mangle]\n"]'
+//@ is "$.index[?(@.name=='example')].attrs" '["#[no_mangle]"]'
#[unsafe(no_mangle)]
pub extern "C" fn example() {}
diff --git a/tests/rustdoc-json/attrs/non_exhaustive.rs b/tests/rustdoc-json/attrs/non_exhaustive.rs
index 3064b86..b95f1a8 100644
--- a/tests/rustdoc-json/attrs/non_exhaustive.rs
+++ b/tests/rustdoc-json/attrs/non_exhaustive.rs
@@ -1,18 +1,18 @@
#![no_std]
-//@ is "$.index[?(@.name=='MyEnum')].attrs" '["#[non_exhaustive]\n"]'
+//@ is "$.index[?(@.name=='MyEnum')].attrs" '["#[non_exhaustive]"]'
#[non_exhaustive]
pub enum MyEnum {
First,
}
pub enum NonExhaustiveVariant {
- //@ is "$.index[?(@.name=='Variant')].attrs" '["#[non_exhaustive]\n"]'
+ //@ is "$.index[?(@.name=='Variant')].attrs" '["#[non_exhaustive]"]'
#[non_exhaustive]
Variant(i64),
}
-//@ is "$.index[?(@.name=='MyStruct')].attrs" '["#[non_exhaustive]\n"]'
+//@ is "$.index[?(@.name=='MyStruct')].attrs" '["#[non_exhaustive]"]'
#[non_exhaustive]
pub struct MyStruct {
pub x: i64,
diff --git a/tests/rustdoc-json/keyword_private.rs b/tests/rustdoc-json/keyword_private.rs
index 5e9a2c1..fea546c 100644
--- a/tests/rustdoc-json/keyword_private.rs
+++ b/tests/rustdoc-json/keyword_private.rs
@@ -5,7 +5,7 @@
//@ !has "$.index[?(@.name=='match')]"
//@ has "$.index[?(@.name=='foo')]"
-//@ is "$.index[?(@.name=='foo')].attrs" '["#[doc(keyword = \"match\")]\n"]'
+//@ is "$.index[?(@.name=='foo')].attrs" '["#[doc(keyword = \"match\")]"]'
//@ is "$.index[?(@.name=='foo')].docs" '"this is a test!"'
#[doc(keyword = "match")]
/// this is a test!
@@ -13,7 +13,7 @@ pub mod foo {}
//@ !has "$.index[?(@.name=='break')]"
//@ has "$.index[?(@.name=='bar')]"
-//@ is "$.index[?(@.name=='bar')].attrs" '["#[doc(keyword = \"break\")]\n"]'
+//@ is "$.index[?(@.name=='bar')].attrs" '["#[doc(keyword = \"break\")]"]'
//@ is "$.index[?(@.name=='bar')].docs" '"hello"'
#[doc(keyword = "break")]
/// hello
diff --git a/tests/ui/borrowck/regions-bound-missing-bound-in-impl.rs b/tests/ui/borrowck/regions-bound-missing-bound-in-impl.rs
index 141ad5b..7c0378e 100644
--- a/tests/ui/borrowck/regions-bound-missing-bound-in-impl.rs
+++ b/tests/ui/borrowck/regions-bound-missing-bound-in-impl.rs
@@ -17,11 +17,11 @@ pub trait Foo<'a, 't> {
impl<'a, 't> Foo<'a, 't> for &'a isize {
fn no_bound<'b:'a>(self, b: Inv<'b>) {
- //~^ ERROR lifetime parameters or bounds on method `no_bound` do not match
+ //~^ ERROR lifetime parameters do not match the trait definition
}
fn has_bound<'b>(self, b: Inv<'b>) {
- //~^ ERROR lifetime parameters or bounds on method `has_bound` do not match
+ //~^ ERROR lifetime parameters do not match the trait definition
}
fn wrong_bound1<'b,'c,'d:'a+'c>(self, b: Inv<'b>, c: Inv<'c>, d: Inv<'d>) {
@@ -40,7 +40,7 @@ fn wrong_bound1<'b,'c,'d:'a+'c>(self, b: Inv<'b>, c: Inv<'c>, d: Inv<'d>) {
}
fn wrong_bound2(self, b: Inv, c: Inv, d: Inv) {
- //~^ ERROR lifetime parameters or bounds on method `wrong_bound2` do not match the trait
+ //~^ ERROR lifetime parameters do not match the trait definition
}
fn okay_bound<'b,'c,'e:'b+'c>(self, b: Inv<'b>, c: Inv<'c>, e: Inv<'e>) {
diff --git a/tests/ui/borrowck/regions-bound-missing-bound-in-impl.stderr b/tests/ui/borrowck/regions-bound-missing-bound-in-impl.stderr
index 5f0347b..207ca57 100644
--- a/tests/ui/borrowck/regions-bound-missing-bound-in-impl.stderr
+++ b/tests/ui/borrowck/regions-bound-missing-bound-in-impl.stderr
@@ -1,20 +1,48 @@
-error[E0195]: lifetime parameters or bounds on method `no_bound` do not match the trait declaration
- --> $DIR/regions-bound-missing-bound-in-impl.rs:19:16
+error[E0195]: lifetime parameters do not match the trait definition
+ --> $DIR/regions-bound-missing-bound-in-impl.rs:19:17
|
-LL | fn no_bound<'b>(self, b: Inv<'b>);
- | ---- lifetimes in impl do not match this method in trait
-...
LL | fn no_bound<'b:'a>(self, b: Inv<'b>) {
- | ^^^^^^^ lifetimes do not match method in trait
-
-error[E0195]: lifetime parameters or bounds on method `has_bound` do not match the trait declaration
- --> $DIR/regions-bound-missing-bound-in-impl.rs:23:17
+ | ^^
|
+ = note: lifetime parameters differ in whether they are early- or late-bound
+note: `'b` differs between the trait and impl
+ --> $DIR/regions-bound-missing-bound-in-impl.rs:10:17
+ |
+LL | pub trait Foo<'a, 't> {
+ | --------------------- in this trait...
+LL | fn no_bound<'b>(self, b: Inv<'b>);
+ | ^^ `'b` is late-bound
+...
+LL | impl<'a, 't> Foo<'a, 't> for &'a isize {
+ | -------------------------------------- in this impl...
+LL | fn no_bound<'b:'a>(self, b: Inv<'b>) {
+ | ^^ -- this lifetime bound makes `'b` early-bound
+ | |
+ | `'b` is early-bound
+
+error[E0195]: lifetime parameters do not match the trait definition
+ --> $DIR/regions-bound-missing-bound-in-impl.rs:23:18
+ |
+LL | fn has_bound<'b>(self, b: Inv<'b>) {
+ | ^^
+ |
+ = note: lifetime parameters differ in whether they are early- or late-bound
+note: `'b` differs between the trait and impl
+ --> $DIR/regions-bound-missing-bound-in-impl.rs:11:18
+ |
+LL | pub trait Foo<'a, 't> {
+ | --------------------- in this trait...
+LL | fn no_bound<'b>(self, b: Inv<'b>);
LL | fn has_bound<'b:'a>(self, b: Inv<'b>);
- | ------- lifetimes in impl do not match this method in trait
+ | ^^ -- this lifetime bound makes `'b` early-bound
+ | |
+ | `'b` is early-bound
+...
+LL | impl<'a, 't> Foo<'a, 't> for &'a isize {
+ | -------------------------------------- in this impl...
...
LL | fn has_bound<'b>(self, b: Inv<'b>) {
- | ^^^^ lifetimes do not match method in trait
+ | ^^ `'b` is late-bound
error[E0308]: method not compatible with trait
--> $DIR/regions-bound-missing-bound-in-impl.rs:27:5
@@ -54,14 +82,45 @@
LL | fn wrong_bound1<'b,'c,'d:'a+'c>(self, b: Inv<'b>, c: Inv<'c>, d: Inv<'d>) {
| ^^
-error[E0195]: lifetime parameters or bounds on method `wrong_bound2` do not match the trait declaration
- --> $DIR/regions-bound-missing-bound-in-impl.rs:42:20
+error[E0195]: lifetime parameters do not match the trait definition
+ --> $DIR/regions-bound-missing-bound-in-impl.rs:42:30
|
+LL | fn wrong_bound2(self, b: Inv, c: Inv, d: Inv) {
+ | ^^^ ^^^
+ |
+ = note: lifetime parameters differ in whether they are early- or late-bound
+note: `'_` differs between the trait and impl
+ --> $DIR/regions-bound-missing-bound-in-impl.rs:13:21
+ |
+LL | pub trait Foo<'a, 't> {
+ | --------------------- in this trait...
+...
LL | fn wrong_bound2<'b,'c,'d:'a+'b>(self, b: Inv<'b>, c: Inv<'c>, d: Inv<'d>);
- | ---------------- lifetimes in impl do not match this method in trait
+ | ^^ -- this lifetime bound makes `'b` early-bound
+ | |
+ | `'b` is early-bound
+...
+LL | impl<'a, 't> Foo<'a, 't> for &'a isize {
+ | -------------------------------------- in this impl...
...
LL | fn wrong_bound2(self, b: Inv, c: Inv, d: Inv) {
- | ^ lifetimes do not match method in trait
+ | ^^^ `'_` is late-bound
+note: `'_` differs between the trait and impl
+ --> $DIR/regions-bound-missing-bound-in-impl.rs:13:27
+ |
+LL | pub trait Foo<'a, 't> {
+ | --------------------- in this trait...
+...
+LL | fn wrong_bound2<'b,'c,'d:'a+'b>(self, b: Inv<'b>, c: Inv<'c>, d: Inv<'d>);
+ | ^^ -- this lifetime bound makes `'d` early-bound
+ | |
+ | `'d` is early-bound
+...
+LL | impl<'a, 't> Foo<'a, 't> for &'a isize {
+ | -------------------------------------- in this impl...
+...
+LL | fn wrong_bound2(self, b: Inv, c: Inv, d: Inv) {
+ | ^^^ `'_` is late-bound
error[E0276]: impl has stricter requirements than trait
--> $DIR/regions-bound-missing-bound-in-impl.rs:49:26
diff --git a/tests/ui/error-codes/E0195.rs b/tests/ui/error-codes/E0195.rs
index a7e51df..66968f7 100644
--- a/tests/ui/error-codes/E0195.rs
+++ b/tests/ui/error-codes/E0195.rs
@@ -1,13 +1,25 @@
trait Trait {
+//~^ NOTE in this trait...
+//~| NOTE in this trait...
fn bar<'a,'b:'a>(x: &'a str, y: &'b str);
- //~^ NOTE lifetimes in impl do not match this associated function in trait
+ //~^ NOTE `'a` is early-bound
+ //~| NOTE this lifetime bound makes `'a` early-bound
+ //~| NOTE `'b` is early-bound
+ //~| NOTE this lifetime bound makes `'b` early-bound
}
struct Foo;
impl Trait for Foo {
- fn bar<'a,'b>(x: &'a str, y: &'b str) { //~ ERROR E0195
- //~^ NOTE lifetimes do not match associated function in trait
+//~^ NOTE in this impl...
+//~| NOTE in this impl...
+ fn bar<'a,'b>(x: &'a str, y: &'b str) {
+ //~^ ERROR E0195
+ //~| NOTE `'a` differs between the trait and impl
+ //~| NOTE `'a` is late-bound
+ //~| NOTE `'b` differs between the trait and impl
+ //~| NOTE `'b` is late-bound
+ //~| NOTE lifetime parameters differ in whether they are early- or late-bound
}
}
diff --git a/tests/ui/error-codes/E0195.stderr b/tests/ui/error-codes/E0195.stderr
index 9767dee..d0295b3 100644
--- a/tests/ui/error-codes/E0195.stderr
+++ b/tests/ui/error-codes/E0195.stderr
@@ -1,11 +1,42 @@
-error[E0195]: lifetime parameters or bounds on associated function `bar` do not match the trait declaration
- --> $DIR/E0195.rs:9:11
+error[E0195]: lifetime parameters do not match the trait definition
+ --> $DIR/E0195.rs:16:12
|
+LL | fn bar<'a,'b>(x: &'a str, y: &'b str) {
+ | ^^ ^^
+ |
+ = note: lifetime parameters differ in whether they are early- or late-bound
+note: `'a` differs between the trait and impl
+ --> $DIR/E0195.rs:4:12
+ |
+LL | trait Trait {
+ | ----------- in this trait...
+...
LL | fn bar<'a,'b:'a>(x: &'a str, y: &'b str);
- | ---------- lifetimes in impl do not match this associated function in trait
+ | ^^ -- this lifetime bound makes `'a` early-bound
+ | |
+ | `'a` is early-bound
+...
+LL | impl Trait for Foo {
+ | ------------------ in this impl...
...
LL | fn bar<'a,'b>(x: &'a str, y: &'b str) {
- | ^^^^^^^ lifetimes do not match associated function in trait
+ | ^^ `'a` is late-bound
+note: `'b` differs between the trait and impl
+ --> $DIR/E0195.rs:4:15
+ |
+LL | trait Trait {
+ | ----------- in this trait...
+...
+LL | fn bar<'a,'b:'a>(x: &'a str, y: &'b str);
+ | ^^ -- this lifetime bound makes `'b` early-bound
+ | |
+ | `'b` is early-bound
+...
+LL | impl Trait for Foo {
+ | ------------------ in this impl...
+...
+LL | fn bar<'a,'b>(x: &'a str, y: &'b str) {
+ | ^^ `'b` is late-bound
error: aborting due to 1 previous error
diff --git a/tests/ui/explain.rs b/tests/ui/explain/basic.rs
similarity index 100%
rename from tests/ui/explain.rs
rename to tests/ui/explain/basic.rs
diff --git a/tests/ui/explain.stdout b/tests/ui/explain/basic.stdout
similarity index 100%
rename from tests/ui/explain.stdout
rename to tests/ui/explain/basic.stdout
diff --git a/tests/ui/explain/error-with-no-explanation.rs b/tests/ui/explain/error-with-no-explanation.rs
new file mode 100644
index 0000000..383820e
--- /dev/null
+++ b/tests/ui/explain/error-with-no-explanation.rs
@@ -0,0 +1,3 @@
+// It's a valid error with no added explanation
+//@ compile-flags: --explain E9999
+//~? ERROR: E9999 is not a valid error code
diff --git a/tests/ui/explain/error-with-no-explanation.stderr b/tests/ui/explain/error-with-no-explanation.stderr
new file mode 100644
index 0000000..afb738c
--- /dev/null
+++ b/tests/ui/explain/error-with-no-explanation.stderr
@@ -0,0 +1,2 @@
+error: E9999 is not a valid error code
+
diff --git a/tests/ui/explain/invalid-error-code.rs b/tests/ui/explain/invalid-error-code.rs
new file mode 100644
index 0000000..114118d
--- /dev/null
+++ b/tests/ui/explain/invalid-error-code.rs
@@ -0,0 +1,2 @@
+//@ compile-flags: --explain error_code
+//~? ERROR: error_code is not a valid error code
diff --git a/tests/ui/explain/invalid-error-code.stderr b/tests/ui/explain/invalid-error-code.stderr
new file mode 100644
index 0000000..c33122e
--- /dev/null
+++ b/tests/ui/explain/invalid-error-code.stderr
@@ -0,0 +1,2 @@
+error: error_code is not a valid error code
+
diff --git a/tests/ui/explain/no-E-prefix.rs b/tests/ui/explain/no-E-prefix.rs
new file mode 100644
index 0000000..39c9122
--- /dev/null
+++ b/tests/ui/explain/no-E-prefix.rs
@@ -0,0 +1,2 @@
+//@ compile-flags: --explain 425
+//@ check-pass
diff --git a/tests/ui/explain/no-E-prefix.stdout b/tests/ui/explain/no-E-prefix.stdout
new file mode 100644
index 0000000..756b970
--- /dev/null
+++ b/tests/ui/explain/no-E-prefix.stdout
@@ -0,0 +1,57 @@
+An unresolved name was used.
+
+Erroneous code examples:
+
+```
+something_that_doesnt_exist::foo;
+// error: unresolved name `something_that_doesnt_exist::foo`
+
+// or:
+
+trait Foo {
+ fn bar() {
+ Self; // error: unresolved name `Self`
+ }
+}
+
+// or:
+
+let x = unknown_variable; // error: unresolved name `unknown_variable`
+```
+
+Please verify that the name wasn't misspelled and ensure that the
+identifier being referred to is valid for the given situation. Example:
+
+```
+enum something_that_does_exist {
+ Foo,
+}
+```
+
+Or:
+
+```
+mod something_that_does_exist {
+ pub static foo : i32 = 0i32;
+}
+
+something_that_does_exist::foo; // ok!
+```
+
+Or:
+
+```
+let unknown_variable = 12u32;
+let x = unknown_variable; // ok!
+```
+
+If the item is not defined in the current module, it must be imported using a
+`use` statement, like so:
+
+```
+use foo::bar;
+bar();
+```
+
+If the item you are importing is not defined in some super-module of the
+current module, then it must also be declared as public (e.g., `pub fn`).
diff --git a/tests/ui/explain/overflow-error-code.rs b/tests/ui/explain/overflow-error-code.rs
new file mode 100644
index 0000000..284d5cd
--- /dev/null
+++ b/tests/ui/explain/overflow-error-code.rs
@@ -0,0 +1,4 @@
+// Check that we don't crash on error codes exceeding our internal limit.
+// issue: <https://github.com/rust-lang/rust/issues/140647>
+//@ compile-flags: --explain E10000
+//~? ERROR: E10000 is not a valid error code
diff --git a/tests/ui/explain/overflow-error-code.stderr b/tests/ui/explain/overflow-error-code.stderr
new file mode 100644
index 0000000..67e584e
--- /dev/null
+++ b/tests/ui/explain/overflow-error-code.stderr
@@ -0,0 +1,2 @@
+error: E10000 is not a valid error code
+
diff --git a/tests/ui/fn/fn-trait-use-named-params-issue-140169.rs b/tests/ui/fn/fn-trait-use-named-params-issue-140169.rs
new file mode 100644
index 0000000..218450a
--- /dev/null
+++ b/tests/ui/fn/fn-trait-use-named-params-issue-140169.rs
@@ -0,0 +1,12 @@
+fn f1(_: fn(a: u8)) {}
+fn f2(_: impl Fn(u8, vvvv: u8)) {} //~ ERROR `Trait(...)` syntax does not support named parameters
+fn f3(_: impl Fn(aaaa: u8, u8)) {} //~ ERROR `Trait(...)` syntax does not support named parameters
+fn f4(_: impl Fn(aaaa: u8, vvvv: u8)) {}
+//~^ ERROR `Trait(...)` syntax does not support named parameters
+//~| ERROR `Trait(...)` syntax does not support named parameters
+fn f5(_: impl Fn(u8, ...)) {}
+//~^ ERROR `Trait(...)` syntax does not support c_variadic parameters
+fn f6(_: impl Fn(u8, #[allow(unused_attributes)] u8)) {}
+//~^ ERROR `Trait(...)` syntax does not support attributes in parameters
+
+fn main(){}
diff --git a/tests/ui/fn/fn-trait-use-named-params-issue-140169.stderr b/tests/ui/fn/fn-trait-use-named-params-issue-140169.stderr
new file mode 100644
index 0000000..b72d5b7
--- /dev/null
+++ b/tests/ui/fn/fn-trait-use-named-params-issue-140169.stderr
@@ -0,0 +1,38 @@
+error: `Trait(...)` syntax does not support named parameters
+ --> $DIR/fn-trait-use-named-params-issue-140169.rs:2:22
+ |
+LL | fn f2(_: impl Fn(u8, vvvv: u8)) {}
+ | ^^^^ help: remove the parameter name
+
+error: `Trait(...)` syntax does not support named parameters
+ --> $DIR/fn-trait-use-named-params-issue-140169.rs:3:18
+ |
+LL | fn f3(_: impl Fn(aaaa: u8, u8)) {}
+ | ^^^^ help: remove the parameter name
+
+error: `Trait(...)` syntax does not support named parameters
+ --> $DIR/fn-trait-use-named-params-issue-140169.rs:4:18
+ |
+LL | fn f4(_: impl Fn(aaaa: u8, vvvv: u8)) {}
+ | ^^^^ help: remove the parameter name
+
+error: `Trait(...)` syntax does not support named parameters
+ --> $DIR/fn-trait-use-named-params-issue-140169.rs:4:28
+ |
+LL | fn f4(_: impl Fn(aaaa: u8, vvvv: u8)) {}
+ | ^^^^ help: remove the parameter name
+
+error: `Trait(...)` syntax does not support c_variadic parameters
+ --> $DIR/fn-trait-use-named-params-issue-140169.rs:7:22
+ |
+LL | fn f5(_: impl Fn(u8, ...)) {}
+ | ^^^ help: remove the `...`
+
+error: `Trait(...)` syntax does not support attributes in parameters
+ --> $DIR/fn-trait-use-named-params-issue-140169.rs:9:22
+ |
+LL | fn f6(_: impl Fn(u8, #[allow(unused_attributes)] u8)) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the attributes
+
+error: aborting due to 6 previous errors
+
diff --git a/tests/ui/imports/reexports.stderr b/tests/ui/imports/reexports.stderr
index bf4ba47..fa05c0c 100644
--- a/tests/ui/imports/reexports.stderr
+++ b/tests/ui/imports/reexports.stderr
@@ -62,7 +62,7 @@
LL | pub use super::*;
| ^^^^^^^^
|
-note: the most public imported item is `pub(a)`
+note: the most public imported item is `pub(in crate::a)`
--> $DIR/reexports.rs:11:17
|
LL | pub use super::*;
diff --git a/tests/ui/nll/nested-bodies-in-dead-code.rs b/tests/ui/nll/nested-bodies-in-dead-code.rs
new file mode 100644
index 0000000..6ac68f5
--- /dev/null
+++ b/tests/ui/nll/nested-bodies-in-dead-code.rs
@@ -0,0 +1,28 @@
+//@ edition: 2024
+
+// Regression test for #140583. We want to borrowck nested
+// bodies even if they are in dead code. While not necessary for
+// soundness, it is desirable to error in such cases.
+
+fn main() {
+ return;
+ |x: &str| -> &'static str { x };
+ //~^ ERROR lifetime may not live long enough
+ || {
+ || {
+ let temp = 1;
+ let p: &'static u32 = &temp;
+ //~^ ERROR `temp` does not live long enough
+ };
+ };
+ const {
+ let temp = 1;
+ let p: &'static u32 = &temp;
+ //~^ ERROR `temp` does not live long enough
+ };
+ async {
+ let temp = 1;
+ let p: &'static u32 = &temp;
+ //~^ ERROR `temp` does not live long enough
+ };
+}
diff --git a/tests/ui/nll/nested-bodies-in-dead-code.stderr b/tests/ui/nll/nested-bodies-in-dead-code.stderr
new file mode 100644
index 0000000..da6ccff
--- /dev/null
+++ b/tests/ui/nll/nested-bodies-in-dead-code.stderr
@@ -0,0 +1,50 @@
+error: lifetime may not live long enough
+ --> $DIR/nested-bodies-in-dead-code.rs:9:33
+ |
+LL | |x: &str| -> &'static str { x };
+ | - ^ returning this value requires that `'1` must outlive `'static`
+ | |
+ | let's call the lifetime of this reference `'1`
+
+error[E0597]: `temp` does not live long enough
+ --> $DIR/nested-bodies-in-dead-code.rs:14:35
+ |
+LL | let temp = 1;
+ | ---- binding `temp` declared here
+LL | let p: &'static u32 = &temp;
+ | ------------ ^^^^^ borrowed value does not live long enough
+ | |
+ | type annotation requires that `temp` is borrowed for `'static`
+LL |
+LL | };
+ | - `temp` dropped here while still borrowed
+
+error[E0597]: `temp` does not live long enough
+ --> $DIR/nested-bodies-in-dead-code.rs:20:31
+ |
+LL | let temp = 1;
+ | ---- binding `temp` declared here
+LL | let p: &'static u32 = &temp;
+ | ------------ ^^^^^ borrowed value does not live long enough
+ | |
+ | type annotation requires that `temp` is borrowed for `'static`
+LL |
+LL | };
+ | - `temp` dropped here while still borrowed
+
+error[E0597]: `temp` does not live long enough
+ --> $DIR/nested-bodies-in-dead-code.rs:25:31
+ |
+LL | let temp = 1;
+ | ---- binding `temp` declared here
+LL | let p: &'static u32 = &temp;
+ | ------------ ^^^^^ borrowed value does not live long enough
+ | |
+ | type annotation requires that `temp` is borrowed for `'static`
+LL |
+LL | };
+ | - `temp` dropped here while still borrowed
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0597`.
diff --git a/tests/ui/parser/diagnostics-parenthesized-type-arguments-ice-issue-122345.rs b/tests/ui/parser/diagnostics-parenthesized-type-arguments-ice-issue-122345.rs
index 6bfe16a..db25ce4 100644
--- a/tests/ui/parser/diagnostics-parenthesized-type-arguments-ice-issue-122345.rs
+++ b/tests/ui/parser/diagnostics-parenthesized-type-arguments-ice-issue-122345.rs
@@ -2,8 +2,10 @@
fn main() {
unsafe {
- dealloc(ptr2, Layout::(x: !)(1, 1)); //~ ERROR: expected one of `!`, `(`, `)`, `+`, `,`, `::`, or `<`, found `:`
- //~^ ERROR: expected one of `.`, `;`, `?`, `}`, or an operator, found `)`
- //~| NOTE while parsing this parenthesized list of type arguments starting here
+ dealloc(ptr2, Layout::(x: !)(1, 1)); //~ ERROR `Trait(...)` syntax does not support named parameters
+ //~^ ERROR cannot find function `dealloc` in this scope [E0425]
+ //~| ERROR cannot find value `ptr2` in this scope [E0425]
+ //~| ERROR the `!` type is experimental [E0658]
+ //~| ERROR cannot find function, tuple struct or tuple variant `Layout` in this scope [E0425]
}
}
diff --git a/tests/ui/parser/diagnostics-parenthesized-type-arguments-ice-issue-122345.stderr b/tests/ui/parser/diagnostics-parenthesized-type-arguments-ice-issue-122345.stderr
index c12bf7f..a083883 100644
--- a/tests/ui/parser/diagnostics-parenthesized-type-arguments-ice-issue-122345.stderr
+++ b/tests/ui/parser/diagnostics-parenthesized-type-arguments-ice-issue-122345.stderr
@@ -1,16 +1,43 @@
-error: expected one of `!`, `(`, `)`, `+`, `,`, `::`, or `<`, found `:`
- --> $DIR/diagnostics-parenthesized-type-arguments-ice-issue-122345.rs:5:33
+error: `Trait(...)` syntax does not support named parameters
+ --> $DIR/diagnostics-parenthesized-type-arguments-ice-issue-122345.rs:5:32
|
LL | dealloc(ptr2, Layout::(x: !)(1, 1));
- | --- ^ expected one of 7 possible tokens
- | |
- | while parsing this parenthesized list of type arguments starting here
+ | ^ help: remove the parameter name
-error: expected one of `.`, `;`, `?`, `}`, or an operator, found `)`
- --> $DIR/diagnostics-parenthesized-type-arguments-ice-issue-122345.rs:5:43
+error[E0425]: cannot find function `dealloc` in this scope
+ --> $DIR/diagnostics-parenthesized-type-arguments-ice-issue-122345.rs:5:9
|
LL | dealloc(ptr2, Layout::(x: !)(1, 1));
- | ^ expected one of `.`, `;`, `?`, `}`, or an operator
+ | ^^^^^^^ not found in this scope
+ |
+help: consider importing this function
+ |
+LL + use std::alloc::dealloc;
+ |
-error: aborting due to 2 previous errors
+error[E0425]: cannot find value `ptr2` in this scope
+ --> $DIR/diagnostics-parenthesized-type-arguments-ice-issue-122345.rs:5:17
+ |
+LL | dealloc(ptr2, Layout::(x: !)(1, 1));
+ | ^^^^ not found in this scope
+error[E0658]: the `!` type is experimental
+ --> $DIR/diagnostics-parenthesized-type-arguments-ice-issue-122345.rs:5:35
+ |
+LL | dealloc(ptr2, Layout::(x: !)(1, 1));
+ | ^
+ |
+ = note: see issue #35121 <https://github.com/rust-lang/rust/issues/35121> for more information
+ = help: add `#![feature(never_type)]` to the crate attributes to enable
+ = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0425]: cannot find function, tuple struct or tuple variant `Layout` in this scope
+ --> $DIR/diagnostics-parenthesized-type-arguments-ice-issue-122345.rs:5:23
+ |
+LL | dealloc(ptr2, Layout::(x: !)(1, 1));
+ | ^^^^^^ not found in this scope
+
+error: aborting due to 5 previous errors
+
+Some errors have detailed explanations: E0425, E0658.
+For more information about an error, try `rustc --explain E0425`.
diff --git a/tests/ui/parser/issues/issue-103748-ICE-wrong-braces.rs b/tests/ui/parser/issues/issue-103748-ICE-wrong-braces.rs
index 1c28c06..60dd88e 100644
--- a/tests/ui/parser/issues/issue-103748-ICE-wrong-braces.rs
+++ b/tests/ui/parser/issues/issue-103748-ICE-wrong-braces.rs
@@ -2,7 +2,4 @@
struct Apple((Apple, Option(Banana ? Citron)));
//~^ ERROR invalid `?` in type
-//~| ERROR expected one of `)` or `,`, found `Citron`
-//~| ERROR cannot find type `Citron` in this scope [E0412]
-//~| ERROR parenthesized type parameters may only be used with a `Fn` trait [E0214]
-//~| ERROR `Apple` has infinite size
+//~| ERROR unexpected token: `Citron`
diff --git a/tests/ui/parser/issues/issue-103748-ICE-wrong-braces.stderr b/tests/ui/parser/issues/issue-103748-ICE-wrong-braces.stderr
index 97a73b4..c92535c 100644
--- a/tests/ui/parser/issues/issue-103748-ICE-wrong-braces.stderr
+++ b/tests/ui/parser/issues/issue-103748-ICE-wrong-braces.stderr
@@ -10,44 +10,11 @@
LL + struct Apple((Apple, Option(Option<Banana > Citron)));
|
-error: expected one of `)` or `,`, found `Citron`
+error: unexpected token: `Citron`
--> $DIR/issue-103748-ICE-wrong-braces.rs:3:38
|
LL | struct Apple((Apple, Option(Banana ? Citron)));
- | -^^^^^^ expected one of `)` or `,`
- | |
- | help: missing `,`
+ | ^^^^^^ unexpected token after this
-error[E0412]: cannot find type `Citron` in this scope
- --> $DIR/issue-103748-ICE-wrong-braces.rs:3:38
- |
-LL | struct Apple((Apple, Option(Banana ? Citron)));
- | ^^^^^^ not found in this scope
+error: aborting due to 2 previous errors
-error[E0214]: parenthesized type parameters may only be used with a `Fn` trait
- --> $DIR/issue-103748-ICE-wrong-braces.rs:3:22
- |
-LL | struct Apple((Apple, Option(Banana ? Citron)));
- | ^^^^^^^^^^^^^^^^^^^^^^^ only `Fn` traits may use parentheses
- |
-help: use angle brackets instead
- |
-LL - struct Apple((Apple, Option(Banana ? Citron)));
-LL + struct Apple((Apple, Option<Banana ? Citron>));
- |
-
-error[E0072]: recursive type `Apple` has infinite size
- --> $DIR/issue-103748-ICE-wrong-braces.rs:3:1
- |
-LL | struct Apple((Apple, Option(Banana ? Citron)));
- | ^^^^^^^^^^^^ ----- recursive without indirection
- |
-help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle
- |
-LL | struct Apple((Box<Apple>, Option(Banana ? Citron)));
- | ++++ +
-
-error: aborting due to 5 previous errors
-
-Some errors have detailed explanations: E0072, E0214, E0412.
-For more information about an error, try `rustc --explain E0072`.
diff --git a/tests/ui/pattern/deref-patterns/bindings.rs b/tests/ui/pattern/deref-patterns/bindings.rs
index ac48e3f..92c01d7 100644
--- a/tests/ui/pattern/deref-patterns/bindings.rs
+++ b/tests/ui/pattern/deref-patterns/bindings.rs
@@ -13,7 +13,6 @@ fn simple_vec(vec: Vec<u32>) -> u32 {
deref!([x]) => x,
deref!([1, x]) => x + 200,
deref!(ref slice) => slice.iter().sum(),
- _ => 2000,
}
}
@@ -25,7 +24,6 @@ fn simple_vec(vec: Vec<u32>) -> u32 {
[x] => x,
[1, x] => x + 200,
deref!(ref slice) => slice.iter().sum(),
- _ => 2000,
}
}
diff --git a/tests/ui/pattern/deref-patterns/closure_capture.rs b/tests/ui/pattern/deref-patterns/closure_capture.rs
index cf78eed..497ec62 100644
--- a/tests/ui/pattern/deref-patterns/closure_capture.rs
+++ b/tests/ui/pattern/deref-patterns/closure_capture.rs
@@ -9,7 +9,7 @@
fn main() {
let b = Rc::new("aaa".to_string());
let f = || {
- let deref!(ref s) = b else { unreachable!() };
+ let deref!(ref s) = b;
assert_eq!(s.len(), 3);
};
assert_eq!(b.len(), 3);
@@ -26,7 +26,7 @@ fn main() {
let mut b = "aaa".to_string();
let mut f = || {
- let deref!(ref mut s) = b else { unreachable!() };
+ let deref!(ref mut s) = b;
s.make_ascii_uppercase();
};
f();
@@ -53,7 +53,7 @@ fn main() {
let b = Box::new(NoCopy);
let f = || {
// this should move out of the box rather than borrow.
- let deref!(x) = b else { unreachable!() };
+ let deref!(x) = b;
drop::<NoCopy>(x);
};
f();
@@ -61,7 +61,7 @@ fn main() {
let b = Box::new((NoCopy,));
let f = || {
// this should move out of the box rather than borrow.
- let (x,) = b else { unreachable!() };
+ let (x,) = b;
drop::<NoCopy>(x);
};
f();
diff --git a/tests/ui/pattern/deref-patterns/deref-box.rs b/tests/ui/pattern/deref-patterns/deref-box.rs
index 2d0a8d0..39b23dc 100644
--- a/tests/ui/pattern/deref-patterns/deref-box.rs
+++ b/tests/ui/pattern/deref-patterns/deref-box.rs
@@ -6,18 +6,18 @@
#![expect(incomplete_features)]
fn unbox_1<T>(b: Box<T>) -> T {
- let deref!(x) = b else { unreachable!() };
+ let deref!(x) = b;
x
}
fn unbox_2<T>(b: Box<(T,)>) -> T {
- let (x,) = b else { unreachable!() };
+ let (x,) = b;
x
}
fn unbox_separately<T>(b: Box<(T, T)>) -> (T, T) {
- let (x, _) = b else { unreachable!() };
- let (_, y) = b else { unreachable!() };
+ let (x, _) = b;
+ let (_, y) = b;
(x, y)
}
@@ -31,7 +31,7 @@ fn main() {
// test that borrowing from a box also works
let mut b = "hi".to_owned().into_boxed_str();
- let deref!(ref mut s) = b else { unreachable!() };
+ let deref!(ref mut s) = b;
s.make_ascii_uppercase();
assert_eq!(&*b, "HI");
}
diff --git a/tests/ui/pattern/deref-patterns/implicit-cow-deref.rs b/tests/ui/pattern/deref-patterns/implicit-cow-deref.rs
index 04c83d4..2477026 100644
--- a/tests/ui/pattern/deref-patterns/implicit-cow-deref.rs
+++ b/tests/ui/pattern/deref-patterns/implicit-cow-deref.rs
@@ -11,7 +11,6 @@ fn main() {
match cow {
[..] => {}
- _ => unreachable!(),
}
match cow {
@@ -22,14 +21,12 @@ fn main() {
match Rc::new(&cow) {
Cow::Borrowed { 0: _ } => {}
Cow::Owned { 0: _ } => unreachable!(),
- _ => unreachable!(),
}
let cow_of_cow: Cow<'_, Cow<'static, [u8]>> = Cow::Owned(cow);
match cow_of_cow {
[..] => {}
- _ => unreachable!(),
}
// This matches on the outer `Cow` (the owned one).
@@ -41,6 +38,5 @@ fn main() {
match Rc::new(&cow_of_cow) {
Cow::Borrowed { 0: _ } => unreachable!(),
Cow::Owned { 0: _ } => {}
- _ => unreachable!(),
}
}
diff --git a/tests/ui/pattern/deref-patterns/usefulness/empty-types.rs b/tests/ui/pattern/deref-patterns/usefulness/empty-types.rs
new file mode 100644
index 0000000..0341903
--- /dev/null
+++ b/tests/ui/pattern/deref-patterns/usefulness/empty-types.rs
@@ -0,0 +1,47 @@
+//! Test that the place behind a deref pattern is treated as maybe-invalid, and thus empty arms
+//! cannot be omitted. This is handled the same as for refs and union fields, so this leaves the
+//! bulk of the testing to `tests/ui/pattern/usefulness/empty-types.rs`.
+// FIXME(deref_patterns): On stabilization, cases for deref patterns could be worked into that file
+// to keep the tests for empty types in one place and test more thoroughly.
+#![feature(deref_patterns)]
+#![expect(incomplete_features)]
+#![deny(unreachable_patterns)]
+
+enum Void {}
+
+fn main() {
+ // Sanity check: matching on an empty type without pointer indirection lets us omit arms.
+ let opt_void: Option<Void> = None;
+ match opt_void {
+ None => {}
+ }
+
+ // But if we hide it behind a smart pointer, we need an arm.
+ let box_opt_void: Box<Option<Void>> = Box::new(None);
+ match box_opt_void {
+ //~^ ERROR non-exhaustive patterns: `deref!(Some(_))` not covered
+ None => {}
+ }
+ match box_opt_void {
+ None => {}
+ Some(_) => {}
+ }
+ match box_opt_void {
+ None => {}
+ _ => {}
+ }
+
+ // For consistency, this behaves the same as if we manually dereferenced the scrutinee.
+ match *box_opt_void {
+ //~^ ERROR non-exhaustive patterns: `Some(_)` not covered
+ None => {}
+ }
+ match *box_opt_void {
+ None => {}
+ Some(_) => {}
+ }
+ match *box_opt_void {
+ None => {}
+ _ => {}
+ }
+}
diff --git a/tests/ui/pattern/deref-patterns/usefulness/empty-types.stderr b/tests/ui/pattern/deref-patterns/usefulness/empty-types.stderr
new file mode 100644
index 0000000..e324770
--- /dev/null
+++ b/tests/ui/pattern/deref-patterns/usefulness/empty-types.stderr
@@ -0,0 +1,38 @@
+error[E0004]: non-exhaustive patterns: `deref!(Some(_))` not covered
+ --> $DIR/empty-types.rs:21:11
+ |
+LL | match box_opt_void {
+ | ^^^^^^^^^^^^ pattern `deref!(Some(_))` not covered
+ |
+note: `Box<Option<Void>>` defined here
+ --> $SRC_DIR/alloc/src/boxed.rs:LL:COL
+ = note: the matched value is of type `Box<Option<Void>>`
+ = note: `Void` is uninhabited but is not being matched by value, so a wildcard `_` is required
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
+ |
+LL ~ None => {},
+LL + deref!(Some(_)) => todo!()
+ |
+
+error[E0004]: non-exhaustive patterns: `Some(_)` not covered
+ --> $DIR/empty-types.rs:35:11
+ |
+LL | match *box_opt_void {
+ | ^^^^^^^^^^^^^ pattern `Some(_)` not covered
+ |
+note: `Option<Void>` defined here
+ --> $SRC_DIR/core/src/option.rs:LL:COL
+ ::: $SRC_DIR/core/src/option.rs:LL:COL
+ |
+ = note: not covered
+ = note: the matched value is of type `Option<Void>`
+ = note: `Void` is uninhabited but is not being matched by value, so a wildcard `_` is required
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
+ |
+LL ~ None => {},
+LL + Some(_) => todo!()
+ |
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/deref-patterns/usefulness/mixed-constructors.rs b/tests/ui/pattern/deref-patterns/usefulness/mixed-constructors.rs
new file mode 100644
index 0000000..f567dc0
--- /dev/null
+++ b/tests/ui/pattern/deref-patterns/usefulness/mixed-constructors.rs
@@ -0,0 +1,48 @@
+//! Test matches with a mix of ADT constructors and deref patterns. Currently, usefulness analysis
+//! doesn't support this, so make sure we catch it beforehand. As a consequence, it takes priority
+//! over non-exhaustive match and unreachable pattern errors.
+#![feature(deref_patterns)]
+#![expect(incomplete_features)]
+#![deny(unreachable_patterns)]
+
+use std::borrow::Cow;
+
+fn main() {
+ let cow: Cow<'static, bool> = Cow::Borrowed(&false);
+
+ match cow {
+ true => {}
+ //~v ERROR mix of deref patterns and normal constructors
+ false => {}
+ Cow::Borrowed(_) => {}
+ }
+
+ match cow {
+ Cow::Owned(_) => {}
+ Cow::Borrowed(_) => {}
+ true => {}
+ //~^ ERROR mix of deref patterns and normal constructors
+ }
+
+ match cow {
+ _ => {}
+ Cow::Owned(_) => {}
+ false => {}
+ //~^ ERROR mix of deref patterns and normal constructors
+ }
+
+ match (cow, 0) {
+ (Cow::Owned(_), 0) => {}
+ (Cow::Borrowed(_), 0) => {}
+ (true, 0) => {}
+ //~^ ERROR mix of deref patterns and normal constructors
+ }
+
+ match (0, cow) {
+ (0, Cow::Owned(_)) => {}
+ (0, Cow::Borrowed(_)) => {}
+ _ => {}
+ (1, true) => {}
+ //~^ ERROR mix of deref patterns and normal constructors
+ }
+}
diff --git a/tests/ui/pattern/deref-patterns/usefulness/mixed-constructors.stderr b/tests/ui/pattern/deref-patterns/usefulness/mixed-constructors.stderr
new file mode 100644
index 0000000..5ad2416
--- /dev/null
+++ b/tests/ui/pattern/deref-patterns/usefulness/mixed-constructors.stderr
@@ -0,0 +1,43 @@
+error: mix of deref patterns and normal constructors
+ --> $DIR/mixed-constructors.rs:16:9
+ |
+LL | false => {}
+ | ^^^^^ matches on the result of dereferencing `Cow<'_, bool>`
+LL | Cow::Borrowed(_) => {}
+ | ^^^^^^^^^^^^^^^^ matches directly on `Cow<'_, bool>`
+
+error: mix of deref patterns and normal constructors
+ --> $DIR/mixed-constructors.rs:22:9
+ |
+LL | Cow::Borrowed(_) => {}
+ | ^^^^^^^^^^^^^^^^ matches directly on `Cow<'_, bool>`
+LL | true => {}
+ | ^^^^ matches on the result of dereferencing `Cow<'_, bool>`
+
+error: mix of deref patterns and normal constructors
+ --> $DIR/mixed-constructors.rs:29:9
+ |
+LL | Cow::Owned(_) => {}
+ | ^^^^^^^^^^^^^ matches directly on `Cow<'_, bool>`
+LL | false => {}
+ | ^^^^^ matches on the result of dereferencing `Cow<'_, bool>`
+
+error: mix of deref patterns and normal constructors
+ --> $DIR/mixed-constructors.rs:36:10
+ |
+LL | (Cow::Borrowed(_), 0) => {}
+ | ^^^^^^^^^^^^^^^^ matches directly on `Cow<'_, bool>`
+LL | (true, 0) => {}
+ | ^^^^ matches on the result of dereferencing `Cow<'_, bool>`
+
+error: mix of deref patterns and normal constructors
+ --> $DIR/mixed-constructors.rs:43:13
+ |
+LL | (0, Cow::Borrowed(_)) => {}
+ | ^^^^^^^^^^^^^^^^ matches directly on `Cow<'_, bool>`
+LL | _ => {}
+LL | (1, true) => {}
+ | ^^^^ matches on the result of dereferencing `Cow<'_, bool>`
+
+error: aborting due to 5 previous errors
+
diff --git a/tests/ui/pattern/deref-patterns/usefulness/non-exhaustive.rs b/tests/ui/pattern/deref-patterns/usefulness/non-exhaustive.rs
new file mode 100644
index 0000000..704cae8
--- /dev/null
+++ b/tests/ui/pattern/deref-patterns/usefulness/non-exhaustive.rs
@@ -0,0 +1,28 @@
+//! Test non-exhaustive matches involving deref patterns.
+#![feature(deref_patterns)]
+#![expect(incomplete_features)]
+#![deny(unreachable_patterns)]
+
+fn main() {
+ match Box::new(false) {
+ //~^ ERROR non-exhaustive patterns: `deref!(true)` not covered
+ false => {}
+ }
+
+ match Box::new(Box::new(false)) {
+ //~^ ERROR non-exhaustive patterns: `deref!(deref!(false))` not covered
+ true => {}
+ }
+
+ match Box::new((true, Box::new(false))) {
+ //~^ ERROR non-exhaustive patterns: `deref!((false, deref!(false)))` and `deref!((true, deref!(true)))` not covered
+ (true, false) => {}
+ (false, true) => {}
+ }
+
+ enum T { A, B, C }
+ match Box::new((Box::new(T::A), Box::new(T::A))) {
+ //~^ ERROR non-exhaustive patterns: `deref!((deref!(T::C), _))` not covered
+ (T::A | T::B, T::C) => {}
+ }
+}
diff --git a/tests/ui/pattern/deref-patterns/usefulness/non-exhaustive.stderr b/tests/ui/pattern/deref-patterns/usefulness/non-exhaustive.stderr
new file mode 100644
index 0000000..55fa84b
--- /dev/null
+++ b/tests/ui/pattern/deref-patterns/usefulness/non-exhaustive.stderr
@@ -0,0 +1,63 @@
+error[E0004]: non-exhaustive patterns: `deref!(true)` not covered
+ --> $DIR/non-exhaustive.rs:7:11
+ |
+LL | match Box::new(false) {
+ | ^^^^^^^^^^^^^^^ pattern `deref!(true)` not covered
+ |
+note: `Box<bool>` defined here
+ --> $SRC_DIR/alloc/src/boxed.rs:LL:COL
+ = note: the matched value is of type `Box<bool>`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
+ |
+LL ~ false => {},
+LL + deref!(true) => todo!()
+ |
+
+error[E0004]: non-exhaustive patterns: `deref!(deref!(false))` not covered
+ --> $DIR/non-exhaustive.rs:12:11
+ |
+LL | match Box::new(Box::new(false)) {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `deref!(deref!(false))` not covered
+ |
+note: `Box<Box<bool>>` defined here
+ --> $SRC_DIR/alloc/src/boxed.rs:LL:COL
+ = note: the matched value is of type `Box<Box<bool>>`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
+ |
+LL ~ true => {},
+LL + deref!(deref!(false)) => todo!()
+ |
+
+error[E0004]: non-exhaustive patterns: `deref!((false, deref!(false)))` and `deref!((true, deref!(true)))` not covered
+ --> $DIR/non-exhaustive.rs:17:11
+ |
+LL | match Box::new((true, Box::new(false))) {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ patterns `deref!((false, deref!(false)))` and `deref!((true, deref!(true)))` not covered
+ |
+note: `Box<(bool, Box<bool>)>` defined here
+ --> $SRC_DIR/alloc/src/boxed.rs:LL:COL
+ = note: the matched value is of type `Box<(bool, Box<bool>)>`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms
+ |
+LL ~ (false, true) => {},
+LL + deref!((false, deref!(false))) | deref!((true, deref!(true))) => todo!()
+ |
+
+error[E0004]: non-exhaustive patterns: `deref!((deref!(T::C), _))` not covered
+ --> $DIR/non-exhaustive.rs:24:11
+ |
+LL | match Box::new((Box::new(T::A), Box::new(T::A))) {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `deref!((deref!(T::C), _))` not covered
+ |
+note: `Box<(Box<T>, Box<T>)>` defined here
+ --> $SRC_DIR/alloc/src/boxed.rs:LL:COL
+ = note: the matched value is of type `Box<(Box<T>, Box<T>)>`
+help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
+ |
+LL ~ (T::A | T::B, T::C) => {},
+LL + deref!((deref!(T::C), _)) => todo!()
+ |
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/pattern/deref-patterns/usefulness/unreachable-patterns.rs b/tests/ui/pattern/deref-patterns/usefulness/unreachable-patterns.rs
new file mode 100644
index 0000000..2677fc5
--- /dev/null
+++ b/tests/ui/pattern/deref-patterns/usefulness/unreachable-patterns.rs
@@ -0,0 +1,33 @@
+//! Test unreachable patterns involving deref patterns.
+#![feature(deref_patterns)]
+#![expect(incomplete_features)]
+#![deny(unreachable_patterns)]
+
+fn main() {
+ match Box::new(false) {
+ true => {}
+ false => {}
+ false => {} //~ ERROR unreachable pattern
+ }
+
+ match Box::new(Box::new(false)) {
+ true => {}
+ false => {}
+ true => {} //~ ERROR unreachable pattern
+ }
+
+ match Box::new((true, Box::new(false))) {
+ (true, _) => {}
+ (_, true) => {}
+ (false, false) => {}
+ _ => {} //~ ERROR unreachable pattern
+ }
+
+ enum T { A, B, C }
+ match Box::new((Box::new(T::A), Box::new(T::A))) {
+ (T::A | T::B, T::A | T::C) => {}
+ (T::A, T::C) => {} //~ ERROR unreachable pattern
+ (T::B, T::A) => {} //~ ERROR unreachable pattern
+ _ => {}
+ }
+}
diff --git a/tests/ui/pattern/deref-patterns/usefulness/unreachable-patterns.stderr b/tests/ui/pattern/deref-patterns/usefulness/unreachable-patterns.stderr
new file mode 100644
index 0000000..045e11b
--- /dev/null
+++ b/tests/ui/pattern/deref-patterns/usefulness/unreachable-patterns.stderr
@@ -0,0 +1,60 @@
+error: unreachable pattern
+ --> $DIR/unreachable-patterns.rs:10:9
+ |
+LL | false => {}
+ | ----- matches all the relevant values
+LL | false => {}
+ | ^^^^^ no value can reach this
+ |
+note: the lint level is defined here
+ --> $DIR/unreachable-patterns.rs:4:9
+ |
+LL | #![deny(unreachable_patterns)]
+ | ^^^^^^^^^^^^^^^^^^^^
+
+error: unreachable pattern
+ --> $DIR/unreachable-patterns.rs:16:9
+ |
+LL | true => {}
+ | ---- matches all the relevant values
+LL | false => {}
+LL | true => {}
+ | ^^^^ no value can reach this
+
+error: unreachable pattern
+ --> $DIR/unreachable-patterns.rs:23:9
+ |
+LL | _ => {}
+ | ^ no value can reach this
+ |
+note: multiple earlier patterns match some of the same values
+ --> $DIR/unreachable-patterns.rs:23:9
+ |
+LL | (true, _) => {}
+ | --------- matches some of the same values
+LL | (_, true) => {}
+ | --------- matches some of the same values
+LL | (false, false) => {}
+ | -------------- matches some of the same values
+LL | _ => {}
+ | ^ collectively making this unreachable
+
+error: unreachable pattern
+ --> $DIR/unreachable-patterns.rs:29:9
+ |
+LL | (T::A | T::B, T::A | T::C) => {}
+ | -------------------------- matches all the relevant values
+LL | (T::A, T::C) => {}
+ | ^^^^^^^^^^^^ no value can reach this
+
+error: unreachable pattern
+ --> $DIR/unreachable-patterns.rs:30:9
+ |
+LL | (T::A | T::B, T::A | T::C) => {}
+ | -------------------------- matches all the relevant values
+LL | (T::A, T::C) => {}
+LL | (T::B, T::A) => {}
+ | ^^^^^^^^^^^^ no value can reach this
+
+error: aborting due to 5 previous errors
+
diff --git a/tests/ui/pub/pub-restricted-warning.rs b/tests/ui/pub/pub-restricted-warning.rs
new file mode 100644
index 0000000..80384af
--- /dev/null
+++ b/tests/ui/pub/pub-restricted-warning.rs
@@ -0,0 +1,25 @@
+//@ check-pass
+
+#![allow(dead_code)]
+
+mod outer {
+ pub mod inner {
+ pub(in crate::outer) struct Foo;
+ pub fn bar() -> Foo {
+ //~^ WARNING type `Foo` is more private than the item `outer::inner::bar` [private_interfaces]
+ Foo
+ }
+ }
+
+ pub mod nested {
+ pub mod inner {
+ pub(in crate::outer::nested) struct NestedFoo;
+ pub fn bar() -> NestedFoo {
+ //~^ WARNING type `NestedFoo` is more private than the item `nested::inner::bar` [private_interfaces]
+ NestedFoo
+ }
+ }
+ }
+}
+
+fn main() {}
diff --git a/tests/ui/pub/pub-restricted-warning.stderr b/tests/ui/pub/pub-restricted-warning.stderr
new file mode 100644
index 0000000..74f32d3
--- /dev/null
+++ b/tests/ui/pub/pub-restricted-warning.stderr
@@ -0,0 +1,27 @@
+warning: type `Foo` is more private than the item `outer::inner::bar`
+ --> $DIR/pub-restricted-warning.rs:8:9
+ |
+LL | pub fn bar() -> Foo {
+ | ^^^^^^^^^^^^^^^^^^^ function `outer::inner::bar` is reachable at visibility `pub(crate)`
+ |
+note: but type `Foo` is only usable at visibility `pub(in crate::outer)`
+ --> $DIR/pub-restricted-warning.rs:7:9
+ |
+LL | pub(in crate::outer) struct Foo;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = note: `#[warn(private_interfaces)]` on by default
+
+warning: type `NestedFoo` is more private than the item `nested::inner::bar`
+ --> $DIR/pub-restricted-warning.rs:17:13
+ |
+LL | pub fn bar() -> NestedFoo {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ function `nested::inner::bar` is reachable at visibility `pub(crate)`
+ |
+note: but type `NestedFoo` is only usable at visibility `pub(in crate::outer::nested)`
+ --> $DIR/pub-restricted-warning.rs:16:13
+ |
+LL | pub(in crate::outer::nested) struct NestedFoo;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+warning: 2 warnings emitted
+
diff --git a/tests/ui/trait-bounds/impl-missing-where-clause-lifetimes-from-trait.stderr b/tests/ui/trait-bounds/impl-missing-where-clause-lifetimes-from-trait.stderr
index e26cb22..f8a6252 100644
--- a/tests/ui/trait-bounds/impl-missing-where-clause-lifetimes-from-trait.stderr
+++ b/tests/ui/trait-bounds/impl-missing-where-clause-lifetimes-from-trait.stderr
@@ -10,26 +10,53 @@
LL | fn foo<'a, K>(self, _: (), _: K) where {
| ^^^^^^^ lifetimes do not match method in trait
-error[E0195]: lifetime parameters or bounds on method `foo` do not match the trait declaration
- --> $DIR/impl-missing-where-clause-lifetimes-from-trait.rs:23:11
+error[E0195]: lifetime parameters do not match the trait definition
+ --> $DIR/impl-missing-where-clause-lifetimes-from-trait.rs:23:12
|
-LL | fn foo<'a>(&self, state: &'a State) -> &'a T
- | ---- lifetimes in impl do not match this method in trait
-LL | where
-LL | T: 'a;
- | -- this bound might be missing in the impl
-...
LL | fn foo<'a>(&self, state: &'a State) -> &'a T {
- | ^^^^ lifetimes do not match method in trait
-
-error[E0195]: lifetime parameters or bounds on method `foo` do not match the trait declaration
- --> $DIR/impl-missing-where-clause-lifetimes-from-trait.rs:33:11
+ | ^^
|
-LL | fn foo<'a>(&'a self) {}
- | ---- lifetimes in impl do not match this method in trait
+ = note: lifetime parameters differ in whether they are early- or late-bound
+note: `'a` differs between the trait and impl
+ --> $DIR/impl-missing-where-clause-lifetimes-from-trait.rs:14:12
+ |
+LL | trait Foo<T> {
+ | ------------ in this trait...
+LL | fn foo<'a>(&self, state: &'a State) -> &'a T
+ | ^^ `'a` is early-bound
+LL | where
+LL | T: 'a;
+ | -- this lifetime bound makes `'a` early-bound
...
+LL | / impl<F, T> Foo<T> for F
+LL | | where
+LL | | F: Fn(&State) -> &T,
+ | |________________________- in this impl...
+LL | {
+LL | fn foo<'a>(&self, state: &'a State) -> &'a T {
+ | ^^ `'a` is late-bound
+
+error[E0195]: lifetime parameters do not match the trait definition
+ --> $DIR/impl-missing-where-clause-lifetimes-from-trait.rs:33:12
+ |
LL | fn foo<'a: 'a>(&'a self) {}
- | ^^^^^^^^ lifetimes do not match method in trait
+ | ^^
+ |
+ = note: lifetime parameters differ in whether they are early- or late-bound
+note: `'a` differs between the trait and impl
+ --> $DIR/impl-missing-where-clause-lifetimes-from-trait.rs:29:12
+ |
+LL | trait Bar {
+ | --------- in this trait...
+LL | fn foo<'a>(&'a self) {}
+ | ^^ `'a` is late-bound
+...
+LL | impl Bar for () {
+ | --------------- in this impl...
+LL | fn foo<'a: 'a>(&'a self) {}
+ | ^^ -- this lifetime bound makes `'a` early-bound
+ | |
+ | `'a` is early-bound
error: aborting due to 3 previous errors
diff --git a/tests/ui/traits/next-solver/global-param-env-after-norm.rs b/tests/ui/traits/next-solver/global-param-env-after-norm.rs
new file mode 100644
index 0000000..0d098db
--- /dev/null
+++ b/tests/ui/traits/next-solver/global-param-env-after-norm.rs
@@ -0,0 +1,15 @@
+//@ check-pass
+//@ compile-flags: -Znext-solver
+
+struct NewSolver;
+struct OldSolver;
+
+fn foo<T>()
+where
+ T: Iterator<Item = NewSolver>,
+ OldSolver: Into<T::Item>,
+{
+ let x: OldSolver = OldSolver.into();
+}
+
+fn main() {}
diff --git a/tests/ui/traits/next-solver/normalize/normalize-param-env-4.next.stderr b/tests/ui/traits/next-solver/normalize/normalize-param-env-4.next.stderr
index e91a48f..f5fd9ce 100644
--- a/tests/ui/traits/next-solver/normalize/normalize-param-env-4.next.stderr
+++ b/tests/ui/traits/next-solver/normalize/normalize-param-env-4.next.stderr
@@ -4,24 +4,6 @@
LL | <T as Trait>::Assoc: Trait,
| ^^^^^
-error[E0275]: overflow evaluating the requirement `<T as Trait>::Assoc well-formed`
- --> $DIR/normalize-param-env-4.rs:19:26
- |
-LL | <T as Trait>::Assoc: Trait,
- | ^^^^^
-
-error[E0275]: overflow evaluating the requirement `T: Trait`
- --> $DIR/normalize-param-env-4.rs:32:19
- |
-LL | impls_trait::<T>();
- | ^
- |
-note: required by a bound in `impls_trait`
- --> $DIR/normalize-param-env-4.rs:15:19
- |
-LL | fn impls_trait<T: Trait>() {}
- | ^^^^^ required by this bound in `impls_trait`
-
-error: aborting due to 3 previous errors
+error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0275`.
diff --git a/tests/ui/traits/next-solver/overflow-plus-ambiguity-normalizes-to-response.rs b/tests/ui/traits/next-solver/overflow-plus-ambiguity-normalizes-to-response.rs
new file mode 100644
index 0000000..4b20cd1
--- /dev/null
+++ b/tests/ui/traits/next-solver/overflow-plus-ambiguity-normalizes-to-response.rs
@@ -0,0 +1,56 @@
+//@ check-pass
+//@ compile-flags: -Znext-solver
+
+// Regression test for <https://github.com/rust-lang/trait-system-refactor-initiative/issues/201>.
+// See comment below on `fn main`.
+
+trait Intersect<U> {
+ type Output;
+}
+
+impl<T, U> Intersect<Vec<U>> for T
+where
+ T: Intersect<U>,
+{
+ type Output = T;
+}
+
+impl Intersect<Cuboid> for Cuboid {
+ type Output = Cuboid;
+}
+
+fn intersect<T, U>(_: &T, _: &U) -> T::Output
+where
+ T: Intersect<U>,
+{
+ todo!()
+}
+
+struct Cuboid;
+impl Cuboid {
+ fn method(&self) {}
+}
+
+fn main() {
+ let x = vec![];
+ // Here we end up trying to normalize `<Cuboid as Intersect<Vec<?0>>>::Output`
+ // for the return type of the function, to constrain `y`. The impl then requires
+ // `Cuboid: Intersect<?0>`, which has two candidates. One that constrains
+ // `?0 = Vec<?1>`, which itself has the same two candidates and ends up leading
+ // to a recursion depth overflow. In the second impl, we constrain `?0 = Cuboid`.
+ //
+ // Floundering leads to us combining the overflow candidate and yes candidate to
+ // overflow. Because the response was overflow, we used to bubble it up to the
+ // parent normalizes-to goal, which caused us to drop its constraint that would
+ // guide us to normalize the associated type mentioned before.
+ //
+ // After this PR, we implement a new floundering "algebra" such that `Overflow OR Maybe`
+ // returns anew `Overflow { keep_constraints: true }`, which means that we don't
+ // need to drop constraints in the parent normalizes-to goal. This allows us to
+ // normalize `y` to `Cuboid`, and allows us to call the method successfully. We
+ // then constrain the `?0` in `let x: Vec<Cuboid> = x` below, so that we don't have
+ // a left over ambiguous goal.
+ let y = intersect(&Cuboid, &x);
+ y.method();
+ let x: Vec<Cuboid> = x;
+}
diff --git a/tests/ui/type-alias-impl-trait/lifetime-def-path-conflict-40731.rs b/tests/ui/type-alias-impl-trait/lifetime-def-path-conflict-40731.rs
new file mode 100644
index 0000000..bfaa485
--- /dev/null
+++ b/tests/ui/type-alias-impl-trait/lifetime-def-path-conflict-40731.rs
@@ -0,0 +1,16 @@
+// https://github.com/rust-lang/rust/issues/140731
+// This tests that there's no def path conflict between the
+// remapped lifetime and the lifetime present in the source.
+
+#![feature(impl_trait_in_assoc_type)]
+
+trait Trait<'a> {}
+
+impl<'a> Trait<'a> for u32 {
+ type Opq2 = impl for<'a> Trait<'a>;
+ //~^ ERROR: unconstrained opaque type
+ //~| ERROR: type `Opq2` is not a member of trait `Trait`
+ //~| ERROR: lifetime name `'a` shadows a lifetime name that is already in scope
+}
+
+fn main() {}
diff --git a/tests/ui/type-alias-impl-trait/lifetime-def-path-conflict-40731.stderr b/tests/ui/type-alias-impl-trait/lifetime-def-path-conflict-40731.stderr
new file mode 100644
index 0000000..e1544c5
--- /dev/null
+++ b/tests/ui/type-alias-impl-trait/lifetime-def-path-conflict-40731.stderr
@@ -0,0 +1,26 @@
+error[E0437]: type `Opq2` is not a member of trait `Trait`
+ --> $DIR/lifetime-def-path-conflict-40731.rs:10:5
+ |
+LL | type Opq2 = impl for<'a> Trait<'a>;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not a member of trait `Trait`
+
+error[E0496]: lifetime name `'a` shadows a lifetime name that is already in scope
+ --> $DIR/lifetime-def-path-conflict-40731.rs:10:26
+ |
+LL | impl<'a> Trait<'a> for u32 {
+ | -- first declared here
+LL | type Opq2 = impl for<'a> Trait<'a>;
+ | ^^ lifetime `'a` already in scope
+
+error: unconstrained opaque type
+ --> $DIR/lifetime-def-path-conflict-40731.rs:10:17
+ |
+LL | type Opq2 = impl for<'a> Trait<'a>;
+ | ^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `Opq2` must be used in combination with a concrete type within the same impl
+
+error: aborting due to 3 previous errors
+
+Some errors have detailed explanations: E0437, E0496.
+For more information about an error, try `rustc --explain E0437`.
diff --git a/triagebot.toml b/triagebot.toml
index 422996c..57153f1 100644
--- a/triagebot.toml
+++ b/triagebot.toml
@@ -1001,7 +1001,15 @@
message = "This PR changes a file inside `tests/crashes`. If a crash was fixed, please move into the corresponding `ui` subdir and add 'Fixes #<issueNr>' to the PR description to autoclose the issue upon merge."
[mentions."tests/rustdoc-json"]
-cc = ["@aDotInTheVoid"]
+message = """
+These commits modify `test/rustdoc-json`.
+rustdoc-json is a **public** (but unstable) interface.
+
+Please ensure that if you've changed the output:
+- It's intentional.
+- The `FORMAT_VERSION` in `src/librustdoc-json-types` is bumped if necessary.
+"""
+cc = ["@aDotInTheVoid", "@obi1kenobi"]
[mentions."tests/ui/deriving/deriving-all-codegen.stdout"]
message = "Changes to the code generated for builtin derived traits."
@@ -1161,7 +1169,6 @@
"@BoxyUwU",
"@compiler-errors",
"@davidtwco",
- "@estebank",
"@fee1-dead",
"@fmease",
"@jieyouxu",
@@ -1221,14 +1228,12 @@
diagnostics = [
"@compiler-errors",
"@davidtwco",
- "@estebank",
"@oli-obk",
"@chenyukang",
]
parser = [
"@compiler-errors",
"@davidtwco",
- "@estebank",
"@nnethercote",
"@petrochenkov",
"@spastorino",
@@ -1236,7 +1241,6 @@
lexer = [
"@nnethercote",
"@petrochenkov",
- "@estebank",
"@chenyukang",
]
arena = [
@@ -1268,7 +1272,6 @@
]
ast_lowering = [
"@compiler-errors",
- "@estebank",
"@spastorino",
]
debuginfo = [