Skip to content

Commit

Permalink
Auto merge of #612 - memoryleak47:fix-auto-traits, r=jackh726
Browse files Browse the repository at this point in the history
Extend push_auto_trait_impl to built-in types

cc #604.
  • Loading branch information
bors committed Sep 19, 2020
2 parents 3ea88b7 + c35689c commit 9a3d72d
Show file tree
Hide file tree
Showing 11 changed files with 313 additions and 78 deletions.
24 changes: 12 additions & 12 deletions book/src/clauses/well_known_traits.md
Original file line number Diff line number Diff line change
Expand Up @@ -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❌ ||||||||||
| ----------- | | | | | | | | | |
Expand Down
14 changes: 9 additions & 5 deletions chalk-integration/src/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -131,10 +131,14 @@ impl RustIrDatabase<ChalkIr> for ChalkDatabase {
.local_impls_to_coherence_check(trait_id)
}

fn impl_provided_for(&self, auto_trait_id: TraitId<ChalkIr>, adt_id: AdtId<ChalkIr>) -> bool {
fn impl_provided_for(
&self,
auto_trait_id: TraitId<ChalkIr>,
app_ty: &ApplicationTy<ChalkIr>,
) -> 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<TraitId<ChalkIr>> {
Expand Down
38 changes: 30 additions & 8 deletions chalk-integration/src/program.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -422,14 +422,36 @@ impl RustIrDatabase<ChalkIr> for Program {
.collect()
}

fn impl_provided_for(&self, auto_trait_id: TraitId<ChalkIr>, adt_id: AdtId<ChalkIr>) -> bool {
fn impl_provided_for(
&self,
auto_trait_id: TraitId<ChalkIr>,
app_ty: &ApplicationTy<ChalkIr>,
) -> 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<T>`; an implementation for `S<A>` should suppress an auto impl for `S<B>`, 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<TraitId<ChalkIr>> {
Expand Down
8 changes: 6 additions & 2 deletions chalk-integration/src/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -225,7 +225,11 @@ fn environment(db: &dyn LoweringDatabase) -> Result<Arc<ProgramEnvironment>, 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);
}
}

Expand Down
127 changes: 90 additions & 37 deletions chalk-solve/src/clauses.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,49 @@ mod env_elaborator;
mod generalize;
pub mod program_clauses;

// yields the types "contained" in `app_ty`
fn constituent_types<I: Interner>(
db: &dyn RustIrDatabase<I>,
app_ty: &ApplicationTy<I>,
) -> Vec<Ty<I>> {
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<T>`, 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.
Expand Down Expand Up @@ -53,9 +96,8 @@ pub mod program_clauses;
pub fn push_auto_trait_impls<I: Interner>(
builder: &mut ClauseBuilder<'_, I>,
auto_trait_id: TraitId<I>,
adt_id: AdtId<I>,
app_ty: &ApplicationTy<I>,
) {
let adt_datum = &builder.db.adt_datum(adt_id);
let interner = builder.interner();

// Must be an auto trait.
Expand All @@ -67,44 +109,48 @@ pub fn push_auto_trait_impls<I: Interner>(
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<I>| 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<P0..Pn> { // 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.
Expand Down Expand Up @@ -253,13 +299,20 @@ fn program_clauses_that_could_match<I: Interner>(
// 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);
}
Expand Down
2 changes: 1 addition & 1 deletion chalk-solve/src/display/stub.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ impl<I: Interner, DB: RustIrDatabase<I>> RustIrDatabase<I> for StubWrapper<'_, D
fn impl_provided_for(
&self,
_auto_trait_id: chalk_ir::TraitId<I>,
_adt_id: chalk_ir::AdtId<I>,
_app_ty: &chalk_ir::ApplicationTy<I>,
) -> bool {
// We panic here because the returned ids may not be collected,
// resulting in unresolvable names.
Expand Down
9 changes: 4 additions & 5 deletions chalk-solve/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,12 +101,11 @@ pub trait RustIrDatabase<I: Interner>: Debug {
fn local_impls_to_coherence_check(&self, trait_id: TraitId<I>) -> Vec<ImplId<I>>;

/// 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<I>, adt_id: AdtId<I>) -> 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<I>, app_ty: &ApplicationTy<I>) -> bool;

/// Returns id of a trait lang item, if found
fn well_known_trait_id(&self, well_known_trait: WellKnownTrait) -> Option<TraitId<I>>;
Expand Down
12 changes: 7 additions & 5 deletions chalk-solve/src/logging_db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,10 +166,12 @@ where
self.ws.db().local_impls_to_coherence_check(trait_id)
}

fn impl_provided_for(&self, auto_trait_id: TraitId<I>, adt_id: AdtId<I>) -> bool {
fn impl_provided_for(&self, auto_trait_id: TraitId<I>, app_ty: &ApplicationTy<I>) -> 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(
Expand Down Expand Up @@ -379,8 +381,8 @@ where
self.db.local_impls_to_coherence_check(trait_id)
}

fn impl_provided_for(&self, auto_trait_id: TraitId<I>, adt_id: AdtId<I>) -> bool {
self.db.impl_provided_for(auto_trait_id, adt_id)
fn impl_provided_for(&self, auto_trait_id: TraitId<I>, app_ty: &ApplicationTy<I>) -> bool {
self.db.impl_provided_for(auto_trait_id, app_ty)
}

fn well_known_trait_id(
Expand Down
4 changes: 2 additions & 2 deletions tests/display/unique_names.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,9 +122,9 @@ where
fn impl_provided_for(
&self,
auto_trait_id: chalk_ir::TraitId<I>,
adt_id: chalk_ir::AdtId<I>,
app_ty: &chalk_ir::ApplicationTy<I>,
) -> 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,
Expand Down
2 changes: 1 addition & 1 deletion tests/integration/panic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ impl RustIrDatabase<ChalkIr> for MockDatabase {
fn impl_provided_for(
&self,
auto_trait_id: TraitId<ChalkIr>,
struct_id: AdtId<ChalkIr>,
app_ty: &ApplicationTy<ChalkIr>,
) -> bool {
unimplemented!()
}
Expand Down
Loading

0 comments on commit 9a3d72d

Please sign in to comment.