Skip to content

Commit 1659600

Browse files
committed
Auto merge of #121679 - lcnr:opaque-wf-check-2, r=<try>
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, I add 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 71a7b66 + af0d508 commit 1659600

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>(
@@ -1551,14 +1541,16 @@ fn opaque_type_cycle_error(
15511541
err.emit()
15521542
}
15531543

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

15601552
let typeck = tcx.typeck(def_id);
1561-
let param_env = tcx.param_env(def_id);
1553+
let param_env = tcx.param_env(typeck.hir_owner.def_id);
15621554

15631555
let coroutine_interior_predicates = &typeck.coroutine_interior_predicates[&def_id];
15641556
debug!(?coroutine_interior_predicates);

compiler/rustc_infer/src/infer/opaque_types.rs

+18-1
Original file line numberDiff line numberDiff line change
@@ -605,8 +605,25 @@ impl<'tcx> InferCtxt<'tcx> {
605605
obligations: &mut Vec<PredicateObligation<'tcx>>,
606606
) {
607607
let tcx = self.tcx;
608-
let item_bounds = tcx.explicit_item_bounds(def_id);
608+
// Require that the hidden type is well-formed. We have to
609+
// make sure we wf-check the hidden type to fix #114728.
610+
//
611+
// However, we don't check that all types are well-formed.
612+
// We only do so for types provided by the user or if they are
613+
// "used", e.g. for method selection.
614+
//
615+
// This means we never check the wf requirements of the hidden
616+
// type during MIR borrowck, causing us to infer the wrong
617+
// lifetime for its member constraints which then results in
618+
// unexpected region errors.
619+
obligations.push(traits::Obligation::new(
620+
tcx,
621+
cause.clone(),
622+
param_env,
623+
ty::ClauseKind::WellFormed(hidden_ty.into()),
624+
));
609625

626+
let item_bounds = tcx.explicit_item_bounds(def_id);
610627
for (predicate, _) in item_bounds.iter_instantiated_copied(tcx, args) {
611628
let predicate = predicate.fold_with(&mut BottomUpFolder {
612629
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)