Skip to content

Commit 7eb0f08

Browse files
authored
Merge pull request #54 from scalexm/cologic
Auto traits
2 parents bb32a14 + 72e6553 commit 7eb0f08

File tree

13 files changed

+640
-51
lines changed

13 files changed

+640
-51
lines changed

chalk-parse/src/ast.rs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ pub struct TraitDefn {
3535
pub parameter_kinds: Vec<ParameterKind>,
3636
pub where_clauses: Vec<WhereClause>,
3737
pub assoc_ty_defns: Vec<AssocTyDefn>,
38+
pub auto: bool,
3839
}
3940

4041
pub struct AssocTyDefn {
@@ -93,7 +94,7 @@ impl Kinded for Parameter {
9394

9495
pub struct Impl {
9596
pub parameter_kinds: Vec<ParameterKind>,
96-
pub trait_ref: TraitRef,
97+
pub trait_ref: PolarizedTraitRef,
9798
pub where_clauses: Vec<WhereClause>,
9899
pub assoc_ty_values: Vec<AssocTyValue>,
99100
}
@@ -139,6 +140,21 @@ pub struct TraitRef {
139140
pub args: Vec<Parameter>,
140141
}
141142

143+
pub enum PolarizedTraitRef {
144+
Positive(TraitRef),
145+
Negative(TraitRef),
146+
}
147+
148+
impl PolarizedTraitRef {
149+
pub fn from_bool(polarity: bool, trait_ref: TraitRef) -> PolarizedTraitRef {
150+
if polarity {
151+
PolarizedTraitRef::Positive(trait_ref)
152+
} else {
153+
PolarizedTraitRef::Negative(trait_ref)
154+
}
155+
}
156+
}
157+
142158
#[derive(Copy, Clone, Debug)]
143159
pub struct Identifier {
144160
pub str: InternedString,

chalk-parse/src/parser.lalrpop

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,17 @@ StructDefn: StructDefn = {
4747
}
4848
};
4949

50+
AutoKeyword: () = "#" "[" "auto" "]";
51+
5052
TraitDefn: TraitDefn = {
51-
"trait" <n:Id><p:Angle<ParameterKind>> <w:WhereClauses> "{" <a:AssocTyDefn*> "}" => TraitDefn {
53+
<auto:AutoKeyword?> "trait" <n:Id><p:Angle<ParameterKind>> <w:WhereClauses> "{"
54+
<a:AssocTyDefn*> "}" =>
55+
TraitDefn {
5256
name: n,
5357
parameter_kinds: p,
5458
where_clauses: w,
5559
assoc_ty_defns: a,
60+
auto: auto.is_some(),
5661
}
5762
};
5863

@@ -64,14 +69,17 @@ AssocTyDefn: AssocTyDefn = {
6469
};
6570

6671
Impl: Impl = {
67-
"impl" <p:Angle<ParameterKind>> <t:Id> <a:Angle<Parameter>> "for" <s:Ty> <w:WhereClauses> "{"
68-
<assoc:AssocTyValue*>"}" =>
72+
"impl" <p:Angle<ParameterKind>> <mark:"!"?> <t:Id> <a:Angle<Parameter>> "for" <s:Ty> <w:WhereClauses> "{"
73+
<assoc:AssocTyValue*> "}" =>
6974
{
7075
let mut args = vec![Parameter::Ty(s)];
7176
args.extend(a);
7277
Impl {
7378
parameter_kinds: p,
74-
trait_ref: TraitRef { trait_name: t, args: args },
79+
trait_ref: PolarizedTraitRef::from_bool(mark.is_none(), TraitRef {
80+
trait_name: t,
81+
args: args,
82+
}),
7583
where_clauses: w,
7684
assoc_ty_values: assoc,
7785
}

src/coherence/solve.rs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,12 @@ impl Program {
1717

1818
// Create a vector of references to impl datums, sorted by trait ref
1919
let impl_data = self.impl_data.iter().sorted_by(|&(_, lhs), &(_, rhs)| {
20-
lhs.binders.value.trait_ref.trait_id.cmp(&rhs.binders.value.trait_ref.trait_id)
20+
lhs.binders.value.trait_ref.trait_ref().trait_id.cmp(&rhs.binders.value.trait_ref.trait_ref().trait_id)
2121
});
2222

2323
// Group impls by trait.
2424
let impl_groupings = impl_data.into_iter().group_by(|&(_, impl_datum)| {
25-
impl_datum.binders.value.trait_ref.trait_id
25+
impl_datum.binders.value.trait_ref.trait_ref().trait_id
2626
});
2727

2828

@@ -31,6 +31,11 @@ impl Program {
3131
let impls: Vec<(&ItemId, &ImplDatum)> = impls.collect();
3232

3333
for ((&l_id, lhs), (&r_id, rhs)) in impls.into_iter().tuple_combinations() {
34+
// Two negative impls never overlap.
35+
if !lhs.binders.value.trait_ref.is_positive() && !rhs.binders.value.trait_ref.is_positive() {
36+
continue;
37+
}
38+
3439
// Check if the impls overlap, then if they do, check if one specializes
3540
// the other. Note that specialization can only run one way - if both
3641
// specialization checks return *either* true or false, that's an error.
@@ -133,6 +138,11 @@ impl Solver {
133138
// }
134139
// }
135140
fn specializes(&mut self, less_special: &ImplDatum, more_special: &ImplDatum) -> bool {
141+
// Negative impls cannot specialize.
142+
if !less_special.binders.value.trait_ref.is_positive() || !more_special.binders.value.trait_ref.is_positive() {
143+
return false;
144+
}
145+
136146
let more_len = more_special.binders.len();
137147

138148
// Create parameter equality goals.
@@ -160,5 +170,5 @@ impl Solver {
160170
}
161171

162172
fn params(impl_datum: &ImplDatum) -> &[Parameter] {
163-
&impl_datum.binders.value.trait_ref.parameters
173+
&impl_datum.binders.value.trait_ref.trait_ref().parameters
164174
}

src/fold/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@ macro_rules! enum_fold {
195195
}
196196
}
197197

198+
enum_fold!(PolarizedTraitRef[] { Positive(a), Negative(a) });
198199
enum_fold!(ParameterKind[T,L] { Ty(a), Lifetime(a) } where T: Fold, L: Fold);
199200
enum_fold!(DomainGoal[] { Implemented(a), Normalize(a), WellFormed(a) });
200201
enum_fold!(WellFormed[] { Ty(a), TraitRef(a) });

src/ir/mod.rs

Lines changed: 69 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use cast::Cast;
22
use chalk_parse::ast;
33
use lalrpop_intern::InternedString;
44
use solve::infer::{TyInferenceVariable, LifetimeInferenceVariable};
5-
use std::collections::{HashMap, BTreeMap};
5+
use std::collections::{HashSet, HashMap, BTreeMap};
66
use std::sync::Arc;
77

88
pub type Identifier = InternedString;
@@ -24,8 +24,11 @@ pub struct Program {
2424
/// For each trait:
2525
pub trait_data: HashMap<ItemId, TraitDatum>,
2626

27-
/// For each trait:
27+
/// For each associated ty:
2828
pub associated_ty_data: HashMap<ItemId, AssociatedTyDatum>,
29+
30+
/// For each default impl (automatically generated for auto traits):
31+
pub default_impl_data: Vec<DefaultImplDatum>,
2932
}
3033

3134
impl Program {
@@ -85,7 +88,8 @@ impl Environment {
8588
where I: IntoIterator<Item = DomainGoal>
8689
{
8790
let mut env = self.clone();
88-
env.clauses.extend(clauses);
91+
let env_clauses: HashSet<_> = env.clauses.into_iter().chain(clauses).collect();
92+
env.clauses = env_clauses.into_iter().collect();
8993
Arc::new(env)
9094
}
9195

@@ -182,12 +186,23 @@ pub struct ImplDatum {
182186

183187
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
184188
pub struct ImplDatumBound {
185-
pub trait_ref: TraitRef,
189+
pub trait_ref: PolarizedTraitRef,
186190
pub where_clauses: Vec<DomainGoal>,
187191
pub associated_ty_values: Vec<AssociatedTyValue>,
188192
pub specialization_priority: usize,
189193
}
190194

195+
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
196+
pub struct DefaultImplDatum {
197+
pub binders: Binders<DefaultImplDatumBound>,
198+
}
199+
200+
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
201+
pub struct DefaultImplDatumBound {
202+
pub trait_ref: TraitRef,
203+
pub accessible_tys: Vec<Ty>,
204+
}
205+
191206
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
192207
pub struct StructDatum {
193208
pub binders: Binders<StructDatumBound>,
@@ -209,6 +224,7 @@ pub struct TraitDatum {
209224
pub struct TraitDatumBound {
210225
pub trait_ref: TraitRef,
211226
pub where_clauses: Vec<DomainGoal>,
227+
pub auto: bool,
212228
}
213229

214230
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
@@ -343,6 +359,28 @@ pub struct TraitRef {
343359
pub parameters: Vec<Parameter>,
344360
}
345361

362+
#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
363+
pub enum PolarizedTraitRef {
364+
Positive(TraitRef),
365+
Negative(TraitRef),
366+
}
367+
368+
impl PolarizedTraitRef {
369+
pub fn is_positive(&self) -> bool {
370+
match *self {
371+
PolarizedTraitRef::Positive(_) => true,
372+
PolarizedTraitRef::Negative(_) => false,
373+
}
374+
}
375+
376+
pub fn trait_ref(&self) -> &TraitRef {
377+
match *self {
378+
PolarizedTraitRef::Positive(ref tr) |
379+
PolarizedTraitRef::Negative(ref tr) => tr
380+
}
381+
}
382+
}
383+
346384
/// A "domain goal" is a goal that is directly about Rust, rather than a pure
347385
/// logical statement. As much as possible, the Chalk solver should avoid
348386
/// decomposing this enum, and instead treat its values opaquely.
@@ -374,9 +412,8 @@ impl DomainGoal {
374412
pub fn expanded(self, program: &Program) -> impl Iterator<Item = DomainGoal> {
375413
let mut expanded = vec![];
376414
match self {
377-
DomainGoal::Implemented(ref trait_ref) => {
378-
expanded.push(WellFormed::TraitRef(trait_ref.clone()).cast());
379-
}
415+
DomainGoal::Implemented(ref trait_ref) =>
416+
expanded.push(WellFormed::TraitRef(trait_ref.clone()).cast()),
380417
DomainGoal::Normalize(Normalize { ref projection, .. }) => {
381418
let (associated_ty_data, trait_params, _) = program.split_projection(&projection);
382419
let trait_ref = TraitRef {
@@ -500,6 +537,31 @@ pub enum FullyReducedGoal {
500537
DomainGoal(Canonical<InEnvironment<DomainGoal>>),
501538
}
502539

540+
impl FullyReducedGoal {
541+
pub fn into_binders(self) -> Vec<ParameterKind<UniverseIndex>> {
542+
match self {
543+
FullyReducedGoal::EqGoal(Canonical { binders, .. }) |
544+
FullyReducedGoal::DomainGoal(Canonical { binders, ..}) => binders,
545+
}
546+
}
547+
548+
/// A goal has coinductive semantics if it is of the form `T: AutoTrait`.
549+
pub fn is_coinductive(&self, program: &ProgramEnvironment) -> bool {
550+
if let FullyReducedGoal::DomainGoal(Canonical {
551+
value: InEnvironment {
552+
goal: DomainGoal::Implemented(ref tr),
553+
..
554+
},
555+
..
556+
}) = *self {
557+
let trait_datum = &program.trait_data[&tr.trait_id];
558+
return trait_datum.binders.value.auto;
559+
}
560+
561+
false
562+
}
563+
}
564+
503565
impl<T> Canonical<T> {
504566
pub fn map<OP, U>(self, op: OP) -> Canonical<U>
505567
where OP: FnOnce(T) -> U

src/lower/default.rs

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
use ir::*;
2+
use solve::infer::InferenceTable;
3+
use cast::Cast;
4+
5+
impl Program {
6+
pub(super) fn add_default_impls(&mut self) {
7+
// For each auto trait `MyAutoTrait` and for each struct/type `MyStruct`
8+
for auto_trait in self.trait_data.values().filter(|t| t.binders.value.auto) {
9+
for struct_datum in self.struct_data.values() {
10+
11+
// `MyStruct: MyAutoTrait`
12+
let trait_ref = TraitRef {
13+
trait_id: auto_trait.binders.value.trait_ref.trait_id,
14+
parameters: vec![
15+
ParameterKind::Ty(Ty::Apply(struct_datum.binders.value.self_ty.clone()))
16+
]
17+
};
18+
19+
// If a positive or negative impl is already provided for a type family
20+
// which includes `MyStruct`, we do not generate a default impl.
21+
if self.impl_provided_for(trait_ref.clone(), struct_datum) {
22+
continue;
23+
}
24+
25+
self.default_impl_data.push(DefaultImplDatum {
26+
binders: Binders {
27+
binders: struct_datum.binders.binders.clone(),
28+
value: DefaultImplDatumBound {
29+
trait_ref,
30+
accessible_tys: struct_datum.binders.value.fields.clone(),
31+
}
32+
}
33+
});
34+
}
35+
}
36+
}
37+
38+
fn impl_provided_for(&self, trait_ref: TraitRef, struct_datum: &StructDatum) -> bool {
39+
let goal: DomainGoal = trait_ref.cast();
40+
41+
let env = Environment::new();
42+
let mut infer = InferenceTable::new();
43+
44+
let goal = infer.instantiate_in(env.universe, struct_datum.binders.binders.clone(), &goal);
45+
46+
for impl_datum in self.impl_data.values() {
47+
// We retrieve the trait ref given by the positive impl (even if the actual impl is negative)
48+
let impl_goal: DomainGoal = impl_datum.binders.value.trait_ref.trait_ref().clone().cast();
49+
50+
let impl_goal = infer.instantiate_in(env.universe, impl_datum.binders.binders.clone(), &impl_goal);
51+
52+
// We check whether the impl `MyStruct: (!)MyAutoTrait` unifies with an existing impl.
53+
// Examples:
54+
//
55+
// ```
56+
// struct MyStruct;
57+
// impl<T> Send for T where T: Foo { }
58+
// ```
59+
// `MyStruct: Send` unifies with `T: Send` so no default impl is generated for `MyStruct`.
60+
//
61+
// ```
62+
// struct MyStruct;
63+
// impl<T> Send for Vec<T> where T: Foo { }
64+
// ```
65+
// `Vec<i32>: Send` unifies with `Vec<T>: Send` so no default impl is generated for `Vec<i32>`.
66+
// But a default impl is generated for `MyStruct`.
67+
//
68+
// ```
69+
// struct MyStruct;
70+
// impl<T> !Send for T where T: Foo { }
71+
// ```
72+
// `MyStruct: !Send` unifies with `T: !Send` so no default impl is generated for `MyStruct`.
73+
if infer.unify(&Environment::new(), &goal, &impl_goal).is_ok() {
74+
return true;
75+
}
76+
}
77+
78+
false
79+
}
80+
}

0 commit comments

Comments
 (0)