| use rustc_errors::ErrorGuaranteed; | |
| use rustc_hir::def::DefKind; | |
| use rustc_hir::def_id::LocalDefId; | |
| use rustc_middle::query::Providers; | |
| use rustc_middle::thir::visit; | |
| use rustc_middle::thir::visit::Visitor; | |
| use rustc_middle::ty::abstract_const::CastKind; | |
| use rustc_middle::ty::{self, Expr, LitToConstInput, TyCtxt, TypeVisitableExt}; | |
| use rustc_middle::{mir, thir}; | |
| use rustc_span::Span; | |
| use tracing::instrument; | |
| use crate::errors::{GenericConstantTooComplex, GenericConstantTooComplexSub}; | |
| /// We do not allow all binary operations in abstract consts, so filter disallowed ones. | |
| fn check_binop(op: mir::BinOp) -> bool { | |
| use mir::BinOp::*; | |
| match op { | |
| Add | AddUnchecked | AddWithOverflow | Sub | SubUnchecked | SubWithOverflow | Mul | |
| | MulUnchecked | MulWithOverflow | Div | Rem | BitXor | BitAnd | BitOr | Shl | |
| | ShlUnchecked | Shr | ShrUnchecked | Eq | Lt | Le | Ne | Ge | Gt | Cmp => true, | |
| Offset => false, | |
| } | |
| } | |
| /// While we currently allow all unary operations, we still want to explicitly guard against | |
| /// future changes here. | |
| fn check_unop(op: mir::UnOp) -> bool { | |
| use mir::UnOp::*; | |
| match op { | |
| Not | Neg | PtrMetadata => true, | |
| } | |
| } | |
| fn recurse_build<'tcx>( | |
| tcx: TyCtxt<'tcx>, | |
| body: &thir::Thir<'tcx>, | |
| node: thir::ExprId, | |
| root_span: Span, | |
| ) -> Result<ty::Const<'tcx>, ErrorGuaranteed> { | |
| use thir::ExprKind; | |
| let node = &body.exprs[node]; | |
| let maybe_supported_error = |a| maybe_supported_error(tcx, a, root_span); | |
| let error = |a| error(tcx, a, root_span); | |
| Ok(match &node.kind { | |
| // I dont know if handling of these 3 is correct | |
| &ExprKind::Scope { value, .. } => recurse_build(tcx, body, value, root_span)?, | |
| &ExprKind::PlaceTypeAscription { source, .. } | |
| | &ExprKind::ValueTypeAscription { source, .. } => { | |
| recurse_build(tcx, body, source, root_span)? | |
| } | |
| &ExprKind::PlaceUnwrapUnsafeBinder { .. } | |
| | &ExprKind::ValueUnwrapUnsafeBinder { .. } | |
| | &ExprKind::WrapUnsafeBinder { .. } => { | |
| todo!("FIXME(unsafe_binders)") | |
| } | |
| &ExprKind::Literal { lit, neg } => { | |
| let sp = node.span; | |
| match tcx.at(sp).lit_to_const(LitToConstInput { lit: lit.node, ty: Some(node.ty), neg }) | |
| { | |
| Some(value) => ty::Const::new_value(tcx, value.valtree, value.ty), | |
| None => ty::Const::new_misc_error(tcx), | |
| } | |
| } | |
| &ExprKind::NonHirLiteral { lit, user_ty: _ } => { | |
| let val = ty::ValTree::from_scalar_int(tcx, lit); | |
| ty::Const::new_value(tcx, val, node.ty) | |
| } | |
| &ExprKind::ZstLiteral { user_ty: _ } => ty::Const::zero_sized(tcx, node.ty), | |
| &ExprKind::NamedConst { def_id, args, user_ty: _ } => { | |
| let uneval = ty::UnevaluatedConst::new(def_id, args); | |
| ty::Const::new_unevaluated(tcx, uneval) | |
| } | |
| ExprKind::ConstParam { param, .. } => ty::Const::new_param(tcx, *param), | |
| ExprKind::Call { fun, args, .. } => { | |
| let fun_ty = body.exprs[*fun].ty; | |
| let fun = recurse_build(tcx, body, *fun, root_span)?; | |
| let mut new_args = Vec::<ty::Const<'tcx>>::with_capacity(args.len()); | |
| for &id in args.iter() { | |
| new_args.push(recurse_build(tcx, body, id, root_span)?); | |
| } | |
| ty::Const::new_expr(tcx, Expr::new_call(tcx, fun_ty, fun, new_args)) | |
| } | |
| &ExprKind::Binary { op, lhs, rhs } if check_binop(op) => { | |
| let lhs_ty = body.exprs[lhs].ty; | |
| let lhs = recurse_build(tcx, body, lhs, root_span)?; | |
| let rhs_ty = body.exprs[rhs].ty; | |
| let rhs = recurse_build(tcx, body, rhs, root_span)?; | |
| ty::Const::new_expr(tcx, Expr::new_binop(tcx, op, lhs_ty, rhs_ty, lhs, rhs)) | |
| } | |
| &ExprKind::Unary { op, arg } if check_unop(op) => { | |
| let arg_ty = body.exprs[arg].ty; | |
| let arg = recurse_build(tcx, body, arg, root_span)?; | |
| ty::Const::new_expr(tcx, Expr::new_unop(tcx, op, arg_ty, arg)) | |
| } | |
| // This is necessary so that the following compiles: | |
| // | |
| // ``` | |
| // fn foo<const N: usize>(a: [(); N + 1]) { | |
| // bar::<{ N + 1 }>(); | |
| // } | |
| // ``` | |
| ExprKind::Block { block } => { | |
| if let thir::Block { stmts: box [], expr: Some(e), .. } = &body.blocks[*block] { | |
| recurse_build(tcx, body, *e, root_span)? | |
| } else { | |
| maybe_supported_error(GenericConstantTooComplexSub::BlockNotSupported(node.span))? | |
| } | |
| } | |
| // `ExprKind::Use` happens when a `hir::ExprKind::Cast` is a | |
| // "coercion cast" i.e. using a coercion or is a no-op. | |
| // This is important so that `N as usize as usize` doesn't unify with `N as usize`. (untested) | |
| &ExprKind::Use { source } => { | |
| let value_ty = body.exprs[source].ty; | |
| let value = recurse_build(tcx, body, source, root_span)?; | |
| ty::Const::new_expr(tcx, Expr::new_cast(tcx, CastKind::Use, value_ty, value, node.ty)) | |
| } | |
| &ExprKind::Cast { source } => { | |
| let value_ty = body.exprs[source].ty; | |
| let value = recurse_build(tcx, body, source, root_span)?; | |
| ty::Const::new_expr(tcx, Expr::new_cast(tcx, CastKind::As, value_ty, value, node.ty)) | |
| } | |
| ExprKind::Borrow { arg, .. } => { | |
| let arg_node = &body.exprs[*arg]; | |
| // Skip reborrows for now until we allow Deref/Borrow/RawBorrow | |
| // expressions. | |
| // FIXME(generic_const_exprs): Verify/explain why this is sound | |
| if let ExprKind::Deref { arg } = arg_node.kind { | |
| recurse_build(tcx, body, arg, root_span)? | |
| } else { | |
| maybe_supported_error(GenericConstantTooComplexSub::BorrowNotSupported(node.span))? | |
| } | |
| } | |
| // FIXME(generic_const_exprs): We may want to support these. | |
| ExprKind::RawBorrow { .. } | ExprKind::Deref { .. } => maybe_supported_error( | |
| GenericConstantTooComplexSub::AddressAndDerefNotSupported(node.span), | |
| )?, | |
| ExprKind::Repeat { .. } | ExprKind::Array { .. } => { | |
| maybe_supported_error(GenericConstantTooComplexSub::ArrayNotSupported(node.span))? | |
| } | |
| ExprKind::NeverToAny { .. } => { | |
| maybe_supported_error(GenericConstantTooComplexSub::NeverToAnyNotSupported(node.span))? | |
| } | |
| ExprKind::Tuple { .. } => { | |
| maybe_supported_error(GenericConstantTooComplexSub::TupleNotSupported(node.span))? | |
| } | |
| ExprKind::Index { .. } => { | |
| maybe_supported_error(GenericConstantTooComplexSub::IndexNotSupported(node.span))? | |
| } | |
| ExprKind::Field { .. } => { | |
| maybe_supported_error(GenericConstantTooComplexSub::FieldNotSupported(node.span))? | |
| } | |
| ExprKind::ConstBlock { .. } => { | |
| maybe_supported_error(GenericConstantTooComplexSub::ConstBlockNotSupported(node.span))? | |
| } | |
| ExprKind::Adt(_) => { | |
| maybe_supported_error(GenericConstantTooComplexSub::AdtNotSupported(node.span))? | |
| } | |
| // dont know if this is correct | |
| ExprKind::PointerCoercion { .. } => { | |
| error(GenericConstantTooComplexSub::PointerNotSupported(node.span))? | |
| } | |
| ExprKind::Yield { .. } => { | |
| error(GenericConstantTooComplexSub::YieldNotSupported(node.span))? | |
| } | |
| ExprKind::Continue { .. } | |
| | ExprKind::ConstContinue { .. } | |
| | ExprKind::Break { .. } | |
| | ExprKind::Loop { .. } | |
| | ExprKind::LoopMatch { .. } => { | |
| error(GenericConstantTooComplexSub::LoopNotSupported(node.span))? | |
| } | |
| ExprKind::ByUse { .. } => { | |
| error(GenericConstantTooComplexSub::ByUseNotSupported(node.span))? | |
| } | |
| ExprKind::Unary { .. } => unreachable!(), | |
| // we handle valid unary/binary ops above | |
| ExprKind::Binary { .. } => { | |
| error(GenericConstantTooComplexSub::BinaryNotSupported(node.span))? | |
| } | |
| ExprKind::LogicalOp { .. } => { | |
| error(GenericConstantTooComplexSub::LogicalOpNotSupported(node.span))? | |
| } | |
| ExprKind::Assign { .. } | ExprKind::AssignOp { .. } => { | |
| error(GenericConstantTooComplexSub::AssignNotSupported(node.span))? | |
| } | |
| // FIXME(explicit_tail_calls): maybe get `become` a new error | |
| ExprKind::Closure { .. } | ExprKind::Return { .. } | ExprKind::Become { .. } => { | |
| error(GenericConstantTooComplexSub::ClosureAndReturnNotSupported(node.span))? | |
| } | |
| // let expressions imply control flow | |
| ExprKind::Match { .. } | ExprKind::If { .. } | ExprKind::Let { .. } => { | |
| error(GenericConstantTooComplexSub::ControlFlowNotSupported(node.span))? | |
| } | |
| ExprKind::InlineAsm { .. } => { | |
| error(GenericConstantTooComplexSub::InlineAsmNotSupported(node.span))? | |
| } | |
| // we dont permit let stmts so `VarRef` and `UpvarRef` cant happen | |
| ExprKind::VarRef { .. } | |
| | ExprKind::UpvarRef { .. } | |
| | ExprKind::StaticRef { .. } | |
| | ExprKind::ThreadLocalRef(_) => { | |
| error(GenericConstantTooComplexSub::OperationNotSupported(node.span))? | |
| } | |
| }) | |
| } | |
| struct IsThirPolymorphic<'a, 'tcx> { | |
| is_poly: bool, | |
| thir: &'a thir::Thir<'tcx>, | |
| } | |
| fn error( | |
| tcx: TyCtxt<'_>, | |
| sub: GenericConstantTooComplexSub, | |
| root_span: Span, | |
| ) -> Result<!, ErrorGuaranteed> { | |
| let reported = tcx.dcx().emit_err(GenericConstantTooComplex { | |
| span: root_span, | |
| maybe_supported: false, | |
| sub, | |
| }); | |
| Err(reported) | |
| } | |
| fn maybe_supported_error( | |
| tcx: TyCtxt<'_>, | |
| sub: GenericConstantTooComplexSub, | |
| root_span: Span, | |
| ) -> Result<!, ErrorGuaranteed> { | |
| let reported = tcx.dcx().emit_err(GenericConstantTooComplex { | |
| span: root_span, | |
| maybe_supported: true, | |
| sub, | |
| }); | |
| Err(reported) | |
| } | |
| impl<'a, 'tcx> IsThirPolymorphic<'a, 'tcx> { | |
| fn expr_is_poly(&mut self, expr: &thir::Expr<'tcx>) -> bool { | |
| if expr.ty.has_non_region_param() { | |
| return true; | |
| } | |
| match expr.kind { | |
| thir::ExprKind::NamedConst { args, .. } | thir::ExprKind::ConstBlock { args, .. } => { | |
| args.has_non_region_param() | |
| } | |
| thir::ExprKind::ConstParam { .. } => true, | |
| thir::ExprKind::Repeat { value, count } => { | |
| self.visit_expr(&self.thir()[value]); | |
| count.has_non_region_param() | |
| } | |
| thir::ExprKind::Scope { .. } | |
| | thir::ExprKind::If { .. } | |
| | thir::ExprKind::Call { .. } | |
| | thir::ExprKind::ByUse { .. } | |
| | thir::ExprKind::Deref { .. } | |
| | thir::ExprKind::Binary { .. } | |
| | thir::ExprKind::LogicalOp { .. } | |
| | thir::ExprKind::Unary { .. } | |
| | thir::ExprKind::Cast { .. } | |
| | thir::ExprKind::Use { .. } | |
| | thir::ExprKind::NeverToAny { .. } | |
| | thir::ExprKind::PointerCoercion { .. } | |
| | thir::ExprKind::Loop { .. } | |
| | thir::ExprKind::LoopMatch { .. } | |
| | thir::ExprKind::Let { .. } | |
| | thir::ExprKind::Match { .. } | |
| | thir::ExprKind::Block { .. } | |
| | thir::ExprKind::Assign { .. } | |
| | thir::ExprKind::AssignOp { .. } | |
| | thir::ExprKind::Field { .. } | |
| | thir::ExprKind::Index { .. } | |
| | thir::ExprKind::VarRef { .. } | |
| | thir::ExprKind::UpvarRef { .. } | |
| | thir::ExprKind::Borrow { .. } | |
| | thir::ExprKind::RawBorrow { .. } | |
| | thir::ExprKind::Break { .. } | |
| | thir::ExprKind::Continue { .. } | |
| | thir::ExprKind::ConstContinue { .. } | |
| | thir::ExprKind::Return { .. } | |
| | thir::ExprKind::Become { .. } | |
| | thir::ExprKind::Array { .. } | |
| | thir::ExprKind::Tuple { .. } | |
| | thir::ExprKind::Adt(_) | |
| | thir::ExprKind::PlaceTypeAscription { .. } | |
| | thir::ExprKind::ValueTypeAscription { .. } | |
| | thir::ExprKind::PlaceUnwrapUnsafeBinder { .. } | |
| | thir::ExprKind::ValueUnwrapUnsafeBinder { .. } | |
| | thir::ExprKind::WrapUnsafeBinder { .. } | |
| | thir::ExprKind::Closure(_) | |
| | thir::ExprKind::Literal { .. } | |
| | thir::ExprKind::NonHirLiteral { .. } | |
| | thir::ExprKind::ZstLiteral { .. } | |
| | thir::ExprKind::StaticRef { .. } | |
| | thir::ExprKind::InlineAsm(_) | |
| | thir::ExprKind::ThreadLocalRef(_) | |
| | thir::ExprKind::Yield { .. } => false, | |
| } | |
| } | |
| fn pat_is_poly(&mut self, pat: &thir::Pat<'tcx>) -> bool { | |
| if pat.ty.has_non_region_param() { | |
| return true; | |
| } | |
| match pat.kind { | |
| thir::PatKind::Constant { value } => value.has_non_region_param(), | |
| thir::PatKind::Range(ref range) => { | |
| let &thir::PatRange { lo, hi, .. } = range.as_ref(); | |
| lo.has_non_region_param() || hi.has_non_region_param() | |
| } | |
| _ => 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: &'a thir::Expr<'tcx>) { | |
| 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: &'a thir::Pat<'tcx>) { | |
| self.is_poly |= self.pat_is_poly(pat); | |
| if !self.is_poly { | |
| visit::walk_pat(self, pat); | |
| } | |
| } | |
| } | |
| /// Builds an abstract const, do not use this directly, but use `AbstractConst::new` instead. | |
| fn thir_abstract_const<'tcx>( | |
| tcx: TyCtxt<'tcx>, | |
| def: LocalDefId, | |
| ) -> Result<Option<ty::EarlyBinder<'tcx, ty::Const<'tcx>>>, ErrorGuaranteed> { | |
| if !tcx.features().generic_const_exprs() { | |
| return Ok(None); | |
| } | |
| match tcx.def_kind(def) { | |
| // FIXME(generic_const_exprs): We currently only do this for anonymous constants, | |
| // meaning that we do not look into associated constants. I(@lcnr) am not yet sure whether | |
| // we want to look into them or treat them as opaque projections. | |
| // | |
| // Right now we do neither of that and simply always fail to unify them. | |
| DefKind::AnonConst | DefKind::InlineConst => (), | |
| _ => return Ok(None), | |
| } | |
| let body = tcx.thir_body(def)?; | |
| let (body, body_id) = (&*body.0.borrow(), body.1); | |
| let mut is_poly_vis = IsThirPolymorphic { is_poly: false, thir: body }; | |
| visit::walk_expr(&mut is_poly_vis, &body[body_id]); | |
| if !is_poly_vis.is_poly { | |
| return Ok(None); | |
| } | |
| let root_span = body.exprs[body_id].span; | |
| Ok(Some(ty::EarlyBinder::bind(recurse_build(tcx, body, body_id, root_span)?))) | |
| } | |
| pub(crate) fn provide(providers: &mut Providers) { | |
| *providers = Providers { thir_abstract_const, ..*providers }; | |
| } |