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>,
|
ecx: &mut EvalCtxt<'_, 'tcx>,
|
||||||
goal: Goal<'tcx, Self>,
|
goal: Goal<'tcx, Self>,
|
||||||
) -> QueryResult<'tcx>;
|
) -> 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> {
|
impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||||
|
@ -137,7 +148,9 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||||
if goal.predicate.self_ty().is_ty_var() {
|
if goal.predicate.self_ty().is_ty_var() {
|
||||||
return vec![Candidate {
|
return vec![Candidate {
|
||||||
source: CandidateSource::BuiltinImpl,
|
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)
|
G::consider_builtin_copy_clone_candidate(self, goal)
|
||||||
} else if lang_items.pointer_sized() == Some(trait_def_id) {
|
} else if lang_items.pointer_sized() == Some(trait_def_id) {
|
||||||
G::consider_builtin_pointer_sized_candidate(self, goal)
|
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 {
|
} else {
|
||||||
Err(NoSolution)
|
Err(NoSolution)
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,6 +2,7 @@ use crate::traits::{specialization_graph, translate_substs};
|
||||||
|
|
||||||
use super::assembly::{self, Candidate, CandidateSource};
|
use super::assembly::{self, Candidate, CandidateSource};
|
||||||
use super::infcx_ext::InferCtxtExt;
|
use super::infcx_ext::InferCtxtExt;
|
||||||
|
use super::trait_goals::structural_traits;
|
||||||
use super::{Certainty, EvalCtxt, Goal, MaybeCause, QueryResult};
|
use super::{Certainty, EvalCtxt, Goal, MaybeCause, QueryResult};
|
||||||
use rustc_errors::ErrorGuaranteed;
|
use rustc_errors::ErrorGuaranteed;
|
||||||
use rustc_hir::def::DefKind;
|
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::specialization_graph::LeafDef;
|
||||||
use rustc_infer::traits::Reveal;
|
use rustc_infer::traits::Reveal;
|
||||||
use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
|
use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
|
||||||
use rustc_middle::ty::TypeVisitable;
|
|
||||||
use rustc_middle::ty::{self, Ty, TyCtxt};
|
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||||
use rustc_middle::ty::{ProjectionPredicate, TypeSuperVisitable, TypeVisitor};
|
use rustc_middle::ty::{ProjectionPredicate, TypeSuperVisitable, TypeVisitor};
|
||||||
|
use rustc_middle::ty::{ToPredicate, TypeVisitable};
|
||||||
use rustc_span::DUMMY_SP;
|
use rustc_span::DUMMY_SP;
|
||||||
use std::iter;
|
use std::iter;
|
||||||
use std::ops::ControlFlow;
|
use std::ops::ControlFlow;
|
||||||
|
@ -353,11 +354,44 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn consider_builtin_pointer_sized_candidate(
|
fn consider_builtin_pointer_sized_candidate(
|
||||||
ecx: &mut EvalCtxt<'_, 'tcx>,
|
_ecx: &mut EvalCtxt<'_, 'tcx>,
|
||||||
goal: Goal<'tcx, Self>,
|
goal: Goal<'tcx, Self>,
|
||||||
) -> QueryResult<'tcx> {
|
) -> QueryResult<'tcx> {
|
||||||
bug!("`PointerSized` does not have an associated type: {:?}", goal);
|
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.
|
/// 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::assembly::{self, Candidate, CandidateSource};
|
||||||
use super::infcx_ext::InferCtxtExt;
|
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_hir::def_id::DefId;
|
||||||
use rustc_infer::infer::InferCtxt;
|
use rustc_infer::infer::InferCtxt;
|
||||||
use rustc_infer::traits::query::NoSolution;
|
use rustc_infer::traits::query::NoSolution;
|
||||||
use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
|
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_middle::ty::{TraitPredicate, TypeVisitable};
|
||||||
use rustc_span::DUMMY_SP;
|
use rustc_span::DUMMY_SP;
|
||||||
|
|
||||||
mod structural_traits;
|
pub mod structural_traits;
|
||||||
|
|
||||||
impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
|
impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
|
||||||
fn self_ty(self) -> Ty<'tcx> {
|
fn self_ty(self) -> Ty<'tcx> {
|
||||||
|
@ -150,6 +150,41 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
|
||||||
Err(NoSolution)
|
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> {
|
impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use rustc_hir::{Movability, Mutability};
|
use rustc_hir::{Movability, Mutability};
|
||||||
use rustc_infer::{infer::InferCtxt, traits::query::NoSolution};
|
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.
|
// 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