Skip to content

Commit

Permalink
Normalize opaques before defining them in the new solver
Browse files Browse the repository at this point in the history
  • Loading branch information
compiler-errors committed Oct 17, 2023
1 parent 09df610 commit 02aefd4
Show file tree
Hide file tree
Showing 19 changed files with 137 additions and 18 deletions.
14 changes: 13 additions & 1 deletion compiler/rustc_trait_selection/src/solve/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ use rustc_middle::traits::solve::{
CanonicalResponse, Certainty, ExternalConstraintsData, Goal, IsNormalizesToHack, QueryResult,
Response,
};
use rustc_middle::traits::Reveal;
use rustc_middle::ty::{self, Ty, TyCtxt, UniverseIndex};
use rustc_middle::ty::{
CoercePredicate, RegionOutlivesPredicate, SubtypePredicate, TypeOutlivesPredicate,
Expand Down Expand Up @@ -302,10 +303,21 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
mut ty: Ty<'tcx>,
) -> Result<Option<Ty<'tcx>>, NoSolution> {
for _ in 0..self.local_overflow_limit() {
let ty::Alias(_, projection_ty) = *ty.kind() else {
let ty::Alias(kind, projection_ty) = *ty.kind() else {
return Ok(Some(ty));
};

// Don't try normalizing an opaque that is not in the defining scope
if kind == ty::Opaque
&& param_env.reveal() == Reveal::UserFacing
&& !projection_ty
.def_id
.as_local()
.is_some_and(|def_id| self.can_define_opaque_ty(def_id))
{
return Ok(Some(ty));
}

let normalized_ty = self.next_ty_infer();
let normalizes_to_goal = Goal::new(
self.tcx(),
Expand Down
19 changes: 19 additions & 0 deletions compiler/rustc_trait_selection/src/solve/project_goals/opaques.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,14 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
let Some(opaque_ty_def_id) = opaque_ty.def_id.as_local() else {
return Err(NoSolution);
};

// FIXME: at some point we should call queries without defining
// new opaque types but having the existing opaque type definitions.
// This will require moving this below "Prefer opaques registered already".
if !self.can_define_opaque_ty(opaque_ty_def_id) {
return Err(NoSolution);
}

// FIXME: This may have issues when the args contain aliases...
match self.tcx().uses_unique_placeholders_ignoring_regions(opaque_ty.args) {
Err(NotUniqueParam::NotParam(param)) if param.is_non_region_infer() => {
Expand All @@ -41,6 +43,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
}
Ok(()) => {}
}

// Prefer opaques registered already.
let opaque_type_key =
ty::OpaqueTypeKey { def_id: opaque_ty_def_id, args: opaque_ty.args };
Expand All @@ -53,6 +56,22 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
return self.flounder(&matches);
}
}

// Try normalizing the opaque's hidden type. If it's a ty var,
// then refuse to define the opaque type yet. This allows us to
// avoid inferring opaque cycles so eagerly.
let expected = match self.try_normalize_ty(goal.param_env, expected) {
Ok(Some(ty)) if !ty.is_ty_var() => ty,
Ok(_) => {
return self.evaluate_added_goals_and_make_canonical_response(
Certainty::AMBIGUOUS,
);
}
Err(_) => {
return Err(NoSolution);
}
};

// Otherwise, define a new opaque type
self.insert_hidden_type(opaque_type_key, goal.param_env, expected)?;
self.add_item_bounds_for_hidden_type(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
error[E0277]: `{integer}` is not a future
--> $DIR/issue-83919.rs:21:26
--> $DIR/issue-83919.rs:23:26
|
LL | fn get_fut(&self) -> Self::Fut {
| ^^^^^^^^^ `{integer}` is not a future
Expand Down
23 changes: 23 additions & 0 deletions tests/ui/impl-trait/issues/issue-83919.next.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
error[E0308]: mismatched types
--> $DIR/issue-83919.rs:25:9
|
LL | fn get_fut(&self) -> Self::Fut {
| --------- expected `<Implementor as Foo>::Fut` because of return type
LL |
LL | / async move {
LL | |
LL | | 42
LL | | // 42 does not impl Future and rustc does actually point out the error,
LL | | // but rustc used to panic.
LL | | // Putting a valid Future here always worked fine.
LL | | }
| |_________^ types differ
|
= note: expected associated type `<Implementor as Foo>::Fut`
found `async` block `{async block@$DIR/issue-83919.rs:25:9: 31:10}`
= 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`
= note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html

error: aborting due to previous error

For more information about this error, try `rustc --explain E0308`.
9 changes: 6 additions & 3 deletions tests/ui/impl-trait/issues/issue-83919.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
#![feature(impl_trait_in_assoc_type)]

// revisions: current next
//[next] compile-flags: -Ztrait-solver=next
// edition:2021

#![feature(impl_trait_in_assoc_type)]

use std::future::Future;

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

fn get_fut(&self) -> Self::Fut {
//~^ ERROR `{integer}` is not a future
//[current]~^ ERROR `{integer}` is not a future
async move {
//[next]~^ ERROR mismatched types
42
// 42 does not impl Future and rustc does actually point out the error,
// but rustc used to panic.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
error[E0720]: cannot resolve opaque type
--> $DIR/recursive-generator.rs:5:13
--> $DIR/recursive-generator.rs:8:13
|
LL | fn foo() -> impl Generator<Yield = (), Return = ()> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ recursive opaque type
Expand Down
12 changes: 12 additions & 0 deletions tests/ui/impl-trait/recursive-generator.next.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
error[E0720]: cannot resolve opaque type
--> $DIR/recursive-generator.rs:8:13
|
LL | fn foo() -> impl Generator<Yield = (), Return = ()> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ recursive opaque type
...
LL | let mut gen = Box::pin(foo());
| ------- generator captures itself here

error: aborting due to previous error

For more information about this error, try `rustc --explain E0720`.
3 changes: 3 additions & 0 deletions tests/ui/impl-trait/recursive-generator.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// revisions: current next
//[next] compile-flags: -Ztrait-solver=next

#![feature(generators, generator_trait)]

use std::ops::{Generator, GeneratorState};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
error: opaque type's hidden type cannot be another opaque type from the same scope
--> $DIR/two_tait_defining_each_other.rs:12:5
--> $DIR/two_tait_defining_each_other.rs:16:5
|
LL | x // A's hidden type is `Bar`, because all the hidden types of `B` are compared with each other
| ^ one of the two opaque types used here has to be outside its defining scope
|
note: opaque type whose hidden type is being assigned
--> $DIR/two_tait_defining_each_other.rs:4:10
--> $DIR/two_tait_defining_each_other.rs:8:10
|
LL | type B = impl Foo;
| ^^^^^^^^
note: opaque type being used as hidden type
--> $DIR/two_tait_defining_each_other.rs:3:10
--> $DIR/two_tait_defining_each_other.rs:7:10
|
LL | type A = impl Foo;
| ^^^^^^^^
Expand Down
6 changes: 5 additions & 1 deletion tests/ui/impl-trait/two_tait_defining_each_other.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
// revisions: current next
//[next] compile-flags: -Ztrait-solver=next
//[next] check-pass

#![feature(type_alias_impl_trait)]

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

struct Bar;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
error: opaque type's hidden type cannot be another opaque type from the same scope
--> $DIR/two_tait_defining_each_other3.rs:10:16
--> $DIR/two_tait_defining_each_other3.rs:14:16
|
LL | return x; // B's hidden type is A (opaquely)
| ^ one of the two opaque types used here has to be outside its defining scope
|
note: opaque type whose hidden type is being assigned
--> $DIR/two_tait_defining_each_other3.rs:4:10
--> $DIR/two_tait_defining_each_other3.rs:8:10
|
LL | type B = impl Foo;
| ^^^^^^^^
note: opaque type being used as hidden type
--> $DIR/two_tait_defining_each_other3.rs:3:10
--> $DIR/two_tait_defining_each_other3.rs:7:10
|
LL | type A = impl Foo;
| ^^^^^^^^
Expand Down
6 changes: 5 additions & 1 deletion tests/ui/impl-trait/two_tait_defining_each_other3.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
// revisions: current next
//[next] compile-flags: -Ztrait-solver=next
//[next] check-pass

#![feature(type_alias_impl_trait)]

type A = impl Foo;
Expand All @@ -8,7 +12,7 @@ trait Foo {}
fn muh(x: A) -> B {
if false {
return x; // B's hidden type is A (opaquely)
//~^ ERROR opaque type's hidden type cannot be another opaque type
//[current]~^ ERROR opaque type's hidden type cannot be another opaque type
}
Bar // A's hidden type is `Bar`, because all the return types are compared with each other
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// known-bug: unknown
// compile-flags: -Ztrait-solver=next

fn test<T: Iterator>(x: T::Item) -> impl Sized {
x
}

fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
error[E0308]: mismatched types
--> $DIR/opaque-hidden-ty-is-rigid-projection.rs:5:5
|
LL | fn test<T: Iterator>(x: T::Item) -> impl Sized {
| ----------
| |
| the expected opaque type
| expected `impl Sized` because of return type
LL | x
| ^ types differ
|
= note: expected opaque type `impl Sized`
found associated type `<T as Iterator>::Item`
help: consider constraining the associated type `<T as Iterator>::Item` to `impl Sized`
|
LL | fn test<T: Iterator<Item = impl Sized>>(x: T::Item) -> impl Sized {
| +++++++++++++++++++

error: aborting due to previous error

For more information about this error, try `rustc --explain E0308`.
3 changes: 3 additions & 0 deletions tests/ui/type-alias-impl-trait/assoc-type-const.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
// Tests that we properly detect defining usages when using
// const generics in an associated opaque type

// check-pass
// revisions: current next
//[next] compile-flags: -Ztrait-solver=next

#![feature(impl_trait_in_assoc_type)]

Expand Down
3 changes: 3 additions & 0 deletions tests/ui/type-alias-impl-trait/assoc-type-lifetime.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
// Tests that we still detect defining usages when
// lifetimes are used in an associated opaque type

// check-pass
// revisions: current next
//[next] compile-flags: -Ztrait-solver=next

#![feature(impl_trait_in_assoc_type)]

Expand Down
2 changes: 2 additions & 0 deletions tests/ui/type-alias-impl-trait/issue-78450.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
// check-pass
// revisions: current next
//[next] compile-flags: -Ztrait-solver=next

#![feature(impl_trait_in_assoc_type)]

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
error[E0309]: the parameter type `T` may not live long enough
--> $DIR/wf-in-associated-type.rs:36:23
--> $DIR/wf-in-associated-type.rs:38:23
|
LL | impl<'a, T> Trait<'a, T> for () {
| -- the parameter type `T` must be valid for the lifetime `'a` as defined here...
Expand All @@ -12,7 +12,7 @@ LL | impl<'a, T: 'a> Trait<'a, T> for () {
| ++++

error[E0309]: the parameter type `T` may not live long enough
--> $DIR/wf-in-associated-type.rs:36:23
--> $DIR/wf-in-associated-type.rs:38:23
|
LL | impl<'a, T> Trait<'a, T> for () {
| -- the parameter type `T` must be valid for the lifetime `'a` as defined here...
Expand Down
6 changes: 4 additions & 2 deletions tests/ui/type-alias-impl-trait/wf-in-associated-type.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
// WF check for impl Trait in associated type position.
//
// revisions: pass fail
// revisions: pass pass_next fail
// [pass] check-pass
// [pass_next] compile-flags: -Ztrait-solver=next
// [pass_next] check-pass
// [fail] check-fail

#![feature(impl_trait_in_assoc_type)]

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

0 comments on commit 02aefd4

Please sign in to comment.