Skip to content

Commit ed6aebb

Browse files
trait solver: Implement Fn traits and tuple trait
1 parent 69890b2 commit ed6aebb

File tree

6 files changed

+170
-7
lines changed

6 files changed

+170
-7
lines changed

compiler/rustc_trait_selection/src/solve/assembly.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,17 @@ pub(super) trait GoalKind<'tcx>: TypeFoldable<'tcx> + Copy + Eq {
122122
ecx: &mut EvalCtxt<'_, 'tcx>,
123123
goal: Goal<'tcx, Self>,
124124
) -> QueryResult<'tcx>;
125+
126+
fn consider_builtin_fn_trait_candidates(
127+
ecx: &mut EvalCtxt<'_, 'tcx>,
128+
goal: Goal<'tcx, Self>,
129+
kind: ty::ClosureKind,
130+
) -> QueryResult<'tcx>;
131+
132+
fn consider_builtin_tuple_candidate(
133+
ecx: &mut EvalCtxt<'_, 'tcx>,
134+
goal: Goal<'tcx, Self>,
135+
) -> QueryResult<'tcx>;
125136
}
126137

127138
impl<'tcx> EvalCtxt<'_, 'tcx> {
@@ -137,7 +148,9 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
137148
if goal.predicate.self_ty().is_ty_var() {
138149
return vec![Candidate {
139150
source: CandidateSource::BuiltinImpl,
140-
result: self.make_canonical_response(Certainty::Maybe(MaybeCause::Ambiguity)).unwrap(),
151+
result: self
152+
.make_canonical_response(Certainty::Maybe(MaybeCause::Ambiguity))
153+
.unwrap(),
141154
}];
142155
}
143156

@@ -244,6 +257,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
244257
G::consider_builtin_copy_clone_candidate(self, goal)
245258
} else if lang_items.pointer_sized() == Some(trait_def_id) {
246259
G::consider_builtin_pointer_sized_candidate(self, goal)
260+
} else if let Some(kind) = self.tcx().fn_trait_kind_from_def_id(trait_def_id) {
261+
G::consider_builtin_fn_trait_candidates(self, goal, kind)
262+
} else if lang_items.tuple_trait() == Some(trait_def_id) {
263+
G::consider_builtin_tuple_candidate(self, goal)
247264
} else {
248265
Err(NoSolution)
249266
};

compiler/rustc_trait_selection/src/solve/project_goals.rs

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use crate::traits::{specialization_graph, translate_substs};
22

33
use super::assembly::{self, Candidate, CandidateSource};
44
use super::infcx_ext::InferCtxtExt;
5+
use super::trait_goals::structural_traits;
56
use super::{Certainty, EvalCtxt, Goal, MaybeCause, QueryResult};
67
use rustc_errors::ErrorGuaranteed;
78
use rustc_hir::def::DefKind;
@@ -11,9 +12,9 @@ use rustc_infer::traits::query::NoSolution;
1112
use rustc_infer::traits::specialization_graph::LeafDef;
1213
use rustc_infer::traits::Reveal;
1314
use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
14-
use rustc_middle::ty::TypeVisitable;
1515
use rustc_middle::ty::{self, Ty, TyCtxt};
1616
use rustc_middle::ty::{ProjectionPredicate, TypeSuperVisitable, TypeVisitor};
17+
use rustc_middle::ty::{ToPredicate, TypeVisitable};
1718
use rustc_span::DUMMY_SP;
1819
use std::iter;
1920
use std::ops::ControlFlow;
@@ -353,11 +354,44 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
353354
}
354355

355356
fn consider_builtin_pointer_sized_candidate(
356-
ecx: &mut EvalCtxt<'_, 'tcx>,
357+
_ecx: &mut EvalCtxt<'_, 'tcx>,
357358
goal: Goal<'tcx, Self>,
358359
) -> QueryResult<'tcx> {
359360
bug!("`PointerSized` does not have an associated type: {:?}", goal);
360361
}
362+
363+
fn consider_builtin_fn_trait_candidates(
364+
ecx: &mut EvalCtxt<'_, 'tcx>,
365+
goal: Goal<'tcx, Self>,
366+
goal_kind: ty::ClosureKind,
367+
) -> QueryResult<'tcx> {
368+
if let Some(tupled_inputs_and_output) =
369+
structural_traits::extract_tupled_inputs_and_output_from_callable(
370+
ecx.tcx(),
371+
goal.predicate.self_ty(),
372+
goal_kind,
373+
)?
374+
{
375+
let pred = tupled_inputs_and_output
376+
.map_bound(|(inputs, output)| ty::ProjectionPredicate {
377+
projection_ty: ecx
378+
.tcx()
379+
.mk_alias_ty(goal.predicate.def_id(), [goal.predicate.self_ty(), inputs]),
380+
term: output.into(),
381+
})
382+
.to_predicate(ecx.tcx());
383+
Self::consider_assumption(ecx, goal, pred)
384+
} else {
385+
ecx.make_canonical_response(Certainty::Maybe(MaybeCause::Ambiguity))
386+
}
387+
}
388+
389+
fn consider_builtin_tuple_candidate(
390+
_ecx: &mut EvalCtxt<'_, 'tcx>,
391+
goal: Goal<'tcx, Self>,
392+
) -> QueryResult<'tcx> {
393+
bug!("`Tuple` does not have an associated type: {:?}", goal);
394+
}
361395
}
362396

363397
/// This behavior is also implemented in `rustc_ty_utils` and in the old `project` code.

compiler/rustc_trait_selection/src/solve/trait_goals.rs

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,16 @@ use std::iter;
44

55
use super::assembly::{self, Candidate, CandidateSource};
66
use super::infcx_ext::InferCtxtExt;
7-
use super::{Certainty, EvalCtxt, Goal, QueryResult};
7+
use super::{Certainty, EvalCtxt, Goal, MaybeCause, QueryResult};
88
use rustc_hir::def_id::DefId;
99
use rustc_infer::infer::InferCtxt;
1010
use rustc_infer::traits::query::NoSolution;
1111
use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
12-
use rustc_middle::ty::{self, Ty, TyCtxt};
12+
use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt};
1313
use rustc_middle::ty::{TraitPredicate, TypeVisitable};
1414
use rustc_span::DUMMY_SP;
1515

16-
mod structural_traits;
16+
pub mod structural_traits;
1717

1818
impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
1919
fn self_ty(self) -> Ty<'tcx> {
@@ -150,6 +150,41 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
150150
Err(NoSolution)
151151
}
152152
}
153+
154+
fn consider_builtin_fn_trait_candidates(
155+
ecx: &mut EvalCtxt<'_, 'tcx>,
156+
goal: Goal<'tcx, Self>,
157+
goal_kind: ty::ClosureKind,
158+
) -> QueryResult<'tcx> {
159+
if let Some(tupled_inputs_and_output) =
160+
structural_traits::extract_tupled_inputs_and_output_from_callable(
161+
ecx.tcx(),
162+
goal.predicate.self_ty(),
163+
goal_kind,
164+
)?
165+
{
166+
let pred = tupled_inputs_and_output
167+
.map_bound(|(inputs, _)| {
168+
ecx.tcx()
169+
.mk_trait_ref(goal.predicate.def_id(), [goal.predicate.self_ty(), inputs])
170+
})
171+
.to_predicate(ecx.tcx());
172+
Self::consider_assumption(ecx, goal, pred)
173+
} else {
174+
ecx.make_canonical_response(Certainty::Maybe(MaybeCause::Ambiguity))
175+
}
176+
}
177+
178+
fn consider_builtin_tuple_candidate(
179+
ecx: &mut EvalCtxt<'_, 'tcx>,
180+
goal: Goal<'tcx, Self>,
181+
) -> QueryResult<'tcx> {
182+
if let ty::Tuple(..) = goal.predicate.self_ty().kind() {
183+
ecx.make_canonical_response(Certainty::Yes)
184+
} else {
185+
Err(NoSolution)
186+
}
187+
}
153188
}
154189

155190
impl<'tcx> EvalCtxt<'_, 'tcx> {

compiler/rustc_trait_selection/src/solve/trait_goals/structural_traits.rs

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use rustc_hir::{Movability, Mutability};
22
use rustc_infer::{infer::InferCtxt, traits::query::NoSolution};
3-
use rustc_middle::ty::{self, Ty};
3+
use rustc_middle::ty::{self, Ty, TyCtxt};
44

55
// Calculates the constituent types of a type for `auto trait` purposes.
66
//
@@ -172,3 +172,52 @@ pub(super) fn instantiate_constituent_tys_for_copy_clone_trait<'tcx>(
172172
}
173173
}
174174
}
175+
176+
pub(crate) fn extract_tupled_inputs_and_output_from_callable<'tcx>(
177+
tcx: TyCtxt<'tcx>,
178+
self_ty: Ty<'tcx>,
179+
goal_kind: ty::ClosureKind,
180+
) -> Result<Option<ty::Binder<'tcx, (Ty<'tcx>, Ty<'tcx>)>>, NoSolution> {
181+
match *self_ty.kind() {
182+
ty::FnDef(def_id, substs) => Ok(Some(
183+
tcx.bound_fn_sig(def_id)
184+
.subst(tcx, substs)
185+
.map_bound(|sig| (tcx.mk_tup(sig.inputs().iter()), sig.output())),
186+
)),
187+
ty::FnPtr(sig) => {
188+
Ok(Some(sig.map_bound(|sig| (tcx.mk_tup(sig.inputs().iter()), sig.output()))))
189+
}
190+
ty::Closure(_, substs) => {
191+
let closure_substs = substs.as_closure();
192+
match closure_substs.kind_ty().to_opt_closure_kind() {
193+
Some(closure_kind) if closure_kind.extends(goal_kind) => {}
194+
None => return Ok(None),
195+
_ => return Err(NoSolution),
196+
}
197+
Ok(Some(closure_substs.sig().map_bound(|sig| (sig.inputs()[0], sig.output()))))
198+
}
199+
ty::Bool
200+
| ty::Char
201+
| ty::Int(_)
202+
| ty::Uint(_)
203+
| ty::Float(_)
204+
| ty::Adt(_, _)
205+
| ty::Foreign(_)
206+
| ty::Str
207+
| ty::Array(_, _)
208+
| ty::Slice(_)
209+
| ty::RawPtr(_)
210+
| ty::Ref(_, _, _)
211+
| ty::Dynamic(_, _, _)
212+
| ty::Generator(_, _, _)
213+
| ty::GeneratorWitness(_)
214+
| ty::Never
215+
| ty::Tuple(_)
216+
| ty::Alias(_, _)
217+
| ty::Param(_)
218+
| ty::Placeholder(_)
219+
| ty::Bound(_, _)
220+
| ty::Infer(_)
221+
| ty::Error(_) => Err(NoSolution),
222+
}
223+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// compile-flags: -Ztrait-solver=next
2+
// known-bug: unknown
3+
// failure-status: 101
4+
// dont-check-compiler-stderr
5+
6+
// This test will fail until we fix `FulfillmentCtxt::relationships`. That's
7+
// because we create a type variable for closure upvar types, which is not
8+
// constrained until after we try to do fallback on diverging type variables.
9+
// Thus, we will call that function, which is unimplemented.
10+
11+
fn require_fn(_: impl Fn() -> i32) {}
12+
13+
fn main() {
14+
require_fn(|| -> i32 { 1i32 });
15+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// compile-flags: -Ztrait-solver=next
2+
// check-pass
3+
4+
fn require_fn(_: impl Fn() -> i32) {}
5+
6+
fn f() -> i32 {
7+
1i32
8+
}
9+
10+
fn main() {
11+
require_fn(f);
12+
require_fn(f as fn() -> i32);
13+
}

0 commit comments

Comments
 (0)