Auto merge of #144704 - compiler-errors:explode-wf, r=lcnr

expand WF obligations when checking method calls

Don't wrap a bunch of signatures in `FnPtr` then check their WF; instead, check the WFness of each input/output separately.

This is useful for the new trait solver, since because we stall on root obligations we end up needing to repeatedly recompute the WFness of possibly very large function signature types if it ends up bottoming out in ambiguity.

This may also give us more chances to hit the WF fast path for certain types like built-ins.

Finally, this just seems conceptually correct to do. There's nothing conceptually that suggests that wrapping the function signature in an fn pointer makes sense at all to do; I'm guessing that it was just convenient so that we didn't have to register WF obligations in a loop, but it doesn't affect the readability of this code at all.
This commit is contained in:
bors
2025-08-03 09:29:54 +00:00
3 changed files with 30 additions and 35 deletions

View File

@@ -356,14 +356,14 @@ fn compare_method_predicate_entailment<'tcx>(
} }
if !(impl_sig, trait_sig).references_error() { if !(impl_sig, trait_sig).references_error() {
ocx.register_obligation(traits::Obligation::new( for ty in unnormalized_impl_sig.inputs_and_output {
infcx.tcx, ocx.register_obligation(traits::Obligation::new(
cause, infcx.tcx,
param_env, cause.clone(),
ty::ClauseKind::WellFormed( param_env,
Ty::new_fn_ptr(tcx, ty::Binder::dummy(unnormalized_impl_sig)).into(), ty::ClauseKind::WellFormed(ty.into()),
), ));
)); }
} }
// Check that all obligations are satisfied by the implementation's // Check that all obligations are satisfied by the implementation's

View File

@@ -142,7 +142,6 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
let (method_sig, method_predicates) = let (method_sig, method_predicates) =
self.normalize(self.span, (method_sig, method_predicates)); self.normalize(self.span, (method_sig, method_predicates));
let method_sig = ty::Binder::dummy(method_sig);
// Make sure nobody calls `drop()` explicitly. // Make sure nobody calls `drop()` explicitly.
self.check_for_illegal_method_calls(pick); self.check_for_illegal_method_calls(pick);
@@ -154,20 +153,11 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
// We won't add these if we encountered an illegal sized bound, so that we can use // We won't add these if we encountered an illegal sized bound, so that we can use
// a custom error in that case. // a custom error in that case.
if illegal_sized_bound.is_none() { if illegal_sized_bound.is_none() {
self.add_obligations( self.add_obligations(method_sig, all_args, method_predicates, pick.item.def_id);
Ty::new_fn_ptr(self.tcx, method_sig),
all_args,
method_predicates,
pick.item.def_id,
);
} }
// Create the final `MethodCallee`. // Create the final `MethodCallee`.
let callee = MethodCallee { let callee = MethodCallee { def_id: pick.item.def_id, args: all_args, sig: method_sig };
def_id: pick.item.def_id,
args: all_args,
sig: method_sig.skip_binder(),
};
ConfirmResult { callee, illegal_sized_bound } ConfirmResult { callee, illegal_sized_bound }
} }
@@ -601,14 +591,14 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
fn add_obligations( fn add_obligations(
&mut self, &mut self,
fty: Ty<'tcx>, sig: ty::FnSig<'tcx>,
all_args: GenericArgsRef<'tcx>, all_args: GenericArgsRef<'tcx>,
method_predicates: ty::InstantiatedPredicates<'tcx>, method_predicates: ty::InstantiatedPredicates<'tcx>,
def_id: DefId, def_id: DefId,
) { ) {
debug!( debug!(
"add_obligations: fty={:?} all_args={:?} method_predicates={:?} def_id={:?}", "add_obligations: sig={:?} all_args={:?} method_predicates={:?} def_id={:?}",
fty, all_args, method_predicates, def_id sig, all_args, method_predicates, def_id
); );
// FIXME: could replace with the following, but we already calculated `method_predicates`, // FIXME: could replace with the following, but we already calculated `method_predicates`,
@@ -637,7 +627,13 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
// the function type must also be well-formed (this is not // the function type must also be well-formed (this is not
// implied by the args being well-formed because of inherent // implied by the args being well-formed because of inherent
// impls and late-bound regions - see issue #28609). // impls and late-bound regions - see issue #28609).
self.register_wf_obligation(fty.into(), self.span, ObligationCauseCode::WellFormed(None)); for ty in sig.inputs_and_output {
self.register_wf_obligation(
ty.into(),
self.span,
ObligationCauseCode::WellFormed(None),
);
}
} }
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////

View File

@@ -428,19 +428,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
)); ));
// Also add an obligation for the method type being well-formed. // Also add an obligation for the method type being well-formed.
let method_ty = Ty::new_fn_ptr(tcx, ty::Binder::dummy(fn_sig));
debug!( debug!(
"lookup_method_in_trait: matched method method_ty={:?} obligation={:?}", "lookup_method_in_trait: matched method fn_sig={:?} obligation={:?}",
method_ty, obligation fn_sig, obligation
); );
obligations.push(traits::Obligation::new( for ty in fn_sig.inputs_and_output {
tcx, obligations.push(traits::Obligation::new(
obligation.cause, tcx,
self.param_env, obligation.cause.clone(),
ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed( self.param_env,
method_ty.into(), ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(ty.into()))),
))), ));
)); }
let callee = MethodCallee { def_id, args, sig: fn_sig }; let callee = MethodCallee { def_id, args, sig: fn_sig };
debug!("callee = {:?}", callee); debug!("callee = {:?}", callee);