Check that opaque is a defining use, prefer pre-defined opaques
This commit is contained in:
parent
f3c9c21658
commit
e3f8beaed6
@ -518,6 +518,42 @@ impl<'tcx> TyCtxt<'tcx> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Checks whether each generic argument is simply a unique generic placeholder.
|
||||
///
|
||||
/// This is used in the new solver, which canonicalizes params to placeholders
|
||||
/// for better caching.
|
||||
pub fn uses_unique_placeholders_ignoring_regions(
|
||||
self,
|
||||
substs: SubstsRef<'tcx>,
|
||||
) -> Result<(), NotUniqueParam<'tcx>> {
|
||||
let mut seen = GrowableBitSet::default();
|
||||
for arg in substs {
|
||||
match arg.unpack() {
|
||||
// Ignore regions, since we can't resolve those in a canonicalized
|
||||
// query in the trait solver.
|
||||
GenericArgKind::Lifetime(_) => {}
|
||||
GenericArgKind::Type(t) => match t.kind() {
|
||||
ty::Placeholder(p) => {
|
||||
if !seen.insert(p.bound.var) {
|
||||
return Err(NotUniqueParam::DuplicateParam(t.into()));
|
||||
}
|
||||
}
|
||||
_ => return Err(NotUniqueParam::NotParam(t.into())),
|
||||
},
|
||||
GenericArgKind::Const(c) => match c.kind() {
|
||||
ty::ConstKind::Placeholder(p) => {
|
||||
if !seen.insert(p.bound) {
|
||||
return Err(NotUniqueParam::DuplicateParam(c.into()));
|
||||
}
|
||||
}
|
||||
_ => return Err(NotUniqueParam::NotParam(c.into())),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns `true` if `def_id` refers to a closure (e.g., `|x| x * 2`). Note
|
||||
/// that closures have a `DefId`, but the closure *expression* also
|
||||
/// has a `HirId` that is located within the context where the
|
||||
|
@ -10,7 +10,8 @@ use rustc_infer::traits::query::NoSolution;
|
||||
use rustc_infer::traits::ObligationCause;
|
||||
use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind};
|
||||
use rustc_middle::traits::solve::{
|
||||
CanonicalInput, Certainty, MaybeCause, PredefinedOpaques, PredefinedOpaquesData, QueryResult,
|
||||
CanonicalInput, CanonicalResponse, Certainty, MaybeCause, PredefinedOpaques,
|
||||
PredefinedOpaquesData, QueryResult,
|
||||
};
|
||||
use rustc_middle::traits::DefiningAnchor;
|
||||
use rustc_middle::ty::{
|
||||
@ -726,7 +727,12 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn handle_opaque_ty(
|
||||
pub(super) fn can_define_opaque_ty(&mut self, def_id: DefId) -> bool {
|
||||
let Some(def_id) = def_id.as_local() else { return false; };
|
||||
self.infcx.opaque_type_origin(def_id).is_some()
|
||||
}
|
||||
|
||||
pub(super) fn register_opaque_ty(
|
||||
&mut self,
|
||||
a: Ty<'tcx>,
|
||||
b: Ty<'tcx>,
|
||||
@ -737,4 +743,42 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||
self.add_goals(obligations.into_iter().map(|obligation| obligation.into()));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Do something for each opaque/hidden pair defined with `def_id` in the
|
||||
// current inference context.
|
||||
pub(super) fn unify_existing_opaque_tys(
|
||||
&mut self,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
key: ty::AliasTy<'tcx>,
|
||||
ty: Ty<'tcx>,
|
||||
) -> Vec<CanonicalResponse<'tcx>> {
|
||||
let Some(def_id) = key.def_id.as_local() else { return vec![]; };
|
||||
|
||||
// FIXME: Super inefficient to be cloning this...
|
||||
let opaques = self.infcx.clone_opaque_types_for_query_response();
|
||||
|
||||
let mut values = vec![];
|
||||
for (candidate_key, candidate_ty) in opaques {
|
||||
if candidate_key.def_id != def_id {
|
||||
continue;
|
||||
}
|
||||
values.extend(self.probe(|ecx| {
|
||||
for (a, b) in std::iter::zip(candidate_key.substs, key.substs) {
|
||||
ecx.eq(param_env, a, b)?;
|
||||
}
|
||||
ecx.eq(param_env, candidate_ty, ty)?;
|
||||
let mut obl = vec![];
|
||||
ecx.infcx.add_item_bounds_for_hidden_type(
|
||||
candidate_key,
|
||||
ObligationCause::dummy(),
|
||||
param_env,
|
||||
candidate_ty,
|
||||
&mut obl,
|
||||
);
|
||||
ecx.add_goals(obl.into_iter().map(Into::into));
|
||||
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||
}));
|
||||
}
|
||||
values
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
use rustc_middle::traits::query::NoSolution;
|
||||
use rustc_middle::traits::solve::{Certainty, Goal, QueryResult};
|
||||
use rustc_middle::traits::Reveal;
|
||||
use rustc_middle::ty::{self};
|
||||
use rustc_middle::traits::{ObligationCause, Reveal};
|
||||
use rustc_middle::ty;
|
||||
use rustc_middle::ty::util::NotUniqueParam;
|
||||
|
||||
use super::{EvalCtxt, SolverMode};
|
||||
|
||||
@ -15,22 +17,53 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||
|
||||
match goal.param_env.reveal() {
|
||||
Reveal::UserFacing => match self.solver_mode() {
|
||||
SolverMode::Normal => self.probe(|ecx| {
|
||||
// FIXME: Check that the usage is "defining" (all free params), otherwise bail.
|
||||
// FIXME: This should probably just check the anchor directly
|
||||
SolverMode::Normal => {
|
||||
// FIXME: at some point we should call queries without defining
|
||||
// new opaque types but having the existing opaque type definitions.
|
||||
// This will require moving this below "Prefer opaques registered already".
|
||||
if !self.can_define_opaque_ty(opaque_ty.def_id) {
|
||||
return Err(NoSolution);
|
||||
}
|
||||
// FIXME: This may have issues when the substs contain aliases...
|
||||
match self.tcx().uses_unique_placeholders_ignoring_regions(opaque_ty.substs) {
|
||||
Err(NotUniqueParam::NotParam(param)) if param.is_non_region_infer() => {
|
||||
return self.evaluate_added_goals_and_make_canonical_response(
|
||||
Certainty::AMBIGUOUS,
|
||||
);
|
||||
}
|
||||
Err(_) => {
|
||||
return Err(NoSolution);
|
||||
}
|
||||
Ok(()) => {}
|
||||
}
|
||||
// Prefer opaques registered already.
|
||||
let matches = self.unify_existing_opaque_tys(
|
||||
goal.param_env,
|
||||
opaque_ty,
|
||||
expected
|
||||
);
|
||||
if !matches.is_empty() {
|
||||
if let Some(response) = self.try_merge_responses(&matches) {
|
||||
return Ok(response);
|
||||
} else {
|
||||
return self.flounder(&matches);
|
||||
}
|
||||
}
|
||||
// Otherwise, define a new opaque type
|
||||
let opaque_ty = tcx.mk_opaque(opaque_ty.def_id, opaque_ty.substs);
|
||||
ecx.handle_opaque_ty(expected, opaque_ty, goal.param_env)?;
|
||||
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||
}),
|
||||
self.register_opaque_ty(expected, opaque_ty, goal.param_env)?;
|
||||
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||
}
|
||||
SolverMode::Coherence => {
|
||||
self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
|
||||
}
|
||||
},
|
||||
Reveal::All => self.probe(|ecx| {
|
||||
Reveal::All => {
|
||||
// FIXME: Add an assertion that opaque type storage is empty.
|
||||
let actual = tcx.type_of(opaque_ty.def_id).subst(tcx, opaque_ty.substs);
|
||||
ecx.eq(goal.param_env, expected, actual)?;
|
||||
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||
}),
|
||||
self.eq(goal.param_env, expected, actual)?;
|
||||
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user