Prepopulate opaques in canonical input

This commit is contained in:
Michael Goulet 2023-05-16 23:51:23 +00:00
parent a2d7ffc635
commit f3c9c21658
11 changed files with 243 additions and 81 deletions

View File

@ -121,6 +121,7 @@ macro_rules! arena_types {
>, >,
[] bit_set_u32: rustc_index::bit_set::BitSet<u32>, [] bit_set_u32: rustc_index::bit_set::BitSet<u32>,
[] external_constraints: rustc_middle::traits::solve::ExternalConstraintsData<'tcx>, [] external_constraints: rustc_middle::traits::solve::ExternalConstraintsData<'tcx>,
[] predefined_opaques_in_body: rustc_middle::traits::solve::PredefinedOpaquesData<'tcx>,
[decode] doc_link_resolutions: rustc_hir::def::DocLinkResMap, [decode] doc_link_resolutions: rustc_hir::def::DocLinkResMap,
[] closure_kind_origin: (rustc_span::Span, rustc_middle::hir::place::Place<'tcx>), [] closure_kind_origin: (rustc_span::Span, rustc_middle::hir::place::Place<'tcx>),
[] mod_child: rustc_middle::metadata::ModChild, [] mod_child: rustc_middle::metadata::ModChild,

View File

@ -5,13 +5,13 @@
use crate::infer::canonical::{CanonicalVarValues, QueryRegionConstraints}; use crate::infer::canonical::{CanonicalVarValues, QueryRegionConstraints};
use crate::traits::query::NoSolution; use crate::traits::query::NoSolution;
use crate::traits::Canonical; use crate::traits::{Canonical, DefiningAnchor};
use crate::ty::{ use crate::ty::{
self, FallibleTypeFolder, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeVisitable, self, FallibleTypeFolder, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeVisitable,
TypeVisitor, TypeVisitor,
}; };
pub type EvaluationCache<'tcx> = Cache<CanonicalGoal<'tcx>, QueryResult<'tcx>>; pub type EvaluationCache<'tcx> = Cache<CanonicalInput<'tcx>, QueryResult<'tcx>>;
/// A goal is a statement, i.e. `predicate`, we want to prove /// A goal is a statement, i.e. `predicate`, we want to prove
/// given some assumptions, i.e. `param_env`. /// given some assumptions, i.e. `param_env`.
@ -96,7 +96,31 @@ pub enum MaybeCause {
Overflow, Overflow,
} }
pub type CanonicalGoal<'tcx, T = ty::Predicate<'tcx>> = Canonical<'tcx, Goal<'tcx, T>>; #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, TypeFoldable, TypeVisitable)]
pub struct QueryInput<'tcx, T> {
pub goal: Goal<'tcx, T>,
pub anchor: DefiningAnchor,
pub predefined_opaques_in_body: PredefinedOpaques<'tcx>,
}
/// Additional constraints returned on success.
#[derive(Debug, PartialEq, Eq, Clone, Hash, Default)]
pub struct PredefinedOpaquesData<'tcx> {
pub opaque_types: Vec<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)>,
}
#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
pub struct PredefinedOpaques<'tcx>(pub(crate) Interned<'tcx, PredefinedOpaquesData<'tcx>>);
impl<'tcx> std::ops::Deref for PredefinedOpaques<'tcx> {
type Target = PredefinedOpaquesData<'tcx>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
pub type CanonicalInput<'tcx, T = ty::Predicate<'tcx>> = Canonical<'tcx, QueryInput<'tcx, T>>;
pub type CanonicalResponse<'tcx> = Canonical<'tcx, Response<'tcx>>; pub type CanonicalResponse<'tcx> = Canonical<'tcx, Response<'tcx>>;
@ -165,3 +189,40 @@ fn visit_with<V: TypeVisitor<TyCtxt<'tcx>>>(
ControlFlow::Continue(()) ControlFlow::Continue(())
} }
} }
// FIXME: Having to clone `region_constraints` for folding feels bad and
// probably isn't great wrt performance.
//
// Not sure how to fix this, maybe we should also intern `opaque_types` and
// `region_constraints` here or something.
impl<'tcx> TypeFoldable<TyCtxt<'tcx>> for PredefinedOpaques<'tcx> {
fn try_fold_with<F: FallibleTypeFolder<TyCtxt<'tcx>>>(
self,
folder: &mut F,
) -> Result<Self, F::Error> {
Ok(FallibleTypeFolder::interner(folder).mk_predefined_opaques_in_body(
PredefinedOpaquesData {
opaque_types: self
.opaque_types
.iter()
.map(|opaque| opaque.try_fold_with(folder))
.collect::<Result<_, F::Error>>()?,
},
))
}
fn fold_with<F: TypeFolder<TyCtxt<'tcx>>>(self, folder: &mut F) -> Self {
TypeFolder::interner(folder).mk_predefined_opaques_in_body(PredefinedOpaquesData {
opaque_types: self.opaque_types.iter().map(|opaque| opaque.fold_with(folder)).collect(),
})
}
}
impl<'tcx> TypeVisitable<TyCtxt<'tcx>> for PredefinedOpaques<'tcx> {
fn visit_with<V: TypeVisitor<TyCtxt<'tcx>>>(
&self,
visitor: &mut V,
) -> std::ops::ControlFlow<V::BreakTy> {
self.opaque_types.visit_with(visitor)
}
}

View File

@ -21,7 +21,9 @@
use crate::thir::Thir; use crate::thir::Thir;
use crate::traits; use crate::traits;
use crate::traits::solve; use crate::traits::solve;
use crate::traits::solve::{ExternalConstraints, ExternalConstraintsData}; use crate::traits::solve::{
ExternalConstraints, ExternalConstraintsData, PredefinedOpaques, PredefinedOpaquesData,
};
use crate::ty::{ use crate::ty::{
self, AdtDef, AdtDefData, AdtKind, Binder, Const, ConstData, FloatTy, FloatVar, FloatVid, self, AdtDef, AdtDefData, AdtKind, Binder, Const, ConstData, FloatTy, FloatVar, FloatVid,
GenericParamDefKind, ImplPolarity, InferTy, IntTy, IntVar, IntVid, List, ParamConst, ParamTy, GenericParamDefKind, ImplPolarity, InferTy, IntTy, IntVar, IntVid, List, ParamConst, ParamTy,
@ -140,6 +142,7 @@ pub struct CtxtInterners<'tcx> {
layout: InternedSet<'tcx, LayoutS>, layout: InternedSet<'tcx, LayoutS>,
adt_def: InternedSet<'tcx, AdtDefData>, adt_def: InternedSet<'tcx, AdtDefData>,
external_constraints: InternedSet<'tcx, ExternalConstraintsData<'tcx>>, external_constraints: InternedSet<'tcx, ExternalConstraintsData<'tcx>>,
predefined_opaques_in_body: InternedSet<'tcx, PredefinedOpaquesData<'tcx>>,
fields: InternedSet<'tcx, List<FieldIdx>>, fields: InternedSet<'tcx, List<FieldIdx>>,
} }
@ -164,6 +167,7 @@ fn new(arena: &'tcx WorkerLocal<Arena<'tcx>>) -> CtxtInterners<'tcx> {
layout: Default::default(), layout: Default::default(),
adt_def: Default::default(), adt_def: Default::default(),
external_constraints: Default::default(), external_constraints: Default::default(),
predefined_opaques_in_body: Default::default(),
fields: Default::default(), fields: Default::default(),
} }
} }
@ -1520,6 +1524,8 @@ impl<'tcx> TyCtxt<'tcx> {
adt_def: pub mk_adt_def_from_data(AdtDefData): AdtDef -> AdtDef<'tcx>, adt_def: pub mk_adt_def_from_data(AdtDefData): AdtDef -> AdtDef<'tcx>,
external_constraints: pub mk_external_constraints(ExternalConstraintsData<'tcx>): external_constraints: pub mk_external_constraints(ExternalConstraintsData<'tcx>):
ExternalConstraints -> ExternalConstraints<'tcx>, ExternalConstraints -> ExternalConstraints<'tcx>,
predefined_opaques_in_body: pub mk_predefined_opaques_in_body(PredefinedOpaquesData<'tcx>):
PredefinedOpaques -> PredefinedOpaques<'tcx>,
} }
macro_rules! slice_interners { macro_rules! slice_interners {

View File

@ -333,8 +333,7 @@ fn assemble_candidates_after_normalizing_self_ty<G: GoalKind<'tcx>>(
candidates: &mut Vec<Candidate<'tcx>>, candidates: &mut Vec<Candidate<'tcx>>,
) { ) {
let tcx = self.tcx(); let tcx = self.tcx();
// FIXME: We also have to normalize opaque types, not sure where to best fit that in. let &ty::Alias(_, projection_ty) = goal.predicate.self_ty().kind() else {
let &ty::Alias(ty::Projection, projection_ty) = goal.predicate.self_ty().kind() else {
return return
}; };
@ -356,8 +355,11 @@ fn assemble_candidates_after_normalizing_self_ty<G: GoalKind<'tcx>>(
}), }),
); );
ecx.add_goal(normalizes_to_goal); ecx.add_goal(normalizes_to_goal);
let _ = ecx.try_evaluate_added_goals()?; let _ = ecx.try_evaluate_added_goals().inspect_err(|_| {
debug!("self type normalization failed");
})?;
let normalized_ty = ecx.resolve_vars_if_possible(normalized_ty); let normalized_ty = ecx.resolve_vars_if_possible(normalized_ty);
debug!(?normalized_ty, "self type normalized");
// NOTE: Alternatively we could call `evaluate_goal` here and only // NOTE: Alternatively we could call `evaluate_goal` here and only
// have a `Normalized` candidate. This doesn't work as long as we // have a `Normalized` candidate. This doesn't work as long as we
// use `CandidateSource` in winnowing. // use `CandidateSource` in winnowing.

View File

@ -9,7 +9,10 @@
use rustc_infer::traits::query::NoSolution; use rustc_infer::traits::query::NoSolution;
use rustc_infer::traits::ObligationCause; use rustc_infer::traits::ObligationCause;
use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind};
use rustc_middle::traits::solve::{CanonicalGoal, Certainty, MaybeCause, QueryResult}; use rustc_middle::traits::solve::{
CanonicalInput, Certainty, MaybeCause, PredefinedOpaques, PredefinedOpaquesData, QueryResult,
};
use rustc_middle::traits::DefiningAnchor;
use rustc_middle::ty::{ use rustc_middle::ty::{
self, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, self, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt,
TypeVisitor, TypeVisitor,
@ -44,6 +47,9 @@ pub struct EvalCtxt<'a, 'tcx> {
infcx: &'a InferCtxt<'tcx>, infcx: &'a InferCtxt<'tcx>,
pub(super) var_values: CanonicalVarValues<'tcx>, pub(super) var_values: CanonicalVarValues<'tcx>,
predefined_opaques_in_body: PredefinedOpaques<'tcx>,
/// The highest universe index nameable by the caller. /// The highest universe index nameable by the caller.
/// ///
/// When we enter a new binder inside of the query we create new universes /// When we enter a new binder inside of the query we create new universes
@ -126,6 +132,11 @@ fn evaluate_root_goal(
let mut ecx = EvalCtxt { let mut ecx = EvalCtxt {
search_graph: &mut search_graph, search_graph: &mut search_graph,
infcx: self, infcx: self,
// Only relevant when canonicalizing the response,
// which we don't do within this evaluation context.
predefined_opaques_in_body: self
.tcx
.mk_predefined_opaques_in_body(PredefinedOpaquesData::default()),
// Only relevant when canonicalizing the response. // Only relevant when canonicalizing the response.
max_input_universe: ty::UniverseIndex::ROOT, max_input_universe: ty::UniverseIndex::ROOT,
var_values: CanonicalVarValues::dummy(), var_values: CanonicalVarValues::dummy(),
@ -162,29 +173,59 @@ pub(super) fn solver_mode(&self) -> SolverMode {
fn evaluate_canonical_goal( fn evaluate_canonical_goal(
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
search_graph: &'a mut search_graph::SearchGraph<'tcx>, search_graph: &'a mut search_graph::SearchGraph<'tcx>,
canonical_goal: CanonicalGoal<'tcx>, canonical_input: CanonicalInput<'tcx>,
) -> QueryResult<'tcx> { ) -> QueryResult<'tcx> {
// Deal with overflow, caching, and coinduction. // Deal with overflow, caching, and coinduction.
// //
// The actual solver logic happens in `ecx.compute_goal`. // The actual solver logic happens in `ecx.compute_goal`.
search_graph.with_new_goal(tcx, canonical_goal, |search_graph| { search_graph.with_new_goal(tcx, canonical_input, |search_graph| {
let intercrate = match search_graph.solver_mode() { let intercrate = match search_graph.solver_mode() {
SolverMode::Normal => false, SolverMode::Normal => false,
SolverMode::Coherence => true, SolverMode::Coherence => true,
}; };
let (ref infcx, goal, var_values) = tcx let (ref infcx, input, var_values) = tcx
.infer_ctxt() .infer_ctxt()
.intercrate(intercrate) .intercrate(intercrate)
.build_with_canonical(DUMMY_SP, &canonical_goal); .with_opaque_type_inference(canonical_input.value.anchor)
.build_with_canonical(DUMMY_SP, &canonical_input);
for &(a, b) in &input.predefined_opaques_in_body.opaque_types {
let InferOk { value: (), obligations } = infcx
.handle_opaque_type(
tcx.mk_opaque(a.def_id.to_def_id(), a.substs),
b,
true,
&ObligationCause::dummy(),
input.goal.param_env,
)
.expect("expected opaque type instantiation to succeed");
// We're only registering opaques already defined by the caller,
// so we're not responsible for proving that they satisfy their
// item bounds, unless we use them in a normalizes-to goal,
// which is handled in `EvalCtxt::unify_existing_opaque_tys`.
let _ = obligations;
}
let mut ecx = EvalCtxt { let mut ecx = EvalCtxt {
infcx, infcx,
var_values, var_values,
max_input_universe: canonical_goal.max_universe, predefined_opaques_in_body: input.predefined_opaques_in_body,
max_input_universe: canonical_input.max_universe,
search_graph, search_graph,
nested_goals: NestedGoals::new(), nested_goals: NestedGoals::new(),
tainted: Ok(()), tainted: Ok(()),
}; };
ecx.compute_goal(goal)
let result = ecx.compute_goal(input.goal);
// When creating a query response we clone the opaque type constraints
// instead of taking them. This would cause an ICE here, since we have
// assertions against dropping an `InferCtxt` without taking opaques.
// FIXME: Once we remove support for the old impl we can remove this.
if input.anchor != DefiningAnchor::Error {
let _ = infcx.take_opaque_types();
}
result
}) })
} }
@ -199,7 +240,8 @@ fn evaluate_goal(
let canonical_response = let canonical_response =
EvalCtxt::evaluate_canonical_goal(self.tcx(), self.search_graph, canonical_goal)?; EvalCtxt::evaluate_canonical_goal(self.tcx(), self.search_graph, canonical_goal)?;
let has_changed = !canonical_response.value.var_values.is_identity(); let has_changed = !canonical_response.value.var_values.is_identity()
|| !canonical_response.value.external_constraints.opaque_types.is_empty();
let (certainty, nested_goals) = self.instantiate_and_apply_query_response( let (certainty, nested_goals) = self.instantiate_and_apply_query_response(
goal.param_env, goal.param_env,
orig_values, orig_values,
@ -418,6 +460,7 @@ pub(super) fn probe<T>(&mut self, f: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> T)
let mut ecx = EvalCtxt { let mut ecx = EvalCtxt {
infcx: self.infcx, infcx: self.infcx,
var_values: self.var_values, var_values: self.var_values,
predefined_opaques_in_body: self.predefined_opaques_in_body,
max_input_universe: self.max_input_universe, max_input_universe: self.max_input_universe,
search_graph: self.search_graph, search_graph: self.search_graph,
nested_goals: self.nested_goals.clone(), nested_goals: self.nested_goals.clone(),
@ -682,4 +725,16 @@ pub(super) fn is_transmutable(
| rustc_transmute::Answer::IfAny(_) => Err(NoSolution), | rustc_transmute::Answer::IfAny(_) => Err(NoSolution),
} }
} }
pub(super) fn handle_opaque_ty(
&mut self,
a: Ty<'tcx>,
b: Ty<'tcx>,
param_env: ty::ParamEnv<'tcx>,
) -> Result<(), NoSolution> {
let InferOk { value: (), obligations } =
self.infcx.handle_opaque_type(a, b, true, &ObligationCause::dummy(), param_env)?;
self.add_goals(obligations.into_iter().map(|obligation| obligation.into()));
Ok(())
}
} }

View File

@ -8,7 +8,7 @@
/// section of the [rustc-dev-guide][c]. /// section of the [rustc-dev-guide][c].
/// ///
/// [c]: https://rustc-dev-guide.rust-lang.org/solve/canonicalization.html /// [c]: https://rustc-dev-guide.rust-lang.org/solve/canonicalization.html
use super::{CanonicalGoal, Certainty, EvalCtxt, Goal}; use super::{CanonicalInput, Certainty, EvalCtxt, Goal};
use crate::solve::canonicalize::{CanonicalizeMode, Canonicalizer}; use crate::solve::canonicalize::{CanonicalizeMode, Canonicalizer};
use crate::solve::{CanonicalResponse, QueryResult, Response}; use crate::solve::{CanonicalResponse, QueryResult, Response};
use rustc_index::IndexVec; use rustc_index::IndexVec;
@ -16,8 +16,11 @@
use rustc_infer::infer::canonical::CanonicalVarValues; use rustc_infer::infer::canonical::CanonicalVarValues;
use rustc_infer::infer::canonical::{CanonicalExt, QueryRegionConstraints}; use rustc_infer::infer::canonical::{CanonicalExt, QueryRegionConstraints};
use rustc_middle::traits::query::NoSolution; use rustc_middle::traits::query::NoSolution;
use rustc_middle::traits::solve::{ExternalConstraints, ExternalConstraintsData, MaybeCause}; use rustc_middle::traits::solve::{
use rustc_middle::ty::{self, BoundVar, GenericArgKind}; ExternalConstraints, ExternalConstraintsData, MaybeCause, PredefinedOpaquesData, QueryInput,
};
use rustc_middle::traits::ObligationCause;
use rustc_middle::ty::{self, BoundVar, GenericArgKind, Ty};
use rustc_span::DUMMY_SP; use rustc_span::DUMMY_SP;
use std::iter; use std::iter;
use std::ops::Deref; use std::ops::Deref;
@ -28,13 +31,21 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
pub(super) fn canonicalize_goal( pub(super) fn canonicalize_goal(
&self, &self,
goal: Goal<'tcx, ty::Predicate<'tcx>>, goal: Goal<'tcx, ty::Predicate<'tcx>>,
) -> (Vec<ty::GenericArg<'tcx>>, CanonicalGoal<'tcx>) { ) -> (Vec<ty::GenericArg<'tcx>>, CanonicalInput<'tcx>) {
let mut orig_values = Default::default(); let mut orig_values = Default::default();
let canonical_goal = Canonicalizer::canonicalize( let canonical_goal = Canonicalizer::canonicalize(
self.infcx, self.infcx,
CanonicalizeMode::Input, CanonicalizeMode::Input,
&mut orig_values, &mut orig_values,
goal, QueryInput {
goal,
anchor: self.infcx.defining_use_anchor,
predefined_opaques_in_body: self.tcx().mk_predefined_opaques_in_body(
PredefinedOpaquesData {
opaque_types: self.infcx.clone_opaque_types_for_query_response(),
},
),
},
); );
(orig_values, canonical_goal) (orig_values, canonical_goal)
} }
@ -138,7 +149,13 @@ fn compute_external_query_constraints(&self) -> Result<ExternalConstraints<'tcx>
region_constraints, region_constraints,
) )
}); });
let opaque_types = self.infcx.clone_opaque_types_for_query_response();
let mut opaque_types = self.infcx.clone_opaque_types_for_query_response();
// Only return opaque type keys for newly-defined opaques
opaque_types.retain(|(a, _)| {
self.predefined_opaques_in_body.opaque_types.iter().all(|(pa, _)| pa != a)
});
Ok(self Ok(self
.tcx() .tcx()
.mk_external_constraints(ExternalConstraintsData { region_constraints, opaque_types })) .mk_external_constraints(ExternalConstraintsData { region_constraints, opaque_types }))
@ -162,12 +179,13 @@ pub(super) fn instantiate_and_apply_query_response(
let Response { var_values, external_constraints, certainty } = let Response { var_values, external_constraints, certainty } =
response.substitute(self.tcx(), &substitution); response.substitute(self.tcx(), &substitution);
let nested_goals = self.unify_query_var_values(param_env, &original_values, var_values)?; let mut nested_goals =
self.unify_query_var_values(param_env, &original_values, var_values)?;
// FIXME: implement external constraints. let ExternalConstraintsData { region_constraints, opaque_types } =
let ExternalConstraintsData { region_constraints, opaque_types: _ } =
external_constraints.deref(); external_constraints.deref();
self.register_region_constraints(region_constraints); self.register_region_constraints(region_constraints);
nested_goals.extend(self.register_opaque_types(param_env, opaque_types)?);
Ok((certainty, nested_goals)) Ok((certainty, nested_goals))
} }
@ -287,4 +305,29 @@ fn register_region_constraints(&mut self, region_constraints: &QueryRegionConstr
let _ = member_constraint; let _ = member_constraint;
} }
} }
fn register_opaque_types(
&mut self,
param_env: ty::ParamEnv<'tcx>,
opaque_types: &[(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)],
) -> Result<Vec<Goal<'tcx, ty::Predicate<'tcx>>>, NoSolution> {
let mut nested_goals = vec![];
for &(a, b) in opaque_types {
nested_goals.extend(
self.infcx
.handle_opaque_type(
self.tcx().mk_opaque(a.def_id.to_def_id(), a.substs),
b,
true,
&ObligationCause::dummy(),
param_env,
)?
.into_obligations()
.into_iter()
.map(Goal::from),
);
}
Ok(nested_goals)
}
} }

View File

@ -1,14 +1,13 @@
use rustc_infer::infer::InferOk;
use rustc_middle::traits::solve::{Certainty, Goal, QueryResult}; use rustc_middle::traits::solve::{Certainty, Goal, QueryResult};
use rustc_middle::traits::{ObligationCause, Reveal}; use rustc_middle::traits::Reveal;
use rustc_middle::ty::ProjectionPredicate; use rustc_middle::ty::{self};
use super::{EvalCtxt, SolverMode}; use super::{EvalCtxt, SolverMode};
impl<'tcx> EvalCtxt<'_, 'tcx> { impl<'tcx> EvalCtxt<'_, 'tcx> {
pub(super) fn normalize_opaque_type( pub(super) fn normalize_opaque_type(
&mut self, &mut self,
goal: Goal<'tcx, ProjectionPredicate<'tcx>>, goal: Goal<'tcx, ty::ProjectionPredicate<'tcx>>,
) -> QueryResult<'tcx> { ) -> QueryResult<'tcx> {
let tcx = self.tcx(); let tcx = self.tcx();
let opaque_ty = goal.predicate.projection_ty; let opaque_ty = goal.predicate.projection_ty;
@ -19,15 +18,8 @@ pub(super) fn normalize_opaque_type(
SolverMode::Normal => self.probe(|ecx| { SolverMode::Normal => self.probe(|ecx| {
// FIXME: Check that the usage is "defining" (all free params), otherwise bail. // FIXME: Check that the usage is "defining" (all free params), otherwise bail.
// FIXME: This should probably just check the anchor directly // FIXME: This should probably just check the anchor directly
let InferOk { value: (), obligations } = self.infcx.handle_opaque_type( let opaque_ty = tcx.mk_opaque(opaque_ty.def_id, opaque_ty.substs);
expected, ecx.handle_opaque_ty(expected, opaque_ty, goal.param_env)?;
tcx.mk_opaque(opaque_ty.def_id, opaque_ty.substs),
true,
&ObligationCause::dummy(),
goal.param_env,
)?;
// FIXME: Need to fold these to replace the opaque ty with the expected ty.
ecx.add_goals(obligations.into_iter().map(Into::into));
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}), }),
SolverMode::Coherence => { SolverMode::Coherence => {

View File

@ -22,24 +22,25 @@ pub(super) fn compute_projection_goal(
&mut self, &mut self,
goal: Goal<'tcx, ProjectionPredicate<'tcx>>, goal: Goal<'tcx, ProjectionPredicate<'tcx>>,
) -> QueryResult<'tcx> { ) -> QueryResult<'tcx> {
// To only compute normalization once for each projection we only match goal.predicate.projection_ty.kind(self.tcx()) {
// normalize if the expected term is an unconstrained inference variable. ty::AliasKind::Projection => {
// // To only compute normalization once for each projection we only
// E.g. for `<T as Trait>::Assoc == u32` we recursively compute the goal // normalize if the expected term is an unconstrained inference variable.
// `exists<U> <T as Trait>::Assoc == U` and then take the resulting type for //
// `U` and equate it with `u32`. This means that we don't need a separate // E.g. for `<T as Trait>::Assoc == u32` we recursively compute the goal
// projection cache in the solver. // `exists<U> <T as Trait>::Assoc == U` and then take the resulting type for
if self.term_is_fully_unconstrained(goal) { // `U` and equate it with `u32`. This means that we don't need a separate
match goal.predicate.projection_ty.kind(self.tcx()) { // projection cache in the solver.
ty::AliasKind::Projection => { if self.term_is_fully_unconstrained(goal) {
let candidates = self.assemble_and_evaluate_candidates(goal); let candidates = self.assemble_and_evaluate_candidates(goal);
self.merge_candidates(candidates) self.merge_candidates(candidates)
} else {
self.set_normalizes_to_hack_goal(goal);
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
} }
ty::AliasKind::Opaque => self.normalize_opaque_type(goal),
} }
} else { ty::AliasKind::Opaque => self.normalize_opaque_type(goal),
self.set_normalizes_to_hack_goal(goal); ty::AliasKind::Inherent => bug!("IATs not supported here yet"),
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
} }
} }
} }

View File

@ -11,7 +11,7 @@
use super::StackDepth; use super::StackDepth;
use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::fx::FxHashMap;
use rustc_index::IndexVec; use rustc_index::IndexVec;
use rustc_middle::traits::solve::{CanonicalGoal, QueryResult}; use rustc_middle::traits::solve::{CanonicalInput, QueryResult};
rustc_index::newtype_index! { rustc_index::newtype_index! {
pub struct EntryIndex {} pub struct EntryIndex {}
@ -34,7 +34,7 @@ pub(super) struct ProvisionalEntry<'tcx> {
// The goal for this entry. Should always be equal to the corresponding goal // The goal for this entry. Should always be equal to the corresponding goal
// in the lookup table. // in the lookup table.
pub(super) goal: CanonicalGoal<'tcx>, pub(super) input: CanonicalInput<'tcx>,
} }
pub(super) struct ProvisionalCache<'tcx> { pub(super) struct ProvisionalCache<'tcx> {
@ -42,7 +42,7 @@ pub(super) struct ProvisionalCache<'tcx> {
// FIXME: This is only used to quickly check whether a given goal // FIXME: This is only used to quickly check whether a given goal
// is in the cache. We should experiment with using something like // is in the cache. We should experiment with using something like
// `SsoHashSet` here because in most cases there are only a few entries. // `SsoHashSet` here because in most cases there are only a few entries.
pub(super) lookup_table: FxHashMap<CanonicalGoal<'tcx>, EntryIndex>, pub(super) lookup_table: FxHashMap<CanonicalInput<'tcx>, EntryIndex>,
} }
impl<'tcx> ProvisionalCache<'tcx> { impl<'tcx> ProvisionalCache<'tcx> {

View File

@ -8,7 +8,7 @@
use overflow::OverflowData; use overflow::OverflowData;
use rustc_index::IndexVec; use rustc_index::IndexVec;
use rustc_middle::dep_graph::DepKind; use rustc_middle::dep_graph::DepKind;
use rustc_middle::traits::solve::{CanonicalGoal, Certainty, MaybeCause, QueryResult}; use rustc_middle::traits::solve::{CanonicalInput, Certainty, MaybeCause, QueryResult};
use rustc_middle::ty::TyCtxt; use rustc_middle::ty::TyCtxt;
use std::{collections::hash_map::Entry, mem}; use std::{collections::hash_map::Entry, mem};
@ -19,7 +19,7 @@ pub struct StackDepth {}
} }
struct StackElem<'tcx> { struct StackElem<'tcx> {
goal: CanonicalGoal<'tcx>, input: CanonicalInput<'tcx>,
has_been_used: bool, has_been_used: bool,
} }
@ -77,7 +77,7 @@ pub(super) fn in_cycle(&self) -> bool {
} }
// ...or it depends on a goal with a lower depth. // ...or it depends on a goal with a lower depth.
let current_goal = self.stack[stack_depth].goal; let current_goal = self.stack[stack_depth].input;
let entry_index = self.provisional_cache.lookup_table[&current_goal]; let entry_index = self.provisional_cache.lookup_table[&current_goal];
self.provisional_cache.entries[entry_index].depth != stack_depth self.provisional_cache.entries[entry_index].depth != stack_depth
} else { } else {
@ -92,20 +92,20 @@ pub(super) fn in_cycle(&self) -> bool {
fn try_push_stack( fn try_push_stack(
&mut self, &mut self,
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
goal: CanonicalGoal<'tcx>, input: CanonicalInput<'tcx>,
) -> Result<(), QueryResult<'tcx>> { ) -> Result<(), QueryResult<'tcx>> {
// Look at the provisional cache to check for cycles. // Look at the provisional cache to check for cycles.
let cache = &mut self.provisional_cache; let cache = &mut self.provisional_cache;
match cache.lookup_table.entry(goal) { match cache.lookup_table.entry(input) {
// No entry, simply push this goal on the stack after dealing with overflow. // No entry, simply push this goal on the stack after dealing with overflow.
Entry::Vacant(v) => { Entry::Vacant(v) => {
if self.overflow_data.has_overflow(self.stack.len()) { if self.overflow_data.has_overflow(self.stack.len()) {
return Err(self.deal_with_overflow(tcx, goal)); return Err(self.deal_with_overflow(tcx, input));
} }
let depth = self.stack.push(StackElem { goal, has_been_used: false }); let depth = self.stack.push(StackElem { input, has_been_used: false });
let response = super::response_no_constraints(tcx, goal, Certainty::Yes); let response = super::response_no_constraints(tcx, input, Certainty::Yes);
let entry_index = cache.entries.push(ProvisionalEntry { response, depth, goal }); let entry_index = cache.entries.push(ProvisionalEntry { response, depth, input });
v.insert(entry_index); v.insert(entry_index);
Ok(()) Ok(())
} }
@ -135,13 +135,13 @@ fn try_push_stack(
// the stack is enough. // the stack is enough.
if self.stack.raw[stack_depth.index()..] if self.stack.raw[stack_depth.index()..]
.iter() .iter()
.all(|g| g.goal.value.predicate.is_coinductive(tcx)) .all(|g| g.input.value.goal.predicate.is_coinductive(tcx))
{ {
Err(cache.provisional_result(entry_index)) Err(cache.provisional_result(entry_index))
} else { } else {
Err(super::response_no_constraints( Err(super::response_no_constraints(
tcx, tcx,
goal, input,
Certainty::Maybe(MaybeCause::Overflow), Certainty::Maybe(MaybeCause::Overflow),
)) ))
} }
@ -161,18 +161,18 @@ fn try_push_stack(
/// updated the provisional cache and we have to recompute the current goal. /// updated the provisional cache and we have to recompute the current goal.
/// ///
/// FIXME: Refer to the rustc-dev-guide entry once it exists. /// FIXME: Refer to the rustc-dev-guide entry once it exists.
#[instrument(level = "debug", skip(self, actual_goal), ret)] #[instrument(level = "debug", skip(self, actual_input), ret)]
fn try_finalize_goal( fn try_finalize_goal(
&mut self, &mut self,
actual_goal: CanonicalGoal<'tcx>, actual_input: CanonicalInput<'tcx>,
response: QueryResult<'tcx>, response: QueryResult<'tcx>,
) -> bool { ) -> bool {
let stack_elem = self.stack.pop().unwrap(); let stack_elem = self.stack.pop().unwrap();
let StackElem { goal, has_been_used } = stack_elem; let StackElem { input, has_been_used } = stack_elem;
assert_eq!(goal, actual_goal); assert_eq!(input, actual_input);
let cache = &mut self.provisional_cache; let cache = &mut self.provisional_cache;
let provisional_entry_index = *cache.lookup_table.get(&goal).unwrap(); let provisional_entry_index = *cache.lookup_table.get(&input).unwrap();
let provisional_entry = &mut cache.entries[provisional_entry_index]; let provisional_entry = &mut cache.entries[provisional_entry_index];
// We eagerly update the response in the cache here. If we have to reevaluate // We eagerly update the response in the cache here. If we have to reevaluate
// this goal we use the new response when hitting a cycle, and we definitely // this goal we use the new response when hitting a cycle, and we definitely
@ -194,7 +194,7 @@ fn try_finalize_goal(
cache.entries.truncate(provisional_entry_index.index() + 1); cache.entries.truncate(provisional_entry_index.index() + 1);
// ...and finally push our goal back on the stack and reevaluate it. // ...and finally push our goal back on the stack and reevaluate it.
self.stack.push(StackElem { goal, has_been_used: false }); self.stack.push(StackElem { input, has_been_used: false });
false false
} else { } else {
true true
@ -204,17 +204,17 @@ fn try_finalize_goal(
pub(super) fn with_new_goal( pub(super) fn with_new_goal(
&mut self, &mut self,
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
canonical_goal: CanonicalGoal<'tcx>, canonical_input: CanonicalInput<'tcx>,
mut loop_body: impl FnMut(&mut Self) -> QueryResult<'tcx>, mut loop_body: impl FnMut(&mut Self) -> QueryResult<'tcx>,
) -> QueryResult<'tcx> { ) -> QueryResult<'tcx> {
if self.should_use_global_cache() { if self.should_use_global_cache() {
if let Some(result) = tcx.new_solver_evaluation_cache.get(&canonical_goal, tcx) { if let Some(result) = tcx.new_solver_evaluation_cache.get(&canonical_input, tcx) {
debug!(?canonical_goal, ?result, "cache hit"); debug!(?canonical_input, ?result, "cache hit");
return result; return result;
} }
} }
match self.try_push_stack(tcx, canonical_goal) { match self.try_push_stack(tcx, canonical_input) {
Ok(()) => {} Ok(()) => {}
// Our goal is already on the stack, eager return. // Our goal is already on the stack, eager return.
Err(response) => return response, Err(response) => return response,
@ -226,19 +226,19 @@ pub(super) fn with_new_goal(
let (result, dep_node) = tcx.dep_graph.with_anon_task(tcx, DepKind::TraitSelect, || { let (result, dep_node) = tcx.dep_graph.with_anon_task(tcx, DepKind::TraitSelect, || {
self.repeat_while_none( self.repeat_while_none(
|this| { |this| {
let result = this.deal_with_overflow(tcx, canonical_goal); let result = this.deal_with_overflow(tcx, canonical_input);
let _ = this.stack.pop().unwrap(); let _ = this.stack.pop().unwrap();
result result
}, },
|this| { |this| {
let result = loop_body(this); let result = loop_body(this);
this.try_finalize_goal(canonical_goal, result).then(|| result) this.try_finalize_goal(canonical_input, result).then(|| result)
}, },
) )
}); });
let cache = &mut self.provisional_cache; let cache = &mut self.provisional_cache;
let provisional_entry_index = *cache.lookup_table.get(&canonical_goal).unwrap(); let provisional_entry_index = *cache.lookup_table.get(&canonical_input).unwrap();
let provisional_entry = &mut cache.entries[provisional_entry_index]; let provisional_entry = &mut cache.entries[provisional_entry_index];
let depth = provisional_entry.depth; let depth = provisional_entry.depth;
@ -254,13 +254,13 @@ pub(super) fn with_new_goal(
// cycle participants without moving them to the global cache. // cycle participants without moving them to the global cache.
let other_cycle_participants = provisional_entry_index.index() + 1; let other_cycle_participants = provisional_entry_index.index() + 1;
for (i, entry) in cache.entries.drain_enumerated(other_cycle_participants..) { for (i, entry) in cache.entries.drain_enumerated(other_cycle_participants..) {
let actual_index = cache.lookup_table.remove(&entry.goal); let actual_index = cache.lookup_table.remove(&entry.input);
debug_assert_eq!(Some(i), actual_index); debug_assert_eq!(Some(i), actual_index);
debug_assert!(entry.depth == depth); debug_assert!(entry.depth == depth);
} }
let current_goal = cache.entries.pop().unwrap(); let current_goal = cache.entries.pop().unwrap();
let actual_index = cache.lookup_table.remove(&current_goal.goal); let actual_index = cache.lookup_table.remove(&current_goal.input);
debug_assert_eq!(Some(provisional_entry_index), actual_index); debug_assert_eq!(Some(provisional_entry_index), actual_index);
debug_assert!(current_goal.depth == depth); debug_assert!(current_goal.depth == depth);
@ -274,7 +274,7 @@ pub(super) fn with_new_goal(
let can_cache = !self.overflow_data.did_overflow() || self.stack.is_empty(); let can_cache = !self.overflow_data.did_overflow() || self.stack.is_empty();
if self.should_use_global_cache() && can_cache { if self.should_use_global_cache() && can_cache {
tcx.new_solver_evaluation_cache.insert( tcx.new_solver_evaluation_cache.insert(
current_goal.goal, current_goal.input,
dep_node, dep_node,
current_goal.response, current_goal.response,
); );

View File

@ -16,6 +16,7 @@ fn evaluate_obligation<'tcx>(
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
canonical_goal: CanonicalPredicateGoal<'tcx>, canonical_goal: CanonicalPredicateGoal<'tcx>,
) -> Result<EvaluationResult, OverflowError> { ) -> Result<EvaluationResult, OverflowError> {
assert!(!tcx.trait_solver_next());
debug!("evaluate_obligation(canonical_goal={:#?})", canonical_goal); debug!("evaluate_obligation(canonical_goal={:#?})", canonical_goal);
// HACK This bubble is required for this tests to pass: // HACK This bubble is required for this tests to pass:
// impl-trait/issue99642.rs // impl-trait/issue99642.rs