Skip to content

Commit 90c8d6b

Browse files
committed
add predicate evaluation logic
1 parent e919d7e commit 90c8d6b

File tree

9 files changed

+124
-18
lines changed

9 files changed

+124
-18
lines changed

compiler/rustc_hir_analysis/src/collect/predicates_of.rs

+52-5
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ use rustc_hir::def::DefKind;
99
use rustc_hir::def_id::{DefId, LocalDefId};
1010
use rustc_hir::intravisit::{self, Visitor};
1111
use rustc_middle::ty::subst::InternalSubsts;
12-
use rustc_middle::ty::ToPredicate;
1312
use rustc_middle::ty::{self, Ty, TyCtxt};
13+
use rustc_middle::ty::{GenericPredicates, ToPredicate};
1414
use rustc_span::symbol::{sym, Ident};
1515
use rustc_span::{Span, DUMMY_SP};
1616

@@ -151,7 +151,8 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericP
151151
trace!(?generics);
152152

153153
// Collect the predicates that were written inline by the user on each
154-
// type parameter (e.g., `<T: Foo>`).
154+
// type parameter (e.g., `<T: Foo>`). Also add `ConstArgHasType` predicates
155+
// for each const parameter.
155156
for param in ast_generics.params {
156157
match param.kind {
157158
// We already dealt with early bound lifetimes above.
@@ -175,7 +176,19 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericP
175176
trace!(?predicates);
176177
}
177178
GenericParamKind::Const { .. } => {
178-
// Bounds on const parameters are currently not possible.
179+
let name = param.name.ident().name;
180+
let param_const = ty::ParamConst::new(index, name);
181+
182+
let ct_ty = tcx.type_of(param.def_id.to_def_id()).subst_identity();
183+
184+
let ct = tcx.mk_const(param_const, ct_ty);
185+
186+
let predicate = ty::Binder::dummy(ty::PredicateKind::Clause(
187+
ty::Clause::ConstArgHasType(ct, ct_ty),
188+
))
189+
.to_predicate(tcx);
190+
predicates.insert((predicate, param.span));
191+
179192
index += 1;
180193
}
181194
}
@@ -439,7 +452,9 @@ pub(super) fn explicit_predicates_of<'tcx>(
439452
let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local());
440453
let parent_def_id = tcx.hir().get_parent_item(hir_id);
441454

442-
if tcx.hir().opt_const_param_default_param_def_id(hir_id).is_some() {
455+
if let Some(defaulted_param_def_id) =
456+
tcx.hir().opt_const_param_default_param_def_id(hir_id)
457+
{
443458
// In `generics_of` we set the generics' parent to be our parent's parent which means that
444459
// we lose out on the predicates of our actual parent if we dont return those predicates here.
445460
// (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>(
452467
//
453468
// In the above code we want the anon const to have predicates in its param env for `T: Trait`
454469
// and we would be calling `explicit_predicates_of(Foo)` here
455-
return tcx.explicit_predicates_of(parent_def_id);
470+
let parent_preds = tcx.explicit_predicates_of(parent_def_id);
471+
472+
// If we dont filter out `ConstArgHasType` predicates then every single defaulted const parameter
473+
// will ICE because of #106994. FIXME(generic_const_exprs): remove this when a more general solution
474+
// to #106994 is implemented.
475+
let filtered_predicates = parent_preds
476+
.predicates
477+
.into_iter()
478+
.filter(|(pred, _)| {
479+
if let ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(ct, _)) =
480+
pred.kind().skip_binder()
481+
{
482+
match ct.kind() {
483+
ty::ConstKind::Param(param_const) => {
484+
let defaulted_param_idx = tcx
485+
.generics_of(parent_def_id)
486+
.param_def_id_to_index[&defaulted_param_def_id.to_def_id()];
487+
param_const.index < defaulted_param_idx
488+
}
489+
_ => bug!(
490+
"`ConstArgHasType` in `predicates_of`\
491+
that isn't a `Param` const"
492+
),
493+
}
494+
} else {
495+
true
496+
}
497+
})
498+
.cloned();
499+
return GenericPredicates {
500+
parent: parent_preds.parent,
501+
predicates: { tcx.arena.alloc_from_iter(filtered_predicates) },
502+
};
456503
}
457504

458505
let parent_def_kind = tcx.def_kind(parent_def_id);

compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs

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

compiler/rustc_privacy/src/lib.rs

+13-1
Original file line numberDiff line numberDiff line change
@@ -159,9 +159,21 @@ where
159159
_region,
160160
))) => ty.visit_with(self),
161161
ty::PredicateKind::Clause(ty::Clause::RegionOutlives(..)) => ControlFlow::Continue(()),
162+
ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(ct, ty)) => {
163+
ct.visit_with(self)?;
164+
ty.visit_with(self)
165+
}
162166
ty::PredicateKind::ConstEvaluatable(ct) => ct.visit_with(self),
163167
ty::PredicateKind::WellFormed(arg) => arg.visit_with(self),
164-
_ => bug!("unexpected predicate: {:?}", predicate),
168+
169+
ty::PredicateKind::ObjectSafe(_)
170+
| ty::PredicateKind::ClosureKind(_, _, _)
171+
| ty::PredicateKind::Subtype(_)
172+
| ty::PredicateKind::Coerce(_)
173+
| ty::PredicateKind::ConstEquate(_, _)
174+
| ty::PredicateKind::TypeWellFormedFromEnv(_)
175+
| ty::PredicateKind::Ambiguous
176+
| ty::PredicateKind::AliasEq(_, _) => bug!("unexpected predicate: {:?}", predicate),
165177
}
166178
}
167179

compiler/rustc_trait_selection/src/solve/mod.rs

+13-3
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ use rustc_infer::traits::query::NoSolution;
2525
use rustc_infer::traits::Obligation;
2626
use rustc_middle::infer::canonical::Certainty as OldCertainty;
2727
use rustc_middle::traits::solve::{ExternalConstraints, ExternalConstraintsData};
28-
use rustc_middle::ty::{self, TyCtxt};
28+
use rustc_middle::ty::{self, Ty, TyCtxt};
2929
use rustc_middle::ty::{
3030
CoercePredicate, RegionOutlivesPredicate, SubtypePredicate, ToPredicate, TypeOutlivesPredicate,
3131
};
@@ -290,8 +290,8 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
290290
ty::PredicateKind::Clause(ty::Clause::RegionOutlives(predicate)) => {
291291
self.compute_region_outlives_goal(Goal { param_env, predicate })
292292
}
293-
ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(..)) => {
294-
unimplemented!()
293+
ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(ct, ty)) => {
294+
self.compute_const_arg_has_type_goal(Goal { param_env, predicate: (ct, ty) })
295295
}
296296
ty::PredicateKind::Subtype(predicate) => {
297297
self.compute_subtype_goal(Goal { param_env, predicate })
@@ -474,6 +474,16 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
474474
}
475475
}
476476
}
477+
478+
#[instrument(level = "debug", skip(self), ret)]
479+
fn compute_const_arg_has_type_goal(
480+
&mut self,
481+
goal: Goal<'tcx, (ty::Const<'tcx>, Ty<'tcx>)>,
482+
) -> QueryResult<'tcx> {
483+
let (ct, ty) = goal.predicate;
484+
let nested_goals = self.infcx.eq(goal.param_env, ct.ty(), ty)?;
485+
self.evaluate_all_and_make_canonical_response(nested_goals)
486+
}
477487
}
478488

479489
impl<'tcx> EvalCtxt<'_, 'tcx> {

compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -1283,9 +1283,11 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
12831283
"AliasEq predicate should never be the predicate cause of a SelectionError"
12841284
),
12851285

1286-
ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(..)) => {
1287-
// FIXME: don't know how selection error works so unsure when this is reachable (if it is)
1288-
unimplemented!()
1286+
ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(ct, ty)) => {
1287+
self.tcx.sess.struct_span_err(
1288+
span,
1289+
&format!("the constant `{}` is not of type `{}`", ct, ty),
1290+
)
12891291
}
12901292
}
12911293
}

compiler/rustc_trait_selection/src/traits/fulfill.rs

+12-2
Original file line numberDiff line numberDiff line change
@@ -601,8 +601,18 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
601601
ty::PredicateKind::AliasEq(..) => {
602602
bug!("AliasEq is only used for new solver")
603603
}
604-
ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(..)) => {
605-
unimplemented!()
604+
ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(ct, ty)) => {
605+
match self
606+
.selcx
607+
.infcx
608+
.at(&obligation.cause, obligation.param_env)
609+
.eq(ct.ty(), ty)
610+
{
611+
Ok(inf_ok) => ProcessResult::Changed(mk_pending(inf_ok.into_obligations())),
612+
Err(_) => ProcessResult::Error(FulfillmentErrorCode::CodeSelectionError(
613+
SelectionError::Unimplemented,
614+
)),
615+
}
606616
}
607617
},
608618
}

compiler/rustc_trait_selection/src/traits/select/mod.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -993,8 +993,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
993993
bug!("AliasEq is only used for new solver")
994994
}
995995
ty::PredicateKind::Ambiguous => Ok(EvaluatedToAmbig),
996-
ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(..)) => {
997-
unimplemented!()
996+
ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(ct, ty)) => {
997+
match self.infcx.at(&obligation.cause, obligation.param_env).eq(ct.ty(), ty) {
998+
Ok(inf_ok) => self.evaluate_predicates_recursively(
999+
previous_stack,
1000+
inf_ok.into_obligations(),
1001+
),
1002+
Err(_) => Ok(EvaluatedToErr),
1003+
}
9981004
}
9991005
}
10001006
})

tests/ui/const-generics/nested-type.min.stderr

+10-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
error[E0015]: cannot call non-const fn `Foo::{constant#0}::Foo::<17>::value` in constants
2+
--> $DIR/nested-type.rs:15:5
3+
|
4+
LL | Foo::<17>::value()
5+
| ^^^^^^^^^^^^^^^^^^
6+
|
7+
= note: calls in constants are limited to constant functions, tuple structs and tuple variants
8+
19
error: `[u8; {
210
struct Foo<const N: usize>;
311

@@ -24,5 +32,6 @@ LL | | }]>;
2432
= note: the only supported types are integers, `bool` and `char`
2533
= help: more complex types are supported with `#![feature(adt_const_params)]`
2634

27-
error: aborting due to previous error
35+
error: aborting due to 2 previous errors
2836

37+
For more information about this error, try `rustc --explain E0015`.

tests/ui/const-generics/nested-type.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ struct Foo<const N: [u8; { //[min]~ ERROR `[u8; _]` is forbidden
1313
}
1414

1515
Foo::<17>::value()
16-
//[full]~^ ERROR cannot call non-const fn
16+
//~^ ERROR cannot call non-const fn
1717
}]>;
1818

1919
fn main() {}

0 commit comments

Comments
 (0)