Skip to content

Commit fcdbd1c

Browse files
committed
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.
2 parents f77f4d5 + 6402c98 commit fcdbd1c

File tree

5 files changed

+135
-74
lines changed

5 files changed

+135
-74
lines changed

compiler/rustc_trait_selection/src/solve/assembly.rs

+11-7
Original file line numberDiff line numberDiff line change
@@ -90,16 +90,20 @@ pub(super) trait GoalKind<'tcx>: TypeFoldable<'tcx> + Copy + Eq {
9090

9191
fn trait_def_id(self, tcx: TyCtxt<'tcx>) -> DefId;
9292

93-
fn consider_impl_candidate(
93+
// Consider a clause, which consists of a "assumption" and some "requirements",
94+
// to satisfy a goal. If the requirements hold, then attempt to satisfy our
95+
// goal by equating it with the assumption.
96+
fn consider_implied_clause(
9497
ecx: &mut EvalCtxt<'_, 'tcx>,
9598
goal: Goal<'tcx, Self>,
96-
impl_def_id: DefId,
99+
assumption: ty::Predicate<'tcx>,
100+
requirements: impl IntoIterator<Item = Goal<'tcx, ty::Predicate<'tcx>>>,
97101
) -> QueryResult<'tcx>;
98102

99-
fn consider_assumption(
103+
fn consider_impl_candidate(
100104
ecx: &mut EvalCtxt<'_, 'tcx>,
101105
goal: Goal<'tcx, Self>,
102-
assumption: ty::Predicate<'tcx>,
106+
impl_def_id: DefId,
103107
) -> QueryResult<'tcx>;
104108

105109
// A type implements an `auto trait` if its components do as well. These components
@@ -355,7 +359,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
355359
candidates: &mut Vec<Candidate<'tcx>>,
356360
) {
357361
for (i, assumption) in goal.param_env.caller_bounds().iter().enumerate() {
358-
match G::consider_assumption(self, goal, assumption) {
362+
match G::consider_implied_clause(self, goal, assumption, []) {
359363
Ok(result) => {
360364
candidates.push(Candidate { source: CandidateSource::ParamEnv(i), result })
361365
}
@@ -402,7 +406,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
402406

403407
for assumption in self.tcx().item_bounds(alias_ty.def_id).subst(self.tcx(), alias_ty.substs)
404408
{
405-
match G::consider_assumption(self, goal, assumption) {
409+
match G::consider_implied_clause(self, goal, assumption, []) {
406410
Ok(result) => {
407411
candidates.push(Candidate { source: CandidateSource::AliasBound, result })
408412
}
@@ -452,7 +456,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
452456
for assumption in
453457
elaborate_predicates(tcx, bounds.iter().map(|bound| bound.with_self_ty(tcx, self_ty)))
454458
{
455-
match G::consider_assumption(self, goal, assumption.predicate) {
459+
match G::consider_implied_clause(self, goal, assumption.predicate, []) {
456460
Ok(result) => {
457461
candidates.push(Candidate { source: CandidateSource::BuiltinImpl, result })
458462
}

compiler/rustc_trait_selection/src/solve/project_goals.rs

+61-50
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,37 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
168168
self.trait_def_id(tcx)
169169
}
170170

171+
fn consider_implied_clause(
172+
ecx: &mut EvalCtxt<'_, 'tcx>,
173+
goal: Goal<'tcx, Self>,
174+
assumption: ty::Predicate<'tcx>,
175+
requirements: impl IntoIterator<Item = Goal<'tcx, ty::Predicate<'tcx>>>,
176+
) -> QueryResult<'tcx> {
177+
if let Some(poly_projection_pred) = assumption.to_opt_poly_projection_pred()
178+
&& poly_projection_pred.projection_def_id() == goal.predicate.def_id()
179+
{
180+
ecx.infcx.probe(|_| {
181+
let assumption_projection_pred =
182+
ecx.infcx.instantiate_binder_with_infer(poly_projection_pred);
183+
let mut nested_goals = ecx.infcx.eq(
184+
goal.param_env,
185+
goal.predicate.projection_ty,
186+
assumption_projection_pred.projection_ty,
187+
)?;
188+
nested_goals.extend(requirements);
189+
let subst_certainty = ecx.evaluate_all(nested_goals)?;
190+
191+
ecx.eq_term_and_make_canonical_response(
192+
goal,
193+
subst_certainty,
194+
assumption_projection_pred.term,
195+
)
196+
})
197+
} else {
198+
Err(NoSolution)
199+
}
200+
}
201+
171202
fn consider_impl_candidate(
172203
ecx: &mut EvalCtxt<'_, 'tcx>,
173204
goal: Goal<'tcx, ProjectionPredicate<'tcx>>,
@@ -260,35 +291,6 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
260291
})
261292
}
262293

263-
fn consider_assumption(
264-
ecx: &mut EvalCtxt<'_, 'tcx>,
265-
goal: Goal<'tcx, Self>,
266-
assumption: ty::Predicate<'tcx>,
267-
) -> QueryResult<'tcx> {
268-
if let Some(poly_projection_pred) = assumption.to_opt_poly_projection_pred()
269-
&& poly_projection_pred.projection_def_id() == goal.predicate.def_id()
270-
{
271-
ecx.infcx.probe(|_| {
272-
let assumption_projection_pred =
273-
ecx.infcx.instantiate_binder_with_infer(poly_projection_pred);
274-
let nested_goals = ecx.infcx.eq(
275-
goal.param_env,
276-
goal.predicate.projection_ty,
277-
assumption_projection_pred.projection_ty,
278-
)?;
279-
let subst_certainty = ecx.evaluate_all(nested_goals)?;
280-
281-
ecx.eq_term_and_make_canonical_response(
282-
goal,
283-
subst_certainty,
284-
assumption_projection_pred.term,
285-
)
286-
})
287-
} else {
288-
Err(NoSolution)
289-
}
290-
}
291-
292294
fn consider_auto_trait_candidate(
293295
_ecx: &mut EvalCtxt<'_, 'tcx>,
294296
goal: Goal<'tcx, Self>,
@@ -329,25 +331,28 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
329331
goal: Goal<'tcx, Self>,
330332
goal_kind: ty::ClosureKind,
331333
) -> QueryResult<'tcx> {
332-
if let Some(tupled_inputs_and_output) =
333-
structural_traits::extract_tupled_inputs_and_output_from_callable(
334-
ecx.tcx(),
335-
goal.predicate.self_ty(),
336-
goal_kind,
337-
)?
338-
{
339-
let pred = tupled_inputs_and_output
340-
.map_bound(|(inputs, output)| ty::ProjectionPredicate {
341-
projection_ty: ecx
342-
.tcx()
343-
.mk_alias_ty(goal.predicate.def_id(), [goal.predicate.self_ty(), inputs]),
344-
term: output.into(),
345-
})
346-
.to_predicate(ecx.tcx());
347-
Self::consider_assumption(ecx, goal, pred)
348-
} else {
349-
ecx.make_canonical_response(Certainty::AMBIGUOUS)
350-
}
334+
let tcx = ecx.tcx();
335+
let Some(tupled_inputs_and_output) =
336+
structural_traits::extract_tupled_inputs_and_output_from_callable(
337+
tcx,
338+
goal.predicate.self_ty(),
339+
goal_kind,
340+
)? else {
341+
return ecx.make_canonical_response(Certainty::AMBIGUOUS);
342+
};
343+
let output_is_sized_pred = tupled_inputs_and_output
344+
.map_bound(|(_, output)| tcx.at(DUMMY_SP).mk_trait_ref(LangItem::Sized, [output]));
345+
346+
let pred = tupled_inputs_and_output
347+
.map_bound(|(inputs, output)| ty::ProjectionPredicate {
348+
projection_ty: tcx
349+
.mk_alias_ty(goal.predicate.def_id(), [goal.predicate.self_ty(), inputs]),
350+
term: output.into(),
351+
})
352+
.to_predicate(tcx);
353+
// A built-in `Fn` impl only holds if the output is sized.
354+
// (FIXME: technically we only need to check this if the type is a fn ptr...)
355+
Self::consider_implied_clause(ecx, goal, pred, [goal.with(tcx, output_is_sized_pred)])
351356
}
352357

353358
fn consider_builtin_tuple_candidate(
@@ -466,14 +471,17 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
466471

467472
let term = substs.as_generator().return_ty().into();
468473

469-
Self::consider_assumption(
474+
Self::consider_implied_clause(
470475
ecx,
471476
goal,
472477
ty::Binder::dummy(ty::ProjectionPredicate {
473478
projection_ty: ecx.tcx().mk_alias_ty(goal.predicate.def_id(), [self_ty]),
474479
term,
475480
})
476481
.to_predicate(tcx),
482+
// Technically, we need to check that the future type is Sized,
483+
// but that's already proven by the generator being WF.
484+
[],
477485
)
478486
}
479487

@@ -503,7 +511,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
503511
bug!("unexpected associated item `<{self_ty} as Generator>::{name}`")
504512
};
505513

506-
Self::consider_assumption(
514+
Self::consider_implied_clause(
507515
ecx,
508516
goal,
509517
ty::Binder::dummy(ty::ProjectionPredicate {
@@ -513,6 +521,9 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
513521
term,
514522
})
515523
.to_predicate(tcx),
524+
// Technically, we need to check that the future type is Sized,
525+
// but that's already proven by the generator being WF.
526+
[],
516527
)
517528
}
518529

compiler/rustc_trait_selection/src/solve/trait_goals.rs

+28-17
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use super::assembly;
66
use super::infcx_ext::InferCtxtExt;
77
use super::{CanonicalResponse, Certainty, EvalCtxt, Goal, QueryResult};
88
use rustc_hir::def_id::DefId;
9+
use rustc_hir::LangItem;
910
use rustc_infer::infer::InferCtxt;
1011
use rustc_infer::traits::query::NoSolution;
1112
use rustc_infer::traits::util::supertraits;
@@ -61,10 +62,11 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
6162
})
6263
}
6364

64-
fn consider_assumption(
65+
fn consider_implied_clause(
6566
ecx: &mut EvalCtxt<'_, 'tcx>,
6667
goal: Goal<'tcx, Self>,
6768
assumption: ty::Predicate<'tcx>,
69+
requirements: impl IntoIterator<Item = Goal<'tcx, ty::Predicate<'tcx>>>,
6870
) -> QueryResult<'tcx> {
6971
if let Some(poly_trait_pred) = assumption.to_opt_poly_trait_pred()
7072
&& poly_trait_pred.def_id() == goal.predicate.def_id()
@@ -73,11 +75,12 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
7375
ecx.infcx.probe(|_| {
7476
let assumption_trait_pred =
7577
ecx.infcx.instantiate_binder_with_infer(poly_trait_pred);
76-
let nested_goals = ecx.infcx.eq(
78+
let mut nested_goals = ecx.infcx.eq(
7779
goal.param_env,
7880
goal.predicate.trait_ref,
7981
assumption_trait_pred.trait_ref,
8082
)?;
83+
nested_goals.extend(requirements);
8184
ecx.evaluate_all_and_make_canonical_response(nested_goals)
8285
})
8386
} else {
@@ -173,23 +176,26 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
173176
goal: Goal<'tcx, Self>,
174177
goal_kind: ty::ClosureKind,
175178
) -> QueryResult<'tcx> {
176-
if let Some(tupled_inputs_and_output) =
179+
let tcx = ecx.tcx();
180+
let Some(tupled_inputs_and_output) =
177181
structural_traits::extract_tupled_inputs_and_output_from_callable(
178-
ecx.tcx(),
182+
tcx,
179183
goal.predicate.self_ty(),
180184
goal_kind,
181-
)?
182-
{
183-
let pred = tupled_inputs_and_output
184-
.map_bound(|(inputs, _)| {
185-
ecx.tcx()
186-
.mk_trait_ref(goal.predicate.def_id(), [goal.predicate.self_ty(), inputs])
187-
})
188-
.to_predicate(ecx.tcx());
189-
Self::consider_assumption(ecx, goal, pred)
190-
} else {
191-
ecx.make_canonical_response(Certainty::AMBIGUOUS)
192-
}
185+
)? else {
186+
return ecx.make_canonical_response(Certainty::AMBIGUOUS);
187+
};
188+
let output_is_sized_pred = tupled_inputs_and_output
189+
.map_bound(|(_, output)| tcx.at(DUMMY_SP).mk_trait_ref(LangItem::Sized, [output]));
190+
191+
let pred = tupled_inputs_and_output
192+
.map_bound(|(inputs, _)| {
193+
tcx.mk_trait_ref(goal.predicate.def_id(), [goal.predicate.self_ty(), inputs])
194+
})
195+
.to_predicate(tcx);
196+
// A built-in `Fn` impl only holds if the output is sized.
197+
// (FIXME: technically we only need to check this if the type is a fn ptr...)
198+
Self::consider_implied_clause(ecx, goal, pred, [goal.with(tcx, output_is_sized_pred)])
193199
}
194200

195201
fn consider_builtin_tuple_candidate(
@@ -225,6 +231,8 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
225231
}
226232

227233
// Async generator unconditionally implement `Future`
234+
// Technically, we need to check that the future output type is Sized,
235+
// but that's already proven by the generator being WF.
228236
ecx.make_canonical_response(Certainty::Yes)
229237
}
230238

@@ -244,13 +252,16 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
244252
}
245253

246254
let generator = substs.as_generator();
247-
Self::consider_assumption(
255+
Self::consider_implied_clause(
248256
ecx,
249257
goal,
250258
ty::Binder::dummy(
251259
tcx.mk_trait_ref(goal.predicate.def_id(), [self_ty, generator.resume_ty()]),
252260
)
253261
.to_predicate(tcx),
262+
// Technically, we need to check that the generator types are Sized,
263+
// but that's already proven by the generator being WF.
264+
[],
254265
)
255266
}
256267

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// compile-flags: -Ztrait-solver=next
2+
3+
#![feature(fn_traits)]
4+
#![feature(unboxed_closures)]
5+
#![feature(tuple_trait)]
6+
7+
use std::ops::Fn;
8+
use std::marker::Tuple;
9+
10+
fn foo<F: Fn<T>, T: Tuple>(f: Option<F>, t: T) {
11+
let y = (f.unwrap()).call(t);
12+
}
13+
14+
fn main() {
15+
foo::<fn() -> str, _>(None, ());
16+
//~^ expected a `Fn<_>` closure, found `fn() -> str`
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
error[E0277]: expected a `Fn<_>` closure, found `fn() -> str`
2+
--> $DIR/builtin-fn-must-return-sized.rs:15:27
3+
|
4+
LL | foo::<fn() -> str, _>(None, ());
5+
| --------------------- ^^^^ expected an `Fn<_>` closure, found `fn() -> str`
6+
| |
7+
| required by a bound introduced by this call
8+
|
9+
= help: the trait `Fn<_>` is not implemented for `fn() -> str`
10+
note: required by a bound in `foo`
11+
--> $DIR/builtin-fn-must-return-sized.rs:10:11
12+
|
13+
LL | fn foo<F: Fn<T>, T: Tuple>(f: Option<F>, t: T) {
14+
| ^^^^^ required by this bound in `foo`
15+
16+
error: aborting due to previous error
17+
18+
For more information about this error, try `rustc --explain E0277`.

0 commit comments

Comments
 (0)