Skip to content

Commit bd767b5

Browse files
Implement ~const Fn trait goals in the new solver
1 parent 432972c commit bd767b5

File tree

7 files changed

+147
-15
lines changed

7 files changed

+147
-15
lines changed

compiler/rustc_middle/src/ty/context.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -374,7 +374,11 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
374374
self.explicit_implied_predicates_of(def_id).map_bound(|preds| preds.into_iter().copied())
375375
}
376376

377-
fn is_const_impl(self, def_id: DefId) -> bool {
377+
fn impl_is_const(self, def_id: DefId) -> bool {
378+
self.is_conditionally_const(def_id)
379+
}
380+
381+
fn fn_is_const(self, def_id: DefId) -> bool {
378382
self.is_conditionally_const(def_id)
379383
}
380384

compiler/rustc_next_trait_solver/src/solve/effect_goals.rs

+101-8
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,15 @@
33
44
use rustc_type_ir::fast_reject::DeepRejectCtxt;
55
use rustc_type_ir::inherent::*;
6+
use rustc_type_ir::lang_items::TraitSolverLangItem;
67
use rustc_type_ir::{self as ty, Interner, elaborate};
78
use tracing::instrument;
89

910
use super::assembly::Candidate;
1011
use crate::delegate::SolverDelegate;
11-
use crate::solve::assembly::{self};
1212
use crate::solve::{
1313
BuiltinImplSource, CandidateSource, Certainty, EvalCtxt, Goal, GoalSource, NoSolution,
14-
QueryResult,
14+
QueryResult, assembly,
1515
};
1616

1717
impl<D, I> assembly::GoalKind<D> for ty::HostEffectPredicate<I>
@@ -142,7 +142,7 @@ where
142142
ty::ImplPolarity::Positive => {}
143143
};
144144

145-
if !cx.is_const_impl(impl_def_id) {
145+
if !cx.impl_is_const(impl_def_id) {
146146
return Err(NoSolution);
147147
}
148148

@@ -207,7 +207,7 @@ where
207207
_ecx: &mut EvalCtxt<'_, D>,
208208
_goal: Goal<I, Self>,
209209
) -> Result<Candidate<I>, NoSolution> {
210-
todo!("Copy/Clone is not yet const")
210+
Err(NoSolution)
211211
}
212212

213213
fn consider_builtin_pointer_like_candidate(
@@ -225,11 +225,104 @@ where
225225
}
226226

227227
fn consider_builtin_fn_trait_candidates(
228-
_ecx: &mut EvalCtxt<'_, D>,
229-
_goal: Goal<I, Self>,
228+
ecx: &mut EvalCtxt<'_, D>,
229+
goal: Goal<I, Self>,
230230
_kind: rustc_type_ir::ClosureKind,
231231
) -> Result<Candidate<I>, NoSolution> {
232-
todo!("Fn* are not yet const")
232+
let cx = ecx.cx();
233+
234+
let self_ty = goal.predicate.self_ty();
235+
let (tupled_inputs_and_output, def_id, args) = match self_ty.kind() {
236+
// keep this in sync with assemble_fn_pointer_candidates until the old solver is removed.
237+
ty::FnDef(def_id, args) => {
238+
let sig = cx.fn_sig(def_id);
239+
if sig.skip_binder().is_fn_trait_compatible()
240+
&& !cx.has_target_features(def_id)
241+
&& cx.fn_is_const(def_id)
242+
{
243+
(
244+
sig.instantiate(cx, args).map_bound(|sig| {
245+
(Ty::new_tup(cx, sig.inputs().as_slice()), sig.output())
246+
}),
247+
def_id,
248+
args,
249+
)
250+
} else {
251+
return Err(NoSolution);
252+
}
253+
}
254+
// `FnPtr`s are not const for now.
255+
ty::FnPtr(..) => {
256+
return Err(NoSolution);
257+
}
258+
// `Closure`s are not const for now.
259+
ty::Closure(..) => {
260+
return Err(NoSolution);
261+
}
262+
// `CoroutineClosure`s are not const for now.
263+
ty::CoroutineClosure(..) => {
264+
return Err(NoSolution);
265+
}
266+
267+
ty::Bool
268+
| ty::Char
269+
| ty::Int(_)
270+
| ty::Uint(_)
271+
| ty::Float(_)
272+
| ty::Adt(_, _)
273+
| ty::Foreign(_)
274+
| ty::Str
275+
| ty::Array(_, _)
276+
| ty::Slice(_)
277+
| ty::RawPtr(_, _)
278+
| ty::Ref(_, _, _)
279+
| ty::Dynamic(_, _, _)
280+
| ty::Coroutine(_, _)
281+
| ty::CoroutineWitness(..)
282+
| ty::Never
283+
| ty::Tuple(_)
284+
| ty::Pat(_, _)
285+
| ty::Alias(_, _)
286+
| ty::Param(_)
287+
| ty::Placeholder(..)
288+
| ty::Infer(ty::IntVar(_) | ty::FloatVar(_))
289+
| ty::Error(_) => return Err(NoSolution),
290+
291+
ty::Bound(..)
292+
| ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
293+
panic!("unexpected type `{self_ty:?}`")
294+
}
295+
};
296+
297+
let output_is_sized_pred = tupled_inputs_and_output.map_bound(|(_, output)| {
298+
ty::TraitRef::new(cx, cx.require_lang_item(TraitSolverLangItem::Sized), [output])
299+
});
300+
let requirements = cx
301+
.const_conditions(def_id)
302+
.iter_instantiated(cx, args)
303+
.map(|trait_ref| {
304+
(
305+
GoalSource::ImplWhereBound,
306+
goal.with(cx, trait_ref.to_host_effect_clause(cx, goal.predicate.constness)),
307+
)
308+
})
309+
// A built-in `Fn` impl only holds if the output is sized.
310+
// (FIXME: technically we only need to check this if the type is a fn ptr...)
311+
.chain([(GoalSource::ImplWhereBound, goal.with(cx, output_is_sized_pred))]);
312+
313+
let pred = tupled_inputs_and_output
314+
.map_bound(|(inputs, _)| {
315+
ty::TraitRef::new(cx, goal.predicate.def_id(), [goal.predicate.self_ty(), inputs])
316+
})
317+
.to_host_effect_clause(cx, goal.predicate.constness);
318+
319+
Self::probe_and_consider_implied_clause(
320+
ecx,
321+
CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
322+
goal,
323+
pred,
324+
requirements,
325+
)
233326
}
234327

235328
fn consider_builtin_async_fn_trait_candidates(
@@ -314,7 +407,7 @@ where
314407
_ecx: &mut EvalCtxt<'_, D>,
315408
_goal: Goal<I, Self>,
316409
) -> Result<Candidate<I>, NoSolution> {
317-
unreachable!("Destruct is not const")
410+
Err(NoSolution)
318411
}
319412

320413
fn consider_builtin_transmute_candidate(

compiler/rustc_type_ir/src/interner.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,8 @@ pub trait Interner:
223223
def_id: Self::DefId,
224224
) -> ty::EarlyBinder<Self, impl IntoIterator<Item = (Self::Clause, Self::Span)>>;
225225

226-
fn is_const_impl(self, def_id: Self::DefId) -> bool;
226+
fn impl_is_const(self, def_id: Self::DefId) -> bool;
227+
fn fn_is_const(self, def_id: Self::DefId) -> bool;
227228
fn const_conditions(
228229
self,
229230
def_id: Self::DefId,

tests/ui/traits/const-traits/const-fns-are-early-bound.rs

+3-5
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
1-
//@ known-bug: #110395
2-
//@ failure-status: 101
3-
//@ dont-check-compiler-stderr
4-
// FIXME(const_trait_impl) check-pass
1+
//@ check-pass
52
//@ compile-flags: -Znext-solver
63

74
#![crate_type = "lib"]
@@ -11,7 +8,6 @@
118
#![feature(
129
auto_traits,
1310
const_trait_impl,
14-
effects,
1511
lang_items,
1612
no_core,
1713
staged_api,
@@ -88,3 +84,5 @@ trait Tuple {}
8884
trait LegacyReceiver {}
8985

9086
impl<T: ?Sized> LegacyReceiver for &T {}
87+
88+
impl<T: ?Sized> LegacyReceiver for &mut T {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//@ aux-build:minicore.rs
2+
//@ compile-flags: --crate-type=lib -Znext-solver
3+
4+
#![feature(no_core, const_trait_impl)]
5+
#![no_std]
6+
#![no_core]
7+
8+
extern crate minicore;
9+
use minicore::*;
10+
11+
const fn call_indirect<T: ~const Fn()>(t: &T) { t() }
12+
13+
#[const_trait]
14+
trait Foo {}
15+
impl Foo for () {}
16+
const fn foo<T: ~const Foo>() {}
17+
18+
const fn test() {
19+
call_indirect(&foo::<()>);
20+
//~^ ERROR the trait bound `(): ~const Foo` is not satisfied
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
error[E0277]: the trait bound `(): ~const Foo` is not satisfied
2+
--> $DIR/minicore-fn-fail.rs:19:5
3+
|
4+
LL | call_indirect(&foo::<()>);
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^
6+
7+
error: aborting due to 1 previous error
8+
9+
For more information about this error, try `rustc --explain E0277`.

tests/ui/traits/const-traits/effects/minicore-works.rs

+6
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,9 @@ const fn test_op() {
2020
let _x = Add::add(1, 2);
2121
let _y = Custom + Custom;
2222
}
23+
24+
const fn call_indirect<T: ~const Fn()>(t: &T) { t() }
25+
26+
const fn call() {
27+
call_indirect(&call);
28+
}

0 commit comments

Comments
 (0)