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

@ -4,8 +4,9 @@
use rustc_hir::def_id::LocalDefId;
use rustc_index::{IndexSlice, IndexVec};
use rustc_infer::infer::{DefiningAnchor, TyCtxtInferExt};
use rustc_infer::infer::TyCtxtInferExt;
use rustc_middle::mir::{Body, Promoted};
use rustc_middle::traits::DefiningAnchor;
use rustc_middle::ty::TyCtxt;
use std::rc::Rc;

View File

@ -26,7 +26,7 @@
use rustc_index::bit_set::ChunkedBitSet;
use rustc_index::{IndexSlice, IndexVec};
use rustc_infer::infer::{
DefiningAnchor, InferCtxt, NllRegionVariableOrigin, RegionVariableOrigin, TyCtxtInferExt,
InferCtxt, NllRegionVariableOrigin, RegionVariableOrigin, TyCtxtInferExt,
};
use rustc_middle::mir::{
traversal, Body, ClearCrossCrate, Local, Location, Mutability, NonDivergingIntrinsic, Operand,
@ -36,6 +36,7 @@
use rustc_middle::mir::{InlineAsmOperand, Terminator, TerminatorKind};
use rustc_middle::mir::{ProjectionElem, Promoted, Rvalue, Statement, StatementKind};
use rustc_middle::query::Providers;
use rustc_middle::traits::DefiningAnchor;
use rustc_middle::ty::{self, CapturedPlace, ParamEnv, RegionVid, TyCtxt};
use rustc_session::lint::builtin::UNUSED_MUT;
use rustc_span::{Span, Symbol};

View File

@ -2,9 +2,10 @@
use rustc_errors::ErrorGuaranteed;
use rustc_hir::def_id::LocalDefId;
use rustc_hir::OpaqueTyOrigin;
use rustc_infer::infer::InferCtxt;
use rustc_infer::infer::TyCtxtInferExt as _;
use rustc_infer::infer::{DefiningAnchor, InferCtxt};
use rustc_infer::traits::{Obligation, ObligationCause};
use rustc_middle::traits::DefiningAnchor;
use rustc_middle::ty::subst::{GenericArgKind, InternalSubsts};
use rustc_middle::ty::visit::TypeVisitableExt;
use rustc_middle::ty::{self, OpaqueHiddenType, OpaqueTypeKey, Ty, TyCtxt, TypeFoldable};

View File

@ -48,6 +48,7 @@
use rustc_mir_dataflow::move_paths::MoveData;
use rustc_mir_dataflow::ResultsCursor;
use crate::renumber::RegionCtxt;
use crate::session_diagnostics::MoveUnsized;
use crate::{
borrow_set::BorrowSet,
@ -183,6 +184,15 @@ pub(crate) fn type_check<'mir, 'tcx>(
&mut borrowck_context,
);
// FIXME(-Ztrait-solver=next): A bit dubious that we're only registering
// predefined opaques in the typeck root.
// FIXME(-Ztrait-solver=next): This is also totally wrong for TAITs, since
// the HIR typeck map defining usages back to their definition params,
// they won't actually match up with the usages in this body...
if infcx.tcx.trait_solver_next() && !infcx.tcx.is_typeck_child(body.source.def_id()) {
checker.register_predefined_opaques_in_new_solver();
}
let mut verifier = TypeVerifier::new(&mut checker, promoted);
verifier.visit_body(&body);
@ -1023,6 +1033,57 @@ fn new(
checker
}
pub(super) fn register_predefined_opaques_in_new_solver(&mut self) {
// OK to use the identity substitutions for each opaque type key, since
// we remap opaques from HIR typeck back to their definition params.
let opaques: Vec<_> = self
.infcx
.tcx
.typeck(self.body.source.def_id().expect_local())
.concrete_opaque_types
.iter()
.map(|(&def_id, &hidden_ty)| {
let substs = ty::InternalSubsts::identity_for_item(self.infcx.tcx, def_id);
(ty::OpaqueTypeKey { def_id, substs }, hidden_ty)
})
.collect();
let renumbered_opaques = self.infcx.tcx.fold_regions(opaques, |_, _| {
self.infcx.next_nll_region_var(
NllRegionVariableOrigin::Existential { from_forall: false },
|| RegionCtxt::Unknown,
)
});
let param_env = self.param_env;
let result = self.fully_perform_op(
Locations::All(self.body.span),
ConstraintCategory::OpaqueType,
CustomTypeOp::new(
|ocx| {
for (key, hidden_ty) in renumbered_opaques {
ocx.register_infer_ok_obligations(
ocx.infcx.register_hidden_type_in_new_solver(
key,
param_env,
hidden_ty.ty,
)?,
);
}
Ok(())
},
"register pre-defined opaques",
),
);
if result.is_err() {
self.infcx.tcx.sess.delay_span_bug(
self.body.span,
"failed re-defining predefined opaques in mir typeck",
);
}
}
fn body(&self) -> &Body<'tcx> {
self.body
}

View File

@ -3,8 +3,8 @@
//! FIXME: Move this to a more general place. The utility of this extends to
//! other areas of the compiler as well.
use rustc_infer::infer::{DefiningAnchor, TyCtxtInferExt};
use rustc_infer::traits::ObligationCause;
use rustc_infer::infer::TyCtxtInferExt;
use rustc_middle::traits::{DefiningAnchor, ObligationCause};
use rustc_middle::ty::{ParamEnv, Ty, TyCtxt};
use rustc_trait_selection::traits::ObligationCtxt;

View File

@ -13,11 +13,12 @@
use rustc_hir::{ItemKind, Node, PathSegment};
use rustc_infer::infer::opaque_types::ConstrainOpaqueTypeRegionVisitor;
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
use rustc_infer::infer::{DefiningAnchor, RegionVariableOrigin, TyCtxtInferExt};
use rustc_infer::infer::{RegionVariableOrigin, TyCtxtInferExt};
use rustc_infer::traits::{Obligation, TraitEngineExt as _};
use rustc_lint_defs::builtin::REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS;
use rustc_middle::hir::nested_filter;
use rustc_middle::middle::stability::EvalResult;
use rustc_middle::traits::DefiningAnchor;
use rustc_middle::ty::layout::{LayoutError, MAX_SIMD_LANES};
use rustc_middle::ty::subst::GenericArgKind;
use rustc_middle::ty::util::{Discr, IntTypeExt};

View File

@ -4,7 +4,8 @@
use rustc_hir as hir;
use rustc_hir::def_id::LocalDefId;
use rustc_hir::HirIdMap;
use rustc_infer::infer::{DefiningAnchor, InferCtxt, InferOk, TyCtxtInferExt};
use rustc_infer::infer::{InferCtxt, InferOk, TyCtxtInferExt};
use rustc_middle::traits::DefiningAnchor;
use rustc_middle::ty::visit::TypeVisitableExt;
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_span::def_id::LocalDefIdMap;

View File

@ -113,10 +113,7 @@ pub fn super_combine_tys<R>(
bug!()
}
(_, ty::Alias(AliasKind::Projection | AliasKind::Inherent, _))
| (ty::Alias(AliasKind::Projection | AliasKind::Inherent, _), _)
if self.tcx.trait_solver_next() =>
{
(_, ty::Alias(..)) | (ty::Alias(..), _) if self.tcx.trait_solver_next() => {
relation.register_type_relate_obligation(a, b);
Ok(a)
}

View File

@ -104,7 +104,8 @@ fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
(&ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }), _)
| (_, &ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }))
if self.fields.define_opaque_types == DefineOpaqueTypes::Yes
&& def_id.is_local() =>
&& def_id.is_local()
&& !self.tcx().trait_solver_next() =>
{
self.fields.obligations.extend(
infcx

View File

@ -108,9 +108,12 @@ pub fn super_lattice_tys<'a, 'tcx: 'a, L>(
&ty::Alias(ty::Opaque, ty::AliasTy { def_id: a_def_id, .. }),
&ty::Alias(ty::Opaque, ty::AliasTy { def_id: b_def_id, .. }),
) if a_def_id == b_def_id => infcx.super_combine_tys(this, a, b),
(&ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }), _)
| (_, &ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }))
if this.define_opaque_types() == DefineOpaqueTypes::Yes && def_id.is_local() =>
if this.define_opaque_types() == DefineOpaqueTypes::Yes
&& def_id.is_local()
&& !this.tcx().trait_solver_next() =>
{
this.register_obligations(
infcx

View File

@ -24,7 +24,7 @@
use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind, ToType};
use rustc_middle::mir::interpret::{ErrorHandled, EvalToValTreeResult};
use rustc_middle::mir::ConstraintCategory;
use rustc_middle::traits::select;
use rustc_middle::traits::{select, DefiningAnchor};
use rustc_middle::ty::error::{ExpectedFound, TypeError};
use rustc_middle::ty::fold::BoundVarReplacerDelegate;
use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable};
@ -231,17 +231,6 @@ pub fn unwrap_region_constraints(&mut self) -> RegionConstraintCollector<'_, 'tc
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
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,
}
pub struct InferCtxt<'tcx> {
pub tcx: TyCtxt<'tcx>,

View File

@ -491,16 +491,22 @@ fn tys(&mut self, a: Ty<'tcx>, mut b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>>
(
&ty::Alias(ty::Opaque, ty::AliasTy { def_id: a_def_id, .. }),
&ty::Alias(ty::Opaque, ty::AliasTy { def_id: b_def_id, .. }),
) if a_def_id == b_def_id => infcx.super_combine_tys(self, a, b).or_else(|err| {
self.tcx().sess.delay_span_bug(
self.delegate.span(),
"failure to relate an opaque to itself should result in an error later on",
);
if a_def_id.is_local() { self.relate_opaques(a, b) } else { Err(err) }
}),
) if a_def_id == b_def_id || infcx.tcx.trait_solver_next() => {
infcx.super_combine_tys(self, a, b).or_else(|err| {
// This behavior is only there for the old solver, the new solver
// shouldn't ever fail. Instead, it unconditionally emits an
// alias-relate goal.
assert!(!self.tcx().trait_solver_next());
self.tcx().sess.delay_span_bug(
self.delegate.span(),
"failure to relate an opaque to itself should result in an error later on",
);
if a_def_id.is_local() { self.relate_opaques(a, b) } else { Err(err) }
})
}
(&ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }), _)
| (_, &ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }))
if def_id.is_local() =>
if def_id.is_local() && !self.tcx().trait_solver_next() =>
{
self.relate_opaques(a, b)
}

View File

@ -1,14 +1,14 @@
use super::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use super::{DefineOpaqueTypes, InferResult};
use crate::errors::OpaqueHiddenTypeDiag;
use crate::infer::{DefiningAnchor, InferCtxt, InferOk};
use crate::traits;
use crate::infer::{InferCtxt, InferOk};
use crate::traits::{self, PredicateObligation};
use hir::def_id::{DefId, LocalDefId};
use hir::OpaqueTyOrigin;
use rustc_data_structures::fx::FxIndexMap;
use rustc_data_structures::sync::Lrc;
use rustc_hir as hir;
use rustc_middle::traits::ObligationCause;
use rustc_middle::traits::{DefiningAnchor, ObligationCause};
use rustc_middle::ty::error::{ExpectedFound, TypeError};
use rustc_middle::ty::fold::BottomUpFolder;
use rustc_middle::ty::GenericArgKind;
@ -48,9 +48,15 @@ pub fn replace_opaque_types_with_inference_vars<T: TypeFoldable<TyCtxt<'tcx>>>(
span: Span,
param_env: ty::ParamEnv<'tcx>,
) -> InferOk<'tcx, T> {
// We handle opaque types differently in the new solver.
if self.tcx.trait_solver_next() {
return InferOk { value, obligations: vec![] };
}
if !value.has_opaque_types() {
return InferOk { value, obligations: vec![] };
}
let mut obligations = vec![];
let replace_opaque_type = |def_id: DefId| {
def_id.as_local().is_some_and(|def_id| self.opaque_type_origin(def_id).is_some())
@ -521,9 +527,6 @@ fn register_hidden_type(
origin: hir::OpaqueTyOrigin,
a_is_expected: bool,
) -> InferResult<'tcx, ()> {
let tcx = self.tcx;
let OpaqueTypeKey { def_id, substs } = opaque_type_key;
// Ideally, we'd get the span where *this specific `ty` came
// from*, but right now we just use the span from the overall
// value being folded. In simple cases like `-> impl Foo`,
@ -531,7 +534,7 @@ fn register_hidden_type(
// Foo, impl Bar)`.
let span = cause.span;
let prev = self.inner.borrow_mut().opaque_types().register(
OpaqueTypeKey { def_id, substs },
opaque_type_key,
OpaqueHiddenType { ty: hidden_ty, span },
origin,
);
@ -543,6 +546,49 @@ fn register_hidden_type(
Vec::new()
};
self.add_item_bounds_for_hidden_type(
opaque_type_key,
cause,
param_env,
hidden_ty,
&mut obligations,
);
Ok(InferOk { value: (), obligations })
}
/// Registers an opaque's hidden type -- only should be used when the opaque
/// can be defined. For something more fallible -- checks the anchors, tries
/// to unify opaques in both dirs, etc. -- use `InferCtxt::handle_opaque_type`.
pub fn register_hidden_type_in_new_solver(
&self,
opaque_type_key: OpaqueTypeKey<'tcx>,
param_env: ty::ParamEnv<'tcx>,
hidden_ty: Ty<'tcx>,
) -> InferResult<'tcx, ()> {
assert!(self.tcx.trait_solver_next());
let origin = self
.opaque_type_origin(opaque_type_key.def_id)
.expect("should be called for defining usages only");
self.register_hidden_type(
opaque_type_key,
ObligationCause::dummy(),
param_env,
hidden_ty,
origin,
true,
)
}
pub fn add_item_bounds_for_hidden_type(
&self,
OpaqueTypeKey { def_id, substs }: OpaqueTypeKey<'tcx>,
cause: ObligationCause<'tcx>,
param_env: ty::ParamEnv<'tcx>,
hidden_ty: Ty<'tcx>,
obligations: &mut Vec<PredicateObligation<'tcx>>,
) {
let tcx = self.tcx;
let item_bounds = tcx.explicit_item_bounds(def_id);
for (predicate, _) in item_bounds.subst_iter_copied(tcx, substs) {
@ -555,14 +601,15 @@ fn register_hidden_type(
// FIXME(inherent_associated_types): Extend this to support `ty::Inherent`, too.
ty::Alias(ty::Projection, projection_ty)
if !projection_ty.has_escaping_bound_vars()
&& !tcx.is_impl_trait_in_trait(projection_ty.def_id) =>
&& !tcx.is_impl_trait_in_trait(projection_ty.def_id)
&& !tcx.trait_solver_next() =>
{
self.infer_projection(
param_env,
projection_ty,
cause.clone(),
0,
&mut obligations,
obligations,
)
}
// Replace all other mentions of the same opaque type with the hidden type,
@ -588,10 +635,10 @@ fn register_hidden_type(
predicate.kind().skip_binder()
{
if projection.term.references_error() {
// No point on adding these obligations since there's a type error involved.
return Ok(InferOk { value: (), obligations: vec![] });
// No point on adding any obligations since there's a type error involved.
obligations.clear();
return;
}
trace!("{:#?}", projection.term);
}
// Require that the predicate holds for the concrete type.
debug!(?predicate);
@ -602,7 +649,6 @@ fn register_hidden_type(
predicate,
));
}
Ok(InferOk { value: (), obligations })
}
}

View File

@ -131,7 +131,8 @@ fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
(&ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }), _)
| (_, &ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }))
if self.fields.define_opaque_types == DefineOpaqueTypes::Yes
&& def_id.is_local() =>
&& def_id.is_local()
&& !self.tcx().trait_solver_next() =>
{
self.fields.obligations.extend(
infcx

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 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 @@ fn visit_with<V: TypeVisitor<TyCtxt<'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::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 @@ fn new(arena: &'tcx WorkerLocal<Arena<'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 @@ impl<'tcx> TyCtxt<'tcx> {
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 @@ pub fn into_arg(self) -> GenericArg<'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 @@ pub fn uses_unique_generic_params(
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

View File

@ -2,7 +2,6 @@
use super::search_graph::OverflowHandler;
use super::{EvalCtxt, SolverMode};
use crate::solve::CanonicalResponseExt;
use crate::traits::coherence;
use rustc_data_structures::fx::FxIndexSet;
use rustc_hir::def_id::DefId;
@ -333,8 +332,7 @@ fn assemble_candidates_after_normalizing_self_ty<G: GoalKind<'tcx>>(
candidates: &mut Vec<Candidate<'tcx>>,
) {
let tcx = self.tcx();
// FIXME: We also have to normalize opaque types, not sure where to best fit that in.
let &ty::Alias(ty::Projection, projection_ty) = goal.predicate.self_ty().kind() else {
let &ty::Alias(_, projection_ty) = goal.predicate.self_ty().kind() else {
return
};
@ -356,8 +354,11 @@ fn assemble_candidates_after_normalizing_self_ty<G: GoalKind<'tcx>>(
}),
);
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);
debug!(?normalized_ty, "self type normalized");
// NOTE: Alternatively we could call `evaluate_goal` here and only
// have a `Normalized` candidate. This doesn't work as long as we
// use `CandidateSource` in winnowing.
@ -742,13 +743,18 @@ pub(super) fn merge_candidates(
SolverMode::Normal => {
let param_env_responses = candidates
.iter()
.filter(|c| matches!(c.source, CandidateSource::ParamEnv(_)))
.filter(|c| {
matches!(
c.source,
CandidateSource::ParamEnv(_) | CandidateSource::AliasBound
)
})
.map(|c| c.result)
.collect::<Vec<_>>();
if let Some(result) = self.try_merge_responses(&param_env_responses) {
if result.has_only_region_constraints() {
return Ok(result);
}
// We strongly prefer alias and param-env bounds here, even if they affect inference.
// See https://github.com/rust-lang/trait-system-refactor-initiative/issues/11.
return Ok(result);
}
}
}

View File

@ -1,4 +1,4 @@
use rustc_hir::def_id::DefId;
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_infer::infer::at::ToTrace;
use rustc_infer::infer::canonical::CanonicalVarValues;
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
@ -9,7 +9,11 @@
use rustc_infer::traits::query::NoSolution;
use rustc_infer::traits::ObligationCause;
use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind};
use rustc_middle::traits::solve::{CanonicalGoal, Certainty, MaybeCause, QueryResult};
use rustc_middle::traits::solve::{
CanonicalInput, CanonicalResponse, Certainty, MaybeCause, PredefinedOpaques,
PredefinedOpaquesData, QueryResult,
};
use rustc_middle::traits::DefiningAnchor;
use rustc_middle::ty::{
self, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt,
TypeVisitor,
@ -44,6 +48,9 @@ pub struct EvalCtxt<'a, 'tcx> {
infcx: &'a InferCtxt<'tcx>,
pub(super) var_values: CanonicalVarValues<'tcx>,
predefined_opaques_in_body: PredefinedOpaques<'tcx>,
/// The highest universe index nameable by the caller.
///
/// When we enter a new binder inside of the query we create new universes
@ -126,6 +133,11 @@ fn evaluate_root_goal(
let mut ecx = EvalCtxt {
search_graph: &mut search_graph,
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.
max_input_universe: ty::UniverseIndex::ROOT,
var_values: CanonicalVarValues::dummy(),
@ -162,29 +174,53 @@ pub(super) fn solver_mode(&self) -> SolverMode {
fn evaluate_canonical_goal(
tcx: TyCtxt<'tcx>,
search_graph: &'a mut search_graph::SearchGraph<'tcx>,
canonical_goal: CanonicalGoal<'tcx>,
canonical_input: CanonicalInput<'tcx>,
) -> QueryResult<'tcx> {
// Deal with overflow, caching, and coinduction.
//
// 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() {
SolverMode::Normal => false,
SolverMode::Coherence => true,
};
let (ref infcx, goal, var_values) = tcx
let (ref infcx, input, var_values) = tcx
.infer_ctxt()
.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
.register_hidden_type_in_new_solver(a, input.goal.param_env, b)
.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 {
infcx,
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,
nested_goals: NestedGoals::new(),
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 +235,8 @@ fn evaluate_goal(
let canonical_response =
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(
goal.param_env,
orig_values,
@ -418,6 +455,7 @@ pub(super) fn probe<T>(&mut self, f: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> T)
let mut ecx = EvalCtxt {
infcx: self.infcx,
var_values: self.var_values,
predefined_opaques_in_body: self.predefined_opaques_in_body,
max_input_universe: self.max_input_universe,
search_graph: self.search_graph,
nested_goals: self.nested_goals.clone(),
@ -682,4 +720,56 @@ pub(super) fn is_transmutable(
| rustc_transmute::Answer::IfAny(_) => Err(NoSolution),
}
}
pub(super) fn can_define_opaque_ty(&mut self, def_id: LocalDefId) -> bool {
self.infcx.opaque_type_origin(def_id).is_some()
}
pub(super) fn register_opaque_ty(
&mut self,
a: ty::OpaqueTypeKey<'tcx>,
b: Ty<'tcx>,
param_env: ty::ParamEnv<'tcx>,
) -> Result<(), NoSolution> {
let InferOk { value: (), obligations } =
self.infcx.register_hidden_type_in_new_solver(a, param_env, b)?;
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::OpaqueTypeKey<'tcx>,
ty: Ty<'tcx>,
) -> Vec<CanonicalResponse<'tcx>> {
// 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 != key.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
}
}

View File

@ -8,16 +8,19 @@
/// section of the [rustc-dev-guide][c].
///
/// [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::{CanonicalResponse, QueryResult, Response};
use rustc_index::IndexVec;
use rustc_infer::infer::canonical::query_response::make_query_region_constraints;
use rustc_infer::infer::canonical::CanonicalVarValues;
use rustc_infer::infer::canonical::{CanonicalExt, QueryRegionConstraints};
use rustc_infer::infer::InferOk;
use rustc_middle::traits::query::NoSolution;
use rustc_middle::traits::solve::{ExternalConstraints, ExternalConstraintsData, MaybeCause};
use rustc_middle::ty::{self, BoundVar, GenericArgKind};
use rustc_middle::traits::solve::{
ExternalConstraints, ExternalConstraintsData, MaybeCause, PredefinedOpaquesData, QueryInput,
};
use rustc_middle::ty::{self, BoundVar, GenericArgKind, Ty};
use rustc_span::DUMMY_SP;
use std::iter;
use std::ops::Deref;
@ -28,13 +31,21 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
pub(super) fn canonicalize_goal(
&self,
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 canonical_goal = Canonicalizer::canonicalize(
self.infcx,
CanonicalizeMode::Input,
&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)
}
@ -138,7 +149,13 @@ fn compute_external_query_constraints(&self) -> Result<ExternalConstraints<'tcx>
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
.tcx()
.mk_external_constraints(ExternalConstraintsData { region_constraints, opaque_types }))
@ -164,10 +181,10 @@ pub(super) fn instantiate_and_apply_query_response(
let 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();
self.register_region_constraints(region_constraints);
self.register_opaque_types(param_env, opaque_types)?;
Ok((certainty, nested_goals))
}
@ -287,4 +304,19 @@ fn register_region_constraints(&mut self, region_constraints: &QueryRegionConstr
let _ = member_constraint;
}
}
fn register_opaque_types(
&mut self,
param_env: ty::ParamEnv<'tcx>,
opaque_types: &[(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)],
) -> Result<(), NoSolution> {
for &(a, b) in opaque_types {
let InferOk { value: (), obligations } =
self.infcx.register_hidden_type_in_new_solver(a, param_env, b)?;
// It's sound to drop these obligations, since the normalizes-to goal
// is responsible for proving these obligations.
let _ = obligations;
}
Ok(())
}
}

View File

@ -133,12 +133,14 @@ fn select_where_possible(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<FulfillmentE
| ty::PredicateKind::ObjectSafe(_)
| ty::PredicateKind::ClosureKind(_, _, _)
| ty::PredicateKind::ConstEvaluatable(_)
| ty::PredicateKind::TypeWellFormedFromEnv(_)
| ty::PredicateKind::Ambiguous => {
FulfillmentErrorCode::CodeSelectionError(
SelectionError::Unimplemented,
)
}
ty::PredicateKind::TypeWellFormedFromEnv(_) => {
bug!("unexpected goal: {goal:?}")
}
},
root_obligation: obligation,
});

View File

@ -24,6 +24,7 @@
mod canonicalize;
mod eval_ctxt;
mod fulfill;
mod opaques;
mod project_goals;
mod search_graph;
mod trait_goals;
@ -212,7 +213,7 @@ enum Invert {
);
}
match (lhs.to_projection_term(tcx), rhs.to_projection_term(tcx)) {
match (lhs.to_alias_ty(tcx), rhs.to_alias_ty(tcx)) {
(None, None) => bug!("`AliasRelate` goal without an alias on either lhs or rhs"),
// RHS is not a projection, only way this is true is if LHS normalizes-to RHS
@ -238,34 +239,34 @@ enum Invert {
evaluate_normalizes_to(self, alias_rhs, lhs, direction, Invert::Yes).ok(),
);
// Relate via substs
candidates.extend(
self.probe(|ecx| {
let span = tracing::span!(
tracing::Level::DEBUG,
"compute_alias_relate_goal(relate_via_substs)",
?alias_lhs,
?alias_rhs,
?direction
);
let _enter = span.enter();
let subst_relate_response = self.probe(|ecx| {
let span = tracing::span!(
tracing::Level::DEBUG,
"compute_alias_relate_goal(relate_via_substs)",
?alias_lhs,
?alias_rhs,
?direction
);
let _enter = span.enter();
match direction {
ty::AliasRelationDirection::Equate => {
ecx.eq(goal.param_env, alias_lhs, alias_rhs)?;
}
ty::AliasRelationDirection::Subtype => {
ecx.sub(goal.param_env, alias_lhs, alias_rhs)?;
}
match direction {
ty::AliasRelationDirection::Equate => {
ecx.eq(goal.param_env, alias_lhs, alias_rhs)?;
}
ty::AliasRelationDirection::Subtype => {
ecx.sub(goal.param_env, alias_lhs, alias_rhs)?;
}
}
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
})
.ok(),
);
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
});
candidates.extend(subst_relate_response);
debug!(?candidates);
if let Some(merged) = self.try_merge_responses(&candidates) {
Ok(merged)
} else if let Ok(subst_relate_response) = subst_relate_response {
Ok(subst_relate_response)
} else {
self.flounder(&candidates)
}

View File

@ -0,0 +1,67 @@
use rustc_middle::traits::query::NoSolution;
use rustc_middle::traits::solve::{Certainty, Goal, QueryResult};
use rustc_middle::traits::Reveal;
use rustc_middle::ty;
use rustc_middle::ty::util::NotUniqueParam;
use super::{EvalCtxt, SolverMode};
impl<'tcx> EvalCtxt<'_, 'tcx> {
pub(super) fn normalize_opaque_type(
&mut self,
goal: Goal<'tcx, ty::ProjectionPredicate<'tcx>>,
) -> QueryResult<'tcx> {
let tcx = self.tcx();
let opaque_ty = goal.predicate.projection_ty;
let expected = goal.predicate.term.ty().expect("no such thing as an opaque const");
match (goal.param_env.reveal(), self.solver_mode()) {
(Reveal::UserFacing, SolverMode::Normal) => {
let Some(opaque_ty_def_id) = opaque_ty.def_id.as_local() else {
return Err(NoSolution);
};
let opaque_ty =
ty::OpaqueTypeKey { def_id: opaque_ty_def_id, substs: opaque_ty.substs };
// 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
self.register_opaque_ty(opaque_ty, expected, goal.param_env)?;
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}
(Reveal::UserFacing, SolverMode::Coherence) => {
self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
}
(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);
self.eq(goal.param_env, expected, actual)?;
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}
}
}
}

View File

@ -22,19 +22,25 @@ pub(super) fn compute_projection_goal(
&mut self,
goal: Goal<'tcx, ProjectionPredicate<'tcx>>,
) -> QueryResult<'tcx> {
// To only compute normalization once for each projection we only
// normalize if the expected term is an unconstrained inference variable.
//
// E.g. for `<T as Trait>::Assoc == u32` we recursively compute the goal
// `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
// projection cache in the solver.
if self.term_is_fully_unconstrained(goal) {
let candidates = self.assemble_and_evaluate_candidates(goal);
self.merge_candidates(candidates)
} else {
self.set_normalizes_to_hack_goal(goal);
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
match goal.predicate.projection_ty.kind(self.tcx()) {
ty::AliasKind::Projection => {
// To only compute normalization once for each projection we only
// normalize if the expected term is an unconstrained inference variable.
//
// E.g. for `<T as Trait>::Assoc == u32` we recursively compute the goal
// `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
// projection cache in the solver.
if self.term_is_fully_unconstrained(goal) {
let candidates = self.assemble_and_evaluate_candidates(goal);
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),
ty::AliasKind::Inherent => bug!("IATs not supported here yet"),
}
}
}

View File

@ -11,7 +11,7 @@
use super::StackDepth;
use rustc_data_structures::fx::FxHashMap;
use rustc_index::IndexVec;
use rustc_middle::traits::solve::{CanonicalGoal, QueryResult};
use rustc_middle::traits::solve::{CanonicalInput, QueryResult};
rustc_index::newtype_index! {
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
// in the lookup table.
pub(super) goal: CanonicalGoal<'tcx>,
pub(super) input: CanonicalInput<'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
// is in the cache. We should experiment with using something like
// `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> {

View File

@ -8,7 +8,7 @@
use overflow::OverflowData;
use rustc_index::IndexVec;
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 std::{collections::hash_map::Entry, mem};
@ -19,7 +19,7 @@ pub struct StackDepth {}
}
struct StackElem<'tcx> {
goal: CanonicalGoal<'tcx>,
input: CanonicalInput<'tcx>,
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.
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];
self.provisional_cache.entries[entry_index].depth != stack_depth
} else {
@ -92,20 +92,20 @@ pub(super) fn in_cycle(&self) -> bool {
fn try_push_stack(
&mut self,
tcx: TyCtxt<'tcx>,
goal: CanonicalGoal<'tcx>,
input: CanonicalInput<'tcx>,
) -> Result<(), QueryResult<'tcx>> {
// Look at the provisional cache to check for cycles.
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.
Entry::Vacant(v) => {
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 response = super::response_no_constraints(tcx, goal, Certainty::Yes);
let entry_index = cache.entries.push(ProvisionalEntry { response, depth, goal });
let depth = self.stack.push(StackElem { input, has_been_used: false });
let response = super::response_no_constraints(tcx, input, Certainty::Yes);
let entry_index = cache.entries.push(ProvisionalEntry { response, depth, input });
v.insert(entry_index);
Ok(())
}
@ -135,13 +135,13 @@ fn try_push_stack(
// the stack is enough.
if self.stack.raw[stack_depth.index()..]
.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))
} else {
Err(super::response_no_constraints(
tcx,
goal,
input,
Certainty::Maybe(MaybeCause::Overflow),
))
}
@ -161,18 +161,18 @@ fn try_push_stack(
/// updated the provisional cache and we have to recompute the current goal.
///
/// 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(
&mut self,
actual_goal: CanonicalGoal<'tcx>,
actual_input: CanonicalInput<'tcx>,
response: QueryResult<'tcx>,
) -> bool {
let stack_elem = self.stack.pop().unwrap();
let StackElem { goal, has_been_used } = stack_elem;
assert_eq!(goal, actual_goal);
let StackElem { input, has_been_used } = stack_elem;
assert_eq!(input, actual_input);
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];
// 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
@ -194,7 +194,7 @@ fn try_finalize_goal(
cache.entries.truncate(provisional_entry_index.index() + 1);
// ...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
} else {
true
@ -204,17 +204,17 @@ fn try_finalize_goal(
pub(super) fn with_new_goal(
&mut self,
tcx: TyCtxt<'tcx>,
canonical_goal: CanonicalGoal<'tcx>,
canonical_input: CanonicalInput<'tcx>,
mut loop_body: impl FnMut(&mut Self) -> QueryResult<'tcx>,
) -> QueryResult<'tcx> {
if self.should_use_global_cache() {
if let Some(result) = tcx.new_solver_evaluation_cache.get(&canonical_goal, tcx) {
debug!(?canonical_goal, ?result, "cache hit");
if let Some(result) = tcx.new_solver_evaluation_cache.get(&canonical_input, tcx) {
debug!(?canonical_input, ?result, "cache hit");
return result;
}
}
match self.try_push_stack(tcx, canonical_goal) {
match self.try_push_stack(tcx, canonical_input) {
Ok(()) => {}
// Our goal is already on the stack, eager return.
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, || {
self.repeat_while_none(
|this| {
let result = this.deal_with_overflow(tcx, canonical_goal);
let result = this.deal_with_overflow(tcx, canonical_input);
let _ = this.stack.pop().unwrap();
result
},
|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 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 depth = provisional_entry.depth;
@ -254,13 +254,13 @@ pub(super) fn with_new_goal(
// cycle participants without moving them to the global cache.
let other_cycle_participants = provisional_entry_index.index() + 1;
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!(entry.depth == depth);
}
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!(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();
if self.should_use_global_cache() && can_cache {
tcx.new_solver_evaluation_cache.insert(
current_goal.goal,
current_goal.input,
dep_node,
current_goal.response,
);

View File

@ -834,8 +834,10 @@ fn evaluate_nested_obligations(
| ty::PredicateKind::Subtype(..)
// FIXME(generic_const_exprs): you can absolutely add this as a where clauses
| ty::PredicateKind::ConstEvaluatable(..)
| ty::PredicateKind::Coerce(..)
| ty::PredicateKind::TypeWellFormedFromEnv(..) => {}
| ty::PredicateKind::Coerce(..) => {}
ty::PredicateKind::TypeWellFormedFromEnv(..) => {
bug!("predicate should only exist in the environment: {bound_predicate:?}")
}
ty::PredicateKind::Ambiguous => return false,
};
}

View File

@ -17,9 +17,10 @@
use rustc_data_structures::fx::FxIndexSet;
use rustc_errors::Diagnostic;
use rustc_hir::def_id::{DefId, CRATE_DEF_ID, LOCAL_CRATE};
use rustc_infer::infer::{DefineOpaqueTypes, DefiningAnchor, InferCtxt, TyCtxtInferExt};
use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, TyCtxtInferExt};
use rustc_infer::traits::util;
use rustc_middle::traits::specialization_graph::OverlapMode;
use rustc_middle::traits::DefiningAnchor;
use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
use rustc_middle::ty::visit::{TypeVisitable, TypeVisitableExt};
use rustc_middle::ty::{self, ImplSubject, Ty, TyCtxt, TypeVisitor};

View File

@ -679,7 +679,7 @@ fn lower_into(
| ty::PredicateKind::ConstEquate(..)
| ty::PredicateKind::Ambiguous
| ty::PredicateKind::TypeWellFormedFromEnv(..) => {
bug!("unexpected predicate {}", &self)
bug!("unexpected predicate {self}")
}
};
value.map(|value| chalk_ir::Binders::new(binders, value))

View File

@ -3,9 +3,9 @@
// seems likely that they should eventually be merged into more
// general routines.
use rustc_infer::infer::{DefiningAnchor, TyCtxtInferExt};
use rustc_infer::infer::TyCtxtInferExt;
use rustc_infer::traits::{FulfillmentErrorCode, TraitEngineExt as _};
use rustc_middle::traits::CodegenObligationError;
use rustc_middle::traits::{CodegenObligationError, DefiningAnchor};
use rustc_middle::ty::{self, TyCtxt};
use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
use rustc_trait_selection::traits::{

View File

@ -1,5 +1,6 @@
use rustc_infer::infer::{DefiningAnchor, TyCtxtInferExt};
use rustc_infer::infer::TyCtxtInferExt;
use rustc_middle::query::Providers;
use rustc_middle::traits::DefiningAnchor;
use rustc_middle::ty::{ParamEnvAnd, TyCtxt};
use rustc_span::source_map::DUMMY_SP;
use rustc_trait_selection::traits::query::CanonicalPredicateGoal;
@ -15,6 +16,7 @@ fn evaluate_obligation<'tcx>(
tcx: TyCtxt<'tcx>,
canonical_goal: CanonicalPredicateGoal<'tcx>,
) -> Result<EvaluationResult, OverflowError> {
assert!(!tcx.trait_solver_next());
debug!("evaluate_obligation(canonical_goal={:#?})", canonical_goal);
// HACK This bubble is required for this tests to pass:
// impl-trait/issue99642.rs

View File

@ -1,8 +1,8 @@
use rustc_hir as hir;
use rustc_infer::infer::canonical::{Canonical, QueryResponse};
use rustc_infer::infer::{DefiningAnchor, TyCtxtInferExt};
use rustc_infer::traits::ObligationCauseCode;
use rustc_infer::infer::TyCtxtInferExt;
use rustc_middle::query::Providers;
use rustc_middle::traits::{DefiningAnchor, ObligationCauseCode};
use rustc_middle::ty::{self, FnSig, Lift, PolyFnSig, Ty, TyCtxt, TypeFoldable};
use rustc_middle::ty::{ParamEnvAnd, Predicate};
use rustc_middle::ty::{UserSelfTy, UserSubsts, UserType};

View File

@ -8,7 +8,7 @@ LL | #![feature(dyn_star, pointer_like_trait)]
= note: `#[warn(incomplete_features)]` on by default
error[E0282]: type annotations needed
--> $DIR/param-env-infer.rs:12:10
--> $DIR/param-env-infer.rs:13:10
|
LL | t as _
| ^ cannot infer type

View File

@ -7,12 +7,67 @@ LL | #![feature(dyn_star, pointer_like_trait)]
= note: see issue #102425 <https://github.com/rust-lang/rust/issues/102425> for more information
= note: `#[warn(incomplete_features)]` on by default
error[E0282]: type annotations needed
--> $DIR/param-env-infer.rs:12:10
error[E0391]: cycle detected when computing type of `make_dyn_star::{opaque#0}`
--> $DIR/param-env-infer.rs:11:60
|
LL | t as _
| ^ cannot infer type
LL | fn make_dyn_star<'a, T: PointerLike + Debug + 'a>(t: T) -> impl PointerLike + Debug + 'a {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: ...which requires borrow-checking `make_dyn_star`...
--> $DIR/param-env-infer.rs:11:1
|
LL | fn make_dyn_star<'a, T: PointerLike + Debug + 'a>(t: T) -> impl PointerLike + Debug + 'a {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: ...which requires promoting constants in MIR for `make_dyn_star`...
--> $DIR/param-env-infer.rs:11:1
|
LL | fn make_dyn_star<'a, T: PointerLike + Debug + 'a>(t: T) -> impl PointerLike + Debug + 'a {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: ...which requires preparing `make_dyn_star` for borrow checking...
--> $DIR/param-env-infer.rs:11:1
|
LL | fn make_dyn_star<'a, T: PointerLike + Debug + 'a>(t: T) -> impl PointerLike + Debug + 'a {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: ...which requires unsafety-checking `make_dyn_star`...
--> $DIR/param-env-infer.rs:11:1
|
LL | fn make_dyn_star<'a, T: PointerLike + Debug + 'a>(t: T) -> impl PointerLike + Debug + 'a {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: ...which requires building MIR for `make_dyn_star`...
--> $DIR/param-env-infer.rs:11:1
|
LL | fn make_dyn_star<'a, T: PointerLike + Debug + 'a>(t: T) -> impl PointerLike + Debug + 'a {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: ...which requires match-checking `make_dyn_star`...
--> $DIR/param-env-infer.rs:11:1
|
LL | fn make_dyn_star<'a, T: PointerLike + Debug + 'a>(t: T) -> impl PointerLike + Debug + 'a {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: ...which requires building THIR for `make_dyn_star`...
--> $DIR/param-env-infer.rs:11:1
|
LL | fn make_dyn_star<'a, T: PointerLike + Debug + 'a>(t: T) -> impl PointerLike + Debug + 'a {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: ...which requires type-checking `make_dyn_star`...
--> $DIR/param-env-infer.rs:11:1
|
LL | fn make_dyn_star<'a, T: PointerLike + Debug + 'a>(t: T) -> impl PointerLike + Debug + 'a {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: ...which requires computing layout of `make_dyn_star::{opaque#0}`...
= note: ...which requires normalizing `make_dyn_star::{opaque#0}`...
= note: ...which again requires computing type of `make_dyn_star::{opaque#0}`, completing the cycle
note: cycle used when checking item types in top-level module
--> $DIR/param-env-infer.rs:5:1
|
LL | / #![feature(dyn_star, pointer_like_trait)]
LL | |
LL | |
LL | | use std::fmt::Debug;
... |
LL | |
LL | | fn main() {}
| |____________^
error: aborting due to previous error; 1 warning emitted
For more information about this error, try `rustc --explain E0282`.
For more information about this error, try `rustc --explain E0391`.

View File

@ -9,8 +9,9 @@
use std::marker::PointerLike;
fn make_dyn_star<'a, T: PointerLike + Debug + 'a>(t: T) -> impl PointerLike + Debug + 'a {
//[next]~^ ERROR cycle detected when computing type of `make_dyn_star::{opaque#0}`
t as _
//~^ ERROR type annotations needed
//[current]~^ ERROR type annotations needed
}
fn main() {}

View File

@ -1,5 +1,8 @@
// compile-flags: -Ztrait-solver=next
// check-pass
// (should not pass, should be turned into a coherence-only test)
// check that when computing `alias-eq(<() as Foo<u16, T>>::Assoc, <() as Foo<?0, T>>::Assoc)`
// we do not infer `?0 = u8` via the `for<STOP> (): Foo<u8, STOP>` impl or `?0 = u16` by
// relating substs as either could be a valid solution.
@ -36,7 +39,6 @@ fn incomplete<T>()
{
// `<() as Foo<u16, STOP>>::Assoc == <() as Foo<_, STOP>>::Assoc`
let _: <() as Foo<u16, T>>::Assoc = output::<_, T>();
//~^ error: type annotations needed
// let _: <() as Foo<u16, T>>::Assoc = output::<u8, T>(); // OK
// let _: <() as Foo<u16, T>>::Assoc = output::<u16, T>(); // OK

View File

@ -1,9 +0,0 @@
error[E0282]: type annotations needed
--> $DIR/alias_eq_dont_use_normalizes_to_if_substs_eq.rs:38:41
|
LL | let _: <() as Foo<u16, T>>::Assoc = output::<_, T>();
| ^^^^^^^^^^^^^^ cannot infer type of the type parameter `T` declared on the function `output`
error: aborting due to previous error
For more information about this error, try `rustc --explain E0282`.

View File

@ -1,5 +1,8 @@
// compile-flags: -Ztrait-solver=next
// check-pass
// (should not pass, should be turned into a coherence-only test)
// check that a `alias-eq(<?0 as TraitB>::Assoc, <T as TraitB>::Assoc)` goal fails.
// FIXME(deferred_projection_equality): add a test that this is true during coherence
@ -14,7 +17,6 @@ fn needs_a<T: TraitB>() -> T::Assoc {
fn bar<T: TraitB>() {
let _: <_ as TraitB>::Assoc = needs_a::<T>();
//~^ error: type annotations needed
}
fn main() {}

View File

@ -1,9 +0,0 @@
error[E0282]: type annotations needed
--> $DIR/alias_eq_substs_eq_not_intercrate.rs:16:12
|
LL | let _: <_ as TraitB>::Assoc = needs_a::<T>();
| ^^^^^^^^^^^^^^^^^^^^ cannot infer type for associated type `<_ as TraitB>::Assoc`
error: aborting due to previous error
For more information about this error, try `rustc --explain E0282`.