Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extend push_auto_trait_impl to built-in types #612

Merged
merged 14 commits into from
Sep 19, 2020
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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if it's worth passing the substitution to this function, even if unused? Theoretically, if we wanted to match rustc's behavior in the integration, we could.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the answer is no. Interesting thought, but let's not overcomplicate.

// - 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
122 changes: 84 additions & 38 deletions chalk-solve/src/clauses.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,45 @@ 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>> {
jackh726 marked this conversation as resolved.
Show resolved Hide resolved
let interner = db.interner();

match app_ty.name {
TypeName::Adt(adt_id) => {
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()
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PhantomData<T> should have T as a constituent type

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 +92,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 +105,45 @@ 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());

// auto traits are not implemented for foreign types
if let TypeName::Foreign(_) = app_ty.name {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I feel like it would be simpler to make constituent_types return an Option and then have it return None for this.

return;
}

// 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(),
}
.intern(interner);
let mk_ref = |ty: Ty<I>| TraitRef {
trait_id: auto_trait_id,
substitution: Substitution::from1(interner, ty.cast(interner)),
};

// trait_ref = `MyStruct<...>: MyAutoTrait`
let auto_trait_ref = TraitRef {
trait_id: auto_trait_id,
substitution: Substitution::from1(interner, self_ty),
};
let consequence = mk_ref(app_ty.clone().intern(interner));

// 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()),
})
}),
);
});
// closures require binders, while the other types do not
if let TypeName::Closure(closure_id) = app_ty.name {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alternatively, just move the Foreign check here.

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);
});
} else {
let conditions = constituent_types(builder.db, app_ty)
.into_iter()
.map(mk_ref);

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 +292,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
10 changes: 5 additions & 5 deletions chalk-solve/src/logging_db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,10 +166,10 @@ 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)
// TODO record app_ty
self.ws.db().impl_provided_for(auto_trait_id, app_ty)
}

fn well_known_trait_id(
Expand Down Expand Up @@ -379,8 +379,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