1
Fork 0

Implement ~const Fn trait goals in the new solver

This commit is contained in:
Michael Goulet 2024-10-29 17:51:38 +00:00
parent 89b6885529
commit def7ed08e7
11 changed files with 193 additions and 109 deletions

View file

@ -633,6 +633,76 @@ fn coroutine_closure_to_ambiguous_coroutine<I: Interner>(
)
}
/// This duplicates `extract_tupled_inputs_and_output_from_callable` but needs
/// to return different information (namely, the def id and args) so that we can
/// create const conditions.
///
/// Doing so on all calls to `extract_tupled_inputs_and_output_from_callable`
/// would be wasteful.
pub(in crate::solve) fn extract_fn_def_from_const_callable<I: Interner>(
cx: I,
self_ty: I::Ty,
) -> Result<(ty::Binder<I, (I::FnInputTys, I::Ty)>, I::DefId, I::GenericArgs), NoSolution> {
match self_ty.kind() {
ty::FnDef(def_id, args) => {
let sig = cx.fn_sig(def_id);
if sig.skip_binder().is_fn_trait_compatible()
&& !cx.has_target_features(def_id)
&& cx.fn_is_const(def_id)
{
Ok((
sig.instantiate(cx, args).map_bound(|sig| (sig.inputs(), sig.output())),
def_id,
args,
))
} else {
return Err(NoSolution);
}
}
// `FnPtr`s are not const for now.
ty::FnPtr(..) => {
return Err(NoSolution);
}
// `Closure`s are not const for now.
ty::Closure(..) => {
return Err(NoSolution);
}
// `CoroutineClosure`s are not const for now.
ty::CoroutineClosure(..) => {
return Err(NoSolution);
}
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::Coroutine(_, _)
| ty::CoroutineWitness(..)
| ty::Never
| ty::Tuple(_)
| ty::Pat(_, _)
| ty::Alias(_, _)
| ty::Param(_)
| ty::Placeholder(..)
| ty::Infer(ty::IntVar(_) | ty::FloatVar(_))
| ty::Error(_) => return Err(NoSolution),
ty::Bound(..)
| ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
panic!("unexpected type `{self_ty:?}`")
}
}
}
/// Assemble a list of predicates that would be present on a theoretical
/// user impl for an object type. These predicates must be checked any time
/// we assemble a built-in object candidate for an object type, since they

View file

@ -3,15 +3,15 @@
use rustc_type_ir::fast_reject::DeepRejectCtxt;
use rustc_type_ir::inherent::*;
use rustc_type_ir::lang_items::TraitSolverLangItem;
use rustc_type_ir::{self as ty, Interner, elaborate};
use tracing::instrument;
use super::assembly::Candidate;
use super::assembly::{Candidate, structural_traits};
use crate::delegate::SolverDelegate;
use crate::solve::assembly::{self};
use crate::solve::{
BuiltinImplSource, CandidateSource, Certainty, EvalCtxt, Goal, GoalSource, NoSolution,
QueryResult,
QueryResult, assembly,
};
impl<D, I> assembly::GoalKind<D> for ty::HostEffectPredicate<I>
@ -142,7 +142,7 @@ where
ty::ImplPolarity::Positive => {}
};
if !cx.is_const_impl(impl_def_id) {
if !cx.impl_is_const(impl_def_id) {
return Err(NoSolution);
}
@ -207,7 +207,7 @@ where
_ecx: &mut EvalCtxt<'_, D>,
_goal: Goal<I, Self>,
) -> Result<Candidate<I>, NoSolution> {
todo!("Copy/Clone is not yet const")
Err(NoSolution)
}
fn consider_builtin_pointer_like_candidate(
@ -225,11 +225,48 @@ where
}
fn consider_builtin_fn_trait_candidates(
_ecx: &mut EvalCtxt<'_, D>,
_goal: Goal<I, Self>,
ecx: &mut EvalCtxt<'_, D>,
goal: Goal<I, Self>,
_kind: rustc_type_ir::ClosureKind,
) -> Result<Candidate<I>, NoSolution> {
todo!("Fn* are not yet const")
let cx = ecx.cx();
let self_ty = goal.predicate.self_ty();
let (inputs_and_output, def_id, args) =
structural_traits::extract_fn_def_from_const_callable(cx, self_ty)?;
// A built-in `Fn` impl only holds if the output is sized.
// (FIXME: technically we only need to check this if the type is a fn ptr...)
let output_is_sized_pred = inputs_and_output.map_bound(|(_, output)| {
ty::TraitRef::new(cx, cx.require_lang_item(TraitSolverLangItem::Sized), [output])
});
let requirements = cx
.const_conditions(def_id)
.iter_instantiated(cx, args)
.map(|trait_ref| {
(
GoalSource::ImplWhereBound,
goal.with(cx, trait_ref.to_host_effect_clause(cx, goal.predicate.constness)),
)
})
.chain([(GoalSource::ImplWhereBound, goal.with(cx, output_is_sized_pred))]);
let pred = inputs_and_output
.map_bound(|(inputs, _)| {
ty::TraitRef::new(cx, goal.predicate.def_id(), [
goal.predicate.self_ty(),
Ty::new_tup(cx, inputs.as_slice()),
])
})
.to_host_effect_clause(cx, goal.predicate.constness);
Self::probe_and_consider_implied_clause(
ecx,
CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
goal,
pred,
requirements,
)
}
fn consider_builtin_async_fn_trait_candidates(
@ -314,7 +351,7 @@ where
_ecx: &mut EvalCtxt<'_, D>,
_goal: Goal<I, Self>,
) -> Result<Candidate<I>, NoSolution> {
unreachable!("Destruct is not const")
Err(NoSolution)
}
fn consider_builtin_transmute_candidate(

View file

@ -394,6 +394,9 @@ where
return ecx.forced_ambiguity(MaybeCause::Ambiguity);
}
};
// A built-in `Fn` impl only holds if the output is sized.
// (FIXME: technically we only need to check this if the type is a fn ptr...)
let output_is_sized_pred = tupled_inputs_and_output.map_bound(|(_, output)| {
ty::TraitRef::new(cx, cx.require_lang_item(TraitSolverLangItem::Sized), [output])
});
@ -408,8 +411,6 @@ where
})
.upcast(cx);
// A built-in `Fn` impl only holds if the output is sized.
// (FIXME: technically we only need to check this if the type is a fn ptr...)
Self::probe_and_consider_implied_clause(
ecx,
CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
@ -438,6 +439,9 @@ where
goal_kind,
env_region,
)?;
// A built-in `AsyncFn` impl only holds if the output is sized.
// (FIXME: technically we only need to check this if the type is a fn ptr...)
let output_is_sized_pred = tupled_inputs_and_output_and_coroutine.map_bound(
|AsyncCallableRelevantTypes { output_coroutine_ty: output_ty, .. }| {
ty::TraitRef::new(cx, cx.require_lang_item(TraitSolverLangItem::Sized), [output_ty])
@ -494,8 +498,6 @@ where
)
.upcast(cx);
// A built-in `AsyncFn` impl only holds if the output is sized.
// (FIXME: technically we only need to check this if the type is a fn ptr...)
Self::probe_and_consider_implied_clause(
ecx,
CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),

View file

@ -326,6 +326,9 @@ where
return ecx.forced_ambiguity(MaybeCause::Ambiguity);
}
};
// A built-in `Fn` impl only holds if the output is sized.
// (FIXME: technically we only need to check this if the type is a fn ptr...)
let output_is_sized_pred = tupled_inputs_and_output.map_bound(|(_, output)| {
ty::TraitRef::new(cx, cx.require_lang_item(TraitSolverLangItem::Sized), [output])
});
@ -335,8 +338,6 @@ where
ty::TraitRef::new(cx, goal.predicate.def_id(), [goal.predicate.self_ty(), inputs])
})
.upcast(cx);
// A built-in `Fn` impl only holds if the output is sized.
// (FIXME: technically we only need to check this if the type is a fn ptr...)
Self::probe_and_consider_implied_clause(
ecx,
CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
@ -364,6 +365,9 @@ where
// This region doesn't matter because we're throwing away the coroutine type
Region::new_static(cx),
)?;
// A built-in `AsyncFn` impl only holds if the output is sized.
// (FIXME: technically we only need to check this if the type is a fn ptr...)
let output_is_sized_pred = tupled_inputs_and_output_and_coroutine.map_bound(
|AsyncCallableRelevantTypes { output_coroutine_ty, .. }| {
ty::TraitRef::new(cx, cx.require_lang_item(TraitSolverLangItem::Sized), [
@ -380,8 +384,6 @@ where
])
})
.upcast(cx);
// A built-in `AsyncFn` impl only holds if the output is sized.
// (FIXME: technically we only need to check this if the type is a fn ptr...)
Self::probe_and_consider_implied_clause(
ecx,
CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),