Auto merge of #111881 - lcnr:leak-check, r=nikomatsakis,jackh726

refactor and cleanup the leak check, add it to new solver

ended up being a bit more involved than I wanted but is hopefully still easy enough to review as a single PR, can split it into separate ones otherwise.

this can be reviewed commit by commit:
a473d55cdb9284aa2b01282d1b529a2a4d26547b 31a686646534ca006d906ec757ece4e771d6f973 949039c107852a5e36361c08b62821a0613656f5 242917bf5170d9a723c6c8e23e9d9d0c2fa8dc9d ed2b25a7aa28be3184be9e3022c2796a30eaad87 are all pretty straightforward.

03dd83b4c3f4ff27558f5c8ab859bd9f83db1d04 makes it easier to refactor coherence in a later commit, see the commit description, cc `@oli-obk`

4fe311d807a77b6270f384e41689bf5d58f46aec I don't quite remember what we wanted to test here, this definitely doesn't test that the occurs check doesn't cause incorrect errors in coherence, also cc `@oli-obk` here. I may end up writing a new test for this myself later.

5c200d88a91b75bd0875b973150655bd581ef97a is the main refactor of the leak check, changing it to take the `outer_universe` instead of getting it from a snapshot. Using a snapshot requires us to be in a probe which we aren't in the new solver, it also just feels dirty as snapshots don't really have anything to do with universes.

with all of this cfc230d54188d9c7ed867a9a0d1f51be77b485f9 is now kind of trivial.

r? `@nikomatsakis`
This commit is contained in:
bors 2023-05-30 18:48:12 +00:00
commit f0411ffceb
138 changed files with 320 additions and 244 deletions

View File

@ -808,6 +808,8 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
G: FnOnce(Ty<'tcx>) -> Vec<Adjustment<'tcx>>,
{
self.commit_if_ok(|snapshot| {
let outer_universe = self.infcx.universe();
let result = if let ty::FnPtr(fn_ty_b) = b.kind()
&& let (hir::Unsafety::Normal, hir::Unsafety::Unsafe) =
(fn_ty_a.unsafety(), fn_ty_b.unsafety())
@ -824,7 +826,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
// want the coerced type to be the actual supertype of these two,
// but for now, we want to just error to ensure we don't lock
// ourselves into a specific behavior with NLL.
self.leak_check(false, snapshot)?;
self.leak_check(outer_universe, Some(snapshot))?;
result
})

View File

@ -70,8 +70,8 @@ impl<'tcx> InferCtxt<'tcx> {
tcx: self.tcx,
defining_use_anchor: self.defining_use_anchor,
considering_regions: self.considering_regions,
skip_leak_check: self.skip_leak_check,
inner: self.inner.clone(),
skip_leak_check: self.skip_leak_check.clone(),
lexical_region_resolutions: self.lexical_region_resolutions.clone(),
selection_cache: self.selection_cache.clone(),
evaluation_cache: self.evaluation_cache.clone(),

View File

@ -105,13 +105,15 @@ impl<'tcx> InferCtxt<'tcx> {
self.tcx.replace_bound_vars_uncached(binder, delegate)
}
/// See [RegionConstraintCollector::leak_check][1].
/// See [RegionConstraintCollector::leak_check][1]. We only check placeholder
/// leaking into `outer_universe`, i.e. placeholders which cannot be named by that
/// universe.
///
/// [1]: crate::infer::region_constraints::RegionConstraintCollector::leak_check
pub fn leak_check(
&self,
overly_polymorphic: bool,
snapshot: &CombinedSnapshot<'tcx>,
outer_universe: ty::UniverseIndex,
only_consider_snapshot: Option<&CombinedSnapshot<'tcx>>,
) -> RelateResult<'tcx, ()> {
// If the user gave `-Zno-leak-check`, or we have been
// configured to skip the leak check, then skip the leak check
@ -119,15 +121,15 @@ impl<'tcx> InferCtxt<'tcx> {
// subtyping errors that it would have caught will now be
// caught later on, during region checking. However, we
// continue to use it for a transition period.
if self.tcx.sess.opts.unstable_opts.no_leak_check || self.skip_leak_check.get() {
if self.tcx.sess.opts.unstable_opts.no_leak_check || self.skip_leak_check {
return Ok(());
}
self.inner.borrow_mut().unwrap_region_constraints().leak_check(
self.tcx,
overly_polymorphic,
outer_universe,
self.universe(),
snapshot,
only_consider_snapshot,
)
}
}

View File

@ -251,14 +251,13 @@ pub struct InferCtxt<'tcx> {
/// solving is left to borrowck instead.
pub considering_regions: bool,
pub inner: RefCell<InferCtxtInner<'tcx>>,
/// If set, this flag causes us to skip the 'leak check' during
/// higher-ranked subtyping operations. This flag is a temporary one used
/// to manage the removal of the leak-check: for the time being, we still run the
/// leak-check, but we issue warnings. This flag can only be set to true
/// when entering a snapshot.
skip_leak_check: Cell<bool>,
/// leak-check, but we issue warnings.
skip_leak_check: bool,
pub inner: RefCell<InferCtxtInner<'tcx>>,
/// Once region inference is done, the values for each variable.
lexical_region_resolutions: RefCell<Option<LexicalRegionResolutions<'tcx>>>,
@ -543,6 +542,7 @@ pub struct InferCtxtBuilder<'tcx> {
tcx: TyCtxt<'tcx>,
defining_use_anchor: DefiningAnchor,
considering_regions: bool,
skip_leak_check: bool,
/// Whether we are in coherence mode.
intercrate: bool,
}
@ -557,6 +557,7 @@ impl<'tcx> TyCtxtInferExt<'tcx> for TyCtxt<'tcx> {
tcx: self,
defining_use_anchor: DefiningAnchor::Error,
considering_regions: true,
skip_leak_check: false,
intercrate: false,
}
}
@ -584,6 +585,11 @@ impl<'tcx> InferCtxtBuilder<'tcx> {
self
}
pub fn skip_leak_check(mut self, skip_leak_check: bool) -> Self {
self.skip_leak_check = skip_leak_check;
self
}
/// Given a canonical value `C` as a starting point, create an
/// inference context that contains each of the bound values
/// within instantiated as a fresh variable. The `f` closure is
@ -605,11 +611,18 @@ impl<'tcx> InferCtxtBuilder<'tcx> {
}
pub fn build(&mut self) -> InferCtxt<'tcx> {
let InferCtxtBuilder { tcx, defining_use_anchor, considering_regions, intercrate } = *self;
let InferCtxtBuilder {
tcx,
defining_use_anchor,
considering_regions,
skip_leak_check,
intercrate,
} = *self;
InferCtxt {
tcx,
defining_use_anchor,
considering_regions,
skip_leak_check,
inner: RefCell::new(InferCtxtInner::new()),
lexical_region_resolutions: RefCell::new(None),
selection_cache: Default::default(),
@ -619,7 +632,6 @@ impl<'tcx> InferCtxtBuilder<'tcx> {
tainted_by_errors: Cell::new(None),
err_count_on_creation: tcx.sess.err_count(),
in_snapshot: Cell::new(false),
skip_leak_check: Cell::new(false),
universe: Cell::new(ty::UniverseIndex::ROOT),
intercrate,
}
@ -815,32 +827,9 @@ impl<'tcx> InferCtxt<'tcx> {
r
}
/// If `should_skip` is true, then execute `f` then unroll any bindings it creates.
#[instrument(skip(self, f), level = "debug")]
pub fn probe_maybe_skip_leak_check<R, F>(&self, should_skip: bool, f: F) -> R
where
F: FnOnce(&CombinedSnapshot<'tcx>) -> R,
{
let snapshot = self.start_snapshot();
let was_skip_leak_check = self.skip_leak_check.get();
if should_skip {
self.skip_leak_check.set(true);
}
let r = f(&snapshot);
self.rollback_to("probe", snapshot);
self.skip_leak_check.set(was_skip_leak_check);
r
}
/// Scan the constraints produced since `snapshot` began and returns:
///
/// - `None` -- if none of them involves "region outlives" constraints.
/// - `Some(true)` -- if there are `'a: 'b` constraints where `'a` or `'b` is a placeholder.
/// - `Some(false)` -- if there are `'a: 'b` constraints but none involve placeholders.
pub fn region_constraints_added_in_snapshot(
&self,
snapshot: &CombinedSnapshot<'tcx>,
) -> Option<bool> {
/// Scan the constraints produced since `snapshot` and check whether
/// we added any region constraints.
pub fn region_constraints_added_in_snapshot(&self, snapshot: &CombinedSnapshot<'tcx>) -> bool {
self.inner
.borrow_mut()
.unwrap_region_constraints()

View File

@ -533,17 +533,29 @@ impl<'tcx> InferCtxt<'tcx> {
// these are the same span, but not in cases like `-> (impl
// Foo, impl Bar)`.
let span = cause.span;
let prev = self.inner.borrow_mut().opaque_types().register(
opaque_type_key,
OpaqueHiddenType { ty: hidden_ty, span },
origin,
);
let mut obligations = if let Some(prev) = prev {
self.at(&cause, param_env)
.eq_exp(DefineOpaqueTypes::Yes, a_is_expected, prev, hidden_ty)?
.obligations
let mut obligations = if self.intercrate {
// During intercrate we do not define opaque types but instead always
// force ambiguity unless the hidden type is known to not implement
// our trait.
vec![traits::Obligation::new(
self.tcx,
cause.clone(),
param_env,
ty::PredicateKind::Ambiguous,
)]
} else {
Vec::new()
let prev = self.inner.borrow_mut().opaque_types().register(
opaque_type_key,
OpaqueHiddenType { ty: hidden_ty, span },
origin,
);
if let Some(prev) = prev {
self.at(&cause, param_env)
.eq_exp(DefineOpaqueTypes::Yes, a_is_expected, prev, hidden_ty)?
.obligations
} else {
Vec::new()
}
};
self.add_item_bounds_for_hidden_type(

View File

@ -3,7 +3,6 @@ use crate::infer::CombinedSnapshot;
use rustc_data_structures::{
fx::FxIndexMap,
graph::{scc::Sccs, vec_graph::VecGraph},
undo_log::UndoLogs,
};
use rustc_index::Idx;
use rustc_middle::ty::error::TypeError;
@ -13,7 +12,9 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> {
/// Searches new universes created during `snapshot`, looking for
/// placeholders that may "leak" out from the universes they are contained
/// in. If any leaking placeholders are found, then an `Err` is returned
/// (typically leading to the snapshot being reversed).
/// (typically leading to the snapshot being reversed). This algorithm
/// only looks at placeholders which cannot be named by `outer_universe`,
/// as this is the universe we're currently checking for a leak.
///
/// The leak check *used* to be the only way we had to handle higher-ranked
/// obligations. Now that we have integrated universes into the region
@ -55,6 +56,12 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> {
/// * if they must also be equal to a placeholder P, and U cannot name P, report an error, as that
/// indicates `P: R` and `R` is in an incompatible universe
///
/// To improve performance and for the old trait solver caching to be sound, this takes
/// an optional snapshot in which case we only look at region constraints added in that
/// snapshot. If we were to not do that the `leak_check` during evaluation can rely on
/// region constraints added outside of that evaluation. As that is not reflected in the
/// cache key this would be unsound.
///
/// # Historical note
///
/// Older variants of the leak check used to report errors for these
@ -62,36 +69,21 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> {
///
/// * R: P1, even if R cannot name P1, because R = 'static is a valid sol'n
/// * R: P1, R: P2, as above
#[instrument(level = "debug", skip(self, tcx, only_consider_snapshot), ret)]
pub fn leak_check(
&mut self,
tcx: TyCtxt<'tcx>,
overly_polymorphic: bool,
outer_universe: ty::UniverseIndex,
max_universe: ty::UniverseIndex,
snapshot: &CombinedSnapshot<'tcx>,
only_consider_snapshot: Option<&CombinedSnapshot<'tcx>>,
) -> RelateResult<'tcx, ()> {
debug!(
"leak_check(max_universe={:?}, snapshot.universe={:?}, overly_polymorphic={:?})",
max_universe, snapshot.universe, overly_polymorphic
);
assert!(UndoLogs::<super::UndoLog<'_>>::in_snapshot(&self.undo_log));
let universe_at_start_of_snapshot = snapshot.universe;
if universe_at_start_of_snapshot == max_universe {
if outer_universe == max_universe {
return Ok(());
}
let mini_graph =
&MiniGraph::new(tcx, self.undo_log.region_constraints(), &self.storage.data.verifys);
let mini_graph = &MiniGraph::new(tcx, &self, only_consider_snapshot);
let mut leak_check = LeakCheck::new(
tcx,
universe_at_start_of_snapshot,
max_universe,
overly_polymorphic,
mini_graph,
self,
);
let mut leak_check = LeakCheck::new(tcx, outer_universe, max_universe, mini_graph, self);
leak_check.assign_placeholder_values()?;
leak_check.propagate_scc_value()?;
Ok(())
@ -100,9 +92,7 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> {
struct LeakCheck<'me, 'tcx> {
tcx: TyCtxt<'tcx>,
universe_at_start_of_snapshot: ty::UniverseIndex,
/// Only used when reporting region errors.
overly_polymorphic: bool,
outer_universe: ty::UniverseIndex,
mini_graph: &'me MiniGraph<'tcx>,
rcc: &'me RegionConstraintCollector<'me, 'tcx>,
@ -130,17 +120,15 @@ struct LeakCheck<'me, 'tcx> {
impl<'me, 'tcx> LeakCheck<'me, 'tcx> {
fn new(
tcx: TyCtxt<'tcx>,
universe_at_start_of_snapshot: ty::UniverseIndex,
outer_universe: ty::UniverseIndex,
max_universe: ty::UniverseIndex,
overly_polymorphic: bool,
mini_graph: &'me MiniGraph<'tcx>,
rcc: &'me RegionConstraintCollector<'me, 'tcx>,
) -> Self {
let dummy_scc_universe = SccUniverse { universe: max_universe, region: None };
Self {
tcx,
universe_at_start_of_snapshot,
overly_polymorphic,
outer_universe,
mini_graph,
rcc,
scc_placeholders: IndexVec::from_elem_n(None, mini_graph.sccs.num_sccs()),
@ -165,7 +153,7 @@ impl<'me, 'tcx> LeakCheck<'me, 'tcx> {
// Detect those SCCs that directly contain a placeholder
if let ty::RePlaceholder(placeholder) = **region {
if self.universe_at_start_of_snapshot.cannot_name(placeholder.universe) {
if self.outer_universe.cannot_name(placeholder.universe) {
self.assign_scc_value(scc, placeholder)?;
}
}
@ -289,11 +277,7 @@ impl<'me, 'tcx> LeakCheck<'me, 'tcx> {
other_region: ty::Region<'tcx>,
) -> TypeError<'tcx> {
debug!("error: placeholder={:?}, other_region={:?}", placeholder, other_region);
if self.overly_polymorphic {
TypeError::RegionsOverlyPolymorphic(placeholder.bound.kind, other_region)
} else {
TypeError::RegionsInsufficientlyPolymorphic(placeholder.bound.kind, other_region)
}
TypeError::RegionsInsufficientlyPolymorphic(placeholder.bound.kind, other_region)
}
}
@ -379,56 +363,70 @@ struct MiniGraph<'tcx> {
}
impl<'tcx> MiniGraph<'tcx> {
fn new<'a>(
fn new(
tcx: TyCtxt<'tcx>,
undo_log: impl Iterator<Item = &'a UndoLog<'tcx>>,
verifys: &[Verify<'tcx>],
) -> Self
where
'tcx: 'a,
{
region_constraints: &RegionConstraintCollector<'_, 'tcx>,
only_consider_snapshot: Option<&CombinedSnapshot<'tcx>>,
) -> Self {
let mut nodes = FxIndexMap::default();
let mut edges = Vec::new();
// Note that if `R2: R1`, we get a callback `r1, r2`, so `target` is first parameter.
Self::iterate_undo_log(tcx, undo_log, verifys, |target, source| {
let source_node = Self::add_node(&mut nodes, source);
let target_node = Self::add_node(&mut nodes, target);
edges.push((source_node, target_node));
});
Self::iterate_region_constraints(
tcx,
region_constraints,
only_consider_snapshot,
|target, source| {
let source_node = Self::add_node(&mut nodes, source);
let target_node = Self::add_node(&mut nodes, target);
edges.push((source_node, target_node));
},
);
let graph = VecGraph::new(nodes.len(), edges);
let sccs = Sccs::new(&graph);
Self { nodes, sccs }
}
/// Invokes `each_edge(R1, R2)` for each edge where `R2: R1`
fn iterate_undo_log<'a>(
fn iterate_region_constraints(
tcx: TyCtxt<'tcx>,
undo_log: impl Iterator<Item = &'a UndoLog<'tcx>>,
verifys: &[Verify<'tcx>],
region_constraints: &RegionConstraintCollector<'_, 'tcx>,
only_consider_snapshot: Option<&CombinedSnapshot<'tcx>>,
mut each_edge: impl FnMut(ty::Region<'tcx>, ty::Region<'tcx>),
) where
'tcx: 'a,
{
for undo_entry in undo_log {
match undo_entry {
&AddConstraint(Constraint::VarSubVar(a, b)) => {
each_edge(ty::Region::new_var(tcx, a), ty::Region::new_var(tcx, b));
) {
let mut each_constraint = |constraint| match constraint {
&Constraint::VarSubVar(a, b) => {
each_edge(ty::Region::new_var(tcx, a), ty::Region::new_var(tcx, b));
}
&Constraint::RegSubVar(a, b) => {
each_edge(a, ty::Region::new_var(tcx, b));
}
&Constraint::VarSubReg(a, b) => {
each_edge(ty::Region::new_var(tcx, a), b);
}
&Constraint::RegSubReg(a, b) => {
each_edge(a, b);
}
};
if let Some(snapshot) = only_consider_snapshot {
for undo_entry in
region_constraints.undo_log.region_constraints_in_snapshot(&snapshot.undo_snapshot)
{
match undo_entry {
AddConstraint(constraint) => {
each_constraint(constraint);
}
&AddVerify(i) => span_bug!(
region_constraints.data().verifys[i].origin.span(),
"we never add verifications while doing higher-ranked things",
),
&AddCombination(..) | &AddVar(..) => {}
}
&AddConstraint(Constraint::RegSubVar(a, b)) => {
each_edge(a, ty::Region::new_var(tcx, b));
}
&AddConstraint(Constraint::VarSubReg(a, b)) => {
each_edge(ty::Region::new_var(tcx, a), b);
}
&AddConstraint(Constraint::RegSubReg(a, b)) => {
each_edge(a, b);
}
&AddVerify(i) => span_bug!(
verifys[i].origin.span(),
"we never add verifications while doing higher-ranked things",
),
&AddCombination(..) | &AddVar(..) => {}
}
} else {
for (constraint, _origin) in &region_constraints.data().constraints {
each_constraint(constraint)
}
}
}

View File

@ -400,7 +400,7 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> {
data
}
pub(super) fn data(&self) -> &RegionConstraintData<'tcx> {
pub fn data(&self) -> &RegionConstraintData<'tcx> {
&self.data
}
@ -683,15 +683,10 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> {
}
/// See `InferCtxt::region_constraints_added_in_snapshot`.
pub fn region_constraints_added_in_snapshot(&self, mark: &Snapshot<'tcx>) -> Option<bool> {
pub fn region_constraints_added_in_snapshot(&self, mark: &Snapshot<'tcx>) -> bool {
self.undo_log
.region_constraints_in_snapshot(mark)
.map(|&elt| match elt {
AddConstraint(constraint) => Some(constraint.involves_placeholders()),
_ => None,
})
.max()
.unwrap_or(None)
.any(|&elt| matches!(elt, AddConstraint(_)))
}
#[inline]

View File

@ -138,11 +138,9 @@ impl<'tcx> InferCtxtInner<'tcx> {
}
if self.undo_log.num_open_snapshots == 1 {
// The root snapshot. It's safe to clear the undo log because
// there's no snapshot further out that we might need to roll back
// to.
// After the root snapshot the undo log should be empty.
assert!(snapshot.undo_len == 0);
self.undo_log.logs.clear();
assert!(self.undo_log.logs.is_empty());
}
self.undo_log.num_open_snapshots -= 1;
@ -183,15 +181,6 @@ impl<'tcx> InferCtxtUndoLogs<'tcx> {
self.logs[s.undo_len..].iter().any(|log| matches!(log, UndoLog::OpaqueTypes(..)))
}
pub(crate) fn region_constraints(
&self,
) -> impl Iterator<Item = &'_ region_constraints::UndoLog<'tcx>> + Clone {
self.logs.iter().filter_map(|log| match log {
UndoLog::RegionConstraintCollector(log) => Some(log),
_ => None,
})
}
fn assert_open_snapshot(&self, snapshot: &Snapshot<'tcx>) {
// Failures here may indicate a failure to follow a stack discipline.
assert!(self.logs.len() >= snapshot.undo_len);

View File

@ -45,7 +45,6 @@ pub enum TypeError<'tcx> {
RegionsDoesNotOutlive(Region<'tcx>, Region<'tcx>),
RegionsInsufficientlyPolymorphic(BoundRegionKind, Region<'tcx>),
RegionsOverlyPolymorphic(BoundRegionKind, Region<'tcx>),
RegionsPlaceholderMismatch,
Sorts(ExpectedFound<Ty<'tcx>>),
@ -74,7 +73,6 @@ impl TypeError<'_> {
match self {
TypeError::RegionsDoesNotOutlive(_, _)
| TypeError::RegionsInsufficientlyPolymorphic(_, _)
| TypeError::RegionsOverlyPolymorphic(_, _)
| TypeError::RegionsPlaceholderMismatch => true,
_ => false,
}
@ -98,11 +96,6 @@ impl<'tcx> TypeError<'tcx> {
}
}
let br_string = |br: ty::BoundRegionKind| match br {
ty::BrNamed(_, name) => format!(" {}", name),
_ => String::new(),
};
match self {
CyclicTy(_) => "cyclic type of infinite size".into(),
CyclicConst(_) => "encountered a self-referencing constant".into(),
@ -144,11 +137,6 @@ impl<'tcx> TypeError<'tcx> {
RegionsInsufficientlyPolymorphic(..) => {
"one type is more general than the other".into()
}
RegionsOverlyPolymorphic(br, _) => format!(
"expected concrete lifetime, found bound lifetime parameter{}",
br_string(br)
)
.into(),
RegionsPlaceholderMismatch => "one type is more general than the other".into(),
ArgumentSorts(values, _) | Sorts(values) => {
let expected = values.expected.sort_string(tcx);
@ -228,7 +216,6 @@ impl<'tcx> TypeError<'tcx> {
| FieldMisMatch(..)
| RegionsDoesNotOutlive(..)
| RegionsInsufficientlyPolymorphic(..)
| RegionsOverlyPolymorphic(..)
| RegionsPlaceholderMismatch
| Traits(_)
| ProjectionMismatched(_)

View File

@ -137,6 +137,13 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
#[instrument(level = "debug", skip(self), ret)]
fn compute_external_query_constraints(&self) -> Result<ExternalConstraints<'tcx>, NoSolution> {
// We only check for leaks from universes which were entered inside
// of the query.
self.infcx.leak_check(self.max_input_universe, None).map_err(|e| {
debug!(?e, "failed the leak check");
NoSolution
})?;
// Cannot use `take_registered_region_obligations` as we may compute the response
// inside of a `probe` whenever we have multiple choices inside of the solver.
let region_obligations = self.infcx.inner.borrow().region_obligations().to_owned();

View File

@ -5,7 +5,7 @@
//! [trait-specialization]: https://rustc-dev-guide.rust-lang.org/traits/specialization.html
use crate::infer::outlives::env::OutlivesEnvironment;
use crate::infer::{CombinedSnapshot, InferOk};
use crate::infer::InferOk;
use crate::traits::outlives_bounds::InferCtxtExt as _;
use crate::traits::select::IntercrateAmbiguityCause;
use crate::traits::util::impl_subject_and_oblig;
@ -62,6 +62,21 @@ pub fn add_placeholder_note(err: &mut Diagnostic) {
);
}
#[derive(Debug, Clone, Copy)]
enum TrackAmbiguityCauses {
Yes,
No,
}
impl TrackAmbiguityCauses {
fn is_yes(self) -> bool {
match self {
TrackAmbiguityCauses::Yes => true,
TrackAmbiguityCauses::No => false,
}
}
}
/// If there are types that satisfy both impls, returns `Some`
/// with a suitably-freshened `ImplHeader` with those types
/// substituted. Otherwise, returns `None`.
@ -97,29 +112,28 @@ pub fn overlapping_impls(
return None;
}
let infcx = tcx
.infer_ctxt()
.with_opaque_type_inference(DefiningAnchor::Bubble)
.intercrate(true)
.build();
let selcx = &mut SelectionContext::new(&infcx);
let overlaps =
overlap(selcx, skip_leak_check, impl1_def_id, impl2_def_id, overlap_mode).is_some();
if !overlaps {
return None;
}
let _overlap_with_bad_diagnostics = overlap(
tcx,
TrackAmbiguityCauses::No,
skip_leak_check,
impl1_def_id,
impl2_def_id,
overlap_mode,
)?;
// In the case where we detect an error, run the check again, but
// this time tracking intercrate ambiguity causes for better
// diagnostics. (These take time and can lead to false errors.)
let infcx = tcx
.infer_ctxt()
.with_opaque_type_inference(DefiningAnchor::Bubble)
.intercrate(true)
.build();
let selcx = &mut SelectionContext::new(&infcx);
selcx.enable_tracking_intercrate_ambiguity_causes();
Some(overlap(selcx, skip_leak_check, impl1_def_id, impl2_def_id, overlap_mode).unwrap())
let overlap = overlap(
tcx,
TrackAmbiguityCauses::Yes,
skip_leak_check,
impl1_def_id,
impl2_def_id,
overlap_mode,
)
.unwrap();
Some(overlap)
}
fn with_fresh_ty_vars<'cx, 'tcx>(
@ -146,40 +160,34 @@ fn with_fresh_ty_vars<'cx, 'tcx>(
/// Can both impl `a` and impl `b` be satisfied by a common type (including
/// where-clauses)? If so, returns an `ImplHeader` that unifies the two impls.
fn overlap<'cx, 'tcx>(
selcx: &mut SelectionContext<'cx, 'tcx>,
#[instrument(level = "debug", skip(tcx))]
fn overlap<'tcx>(
tcx: TyCtxt<'tcx>,
track_ambiguity_causes: TrackAmbiguityCauses,
skip_leak_check: SkipLeakCheck,
impl1_def_id: DefId,
impl2_def_id: DefId,
overlap_mode: OverlapMode,
) -> Option<OverlapResult<'tcx>> {
debug!(
"overlap(impl1_def_id={:?}, impl2_def_id={:?}, overlap_mode={:?})",
impl1_def_id, impl2_def_id, overlap_mode
);
selcx.infcx.probe_maybe_skip_leak_check(skip_leak_check.is_yes(), |snapshot| {
overlap_within_probe(selcx, impl1_def_id, impl2_def_id, overlap_mode, snapshot)
})
}
fn overlap_within_probe<'cx, 'tcx>(
selcx: &mut SelectionContext<'cx, 'tcx>,
impl1_def_id: DefId,
impl2_def_id: DefId,
overlap_mode: OverlapMode,
snapshot: &CombinedSnapshot<'tcx>,
) -> Option<OverlapResult<'tcx>> {
let infcx = selcx.infcx;
if overlap_mode.use_negative_impl() {
if negative_impl(infcx.tcx, impl1_def_id, impl2_def_id)
|| negative_impl(infcx.tcx, impl2_def_id, impl1_def_id)
if negative_impl(tcx, impl1_def_id, impl2_def_id)
|| negative_impl(tcx, impl2_def_id, impl1_def_id)
{
return None;
}
}
let infcx = tcx
.infer_ctxt()
.with_opaque_type_inference(DefiningAnchor::Bubble)
.skip_leak_check(skip_leak_check.is_yes())
.intercrate(true)
.build();
let selcx = &mut SelectionContext::new(&infcx);
if track_ambiguity_causes.is_yes() {
selcx.enable_tracking_intercrate_ambiguity_causes();
}
// For the purposes of this check, we don't bring any placeholder
// types into scope; instead, we replace the generic types with
// fresh type variables, and hence we do our evaluations in an
@ -198,18 +206,23 @@ fn overlap_within_probe<'cx, 'tcx>(
}
}
// We disable the leak when creating the `snapshot` by using
// `infcx.probe_maybe_disable_leak_check`.
if infcx.leak_check(true, snapshot).is_err() {
// We toggle the `leak_check` by using `skip_leak_check` when constructing the
// inference context, so this may be a noop.
if infcx.leak_check(ty::UniverseIndex::ROOT, None).is_err() {
debug!("overlap: leak check failed");
return None;
}
let intercrate_ambiguity_causes = selcx.take_intercrate_ambiguity_causes();
debug!("overlap: intercrate_ambiguity_causes={:#?}", intercrate_ambiguity_causes);
let involves_placeholder =
matches!(selcx.infcx.region_constraints_added_in_snapshot(snapshot), Some(true));
let involves_placeholder = infcx
.inner
.borrow_mut()
.unwrap_region_constraints()
.data()
.constraints
.iter()
.any(|c| c.0.involves_placeholders());
let impl_header = selcx.infcx.resolve_vars_if_possible(impl1_header);
Some(OverlapResult { impl_header, intercrate_ambiguity_causes, involves_placeholder })

View File

@ -90,7 +90,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
Ok(EvaluationResult::EvaluatedToAmbig)
} else if self.opaque_types_added_in_snapshot(snapshot) {
Ok(EvaluationResult::EvaluatedToOkModuloOpaqueTypes)
} else if self.region_constraints_added_in_snapshot(snapshot).is_some() {
} else if self.region_constraints_added_in_snapshot(snapshot) {
Ok(EvaluationResult::EvaluatedToOkModuloRegions)
} else {
Ok(EvaluationResult::EvaluatedToOk)

View File

@ -561,9 +561,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
op: impl FnOnce(&mut Self) -> Result<EvaluationResult, OverflowError>,
) -> Result<EvaluationResult, OverflowError> {
self.infcx.probe(|snapshot| -> Result<EvaluationResult, OverflowError> {
let outer_universe = self.infcx.universe();
let result = op(self)?;
match self.infcx.leak_check(true, snapshot) {
match self.infcx.leak_check(outer_universe, Some(snapshot)) {
Ok(()) => {}
Err(_) => return Ok(EvaluatedToErr),
}
@ -572,9 +573,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
return Ok(result.max(EvaluatedToOkModuloOpaqueTypes));
}
match self.infcx.region_constraints_added_in_snapshot(snapshot) {
None => Ok(result),
Some(_) => Ok(result.max(EvaluatedToOkModuloRegions)),
if self.infcx.region_constraints_added_in_snapshot(snapshot) {
Ok(result.max(EvaluatedToOkModuloRegions))
} else {
Ok(result)
}
})
}

View File

@ -11,7 +11,7 @@ use std::path::{Path, PathBuf};
const ENTRY_LIMIT: usize = 900;
// FIXME: The following limits should be reduced eventually.
const ISSUES_ENTRY_LIMIT: usize = 1898;
const ROOT_ENTRY_LIMIT: usize = 894;
const ROOT_ENTRY_LIMIT: usize = 891;
const EXPECTED_TEST_FILE_EXTENSIONS: &[&str] = &[
"rs", // test source files

View File

@ -1,14 +0,0 @@
error[E0592]: duplicate definitions with name `method1`
--> $DIR/coherence-inherited-subtyping.rs:14:5
|
LL | fn method1(&self) {}
| ^^^^^^^^^^^^^^^^^ duplicate definitions for `method1`
...
LL | fn method1(&self) {}
| ----------------- other definition for `method1`
|
= note: this behavior recently changed as a result of a bug fix; see rust-lang/rust#56105 for details
error: aborting due to previous error
For more information about this error, try `rustc --explain E0592`.

View File

@ -4,8 +4,6 @@
// Note: This scenario is currently accepted, but as part of the
// universe transition (#56105) may eventually become an error.
// revisions: old re
struct Foo<T> {
t: T,
}

View File

@ -1,5 +1,5 @@
error[E0592]: duplicate definitions with name `method1`
--> $DIR/coherence-inherited-subtyping.rs:14:5
--> $DIR/coherence-inherited-subtyping.rs:12:5
|
LL | fn method1(&self) {}
| ^^^^^^^^^^^^^^^^^ duplicate definitions for `method1`

View File

@ -1,6 +1,6 @@
// check-pass
// revisions: old new
//[new] compile-flags: -Ztrait-solver=next
// revisions: old next
//[next] compile-flags: -Ztrait-solver=next
// If we use canonical goals during trait solving we have to reevaluate
// the root goal of a cycle until we hit a fixpoint.

View File

@ -1,11 +0,0 @@
// revisions: old new
//[new] compile-flags: -Ztrait-solver=next
//[old] check-pass
//[new] known-bug: #109764
pub struct Bar
where
for<'a> &'a mut Self:;
fn main() {}

View File

@ -0,0 +1,24 @@
// run-pass
// revisions: old next
//[next] compile-flags: -Ztrait-solver=next
#![allow(coherence_leak_check)]
trait Trait: Sized {
fn is_higher_ranked(self) -> bool;
}
impl Trait for for<'a> fn(&'a ()) {
fn is_higher_ranked(self) -> bool {
true
}
}
impl<'a> Trait for fn(&'a ()) {
fn is_higher_ranked(self) -> bool {
false
}
}
fn main() {
let x: for<'a> fn(&'a ()) = |&()| ();
assert!(x.is_higher_ranked());
}

View File

@ -14,7 +14,7 @@ LL | f
| ^ expected `&dyn Fn(&dyn Fn(&dyn Fn(&...)))`, found `&dyn Fn(u32)`
|
= note: expected reference `&dyn Fn(&dyn Fn(&dyn Fn(&dyn Fn(&dyn Fn(&dyn Fn(&dyn Fn(&dyn Fn(&dyn Fn(&dyn Fn(&dyn Fn(&...)))))))))))`
the full type name has been written to '$TEST_BUILD_DIR/higher-rank-trait-bounds/hang-on-deeply-nested-dyn/hang-on-deeply-nested-dyn.long-type-hash.txt'
the full type name has been written to '$TEST_BUILD_DIR/higher-ranked/trait-bounds/hang-on-deeply-nested-dyn/hang-on-deeply-nested-dyn.long-type-hash.txt'
found reference `&dyn Fn(u32)`
error: aborting due to previous error

View File

@ -31,7 +31,7 @@ LL | pub struct Filter<S, F> {
LL | let count = filter.countx();
| ^^^^^^ method cannot be called due to unsatisfied trait bounds
|
= note: the full type name has been written to '$TEST_BUILD_DIR/higher-rank-trait-bounds/issue-30786/issue-30786.long-type-hash.txt'
= note: the full type name has been written to '$TEST_BUILD_DIR/higher-ranked/trait-bounds/issue-30786/issue-30786.long-type-hash.txt'
note: the following trait bounds were not satisfied:
`&'a mut &Filter<Map<Repeat, for<'a> fn(&'a u64) -> &'a u64 {identity::<u64>}>, [closure@$DIR/issue-30786.rs:131:30: 131:37]>: Stream`
`&'a mut &mut Filter<Map<Repeat, for<'a> fn(&'a u64) -> &'a u64 {identity::<u64>}>, [closure@$DIR/issue-30786.rs:131:30: 131:37]>: Stream`

Some files were not shown because too many files have changed in this diff Show More