trait solver: Implement Fn traits and tuple trait
This commit is contained in:
parent
69890b2df4
commit
ed6aebbfec
6 changed files with 170 additions and 7 deletions
|
@ -122,6 +122,17 @@ pub(super) trait GoalKind<'tcx>: TypeFoldable<'tcx> + Copy + Eq {
|
|||
ecx: &mut EvalCtxt<'_, 'tcx>,
|
||||
goal: Goal<'tcx, Self>,
|
||||
) -> QueryResult<'tcx>;
|
||||
|
||||
fn consider_builtin_fn_trait_candidates(
|
||||
ecx: &mut EvalCtxt<'_, 'tcx>,
|
||||
goal: Goal<'tcx, Self>,
|
||||
kind: ty::ClosureKind,
|
||||
) -> QueryResult<'tcx>;
|
||||
|
||||
fn consider_builtin_tuple_candidate(
|
||||
ecx: &mut EvalCtxt<'_, 'tcx>,
|
||||
goal: Goal<'tcx, Self>,
|
||||
) -> QueryResult<'tcx>;
|
||||
}
|
||||
|
||||
impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||
|
@ -137,7 +148,9 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
|||
if goal.predicate.self_ty().is_ty_var() {
|
||||
return vec![Candidate {
|
||||
source: CandidateSource::BuiltinImpl,
|
||||
result: self.make_canonical_response(Certainty::Maybe(MaybeCause::Ambiguity)).unwrap(),
|
||||
result: self
|
||||
.make_canonical_response(Certainty::Maybe(MaybeCause::Ambiguity))
|
||||
.unwrap(),
|
||||
}];
|
||||
}
|
||||
|
||||
|
@ -244,6 +257,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
|||
G::consider_builtin_copy_clone_candidate(self, goal)
|
||||
} else if lang_items.pointer_sized() == Some(trait_def_id) {
|
||||
G::consider_builtin_pointer_sized_candidate(self, goal)
|
||||
} else if let Some(kind) = self.tcx().fn_trait_kind_from_def_id(trait_def_id) {
|
||||
G::consider_builtin_fn_trait_candidates(self, goal, kind)
|
||||
} else if lang_items.tuple_trait() == Some(trait_def_id) {
|
||||
G::consider_builtin_tuple_candidate(self, goal)
|
||||
} else {
|
||||
Err(NoSolution)
|
||||
};
|
||||
|
|
|
@ -2,6 +2,7 @@ use crate::traits::{specialization_graph, translate_substs};
|
|||
|
||||
use super::assembly::{self, Candidate, CandidateSource};
|
||||
use super::infcx_ext::InferCtxtExt;
|
||||
use super::trait_goals::structural_traits;
|
||||
use super::{Certainty, EvalCtxt, Goal, MaybeCause, QueryResult};
|
||||
use rustc_errors::ErrorGuaranteed;
|
||||
use rustc_hir::def::DefKind;
|
||||
|
@ -11,9 +12,9 @@ use rustc_infer::traits::query::NoSolution;
|
|||
use rustc_infer::traits::specialization_graph::LeafDef;
|
||||
use rustc_infer::traits::Reveal;
|
||||
use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
|
||||
use rustc_middle::ty::TypeVisitable;
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||
use rustc_middle::ty::{ProjectionPredicate, TypeSuperVisitable, TypeVisitor};
|
||||
use rustc_middle::ty::{ToPredicate, TypeVisitable};
|
||||
use rustc_span::DUMMY_SP;
|
||||
use std::iter;
|
||||
use std::ops::ControlFlow;
|
||||
|
@ -353,11 +354,44 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
|
|||
}
|
||||
|
||||
fn consider_builtin_pointer_sized_candidate(
|
||||
ecx: &mut EvalCtxt<'_, 'tcx>,
|
||||
_ecx: &mut EvalCtxt<'_, 'tcx>,
|
||||
goal: Goal<'tcx, Self>,
|
||||
) -> QueryResult<'tcx> {
|
||||
bug!("`PointerSized` does not have an associated type: {:?}", goal);
|
||||
}
|
||||
|
||||
fn consider_builtin_fn_trait_candidates(
|
||||
ecx: &mut EvalCtxt<'_, '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::Maybe(MaybeCause::Ambiguity))
|
||||
}
|
||||
}
|
||||
|
||||
fn consider_builtin_tuple_candidate(
|
||||
_ecx: &mut EvalCtxt<'_, 'tcx>,
|
||||
goal: Goal<'tcx, Self>,
|
||||
) -> QueryResult<'tcx> {
|
||||
bug!("`Tuple` does not have an associated type: {:?}", goal);
|
||||
}
|
||||
}
|
||||
|
||||
/// This behavior is also implemented in `rustc_ty_utils` and in the old `project` code.
|
||||
|
|
|
@ -4,16 +4,16 @@ use std::iter;
|
|||
|
||||
use super::assembly::{self, Candidate, CandidateSource};
|
||||
use super::infcx_ext::InferCtxtExt;
|
||||
use super::{Certainty, EvalCtxt, Goal, QueryResult};
|
||||
use super::{Certainty, EvalCtxt, Goal, MaybeCause, QueryResult};
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_infer::infer::InferCtxt;
|
||||
use rustc_infer::traits::query::NoSolution;
|
||||
use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||
use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt};
|
||||
use rustc_middle::ty::{TraitPredicate, TypeVisitable};
|
||||
use rustc_span::DUMMY_SP;
|
||||
|
||||
mod structural_traits;
|
||||
pub mod structural_traits;
|
||||
|
||||
impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
|
||||
fn self_ty(self) -> Ty<'tcx> {
|
||||
|
@ -150,6 +150,41 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
|
|||
Err(NoSolution)
|
||||
}
|
||||
}
|
||||
|
||||
fn consider_builtin_fn_trait_candidates(
|
||||
ecx: &mut EvalCtxt<'_, '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, _)| {
|
||||
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::Maybe(MaybeCause::Ambiguity))
|
||||
}
|
||||
}
|
||||
|
||||
fn consider_builtin_tuple_candidate(
|
||||
ecx: &mut EvalCtxt<'_, 'tcx>,
|
||||
goal: Goal<'tcx, Self>,
|
||||
) -> QueryResult<'tcx> {
|
||||
if let ty::Tuple(..) = goal.predicate.self_ty().kind() {
|
||||
ecx.make_canonical_response(Certainty::Yes)
|
||||
} else {
|
||||
Err(NoSolution)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use rustc_hir::{Movability, Mutability};
|
||||
use rustc_infer::{infer::InferCtxt, traits::query::NoSolution};
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||
|
||||
// Calculates the constituent types of a type for `auto trait` purposes.
|
||||
//
|
||||
|
@ -172,3 +172,52 @@ pub(super) fn instantiate_constituent_tys_for_copy_clone_trait<'tcx>(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn extract_tupled_inputs_and_output_from_callable<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
self_ty: Ty<'tcx>,
|
||||
goal_kind: ty::ClosureKind,
|
||||
) -> Result<Option<ty::Binder<'tcx, (Ty<'tcx>, Ty<'tcx>)>>, NoSolution> {
|
||||
match *self_ty.kind() {
|
||||
ty::FnDef(def_id, substs) => Ok(Some(
|
||||
tcx.bound_fn_sig(def_id)
|
||||
.subst(tcx, substs)
|
||||
.map_bound(|sig| (tcx.mk_tup(sig.inputs().iter()), sig.output())),
|
||||
)),
|
||||
ty::FnPtr(sig) => {
|
||||
Ok(Some(sig.map_bound(|sig| (tcx.mk_tup(sig.inputs().iter()), sig.output()))))
|
||||
}
|
||||
ty::Closure(_, substs) => {
|
||||
let closure_substs = substs.as_closure();
|
||||
match closure_substs.kind_ty().to_opt_closure_kind() {
|
||||
Some(closure_kind) if closure_kind.extends(goal_kind) => {}
|
||||
None => return Ok(None),
|
||||
_ => return Err(NoSolution),
|
||||
}
|
||||
Ok(Some(closure_substs.sig().map_bound(|sig| (sig.inputs()[0], sig.output()))))
|
||||
}
|
||||
ty::Bool
|
||||
| ty::Char
|
||||
| ty::Int(_)
|
||||
| ty::Uint(_)
|
||||
| ty::Float(_)
|
||||
| ty::Adt(_, _)
|
||||
| ty::Foreign(_)
|
||||
| ty::Str
|
||||
| ty::Array(_, _)
|
||||
| ty::Slice(_)
|
||||
| ty::RawPtr(_)
|
||||
| ty::Ref(_, _, _)
|
||||
| ty::Dynamic(_, _, _)
|
||||
| ty::Generator(_, _, _)
|
||||
| ty::GeneratorWitness(_)
|
||||
| ty::Never
|
||||
| ty::Tuple(_)
|
||||
| ty::Alias(_, _)
|
||||
| ty::Param(_)
|
||||
| ty::Placeholder(_)
|
||||
| ty::Bound(_, _)
|
||||
| ty::Infer(_)
|
||||
| ty::Error(_) => Err(NoSolution),
|
||||
}
|
||||
}
|
||||
|
|
15
tests/ui/traits/new-solver/fn-trait-closure.rs
Normal file
15
tests/ui/traits/new-solver/fn-trait-closure.rs
Normal file
|
@ -0,0 +1,15 @@
|
|||
// compile-flags: -Ztrait-solver=next
|
||||
// known-bug: unknown
|
||||
// failure-status: 101
|
||||
// dont-check-compiler-stderr
|
||||
|
||||
// This test will fail until we fix `FulfillmentCtxt::relationships`. That's
|
||||
// because we create a type variable for closure upvar types, which is not
|
||||
// constrained until after we try to do fallback on diverging type variables.
|
||||
// Thus, we will call that function, which is unimplemented.
|
||||
|
||||
fn require_fn(_: impl Fn() -> i32) {}
|
||||
|
||||
fn main() {
|
||||
require_fn(|| -> i32 { 1i32 });
|
||||
}
|
13
tests/ui/traits/new-solver/fn-trait.rs
Normal file
13
tests/ui/traits/new-solver/fn-trait.rs
Normal file
|
@ -0,0 +1,13 @@
|
|||
// compile-flags: -Ztrait-solver=next
|
||||
// check-pass
|
||||
|
||||
fn require_fn(_: impl Fn() -> i32) {}
|
||||
|
||||
fn f() -> i32 {
|
||||
1i32
|
||||
}
|
||||
|
||||
fn main() {
|
||||
require_fn(f);
|
||||
require_fn(f as fn() -> i32);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue