Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Don't deduce a signature that makes a closure cyclic #105409

Merged
merged 3 commits into from
Jan 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 31 additions & 7 deletions compiler/rustc_hir_typeck/src/closure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,15 @@ use rustc_infer::infer::{InferOk, InferResult};
use rustc_macros::{TypeFoldable, TypeVisitable};
use rustc_middle::ty::subst::InternalSubsts;
use rustc_middle::ty::visit::TypeVisitable;
use rustc_middle::ty::{self, Ty};
use rustc_middle::ty::{self, Ty, TypeSuperVisitable, TypeVisitor};
use rustc_span::source_map::Span;
use rustc_target::spec::abi::Abi;
use rustc_trait_selection::traits;
use rustc_trait_selection::traits::error_reporting::ArgKind;
use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _;
use std::cmp;
use std::iter;
use std::ops::ControlFlow;

/// What signature do we *expect* the closure to have from context?
#[derive(Debug, Clone, TypeFoldable, TypeVisitable)]
Expand Down Expand Up @@ -54,7 +55,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// closure sooner rather than later, so first examine the expected
// type, and see if can glean a closure kind from there.
let (expected_sig, expected_kind) = match expected.to_option(self) {
Some(ty) => self.deduce_expectations_from_expected_type(ty),
Some(ty) => self.deduce_closure_signature(ty),
None => (None, None),
};
let body = self.tcx.hir().body(closure.body);
Expand Down Expand Up @@ -162,13 +163,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
/// Given the expected type, figures out what it can about this closure we
/// are about to type check:
#[instrument(skip(self), level = "debug")]
fn deduce_expectations_from_expected_type(
fn deduce_closure_signature(
&self,
expected_ty: Ty<'tcx>,
) -> (Option<ExpectedSig<'tcx>>, Option<ty::ClosureKind>) {
match *expected_ty.kind() {
ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) => self
.deduce_signature_from_predicates(
.deduce_closure_signature_from_predicates(
expected_ty,
self.tcx.bound_explicit_item_bounds(def_id).subst_iter_copied(self.tcx, substs),
),
ty::Dynamic(ref object_type, ..) => {
Expand All @@ -181,7 +183,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
.and_then(|did| self.tcx.fn_trait_kind_from_def_id(did));
(sig, kind)
}
ty::Infer(ty::TyVar(vid)) => self.deduce_signature_from_predicates(
ty::Infer(ty::TyVar(vid)) => self.deduce_closure_signature_from_predicates(
self.tcx.mk_ty_var(self.root_var(vid)),
self.obligations_for_self_ty(vid).map(|obl| (obl.predicate, obl.cause.span)),
),
ty::FnPtr(sig) => {
Expand All @@ -192,8 +195,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}

fn deduce_signature_from_predicates(
fn deduce_closure_signature_from_predicates(
&self,
expected_ty: Ty<'tcx>,
predicates: impl DoubleEndedIterator<Item = (ty::Predicate<'tcx>, Span)>,
) -> (Option<ExpectedSig<'tcx>>, Option<ty::ClosureKind>) {
let mut expected_sig = None;
Expand All @@ -214,13 +218,33 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
if expected_sig.is_none()
&& let ty::PredicateKind::Clause(ty::Clause::Projection(proj_predicate)) = bound_predicate.skip_binder()
{
expected_sig = self.normalize(
let inferred_sig = self.normalize(
obligation.cause.span,
self.deduce_sig_from_projection(
Some(obligation.cause.span),
bound_predicate.rebind(proj_predicate),
),
);
// Make sure that we didn't infer a signature that mentions itself.
// This can happen when we elaborate certain supertrait bounds that
// mention projections containing the `Self` type. See #105401.
struct MentionsTy<'tcx> {
expected_ty: Ty<'tcx>,
}
impl<'tcx> TypeVisitor<'tcx> for MentionsTy<'tcx> {
type BreakTy = ();

fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
if t == self.expected_ty {
ControlFlow::BREAK
} else {
t.super_visit_with(self)
}
}
}
if inferred_sig.visit_with(&mut MentionsTy { expected_ty }).is_continue() {
expected_sig = inferred_sig;
}
}

// Even if we can't infer the full signature, we may be able to
Expand Down
21 changes: 20 additions & 1 deletion compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ use rustc_infer::infer::error_reporting::TypeErrCtxt;
use rustc_infer::infer::{InferOk, TypeTrace};
use rustc_middle::traits::select::OverflowError;
use rustc_middle::ty::abstract_const::NotConstEvaluatable;
use rustc_middle::ty::error::ExpectedFound;
use rustc_middle::ty::error::{ExpectedFound, TypeError};
use rustc_middle::ty::fold::{TypeFolder, TypeSuperFoldable};
use rustc_middle::ty::print::{with_forced_trimmed_paths, FmtPrinter, Print};
use rustc_middle::ty::{
Expand Down Expand Up @@ -1215,6 +1215,25 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
}
}

OutputTypeParameterMismatch(
found_trait_ref,
expected_trait_ref,
terr @ TypeError::CyclicTy(_),
) => {
let self_ty = found_trait_ref.self_ty().skip_binder();
let (cause, terr) = if let ty::Closure(def_id, _) = self_ty.kind() {
(
ObligationCause::dummy_with_span(tcx.def_span(def_id)),
TypeError::CyclicTy(self_ty),
)
} else {
(obligation.cause.clone(), terr)
};
self.report_and_explain_type_error(
TypeTrace::poly_trait_refs(&cause, true, expected_trait_ref, found_trait_ref),
terr,
)
}
OutputTypeParameterMismatch(found_trait_ref, expected_trait_ref, _) => {
let found_trait_ref = self.resolve_vars_if_possible(found_trait_ref);
let expected_trait_ref = self.resolve_vars_if_possible(expected_trait_ref);
Expand Down
18 changes: 18 additions & 0 deletions src/test/ui/closures/supertrait-hint-cycle-2.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// check-pass

trait Foo<'a> {
type Input;
}

impl<F: Fn(u32)> Foo<'_> for F {
type Input = u32;
}

trait SuperFn: for<'a> Foo<'a> + for<'a> Fn(<Self as Foo<'a>>::Input) {}
impl<T> SuperFn for T where T: for<'a> Fn(<Self as Foo<'a>>::Input) + for<'a> Foo<'a> {}

fn needs_super(_: impl SuperFn) {}

fn main() {
needs_super(|_: u32| {});
}
16 changes: 16 additions & 0 deletions src/test/ui/closures/supertrait-hint-cycle-3.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// check-pass


trait Foo<'a> {
type Input;
}

impl<F: Fn(u32)> Foo<'_> for F {
type Input = u32;
}

fn needs_super<F: for<'a> Fn(<F as Foo<'a>>::Input) + for<'a> Foo<'a>>(_: F) {}

fn main() {
needs_super(|_: u32| {});
}
65 changes: 65 additions & 0 deletions src/test/ui/closures/supertrait-hint-cycle.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// edition:2021
// check-pass

#![feature(type_alias_impl_trait)]
#![feature(closure_lifetime_binder)]

use std::future::Future;

trait AsyncFn<I, R>: FnMut(I) -> Self::Fut {
type Fut: Future<Output = R>;
}

impl<F, I, R, Fut> AsyncFn<I, R> for F
where
Fut: Future<Output = R>,
F: FnMut(I) -> Fut,
{
type Fut = Fut;
}

async fn call<C, R, F>(mut ctx: C, mut f: F) -> Result<R, ()>
where
F: for<'a> AsyncFn<&'a mut C, Result<R, ()>>,
{
loop {
match f(&mut ctx).await {
Ok(val) => return Ok(val),
Err(_) => continue,
}
}
}

trait Cap<'a> {}
impl<T> Cap<'_> for T {}

fn works(ctx: &mut usize) {
let mut inner = 0;

type Ret<'a, 'b: 'a> = impl Future<Output = Result<usize, ()>> + 'a + Cap<'b>;

let callback = for<'a, 'b> |c: &'a mut &'b mut usize| -> Ret<'a, 'b> {
inner += 1;
async move {
let _c = c;
Ok(1usize)
}
};
call(ctx, callback);
}

fn doesnt_work_but_should(ctx: &mut usize) {
let mut inner = 0;

type Ret<'a, 'b: 'a> = impl Future<Output = Result<usize, ()>> + 'a + Cap<'b>;

call(ctx, for<'a, 'b> |c: &'a mut &'b mut usize| -> Ret<'a, 'b> {
inner += 1;
async move {
let _c = c;
Ok(1usize)
}
});
}

fn main() {}
7 changes: 6 additions & 1 deletion src/test/ui/issues/issue-25439.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,17 @@ error[E0644]: closure/generator type that references itself
--> $DIR/issue-25439.rs:8:9
|
LL | fix(|_, x| x);
| ^^^^^^^^ cyclic type of infinite size
| ^^^^^^ cyclic type of infinite size
|
= note: closures cannot capture themselves or take themselves as argument;
this error may be the result of a recent compiler bug-fix,
see issue #46062 <https://github.com/rust-lang/rust/issues/46062>
for more information
note: required by a bound in `fix`
--> $DIR/issue-25439.rs:3:33
|
LL | fn fix<F>(f: F) -> i32 where F: Fn(Helper<F>, i32) -> i32 {
| ^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `fix`

error: aborting due to previous error

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,17 @@ error[E0644]: closure/generator type that references itself
--> $DIR/unboxed-closure-no-cyclic-sig.rs:8:7
|
LL | g(|_| { });
| ^^^^^^^^ cyclic type of infinite size
| ^^^ cyclic type of infinite size
|
= note: closures cannot capture themselves or take themselves as argument;
this error may be the result of a recent compiler bug-fix,
see issue #46062 <https://github.com/rust-lang/rust/issues/46062>
for more information
note: required by a bound in `g`
--> $DIR/unboxed-closure-no-cyclic-sig.rs:5:24
|
LL | fn g<F>(_: F) where F: FnOnce(Option<F>) {}
| ^^^^^^^^^^^^^^^^^ required by this bound in `g`

error: aborting due to previous error

Expand Down