Skip to content

Commit

Permalink
Rollup merge of #105339 - BoxyUwU:wf_ct_kind_expr, r=TaKO8Ki
Browse files Browse the repository at this point in the history
support `ConstKind::Expr` in `is_const_evaluatable` and `WfPredicates::compute`

Fixes #105205

Currently we haven't implemented a way to evaluate `ConstKind::Expr(Expr::Binop(Add, 1, 2))` so I just left that with a `FIXME` and a `delay_span_bug` since I have no idea how to do that and it would make this a much larger (and more complicated) PR :P
  • Loading branch information
matthiaskrgr authored Dec 6, 2022
2 parents e29a510 + c9bab74 commit 762d254
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 20 deletions.
58 changes: 40 additions & 18 deletions compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,13 @@ use crate::traits::ObligationCtxt;
#[instrument(skip(infcx), level = "debug")]
pub fn is_const_evaluatable<'tcx>(
infcx: &InferCtxt<'tcx>,
ct: ty::Const<'tcx>,
unexpanded_ct: ty::Const<'tcx>,
param_env: ty::ParamEnv<'tcx>,
span: Span,
) -> Result<(), NotConstEvaluatable> {
let tcx = infcx.tcx;
let uv = match ct.kind() {
ty::ConstKind::Unevaluated(uv) => uv,
// FIXME(generic_const_exprs): this seems wrong but I couldn't find a way to get this to trigger
ty::ConstKind::Expr(_) => bug!("unexpected expr in `is_const_evaluatable: {ct:?}"),
match unexpanded_ct.kind() {
ty::ConstKind::Unevaluated(_) | ty::ConstKind::Expr(_) => (),
ty::ConstKind::Param(_)
| ty::ConstKind::Bound(_, _)
| ty::ConstKind::Placeholder(_)
Expand All @@ -43,7 +41,7 @@ pub fn is_const_evaluatable<'tcx>(
};

if tcx.features().generic_const_exprs {
let ct = tcx.expand_abstract_consts(ct);
let ct = tcx.expand_abstract_consts(unexpanded_ct);

let is_anon_ct = if let ty::ConstKind::Unevaluated(uv) = ct.kind() {
tcx.def_kind(uv.def.did) == DefKind::AnonConst
Expand All @@ -62,18 +60,40 @@ pub fn is_const_evaluatable<'tcx>(
}
}

let concrete = infcx.const_eval_resolve(param_env, uv, Some(span));
match concrete {
Err(ErrorHandled::TooGeneric) => Err(NotConstEvaluatable::Error(
infcx
.tcx
.sess
.delay_span_bug(span, "Missing value for constant, but no error reported?"),
)),
Err(ErrorHandled::Reported(e)) => Err(NotConstEvaluatable::Error(e)),
Ok(_) => Ok(()),
match unexpanded_ct.kind() {
ty::ConstKind::Expr(_) => {
// FIXME(generic_const_exprs): we have a `ConstKind::Expr` which is fully concrete, but
// currently it is not possible to evaluate `ConstKind::Expr` so we are unable to tell if it
// is evaluatable or not. For now we just ICE until this is implemented this.
Err(NotConstEvaluatable::Error(tcx.sess.delay_span_bug(
span,
"evaluating `ConstKind::Expr` is not currently supported",
)))
}
ty::ConstKind::Unevaluated(uv) => {
let concrete = infcx.const_eval_resolve(param_env, uv, Some(span));
match concrete {
Err(ErrorHandled::TooGeneric) => {
Err(NotConstEvaluatable::Error(infcx.tcx.sess.delay_span_bug(
span,
"Missing value for constant, but no error reported?",
)))
}
Err(ErrorHandled::Reported(e)) => Err(NotConstEvaluatable::Error(e)),
Ok(_) => Ok(()),
}
}
_ => bug!("unexpected constkind in `is_const_evalautable: {unexpanded_ct:?}`"),
}
} else {
let uv = match unexpanded_ct.kind() {
ty::ConstKind::Unevaluated(uv) => uv,
ty::ConstKind::Expr(_) => {
bug!("`ConstKind::Expr` without `feature(generic_const_exprs)` enabled")
}
_ => bug!("unexpected constkind in `is_const_evalautable: {unexpanded_ct:?}`"),
};

// FIXME: We should only try to evaluate a given constant here if it is fully concrete
// as we don't want to allow things like `[u8; std::mem::size_of::<*mut T>()]`.
//
Expand All @@ -92,7 +112,7 @@ pub fn is_const_evaluatable<'tcx>(
&& satisfied_from_param_env(
tcx,
infcx,
tcx.expand_abstract_consts(ct),
tcx.expand_abstract_consts(unexpanded_ct),
param_env,
) =>
{
Expand Down Expand Up @@ -152,6 +172,7 @@ fn satisfied_from_param_env<'tcx>(
impl<'a, 'tcx> TypeVisitor<'tcx> for Visitor<'a, 'tcx> {
type BreakTy = ();
fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
debug!("is_const_evaluatable: candidate={:?}", c);
if let Ok(()) = self.infcx.commit_if_ok(|_| {
let ocx = ObligationCtxt::new_in_snapshot(self.infcx);
if let Ok(()) = ocx.eq(&ObligationCause::dummy(), self.param_env, c.ty(), self.ct.ty())
Expand Down Expand Up @@ -187,13 +208,14 @@ fn satisfied_from_param_env<'tcx>(
let result = b_ct.visit_with(&mut v);

if let ControlFlow::Break(()) = result {
debug!("is_const_evaluatable: abstract_const ~~> ok");
debug!("is_const_evaluatable: yes");
return true;
}
}
_ => {} // don't care
}
}

debug!("is_const_evaluatable: no");
false
}
19 changes: 17 additions & 2 deletions compiler/rustc_trait_selection/src/traits/wf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -476,9 +476,24 @@ impl<'tcx> WfPredicates<'tcx> {
ty::Binder::dummy(ty::PredicateKind::WellFormed(ct.into())),
));
}
// FIXME(generic_const_exprs): This seems wrong but I could not find a way to get this to trigger
ty::ConstKind::Expr(_) => {
bug!("checking wfness of `ConstKind::Expr` is unsupported")
// FIXME(generic_const_exprs): this doesnt verify that given `Expr(N + 1)` the
// trait bound `typeof(N): Add<typeof(1)>` holds. This is currently unnecessary
// as `ConstKind::Expr` is only produced via normalization of `ConstKind::Unevaluated`
// which means that the `DefId` would have been typeck'd elsewhere. However in
// the future we may allow directly lowering to `ConstKind::Expr` in which case
// we would not be proving bounds we should.

let predicate =
ty::Binder::dummy(ty::PredicateKind::ConstEvaluatable(ct));
let cause = self.cause(traits::WellFormed(None));
self.out.push(traits::Obligation::with_depth(
self.tcx(),
cause,
self.recursion_depth,
self.param_env,
predicate,
));
}

ty::ConstKind::Error(_)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#![feature(generic_const_exprs, generic_arg_infer)]
#![allow(incomplete_features)]

// minimized repro for #105205
//
// the `foo::<_, L>` call results in a `WellFormed(_)` obligation and a
// `ConstEvaluatable(Unevaluated(_ + 1 + L))` obligation. Attempting to fulfill the latter
// unifies the `_` with `Expr(L - 1)` from the paramenv which turns the `WellFormed`
// obligation into `WellFormed(Expr(L - 1))`

fn foo<const N: usize, const M: usize>(_: [(); N + 1 + M]) {}

fn ice<const L: usize>()
where
[(); (L - 1) + 1 + L]:,
{
foo::<_, L>([(); L + 1 + L]);
//~^ ERROR: mismatched types
//~^^ ERROR: unconstrained generic constant
}

fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
error[E0308]: mismatched types
--> $DIR/wf_obligation.rs:17:17
|
LL | foo::<_, L>([(); L + 1 + L]);
| ^^^^^^^^^^^^^^^ expected `N + 1 + M`, found `L + 1 + L`
|
= note: expected constant `N + 1 + M`
found constant `L + 1 + L`

error: unconstrained generic constant
--> $DIR/wf_obligation.rs:17:22
|
LL | foo::<_, L>([(); L + 1 + L]);
| ^^^^^^^^^
|
= help: try adding a `where` bound using this expression: `where [(); L + 1 + L]:`

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0308`.

0 comments on commit 762d254

Please sign in to comment.