Skip to content

Commit

Permalink
Implement ~const Fn trait goals in the new solver
Browse files Browse the repository at this point in the history
  • Loading branch information
compiler-errors committed Oct 29, 2024
1 parent d8ea700 commit 76614e4
Show file tree
Hide file tree
Showing 7 changed files with 164 additions and 15 deletions.
6 changes: 5 additions & 1 deletion compiler/rustc_middle/src/ty/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,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)
}

Expand Down
117 changes: 108 additions & 9 deletions compiler/rustc_next_trait_solver/src/solve/effect_goals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@
use rustc_type_ir::fast_reject::DeepRejectCtxt;
use rustc_type_ir::inherent::*;
use rustc_type_ir::{self as ty, Interner, elaborate};
use rustc_type_ir::lang_items::TraitSolverLangItem;
use rustc_type_ir::{self as ty, Interner, Upcast, elaborate};
use tracing::instrument;

use super::assembly::Candidate;
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>
Expand Down Expand Up @@ -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);
}

Expand Down Expand Up @@ -204,7 +204,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(
Expand All @@ -222,11 +222,110 @@ 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 (tupled_inputs_and_output, def_id, args) = match self_ty.kind() {
// keep this in sync with assemble_fn_pointer_candidates until the old solver is removed.
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)
{
(
sig.instantiate(cx, args).map_bound(|sig| {
(Ty::new_tup(cx, sig.inputs().as_slice()), 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:?}`")
}
};

let output_is_sized_pred = tupled_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.host)),
)
})
// 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...)
.chain([(GoalSource::ImplWhereBound, goal.with(cx, output_is_sized_pred))]);

let pred = tupled_inputs_and_output
.map_bound(|(inputs, _)| {
ty::ClauseKind::HostEffect(ty::HostEffectPredicate {
trait_ref: ty::TraitRef::new(cx, goal.predicate.def_id(), [
goal.predicate.self_ty(),
inputs,
]),
host: goal.predicate.host,
})
})
.upcast(cx);

Self::probe_and_consider_implied_clause(
ecx,
CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
goal,
pred,
requirements,
)
}

fn consider_builtin_async_fn_trait_candidates(
Expand Down Expand Up @@ -311,7 +410,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(
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_type_ir/src/interner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,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,
Expand Down
7 changes: 3 additions & 4 deletions tests/ui/traits/const-traits/const-fns-are-early-bound.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
//@ known-bug: #110395
//@ failure-status: 101
//@ dont-check-compiler-stderr
// FIXME(effects) check-pass
//@ check-pass
//@ compile-flags: -Znext-solver

#![crate_type = "lib"]
Expand Down Expand Up @@ -88,3 +85,5 @@ trait Tuple {}
trait LegacyReceiver {}

impl<T: ?Sized> LegacyReceiver for &T {}

impl<T: ?Sized> LegacyReceiver for &mut T {}
22 changes: 22 additions & 0 deletions tests/ui/traits/const-traits/effects/minicore-fn-fail.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//@ aux-build:minicore.rs
//@ compile-flags: --crate-type=lib -Znext-solver

#![feature(no_core, const_trait_impl, effects)]
//~^ WARN the feature `effects` is incomplete
#![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
}
18 changes: 18 additions & 0 deletions tests/ui/traits/const-traits/effects/minicore-fn-fail.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
warning: the feature `effects` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/minicore-fn-fail.rs:4:39
|
LL | #![feature(no_core, const_trait_impl, effects)]
| ^^^^^^^
|
= note: see issue #102090 <https://github.com/rust-lang/rust/issues/102090> for more information
= note: `#[warn(incomplete_features)]` on by default

error[E0277]: the trait bound `(): ~const Foo` is not satisfied
--> $DIR/minicore-fn-fail.rs:20:5
|
LL | call_indirect(&foo::<()>);
| ^^^^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to 1 previous error; 1 warning emitted

For more information about this error, try `rustc --explain E0277`.
6 changes: 6 additions & 0 deletions tests/ui/traits/const-traits/effects/minicore-works.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,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);
}

0 comments on commit 76614e4

Please sign in to comment.