Skip to content

Commit 23f4277

Browse files
committed
Implement normalization fallback via lowering
This commit introduces a new domain goal: `KnownProjection`. It holds whenever you're able to directly normalize a projection, i.e. through an impl or where clause. Normalizations are then expanded to include a fallback precisely in the cases where `KnownProjection` cannot be proved.
1 parent 3a8f32b commit 23f4277

File tree

7 files changed

+120
-64
lines changed

7 files changed

+120
-64
lines changed

src/fold/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ macro_rules! enum_fold {
196196
}
197197

198198
enum_fold!(ParameterKind[T,L] { Ty(a), Lifetime(a) } where T: Fold, L: Fold);
199-
enum_fold!(DomainGoal[] { Implemented(a), Normalize(a), WellFormed(a) });
199+
enum_fold!(DomainGoal[] { Implemented(a), KnownProjection(a), Normalize(a), WellFormed(a) });
200200
enum_fold!(WellFormed[] { Ty(a), TraitRef(a) });
201201
enum_fold!(LeafGoal[] { EqGoal(a), DomainGoal(a) });
202202
enum_fold!(Constraint[] { LifetimeEq(a, b) });

src/fold/shifter.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ up_shift_method!(Ty);
3434
up_shift_method!(Parameter);
3535
up_shift_method!(Lifetime);
3636
up_shift_method!(TraitRef);
37+
up_shift_method!(ProjectionTy);
3738

3839
impl Folder for Shifter {
3940
fn fold_free_var(&mut self, depth: usize, binders: usize) -> Result<Ty> {

src/ir/debug.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ impl Debug for DomainGoal {
149149
fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
150150
match *self {
151151
DomainGoal::Normalize(ref n) => write!(fmt, "{:?}", n),
152+
DomainGoal::KnownProjection(ref n) => write!(fmt, "known {{ {:?} }}", n),
152153
DomainGoal::Implemented(ref n) => {
153154
write!(fmt,
154155
"{:?}: {:?}{:?}",

src/ir/mod.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,11 @@ impl Environment {
136136
parameters: trait_params.to_owned()
137137
};
138138
push_clause(trait_ref.cast());
139+
140+
// <T as Trait<U>>::Foo ===> V
141+
// ----------------------------------------------------------
142+
// known { Trait<U>::Foo<T> }
143+
push_clause(DomainGoal::KnownProjection(projection.clone()));
139144
}
140145
_ => {}
141146
}
@@ -259,6 +264,9 @@ pub struct AssociatedTyDatum {
259264
/// The trait this associated type is defined in.
260265
pub trait_id: ItemId,
261266

267+
/// The ID of this associated type
268+
pub id: ItemId,
269+
262270
/// Name of this associated type.
263271
pub name: Identifier,
264272

@@ -389,6 +397,8 @@ pub struct TraitRef {
389397
/// decomposing this enum, and instead treat its values opaquely.
390398
pub enum DomainGoal {
391399
Implemented(TraitRef),
400+
/// Is the projection something we know definitively from impls?
401+
KnownProjection(ProjectionTy),
392402
Normalize(Normalize),
393403
WellFormed(WellFormed),
394404
}

src/lower/mod.rs

Lines changed: 84 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@ impl LowerProgram for Program {
170170

171171
associated_ty_data.insert(info.id, ir::AssociatedTyDatum {
172172
trait_id: item_id,
173+
id: info.id,
173174
name: defn.name.str,
174175
parameter_kinds: parameter_kinds,
175176
where_clauses: vec![ir::DomainGoal::Implemented(trait_ref)]
@@ -194,11 +195,19 @@ trait LowerParameterMap {
194195
fn synthetic_parameters(&self) -> Option<ir::ParameterKind<ir::Identifier>>;
195196
fn declared_parameters(&self) -> &[ParameterKind];
196197
fn all_parameters(&self) -> Vec<ir::ParameterKind<ir::Identifier>> {
198+
self.synthetic_parameters()
199+
.into_iter()
200+
.chain(self.declared_parameters().iter().map(|id| id.lower()))
201+
.collect()
202+
203+
/* TODO: switch to this ordering, but adjust *all* the code to match
204+
197205
self.declared_parameters()
198206
.iter()
199207
.map(|id| id.lower())
200-
.chain(self.synthetic_parameters()) // (*) see above
208+
.chain(self.synthetic_parameters()) // (*) see below
201209
.collect()
210+
*/
202211
}
203212

204213
fn parameter_refs(&self) -> Vec<ir::Parameter> {
@@ -756,6 +765,7 @@ impl ir::Program {
756765

757766
program_clauses.extend(self.struct_data.values().flat_map(|d| d.to_program_clauses()));
758767
program_clauses.extend(self.trait_data.values().flat_map(|d| d.to_program_clauses()));
768+
program_clauses.extend(self.associated_ty_data.values().flat_map(|d| d.to_program_clauses()));
759769

760770
for datum in self.impl_data.values() {
761771
program_clauses.push(datum.to_program_clause());
@@ -805,6 +815,10 @@ impl ir::AssociatedTyValue {
805815
/// (Vec<T>: Iterable<IntoIter<'a> = Iter<'a, T>>) :-
806816
/// (Vec<T>: Iterable), // (1)
807817
/// (T: 'a) // (2)
818+
///
819+
/// known(Iterable::IntoIter<Vec<T>, 'a>) :-
820+
/// (Vec<T>: Iterable), // (1)
821+
/// (T: 'a) // (2)
808822
/// }
809823
/// ```
810824
fn to_program_clauses(&self, impl_datum: &ir::ImplDatum) -> Vec<ir::ProgramClause> {
@@ -828,35 +842,45 @@ impl ir::AssociatedTyValue {
828842
.chain(self.value.value.where_clauses.clone().cast())
829843
.collect();
830844

831-
// The consequence is that the normalization holds.
832-
let consequence = {
845+
let projection = {
833846
// First add refs to the bound parameters (`'a`, in above example)
834847
let parameters = self.value.binders.iter().zip(0..).map(|p| p.to_parameter());
835848

836849
// Then add the trait-ref parameters (`Vec<T>`, in above example)
837850
let parameters = parameters.chain(impl_trait_ref.parameters.clone());
838851

839-
// Construct normalization predicate
840-
ir::Normalize {
841-
projection: ir::ProjectionTy {
842-
associated_ty_id: self.associated_ty_id,
843-
parameters: parameters.collect(),
844-
},
845-
ty: self.value.value.ty.clone()
852+
ir::ProjectionTy {
853+
associated_ty_id: self.associated_ty_id,
854+
parameters: parameters.collect(),
846855
}
847856
};
848857

849-
let pos_clause = ir::ProgramClause {
858+
// Determine the normalization
859+
let norm_clause = ir::ProgramClause {
850860
implication: ir::Binders {
851861
binders: all_binders.clone(),
852862
value: ir::ProgramClauseImplication {
853-
consequence: consequence.clone().cast(),
854-
conditions: conditions,
863+
consequence: ir::DomainGoal::Normalize(ir::Normalize {
864+
projection: projection.clone(),
865+
ty: self.value.value.ty.clone()
866+
}),
867+
conditions: conditions.clone(),
855868
}
856869
}
857870
};
858871

859-
vec![pos_clause]
872+
// Record that the projection is known
873+
let known_clause = ir::ProgramClause {
874+
implication: ir::Binders {
875+
binders: all_binders.clone(),
876+
value: ir::ProgramClauseImplication {
877+
consequence: ir::DomainGoal::KnownProjection(projection),
878+
conditions: conditions.clone(),
879+
}
880+
}
881+
};
882+
883+
vec![norm_clause, known_clause]
860884
}
861885
}
862886

@@ -957,3 +981,49 @@ impl ir::TraitDatum {
957981
vec![wf]
958982
}
959983
}
984+
985+
impl ir::AssociatedTyDatum {
986+
fn to_program_clauses(&self) -> Vec<ir::ProgramClause> {
987+
// For each associated type, we have a "normalization fallback" clause.
988+
//
989+
// Given:
990+
//
991+
// trait SomeTrait {
992+
// type Assoc;
993+
// }
994+
//
995+
// we generate:
996+
//
997+
// ?T: SomeTrait<Assoc = (SomeTrait::Assoc)<?T>> :-
998+
// // we can't resolve the normalization through an impl clause
999+
// not { known { <?T as SomeTrait>::Assoc } }.
1000+
1001+
let binders: Vec<_> = self.parameter_kinds.iter().map(|pk| pk.map(|_| ())).collect();
1002+
let parameters: Vec<_> = binders.iter().zip(0..).map(|p| p.to_parameter()).collect();
1003+
1004+
let projection = ir::ProjectionTy {
1005+
associated_ty_id: self.id,
1006+
parameters: parameters.clone(),
1007+
};
1008+
1009+
// Construct an application from the projection. So if we have `<T as
1010+
// Iterator>::Item`, we would produce `(Iterator::Item)<T>`.
1011+
let app = ir::ApplicationTy { name: ir::TypeName::AssociatedType(self.id), parameters };
1012+
let fallback_ty = ir::Ty::Apply(app);
1013+
let norm_fallback = ir::Normalize { projection: projection.clone(), ty: fallback_ty };
1014+
1015+
let fallback = ir::ProgramClause {
1016+
implication: ir::Binders {
1017+
binders,
1018+
value: ir::ProgramClauseImplication {
1019+
consequence: ir::DomainGoal::Normalize(norm_fallback),
1020+
conditions: vec![
1021+
ir::Goal::Not(Box::new(ir::DomainGoal::KnownProjection(projection).cast()))
1022+
]
1023+
}
1024+
}
1025+
};
1026+
1027+
vec![fallback]
1028+
}
1029+
}

src/solve/test.rs

Lines changed: 22 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -290,37 +290,23 @@ fn normalize() {
290290
"Unique; substitution [?0 := u32], lifetime constraints []"
291291
}
292292

293-
// TODO: re-enable this once normalization fallback is reimplemented
294-
295-
// goal {
296-
// forall<T> {
297-
// if (T: Iterator) {
298-
// exists<U> {
299-
// T: Iterator<Item = U>
300-
// }
301-
// }
302-
// }
303-
// } yields {
304-
// "Solution {
305-
// successful: Yes,
306-
// refined_goal: Query {
307-
// value: Constrained {
308-
// value: [
309-
// <!1 as Iterator>::Item ==> (Iterator::Item)<!1>
310-
// ],
311-
// constraints: []
312-
// },
313-
// binders: []
314-
// }
315-
// }"
316-
// }
293+
goal {
294+
forall<T> {
295+
if (T: Iterator) {
296+
exists<U> {
297+
T: Iterator<Item = U>
298+
}
299+
}
300+
}
301+
} yields {
302+
"Unique; substitution [?0 := (Iterator::Item)<!1>]"
303+
}
317304
}
318305
}
319306

320307
/// Demonstrates that, given the expected value of the associated
321308
/// type, we can use that to narrow down the relevant impls.
322-
// TODO: re-enable once normalization fallback is reimplemented
323-
//#[test]
309+
#[test]
324310
fn normalize_rev_infer() {
325311
test! {
326312
program {
@@ -336,18 +322,7 @@ fn normalize_rev_infer() {
336322
T: Identity<Item = u32>
337323
}
338324
} yields {
339-
"Solution {
340-
successful: Yes,
341-
refined_goal: Query {
342-
value: Constrained {
343-
value: [
344-
<u32 as Identity>::Item ==> u32
345-
],
346-
constraints: []
347-
},
348-
binders: []
349-
}
350-
}"
325+
"Unique; substitution [?0 := u32]"
351326
}
352327
}
353328
}
@@ -861,8 +836,7 @@ fn mixed_indices_match_program() {
861836
}
862837
}
863838

864-
// TODO: re-enable once normalization fallback is reimplemented
865-
//#[test]
839+
#[test]
866840
fn mixed_indices_normalize_application() {
867841
test! {
868842
program {
@@ -881,15 +855,15 @@ fn mixed_indices_normalize_application() {
881855
}
882856
}
883857
} yields {
884-
"Solution { successful: Yes"
858+
"Ambig"
885859
}
886860
}
887861
}
888862

889863
#[test]
890864
// Test that we properly detect failure even if there are applicable impls at
891865
// the top level, if we can't find anything to fill in those impls with
892-
fn test_deep_failure() {
866+
fn deep_failure() {
893867
test! {
894868
program {
895869
struct Foo<T> {}
@@ -916,7 +890,7 @@ fn test_deep_failure() {
916890
#[test]
917891
// Test that we infer a unique solution even if it requires multiple levels of
918892
// search to do so
919-
fn test_deep_success() {
893+
fn deep_success() {
920894
test! {
921895
program {
922896
struct Foo<T> {}
@@ -938,7 +912,7 @@ fn test_deep_success() {
938912

939913

940914
#[test]
941-
fn test_definite_guidance() {
915+
fn definite_guidance() {
942916
test! {
943917
program {
944918
trait Display {}
@@ -964,7 +938,7 @@ fn test_definite_guidance() {
964938
}
965939

966940
#[test]
967-
fn test_suggested_subst() {
941+
fn suggested_subst() {
968942
test! {
969943
program {
970944
trait SomeTrait<A> {}
@@ -1070,7 +1044,7 @@ fn test_suggested_subst() {
10701044
}
10711045

10721046
#[test]
1073-
fn test_simple_negation() {
1047+
fn simple_negation() {
10741048
test! {
10751049
program {
10761050
struct i32 {}
@@ -1128,7 +1102,7 @@ fn test_simple_negation() {
11281102
}
11291103

11301104
#[test]
1131-
fn test_deep_negation() {
1105+
fn deep_negation() {
11321106
test! {
11331107
program {
11341108
struct Foo<T> {}
@@ -1157,7 +1131,7 @@ fn test_deep_negation() {
11571131
}
11581132

11591133
#[test]
1160-
fn test_negation_quantifiers() {
1134+
fn negation_quantifiers() {
11611135
test! {
11621136
program {
11631137
struct i32 {}

src/zip.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,6 @@ macro_rules! enum_zip {
155155
}
156156
}
157157

158-
enum_zip!(DomainGoal { Implemented, Normalize, WellFormed });
158+
enum_zip!(DomainGoal { Implemented, KnownProjection, Normalize, WellFormed });
159159
enum_zip!(LeafGoal { DomainGoal, EqGoal });
160160
enum_zip!(WellFormed { Ty, TraitRef });

0 commit comments

Comments
 (0)