Skip to content

Commit

Permalink
Fix regression from lazy opaque types
Browse files Browse the repository at this point in the history
  • Loading branch information
oli-obk committed Feb 8, 2022
1 parent 775e480 commit 239f1e7
Show file tree
Hide file tree
Showing 7 changed files with 126 additions and 10 deletions.
13 changes: 6 additions & 7 deletions compiler/rustc_typeck/src/check/closure.rs
Expand Up @@ -259,22 +259,21 @@ 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<Span>,
projection: ty::PolyProjectionPredicate<'tcx>,
) -> Option<ExpectedSig<'tcx>> {
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;
}

Expand All @@ -283,15 +282,15 @@ 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;
}
}

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::<Vec<_>>(),
Expand All @@ -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(),
Expand All @@ -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 })
}
Expand Down
27 changes: 26 additions & 1 deletion compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs
Expand Up @@ -730,7 +730,32 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
) -> Vec<Ty<'tcx>> {
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
Expand Down
34 changes: 34 additions & 0 deletions 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<impl FnOnce(Continuation) -> Continuation> {
Thunk::new(|mut cont| { //~ ERROR type annotations needed
cont.reify_as();
cont
})
}

#[must_use]
struct Thunk<F>(F);

impl<F> Thunk<F> {
fn new(f: F) -> Self
where
F: ContFn,
{
Thunk(f)
}
}

trait ContFn {}

impl<F: FnOnce(Continuation) -> Continuation> ContFn for F {}

struct Continuation;

impl Continuation {
fn reify_as(&mut self) {}
}

fn main() {}
11 changes: 11 additions & 0 deletions 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`.
32 changes: 32 additions & 0 deletions src/test/ui/impl-trait/hidden-type-is-opaque.rs
@@ -0,0 +1,32 @@
// check-pass

fn reify_as() -> Thunk<impl ContFn> {
Thunk::new(|mut cont| {
cont.reify_as();
cont
})
}

#[must_use]
struct Thunk<F>(F);

impl<F> Thunk<F> {
fn new(f: F) -> Self
where
F: FnOnce(Continuation) -> Continuation,
{
Thunk(f)
}
}

trait ContFn {}

impl<F: FnOnce(Continuation) -> Continuation> ContFn for F {}

struct Continuation;

impl Continuation {
fn reify_as(&mut self) {}
}

fn main() {}
2 changes: 1 addition & 1 deletion src/test/ui/impl-trait/issues/issue-70877.rs
Expand Up @@ -13,7 +13,7 @@ impl Iterator for Bar {
type Item = FooItem;

fn next(&mut self) -> Option<Self::Item> {
Some(Box::new(quux))
Some(Box::new(quux)) //~ ERROR mismatched types
}
}

Expand Down
17 changes: 16 additions & 1 deletion 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<Self::Item> {
| ------------------ expected `Option<Box<(dyn for<'r> 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<Box<(dyn for<'r> Fn(&'r (dyn ToString + 'r)) -> FooRet + 'static)>>`
found enum `Option<Box<for<'r> 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
|
Expand All @@ -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`.

0 comments on commit 239f1e7

Please sign in to comment.