Auto merge of #140781 - matthiaskrgr:rollup-90sig9g, r=matthiaskrgr
Rollup of 9 pull requests Successful merges: - #140260 (Only prefer param-env candidates if they remain non-global after norm) - #140523 (Better error message for late/early lifetime param mismatch) - #140579 (Remove estebank from automated review assignment) - #140641 (detect additional uses of opaques after writeback) - #140711 (Do not discard constraints on overflow if there was candidate ambiguity) - #140762 (rustdoc-json: Remove newlines from attributes) - #140764 (style: Never break within a nullary function call `func()` or a unit literal `()`) - #140769 (Add `DefPathData::OpaqueLifetime` to avoid conflicts for remapped opaque lifetimes) - #140773 (triagebot: Better message for changes to `tests/rustdoc-json`) r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
1973872013
61 changed files with 1199 additions and 458 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 @@ impl DefPathData {
|
|||
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 @@ impl DefPathData {
|
|||
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 },
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use std::iter;
|
|||
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_infer::traits::util;
|
|||
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))]
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ use rustc_ast::visit::walk_list;
|
|||
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 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
|
|||
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);
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@ use rustc_hir::def_id::{DefId, LocalDefId};
|
|||
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 {
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ mod gather_locals;
|
|||
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.
|
||||
|
|
|
|||
26
compiler/rustc_hir_typeck/src/opaque_types.rs
Normal file
26
compiler/rustc_hir_typeck/src/opaque_types.rs
Normal file
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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 @@ impl<'tcx> TypeckRootCtxt<'tcx> {
|
|||
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()),
|
||||
|
|
|
|||
|
|
@ -535,13 +535,10 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
|
|||
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);
|
||||
|
|
|
|||
|
|
@ -6,7 +6,10 @@ use rustc_middle::ty::relate::combine::PredicateEmittingRelation;
|
|||
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 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> {
|
|||
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();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ use free_regions::RegionRelations;
|
|||
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,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ use crate::traits::{self, Obligation, PredicateObligations};
|
|||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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 @@ impl<'tcx> OpaqueTypeStorage<'tcx> {
|
|||
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.
|
||||
///
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -15,7 +15,8 @@ use rustc_middle::ty::relate::{
|
|||
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 @@ where
|
|||
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() {
|
||||
|
|
|
|||
|
|
@ -2934,7 +2934,7 @@ impl<'tcx> FmtPrinter<'_, 'tcx> {
|
|||
|
||||
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>,
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
{
|
||||
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 @@ impl LateBoundRegionsCollector {
|
|||
}
|
||||
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -39,13 +39,6 @@ pub trait SolverDelegate: Deref<Target = Self::Infcx> + Sized {
|
|||
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 @@ pub trait SolverDelegate: Deref<Target = Self::Infcx> + Sized {
|
|||
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 @@ pub trait SolverDelegate: Deref<Target = Self::Infcx> + Sized {
|
|||
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,
|
||||
|
|
|
|||
|
|
@ -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 @@ where
|
|||
|
||||
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 @@ where
|
|||
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 @@ where
|
|||
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 @@ where
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 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!()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,39 +36,38 @@ where
|
|||
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 @@ where
|
|||
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 @@ where
|
|||
|
||||
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))
|
||||
|
|
|
|||
|
|
@ -132,12 +132,14 @@ where
|
|||
(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 @@ where
|
|||
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 @@ where
|
|||
// 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 }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,8 +23,7 @@ use crate::solve::inspect::{self, ProofTreeBuilder};
|
|||
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 @@ where
|
|||
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 @@ where
|
|||
/// 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 @@ where
|
|||
|
||||
// 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 @@ where
|
|||
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 @@ where
|
|||
// 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 @@ where
|
|||
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 @@ where
|
|||
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 @@ where
|
|||
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() {
|
||||
|
|
|
|||
|
|
@ -26,32 +26,33 @@ where
|
|||
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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -253,16 +253,18 @@ where
|
|||
}
|
||||
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -106,50 +106,48 @@ where
|
|||
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(
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ use crate::solve::assembly::{self, AllowInferenceConstraints, AssembleCandidates
|
|||
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 @@ where
|
|||
.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 @@ where
|
|||
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 @@ where
|
|||
|
||||
// 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 @@ where
|
|||
// 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 @@ where
|
|||
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(
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -890,6 +890,7 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> {
|
|||
| DefPathData::Impl
|
||||
| DefPathData::MacroNs(_)
|
||||
| DefPathData::LifetimeNs(_)
|
||||
| DefPathData::OpaqueLifetime(_)
|
||||
| DefPathData::AnonAssocTy(..) => {
|
||||
bug!("symbol_names: unexpected DefPathData: {:?}", disambiguated_data.data)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -104,25 +104,6 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate<
|
|||
.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 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate<
|
|||
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 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate<
|
|||
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>,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -382,7 +382,7 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> {
|
|||
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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ use rustc_macros::extension;
|
|||
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);
|
||||
|
|
|
|||
|
|
@ -7,7 +7,8 @@ use rustc_infer::infer::TyCtxtInferExt;
|
|||
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);
|
||||
|
|
|
|||
|
|
@ -128,7 +128,7 @@ impl<I: Interner, T: TypeFoldable<I>> TypeFoldable<I> for Binder<I, T> {
|
|||
}
|
||||
}
|
||||
|
||||
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 @@ impl<I: Interner, T: TypeFoldable<I>> TypeSuperFoldable<I> for Binder<I, T> {
|
|||
}
|
||||
}
|
||||
|
||||
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 @@ impl<I: Interner> ValidateBoundVars<I> {
|
|||
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);
|
||||
|
|
|
|||
|
|
@ -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};
|
||||
|
|
@ -245,4 +247,32 @@ pub trait InferCtxtLike: Sized {
|
|||
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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
@ -189,6 +188,14 @@ pub enum CandidateSource<I: Interner> {
|
|||
CoherenceUnknowable,
|
||||
}
|
||||
|
||||
#[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",
|
||||
|
|
@ -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,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ use smallvec::SmallVec;
|
|||
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 @@ impl std::fmt::Debug for HasTypeFlagsVisitor {
|
|||
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);
|
||||
|
|
|
|||
|
|
@ -26,6 +26,10 @@ edition).
|
|||
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
|
||||
|
|
|
|||
|
|
@ -183,6 +183,10 @@ let f = Foo {
|
|||
};
|
||||
```
|
||||
|
||||
## 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 @@ Do put a space between an argument, and the comma which precedes it.
|
|||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -786,7 +786,11 @@ impl Item {
|
|||
// 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(
|
||||
|
|
|
|||
|
|
@ -20,8 +20,8 @@ use rustc_middle::traits::EvaluationResult;
|
|||
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 @@ pub fn for_each_top_level_late_bound_region<B>(
|
|||
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;
|
||||
|
|
|
|||
|
|
@ -9,5 +9,5 @@ impl Default for Manual {
|
|||
}
|
||||
}
|
||||
|
||||
//@ 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' '[]'
|
||||
|
|
|
|||
|
|
@ -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() {}
|
||||
|
|
|
|||
|
|
@ -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() {}
|
||||
|
|
|
|||
|
|
@ -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> {}
|
||||
|
|
|
|||
|
|
@ -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() {}
|
||||
|
|
|
|||
|
|
@ -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() {}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 @@ impl<'a, 't> Foo<'a, 't> for &'a isize {
|
|||
}
|
||||
|
||||
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>) {
|
||||
|
|
|
|||
|
|
@ -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 @@ note: ...does not necessarily outlive the lifetime `'c` as defined here
|
|||
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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -10,26 +10,53 @@ LL | fn foo<'a, K>(self, _: T, _: K) where T: 'a, K: 'a;
|
|||
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
|
||||
|
||||
|
|
|
|||
15
tests/ui/traits/next-solver/global-param-env-after-norm.rs
Normal file
15
tests/ui/traits/next-solver/global-param-env-after-norm.rs
Normal file
|
|
@ -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() {}
|
||||
|
|
@ -4,24 +4,6 @@ error[E0275]: overflow evaluating the requirement `<T as Trait>::Assoc: Trait`
|
|||
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`.
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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() {}
|
||||
|
|
@ -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`.
|
||||
|
|
@ -1001,7 +1001,15 @@ message = "This PR changes how GCC is built. Consider updating src/bootstrap/dow
|
|||
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 @@ compiler = [
|
|||
"@BoxyUwU",
|
||||
"@compiler-errors",
|
||||
"@davidtwco",
|
||||
"@estebank",
|
||||
"@fee1-dead",
|
||||
"@fmease",
|
||||
"@jieyouxu",
|
||||
|
|
@ -1221,14 +1228,12 @@ incremental = [
|
|||
diagnostics = [
|
||||
"@compiler-errors",
|
||||
"@davidtwco",
|
||||
"@estebank",
|
||||
"@oli-obk",
|
||||
"@chenyukang",
|
||||
]
|
||||
parser = [
|
||||
"@compiler-errors",
|
||||
"@davidtwco",
|
||||
"@estebank",
|
||||
"@nnethercote",
|
||||
"@petrochenkov",
|
||||
"@spastorino",
|
||||
|
|
@ -1236,7 +1241,6 @@ parser = [
|
|||
lexer = [
|
||||
"@nnethercote",
|
||||
"@petrochenkov",
|
||||
"@estebank",
|
||||
"@chenyukang",
|
||||
]
|
||||
arena = [
|
||||
|
|
@ -1268,7 +1272,6 @@ borrowck = [
|
|||
]
|
||||
ast_lowering = [
|
||||
"@compiler-errors",
|
||||
"@estebank",
|
||||
"@spastorino",
|
||||
]
|
||||
debuginfo = [
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue