Skip to content

Commit e4952af

Browse files
Normalize opaques before defining them in the new solver
1 parent 5333b87 commit e4952af

19 files changed

+136
-18
lines changed

compiler/rustc_trait_selection/src/solve/mod.rs

+13-1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ use rustc_middle::traits::solve::{
2222
CanonicalResponse, Certainty, ExternalConstraintsData, Goal, IsNormalizesToHack, QueryResult,
2323
Response,
2424
};
25+
use rustc_middle::traits::Reveal;
2526
use rustc_middle::ty::{self, Ty, TyCtxt, UniverseIndex};
2627
use rustc_middle::ty::{
2728
CoercePredicate, RegionOutlivesPredicate, SubtypePredicate, TypeOutlivesPredicate,
@@ -305,10 +306,21 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
305306
mut ty: Ty<'tcx>,
306307
) -> Result<Option<Ty<'tcx>>, NoSolution> {
307308
for _ in 0..self.local_overflow_limit() {
308-
let ty::Alias(_, projection_ty) = *ty.kind() else {
309+
let ty::Alias(kind, projection_ty) = *ty.kind() else {
309310
return Ok(Some(ty));
310311
};
311312

313+
// Don't try normalizing an opaque that is not in the defining scope
314+
if kind == ty::Opaque
315+
&& param_env.reveal() == Reveal::UserFacing
316+
&& !projection_ty
317+
.def_id
318+
.as_local()
319+
.is_some_and(|def_id| self.can_define_opaque_ty(def_id))
320+
{
321+
return Ok(Some(ty));
322+
}
323+
312324
let normalized_ty = self.next_ty_infer();
313325
let normalizes_to_goal = Goal::new(
314326
self.tcx(),

compiler/rustc_trait_selection/src/solve/opaques.rs

+19
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,14 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
2323
let Some(opaque_ty_def_id) = opaque_ty.def_id.as_local() else {
2424
return Err(NoSolution);
2525
};
26+
2627
// FIXME: at some point we should call queries without defining
2728
// new opaque types but having the existing opaque type definitions.
2829
// This will require moving this below "Prefer opaques registered already".
2930
if !self.can_define_opaque_ty(opaque_ty_def_id) {
3031
return Err(NoSolution);
3132
}
33+
3234
// FIXME: This may have issues when the args contain aliases...
3335
match self.tcx().uses_unique_placeholders_ignoring_regions(opaque_ty.args) {
3436
Err(NotUniqueParam::NotParam(param)) if param.is_non_region_infer() => {
@@ -41,6 +43,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
4143
}
4244
Ok(()) => {}
4345
}
46+
4447
// Prefer opaques registered already.
4548
let opaque_type_key =
4649
ty::OpaqueTypeKey { def_id: opaque_ty_def_id, args: opaque_ty.args };
@@ -53,6 +56,22 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
5356
return self.flounder(&matches);
5457
}
5558
}
59+
60+
// Try normalizing the opaque's hidden type. If it's a ty var,
61+
// then refuse to define the opaque type yet. This allows us to
62+
// avoid inferring opaque cycles so eagerly.
63+
let expected = match self.try_normalize_ty(goal.param_env, expected) {
64+
Ok(Some(ty)) if !ty.is_ty_var() => ty,
65+
Ok(_) => {
66+
return self.evaluate_added_goals_and_make_canonical_response(
67+
Certainty::AMBIGUOUS,
68+
);
69+
}
70+
Err(_) => {
71+
return Err(NoSolution);
72+
}
73+
};
74+
5675
// Otherwise, define a new opaque type
5776
self.insert_hidden_type(opaque_type_key, goal.param_env, expected)?;
5877
self.add_item_bounds_for_hidden_type(

tests/ui/impl-trait/issues/issue-83919.stderr renamed to tests/ui/impl-trait/issues/issue-83919.current.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error[E0277]: `{integer}` is not a future
2-
--> $DIR/issue-83919.rs:21:26
2+
--> $DIR/issue-83919.rs:23:26
33
|
44
LL | fn get_fut(&self) -> Self::Fut {
55
| ^^^^^^^^^ `{integer}` is not a future
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/issue-83919.rs:25:9
3+
|
4+
LL | fn get_fut(&self) -> Self::Fut {
5+
| --------- expected `<Implementor as Foo>::Fut` because of return type
6+
LL |
7+
LL | / async move {
8+
LL | |
9+
LL | | 42
10+
LL | | // 42 does not impl Future and rustc does actually point out the error,
11+
LL | | // but rustc used to panic.
12+
LL | | // Putting a valid Future here always worked fine.
13+
LL | | }
14+
| |_________^ types differ
15+
|
16+
= note: expected associated type `<Implementor as Foo>::Fut`
17+
found `async` block `{async block@$DIR/issue-83919.rs:25:9: 31:10}`
18+
= help: consider constraining the associated type `<Implementor as Foo>::Fut` to `{async block@$DIR/issue-83919.rs:25:9: 31:10}` or calling a method that returns `<Implementor as Foo>::Fut`
19+
= note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html
20+
21+
error: aborting due to previous error
22+
23+
For more information about this error, try `rustc --explain E0308`.

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

+6-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
#![feature(impl_trait_in_assoc_type)]
2-
1+
// revisions: current next
2+
//[next] compile-flags: -Ztrait-solver=next
33
// edition:2021
44

5+
#![feature(impl_trait_in_assoc_type)]
6+
57
use std::future::Future;
68

79
trait Foo {
@@ -19,8 +21,9 @@ impl Foo for Implementor {
1921
type Fut = impl Future<Output = Self::Fut2>;
2022

2123
fn get_fut(&self) -> Self::Fut {
22-
//~^ ERROR `{integer}` is not a future
24+
//[current]~^ ERROR `{integer}` is not a future
2325
async move {
26+
//[next]~^ ERROR mismatched types
2427
42
2528
// 42 does not impl Future and rustc does actually point out the error,
2629
// but rustc used to panic.

tests/ui/impl-trait/recursive-generator.stderr renamed to tests/ui/impl-trait/recursive-generator.current.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error[E0720]: cannot resolve opaque type
2-
--> $DIR/recursive-generator.rs:5:13
2+
--> $DIR/recursive-generator.rs:8:13
33
|
44
LL | fn foo() -> impl Generator<Yield = (), Return = ()> {
55
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ recursive opaque type
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
error[E0720]: cannot resolve opaque type
2+
--> $DIR/recursive-generator.rs:8:13
3+
|
4+
LL | fn foo() -> impl Generator<Yield = (), Return = ()> {
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ recursive opaque type
6+
...
7+
LL | let mut gen = Box::pin(foo());
8+
| ------- generator captures itself here
9+
10+
error: aborting due to previous error
11+
12+
For more information about this error, try `rustc --explain E0720`.

tests/ui/impl-trait/recursive-generator.rs

+3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
// revisions: current next
2+
//[next] compile-flags: -Ztrait-solver=next
3+
14
#![feature(generators, generator_trait)]
25

36
use std::ops::{Generator, GeneratorState};

tests/ui/impl-trait/two_tait_defining_each_other.stderr renamed to tests/ui/impl-trait/two_tait_defining_each_other.current.stderr

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
error: opaque type's hidden type cannot be another opaque type from the same scope
2-
--> $DIR/two_tait_defining_each_other.rs:12:5
2+
--> $DIR/two_tait_defining_each_other.rs:16:5
33
|
44
LL | x // A's hidden type is `Bar`, because all the hidden types of `B` are compared with each other
55
| ^ one of the two opaque types used here has to be outside its defining scope
66
|
77
note: opaque type whose hidden type is being assigned
8-
--> $DIR/two_tait_defining_each_other.rs:4:10
8+
--> $DIR/two_tait_defining_each_other.rs:8:10
99
|
1010
LL | type B = impl Foo;
1111
| ^^^^^^^^
1212
note: opaque type being used as hidden type
13-
--> $DIR/two_tait_defining_each_other.rs:3:10
13+
--> $DIR/two_tait_defining_each_other.rs:7:10
1414
|
1515
LL | type A = impl Foo;
1616
| ^^^^^^^^

tests/ui/impl-trait/two_tait_defining_each_other.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
// revisions: current next
2+
//[next] compile-flags: -Ztrait-solver=next
3+
//[next] check-pass
4+
15
#![feature(type_alias_impl_trait)]
26

37
type A = impl Foo;
@@ -10,7 +14,7 @@ fn muh(x: A) -> B {
1014
return Bar; // B's hidden type is Bar
1115
}
1216
x // A's hidden type is `Bar`, because all the hidden types of `B` are compared with each other
13-
//~^ ERROR opaque type's hidden type cannot be another opaque type
17+
//[current]~^ ERROR opaque type's hidden type cannot be another opaque type
1418
}
1519

1620
struct Bar;

tests/ui/impl-trait/two_tait_defining_each_other3.stderr renamed to tests/ui/impl-trait/two_tait_defining_each_other3.current.stderr

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
error: opaque type's hidden type cannot be another opaque type from the same scope
2-
--> $DIR/two_tait_defining_each_other3.rs:10:16
2+
--> $DIR/two_tait_defining_each_other3.rs:14:16
33
|
44
LL | return x; // B's hidden type is A (opaquely)
55
| ^ one of the two opaque types used here has to be outside its defining scope
66
|
77
note: opaque type whose hidden type is being assigned
8-
--> $DIR/two_tait_defining_each_other3.rs:4:10
8+
--> $DIR/two_tait_defining_each_other3.rs:8:10
99
|
1010
LL | type B = impl Foo;
1111
| ^^^^^^^^
1212
note: opaque type being used as hidden type
13-
--> $DIR/two_tait_defining_each_other3.rs:3:10
13+
--> $DIR/two_tait_defining_each_other3.rs:7:10
1414
|
1515
LL | type A = impl Foo;
1616
| ^^^^^^^^

tests/ui/impl-trait/two_tait_defining_each_other3.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
// revisions: current next
2+
//[next] compile-flags: -Ztrait-solver=next
3+
//[next] check-pass
4+
15
#![feature(type_alias_impl_trait)]
26

37
type A = impl Foo;
@@ -8,7 +12,7 @@ trait Foo {}
812
fn muh(x: A) -> B {
913
if false {
1014
return x; // B's hidden type is A (opaquely)
11-
//~^ ERROR opaque type's hidden type cannot be another opaque type
15+
//[current]~^ ERROR opaque type's hidden type cannot be another opaque type
1216
}
1317
Bar // A's hidden type is `Bar`, because all the return types are compared with each other
1418
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// known-bug: unknown
2+
// compile-flags: -Ztrait-solver=next
3+
4+
fn test<T: Iterator>(x: T::Item) -> impl Sized {
5+
x
6+
}
7+
8+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/opaque-hidden-ty-is-rigid-projection.rs:5:5
3+
|
4+
LL | fn test<T: Iterator>(x: T::Item) -> impl Sized {
5+
| ----------
6+
| |
7+
| the expected opaque type
8+
| expected `impl Sized` because of return type
9+
LL | x
10+
| ^ types differ
11+
|
12+
= note: expected opaque type `impl Sized`
13+
found associated type `<T as Iterator>::Item`
14+
help: consider constraining the associated type `<T as Iterator>::Item` to `impl Sized`
15+
|
16+
LL | fn test<T: Iterator<Item = impl Sized>>(x: T::Item) -> impl Sized {
17+
| +++++++++++++++++++
18+
19+
error: aborting due to previous error
20+
21+
For more information about this error, try `rustc --explain E0308`.

tests/ui/type-alias-impl-trait/assoc-type-const.rs

+3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
// Tests that we properly detect defining usages when using
22
// const generics in an associated opaque type
3+
34
// check-pass
5+
// revisions: current next
6+
//[next] compile-flags: -Ztrait-solver=next
47

58
#![feature(impl_trait_in_assoc_type)]
69

tests/ui/type-alias-impl-trait/assoc-type-lifetime.rs

+3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
// Tests that we still detect defining usages when
22
// lifetimes are used in an associated opaque type
3+
34
// check-pass
5+
// revisions: current next
6+
//[next] compile-flags: -Ztrait-solver=next
47

58
#![feature(impl_trait_in_assoc_type)]
69

tests/ui/type-alias-impl-trait/issue-78450.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
// check-pass
2+
// revisions: current next
3+
//[next] compile-flags: -Ztrait-solver=next
24

35
#![feature(impl_trait_in_assoc_type)]
46

tests/ui/type-alias-impl-trait/wf-in-associated-type.fail.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error[E0309]: the parameter type `T` may not live long enough
2-
--> $DIR/wf-in-associated-type.rs:36:23
2+
--> $DIR/wf-in-associated-type.rs:37:23
33
|
44
LL | type Opaque = impl Sized + 'a;
55
| ^^^^^^^^^^^^^^^ ...so that the type `&'a T` will meet its required lifetime bounds
@@ -10,7 +10,7 @@ LL | impl<'a, T: 'a> Trait<'a, T> for () {
1010
| ++++
1111

1212
error[E0309]: the parameter type `T` may not live long enough
13-
--> $DIR/wf-in-associated-type.rs:36:23
13+
--> $DIR/wf-in-associated-type.rs:37:23
1414
|
1515
LL | type Opaque = impl Sized + 'a;
1616
| ^^^^^^^^^^^^^^^ ...so that the reference type `&'a T` does not outlive the data it points at

tests/ui/type-alias-impl-trait/wf-in-associated-type.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
// WF check for impl Trait in associated type position.
22
//
3-
// revisions: pass fail
3+
// revisions: pass pass_next fail
44
// [pass] check-pass
5+
// [pass_next] check-pass
56
// [fail] check-fail
67

78
#![feature(impl_trait_in_assoc_type)]
89

910
// The hidden type here (`&'a T`) requires proving `T: 'a`.
1011
// We know it holds because of implied bounds from the impl header.
11-
#[cfg(pass)]
12+
#[cfg(any(pass, pass_next))]
1213
mod pass {
1314
trait Trait<Req> {
1415
type Opaque1;

0 commit comments

Comments
 (0)