Skip to content

Commit f79f6dd

Browse files
committed
Revamp normalization fallback to follow Niko's review suggestion
1 parent 1444a69 commit f79f6dd

File tree

6 files changed

+82
-62
lines changed

6 files changed

+82
-62
lines changed

src/fold/mod.rs

+1-1
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), KnownProjection(a), Normalize(a), WellFormed(a) });
199+
enum_fold!(DomainGoal[] { Implemented(a), RawNormalize(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/ir/debug.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -149,7 +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),
152+
DomainGoal::RawNormalize(ref n) => write!(fmt, "raw {{ {:?} }}", n),
153153
DomainGoal::Implemented(ref n) => {
154154
write!(fmt,
155155
"{:?}: {:?}{:?}",

src/ir/mod.rs

+5-9
Original file line numberDiff line numberDiff line change
@@ -129,8 +129,8 @@ impl Environment {
129129
push_clause(where_clause);
130130
}
131131
}
132-
DomainGoal::Normalize(Normalize { ref projection, ty: _ }) => {
133-
// <T as Trait<U>>::Foo ===> V
132+
DomainGoal::RawNormalize(Normalize { ref projection, ty: _ }) => {
133+
// raw { <T as Trait<U>>::Foo ===> V }
134134
// ----------------------------------------------------------
135135
// T: Trait<U>
136136

@@ -140,11 +140,6 @@ impl Environment {
140140
parameters: trait_params.to_owned()
141141
};
142142
push_clause(trait_ref.cast());
143-
144-
// <T as Trait<U>>::Foo ===> V
145-
// ----------------------------------------------------------
146-
// known { Trait<U>::Foo<T> }
147-
push_clause(DomainGoal::KnownProjection(projection.clone()));
148143
}
149144
_ => {}
150145
}
@@ -401,8 +396,9 @@ pub struct TraitRef {
401396
#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
402397
pub enum DomainGoal {
403398
Implemented(TraitRef),
404-
/// Is the projection something we know definitively from impls?
405-
KnownProjection(ProjectionTy),
399+
/// A projection we know definitively via an impl or where clause
400+
RawNormalize(Normalize),
401+
/// A general projection, which might employ fallback
406402
Normalize(Normalize),
407403
WellFormed(WellFormed),
408404
}

src/lower/mod.rs

+73-49
Original file line numberDiff line numberDiff line change
@@ -371,16 +371,20 @@ trait LowerWhereClause<T> {
371371
fn lower(&self, env: &Env) -> Result<T>;
372372
}
373373

374-
/// Lowers a where-clause in the context of a clause; this is limited
375-
/// to the kinds of where-clauses users can actually type in Rust.
374+
/// Lowers a where-clause in the context of a clause (i.e. in "negative"
375+
/// position); this is limited to the kinds of where-clauses users can actually
376+
/// type in Rust.
376377
impl LowerWhereClause<ir::DomainGoal> for WhereClause {
377378
fn lower(&self, env: &Env) -> Result<ir::DomainGoal> {
378379
Ok(match *self {
379380
WhereClause::Implemented { ref trait_ref } => {
380381
ir::DomainGoal::Implemented(trait_ref.lower(env)?)
381382
}
382383
WhereClause::ProjectionEq { ref projection, ref ty } => {
383-
ir::DomainGoal::Normalize(ir::Normalize {
384+
// NB: here we generate a RawNormalize, because we want to make
385+
// make a maximally-strong assumption (and not allow fallback to
386+
// trigger).
387+
ir::DomainGoal::RawNormalize(ir::Normalize {
384388
projection: projection.lower(env)?,
385389
ty: ty.lower(env)?,
386390
})
@@ -395,16 +399,24 @@ impl LowerWhereClause<ir::DomainGoal> for WhereClause {
395399
}
396400
}
397401

398-
/// Lowers a where-clause in the context of a goal; this is richer in
399-
/// terms of the legal sorts of where-clauses that can appear, because
400-
/// it includes all the sorts of things that the compiler must verify.
402+
/// Lowers a where-clause in the context of a goal (i.e. in "positive"
403+
/// position); this is richer in terms of the legal sorts of where-clauses that
404+
/// can appear, because it includes all the sorts of things that the compiler
405+
/// must verify.
401406
impl LowerWhereClause<ir::LeafGoal> for WhereClause {
402407
fn lower(&self, env: &Env) -> Result<ir::LeafGoal> {
403408
Ok(match *self {
404-
WhereClause::Implemented { .. } |
405-
WhereClause::ProjectionEq { .. } => {
406-
let wc: ir::DomainGoal = self.lower(env)?;
407-
wc.cast()
409+
WhereClause::Implemented { .. } => {
410+
let g: ir::DomainGoal = self.lower(env)?;
411+
g.cast()
412+
}
413+
WhereClause::ProjectionEq { ref projection, ref ty } => {
414+
// NB: here we generate a full Normalize clause, allowing for
415+
// fallback to trigger when we're trying to *prove* a goal
416+
ir::DomainGoal::Normalize(ir::Normalize {
417+
projection: projection.lower(env)?,
418+
ty: ty.lower(env)?,
419+
}).cast()
408420
}
409421
WhereClause::TyWellFormed { ref ty } => {
410422
ir::WellFormed::Ty(ty.lower(env)?).cast()
@@ -812,11 +824,7 @@ impl ir::AssociatedTyValue {
812824
///
813825
/// ```notrust
814826
/// forall<'a, T> {
815-
/// (Vec<T>: Iterable<IntoIter<'a> = Iter<'a, T>>) :-
816-
/// (Vec<T>: Iterable), // (1)
817-
/// (T: 'a) // (2)
818-
///
819-
/// known(Iterable::IntoIter<Vec<T>, 'a>) :-
827+
/// (Vec<T>: Iterable<IntoIter<'a> =raw Iter<'a, T>>) :-
820828
/// (Vec<T>: Iterable), // (1)
821829
/// (T: 'a) // (2)
822830
/// }
@@ -856,11 +864,11 @@ impl ir::AssociatedTyValue {
856864
};
857865

858866
// Determine the normalization
859-
let norm_clause = ir::ProgramClause {
867+
let normalization = ir::ProgramClause {
860868
implication: ir::Binders {
861869
binders: all_binders.clone(),
862870
value: ir::ProgramClauseImplication {
863-
consequence: ir::DomainGoal::Normalize(ir::Normalize {
871+
consequence: ir::DomainGoal::RawNormalize(ir::Normalize {
864872
projection: projection.clone(),
865873
ty: self.value.value.ty.clone()
866874
}),
@@ -869,18 +877,7 @@ impl ir::AssociatedTyValue {
869877
}
870878
};
871879

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]
880+
vec![normalization]
884881
}
885882
}
886883

@@ -984,46 +981,73 @@ impl ir::TraitDatum {
984981

985982
impl ir::AssociatedTyDatum {
986983
fn to_program_clauses(&self) -> Vec<ir::ProgramClause> {
987-
// For each associated type, we have a "normalization fallback" clause.
984+
// For each associated type, we define normalization including a
985+
// "fallback" if we can't resolve a projection using an impl/where clauses.
988986
//
989987
// Given:
990988
//
991-
// trait SomeTrait {
989+
// trait Foo {
992990
// type Assoc;
993991
// }
994992
//
995993
// we generate:
996994
//
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 } }.
995+
// ?T: Foo<Assoc = ?U> :- ?T: Foo<Assoc =raw ?U>.
996+
// ?T: Foo<Assoc = (Foo::Assoc)<?T>> :- not { exists<U> { ?T: Foo<Assoc =raw U> } }.
1000997

1001998
let binders: Vec<_> = self.parameter_kinds.iter().map(|pk| pk.map(|_| ())).collect();
1002999
let parameters: Vec<_> = binders.iter().zip(0..).map(|p| p.to_parameter()).collect();
1003-
10041000
let projection = ir::ProjectionTy {
10051001
associated_ty_id: self.id,
10061002
parameters: parameters.clone(),
10071003
};
10081004

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 };
1005+
let raw = {
1006+
let binders: Vec<_> = binders.iter()
1007+
.cloned()
1008+
.chain(Some(ir::ParameterKind::Ty(())))
1009+
.collect();
1010+
let ty = ir::Ty::Var(binders.len() - 1);
1011+
let normalize = ir::Normalize { projection: projection.clone(), ty };
1012+
1013+
ir::ProgramClause {
1014+
implication: ir::Binders {
1015+
binders,
1016+
value: ir::ProgramClauseImplication {
1017+
consequence: normalize.clone().cast(),
1018+
conditions: vec![ir::DomainGoal::RawNormalize(normalize).cast()],
1019+
}
1020+
}
1021+
}
1022+
};
10141023

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-
]
1024+
let fallback = {
1025+
// Construct an application from the projection. So if we have `<T as Iterator>::Item`,
1026+
// we would produce `(Iterator::Item)<T>`.
1027+
let app = ir::ApplicationTy { name: ir::TypeName::AssociatedType(self.id), parameters };
1028+
let ty = ir::Ty::Apply(app);
1029+
1030+
let raw = ir::DomainGoal::RawNormalize(ir::Normalize {
1031+
projection: projection.clone().up_shift(1),
1032+
ty: ir::Ty::Var(0),
1033+
});
1034+
let exists_binders = ir::Binders {
1035+
binders: vec![ir::ParameterKind::Ty(())],
1036+
value: Box::new(raw.cast()),
1037+
};
1038+
let exists = ir::Goal::Quantified(ir::QuantifierKind::Exists, exists_binders);
1039+
1040+
ir::ProgramClause {
1041+
implication: ir::Binders {
1042+
binders,
1043+
value: ir::ProgramClauseImplication {
1044+
consequence: ir::Normalize { projection: projection.clone(), ty }.cast(),
1045+
conditions: vec![ir::Goal::Not(Box::new(exists))]
1046+
}
10231047
}
10241048
}
10251049
};
10261050

1027-
vec![fallback]
1051+
vec![raw, fallback]
10281052
}
10291053
}

src/solve/test.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -859,7 +859,7 @@ fn mixed_indices_normalize_application() {
859859
}
860860
}
861861
} yields {
862-
"Ambig"
862+
"Unique" // normalizations exist even if the trait isn't implemented
863863
}
864864
}
865865
}

src/zip.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,6 @@ macro_rules! enum_zip {
155155
}
156156
}
157157

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

0 commit comments

Comments
 (0)