Skip to content

Commit e3f8bea

Browse files
Check that opaque is a defining use, prefer pre-defined opaques
1 parent f3c9c21 commit e3f8bea

File tree

3 files changed

+127
-14
lines changed

3 files changed

+127
-14
lines changed

compiler/rustc_middle/src/ty/util.rs

+36
Original file line numberDiff line numberDiff line change
@@ -518,6 +518,42 @@ impl<'tcx> TyCtxt<'tcx> {
518518
Ok(())
519519
}
520520

521+
/// Checks whether each generic argument is simply a unique generic placeholder.
522+
///
523+
/// This is used in the new solver, which canonicalizes params to placeholders
524+
/// for better caching.
525+
pub fn uses_unique_placeholders_ignoring_regions(
526+
self,
527+
substs: SubstsRef<'tcx>,
528+
) -> Result<(), NotUniqueParam<'tcx>> {
529+
let mut seen = GrowableBitSet::default();
530+
for arg in substs {
531+
match arg.unpack() {
532+
// Ignore regions, since we can't resolve those in a canonicalized
533+
// query in the trait solver.
534+
GenericArgKind::Lifetime(_) => {}
535+
GenericArgKind::Type(t) => match t.kind() {
536+
ty::Placeholder(p) => {
537+
if !seen.insert(p.bound.var) {
538+
return Err(NotUniqueParam::DuplicateParam(t.into()));
539+
}
540+
}
541+
_ => return Err(NotUniqueParam::NotParam(t.into())),
542+
},
543+
GenericArgKind::Const(c) => match c.kind() {
544+
ty::ConstKind::Placeholder(p) => {
545+
if !seen.insert(p.bound) {
546+
return Err(NotUniqueParam::DuplicateParam(c.into()));
547+
}
548+
}
549+
_ => return Err(NotUniqueParam::NotParam(c.into())),
550+
},
551+
}
552+
}
553+
554+
Ok(())
555+
}
556+
521557
/// Returns `true` if `def_id` refers to a closure (e.g., `|x| x * 2`). Note
522558
/// that closures have a `DefId`, but the closure *expression* also
523559
/// has a `HirId` that is located within the context where the

compiler/rustc_trait_selection/src/solve/eval_ctxt.rs

+46-2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ use rustc_infer::traits::query::NoSolution;
1010
use rustc_infer::traits::ObligationCause;
1111
use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind};
1212
use rustc_middle::traits::solve::{
13-
CanonicalInput, Certainty, MaybeCause, PredefinedOpaques, PredefinedOpaquesData, QueryResult,
13+
CanonicalInput, CanonicalResponse, Certainty, MaybeCause, PredefinedOpaques,
14+
PredefinedOpaquesData, QueryResult,
1415
};
1516
use rustc_middle::traits::DefiningAnchor;
1617
use rustc_middle::ty::{
@@ -726,7 +727,12 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
726727
}
727728
}
728729

729-
pub(super) fn handle_opaque_ty(
730+
pub(super) fn can_define_opaque_ty(&mut self, def_id: DefId) -> bool {
731+
let Some(def_id) = def_id.as_local() else { return false; };
732+
self.infcx.opaque_type_origin(def_id).is_some()
733+
}
734+
735+
pub(super) fn register_opaque_ty(
730736
&mut self,
731737
a: Ty<'tcx>,
732738
b: Ty<'tcx>,
@@ -737,4 +743,42 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
737743
self.add_goals(obligations.into_iter().map(|obligation| obligation.into()));
738744
Ok(())
739745
}
746+
747+
// Do something for each opaque/hidden pair defined with `def_id` in the
748+
// current inference context.
749+
pub(super) fn unify_existing_opaque_tys(
750+
&mut self,
751+
param_env: ty::ParamEnv<'tcx>,
752+
key: ty::AliasTy<'tcx>,
753+
ty: Ty<'tcx>,
754+
) -> Vec<CanonicalResponse<'tcx>> {
755+
let Some(def_id) = key.def_id.as_local() else { return vec![]; };
756+
757+
// FIXME: Super inefficient to be cloning this...
758+
let opaques = self.infcx.clone_opaque_types_for_query_response();
759+
760+
let mut values = vec![];
761+
for (candidate_key, candidate_ty) in opaques {
762+
if candidate_key.def_id != def_id {
763+
continue;
764+
}
765+
values.extend(self.probe(|ecx| {
766+
for (a, b) in std::iter::zip(candidate_key.substs, key.substs) {
767+
ecx.eq(param_env, a, b)?;
768+
}
769+
ecx.eq(param_env, candidate_ty, ty)?;
770+
let mut obl = vec![];
771+
ecx.infcx.add_item_bounds_for_hidden_type(
772+
candidate_key,
773+
ObligationCause::dummy(),
774+
param_env,
775+
candidate_ty,
776+
&mut obl,
777+
);
778+
ecx.add_goals(obl.into_iter().map(Into::into));
779+
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
780+
}));
781+
}
782+
values
783+
}
740784
}
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
use rustc_middle::traits::query::NoSolution;
12
use rustc_middle::traits::solve::{Certainty, Goal, QueryResult};
2-
use rustc_middle::traits::Reveal;
3-
use rustc_middle::ty::{self};
3+
use rustc_middle::traits::{ObligationCause, Reveal};
4+
use rustc_middle::ty;
5+
use rustc_middle::ty::util::NotUniqueParam;
46

57
use super::{EvalCtxt, SolverMode};
68

@@ -15,22 +17,53 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
1517

1618
match goal.param_env.reveal() {
1719
Reveal::UserFacing => match self.solver_mode() {
18-
SolverMode::Normal => self.probe(|ecx| {
19-
// FIXME: Check that the usage is "defining" (all free params), otherwise bail.
20-
// FIXME: This should probably just check the anchor directly
20+
SolverMode::Normal => {
21+
// FIXME: at some point we should call queries without defining
22+
// new opaque types but having the existing opaque type definitions.
23+
// This will require moving this below "Prefer opaques registered already".
24+
if !self.can_define_opaque_ty(opaque_ty.def_id) {
25+
return Err(NoSolution);
26+
}
27+
// FIXME: This may have issues when the substs contain aliases...
28+
match self.tcx().uses_unique_placeholders_ignoring_regions(opaque_ty.substs) {
29+
Err(NotUniqueParam::NotParam(param)) if param.is_non_region_infer() => {
30+
return self.evaluate_added_goals_and_make_canonical_response(
31+
Certainty::AMBIGUOUS,
32+
);
33+
}
34+
Err(_) => {
35+
return Err(NoSolution);
36+
}
37+
Ok(()) => {}
38+
}
39+
// Prefer opaques registered already.
40+
let matches = self.unify_existing_opaque_tys(
41+
goal.param_env,
42+
opaque_ty,
43+
expected
44+
);
45+
if !matches.is_empty() {
46+
if let Some(response) = self.try_merge_responses(&matches) {
47+
return Ok(response);
48+
} else {
49+
return self.flounder(&matches);
50+
}
51+
}
52+
// Otherwise, define a new opaque type
2153
let opaque_ty = tcx.mk_opaque(opaque_ty.def_id, opaque_ty.substs);
22-
ecx.handle_opaque_ty(expected, opaque_ty, goal.param_env)?;
23-
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
24-
}),
54+
self.register_opaque_ty(expected, opaque_ty, goal.param_env)?;
55+
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
56+
}
2557
SolverMode::Coherence => {
2658
self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
2759
}
2860
},
29-
Reveal::All => self.probe(|ecx| {
61+
Reveal::All => {
62+
// FIXME: Add an assertion that opaque type storage is empty.
3063
let actual = tcx.type_of(opaque_ty.def_id).subst(tcx, opaque_ty.substs);
31-
ecx.eq(goal.param_env, expected, actual)?;
32-
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
33-
}),
64+
self.eq(goal.param_env, expected, actual)?;
65+
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
66+
}
3467
}
3568
}
3669
}

0 commit comments

Comments
 (0)