From bbe2f6c0b246abdfdbf309f537c447ecab1664e1 Mon Sep 17 00:00:00 2001 From: lcnr Date: Mon, 29 Jan 2024 17:09:17 +0100 Subject: [PATCH] also try to normalize opaque types in alias-relate with this, alias-relate treats all aliases the same way and it can be used for structural normalization. --- compiler/rustc_hir_typeck/src/lib.rs | 2 + compiler/rustc_hir_typeck/src/writeback.rs | 4 +- compiler/rustc_infer/src/infer/mod.rs | 6 ++ .../src/solve/alias_relate.rs | 86 +++---------------- .../rustc_trait_selection/src/solve/mod.rs | 27 +----- 5 files changed, 27 insertions(+), 98 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index c1af4b5983eb..6d343b3d05e1 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -304,6 +304,8 @@ fn typeck_with_fallback<'tcx>( let typeck_results = fcx.resolve_type_vars_in_body(body); + let _ = fcx.infcx.take_opaque_types(); + // Consistency check our TypeckResults instance can hold all ItemLocalIds // it will need to hold. assert_eq!(typeck_results.hir_owner, id.owner); diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index 5ce80ef5c10d..26279098d9e3 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -562,7 +562,9 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { #[instrument(skip(self), level = "debug")] fn visit_opaque_types(&mut self) { - let opaque_types = self.fcx.infcx.take_opaque_types(); + // We clone the opaques instead of stealing them here as they are still used for + // normalization in the next generation trait solver. + let opaque_types = self.fcx.infcx.clone_opaque_types(); for (opaque_type_key, decl) in opaque_types { let hidden_type = self.resolve(decl.hidden_type, &decl.hidden_type.span); let opaque_type_key = self.resolve(opaque_type_key, &decl.hidden_type.span); diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 0bf4598608f2..2caf3b3cc936 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -1325,6 +1325,12 @@ impl<'tcx> InferCtxt<'tcx> { std::mem::take(&mut self.inner.borrow_mut().opaque_type_storage.opaque_types) } + #[instrument(level = "debug", skip(self), ret)] + pub fn clone_opaque_types(&self) -> opaque_types::OpaqueTypeMap<'tcx> { + debug_assert_ne!(self.defining_use_anchor, DefiningAnchor::Error); + self.inner.borrow().opaque_type_storage.opaque_types.clone() + } + pub fn ty_to_string(&self, t: Ty<'tcx>) -> String { self.resolve_vars_if_possible(t).to_string() } diff --git a/compiler/rustc_trait_selection/src/solve/alias_relate.rs b/compiler/rustc_trait_selection/src/solve/alias_relate.rs index c05c99617504..999367e75897 100644 --- a/compiler/rustc_trait_selection/src/solve/alias_relate.rs +++ b/compiler/rustc_trait_selection/src/solve/alias_relate.rs @@ -3,27 +3,22 @@ //! of our more general approach to "lazy normalization". //! //! This is done by first normalizing both sides of the goal, ending up in -//! either a concrete type, rigid projection, opaque, or an infer variable. +//! either a concrete type, rigid alias, or an infer variable. //! These are related further according to the rules below: //! -//! (1.) If we end up with a rigid projection and a rigid projection, then we -//! relate those projections structurally. +//! (1.) If we end up with two rigid aliases, then we relate them structurally. //! -//! (2.) If we end up with a rigid projection and an alias, then the opaque will -//! have its hidden type defined to be that rigid projection. -//! -//! (3.) If we end up with an opaque and an opaque, then we assemble two -//! candidates, one defining the LHS to be the hidden type of the RHS, and vice -//! versa. -//! -//! (4.) If we end up with an infer var and an opaque or rigid projection, then +//! (2.) If we end up with an infer var and a rigid alias, then //! we assign the alias to the infer var. //! -//! (5.) If we end up with an opaque and a rigid (non-projection) type, then we -//! define the hidden type of the opaque to be the rigid type. -//! -//! (6.) Otherwise, if we end with two rigid (non-projection) or infer types, +//! (3.) Otherwise, if we end with two rigid (non-projection) or infer types, //! relate them structurally. +//! +//! Subtle: when relating an opaque to another type, we emit a +//! `NormalizesTo(opaque, ?fresh_var)` goal when trying to normalize the opaque. +//! This nested goal starts out as ambiguous and does not actually define the opaque. +//! However, if `?fresh_var` ends up geteting equated to another type, we retry the +//! `NormalizesTo` goal, at which point the opaque is actually defined. use super::{EvalCtxt, GoalSource}; use rustc_infer::infer::DefineOpaqueTypes; @@ -59,31 +54,26 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } - (Some(alias), None) => { + (Some(_), None) => { if rhs.is_infer() { self.relate(param_env, lhs, variance, rhs)?; self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - } else if alias.is_opaque(tcx) { - // FIXME: This doesn't account for variance. - self.define_opaque(param_env, alias, rhs) } else { Err(NoSolution) } } - (None, Some(alias)) => { + (None, Some(_)) => { if lhs.is_infer() { self.relate(param_env, lhs, variance, rhs)?; self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - } else if alias.is_opaque(tcx) { - // FIXME: This doesn't account for variance. - self.define_opaque(param_env, alias, lhs) } else { Err(NoSolution) } } (Some(alias_lhs), Some(alias_rhs)) => { - self.relate_rigid_alias_or_opaque(param_env, alias_lhs, variance, alias_rhs) + self.relate(param_env, alias_lhs, variance, alias_rhs)?; + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } } } @@ -118,52 +108,4 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { } } } - - fn define_opaque( - &mut self, - param_env: ty::ParamEnv<'tcx>, - opaque: ty::AliasTy<'tcx>, - term: ty::Term<'tcx>, - ) -> QueryResult<'tcx> { - self.add_goal( - GoalSource::Misc, - Goal::new(self.tcx(), param_env, ty::NormalizesTo { alias: opaque, term }), - ); - self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - } - - fn relate_rigid_alias_or_opaque( - &mut self, - param_env: ty::ParamEnv<'tcx>, - lhs: ty::AliasTy<'tcx>, - variance: ty::Variance, - rhs: ty::AliasTy<'tcx>, - ) -> QueryResult<'tcx> { - let tcx = self.tcx(); - let mut candidates = vec![]; - if lhs.is_opaque(tcx) { - candidates.extend( - self.probe_misc_candidate("define-lhs-opaque") - .enter(|ecx| ecx.define_opaque(param_env, lhs, rhs.to_ty(tcx).into())), - ); - } - - if rhs.is_opaque(tcx) { - candidates.extend( - self.probe_misc_candidate("define-rhs-opaque") - .enter(|ecx| ecx.define_opaque(param_env, rhs, lhs.to_ty(tcx).into())), - ); - } - - candidates.extend(self.probe_misc_candidate("args-relate").enter(|ecx| { - ecx.relate(param_env, lhs, variance, rhs)?; - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - })); - - if let Some(result) = self.try_merge_responses(&candidates) { - Ok(result) - } else { - self.flounder(&candidates) - } - } } diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_trait_selection/src/solve/mod.rs index a7330136fe78..2331931b7b76 100644 --- a/compiler/rustc_trait_selection/src/solve/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/mod.rs @@ -22,8 +22,7 @@ use rustc_middle::traits::solve::{ CanonicalResponse, Certainty, ExternalConstraintsData, Goal, GoalSource, IsNormalizesToHack, QueryResult, Response, }; -use rustc_middle::traits::Reveal; -use rustc_middle::ty::{self, OpaqueTypeKey, Ty, TyCtxt, UniverseIndex}; +use rustc_middle::ty::{self, Ty, TyCtxt, UniverseIndex}; use rustc_middle::ty::{ CoercePredicate, RegionOutlivesPredicate, SubtypePredicate, TypeOutlivesPredicate, }; @@ -292,32 +291,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { return None; } - let ty::Alias(kind, alias) = *ty.kind() else { + let ty::Alias(_, alias) = *ty.kind() else { return Some(ty); }; - // We do no always define opaque types eagerly to allow non-defining uses - // in the defining scope. However, if we can unify this opaque to an existing - // opaque, then we should attempt to eagerly reveal the opaque, and we fall - // through. - if let DefineOpaqueTypes::No = define_opaque_types - && let Reveal::UserFacing = param_env.reveal() - && let ty::Opaque = kind - && let Some(def_id) = alias.def_id.as_local() - && self.can_define_opaque_ty(def_id) - { - if self - .unify_existing_opaque_tys( - param_env, - OpaqueTypeKey { def_id, args: alias.args }, - self.next_ty_infer(), - ) - .is_empty() - { - return Some(ty); - } - } - match self.commit_if_ok(|this| { let normalized_ty = this.next_ty_infer(); let normalizes_to_goal = Goal::new(