Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement ~const Fn trait goal in the new solver #133216

Merged
merged 1 commit into from
Nov 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -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)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
55 changes: 46 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::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>
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 @@ -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(
Expand All @@ -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(
Expand Down Expand Up @@ -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(
Expand Down
10 changes: 6 additions & 4 deletions compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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])
});
Expand All @@ -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),
Expand Down Expand Up @@ -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])
Expand Down Expand Up @@ -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),
Expand Down
10 changes: 6 additions & 4 deletions compiler/rustc_next_trait_solver/src/solve/trait_goals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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])
});
Expand All @@ -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),
Expand Down Expand Up @@ -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), [
Expand All @@ -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),
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 @@ -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,
Expand Down
90 changes: 0 additions & 90 deletions tests/ui/traits/const-traits/const-fns-are-early-bound.rs

This file was deleted.

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 -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);
}
Loading
Loading