Skip to content

Commit 09bc67b

Browse files
committed
Auto merge of #121679 - lcnr:opaque-wf-check-2, r=oli-obk
stricter hidden type wf-check [based on #115008] Original work by `@aliemjay` in #115008. A huge thanks to them for originally figuring out this approach ❤️ Fixes #114728 Fixes #114572 Instead of adding the `WellFormed` obligations when relating opaque types, we now always emit such an obligation when defining the hidden type. This causes nested opaque types which aren't wf to error, see the comment below for the described impact. I believe this change to be desirable as it significantly reduces complexity by removing special-cases. It also caused an issue with RPITIT: in defaulted trait methods, we add a `Projection(synthetic_assoc, rpit_of_trait_method)` clause to the `param_env`. This clause is not added to the `ParamEnv` of the nested coroutines. This caused a normalization failure in `fn check_coroutine_obligations` with the new solver. I fixed that by using the env of the typeck root instead. r? `@oli-obk`
2 parents 8039906 + af0d508 commit 09bc67b

21 files changed

+315
-138
lines changed

compiler/rustc_hir_analysis/src/check/check.rs

+24-32
Original file line numberDiff line numberDiff line change
@@ -402,39 +402,29 @@ fn check_opaque_meets_bounds<'tcx>(
402402
let guar = infcx.err_ctxt().report_fulfillment_errors(errors);
403403
return Err(guar);
404404
}
405-
match origin {
406-
// Checked when type checking the function containing them.
407-
hir::OpaqueTyOrigin::FnReturn(..) | hir::OpaqueTyOrigin::AsyncFn(..) => {
408-
// HACK: this should also fall through to the hidden type check below, but the original
409-
// implementation had a bug where equivalent lifetimes are not identical. This caused us
410-
// to reject existing stable code that is otherwise completely fine. The real fix is to
411-
// compare the hidden types via our type equivalence/relation infra instead of doing an
412-
// identity check.
413-
let _ = infcx.take_opaque_types();
414-
return Ok(());
415-
}
416-
// Nested opaque types occur only in associated types:
417-
// ` type Opaque<T> = impl Trait<&'static T, AssocTy = impl Nested>; `
418-
// They can only be referenced as `<Opaque<T> as Trait<&'static T>>::AssocTy`.
419-
// We don't have to check them here because their well-formedness follows from the WF of
420-
// the projection input types in the defining- and use-sites.
421-
hir::OpaqueTyOrigin::TyAlias { .. }
422-
if tcx.def_kind(tcx.parent(def_id.to_def_id())) == DefKind::OpaqueTy => {}
423-
// Can have different predicates to their defining use
424-
hir::OpaqueTyOrigin::TyAlias { .. } => {
425-
let wf_tys = ocx.assumed_wf_types_and_report_errors(param_env, def_id)?;
426-
let implied_bounds = infcx.implied_bounds_tys(param_env, def_id, &wf_tys);
427-
let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds);
428-
ocx.resolve_regions_and_report_errors(defining_use_anchor, &outlives_env)?;
405+
406+
let wf_tys = ocx.assumed_wf_types_and_report_errors(param_env, defining_use_anchor)?;
407+
let implied_bounds = infcx.implied_bounds_tys(param_env, def_id, &wf_tys);
408+
let outlives_env = OutlivesEnvironment::with_bounds(param_env, implied_bounds);
409+
ocx.resolve_regions_and_report_errors(defining_use_anchor, &outlives_env)?;
410+
411+
if let hir::OpaqueTyOrigin::FnReturn(..) | hir::OpaqueTyOrigin::AsyncFn(..) = origin {
412+
// HACK: this should also fall through to the hidden type check below, but the original
413+
// implementation had a bug where equivalent lifetimes are not identical. This caused us
414+
// to reject existing stable code that is otherwise completely fine. The real fix is to
415+
// compare the hidden types via our type equivalence/relation infra instead of doing an
416+
// identity check.
417+
let _ = infcx.take_opaque_types();
418+
Ok(())
419+
} else {
420+
// Check that any hidden types found during wf checking match the hidden types that `type_of` sees.
421+
for (mut key, mut ty) in infcx.take_opaque_types() {
422+
ty.hidden_type.ty = infcx.resolve_vars_if_possible(ty.hidden_type.ty);
423+
key = infcx.resolve_vars_if_possible(key);
424+
sanity_check_found_hidden_type(tcx, key, ty.hidden_type)?;
429425
}
426+
Ok(())
430427
}
431-
// Check that any hidden types found during wf checking match the hidden types that `type_of` sees.
432-
for (mut key, mut ty) in infcx.take_opaque_types() {
433-
ty.hidden_type.ty = infcx.resolve_vars_if_possible(ty.hidden_type.ty);
434-
key = infcx.resolve_vars_if_possible(key);
435-
sanity_check_found_hidden_type(tcx, key, ty.hidden_type)?;
436-
}
437-
Ok(())
438428
}
439429

440430
fn sanity_check_found_hidden_type<'tcx>(
@@ -1549,14 +1539,16 @@ fn opaque_type_cycle_error(
15491539
err.emit()
15501540
}
15511541

1542+
// FIXME(@lcnr): This should not be computed per coroutine, but instead once for
1543+
// each typeck root.
15521544
pub(super) fn check_coroutine_obligations(
15531545
tcx: TyCtxt<'_>,
15541546
def_id: LocalDefId,
15551547
) -> Result<(), ErrorGuaranteed> {
15561548
debug_assert!(tcx.is_coroutine(def_id.to_def_id()));
15571549

15581550
let typeck = tcx.typeck(def_id);
1559-
let param_env = tcx.param_env(def_id);
1551+
let param_env = tcx.param_env(typeck.hir_owner.def_id);
15601552

15611553
let coroutine_interior_predicates = &typeck.coroutine_interior_predicates[&def_id];
15621554
debug!(?coroutine_interior_predicates);

compiler/rustc_infer/src/infer/opaque_types.rs

+18-1
Original file line numberDiff line numberDiff line change
@@ -592,8 +592,25 @@ impl<'tcx> InferCtxt<'tcx> {
592592
obligations: &mut Vec<PredicateObligation<'tcx>>,
593593
) {
594594
let tcx = self.tcx;
595-
let item_bounds = tcx.explicit_item_bounds(def_id);
595+
// Require that the hidden type is well-formed. We have to
596+
// make sure we wf-check the hidden type to fix #114728.
597+
//
598+
// However, we don't check that all types are well-formed.
599+
// We only do so for types provided by the user or if they are
600+
// "used", e.g. for method selection.
601+
//
602+
// This means we never check the wf requirements of the hidden
603+
// type during MIR borrowck, causing us to infer the wrong
604+
// lifetime for its member constraints which then results in
605+
// unexpected region errors.
606+
obligations.push(traits::Obligation::new(
607+
tcx,
608+
cause.clone(),
609+
param_env,
610+
ty::ClauseKind::WellFormed(hidden_ty.into()),
611+
));
596612

613+
let item_bounds = tcx.explicit_item_bounds(def_id);
597614
for (predicate, _) in item_bounds.iter_instantiated_copied(tcx, args) {
598615
let predicate = predicate.fold_with(&mut BottomUpFolder {
599616
tcx,

compiler/rustc_ty_utils/src/implied_bounds.rs

+1-12
Original file line numberDiff line numberDiff line change
@@ -121,18 +121,7 @@ fn assumed_wf_types<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &'tcx [(Ty<'
121121
}
122122
}
123123
DefKind::AssocConst | DefKind::AssocTy => tcx.assumed_wf_types(tcx.local_parent(def_id)),
124-
DefKind::OpaqueTy => match tcx.def_kind(tcx.local_parent(def_id)) {
125-
DefKind::TyAlias => ty::List::empty(),
126-
DefKind::AssocTy => tcx.assumed_wf_types(tcx.local_parent(def_id)),
127-
// Nested opaque types only occur in associated types:
128-
// ` type Opaque<T> = impl Trait<&'static T, AssocTy = impl Nested>; `
129-
// assumed_wf_types should include those of `Opaque<T>`, `Opaque<T>` itself
130-
// and `&'static T`.
131-
DefKind::OpaqueTy => bug!("unimplemented implied bounds for nested opaque types"),
132-
def_kind => {
133-
bug!("unimplemented implied bounds for opaque types with parent {def_kind:?}")
134-
}
135-
},
124+
DefKind::OpaqueTy => bug!("implied bounds are not defined for opaques"),
136125
DefKind::Mod
137126
| DefKind::Struct
138127
| DefKind::Union

tests/ui/closures/issue-78720.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
fn server() -> impl {
2-
//~^ ERROR at least one trait must be specified
2+
//~^ ERROR at least one trait must be specified
3+
//~| ERROR type annotations needed
34
().map2(|| "")
4-
//~^ ERROR type annotations needed
55
}
66

77
trait FilterBase2 {

tests/ui/closures/issue-78720.stderr

+3-3
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,10 @@ LL | struct Map2<Segment2, F> {
2323
| +++
2424

2525
error[E0282]: type annotations needed
26-
--> $DIR/issue-78720.rs:3:5
26+
--> $DIR/issue-78720.rs:1:16
2727
|
28-
LL | ().map2(|| "")
29-
| ^^^^^^^^^^^^^^ cannot infer type
28+
LL | fn server() -> impl {
29+
| ^^^^ cannot infer type
3030

3131
error[E0308]: mismatched types
3232
--> $DIR/issue-78720.rs:8:39

tests/ui/impl-trait/issues/issue-86800.rs

+3-6
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,6 @@
11
#![feature(type_alias_impl_trait)]
22

33
//@ edition:2021
4-
//@ compile-flags:-Z treat-err-as-bug=2
5-
//@ error-pattern: due to `-Z treat-err-as-bug=2
6-
//@ failure-status:101
7-
//@ normalize-stderr-test ".*note: .*\n\n" -> ""
8-
//@ normalize-stderr-test "thread 'rustc' panicked.*:\n.*\n" -> ""
9-
//@ rustc-env:RUST_BACKTRACE=0
104

115
use std::future::Future;
126

@@ -29,6 +23,7 @@ struct Context {
2923
type TransactionResult<O> = Result<O, ()>;
3024

3125
type TransactionFuture<'__, O> = impl '__ + Future<Output = TransactionResult<O>>;
26+
//~^ ERROR unconstrained opaque type
3227

3328
fn execute_transaction_fut<'f, F, O>(
3429
f: F,
@@ -37,13 +32,15 @@ where
3732
F: FnOnce(&mut dyn Transaction) -> TransactionFuture<'_, O> + 'f
3833
{
3934
f
35+
//~^ ERROR expected generic lifetime parameter, found `'_`
4036
}
4137

4238
impl Context {
4339
async fn do_transaction<O>(
4440
&self, f: impl FnOnce(&mut dyn Transaction) -> TransactionFuture<'_, O>
4541
) -> TransactionResult<O>
4642
{
43+
//~^ ERROR expected generic lifetime parameter, found `'_`
4744
let mut conn = Connection {};
4845
let mut transaction = TestTransaction { conn: &mut conn };
4946
f(&mut transaction).await
+21-8
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,34 @@
11
error: unconstrained opaque type
2-
--> $DIR/issue-86800.rs:31:34
2+
--> $DIR/issue-86800.rs:25:34
33
|
44
LL | type TransactionFuture<'__, O> = impl '__ + Future<Output = TransactionResult<O>>;
55
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
66
|
7-
error: internal compiler error[E0792]: expected generic lifetime parameter, found `'_`
8-
--> $DIR/issue-86800.rs:39:5
7+
= note: `TransactionFuture` must be used in combination with a concrete type within the same module
8+
9+
error[E0792]: expected generic lifetime parameter, found `'_`
10+
--> $DIR/issue-86800.rs:34:5
911
|
1012
LL | type TransactionFuture<'__, O> = impl '__ + Future<Output = TransactionResult<O>>;
1113
| --- this generic parameter must be used with a generic lifetime parameter
1214
...
1315
LL | f
1416
| ^
1517

16-
error: the compiler unexpectedly panicked. this is a bug.
18+
error[E0792]: expected generic lifetime parameter, found `'_`
19+
--> $DIR/issue-86800.rs:42:5
20+
|
21+
LL | type TransactionFuture<'__, O> = impl '__ + Future<Output = TransactionResult<O>>;
22+
| --- this generic parameter must be used with a generic lifetime parameter
23+
...
24+
LL | / {
25+
LL | |
26+
LL | | let mut conn = Connection {};
27+
LL | | let mut transaction = TestTransaction { conn: &mut conn };
28+
LL | | f(&mut transaction).await
29+
LL | | }
30+
| |_____^
31+
32+
error: aborting due to 3 previous errors
1733

18-
query stack during panic:
19-
#0 [mir_borrowck] borrow-checking `execute_transaction_fut`
20-
#1 [type_of_opaque] computing type of opaque `execute_transaction_fut::{opaque#0}`
21-
end of query stack
34+
For more information about this error, try `rustc --explain E0792`.

tests/ui/impl-trait/recursive-coroutine-boxed.next.stderr

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
error[E0282]: type annotations needed
2-
--> $DIR/recursive-coroutine-boxed.rs:10:23
2+
--> $DIR/recursive-coroutine-boxed.rs:11:23
33
|
44
LL | let mut gen = Box::pin(foo());
55
| ^^^^^^^^ cannot infer type of the type parameter `T` declared on the struct `Box`
6-
...
6+
LL |
77
LL | let mut r = gen.as_mut().resume(());
88
| ------ type must be known at this point
99
|
@@ -13,10 +13,10 @@ LL | let mut gen = Box::<T>::pin(foo());
1313
| +++++
1414

1515
error[E0282]: type annotations needed
16-
--> $DIR/recursive-coroutine-boxed.rs:10:32
16+
--> $DIR/recursive-coroutine-boxed.rs:8:13
1717
|
18-
LL | let mut gen = Box::pin(foo());
19-
| ^^^^^ cannot infer type for opaque type `impl Coroutine<Yield = (), Return = ()>`
18+
LL | fn foo() -> impl Coroutine<Yield = (), Return = ()> {
19+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type for opaque type `impl Coroutine<Yield = (), Return = ()>`
2020

2121
error: aborting due to 2 previous errors
2222

tests/ui/impl-trait/recursive-coroutine-boxed.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@
66
use std::ops::{Coroutine, CoroutineState};
77

88
fn foo() -> impl Coroutine<Yield = (), Return = ()> {
9+
//[next]~^ ERROR type annotations needed
910
|| {
1011
let mut gen = Box::pin(foo());
1112
//[next]~^ ERROR type annotations needed
12-
//[next]~| ERROR type annotations needed
1313
let mut r = gen.as_mut().resume(());
1414
while let CoroutineState::Yielded(v) = r {
1515
yield v;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//! Regression test for #114728.
2+
3+
trait Extend<'a, 'b> {
4+
fn extend(self, _: &'a str) -> &'b str;
5+
}
6+
7+
impl<'a, 'b> Extend<'a, 'b> for Option<&'b &'a ()> {
8+
fn extend(self, s: &'a str) -> &'b str {
9+
s
10+
}
11+
}
12+
13+
fn boom<'a, 'b>() -> impl Extend<'a, 'b> {
14+
None::<&'_ &'_ ()> //~ ERROR lifetime may not live long enough
15+
}
16+
17+
fn main() {
18+
let y = boom().extend(&String::from("temporary"));
19+
println!("{}", y);
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
error: lifetime may not live long enough
2+
--> $DIR/wf-check-hidden-type.rs:14:5
3+
|
4+
LL | fn boom<'a, 'b>() -> impl Extend<'a, 'b> {
5+
| -- -- lifetime `'b` defined here
6+
| |
7+
| lifetime `'a` defined here
8+
LL | None::<&'_ &'_ ()>
9+
| ^^^^^^^^^^^^^^^^^^ function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a`
10+
|
11+
= help: consider adding the following bound: `'a: 'b`
12+
13+
error: aborting due to 1 previous error
14+

tests/ui/lifetimes/issue-76168-hr-outlives-3.rs

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use std::future::Future;
66
async fn wrapper<F>(f: F)
77
//~^ ERROR: expected a `FnOnce(&'a mut i32)` closure, found `i32`
88
//~| ERROR: expected a `FnOnce(&'a mut i32)` closure, found `i32`
9+
//~| ERROR: expected a `FnOnce(&'a mut i32)` closure, found `i32`
910
where
1011
F:,
1112
for<'a> <i32 as FnOnce<(&'a mut i32,)>>::Output: Future<Output = ()> + 'a,

tests/ui/lifetimes/issue-76168-hr-outlives-3.stderr

+17-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ error[E0277]: expected a `FnOnce(&'a mut i32)` closure, found `i32`
44
LL | / async fn wrapper<F>(f: F)
55
LL | |
66
LL | |
7+
LL | |
78
LL | | where
89
LL | | F:,
910
LL | | for<'a> <i32 as FnOnce<(&'a mut i32,)>>::Output: Future<Output = ()> + 'a,
@@ -20,7 +21,21 @@ LL | async fn wrapper<F>(f: F)
2021
= help: the trait `for<'a> FnOnce<(&'a mut i32,)>` is not implemented for `i32`
2122

2223
error[E0277]: expected a `FnOnce(&'a mut i32)` closure, found `i32`
23-
--> $DIR/issue-76168-hr-outlives-3.rs:12:1
24+
--> $DIR/issue-76168-hr-outlives-3.rs:6:1
25+
|
26+
LL | / async fn wrapper<F>(f: F)
27+
LL | |
28+
LL | |
29+
LL | |
30+
LL | | where
31+
LL | | F:,
32+
LL | | for<'a> <i32 as FnOnce<(&'a mut i32,)>>::Output: Future<Output = ()> + 'a,
33+
| |__________________________________________________________________________^ expected an `FnOnce(&'a mut i32)` closure, found `i32`
34+
|
35+
= help: the trait `for<'a> FnOnce<(&'a mut i32,)>` is not implemented for `i32`
36+
37+
error[E0277]: expected a `FnOnce(&'a mut i32)` closure, found `i32`
38+
--> $DIR/issue-76168-hr-outlives-3.rs:13:1
2439
|
2540
LL | / {
2641
LL | |
@@ -31,6 +46,6 @@ LL | | }
3146
|
3247
= help: the trait `for<'a> FnOnce<(&'a mut i32,)>` is not implemented for `i32`
3348

34-
error: aborting due to 3 previous errors
49+
error: aborting due to 4 previous errors
3550

3651
For more information about this error, try `rustc --explain E0277`.

tests/ui/traits/non_lifetime_binders/type-match-with-late-bound.stderr

+15-1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,20 @@ help: consider adding an explicit lifetime bound
2121
LL | for<F> F: 'a, !1_"F": 'a
2222
| ~~~~~~~~~~~~
2323

24-
error: aborting due to 1 previous error; 1 warning emitted
24+
error[E0309]: the placeholder type `!2_"F"` may not live long enough
25+
--> $DIR/type-match-with-late-bound.rs:11:1
26+
|
27+
LL | async fn walk2<'a, T: 'a>(_: T)
28+
| -- the placeholder type `!2_"F"` must be valid for the lifetime `'a` as defined here...
29+
...
30+
LL | {}
31+
| ^^ ...so that the type `F` will meet its required lifetime bounds
32+
|
33+
help: consider adding an explicit lifetime bound
34+
|
35+
LL | for<F> F: 'a, !2_"F": 'a
36+
| ~~~~~~~~~~~~
37+
38+
error: aborting due to 2 previous errors; 1 warning emitted
2539

2640
For more information about this error, try `rustc --explain E0309`.

tests/ui/type-alias-impl-trait/issue-90400-2.stderr

+5-7
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,13 @@ error[E0277]: the trait bound `B: Bar` is not satisfied
22
--> $DIR/issue-90400-2.rs:25:9
33
|
44
LL | MyBaz(bar)
5-
| ^^^^^^^^^^ the trait `Bar` is not implemented for `B`, which is required by `MyBaz<B>: Baz`
5+
| ^^^^^^^^^^ the trait `Bar` is not implemented for `B`
66
|
7-
note: required for `MyBaz<B>` to implement `Baz`
8-
--> $DIR/issue-90400-2.rs:30:14
7+
note: required by a bound in `MyBaz`
8+
--> $DIR/issue-90400-2.rs:29:17
99
|
10-
LL | impl<B: Bar> Baz for MyBaz<B> {
11-
| --- ^^^ ^^^^^^^^
12-
| |
13-
| unsatisfied trait bound introduced here
10+
LL | struct MyBaz<B: Bar>(B);
11+
| ^^^ required by this bound in `MyBaz`
1412
help: consider restricting type parameter `B`
1513
|
1614
LL | type FooFn<B: Bar> = impl Baz;

0 commit comments

Comments
 (0)