-
Notifications
You must be signed in to change notification settings - Fork 181
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
Changes from 13 commits
2a96aa3
e87aa6d
6bd2da5
85f93c9
ef0b55b
4aa6430
ee8fa77
40d4edc
0604b12
fdfce1d
c613abd
d8fe6bc
17eb26e
c35689c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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>> { | ||
jackh726 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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. | ||
|
@@ -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. | ||
|
@@ -67,44 +109,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 { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm, I feel like it would be simpler to make |
||
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 { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Alternatively, just move the |
||
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. | ||
|
@@ -253,13 +296,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); | ||
} | ||
|
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.