diff options
Diffstat (limited to 'vendor/chalk-solve-0.87.0/src/wf.rs')
-rw-r--r-- | vendor/chalk-solve-0.87.0/src/wf.rs | 1202 |
1 files changed, 1202 insertions, 0 deletions
diff --git a/vendor/chalk-solve-0.87.0/src/wf.rs b/vendor/chalk-solve-0.87.0/src/wf.rs new file mode 100644 index 000000000..d552cdde7 --- /dev/null +++ b/vendor/chalk-solve-0.87.0/src/wf.rs @@ -0,0 +1,1202 @@ +use std::ops::ControlFlow; +use std::{fmt, iter}; + +use crate::{ + ext::*, goal_builder::GoalBuilder, rust_ir::*, solve::Solver, split::Split, RustIrDatabase, +}; +use chalk_ir::{ + cast::*, + fold::shift::Shift, + interner::Interner, + visit::{TypeVisitable, TypeVisitor}, + *, +}; +use tracing::debug; + +#[derive(Debug)] +pub enum WfError<I: Interner> { + IllFormedTypeDecl(chalk_ir::AdtId<I>), + IllFormedOpaqueTypeDecl(chalk_ir::OpaqueTyId<I>), + IllFormedTraitImpl(chalk_ir::TraitId<I>), +} + +impl<I: Interner> fmt::Display for WfError<I> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + WfError::IllFormedTypeDecl(id) => write!( + f, + "type declaration `{:?}` does not meet well-formedness requirements", + id + ), + WfError::IllFormedOpaqueTypeDecl(id) => write!( + f, + "opaque type declaration `{:?}` does not meet well-formedness requirements", + id + ), + WfError::IllFormedTraitImpl(id) => write!( + f, + "trait impl for `{:?}` does not meet well-formedness requirements", + id + ), + } + } +} + +impl<I: Interner> std::error::Error for WfError<I> {} + +pub struct WfSolver<'a, I: Interner> { + db: &'a dyn RustIrDatabase<I>, + solver_builder: &'a dyn Fn() -> Box<dyn Solver<I>>, +} + +struct InputTypeCollector<I: Interner> { + types: Vec<Ty<I>>, + interner: I, +} + +impl<I: Interner> InputTypeCollector<I> { + fn new(interner: I) -> Self { + Self { + types: Vec::new(), + interner, + } + } + + fn types_in(interner: I, value: impl TypeVisitable<I>) -> Vec<Ty<I>> { + let mut collector = Self::new(interner); + value.visit_with(&mut collector, DebruijnIndex::INNERMOST); + collector.types + } +} + +impl<I: Interner> TypeVisitor<I> for InputTypeCollector<I> { + type BreakTy = (); + fn as_dyn(&mut self) -> &mut dyn TypeVisitor<I, BreakTy = Self::BreakTy> { + self + } + + fn interner(&self) -> I { + self.interner + } + + fn visit_where_clause( + &mut self, + where_clause: &WhereClause<I>, + outer_binder: DebruijnIndex, + ) -> ControlFlow<()> { + match where_clause { + WhereClause::AliasEq(alias_eq) => alias_eq + .alias + .clone() + .intern(self.interner) + .visit_with(self, outer_binder), + WhereClause::Implemented(trait_ref) => trait_ref.visit_with(self, outer_binder), + WhereClause::TypeOutlives(TypeOutlives { ty, .. }) => ty.visit_with(self, outer_binder), + WhereClause::LifetimeOutlives(..) => ControlFlow::Continue(()), + } + } + + fn visit_ty(&mut self, ty: &Ty<I>, outer_binder: DebruijnIndex) -> ControlFlow<()> { + let interner = self.interner(); + + let mut push_ty = || { + self.types + .push(ty.clone().shifted_out_to(interner, outer_binder).unwrap()) + }; + match ty.kind(interner) { + TyKind::Adt(id, substitution) => { + push_ty(); + id.visit_with(self, outer_binder); + substitution.visit_with(self, outer_binder) + } + TyKind::AssociatedType(assoc_ty, substitution) => { + push_ty(); + assoc_ty.visit_with(self, outer_binder); + substitution.visit_with(self, outer_binder) + } + TyKind::Scalar(scalar) => { + push_ty(); + scalar.visit_with(self, outer_binder) + } + TyKind::Str => { + push_ty(); + ControlFlow::Continue(()) + } + TyKind::Tuple(arity, substitution) => { + push_ty(); + arity.visit_with(self, outer_binder); + substitution.visit_with(self, outer_binder) + } + TyKind::OpaqueType(opaque_ty, substitution) => { + push_ty(); + opaque_ty.visit_with(self, outer_binder); + substitution.visit_with(self, outer_binder) + } + TyKind::Slice(substitution) => { + push_ty(); + substitution.visit_with(self, outer_binder) + } + TyKind::FnDef(fn_def, substitution) => { + push_ty(); + fn_def.visit_with(self, outer_binder); + substitution.visit_with(self, outer_binder) + } + TyKind::Ref(mutability, lifetime, ty) => { + push_ty(); + mutability.visit_with(self, outer_binder); + lifetime.visit_with(self, outer_binder); + ty.visit_with(self, outer_binder) + } + TyKind::Raw(mutability, substitution) => { + push_ty(); + mutability.visit_with(self, outer_binder); + substitution.visit_with(self, outer_binder) + } + TyKind::Never => { + push_ty(); + ControlFlow::Continue(()) + } + TyKind::Array(ty, const_) => { + push_ty(); + ty.visit_with(self, outer_binder); + const_.visit_with(self, outer_binder) + } + TyKind::Closure(_id, substitution) => { + push_ty(); + substitution.visit_with(self, outer_binder) + } + TyKind::Generator(_generator, substitution) => { + push_ty(); + substitution.visit_with(self, outer_binder) + } + TyKind::GeneratorWitness(_witness, substitution) => { + push_ty(); + substitution.visit_with(self, outer_binder) + } + TyKind::Foreign(_foreign_ty) => { + push_ty(); + ControlFlow::Continue(()) + } + TyKind::Error => { + push_ty(); + ControlFlow::Continue(()) + } + + TyKind::Dyn(clauses) => { + push_ty(); + clauses.visit_with(self, outer_binder) + } + + TyKind::Alias(AliasTy::Projection(proj)) => { + push_ty(); + proj.visit_with(self, outer_binder) + } + + TyKind::Alias(AliasTy::Opaque(opaque_ty)) => { + push_ty(); + opaque_ty.visit_with(self, outer_binder) + } + + TyKind::Placeholder(_) => { + push_ty(); + ControlFlow::Continue(()) + } + + // Type parameters do not carry any input types (so we can sort of assume they are + // always WF). + TyKind::BoundVar(..) => ControlFlow::Continue(()), + + // Higher-kinded types such as `for<'a> fn(&'a u32)` introduce their own implied + // bounds, and these bounds will be enforced upon calling such a function. In some + // sense, well-formedness requirements for the input types of an HKT will be enforced + // lazily, so no need to include them here. + TyKind::Function(..) => ControlFlow::Continue(()), + + TyKind::InferenceVar(..) => { + panic!("unexpected inference variable in wf rules: {:?}", ty) + } + } + } +} + +impl<'a, I> WfSolver<'a, I> +where + I: Interner, +{ + /// Constructs a new `WfSolver`. + pub fn new( + db: &'a dyn RustIrDatabase<I>, + solver_builder: &'a dyn Fn() -> Box<dyn Solver<I>>, + ) -> Self { + Self { db, solver_builder } + } + + pub fn verify_adt_decl(&self, adt_id: AdtId<I>) -> Result<(), WfError<I>> { + let interner = self.db.interner(); + + // Given a struct like + // + // ```rust + // struct Foo<T> where T: Eq { + // data: Vec<T> + // } + // ``` + let adt_datum = self.db.adt_datum(adt_id); + let is_enum = adt_datum.kind == AdtKind::Enum; + + let mut gb = GoalBuilder::new(self.db); + let adt_data = adt_datum + .binders + .map_ref(|b| (&b.variants, &b.where_clauses)); + + // We make a goal like... + // + // forall<T> { ... } + let wg_goal = gb.forall( + &adt_data, + is_enum, + |gb, _, (variants, where_clauses), is_enum| { + let interner = gb.interner(); + + // (FromEnv(T: Eq) => ...) + gb.implies( + where_clauses + .iter() + .cloned() + .map(|wc| wc.into_from_env_goal(interner)), + |gb| { + let sub_goals: Vec<_> = variants + .iter() + .flat_map(|variant| { + let fields = &variant.fields; + + // When checking if Enum is well-formed, we require that all fields of + // each variant are sized. For `structs`, we relax this requirement to + // all but the last field. + let sized_constraint_goal = + WfWellKnownConstraints::struct_sized_constraint( + gb.db(), + fields, + is_enum, + ); + + // WellFormed(Vec<T>), for each field type `Vec<T>` or type that appears in the where clauses + let types = InputTypeCollector::types_in( + gb.interner(), + (&fields, &where_clauses), + ); + + types + .into_iter() + .map(|ty| ty.well_formed().cast(interner)) + .chain(sized_constraint_goal.into_iter()) + }) + .collect(); + + gb.all(sub_goals) + }, + ) + }, + ); + + let wg_goal = wg_goal.into_closed_goal(interner); + let mut fresh_solver = (self.solver_builder)(); + let is_legal = fresh_solver.has_unique_solution(self.db, &wg_goal); + + if !is_legal { + Err(WfError::IllFormedTypeDecl(adt_id)) + } else { + Ok(()) + } + } + + pub fn verify_trait_impl(&self, impl_id: ImplId<I>) -> Result<(), WfError<I>> { + let interner = self.db.interner(); + + let impl_datum = self.db.impl_datum(impl_id); + let trait_id = impl_datum.trait_id(); + + let impl_goal = Goal::all( + interner, + impl_header_wf_goal(self.db, impl_id).into_iter().chain( + impl_datum + .associated_ty_value_ids + .iter() + .filter_map(|&id| compute_assoc_ty_goal(self.db, id)), + ), + ); + + if let Some(well_known) = self.db.trait_datum(trait_id).well_known { + self.verify_well_known_impl(impl_id, well_known)? + } + + debug!("WF trait goal: {:?}", impl_goal); + + let mut fresh_solver = (self.solver_builder)(); + let is_legal = + fresh_solver.has_unique_solution(self.db, &impl_goal.into_closed_goal(interner)); + + if is_legal { + Ok(()) + } else { + Err(WfError::IllFormedTraitImpl(trait_id)) + } + } + + pub fn verify_opaque_ty_decl(&self, opaque_ty_id: OpaqueTyId<I>) -> Result<(), WfError<I>> { + // Given an opaque type like + // ```notrust + // opaque type Foo<T>: Clone where T: Bar = Baz; + // ``` + let interner = self.db.interner(); + + let mut gb = GoalBuilder::new(self.db); + + let datum = self.db.opaque_ty_data(opaque_ty_id); + let bound = &datum.bound; + + // We make a goal like + // + // forall<T> + let goal = gb.forall(bound, opaque_ty_id, |gb, _, bound, opaque_ty_id| { + let interner = gb.interner(); + + let subst = Substitution::from1(interner, gb.db().hidden_opaque_type(opaque_ty_id)); + + let bounds = bound.bounds.clone().substitute(interner, &subst); + let where_clauses = bound.where_clauses.clone().substitute(interner, &subst); + + let clauses = where_clauses + .iter() + .cloned() + .map(|wc| wc.into_from_env_goal(interner)); + + // if (WellFormed(T: Bar)) + gb.implies(clauses, |gb| { + let interner = gb.interner(); + + // all(WellFormed(Baz: Clone)) + gb.all( + bounds + .iter() + .cloned() + .map(|b| b.into_well_formed_goal(interner)), + ) + }) + }); + + debug!("WF opaque type goal: {:#?}", goal); + + let mut new_solver = (self.solver_builder)(); + let is_legal = new_solver.has_unique_solution(self.db, &goal.into_closed_goal(interner)); + + if is_legal { + Ok(()) + } else { + Err(WfError::IllFormedOpaqueTypeDecl(opaque_ty_id)) + } + } + + /// Verify builtin rules for well-known traits + pub fn verify_well_known_impl( + &self, + impl_id: ImplId<I>, + well_known: WellKnownTrait, + ) -> Result<(), WfError<I>> { + let mut solver = (self.solver_builder)(); + let impl_datum = self.db.impl_datum(impl_id); + + let is_legal = match well_known { + WellKnownTrait::Copy => { + WfWellKnownConstraints::copy_impl_constraint(&mut *solver, self.db, &impl_datum) + } + WellKnownTrait::Drop => { + WfWellKnownConstraints::drop_impl_constraint(&mut *solver, self.db, &impl_datum) + } + WellKnownTrait::CoerceUnsized => { + WfWellKnownConstraints::coerce_unsized_impl_constraint( + &mut *solver, + self.db, + &impl_datum, + ) + } + WellKnownTrait::DispatchFromDyn => { + WfWellKnownConstraints::dispatch_from_dyn_constraint( + &mut *solver, + self.db, + &impl_datum, + ) + } + WellKnownTrait::Clone | WellKnownTrait::Unpin => true, + // You can't add a manual implementation for the following traits: + WellKnownTrait::Fn + | WellKnownTrait::FnOnce + | WellKnownTrait::FnMut + | WellKnownTrait::Unsize + | WellKnownTrait::Sized + | WellKnownTrait::DiscriminantKind + | WellKnownTrait::Generator + | WellKnownTrait::Tuple => false, + }; + + if is_legal { + Ok(()) + } else { + Err(WfError::IllFormedTraitImpl(impl_datum.trait_id())) + } + } +} + +fn impl_header_wf_goal<I: Interner>( + db: &dyn RustIrDatabase<I>, + impl_id: ImplId<I>, +) -> Option<Goal<I>> { + let impl_datum = db.impl_datum(impl_id); + + if !impl_datum.is_positive() { + return None; + } + + let impl_fields = impl_datum + .binders + .map_ref(|v| (&v.trait_ref, &v.where_clauses)); + + let mut gb = GoalBuilder::new(db); + // forall<P0...Pn> {...} + let well_formed_goal = gb.forall(&impl_fields, (), |gb, _, (trait_ref, where_clauses), ()| { + let interner = gb.interner(); + + // if (WC && input types are well formed) { ... } + gb.implies( + impl_wf_environment(interner, where_clauses, trait_ref), + |gb| { + // We retrieve all the input types of the where clauses appearing on the trait impl, + // e.g. in: + // ``` + // impl<T, K> Foo for (T, K) where T: Iterator<Item = (HashSet<K>, Vec<Box<T>>)> { ... } + // ``` + // we would retrieve `HashSet<K>`, `Box<T>`, `Vec<Box<T>>`, `(HashSet<K>, Vec<Box<T>>)`. + // We will have to prove that these types are well-formed (e.g. an additional `K: Hash` + // bound would be needed here). + let types = InputTypeCollector::types_in(gb.interner(), &where_clauses); + + // Things to prove well-formed: input types of the where-clauses, projection types + // appearing in the header, associated type values, and of course the trait ref. + debug!(input_types=?types); + let goals = types + .into_iter() + .map(|ty| ty.well_formed().cast(interner)) + .chain(Some((*trait_ref).clone().well_formed().cast(interner))); + + gb.all::<_, Goal<I>>(goals) + }, + ) + }); + + Some(well_formed_goal) +} + +/// Creates the conditions that an impl (and its contents of an impl) +/// can assume to be true when proving that it is well-formed. +fn impl_wf_environment<'i, I: Interner>( + interner: I, + where_clauses: &'i [QuantifiedWhereClause<I>], + trait_ref: &'i TraitRef<I>, +) -> impl Iterator<Item = ProgramClause<I>> + 'i { + // if (WC) { ... } + let wc = where_clauses + .iter() + .cloned() + .map(move |qwc| qwc.into_from_env_goal(interner).cast(interner)); + + // We retrieve all the input types of the type on which we implement the trait: we will + // *assume* that these types are well-formed, e.g. we will be able to derive that + // `K: Hash` holds without writing any where clause. + // + // Example: + // ``` + // struct HashSet<K> where K: Hash { ... } + // + // impl<K> Foo for HashSet<K> { + // // Inside here, we can rely on the fact that `K: Hash` holds + // } + // ``` + let types = InputTypeCollector::types_in(interner, trait_ref); + + let types_wf = types + .into_iter() + .map(move |ty| ty.into_from_env_goal(interner).cast(interner)); + + wc.chain(types_wf) +} + +/// Associated type values are special because they can be parametric (independently of +/// the impl), so we issue a special goal which is quantified using the binders of the +/// associated type value, for example in: +/// +/// ```ignore +/// trait Foo { +/// type Item<'a>: Clone where Self: 'a +/// } +/// +/// impl<T> Foo for Box<T> { +/// type Item<'a> = Box<&'a T>; +/// } +/// ``` +/// +/// we would issue the following subgoal: `forall<'a> { WellFormed(Box<&'a T>) }`. +/// +/// Note that there is no binder for `T` in the above: the goal we +/// generate is expected to be exected in the context of the +/// larger WF goal for the impl, which already has such a +/// binder. So the entire goal for the impl might be: +/// +/// ```ignore +/// forall<T> { +/// WellFormed(Box<T>) /* this comes from the impl, not this routine */, +/// forall<'a> { WellFormed(Box<&'a T>) }, +/// } +/// ``` +fn compute_assoc_ty_goal<I: Interner>( + db: &dyn RustIrDatabase<I>, + assoc_ty_id: AssociatedTyValueId<I>, +) -> Option<Goal<I>> { + let mut gb = GoalBuilder::new(db); + let assoc_ty = &db.associated_ty_value(assoc_ty_id); + + // Create `forall<T, 'a> { .. }` + Some(gb.forall( + &assoc_ty.value.map_ref(|v| &v.ty), + assoc_ty_id, + |gb, assoc_ty_substitution, value_ty, assoc_ty_id| { + let interner = gb.interner(); + let db = gb.db(); + + // Hmm, because `Arc<AssociatedTyValue>` does not implement `TypeFoldable`, we can't pass this value through, + // just the id, so we have to fetch `assoc_ty` from the database again. + // Implementing `TypeFoldable` for `AssociatedTyValue` doesn't *quite* seem right though, as that + // would result in a deep clone, and the value is inert. We could do some more refatoring + // (move the `Arc` behind a newtype, for example) to fix this, but for now doesn't + // seem worth it. + let assoc_ty = &db.associated_ty_value(assoc_ty_id); + + let (impl_parameters, projection) = db + .impl_parameters_and_projection_from_associated_ty_value( + assoc_ty_substitution.as_slice(interner), + assoc_ty, + ); + + // If (/* impl WF environment */) { ... } + let impl_id = assoc_ty.impl_id; + let impl_datum = &db.impl_datum(impl_id); + let ImplDatumBound { + trait_ref: impl_trait_ref, + where_clauses: impl_where_clauses, + } = impl_datum + .binders + .clone() + .substitute(interner, impl_parameters); + let impl_wf_clauses = + impl_wf_environment(interner, &impl_where_clauses, &impl_trait_ref); + gb.implies(impl_wf_clauses, |gb| { + // Get the bounds and where clauses from the trait + // declaration, substituted appropriately. + // + // From our example: + // + // * bounds + // * original in trait, `Clone` + // * after substituting impl parameters, `Clone` + // * note that the self-type is not yet supplied for bounds, + // we will do that later + // * where clauses + // * original in trait, `Self: 'a` + // * after substituting impl parameters, `Box<!T>: '!a` + let assoc_ty_datum = db.associated_ty_data(projection.associated_ty_id); + let AssociatedTyDatumBound { + bounds: defn_bounds, + where_clauses: defn_where_clauses, + } = assoc_ty_datum + .binders + .clone() + .substitute(interner, &projection.substitution); + + // Create `if (/* where clauses on associated type value */) { .. }` + gb.implies( + defn_where_clauses + .iter() + .cloned() + .map(|qwc| qwc.into_from_env_goal(interner)), + |gb| { + let types = InputTypeCollector::types_in(gb.interner(), value_ty); + + // We require that `WellFormed(T)` for each type that appears in the value + let wf_goals = types + .into_iter() + .map(|ty| ty.well_formed()) + .casted(interner); + + // Check that the `value_ty` meets the bounds from the trait. + // Here we take the substituted bounds (`defn_bounds`) and we + // supply the self-type `value_ty` to yield the final result. + // + // In our example, the bound was `Clone`, so the combined + // result is `Box<!T>: Clone`. This is then converted to a + // well-formed goal like `WellFormed(Box<!T>: Clone)`. + let bound_goals = defn_bounds + .iter() + .cloned() + .flat_map(|qb| qb.into_where_clauses(interner, (*value_ty).clone())) + .map(|qwc| qwc.into_well_formed_goal(interner)) + .casted(interner); + + // Concatenate the WF goals of inner types + the requirements from trait + gb.all::<_, Goal<I>>(wf_goals.chain(bound_goals)) + }, + ) + }) + }, + )) +} + +/// Defines methods to compute well-formedness goals for well-known +/// traits (e.g. a goal for all fields of struct in a Copy impl to be Copy) +struct WfWellKnownConstraints; + +impl WfWellKnownConstraints { + /// Computes a goal to prove Sized constraints on a struct definition. + /// Struct is considered well-formed (in terms of Sized) when it either + /// has no fields or all of it's fields except the last are proven to be Sized. + pub fn struct_sized_constraint<I: Interner>( + db: &dyn RustIrDatabase<I>, + fields: &[Ty<I>], + size_all: bool, + ) -> Option<Goal<I>> { + let excluded = if size_all { 0 } else { 1 }; + + if fields.len() <= excluded { + return None; + } + + let interner = db.interner(); + + let sized_trait = db.well_known_trait_id(WellKnownTrait::Sized)?; + + Some(Goal::all( + interner, + fields[..fields.len() - excluded].iter().map(|ty| { + TraitRef { + trait_id: sized_trait, + substitution: Substitution::from1(interner, ty.clone()), + } + .cast(interner) + }), + )) + } + + /// Verify constraints on a Copy implementation. + /// Copy impl is considered well-formed for + /// a) certain builtin types (scalar values, shared ref, etc..) + /// b) adts which + /// 1) have all Copy fields + /// 2) don't have a Drop impl + fn copy_impl_constraint<I: Interner>( + solver: &mut dyn Solver<I>, + db: &dyn RustIrDatabase<I>, + impl_datum: &ImplDatum<I>, + ) -> bool { + let interner = db.interner(); + + let mut gb = GoalBuilder::new(db); + + let impl_fields = impl_datum + .binders + .map_ref(|v| (&v.trait_ref, &v.where_clauses)); + + // Implementations for scalars, pointer types and never type are provided by libcore. + // User implementations on types other than ADTs are forbidden. + match impl_datum + .binders + .skip_binders() + .trait_ref + .self_type_parameter(interner) + .kind(interner) + { + TyKind::Scalar(_) + | TyKind::Raw(_, _) + | TyKind::Ref(Mutability::Not, _, _) + | TyKind::Never => return true, + + TyKind::Adt(_, _) => (), + + _ => return false, + }; + + // Well fomedness goal for ADTs + let well_formed_goal = + gb.forall(&impl_fields, (), |gb, _, (trait_ref, where_clauses), ()| { + let interner = gb.interner(); + + let ty = trait_ref.self_type_parameter(interner); + + let (adt_id, substitution) = match ty.kind(interner) { + TyKind::Adt(adt_id, substitution) => (*adt_id, substitution), + + _ => unreachable!(), + }; + + // if (WC) { ... } + gb.implies( + impl_wf_environment(interner, where_clauses, trait_ref), + |gb| -> Goal<I> { + let db = gb.db(); + + // not { Implemented(ImplSelfTy: Drop) } + let neg_drop_goal = + db.well_known_trait_id(WellKnownTrait::Drop) + .map(|drop_trait_id| { + TraitRef { + trait_id: drop_trait_id, + substitution: Substitution::from1(interner, ty.clone()), + } + .cast::<Goal<I>>(interner) + .negate(interner) + }); + + let adt_datum = db.adt_datum(adt_id); + + let goals = adt_datum + .binders + .map_ref(|b| &b.variants) + .cloned() + .substitute(interner, substitution) + .into_iter() + .flat_map(|v| { + v.fields.into_iter().map(|f| { + // Implemented(FieldTy: Copy) + TraitRef { + trait_id: trait_ref.trait_id, + substitution: Substitution::from1(interner, f), + } + .cast(interner) + }) + }) + .chain(neg_drop_goal.into_iter()); + gb.all(goals) + }, + ) + }); + + solver.has_unique_solution(db, &well_formed_goal.into_closed_goal(interner)) + } + + /// Verifies constraints on a Drop implementation + /// Drop implementation is considered well-formed if: + /// a) it's implemented on an ADT + /// b) The generic parameters of the impl's type must all be parameters + /// of the Drop impl itself (i.e., no specialization like + /// `impl Drop for S<Foo> {...}` is allowed). + /// c) Any bounds on the genereic parameters of the impl must be + /// deductible from the bounds imposed by the struct definition + /// (i.e. the implementation must be exactly as generic as the ADT definition). + /// + /// ```rust,ignore + /// struct S<T1, T2> { } + /// struct Foo<T> { } + /// + /// impl<U1: Copy, U2: Sized> Drop for S<U2, Foo<U1>> { } + /// ``` + /// + /// generates the following: + /// goal derived from c): + /// + /// ```notrust + /// forall<U1, U2> { + /// Implemented(U1: Copy), Implemented(U2: Sized) :- FromEnv(S<U2, Foo<U1>>) + /// } + /// ``` + /// + /// goal derived from b): + /// ```notrust + /// forall <T1, T2> { + /// exists<U1, U2> { + /// S<T1, T2> = S<U2, Foo<U1>> + /// } + /// } + /// ``` + fn drop_impl_constraint<I: Interner>( + solver: &mut dyn Solver<I>, + db: &dyn RustIrDatabase<I>, + impl_datum: &ImplDatum<I>, + ) -> bool { + let interner = db.interner(); + + let adt_id = match impl_datum.self_type_adt_id(interner) { + Some(id) => id, + // Drop can only be implemented on a nominal type + None => return false, + }; + + let mut gb = GoalBuilder::new(db); + + let adt_datum = db.adt_datum(adt_id); + + let impl_fields = impl_datum + .binders + .map_ref(|v| (&v.trait_ref, &v.where_clauses)); + + // forall<ImplP1...ImplPn> { .. } + let implied_by_adt_def_goal = + gb.forall(&impl_fields, (), |gb, _, (trait_ref, where_clauses), ()| { + let interner = gb.interner(); + + // FromEnv(ImplSelfType) => ... + gb.implies( + iter::once( + FromEnv::Ty(trait_ref.self_type_parameter(interner)) + .cast::<DomainGoal<I>>(interner), + ), + |gb| { + // All(ImplWhereClauses) + gb.all( + where_clauses + .iter() + .map(|wc| wc.clone().into_well_formed_goal(interner)), + ) + }, + ) + }); + + let impl_self_ty = impl_datum + .binders + .map_ref(|b| b.trait_ref.self_type_parameter(interner)); + + // forall<StructP1..StructPN> {...} + let eq_goal = gb.forall( + &adt_datum.binders, + (adt_id, impl_self_ty), + |gb, substitution, _, (adt_id, impl_self_ty)| { + let interner = gb.interner(); + + let def_adt = TyKind::Adt(adt_id, substitution).intern(interner); + + // exists<ImplP1...ImplPn> { .. } + gb.exists(&impl_self_ty, def_adt, |gb, _, impl_adt, def_adt| { + let interner = gb.interner(); + + // StructName<StructP1..StructPn> = ImplSelfType + GoalData::EqGoal(EqGoal { + a: GenericArgData::Ty(def_adt).intern(interner), + b: GenericArgData::Ty(impl_adt.clone()).intern(interner), + }) + .intern(interner) + }) + }, + ); + + let well_formed_goal = gb.all([implied_by_adt_def_goal, eq_goal].iter()); + + solver.has_unique_solution(db, &well_formed_goal.into_closed_goal(interner)) + } + + /// Verify constraints a CoerceUnsized impl. + /// Rules for CoerceUnsized impl to be considered well-formed: + /// 1) pointer conversions: `&[mut] T -> &[mut] U`, `&[mut] T -> *[mut] U`, + /// `*[mut] T -> *[mut] U` are considered valid if + /// 1) `T: Unsize<U>` + /// 2) mutability is respected, i.e. immutable -> immutable, mutable -> immutable, + /// mutable -> mutable conversions are allowed, immutable -> mutable is not. + /// 2) struct conversions of structures with the same definition, `S<P0...Pn>` -> `S<Q0...Qn>`. + /// To check if this impl is legal, we would walk down the fields of `S` + /// and consider their types with both substitutes. We are looking to find + /// exactly one (non-phantom) field that has changed its type (from `T` to `U`), and + /// expect `T` to be unsizeable to `U`, i.e. `T: CoerceUnsized<U>`. + /// + /// As an example, consider a struct + /// ```rust + /// struct Foo<T, U> { + /// extra: T, + /// ptr: *mut U, + /// } + /// ``` + /// + /// We might have an impl that allows (e.g.) `Foo<T, [i32; 3]>` to be unsized + /// to `Foo<T, [i32]>`. That impl would look like: + /// ```rust,ignore + /// impl<T, U: Unsize<V>, V> CoerceUnsized<Foo<T, V>> for Foo<T, U> {} + /// ``` + /// In this case: + /// + /// - `extra` has type `T` before and type `T` after + /// - `ptr` has type `*mut U` before and type `*mut V` after + /// + /// Since just one field changed, we would then check that `*mut U: CoerceUnsized<*mut V>` + /// is implemented. This will work out because `U: Unsize<V>`, and we have a libcore rule + /// that `*mut U` can be coerced to `*mut V` if `U: Unsize<V>`. + fn coerce_unsized_impl_constraint<I: Interner>( + solver: &mut dyn Solver<I>, + db: &dyn RustIrDatabase<I>, + impl_datum: &ImplDatum<I>, + ) -> bool { + let interner = db.interner(); + let mut gb = GoalBuilder::new(db); + + let (binders, impl_datum) = impl_datum.binders.as_ref().into(); + + let trait_ref: &TraitRef<I> = &impl_datum.trait_ref; + + let source = trait_ref.self_type_parameter(interner); + let target = trait_ref + .substitution + .at(interner, 1) + .assert_ty_ref(interner) + .clone(); + + let mut place_in_environment = |goal| -> Goal<I> { + gb.forall( + &Binders::new( + binders.clone(), + (goal, trait_ref, &impl_datum.where_clauses), + ), + (), + |gb, _, (goal, trait_ref, where_clauses), ()| { + let interner = gb.interner(); + gb.implies( + impl_wf_environment(interner, where_clauses, trait_ref), + |_| goal, + ) + }, + ) + }; + + match (source.kind(interner), target.kind(interner)) { + (TyKind::Ref(s_m, _, source), TyKind::Ref(t_m, _, target)) + | (TyKind::Ref(s_m, _, source), TyKind::Raw(t_m, target)) + | (TyKind::Raw(s_m, source), TyKind::Raw(t_m, target)) => { + if (*s_m, *t_m) == (Mutability::Not, Mutability::Mut) { + return false; + } + + let unsize_trait_id = + if let Some(id) = db.well_known_trait_id(WellKnownTrait::Unsize) { + id + } else { + return false; + }; + + // Source: Unsize<Target> + let unsize_goal: Goal<I> = TraitRef { + trait_id: unsize_trait_id, + substitution: Substitution::from_iter( + interner, + [source.clone(), target.clone()].iter().cloned(), + ), + } + .cast(interner); + + // ImplEnv -> Source: Unsize<Target> + let unsize_goal = place_in_environment(unsize_goal); + + solver.has_unique_solution(db, &unsize_goal.into_closed_goal(interner)) + } + (TyKind::Adt(source_id, subst_a), TyKind::Adt(target_id, subst_b)) => { + let adt_datum = db.adt_datum(*source_id); + + if source_id != target_id || adt_datum.kind != AdtKind::Struct { + return false; + } + + let fields = adt_datum + .binders + .map_ref(|bound| &bound.variants.last().unwrap().fields) + .cloned(); + + let (source_fields, target_fields) = ( + fields.clone().substitute(interner, subst_a), + fields.substitute(interner, subst_b), + ); + + // collect fields with unequal ids + let uneq_field_ids: Vec<usize> = (0..source_fields.len()) + .filter(|&i| { + // ignore phantom data fields + if let Some(adt_id) = source_fields[i].adt_id(interner) { + if db.adt_datum(adt_id).flags.phantom_data { + return false; + } + } + + let eq_goal: Goal<I> = EqGoal { + a: source_fields[i].clone().cast(interner), + b: target_fields[i].clone().cast(interner), + } + .cast(interner); + + // ImplEnv -> Source.fields[i] = Target.fields[i] + let eq_goal = place_in_environment(eq_goal); + + // We are interested in !UNEQUAL! fields + !solver.has_unique_solution(db, &eq_goal.into_closed_goal(interner)) + }) + .collect(); + + if uneq_field_ids.len() != 1 { + return false; + } + + let field_id = uneq_field_ids[0]; + + // Source.fields[i]: CoerceUnsized<TargetFields[i]> + let coerce_unsized_goal: Goal<I> = TraitRef { + trait_id: trait_ref.trait_id, + substitution: Substitution::from_iter( + interner, + [ + source_fields[field_id].clone(), + target_fields[field_id].clone(), + ] + .iter() + .cloned(), + ), + } + .cast(interner); + + // ImplEnv -> Source.fields[i]: CoerceUnsized<TargetFields[i]> + let coerce_unsized_goal = place_in_environment(coerce_unsized_goal); + + solver.has_unique_solution(db, &coerce_unsized_goal.into_closed_goal(interner)) + } + _ => false, + } + } + + /// Verify constraints of a DispatchFromDyn impl. + /// + /// Rules for DispatchFromDyn impl to be considered well-formed: + /// + /// * Self and the type parameter must both be references or raw pointers with the same mutabilty + /// * OR all the following hold: + /// - Self and the type parameter must be structs + /// - Self and the type parameter must have the same definitions + /// - Self must not be `#[repr(packed)]` or `#[repr(C)]` + /// - Self must have exactly one field which is not a 1-ZST (there may be any number of 1-ZST + /// fields), and that field must have a different type in the type parameter (i.e., it is + /// the field being coerced) + /// - `DispatchFromDyn` is implemented for the type of the field being coerced. + fn dispatch_from_dyn_constraint<I: Interner>( + solver: &mut dyn Solver<I>, + db: &dyn RustIrDatabase<I>, + impl_datum: &ImplDatum<I>, + ) -> bool { + let interner = db.interner(); + let mut gb = GoalBuilder::new(db); + + let (binders, impl_datum) = impl_datum.binders.as_ref().into(); + + let trait_ref: &TraitRef<I> = &impl_datum.trait_ref; + + // DispatchFromDyn specifies that Self (source) can be coerced to T (target; its single type parameter). + let source = trait_ref.self_type_parameter(interner); + let target = trait_ref + .substitution + .at(interner, 1) + .assert_ty_ref(interner) + .clone(); + + let mut place_in_environment = |goal| -> Goal<I> { + gb.forall( + &Binders::new( + binders.clone(), + (goal, trait_ref, &impl_datum.where_clauses), + ), + (), + |gb, _, (goal, trait_ref, where_clauses), ()| { + let interner = gb.interner(); + gb.implies( + impl_wf_environment(interner, &where_clauses, &trait_ref), + |_| goal, + ) + }, + ) + }; + + match (source.kind(interner), target.kind(interner)) { + (TyKind::Ref(s_m, _, _), TyKind::Ref(t_m, _, _)) + | (TyKind::Raw(s_m, _), TyKind::Raw(t_m, _)) + if s_m == t_m => + { + true + } + (TyKind::Adt(source_id, subst_a), TyKind::Adt(target_id, subst_b)) => { + let adt_datum = db.adt_datum(*source_id); + + // Definitions are equal and are structs. + if source_id != target_id || adt_datum.kind != AdtKind::Struct { + return false; + } + + // Not repr(C) or repr(packed). + let repr = db.adt_repr(*source_id); + if repr.c || repr.packed { + return false; + } + + // Collect non 1-ZST fields; there must be exactly one. + let fields = adt_datum + .binders + .map_ref(|bound| &bound.variants.last().unwrap().fields) + .cloned(); + + let (source_fields, target_fields) = ( + fields.clone().substitute(interner, subst_a), + fields.substitute(interner, subst_b), + ); + + let mut non_zst_fields: Vec<_> = source_fields + .iter() + .zip(target_fields.iter()) + .filter(|(sf, _)| match sf.adt_id(interner) { + Some(adt) => !db.adt_size_align(adt).one_zst(), + None => true, + }) + .collect(); + + if non_zst_fields.len() != 1 { + return false; + } + + // The field being coerced (the interesting field). + let (field_src, field_tgt) = non_zst_fields.pop().unwrap(); + + // The interesting field is different in the source and target types. + let eq_goal: Goal<I> = EqGoal { + a: field_src.clone().cast(interner), + b: field_tgt.clone().cast(interner), + } + .cast(interner); + let eq_goal = place_in_environment(eq_goal); + if solver.has_unique_solution(db, &eq_goal.into_closed_goal(interner)) { + return false; + } + + // Type(field_src): DispatchFromDyn<Type(field_tgt)> + let field_dispatch_goal: Goal<I> = TraitRef { + trait_id: trait_ref.trait_id, + substitution: Substitution::from_iter( + interner, + [field_src.clone(), field_tgt.clone()].iter().cloned(), + ), + } + .cast(interner); + let field_dispatch_goal = place_in_environment(field_dispatch_goal); + if !solver.has_unique_solution(db, &field_dispatch_goal.into_closed_goal(interner)) + { + return false; + } + + true + } + _ => false, + } + } +} |