-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
Be more permissive with required bounds on existential types #57896
Changes from all commits
381c541
6982fd2
ed10a5b
0d25ff8
cf01b51
6f83dcc
984688a
f2241f6
c93bce8
eb98d31
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 |
---|---|---|
|
@@ -23,9 +23,10 @@ use middle::resolve_lifetime as rl; | |
use middle::weak_lang_items; | ||
use rustc::mir::mono::Linkage; | ||
use rustc::ty::query::Providers; | ||
use rustc::ty::subst::Substs; | ||
use rustc::ty::subst::{Subst, Substs}; | ||
use rustc::ty::util::Discr; | ||
use rustc::ty::util::IntTypeExt; | ||
use rustc::ty::subst::UnpackedKind; | ||
use rustc::ty::{self, AdtKind, ToPolyTraitRef, Ty, TyCtxt}; | ||
use rustc::ty::{ReprOptions, ToPredicate}; | ||
use rustc::util::captures::Captures; | ||
|
@@ -1193,7 +1194,7 @@ fn type_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> Ty<'tcx> { | |
tcx.typeck_tables_of(owner) | ||
.concrete_existential_types | ||
.get(&def_id) | ||
.cloned() | ||
.map(|opaque| opaque.concrete_type) | ||
.unwrap_or_else(|| { | ||
// This can occur if some error in the | ||
// owner fn prevented us from populating | ||
|
@@ -1325,7 +1326,13 @@ fn find_existential_constraints<'a, 'tcx>( | |
struct ConstraintLocator<'a, 'tcx: 'a> { | ||
tcx: TyCtxt<'a, 'tcx, 'tcx>, | ||
def_id: DefId, | ||
found: Option<(Span, ty::Ty<'tcx>)>, | ||
// First found type span, actual type, mapping from the existential type's generic | ||
// parameters to the concrete type's generic parameters | ||
// | ||
// The mapping is an index for each use site of a generic parameter in the concrete type | ||
// | ||
// The indices index into the generic parameters on the existential type. | ||
found: Option<(Span, ty::Ty<'tcx>, Vec<usize>)>, | ||
} | ||
|
||
impl<'a, 'tcx> ConstraintLocator<'a, 'tcx> { | ||
|
@@ -1340,23 +1347,106 @@ fn find_existential_constraints<'a, 'tcx>( | |
.tcx | ||
.typeck_tables_of(def_id) | ||
.concrete_existential_types | ||
.get(&self.def_id) | ||
.cloned(); | ||
if let Some(ty) = ty { | ||
.get(&self.def_id); | ||
if let Some(ty::ResolvedOpaqueTy { concrete_type, substs }) = ty { | ||
// FIXME(oli-obk): trace the actual span from inference to improve errors | ||
let span = self.tcx.def_span(def_id); | ||
if let Some((prev_span, prev_ty)) = self.found { | ||
if ty != prev_ty { | ||
// used to quickly look up the position of a generic parameter | ||
let mut index_map: FxHashMap<ty::ParamTy, usize> = FxHashMap::default(); | ||
// skip binder is ok, since we only use this to find generic parameters and their | ||
// positions. | ||
for (idx, subst) in substs.iter().enumerate() { | ||
if let UnpackedKind::Type(ty) = subst.unpack() { | ||
if let ty::Param(p) = ty.sty { | ||
if index_map.insert(p, idx).is_some() { | ||
// there was already an entry for `p`, meaning a generic parameter | ||
// was used twice | ||
self.tcx.sess.span_err( | ||
span, | ||
&format!("defining existential type use restricts existential \ | ||
type by using the generic parameter `{}` twice", p.name), | ||
); | ||
return; | ||
} | ||
} else { | ||
self.tcx.sess.delay_span_bug( | ||
span, | ||
&format!( | ||
"non-defining exist ty use in defining scope: {:?}, {:?}", | ||
concrete_type, substs, | ||
), | ||
); | ||
} | ||
} | ||
} | ||
// compute the index within the existential type for each generic parameter used in | ||
// the concrete type | ||
let indices = concrete_type | ||
.subst(self.tcx, substs) | ||
.walk() | ||
.filter_map(|t| match &t.sty { | ||
ty::Param(p) => Some(*index_map.get(p).unwrap()), | ||
_ => None, | ||
}).collect(); | ||
Comment on lines
+1384
to
+1390
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. Trying to understand what this does, we have these facts:
All that means let indices = concrete_type
.walk()
.filter_map(|t| match &t.sty {
ty::Param(p) => Some(p.index),
_ => None,
}).collect(); If Am I missing something? This entire PR, other than checking I'll attempt a revert of the parts that look like a noop, and generalize to handle 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. Opened #70272. |
||
let is_param = |ty: ty::Ty| match ty.sty { | ||
ty::Param(_) => true, | ||
_ => false, | ||
}; | ||
if !substs.types().all(is_param) { | ||
self.tcx.sess.span_err( | ||
span, | ||
"defining existential type use does not fully define existential type", | ||
); | ||
} else if let Some((prev_span, prev_ty, ref prev_indices)) = self.found { | ||
let mut ty = concrete_type.walk().fuse(); | ||
let mut p_ty = prev_ty.walk().fuse(); | ||
let iter_eq = (&mut ty).zip(&mut p_ty).all(|(t, p)| match (&t.sty, &p.sty) { | ||
// type parameters are equal to any other type parameter for the purpose of | ||
// concrete type equality, as it is possible to obtain the same type just | ||
// by passing matching parameters to a function. | ||
(ty::Param(_), ty::Param(_)) => true, | ||
_ => t == p, | ||
Comment on lines
+1404
to
+1408
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. I'm suspicious of this entire approach. Only the outermost type could be a differing There are two ways to compare types deeply, and they're:
"Walking" a type is a not a good way to achieve almost anything, the main legitimate usecase I've found is gathering leaves of some kind (e.g. |
||
}); | ||
if !iter_eq || ty.next().is_some() || p_ty.next().is_some() { | ||
// found different concrete types for the existential type | ||
let mut err = self.tcx.sess.struct_span_err( | ||
span, | ||
"defining existential type use differs from previous", | ||
"concrete type differs from previous defining existential type use", | ||
); | ||
err.span_label( | ||
span, | ||
format!("expected `{}`, got `{}`", prev_ty, concrete_type), | ||
); | ||
err.span_note(prev_span, "previous use here"); | ||
err.emit(); | ||
} else if indices != *prev_indices { | ||
// found "same" concrete types, but the generic parameter order differs | ||
let mut err = self.tcx.sess.struct_span_err( | ||
span, | ||
"concrete type's generic parameters differ from previous defining use", | ||
); | ||
use std::fmt::Write; | ||
let mut s = String::new(); | ||
write!(s, "expected [").unwrap(); | ||
let list = |s: &mut String, indices: &Vec<usize>| { | ||
let mut indices = indices.iter().cloned(); | ||
if let Some(first) = indices.next() { | ||
write!(s, "`{}`", substs[first]).unwrap(); | ||
for i in indices { | ||
write!(s, ", `{}`", substs[i]).unwrap(); | ||
} | ||
} | ||
}; | ||
list(&mut s, prev_indices); | ||
write!(s, "], got [").unwrap(); | ||
list(&mut s, &indices); | ||
write!(s, "]").unwrap(); | ||
err.span_label(span, s); | ||
err.span_note(prev_span, "previous use here"); | ||
err.emit(); | ||
} | ||
} else { | ||
self.found = Some((span, ty)); | ||
self.found = Some((span, concrete_type, indices)); | ||
} | ||
} | ||
} | ||
|
@@ -1415,7 +1505,7 @@ fn find_existential_constraints<'a, 'tcx>( | |
} | ||
|
||
match locator.found { | ||
Some((_, ty)) => ty, | ||
Some((_, ty, _)) => ty, | ||
None => { | ||
let span = tcx.def_span(def_id); | ||
tcx.sess.span_err(span, "could not find defining uses"); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,16 +1,16 @@ | ||
error: non-defining existential type use in defining scope | ||
--> $DIR/bound_reduction2.rs:16:1 | ||
error: defining existential type use does not fully define existential type | ||
--> $DIR/bound_reduction2.rs:17:1 | ||
| | ||
LL | / fn foo_desugared<T: TraitWithAssoc>(_: T) -> Foo<T::Assoc> { //~ ERROR non-defining | ||
LL | / fn foo_desugared<T: TraitWithAssoc>(_: T) -> Foo<T::Assoc> { //~ ERROR does not fully define | ||
LL | | () | ||
LL | | } | ||
| |_^ | ||
| | ||
note: used non-generic type <T as TraitWithAssoc>::Assoc for generic parameter | ||
--> $DIR/bound_reduction2.rs:10:22 | ||
|
||
error: could not find defining uses | ||
--> $DIR/bound_reduction2.rs:10:1 | ||
| | ||
LL | existential type Foo<V>: Trait<V>; | ||
| ^ | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ||
|
||
error: aborting due to previous error | ||
error: aborting due to 2 previous errors | ||
|
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.
Hang on, isn't substs checking handled by
wfcheck
?rust/src/librustc_typeck/check/wfcheck.rs
Lines 818 to 837 in 5574b1d
Wouldn't that make
ResolvedOpaqueTy
and most of this PR entirely redundant, if params were checked there?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.
Hmm, I can't remove
ResolvedOpaqueTy
as it seems MIR borrowck needs theSubsts
, and I'd rather not touch that code if I can help it.