diff --git a/compiler/rustc_next_trait_solver/src/solve/alias_relate.rs b/compiler/rustc_next_trait_solver/src/solve/alias_relate.rs index d8c1dc8b4e9f3..9f29e247e8c62 100644 --- a/compiler/rustc_next_trait_solver/src/solve/alias_relate.rs +++ b/compiler/rustc_next_trait_solver/src/solve/alias_relate.rs @@ -15,12 +15,19 @@ //! (3.) Otherwise, if we end with two rigid (non-projection) or infer types, //! relate them structurally. +use rustc_type_ir::data_structures::HashSet; use rustc_type_ir::inherent::*; +use rustc_type_ir::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor}; use rustc_type_ir::{self as ty, Interner}; use tracing::{instrument, trace}; use crate::delegate::SolverDelegate; -use crate::solve::{Certainty, EvalCtxt, Goal, QueryResult}; +use crate::solve::{Certainty, EvalCtxt, Goal, NoSolution, QueryResult}; + +enum IgnoreAliases { + Yes, + No, +} impl EvalCtxt<'_, D> where @@ -36,6 +43,12 @@ where let Goal { param_env, predicate: (lhs, rhs, direction) } = goal; debug_assert!(lhs.to_alias_term().is_some() || rhs.to_alias_term().is_some()); + if self.alias_cannot_name_placeholder_in_rigid(param_env, lhs, rhs) + || self.alias_cannot_name_placeholder_in_rigid(param_env, rhs, lhs) + { + return Err(NoSolution); + } + // Structurally normalize the lhs. let lhs = if let Some(alias) = lhs.to_alias_term() { let term = self.next_term_infer_of_kind(lhs); @@ -96,4 +109,100 @@ where } } } + + /// In case a rigid term refers to a placeholder which is not referenced by the + /// alias, the alias cannot be normalized to that rigid term unless it contains + /// either inference variables or these placeholders are referenced in a term + /// of a `Projection`-clause in the environment. + fn alias_cannot_name_placeholder_in_rigid( + &mut self, + param_env: I::ParamEnv, + rigid_term: I::Term, + alias: I::Term, + ) -> bool { + // Check that the rigid term is actually rigid. + if rigid_term.to_alias_term().is_some() || alias.to_alias_term().is_none() { + return false; + } + + // If the alias has any type or const inference variables, + // do not try to apply the fast path as these inference variables + // may resolve to something containing placeholders. + if alias.has_non_region_infer() { + return false; + } + + let mut referenced_placeholders = + self.collect_placeholders_in_term(rigid_term, IgnoreAliases::Yes); + for clause in param_env.caller_bounds().iter() { + match clause.kind().skip_binder() { + ty::ClauseKind::Projection(ty::ProjectionPredicate { term, .. }) => { + if term.has_non_region_infer() { + return false; + } + + let env_term_placeholders = + self.collect_placeholders_in_term(term, IgnoreAliases::No); + #[allow(rustc::potential_query_instability)] + referenced_placeholders.retain(|p| !env_term_placeholders.contains(p)); + } + ty::ClauseKind::Trait(_) + | ty::ClauseKind::HostEffect(_) + | ty::ClauseKind::TypeOutlives(_) + | ty::ClauseKind::RegionOutlives(_) + | ty::ClauseKind::ConstArgHasType(..) + | ty::ClauseKind::WellFormed(_) + | ty::ClauseKind::ConstEvaluatable(_) => continue, + } + } + + if referenced_placeholders.is_empty() { + return false; + } + + let alias_placeholders = self.collect_placeholders_in_term(alias, IgnoreAliases::No); + // If the rigid term references a placeholder not mentioned by the alias, + // they can never unify. + !referenced_placeholders.is_subset(&alias_placeholders) + } + + fn collect_placeholders_in_term( + &mut self, + term: I::Term, + ignore_aliases: IgnoreAliases, + ) -> HashSet { + // Fast path to avoid walking the term. + if !term.has_placeholders() { + return Default::default(); + } + + struct PlaceholderCollector { + ignore_aliases: IgnoreAliases, + placeholders: HashSet, + } + impl TypeVisitor for PlaceholderCollector { + type Result = (); + + fn visit_ty(&mut self, t: I::Ty) { + match t.kind() { + ty::Placeholder(_) => drop(self.placeholders.insert(t.into())), + ty::Alias(..) if matches!(self.ignore_aliases, IgnoreAliases::Yes) => {} + _ => t.super_visit_with(self), + } + } + + fn visit_const(&mut self, ct: I::Const) { + match ct.kind() { + ty::ConstKind::Placeholder(_) => drop(self.placeholders.insert(ct.into())), + ty::ConstKind::Unevaluated(_) | ty::ConstKind::Expr(_) + if matches!(self.ignore_aliases, IgnoreAliases::Yes) => {} + _ => ct.super_visit_with(self), + } + } + } + + let mut visitor = PlaceholderCollector { ignore_aliases, placeholders: Default::default() }; + term.visit_with(&mut visitor); + visitor.placeholders + } } diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 4784a4d1953df..5917159d5b201 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -853,7 +853,7 @@ pub struct NextSolverConfig { } impl Default for NextSolverConfig { fn default() -> Self { - NextSolverConfig { coherence: true, globally: false } + NextSolverConfig { coherence: true, globally: true } } }