diff --git a/compiler/rustc_typeck/src/check/closure.rs b/compiler/rustc_typeck/src/check/closure.rs index d4336563b9603..c3deab0938d3b 100644 --- a/compiler/rustc_typeck/src/check/closure.rs +++ b/compiler/rustc_typeck/src/check/closure.rs @@ -259,6 +259,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// The `cause_span` should be the span that caused us to /// have this expected signature, or `None` if we can't readily /// know that. + #[instrument(level = "debug", skip(self, cause_span))] fn deduce_sig_from_projection( &self, cause_span: Option, @@ -266,15 +267,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) -> Option> { let tcx = self.tcx; - debug!("deduce_sig_from_projection({:?})", projection); - let trait_def_id = projection.trait_def_id(tcx); let is_fn = tcx.fn_trait_kind_from_lang_item(trait_def_id).is_some(); let gen_trait = tcx.require_lang_item(LangItem::Generator, cause_span); let is_gen = gen_trait == trait_def_id; if !is_fn && !is_gen { - debug!("deduce_sig_from_projection: not fn or generator"); + debug!("not fn or generator"); return None; } @@ -283,7 +282,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // associated item and not yield. let return_assoc_item = self.tcx.associated_item_def_ids(gen_trait)[1]; if return_assoc_item != projection.projection_def_id() { - debug!("deduce_sig_from_projection: not return assoc item of generator"); + debug!("not return assoc item of generator"); return None; } } @@ -291,7 +290,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let input_tys = if is_fn { let arg_param_ty = projection.skip_binder().projection_ty.substs.type_at(1); let arg_param_ty = self.resolve_vars_if_possible(arg_param_ty); - debug!("deduce_sig_from_projection: arg_param_ty={:?}", arg_param_ty); + debug!(?arg_param_ty); match arg_param_ty.kind() { ty::Tuple(tys) => tys.into_iter().map(|k| k.expect_ty()).collect::>(), @@ -306,7 +305,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Since this is a return parameter type it is safe to unwrap. let ret_param_ty = projection.skip_binder().term.ty().unwrap(); let ret_param_ty = self.resolve_vars_if_possible(ret_param_ty); - debug!("deduce_sig_from_projection: ret_param_ty={:?}", ret_param_ty); + debug!(?ret_param_ty); let sig = projection.rebind(self.tcx.mk_fn_sig( input_tys.iter(), @@ -315,7 +314,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { hir::Unsafety::Normal, Abi::Rust, )); - debug!("deduce_sig_from_projection: sig={:?}", sig); + debug!(?sig); Some(ExpectedSig { cause_span, sig }) } diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs b/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs index e60893a658b3d..52db15b69a375 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs @@ -730,7 +730,32 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) -> Vec> { let formal_ret = self.resolve_vars_with_obligations(formal_ret); let ret_ty = match expected_ret.only_has_type(self) { - Some(ret) => ret, + Some(ret) => { + // HACK(oli-obk): This is a backwards compatibility hack. Without it, the inference + // variable will get instantiated with the opaque type. The inference variable often + // has various helpful obligations registered for it that help closures figure out their + // signature. If we infer the inference var to the opaque type, the closure won't be able + // to find those obligations anymore, and it can't necessarily find them from the opaque + // type itself. We could be more powerful with inference if we *combined* the obligations + // so that we got both the obligations from the opaque type and the ones from the inference + // variable. That will accept more code than we do right now, so we need to carefully consider + // the implications. + // Note: this check is pessimistic, as the inference type could be matched with something other + // than the opaque type, but then we need a new `TypeRelation` just for this specific case and + // can't re-use `sup` below. + if formal_ret.has_infer_types() { + for ty in ret.walk() { + if let ty::subst::GenericArgKind::Type(ty) = ty.unpack() { + if let ty::Opaque(def_id, _) = *ty.kind() { + if self.infcx.opaque_type_origin(def_id, DUMMY_SP).is_some() { + return Vec::new(); + } + } + } + } + } + ret + } None => return Vec::new(), }; let expect_args = self diff --git a/src/test/ui/impl-trait/hidden-type-is-opaque-2.rs b/src/test/ui/impl-trait/hidden-type-is-opaque-2.rs new file mode 100644 index 0000000000000..1b65685a6c06f --- /dev/null +++ b/src/test/ui/impl-trait/hidden-type-is-opaque-2.rs @@ -0,0 +1,34 @@ +// This doesn't work, because we don't flow information from opaque types +// into function arguments via the function's generic parameters +// FIXME(oli-obk): make `expected_inputs_for_expected_output` support this + +fn reify_as() -> Thunk Continuation> { + Thunk::new(|mut cont| { //~ ERROR type annotations needed + cont.reify_as(); + cont + }) +} + +#[must_use] +struct Thunk(F); + +impl Thunk { + fn new(f: F) -> Self + where + F: ContFn, + { + Thunk(f) + } +} + +trait ContFn {} + +impl Continuation> ContFn for F {} + +struct Continuation; + +impl Continuation { + fn reify_as(&mut self) {} +} + +fn main() {} diff --git a/src/test/ui/impl-trait/hidden-type-is-opaque-2.stderr b/src/test/ui/impl-trait/hidden-type-is-opaque-2.stderr new file mode 100644 index 0000000000000..e538aaeb4c5ed --- /dev/null +++ b/src/test/ui/impl-trait/hidden-type-is-opaque-2.stderr @@ -0,0 +1,11 @@ +error[E0282]: type annotations needed + --> $DIR/hidden-type-is-opaque-2.rs:6:17 + | +LL | Thunk::new(|mut cont| { + | ^^^^^^^^ consider giving this closure parameter a type + | + = note: type must be known at this point + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0282`. diff --git a/src/test/ui/impl-trait/hidden-type-is-opaque.rs b/src/test/ui/impl-trait/hidden-type-is-opaque.rs new file mode 100644 index 0000000000000..b302ae36ef42c --- /dev/null +++ b/src/test/ui/impl-trait/hidden-type-is-opaque.rs @@ -0,0 +1,32 @@ +// check-pass + +fn reify_as() -> Thunk { + Thunk::new(|mut cont| { + cont.reify_as(); + cont + }) +} + +#[must_use] +struct Thunk(F); + +impl Thunk { + fn new(f: F) -> Self + where + F: FnOnce(Continuation) -> Continuation, + { + Thunk(f) + } +} + +trait ContFn {} + +impl Continuation> ContFn for F {} + +struct Continuation; + +impl Continuation { + fn reify_as(&mut self) {} +} + +fn main() {} diff --git a/src/test/ui/impl-trait/issues/issue-70877.rs b/src/test/ui/impl-trait/issues/issue-70877.rs index 8169cfafac711..1a86fa00ed1a0 100644 --- a/src/test/ui/impl-trait/issues/issue-70877.rs +++ b/src/test/ui/impl-trait/issues/issue-70877.rs @@ -13,7 +13,7 @@ impl Iterator for Bar { type Item = FooItem; fn next(&mut self) -> Option { - Some(Box::new(quux)) + Some(Box::new(quux)) //~ ERROR mismatched types } } diff --git a/src/test/ui/impl-trait/issues/issue-70877.stderr b/src/test/ui/impl-trait/issues/issue-70877.stderr index 8813bff3c353e..7cbd58bdabf2f 100644 --- a/src/test/ui/impl-trait/issues/issue-70877.stderr +++ b/src/test/ui/impl-trait/issues/issue-70877.stderr @@ -1,3 +1,17 @@ +error[E0308]: mismatched types + --> $DIR/issue-70877.rs:16:9 + | +LL | type FooRet = impl std::fmt::Debug; + | -------------------- the expected opaque type +... +LL | fn next(&mut self) -> Option { + | ------------------ expected `Option Fn(&'r (dyn ToString + 'r)) -> FooRet + 'static)>>` because of return type +LL | Some(Box::new(quux)) + | ^^^^^^^^^^^^^^^^^^^^ expected trait object `dyn Fn`, found fn item + | + = note: expected enum `Option Fn(&'r (dyn ToString + 'r)) -> FooRet + 'static)>>` + found enum `Option fn(&'r (dyn ToString + 'r)) -> FooRet {quux}>>` + error: opaque type's hidden type cannot be another opaque type from the same scope --> $DIR/issue-70877.rs:31:12 | @@ -15,5 +29,6 @@ note: opaque type being used as hidden type LL | type FooRet = impl std::fmt::Debug; | ^^^^^^^^^^^^^^^^^^^^ -error: aborting due to previous error +error: aborting due to 2 previous errors +For more information about this error, try `rustc --explain E0308`.