add predicate evaluation logic

This commit is contained in:
Boxy 2023-02-17 09:32:33 +00:00
parent e919d7e348
commit 90c8d6bbe4
9 changed files with 124 additions and 18 deletions

View File

@ -9,8 +9,8 @@ use rustc_hir::def::DefKind;
use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::intravisit::{self, Visitor};
use rustc_middle::ty::subst::InternalSubsts; use rustc_middle::ty::subst::InternalSubsts;
use rustc_middle::ty::ToPredicate;
use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_middle::ty::{GenericPredicates, ToPredicate};
use rustc_span::symbol::{sym, Ident}; use rustc_span::symbol::{sym, Ident};
use rustc_span::{Span, DUMMY_SP}; use rustc_span::{Span, DUMMY_SP};
@ -151,7 +151,8 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericP
trace!(?generics); trace!(?generics);
// Collect the predicates that were written inline by the user on each // Collect the predicates that were written inline by the user on each
// type parameter (e.g., `<T: Foo>`). // type parameter (e.g., `<T: Foo>`). Also add `ConstArgHasType` predicates
// for each const parameter.
for param in ast_generics.params { for param in ast_generics.params {
match param.kind { match param.kind {
// We already dealt with early bound lifetimes above. // We already dealt with early bound lifetimes above.
@ -175,7 +176,19 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericP
trace!(?predicates); trace!(?predicates);
} }
GenericParamKind::Const { .. } => { GenericParamKind::Const { .. } => {
// Bounds on const parameters are currently not possible. let name = param.name.ident().name;
let param_const = ty::ParamConst::new(index, name);
let ct_ty = tcx.type_of(param.def_id.to_def_id()).subst_identity();
let ct = tcx.mk_const(param_const, ct_ty);
let predicate = ty::Binder::dummy(ty::PredicateKind::Clause(
ty::Clause::ConstArgHasType(ct, ct_ty),
))
.to_predicate(tcx);
predicates.insert((predicate, param.span));
index += 1; index += 1;
} }
} }
@ -439,7 +452,9 @@ pub(super) fn explicit_predicates_of<'tcx>(
let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local());
let parent_def_id = tcx.hir().get_parent_item(hir_id); let parent_def_id = tcx.hir().get_parent_item(hir_id);
if tcx.hir().opt_const_param_default_param_def_id(hir_id).is_some() { if let Some(defaulted_param_def_id) =
tcx.hir().opt_const_param_default_param_def_id(hir_id)
{
// In `generics_of` we set the generics' parent to be our parent's parent which means that // In `generics_of` we set the generics' parent to be our parent's parent which means that
// we lose out on the predicates of our actual parent if we dont return those predicates here. // we lose out on the predicates of our actual parent if we dont return those predicates here.
// (See comment in `generics_of` for more information on why the parent shenanigans is necessary) // (See comment in `generics_of` for more information on why the parent shenanigans is necessary)
@ -452,7 +467,39 @@ pub(super) fn explicit_predicates_of<'tcx>(
// //
// In the above code we want the anon const to have predicates in its param env for `T: Trait` // In the above code we want the anon const to have predicates in its param env for `T: Trait`
// and we would be calling `explicit_predicates_of(Foo)` here // and we would be calling `explicit_predicates_of(Foo)` here
return tcx.explicit_predicates_of(parent_def_id); let parent_preds = tcx.explicit_predicates_of(parent_def_id);
// If we dont filter out `ConstArgHasType` predicates then every single defaulted const parameter
// will ICE because of #106994. FIXME(generic_const_exprs): remove this when a more general solution
// to #106994 is implemented.
let filtered_predicates = parent_preds
.predicates
.into_iter()
.filter(|(pred, _)| {
if let ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(ct, _)) =
pred.kind().skip_binder()
{
match ct.kind() {
ty::ConstKind::Param(param_const) => {
let defaulted_param_idx = tcx
.generics_of(parent_def_id)
.param_def_id_to_index[&defaulted_param_def_id.to_def_id()];
param_const.index < defaulted_param_idx
}
_ => bug!(
"`ConstArgHasType` in `predicates_of`\
that isn't a `Param` const"
),
}
} else {
true
}
})
.cloned();
return GenericPredicates {
parent: parent_preds.parent,
predicates: { tcx.arena.alloc_from_iter(filtered_predicates) },
};
} }
let parent_def_kind = tcx.def_kind(parent_def_id); let parent_def_kind = tcx.def_kind(parent_def_id);

View File

@ -496,6 +496,16 @@ fn check_specialization_on<'tcx>(tcx: TyCtxt<'tcx>, predicate: ty::Predicate<'tc
) )
.emit(); .emit();
} }
ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(..)) => {
// FIXME(min_specialization), FIXME(const_generics):
// It probably isn't right to allow _every_ `ConstArgHasType` but I am somewhat unsure
// about the actual rules that would be sound. Can't just always error here because otherwise
// std/core doesn't even compile as they have `const N: usize` in some specializing impls.
//
// While we do not support constructs like `<T, const N: T>` there is probably no risk of
// soundness bugs, but when we support generic const parameter types this will need to be
// revisited.
}
_ => { _ => {
tcx.sess tcx.sess
.struct_span_err(span, &format!("cannot specialize on predicate `{}`", predicate)) .struct_span_err(span, &format!("cannot specialize on predicate `{}`", predicate))

View File

@ -159,9 +159,21 @@ where
_region, _region,
))) => ty.visit_with(self), ))) => ty.visit_with(self),
ty::PredicateKind::Clause(ty::Clause::RegionOutlives(..)) => ControlFlow::Continue(()), ty::PredicateKind::Clause(ty::Clause::RegionOutlives(..)) => ControlFlow::Continue(()),
ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(ct, ty)) => {
ct.visit_with(self)?;
ty.visit_with(self)
}
ty::PredicateKind::ConstEvaluatable(ct) => ct.visit_with(self), ty::PredicateKind::ConstEvaluatable(ct) => ct.visit_with(self),
ty::PredicateKind::WellFormed(arg) => arg.visit_with(self), ty::PredicateKind::WellFormed(arg) => arg.visit_with(self),
_ => bug!("unexpected predicate: {:?}", predicate),
ty::PredicateKind::ObjectSafe(_)
| ty::PredicateKind::ClosureKind(_, _, _)
| ty::PredicateKind::Subtype(_)
| ty::PredicateKind::Coerce(_)
| ty::PredicateKind::ConstEquate(_, _)
| ty::PredicateKind::TypeWellFormedFromEnv(_)
| ty::PredicateKind::Ambiguous
| ty::PredicateKind::AliasEq(_, _) => bug!("unexpected predicate: {:?}", predicate),
} }
} }

View File

@ -25,7 +25,7 @@ use rustc_infer::traits::query::NoSolution;
use rustc_infer::traits::Obligation; use rustc_infer::traits::Obligation;
use rustc_middle::infer::canonical::Certainty as OldCertainty; use rustc_middle::infer::canonical::Certainty as OldCertainty;
use rustc_middle::traits::solve::{ExternalConstraints, ExternalConstraintsData}; use rustc_middle::traits::solve::{ExternalConstraints, ExternalConstraintsData};
use rustc_middle::ty::{self, TyCtxt}; use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_middle::ty::{ use rustc_middle::ty::{
CoercePredicate, RegionOutlivesPredicate, SubtypePredicate, ToPredicate, TypeOutlivesPredicate, CoercePredicate, RegionOutlivesPredicate, SubtypePredicate, ToPredicate, TypeOutlivesPredicate,
}; };
@ -290,8 +290,8 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
ty::PredicateKind::Clause(ty::Clause::RegionOutlives(predicate)) => { ty::PredicateKind::Clause(ty::Clause::RegionOutlives(predicate)) => {
self.compute_region_outlives_goal(Goal { param_env, predicate }) self.compute_region_outlives_goal(Goal { param_env, predicate })
} }
ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(..)) => { ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(ct, ty)) => {
unimplemented!() self.compute_const_arg_has_type_goal(Goal { param_env, predicate: (ct, ty) })
} }
ty::PredicateKind::Subtype(predicate) => { ty::PredicateKind::Subtype(predicate) => {
self.compute_subtype_goal(Goal { param_env, predicate }) self.compute_subtype_goal(Goal { param_env, predicate })
@ -474,6 +474,16 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
} }
} }
} }
#[instrument(level = "debug", skip(self), ret)]
fn compute_const_arg_has_type_goal(
&mut self,
goal: Goal<'tcx, (ty::Const<'tcx>, Ty<'tcx>)>,
) -> QueryResult<'tcx> {
let (ct, ty) = goal.predicate;
let nested_goals = self.infcx.eq(goal.param_env, ct.ty(), ty)?;
self.evaluate_all_and_make_canonical_response(nested_goals)
}
} }
impl<'tcx> EvalCtxt<'_, 'tcx> { impl<'tcx> EvalCtxt<'_, 'tcx> {

View File

@ -1283,9 +1283,11 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
"AliasEq predicate should never be the predicate cause of a SelectionError" "AliasEq predicate should never be the predicate cause of a SelectionError"
), ),
ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(..)) => { ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(ct, ty)) => {
// FIXME: don't know how selection error works so unsure when this is reachable (if it is) self.tcx.sess.struct_span_err(
unimplemented!() span,
&format!("the constant `{}` is not of type `{}`", ct, ty),
)
} }
} }
} }

View File

@ -601,8 +601,18 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
ty::PredicateKind::AliasEq(..) => { ty::PredicateKind::AliasEq(..) => {
bug!("AliasEq is only used for new solver") bug!("AliasEq is only used for new solver")
} }
ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(..)) => { ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(ct, ty)) => {
unimplemented!() match self
.selcx
.infcx
.at(&obligation.cause, obligation.param_env)
.eq(ct.ty(), ty)
{
Ok(inf_ok) => ProcessResult::Changed(mk_pending(inf_ok.into_obligations())),
Err(_) => ProcessResult::Error(FulfillmentErrorCode::CodeSelectionError(
SelectionError::Unimplemented,
)),
}
} }
}, },
} }

View File

@ -993,8 +993,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
bug!("AliasEq is only used for new solver") bug!("AliasEq is only used for new solver")
} }
ty::PredicateKind::Ambiguous => Ok(EvaluatedToAmbig), ty::PredicateKind::Ambiguous => Ok(EvaluatedToAmbig),
ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(..)) => { ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(ct, ty)) => {
unimplemented!() match self.infcx.at(&obligation.cause, obligation.param_env).eq(ct.ty(), ty) {
Ok(inf_ok) => self.evaluate_predicates_recursively(
previous_stack,
inf_ok.into_obligations(),
),
Err(_) => Ok(EvaluatedToErr),
}
} }
} }
}) })

View File

@ -1,3 +1,11 @@
error[E0015]: cannot call non-const fn `Foo::{constant#0}::Foo::<17>::value` in constants
--> $DIR/nested-type.rs:15:5
|
LL | Foo::<17>::value()
| ^^^^^^^^^^^^^^^^^^
|
= note: calls in constants are limited to constant functions, tuple structs and tuple variants
error: `[u8; { error: `[u8; {
struct Foo<const N: usize>; struct Foo<const N: usize>;
@ -24,5 +32,6 @@ LL | | }]>;
= note: the only supported types are integers, `bool` and `char` = note: the only supported types are integers, `bool` and `char`
= help: more complex types are supported with `#![feature(adt_const_params)]` = help: more complex types are supported with `#![feature(adt_const_params)]`
error: aborting due to previous error error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0015`.

View File

@ -13,7 +13,7 @@ struct Foo<const N: [u8; { //[min]~ ERROR `[u8; _]` is forbidden
} }
Foo::<17>::value() Foo::<17>::value()
//[full]~^ ERROR cannot call non-const fn //~^ ERROR cannot call non-const fn
}]>; }]>;
fn main() {} fn main() {}