Skip to content

Commit 74e35d2

Browse files
committed
Auto merge of #57896 - oli-obk:permissive_existence, r=cramertj
Be more permissive with required bounds on existential types fixes #54184 r? @pnkfelix
2 parents 4d66b7b + eb98d31 commit 74e35d2

39 files changed

+548
-79
lines changed

src/librustc/ich/impls_ty.rs

+5
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,11 @@ impl_stable_hash_for!(struct ty::FnSig<'tcx> {
237237
abi
238238
});
239239

240+
impl_stable_hash_for!(struct ty::ResolvedOpaqueTy<'tcx> {
241+
concrete_type,
242+
substs
243+
});
244+
240245
impl<'a, 'gcx, T> HashStable<StableHashingContext<'a>> for ty::Binder<T>
241246
where T: HashStable<StableHashingContext<'a>>
242247
{

src/librustc/infer/opaque_types/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ pub struct OpaqueTypeDecl<'tcx> {
2626
///
2727
/// winds up desugared to:
2828
///
29-
/// abstract type Foo<'x, T>: Trait<'x>
29+
/// abstract type Foo<'x, X>: Trait<'x>
3030
/// fn foo<'a, 'b, T>() -> Foo<'a, T>
3131
///
3232
/// then `substs` would be `['a, T]`.

src/librustc/ty/context.rs

+12-1
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,17 @@ impl<'a, V> LocalTableInContextMut<'a, V> {
317317
}
318318
}
319319

320+
/// All information necessary to validate and reveal an `impl Trait` or `existential Type`
321+
#[derive(RustcEncodable, RustcDecodable, Debug)]
322+
pub struct ResolvedOpaqueTy<'tcx> {
323+
/// The revealed type as seen by this function.
324+
pub concrete_type: Ty<'tcx>,
325+
/// Generic parameters on the opaque type as passed by this function.
326+
/// For `existential type Foo<A, B>; fn foo<T, U>() -> Foo<T, U> { .. }` this is `[T, U]`, not
327+
/// `[A, B]`
328+
pub substs: &'tcx Substs<'tcx>,
329+
}
330+
320331
#[derive(RustcEncodable, RustcDecodable, Debug)]
321332
pub struct TypeckTables<'tcx> {
322333
/// The HirId::owner all ItemLocalIds in this table are relative to.
@@ -419,7 +430,7 @@ pub struct TypeckTables<'tcx> {
419430

420431
/// All the existential types that are restricted to concrete types
421432
/// by this function
422-
pub concrete_existential_types: FxHashMap<DefId, Ty<'tcx>>,
433+
pub concrete_existential_types: FxHashMap<DefId, ResolvedOpaqueTy<'tcx>>,
423434

424435
/// Given the closure ID this map provides the list of UpvarIDs used by it.
425436
/// The upvarID contains the HIR node ID and it also contains the full path

src/librustc/ty/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ pub use self::context::{TyCtxt, FreeRegionInfo, GlobalArenas, AllArenas, tls, ke
7474
pub use self::context::{Lift, TypeckTables, CtxtInterners};
7575
pub use self::context::{
7676
UserTypeAnnotationIndex, UserType, CanonicalUserType,
77-
CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations,
77+
CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations, ResolvedOpaqueTy,
7878
};
7979

8080
pub use self::instance::{Instance, InstanceDef};

src/librustc_typeck/check/mod.rs

-3
Original file line numberDiff line numberDiff line change
@@ -1386,10 +1386,7 @@ pub fn check_item_type<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, it: &'tcx hir::Ite
13861386
}
13871387
hir::ItemKind::Existential(..) => {
13881388
let def_id = tcx.hir().local_def_id(it.id);
1389-
let pty_ty = tcx.type_of(def_id);
1390-
let generics = tcx.generics_of(def_id);
13911389

1392-
check_bounds_are_used(tcx, &generics, pty_ty);
13931390
let substs = Substs::identity_for_item(tcx, def_id);
13941391
check_opaque(tcx, def_id, substs, it.span);
13951392
}

src/librustc_typeck/check/writeback.rs

+11-3
Original file line numberDiff line numberDiff line change
@@ -560,21 +560,29 @@ impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> {
560560
if def_id == defin_ty_def_id {
561561
// Concrete type resolved to the existential type itself
562562
// Force a cycle error
563+
// FIXME(oli-obk): we could just not insert it into `concrete_existential_types`
564+
// which simply would make this use not a defining use.
563565
self.tcx().at(span).type_of(defin_ty_def_id);
564566
}
565567
}
566568

569+
let new = ty::ResolvedOpaqueTy {
570+
concrete_type: definition_ty,
571+
substs: self.tcx().lift_to_global(&opaque_defn.substs).unwrap(),
572+
};
573+
567574
let old = self.tables
568575
.concrete_existential_types
569-
.insert(def_id, definition_ty);
576+
.insert(def_id, new);
570577
if let Some(old) = old {
571-
if old != definition_ty {
578+
if old.concrete_type != definition_ty || old.substs != opaque_defn.substs {
572579
span_bug!(
573580
span,
574581
"visit_opaque_types tried to write \
575-
different types for the same existential type: {:?}, {:?}, {:?}",
582+
different types for the same existential type: {:?}, {:?}, {:?}, {:?}",
576583
def_id,
577584
definition_ty,
585+
opaque_defn,
578586
old,
579587
);
580588
}

src/librustc_typeck/collect.rs

+101-11
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,10 @@ use crate::middle::resolve_lifetime as rl;
2323
use crate::middle::weak_lang_items;
2424
use rustc::mir::mono::Linkage;
2525
use rustc::ty::query::Providers;
26-
use rustc::ty::subst::Substs;
26+
use rustc::ty::subst::{Subst, Substs};
2727
use rustc::ty::util::Discr;
2828
use rustc::ty::util::IntTypeExt;
29+
use rustc::ty::subst::UnpackedKind;
2930
use rustc::ty::{self, AdtKind, ToPolyTraitRef, Ty, TyCtxt};
3031
use rustc::ty::{ReprOptions, ToPredicate};
3132
use rustc::util::captures::Captures;
@@ -1211,7 +1212,7 @@ fn type_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> Ty<'tcx> {
12111212
tcx.typeck_tables_of(owner)
12121213
.concrete_existential_types
12131214
.get(&def_id)
1214-
.cloned()
1215+
.map(|opaque| opaque.concrete_type)
12151216
.unwrap_or_else(|| {
12161217
// This can occur if some error in the
12171218
// owner fn prevented us from populating
@@ -1343,7 +1344,13 @@ fn find_existential_constraints<'a, 'tcx>(
13431344
struct ConstraintLocator<'a, 'tcx: 'a> {
13441345
tcx: TyCtxt<'a, 'tcx, 'tcx>,
13451346
def_id: DefId,
1346-
found: Option<(Span, ty::Ty<'tcx>)>,
1347+
// First found type span, actual type, mapping from the existential type's generic
1348+
// parameters to the concrete type's generic parameters
1349+
//
1350+
// The mapping is an index for each use site of a generic parameter in the concrete type
1351+
//
1352+
// The indices index into the generic parameters on the existential type.
1353+
found: Option<(Span, ty::Ty<'tcx>, Vec<usize>)>,
13471354
}
13481355

13491356
impl<'a, 'tcx> ConstraintLocator<'a, 'tcx> {
@@ -1358,23 +1365,106 @@ fn find_existential_constraints<'a, 'tcx>(
13581365
.tcx
13591366
.typeck_tables_of(def_id)
13601367
.concrete_existential_types
1361-
.get(&self.def_id)
1362-
.cloned();
1363-
if let Some(ty) = ty {
1368+
.get(&self.def_id);
1369+
if let Some(ty::ResolvedOpaqueTy { concrete_type, substs }) = ty {
13641370
// FIXME(oli-obk): trace the actual span from inference to improve errors
13651371
let span = self.tcx.def_span(def_id);
1366-
if let Some((prev_span, prev_ty)) = self.found {
1367-
if ty != prev_ty {
1372+
// used to quickly look up the position of a generic parameter
1373+
let mut index_map: FxHashMap<ty::ParamTy, usize> = FxHashMap::default();
1374+
// skip binder is ok, since we only use this to find generic parameters and their
1375+
// positions.
1376+
for (idx, subst) in substs.iter().enumerate() {
1377+
if let UnpackedKind::Type(ty) = subst.unpack() {
1378+
if let ty::Param(p) = ty.sty {
1379+
if index_map.insert(p, idx).is_some() {
1380+
// there was already an entry for `p`, meaning a generic parameter
1381+
// was used twice
1382+
self.tcx.sess.span_err(
1383+
span,
1384+
&format!("defining existential type use restricts existential \
1385+
type by using the generic parameter `{}` twice", p.name),
1386+
);
1387+
return;
1388+
}
1389+
} else {
1390+
self.tcx.sess.delay_span_bug(
1391+
span,
1392+
&format!(
1393+
"non-defining exist ty use in defining scope: {:?}, {:?}",
1394+
concrete_type, substs,
1395+
),
1396+
);
1397+
}
1398+
}
1399+
}
1400+
// compute the index within the existential type for each generic parameter used in
1401+
// the concrete type
1402+
let indices = concrete_type
1403+
.subst(self.tcx, substs)
1404+
.walk()
1405+
.filter_map(|t| match &t.sty {
1406+
ty::Param(p) => Some(*index_map.get(p).unwrap()),
1407+
_ => None,
1408+
}).collect();
1409+
let is_param = |ty: ty::Ty| match ty.sty {
1410+
ty::Param(_) => true,
1411+
_ => false,
1412+
};
1413+
if !substs.types().all(is_param) {
1414+
self.tcx.sess.span_err(
1415+
span,
1416+
"defining existential type use does not fully define existential type",
1417+
);
1418+
} else if let Some((prev_span, prev_ty, ref prev_indices)) = self.found {
1419+
let mut ty = concrete_type.walk().fuse();
1420+
let mut p_ty = prev_ty.walk().fuse();
1421+
let iter_eq = (&mut ty).zip(&mut p_ty).all(|(t, p)| match (&t.sty, &p.sty) {
1422+
// type parameters are equal to any other type parameter for the purpose of
1423+
// concrete type equality, as it is possible to obtain the same type just
1424+
// by passing matching parameters to a function.
1425+
(ty::Param(_), ty::Param(_)) => true,
1426+
_ => t == p,
1427+
});
1428+
if !iter_eq || ty.next().is_some() || p_ty.next().is_some() {
13681429
// found different concrete types for the existential type
13691430
let mut err = self.tcx.sess.struct_span_err(
13701431
span,
1371-
"defining existential type use differs from previous",
1432+
"concrete type differs from previous defining existential type use",
1433+
);
1434+
err.span_label(
1435+
span,
1436+
format!("expected `{}`, got `{}`", prev_ty, concrete_type),
1437+
);
1438+
err.span_note(prev_span, "previous use here");
1439+
err.emit();
1440+
} else if indices != *prev_indices {
1441+
// found "same" concrete types, but the generic parameter order differs
1442+
let mut err = self.tcx.sess.struct_span_err(
1443+
span,
1444+
"concrete type's generic parameters differ from previous defining use",
13721445
);
1446+
use std::fmt::Write;
1447+
let mut s = String::new();
1448+
write!(s, "expected [").unwrap();
1449+
let list = |s: &mut String, indices: &Vec<usize>| {
1450+
let mut indices = indices.iter().cloned();
1451+
if let Some(first) = indices.next() {
1452+
write!(s, "`{}`", substs[first]).unwrap();
1453+
for i in indices {
1454+
write!(s, ", `{}`", substs[i]).unwrap();
1455+
}
1456+
}
1457+
};
1458+
list(&mut s, prev_indices);
1459+
write!(s, "], got [").unwrap();
1460+
list(&mut s, &indices);
1461+
write!(s, "]").unwrap();
1462+
err.span_label(span, s);
13731463
err.span_note(prev_span, "previous use here");
13741464
err.emit();
13751465
}
13761466
} else {
1377-
self.found = Some((span, ty));
1467+
self.found = Some((span, concrete_type, indices));
13781468
}
13791469
}
13801470
}
@@ -1433,7 +1523,7 @@ fn find_existential_constraints<'a, 'tcx>(
14331523
}
14341524

14351525
match locator.found {
1436-
Some((_, ty)) => ty,
1526+
Some((_, ty, _)) => ty,
14371527
None => {
14381528
let span = tcx.def_span(def_id);
14391529
tcx.sess.span_err(span, "could not find defining uses");

src/test/ui/existential_types/bound_reduction2.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,12 @@ trait TraitWithAssoc {
88
}
99

1010
existential type Foo<V>: Trait<V>;
11+
//~^ ERROR could not find defining uses
1112

1213
trait Trait<U> {}
1314

1415
impl<W> Trait<W> for () {}
1516

16-
fn foo_desugared<T: TraitWithAssoc>(_: T) -> Foo<T::Assoc> { //~ ERROR non-defining
17+
fn foo_desugared<T: TraitWithAssoc>(_: T) -> Foo<T::Assoc> { //~ ERROR does not fully define
1718
()
1819
}
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
1-
error: non-defining existential type use in defining scope
2-
--> $DIR/bound_reduction2.rs:16:1
1+
error: defining existential type use does not fully define existential type
2+
--> $DIR/bound_reduction2.rs:17:1
33
|
4-
LL | / fn foo_desugared<T: TraitWithAssoc>(_: T) -> Foo<T::Assoc> { //~ ERROR non-defining
4+
LL | / fn foo_desugared<T: TraitWithAssoc>(_: T) -> Foo<T::Assoc> { //~ ERROR does not fully define
55
LL | | ()
66
LL | | }
77
| |_^
8-
|
9-
note: used non-generic type <T as TraitWithAssoc>::Assoc for generic parameter
10-
--> $DIR/bound_reduction2.rs:10:22
8+
9+
error: could not find defining uses
10+
--> $DIR/bound_reduction2.rs:10:1
1111
|
1212
LL | existential type Foo<V>: Trait<V>;
13-
| ^
13+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1414

15-
error: aborting due to previous error
15+
error: aborting due to 2 previous errors
1616

src/test/ui/existential_types/different_defining_uses.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,6 @@ fn foo() -> Foo {
99
""
1010
}
1111

12-
fn bar() -> Foo { //~ ERROR defining existential type use differs from previous
12+
fn bar() -> Foo { //~ ERROR concrete type differs from previous
1313
42i32
1414
}

src/test/ui/existential_types/different_defining_uses.stderr

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
error: defining existential type use differs from previous
1+
error: concrete type differs from previous defining existential type use
22
--> $DIR/different_defining_uses.rs:12:1
33
|
4-
LL | / fn bar() -> Foo { //~ ERROR defining existential type use differs from previous
4+
LL | / fn bar() -> Foo { //~ ERROR concrete type differs from previous
55
LL | | 42i32
66
LL | | }
7-
| |_^
7+
| |_^ expected `&'static str`, got `i32`
88
|
99
note: previous use here
1010
--> $DIR/different_defining_uses.rs:8:1

src/test/ui/existential_types/different_defining_uses_never_type.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@ fn foo() -> Foo {
99
""
1010
}
1111

12-
fn bar() -> Foo { //~ ERROR defining existential type use differs from previous
12+
fn bar() -> Foo { //~ ERROR concrete type differs from previous
1313
panic!()
1414
}
1515

16-
fn boo() -> Foo { //~ ERROR defining existential type use differs from previous
16+
fn boo() -> Foo { //~ ERROR concrete type differs from previous
1717
loop {}
1818
}

src/test/ui/existential_types/different_defining_uses_never_type.stderr

+6-6
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
error: defining existential type use differs from previous
1+
error: concrete type differs from previous defining existential type use
22
--> $DIR/different_defining_uses_never_type.rs:12:1
33
|
4-
LL | / fn bar() -> Foo { //~ ERROR defining existential type use differs from previous
4+
LL | / fn bar() -> Foo { //~ ERROR concrete type differs from previous
55
LL | | panic!()
66
LL | | }
7-
| |_^
7+
| |_^ expected `&'static str`, got `()`
88
|
99
note: previous use here
1010
--> $DIR/different_defining_uses_never_type.rs:8:1
@@ -14,13 +14,13 @@ LL | | ""
1414
LL | | }
1515
| |_^
1616

17-
error: defining existential type use differs from previous
17+
error: concrete type differs from previous defining existential type use
1818
--> $DIR/different_defining_uses_never_type.rs:16:1
1919
|
20-
LL | / fn boo() -> Foo { //~ ERROR defining existential type use differs from previous
20+
LL | / fn boo() -> Foo { //~ ERROR concrete type differs from previous
2121
LL | | loop {}
2222
LL | | }
23-
| |_^
23+
| |_^ expected `&'static str`, got `()`
2424
|
2525
note: previous use here
2626
--> $DIR/different_defining_uses_never_type.rs:8:1

src/test/ui/existential_types/generic_different_defining_uses.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,6 @@ fn my_iter<T>(t: T) -> MyIter<T> {
88
std::iter::once(t)
99
}
1010

11-
fn my_iter2<T>(t: T) -> MyIter<T> { //~ ERROR defining existential type use differs from previous
11+
fn my_iter2<T>(t: T) -> MyIter<T> { //~ ERROR concrete type differs from previous
1212
Some(t).into_iter()
1313
}

src/test/ui/existential_types/generic_different_defining_uses.stderr

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
error: defining existential type use differs from previous
1+
error: concrete type differs from previous defining existential type use
22
--> $DIR/generic_different_defining_uses.rs:11:1
33
|
4-
LL | / fn my_iter2<T>(t: T) -> MyIter<T> { //~ ERROR defining existential type use differs from previous
4+
LL | / fn my_iter2<T>(t: T) -> MyIter<T> { //~ ERROR concrete type differs from previous
55
LL | | Some(t).into_iter()
66
LL | | }
7-
| |_^
7+
| |_^ expected `std::iter::Once<T>`, got `std::option::IntoIter<T>`
88
|
99
note: previous use here
1010
--> $DIR/generic_different_defining_uses.rs:7:1

0 commit comments

Comments
 (0)