X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=compiler%2Frustc_trait_selection%2Fsrc%2Ftraits%2Fconst_evaluatable.rs;h=709dd346efcc5203ce4f68996a29af983e3f3ecf;hb=ac60db231c96738b874fb31a755ef49a0d71926c;hp=40a39c4cfc2e39564eed2c6cd2f87d4c28eddc84;hpb=db374bd440fb19dd697524ff7b317219522dc545;p=rust.git diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index 40a39c4cfc2..709dd346efc 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -14,7 +14,9 @@ use rustc_index::vec::IndexVec; use rustc_infer::infer::InferCtxt; use rustc_middle::mir; -use rustc_middle::mir::interpret::ErrorHandled; +use rustc_middle::mir::interpret::{ + ConstValue, ErrorHandled, LitToConstError, LitToConstInput, Scalar, +}; use rustc_middle::thir; use rustc_middle::thir::abstract_const::{self, Node, NodeId, NotConstEvaluatable}; use rustc_middle::ty::subst::{Subst, SubstsRef}; @@ -28,13 +30,13 @@ use std::ops::ControlFlow; /// Check if a given constant can be evaluated. +#[instrument(skip(infcx), level = "debug")] pub fn is_const_evaluatable<'cx, 'tcx>( infcx: &InferCtxt<'cx, 'tcx>, uv: ty::Unevaluated<'tcx, ()>, param_env: ty::ParamEnv<'tcx>, span: Span, ) -> Result<(), NotConstEvaluatable> { - debug!("is_const_evaluatable({:?})", uv); let tcx = infcx.tcx; if tcx.features().generic_const_exprs { @@ -166,8 +168,7 @@ enum FailureKind { "#![feature(generic_const_exprs)]\n".to_string(), rustc_errors::Applicability::MaybeIncorrect, ) - .emit(); - rustc_errors::FatalError.raise(); + .emit() } debug!(?concrete, "is_const_evaluatable"); @@ -186,6 +187,7 @@ enum FailureKind { } } +#[instrument(skip(tcx), level = "debug")] fn satisfied_from_param_env<'tcx>( tcx: TyCtxt<'tcx>, ct: AbstractConst<'tcx>, @@ -195,14 +197,17 @@ fn satisfied_from_param_env<'tcx>( match pred.kind().skip_binder() { ty::PredicateKind::ConstEvaluatable(uv) => { if let Some(b_ct) = AbstractConst::new(tcx, uv)? { + let const_unify_ctxt = ConstUnifyCtxt { tcx, param_env }; + // Try to unify with each subtree in the AbstractConst to allow for // `N + 1` being const evaluatable even if theres only a `ConstEvaluatable` // predicate for `(N + 1) * 2` - let result = - walk_abstract_const(tcx, b_ct, |b_ct| match try_unify(tcx, ct, b_ct) { + let result = walk_abstract_const(tcx, b_ct, |b_ct| { + match const_unify_ctxt.try_unify(ct, b_ct) { true => ControlFlow::BREAK, false => ControlFlow::CONTINUE, - }); + } + }); if let ControlFlow::Break(()) = result { debug!("is_const_evaluatable: abstract_const ~~> ok"); @@ -304,6 +309,7 @@ fn maybe_supported_error(&mut self, span: Span, msg: &str) -> Result, (body, body_id): (&'a thir::Thir<'tcx>, thir::ExprId), @@ -315,21 +321,57 @@ struct IsThirPolymorphic<'a, 'tcx> { thir: &'a thir::Thir<'tcx>, } + use crate::rustc_middle::thir::visit::Visitor; use thir::visit; - impl<'a, 'tcx: 'a> visit::Visitor<'a, 'tcx> for IsThirPolymorphic<'a, 'tcx> { + + impl<'a, 'tcx> IsThirPolymorphic<'a, 'tcx> { + fn expr_is_poly(&mut self, expr: &thir::Expr<'tcx>) -> bool { + if expr.ty.has_param_types_or_consts() { + return true; + } + + match expr.kind { + thir::ExprKind::NamedConst { substs, .. } => substs.has_param_types_or_consts(), + thir::ExprKind::ConstParam { .. } => true, + thir::ExprKind::Repeat { value, count } => { + self.visit_expr(&self.thir()[value]); + count.has_param_types_or_consts() + } + _ => false, + } + } + + fn pat_is_poly(&mut self, pat: &thir::Pat<'tcx>) -> bool { + if pat.ty.has_param_types_or_consts() { + return true; + } + + match pat.kind.as_ref() { + thir::PatKind::Constant { value } => value.has_param_types_or_consts(), + thir::PatKind::Range(thir::PatRange { lo, hi, .. }) => { + lo.has_param_types_or_consts() || hi.has_param_types_or_consts() + } + _ => false, + } + } + } + + impl<'a, 'tcx> visit::Visitor<'a, 'tcx> for IsThirPolymorphic<'a, 'tcx> { fn thir(&self) -> &'a thir::Thir<'tcx> { &self.thir } + #[instrument(skip(self), level = "debug")] fn visit_expr(&mut self, expr: &thir::Expr<'tcx>) { - self.is_poly |= expr.ty.has_param_types_or_consts(); + self.is_poly |= self.expr_is_poly(expr); if !self.is_poly { visit::walk_expr(self, expr) } } + #[instrument(skip(self), level = "debug")] fn visit_pat(&mut self, pat: &thir::Pat<'tcx>) { - self.is_poly |= pat.ty.has_param_types_or_consts(); + self.is_poly |= self.pat_is_poly(pat); if !self.is_poly { visit::walk_pat(self, pat); } @@ -338,6 +380,10 @@ fn visit_pat(&mut self, pat: &thir::Pat<'tcx>) { fn visit_const(&mut self, ct: ty::Const<'tcx>) { self.is_poly |= ct.has_param_types_or_consts(); } + + fn visit_constant(&mut self, ct: mir::ConstantKind<'tcx>) { + self.is_poly |= ct.has_param_types_or_consts(); + } } let mut is_poly_vis = IsThirPolymorphic { is_poly: false, thir: body }; @@ -370,7 +416,7 @@ fn check_unop(op: mir::UnOp) -> bool { } /// Builds the abstract const by walking the thir and bailing out when - /// encountering an unspported operation. + /// encountering an unsupported operation. fn build(mut self) -> Result<&'tcx [Node<'tcx>], ErrorGuaranteed> { debug!("Abstractconstbuilder::build: body={:?}", &*self.body); self.recurse_build(self.body_id)?; @@ -393,16 +439,49 @@ fn build(mut self) -> Result<&'tcx [Node<'tcx>], ErrorGuaranteed> { fn recurse_build(&mut self, node: thir::ExprId) -> Result { use thir::ExprKind; let node = &self.body.exprs[node]; - debug!("recurse_build: node={:?}", node); Ok(match &node.kind { // I dont know if handling of these 3 is correct &ExprKind::Scope { value, .. } => self.recurse_build(value)?, &ExprKind::PlaceTypeAscription { source, .. } | &ExprKind::ValueTypeAscription { source, .. } => self.recurse_build(source)?, + &ExprKind::Literal { lit, neg} => { + let sp = node.span; + let constant = + match self.tcx.at(sp).lit_to_const(LitToConstInput { lit: &lit.node, ty: node.ty, neg }) { + Ok(c) => c, + Err(LitToConstError::Reported) => { + self.tcx.const_error(node.ty) + } + Err(LitToConstError::TypeError) => { + bug!("encountered type error in lit_to_const") + } + }; + + self.nodes.push(Node::Leaf(constant)) + } + &ExprKind::NonHirLiteral { lit , user_ty: _} => { + // FIXME Construct a Valtree from this ScalarInt when introducing Valtrees + let const_value = ConstValue::Scalar(Scalar::Int(lit)); + self.nodes.push(Node::Leaf(ty::Const::from_value(self.tcx, const_value, node.ty))) + } + &ExprKind::NamedConst { def_id, substs, user_ty: _ } => { + let uneval = ty::Unevaluated::new(ty::WithOptConstParam::unknown(def_id), substs); + + let constant = self.tcx.mk_const(ty::ConstS { + val: ty::ConstKind::Unevaluated(uneval), + ty: node.ty, + }); + + self.nodes.push(Node::Leaf(constant)) + } - // subtle: associated consts are literals this arm handles - // `::ASSOC` as well as `12` - &ExprKind::Literal { literal, .. } => self.nodes.push(Node::Leaf(literal)), + ExprKind::ConstParam {param, ..} => { + let const_param = self.tcx.mk_const(ty::ConstS { + val: ty::ConstKind::Param(*param), + ty: node.ty, + }); + self.nodes.push(Node::Leaf(const_param)) + } ExprKind::Call { fun, args, .. } => { let fun = self.recurse_build(*fun)?; @@ -566,14 +645,17 @@ pub(super) fn thir_abstract_const<'tcx>( } } +#[instrument(skip(tcx), level = "debug")] pub(super) fn try_unify_abstract_consts<'tcx>( tcx: TyCtxt<'tcx>, (a, b): (ty::Unevaluated<'tcx, ()>, ty::Unevaluated<'tcx, ()>), + param_env: ty::ParamEnv<'tcx>, ) -> bool { (|| { if let Some(a) = AbstractConst::new(tcx, a)? { if let Some(b) = AbstractConst::new(tcx, b)? { - return Ok(try_unify(tcx, a, b)); + let const_unify_ctxt = ConstUnifyCtxt { tcx, param_env }; + return Ok(const_unify_ctxt.try_unify(a, b)); } } @@ -585,6 +667,7 @@ pub(super) fn try_unify_abstract_consts<'tcx>( // on `ErrorGuaranteed`. } +#[instrument(skip(tcx, f), level = "debug")] pub fn walk_abstract_const<'tcx, R, F>( tcx: TyCtxt<'tcx>, ct: AbstractConst<'tcx>, @@ -593,6 +676,7 @@ pub fn walk_abstract_const<'tcx, R, F>( where F: FnMut(AbstractConst<'tcx>) -> ControlFlow, { + #[instrument(skip(tcx, f), level = "debug")] fn recurse<'tcx, R>( tcx: TyCtxt<'tcx>, ct: AbstractConst<'tcx>, @@ -600,6 +684,7 @@ fn recurse<'tcx, R>( ) -> ControlFlow { f(ct)?; let root = ct.root(tcx); + debug!(?root); match root { Node::Leaf(_) => ControlFlow::CONTINUE, Node::Binop(_, l, r) => { @@ -618,88 +703,163 @@ fn recurse<'tcx, R>( recurse(tcx, ct, &mut f) } -/// Tries to unify two abstract constants using structural equality. -pub(super) fn try_unify<'tcx>( +struct ConstUnifyCtxt<'tcx> { tcx: TyCtxt<'tcx>, - mut a: AbstractConst<'tcx>, - mut b: AbstractConst<'tcx>, -) -> bool { - // We substitute generics repeatedly to allow AbstractConsts to unify where a - // ConstKind::Unevalated could be turned into an AbstractConst that would unify e.g. + param_env: ty::ParamEnv<'tcx>, +} + +impl<'tcx> ConstUnifyCtxt<'tcx> { + // Substitutes generics repeatedly to allow AbstractConsts to unify where a + // ConstKind::Unevaluated could be turned into an AbstractConst that would unify e.g. // Param(N) should unify with Param(T), substs: [Unevaluated("T2", [Unevaluated("T3", [Param(N)])])] - while let Node::Leaf(a_ct) = a.root(tcx) { - match AbstractConst::from_const(tcx, a_ct) { - Ok(Some(a_act)) => a = a_act, - Ok(None) => break, - Err(_) => return true, - } - } - while let Node::Leaf(b_ct) = b.root(tcx) { - match AbstractConst::from_const(tcx, b_ct) { - Ok(Some(b_act)) => b = b_act, - Ok(None) => break, - Err(_) => return true, + #[inline] + #[instrument(skip(self), level = "debug")] + fn try_replace_substs_in_root( + &self, + mut abstr_const: AbstractConst<'tcx>, + ) -> Option> { + while let Node::Leaf(ct) = abstr_const.root(self.tcx) { + match AbstractConst::from_const(self.tcx, ct) { + Ok(Some(act)) => abstr_const = act, + Ok(None) => break, + Err(_) => return None, + } } - } - match (a.root(tcx), b.root(tcx)) { - (Node::Leaf(a_ct), Node::Leaf(b_ct)) => { - if a_ct.ty() != b_ct.ty() { - return false; - } + Some(abstr_const) + } - match (a_ct.val(), b_ct.val()) { - // We can just unify errors with everything to reduce the amount of - // emitted errors here. - (ty::ConstKind::Error(_), _) | (_, ty::ConstKind::Error(_)) => true, - (ty::ConstKind::Param(a_param), ty::ConstKind::Param(b_param)) => { - a_param == b_param + /// Tries to unify two abstract constants using structural equality. + #[instrument(skip(self), level = "debug")] + fn try_unify(&self, a: AbstractConst<'tcx>, b: AbstractConst<'tcx>) -> bool { + let a = if let Some(a) = self.try_replace_substs_in_root(a) { + a + } else { + return true; + }; + + let b = if let Some(b) = self.try_replace_substs_in_root(b) { + b + } else { + return true; + }; + + let a_root = a.root(self.tcx); + let b_root = b.root(self.tcx); + debug!(?a_root, ?b_root); + + match (a_root, b_root) { + (Node::Leaf(a_ct), Node::Leaf(b_ct)) => { + let a_ct = a_ct.eval(self.tcx, self.param_env); + debug!("a_ct evaluated: {:?}", a_ct); + let b_ct = b_ct.eval(self.tcx, self.param_env); + debug!("b_ct evaluated: {:?}", b_ct); + + if a_ct.ty() != b_ct.ty() { + return false; } - (ty::ConstKind::Value(a_val), ty::ConstKind::Value(b_val)) => a_val == b_val, - // If we have `fn a() -> [u8; N + 1]` and `fn b() -> [u8; 1 + M]` - // we do not want to use `assert_eq!(a(), b())` to infer that `N` and `M` have to be `1`. This - // means that we only allow inference variables if they are equal. - (ty::ConstKind::Infer(a_val), ty::ConstKind::Infer(b_val)) => a_val == b_val, - // We expand generic anonymous constants at the start of this function, so this - // branch should only be taking when dealing with associated constants, at - // which point directly comparing them seems like the desired behavior. - // - // FIXME(generic_const_exprs): This isn't actually the case. - // We also take this branch for concrete anonymous constants and - // expand generic anonymous constants with concrete substs. - (ty::ConstKind::Unevaluated(a_uv), ty::ConstKind::Unevaluated(b_uv)) => { - a_uv == b_uv + + match (a_ct.val(), b_ct.val()) { + // We can just unify errors with everything to reduce the amount of + // emitted errors here. + (ty::ConstKind::Error(_), _) | (_, ty::ConstKind::Error(_)) => true, + (ty::ConstKind::Param(a_param), ty::ConstKind::Param(b_param)) => { + a_param == b_param + } + (ty::ConstKind::Value(a_val), ty::ConstKind::Value(b_val)) => a_val == b_val, + // If we have `fn a() -> [u8; N + 1]` and `fn b() -> [u8; 1 + M]` + // we do not want to use `assert_eq!(a(), b())` to infer that `N` and `M` have to be `1`. This + // means that we only allow inference variables if they are equal. + (ty::ConstKind::Infer(a_val), ty::ConstKind::Infer(b_val)) => a_val == b_val, + // We expand generic anonymous constants at the start of this function, so this + // branch should only be taking when dealing with associated constants, at + // which point directly comparing them seems like the desired behavior. + // + // FIXME(generic_const_exprs): This isn't actually the case. + // We also take this branch for concrete anonymous constants and + // expand generic anonymous constants with concrete substs. + (ty::ConstKind::Unevaluated(a_uv), ty::ConstKind::Unevaluated(b_uv)) => { + a_uv == b_uv + } + // FIXME(generic_const_exprs): We may want to either actually try + // to evaluate `a_ct` and `b_ct` if they are are fully concrete or something like + // this, for now we just return false here. + _ => false, } - // FIXME(generic_const_exprs): We may want to either actually try - // to evaluate `a_ct` and `b_ct` if they are are fully concrete or something like - // this, for now we just return false here. - _ => false, } + (Node::Binop(a_op, al, ar), Node::Binop(b_op, bl, br)) if a_op == b_op => { + self.try_unify(a.subtree(al), b.subtree(bl)) + && self.try_unify(a.subtree(ar), b.subtree(br)) + } + (Node::UnaryOp(a_op, av), Node::UnaryOp(b_op, bv)) if a_op == b_op => { + self.try_unify(a.subtree(av), b.subtree(bv)) + } + (Node::FunctionCall(a_f, a_args), Node::FunctionCall(b_f, b_args)) + if a_args.len() == b_args.len() => + { + self.try_unify(a.subtree(a_f), b.subtree(b_f)) + && iter::zip(a_args, b_args) + .all(|(&an, &bn)| self.try_unify(a.subtree(an), b.subtree(bn))) + } + (Node::Cast(a_kind, a_operand, a_ty), Node::Cast(b_kind, b_operand, b_ty)) + if (a_ty == b_ty) && (a_kind == b_kind) => + { + self.try_unify(a.subtree(a_operand), b.subtree(b_operand)) + } + // use this over `_ => false` to make adding variants to `Node` less error prone + (Node::Cast(..), _) + | (Node::FunctionCall(..), _) + | (Node::UnaryOp(..), _) + | (Node::Binop(..), _) + | (Node::Leaf(..), _) => false, } - (Node::Binop(a_op, al, ar), Node::Binop(b_op, bl, br)) if a_op == b_op => { - try_unify(tcx, a.subtree(al), b.subtree(bl)) - && try_unify(tcx, a.subtree(ar), b.subtree(br)) - } - (Node::UnaryOp(a_op, av), Node::UnaryOp(b_op, bv)) if a_op == b_op => { - try_unify(tcx, a.subtree(av), b.subtree(bv)) - } - (Node::FunctionCall(a_f, a_args), Node::FunctionCall(b_f, b_args)) - if a_args.len() == b_args.len() => - { - try_unify(tcx, a.subtree(a_f), b.subtree(b_f)) - && iter::zip(a_args, b_args) - .all(|(&an, &bn)| try_unify(tcx, a.subtree(an), b.subtree(bn))) - } - (Node::Cast(a_kind, a_operand, a_ty), Node::Cast(b_kind, b_operand, b_ty)) - if (a_ty == b_ty) && (a_kind == b_kind) => - { - try_unify(tcx, a.subtree(a_operand), b.subtree(b_operand)) - } - // use this over `_ => false` to make adding variants to `Node` less error prone - (Node::Cast(..), _) - | (Node::FunctionCall(..), _) - | (Node::UnaryOp(..), _) - | (Node::Binop(..), _) - | (Node::Leaf(..), _) => false, } } + +/* Think I need these changes +======= + match (a_ct, b_ct) { + (mir::ConstantKind::Ty(a_ct), mir::ConstantKind::Ty(b_ct)) => { + match (a_ct.val(), b_ct.val()) { + // We can just unify errors with everything to reduce the amount of + // emitted errors here. + (ty::ConstKind::Error(_), _) | (_, ty::ConstKind::Error(_)) => true, + (ty::ConstKind::Param(a_param), ty::ConstKind::Param(b_param)) => { + a_param == b_param + } + (ty::ConstKind::Value(a_val), ty::ConstKind::Value(b_val)) => { + a_val == b_val + } + + // If we have `fn a() -> [u8; N + 1]` and `fn b() -> [u8; 1 + M]` + // we do not want to use `assert_eq!(a(), b())` to infer that `N` and `M` have to be `1`. This + // means that we only allow inference variables if they are equal. + (ty::ConstKind::Infer(a_val), ty::ConstKind::Infer(b_val)) => { + a_val == b_val + } + // We expand generic anonymous constants at the start of this function, so this + // branch should only be taking when dealing with associated constants, at + // which point directly comparing them seems like the desired behavior. + // + // FIXME(generic_const_exprs): This isn't actually the case. + // We also take this branch for concrete anonymous constants and + // expand generic anonymous constants with concrete substs. + (ty::ConstKind::Unevaluated(a_uv), ty::ConstKind::Unevaluated(b_uv)) => { + a_uv == b_uv + } + // FIXME(generic_const_exprs): We may want to either actually try + // to evaluate `a_ct` and `b_ct` if they are are fully concrete or something like + // this, for now we just return false here. + _ => false, + } + } + (mir::ConstantKind::Val(a_val, a_ty), mir::ConstantKind::Val(b_val, b_ty)) => { + a_val == b_val && a_ty == b_ty + } + _ => { + // FIXME Can it happen that we need to compare ConstantKind::Ty(ConstKind::Value) + // with a ConstantKind::Val and vice versa? + false +>>>>>>> 6064f16d846 (change thir to use mir::ConstantKind instead of ty::Const) + + */