From 90c8d6bbe4725ca83e79e73e9cdf1f66840db79f Mon Sep 17 00:00:00 2001 From: Boxy Date: Fri, 17 Feb 2023 09:32:33 +0000 Subject: [PATCH] add predicate evaluation logic --- .../src/collect/predicates_of.rs | 57 +++++++++++++++++-- .../src/impl_wf_check/min_specialization.rs | 10 ++++ compiler/rustc_privacy/src/lib.rs | 14 ++++- .../rustc_trait_selection/src/solve/mod.rs | 16 +++++- .../src/traits/error_reporting/mod.rs | 8 ++- .../src/traits/fulfill.rs | 14 ++++- .../src/traits/select/mod.rs | 10 +++- .../ui/const-generics/nested-type.min.stderr | 11 +++- tests/ui/const-generics/nested-type.rs | 2 +- 9 files changed, 124 insertions(+), 18 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs index b67f8ded2a5..2badd66e346 100644 --- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs @@ -9,8 +9,8 @@ use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::{self, Visitor}; use rustc_middle::ty::subst::InternalSubsts; -use rustc_middle::ty::ToPredicate; use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::{GenericPredicates, ToPredicate}; use rustc_span::symbol::{sym, Ident}; use rustc_span::{Span, DUMMY_SP}; @@ -151,7 +151,8 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericP trace!(?generics); // Collect the predicates that were written inline by the user on each - // type parameter (e.g., ``). + // type parameter (e.g., ``). Also add `ConstArgHasType` predicates + // for each const parameter. for param in ast_generics.params { match param.kind { // 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); } 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; } } @@ -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 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 // 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) @@ -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` // 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); diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs index 2562a1739f5..4741e4b095d 100644 --- a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs +++ b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs @@ -496,6 +496,16 @@ fn check_specialization_on<'tcx>(tcx: TyCtxt<'tcx>, predicate: ty::Predicate<'tc ) .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 `` there is probably no risk of + // soundness bugs, but when we support generic const parameter types this will need to be + // revisited. + } _ => { tcx.sess .struct_span_err(span, &format!("cannot specialize on predicate `{}`", predicate)) diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index 64c7fafa713..58dfca75c65 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -159,9 +159,21 @@ where _region, ))) => ty.visit_with(self), 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::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), } } diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_trait_selection/src/solve/mod.rs index 217851ba1d9..6890811fd04 100644 --- a/compiler/rustc_trait_selection/src/solve/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/mod.rs @@ -25,7 +25,7 @@ use rustc_infer::traits::query::NoSolution; use rustc_infer::traits::Obligation; use rustc_middle::infer::canonical::Certainty as OldCertainty; use rustc_middle::traits::solve::{ExternalConstraints, ExternalConstraintsData}; -use rustc_middle::ty::{self, TyCtxt}; +use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::ty::{ CoercePredicate, RegionOutlivesPredicate, SubtypePredicate, ToPredicate, TypeOutlivesPredicate, }; @@ -290,8 +290,8 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { ty::PredicateKind::Clause(ty::Clause::RegionOutlives(predicate)) => { self.compute_region_outlives_goal(Goal { param_env, predicate }) } - ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(..)) => { - unimplemented!() + ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(ct, ty)) => { + self.compute_const_arg_has_type_goal(Goal { param_env, predicate: (ct, ty) }) } ty::PredicateKind::Subtype(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> { diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index 02652200811..2511e9a955a 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -1283,9 +1283,11 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { "AliasEq predicate should never be the predicate cause of a SelectionError" ), - ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(..)) => { - // FIXME: don't know how selection error works so unsure when this is reachable (if it is) - unimplemented!() + ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(ct, ty)) => { + self.tcx.sess.struct_span_err( + span, + &format!("the constant `{}` is not of type `{}`", ct, ty), + ) } } } diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index 59be80236f8..deeed930e50 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -601,8 +601,18 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { ty::PredicateKind::AliasEq(..) => { bug!("AliasEq is only used for new solver") } - ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(..)) => { - unimplemented!() + ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(ct, ty)) => { + 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, + )), + } } }, } diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 2cfaaf1d376..e6fc9bb9239 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -993,8 +993,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { bug!("AliasEq is only used for new solver") } ty::PredicateKind::Ambiguous => Ok(EvaluatedToAmbig), - ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(..)) => { - unimplemented!() + ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(ct, ty)) => { + 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), + } } } }) diff --git a/tests/ui/const-generics/nested-type.min.stderr b/tests/ui/const-generics/nested-type.min.stderr index cff02b0d445..257a9e31e14 100644 --- a/tests/ui/const-generics/nested-type.min.stderr +++ b/tests/ui/const-generics/nested-type.min.stderr @@ -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; { struct Foo; @@ -24,5 +32,6 @@ LL | | }]>; = note: the only supported types are integers, `bool` and `char` = 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`. diff --git a/tests/ui/const-generics/nested-type.rs b/tests/ui/const-generics/nested-type.rs index 742340f430e..5240f5c3b0b 100644 --- a/tests/ui/const-generics/nested-type.rs +++ b/tests/ui/const-generics/nested-type.rs @@ -13,7 +13,7 @@ struct Foo::value() - //[full]~^ ERROR cannot call non-const fn + //~^ ERROR cannot call non-const fn }]>; fn main() {}