mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-21 22:34:05 +00:00
Implement ~const Fn trait goals in the new solver
This commit is contained in:
parent
89b6885529
commit
def7ed08e7
@ -374,7 +374,11 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
|
||||
self.explicit_implied_predicates_of(def_id).map_bound(|preds| preds.into_iter().copied())
|
||||
}
|
||||
|
||||
fn is_const_impl(self, def_id: DefId) -> bool {
|
||||
fn impl_is_const(self, def_id: DefId) -> bool {
|
||||
self.is_conditionally_const(def_id)
|
||||
}
|
||||
|
||||
fn fn_is_const(self, def_id: DefId) -> bool {
|
||||
self.is_conditionally_const(def_id)
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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(
|
||||
|
@ -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),
|
||||
|
@ -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),
|
||||
|
@ -223,7 +223,8 @@ pub trait Interner:
|
||||
def_id: Self::DefId,
|
||||
) -> ty::EarlyBinder<Self, impl IntoIterator<Item = (Self::Clause, Self::Span)>>;
|
||||
|
||||
fn is_const_impl(self, def_id: Self::DefId) -> bool;
|
||||
fn impl_is_const(self, def_id: Self::DefId) -> bool;
|
||||
fn fn_is_const(self, def_id: Self::DefId) -> bool;
|
||||
fn const_conditions(
|
||||
self,
|
||||
def_id: Self::DefId,
|
||||
|
@ -1,90 +0,0 @@
|
||||
//@ known-bug: #110395
|
||||
//@ failure-status: 101
|
||||
//@ dont-check-compiler-stderr
|
||||
// FIXME(const_trait_impl) check-pass
|
||||
//@ compile-flags: -Znext-solver
|
||||
|
||||
#![crate_type = "lib"]
|
||||
#![allow(internal_features, incomplete_features)]
|
||||
#![no_std]
|
||||
#![no_core]
|
||||
#![feature(
|
||||
auto_traits,
|
||||
const_trait_impl,
|
||||
effects,
|
||||
lang_items,
|
||||
no_core,
|
||||
staged_api,
|
||||
unboxed_closures,
|
||||
rustc_attrs,
|
||||
marker_trait_attr,
|
||||
)]
|
||||
#![stable(feature = "minicore", since = "1.0.0")]
|
||||
|
||||
fn test() {
|
||||
fn is_const_fn<F>(_: F)
|
||||
where
|
||||
F: const FnOnce<()>,
|
||||
{
|
||||
}
|
||||
|
||||
const fn foo() {}
|
||||
|
||||
is_const_fn(foo);
|
||||
}
|
||||
|
||||
/// ---------------------------------------------------------------------- ///
|
||||
/// Const fn trait definitions
|
||||
|
||||
#[const_trait]
|
||||
#[lang = "fn"]
|
||||
#[rustc_paren_sugar]
|
||||
trait Fn<Args: Tuple>: ~const FnMut<Args> {
|
||||
extern "rust-call" fn call(&self, args: Args) -> Self::Output;
|
||||
}
|
||||
|
||||
#[const_trait]
|
||||
#[lang = "fn_mut"]
|
||||
#[rustc_paren_sugar]
|
||||
trait FnMut<Args: Tuple>: ~const FnOnce<Args> {
|
||||
extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output;
|
||||
}
|
||||
|
||||
#[const_trait]
|
||||
#[lang = "fn_once"]
|
||||
#[rustc_paren_sugar]
|
||||
trait FnOnce<Args: Tuple> {
|
||||
#[lang = "fn_once_output"]
|
||||
type Output;
|
||||
|
||||
extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
|
||||
}
|
||||
|
||||
/// ---------------------------------------------------------------------- ///
|
||||
/// All this other stuff needed for core. Unrelated to test.
|
||||
|
||||
#[lang = "destruct"]
|
||||
#[const_trait]
|
||||
trait Destruct {}
|
||||
|
||||
#[lang = "freeze"]
|
||||
unsafe auto trait Freeze {}
|
||||
|
||||
#[lang = "drop"]
|
||||
#[const_trait]
|
||||
trait Drop {
|
||||
fn drop(&mut self);
|
||||
}
|
||||
|
||||
#[lang = "sized"]
|
||||
trait Sized {}
|
||||
#[lang = "copy"]
|
||||
trait Copy {}
|
||||
|
||||
#[lang = "tuple_trait"]
|
||||
trait Tuple {}
|
||||
|
||||
#[lang = "legacy_receiver"]
|
||||
trait LegacyReceiver {}
|
||||
|
||||
impl<T: ?Sized> LegacyReceiver for &T {}
|
@ -0,0 +1,22 @@
|
||||
//@ aux-build:minicore.rs
|
||||
//@ compile-flags: --crate-type=lib -Znext-solver -Cpanic=abort
|
||||
//@ check-pass
|
||||
|
||||
#![feature(no_core, const_trait_impl)]
|
||||
#![no_std]
|
||||
#![no_core]
|
||||
|
||||
extern crate minicore;
|
||||
use minicore::*;
|
||||
|
||||
fn is_const_fn<F>(_: F)
|
||||
where
|
||||
F: const FnOnce(),
|
||||
{
|
||||
}
|
||||
|
||||
const fn foo() {}
|
||||
|
||||
fn test() {
|
||||
is_const_fn(foo);
|
||||
}
|
21
tests/ui/traits/const-traits/effects/minicore-fn-fail.rs
Normal file
21
tests/ui/traits/const-traits/effects/minicore-fn-fail.rs
Normal file
@ -0,0 +1,21 @@
|
||||
//@ aux-build:minicore.rs
|
||||
//@ compile-flags: --crate-type=lib -Znext-solver
|
||||
|
||||
#![feature(no_core, const_trait_impl)]
|
||||
#![no_std]
|
||||
#![no_core]
|
||||
|
||||
extern crate minicore;
|
||||
use minicore::*;
|
||||
|
||||
const fn call_indirect<T: ~const Fn()>(t: &T) { t() }
|
||||
|
||||
#[const_trait]
|
||||
trait Foo {}
|
||||
impl Foo for () {}
|
||||
const fn foo<T: ~const Foo>() {}
|
||||
|
||||
const fn test() {
|
||||
call_indirect(&foo::<()>);
|
||||
//~^ ERROR the trait bound `(): ~const Foo` is not satisfied
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
error[E0277]: the trait bound `(): ~const Foo` is not satisfied
|
||||
--> $DIR/minicore-fn-fail.rs:19:5
|
||||
|
|
||||
LL | call_indirect(&foo::<()>);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0277`.
|
@ -20,3 +20,9 @@ const fn test_op() {
|
||||
let _x = Add::add(1, 2);
|
||||
let _y = Custom + Custom;
|
||||
}
|
||||
|
||||
const fn call_indirect<T: ~const Fn()>(t: &T) { t() }
|
||||
|
||||
const fn call() {
|
||||
call_indirect(&call);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user