1
Fork 0

Auto merge of #122077 - oli-obk:eager_opaque_checks4, r=lcnr

Pass list of defineable opaque types into canonical queries

This eliminates `DefiningAnchor::Bubble` for good and brings the old solver closer to the new one wrt cycles and nested obligations. At that point the difference between `DefiningAnchor::Bind([])` and `DefiningAnchor::Error` was academic. We only used the difference for some sanity checks, which actually had to be worked around in places, so I just removed `DefiningAnchor` entirely and just stored the list of opaques that may be defined.

fixes #108498
fixes https://github.com/rust-lang/rust/issues/116877

* [x] run crater
  - https://github.com/rust-lang/rust/pull/122077#issuecomment-2013293931
This commit is contained in:
bors 2024-04-08 23:01:50 +00:00
commit b234e44944
59 changed files with 469 additions and 387 deletions

View file

@ -1,5 +1,5 @@
use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
use crate::traits::{self, DefiningAnchor, ObligationCtxt, SelectionContext};
use crate::traits::{self, ObligationCtxt, SelectionContext};
use crate::traits::TraitEngineExt as _;
use rustc_hir::def_id::DefId;
@ -132,9 +132,8 @@ impl<'tcx> InferCtxtBuilder<'tcx> {
R: Debug + TypeFoldable<TyCtxt<'tcx>>,
Canonical<'tcx, QueryResponse<'tcx, R>>: ArenaAllocatable<'tcx>,
{
let (infcx, key, canonical_inference_vars) = self
.with_opaque_type_inference(DefiningAnchor::Bubble)
.build_with_canonical(DUMMY_SP, canonical_key);
let (infcx, key, canonical_inference_vars) =
self.build_with_canonical(DUMMY_SP, canonical_key);
let ocx = ObligationCtxt::new(&infcx);
let value = operation(&ocx, key)?;
ocx.make_canonicalized_query_response(canonical_inference_vars, value)

View file

@ -68,7 +68,6 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
&mut orig_values,
QueryInput {
goal,
anchor: self.infcx.defining_use_anchor,
predefined_opaques_in_body: self
.tcx()
.mk_predefined_opaques_in_body(PredefinedOpaquesData { opaque_types }),

View file

@ -1,5 +1,5 @@
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::def_id::DefId;
use rustc_infer::infer::at::ToTrace;
use rustc_infer::infer::canonical::CanonicalVarValues;
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
@ -230,7 +230,6 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
.infer_ctxt()
.intercrate(intercrate)
.with_next_trait_solver(true)
.with_opaque_type_inference(canonical_input.value.anchor)
.build_with_canonical(DUMMY_SP, &canonical_input);
let mut ecx = EvalCtxt {
@ -936,8 +935,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
}
}
pub(super) fn can_define_opaque_ty(&self, def_id: LocalDefId) -> bool {
self.infcx.opaque_type_origin(def_id).is_some()
pub(super) fn can_define_opaque_ty(&self, def_id: impl Into<DefId>) -> bool {
self.infcx.can_define_opaque_ty(def_id)
}
pub(super) fn insert_hidden_type(

View file

@ -316,5 +316,6 @@ fn response_no_constraints_raw<'tcx>(
external_constraints: tcx.mk_external_constraints(ExternalConstraintsData::default()),
certainty,
},
defining_opaque_types: Default::default(),
}
}

View file

@ -155,10 +155,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
if let ty::Alias(ty::Opaque, opaque_ty) = goal.predicate.self_ty().kind() {
if matches!(goal.param_env.reveal(), Reveal::All)
|| matches!(ecx.solver_mode(), SolverMode::Coherence)
|| opaque_ty
.def_id
.as_local()
.is_some_and(|def_id| ecx.can_define_opaque_ty(def_id))
|| ecx.can_define_opaque_ty(opaque_ty.def_id)
{
return Err(NoSolution);
}

View file

@ -159,53 +159,70 @@ where
.0);
}
let mut error_info = None;
let mut region_constraints = QueryRegionConstraints::default();
let (output, error_info, mut obligations, _) =
Q::fully_perform_into(self, infcx, &mut region_constraints, span).map_err(|_| {
infcx.dcx().span_delayed_bug(span, format!("error performing {self:?}"))
})?;
// Typically, instantiating NLL query results does not
// create obligations. However, in some cases there
// are unresolved type variables, and unify them *can*
// create obligations. In that case, we have to go
// fulfill them. We do this via a (recursive) query.
while !obligations.is_empty() {
trace!("{:#?}", obligations);
let mut progress = false;
for obligation in std::mem::take(&mut obligations) {
let obligation = infcx.resolve_vars_if_possible(obligation);
match ProvePredicate::fully_perform_into(
obligation.param_env.and(ProvePredicate::new(obligation.predicate)),
infcx,
&mut region_constraints,
span,
) {
Ok(((), _, new, certainty)) => {
obligations.extend(new);
progress = true;
if let Certainty::Ambiguous = certainty {
obligations.push(obligation);
// HACK(type_alias_impl_trait): When moving an opaque type to hidden type mapping from the query to the current inferctxt,
// we sometimes end up with `Opaque<'a> = Opaque<'b>` instead of an actual hidden type. In that case we don't register a
// hidden type but just equate the lifetimes. Thus we need to scrape the region constraints even though we're also manually
// collecting region constraints via `region_constraints`.
let (mut output, _) = scrape_region_constraints(
infcx,
|_ocx| {
let (output, ei, mut obligations, _) =
Q::fully_perform_into(self, infcx, &mut region_constraints, span)?;
error_info = ei;
// Typically, instantiating NLL query results does not
// create obligations. However, in some cases there
// are unresolved type variables, and unify them *can*
// create obligations. In that case, we have to go
// fulfill them. We do this via a (recursive) query.
while !obligations.is_empty() {
trace!("{:#?}", obligations);
let mut progress = false;
for obligation in std::mem::take(&mut obligations) {
let obligation = infcx.resolve_vars_if_possible(obligation);
match ProvePredicate::fully_perform_into(
obligation.param_env.and(ProvePredicate::new(obligation.predicate)),
infcx,
&mut region_constraints,
span,
) {
Ok(((), _, new, certainty)) => {
obligations.extend(new);
progress = true;
if let Certainty::Ambiguous = certainty {
obligations.push(obligation);
}
}
Err(_) => obligations.push(obligation),
}
}
Err(_) => obligations.push(obligation),
if !progress {
infcx.dcx().span_bug(
span,
format!("ambiguity processing {obligations:?} from {self:?}"),
);
}
}
}
if !progress {
infcx
.dcx()
.span_bug(span, format!("ambiguity processing {obligations:?} from {self:?}"));
}
}
Ok(TypeOpOutput {
output,
constraints: if region_constraints.is_empty() {
None
} else {
Some(infcx.tcx.arena.alloc(region_constraints))
Ok(output)
},
error_info,
})
"fully_perform",
span,
)?;
output.error_info = error_info;
if let Some(constraints) = output.constraints {
region_constraints
.member_constraints
.extend(constraints.member_constraints.iter().cloned());
region_constraints.outlives.extend(constraints.outlives.iter().cloned());
}
output.constraints = if region_constraints.is_empty() {
None
} else {
Some(infcx.tcx.arena.alloc(region_constraints))
};
Ok(output)
}
}