From 4d80b8090c30980c03ff4bb6f47a7560ffaa68a4 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 9 May 2023 18:56:43 +0000 Subject: [PATCH 1/8] Pull out logic from #111131, plus some new logic in EvalCtxt::normalize_opaque_type Co-authored-by: lcnr --- compiler/rustc_infer/src/infer/combine.rs | 5 +-- compiler/rustc_infer/src/infer/equate.rs | 3 +- compiler/rustc_infer/src/infer/lattice.rs | 5 ++- .../rustc_infer/src/infer/nll_relate/mod.rs | 22 +++++---- .../rustc_infer/src/infer/opaque_types.rs | 45 ++++++++++++++----- compiler/rustc_infer/src/infer/sub.rs | 3 +- compiler/rustc_middle/src/ty/mod.rs | 14 ++---- .../src/solve/fulfill.rs | 4 +- .../rustc_trait_selection/src/solve/mod.rs | 3 +- .../src/solve/opaques.rs | 44 ++++++++++++++++++ .../src/solve/project_goals.rs | 9 +++- .../src/traits/auto_trait.rs | 6 ++- compiler/rustc_traits/src/chalk/lowering.rs | 2 +- 13 files changed, 122 insertions(+), 43 deletions(-) create mode 100644 compiler/rustc_trait_selection/src/solve/opaques.rs diff --git a/compiler/rustc_infer/src/infer/combine.rs b/compiler/rustc_infer/src/infer/combine.rs index 79fc02c6c7987..b6b935de68c84 100644 --- a/compiler/rustc_infer/src/infer/combine.rs +++ b/compiler/rustc_infer/src/infer/combine.rs @@ -113,10 +113,7 @@ impl<'tcx> InferCtxt<'tcx> { 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) } diff --git a/compiler/rustc_infer/src/infer/equate.rs b/compiler/rustc_infer/src/infer/equate.rs index 793505e4ab23d..42dfe4f6bb812 100644 --- a/compiler/rustc_infer/src/infer/equate.rs +++ b/compiler/rustc_infer/src/infer/equate.rs @@ -104,7 +104,8 @@ impl<'tcx> TypeRelation<'tcx> for Equate<'_, '_, '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 diff --git a/compiler/rustc_infer/src/infer/lattice.rs b/compiler/rustc_infer/src/infer/lattice.rs index 7f4c141b97af2..7190d33d299b7 100644 --- a/compiler/rustc_infer/src/infer/lattice.rs +++ b/compiler/rustc_infer/src/infer/lattice.rs @@ -108,9 +108,12 @@ where &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 diff --git a/compiler/rustc_infer/src/infer/nll_relate/mod.rs b/compiler/rustc_infer/src/infer/nll_relate/mod.rs index 4ae6af5f5be75..d3fd01b964255 100644 --- a/compiler/rustc_infer/src/infer/nll_relate/mod.rs +++ b/compiler/rustc_infer/src/infer/nll_relate/mod.rs @@ -491,16 +491,22 @@ where ( &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) } diff --git a/compiler/rustc_infer/src/infer/opaque_types.rs b/compiler/rustc_infer/src/infer/opaque_types.rs index 213b3f0fe0ed5..21df3291f361d 100644 --- a/compiler/rustc_infer/src/infer/opaque_types.rs +++ b/compiler/rustc_infer/src/infer/opaque_types.rs @@ -2,7 +2,7 @@ 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::traits::{self, PredicateObligation}; use hir::def_id::{DefId, LocalDefId}; use hir::OpaqueTyOrigin; use rustc_data_structures::fx::FxIndexMap; @@ -48,9 +48,15 @@ impl<'tcx> InferCtxt<'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 @@ impl<'tcx> InferCtxt<'tcx> { 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 @@ impl<'tcx> InferCtxt<'tcx> { // 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,26 @@ impl<'tcx> InferCtxt<'tcx> { Vec::new() }; + self.add_item_bounds_for_hidden_type( + opaque_type_key, + cause, + param_env, + hidden_ty, + &mut obligations, + ); + + Ok(InferOk { value: (), obligations }) + } + + 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>, + ) { + 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 +578,15 @@ impl<'tcx> InferCtxt<'tcx> { // 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 +612,10 @@ impl<'tcx> InferCtxt<'tcx> { 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 +626,6 @@ impl<'tcx> InferCtxt<'tcx> { predicate, )); } - Ok(InferOk { value: (), obligations }) } } diff --git a/compiler/rustc_infer/src/infer/sub.rs b/compiler/rustc_infer/src/infer/sub.rs index e0f29a8de8fc8..ceafafb5582cd 100644 --- a/compiler/rustc_infer/src/infer/sub.rs +++ b/compiler/rustc_infer/src/infer/sub.rs @@ -131,7 +131,8 @@ impl<'tcx> TypeRelation<'tcx> for Sub<'_, '_, '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 diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index c51548501ef29..73f435d4840ac 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -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> { + /// This function returns the inner `AliasTy` for a `ty::Alias` or `ConstKind::Unevaluated`. + pub fn to_alias_ty(&self, tcx: TyCtxt<'tcx>) -> Option> { 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() { diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs index 32bd10f0beba5..4a403196c7e05 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -133,12 +133,14 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> { | 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, }); diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_trait_selection/src/solve/mod.rs index d94679fef2833..1427268f8291f 100644 --- a/compiler/rustc_trait_selection/src/solve/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/mod.rs @@ -24,6 +24,7 @@ mod assembly; mod canonicalize; mod eval_ctxt; mod fulfill; +mod opaques; mod project_goals; mod search_graph; mod trait_goals; @@ -212,7 +213,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { ); } - 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 diff --git a/compiler/rustc_trait_selection/src/solve/opaques.rs b/compiler/rustc_trait_selection/src/solve/opaques.rs new file mode 100644 index 0000000000000..1a7704593382d --- /dev/null +++ b/compiler/rustc_trait_selection/src/solve/opaques.rs @@ -0,0 +1,44 @@ +use rustc_infer::infer::InferOk; +use rustc_middle::traits::solve::{Certainty, Goal, QueryResult}; +use rustc_middle::traits::{ObligationCause, Reveal}; +use rustc_middle::ty::ProjectionPredicate; + +use super::{EvalCtxt, SolverMode}; + +impl<'tcx> EvalCtxt<'_, 'tcx> { + pub(super) fn normalize_opaque_type( + &mut self, + goal: Goal<'tcx, 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() { + 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 + let InferOk { value: (), obligations } = self.infcx.handle_opaque_type( + expected, + 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) + }), + SolverMode::Coherence => { + self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) + } + }, + Reveal::All => self.probe(|ecx| { + 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) + }), + } + } +} diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs index d322807442130..248b750b3bdaf 100644 --- a/compiler/rustc_trait_selection/src/solve/project_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs @@ -30,8 +30,13 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { // `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) + match goal.predicate.projection_ty.kind(self.tcx()) { + ty::AliasKind::Projection => { + let candidates = self.assemble_and_evaluate_candidates(goal); + self.merge_candidates(candidates) + } + ty::AliasKind::Opaque => self.normalize_opaque_type(goal), + } } else { self.set_normalizes_to_hack_goal(goal); self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs index 183c2401fc3ac..62d2aad5277f6 100644 --- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs +++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs @@ -834,8 +834,10 @@ impl<'tcx> AutoTraitFinder<'tcx> { | 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, }; } diff --git a/compiler/rustc_traits/src/chalk/lowering.rs b/compiler/rustc_traits/src/chalk/lowering.rs index 2f9e480d8bd52..e447ab94f6408 100644 --- a/compiler/rustc_traits/src/chalk/lowering.rs +++ b/compiler/rustc_traits/src/chalk/lowering.rs @@ -679,7 +679,7 @@ impl<'tcx> LowerInto<'tcx, Option { - bug!("unexpected predicate {}", &self) + bug!("unexpected predicate {self}") } }; value.map(|value| chalk_ir::Binders::new(binders, value)) From a2d7ffc6353b65ff730dd1e7d8027b3751bf82aa Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 9 May 2023 20:18:22 +0000 Subject: [PATCH 2/8] Move DefiningAnchor --- compiler/rustc_borrowck/src/consumers.rs | 3 ++- compiler/rustc_borrowck/src/lib.rs | 3 ++- .../rustc_borrowck/src/region_infer/opaque_types.rs | 3 ++- compiler/rustc_const_eval/src/util/compare_types.rs | 4 ++-- compiler/rustc_hir_analysis/src/check/check.rs | 3 ++- compiler/rustc_hir_typeck/src/inherited.rs | 3 ++- compiler/rustc_infer/src/infer/mod.rs | 13 +------------ compiler/rustc_infer/src/infer/opaque_types.rs | 4 ++-- compiler/rustc_middle/src/traits/mod.rs | 11 +++++++++++ .../rustc_trait_selection/src/traits/coherence.rs | 3 ++- compiler/rustc_traits/src/codegen.rs | 4 ++-- compiler/rustc_traits/src/evaluate_obligation.rs | 3 ++- compiler/rustc_traits/src/type_op.rs | 4 ++-- 13 files changed, 34 insertions(+), 27 deletions(-) diff --git a/compiler/rustc_borrowck/src/consumers.rs b/compiler/rustc_borrowck/src/consumers.rs index dc20b371d92e0..d257145373f72 100644 --- a/compiler/rustc_borrowck/src/consumers.rs +++ b/compiler/rustc_borrowck/src/consumers.rs @@ -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; diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 5124aa459b650..9277a262f9789 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -26,7 +26,7 @@ use rustc_hir::def_id::LocalDefId; 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::{AggregateKind, BasicBlock, BorrowCheckResult, BorrowKind 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}; diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs index e9b5c47ce23c4..7fc89e89a3599 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs @@ -2,9 +2,10 @@ use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; 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}; diff --git a/compiler/rustc_const_eval/src/util/compare_types.rs b/compiler/rustc_const_eval/src/util/compare_types.rs index f5f3d5de6b5a2..d6a2ffb75111a 100644 --- a/compiler/rustc_const_eval/src/util/compare_types.rs +++ b/compiler/rustc_const_eval/src/util/compare_types.rs @@ -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; diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 933923f9dfc2d..3b2c052e8f459 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -13,11 +13,12 @@ use rustc_hir::intravisit::Visitor; 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}; diff --git a/compiler/rustc_hir_typeck/src/inherited.rs b/compiler/rustc_hir_typeck/src/inherited.rs index 95fb32ee1cdf8..294c3bb78a5ba 100644 --- a/compiler/rustc_hir_typeck/src/inherited.rs +++ b/compiler/rustc_hir_typeck/src/inherited.rs @@ -4,7 +4,8 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; 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; diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index f8329965c4360..cd99fc312129b 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -24,7 +24,7 @@ use rustc_middle::infer::unify_key::{ConstVarValue, ConstVariableValue}; 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 @@ impl<'tcx> InferCtxtInner<'tcx> { } } -#[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>, diff --git a/compiler/rustc_infer/src/infer/opaque_types.rs b/compiler/rustc_infer/src/infer/opaque_types.rs index 21df3291f361d..38bb5a3143b35 100644 --- a/compiler/rustc_infer/src/infer/opaque_types.rs +++ b/compiler/rustc_infer/src/infer/opaque_types.rs @@ -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::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; diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index 5b0b40cbfd9bc..0a903a76974da 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -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, +} diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index aacbfbd3c877a..e8c5a8fab2a37 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -17,9 +17,10 @@ use crate::traits::{ 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}; diff --git a/compiler/rustc_traits/src/codegen.rs b/compiler/rustc_traits/src/codegen.rs index 6f81d343e0fd8..ddba03b0b12b3 100644 --- a/compiler/rustc_traits/src/codegen.rs +++ b/compiler/rustc_traits/src/codegen.rs @@ -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::{ diff --git a/compiler/rustc_traits/src/evaluate_obligation.rs b/compiler/rustc_traits/src/evaluate_obligation.rs index 149dffc7e3115..3d630f14f87f0 100644 --- a/compiler/rustc_traits/src/evaluate_obligation.rs +++ b/compiler/rustc_traits/src/evaluate_obligation.rs @@ -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; diff --git a/compiler/rustc_traits/src/type_op.rs b/compiler/rustc_traits/src/type_op.rs index 70dc7ccec631b..b1cbd88ce27ea 100644 --- a/compiler/rustc_traits/src/type_op.rs +++ b/compiler/rustc_traits/src/type_op.rs @@ -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}; From f3c9c216583d396e717e967becb42ece74651bd0 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 16 May 2023 23:51:23 +0000 Subject: [PATCH 3/8] Prepopulate opaques in canonical input --- compiler/rustc_middle/src/arena.rs | 1 + compiler/rustc_middle/src/traits/solve.rs | 67 ++++++++++++++++- compiler/rustc_middle/src/ty/context.rs | 8 ++- .../src/solve/assembly/mod.rs | 8 ++- .../src/solve/eval_ctxt.rs | 71 ++++++++++++++++--- .../src/solve/eval_ctxt/canonical.rs | 61 +++++++++++++--- .../src/solve/opaques.rs | 18 ++--- .../src/solve/project_goals.rs | 29 ++++---- .../src/solve/search_graph/cache.rs | 6 +- .../src/solve/search_graph/mod.rs | 54 +++++++------- .../rustc_traits/src/evaluate_obligation.rs | 1 + 11 files changed, 243 insertions(+), 81 deletions(-) diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs index 6a1a2a061ddd6..a149a61ec136e 100644 --- a/compiler/rustc_middle/src/arena.rs +++ b/compiler/rustc_middle/src/arena.rs @@ -121,6 +121,7 @@ macro_rules! arena_types { >, [] bit_set_u32: rustc_index::bit_set::BitSet, [] 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, diff --git a/compiler/rustc_middle/src/traits/solve.rs b/compiler/rustc_middle/src/traits/solve.rs index 1511c906d1e86..2c5b64a59cdb9 100644 --- a/compiler/rustc_middle/src/traits/solve.rs +++ b/compiler/rustc_middle/src/traits/solve.rs @@ -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, QueryResult<'tcx>>; +pub type EvaluationCache<'tcx> = Cache, 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> 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> for PredefinedOpaques<'tcx> { + fn try_fold_with>>( + self, + folder: &mut F, + ) -> Result { + Ok(FallibleTypeFolder::interner(folder).mk_predefined_opaques_in_body( + PredefinedOpaquesData { + opaque_types: self + .opaque_types + .iter() + .map(|opaque| opaque.try_fold_with(folder)) + .collect::>()?, + }, + )) + } + + fn fold_with>>(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> for PredefinedOpaques<'tcx> { + fn visit_with>>( + &self, + visitor: &mut V, + ) -> std::ops::ControlFlow { + self.opaque_types.visit_with(visitor) + } +} diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 6c931a24462cf..2bde55bc4fd31 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -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>, } @@ -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 { diff --git a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs index d14e6244f7d07..4e06832e65bd1 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs @@ -333,8 +333,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { candidates: &mut Vec>, ) { 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 +355,11 @@ impl<'tcx> EvalCtxt<'_, '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. diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs index 63a73f8d50d93..bd83666eb1ee5 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs @@ -9,7 +9,10 @@ use rustc_infer::infer::{ 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, Certainty, MaybeCause, PredefinedOpaques, PredefinedOpaquesData, QueryResult, +}; +use rustc_middle::traits::DefiningAnchor; use rustc_middle::ty::{ self, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, @@ -44,6 +47,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 +132,11 @@ impl<'tcx> InferCtxtEvalExt<'tcx> for InferCtxt<'tcx> { 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 +173,59 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { 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 + .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 { 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 +240,8 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { 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 +460,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { 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 +725,16 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { | 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(()) + } } diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs index 67ad7fb4bd21d..8d12c7edfe3b8 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs @@ -8,7 +8,7 @@ /// 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; @@ -16,8 +16,11 @@ 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_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::traits::ObligationCause; +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>, CanonicalGoal<'tcx>) { + ) -> (Vec>, 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 @@ impl<'tcx> EvalCtxt<'_, '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 })) @@ -162,12 +179,13 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { let Response { var_values, external_constraints, certainty } = 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(); self.register_region_constraints(region_constraints); + nested_goals.extend(self.register_opaque_types(param_env, opaque_types)?); Ok((certainty, nested_goals)) } @@ -287,4 +305,29 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { let _ = member_constraint; } } + + fn register_opaque_types( + &mut self, + param_env: ty::ParamEnv<'tcx>, + opaque_types: &[(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)], + ) -> Result>>, 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) + } } diff --git a/compiler/rustc_trait_selection/src/solve/opaques.rs b/compiler/rustc_trait_selection/src/solve/opaques.rs index 1a7704593382d..7d6e1647cfcb5 100644 --- a/compiler/rustc_trait_selection/src/solve/opaques.rs +++ b/compiler/rustc_trait_selection/src/solve/opaques.rs @@ -1,14 +1,13 @@ -use rustc_infer::infer::InferOk; use rustc_middle::traits::solve::{Certainty, Goal, QueryResult}; -use rustc_middle::traits::{ObligationCause, Reveal}; -use rustc_middle::ty::ProjectionPredicate; +use rustc_middle::traits::Reveal; +use rustc_middle::ty::{self}; use super::{EvalCtxt, SolverMode}; impl<'tcx> EvalCtxt<'_, 'tcx> { pub(super) fn normalize_opaque_type( &mut self, - goal: Goal<'tcx, ProjectionPredicate<'tcx>>, + goal: Goal<'tcx, ty::ProjectionPredicate<'tcx>>, ) -> QueryResult<'tcx> { let tcx = self.tcx(); let opaque_ty = goal.predicate.projection_ty; @@ -19,15 +18,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { 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 - let InferOk { value: (), obligations } = self.infcx.handle_opaque_type( - expected, - 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)); + 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) }), SolverMode::Coherence => { diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs index 248b750b3bdaf..7d7dfa2c83776 100644 --- a/compiler/rustc_trait_selection/src/solve/project_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs @@ -22,24 +22,25 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { &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 `::Assoc == u32` we recursively compute the goal - // `exists ::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) { - match goal.predicate.projection_ty.kind(self.tcx()) { - ty::AliasKind::Projection => { + 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 `::Assoc == u32` we recursively compute the goal + // `exists ::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), } - } 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"), } } } diff --git a/compiler/rustc_trait_selection/src/solve/search_graph/cache.rs b/compiler/rustc_trait_selection/src/solve/search_graph/cache.rs index e6941af7b57d8..56f126e91572f 100644 --- a/compiler/rustc_trait_selection/src/solve/search_graph/cache.rs +++ b/compiler/rustc_trait_selection/src/solve/search_graph/cache.rs @@ -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, EntryIndex>, + pub(super) lookup_table: FxHashMap, EntryIndex>, } impl<'tcx> ProvisionalCache<'tcx> { diff --git a/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs b/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs index c1904352574e5..19e4b23009a79 100644 --- a/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs @@ -8,7 +8,7 @@ use cache::ProvisionalCache; 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 @@ rustc_index::newtype_index! { } struct StackElem<'tcx> { - goal: CanonicalGoal<'tcx>, + input: CanonicalInput<'tcx>, has_been_used: bool, } @@ -77,7 +77,7 @@ impl<'tcx> SearchGraph<'tcx> { } // ...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[¤t_goal]; self.provisional_cache.entries[entry_index].depth != stack_depth } else { @@ -92,20 +92,20 @@ impl<'tcx> SearchGraph<'tcx> { 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 @@ impl<'tcx> SearchGraph<'tcx> { // 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 @@ impl<'tcx> SearchGraph<'tcx> { /// 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 @@ impl<'tcx> SearchGraph<'tcx> { 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 @@ impl<'tcx> SearchGraph<'tcx> { 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 @@ impl<'tcx> SearchGraph<'tcx> { 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 @@ impl<'tcx> SearchGraph<'tcx> { // 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(¤t_goal.goal); + let actual_index = cache.lookup_table.remove(¤t_goal.input); debug_assert_eq!(Some(provisional_entry_index), actual_index); debug_assert!(current_goal.depth == depth); @@ -274,7 +274,7 @@ impl<'tcx> SearchGraph<'tcx> { 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, ); diff --git a/compiler/rustc_traits/src/evaluate_obligation.rs b/compiler/rustc_traits/src/evaluate_obligation.rs index 3d630f14f87f0..f5b2753b7973d 100644 --- a/compiler/rustc_traits/src/evaluate_obligation.rs +++ b/compiler/rustc_traits/src/evaluate_obligation.rs @@ -16,6 +16,7 @@ fn evaluate_obligation<'tcx>( tcx: TyCtxt<'tcx>, canonical_goal: CanonicalPredicateGoal<'tcx>, ) -> Result { + 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 From e3f8beaed6d70d4b990f834fa617149e2bf40a2d Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 17 May 2023 20:45:14 +0000 Subject: [PATCH 4/8] Check that opaque is a defining use, prefer pre-defined opaques --- compiler/rustc_middle/src/ty/util.rs | 36 ++++++++++++ .../src/solve/eval_ctxt.rs | 48 +++++++++++++++- .../src/solve/opaques.rs | 57 +++++++++++++++---- 3 files changed, 127 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index eb903ebfd99fb..ba05135638e1f 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -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 diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs index bd83666eb1ee5..32a8bda2663da 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs @@ -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> { + 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 + } } diff --git a/compiler/rustc_trait_selection/src/solve/opaques.rs b/compiler/rustc_trait_selection/src/solve/opaques.rs index 7d6e1647cfcb5..1d5bb46450c8e 100644 --- a/compiler/rustc_trait_selection/src/solve/opaques.rs +++ b/compiler/rustc_trait_selection/src/solve/opaques.rs @@ -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) + } } } } From a2d806d56d3c3390b6874cb1eec90ca3c7191fda Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 18 May 2023 19:05:43 +0000 Subject: [PATCH 5/8] Pre-populate MIR with opaques, prefer subst-relate candidate --- compiler/rustc_borrowck/src/type_check/mod.rs | 62 +++++++++++++++++++ .../rustc_trait_selection/src/solve/mod.rs | 44 ++++++------- .../src/solve/opaques.rs | 9 +-- 3 files changed, 87 insertions(+), 28 deletions(-) diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 162828ee80b44..b63f51138c326 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -26,6 +26,7 @@ use rustc_middle::mir::tcx::PlaceTy; use rustc_middle::mir::visit::{NonMutatingUseContext, PlaceContext, Visitor}; use rustc_middle::mir::AssertKind; use rustc_middle::mir::*; +use rustc_middle::traits::ObligationCause; use rustc_middle::ty::adjustment::PointerCast; use rustc_middle::ty::cast::CastTy; use rustc_middle::ty::subst::{SubstsRef, UserSubsts}; @@ -48,6 +49,7 @@ use rustc_mir_dataflow::impls::MaybeInitializedPlaces; 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 +185,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 +1034,57 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { 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 } diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_trait_selection/src/solve/mod.rs index 1427268f8291f..26ace28f5fd24 100644 --- a/compiler/rustc_trait_selection/src/solve/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/mod.rs @@ -239,34 +239,34 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { 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(); - - 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)?; - } + 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)?; } + } - 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) } diff --git a/compiler/rustc_trait_selection/src/solve/opaques.rs b/compiler/rustc_trait_selection/src/solve/opaques.rs index 1d5bb46450c8e..f97f54e760e33 100644 --- a/compiler/rustc_trait_selection/src/solve/opaques.rs +++ b/compiler/rustc_trait_selection/src/solve/opaques.rs @@ -1,6 +1,6 @@ use rustc_middle::traits::query::NoSolution; use rustc_middle::traits::solve::{Certainty, Goal, QueryResult}; -use rustc_middle::traits::{ObligationCause, Reveal}; +use rustc_middle::traits::Reveal; use rustc_middle::ty; use rustc_middle::ty::util::NotUniqueParam; @@ -37,11 +37,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { Ok(()) => {} } // Prefer opaques registered already. - let matches = self.unify_existing_opaque_tys( - goal.param_env, - opaque_ty, - expected - ); + 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); From 97c11ffb227c906465d339b36286e6fb99051e3a Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 22 May 2023 23:48:06 +0000 Subject: [PATCH 6/8] Strongly prefer alias and param-env bounds --- .../src/solve/assembly/mod.rs | 14 ++-- .../dyn-star/param-env-infer.current.stderr | 2 +- tests/ui/dyn-star/param-env-infer.next.stderr | 65 +++++++++++++++++-- tests/ui/dyn-star/param-env-infer.rs | 3 +- ..._eq_dont_use_normalizes_to_if_substs_eq.rs | 4 +- ...dont_use_normalizes_to_if_substs_eq.stderr | 9 --- .../alias_eq_substs_eq_not_intercrate.rs | 4 +- .../alias_eq_substs_eq_not_intercrate.stderr | 9 --- 8 files changed, 78 insertions(+), 32 deletions(-) delete mode 100644 tests/ui/traits/new-solver/alias_eq_dont_use_normalizes_to_if_substs_eq.stderr delete mode 100644 tests/ui/traits/new-solver/alias_eq_substs_eq_not_intercrate.stderr diff --git a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs index 4e06832e65bd1..f32ff0442a4c5 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs @@ -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; @@ -744,13 +743,18 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { 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::>(); if let Some(result) = self.try_merge_responses(¶m_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); } } } diff --git a/tests/ui/dyn-star/param-env-infer.current.stderr b/tests/ui/dyn-star/param-env-infer.current.stderr index d0785c887d895..b3af7be7950c5 100644 --- a/tests/ui/dyn-star/param-env-infer.current.stderr +++ b/tests/ui/dyn-star/param-env-infer.current.stderr @@ -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 diff --git a/tests/ui/dyn-star/param-env-infer.next.stderr b/tests/ui/dyn-star/param-env-infer.next.stderr index d0785c887d895..64d76bb04b1cb 100644 --- a/tests/ui/dyn-star/param-env-infer.next.stderr +++ b/tests/ui/dyn-star/param-env-infer.next.stderr @@ -7,12 +7,67 @@ LL | #![feature(dyn_star, pointer_like_trait)] = note: see issue #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`. diff --git a/tests/ui/dyn-star/param-env-infer.rs b/tests/ui/dyn-star/param-env-infer.rs index 9039dde5e93e2..1fb16d7685369 100644 --- a/tests/ui/dyn-star/param-env-infer.rs +++ b/tests/ui/dyn-star/param-env-infer.rs @@ -9,8 +9,9 @@ use std::fmt::Debug; 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() {} diff --git a/tests/ui/traits/new-solver/alias_eq_dont_use_normalizes_to_if_substs_eq.rs b/tests/ui/traits/new-solver/alias_eq_dont_use_normalizes_to_if_substs_eq.rs index 531203d9c64f7..3c7fc7403b119 100644 --- a/tests/ui/traits/new-solver/alias_eq_dont_use_normalizes_to_if_substs_eq.rs +++ b/tests/ui/traits/new-solver/alias_eq_dont_use_normalizes_to_if_substs_eq.rs @@ -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>::Assoc, <() as Foo>::Assoc)` // we do not infer `?0 = u8` via the `for (): Foo` impl or `?0 = u16` by // relating substs as either could be a valid solution. @@ -36,7 +39,6 @@ where { // `<() as Foo>::Assoc == <() as Foo<_, STOP>>::Assoc` let _: <() as Foo>::Assoc = output::<_, T>(); - //~^ error: type annotations needed // let _: <() as Foo>::Assoc = output::(); // OK // let _: <() as Foo>::Assoc = output::(); // OK diff --git a/tests/ui/traits/new-solver/alias_eq_dont_use_normalizes_to_if_substs_eq.stderr b/tests/ui/traits/new-solver/alias_eq_dont_use_normalizes_to_if_substs_eq.stderr deleted file mode 100644 index a6712332c37c5..0000000000000 --- a/tests/ui/traits/new-solver/alias_eq_dont_use_normalizes_to_if_substs_eq.stderr +++ /dev/null @@ -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>::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`. diff --git a/tests/ui/traits/new-solver/alias_eq_substs_eq_not_intercrate.rs b/tests/ui/traits/new-solver/alias_eq_substs_eq_not_intercrate.rs index d4cc380fa211b..b036411be838e 100644 --- a/tests/ui/traits/new-solver/alias_eq_substs_eq_not_intercrate.rs +++ b/tests/ui/traits/new-solver/alias_eq_substs_eq_not_intercrate.rs @@ -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(::Assoc, ::Assoc)` goal fails. // FIXME(deferred_projection_equality): add a test that this is true during coherence @@ -14,7 +17,6 @@ fn needs_a() -> T::Assoc { fn bar() { let _: <_ as TraitB>::Assoc = needs_a::(); - //~^ error: type annotations needed } fn main() {} diff --git a/tests/ui/traits/new-solver/alias_eq_substs_eq_not_intercrate.stderr b/tests/ui/traits/new-solver/alias_eq_substs_eq_not_intercrate.stderr deleted file mode 100644 index d063d8fce111c..0000000000000 --- a/tests/ui/traits/new-solver/alias_eq_substs_eq_not_intercrate.stderr +++ /dev/null @@ -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::(); - | ^^^^^^^^^^^^^^^^^^^^ 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`. From 980da667fec746b6be52447169bc4ff29855c651 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 23 May 2023 00:36:20 +0000 Subject: [PATCH 7/8] Add InferCtxt::register_hidden_type_in_new_solver --- compiler/rustc_borrowck/src/type_check/mod.rs | 1 - .../rustc_infer/src/infer/opaque_types.rs | 23 ++++++++++++++ .../src/solve/eval_ctxt.rs | 23 +++++--------- .../src/solve/eval_ctxt/canonical.rs | 31 ++++++------------- .../src/solve/opaques.rs | 10 ++++-- 5 files changed, 47 insertions(+), 41 deletions(-) diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index b63f51138c326..cf204cff6b3a7 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -26,7 +26,6 @@ use rustc_middle::mir::tcx::PlaceTy; use rustc_middle::mir::visit::{NonMutatingUseContext, PlaceContext, Visitor}; use rustc_middle::mir::AssertKind; use rustc_middle::mir::*; -use rustc_middle::traits::ObligationCause; use rustc_middle::ty::adjustment::PointerCast; use rustc_middle::ty::cast::CastTy; use rustc_middle::ty::subst::{SubstsRef, UserSubsts}; diff --git a/compiler/rustc_infer/src/infer/opaque_types.rs b/compiler/rustc_infer/src/infer/opaque_types.rs index 38bb5a3143b35..9d5ec228d827b 100644 --- a/compiler/rustc_infer/src/infer/opaque_types.rs +++ b/compiler/rustc_infer/src/infer/opaque_types.rs @@ -557,6 +557,29 @@ impl<'tcx> InferCtxt<'tcx> { 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>, diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs index 32a8bda2663da..f91c672775301 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs @@ -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}; @@ -192,13 +192,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { 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, - ) + .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 @@ -727,19 +721,18 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { } } - pub(super) fn can_define_opaque_ty(&mut self, def_id: DefId) -> bool { - let Some(def_id) = def_id.as_local() else { return false; }; + 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<'tcx>, + a: ty::OpaqueTypeKey<'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.infcx.register_hidden_type_in_new_solver(a, param_env, b)?; self.add_goals(obligations.into_iter().map(|obligation| obligation.into())); Ok(()) } @@ -749,17 +742,15 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { pub(super) fn unify_existing_opaque_tys( &mut self, param_env: ty::ParamEnv<'tcx>, - key: ty::AliasTy<'tcx>, + key: ty::OpaqueTypeKey<'tcx>, ty: Ty<'tcx>, ) -> Vec> { - 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 { + if candidate_key.def_id != key.def_id { continue; } values.extend(self.probe(|ecx| { diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs index 8d12c7edfe3b8..fdb209fbff871 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs @@ -15,11 +15,11 @@ 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, PredefinedOpaquesData, QueryInput, }; -use rustc_middle::traits::ObligationCause; use rustc_middle::ty::{self, BoundVar, GenericArgKind, Ty}; use rustc_span::DUMMY_SP; use std::iter; @@ -179,13 +179,12 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { let Response { var_values, external_constraints, certainty } = response.substitute(self.tcx(), &substitution); - let mut nested_goals = - self.unify_query_var_values(param_env, &original_values, var_values)?; + let nested_goals = self.unify_query_var_values(param_env, &original_values, var_values)?; let ExternalConstraintsData { region_constraints, opaque_types } = external_constraints.deref(); self.register_region_constraints(region_constraints); - nested_goals.extend(self.register_opaque_types(param_env, opaque_types)?); + self.register_opaque_types(param_env, opaque_types)?; Ok((certainty, nested_goals)) } @@ -310,24 +309,14 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { &mut self, param_env: ty::ParamEnv<'tcx>, opaque_types: &[(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)], - ) -> Result>>, NoSolution> { - let mut nested_goals = vec![]; + ) -> Result<(), NoSolution> { 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), - ); + 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(nested_goals) + Ok(()) } } diff --git a/compiler/rustc_trait_selection/src/solve/opaques.rs b/compiler/rustc_trait_selection/src/solve/opaques.rs index f97f54e760e33..54557aeb9a2dd 100644 --- a/compiler/rustc_trait_selection/src/solve/opaques.rs +++ b/compiler/rustc_trait_selection/src/solve/opaques.rs @@ -18,10 +18,15 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { match goal.param_env.reveal() { Reveal::UserFacing => match self.solver_mode() { 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) { + if !self.can_define_opaque_ty(opaque_ty_def_id) { return Err(NoSolution); } // FIXME: This may have issues when the substs contain aliases... @@ -47,8 +52,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { } } // Otherwise, define a new opaque type - let opaque_ty = tcx.mk_opaque(opaque_ty.def_id, opaque_ty.substs); - self.register_opaque_ty(expected, opaque_ty, goal.param_env)?; + self.register_opaque_ty(opaque_ty, expected, goal.param_env)?; self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } SolverMode::Coherence => { From dd981989726b297d46573ab5a03a661a31927c83 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 23 May 2023 00:56:24 +0000 Subject: [PATCH 8/8] Match on both reveal and solver mode at the same time --- .../src/solve/opaques.rs | 79 +++++++++---------- 1 file changed, 38 insertions(+), 41 deletions(-) diff --git a/compiler/rustc_trait_selection/src/solve/opaques.rs b/compiler/rustc_trait_selection/src/solve/opaques.rs index 54557aeb9a2dd..a5de4ddee82ba 100644 --- a/compiler/rustc_trait_selection/src/solve/opaques.rs +++ b/compiler/rustc_trait_selection/src/solve/opaques.rs @@ -15,51 +15,48 @@ impl<'tcx> EvalCtxt<'_, '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() { - Reveal::UserFacing => match self.solver_mode() { - 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(()) => {} + 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, + ); } - // 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); - } + Err(_) => { + return Err(NoSolution); } - // 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) + Ok(()) => {} } - SolverMode::Coherence => { - self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) + // 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); + } } - }, - Reveal::All => { + // 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)?;