Skip to content

Commit

Permalink
Detect cycle errors hidden by opaques during monomorphization
Browse files Browse the repository at this point in the history
  • Loading branch information
compiler-errors committed Sep 13, 2023
1 parent e5fedce commit 8fbd78c
Show file tree
Hide file tree
Showing 7 changed files with 147 additions and 1 deletion.
26 changes: 25 additions & 1 deletion compiler/rustc_traits/src/normalize_projection_ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@ use rustc_infer::infer::TyCtxtInferExt;
use rustc_middle::query::Providers;
use rustc_middle::ty::{ParamEnvAnd, TyCtxt};
use rustc_trait_selection::infer::InferCtxtBuilderExt;
use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
use rustc_trait_selection::traits::query::{
normalize::NormalizationResult, CanonicalProjectionGoal, NoSolution,
};
use rustc_trait_selection::traits::{self, ObligationCause, SelectionContext};
use rustc_trait_selection::traits::{
self, FulfillmentErrorCode, ObligationCause, SelectionContext,
};
use std::sync::atomic::Ordering;

pub(crate) fn provide(p: &mut Providers) {
Expand Down Expand Up @@ -40,6 +43,27 @@ fn normalize_projection_ty<'tcx>(
&mut obligations,
);
ocx.register_obligations(obligations);
// #112047: With projections and opaques, we are able to create opaques that
// are recursive (given some substitution of the opaque's type variables).
// In that case, we may only realize a cycle error when calling
// `normalize_erasing_regions` in mono.
if !ocx.infcx.next_trait_solver() {
let errors = ocx.select_where_possible();
if !errors.is_empty() {
// Rustdoc may attempt to normalize type alias types which are not
// well-formed. Rustdoc also normalizes types that are just not
// well-formed, since we don't do as much HIR analysis (checking
// that impl vars are constrained by the signature, for example).
if !tcx.sess.opts.actually_rustdoc {
for error in &errors {
if let FulfillmentErrorCode::CodeCycle(cycle) = &error.code {
ocx.infcx.err_ctxt().report_overflow_obligation_cycle(cycle);
}
}
}
return Err(NoSolution);
}
}
// FIXME(associated_const_equality): All users of normalize_projection_ty expected
// a type, but there is the possibility it could've been a const now. Maybe change
// it to a Term later?
Expand Down
38 changes: 38 additions & 0 deletions tests/ui/async-await/in-trait/indirect-recursion-issue-112047.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// edition: 2021
// build-fail
//~^^ ERROR overflow evaluating the requirement `<A as Second>::{opaque#0} == _`

#![feature(async_fn_in_trait)]

fn main() {
let _ = async {
A.first().await.second().await;
};
}

pub trait First {
type Second: Second;
async fn first(self) -> Self::Second;
}

struct A;

impl First for A {
type Second = A;
async fn first(self) -> Self::Second {
A
}
}

pub trait Second {
async fn second(self);
}

impl<C> Second for C
where
C: First,
{
async fn second(self) {
self.first().await.second().await;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
error[E0275]: overflow evaluating the requirement `<A as Second>::{opaque#0} == _`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0275`.
29 changes: 29 additions & 0 deletions tests/ui/type-alias-impl-trait/indirect-recursion-issue-112047.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// edition: 2021
// build-fail
//~^^ ERROR overflow evaluating the requirement `<() as Recur>::Recur == _`

#![feature(impl_trait_in_assoc_type)]

use core::future::Future;

trait Recur {
type Recur: Future<Output = ()>;

fn recur(self) -> Self::Recur;
}

async fn recur(t: impl Recur) {
t.recur().await;
}

impl Recur for () {
type Recur = impl Future<Output = ()>;

fn recur(self) -> Self::Recur {
async move { recur(self).await; }
}
}

fn main() {
recur(());
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
error[E0275]: overflow evaluating the requirement `<() as Recur>::Recur == _`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0275`.
40 changes: 40 additions & 0 deletions tests/ui/type-alias-impl-trait/mututally-recursive-overflow.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// edition: 2021
// build-fail
//~^^ ERROR overflow evaluating the requirement `<() as B>::Assoc == _`

#![feature(rustc_attrs)]
#![feature(impl_trait_in_assoc_type)]

#[rustc_coinductive]
trait A {
type Assoc;

fn test() -> Self::Assoc;
}

#[rustc_coinductive]
trait B {
type Assoc;

fn test() -> Self::Assoc;
}

impl<T: A> B for T {
type Assoc = impl Sized;

fn test() -> <Self as B>::Assoc {
<T as A>::test()
}
}

fn main() {
<() as A>::test();
}

impl<T: B> A for T {
type Assoc = impl Sized;

fn test() -> <Self as A>::Assoc {
<T as B>::test()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
error[E0275]: overflow evaluating the requirement `<() as B>::Assoc == _`

error: aborting due to previous error

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

0 comments on commit 8fbd78c

Please sign in to comment.