Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-ty/src/consteval_nextsolver.rs')
| -rw-r--r-- | crates/hir-ty/src/consteval_nextsolver.rs | 256 |
1 files changed, 256 insertions, 0 deletions
diff --git a/crates/hir-ty/src/consteval_nextsolver.rs b/crates/hir-ty/src/consteval_nextsolver.rs new file mode 100644 index 0000000000..6e07d3afe5 --- /dev/null +++ b/crates/hir-ty/src/consteval_nextsolver.rs @@ -0,0 +1,256 @@ +//! Constant evaluation details +// FIXME(next-solver): this should get removed as things get moved to rustc_type_ir from chalk_ir +#![allow(unused)] + +use base_db::Crate; +use hir_def::{ + EnumVariantId, GeneralConstId, + expr_store::{Body, HygieneId, path::Path}, + hir::{Expr, ExprId}, + resolver::{Resolver, ValueNs}, + type_ref::LiteralConstRef, +}; +use hir_expand::Lookup; +use rustc_type_ir::{ + UnevaluatedConst, + inherent::{IntoKind, SliceLike}, +}; +use stdx::never; +use triomphe::Arc; + +use crate::{ + ConstScalar, Interner, MemoryMap, Substitution, TraitEnvironment, + consteval::ConstEvalError, + db::HirDatabase, + generics::Generics, + infer::InferenceContext, + next_solver::{ + Const, ConstBytes, ConstKind, DbInterner, ErrorGuaranteed, GenericArg, GenericArgs, + ParamConst, SolverDefId, Ty, ValueConst, + mapping::{ChalkToNextSolver, NextSolverToChalk, convert_binder_to_early_binder}, + }, +}; + +use super::mir::{interpret_mir, lower_to_mir, pad16}; + +pub(crate) fn path_to_const<'a, 'g>( + db: &'a dyn HirDatabase, + resolver: &Resolver<'a>, + path: &Path, + args: impl FnOnce() -> &'g Generics, + expected_ty: Ty<'a>, +) -> Option<Const<'a>> { + let interner = DbInterner::new_with(db, Some(resolver.krate()), None); + match resolver.resolve_path_in_value_ns_fully(db, path, HygieneId::ROOT) { + Some(ValueNs::GenericParam(p)) => { + let args = args(); + match args + .type_or_const_param(p.into()) + .and_then(|(idx, p)| p.const_param().map(|p| (idx, p.clone()))) + { + Some((idx, _param)) => { + Some(Const::new_param(interner, ParamConst { index: idx as u32, id: p })) + } + None => { + never!( + "Generic list doesn't contain this param: {:?}, {:?}, {:?}", + args, + path, + p + ); + None + } + } + } + Some(ValueNs::ConstId(c)) => { + let args = GenericArgs::new_from_iter(interner, []); + Some(Const::new( + interner, + rustc_type_ir::ConstKind::Unevaluated(UnevaluatedConst::new( + SolverDefId::ConstId(c), + args, + )), + )) + } + _ => None, + } +} + +pub fn unknown_const<'db>(ty: Ty<'db>) -> Const<'db> { + Const::new(DbInterner::conjure(), rustc_type_ir::ConstKind::Error(ErrorGuaranteed)) +} + +pub fn unknown_const_as_generic<'db>(ty: Ty<'db>) -> GenericArg<'db> { + unknown_const(ty).into() +} + +/// Interns a constant scalar with the given type +pub fn intern_const_ref<'a>( + db: &'a dyn HirDatabase, + value: &LiteralConstRef, + ty: Ty<'a>, + krate: Crate, +) -> Const<'a> { + let interner = DbInterner::new_with(db, Some(krate), None); + let layout = db.layout_of_ty(ty, TraitEnvironment::empty(krate)); + let kind = match value { + LiteralConstRef::Int(i) => { + // FIXME: We should handle failure of layout better. + let size = layout.map(|it| it.size.bytes_usize()).unwrap_or(16); + rustc_type_ir::ConstKind::Value(ValueConst::new( + ty, + ConstBytes(i.to_le_bytes()[0..size].into(), MemoryMap::default()), + )) + } + LiteralConstRef::UInt(i) => { + let size = layout.map(|it| it.size.bytes_usize()).unwrap_or(16); + rustc_type_ir::ConstKind::Value(ValueConst::new( + ty, + ConstBytes(i.to_le_bytes()[0..size].into(), MemoryMap::default()), + )) + } + LiteralConstRef::Bool(b) => rustc_type_ir::ConstKind::Value(ValueConst::new( + ty, + ConstBytes(Box::new([*b as u8]), MemoryMap::default()), + )), + LiteralConstRef::Char(c) => rustc_type_ir::ConstKind::Value(ValueConst::new( + ty, + ConstBytes((*c as u32).to_le_bytes().into(), MemoryMap::default()), + )), + LiteralConstRef::Unknown => rustc_type_ir::ConstKind::Error(ErrorGuaranteed), + }; + Const::new(interner, kind) +} + +/// Interns a possibly-unknown target usize +pub fn usize_const<'db>(db: &'db dyn HirDatabase, value: Option<u128>, krate: Crate) -> Const<'db> { + intern_const_ref( + db, + &value.map_or(LiteralConstRef::Unknown, LiteralConstRef::UInt), + Ty::new_uint(DbInterner::new_with(db, Some(krate), None), rustc_type_ir::UintTy::Usize), + krate, + ) +} + +pub fn try_const_usize<'db>(db: &'db dyn HirDatabase, c: Const<'db>) -> Option<u128> { + let interner = DbInterner::new_with(db, None, None); + match c.kind() { + ConstKind::Param(_) => None, + ConstKind::Infer(_) => None, + ConstKind::Bound(_, _) => None, + ConstKind::Placeholder(_) => None, + ConstKind::Unevaluated(unevaluated_const) => { + let c = match unevaluated_const.def { + SolverDefId::ConstId(id) => GeneralConstId::ConstId(id), + SolverDefId::StaticId(id) => GeneralConstId::StaticId(id), + _ => unreachable!(), + }; + let subst = unevaluated_const.args.to_chalk(interner); + let ec = db.const_eval(c, subst, None).ok()?.to_nextsolver(interner); + try_const_usize(db, ec) + } + ConstKind::Value(val) => Some(u128::from_le_bytes(pad16(&val.value.inner().0, false))), + ConstKind::Error(_) => None, + ConstKind::Expr(_) => None, + } +} + +pub fn try_const_isize<'db>(db: &'db dyn HirDatabase, c: &Const<'db>) -> Option<i128> { + let interner = DbInterner::new_with(db, None, None); + match (*c).kind() { + ConstKind::Param(_) => None, + ConstKind::Infer(_) => None, + ConstKind::Bound(_, _) => None, + ConstKind::Placeholder(_) => None, + ConstKind::Unevaluated(unevaluated_const) => { + let c = match unevaluated_const.def { + SolverDefId::ConstId(id) => GeneralConstId::ConstId(id), + SolverDefId::StaticId(id) => GeneralConstId::StaticId(id), + _ => unreachable!(), + }; + let subst = unevaluated_const.args.to_chalk(interner); + let ec = db.const_eval(c, subst, None).ok()?.to_nextsolver(interner); + try_const_isize(db, &ec) + } + ConstKind::Value(val) => Some(i128::from_le_bytes(pad16(&val.value.inner().0, true))), + ConstKind::Error(_) => None, + ConstKind::Expr(_) => None, + } +} + +pub(crate) fn const_eval_discriminant_variant( + db: &dyn HirDatabase, + variant_id: EnumVariantId, +) -> Result<i128, ConstEvalError> { + let interner = DbInterner::new_with(db, None, None); + let def = variant_id.into(); + let body = db.body(def); + let loc = variant_id.lookup(db); + if matches!(body[body.body_expr], Expr::Missing) { + let prev_idx = loc.index.checked_sub(1); + let value = match prev_idx { + Some(prev_idx) => { + 1 + db.const_eval_discriminant( + loc.parent.enum_variants(db).variants[prev_idx as usize].0, + )? + } + _ => 0, + }; + return Ok(value); + } + + let repr = db.enum_signature(loc.parent).repr; + let is_signed = repr.and_then(|repr| repr.int).is_none_or(|int| int.is_signed()); + + let mir_body = db.monomorphized_mir_body( + def, + Substitution::empty(Interner), + db.trait_environment_for_body(def), + )?; + let c = interpret_mir(db, mir_body, false, None)?.0?; + let c = c.to_nextsolver(interner); + let c = if is_signed { + try_const_isize(db, &c).unwrap() + } else { + try_const_usize(db, c).unwrap() as i128 + }; + Ok(c) +} + +// FIXME: Ideally constants in const eval should have separate body (issue #7434), and this function should +// get an `InferenceResult` instead of an `InferenceContext`. And we should remove `ctx.clone().resolve_all()` here +// and make this function private. See the fixme comment on `InferenceContext::resolve_all`. +pub(crate) fn eval_to_const<'db>(expr: ExprId, ctx: &mut InferenceContext<'db>) -> Const<'db> { + let interner = DbInterner::new_with(ctx.db, None, None); + let infer = ctx.clone().resolve_all(); + fn has_closure(body: &Body, expr: ExprId) -> bool { + if matches!(body[expr], Expr::Closure { .. }) { + return true; + } + let mut r = false; + body.walk_child_exprs(expr, |idx| r |= has_closure(body, idx)); + r + } + if has_closure(ctx.body, expr) { + // Type checking clousres need an isolated body (See the above FIXME). Bail out early to prevent panic. + return unknown_const(infer[expr].clone().to_nextsolver(interner)); + } + if let Expr::Path(p) = &ctx.body[expr] { + let resolver = &ctx.resolver; + if let Some(c) = path_to_const( + ctx.db, + resolver, + p, + || ctx.generics(), + infer[expr].to_nextsolver(interner), + ) { + return c; + } + } + if let Ok(mir_body) = lower_to_mir(ctx.db, ctx.owner, ctx.body, &infer, expr) + && let Ok((Ok(result), _)) = interpret_mir(ctx.db, Arc::new(mir_body), true, None) + { + return result.to_nextsolver(interner); + } + unknown_const(infer[expr].to_nextsolver(interner)) +} |