1
Fork 0

Auto merge of #111473 - compiler-errors:opaques, r=lcnr

Handle opaques in the new solver (take 2?)

Implement a new strategy for handling opaques in the new solver.

First, queries now carry both their defining anchor and the opaques that were defined in the inference context at the time of canonicalization. These are both used to pre-populate the inference context used by the canonical query.

Second, use the normalizes-to goal to handle opaque types in the new solver. This means that opaques are handled like projection aliases, but with their own rules:
* Can only define opaques if they're "defining uses" (i.e. have unique params in all their substs).
* Can only define opaques that are from the anchor.
* Opaque type definitions are modulo regions. So that means `Opaque<'?0r> = HiddenTy1` and `Opaque<?'1r> = HiddenTy2` equate `HiddenTy1` and `HiddenTy2` instead of defining them as different opaque type keys.
This commit is contained in:
bors 2023-05-25 08:41:54 +00:00
commit eb9da7bfa3
42 changed files with 657 additions and 188 deletions

View file

@ -121,6 +121,7 @@ macro_rules! arena_types {
>,
[] bit_set_u32: rustc_index::bit_set::BitSet<u32>,
[] 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,
[] closure_kind_origin: (rustc_span::Span, rustc_middle::hir::place::Place<'tcx>),
[] mod_child: rustc_middle::metadata::ModChild,

View file

@ -1108,3 +1108,14 @@ pub enum CodegenObligationError {
Unimplemented,
FulfillmentError,
}
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, TypeFoldable, TypeVisitable)]
pub enum DefiningAnchor {
/// `DefId` of the item.
Bind(LocalDefId),
/// When opaque types are not resolved, we `Bubble` up, meaning
/// return the opaque/hidden type pair from query, for caller of query to handle it.
Bubble,
/// Used to catch type mismatch errors when handling opaque types.
Error,
}

View file

@ -5,13 +5,13 @@ use rustc_query_system::cache::Cache;
use crate::infer::canonical::{CanonicalVarValues, QueryRegionConstraints};
use crate::traits::query::NoSolution;
use crate::traits::Canonical;
use crate::traits::{Canonical, DefiningAnchor};
use crate::ty::{
self, FallibleTypeFolder, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeVisitable,
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
/// given some assumptions, i.e. `param_env`.
@ -96,7 +96,31 @@ pub enum MaybeCause {
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>>;
@ -165,3 +189,40 @@ impl<'tcx> TypeVisitable<TyCtxt<'tcx>> for ExternalConstraints<'tcx> {
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::query::{IntoQueryParam, TyCtxtAt};
use crate::thir::Thir;
use crate::traits;
use crate::traits::solve;
use crate::traits::solve::{ExternalConstraints, ExternalConstraintsData};
use crate::traits::solve::{
ExternalConstraints, ExternalConstraintsData, PredefinedOpaques, PredefinedOpaquesData,
};
use crate::ty::{
self, AdtDef, AdtDefData, AdtKind, Binder, Const, ConstData, FloatTy, FloatVar, FloatVid,
GenericParamDefKind, ImplPolarity, InferTy, IntTy, IntVar, IntVid, List, ParamConst, ParamTy,
@ -140,6 +142,7 @@ pub struct CtxtInterners<'tcx> {
layout: InternedSet<'tcx, LayoutS>,
adt_def: InternedSet<'tcx, AdtDefData>,
external_constraints: InternedSet<'tcx, ExternalConstraintsData<'tcx>>,
predefined_opaques_in_body: InternedSet<'tcx, PredefinedOpaquesData<'tcx>>,
fields: InternedSet<'tcx, List<FieldIdx>>,
}
@ -164,6 +167,7 @@ impl<'tcx> CtxtInterners<'tcx> {
layout: Default::default(),
adt_def: Default::default(),
external_constraints: Default::default(),
predefined_opaques_in_body: Default::default(),
fields: Default::default(),
}
}
@ -1520,6 +1524,8 @@ direct_interners! {
adt_def: pub mk_adt_def_from_data(AdtDefData): AdtDef -> AdtDef<'tcx>,
external_constraints: pub mk_external_constraints(ExternalConstraintsData<'tcx>):
ExternalConstraints -> ExternalConstraints<'tcx>,
predefined_opaques_in_body: pub mk_predefined_opaques_in_body(PredefinedOpaquesData<'tcx>):
PredefinedOpaques -> PredefinedOpaques<'tcx>,
}
macro_rules! slice_interners {

View file

@ -996,17 +996,11 @@ impl<'tcx> Term<'tcx> {
}
}
/// This function returns the inner `AliasTy` if this term is a projection.
///
/// FIXME: rename `AliasTy` to `AliasTerm` and make sure we correctly
/// deal with constants.
pub fn to_projection_term(&self, tcx: TyCtxt<'tcx>) -> Option<AliasTy<'tcx>> {
/// This function returns the inner `AliasTy` for a `ty::Alias` or `ConstKind::Unevaluated`.
pub fn to_alias_ty(&self, tcx: TyCtxt<'tcx>) -> Option<AliasTy<'tcx>> {
match self.unpack() {
TermKind::Ty(ty) => match ty.kind() {
ty::Alias(kind, alias_ty) => match kind {
AliasKind::Projection | AliasKind::Inherent => Some(*alias_ty),
AliasKind::Opaque => None,
},
TermKind::Ty(ty) => match *ty.kind() {
ty::Alias(_kind, alias_ty) => Some(alias_ty),
_ => None,
},
TermKind::Const(ct) => match ct.kind() {

View file

@ -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