Skip to content

Commit 5e8f1e8

Browse files
Check that RPITs constrained by a recursive call in a closure are compatible
1 parent 90939e6 commit 5e8f1e8

File tree

5 files changed

+139
-50
lines changed

5 files changed

+139
-50
lines changed

compiler/rustc_typeck/src/collect/type_of.rs

+119-29
Original file line numberDiff line numberDiff line change
@@ -335,37 +335,11 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> {
335335
tcx.mk_adt(def, substs)
336336
}
337337
ItemKind::OpaqueTy(OpaqueTy { origin: hir::OpaqueTyOrigin::TyAlias, .. }) => {
338-
find_opaque_ty_constraints(tcx, def_id)
338+
find_opaque_ty_constraints_for_tait(tcx, def_id)
339339
}
340340
// Opaque types desugared from `impl Trait`.
341341
ItemKind::OpaqueTy(OpaqueTy { origin: hir::OpaqueTyOrigin::FnReturn(owner) | hir::OpaqueTyOrigin::AsyncFn(owner), .. }) => {
342-
let concrete_ty = tcx
343-
.mir_borrowck(owner)
344-
.concrete_opaque_types
345-
.get(&def_id)
346-
.copied()
347-
.map(|concrete| concrete.ty)
348-
.unwrap_or_else(|| {
349-
let table = tcx.typeck(owner);
350-
if let Some(_) = table.tainted_by_errors {
351-
// Some error in the
352-
// owner fn prevented us from populating
353-
// the `concrete_opaque_types` table.
354-
tcx.ty_error()
355-
} else {
356-
table.concrete_opaque_types.get(&def_id).copied().unwrap_or_else(|| {
357-
// We failed to resolve the opaque type or it
358-
// resolves to itself. We interpret this as the
359-
// no values of the hidden type ever being constructed,
360-
// so we can just make the hidden type be `!`.
361-
// For backwards compatibility reasons, we fall back to
362-
// `()` until we the diverging default is changed.
363-
Some(tcx.mk_diverging_default())
364-
}).expect("RPIT always have a hidden type from typeck")
365-
}
366-
});
367-
debug!("concrete_ty = {:?}", concrete_ty);
368-
concrete_ty
342+
find_opaque_ty_constraints_for_rpit(tcx, def_id, owner)
369343
}
370344
ItemKind::Trait(..)
371345
| ItemKind::TraitAlias(..)
@@ -519,7 +493,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> {
519493
/// fn b<T>() -> Foo<T, u32> { .. }
520494
/// ```
521495
///
522-
fn find_opaque_ty_constraints(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Ty<'_> {
496+
fn find_opaque_ty_constraints_for_tait(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Ty<'_> {
523497
use rustc_hir::{Expr, ImplItem, Item, TraitItem};
524498

525499
struct ConstraintLocator<'tcx> {
@@ -660,6 +634,122 @@ fn find_opaque_ty_constraints(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Ty<'_> {
660634
}
661635
}
662636

637+
fn find_opaque_ty_constraints_for_rpit(
638+
tcx: TyCtxt<'_>,
639+
def_id: LocalDefId,
640+
owner_def_id: LocalDefId,
641+
) -> Ty<'_> {
642+
use rustc_hir::{Expr, ImplItem, Item, TraitItem};
643+
644+
struct ConstraintChecker<'tcx> {
645+
tcx: TyCtxt<'tcx>,
646+
647+
/// def_id of the opaque type whose defining uses are being checked
648+
def_id: LocalDefId,
649+
650+
found: ty::OpaqueHiddenType<'tcx>,
651+
}
652+
653+
impl ConstraintChecker<'_> {
654+
#[instrument(skip(self), level = "debug")]
655+
fn check(&self, def_id: LocalDefId) {
656+
// Use borrowck to get the type with unerased regions.
657+
let concrete_opaque_types = &self.tcx.mir_borrowck(def_id).concrete_opaque_types;
658+
debug!(?concrete_opaque_types);
659+
for &(def_id, concrete_type) in concrete_opaque_types {
660+
if def_id != self.def_id {
661+
// Ignore constraints for other opaque types.
662+
continue;
663+
}
664+
665+
debug!(?concrete_type, "found constraint");
666+
667+
if concrete_type.ty != self.found.ty
668+
&& !(concrete_type, self.found).references_error()
669+
{
670+
self.found.report_mismatch(&concrete_type, self.tcx);
671+
}
672+
}
673+
}
674+
}
675+
676+
impl<'tcx> intravisit::Visitor<'tcx> for ConstraintChecker<'tcx> {
677+
type NestedFilter = nested_filter::OnlyBodies;
678+
679+
fn nested_visit_map(&mut self) -> Self::Map {
680+
self.tcx.hir()
681+
}
682+
fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) {
683+
if let hir::ExprKind::Closure { .. } = ex.kind {
684+
let def_id = self.tcx.hir().local_def_id(ex.hir_id);
685+
self.check(def_id);
686+
}
687+
intravisit::walk_expr(self, ex);
688+
}
689+
fn visit_item(&mut self, it: &'tcx Item<'tcx>) {
690+
trace!(?it.def_id);
691+
// The opaque type itself or its children are not within its reveal scope.
692+
if it.def_id != self.def_id {
693+
self.check(it.def_id);
694+
intravisit::walk_item(self, it);
695+
}
696+
}
697+
fn visit_impl_item(&mut self, it: &'tcx ImplItem<'tcx>) {
698+
trace!(?it.def_id);
699+
// The opaque type itself or its children are not within its reveal scope.
700+
if it.def_id != self.def_id {
701+
self.check(it.def_id);
702+
intravisit::walk_impl_item(self, it);
703+
}
704+
}
705+
fn visit_trait_item(&mut self, it: &'tcx TraitItem<'tcx>) {
706+
trace!(?it.def_id);
707+
self.check(it.def_id);
708+
intravisit::walk_trait_item(self, it);
709+
}
710+
}
711+
712+
let concrete = tcx.mir_borrowck(owner_def_id).concrete_opaque_types.get(&def_id).copied();
713+
714+
if let Some(concrete) = concrete {
715+
let scope = tcx.hir().local_def_id_to_hir_id(owner_def_id);
716+
debug!(?scope);
717+
let mut locator = ConstraintChecker { def_id: def_id, tcx, found: concrete };
718+
719+
match tcx.hir().get(scope) {
720+
Node::Item(it) => intravisit::walk_item(&mut locator, it),
721+
Node::ImplItem(it) => intravisit::walk_impl_item(&mut locator, it),
722+
Node::TraitItem(it) => intravisit::walk_trait_item(&mut locator, it),
723+
other => bug!("{:?} is not a valid scope for an opaque type item", other),
724+
}
725+
}
726+
727+
concrete.map(|concrete| concrete.ty).unwrap_or_else(|| {
728+
let table = tcx.typeck(owner_def_id);
729+
if let Some(_) = table.tainted_by_errors {
730+
// Some error in the
731+
// owner fn prevented us from populating
732+
// the `concrete_opaque_types` table.
733+
tcx.ty_error()
734+
} else {
735+
table
736+
.concrete_opaque_types
737+
.get(&def_id)
738+
.copied()
739+
.unwrap_or_else(|| {
740+
// We failed to resolve the opaque type or it
741+
// resolves to itself. We interpret this as the
742+
// no values of the hidden type ever being constructed,
743+
// so we can just make the hidden type be `!`.
744+
// For backwards compatibility reasons, we fall back to
745+
// `()` until we the diverging default is changed.
746+
Some(tcx.mk_diverging_default())
747+
})
748+
.expect("RPIT always have a hidden type from typeck")
749+
}
750+
})
751+
}
752+
663753
fn infer_placeholder_type<'a>(
664754
tcx: TyCtxt<'a>,
665755
def_id: LocalDefId,

src/test/ui/impl-trait/issue-99073-2.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ fn main() {
77
fn test<T: Display>(t: T, recurse: bool) -> impl Display {
88
let f = || {
99
let i: u32 = test::<i32>(-1, false);
10-
//~^ ERROR mismatched types
10+
//~^ ERROR concrete type differs from previous defining opaque type use
1111
println!("{i}");
1212
};
1313
if recurse {
+7-8
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
1-
error[E0308]: mismatched types
1+
error: concrete type differs from previous defining opaque type use
22
--> $DIR/issue-99073-2.rs:9:22
33
|
4-
LL | fn test<T: Display>(t: T, recurse: bool) -> impl Display {
5-
| ------------ the expected opaque type
6-
LL | let f = || {
74
LL | let i: u32 = test::<i32>(-1, false);
8-
| ^^^^^^^^^^^^^^^^^^^^^^ types differ
5+
| ^^^^^^^^^^^^^^^^^^^^^^ expected `T`, got `u32`
96
|
10-
= note: expected opaque type `impl std::fmt::Display`
11-
found type `u32`
7+
note: previous use here
8+
--> $DIR/issue-99073-2.rs:16:5
9+
|
10+
LL | t
11+
| ^
1212

1313
error: aborting due to previous error
1414

15-
For more information about this error, try `rustc --explain E0308`.

src/test/ui/impl-trait/issue-99073.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
fn main() {
2-
let _ = fix(|_: &dyn Fn()| {});
2+
let _ = fix(|_: &dyn Fn()| {});
33
}
44

55
fn fix<F: Fn(G), G: Fn()>(f: F) -> impl Fn() {
6-
move || f(fix(&f))
7-
//~^ ERROR mismatched types
6+
move || f(fix(&f))
7+
//~^ ERROR concrete type differs from previous defining opaque type use
88
}
+9-9
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
1-
error[E0308]: mismatched types
2-
--> $DIR/issue-99073.rs:6:13
1+
error: concrete type differs from previous defining opaque type use
2+
--> $DIR/issue-99073.rs:6:11
33
|
4-
LL | fn fix<F: Fn(G), G: Fn()>(f: F) -> impl Fn() {
5-
| --------- the expected opaque type
6-
LL | move || f(fix(&f))
7-
| ^^^^^^^^^^ types differ
4+
LL | move || f(fix(&f))
5+
| ^^^^^^^^^^ expected `[closure@$DIR/issue-99073.rs:6:3: 6:10]`, got `G`
86
|
9-
= note: expected opaque type `impl Fn()`
10-
found type parameter `G`
7+
note: previous use here
8+
--> $DIR/issue-99073.rs:6:3
9+
|
10+
LL | move || f(fix(&f))
11+
| ^^^^^^^^^^^^^^^^^^
1112

1213
error: aborting due to previous error
1314

14-
For more information about this error, try `rustc --explain E0308`.

0 commit comments

Comments
 (0)