Auto merge of #115538 - lcnr:fn-def-wf, r=compiler-errors

check `FnDef` return type for WF

better version of #106807, fixes #84533 (mostly). It's not perfect given that we still ignore WF requirements involving bound regions but I wasn't able to quickly write an example, so even if theoretically exploitable, it should be far harder to trigger.

This is strictly more restrictive than checking the return type for WF as part of the builtin `FnDef: FnOnce` impl (#106807) and moving to this approach in the future will not break any code.

~~It also agrees with my theoretical view of how this should behave~~

r? types
This commit is contained in:
bors 2024-04-04 08:43:53 +00:00
commit 4c6c629866
8 changed files with 153 additions and 45 deletions

View file

@ -647,6 +647,8 @@ impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> {
fn visit_ty(&mut self, t: <TyCtxt<'tcx> as ty::Interner>::Ty) -> Self::Result {
debug!("wf bounds for t={:?} t.kind={:#?}", t, t.kind());
let tcx = self.tcx();
match *t.kind() {
ty::Bool
| ty::Char
@ -707,6 +709,16 @@ impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> {
}
ty::FnDef(did, args) => {
// HACK: Check the return type of function definitions for
// well-formedness to mostly fix #84533. This is still not
// perfect and there may be ways to abuse the fact that we
// ignore requirements with escaping bound vars. That's a
// more general issue however.
//
// FIXME(eddyb) add the type to `walker` instead of recursing.
let fn_sig = tcx.fn_sig(did).instantiate(tcx, args);
fn_sig.output().skip_binder().visit_with(self);
let obligations = self.nominal_obligations(did, args);
self.out.extend(obligations);
}
@ -716,7 +728,7 @@ impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> {
if !r.has_escaping_bound_vars() && !rty.has_escaping_bound_vars() {
let cause = self.cause(traits::ReferenceOutlivesReferent(t));
self.out.push(traits::Obligation::with_depth(
self.tcx(),
tcx,
cause,
self.recursion_depth,
self.param_env,
@ -805,12 +817,12 @@ impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> {
// obligations that don't refer to Self and
// checking those
let defer_to_coercion = self.tcx().features().object_safe_for_dispatch;
let defer_to_coercion = tcx.features().object_safe_for_dispatch;
if !defer_to_coercion {
if let Some(principal) = data.principal_def_id() {
self.out.push(traits::Obligation::with_depth(
self.tcx(),
tcx,
self.cause(traits::WellFormed(None)),
self.recursion_depth,
self.param_env,
@ -835,7 +847,7 @@ impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> {
ty::Infer(_) => {
let cause = self.cause(traits::WellFormed(None));
self.out.push(traits::Obligation::with_depth(
self.tcx(),
tcx,
cause,
self.recursion_depth,
self.param_env,
@ -850,6 +862,8 @@ impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> {
}
fn visit_const(&mut self, c: <TyCtxt<'tcx> as ty::Interner>::Const) -> Self::Result {
let tcx = self.tcx();
match c.kind() {
ty::ConstKind::Unevaluated(uv) => {
if !c.has_escaping_bound_vars() {
@ -861,7 +875,7 @@ impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> {
));
let cause = self.cause(traits::WellFormed(None));
self.out.push(traits::Obligation::with_depth(
self.tcx(),
tcx,
cause,
self.recursion_depth,
self.param_env,
@ -873,7 +887,7 @@ impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> {
let cause = self.cause(traits::WellFormed(None));
self.out.push(traits::Obligation::with_depth(
self.tcx(),
tcx,
cause,
self.recursion_depth,
self.param_env,
@ -895,7 +909,7 @@ impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> {
));
let cause = self.cause(traits::WellFormed(None));
self.out.push(traits::Obligation::with_depth(
self.tcx(),
tcx,
cause,
self.recursion_depth,
self.param_env,