Skip to content

Commit f3750e3

Browse files
authored
Rollup merge of rust-lang#63093 - Aaron1011:fix/existential-closure, r=cramertj
Properly check the defining scope of existential types Fixes rust-lang#52632 Existential types (soon to be 'impl trait' aliases) can either be delcared at a top-level crate/module scope, or within another item such as an fn. Previously, we were handling the second case incorrectly when recursively searching for defining usages - we would check children of the item, but not the item itself. This lead to us missing closures that consituted a defining use of the existential type, as their opaque type instantiations are stored in the TypeckTables of their parent function. This commit ensures that we explicitly visit the defining item itself, not just its children.
2 parents 4413068 + 3e98c3a commit f3750e3

File tree

4 files changed

+61
-9
lines changed

4 files changed

+61
-9
lines changed

src/librustc/infer/opaque_types/mod.rs

+9-6
Original file line numberDiff line numberDiff line change
@@ -1189,11 +1189,7 @@ pub fn may_define_existential_type(
11891189
opaque_hir_id: hir::HirId,
11901190
) -> bool {
11911191
let mut hir_id = tcx.hir().as_local_hir_id(def_id).unwrap();
1192-
trace!(
1193-
"may_define_existential_type(def={:?}, opaque_node={:?})",
1194-
tcx.hir().get(hir_id),
1195-
tcx.hir().get(opaque_hir_id)
1196-
);
1192+
11971193

11981194
// Named existential types can be defined by any siblings or children of siblings.
11991195
let scope = tcx.hir().get_defining_scope(opaque_hir_id).expect("could not get defining scope");
@@ -1202,5 +1198,12 @@ pub fn may_define_existential_type(
12021198
hir_id = tcx.hir().get_parent_item(hir_id);
12031199
}
12041200
// Syntactically, we are allowed to define the concrete type if:
1205-
hir_id == scope
1201+
let res = hir_id == scope;
1202+
trace!(
1203+
"may_define_existential_type(def={:?}, opaque_node={:?}) = {}",
1204+
tcx.hir().get(hir_id),
1205+
tcx.hir().get(opaque_hir_id),
1206+
res
1207+
);
1208+
res
12061209
}

src/librustc_typeck/collect.rs

+20-3
Original file line numberDiff line numberDiff line change
@@ -1664,6 +1664,7 @@ fn find_existential_constraints(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> {
16641664
intravisit::NestedVisitorMap::All(&self.tcx.hir())
16651665
}
16661666
fn visit_item(&mut self, it: &'tcx Item) {
1667+
debug!("find_existential_constraints: visiting {:?}", it);
16671668
let def_id = self.tcx.hir().local_def_id(it.hir_id);
16681669
// The existential type itself or its children are not within its reveal scope.
16691670
if def_id != self.def_id {
@@ -1672,6 +1673,7 @@ fn find_existential_constraints(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> {
16721673
}
16731674
}
16741675
fn visit_impl_item(&mut self, it: &'tcx ImplItem) {
1676+
debug!("find_existential_constraints: visiting {:?}", it);
16751677
let def_id = self.tcx.hir().local_def_id(it.hir_id);
16761678
// The existential type itself or its children are not within its reveal scope.
16771679
if def_id != self.def_id {
@@ -1680,6 +1682,7 @@ fn find_existential_constraints(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> {
16801682
}
16811683
}
16821684
fn visit_trait_item(&mut self, it: &'tcx TraitItem) {
1685+
debug!("find_existential_constraints: visiting {:?}", it);
16831686
let def_id = self.tcx.hir().local_def_id(it.hir_id);
16841687
self.check(def_id);
16851688
intravisit::walk_trait_item(self, it);
@@ -1703,9 +1706,23 @@ fn find_existential_constraints(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> {
17031706
} else {
17041707
debug!("find_existential_constraints: scope={:?}", tcx.hir().get(scope));
17051708
match tcx.hir().get(scope) {
1706-
Node::Item(ref it) => intravisit::walk_item(&mut locator, it),
1707-
Node::ImplItem(ref it) => intravisit::walk_impl_item(&mut locator, it),
1708-
Node::TraitItem(ref it) => intravisit::walk_trait_item(&mut locator, it),
1709+
// We explicitly call `visit_*` methods, instead of using `intravisit::walk_*` methods
1710+
// This allows our visitor to process the defining item itself, causing
1711+
// it to pick up any 'sibling' defining uses.
1712+
//
1713+
// For example, this code:
1714+
// ```
1715+
// fn foo() {
1716+
// existential type Blah: Debug;
1717+
// let my_closure = || -> Blah { true };
1718+
// }
1719+
// ```
1720+
//
1721+
// requires us to explicitly process `foo()` in order
1722+
// to notice the defining usage of `Blah`.
1723+
Node::Item(ref it) => locator.visit_item(it),
1724+
Node::ImplItem(ref it) => locator.visit_impl_item(it),
1725+
Node::TraitItem(ref it) => locator.visit_trait_item(it),
17091726
other => bug!(
17101727
"{:?} is not a valid scope for an existential type item",
17111728
other
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Checks to ensure that we properly detect when a closure constrains an existential type
2+
#![feature(existential_type)]
3+
4+
use std::fmt::Debug;
5+
6+
fn main() {
7+
existential type Existential: Debug;
8+
fn _unused() -> Existential { String::new() }
9+
//~^ ERROR: concrete type differs from previous defining existential type use
10+
let null = || -> Existential { 0 };
11+
println!("{:?}", null());
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
error: concrete type differs from previous defining existential type use
2+
--> $DIR/issue-52843-closure-constrain.rs:8:5
3+
|
4+
LL | fn _unused() -> Existential { String::new() }
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `i32`, got `std::string::String`
6+
|
7+
note: previous use here
8+
--> $DIR/issue-52843-closure-constrain.rs:6:1
9+
|
10+
LL | / fn main() {
11+
LL | | existential type Existential: Debug;
12+
LL | | fn _unused() -> Existential { String::new() }
13+
LL | |
14+
LL | | let null = || -> Existential { 0 };
15+
LL | | println!("{:?}", null());
16+
LL | | }
17+
| |_^
18+
19+
error: aborting due to previous error
20+

0 commit comments

Comments
 (0)