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

Check that closures satisfy their where bounds #96899

Merged
merged 2 commits into from
May 13, 2022
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
107 changes: 1 addition & 106 deletions compiler/rustc_borrowck/src/region_infer/opaque_types.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::vec_map::VecMap;
use rustc_hir::def_id::DefId;
use rustc_hir::OpaqueTyOrigin;
use rustc_infer::infer::InferCtxt;
use rustc_middle::ty::subst::GenericArgKind;
use rustc_middle::ty::{self, OpaqueHiddenType, OpaqueTypeKey, TyCtxt, TypeFoldable};
use rustc_span::Span;
use rustc_trait_selection::opaque_types::InferCtxtExt;

use super::RegionInferenceContext;
Expand Down Expand Up @@ -107,21 +104,11 @@ impl<'tcx> RegionInferenceContext<'tcx> {

let opaque_type_key =
OpaqueTypeKey { def_id: opaque_type_key.def_id, substs: universal_substs };
let remapped_type = infcx.infer_opaque_definition_from_instantiation(
let ty = infcx.infer_opaque_definition_from_instantiation(
opaque_type_key,
universal_concrete_type,
origin,
);
let ty = if check_opaque_type_parameter_valid(
infcx.tcx,
opaque_type_key,
origin,
concrete_type.span,
) {
remapped_type
} else {
infcx.tcx.ty_error()
};
// Sometimes two opaque types are the same only after we remap the generic parameters
// back to the opaque type definition. E.g. we may have `OpaqueType<X, Y>` mapped to `(X, Y)`
// and `OpaqueType<Y, X>` mapped to `(Y, X)`, and those are the same, but we only know that
Expand Down Expand Up @@ -184,95 +171,3 @@ impl<'tcx> RegionInferenceContext<'tcx> {
})
}
}

fn check_opaque_type_parameter_valid(
tcx: TyCtxt<'_>,
opaque_type_key: OpaqueTypeKey<'_>,
origin: OpaqueTyOrigin,
span: Span,
) -> bool {
match origin {
// No need to check return position impl trait (RPIT)
// because for type and const parameters they are correct
// by construction: we convert
//
// fn foo<P0..Pn>() -> impl Trait
//
// into
//
// type Foo<P0...Pn>
// fn foo<P0..Pn>() -> Foo<P0...Pn>.
//
// For lifetime parameters we convert
//
// fn foo<'l0..'ln>() -> impl Trait<'l0..'lm>
//
// into
//
// type foo::<'p0..'pn>::Foo<'q0..'qm>
// fn foo<l0..'ln>() -> foo::<'static..'static>::Foo<'l0..'lm>.
//
// which would error here on all of the `'static` args.
OpaqueTyOrigin::FnReturn(..) | OpaqueTyOrigin::AsyncFn(..) => return true,
// Check these
OpaqueTyOrigin::TyAlias => {}
}
let opaque_generics = tcx.generics_of(opaque_type_key.def_id);
let mut seen_params: FxHashMap<_, Vec<_>> = FxHashMap::default();
for (i, arg) in opaque_type_key.substs.iter().enumerate() {
let arg_is_param = match arg.unpack() {
GenericArgKind::Type(ty) => matches!(ty.kind(), ty::Param(_)),
GenericArgKind::Lifetime(lt) if lt.is_static() => {
tcx.sess
.struct_span_err(span, "non-defining opaque type use in defining scope")
.span_label(
tcx.def_span(opaque_generics.param_at(i, tcx).def_id),
"cannot use static lifetime; use a bound lifetime \
instead or remove the lifetime parameter from the \
opaque type",
)
.emit();
return false;
}
GenericArgKind::Lifetime(lt) => {
matches!(*lt, ty::ReEarlyBound(_) | ty::ReFree(_))
}
GenericArgKind::Const(ct) => matches!(ct.val(), ty::ConstKind::Param(_)),
};

if arg_is_param {
seen_params.entry(arg).or_default().push(i);
} else {
// Prevent `fn foo() -> Foo<u32>` from being defining.
let opaque_param = opaque_generics.param_at(i, tcx);
tcx.sess
.struct_span_err(span, "non-defining opaque type use in defining scope")
.span_note(
tcx.def_span(opaque_param.def_id),
&format!(
"used non-generic {} `{}` for generic parameter",
opaque_param.kind.descr(),
arg,
),
)
.emit();
return false;
}
}

for (_, indices) in seen_params {
if indices.len() > 1 {
let descr = opaque_generics.param_at(indices[0], tcx).kind.descr();
let spans: Vec<_> = indices
.into_iter()
.map(|i| tcx.def_span(opaque_generics.param_at(i, tcx).def_id))
.collect();
tcx.sess
.struct_span_err(span, "non-defining opaque type use in defining scope")
.span_note(spans, &format!("{} used multiple times", descr))
.emit();
return false;
}
}
true
}
101 changes: 101 additions & 0 deletions compiler/rustc_trait_selection/src/opaque_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,15 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
));
debug!(?definition_ty);

if !check_opaque_type_parameter_valid(
self.tcx,
opaque_type_key,
origin,
instantiated_ty.span,
) {
return self.tcx.ty_error();
}

// Only check this for TAIT. RPIT already supports `src/test/ui/impl-trait/nested-return-type2.rs`
// on stable and we'd break that.
if let OpaqueTyOrigin::TyAlias = origin {
Expand Down Expand Up @@ -148,6 +157,98 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
}
}

fn check_opaque_type_parameter_valid(
tcx: TyCtxt<'_>,
opaque_type_key: OpaqueTypeKey<'_>,
origin: OpaqueTyOrigin,
span: Span,
) -> bool {
match origin {
// No need to check return position impl trait (RPIT)
// because for type and const parameters they are correct
// by construction: we convert
//
// fn foo<P0..Pn>() -> impl Trait
//
// into
//
// type Foo<P0...Pn>
// fn foo<P0..Pn>() -> Foo<P0...Pn>.
//
// For lifetime parameters we convert
//
// fn foo<'l0..'ln>() -> impl Trait<'l0..'lm>
//
// into
//
// type foo::<'p0..'pn>::Foo<'q0..'qm>
// fn foo<l0..'ln>() -> foo::<'static..'static>::Foo<'l0..'lm>.
//
// which would error here on all of the `'static` args.
OpaqueTyOrigin::FnReturn(..) | OpaqueTyOrigin::AsyncFn(..) => return true,
// Check these
OpaqueTyOrigin::TyAlias => {}
}
let opaque_generics = tcx.generics_of(opaque_type_key.def_id);
let mut seen_params: FxHashMap<_, Vec<_>> = FxHashMap::default();
for (i, arg) in opaque_type_key.substs.iter().enumerate() {
let arg_is_param = match arg.unpack() {
GenericArgKind::Type(ty) => matches!(ty.kind(), ty::Param(_)),
GenericArgKind::Lifetime(lt) if lt.is_static() => {
tcx.sess
.struct_span_err(span, "non-defining opaque type use in defining scope")
.span_label(
tcx.def_span(opaque_generics.param_at(i, tcx).def_id),
"cannot use static lifetime; use a bound lifetime \
instead or remove the lifetime parameter from the \
opaque type",
)
.emit();
return false;
}
GenericArgKind::Lifetime(lt) => {
matches!(*lt, ty::ReEarlyBound(_) | ty::ReFree(_))
}
GenericArgKind::Const(ct) => matches!(ct.val(), ty::ConstKind::Param(_)),
};

if arg_is_param {
seen_params.entry(arg).or_default().push(i);
} else {
// Prevent `fn foo() -> Foo<u32>` from being defining.
let opaque_param = opaque_generics.param_at(i, tcx);
tcx.sess
.struct_span_err(span, "non-defining opaque type use in defining scope")
.span_note(
tcx.def_span(opaque_param.def_id),
&format!(
"used non-generic {} `{}` for generic parameter",
opaque_param.kind.descr(),
arg,
),
)
.emit();
return false;
}
}

for (_, indices) in seen_params {
if indices.len() > 1 {
let descr = opaque_generics.param_at(indices[0], tcx).kind.descr();
let spans: Vec<_> = indices
.into_iter()
.map(|i| tcx.def_span(opaque_generics.param_at(i, tcx).def_id))
.collect();
tcx.sess
.struct_span_err(span, "non-defining opaque type use in defining scope")
.span_note(spans, &format!("{} used multiple times", descr))
.emit();
return false;
}
}
true
}

struct ReverseMapper<'tcx> {
tcx: TyCtxt<'tcx>,

Expand Down
24 changes: 16 additions & 8 deletions compiler/rustc_trait_selection/src/traits/wf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -575,7 +575,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
// generators don't take arguments.
}

ty::Closure(_, substs) => {
ty::Closure(did, substs) => {
// Only check the upvar types for WF, not the rest
// of the types within. This is needed because we
// capture the signature and it may not be WF
Expand All @@ -596,18 +596,26 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
// probably always be WF, because it should be
// shorthand for something like `where(T: 'a) {
// fn(&'a T) }`, as discussed in #25860.
//
// Note that we are also skipping the generic
// types. This is consistent with the `outlives`
// code, but anyway doesn't matter: within the fn
walker.skip_current_subtree(); // subtree handled below
// FIXME(eddyb) add the type to `walker` instead of recursing.
self.compute(substs.as_closure().tupled_upvars_ty().into());
// Note that we cannot skip the generic types
// types. Normally, within the fn
// body where they are created, the generics will
// always be WF, and outside of that fn body we
// are not directly inspecting closure types
// anyway, except via auto trait matching (which
// only inspects the upvar types).
walker.skip_current_subtree(); // subtree handled below
// FIXME(eddyb) add the type to `walker` instead of recursing.
self.compute(substs.as_closure().tupled_upvars_ty().into());
// But when a closure is part of a type-alias-impl-trait
// then the function that created the defining site may
// have had more bounds available than the type alias
// specifies. This may cause us to have a closure in the
// hidden type that is not actually well formed and
// can cause compiler crashes when the user abuses unsafe
// code to procure such a closure.
// See src/test/ui/type-alias-impl-trait/wf_check_closures.rs
let obligations = self.nominal_obligations(did, substs);
self.out.extend(obligations);
}

ty::FnPtr(_) => {
Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/const-generics/generic_const_exprs/closures.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#![feature(generic_const_exprs)]
#![allow(incomplete_features)]
fn test<const N: usize>() -> [u8; N + (|| 42)()] {}
//~^ ERROR overly complex generic constant
//~^ ERROR cycle detected when building an abstract representation

fn main() {}
25 changes: 19 additions & 6 deletions src/test/ui/const-generics/generic_const_exprs/closures.stderr
Original file line number Diff line number Diff line change
@@ -1,13 +1,26 @@
error: overly complex generic constant
error[E0391]: cycle detected when building an abstract representation for test::{constant#0}
--> $DIR/closures.rs:3:35
|
LL | fn test<const N: usize>() -> [u8; N + (|| 42)()] {}
| ^^^^-------^^
| |
| borrowing is not supported in generic constants
| ^^^^^^^^^^^^^
|
= help: consider moving this anonymous constant into a `const` function
= note: this operation may be supported in the future
note: ...which requires building THIR for `test::{constant#0}`...
--> $DIR/closures.rs:3:35
|
LL | fn test<const N: usize>() -> [u8; N + (|| 42)()] {}
| ^^^^^^^^^^^^^
note: ...which requires type-checking `test::{constant#0}`...
--> $DIR/closures.rs:3:35
|
LL | fn test<const N: usize>() -> [u8; N + (|| 42)()] {}
| ^^^^^^^^^^^^^
= note: ...which again requires building an abstract representation for test::{constant#0}, completing the cycle
note: cycle used when checking that `test` is well-formed
--> $DIR/closures.rs:3:1
|
LL | fn test<const N: usize>() -> [u8; N + (|| 42)()] {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to previous error

For more information about this error, try `rustc --explain E0391`.
18 changes: 18 additions & 0 deletions src/test/ui/higher-rank-trait-bounds/issue-59311.nll.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
error: higher-ranked lifetime error
--> $DIR/issue-59311.rs:17:5
|
LL | v.t(|| {});
| ^^^^^^^^^^
|
= note: could not prove [closure@$DIR/issue-59311.rs:17:9: 17:14] well-formed

error: higher-ranked lifetime error
--> $DIR/issue-59311.rs:17:9
|
LL | v.t(|| {});
| ^^^^^
|
= note: could not prove for<'a> &'a V: 'static

error: aborting due to 2 previous errors

2 changes: 1 addition & 1 deletion src/test/ui/higher-rank-trait-bounds/issue-59311.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ pub fn crash<V>(v: &V)
where
for<'a> &'a V: T + 'static,
{
v.t(|| {}); //~ ERROR: higher-ranked lifetime error
v.t(|| {}); //~ ERROR: `&'a V` does not fulfill the required lifetime
}

fn main() {}
13 changes: 9 additions & 4 deletions src/test/ui/higher-rank-trait-bounds/issue-59311.stderr
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
error: higher-ranked lifetime error
--> $DIR/issue-59311.rs:17:9
error[E0477]: the type `&'a V` does not fulfill the required lifetime
--> $DIR/issue-59311.rs:17:5
|
LL | v.t(|| {});
| ^^^^^
| ^^^^^^^^^^
|
= note: could not prove for<'a> &'a V: 'static
note: type must satisfy the static lifetime as required by this binding
--> $DIR/issue-59311.rs:15:24
|
LL | for<'a> &'a V: T + 'static,
| ^^^^^^^

error: aborting due to previous error

For more information about this error, try `rustc --explain E0477`.
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ type TwoConsts<const X: usize, const Y: usize> = impl Debug;
fn one_ty<T: Debug>(t: T) -> TwoTys<T, T> {
t
//~^ ERROR non-defining opaque type use in defining scope
//~| ERROR `U` doesn't implement `Debug`
}

fn one_lifetime<'a>(t: &'a u32) -> TwoLifetimes<'a, 'a> {
Expand Down
Loading