Rollup merge of #107061 - compiler-errors:new-solver-new-candidates-3, r=lcnr
Implement some more new solver candidates and fix some bugs
First, fix some bugs:
1. `IndexVec::drain_enumerated(a..b)` does not give us an iterator of index keys + items enumerated from `a..b`, but from `0..(b-a)`... That caused a bug. See first commit for the fix.
2. Implement the `_: Trait` ambiguity hack. I put it in assemble, let me know if it should live elsewhere. This is important, since we otherwise consider `_: Sized` to have no solutions, and nothing passes!
3. Swap `Ambiguity` and `Unimplemented` cases for the new solver. Sorry for accidentally swapping them 😄
4. Check GATs' own predicates during projection confirmation.
Then implement a few builtin traits:
5. Implement `PointerSized`. Pretty independent.
6. Implement `Fn` family of traits for fnptr, fndef, and closures. Closures are currently broken because `FulfillCtxt::relationships` is intentionally left unimplemented. See comment in the test.
r? ```@lcnr```
This commit is contained in:
commit
3452104715
10 changed files with 272 additions and 21 deletions
|
@ -1,7 +1,7 @@
|
|||
//! Code shared by trait and projection goals for candidate assembly.
|
||||
|
||||
use super::infcx_ext::InferCtxtExt;
|
||||
use super::{CanonicalResponse, EvalCtxt, Goal, QueryResult};
|
||||
use super::{CanonicalResponse, Certainty, EvalCtxt, Goal, MaybeCause, QueryResult};
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_infer::traits::query::NoSolution;
|
||||
use rustc_infer::traits::util::elaborate_predicates;
|
||||
|
@ -79,7 +79,7 @@ pub(super) enum CandidateSource {
|
|||
AliasBound(usize),
|
||||
}
|
||||
|
||||
pub(super) trait GoalKind<'tcx>: TypeFoldable<'tcx> + Copy {
|
||||
pub(super) trait GoalKind<'tcx>: TypeFoldable<'tcx> + Copy + Eq {
|
||||
fn self_ty(self) -> Ty<'tcx>;
|
||||
|
||||
fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> Self;
|
||||
|
@ -117,6 +117,22 @@ pub(super) trait GoalKind<'tcx>: TypeFoldable<'tcx> + Copy {
|
|||
ecx: &mut EvalCtxt<'_, 'tcx>,
|
||||
goal: Goal<'tcx, Self>,
|
||||
) -> QueryResult<'tcx>;
|
||||
|
||||
fn consider_builtin_pointer_sized_candidate(
|
||||
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> {
|
||||
|
@ -124,6 +140,20 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
|||
&mut self,
|
||||
goal: Goal<'tcx, G>,
|
||||
) -> Vec<Candidate<'tcx>> {
|
||||
debug_assert_eq!(goal, self.infcx.resolve_vars_if_possible(goal));
|
||||
|
||||
// HACK: `_: Trait` is ambiguous, because it may be satisfied via a builtin rule,
|
||||
// object bound, alias bound, etc. We are unable to determine this until we can at
|
||||
// least structually resolve the type one layer.
|
||||
if goal.predicate.self_ty().is_ty_var() {
|
||||
return vec![Candidate {
|
||||
source: CandidateSource::BuiltinImpl,
|
||||
result: self
|
||||
.make_canonical_response(Certainty::Maybe(MaybeCause::Ambiguity))
|
||||
.unwrap(),
|
||||
}];
|
||||
}
|
||||
|
||||
let mut candidates = Vec::new();
|
||||
|
||||
self.assemble_candidates_after_normalizing_self_ty(goal, &mut candidates);
|
||||
|
@ -169,6 +199,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
|||
Ok((_, certainty)) => certainty,
|
||||
Err(NoSolution) => return,
|
||||
};
|
||||
let normalized_ty = self.infcx.resolve_vars_if_possible(normalized_ty);
|
||||
|
||||
// NOTE: Alternatively we could call `evaluate_goal` here and only have a `Normalized` candidate.
|
||||
// This doesn't work as long as we use `CandidateSource` in winnowing.
|
||||
|
@ -224,6 +255,12 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
|||
|| lang_items.clone_trait() == Some(trait_def_id)
|
||||
{
|
||||
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)
|
||||
};
|
||||
|
|
|
@ -52,7 +52,7 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
|
|||
.drain(..)
|
||||
.map(|obligation| FulfillmentError {
|
||||
obligation: obligation.clone(),
|
||||
code: FulfillmentErrorCode::CodeSelectionError(SelectionError::Unimplemented),
|
||||
code: FulfillmentErrorCode::CodeAmbiguity,
|
||||
root_obligation: obligation,
|
||||
})
|
||||
.collect()
|
||||
|
@ -75,7 +75,9 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
|
|||
Err(NoSolution) => {
|
||||
errors.push(FulfillmentError {
|
||||
obligation: obligation.clone(),
|
||||
code: FulfillmentErrorCode::CodeAmbiguity,
|
||||
code: FulfillmentErrorCode::CodeSelectionError(
|
||||
SelectionError::Unimplemented,
|
||||
),
|
||||
root_obligation: obligation,
|
||||
});
|
||||
continue;
|
||||
|
|
|
@ -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;
|
||||
|
@ -351,6 +352,46 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
|
|||
) -> QueryResult<'tcx> {
|
||||
bug!("`Copy`/`Clone` does not have an associated type: {:?}", goal);
|
||||
}
|
||||
|
||||
fn consider_builtin_pointer_sized_candidate(
|
||||
_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::{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::TraitPredicate;
|
||||
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> {
|
||||
|
@ -127,6 +127,64 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
|
|||
structural_traits::instantiate_constituent_tys_for_copy_clone_trait,
|
||||
)
|
||||
}
|
||||
|
||||
fn consider_builtin_pointer_sized_candidate(
|
||||
ecx: &mut EvalCtxt<'_, 'tcx>,
|
||||
goal: Goal<'tcx, Self>,
|
||||
) -> QueryResult<'tcx> {
|
||||
if goal.predicate.self_ty().has_non_region_infer() {
|
||||
return ecx.make_canonical_response(Certainty::Maybe(MaybeCause::Ambiguity));
|
||||
}
|
||||
|
||||
let tcx = ecx.tcx();
|
||||
let self_ty = tcx.erase_regions(goal.predicate.self_ty());
|
||||
|
||||
if let Ok(layout) = tcx.layout_of(goal.param_env.and(self_ty))
|
||||
&& let usize_layout = tcx.layout_of(ty::ParamEnv::empty().and(tcx.types.usize)).unwrap().layout
|
||||
&& layout.layout.size() == usize_layout.size()
|
||||
&& layout.layout.align().abi == usize_layout.align().abi
|
||||
{
|
||||
// FIXME: We could make this faster by making a no-constraints response
|
||||
ecx.make_canonical_response(Certainty::Yes)
|
||||
} else {
|
||||
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.
|
||||
//
|
||||
|
@ -30,10 +30,7 @@ pub(super) fn instantiate_constituent_tys_for_auto_trait<'tcx>(
|
|||
| ty::Foreign(..)
|
||||
| ty::Alias(ty::Projection, ..)
|
||||
| ty::Bound(..)
|
||||
| ty::Infer(ty::TyVar(_)) => {
|
||||
// FIXME: Do we need to mark anything as ambiguous here? Yeah?
|
||||
Err(NoSolution)
|
||||
}
|
||||
| ty::Infer(ty::TyVar(_)) => Err(NoSolution),
|
||||
|
||||
ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => bug!(),
|
||||
|
||||
|
@ -101,9 +98,8 @@ pub(super) fn instantiate_constituent_tys_for_sized_trait<'tcx>(
|
|||
| ty::Dynamic(..)
|
||||
| ty::Foreign(..)
|
||||
| ty::Alias(..)
|
||||
| ty::Param(_) => Err(NoSolution),
|
||||
|
||||
ty::Infer(ty::TyVar(_)) => bug!("FIXME: ambiguous"),
|
||||
| ty::Param(_)
|
||||
| ty::Infer(ty::TyVar(_)) => Err(NoSolution),
|
||||
|
||||
ty::Placeholder(..)
|
||||
| ty::Bound(..)
|
||||
|
@ -151,9 +147,8 @@ pub(super) fn instantiate_constituent_tys_for_copy_clone_trait<'tcx>(
|
|||
| ty::Ref(_, _, Mutability::Mut)
|
||||
| ty::Adt(_, _)
|
||||
| ty::Alias(_, _)
|
||||
| ty::Param(_) => Err(NoSolution),
|
||||
|
||||
ty::Infer(ty::TyVar(_)) => bug!("FIXME: ambiguous"),
|
||||
| ty::Param(_)
|
||||
| ty::Infer(ty::TyVar(_)) => Err(NoSolution),
|
||||
|
||||
ty::Placeholder(..)
|
||||
| ty::Bound(..)
|
||||
|
@ -177,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),
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue