mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-01 23:12:02 +00:00
Auto merge of #107867 - compiler-errors:new-solver-fn-trait-safety, r=lcnr
Check that built-in callable types validate their output type is `Sized` (in new solver) Working on parity with old solver. Putting this up for consideration, it's not *really* needed or anything just yet. Maybe it's better to approach this from another direction (like always checking the item bounds when calling `consider_assumption`? we may need that for coinduction to be sound though?) This basically implements #100096 for the new solver.
This commit is contained in:
commit
fcdbd1c07f
@ -90,18 +90,22 @@ pub(super) trait GoalKind<'tcx>: TypeFoldable<'tcx> + Copy + Eq {
|
||||
|
||||
fn trait_def_id(self, tcx: TyCtxt<'tcx>) -> DefId;
|
||||
|
||||
// Consider a clause, which consists of a "assumption" and some "requirements",
|
||||
// to satisfy a goal. If the requirements hold, then attempt to satisfy our
|
||||
// goal by equating it with the assumption.
|
||||
fn consider_implied_clause(
|
||||
ecx: &mut EvalCtxt<'_, 'tcx>,
|
||||
goal: Goal<'tcx, Self>,
|
||||
assumption: ty::Predicate<'tcx>,
|
||||
requirements: impl IntoIterator<Item = Goal<'tcx, ty::Predicate<'tcx>>>,
|
||||
) -> QueryResult<'tcx>;
|
||||
|
||||
fn consider_impl_candidate(
|
||||
ecx: &mut EvalCtxt<'_, 'tcx>,
|
||||
goal: Goal<'tcx, Self>,
|
||||
impl_def_id: DefId,
|
||||
) -> QueryResult<'tcx>;
|
||||
|
||||
fn consider_assumption(
|
||||
ecx: &mut EvalCtxt<'_, 'tcx>,
|
||||
goal: Goal<'tcx, Self>,
|
||||
assumption: ty::Predicate<'tcx>,
|
||||
) -> QueryResult<'tcx>;
|
||||
|
||||
// A type implements an `auto trait` if its components do as well. These components
|
||||
// are given by built-in rules from [`instantiate_constituent_tys_for_auto_trait`].
|
||||
fn consider_auto_trait_candidate(
|
||||
@ -355,7 +359,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||
candidates: &mut Vec<Candidate<'tcx>>,
|
||||
) {
|
||||
for (i, assumption) in goal.param_env.caller_bounds().iter().enumerate() {
|
||||
match G::consider_assumption(self, goal, assumption) {
|
||||
match G::consider_implied_clause(self, goal, assumption, []) {
|
||||
Ok(result) => {
|
||||
candidates.push(Candidate { source: CandidateSource::ParamEnv(i), result })
|
||||
}
|
||||
@ -402,7 +406,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||
|
||||
for assumption in self.tcx().item_bounds(alias_ty.def_id).subst(self.tcx(), alias_ty.substs)
|
||||
{
|
||||
match G::consider_assumption(self, goal, assumption) {
|
||||
match G::consider_implied_clause(self, goal, assumption, []) {
|
||||
Ok(result) => {
|
||||
candidates.push(Candidate { source: CandidateSource::AliasBound, result })
|
||||
}
|
||||
@ -452,7 +456,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||
for assumption in
|
||||
elaborate_predicates(tcx, bounds.iter().map(|bound| bound.with_self_ty(tcx, self_ty)))
|
||||
{
|
||||
match G::consider_assumption(self, goal, assumption.predicate) {
|
||||
match G::consider_implied_clause(self, goal, assumption.predicate, []) {
|
||||
Ok(result) => {
|
||||
candidates.push(Candidate { source: CandidateSource::BuiltinImpl, result })
|
||||
}
|
||||
|
@ -168,6 +168,37 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
|
||||
self.trait_def_id(tcx)
|
||||
}
|
||||
|
||||
fn consider_implied_clause(
|
||||
ecx: &mut EvalCtxt<'_, 'tcx>,
|
||||
goal: Goal<'tcx, Self>,
|
||||
assumption: ty::Predicate<'tcx>,
|
||||
requirements: impl IntoIterator<Item = Goal<'tcx, ty::Predicate<'tcx>>>,
|
||||
) -> QueryResult<'tcx> {
|
||||
if let Some(poly_projection_pred) = assumption.to_opt_poly_projection_pred()
|
||||
&& poly_projection_pred.projection_def_id() == goal.predicate.def_id()
|
||||
{
|
||||
ecx.infcx.probe(|_| {
|
||||
let assumption_projection_pred =
|
||||
ecx.infcx.instantiate_binder_with_infer(poly_projection_pred);
|
||||
let mut nested_goals = ecx.infcx.eq(
|
||||
goal.param_env,
|
||||
goal.predicate.projection_ty,
|
||||
assumption_projection_pred.projection_ty,
|
||||
)?;
|
||||
nested_goals.extend(requirements);
|
||||
let subst_certainty = ecx.evaluate_all(nested_goals)?;
|
||||
|
||||
ecx.eq_term_and_make_canonical_response(
|
||||
goal,
|
||||
subst_certainty,
|
||||
assumption_projection_pred.term,
|
||||
)
|
||||
})
|
||||
} else {
|
||||
Err(NoSolution)
|
||||
}
|
||||
}
|
||||
|
||||
fn consider_impl_candidate(
|
||||
ecx: &mut EvalCtxt<'_, 'tcx>,
|
||||
goal: Goal<'tcx, ProjectionPredicate<'tcx>>,
|
||||
@ -260,35 +291,6 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
|
||||
})
|
||||
}
|
||||
|
||||
fn consider_assumption(
|
||||
ecx: &mut EvalCtxt<'_, 'tcx>,
|
||||
goal: Goal<'tcx, Self>,
|
||||
assumption: ty::Predicate<'tcx>,
|
||||
) -> QueryResult<'tcx> {
|
||||
if let Some(poly_projection_pred) = assumption.to_opt_poly_projection_pred()
|
||||
&& poly_projection_pred.projection_def_id() == goal.predicate.def_id()
|
||||
{
|
||||
ecx.infcx.probe(|_| {
|
||||
let assumption_projection_pred =
|
||||
ecx.infcx.instantiate_binder_with_infer(poly_projection_pred);
|
||||
let nested_goals = ecx.infcx.eq(
|
||||
goal.param_env,
|
||||
goal.predicate.projection_ty,
|
||||
assumption_projection_pred.projection_ty,
|
||||
)?;
|
||||
let subst_certainty = ecx.evaluate_all(nested_goals)?;
|
||||
|
||||
ecx.eq_term_and_make_canonical_response(
|
||||
goal,
|
||||
subst_certainty,
|
||||
assumption_projection_pred.term,
|
||||
)
|
||||
})
|
||||
} else {
|
||||
Err(NoSolution)
|
||||
}
|
||||
}
|
||||
|
||||
fn consider_auto_trait_candidate(
|
||||
_ecx: &mut EvalCtxt<'_, 'tcx>,
|
||||
goal: Goal<'tcx, Self>,
|
||||
@ -329,25 +331,28 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
|
||||
goal: Goal<'tcx, Self>,
|
||||
goal_kind: ty::ClosureKind,
|
||||
) -> QueryResult<'tcx> {
|
||||
if let Some(tupled_inputs_and_output) =
|
||||
structural_traits::extract_tupled_inputs_and_output_from_callable(
|
||||
ecx.tcx(),
|
||||
goal.predicate.self_ty(),
|
||||
goal_kind,
|
||||
)?
|
||||
{
|
||||
let pred = tupled_inputs_and_output
|
||||
.map_bound(|(inputs, output)| ty::ProjectionPredicate {
|
||||
projection_ty: ecx
|
||||
.tcx()
|
||||
.mk_alias_ty(goal.predicate.def_id(), [goal.predicate.self_ty(), inputs]),
|
||||
term: output.into(),
|
||||
})
|
||||
.to_predicate(ecx.tcx());
|
||||
Self::consider_assumption(ecx, goal, pred)
|
||||
} else {
|
||||
ecx.make_canonical_response(Certainty::AMBIGUOUS)
|
||||
}
|
||||
let tcx = ecx.tcx();
|
||||
let Some(tupled_inputs_and_output) =
|
||||
structural_traits::extract_tupled_inputs_and_output_from_callable(
|
||||
tcx,
|
||||
goal.predicate.self_ty(),
|
||||
goal_kind,
|
||||
)? else {
|
||||
return ecx.make_canonical_response(Certainty::AMBIGUOUS);
|
||||
};
|
||||
let output_is_sized_pred = tupled_inputs_and_output
|
||||
.map_bound(|(_, output)| tcx.at(DUMMY_SP).mk_trait_ref(LangItem::Sized, [output]));
|
||||
|
||||
let pred = tupled_inputs_and_output
|
||||
.map_bound(|(inputs, output)| ty::ProjectionPredicate {
|
||||
projection_ty: tcx
|
||||
.mk_alias_ty(goal.predicate.def_id(), [goal.predicate.self_ty(), inputs]),
|
||||
term: output.into(),
|
||||
})
|
||||
.to_predicate(tcx);
|
||||
// A built-in `Fn` impl only holds if the output is sized.
|
||||
// (FIXME: technically we only need to check this if the type is a fn ptr...)
|
||||
Self::consider_implied_clause(ecx, goal, pred, [goal.with(tcx, output_is_sized_pred)])
|
||||
}
|
||||
|
||||
fn consider_builtin_tuple_candidate(
|
||||
@ -466,7 +471,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
|
||||
|
||||
let term = substs.as_generator().return_ty().into();
|
||||
|
||||
Self::consider_assumption(
|
||||
Self::consider_implied_clause(
|
||||
ecx,
|
||||
goal,
|
||||
ty::Binder::dummy(ty::ProjectionPredicate {
|
||||
@ -474,6 +479,9 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
|
||||
term,
|
||||
})
|
||||
.to_predicate(tcx),
|
||||
// Technically, we need to check that the future type is Sized,
|
||||
// but that's already proven by the generator being WF.
|
||||
[],
|
||||
)
|
||||
}
|
||||
|
||||
@ -503,7 +511,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
|
||||
bug!("unexpected associated item `<{self_ty} as Generator>::{name}`")
|
||||
};
|
||||
|
||||
Self::consider_assumption(
|
||||
Self::consider_implied_clause(
|
||||
ecx,
|
||||
goal,
|
||||
ty::Binder::dummy(ty::ProjectionPredicate {
|
||||
@ -513,6 +521,9 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
|
||||
term,
|
||||
})
|
||||
.to_predicate(tcx),
|
||||
// Technically, we need to check that the future type is Sized,
|
||||
// but that's already proven by the generator being WF.
|
||||
[],
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,7 @@ use super::assembly;
|
||||
use super::infcx_ext::InferCtxtExt;
|
||||
use super::{CanonicalResponse, Certainty, EvalCtxt, Goal, QueryResult};
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::LangItem;
|
||||
use rustc_infer::infer::InferCtxt;
|
||||
use rustc_infer::traits::query::NoSolution;
|
||||
use rustc_infer::traits::util::supertraits;
|
||||
@ -61,10 +62,11 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
|
||||
})
|
||||
}
|
||||
|
||||
fn consider_assumption(
|
||||
fn consider_implied_clause(
|
||||
ecx: &mut EvalCtxt<'_, 'tcx>,
|
||||
goal: Goal<'tcx, Self>,
|
||||
assumption: ty::Predicate<'tcx>,
|
||||
requirements: impl IntoIterator<Item = Goal<'tcx, ty::Predicate<'tcx>>>,
|
||||
) -> QueryResult<'tcx> {
|
||||
if let Some(poly_trait_pred) = assumption.to_opt_poly_trait_pred()
|
||||
&& poly_trait_pred.def_id() == goal.predicate.def_id()
|
||||
@ -73,11 +75,12 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
|
||||
ecx.infcx.probe(|_| {
|
||||
let assumption_trait_pred =
|
||||
ecx.infcx.instantiate_binder_with_infer(poly_trait_pred);
|
||||
let nested_goals = ecx.infcx.eq(
|
||||
let mut nested_goals = ecx.infcx.eq(
|
||||
goal.param_env,
|
||||
goal.predicate.trait_ref,
|
||||
assumption_trait_pred.trait_ref,
|
||||
)?;
|
||||
nested_goals.extend(requirements);
|
||||
ecx.evaluate_all_and_make_canonical_response(nested_goals)
|
||||
})
|
||||
} else {
|
||||
@ -173,23 +176,26 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
|
||||
goal: Goal<'tcx, Self>,
|
||||
goal_kind: ty::ClosureKind,
|
||||
) -> QueryResult<'tcx> {
|
||||
if let Some(tupled_inputs_and_output) =
|
||||
let tcx = ecx.tcx();
|
||||
let Some(tupled_inputs_and_output) =
|
||||
structural_traits::extract_tupled_inputs_and_output_from_callable(
|
||||
ecx.tcx(),
|
||||
tcx,
|
||||
goal.predicate.self_ty(),
|
||||
goal_kind,
|
||||
)?
|
||||
{
|
||||
let pred = tupled_inputs_and_output
|
||||
.map_bound(|(inputs, _)| {
|
||||
ecx.tcx()
|
||||
.mk_trait_ref(goal.predicate.def_id(), [goal.predicate.self_ty(), inputs])
|
||||
})
|
||||
.to_predicate(ecx.tcx());
|
||||
Self::consider_assumption(ecx, goal, pred)
|
||||
} else {
|
||||
ecx.make_canonical_response(Certainty::AMBIGUOUS)
|
||||
}
|
||||
)? else {
|
||||
return ecx.make_canonical_response(Certainty::AMBIGUOUS);
|
||||
};
|
||||
let output_is_sized_pred = tupled_inputs_and_output
|
||||
.map_bound(|(_, output)| tcx.at(DUMMY_SP).mk_trait_ref(LangItem::Sized, [output]));
|
||||
|
||||
let pred = tupled_inputs_and_output
|
||||
.map_bound(|(inputs, _)| {
|
||||
tcx.mk_trait_ref(goal.predicate.def_id(), [goal.predicate.self_ty(), inputs])
|
||||
})
|
||||
.to_predicate(tcx);
|
||||
// A built-in `Fn` impl only holds if the output is sized.
|
||||
// (FIXME: technically we only need to check this if the type is a fn ptr...)
|
||||
Self::consider_implied_clause(ecx, goal, pred, [goal.with(tcx, output_is_sized_pred)])
|
||||
}
|
||||
|
||||
fn consider_builtin_tuple_candidate(
|
||||
@ -225,6 +231,8 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
|
||||
}
|
||||
|
||||
// Async generator unconditionally implement `Future`
|
||||
// Technically, we need to check that the future output type is Sized,
|
||||
// but that's already proven by the generator being WF.
|
||||
ecx.make_canonical_response(Certainty::Yes)
|
||||
}
|
||||
|
||||
@ -244,13 +252,16 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
|
||||
}
|
||||
|
||||
let generator = substs.as_generator();
|
||||
Self::consider_assumption(
|
||||
Self::consider_implied_clause(
|
||||
ecx,
|
||||
goal,
|
||||
ty::Binder::dummy(
|
||||
tcx.mk_trait_ref(goal.predicate.def_id(), [self_ty, generator.resume_ty()]),
|
||||
)
|
||||
.to_predicate(tcx),
|
||||
// Technically, we need to check that the generator types are Sized,
|
||||
// but that's already proven by the generator being WF.
|
||||
[],
|
||||
)
|
||||
}
|
||||
|
||||
|
17
tests/ui/traits/new-solver/builtin-fn-must-return-sized.rs
Normal file
17
tests/ui/traits/new-solver/builtin-fn-must-return-sized.rs
Normal file
@ -0,0 +1,17 @@
|
||||
// compile-flags: -Ztrait-solver=next
|
||||
|
||||
#![feature(fn_traits)]
|
||||
#![feature(unboxed_closures)]
|
||||
#![feature(tuple_trait)]
|
||||
|
||||
use std::ops::Fn;
|
||||
use std::marker::Tuple;
|
||||
|
||||
fn foo<F: Fn<T>, T: Tuple>(f: Option<F>, t: T) {
|
||||
let y = (f.unwrap()).call(t);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
foo::<fn() -> str, _>(None, ());
|
||||
//~^ expected a `Fn<_>` closure, found `fn() -> str`
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
error[E0277]: expected a `Fn<_>` closure, found `fn() -> str`
|
||||
--> $DIR/builtin-fn-must-return-sized.rs:15:27
|
||||
|
|
||||
LL | foo::<fn() -> str, _>(None, ());
|
||||
| --------------------- ^^^^ expected an `Fn<_>` closure, found `fn() -> str`
|
||||
| |
|
||||
| required by a bound introduced by this call
|
||||
|
|
||||
= help: the trait `Fn<_>` is not implemented for `fn() -> str`
|
||||
note: required by a bound in `foo`
|
||||
--> $DIR/builtin-fn-must-return-sized.rs:10:11
|
||||
|
|
||||
LL | fn foo<F: Fn<T>, T: Tuple>(f: Option<F>, t: T) {
|
||||
| ^^^^^ required by this bound in `foo`
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0277`.
|
Loading…
Reference in New Issue
Block a user