diff --git a/book/src/clauses/well_known_traits.md b/book/src/clauses/well_known_traits.md index d32b2d2434d..b5cdb76d154 100644 --- a/book/src/clauses/well_known_traits.md +++ b/book/src/clauses/well_known_traits.md @@ -30,20 +30,20 @@ Some common examples of auto traits are `Send` and `Sync`. # Current state | Type | Copy | Clone | Sized | Unsize | Drop | FnOnce/FnMut/Fn | Unpin | Generator | auto traits | | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | -| tuple types | ✅ | ✅ | ✅ | ✅ | ⚬ | ⚬ | ⚬ | ⚬ | ❌ | +| tuple types | ✅ | ✅ | ✅ | ✅ | ⚬ | ⚬ | ⚬ | ⚬ | ✅ | | structs | ⚬ | ⚬ | ✅ | ✅ | ⚬ | ⚬ | ⚬ | ⚬ | ✅ | -| scalar types | 📚 | 📚 | ✅ | ⚬ | ⚬ | ⚬ | ⚬ | ⚬ | ❌ | -| str | 📚 | 📚 | ⚬ | ⚬ | ⚬ | ⚬ | ⚬ | ⚬ | ❌ | -| never type | 📚 | 📚 | ✅ | ⚬ | ⚬ | ⚬ | ⚬ | ⚬ | ❌ | +| scalar types | 📚 | 📚 | ✅ | ⚬ | ⚬ | ⚬ | ⚬ | ⚬ | ✅ | +| str | 📚 | 📚 | ⚬ | ⚬ | ⚬ | ⚬ | ⚬ | ⚬ | ✅ | +| never type | 📚 | 📚 | ✅ | ⚬ | ⚬ | ⚬ | ⚬ | ⚬ | ✅ | | trait objects | ⚬ | ⚬ | ⚬ | ✅ | ⚬ | ⚬ | ⚬ | ⚬ | ⚬ | -| functions defs | ✅ | ✅ | ✅ | ⚬ | ⚬ | ❌ | ⚬ | ⚬ | ❌ | -| functions ptrs | ✅ | ✅ | ✅ | ⚬ | ⚬ | ✅ | ⚬ | ⚬ | ❌ | -| raw ptrs | 📚 | 📚 | ✅ | ⚬ | ⚬ | ⚬ | ⚬ | ⚬ | ❌ | -| immutable refs | 📚 | 📚 | ✅ | ⚬ | ⚬ | ⚬ | ⚬ | ⚬ | ❌ | -| mutable refs | ⚬ | ⚬ | ✅ | ⚬ | ⚬ | ⚬ | ⚬ | ⚬ | ❌ | -| slices | ⚬ | ⚬ | ⚬ | ✅ | ⚬ | ⚬ | ⚬ | ⚬ | ❌ | -| arrays | ✅ | ✅ | ✅ | ❌ | ⚬ | ⚬ | ⚬ | ⚬ | ❌ | -| closures❌ | ❌ | ❌ | ❌ | ⚬ | ⚬ | ❌ | ⚬ | ⚬ | ❌ | +| functions defs | ✅ | ✅ | ✅ | ⚬ | ⚬ | ❌ | ⚬ | ⚬ | ✅ | +| functions ptrs | ✅ | ✅ | ✅ | ⚬ | ⚬ | ✅ | ⚬ | ⚬ | ✅ | +| raw ptrs | 📚 | 📚 | ✅ | ⚬ | ⚬ | ⚬ | ⚬ | ⚬ | ✅ | +| immutable refs | 📚 | 📚 | ✅ | ⚬ | ⚬ | ⚬ | ⚬ | ⚬ | ✅ | +| mutable refs | ⚬ | ⚬ | ✅ | ⚬ | ⚬ | ⚬ | ⚬ | ⚬ | ✅ | +| slices | ⚬ | ⚬ | ⚬ | ✅ | ⚬ | ⚬ | ⚬ | ⚬ | ✅ | +| arrays | ✅ | ✅ | ✅ | ❌ | ⚬ | ⚬ | ⚬ | ⚬ | ✅ | +| closures❌ | ❌ | ❌ | ❌ | ⚬ | ⚬ | ❌ | ⚬ | ⚬ | ✅ | | generators❌ | ⚬ | ⚬ | ❌ | ⚬ | ⚬ | ⚬ | ❌ | ❌ | ❌ | | gen. witness❌ | ⚬ | ⚬ | ⚬ | ⚬ | ⚬ | ⚬ | ⚬ | ⚬ | ❌ | | ----------- | | | | | | | | | | diff --git a/chalk-integration/src/db.rs b/chalk-integration/src/db.rs index 3d1f05a6026..89175b389fe 100644 --- a/chalk-integration/src/db.rs +++ b/chalk-integration/src/db.rs @@ -7,9 +7,9 @@ use crate::{ tls, SolverChoice, }; use chalk_ir::{ - AdtId, AssocTypeId, Binders, Canonical, CanonicalVarKinds, ClosureId, ConstrainedSubst, - Environment, FnDefId, GenericArg, Goal, ImplId, InEnvironment, OpaqueTyId, ProgramClause, - ProgramClauses, Substitution, TraitId, Ty, UCanonical, + AdtId, ApplicationTy, AssocTypeId, Binders, Canonical, CanonicalVarKinds, ClosureId, + ConstrainedSubst, Environment, FnDefId, GenericArg, Goal, ImplId, InEnvironment, OpaqueTyId, + ProgramClause, ProgramClauses, Substitution, TraitId, Ty, UCanonical, }; use chalk_solve::rust_ir::{ AdtDatum, AdtRepr, AssociatedTyDatum, AssociatedTyValue, AssociatedTyValueId, ClosureKind, @@ -131,10 +131,14 @@ impl RustIrDatabase for ChalkDatabase { .local_impls_to_coherence_check(trait_id) } - fn impl_provided_for(&self, auto_trait_id: TraitId, adt_id: AdtId) -> bool { + fn impl_provided_for( + &self, + auto_trait_id: TraitId, + app_ty: &ApplicationTy, + ) -> bool { self.program_ir() .unwrap() - .impl_provided_for(auto_trait_id, adt_id) + .impl_provided_for(auto_trait_id, app_ty) } fn well_known_trait_id(&self, well_known_trait: WellKnownTrait) -> Option> { diff --git a/chalk-integration/src/program.rs b/chalk-integration/src/program.rs index de12b0875bc..76a3017754e 100644 --- a/chalk-integration/src/program.rs +++ b/chalk-integration/src/program.rs @@ -6,7 +6,7 @@ use chalk_ir::{ debug::SeparatorTraitRef, AdtId, AliasTy, ApplicationTy, AssocTypeId, Binders, CanonicalVarKinds, ClosureId, FnDefId, ForeignDefId, GenericArg, Goal, Goals, ImplId, Lifetime, OpaqueTy, OpaqueTyId, ProgramClause, ProgramClauseImplication, ProgramClauses, ProjectionTy, - Substitution, TraitId, Ty, + Substitution, TraitId, Ty, TyData, }; use chalk_solve::rust_ir::{ AdtDatum, AdtRepr, AssociatedTyDatum, AssociatedTyValue, AssociatedTyValueId, ClosureKind, @@ -422,14 +422,36 @@ impl RustIrDatabase for Program { .collect() } - fn impl_provided_for(&self, auto_trait_id: TraitId, adt_id: AdtId) -> bool { + fn impl_provided_for( + &self, + auto_trait_id: TraitId, + app_ty: &ApplicationTy, + ) -> bool { let interner = self.interner(); - // Look for an impl like `impl Send for Foo` where `Foo` is - // the ADT. See `push_auto_trait_impls` for more. - self.impl_data.values().any(|impl_datum| { - impl_datum.trait_id() == auto_trait_id - && impl_datum.self_type_adt_id(interner) == Some(adt_id) - }) + + // an iterator containing the `ApplicationTy`s which have an impl for the trait `auto_trait_id`. + let mut impl_app_tys = self.impl_data.values().filter_map(|impl_datum| { + if impl_datum.trait_id() != auto_trait_id { + return None; + } + + let ty = impl_datum + .binders + .skip_binders() + .trait_ref + .self_type_parameter(interner); + match ty.data(interner) { + TyData::Apply(app) => Some(app.clone()), + _ => None, + } + }); + + // we only compare the `TypeName`s as + // - given a `struct S`; an implementation for `S` should suppress an auto impl for `S`, and + // - an implementation for `[A]` should suppress an auto impl for `[B]`, and + // - an implementation for `(A, B, C)` should suppress an auto impl for `(D, E, F)` + // this may change later + impl_app_tys.any(|x| x.name == app_ty.name) } fn well_known_trait_id(&self, well_known_trait: WellKnownTrait) -> Option> { diff --git a/chalk-integration/src/query.rs b/chalk-integration/src/query.rs index d35120da445..704dea5ead5 100644 --- a/chalk-integration/src/query.rs +++ b/chalk-integration/src/query.rs @@ -8,7 +8,7 @@ use crate::program::Program; use crate::program_environment::ProgramEnvironment; use crate::tls; use crate::SolverChoice; -use chalk_ir::TraitId; +use chalk_ir::{ApplicationTy, Substitution, TraitId, TypeName}; use chalk_solve::clauses::builder::ClauseBuilder; use chalk_solve::clauses::program_clauses::ToProgramClauses; use chalk_solve::coherence::orphan; @@ -225,7 +225,11 @@ fn environment(db: &dyn LoweringDatabase) -> Result, Cha .filter(|(_, auto_trait)| auto_trait.is_auto_trait()) { for &adt_id in program.adt_data.keys() { - chalk_solve::clauses::push_auto_trait_impls(builder, auto_trait_id, adt_id); + let app_ty = ApplicationTy { + name: TypeName::Adt(adt_id), + substitution: Substitution::empty(builder.interner()), + }; + chalk_solve::clauses::push_auto_trait_impls(builder, auto_trait_id, &app_ty); } } diff --git a/chalk-solve/src/clauses.rs b/chalk-solve/src/clauses.rs index afb6a256bef..ebea5efaf1f 100644 --- a/chalk-solve/src/clauses.rs +++ b/chalk-solve/src/clauses.rs @@ -18,6 +18,49 @@ mod env_elaborator; mod generalize; pub mod program_clauses; +// yields the types "contained" in `app_ty` +fn constituent_types( + db: &dyn RustIrDatabase, + app_ty: &ApplicationTy, +) -> Vec> { + let interner = db.interner(); + + match app_ty.name { + // For non-phantom_data adts we collect its variants/fields + TypeName::Adt(adt_id) if !db.adt_datum(adt_id).flags.phantom_data => { + let adt_datum = &db.adt_datum(adt_id); + let adt_datum_bound = adt_datum.binders.substitute(interner, &app_ty.substitution); + adt_datum_bound + .variants + .into_iter() + .flat_map(|variant| variant.fields.into_iter()) + .collect() + } + // And for `PhantomData`, we pass `T`. + TypeName::Adt(_) + | TypeName::Array + | TypeName::Tuple(_) + | TypeName::Slice + | TypeName::Raw(_) + | TypeName::Ref(_) + | TypeName::Scalar(_) + | TypeName::Str + | TypeName::Never + | TypeName::FnDef(_) => app_ty + .substitution + .iter(interner) + .filter_map(|x| x.ty(interner)) + .cloned() + .collect(), + + TypeName::Closure(_) => panic!("this function should not be called for closures"), + TypeName::Foreign(_) => panic!("constituent_types of foreign types are unknown!"), + TypeName::Error => Vec::new(), + TypeName::OpaqueType(_) => unimplemented!(), + TypeName::AssociatedType(_) => unimplemented!(), + } +} + /// FIXME(#505) update comments for ADTs /// For auto-traits, we generate a default rule for every struct, /// unless there is a manual impl for that struct given explicitly. @@ -53,9 +96,8 @@ pub mod program_clauses; pub fn push_auto_trait_impls( builder: &mut ClauseBuilder<'_, I>, auto_trait_id: TraitId, - adt_id: AdtId, + app_ty: &ApplicationTy, ) { - let adt_datum = &builder.db.adt_datum(adt_id); let interner = builder.interner(); // Must be an auto trait. @@ -67,44 +109,48 @@ pub fn push_auto_trait_impls( 1 ); + // we assume that the builder has no binders so far. + assert!(builder.placeholders_in_scope().is_empty()); + // If there is a `impl AutoTrait for Foo<..>` or `impl !AutoTrait // for Foo<..>`, where `Foo` is the adt we're looking at, then // we don't generate our own rules. - if builder.db.impl_provided_for(auto_trait_id, adt_id) { + if builder.db.impl_provided_for(auto_trait_id, app_ty) { debug!("impl provided"); return; } - let binders = adt_datum.binders.map_ref(|b| &b.variants); - builder.push_binders(&binders, |builder, variants| { - let self_ty: Ty<_> = ApplicationTy { - name: adt_id.cast(interner), - substitution: builder.substitution_in_scope(), + let mk_ref = |ty: Ty| TraitRef { + trait_id: auto_trait_id, + substitution: Substitution::from1(interner, ty.cast(interner)), + }; + + let consequence = mk_ref(app_ty.clone().intern(interner)); + + match app_ty.name { + // auto traits are not implemented for foreign types + TypeName::Foreign(_) => return, + + // closures require binders, while the other types do not + TypeName::Closure(closure_id) => { + let binders = builder + .db + .closure_upvars(closure_id, &Substitution::empty(interner)); + builder.push_binders(&binders, |builder, upvar_ty| { + let conditions = iter::once(mk_ref(upvar_ty)); + builder.push_clause(consequence, conditions); + }); } - .intern(interner); - // trait_ref = `MyStruct<...>: MyAutoTrait` - let auto_trait_ref = TraitRef { - trait_id: auto_trait_id, - substitution: Substitution::from1(interner, self_ty), - }; + // app_ty implements AutoTrait if all constituents of app_ty implement AutoTrait + _ => { + let conditions = constituent_types(builder.db, app_ty) + .into_iter() + .map(mk_ref); - // forall { // generic parameters from struct - // MyStruct<...>: MyAutoTrait :- - // Field0: MyAutoTrait, - // ... - // FieldN: MyAutoTrait - // } - builder.push_clause( - auto_trait_ref, - variants.iter().flat_map(|variant| { - variant.fields.iter().map(|field_ty| TraitRef { - trait_id: auto_trait_id, - substitution: Substitution::from1(interner, field_ty.clone()), - }) - }), - ); - }); + builder.push_clause(consequence, conditions); + } + } } /// Leak auto traits for opaque types, just like `push_auto_trait_impls` does for structs. @@ -253,13 +299,20 @@ fn program_clauses_that_could_match( // the automatic impls for `Foo`. let trait_datum = db.trait_datum(trait_id); if trait_datum.is_auto_trait() { - match trait_ref.self_type_parameter(interner).data(interner) { - TyData::Apply(apply) => match &apply.name { - TypeName::Adt(adt_id) => { - push_auto_trait_impls(builder, trait_id, *adt_id); - } - _ => {} - }, + let ty = trait_ref.self_type_parameter(interner); + match ty.data(interner) { + TyData::Apply(apply) => { + push_auto_trait_impls(builder, trait_id, apply); + } + // function-types implement auto traits unconditionally + TyData::Function(_) => { + let auto_trait_ref = TraitRef { + trait_id, + substitution: Substitution::from1(interner, ty.cast(interner)), + }; + + builder.push_fact(auto_trait_ref); + } TyData::InferenceVar(_, _) | TyData::BoundVar(_) => { return Err(Floundered); } diff --git a/chalk-solve/src/display/stub.rs b/chalk-solve/src/display/stub.rs index 2c433578c22..2ad5791d499 100644 --- a/chalk-solve/src/display/stub.rs +++ b/chalk-solve/src/display/stub.rs @@ -156,7 +156,7 @@ impl> RustIrDatabase for StubWrapper<'_, D fn impl_provided_for( &self, _auto_trait_id: chalk_ir::TraitId, - _adt_id: chalk_ir::AdtId, + _app_ty: &chalk_ir::ApplicationTy, ) -> bool { // We panic here because the returned ids may not be collected, // resulting in unresolvable names. diff --git a/chalk-solve/src/lib.rs b/chalk-solve/src/lib.rs index 0c6d300959e..583595e11c7 100644 --- a/chalk-solve/src/lib.rs +++ b/chalk-solve/src/lib.rs @@ -101,12 +101,11 @@ pub trait RustIrDatabase: Debug { fn local_impls_to_coherence_check(&self, trait_id: TraitId) -> Vec>; /// Returns true if there is an explicit impl of the auto trait - /// `auto_trait_id` for the ADT `adt_id`. This is part of + /// `auto_trait_id` for the type `app_ty`. This is part of /// the auto trait handling -- if there is no explicit impl given - /// by the user for the struct, then we provide default impls - /// based on the field types (otherwise, we rely on the impls the - /// user gave). - fn impl_provided_for(&self, auto_trait_id: TraitId, adt_id: AdtId) -> bool; + /// by the user for `app_ty`, then we provide default impls + /// (otherwise, we rely on the impls the user gave). + fn impl_provided_for(&self, auto_trait_id: TraitId, app_ty: &ApplicationTy) -> bool; /// Returns id of a trait lang item, if found fn well_known_trait_id(&self, well_known_trait: WellKnownTrait) -> Option>; diff --git a/chalk-solve/src/logging_db.rs b/chalk-solve/src/logging_db.rs index 52279110dbe..3681e8c2807 100644 --- a/chalk-solve/src/logging_db.rs +++ b/chalk-solve/src/logging_db.rs @@ -166,10 +166,12 @@ where self.ws.db().local_impls_to_coherence_check(trait_id) } - fn impl_provided_for(&self, auto_trait_id: TraitId, adt_id: AdtId) -> bool { + fn impl_provided_for(&self, auto_trait_id: TraitId, app_ty: &ApplicationTy) -> bool { self.record(auto_trait_id); - self.record(adt_id); - self.ws.db().impl_provided_for(auto_trait_id, adt_id) + if let TypeName::Adt(adt_id) = app_ty.name { + self.record(adt_id); + } + self.ws.db().impl_provided_for(auto_trait_id, app_ty) } fn well_known_trait_id( @@ -379,8 +381,8 @@ where self.db.local_impls_to_coherence_check(trait_id) } - fn impl_provided_for(&self, auto_trait_id: TraitId, adt_id: AdtId) -> bool { - self.db.impl_provided_for(auto_trait_id, adt_id) + fn impl_provided_for(&self, auto_trait_id: TraitId, app_ty: &ApplicationTy) -> bool { + self.db.impl_provided_for(auto_trait_id, app_ty) } fn well_known_trait_id( diff --git a/tests/display/unique_names.rs b/tests/display/unique_names.rs index 60a8ccdb6df..a645ffd18a0 100644 --- a/tests/display/unique_names.rs +++ b/tests/display/unique_names.rs @@ -122,9 +122,9 @@ where fn impl_provided_for( &self, auto_trait_id: chalk_ir::TraitId, - adt_id: chalk_ir::AdtId, + app_ty: &chalk_ir::ApplicationTy, ) -> bool { - self.db.impl_provided_for(auto_trait_id, adt_id) + self.db.impl_provided_for(auto_trait_id, app_ty) } fn well_known_trait_id( &self, diff --git a/tests/integration/panic.rs b/tests/integration/panic.rs index c02dcf881ef..70bb366f56f 100644 --- a/tests/integration/panic.rs +++ b/tests/integration/panic.rs @@ -176,7 +176,7 @@ impl RustIrDatabase for MockDatabase { fn impl_provided_for( &self, auto_trait_id: TraitId, - struct_id: AdtId, + app_ty: &ApplicationTy, ) -> bool { unimplemented!() } diff --git a/tests/test/auto_traits.rs b/tests/test/auto_traits.rs index 37721676b74..d2901360d77 100644 --- a/tests/test/auto_traits.rs +++ b/tests/test/auto_traits.rs @@ -217,3 +217,154 @@ fn enum_auto_trait() { } } } + +#[test] +fn builtin_auto_trait() { + test! { + program { + #[auto] trait AutoTrait {} + struct Struct {} + enum Enum { Var1, Var2 } + fn func(); + + struct Marker {} + impl !AutoTrait for Marker {} + + closure good_closure(self, arg: Marker) -> Marker { i32 } + closure bad_closure(self, arg: i32) -> i32 { Marker } + + extern type Ext; + enum ExtEnum { GoodVariant, BadVariant(Ext) } + } + + // The following types only contain AutoTrait-types, and thus implement AutoTrait themselves. + goal { (i32, f32): AutoTrait } + yields { "Unique; substitution [], lifetime constraints []" } + + goal { [(); 1]: AutoTrait } + yields { "Unique; substitution [], lifetime constraints []" } + + goal { [()]: AutoTrait } + yields { "Unique; substitution [], lifetime constraints []" } + + goal { u32: AutoTrait } + yields { "Unique; substitution [], lifetime constraints []" } + + goal { *const (): AutoTrait } + yields { "Unique; substitution [], lifetime constraints []" } + + goal { *mut (): AutoTrait } + yields { "Unique; substitution [], lifetime constraints []" } + + goal { forall<'a> { &'a (): AutoTrait } } + yields { "Unique; substitution [], lifetime constraints []" } + + goal { forall<'a> { &'a mut (): AutoTrait } } + yields { "Unique; substitution [], lifetime constraints []" } + + goal { str: AutoTrait } + yields { "Unique; substitution [], lifetime constraints []" } + + goal { !: AutoTrait } + yields { "Unique; substitution [], lifetime constraints []" } + + goal { Enum: AutoTrait } + yields { "Unique; substitution [], lifetime constraints []" } + + goal { func: AutoTrait } + yields { "Unique; substitution [], lifetime constraints []" } + + goal { good_closure: AutoTrait } + yields { "Unique; substitution [], lifetime constraints []" } + + goal { fn(Marker) -> Marker: AutoTrait } + yields { "Unique; substitution [], lifetime constraints []" } + + + // foreign types do not implement AutoTraits automatically + goal { Ext: AutoTrait } + yields { "No possible solution" } + + // The following types do contain non-AutoTrait types, and thus do not implement AutoTrait. + goal { bad_closure: AutoTrait } + yields { "No possible solution" } + + goal { ExtEnum: AutoTrait } + yields { "No possible solution" } + + goal { (Struct, Marker): AutoTrait } + yields { "No possible solution" } + } +} + +#[test] +fn adt_auto_trait() { + test! { + program { + #[auto] trait AutoTrait {} + struct Yes {} + struct No {} + impl !AutoTrait for No {} + + struct WrapperNo { t: T } + struct WrapperYes { t: T } + + struct X {} + impl !AutoTrait for WrapperNo {} + } + + goal { + Yes: AutoTrait + } + yields { + "Unique; substitution [], lifetime constraints []" + } + + goal { + No: AutoTrait + } + yields { + "No possible solution" + } + + goal { + X: AutoTrait + } + yields { + "Unique; substitution [], lifetime constraints []" + } + + goal { + WrapperNo: AutoTrait + } + yields { + "No possible solution" + } + + goal { + WrapperYes: AutoTrait + } + yields { + "No possible solution" + } + } +} + +#[test] +fn phantom_auto_trait() { + test! { + program { + #[auto] trait AutoTrait {} + #[phantom_data] struct PhantomData {} + struct Bad {} + impl !AutoTrait for Bad {} + } + + goal { + PhantomData: AutoTrait + } + yields { + "No possible solution" + } + } +}