Merge pull request #20814 from ChayimFriedman2/mir-ns

internal: Migrate MIR to next solver
This commit is contained in:
Shoyu Vanilla (Flint) 2025-10-11 16:43:41 +00:00 committed by GitHub
commit bfb0892525
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
53 changed files with 2505 additions and 2516 deletions

View file

@ -788,6 +788,7 @@ dependencies = [
"intern",
"itertools",
"la-arena 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"macros",
"oorandom",
"petgraph",
"project-model",
@ -1329,6 +1330,16 @@ dependencies = [
"url",
]
[[package]]
name = "macros"
version = "0.0.0"
dependencies = [
"proc-macro2",
"quote",
"syn",
"synstructure",
]
[[package]]
name = "mbe"
version = "0.0.0"

View file

@ -52,6 +52,7 @@ debug = 2
[workspace.dependencies]
# local crates
macros = { path = "./crates/macros", version = "0.0.0" }
base-db = { path = "./crates/base-db", version = "0.0.0" }
cfg = { path = "./crates/cfg", version = "0.0.0", features = ["tt"] }
hir = { path = "./crates/hir", version = "0.0.0" }

View file

@ -349,6 +349,7 @@ bitflags::bitflags! {
#[derive(Debug, Clone, Copy, Eq, PartialEq, Default)]
pub struct ImplFlags: u8 {
const NEGATIVE = 1 << 1;
const DEFAULT = 1 << 2;
const UNSAFE = 1 << 3;
}
}
@ -374,6 +375,9 @@ impl ImplSignature {
if src.value.excl_token().is_some() {
flags.insert(ImplFlags::NEGATIVE);
}
if src.value.default_token().is_some() {
flags.insert(ImplFlags::DEFAULT);
}
let (store, source_map, self_ty, target_trait, generic_params) =
crate::expr_store::lower::lower_impl(db, loc.container, src, id);
@ -389,6 +393,16 @@ impl ImplSignature {
Arc::new(source_map),
)
}
#[inline]
pub fn is_negative(&self) -> bool {
self.flags.contains(ImplFlags::NEGATIVE)
}
#[inline]
pub fn is_default(&self) -> bool {
self.flags.contains(ImplFlags::DEFAULT)
}
}
bitflags::bitflags! {

View file

@ -50,6 +50,7 @@ tracing-tree.workspace = true
# local deps
stdx.workspace = true
macros.workspace = true
intern.workspace = true
hir-def.workspace = true
hir-expand.workspace = true

View file

@ -130,11 +130,14 @@ impl<D> TyBuilder<D> {
}
pub fn fill_with_unknown(self) -> Self {
let interner = DbInterner::conjure();
// self.fill is inlined to make borrow checker happy
let mut this = self;
let filler = this.param_kinds[this.vec.len()..].iter().map(|x| match x {
ParamKind::Type => TyKind::Error.intern(Interner).cast(Interner),
ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()),
ParamKind::Const(ty) => {
unknown_const_as_generic(ty.to_nextsolver(interner)).to_chalk(interner)
}
ParamKind::Lifetime => error_lifetime().cast(Interner),
});
this.vec.extend(filler.casted(Interner));
@ -219,13 +222,16 @@ impl TyBuilder<()> {
}
pub fn unknown_subst(db: &dyn HirDatabase, def: impl Into<GenericDefId>) -> Substitution {
let interner = DbInterner::conjure();
let params = generics(db, def.into());
Substitution::from_iter(
Interner,
params.iter_id().map(|id| match id {
GenericParamId::TypeParamId(_) => TyKind::Error.intern(Interner).cast(Interner),
GenericParamId::ConstParamId(id) => {
unknown_const_as_generic(db.const_param_ty(id)).cast(Interner)
unknown_const_as_generic(db.const_param_ty_ns(id))
.to_chalk(interner)
.cast(Interner)
}
GenericParamId::LifetimeParamId(_) => error_lifetime().cast(Interner),
}),
@ -267,6 +273,7 @@ impl TyBuilder<hir_def::AdtId> {
db: &dyn HirDatabase,
mut fallback: impl FnMut() -> Ty,
) -> Self {
let interner = DbInterner::conjure();
// Note that we're building ADT, so we never have parent generic parameters.
let defaults = db.generic_defaults(self.data.into());
@ -287,7 +294,9 @@ impl TyBuilder<hir_def::AdtId> {
// The defaults may be missing if no param has default, so fill that.
let filler = self.param_kinds[self.vec.len()..].iter().map(|x| match x {
ParamKind::Type => fallback().cast(Interner),
ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()),
ParamKind::Const(ty) => {
unknown_const_as_generic(ty.to_nextsolver(interner)).to_chalk(interner)
}
ParamKind::Lifetime => error_lifetime().cast(Interner),
});
self.vec.extend(filler.casted(Interner));

View file

@ -1,59 +1,88 @@
//! Constant evaluation details
#[cfg(test)]
mod tests;
use base_db::Crate;
use chalk_ir::{BoundVar, DebruijnIndex, cast::Cast};
use hir_def::{
EnumVariantId, GeneralConstId, HasModule as _, StaticId,
expr_store::{HygieneId, path::Path},
hir::Expr,
EnumVariantId, GeneralConstId,
expr_store::{Body, HygieneId, path::Path},
hir::{Expr, ExprId},
resolver::{Resolver, ValueNs},
type_ref::LiteralConstRef,
};
use hir_def::{HasModule, StaticId};
use hir_expand::Lookup;
use rustc_type_ir::{UnevaluatedConst, inherent::IntoKind};
use stdx::never;
use triomphe::Arc;
use crate::{
Const, ConstData, ConstScalar, ConstValue, GenericArg, Interner, MemoryMap, Substitution,
TraitEnvironment, Ty, TyBuilder,
MemoryMap, TraitEnvironment,
db::HirDatabase,
display::DisplayTarget,
generics::Generics,
lower::ParamLoweringMode,
next_solver::{DbInterner, mapping::ChalkToNextSolver},
to_placeholder_idx,
infer::InferenceContext,
mir::{MirEvalError, MirLowerError},
next_solver::{
Const, ConstBytes, ConstKind, DbInterner, ErrorGuaranteed, GenericArg, GenericArgs,
ParamConst, SolverDefId, Ty, ValueConst,
},
};
use super::mir::{MirEvalError, MirLowerError, interpret_mir, pad16};
use super::mir::{interpret_mir, lower_to_mir, pad16};
/// Extension trait for [`Const`]
pub trait ConstExt {
/// Is a [`Const`] unknown?
fn is_unknown(&self) -> bool;
}
impl ConstExt for Const {
fn is_unknown(&self) -> bool {
match self.data(Interner).value {
// interned Unknown
chalk_ir::ConstValue::Concrete(chalk_ir::ConcreteConst {
interned: ConstScalar::Unknown,
}) => true,
// interned concrete anything else
chalk_ir::ConstValue::Concrete(..) => false,
_ => {
tracing::error!(
"is_unknown was called on a non-concrete constant value! {:?}",
self
);
true
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()
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ConstEvalError<'db> {
MirLowerError(MirLowerError<'db>),
@ -94,195 +123,112 @@ impl<'db> From<MirEvalError<'db>> for ConstEvalError<'db> {
}
}
pub(crate) fn path_to_const<'g>(
db: &dyn HirDatabase,
resolver: &Resolver<'_>,
path: &Path,
mode: ParamLoweringMode,
args: impl FnOnce() -> &'g Generics,
debruijn: DebruijnIndex,
expected_ty: Ty,
) -> Option<Const> {
match resolver.resolve_path_in_value_ns_fully(db, path, HygieneId::ROOT) {
Some(ValueNs::GenericParam(p)) => {
let ty = db.const_param_ty(p);
let args = args();
let value = match mode {
ParamLoweringMode::Placeholder => {
let idx = args.type_or_const_param_idx(p.into()).unwrap();
ConstValue::Placeholder(to_placeholder_idx(db, p.into(), idx as u32))
}
ParamLoweringMode::Variable => match args.type_or_const_param_idx(p.into()) {
Some(it) => ConstValue::BoundVar(BoundVar::new(debruijn, it)),
None => {
never!(
"Generic list doesn't contain this param: {:?}, {:?}, {:?}",
args,
path,
p
);
return None;
}
},
};
Some(ConstData { ty, value }.intern(Interner))
}
Some(ValueNs::ConstId(c)) => Some(intern_const_scalar(
ConstScalar::UnevaluatedConst(c.into(), Substitution::empty(Interner)),
expected_ty,
)),
// FIXME: With feature(adt_const_params), we also need to consider other things here, e.g. struct constructors.
_ => None,
}
}
pub fn unknown_const(ty: Ty) -> Const {
ConstData {
ty,
value: ConstValue::Concrete(chalk_ir::ConcreteConst { interned: ConstScalar::Unknown }),
}
.intern(Interner)
}
pub fn unknown_const_as_generic(ty: Ty) -> GenericArg {
unknown_const(ty).cast(Interner)
}
/// Interns a constant scalar with the given type
pub fn intern_const_scalar(value: ConstScalar, ty: Ty) -> Const {
ConstData { ty, value: ConstValue::Concrete(chalk_ir::ConcreteConst { interned: value }) }
.intern(Interner)
}
/// Interns a constant scalar with the given type
pub fn intern_const_ref(
db: &dyn HirDatabase,
pub fn intern_const_ref<'a>(
db: &'a dyn HirDatabase,
value: &LiteralConstRef,
ty: Ty,
ty: Ty<'a>,
krate: Crate,
) -> Const {
) -> Const<'a> {
let interner = DbInterner::new_with(db, Some(krate), None);
let layout = || db.layout_of_ty(ty.to_nextsolver(interner), TraitEnvironment::empty(krate));
let bytes = match value {
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);
ConstScalar::Bytes(i.to_le_bytes()[0..size].into(), MemoryMap::default())
let size = layout.map(|it| it.size.bytes_usize()).unwrap_or(16);
rustc_type_ir::ConstKind::Value(ValueConst::new(
ty,
ConstBytes {
memory: i.to_le_bytes()[0..size].into(),
memory_map: MemoryMap::default(),
},
))
}
LiteralConstRef::UInt(i) => {
let size = layout().map(|it| it.size.bytes_usize()).unwrap_or(16);
ConstScalar::Bytes(i.to_le_bytes()[0..size].into(), MemoryMap::default())
let size = layout.map(|it| it.size.bytes_usize()).unwrap_or(16);
rustc_type_ir::ConstKind::Value(ValueConst::new(
ty,
ConstBytes {
memory: i.to_le_bytes()[0..size].into(),
memory_map: MemoryMap::default(),
},
))
}
LiteralConstRef::Bool(b) => ConstScalar::Bytes(Box::new([*b as u8]), MemoryMap::default()),
LiteralConstRef::Char(c) => {
ConstScalar::Bytes((*c as u32).to_le_bytes().into(), MemoryMap::default())
}
LiteralConstRef::Unknown => ConstScalar::Unknown,
LiteralConstRef::Bool(b) => rustc_type_ir::ConstKind::Value(ValueConst::new(
ty,
ConstBytes { memory: Box::new([*b as u8]), memory_map: MemoryMap::default() },
)),
LiteralConstRef::Char(c) => rustc_type_ir::ConstKind::Value(ValueConst::new(
ty,
ConstBytes {
memory: (*c as u32).to_le_bytes().into(),
memory_map: MemoryMap::default(),
},
)),
LiteralConstRef::Unknown => rustc_type_ir::ConstKind::Error(ErrorGuaranteed),
};
intern_const_scalar(bytes, ty)
Const::new(interner, kind)
}
/// Interns a possibly-unknown target usize
pub fn usize_const(db: &dyn HirDatabase, value: Option<u128>, krate: Crate) -> Const {
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),
TyBuilder::usize(),
Ty::new_uint(DbInterner::new_with(db, Some(krate), None), rustc_type_ir::UintTy::Usize),
krate,
)
}
pub fn try_const_usize(db: &dyn HirDatabase, c: &Const) -> Option<u128> {
match &c.data(Interner).value {
chalk_ir::ConstValue::BoundVar(_) => None,
chalk_ir::ConstValue::InferenceVar(_) => None,
chalk_ir::ConstValue::Placeholder(_) => None,
chalk_ir::ConstValue::Concrete(c) => match &c.interned {
ConstScalar::Bytes(it, _) => Some(u128::from_le_bytes(pad16(it, false))),
ConstScalar::UnevaluatedConst(c, subst) => {
let ec = db.const_eval(*c, subst.clone(), None).ok()?;
try_const_usize(db, &ec)
}
_ => None,
},
pub fn try_const_usize<'db>(db: &'db dyn HirDatabase, c: Const<'db>) -> Option<u128> {
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;
let ec = db.const_eval(c, subst, None).ok()?;
try_const_usize(db, ec)
}
ConstKind::Value(val) => Some(u128::from_le_bytes(pad16(&val.value.inner().memory, false))),
ConstKind::Error(_) => None,
ConstKind::Expr(_) => None,
}
}
pub fn try_const_isize(db: &dyn HirDatabase, c: &Const) -> Option<i128> {
match &c.data(Interner).value {
chalk_ir::ConstValue::BoundVar(_) => None,
chalk_ir::ConstValue::InferenceVar(_) => None,
chalk_ir::ConstValue::Placeholder(_) => None,
chalk_ir::ConstValue::Concrete(c) => match &c.interned {
ConstScalar::Bytes(it, _) => Some(i128::from_le_bytes(pad16(it, true))),
ConstScalar::UnevaluatedConst(c, subst) => {
let ec = db.const_eval(*c, subst.clone(), None).ok()?;
try_const_isize(db, &ec)
}
_ => None,
},
pub fn try_const_isize<'db>(db: &'db dyn HirDatabase, c: &Const<'db>) -> Option<i128> {
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;
let ec = db.const_eval(c, subst, None).ok()?;
try_const_isize(db, &ec)
}
ConstKind::Value(val) => Some(i128::from_le_bytes(pad16(&val.value.inner().memory, true))),
ConstKind::Error(_) => None,
ConstKind::Expr(_) => None,
}
}
pub(crate) fn const_eval_cycle_result<'db>(
_: &'db dyn HirDatabase,
_: GeneralConstId,
_: Substitution,
_: Option<Arc<TraitEnvironment<'db>>>,
) -> Result<Const, ConstEvalError<'db>> {
Err(ConstEvalError::MirLowerError(MirLowerError::Loop))
}
pub(crate) fn const_eval_static_cycle_result<'db>(
_: &'db dyn HirDatabase,
_: StaticId,
) -> Result<Const, ConstEvalError<'db>> {
Err(ConstEvalError::MirLowerError(MirLowerError::Loop))
}
pub(crate) fn const_eval_discriminant_cycle_result<'db>(
_: &'db dyn HirDatabase,
_: EnumVariantId,
) -> Result<i128, ConstEvalError<'db>> {
Err(ConstEvalError::MirLowerError(MirLowerError::Loop))
}
pub(crate) fn const_eval_query<'db>(
db: &'db dyn HirDatabase,
def: GeneralConstId,
subst: Substitution,
trait_env: Option<Arc<TraitEnvironment<'db>>>,
) -> Result<Const, ConstEvalError<'db>> {
let body = match def {
GeneralConstId::ConstId(c) => {
db.monomorphized_mir_body(c.into(), subst, db.trait_environment(c.into()))?
}
GeneralConstId::StaticId(s) => {
let krate = s.module(db).krate();
db.monomorphized_mir_body(s.into(), subst, TraitEnvironment::empty(krate))?
}
};
let c = interpret_mir(db, body, false, trait_env)?.0?;
Ok(c)
}
pub(crate) fn const_eval_static_query<'db>(
db: &'db dyn HirDatabase,
def: StaticId,
) -> Result<Const, ConstEvalError<'db>> {
let body = db.monomorphized_mir_body(
def.into(),
Substitution::empty(Interner),
db.trait_environment_for_body(def.into()),
)?;
let c = interpret_mir(db, body, false, None)?.0?;
Ok(c)
}
pub(crate) fn const_eval_discriminant_variant<'db>(
db: &'db dyn HirDatabase,
variant_id: EnumVariantId,
) -> Result<i128, ConstEvalError<'db>> {
let interner = DbInterner::new_with(db, None, None);
let def = variant_id.into();
let body = db.body(def);
let loc = variant_id.lookup(db);
@ -304,17 +250,101 @@ pub(crate) fn const_eval_discriminant_variant<'db>(
let mir_body = db.monomorphized_mir_body(
def,
Substitution::empty(Interner),
GenericArgs::new_from_iter(interner, []),
db.trait_environment_for_body(def),
)?;
let c = interpret_mir(db, mir_body, false, None)?.0?;
let c = if is_signed {
try_const_isize(db, &c).unwrap()
} else {
try_const_usize(db, &c).unwrap() as i128
try_const_usize(db, c).unwrap() as i128
};
Ok(c)
}
#[cfg(test)]
mod tests;
// 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 infer = ctx.fixme_resolve_all_clone();
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]);
}
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]) {
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;
}
unknown_const(infer[expr])
}
pub(crate) fn const_eval_cycle_result<'db>(
_: &'db dyn HirDatabase,
_: GeneralConstId,
_: GenericArgs<'db>,
_: Option<Arc<TraitEnvironment<'db>>>,
) -> Result<Const<'db>, ConstEvalError<'db>> {
Err(ConstEvalError::MirLowerError(MirLowerError::Loop))
}
pub(crate) fn const_eval_static_cycle_result<'db>(
_: &'db dyn HirDatabase,
_: StaticId,
) -> Result<Const<'db>, ConstEvalError<'db>> {
Err(ConstEvalError::MirLowerError(MirLowerError::Loop))
}
pub(crate) fn const_eval_discriminant_cycle_result<'db>(
_: &'db dyn HirDatabase,
_: EnumVariantId,
) -> Result<i128, ConstEvalError<'db>> {
Err(ConstEvalError::MirLowerError(MirLowerError::Loop))
}
pub(crate) fn const_eval_query<'db>(
db: &'db dyn HirDatabase,
def: GeneralConstId,
subst: GenericArgs<'db>,
trait_env: Option<Arc<TraitEnvironment<'db>>>,
) -> Result<Const<'db>, ConstEvalError<'db>> {
let body = match def {
GeneralConstId::ConstId(c) => {
db.monomorphized_mir_body(c.into(), subst, db.trait_environment(c.into()))?
}
GeneralConstId::StaticId(s) => {
let krate = s.module(db).krate();
db.monomorphized_mir_body(s.into(), subst, TraitEnvironment::empty(krate))?
}
};
let c = interpret_mir(db, body, false, trait_env)?.0?;
Ok(c)
}
pub(crate) fn const_eval_static_query<'db>(
db: &'db dyn HirDatabase,
def: StaticId,
) -> Result<Const<'db>, ConstEvalError<'db>> {
let interner = DbInterner::new_with(db, None, None);
let body = db.monomorphized_mir_body(
def.into(),
GenericArgs::new_from_iter(interner, []),
db.trait_environment_for_body(def.into()),
)?;
let c = interpret_mir(db, body, false, None)?.0?;
Ok(c)
}

View file

@ -1,17 +1,23 @@
use base_db::RootQueryDb;
use chalk_ir::Substitution;
use hir_def::db::DefDatabase;
use hir_expand::EditionedFileId;
use rustc_apfloat::{
Float,
ieee::{Half as f16, Quad as f128},
};
use rustc_type_ir::inherent::IntoKind;
use test_fixture::WithFixture;
use test_utils::skip_slow_tests;
use crate::{
Const, ConstScalar, Interner, MemoryMap, consteval::try_const_usize, db::HirDatabase,
display::DisplayTarget, mir::pad16, setup_tracing, test_db::TestDB,
MemoryMap,
consteval::try_const_usize,
db::HirDatabase,
display::DisplayTarget,
mir::pad16,
next_solver::{Const, ConstBytes, ConstKind, DbInterner, GenericArgs},
setup_tracing,
test_db::TestDB,
};
use super::{
@ -88,14 +94,12 @@ fn check_answer(
panic!("Error in evaluating goal: {err}");
}
};
match &r.data(Interner).value {
chalk_ir::ConstValue::Concrete(c) => match &c.interned {
ConstScalar::Bytes(b, mm) => {
check(b, mm);
}
x => panic!("Expected number but found {x:?}"),
},
_ => panic!("result of const eval wasn't a concrete const"),
match r.kind() {
ConstKind::Value(value) => {
let ConstBytes { memory, memory_map } = value.value.inner();
check(memory, memory_map);
}
_ => panic!("Expected number but found {r:?}"),
}
});
}
@ -117,8 +121,9 @@ fn pretty_print_err(e: ConstEvalError<'_>, db: &TestDB) -> String {
err
}
fn eval_goal(db: &TestDB, file_id: EditionedFileId) -> Result<Const, ConstEvalError<'_>> {
fn eval_goal(db: &TestDB, file_id: EditionedFileId) -> Result<Const<'_>, ConstEvalError<'_>> {
let _tracing = setup_tracing();
let interner = DbInterner::new_with(db, None, None);
let module_id = db.module_for_file(file_id.file_id(db));
let def_map = module_id.def_map(db);
let scope = &def_map[module_id.local_id].scope;
@ -137,7 +142,7 @@ fn eval_goal(db: &TestDB, file_id: EditionedFileId) -> Result<Const, ConstEvalEr
_ => None,
})
.expect("No const named GOAL found in the test");
db.const_eval(const_id.into(), Substitution::empty(Interner), None)
db.const_eval(const_id.into(), GenericArgs::new_from_iter(interner, []), None)
}
#[test]
@ -2508,7 +2513,7 @@ fn enums() {
);
crate::attach_db(&db, || {
let r = eval_goal(&db, file_id).unwrap();
assert_eq!(try_const_usize(&db, &r), Some(1));
assert_eq!(try_const_usize(&db, r), Some(1));
})
}

View file

@ -0,0 +1,185 @@
//! Constant evaluation details
use base_db::Crate;
use chalk_ir::{BoundVar, DebruijnIndex, cast::Cast};
use hir_def::{
expr_store::{HygieneId, path::Path},
resolver::{Resolver, ValueNs},
type_ref::LiteralConstRef,
};
use stdx::never;
use crate::{
Const, ConstData, ConstScalar, ConstValue, GenericArg, Interner, MemoryMap, Substitution,
TraitEnvironment, Ty, TyBuilder,
db::HirDatabase,
generics::Generics,
lower::ParamLoweringMode,
next_solver::{
DbInterner,
mapping::{ChalkToNextSolver, NextSolverToChalk},
},
to_placeholder_idx,
};
use super::mir::pad16;
/// Extension trait for [`Const`]
pub trait ConstExt {
/// Is a [`Const`] unknown?
fn is_unknown(&self) -> bool;
}
impl ConstExt for Const {
fn is_unknown(&self) -> bool {
match self.data(Interner).value {
// interned Unknown
chalk_ir::ConstValue::Concrete(chalk_ir::ConcreteConst {
interned: ConstScalar::Unknown,
}) => true,
// interned concrete anything else
chalk_ir::ConstValue::Concrete(..) => false,
_ => {
tracing::error!(
"is_unknown was called on a non-concrete constant value! {:?}",
self
);
true
}
}
}
}
pub fn path_to_const<'g>(
db: &dyn HirDatabase,
resolver: &Resolver<'_>,
path: &Path,
mode: ParamLoweringMode,
args: impl FnOnce() -> &'g Generics,
debruijn: DebruijnIndex,
expected_ty: Ty,
) -> Option<Const> {
match resolver.resolve_path_in_value_ns_fully(db, path, HygieneId::ROOT) {
Some(ValueNs::GenericParam(p)) => {
let ty = db.const_param_ty(p);
let args = args();
let value = match mode {
ParamLoweringMode::Placeholder => {
let idx = args.type_or_const_param_idx(p.into()).unwrap();
ConstValue::Placeholder(to_placeholder_idx(db, p.into(), idx as u32))
}
ParamLoweringMode::Variable => match args.type_or_const_param_idx(p.into()) {
Some(it) => ConstValue::BoundVar(BoundVar::new(debruijn, it)),
None => {
never!(
"Generic list doesn't contain this param: {:?}, {:?}, {:?}",
args,
path,
p
);
return None;
}
},
};
Some(ConstData { ty, value }.intern(Interner))
}
Some(ValueNs::ConstId(c)) => Some(intern_const_scalar(
ConstScalar::UnevaluatedConst(c.into(), Substitution::empty(Interner)),
expected_ty,
)),
// FIXME: With feature(adt_const_params), we also need to consider other things here, e.g. struct constructors.
_ => None,
}
}
pub fn unknown_const(ty: Ty) -> Const {
ConstData {
ty,
value: ConstValue::Concrete(chalk_ir::ConcreteConst { interned: ConstScalar::Unknown }),
}
.intern(Interner)
}
pub fn unknown_const_as_generic(ty: Ty) -> GenericArg {
unknown_const(ty).cast(Interner)
}
/// Interns a constant scalar with the given type
pub fn intern_const_scalar(value: ConstScalar, ty: Ty) -> Const {
ConstData { ty, value: ConstValue::Concrete(chalk_ir::ConcreteConst { interned: value }) }
.intern(Interner)
}
/// Interns a constant scalar with the given type
pub fn intern_const_ref(
db: &dyn HirDatabase,
value: &LiteralConstRef,
ty: Ty,
krate: Crate,
) -> Const {
let interner = DbInterner::new_with(db, Some(krate), None);
let layout = || db.layout_of_ty(ty.to_nextsolver(interner), TraitEnvironment::empty(krate));
let bytes = 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);
ConstScalar::Bytes(i.to_le_bytes()[0..size].into(), MemoryMap::default())
}
LiteralConstRef::UInt(i) => {
let size = layout().map(|it| it.size.bytes_usize()).unwrap_or(16);
ConstScalar::Bytes(i.to_le_bytes()[0..size].into(), MemoryMap::default())
}
LiteralConstRef::Bool(b) => ConstScalar::Bytes(Box::new([*b as u8]), MemoryMap::default()),
LiteralConstRef::Char(c) => {
ConstScalar::Bytes((*c as u32).to_le_bytes().into(), MemoryMap::default())
}
LiteralConstRef::Unknown => ConstScalar::Unknown,
};
intern_const_scalar(bytes, ty)
}
/// Interns a possibly-unknown target usize
pub fn usize_const(db: &dyn HirDatabase, value: Option<u128>, krate: Crate) -> Const {
intern_const_ref(
db,
&value.map_or(LiteralConstRef::Unknown, LiteralConstRef::UInt),
TyBuilder::usize(),
krate,
)
}
pub fn try_const_usize(db: &dyn HirDatabase, c: &Const) -> Option<u128> {
let interner = DbInterner::new_with(db, None, None);
match &c.data(Interner).value {
chalk_ir::ConstValue::BoundVar(_) => None,
chalk_ir::ConstValue::InferenceVar(_) => None,
chalk_ir::ConstValue::Placeholder(_) => None,
chalk_ir::ConstValue::Concrete(c) => match &c.interned {
ConstScalar::Bytes(it, _) => Some(u128::from_le_bytes(pad16(it, false))),
ConstScalar::UnevaluatedConst(c, subst) => {
let ec = db.const_eval(*c, subst.to_nextsolver(interner), None).ok()?;
try_const_usize(db, &ec.to_chalk(interner))
}
_ => None,
},
}
}
pub fn try_const_isize(db: &dyn HirDatabase, c: &Const) -> Option<i128> {
let interner = DbInterner::new_with(db, None, None);
match &c.data(Interner).value {
chalk_ir::ConstValue::BoundVar(_) => None,
chalk_ir::ConstValue::InferenceVar(_) => None,
chalk_ir::ConstValue::Placeholder(_) => None,
chalk_ir::ConstValue::Concrete(c) => match &c.interned {
ConstScalar::Bytes(it, _) => Some(i128::from_le_bytes(pad16(it, true))),
ConstScalar::UnevaluatedConst(c, subst) => {
let ec = db.const_eval(*c, subst.to_nextsolver(interner), None).ok()?;
try_const_isize(db, &ec.to_chalk(interner))
}
_ => None,
},
}
}

View file

@ -1,250 +0,0 @@
//! 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>(
db: &'db dyn HirDatabase,
variant_id: EnumVariantId,
) -> Result<i128, ConstEvalError<'db>> {
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.fixme_resolve_all_clone();
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]);
}
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]) {
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])
}

View file

@ -16,8 +16,8 @@ use smallvec::SmallVec;
use triomphe::Arc;
use crate::{
Binders, Const, ImplTraitId, ImplTraits, InferenceResult, Substitution, TraitEnvironment, Ty,
TyDefId, ValueTyDefId, chalk_db,
Binders, ImplTraitId, ImplTraits, InferenceResult, TraitEnvironment, Ty, TyDefId, ValueTyDefId,
chalk_db,
consteval::ConstEvalError,
dyn_compatibility::DynCompatibilityViolation,
layout::{Layout, LayoutError},
@ -37,50 +37,56 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug {
#[salsa::invoke(crate::mir::mir_body_query)]
#[salsa::cycle(cycle_result = crate::mir::mir_body_cycle_result)]
fn mir_body<'db>(&'db self, def: DefWithBodyId) -> Result<Arc<MirBody>, MirLowerError<'db>>;
fn mir_body<'db>(
&'db self,
def: DefWithBodyId,
) -> Result<Arc<MirBody<'db>>, MirLowerError<'db>>;
#[salsa::invoke(crate::mir::mir_body_for_closure_query)]
fn mir_body_for_closure<'db>(
&'db self,
def: InternedClosureId,
) -> Result<Arc<MirBody>, MirLowerError<'db>>;
) -> Result<Arc<MirBody<'db>>, MirLowerError<'db>>;
#[salsa::invoke(crate::mir::monomorphized_mir_body_query)]
#[salsa::cycle(cycle_result = crate::mir::monomorphized_mir_body_cycle_result)]
fn monomorphized_mir_body<'db>(
&'db self,
def: DefWithBodyId,
subst: Substitution,
subst: crate::next_solver::GenericArgs<'db>,
env: Arc<TraitEnvironment<'db>>,
) -> Result<Arc<MirBody>, MirLowerError<'db>>;
) -> Result<Arc<MirBody<'db>>, MirLowerError<'db>>;
#[salsa::invoke(crate::mir::monomorphized_mir_body_for_closure_query)]
fn monomorphized_mir_body_for_closure<'db>(
&'db self,
def: InternedClosureId,
subst: Substitution,
subst: crate::next_solver::GenericArgs<'db>,
env: Arc<TraitEnvironment<'db>>,
) -> Result<Arc<MirBody>, MirLowerError<'db>>;
) -> Result<Arc<MirBody<'db>>, MirLowerError<'db>>;
#[salsa::invoke(crate::mir::borrowck_query)]
#[salsa::lru(2024)]
fn borrowck<'db>(
&'db self,
def: DefWithBodyId,
) -> Result<Arc<[BorrowckResult]>, MirLowerError<'db>>;
) -> Result<Arc<[BorrowckResult<'db>]>, MirLowerError<'db>>;
#[salsa::invoke(crate::consteval::const_eval_query)]
#[salsa::cycle(cycle_result = crate::consteval::const_eval_cycle_result)]
fn const_eval<'db>(
&'db self,
def: GeneralConstId,
subst: Substitution,
subst: crate::next_solver::GenericArgs<'db>,
trait_env: Option<Arc<TraitEnvironment<'db>>>,
) -> Result<Const, ConstEvalError<'db>>;
) -> Result<crate::next_solver::Const<'db>, ConstEvalError<'db>>;
#[salsa::invoke(crate::consteval::const_eval_static_query)]
#[salsa::cycle(cycle_result = crate::consteval::const_eval_static_cycle_result)]
fn const_eval_static<'db>(&'db self, def: StaticId) -> Result<Const, ConstEvalError<'db>>;
fn const_eval_static<'db>(
&'db self,
def: StaticId,
) -> Result<crate::next_solver::Const<'db>, ConstEvalError<'db>>;
#[salsa::invoke(crate::consteval::const_eval_discriminant_variant)]
#[salsa::cycle(cycle_result = crate::consteval::const_eval_discriminant_cycle_result)]

View file

@ -52,7 +52,7 @@ use crate::{
AliasEq, AliasTy, Binders, CallableDefId, CallableSig, ConcreteConst, Const, ConstScalar,
ConstValue, DomainGoal, FnAbi, GenericArg, ImplTraitId, Interner, Lifetime, LifetimeData,
LifetimeOutlives, MemoryMap, OpaqueTy, ProjectionTy, ProjectionTyExt, QuantifiedWhereClause,
TraitEnvironment, TraitRef, TraitRefExt, Ty, TyExt, WhereClause, consteval_nextsolver,
TraitEnvironment, TraitRef, TraitRefExt, Ty, TyExt, WhereClause, consteval,
db::{HirDatabase, InternedClosure},
from_assoc_type_id, from_placeholder_idx,
generics::generics,
@ -750,8 +750,8 @@ impl<'db> HirDisplay for crate::next_solver::Const<'db> {
}
rustc_type_ir::ConstKind::Value(const_bytes) => render_const_scalar_ns(
f,
&const_bytes.value.inner().0,
&const_bytes.value.inner().1,
&const_bytes.value.inner().memory,
&const_bytes.value.inner().memory_map,
const_bytes.ty,
),
rustc_type_ir::ConstKind::Unevaluated(unev) => {
@ -1025,7 +1025,7 @@ fn render_const_scalar_inner<'db>(
ty.hir_fmt(f)
}
TyKind::Array(ty, len) => {
let Some(len) = consteval_nextsolver::try_const_usize(f.db, len) else {
let Some(len) = consteval::try_const_usize(f.db, len) else {
return f.write_str("<unknown-array-len>");
};
let Ok(layout) = f.db.layout_of_ty(ty, trait_env) else {

View file

@ -7,7 +7,7 @@ use stdx::never;
use triomphe::Arc;
use crate::{
TraitEnvironment, consteval_nextsolver,
TraitEnvironment, consteval,
db::HirDatabase,
method_resolution::TyFingerprint,
next_solver::{
@ -128,7 +128,7 @@ fn has_drop_glue_impl<'db>(
.max()
.unwrap_or(DropGlue::None),
TyKind::Array(ty, len) => {
if consteval_nextsolver::try_const_usize(db, len) == Some(0) {
if consteval::try_const_usize(db, len) == Some(0) {
// Arrays of size 0 don't have drop glue.
return DropGlue::None;
}

View file

@ -28,10 +28,7 @@ use crate::{
db::{HirDatabase, InternedClosure, InternedClosureId},
infer::InferenceContext,
mir::{BorrowKind, MirSpan, MutBorrowKind, ProjectionElem},
next_solver::{
DbInterner, EarlyBinder, GenericArgs, Ty, TyKind,
mapping::{ChalkToNextSolver, NextSolverToChalk},
},
next_solver::{DbInterner, EarlyBinder, GenericArgs, Ty, TyKind},
traits::FnTrait,
};
@ -47,16 +44,14 @@ impl<'db> HirPlace<'db> {
fn ty(&self, ctx: &mut InferenceContext<'_, 'db>) -> Ty<'db> {
let mut ty = ctx.table.resolve_completely(ctx.result[self.local]);
for p in &self.projections {
ty = p
.projected_ty(
ty.to_chalk(ctx.interner()),
ctx.db,
|_, _, _| {
unreachable!("Closure field only happens in MIR");
},
ctx.owner.module(ctx.db).krate(),
)
.to_nextsolver(ctx.interner());
ty = p.projected_ty(
&ctx.table.infer_ctxt,
ty,
|_, _, _| {
unreachable!("Closure field only happens in MIR");
},
ctx.owner.module(ctx.db).krate(),
);
}
ty
}
@ -865,16 +860,14 @@ impl<'db> InferenceContext<'_, 'db> {
continue;
}
for (i, p) in capture.place.projections.iter().enumerate() {
ty = p
.projected_ty(
ty.to_chalk(self.interner()),
self.db,
|_, _, _| {
unreachable!("Closure field only happens in MIR");
},
self.owner.module(self.db).krate(),
)
.to_nextsolver(self.interner());
ty = p.projected_ty(
&self.table.infer_ctxt,
ty,
|_, _, _| {
unreachable!("Closure field only happens in MIR");
},
self.owner.module(self.db).krate(),
);
if ty.is_raw_ptr() || ty.is_union() {
capture.kind = CaptureKind::ByRef(BorrowKind::Shared);
self.truncate_capture_spans(capture, i + 1);

View file

@ -28,7 +28,7 @@ use crate::{
Adjust, Adjustment, AutoBorrow, CallableDefId, DeclContext, DeclOrigin,
IncorrectGenericsLenKind, Rawness, TraitEnvironment,
autoderef::overloaded_deref_ty,
consteval_nextsolver,
consteval,
generics::generics,
infer::{
AllowTwoPhase, BreakableKind,
@ -896,7 +896,7 @@ impl<'db> InferenceContext<'_, 'db> {
Literal::ByteString(bs) => {
let byte_type = self.types.u8;
let len = consteval_nextsolver::usize_const(
let len = consteval::usize_const(
self.db,
Some(bs.len() as u128),
self.resolver.krate(),
@ -1221,7 +1221,7 @@ impl<'db> InferenceContext<'_, 'db> {
let expected = Expectation::has_type(elem_ty);
let (elem_ty, len) = match array {
Array::ElementList { elements, .. } if elements.is_empty() => {
(elem_ty, consteval_nextsolver::usize_const(self.db, Some(0), krate))
(elem_ty, consteval::usize_const(self.db, Some(0), krate))
}
Array::ElementList { elements, .. } => {
let mut coerce = CoerceMany::with_coercion_sites(elem_ty, elements);
@ -1231,7 +1231,7 @@ impl<'db> InferenceContext<'_, 'db> {
}
(
coerce.complete(self),
consteval_nextsolver::usize_const(self.db, Some(elements.len() as u128), krate),
consteval::usize_const(self.db, Some(elements.len() as u128), krate),
)
}
&Array::Repeat { initializer, repeat } => {
@ -1248,7 +1248,7 @@ impl<'db> InferenceContext<'_, 'db> {
_ => _ = self.infer_expr(repeat, &Expectation::HasType(usize), ExprIsRead::Yes),
}
(elem_ty, consteval_nextsolver::eval_to_const(repeat, self))
(elem_ty, consteval::eval_to_const(repeat, self))
}
};
// Try to evaluate unevaluated constant, and insert variable if is not possible.

View file

@ -14,7 +14,7 @@ use stdx::TupleExt;
use crate::{
DeclContext, DeclOrigin, InferenceDiagnostic,
consteval_nextsolver::{self, try_const_usize, usize_const},
consteval::{self, try_const_usize, usize_const},
infer::{
AllowTwoPhase, BindingMode, Expectation, InferenceContext, TypeMismatch,
coerce::CoerceNever, expr::ExprIsRead,
@ -591,11 +591,7 @@ impl<'db> InferenceContext<'_, 'db> {
}
let len = before.len() + suffix.len();
let size = consteval_nextsolver::usize_const(
self.db,
Some(len as u128),
self.owner.krate(self.db),
);
let size = consteval::usize_const(self.db, Some(len as u128), self.owner.krate(self.db));
let elem_ty = self.table.next_ty_var();
let array_ty = Ty::new_array_with_const_len(self.interner(), elem_ty, size);

View file

@ -10,7 +10,7 @@ use rustc_type_ir::inherent::{SliceLike, Ty as _};
use stdx::never;
use crate::{
InferenceDiagnostic, ValueTyDefId, consteval_nextsolver,
InferenceDiagnostic, ValueTyDefId, consteval,
generics::generics,
infer::diagnostics::InferenceTyLoweringContext as TyLoweringContext,
lower_nextsolver::LifetimeElisionKind,
@ -128,7 +128,7 @@ impl<'db> InferenceContext<'_, 'db> {
match id {
GenericParamId::TypeParamId(_) => self.types.error.into(),
GenericParamId::ConstParamId(id) => {
consteval_nextsolver::unknown_const_as_generic(self.db.const_param_ty_ns(id))
consteval::unknown_const_as_generic(self.db.const_param_ty_ns(id))
}
GenericParamId::LifetimeParamId(_) => self.types.re_error.into(),
}

View file

@ -11,7 +11,9 @@ use triomphe::Arc;
use crate::{
AliasTy, Binders, Interner, Substitution, TraitEnvironment, Ty, TyKind,
consteval::try_const_usize, db::HirDatabase,
consteval::try_const_usize,
db::HirDatabase,
next_solver::{DbInterner, mapping::ChalkToNextSolver},
};
// FIXME: Turn this into a query, it can be quite slow
@ -79,14 +81,17 @@ impl TypeVisitor<Interner> for UninhabitedFrom<'_> {
}
self.recursive_ty.insert(ty.clone());
self.max_depth -= 1;
let interner = DbInterner::new_with(self.db, None, None);
let r = match ty.kind(Interner) {
TyKind::Adt(adt, subst) => self.visit_adt(adt.0, subst),
TyKind::Never => BREAK_VISIBLY_UNINHABITED,
TyKind::Tuple(..) => ty.super_visit_with(self, outer_binder),
TyKind::Array(item_ty, len) => match try_const_usize(self.db, len) {
Some(0) | None => CONTINUE_OPAQUELY_INHABITED,
Some(1..) => item_ty.super_visit_with(self, outer_binder),
},
TyKind::Array(item_ty, len) => {
match try_const_usize(self.db, len.to_nextsolver(interner)) {
Some(0) | None => CONTINUE_OPAQUELY_INHABITED,
Some(1..) => item_ty.super_visit_with(self, outer_binder),
}
}
TyKind::Alias(AliasTy::Projection(projection)) => {
// FIXME: I think this currently isn't used for monomorphized bodies, so there is no need to handle
// `TyKind::AssociatedType`, but perhaps in the future it will.

View file

@ -21,7 +21,7 @@ use triomphe::Arc;
use crate::{
TraitEnvironment,
consteval_nextsolver::try_const_usize,
consteval::try_const_usize,
db::HirDatabase,
next_solver::{
DbInterner, GenericArgs, ParamEnv, Ty, TyKind, TypingMode,

View file

@ -21,6 +21,8 @@ extern crate ra_ap_rustc_type_ir as rustc_type_ir;
extern crate ra_ap_rustc_next_trait_solver as rustc_next_trait_solver;
extern crate self as hir_ty;
mod builder;
mod chalk_db;
mod chalk_ext;
@ -37,7 +39,7 @@ mod utils;
pub mod autoderef;
pub mod consteval;
pub mod consteval_nextsolver;
pub mod consteval_chalk;
pub mod db;
pub mod diagnostics;
pub mod display;
@ -782,7 +784,12 @@ where
_var: InferenceVar,
_outer_binder: DebruijnIndex,
) -> Fallible<Const> {
if cfg!(debug_assertions) { Err(NoSolution) } else { Ok(unknown_const(ty)) }
if cfg!(debug_assertions) {
Err(NoSolution)
} else {
let interner = DbInterner::conjure();
Ok(unknown_const(ty.to_nextsolver(interner)).to_chalk(interner))
}
}
fn try_fold_free_var_const(
@ -791,7 +798,12 @@ where
_bound_var: BoundVar,
_outer_binder: DebruijnIndex,
) -> Fallible<Const> {
if cfg!(debug_assertions) { Err(NoSolution) } else { Ok(unknown_const(ty)) }
if cfg!(debug_assertions) {
Err(NoSolution)
} else {
let interner = DbInterner::conjure();
Ok(unknown_const(ty.to_nextsolver(interner)).to_chalk(interner))
}
}
fn try_fold_inference_lifetime(

View file

@ -49,7 +49,7 @@ use crate::{
ImplTrait, ImplTraitId, ImplTraits, Interner, Lifetime, LifetimeData, LifetimeOutlives,
QuantifiedWhereClause, QuantifiedWhereClauses, Substitution, TraitRef, TraitRefExt, Ty,
TyBuilder, TyKind, WhereClause, all_super_traits,
consteval::{intern_const_ref, path_to_const, unknown_const, unknown_const_as_generic},
consteval_chalk::{intern_const_ref, path_to_const, unknown_const, unknown_const_as_generic},
db::HirDatabase,
error_lifetime,
generics::{Generics, generics, trait_self_param_idx},

View file

@ -23,7 +23,7 @@ use crate::{
Interner, ParamLoweringMode, PathGenericsSource, PathLoweringDiagnostic, ProjectionTy,
QuantifiedWhereClause, Substitution, TraitRef, Ty, TyBuilder, TyDefId, TyKind,
TyLoweringContext, WhereClause,
consteval::{unknown_const, unknown_const_as_generic},
consteval_chalk::{unknown_const, unknown_const_as_generic},
db::HirDatabase,
error_lifetime,
generics::{Generics, generics},

View file

@ -62,7 +62,7 @@ use crate::next_solver::ParamConst;
use crate::{
FnAbi, ImplTraitId, Interner, ParamKind, TraitEnvironment, TyDefId, TyLoweringDiagnostic,
TyLoweringDiagnosticKind,
consteval_nextsolver::{intern_const_ref, path_to_const, unknown_const_as_generic},
consteval::{intern_const_ref, path_to_const, unknown_const_as_generic},
db::HirDatabase,
generics::{Generics, generics, trait_self_param_idx},
lower::{Diagnostics, PathDiagnosticCallbackData, create_diagnostics},

View file

@ -30,7 +30,7 @@ use stdx::never;
use crate::{
GenericArgsProhibitedReason, IncorrectGenericsLenKind, PathGenericsSource,
PathLoweringDiagnostic, TyDefId, ValueTyDefId,
consteval_nextsolver::{unknown_const, unknown_const_as_generic},
consteval::{unknown_const, unknown_const_as_generic},
db::HirDatabase,
generics::{Generics, generics},
lower::PathDiagnosticCallbackData,

View file

@ -4,13 +4,12 @@
//! and the corresponding code mostly in rustc_hir_analysis/check/method/probe.rs.
use std::ops::ControlFlow;
use arrayvec::ArrayVec;
use base_db::Crate;
use chalk_ir::{UniverseIndex, WithKind, cast::Cast};
use hir_def::{
AdtId, AssocItemId, BlockId, ConstId, FunctionId, HasModule, ImplId, ItemContainerId, Lookup,
ModuleId, TraitId, TypeAliasId,
nameres::{DefMap, assoc::ImplItems, block_def_map, crate_def_map},
nameres::{DefMap, block_def_map, crate_def_map},
signatures::{ConstFlags, EnumFlags, FnFlags, StructFlags, TraitFlags, TypeAliasFlags},
};
use hir_expand::name::Name;
@ -18,7 +17,7 @@ use intern::sym;
use rustc_ast_ir::Mutability;
use rustc_hash::{FxHashMap, FxHashSet};
use rustc_type_ir::{
FloatTy, IntTy, UintTy,
FloatTy, IntTy, TypeVisitableExt, UintTy,
inherent::{
AdtDef, BoundExistentialPredicates, GenericArgs as _, IntoKind, SliceLike, Ty as _,
},
@ -27,6 +26,8 @@ use smallvec::{SmallVec, smallvec};
use stdx::never;
use triomphe::Arc;
use crate::next_solver::infer::InferCtxt;
use crate::next_solver::infer::select::ImplSource;
use crate::{
CanonicalVarKinds, DebruijnIndex, GenericArgData, InEnvironment, Interner, TraitEnvironment,
TyBuilder, VariableKind,
@ -36,10 +37,10 @@ use crate::{
lang_items::is_box,
next_solver::{
Canonical, DbInterner, ErrorGuaranteed, GenericArgs, Goal, Predicate, Region, SolverDefId,
TraitRef, Ty, TyKind,
TraitRef, Ty, TyKind, TypingMode,
infer::{
DefineOpaqueTypes,
traits::{ObligationCause, PredicateObligation},
DbInternerInferExt, DefineOpaqueTypes,
traits::{Obligation, ObligationCause, PredicateObligation},
},
mapping::NextSolverToChalk,
obligation_ctxt::ObligationCtxt,
@ -689,11 +690,12 @@ pub(crate) fn iterate_method_candidates<'db, T>(
}
pub fn lookup_impl_const<'db>(
interner: DbInterner<'db>,
infcx: &InferCtxt<'db>,
env: Arc<TraitEnvironment<'db>>,
const_id: ConstId,
subs: GenericArgs<'db>,
) -> (ConstId, GenericArgs<'db>) {
let interner = infcx.interner;
let db = interner.db;
let trait_id = match const_id.lookup(db).container {
@ -708,7 +710,7 @@ pub fn lookup_impl_const<'db>(
None => return (const_id, subs),
};
lookup_impl_assoc_item_for_trait_ref(trait_ref, db, env, name)
lookup_impl_assoc_item_for_trait_ref(infcx, trait_ref, env, name)
.and_then(
|assoc| if let (AssocItemId::ConstId(id), s) = assoc { Some((id, s)) } else { None },
)
@ -759,6 +761,7 @@ pub(crate) fn lookup_impl_method_query<'db>(
fn_subst: GenericArgs<'db>,
) -> (FunctionId, GenericArgs<'db>) {
let interner = DbInterner::new_with(db, Some(env.krate), env.block);
let infcx = interner.infer_ctxt().build(TypingMode::PostAnalysis);
let ItemContainerId::TraitId(trait_id) = func.lookup(db).container else {
return (func, fn_subst);
@ -772,7 +775,7 @@ pub(crate) fn lookup_impl_method_query<'db>(
let name = &db.function_signature(func).name;
let Some((impl_fn, impl_subst)) =
lookup_impl_assoc_item_for_trait_ref(trait_ref, db, env, name).and_then(|assoc| {
lookup_impl_assoc_item_for_trait_ref(&infcx, trait_ref, env, name).and_then(|assoc| {
if let (AssocItemId::FunctionId(id), subst) = assoc { Some((id, subst)) } else { None }
})
else {
@ -789,78 +792,53 @@ pub(crate) fn lookup_impl_method_query<'db>(
}
fn lookup_impl_assoc_item_for_trait_ref<'db>(
infcx: &InferCtxt<'db>,
trait_ref: TraitRef<'db>,
db: &'db dyn HirDatabase,
env: Arc<TraitEnvironment<'db>>,
name: &Name,
) -> Option<(AssocItemId, GenericArgs<'db>)> {
let hir_trait_id = trait_ref.def_id.0;
let self_ty = trait_ref.self_ty();
let self_ty_fp = TyFingerprint::for_trait_impl(self_ty)?;
let impls = db.trait_impls_in_deps(env.krate);
let trait_module = hir_trait_id.module(db);
let type_module = match self_ty_fp {
TyFingerprint::Adt(adt_id) => Some(adt_id.module(db)),
TyFingerprint::ForeignType(type_id) => Some(type_id.module(db)),
TyFingerprint::Dyn(trait_id) => Some(trait_id.module(db)),
_ => None,
};
let def_blocks: ArrayVec<_, 2> =
[trait_module.containing_block(), type_module.and_then(|it| it.containing_block())]
.into_iter()
.flatten()
.filter_map(|block_id| db.trait_impls_in_block(block_id))
.collect();
let impls = impls
.iter()
.chain(&def_blocks)
.flat_map(|impls| impls.for_trait_and_self_ty(hir_trait_id, self_ty_fp));
let table = InferenceTable::new(db, env);
let (impl_data, impl_subst) = find_matching_impl(impls, table, trait_ref)?;
let item = impl_data.items.iter().find_map(|(n, it)| match *it {
AssocItemId::FunctionId(f) => (n == name).then_some(AssocItemId::FunctionId(f)),
AssocItemId::ConstId(c) => (n == name).then_some(AssocItemId::ConstId(c)),
AssocItemId::TypeAliasId(_) => None,
})?;
let (impl_id, impl_subst) = find_matching_impl(infcx, &env, trait_ref)?;
let item =
impl_id.impl_items(infcx.interner.db).items.iter().find_map(|(n, it)| match *it {
AssocItemId::FunctionId(f) => (n == name).then_some(AssocItemId::FunctionId(f)),
AssocItemId::ConstId(c) => (n == name).then_some(AssocItemId::ConstId(c)),
AssocItemId::TypeAliasId(_) => None,
})?;
Some((item, impl_subst))
}
fn find_matching_impl<'db>(
mut impls: impl Iterator<Item = ImplId>,
mut table: InferenceTable<'db>,
actual_trait_ref: TraitRef<'db>,
) -> Option<(&'db ImplItems, GenericArgs<'db>)> {
let db = table.db;
impls.find_map(|impl_| {
table.run_in_snapshot(|table| {
let impl_substs = table.fresh_args_for_item(impl_.into());
let trait_ref = db
.impl_trait(impl_)
.expect("non-trait method in find_matching_impl")
.instantiate(table.interner(), impl_substs);
pub(crate) fn find_matching_impl<'db>(
infcx: &InferCtxt<'db>,
env: &TraitEnvironment<'db>,
trait_ref: TraitRef<'db>,
) -> Option<(ImplId, GenericArgs<'db>)> {
let trait_ref =
infcx.at(&ObligationCause::dummy(), env.env).deeply_normalize(trait_ref).ok()?;
if !table.unify(trait_ref, actual_trait_ref) {
return None;
}
let obligation = Obligation::new(infcx.interner, ObligationCause::dummy(), env.env, trait_ref);
if let Some(predicates) =
db.generic_predicates_ns(impl_.into()).instantiate(table.interner(), impl_substs)
{
for predicate in predicates {
if table.try_obligation(predicate.0).no_solution() {
return None;
}
table.register_obligation(predicate.0);
}
}
Some((impl_.impl_items(db), table.resolve_completely(impl_substs)))
})
})
let selection = infcx.select(&obligation).ok()??;
// Currently, we use a fulfillment context to completely resolve
// all nested obligations. This is because they can inform the
// inference of the impl's type parameters.
let mut ocx = ObligationCtxt::new(infcx);
let impl_source = selection.map(|obligation| ocx.register_obligation(obligation));
let errors = ocx.select_all_or_error();
if !errors.is_empty() {
return None;
}
let impl_source = infcx.resolve_vars_if_possible(impl_source);
if impl_source.has_non_region_infer() {
return None;
}
match impl_source {
ImplSource::UserDefined(impl_source) => Some((impl_source.impl_def_id, impl_source.args)),
ImplSource::Param(_) | ImplSource::Builtin(..) => None,
}
}
fn is_inherent_impl_coherent<'db>(

View file

@ -2,18 +2,7 @@
use std::{collections::hash_map::Entry, fmt::Display, iter};
use crate::{
CallableDefId, ClosureId, Const, ConstScalar, InferenceResult, Interner, MemoryMap,
Substitution, TraitEnvironment, Ty, TyExt, TyKind,
consteval::usize_const,
db::HirDatabase,
display::{DisplayTarget, HirDisplay},
infer::{PointerCast, normalize},
lang_items::is_box,
mapping::ToChalk,
};
use base_db::Crate;
use chalk_ir::Mutability;
use either::Either;
use hir_def::{
DefWithBodyId, FieldId, StaticId, TupleFieldId, UnionId, VariantId,
@ -21,6 +10,25 @@ use hir_def::{
hir::{BindingAnnotation, BindingId, Expr, ExprId, Ordering, PatId},
};
use la_arena::{Arena, ArenaMap, Idx, RawIdx};
use rustc_ast_ir::Mutability;
use rustc_hash::FxHashMap;
use rustc_type_ir::inherent::{AdtDef, GenericArgs as _, IntoKind, SliceLike, Ty as _};
use smallvec::{SmallVec, smallvec};
use stdx::{impl_from, never};
use crate::{
CallableDefId, InferenceResult, MemoryMap,
consteval::usize_const,
db::{HirDatabase, InternedClosureId},
display::{DisplayTarget, HirDisplay},
infer::PointerCast,
lang_items::is_box,
next_solver::{
Const, DbInterner, ErrorGuaranteed, GenericArgs, ParamEnv, Ty, TyKind,
infer::{InferCtxt, traits::ObligationCause},
obligation_ctxt::ObligationCtxt,
},
};
mod borrowck;
mod eval;
@ -36,25 +44,22 @@ pub use lower::{MirLowerError, lower_to_mir, mir_body_for_closure_query, mir_bod
pub use monomorphization::{
monomorphized_mir_body_for_closure_query, monomorphized_mir_body_query,
};
use rustc_hash::FxHashMap;
use smallvec::{SmallVec, smallvec};
use stdx::{impl_from, never};
pub(crate) use lower::mir_body_cycle_result;
pub(crate) use monomorphization::monomorphized_mir_body_cycle_result;
use super::consteval::{intern_const_scalar, try_const_usize};
use super::consteval::try_const_usize;
pub type BasicBlockId = Idx<BasicBlock>;
pub type LocalId = Idx<Local>;
pub type BasicBlockId<'db> = Idx<BasicBlock<'db>>;
pub type LocalId<'db> = Idx<Local<'db>>;
fn return_slot() -> LocalId {
fn return_slot<'db>() -> LocalId<'db> {
LocalId::from_raw(RawIdx::from(0))
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Local {
pub ty: Ty,
pub struct Local<'db> {
pub ty: Ty<'db>,
}
/// An operand in MIR represents a "value" in Rust, the definition of which is undecided and part of
@ -76,19 +81,19 @@ pub struct Local {
/// currently implements it, but it seems like this may be something to check against in the
/// validator.
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct Operand {
kind: OperandKind,
pub struct Operand<'db> {
kind: OperandKind<'db>,
// FIXME : This should actually just be of type `MirSpan`.
span: Option<MirSpan>,
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum OperandKind {
pub enum OperandKind<'db> {
/// Creates a value by loading the given place.
///
/// Before drop elaboration, the type of the place must be `Copy`. After drop elaboration there
/// is no such requirement.
Copy(Place),
Copy(Place<'db>),
/// Creates a value by performing loading the place, just like the `Copy` operand.
///
@ -97,41 +102,41 @@ pub enum OperandKind {
/// place without first re-initializing it.
///
/// [UCG#188]: https://github.com/rust-lang/unsafe-code-guidelines/issues/188
Move(Place),
Move(Place<'db>),
/// Constants are already semantically values, and remain unchanged.
Constant(Const),
Constant { konst: Const<'db>, ty: Ty<'db> },
/// NON STANDARD: This kind of operand returns an immutable reference to that static memory. Rustc
/// handles it with the `Constant` variant somehow.
Static(StaticId),
}
impl Operand {
fn from_concrete_const(data: Box<[u8]>, memory_map: MemoryMap<'static>, ty: Ty) -> Self {
impl<'db> Operand<'db> {
fn from_concrete_const(data: Box<[u8]>, memory_map: MemoryMap<'db>, ty: Ty<'db>) -> Self {
let interner = DbInterner::conjure();
Operand {
kind: OperandKind::Constant(intern_const_scalar(
ConstScalar::Bytes(data, memory_map),
kind: OperandKind::Constant {
konst: Const::new_valtree(interner, ty, data, memory_map),
ty,
)),
},
span: None,
}
}
fn from_bytes(data: Box<[u8]>, ty: Ty) -> Self {
fn from_bytes(data: Box<[u8]>, ty: Ty<'db>) -> Self {
Operand::from_concrete_const(data, MemoryMap::default(), ty)
}
fn const_zst(ty: Ty) -> Operand {
fn const_zst(ty: Ty<'db>) -> Operand<'db> {
Self::from_bytes(Box::default(), ty)
}
fn from_fn(
db: &dyn HirDatabase,
db: &'db dyn HirDatabase,
func_id: hir_def::FunctionId,
generic_args: Substitution,
) -> Operand {
let ty =
chalk_ir::TyKind::FnDef(CallableDefId::FunctionId(func_id).to_chalk(db), generic_args)
.intern(Interner);
generic_args: GenericArgs<'db>,
) -> Operand<'db> {
let interner = DbInterner::new_with(db, None, None);
let ty = Ty::new_fn_def(interner, CallableDefId::FunctionId(func_id).into(), generic_args);
Operand::from_bytes(Box::default(), ty)
}
}
@ -150,83 +155,81 @@ pub enum ProjectionElem<V, T> {
}
impl<V, T> ProjectionElem<V, T> {
pub fn projected_ty(
pub fn projected_ty<'db>(
&self,
mut base: Ty,
db: &dyn HirDatabase,
closure_field: impl FnOnce(ClosureId, &Substitution, usize) -> Ty,
infcx: &InferCtxt<'db>,
mut base: Ty<'db>,
closure_field: impl FnOnce(InternedClosureId, GenericArgs<'db>, usize) -> Ty<'db>,
krate: Crate,
) -> Ty {
) -> Ty<'db> {
let interner = infcx.interner;
let db = interner.db;
// we only bail on mir building when there are type mismatches
// but error types may pop up resulting in us still attempting to build the mir
// so just propagate the error type
if base.is_unknown() {
return TyKind::Error.intern(Interner);
if base.is_ty_error() {
return Ty::new_error(interner, ErrorGuaranteed);
}
if matches!(base.kind(Interner), TyKind::Alias(_) | TyKind::AssociatedType(..)) {
base = normalize(
db,
// FIXME: we should get this from caller
TraitEnvironment::empty(krate),
base,
);
if matches!(base.kind(), TyKind::Alias(..)) {
let mut ocx = ObligationCtxt::new(infcx);
// FIXME: we should get this from caller
let env = ParamEnv::empty();
match ocx.structurally_normalize_ty(&ObligationCause::dummy(), env, base) {
Ok(it) => base = it,
Err(_) => return Ty::new_error(interner, ErrorGuaranteed),
}
}
match self {
ProjectionElem::Deref => match &base.kind(Interner) {
TyKind::Raw(_, inner) | TyKind::Ref(_, _, inner) => inner.clone(),
TyKind::Adt(adt, subst) if is_box(db, adt.0) => {
subst.at(Interner, 0).assert_ty_ref(Interner).clone()
}
ProjectionElem::Deref => match base.kind() {
TyKind::RawPtr(inner, _) | TyKind::Ref(_, inner, _) => inner,
TyKind::Adt(adt_def, subst) if is_box(db, adt_def.def_id().0) => subst.type_at(0),
_ => {
never!(
"Overloaded deref on type {} is not a projection",
base.display(db, DisplayTarget::from_crate(db, krate))
);
TyKind::Error.intern(Interner)
Ty::new_error(interner, ErrorGuaranteed)
}
},
ProjectionElem::Field(Either::Left(f)) => match base.kind(Interner) {
ProjectionElem::Field(Either::Left(f)) => match base.kind() {
TyKind::Adt(_, subst) => {
db.field_types(f.parent)[f.local_id].clone().substitute(Interner, subst)
db.field_types_ns(f.parent)[f.local_id].instantiate(interner, subst)
}
ty => {
never!("Only adt has field, found {:?}", ty);
TyKind::Error.intern(Interner)
Ty::new_error(interner, ErrorGuaranteed)
}
},
ProjectionElem::Field(Either::Right(f)) => match &base.kind(Interner) {
TyKind::Tuple(_, subst) => subst
.as_slice(Interner)
.get(f.index as usize)
.map(|x| x.assert_ty_ref(Interner))
.cloned()
.unwrap_or_else(|| {
ProjectionElem::Field(Either::Right(f)) => match base.kind() {
TyKind::Tuple(subst) => {
subst.as_slice().get(f.index as usize).copied().unwrap_or_else(|| {
never!("Out of bound tuple field");
TyKind::Error.intern(Interner)
}),
Ty::new_error(interner, ErrorGuaranteed)
})
}
ty => {
never!("Only tuple has tuple field: {:?}", ty);
TyKind::Error.intern(Interner)
Ty::new_error(interner, ErrorGuaranteed)
}
},
ProjectionElem::ClosureField(f) => match &base.kind(Interner) {
TyKind::Closure(id, subst) => closure_field(*id, subst, *f),
ProjectionElem::ClosureField(f) => match base.kind() {
TyKind::Closure(id, subst) => closure_field(id.0, subst, *f),
_ => {
never!("Only closure has closure field");
TyKind::Error.intern(Interner)
Ty::new_error(interner, ErrorGuaranteed)
}
},
ProjectionElem::ConstantIndex { .. } | ProjectionElem::Index(_) => {
match &base.kind(Interner) {
TyKind::Array(inner, _) | TyKind::Slice(inner) => inner.clone(),
_ => {
never!("Overloaded index is not a projection");
TyKind::Error.intern(Interner)
}
ProjectionElem::ConstantIndex { .. } | ProjectionElem::Index(_) => match base.kind() {
TyKind::Array(inner, _) | TyKind::Slice(inner) => inner,
_ => {
never!("Overloaded index is not a projection");
Ty::new_error(interner, ErrorGuaranteed)
}
}
&ProjectionElem::Subslice { from, to } => match &base.kind(Interner) {
},
&ProjectionElem::Subslice { from, to } => match base.kind() {
TyKind::Array(inner, c) => {
let next_c = usize_const(
db,
@ -236,34 +239,34 @@ impl<V, T> ProjectionElem<V, T> {
},
krate,
);
TyKind::Array(inner.clone(), next_c).intern(Interner)
Ty::new_array_with_const_len(interner, inner, next_c)
}
TyKind::Slice(_) => base.clone(),
TyKind::Slice(_) => base,
_ => {
never!("Subslice projection should only happen on slice and array");
TyKind::Error.intern(Interner)
Ty::new_error(interner, ErrorGuaranteed)
}
},
ProjectionElem::OpaqueCast(_) => {
never!("We don't emit these yet");
TyKind::Error.intern(Interner)
Ty::new_error(interner, ErrorGuaranteed)
}
}
}
}
type PlaceElem = ProjectionElem<LocalId, Ty>;
type PlaceElem<'db> = ProjectionElem<LocalId<'db>, Ty<'db>>;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ProjectionId(u32);
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ProjectionStore {
id_to_proj: FxHashMap<ProjectionId, Box<[PlaceElem]>>,
proj_to_id: FxHashMap<Box<[PlaceElem]>, ProjectionId>,
pub struct ProjectionStore<'db> {
id_to_proj: FxHashMap<ProjectionId, Box<[PlaceElem<'db>]>>,
proj_to_id: FxHashMap<Box<[PlaceElem<'db>]>, ProjectionId>,
}
impl Default for ProjectionStore {
impl Default for ProjectionStore<'_> {
fn default() -> Self {
let mut this = Self { id_to_proj: Default::default(), proj_to_id: Default::default() };
// Ensure that [] will get the id 0 which is used in `ProjectionId::Empty`
@ -272,17 +275,17 @@ impl Default for ProjectionStore {
}
}
impl ProjectionStore {
impl<'db> ProjectionStore<'db> {
pub fn shrink_to_fit(&mut self) {
self.id_to_proj.shrink_to_fit();
self.proj_to_id.shrink_to_fit();
}
pub fn intern_if_exist(&self, projection: &[PlaceElem]) -> Option<ProjectionId> {
pub fn intern_if_exist(&self, projection: &[PlaceElem<'db>]) -> Option<ProjectionId> {
self.proj_to_id.get(projection).copied()
}
pub fn intern(&mut self, projection: Box<[PlaceElem]>) -> ProjectionId {
pub fn intern(&mut self, projection: Box<[PlaceElem<'db>]>) -> ProjectionId {
let new_id = ProjectionId(self.proj_to_id.len() as u32);
match self.proj_to_id.entry(projection) {
Entry::Occupied(id) => *id.get(),
@ -303,11 +306,15 @@ impl ProjectionId {
self == ProjectionId::EMPTY
}
pub fn lookup(self, store: &ProjectionStore) -> &[PlaceElem] {
pub fn lookup<'a, 'db>(self, store: &'a ProjectionStore<'db>) -> &'a [PlaceElem<'db>] {
store.id_to_proj.get(&self).unwrap()
}
pub fn project(self, projection: PlaceElem, store: &mut ProjectionStore) -> ProjectionId {
pub fn project<'db>(
self,
projection: PlaceElem<'db>,
store: &mut ProjectionStore<'db>,
) -> ProjectionId {
let mut current = self.lookup(store).to_vec();
current.push(projection);
store.intern(current.into())
@ -315,13 +322,13 @@ impl ProjectionId {
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Place {
pub local: LocalId,
pub struct Place<'db> {
pub local: LocalId<'db>,
pub projection: ProjectionId,
}
impl Place {
fn is_parent(&self, child: &Place, store: &ProjectionStore) -> bool {
impl<'db> Place<'db> {
fn is_parent(&self, child: &Place<'db>, store: &ProjectionStore<'db>) -> bool {
self.local == child.local
&& child.projection.lookup(store).starts_with(self.projection.lookup(store))
}
@ -329,39 +336,39 @@ impl Place {
/// The place itself is not included
fn iterate_over_parents<'a>(
&'a self,
store: &'a ProjectionStore,
) -> impl Iterator<Item = Place> + 'a {
store: &'a ProjectionStore<'db>,
) -> impl Iterator<Item = Place<'db>> + 'a {
let projection = self.projection.lookup(store);
(0..projection.len()).map(|x| &projection[0..x]).filter_map(move |x| {
Some(Place { local: self.local, projection: store.intern_if_exist(x)? })
})
}
fn project(&self, projection: PlaceElem, store: &mut ProjectionStore) -> Place {
fn project(&self, projection: PlaceElem<'db>, store: &mut ProjectionStore<'db>) -> Place<'db> {
Place { local: self.local, projection: self.projection.project(projection, store) }
}
}
impl From<LocalId> for Place {
fn from(local: LocalId) -> Self {
impl<'db> From<LocalId<'db>> for Place<'db> {
fn from(local: LocalId<'db>) -> Self {
Self { local, projection: ProjectionId::EMPTY }
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum AggregateKind {
pub enum AggregateKind<'db> {
/// The type is of the element
Array(Ty),
Array(Ty<'db>),
/// The type is of the tuple
Tuple(Ty),
Adt(VariantId, Substitution),
Tuple(Ty<'db>),
Adt(VariantId, GenericArgs<'db>),
Union(UnionId, FieldId),
Closure(Ty),
Closure(Ty<'db>),
//Coroutine(LocalDefId, SubstsRef, Movability),
}
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct SwitchTargets {
pub struct SwitchTargets<'db> {
/// Possible values. The locations to branch to in each case
/// are found in the corresponding indices from the `targets` vector.
values: SmallVec<[u128; 1]>,
@ -378,17 +385,17 @@ pub struct SwitchTargets {
//
// However weve decided to keep this as-is until we figure a case
// where some other approach seems to be strictly better than other.
targets: SmallVec<[BasicBlockId; 2]>,
targets: SmallVec<[BasicBlockId<'db>; 2]>,
}
impl SwitchTargets {
impl<'db> SwitchTargets<'db> {
/// Creates switch targets from an iterator of values and target blocks.
///
/// The iterator may be empty, in which case the `SwitchInt` instruction is equivalent to
/// `goto otherwise;`.
pub fn new(
targets: impl Iterator<Item = (u128, BasicBlockId)>,
otherwise: BasicBlockId,
targets: impl Iterator<Item = (u128, BasicBlockId<'db>)>,
otherwise: BasicBlockId<'db>,
) -> Self {
let (values, mut targets): (SmallVec<_>, SmallVec<_>) = targets.unzip();
targets.push(otherwise);
@ -397,12 +404,12 @@ impl SwitchTargets {
/// Builds a switch targets definition that jumps to `then` if the tested value equals `value`,
/// and to `else_` if not.
pub fn static_if(value: u128, then: BasicBlockId, else_: BasicBlockId) -> Self {
pub fn static_if(value: u128, then: BasicBlockId<'db>, else_: BasicBlockId<'db>) -> Self {
Self { values: smallvec![value], targets: smallvec![then, else_] }
}
/// Returns the fallback target that is jumped to when none of the values match the operand.
pub fn otherwise(&self) -> BasicBlockId {
pub fn otherwise(&self) -> BasicBlockId<'db> {
*self.targets.last().unwrap()
}
@ -412,33 +419,33 @@ impl SwitchTargets {
/// including the `otherwise` fallback target.
///
/// Note that this may yield 0 elements. Only the `otherwise` branch is mandatory.
pub fn iter(&self) -> impl Iterator<Item = (u128, BasicBlockId)> + '_ {
pub fn iter(&self) -> impl Iterator<Item = (u128, BasicBlockId<'db>)> + '_ {
iter::zip(&self.values, &self.targets).map(|(x, y)| (*x, *y))
}
/// Returns a slice with all possible jump targets (including the fallback target).
pub fn all_targets(&self) -> &[BasicBlockId] {
pub fn all_targets(&self) -> &[BasicBlockId<'db>] {
&self.targets
}
/// Finds the `BasicBlock` to which this `SwitchInt` will branch given the
/// specific value. This cannot fail, as it'll return the `otherwise`
/// branch if there's not a specific match for the value.
pub fn target_for_value(&self, value: u128) -> BasicBlockId {
pub fn target_for_value(&self, value: u128) -> BasicBlockId<'db> {
self.iter().find_map(|(v, t)| (v == value).then_some(t)).unwrap_or_else(|| self.otherwise())
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct Terminator {
pub struct Terminator<'db> {
pub span: MirSpan,
pub kind: TerminatorKind,
pub kind: TerminatorKind<'db>,
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum TerminatorKind {
pub enum TerminatorKind<'db> {
/// Block has one successor; we continue execution there.
Goto { target: BasicBlockId },
Goto { target: BasicBlockId<'db> },
/// Switches based on the computed value.
///
@ -450,9 +457,9 @@ pub enum TerminatorKind {
/// Target values may not appear more than once.
SwitchInt {
/// The discriminant value being tested.
discr: Operand,
discr: Operand<'db>,
targets: SwitchTargets,
targets: SwitchTargets<'db>,
},
/// Indicates that the landing pad is finished and that the process should continue unwinding.
@ -503,7 +510,7 @@ pub enum TerminatorKind {
/// > The drop glue is executed if, among all statements executed within this `Body`, an assignment to
/// > the place or one of its "parents" occurred more recently than a move out of it. This does not
/// > consider indirect assignments.
Drop { place: Place, target: BasicBlockId, unwind: Option<BasicBlockId> },
Drop { place: Place<'db>, target: BasicBlockId<'db>, unwind: Option<BasicBlockId<'db>> },
/// Drops the place and assigns a new value to it.
///
@ -536,10 +543,10 @@ pub enum TerminatorKind {
///
/// Disallowed after drop elaboration.
DropAndReplace {
place: Place,
value: Operand,
target: BasicBlockId,
unwind: Option<BasicBlockId>,
place: Place<'db>,
value: Operand<'db>,
target: BasicBlockId<'db>,
unwind: Option<BasicBlockId<'db>>,
},
/// Roughly speaking, evaluates the `func` operand and the arguments, and starts execution of
@ -554,18 +561,18 @@ pub enum TerminatorKind {
/// [#71117]: https://github.com/rust-lang/rust/issues/71117
Call {
/// The function thats being called.
func: Operand,
func: Operand<'db>,
/// Arguments the function is called with.
/// These are owned by the callee, which is free to modify them.
/// This allows the memory occupied by "by-value" arguments to be
/// reused across function calls without duplicating the contents.
args: Box<[Operand]>,
args: Box<[Operand<'db>]>,
/// Where the returned value will be written
destination: Place,
destination: Place<'db>,
/// Where to go after this call returns. If none, the call necessarily diverges.
target: Option<BasicBlockId>,
target: Option<BasicBlockId<'db>>,
/// Cleanups to be done if the call unwinds.
cleanup: Option<BasicBlockId>,
cleanup: Option<BasicBlockId<'db>>,
/// `true` if this is from a call in HIR rather than from an overloaded
/// operator. True for overloaded function call.
from_hir_call: bool,
@ -581,11 +588,11 @@ pub enum TerminatorKind {
/// necessarily executed even in the case of a panic, for example in `-C panic=abort`. If the
/// assertion does not fail, execution continues at the specified basic block.
Assert {
cond: Operand,
cond: Operand<'db>,
expected: bool,
//msg: AssertMessage,
target: BasicBlockId,
cleanup: Option<BasicBlockId>,
target: BasicBlockId<'db>,
cleanup: Option<BasicBlockId<'db>>,
},
/// Marks a suspend point.
@ -602,13 +609,13 @@ pub enum TerminatorKind {
/// **Needs clarification**: What about the evaluation order of the `resume_arg` and `value`?
Yield {
/// The value to return.
value: Operand,
value: Operand<'db>,
/// Where to resume to.
resume: BasicBlockId,
resume: BasicBlockId<'db>,
/// The place to store the resume argument in.
resume_arg: Place,
resume_arg: Place<'db>,
/// Cleanup to be done if the coroutine is dropped at this suspend point.
drop: Option<BasicBlockId>,
drop: Option<BasicBlockId<'db>>,
},
/// Indicates the end of dropping a coroutine.
@ -631,10 +638,10 @@ pub enum TerminatorKind {
/// Disallowed after drop elaboration.
FalseEdge {
/// The target normal control flow will take.
real_target: BasicBlockId,
real_target: BasicBlockId<'db>,
/// A block control flow could conceptually jump to, but won't in
/// practice.
imaginary_target: BasicBlockId,
imaginary_target: BasicBlockId<'db>,
},
/// A terminator for blocks that only take one path in reality, but where we reserve the right
@ -646,14 +653,14 @@ pub enum TerminatorKind {
/// Disallowed after drop elaboration.
FalseUnwind {
/// The target normal control flow will take.
real_target: BasicBlockId,
real_target: BasicBlockId<'db>,
/// The imaginary cleanup block link. This particular path will never be taken
/// in practice, but in order to avoid fragility we want to always
/// consider it in borrowck. We don't want to accept programs which
/// pass borrowck only when `panic=abort` or some assertions are disabled
/// due to release vs. debug mode builds. This needs to be an `Option` because
/// of the `remove_noop_landing_pads` and `abort_unwinding_calls` passes.
unwind: Option<BasicBlockId>,
unwind: Option<BasicBlockId<'db>>,
},
}
@ -840,8 +847,8 @@ impl From<hir_def::hir::CmpOp> for BinOp {
}
}
impl From<Operand> for Rvalue {
fn from(x: Operand) -> Self {
impl<'db> From<Operand<'db>> for Rvalue<'db> {
fn from(x: Operand<'db>) -> Self {
Self::Use(x)
}
}
@ -870,14 +877,14 @@ pub enum CastKind {
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum Rvalue {
pub enum Rvalue<'db> {
/// Yields the operand unchanged
Use(Operand),
Use(Operand<'db>),
/// Creates an array where each element is the value of the operand.
///
/// Corresponds to source code like `[x; 32]`.
Repeat(Operand, Const),
Repeat(Operand<'db>, Const<'db>),
/// Creates a reference of the indicated kind to the place.
///
@ -886,7 +893,7 @@ pub enum Rvalue {
/// exactly what the behavior of this operation should be.
///
/// `Shallow` borrows are disallowed after drop lowering.
Ref(BorrowKind, Place),
Ref(BorrowKind, Place<'db>),
/// Creates a pointer/reference to the given thread local.
///
@ -917,7 +924,7 @@ pub enum Rvalue {
/// If the type of the place is an array, this is the array length. For slices (`[T]`, not
/// `&[T]`) this accesses the place's metadata to determine the length. This rvalue is
/// ill-formed for places of other types.
Len(Place),
Len(Place<'db>),
/// Performs essentially all of the casts that can be performed via `as`.
///
@ -925,7 +932,7 @@ pub enum Rvalue {
///
/// **FIXME**: Document exactly which `CastKind`s allow which types of casts. Figure out why
/// `ArrayToPointer` and `MutToConstPointer` are special.
Cast(CastKind, Operand, Ty),
Cast(CastKind, Operand<'db>, Ty<'db>),
// FIXME link to `pointer::offset` when it hits stable.
/// * `Offset` has the same semantics as `pointer::offset`, except that the second
@ -957,7 +964,7 @@ pub enum Rvalue {
/// when the value of right-hand side is negative.
///
/// Other combinations of types and operators are unsupported.
CheckedBinaryOp(BinOp, Operand, Operand),
CheckedBinaryOp(BinOp, Operand<'db>, Operand<'db>),
/// Computes a value as described by the operation.
//NullaryOp(NullOp, Ty),
@ -968,7 +975,7 @@ pub enum Rvalue {
/// Also does two's-complement arithmetic. Negation requires a signed integer or a float;
/// bitwise not requires a signed integer, unsigned integer, or bool. Both operation kinds
/// return a value with the same type as their operand.
UnaryOp(UnOp, Operand),
UnaryOp(UnOp, Operand<'db>),
/// Computes the discriminant of the place, returning it as an integer of type
/// [`discriminant_ty`]. Returns zero for types without discriminant.
@ -980,7 +987,7 @@ pub enum Rvalue {
/// [`discriminant_ty`]: crate::ty::Ty::discriminant_ty
/// [#91095]: https://github.com/rust-lang/rust/issues/91095
/// [`discriminant_for_variant`]: crate::ty::Ty::discriminant_for_variant
Discriminant(Place),
Discriminant(Place<'db>),
/// Creates an aggregate value, like a tuple or struct.
///
@ -990,17 +997,17 @@ pub enum Rvalue {
///
/// Disallowed after deaggregation for all aggregate kinds except `Array` and `Coroutine`. After
/// coroutine lowering, `Coroutine` aggregate kinds are disallowed too.
Aggregate(AggregateKind, Box<[Operand]>),
Aggregate(AggregateKind<'db>, Box<[Operand<'db>]>),
/// Transmutes a `*mut u8` into shallow-initialized `Box<T>`.
///
/// This is different from a normal transmute because dataflow analysis will treat the box as
/// initialized but its content as uninitialized. Like other pointer casts, this in general
/// affects alias analysis.
ShallowInitBox(Operand, Ty),
ShallowInitBox(Operand<'db>, Ty<'db>),
/// NON STANDARD: allocates memory with the type's layout, and shallow init the box with the resulting pointer.
ShallowInitBoxWithAlloc(Ty),
ShallowInitBoxWithAlloc(Ty<'db>),
/// A CopyForDeref is equivalent to a read from a place at the
/// codegen level, but is treated specially by drop elaboration. When such a read happens, it
@ -1010,41 +1017,41 @@ pub enum Rvalue {
/// read never happened and just projects further. This allows simplifying various MIR
/// optimizations and codegen backends that previously had to handle deref operations anywhere
/// in a place.
CopyForDeref(Place),
CopyForDeref(Place<'db>),
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum StatementKind {
Assign(Place, Rvalue),
FakeRead(Place),
pub enum StatementKind<'db> {
Assign(Place<'db>, Rvalue<'db>),
FakeRead(Place<'db>),
//SetDiscriminant {
// place: Box<Place>,
// variant_index: VariantIdx,
//},
Deinit(Place),
StorageLive(LocalId),
StorageDead(LocalId),
Deinit(Place<'db>),
StorageLive(LocalId<'db>),
StorageDead(LocalId<'db>),
//Retag(RetagKind, Box<Place>),
//AscribeUserType(Place, UserTypeProjection, Variance),
//Intrinsic(Box<NonDivergingIntrinsic>),
Nop,
}
impl StatementKind {
fn with_span(self, span: MirSpan) -> Statement {
impl<'db> StatementKind<'db> {
fn with_span(self, span: MirSpan) -> Statement<'db> {
Statement { kind: self, span }
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct Statement {
pub kind: StatementKind,
pub struct Statement<'db> {
pub kind: StatementKind<'db>,
pub span: MirSpan,
}
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct BasicBlock {
pub struct BasicBlock<'db> {
/// List of statements in this block.
pub statements: Vec<Statement>,
pub statements: Vec<Statement<'db>>,
/// Terminator for this block.
///
@ -1054,7 +1061,7 @@ pub struct BasicBlock {
/// exception is that certain passes, such as `simplify_cfg`, swap
/// out the terminator temporarily with `None` while they continue
/// to recurse over the set of basic blocks.
pub terminator: Option<Terminator>,
pub terminator: Option<Terminator<'db>>,
/// If true, this block lies on an unwind path. This is used
/// during codegen where distinct kinds of basic blocks may be
@ -1064,35 +1071,35 @@ pub struct BasicBlock {
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MirBody {
pub projection_store: ProjectionStore,
pub basic_blocks: Arena<BasicBlock>,
pub locals: Arena<Local>,
pub start_block: BasicBlockId,
pub struct MirBody<'db> {
pub projection_store: ProjectionStore<'db>,
pub basic_blocks: Arena<BasicBlock<'db>>,
pub locals: Arena<Local<'db>>,
pub start_block: BasicBlockId<'db>,
pub owner: DefWithBodyId,
pub binding_locals: ArenaMap<BindingId, LocalId>,
pub param_locals: Vec<LocalId>,
pub binding_locals: ArenaMap<BindingId, LocalId<'db>>,
pub param_locals: Vec<LocalId<'db>>,
/// This field stores the closures directly owned by this body. It is used
/// in traversing every mir body.
pub closures: Vec<ClosureId>,
pub closures: Vec<InternedClosureId>,
}
impl MirBody {
pub fn local_to_binding_map(&self) -> ArenaMap<LocalId, BindingId> {
impl<'db> MirBody<'db> {
pub fn local_to_binding_map(&self) -> ArenaMap<LocalId<'db>, BindingId> {
self.binding_locals.iter().map(|(it, y)| (*y, it)).collect()
}
fn walk_places(&mut self, mut f: impl FnMut(&mut Place, &mut ProjectionStore)) {
fn for_operand(
op: &mut Operand,
f: &mut impl FnMut(&mut Place, &mut ProjectionStore),
store: &mut ProjectionStore,
fn walk_places(&mut self, mut f: impl FnMut(&mut Place<'db>, &mut ProjectionStore<'db>)) {
fn for_operand<'db>(
op: &mut Operand<'db>,
f: &mut impl FnMut(&mut Place<'db>, &mut ProjectionStore<'db>),
store: &mut ProjectionStore<'db>,
) {
match &mut op.kind {
OperandKind::Copy(p) | OperandKind::Move(p) => {
f(p, store);
}
OperandKind::Constant(_) | OperandKind::Static(_) => (),
OperandKind::Constant { .. } | OperandKind::Static(_) => (),
}
}
for (_, block) in self.basic_blocks.iter_mut() {

View file

@ -11,14 +11,15 @@ use rustc_hash::FxHashMap;
use stdx::never;
use triomphe::Arc;
use crate::next_solver::DbInterner;
use crate::next_solver::mapping::{ChalkToNextSolver, NextSolverToChalk};
use crate::{
ClosureId, Interner, Substitution, Ty, TyExt, TypeFlags,
db::{HirDatabase, InternedClosure},
TraitEnvironment,
db::{HirDatabase, InternedClosure, InternedClosureId},
display::DisplayTarget,
mir::OperandKind,
utils::ClosureSubst,
next_solver::{
DbInterner, GenericArgs, SolverDefIds, Ty, TypingMode,
infer::{DbInternerInferExt, InferCtxt},
},
};
use super::{
@ -35,45 +36,45 @@ pub enum MutabilityReason {
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MovedOutOfRef {
pub ty: Ty,
pub struct MovedOutOfRef<'db> {
pub ty: Ty<'db>,
pub span: MirSpan,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PartiallyMoved {
pub ty: Ty,
pub struct PartiallyMoved<'db> {
pub ty: Ty<'db>,
pub span: MirSpan,
pub local: LocalId,
pub local: LocalId<'db>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BorrowRegion {
pub local: LocalId,
pub struct BorrowRegion<'db> {
pub local: LocalId<'db>,
pub kind: BorrowKind,
pub places: Vec<MirSpan>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BorrowckResult {
pub mir_body: Arc<MirBody>,
pub mutability_of_locals: ArenaMap<LocalId, MutabilityReason>,
pub moved_out_of_ref: Vec<MovedOutOfRef>,
pub partially_moved: Vec<PartiallyMoved>,
pub borrow_regions: Vec<BorrowRegion>,
pub struct BorrowckResult<'db> {
pub mir_body: Arc<MirBody<'db>>,
pub mutability_of_locals: ArenaMap<LocalId<'db>, MutabilityReason>,
pub moved_out_of_ref: Vec<MovedOutOfRef<'db>>,
pub partially_moved: Vec<PartiallyMoved<'db>>,
pub borrow_regions: Vec<BorrowRegion<'db>>,
}
fn all_mir_bodies<'db>(
db: &'db dyn HirDatabase,
def: DefWithBodyId,
mut cb: impl FnMut(Arc<MirBody>),
mut cb: impl FnMut(Arc<MirBody<'db>>),
) -> Result<(), MirLowerError<'db>> {
fn for_closure<'db>(
db: &'db dyn HirDatabase,
c: ClosureId,
cb: &mut impl FnMut(Arc<MirBody>),
c: InternedClosureId,
cb: &mut impl FnMut(Arc<MirBody<'db>>),
) -> Result<(), MirLowerError<'db>> {
match db.mir_body_for_closure(c.into()) {
match db.mir_body_for_closure(c) {
Ok(body) => {
cb(body.clone());
body.closures.iter().try_for_each(|&it| for_closure(db, it, cb))
@ -93,14 +94,21 @@ fn all_mir_bodies<'db>(
pub fn borrowck_query<'db>(
db: &'db dyn HirDatabase,
def: DefWithBodyId,
) -> Result<Arc<[BorrowckResult]>, MirLowerError<'db>> {
) -> Result<Arc<[BorrowckResult<'db>]>, MirLowerError<'db>> {
let _p = tracing::info_span!("borrowck_query").entered();
let module = def.module(db);
let interner = DbInterner::new_with(db, Some(module.krate()), module.containing_block());
let env = db.trait_environment_for_body(def);
let mut res = vec![];
all_mir_bodies(db, def, |body| {
// FIXME(next-solver): Opaques.
let infcx = interner.infer_ctxt().build(TypingMode::Borrowck {
defining_opaque_types: SolverDefIds::new_from_iter(interner, []),
});
res.push(BorrowckResult {
mutability_of_locals: mutability_of_locals(db, &body),
moved_out_of_ref: moved_out_of_ref(db, &body),
partially_moved: partially_moved(db, &body),
mutability_of_locals: mutability_of_locals(&infcx, &body),
moved_out_of_ref: moved_out_of_ref(&infcx, &env, &body),
partially_moved: partially_moved(&infcx, &env, &body),
borrow_regions: borrow_regions(db, &body),
mir_body: body,
});
@ -108,51 +116,49 @@ pub fn borrowck_query<'db>(
Ok(res.into())
}
fn make_fetch_closure_field(
db: &dyn HirDatabase,
) -> impl FnOnce(ClosureId, &Substitution, usize) -> Ty + '_ {
|c: ClosureId, subst: &Substitution, f: usize| {
let InternedClosure(def, _) = db.lookup_intern_closure(c.into());
fn make_fetch_closure_field<'db>(
db: &'db dyn HirDatabase,
) -> impl FnOnce(InternedClosureId, GenericArgs<'db>, usize) -> Ty<'db> + use<'db> {
|c: InternedClosureId, subst: GenericArgs<'db>, f: usize| {
let InternedClosure(def, _) = db.lookup_intern_closure(c);
let infer = db.infer(def);
let (captures, _) = infer.closure_info(c.into());
let parent_subst = ClosureSubst(subst).parent_subst(db);
let (captures, _) = infer.closure_info(c);
let parent_subst = subst.split_closure_args_untupled().parent_args;
let interner = DbInterner::new_with(db, None, None);
let parent_subst: crate::next_solver::GenericArgs<'_> =
parent_subst.to_nextsolver(interner);
captures
.get(f)
.expect("broken closure field")
.ty
.instantiate(interner, parent_subst)
.to_chalk(interner)
captures.get(f).expect("broken closure field").ty.instantiate(interner, parent_subst)
}
}
fn moved_out_of_ref(db: &dyn HirDatabase, body: &MirBody) -> Vec<MovedOutOfRef> {
fn moved_out_of_ref<'db>(
infcx: &InferCtxt<'db>,
env: &TraitEnvironment<'db>,
body: &MirBody<'db>,
) -> Vec<MovedOutOfRef<'db>> {
let db = infcx.interner.db;
let mut result = vec![];
let mut for_operand = |op: &Operand, span: MirSpan| match op.kind {
let mut for_operand = |op: &Operand<'db>, span: MirSpan| match op.kind {
OperandKind::Copy(p) | OperandKind::Move(p) => {
let mut ty: Ty = body.locals[p.local].ty.clone();
let mut ty: Ty<'db> = body.locals[p.local].ty;
let mut is_dereference_of_ref = false;
for proj in p.projection.lookup(&body.projection_store) {
if *proj == ProjectionElem::Deref && ty.as_reference().is_some() {
is_dereference_of_ref = true;
}
ty = proj.projected_ty(
infcx,
ty,
db,
make_fetch_closure_field(db),
body.owner.module(db).krate(),
);
}
if is_dereference_of_ref
&& !ty.clone().is_copy(db, body.owner)
&& !ty.data(Interner).flags.intersects(TypeFlags::HAS_ERROR)
&& !infcx.type_is_copy_modulo_regions(env.env, ty)
&& !ty.references_non_lt_error()
{
result.push(MovedOutOfRef { span: op.span.unwrap_or(span), ty });
}
}
OperandKind::Constant(_) | OperandKind::Static(_) => (),
OperandKind::Constant { .. } | OperandKind::Static(_) => (),
};
for (_, block) in body.basic_blocks.iter() {
db.unwind_if_revision_cancelled();
@ -223,26 +229,29 @@ fn moved_out_of_ref(db: &dyn HirDatabase, body: &MirBody) -> Vec<MovedOutOfRef>
result
}
fn partially_moved(db: &dyn HirDatabase, body: &MirBody) -> Vec<PartiallyMoved> {
fn partially_moved<'db>(
infcx: &InferCtxt<'db>,
env: &TraitEnvironment<'db>,
body: &MirBody<'db>,
) -> Vec<PartiallyMoved<'db>> {
let db = infcx.interner.db;
let mut result = vec![];
let mut for_operand = |op: &Operand, span: MirSpan| match op.kind {
let mut for_operand = |op: &Operand<'db>, span: MirSpan| match op.kind {
OperandKind::Copy(p) | OperandKind::Move(p) => {
let mut ty: Ty = body.locals[p.local].ty.clone();
let mut ty: Ty<'db> = body.locals[p.local].ty;
for proj in p.projection.lookup(&body.projection_store) {
ty = proj.projected_ty(
infcx,
ty,
db,
make_fetch_closure_field(db),
body.owner.module(db).krate(),
);
}
if !ty.clone().is_copy(db, body.owner)
&& !ty.data(Interner).flags.intersects(TypeFlags::HAS_ERROR)
{
if !infcx.type_is_copy_modulo_regions(env.env, ty) && !ty.references_non_lt_error() {
result.push(PartiallyMoved { span, ty, local: p.local });
}
}
OperandKind::Constant(_) | OperandKind::Static(_) => (),
OperandKind::Constant { .. } | OperandKind::Static(_) => (),
};
for (_, block) in body.basic_blocks.iter() {
db.unwind_if_revision_cancelled();
@ -313,7 +322,7 @@ fn partially_moved(db: &dyn HirDatabase, body: &MirBody) -> Vec<PartiallyMoved>
result
}
fn borrow_regions(db: &dyn HirDatabase, body: &MirBody) -> Vec<BorrowRegion> {
fn borrow_regions<'db>(db: &'db dyn HirDatabase, body: &MirBody<'db>) -> Vec<BorrowRegion<'db>> {
let mut borrows = FxHashMap::default();
for (_, block) in body.basic_blocks.iter() {
db.unwind_if_revision_cancelled();
@ -321,7 +330,7 @@ fn borrow_regions(db: &dyn HirDatabase, body: &MirBody) -> Vec<BorrowRegion> {
if let StatementKind::Assign(_, Rvalue::Ref(kind, p)) = &statement.kind {
borrows
.entry(p.local)
.and_modify(|it: &mut BorrowRegion| {
.and_modify(|it: &mut BorrowRegion<'db>| {
it.places.push(statement.span);
})
.or_insert_with(|| BorrowRegion {
@ -363,9 +372,14 @@ enum ProjectionCase {
Indirect,
}
fn place_case(db: &dyn HirDatabase, body: &MirBody, lvalue: &Place) -> ProjectionCase {
fn place_case<'db>(
infcx: &InferCtxt<'db>,
body: &MirBody<'db>,
lvalue: &Place<'db>,
) -> ProjectionCase {
let db = infcx.interner.db;
let mut is_part_of = false;
let mut ty = body.locals[lvalue.local].ty.clone();
let mut ty = body.locals[lvalue.local].ty;
for proj in lvalue.projection.lookup(&body.projection_store).iter() {
match proj {
ProjectionElem::Deref if ty.as_adt().is_none() => return ProjectionCase::Indirect, // It's indirect in case of reference and raw
@ -379,7 +393,12 @@ fn place_case(db: &dyn HirDatabase, body: &MirBody, lvalue: &Place) -> Projectio
}
ProjectionElem::OpaqueCast(_) => (),
}
ty = proj.projected_ty(ty, db, make_fetch_closure_field(db), body.owner.module(db).krate());
ty = proj.projected_ty(
infcx,
ty,
make_fetch_closure_field(db),
body.owner.module(db).krate(),
);
}
if is_part_of { ProjectionCase::DirectPart } else { ProjectionCase::Direct }
}
@ -387,18 +406,18 @@ fn place_case(db: &dyn HirDatabase, body: &MirBody, lvalue: &Place) -> Projectio
/// Returns a map from basic blocks to the set of locals that might be ever initialized before
/// the start of the block. Only `StorageDead` can remove something from this map, and we ignore
/// `Uninit` and `drop` and similar after initialization.
fn ever_initialized_map(
db: &dyn HirDatabase,
body: &MirBody,
) -> ArenaMap<BasicBlockId, ArenaMap<LocalId, bool>> {
let mut result: ArenaMap<BasicBlockId, ArenaMap<LocalId, bool>> =
fn ever_initialized_map<'db>(
db: &'db dyn HirDatabase,
body: &MirBody<'db>,
) -> ArenaMap<BasicBlockId<'db>, ArenaMap<LocalId<'db>, bool>> {
let mut result: ArenaMap<BasicBlockId<'db>, ArenaMap<LocalId<'db>, bool>> =
body.basic_blocks.iter().map(|it| (it.0, ArenaMap::default())).collect();
fn dfs(
db: &dyn HirDatabase,
body: &MirBody,
l: LocalId,
stack: &mut Vec<BasicBlockId>,
result: &mut ArenaMap<BasicBlockId, ArenaMap<LocalId, bool>>,
fn dfs<'db>(
db: &'db dyn HirDatabase,
body: &MirBody<'db>,
l: LocalId<'db>,
stack: &mut Vec<BasicBlockId<'db>>,
result: &mut ArenaMap<BasicBlockId<'db>, ArenaMap<LocalId<'db>, bool>>,
) {
while let Some(b) = stack.pop() {
let mut is_ever_initialized = result[b][l]; // It must be filled, as we use it as mark for dfs
@ -486,7 +505,11 @@ fn ever_initialized_map(
result
}
fn push_mut_span(local: LocalId, span: MirSpan, result: &mut ArenaMap<LocalId, MutabilityReason>) {
fn push_mut_span<'db>(
local: LocalId<'db>,
span: MirSpan,
result: &mut ArenaMap<LocalId<'db>, MutabilityReason>,
) {
match &mut result[local] {
MutabilityReason::Mut { spans } => spans.push(span),
it @ (MutabilityReason::Not | MutabilityReason::Unused) => {
@ -495,23 +518,27 @@ fn push_mut_span(local: LocalId, span: MirSpan, result: &mut ArenaMap<LocalId, M
};
}
fn record_usage(local: LocalId, result: &mut ArenaMap<LocalId, MutabilityReason>) {
fn record_usage<'db>(local: LocalId<'db>, result: &mut ArenaMap<LocalId<'db>, MutabilityReason>) {
if let it @ MutabilityReason::Unused = &mut result[local] {
*it = MutabilityReason::Not;
};
}
fn record_usage_for_operand(arg: &Operand, result: &mut ArenaMap<LocalId, MutabilityReason>) {
fn record_usage_for_operand<'db>(
arg: &Operand<'db>,
result: &mut ArenaMap<LocalId<'db>, MutabilityReason>,
) {
if let OperandKind::Copy(p) | OperandKind::Move(p) = arg.kind {
record_usage(p.local, result);
}
}
fn mutability_of_locals(
db: &dyn HirDatabase,
body: &MirBody,
) -> ArenaMap<LocalId, MutabilityReason> {
let mut result: ArenaMap<LocalId, MutabilityReason> =
fn mutability_of_locals<'db>(
infcx: &InferCtxt<'db>,
body: &MirBody<'db>,
) -> ArenaMap<LocalId<'db>, MutabilityReason> {
let db = infcx.interner.db;
let mut result: ArenaMap<LocalId<'db>, MutabilityReason> =
body.locals.iter().map(|it| (it.0, MutabilityReason::Unused)).collect();
let ever_init_maps = ever_initialized_map(db, body);
@ -520,7 +547,7 @@ fn mutability_of_locals(
for statement in &block.statements {
match &statement.kind {
StatementKind::Assign(place, value) => {
match place_case(db, body, place) {
match place_case(infcx, body, place) {
ProjectionCase::Direct => {
if ever_init_map.get(place.local).copied().unwrap_or_default() {
push_mut_span(place.local, statement.span, &mut result);
@ -569,7 +596,7 @@ fn mutability_of_locals(
},
p,
) = value
&& place_case(db, body, p) != ProjectionCase::Indirect
&& place_case(infcx, body, p) != ProjectionCase::Indirect
{
push_mut_span(p.local, statement.span, &mut result);
}

File diff suppressed because it is too large Load diff

View file

@ -3,32 +3,21 @@
//!
use std::cmp::{self, Ordering};
use chalk_ir::TyKind;
use hir_def::signatures::FunctionSignature;
use hir_def::{
CrateRootModuleId,
builtin_type::{BuiltinInt, BuiltinUint},
resolver::HasResolver,
};
use hir_def::{CrateRootModuleId, resolver::HasResolver, signatures::FunctionSignature};
use hir_expand::name::Name;
use intern::{Symbol, sym};
use rustc_type_ir::inherent::{AdtDef, IntoKind, SliceLike, Ty as _};
use stdx::never;
use crate::next_solver::mapping::NextSolverToChalk;
use crate::{
display::DisplayTarget,
drop::{DropGlue, has_drop_glue},
error_lifetime,
mir::eval::{
Address, AdtId, Arc, BuiltinType, Evaluator, FunctionId, HasModule, HirDisplay,
InternedClosure, Interner, Interval, IntervalAndTy, IntervalOrOwned, ItemContainerId,
LangItem, Layout, Locals, Lookup, MirEvalError, MirSpan, Mutability, Result, Substitution,
Ty, TyBuilder, TyExt, pad16,
},
next_solver::{
DbInterner,
mapping::{ChalkToNextSolver, convert_ty_for_result},
Address, AdtId, Arc, Evaluator, FunctionId, GenericArgs, HasModule, HirDisplay,
InternedClosure, Interval, IntervalAndTy, IntervalOrOwned, ItemContainerId, LangItem,
Layout, Locals, Lookup, MirEvalError, MirSpan, Mutability, Result, Ty, TyKind, pad16,
},
next_solver::Region,
};
mod simd;
@ -53,9 +42,9 @@ impl<'db> Evaluator<'db> {
pub(super) fn detect_and_exec_special_function(
&mut self,
def: FunctionId,
args: &[IntervalAndTy],
generic_args: &Substitution,
locals: &Locals,
args: &[IntervalAndTy<'db>],
generic_args: GenericArgs<'db>,
locals: &Locals<'db>,
destination: Interval,
span: MirSpan,
) -> Result<'db, bool> {
@ -118,18 +107,16 @@ impl<'db> Evaluator<'db> {
if let ItemContainerId::TraitId(t) = def.lookup(self.db).container
&& self.db.lang_attr(t.into()) == Some(LangItem::Clone)
{
let [self_ty] = generic_args.as_slice(Interner) else {
let [self_ty] = generic_args.as_slice() else {
not_supported!("wrong generic arg count for clone");
};
let Some(self_ty) = self_ty.ty(Interner) else {
let Some(self_ty) = self_ty.ty() else {
not_supported!("wrong generic arg kind for clone");
};
// Clone has special impls for tuples and function pointers
if matches!(
self_ty.kind(Interner),
TyKind::Function(_) | TyKind::Tuple(..) | TyKind::Closure(..)
) {
self.exec_clone(def, args, self_ty.clone(), locals, destination, span)?;
if matches!(self_ty.kind(), TyKind::FnPtr(..) | TyKind::Tuple(..) | TyKind::Closure(..))
{
self.exec_clone(def, args, self_ty, locals, destination, span)?;
return Ok(true);
}
// Return early to prevent caching clone as non special fn.
@ -161,15 +148,14 @@ impl<'db> Evaluator<'db> {
fn exec_clone(
&mut self,
def: FunctionId,
args: &[IntervalAndTy],
self_ty: Ty,
locals: &Locals,
args: &[IntervalAndTy<'db>],
self_ty: Ty<'db>,
locals: &Locals<'db>,
destination: Interval,
span: MirSpan,
) -> Result<'db, ()> {
let interner = self.interner;
match self_ty.kind(Interner) {
TyKind::Function(_) => {
match self_ty.kind() {
TyKind::FnPtr(..) => {
let [arg] = args else {
not_supported!("wrong arg count for clone");
};
@ -182,30 +168,35 @@ impl<'db> Evaluator<'db> {
not_supported!("wrong arg count for clone");
};
let addr = Address::from_bytes(arg.get(self)?)?;
let InternedClosure(closure_owner, _) = self.db.lookup_intern_closure((*id).into());
let InternedClosure(closure_owner, _) = self.db.lookup_intern_closure(id.0);
let infer = self.db.infer(closure_owner);
let (captures, _) = infer.closure_info((*id).into());
let layout = self.layout(self_ty.to_nextsolver(interner))?;
let (captures, _) = infer.closure_info(id.0);
let layout = self.layout(self_ty)?;
let db = self.db;
let ty_iter = captures
.iter()
.map(|c| c.ty(db, subst.to_nextsolver(interner)).to_chalk(interner));
let ty_iter = captures.iter().map(|c| c.ty(db, subst));
self.exec_clone_for_fields(ty_iter, layout, addr, def, locals, destination, span)?;
}
TyKind::Tuple(_, subst) => {
TyKind::Tuple(subst) => {
let [arg] = args else {
not_supported!("wrong arg count for clone");
};
let addr = Address::from_bytes(arg.get(self)?)?;
let layout = self.layout(self_ty.to_nextsolver(interner))?;
let ty_iter = subst.iter(Interner).map(|ga| ga.assert_ty_ref(Interner).clone());
self.exec_clone_for_fields(ty_iter, layout, addr, def, locals, destination, span)?;
let layout = self.layout(self_ty)?;
self.exec_clone_for_fields(
subst.iter(),
layout,
addr,
def,
locals,
destination,
span,
)?;
}
_ => {
self.exec_fn_with_args(
def,
args,
Substitution::from1(Interner, self_ty),
GenericArgs::new_from_iter(self.interner(), [self_ty.into()]),
locals,
destination,
None,
@ -218,21 +209,25 @@ impl<'db> Evaluator<'db> {
fn exec_clone_for_fields(
&mut self,
ty_iter: impl Iterator<Item = Ty>,
ty_iter: impl Iterator<Item = Ty<'db>>,
layout: Arc<Layout>,
addr: Address,
def: FunctionId,
locals: &Locals,
locals: &Locals<'db>,
destination: Interval,
span: MirSpan,
) -> Result<'db, ()> {
let interner = DbInterner::new_with(self.db, None, None);
for (i, ty) in ty_iter.enumerate() {
let size = self.layout(ty.to_nextsolver(interner))?.size.bytes_usize();
let size = self.layout(ty)?.size.bytes_usize();
let tmp = self.heap_allocate(self.ptr_size(), self.ptr_size())?;
let arg = IntervalAndTy {
interval: Interval { addr: tmp, size: self.ptr_size() },
ty: TyKind::Ref(Mutability::Not, error_lifetime(), ty.clone()).intern(Interner),
ty: Ty::new_ref(
self.interner(),
Region::error(self.interner()),
ty,
Mutability::Not,
),
};
let offset = layout.fields.offset(i).bytes_usize();
self.write_memory(tmp, &addr.offset(offset).to_bytes())?;
@ -251,7 +246,7 @@ impl<'db> Evaluator<'db> {
fn exec_alloc_fn(
&mut self,
alloc_fn: &Symbol,
args: &[IntervalAndTy],
args: &[IntervalAndTy<'db>],
destination: Interval,
) -> Result<'db, ()> {
match alloc_fn {
@ -313,9 +308,9 @@ impl<'db> Evaluator<'db> {
fn exec_lang_item(
&mut self,
it: LangItem,
generic_args: &Substitution,
args: &[IntervalAndTy],
locals: &Locals,
generic_args: GenericArgs<'db>,
args: &[IntervalAndTy<'db>],
locals: &Locals<'db>,
span: MirSpan,
) -> Result<'db, Vec<u8>> {
use LangItem::*;
@ -328,7 +323,7 @@ impl<'db> Evaluator<'db> {
"argument of BeginPanic is not provided".into(),
))?
.clone();
while let TyKind::Ref(_, _, ty) = arg.ty.kind(Interner) {
while let TyKind::Ref(_, ty, _) = arg.ty.kind() {
if ty.is_str() {
let (pointee, metadata) = arg.interval.get(self)?.split_at(self.ptr_size());
let len = from_bytes!(usize, metadata);
@ -347,13 +342,10 @@ impl<'db> Evaluator<'db> {
let pointee = arg.interval.get(self)?;
arg = IntervalAndTy {
interval: Interval::new(Address::from_bytes(pointee)?, size),
ty: ty.clone(),
ty,
};
}
Err(MirEvalError::Panic(format!(
"unknown-panic-payload: {:?}",
arg.ty.kind(Interner)
)))
Err(MirEvalError::Panic(format!("unknown-panic-payload: {:?}", arg.ty.kind())))
}
SliceLen => {
let arg = args.next().ok_or(MirEvalError::InternalError(
@ -364,18 +356,17 @@ impl<'db> Evaluator<'db> {
Ok(arg[ptr_size..].into())
}
DropInPlace => {
let ty =
generic_args.as_slice(Interner).first().and_then(|it| it.ty(Interner)).ok_or(
MirEvalError::InternalError(
"generic argument of drop_in_place is not provided".into(),
),
)?;
let ty = generic_args.as_slice().first().and_then(|it| it.ty()).ok_or(
MirEvalError::InternalError(
"generic argument of drop_in_place is not provided".into(),
),
)?;
let arg = args.next().ok_or(MirEvalError::InternalError(
"argument of drop_in_place is not provided".into(),
))?;
let arg = arg.interval.get(self)?.to_owned();
self.run_drop_glue_deep(
ty.clone(),
ty,
locals,
Address::from_bytes(&arg[0..self.ptr_size()])?,
&arg[self.ptr_size()..],
@ -390,9 +381,9 @@ impl<'db> Evaluator<'db> {
fn exec_syscall(
&mut self,
id: i64,
args: &[IntervalAndTy],
args: &[IntervalAndTy<'db>],
destination: Interval,
_locals: &Locals,
_locals: &Locals<'db>,
_span: MirSpan,
) -> Result<'db, ()> {
match id {
@ -420,10 +411,10 @@ impl<'db> Evaluator<'db> {
fn exec_extern_c(
&mut self,
as_str: &str,
args: &[IntervalAndTy],
_generic_args: &Substitution,
args: &[IntervalAndTy<'db>],
_generic_args: GenericArgs<'db>,
destination: Interval,
locals: &Locals,
locals: &Locals<'db>,
span: MirSpan,
) -> Result<'db, ()> {
match as_str {
@ -586,14 +577,13 @@ impl<'db> Evaluator<'db> {
fn exec_intrinsic(
&mut self,
name: &str,
args: &[IntervalAndTy],
generic_args: &Substitution,
args: &[IntervalAndTy<'db>],
generic_args: GenericArgs<'db>,
destination: Interval,
locals: &Locals,
locals: &Locals<'db>,
span: MirSpan,
needs_override: bool,
) -> Result<'db, bool> {
let interner = DbInterner::new_with(self.db, None, None);
if let Some(name) = name.strip_prefix("atomic_") {
return self
.exec_atomic_intrinsic(name, args, generic_args, destination, locals, span)
@ -751,9 +741,7 @@ impl<'db> Evaluator<'db> {
}
match name {
"size_of" => {
let Some(ty) =
generic_args.as_slice(Interner).first().and_then(|it| it.ty(Interner))
else {
let Some(ty) = generic_args.as_slice().first().and_then(|it| it.ty()) else {
return Err(MirEvalError::InternalError(
"size_of generic arg is not provided".into(),
));
@ -764,20 +752,16 @@ impl<'db> Evaluator<'db> {
// FIXME: `min_align_of` was renamed to `align_of` in Rust 1.89
// (https://github.com/rust-lang/rust/pull/142410)
"min_align_of" | "align_of" => {
let Some(ty) =
generic_args.as_slice(Interner).first().and_then(|it| it.ty(Interner))
else {
let Some(ty) = generic_args.as_slice().first().and_then(|it| it.ty()) else {
return Err(MirEvalError::InternalError(
"align_of generic arg is not provided".into(),
));
};
let align = self.layout(ty.to_nextsolver(interner))?.align.bytes();
let align = self.layout(ty)?.align.bytes();
destination.write_from_bytes(self, &align.to_le_bytes()[0..destination.size])
}
"size_of_val" => {
let Some(ty) =
generic_args.as_slice(Interner).first().and_then(|it| it.ty(Interner))
else {
let Some(ty) = generic_args.as_slice().first().and_then(|it| it.ty()) else {
return Err(MirEvalError::InternalError(
"size_of_val generic arg is not provided".into(),
));
@ -798,9 +782,7 @@ impl<'db> Evaluator<'db> {
// FIXME: `min_align_of_val` was renamed to `align_of_val` in Rust 1.89
// (https://github.com/rust-lang/rust/pull/142410)
"min_align_of_val" | "align_of_val" => {
let Some(ty) =
generic_args.as_slice(Interner).first().and_then(|it| it.ty(Interner))
else {
let Some(ty) = generic_args.as_slice().first().and_then(|it| it.ty()) else {
return Err(MirEvalError::InternalError(
"align_of_val generic arg is not provided".into(),
));
@ -819,9 +801,7 @@ impl<'db> Evaluator<'db> {
}
}
"type_name" => {
let Some(ty) =
generic_args.as_slice(Interner).first().and_then(|it| it.ty(Interner))
else {
let Some(ty) = generic_args.as_slice().first().and_then(|it| it.ty()) else {
return Err(MirEvalError::InternalError(
"type_name generic arg is not provided".into(),
));
@ -848,18 +828,12 @@ impl<'db> Evaluator<'db> {
.write_from_bytes(self, &len.to_le_bytes())
}
"needs_drop" => {
let Some(ty) =
generic_args.as_slice(Interner).first().and_then(|it| it.ty(Interner))
else {
let Some(ty) = generic_args.as_slice().first().and_then(|it| it.ty()) else {
return Err(MirEvalError::InternalError(
"size_of generic arg is not provided".into(),
));
};
let result = match has_drop_glue(
&self.infcx,
ty.to_nextsolver(self.interner),
self.trait_env.clone(),
) {
let result = match has_drop_glue(&self.infcx, ty, self.trait_env.clone()) {
DropGlue::HasDropGlue => true,
DropGlue::None => false,
DropGlue::DependOnParams => {
@ -922,9 +896,7 @@ impl<'db> Evaluator<'db> {
let lhs = i128::from_le_bytes(pad16(lhs.get(self)?, false));
let rhs = i128::from_le_bytes(pad16(rhs.get(self)?, false));
let ans = lhs.wrapping_sub(rhs);
let Some(ty) =
generic_args.as_slice(Interner).first().and_then(|it| it.ty(Interner))
else {
let Some(ty) = generic_args.as_slice().first().and_then(|it| it.ty()) else {
return Err(MirEvalError::InternalError(
"ptr_offset_from generic arg is not provided".into(),
));
@ -1013,13 +985,11 @@ impl<'db> Evaluator<'db> {
"const_eval_select args are not provided".into(),
));
};
let result_ty = TyKind::Tuple(
2,
Substitution::from_iter(Interner, [lhs.ty.clone(), TyBuilder::bool()]),
)
.intern(Interner);
let op_size =
self.size_of_sized(&lhs.ty, locals, "operand of add_with_overflow")?;
let result_ty = Ty::new_tup_from_iter(
self.interner(),
[lhs.ty, Ty::new_bool(self.interner())].into_iter(),
);
let op_size = self.size_of_sized(lhs.ty, locals, "operand of add_with_overflow")?;
let lhs = u128::from_le_bytes(pad16(lhs.get(self)?, false));
let rhs = u128::from_le_bytes(pad16(rhs.get(self)?, false));
let (ans, u128overflow) = match name {
@ -1031,7 +1001,7 @@ impl<'db> Evaluator<'db> {
let is_overflow = u128overflow
|| ans.to_le_bytes()[op_size..].iter().any(|&it| it != 0 && it != 255);
let is_overflow = vec![u8::from(is_overflow)];
let layout = self.layout(result_ty.to_nextsolver(interner))?;
let layout = self.layout(result_ty)?;
let result = self.construct_with_layout(
layout.size.bytes_usize(),
&layout,
@ -1048,9 +1018,7 @@ impl<'db> Evaluator<'db> {
"copy_nonoverlapping args are not provided".into(),
));
};
let Some(ty) =
generic_args.as_slice(Interner).first().and_then(|it| it.ty(Interner))
else {
let Some(ty) = generic_args.as_slice().first().and_then(|it| it.ty()) else {
return Err(MirEvalError::InternalError(
"copy_nonoverlapping generic arg is not provided".into(),
));
@ -1069,43 +1037,35 @@ impl<'db> Evaluator<'db> {
return Err(MirEvalError::InternalError("offset args are not provided".into()));
};
let ty = if name == "offset" {
let Some(ty0) =
generic_args.as_slice(Interner).first().and_then(|it| it.ty(Interner))
else {
let Some(ty0) = generic_args.as_slice().first().and_then(|it| it.ty()) else {
return Err(MirEvalError::InternalError(
"offset generic arg is not provided".into(),
));
};
let Some(ty1) =
generic_args.as_slice(Interner).get(1).and_then(|it| it.ty(Interner))
else {
let Some(ty1) = generic_args.as_slice().get(1).and_then(|it| it.ty()) else {
return Err(MirEvalError::InternalError(
"offset generic arg is not provided".into(),
));
};
if !matches!(
ty1.as_builtin(),
Some(
BuiltinType::Int(BuiltinInt::Isize)
| BuiltinType::Uint(BuiltinUint::Usize)
)
ty1.kind(),
TyKind::Int(rustc_type_ir::IntTy::Isize)
| TyKind::Uint(rustc_type_ir::UintTy::Usize)
) {
return Err(MirEvalError::InternalError(
"offset generic arg is not usize or isize".into(),
));
}
match ty0.as_raw_ptr() {
Some((ty, _)) => ty,
None => {
match ty0.kind() {
TyKind::RawPtr(ty, _) => ty,
_ => {
return Err(MirEvalError::InternalError(
"offset generic arg is not a raw pointer".into(),
));
}
}
} else {
let Some(ty) =
generic_args.as_slice(Interner).first().and_then(|it| it.ty(Interner))
else {
let Some(ty) = generic_args.as_slice().first().and_then(|it| it.ty()) else {
return Err(MirEvalError::InternalError(
"arith_offset generic arg is not provided".into(),
));
@ -1230,9 +1190,7 @@ impl<'db> Evaluator<'db> {
"discriminant_value arg is not provided".into(),
));
};
let Some(ty) =
generic_args.as_slice(Interner).first().and_then(|it| it.ty(Interner))
else {
let Some(ty) = generic_args.as_slice().first().and_then(|it| it.ty()) else {
return Err(MirEvalError::InternalError(
"discriminant_value generic arg is not provided".into(),
));
@ -1240,7 +1198,7 @@ impl<'db> Evaluator<'db> {
let addr = Address::from_bytes(arg.get(self)?)?;
let size = self.size_of_sized(ty, locals, "discriminant_value ptr type")?;
let interval = Interval { addr, size };
let r = self.compute_discriminant(ty.clone(), interval.get(self)?)?;
let r = self.compute_discriminant(ty, interval.get(self)?)?;
destination.write_from_bytes(self, &r.to_le_bytes()[0..destination.size])
}
"const_eval_select" => {
@ -1250,14 +1208,13 @@ impl<'db> Evaluator<'db> {
));
};
let mut args = vec![const_fn.clone()];
let TyKind::Tuple(_, fields) = tuple.ty.kind(Interner) else {
let TyKind::Tuple(fields) = tuple.ty.kind() else {
return Err(MirEvalError::InternalError(
"const_eval_select arg[0] is not a tuple".into(),
));
};
let layout = self.layout(tuple.ty.to_nextsolver(interner))?;
for (i, field) in fields.iter(Interner).enumerate() {
let field = field.assert_ty_ref(Interner).clone();
let layout = self.layout(tuple.ty)?;
for (i, field) in fields.iter().enumerate() {
let offset = layout.fields.offset(i).bytes_usize();
let addr = tuple.interval.addr.offset(offset);
args.push(IntervalAndTy::new(addr, field, self, locals)?);
@ -1271,7 +1228,7 @@ impl<'db> Evaluator<'db> {
def,
&args,
// FIXME: wrong for manual impls of `FnOnce`
Substitution::empty(Interner),
GenericArgs::new_from_iter(self.interner(), []),
locals,
destination,
None,
@ -1297,9 +1254,7 @@ impl<'db> Evaluator<'db> {
));
};
let dst = Address::from_bytes(ptr.get(self)?)?;
let Some(ty) =
generic_args.as_slice(Interner).first().and_then(|it| it.ty(Interner))
else {
let Some(ty) = generic_args.as_slice().first().and_then(|it| it.ty()) else {
return Err(MirEvalError::InternalError(
"write_via_copy generic arg is not provided".into(),
));
@ -1316,9 +1271,7 @@ impl<'db> Evaluator<'db> {
};
let count = from_bytes!(usize, count.get(self)?);
let val = from_bytes!(u8, val.get(self)?);
let Some(ty) =
generic_args.as_slice(Interner).first().and_then(|it| it.ty(Interner))
else {
let Some(ty) = generic_args.as_slice().first().and_then(|it| it.ty()) else {
return Err(MirEvalError::InternalError(
"write_bytes generic arg is not provided".into(),
));
@ -1346,16 +1299,14 @@ impl<'db> Evaluator<'db> {
"three_way_compare args are not provided".into(),
));
};
let Some(ty) =
generic_args.as_slice(Interner).first().and_then(|it| it.ty(Interner))
else {
let Some(ty) = generic_args.as_slice().first().and_then(|it| it.ty()) else {
return Err(MirEvalError::InternalError(
"three_way_compare generic arg is not provided".into(),
));
};
let signed = match ty.as_builtin().unwrap() {
BuiltinType::Int(_) => true,
BuiltinType::Uint(_) => false,
let signed = match ty.kind() {
TyKind::Int(_) => true,
TyKind::Uint(_) => false,
_ => {
return Err(MirEvalError::InternalError(
"three_way_compare expects an integral type".into(),
@ -1379,8 +1330,8 @@ impl<'db> Evaluator<'db> {
result = (l as i8).cmp(&(r as i8));
}
if let Some(e) = LangItem::Ordering.resolve_enum(self.db, self.crate_id) {
let ty = self.db.ty(e.into()).skip_binder().to_chalk(interner);
let r = self.compute_discriminant(ty.clone(), &[result as i8 as u8])?;
let ty = self.db.ty(e.into()).skip_binder();
let r = self.compute_discriminant(ty, &[result as i8 as u8])?;
destination.write_from_bytes(self, &r.to_le_bytes()[0..destination.size])?;
Ok(())
} else {
@ -1409,38 +1360,37 @@ impl<'db> Evaluator<'db> {
fn size_align_of_unsized(
&mut self,
ty: &Ty,
ty: Ty<'db>,
metadata: Interval,
locals: &Locals,
locals: &Locals<'db>,
) -> Result<'db, (usize, usize)> {
let interner = DbInterner::new_with(self.db, None, None);
Ok(match ty.kind(Interner) {
Ok(match ty.kind() {
TyKind::Str => (from_bytes!(usize, metadata.get(self)?), 1),
TyKind::Slice(inner) => {
let len = from_bytes!(usize, metadata.get(self)?);
let (size, align) = self.size_align_of_sized(inner, locals, "slice inner type")?;
(size * len, align)
}
TyKind::Dyn(_) => self.size_align_of_sized(
&convert_ty_for_result(interner, self.vtable_map.ty_of_bytes(metadata.get(self)?)?),
TyKind::Dynamic(..) => self.size_align_of_sized(
self.vtable_map.ty_of_bytes(metadata.get(self)?)?,
locals,
"dyn concrete type",
)?,
TyKind::Adt(id, subst) => {
let id = id.0;
let layout = self.layout_adt(id, subst.clone())?;
TyKind::Adt(adt_def, subst) => {
let id = adt_def.def_id().0;
let layout = self.layout_adt(id, subst)?;
let id = match id {
AdtId::StructId(s) => s,
_ => not_supported!("unsized enum or union"),
};
let field_types = &self.db.field_types(id.into());
let field_types = self.db.field_types_ns(id.into());
let last_field_ty =
field_types.iter().next_back().unwrap().1.clone().substitute(Interner, subst);
field_types.iter().next_back().unwrap().1.instantiate(self.interner(), subst);
let sized_part_size =
layout.fields.offset(field_types.iter().count() - 1).bytes_usize();
let sized_part_align = layout.align.bytes() as usize;
let (unsized_part_size, unsized_part_align) =
self.size_align_of_unsized(&last_field_ty, metadata, locals)?;
self.size_align_of_unsized(last_field_ty, metadata, locals)?;
let align = sized_part_align.max(unsized_part_align) as isize;
let size = (sized_part_size + unsized_part_size) as isize;
// Must add any necessary padding to `size`
@ -1463,13 +1413,12 @@ impl<'db> Evaluator<'db> {
fn exec_atomic_intrinsic(
&mut self,
name: &str,
args: &[IntervalAndTy],
generic_args: &Substitution,
args: &[IntervalAndTy<'db>],
generic_args: GenericArgs<'db>,
destination: Interval,
locals: &Locals,
locals: &Locals<'db>,
_span: MirSpan,
) -> Result<'db, ()> {
let interner = DbInterner::new_with(self.db, None, None);
// We are a single threaded runtime with no UB checking and no optimization, so
// we can implement atomic intrinsics as normal functions.
@ -1479,8 +1428,7 @@ impl<'db> Evaluator<'db> {
// The rest of atomic intrinsics have exactly one generic arg
let Some(ty) = generic_args.as_slice(Interner).first().and_then(|it| it.ty(Interner))
else {
let Some(ty) = generic_args.as_slice().first().and_then(|it| it.ty()) else {
return Err(MirEvalError::InternalError(
"atomic intrinsic generic arg is not provided".into(),
));
@ -1562,12 +1510,11 @@ impl<'db> Evaluator<'db> {
} else {
(arg0_interval, false)
};
let result_ty = TyKind::Tuple(
2,
Substitution::from_iter(Interner, [ty.clone(), TyBuilder::bool()]),
)
.intern(Interner);
let layout = self.layout(result_ty.to_nextsolver(interner))?;
let result_ty = Ty::new_tup_from_iter(
self.interner(),
[ty, Ty::new_bool(self.interner())].into_iter(),
);
let layout = self.layout(result_ty)?;
let result = self.construct_with_layout(
layout.size.bytes_usize(),
&layout,

View file

@ -2,7 +2,6 @@
use std::cmp::Ordering;
use crate::TyKind;
use crate::consteval::try_const_usize;
use super::*;
@ -23,22 +22,20 @@ macro_rules! not_supported {
}
impl<'db> Evaluator<'db> {
fn detect_simd_ty(&self, ty: &Ty) -> Result<'db, (usize, Ty)> {
match ty.kind(Interner) {
TyKind::Adt(id, subst) => {
let len = match subst.as_slice(Interner).get(1).and_then(|it| it.constant(Interner))
{
fn detect_simd_ty(&self, ty: Ty<'db>) -> Result<'db, (usize, Ty<'db>)> {
match ty.kind() {
TyKind::Adt(adt_def, subst) => {
let len = match subst.as_slice().get(1).and_then(|it| it.konst()) {
Some(len) => len,
_ => {
if let AdtId::StructId(id) = id.0 {
if let AdtId::StructId(id) = adt_def.def_id().0 {
let struct_data = id.fields(self.db);
let fields = struct_data.fields();
let Some((first_field, _)) = fields.iter().next() else {
not_supported!("simd type with no field");
};
let field_ty = self.db.field_types(id.into())[first_field]
.clone()
.substitute(Interner, subst);
let field_ty = self.db.field_types_ns(id.into())[first_field]
.instantiate(self.interner(), subst);
return Ok((fields.len(), field_ty));
}
return Err(MirEvalError::InternalError(
@ -48,14 +45,12 @@ impl<'db> Evaluator<'db> {
};
match try_const_usize(self.db, len) {
Some(len) => {
let Some(ty) =
subst.as_slice(Interner).first().and_then(|it| it.ty(Interner))
else {
let Some(ty) = subst.as_slice().first().and_then(|it| it.ty()) else {
return Err(MirEvalError::InternalError(
"simd type with no ty param".into(),
));
};
Ok((len as usize, ty.clone()))
Ok((len as usize, ty))
}
None => Err(MirEvalError::InternalError(
"simd type with unevaluatable len param".into(),
@ -69,10 +64,10 @@ impl<'db> Evaluator<'db> {
pub(super) fn exec_simd_intrinsic(
&mut self,
name: &str,
args: &[IntervalAndTy],
_generic_args: &Substitution,
args: &[IntervalAndTy<'db>],
_generic_args: GenericArgs<'db>,
destination: Interval,
_locals: &Locals,
_locals: &Locals<'db>,
_span: MirSpan,
) -> Result<'db, ()> {
match name {
@ -99,8 +94,8 @@ impl<'db> Evaluator<'db> {
let [left, right] = args else {
return Err(MirEvalError::InternalError("simd args are not provided".into()));
};
let (len, ty) = self.detect_simd_ty(&left.ty)?;
let is_signed = matches!(ty.as_builtin(), Some(BuiltinType::Int(_)));
let (len, ty) = self.detect_simd_ty(left.ty)?;
let is_signed = matches!(ty.kind(), TyKind::Int(_));
let size = left.interval.size / len;
let dest_size = destination.size / len;
let mut destination_bytes = vec![];
@ -137,7 +132,7 @@ impl<'db> Evaluator<'db> {
"simd_bitmask args are not provided".into(),
));
};
let (op_len, _) = self.detect_simd_ty(&op.ty)?;
let (op_len, _) = self.detect_simd_ty(op.ty)?;
let op_count = op.interval.size / op_len;
let mut result: u64 = 0;
for (i, val) in op.get(self)?.chunks(op_count).enumerate() {
@ -153,7 +148,7 @@ impl<'db> Evaluator<'db> {
"simd_shuffle args are not provided".into(),
));
};
let TyKind::Array(_, index_len) = index.ty.kind(Interner) else {
let TyKind::Array(_, index_len) = index.ty.kind() else {
return Err(MirEvalError::InternalError(
"simd_shuffle index argument has non-array type".into(),
));
@ -166,7 +161,7 @@ impl<'db> Evaluator<'db> {
));
}
};
let (left_len, _) = self.detect_simd_ty(&left.ty)?;
let (left_len, _) = self.detect_simd_ty(left.ty)?;
let left_size = left.interval.size / left_len;
let vector =
left.get(self)?.chunks(left_size).chain(right.get(self)?.chunks(left_size));

View file

@ -4,15 +4,20 @@ use span::Edition;
use syntax::{TextRange, TextSize};
use test_fixture::WithFixture;
use crate::display::DisplayTarget;
use crate::{
Interner, Substitution, db::HirDatabase, mir::MirLowerError, setup_tracing, test_db::TestDB,
db::HirDatabase,
display::DisplayTarget,
mir::MirLowerError,
next_solver::{DbInterner, GenericArgs},
setup_tracing,
test_db::TestDB,
};
use super::{MirEvalError, interpret_mir};
fn eval_main(db: &TestDB, file_id: EditionedFileId) -> Result<(String, String), MirEvalError<'_>> {
crate::attach_db(db, || {
let interner = DbInterner::new_with(db, None, None);
let module_id = db.module_for_file(file_id.file_id(db));
let def_map = module_id.def_map(db);
let scope = &def_map[module_id.local_id].scope;
@ -34,7 +39,7 @@ fn eval_main(db: &TestDB, file_id: EditionedFileId) -> Result<(String, String),
let body = db
.monomorphized_mir_body(
func_id.into(),
Substitution::empty(Interner),
GenericArgs::new_from_iter(interner, []),
db.trait_environment(func_id.into()),
)
.map_err(|e| MirEvalError::MirLowerError(func_id, e))?;
@ -631,11 +636,16 @@ fn main() {
);
}
#[ignore = "
FIXME(next-solver):
This does not work currently because I replaced homemade selection with selection by the trait solver;
This will work once we implement `Interner::impl_specializes()` properly.
"]
#[test]
fn specialization_array_clone() {
check_pass(
r#"
//- minicore: copy, derive, slice, index, coerce_unsized
//- minicore: copy, derive, slice, index, coerce_unsized, panic
impl<T: Clone, const N: usize> Clone for [T; N] {
#[inline]
fn clone(&self) -> Self {
@ -650,8 +660,7 @@ trait SpecArrayClone: Clone {
impl<T: Clone> SpecArrayClone for T {
#[inline]
default fn clone<const N: usize>(array: &[T; N]) -> [T; N] {
// FIXME: panic here when we actually implement specialization.
from_slice(array)
panic!("should go to the specialized impl")
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,10 +1,14 @@
//! MIR lowering for places
use crate::mir::{MutBorrowKind, Operand, OperandKind};
use super::*;
use hir_def::FunctionId;
use intern::sym;
use rustc_type_ir::inherent::{AdtDef, Region as _, Ty as _};
use super::*;
use crate::{
mir::{MutBorrowKind, Operand, OperandKind},
next_solver::Region,
};
macro_rules! not_supported {
($it: expr) => {
@ -16,8 +20,8 @@ impl<'db> MirLowerCtx<'_, 'db> {
fn lower_expr_to_some_place_without_adjust(
&mut self,
expr_id: ExprId,
prev_block: BasicBlockId,
) -> Result<'db, Option<(Place, BasicBlockId)>> {
prev_block: BasicBlockId<'db>,
) -> Result<'db, Option<(Place<'db>, BasicBlockId<'db>)>> {
let ty = self.expr_ty_without_adjust(expr_id);
let place = self.temp(ty, prev_block, expr_id.into())?;
let Some(current) =
@ -31,12 +35,12 @@ impl<'db> MirLowerCtx<'_, 'db> {
fn lower_expr_to_some_place_with_adjust(
&mut self,
expr_id: ExprId,
prev_block: BasicBlockId,
prev_block: BasicBlockId<'db>,
adjustments: &[Adjustment<'db>],
) -> Result<'db, Option<(Place, BasicBlockId)>> {
) -> Result<'db, Option<(Place<'db>, BasicBlockId<'db>)>> {
let ty = adjustments
.last()
.map(|it| it.target.to_chalk(self.interner))
.map(|it| it.target)
.unwrap_or_else(|| self.expr_ty_without_adjust(expr_id));
let place = self.temp(ty, prev_block, expr_id.into())?;
let Some(current) =
@ -49,11 +53,11 @@ impl<'db> MirLowerCtx<'_, 'db> {
pub(super) fn lower_expr_as_place_with_adjust(
&mut self,
current: BasicBlockId,
current: BasicBlockId<'db>,
expr_id: ExprId,
upgrade_rvalue: bool,
adjustments: &[Adjustment<'db>],
) -> Result<'db, Option<(Place, BasicBlockId)>> {
) -> Result<'db, Option<(Place<'db>, BasicBlockId<'db>)>> {
let try_rvalue = |this: &mut MirLowerCtx<'_, 'db>| {
if !upgrade_rvalue {
return Err(MirLowerError::MutatingRvalue);
@ -89,11 +93,11 @@ impl<'db> MirLowerCtx<'_, 'db> {
current,
r,
rest.last()
.map(|it| it.target.to_chalk(self.interner))
.map(|it| it.target)
.unwrap_or_else(|| self.expr_ty_without_adjust(expr_id)),
last.target.to_chalk(self.interner),
last.target,
expr_id.into(),
match od.0.to_chalk(self.interner) {
match od.0 {
Some(Mutability::Mut) => true,
Some(Mutability::Not) => false,
None => {
@ -111,10 +115,10 @@ impl<'db> MirLowerCtx<'_, 'db> {
pub(super) fn lower_expr_as_place(
&mut self,
current: BasicBlockId,
current: BasicBlockId<'db>,
expr_id: ExprId,
upgrade_rvalue: bool,
) -> Result<'db, Option<(Place, BasicBlockId)>> {
) -> Result<'db, Option<(Place<'db>, BasicBlockId<'db>)>> {
match self.infer.expr_adjustments.get(&expr_id) {
Some(a) => self.lower_expr_as_place_with_adjust(current, expr_id, upgrade_rvalue, a),
None => self.lower_expr_as_place_without_adjust(current, expr_id, upgrade_rvalue),
@ -123,10 +127,10 @@ impl<'db> MirLowerCtx<'_, 'db> {
pub(super) fn lower_expr_as_place_without_adjust(
&mut self,
current: BasicBlockId,
current: BasicBlockId<'db>,
expr_id: ExprId,
upgrade_rvalue: bool,
) -> Result<'db, Option<(Place, BasicBlockId)>> {
) -> Result<'db, Option<(Place<'db>, BasicBlockId<'db>)>> {
let try_rvalue = |this: &mut MirLowerCtx<'_, 'db>| {
if !upgrade_rvalue {
return Err(MirLowerError::MutatingRvalue);
@ -149,9 +153,13 @@ impl<'db> MirLowerCtx<'_, 'db> {
}
ValueNs::StaticId(s) => {
let ty = self.expr_ty_without_adjust(expr_id);
let ref_ty =
TyKind::Ref(Mutability::Not, static_lifetime(), ty).intern(Interner);
let temp: Place = self.temp(ref_ty, current, expr_id.into())?.into();
let ref_ty = Ty::new_ref(
self.interner(),
Region::new_static(self.interner()),
ty,
Mutability::Not,
);
let temp: Place<'db> = self.temp(ref_ty, current, expr_id.into())?.into();
self.push_assignment(
current,
temp,
@ -167,10 +175,10 @@ impl<'db> MirLowerCtx<'_, 'db> {
}
}
Expr::UnaryOp { expr, op: hir_def::hir::UnaryOp::Deref } => {
let is_builtin = match self.expr_ty_without_adjust(*expr).kind(Interner) {
TyKind::Ref(..) | TyKind::Raw(..) => true,
let is_builtin = match self.expr_ty_without_adjust(*expr).kind() {
TyKind::Ref(..) | TyKind::RawPtr(..) => true,
TyKind::Adt(id, _) => {
if let Some(lang_item) = self.db.lang_attr(id.0.into()) {
if let Some(lang_item) = self.db.lang_attr(id.def_id().0.into()) {
lang_item == LangItem::OwnedBox
} else {
false
@ -219,9 +227,9 @@ impl<'db> MirLowerCtx<'_, 'db> {
Expr::Index { base, index } => {
let base_ty = self.expr_ty_after_adjustments(*base);
let index_ty = self.expr_ty_after_adjustments(*index);
if index_ty != TyBuilder::usize()
if !matches!(index_ty.kind(), TyKind::Uint(rustc_ast_ir::UintTy::Usize))
|| !matches!(
base_ty.strip_reference().kind(Interner),
base_ty.strip_reference().kind(),
TyKind::Array(..) | TyKind::Slice(..)
)
{
@ -230,7 +238,6 @@ impl<'db> MirLowerCtx<'_, 'db> {
"[overloaded index]".to_owned(),
));
};
let index_fn = (index_fn.0, index_fn.1.to_chalk(self.interner));
let Some((base_place, current)) =
self.lower_expr_as_place(current, *base, true)?
else {
@ -279,24 +286,26 @@ impl<'db> MirLowerCtx<'_, 'db> {
fn lower_overloaded_index(
&mut self,
current: BasicBlockId,
place: Place,
base_ty: Ty,
result_ty: Ty,
index_operand: Operand,
current: BasicBlockId<'db>,
place: Place<'db>,
base_ty: Ty<'db>,
result_ty: Ty<'db>,
index_operand: Operand<'db>,
span: MirSpan,
index_fn: (FunctionId, Substitution),
) -> Result<'db, Option<(Place, BasicBlockId)>> {
index_fn: (FunctionId, GenericArgs<'db>),
) -> Result<'db, Option<(Place<'db>, BasicBlockId<'db>)>> {
let mutability = match base_ty.as_reference() {
Some((_, _, mutability)) => mutability,
None => Mutability::Not,
};
let result_ref = TyKind::Ref(mutability, error_lifetime(), result_ty).intern(Interner);
let mut result: Place = self.temp(result_ref, current, span)?.into();
let index_fn_op = Operand::const_zst(
TyKind::FnDef(CallableDefId::FunctionId(index_fn.0).to_chalk(self.db), index_fn.1)
.intern(Interner),
);
let result_ref =
Ty::new_ref(self.interner(), Region::error(self.interner()), result_ty, mutability);
let mut result: Place<'db> = self.temp(result_ref, current, span)?.into();
let index_fn_op = Operand::const_zst(Ty::new_fn_def(
self.interner(),
CallableDefId::FunctionId(index_fn.0).into(),
index_fn.1,
));
let Some(current) = self.lower_call(
index_fn_op,
Box::new([Operand { kind: OperandKind::Copy(place), span: None }, index_operand]),
@ -314,14 +323,14 @@ impl<'db> MirLowerCtx<'_, 'db> {
fn lower_overloaded_deref(
&mut self,
current: BasicBlockId,
place: Place,
source_ty: Ty,
target_ty: Ty,
current: BasicBlockId<'db>,
place: Place<'db>,
source_ty: Ty<'db>,
target_ty: Ty<'db>,
span: MirSpan,
mutability: bool,
) -> Result<'db, Option<(Place, BasicBlockId)>> {
let (chalk_mut, trait_lang_item, trait_method_name, borrow_kind) = if !mutability {
) -> Result<'db, Option<(Place<'db>, BasicBlockId<'db>)>> {
let (mutability, trait_lang_item, trait_method_name, borrow_kind) = if !mutability {
(
Mutability::Not,
LangItem::Deref,
@ -336,9 +345,10 @@ impl<'db> MirLowerCtx<'_, 'db> {
BorrowKind::Mut { kind: MutBorrowKind::Default },
)
};
let ty_ref = TyKind::Ref(chalk_mut, error_lifetime(), source_ty.clone()).intern(Interner);
let target_ty_ref = TyKind::Ref(chalk_mut, error_lifetime(), target_ty).intern(Interner);
let ref_place: Place = self.temp(ty_ref, current, span)?.into();
let error_region = Region::error(self.interner());
let ty_ref = Ty::new_ref(self.interner(), error_region, source_ty, mutability);
let target_ty_ref = Ty::new_ref(self.interner(), error_region, target_ty, mutability);
let ref_place: Place<'db> = self.temp(ty_ref, current, span)?.into();
self.push_assignment(current, ref_place, Rvalue::Ref(borrow_kind, place), span);
let deref_trait = self
.resolve_lang_item(trait_lang_item)?
@ -348,14 +358,12 @@ impl<'db> MirLowerCtx<'_, 'db> {
.trait_items(self.db)
.method_by_name(&trait_method_name)
.ok_or(MirLowerError::LangItemNotFound(trait_lang_item))?;
let deref_fn_op = Operand::const_zst(
TyKind::FnDef(
CallableDefId::FunctionId(deref_fn).to_chalk(self.db),
Substitution::from1(Interner, source_ty),
)
.intern(Interner),
);
let mut result: Place = self.temp(target_ty_ref, current, span)?.into();
let deref_fn_op = Operand::const_zst(Ty::new_fn_def(
self.interner(),
CallableDefId::FunctionId(deref_fn).into(),
GenericArgs::new_from_iter(self.interner(), [source_ty.into()]),
));
let mut result: Place<'db> = self.temp(target_ty_ref, current, span)?.into();
let Some(current) = self.lower_call(
deref_fn_op,
Box::new([Operand { kind: OperandKind::Copy(ref_place), span: None }]),

View file

@ -1,18 +1,18 @@
//! MIR lowering for patterns
use hir_def::{AssocItemId, hir::ExprId, signatures::VariantFields};
use rustc_type_ir::inherent::{IntoKind, SliceLike, Ty as _};
use crate::next_solver::mapping::NextSolverToChalk;
use crate::next_solver::GenericArgs;
use crate::{
BindingMode,
mir::{
LocalId, MutBorrowKind, Operand, OperandKind,
lower::{
BasicBlockId, BinOp, BindingId, BorrowKind, Either, Expr, FieldId, Idx, Interner,
MemoryMap, MirLowerCtx, MirLowerError, MirSpan, Pat, PatId, Place, PlaceElem,
ProjectionElem, RecordFieldPat, ResolveValueResult, Result, Rvalue, Substitution,
SwitchTargets, TerminatorKind, TupleFieldId, TupleId, TyBuilder, TyKind, ValueNs,
VariantId,
BasicBlockId, BinOp, BindingId, BorrowKind, Either, Expr, FieldId, Idx, MemoryMap,
MirLowerCtx, MirLowerError, MirSpan, Pat, PatId, Place, PlaceElem, ProjectionElem,
RecordFieldPat, ResolveValueResult, Result, Rvalue, SwitchTargets, TerminatorKind,
TupleFieldId, TupleId, Ty, TyKind, ValueNs, VariantId,
},
},
};
@ -63,11 +63,11 @@ impl<'db> MirLowerCtx<'_, 'db> {
/// so it should be an empty block.
pub(super) fn pattern_match(
&mut self,
current: BasicBlockId,
current_else: Option<BasicBlockId>,
cond_place: Place,
current: BasicBlockId<'db>,
current_else: Option<BasicBlockId<'db>>,
cond_place: Place<'db>,
pattern: PatId,
) -> Result<'db, (BasicBlockId, Option<BasicBlockId>)> {
) -> Result<'db, (BasicBlockId<'db>, Option<BasicBlockId<'db>>)> {
let (current, current_else) = self.pattern_match_inner(
current,
current_else,
@ -87,10 +87,10 @@ impl<'db> MirLowerCtx<'_, 'db> {
pub(super) fn pattern_match_assignment(
&mut self,
current: BasicBlockId,
value: Place,
current: BasicBlockId<'db>,
value: Place<'db>,
pattern: PatId,
) -> Result<'db, BasicBlockId> {
) -> Result<'db, BasicBlockId<'db>> {
let (current, _) =
self.pattern_match_inner(current, None, value, pattern, MatchingMode::Assign)?;
Ok(current)
@ -99,9 +99,9 @@ impl<'db> MirLowerCtx<'_, 'db> {
pub(super) fn match_self_param(
&mut self,
id: BindingId,
current: BasicBlockId,
local: LocalId,
) -> Result<'db, (BasicBlockId, Option<BasicBlockId>)> {
current: BasicBlockId<'db>,
local: LocalId<'db>,
) -> Result<'db, (BasicBlockId<'db>, Option<BasicBlockId<'db>>)> {
self.pattern_match_binding(
id,
BindingMode::Move,
@ -114,12 +114,12 @@ impl<'db> MirLowerCtx<'_, 'db> {
fn pattern_match_inner(
&mut self,
mut current: BasicBlockId,
mut current_else: Option<BasicBlockId>,
mut cond_place: Place,
mut current: BasicBlockId<'db>,
mut current_else: Option<BasicBlockId<'db>>,
mut cond_place: Place<'db>,
pattern: PatId,
mode: MatchingMode,
) -> Result<'db, (BasicBlockId, Option<BasicBlockId>)> {
) -> Result<'db, (BasicBlockId<'db>, Option<BasicBlockId<'db>>)> {
let cnt = self.infer.pat_adjustments.get(&pattern).map(|x| x.len()).unwrap_or_default();
cond_place.projection = self.result.projection_store.intern(
cond_place
@ -135,8 +135,8 @@ impl<'db> MirLowerCtx<'_, 'db> {
Pat::Missing => return Err(MirLowerError::IncompletePattern),
Pat::Wild => (current, current_else),
Pat::Tuple { args, ellipsis } => {
let subst = match self.infer[pattern].to_chalk(self.interner).kind(Interner) {
TyKind::Tuple(_, s) => s.clone(),
let subst = match self.infer[pattern].kind() {
TyKind::Tuple(s) => s,
_ => {
return Err(MirLowerError::TypeError(
"non tuple type matched with tuple pattern",
@ -148,7 +148,7 @@ impl<'db> MirLowerCtx<'_, 'db> {
current_else,
args,
*ellipsis,
(0..subst.len(Interner)).map(|i| {
(0..subst.len()).map(|i| {
PlaceElem::Field(Either::Right(TupleFieldId {
tuple: TupleId(!0), // Dummy as it is unused
index: i as u32,
@ -209,14 +209,11 @@ impl<'db> MirLowerCtx<'_, 'db> {
}
Pat::Range { start, end } => {
let mut add_check = |l: &ExprId, binop| -> Result<'db, ()> {
let lv = self.lower_literal_or_const_to_operand(
self.infer[pattern].to_chalk(self.interner),
l,
)?;
let lv = self.lower_literal_or_const_to_operand(self.infer[pattern], l)?;
let else_target = *current_else.get_or_insert_with(|| self.new_basic_block());
let next = self.new_basic_block();
let discr: Place =
self.temp(TyBuilder::bool(), current, pattern.into())?.into();
let discr: Place<'db> =
self.temp(Ty::new_bool(self.interner()), current, pattern.into())?.into();
self.push_assignment(
current,
discr,
@ -252,12 +249,11 @@ impl<'db> MirLowerCtx<'_, 'db> {
Pat::Slice { prefix, slice, suffix } => {
if mode == MatchingMode::Check {
// emit runtime length check for slice
if let TyKind::Slice(_) =
self.infer[pattern].to_chalk(self.interner).kind(Interner)
{
if let TyKind::Slice(_) = self.infer[pattern].kind() {
let pattern_len = prefix.len() + suffix.len();
let place_len: Place =
self.temp(TyBuilder::usize(), current, pattern.into())?.into();
let place_len: Place<'db> = self
.temp(Ty::new_usize(self.interner()), current, pattern.into())?
.into();
self.push_assignment(
current,
place_len,
@ -287,10 +283,11 @@ impl<'db> MirLowerCtx<'_, 'db> {
let c = Operand::from_concrete_const(
pattern_len.to_le_bytes().into(),
MemoryMap::default(),
TyBuilder::usize(),
Ty::new_usize(self.interner()),
);
let discr: Place =
self.temp(TyBuilder::bool(), current, pattern.into())?.into();
let discr: Place<'db> = self
.temp(Ty::new_bool(self.interner()), current, pattern.into())?
.into();
self.push_assignment(
current,
discr,
@ -398,26 +395,19 @@ impl<'db> MirLowerCtx<'_, 'db> {
if let Some(x) = self.infer.assoc_resolutions_for_pat(pattern)
&& let AssocItemId::ConstId(c) = x.0
{
break 'b (c, x.1.to_chalk(self.interner));
break 'b (c, x.1);
}
if let ResolveValueResult::ValueNs(ValueNs::ConstId(c), _) = pr {
break 'b (c, Substitution::empty(Interner));
break 'b (c, GenericArgs::new_from_iter(self.interner(), []));
}
not_supported!("path in pattern position that is not const or variant")
};
let tmp: Place = self
.temp(self.infer[pattern].to_chalk(self.interner), current, pattern.into())?
.into();
let tmp: Place<'db> =
self.temp(self.infer[pattern], current, pattern.into())?.into();
let span = pattern.into();
self.lower_const(
c.into(),
current,
tmp,
subst,
span,
self.infer[pattern].to_chalk(self.interner),
)?;
let tmp2: Place = self.temp(TyBuilder::bool(), current, pattern.into())?.into();
self.lower_const(c.into(), current, tmp, subst, span)?;
let tmp2: Place<'db> =
self.temp(Ty::new_bool(self.interner()), current, pattern.into())?.into();
self.push_assignment(
current,
tmp2,
@ -444,10 +434,7 @@ impl<'db> MirLowerCtx<'_, 'db> {
Pat::Lit(l) => match &self.body[*l] {
Expr::Literal(l) => {
if mode == MatchingMode::Check {
let c = self.lower_literal_to_operand(
self.infer[pattern].to_chalk(self.interner),
l,
)?;
let c = self.lower_literal_to_operand(self.infer[pattern], l)?;
self.pattern_match_const(current_else, current, c, cond_place, pattern)?
} else {
(current, current_else)
@ -519,11 +506,11 @@ impl<'db> MirLowerCtx<'_, 'db> {
&mut self,
id: BindingId,
mode: BindingMode,
cond_place: Place,
cond_place: Place<'db>,
span: MirSpan,
current: BasicBlockId,
current_else: Option<BasicBlockId>,
) -> Result<'db, (BasicBlockId, Option<BasicBlockId>)> {
current: BasicBlockId<'db>,
current_else: Option<BasicBlockId<'db>>,
) -> Result<'db, (BasicBlockId<'db>, Option<BasicBlockId<'db>>)> {
let target_place = self.binding_local(id)?;
self.push_storage_live(id, current)?;
self.push_match_assignment(current, target_place, mode, cond_place, span);
@ -532,10 +519,10 @@ impl<'db> MirLowerCtx<'_, 'db> {
fn push_match_assignment(
&mut self,
current: BasicBlockId,
target_place: LocalId,
current: BasicBlockId<'db>,
target_place: LocalId<'db>,
mode: BindingMode,
cond_place: Place,
cond_place: Place<'db>,
span: MirSpan,
) {
self.push_assignment(
@ -558,15 +545,16 @@ impl<'db> MirLowerCtx<'_, 'db> {
fn pattern_match_const(
&mut self,
current_else: Option<BasicBlockId>,
current: BasicBlockId,
c: Operand,
cond_place: Place,
current_else: Option<BasicBlockId<'db>>,
current: BasicBlockId<'db>,
c: Operand<'db>,
cond_place: Place<'db>,
pattern: Idx<Pat>,
) -> Result<'db, (BasicBlockId, Option<BasicBlockId>)> {
) -> Result<'db, (BasicBlockId<'db>, Option<BasicBlockId<'db>>)> {
let then_target = self.new_basic_block();
let else_target = current_else.unwrap_or_else(|| self.new_basic_block());
let discr: Place = self.temp(TyBuilder::bool(), current, pattern.into())?.into();
let discr: Place<'db> =
self.temp(Ty::new_bool(self.interner()), current, pattern.into())?.into();
self.push_assignment(
current,
discr,
@ -591,14 +579,14 @@ impl<'db> MirLowerCtx<'_, 'db> {
fn pattern_matching_variant(
&mut self,
cond_place: Place,
cond_place: Place<'db>,
variant: VariantId,
mut current: BasicBlockId,
mut current: BasicBlockId<'db>,
span: MirSpan,
mut current_else: Option<BasicBlockId>,
mut current_else: Option<BasicBlockId<'db>>,
shape: AdtPatternShape<'_>,
mode: MatchingMode,
) -> Result<'db, (BasicBlockId, Option<BasicBlockId>)> {
) -> Result<'db, (BasicBlockId<'db>, Option<BasicBlockId<'db>>)> {
Ok(match variant {
VariantId::EnumVariantId(v) => {
if mode == MatchingMode::Check {
@ -647,11 +635,11 @@ impl<'db> MirLowerCtx<'_, 'db> {
shape: AdtPatternShape<'_>,
variant_data: &VariantFields,
v: VariantId,
current: BasicBlockId,
current_else: Option<BasicBlockId>,
cond_place: &Place,
current: BasicBlockId<'db>,
current_else: Option<BasicBlockId<'db>>,
cond_place: &Place<'db>,
mode: MatchingMode,
) -> Result<'db, (BasicBlockId, Option<BasicBlockId>)> {
) -> Result<'db, (BasicBlockId<'db>, Option<BasicBlockId<'db>>)> {
Ok(match shape {
AdtPatternShape::Record { args } => {
let it = args
@ -690,12 +678,12 @@ impl<'db> MirLowerCtx<'_, 'db> {
fn pattern_match_adt(
&mut self,
mut current: BasicBlockId,
mut current_else: Option<BasicBlockId>,
args: impl Iterator<Item = (PlaceElem, PatId)>,
cond_place: &Place,
mut current: BasicBlockId<'db>,
mut current_else: Option<BasicBlockId<'db>>,
args: impl Iterator<Item = (PlaceElem<'db>, PatId)>,
cond_place: &Place<'db>,
mode: MatchingMode,
) -> Result<'db, (BasicBlockId, Option<BasicBlockId>)> {
) -> Result<'db, (BasicBlockId<'db>, Option<BasicBlockId<'db>>)> {
for (proj, arg) in args {
let cond_place = cond_place.project(proj, &mut self.result.projection_store);
(current, current_else) =
@ -706,14 +694,14 @@ impl<'db> MirLowerCtx<'_, 'db> {
fn pattern_match_tuple_like(
&mut self,
current: BasicBlockId,
current_else: Option<BasicBlockId>,
current: BasicBlockId<'db>,
current_else: Option<BasicBlockId<'db>>,
args: &[PatId],
ellipsis: Option<u32>,
fields: impl DoubleEndedIterator<Item = PlaceElem> + Clone,
cond_place: &Place,
fields: impl DoubleEndedIterator<Item = PlaceElem<'db>> + Clone,
cond_place: &Place<'db>,
mode: MatchingMode,
) -> Result<'db, (BasicBlockId, Option<BasicBlockId>)> {
) -> Result<'db, (BasicBlockId<'db>, Option<BasicBlockId<'db>>)> {
let (al, ar) = args.split_at(ellipsis.map_or(args.len(), |it| it as usize));
let it = al
.iter()

View file

@ -1,14 +1,8 @@
use hir_def::db::DefDatabase;
use rustc_hash::FxHashMap;
use span::Edition;
use test_fixture::WithFixture;
use triomphe::Arc;
use crate::{db::HirDatabase, mir::MirBody, setup_tracing, test_db::TestDB};
use crate::{db::HirDatabase, setup_tracing, test_db::TestDB};
fn lower_mir(
#[rust_analyzer::rust_fixture] ra_fixture: &str,
) -> FxHashMap<String, Result<Arc<MirBody>, ()>> {
fn lower_mir(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
let _tracing = setup_tracing();
let (db, file_ids) = TestDB::with_many_files(ra_fixture);
crate::attach_db(&db, || {
@ -20,14 +14,9 @@ fn lower_mir(
hir_def::ModuleDefId::FunctionId(it) => Some(it),
_ => None,
});
funcs
.map(|func| {
let name =
db.function_signature(func).name.display(&db, Edition::CURRENT).to_string();
let mir = db.mir_body(func.into());
(name, mir.map_err(drop))
})
.collect()
for func in funcs {
_ = db.mir_body(func.into());
}
})
}

View file

@ -7,232 +7,129 @@
//!
//! So the monomorphization should be called even if the substitution is empty.
use std::mem;
use chalk_ir::{
ConstData, DebruijnIndex,
fold::{FallibleTypeFolder, TypeFoldable, TypeSuperFoldable},
};
use hir_def::DefWithBodyId;
use rustc_type_ir::inherent::{IntoKind, SliceLike};
use rustc_type_ir::{
FallibleTypeFolder, TypeFlags, TypeFoldable, TypeSuperFoldable, TypeVisitableExt,
};
use triomphe::Arc;
use crate::next_solver::DbInterner;
use crate::next_solver::mapping::{ChalkToNextSolver, NextSolverToChalk};
use crate::next_solver::{Const, ConstKind, Region, RegionKind};
use crate::{
Const, Interner, ProjectionTy, Substitution, TraitEnvironment, Ty, TyKind,
consteval::{intern_const_scalar, unknown_const},
db::{HirDatabase, InternedClosure, InternedClosureId},
from_placeholder_idx,
generics::{Generics, generics},
infer::normalize,
TraitEnvironment,
db::{HirDatabase, InternedClosureId},
next_solver::{
DbInterner, GenericArgs, Ty, TyKind, TypingMode,
infer::{DbInternerInferExt, InferCtxt, traits::ObligationCause},
obligation_ctxt::ObligationCtxt,
references_non_lt_error,
},
};
use super::{MirBody, MirLowerError, Operand, OperandKind, Rvalue, StatementKind, TerminatorKind};
macro_rules! not_supported {
($it: expr) => {
return Err(MirLowerError::NotSupported(format!($it)))
};
struct Filler<'db> {
infcx: InferCtxt<'db>,
trait_env: Arc<TraitEnvironment<'db>>,
subst: GenericArgs<'db>,
}
struct Filler<'a, 'db> {
db: &'db dyn HirDatabase,
trait_env: Arc<TraitEnvironment<'db>>,
subst: &'a Substitution,
generics: Option<Generics>,
interner: DbInterner<'db>,
}
impl<'a, 'db> FallibleTypeFolder<Interner> for Filler<'a, 'db> {
impl<'db> FallibleTypeFolder<DbInterner<'db>> for Filler<'db> {
type Error = MirLowerError<'db>;
fn as_dyn(&mut self) -> &mut dyn FallibleTypeFolder<Interner, Error = Self::Error> {
self
fn cx(&self) -> DbInterner<'db> {
self.infcx.interner
}
fn interner(&self) -> Interner {
Interner
}
fn try_fold_ty(&mut self, ty: Ty<'db>) -> Result<Ty<'db>, Self::Error> {
if !ty.has_type_flags(TypeFlags::HAS_ALIAS | TypeFlags::HAS_PARAM) {
return Ok(ty);
}
fn try_fold_ty(
&mut self,
ty: Ty,
outer_binder: DebruijnIndex,
) -> std::result::Result<Ty, Self::Error> {
match ty.kind(Interner) {
TyKind::AssociatedType(id, subst) => {
// I don't know exactly if and why this is needed, but it looks like `normalize_ty` likes
// this kind of associated types.
Ok(TyKind::Alias(chalk_ir::AliasTy::Projection(ProjectionTy {
associated_ty_id: *id,
substitution: subst.clone().try_fold_with(self, outer_binder)?,
}))
.intern(Interner))
match ty.kind() {
TyKind::Alias(..) => {
// First instantiate params.
let ty = ty.try_super_fold_with(self)?;
let mut ocx = ObligationCtxt::new(&self.infcx);
let ty = ocx
.structurally_normalize_ty(&ObligationCause::dummy(), self.trait_env.env, ty)
.map_err(|_| MirLowerError::NotSupported("can't normalize alias".to_owned()))?;
ty.try_super_fold_with(self)
}
TyKind::Alias(chalk_ir::AliasTy::Opaque(chalk_ir::OpaqueTy {
opaque_ty_id: id,
substitution: subst,
}))
| TyKind::OpaqueType(id, subst) => {
let impl_trait_id = self.db.lookup_intern_impl_trait_id((*id).into());
let subst = subst.clone().try_fold_with(self.as_dyn(), outer_binder)?;
match impl_trait_id {
crate::ImplTraitId::ReturnTypeImplTrait(func, idx) => {
let infer = self.db.infer(func.into());
let filler = &mut Filler {
db: self.db,
trait_env: self.trait_env.clone(),
subst: &subst,
generics: Some(generics(self.db, func.into())),
interner: self.interner,
};
filler.try_fold_ty(
infer.type_of_rpit[idx.to_nextsolver(self.interner)]
.to_chalk(self.interner),
outer_binder,
)
}
crate::ImplTraitId::TypeAliasImplTrait(..) => {
not_supported!("type alias impl trait");
}
crate::ImplTraitId::AsyncBlockTypeImplTrait(_, _) => {
not_supported!("async block impl trait");
}
}
}
_ => ty.try_super_fold_with(self.as_dyn(), outer_binder),
TyKind::Param(param) => Ok(self
.subst
.as_slice()
.get(param.index as usize)
.and_then(|arg| arg.ty())
.ok_or_else(|| {
MirLowerError::GenericArgNotProvided(param.id.into(), self.subst)
})?),
_ => ty.try_super_fold_with(self),
}
}
fn try_fold_free_placeholder_const(
&mut self,
_ty: chalk_ir::Ty<Interner>,
idx: chalk_ir::PlaceholderIndex,
_outer_binder: DebruijnIndex,
) -> std::result::Result<chalk_ir::Const<Interner>, Self::Error> {
let it = from_placeholder_idx(self.db, idx).0;
let Some(idx) = self.generics.as_ref().and_then(|g| g.type_or_const_param_idx(it)) else {
not_supported!("missing idx in generics");
fn try_fold_const(&mut self, ct: Const<'db>) -> Result<Const<'db>, Self::Error> {
let ConstKind::Param(param) = ct.kind() else {
return ct.try_super_fold_with(self);
};
Ok(self
.subst
.as_slice(Interner)
.get(idx)
.and_then(|it| it.constant(Interner))
.ok_or_else(|| MirLowerError::GenericArgNotProvided(it, self.subst.clone()))?
.clone())
self.subst
.as_slice()
.get(param.index as usize)
.and_then(|arg| arg.konst())
.ok_or_else(|| MirLowerError::GenericArgNotProvided(param.id.into(), self.subst))
}
fn try_fold_free_placeholder_ty(
&mut self,
idx: chalk_ir::PlaceholderIndex,
_outer_binder: DebruijnIndex,
) -> std::result::Result<Ty, Self::Error> {
let it = from_placeholder_idx(self.db, idx).0;
let Some(idx) = self.generics.as_ref().and_then(|g| g.type_or_const_param_idx(it)) else {
not_supported!("missing idx in generics");
fn try_fold_region(&mut self, region: Region<'db>) -> Result<Region<'db>, Self::Error> {
let RegionKind::ReEarlyParam(param) = region.kind() else {
return Ok(region);
};
Ok(self
.subst
.as_slice(Interner)
.get(idx)
.and_then(|it| it.ty(Interner))
.ok_or_else(|| MirLowerError::GenericArgNotProvided(it, self.subst.clone()))?
.clone())
}
fn try_fold_const(
&mut self,
constant: chalk_ir::Const<Interner>,
outer_binder: DebruijnIndex,
) -> Result<chalk_ir::Const<Interner>, Self::Error> {
let next_ty = normalize(
self.db,
self.trait_env.clone(),
constant.data(Interner).ty.clone().try_fold_with(self, outer_binder)?,
);
ConstData { ty: next_ty, value: constant.data(Interner).value.clone() }
.intern(Interner)
.try_super_fold_with(self, outer_binder)
self.subst
.as_slice()
.get(param.index as usize)
.and_then(|arg| arg.region())
.ok_or_else(|| MirLowerError::GenericArgNotProvided(param.id.into(), self.subst))
}
}
impl<'a, 'db> Filler<'a, 'db> {
fn fill_ty(&mut self, ty: &mut Ty) -> Result<(), MirLowerError<'db>> {
let tmp = mem::replace(ty, TyKind::Error.intern(Interner));
*ty = normalize(
self.db,
self.trait_env.clone(),
tmp.try_fold_with(self, DebruijnIndex::INNERMOST)?,
);
Ok(())
impl<'db> Filler<'db> {
fn new(
db: &'db dyn HirDatabase,
env: Arc<TraitEnvironment<'db>>,
subst: GenericArgs<'db>,
) -> Self {
let interner = DbInterner::new_with(db, Some(env.krate), env.block);
let infcx = interner.infer_ctxt().build(TypingMode::PostAnalysis);
Self { infcx, trait_env: env, subst }
}
fn fill_const(&mut self, c: &mut Const) -> Result<(), MirLowerError<'db>> {
let tmp = mem::replace(c, unknown_const(c.data(Interner).ty.clone()));
*c = tmp.try_fold_with(self, DebruijnIndex::INNERMOST)?;
Ok(())
fn fill<T: TypeFoldable<DbInterner<'db>> + Copy>(
&mut self,
t: &mut T,
) -> Result<(), MirLowerError<'db>> {
// Can't deep normalized as that'll try to normalize consts and fail.
*t = t.try_fold_with(self)?;
if references_non_lt_error(t) {
Err(MirLowerError::NotSupported("monomorphization resulted in errors".to_owned()))
} else {
Ok(())
}
}
fn fill_subst(&mut self, ty: &mut Substitution) -> Result<(), MirLowerError<'db>> {
let tmp = mem::replace(ty, Substitution::empty(Interner));
*ty = tmp.try_fold_with(self, DebruijnIndex::INNERMOST)?;
Ok(())
}
fn fill_operand(&mut self, op: &mut Operand) -> Result<(), MirLowerError<'db>> {
fn fill_operand(&mut self, op: &mut Operand<'db>) -> Result<(), MirLowerError<'db>> {
match &mut op.kind {
OperandKind::Constant(c) => {
match &c.data(Interner).value {
chalk_ir::ConstValue::BoundVar(b) => {
let resolved = self
.subst
.as_slice(Interner)
.get(b.index)
.ok_or_else(|| {
MirLowerError::GenericArgNotProvided(
self.generics
.as_ref()
.and_then(|it| it.iter().nth(b.index))
.and_then(|(id, _)| match id {
hir_def::GenericParamId::ConstParamId(id) => {
Some(hir_def::TypeOrConstParamId::from(id))
}
hir_def::GenericParamId::TypeParamId(id) => {
Some(hir_def::TypeOrConstParamId::from(id))
}
_ => None,
})
.unwrap(),
self.subst.clone(),
)
})?
.assert_const_ref(Interner);
*c = resolved.clone();
}
chalk_ir::ConstValue::InferenceVar(_)
| chalk_ir::ConstValue::Placeholder(_) => {}
chalk_ir::ConstValue::Concrete(cc) => match &cc.interned {
crate::ConstScalar::UnevaluatedConst(const_id, subst) => {
let mut subst = subst.clone();
self.fill_subst(&mut subst)?;
*c = intern_const_scalar(
crate::ConstScalar::UnevaluatedConst(*const_id, subst),
c.data(Interner).ty.clone(),
);
}
crate::ConstScalar::Bytes(_, _) | crate::ConstScalar::Unknown => (),
},
}
self.fill_const(c)?;
OperandKind::Constant { konst, ty } => {
self.fill(konst)?;
self.fill(ty)?;
}
OperandKind::Copy(_) | OperandKind::Move(_) | OperandKind::Static(_) => (),
}
Ok(())
}
fn fill_body(&mut self, body: &mut MirBody) -> Result<(), MirLowerError<'db>> {
fn fill_body(&mut self, body: &mut MirBody<'db>) -> Result<(), MirLowerError<'db>> {
for (_, l) in body.locals.iter_mut() {
self.fill_ty(&mut l.ty)?;
self.fill(&mut l.ty)?;
}
for (_, bb) in body.basic_blocks.iter_mut() {
for statement in &mut bb.statements {
@ -245,20 +142,20 @@ impl<'a, 'db> Filler<'a, 'db> {
match ak {
super::AggregateKind::Array(ty)
| super::AggregateKind::Tuple(ty)
| super::AggregateKind::Closure(ty) => self.fill_ty(ty)?,
super::AggregateKind::Adt(_, subst) => self.fill_subst(subst)?,
| super::AggregateKind::Closure(ty) => self.fill(ty)?,
super::AggregateKind::Adt(_, subst) => self.fill(subst)?,
super::AggregateKind::Union(_, _) => (),
}
}
Rvalue::ShallowInitBox(_, ty) | Rvalue::ShallowInitBoxWithAlloc(ty) => {
self.fill_ty(ty)?;
self.fill(ty)?;
}
Rvalue::Use(op) => {
self.fill_operand(op)?;
}
Rvalue::Repeat(op, len) => {
self.fill_operand(op)?;
self.fill_const(len)?;
self.fill(len)?;
}
Rvalue::Ref(_, _)
| Rvalue::Len(_)
@ -312,12 +209,10 @@ impl<'a, 'db> Filler<'a, 'db> {
pub fn monomorphized_mir_body_query<'db>(
db: &'db dyn HirDatabase,
owner: DefWithBodyId,
subst: Substitution,
subst: GenericArgs<'db>,
trait_env: Arc<crate::TraitEnvironment<'db>>,
) -> Result<Arc<MirBody>, MirLowerError<'db>> {
let generics = owner.as_generic_def_id(db).map(|g_def| generics(db, g_def));
let interner = DbInterner::new_with(db, Some(trait_env.krate), trait_env.block);
let filler = &mut Filler { db, subst: &subst, trait_env, generics, interner };
) -> Result<Arc<MirBody<'db>>, MirLowerError<'db>> {
let mut filler = Filler::new(db, trait_env, subst);
let body = db.mir_body(owner)?;
let mut body = (*body).clone();
filler.fill_body(&mut body)?;
@ -327,22 +222,19 @@ pub fn monomorphized_mir_body_query<'db>(
pub(crate) fn monomorphized_mir_body_cycle_result<'db>(
_db: &'db dyn HirDatabase,
_: DefWithBodyId,
_: Substitution,
_: GenericArgs<'db>,
_: Arc<crate::TraitEnvironment<'db>>,
) -> Result<Arc<MirBody>, MirLowerError<'db>> {
) -> Result<Arc<MirBody<'db>>, MirLowerError<'db>> {
Err(MirLowerError::Loop)
}
pub fn monomorphized_mir_body_for_closure_query<'db>(
db: &'db dyn HirDatabase,
closure: InternedClosureId,
subst: Substitution,
subst: GenericArgs<'db>,
trait_env: Arc<crate::TraitEnvironment<'db>>,
) -> Result<Arc<MirBody>, MirLowerError<'db>> {
let InternedClosure(owner, _) = db.lookup_intern_closure(closure);
let generics = owner.as_generic_def_id(db).map(|g_def| generics(db, g_def));
let interner = DbInterner::new_with(db, Some(trait_env.krate), trait_env.block);
let filler = &mut Filler { db, subst: &subst, trait_env, generics, interner };
) -> Result<Arc<MirBody<'db>>, MirLowerError<'db>> {
let mut filler = Filler::new(db, trait_env, subst);
let body = db.mir_body_for_closure(closure)?;
let mut body = (*body).clone();
filler.fill_body(&mut body)?;

View file

@ -11,8 +11,7 @@ use hir_expand::{Lookup, name::Name};
use la_arena::ArenaMap;
use crate::{
ClosureId,
db::HirDatabase,
db::{HirDatabase, InternedClosureId},
display::{ClosureStyle, DisplayTarget, HirDisplay},
mir::{PlaceElem, ProjectionElem, StatementKind, TerminatorKind},
};
@ -37,8 +36,8 @@ macro_rules! wln {
};
}
impl MirBody {
pub fn pretty_print(&self, db: &dyn HirDatabase, display_target: DisplayTarget) -> String {
impl<'db> MirBody<'db> {
pub fn pretty_print(&self, db: &'db dyn HirDatabase, display_target: DisplayTarget) -> String {
let hir_body = db.body(self.owner);
let mut ctx = MirPrettyCtx::new(self, &hir_body, db, display_target);
ctx.for_body(|this| match ctx.body.owner {
@ -81,7 +80,7 @@ impl MirBody {
// String with lines is rendered poorly in `dbg` macros, which I use very much, so this
// function exists to solve that.
pub fn dbg(&self, db: &dyn HirDatabase, display_target: DisplayTarget) -> impl Debug {
pub fn dbg(&self, db: &'db dyn HirDatabase, display_target: DisplayTarget) -> impl Debug {
struct StringDbg(String);
impl Debug for StringDbg {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
@ -92,17 +91,17 @@ impl MirBody {
}
}
struct MirPrettyCtx<'a> {
body: &'a MirBody,
struct MirPrettyCtx<'a, 'db> {
body: &'a MirBody<'db>,
hir_body: &'a Body,
db: &'a dyn HirDatabase,
db: &'db dyn HirDatabase,
result: String,
indent: String,
local_to_binding: ArenaMap<LocalId, BindingId>,
local_to_binding: ArenaMap<LocalId<'db>, BindingId>,
display_target: DisplayTarget,
}
impl Write for MirPrettyCtx<'_> {
impl Write for MirPrettyCtx<'_, '_> {
fn write_str(&mut self, s: &str) -> std::fmt::Result {
let mut it = s.split('\n'); // note: `.lines()` is wrong here
self.write(it.next().unwrap_or_default());
@ -114,12 +113,12 @@ impl Write for MirPrettyCtx<'_> {
}
}
enum LocalName {
Unknown(LocalId),
Binding(Name, LocalId),
enum LocalName<'db> {
Unknown(LocalId<'db>),
Binding(Name, LocalId<'db>),
}
impl HirDisplay for LocalName {
impl<'db> HirDisplay for LocalName<'db> {
fn hir_fmt(
&self,
f: &mut crate::display::HirFormatter<'_>,
@ -133,8 +132,8 @@ impl HirDisplay for LocalName {
}
}
impl<'a> MirPrettyCtx<'a> {
fn for_body(&mut self, name: impl FnOnce(&mut MirPrettyCtx<'_>)) {
impl<'a, 'db> MirPrettyCtx<'a, 'db> {
fn for_body(&mut self, name: impl FnOnce(&mut MirPrettyCtx<'_, 'db>)) {
name(self);
self.with_block(|this| {
this.locals();
@ -146,8 +145,8 @@ impl<'a> MirPrettyCtx<'a> {
}
}
fn for_closure(&mut self, closure: ClosureId) {
let body = match self.db.mir_body_for_closure(closure.into()) {
fn for_closure(&mut self, closure: InternedClosureId) {
let body = match self.db.mir_body_for_closure(closure) {
Ok(it) => it,
Err(e) => {
wln!(self, "// error in {closure:?}: {e:?}");
@ -168,7 +167,7 @@ impl<'a> MirPrettyCtx<'a> {
self.indent = ctx.indent;
}
fn with_block(&mut self, f: impl FnOnce(&mut MirPrettyCtx<'_>)) {
fn with_block(&mut self, f: impl FnOnce(&mut MirPrettyCtx<'_, 'db>)) {
self.indent += " ";
wln!(self, "{{");
f(self);
@ -180,9 +179,9 @@ impl<'a> MirPrettyCtx<'a> {
}
fn new(
body: &'a MirBody,
body: &'a MirBody<'db>,
hir_body: &'a Body,
db: &'a dyn HirDatabase,
db: &'db dyn HirDatabase,
display_target: DisplayTarget,
) -> Self {
let local_to_binding = body.local_to_binding_map();
@ -217,14 +216,14 @@ impl<'a> MirPrettyCtx<'a> {
}
}
fn local_name(&self, local: LocalId) -> LocalName {
fn local_name(&self, local: LocalId<'db>) -> LocalName<'db> {
match self.local_to_binding.get(local) {
Some(b) => LocalName::Binding(self.hir_body[*b].name.clone(), local),
None => LocalName::Unknown(local),
}
}
fn basic_block_id(&self, basic_block_id: BasicBlockId) -> String {
fn basic_block_id(&self, basic_block_id: BasicBlockId<'db>) -> String {
format!("'bb{}", u32::from(basic_block_id.into_raw()))
}
@ -312,8 +311,12 @@ impl<'a> MirPrettyCtx<'a> {
}
}
fn place(&mut self, p: &Place) {
fn f(this: &mut MirPrettyCtx<'_>, local: LocalId, projections: &[PlaceElem]) {
fn place(&mut self, p: &Place<'db>) {
fn f<'db>(
this: &mut MirPrettyCtx<'_, 'db>,
local: LocalId<'db>,
projections: &[PlaceElem<'db>],
) {
let Some((last, head)) = projections.split_last() else {
// no projection
w!(this, "{}", this.local_name(local).display_test(this.db, this.display_target));
@ -373,19 +376,19 @@ impl<'a> MirPrettyCtx<'a> {
f(self, p.local, p.projection.lookup(&self.body.projection_store));
}
fn operand(&mut self, r: &Operand) {
fn operand(&mut self, r: &Operand<'db>) {
match &r.kind {
OperandKind::Copy(p) | OperandKind::Move(p) => {
// MIR at the time of writing doesn't have difference between move and copy, so we show them
// equally. Feel free to change it.
self.place(p);
}
OperandKind::Constant(c) => w!(self, "Const({})", self.hir_display(c)),
OperandKind::Constant { konst, .. } => w!(self, "Const({})", self.hir_display(konst)),
OperandKind::Static(s) => w!(self, "Static({:?})", s),
}
}
fn rvalue(&mut self, r: &Rvalue) {
fn rvalue(&mut self, r: &Rvalue<'db>) {
match r {
Rvalue::Use(op) => self.operand(op),
Rvalue::Ref(r, p) => {
@ -475,7 +478,7 @@ impl<'a> MirPrettyCtx<'a> {
}
}
fn operand_list(&mut self, it: &[Operand]) {
fn operand_list(&mut self, it: &[Operand<'db>]) {
let mut it = it.iter();
if let Some(first) = it.next() {
self.operand(first);
@ -486,7 +489,10 @@ impl<'a> MirPrettyCtx<'a> {
}
}
fn hir_display<T: HirDisplay>(&self, ty: &'a T) -> impl Display + 'a {
fn hir_display<'b, T: HirDisplay>(&self, ty: &'b T) -> impl Display + use<'a, 'b, 'db, T>
where
'db: 'b,
{
ty.display_test(self.db, self.display_target)
.with_closure_style(ClosureStyle::ClosureWithSubst)
}

View file

@ -4,6 +4,7 @@ use std::hash::Hash;
use hir_def::{ConstParamId, TypeOrConstParamId};
use intern::{Interned, Symbol};
use macros::{TypeFoldable, TypeVisitable};
use rustc_ast_ir::{try_visit, visit::VisitorResult};
use rustc_type_ir::{
BoundVar, FlagComputation, Flags, TypeFoldable, TypeSuperFoldable, TypeSuperVisitable,
@ -23,7 +24,7 @@ use super::{BoundVarKind, DbInterner, ErrorGuaranteed, GenericArgs, Placeholder,
pub type ConstKind<'db> = rustc_type_ir::ConstKind<DbInterner<'db>>;
pub type UnevaluatedConst<'db> = rustc_type_ir::UnevaluatedConst<DbInterner<'db>>;
#[salsa::interned(constructor = new_, debug)]
#[salsa::interned(constructor = new_)]
pub struct Const<'db> {
#[returns(ref)]
kind_: InternedWrapperNoDebug<WithCachedTypeInfo<ConstKind<'db>>>,
@ -61,6 +62,21 @@ impl<'db> Const<'db> {
Const::new(interner, ConstKind::Placeholder(placeholder))
}
pub fn new_valtree(
interner: DbInterner<'db>,
ty: Ty<'db>,
memory: Box<[u8]>,
memory_map: MemoryMap<'db>,
) -> Self {
Const::new(
interner,
ConstKind::Value(ValueConst {
ty,
value: Valtree::new(ConstBytes { memory, memory_map }),
}),
)
}
pub fn is_ct_infer(&self) -> bool {
matches!(&self.inner().internee, ConstKind::Infer(_))
}
@ -77,6 +93,12 @@ impl<'db> Const<'db> {
}
}
impl<'db> std::fmt::Debug for Const<'db> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.inner().internee.fmt(f)
}
}
impl<'db> std::fmt::Debug for InternedWrapperNoDebug<WithCachedTypeInfo<ConstKind<'db>>> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.internee.fmt(f)
@ -135,9 +157,12 @@ impl ParamConst {
/// A type-level constant value.
///
/// Represents a typed, fully evaluated constant.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, TypeFoldable, TypeVisitable)]
pub struct ValueConst<'db> {
pub(crate) ty: Ty<'db>,
// FIXME: Should we ignore this for TypeVisitable, TypeFoldable?
#[type_visitable(ignore)]
#[type_foldable(identity)]
pub(crate) value: Valtree<'db>,
}
@ -158,33 +183,15 @@ impl<'db> rustc_type_ir::inherent::ValueConst<DbInterner<'db>> for ValueConst<'d
}
}
impl<'db> rustc_type_ir::TypeVisitable<DbInterner<'db>> for ValueConst<'db> {
fn visit_with<V: rustc_type_ir::TypeVisitor<DbInterner<'db>>>(
&self,
visitor: &mut V,
) -> V::Result {
self.ty.visit_with(visitor)
}
}
impl<'db> rustc_type_ir::TypeFoldable<DbInterner<'db>> for ValueConst<'db> {
fn fold_with<F: rustc_type_ir::TypeFolder<DbInterner<'db>>>(self, folder: &mut F) -> Self {
ValueConst { ty: self.ty.fold_with(folder), value: self.value }
}
fn try_fold_with<F: rustc_type_ir::FallibleTypeFolder<DbInterner<'db>>>(
self,
folder: &mut F,
) -> Result<Self, F::Error> {
Ok(ValueConst { ty: self.ty.try_fold_with(folder)?, value: self.value })
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ConstBytes<'db>(pub Box<[u8]>, pub MemoryMap<'db>);
pub struct ConstBytes<'db> {
pub memory: Box<[u8]>,
pub memory_map: MemoryMap<'db>,
}
impl Hash for ConstBytes<'_> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.0.hash(state)
self.memory.hash(state)
}
}
@ -212,7 +219,7 @@ impl<'db> Valtree<'db> {
}
}
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, TypeVisitable, TypeFoldable)]
pub struct ExprConst;
impl rustc_type_ir::inherent::ParamLike for ParamConst {
@ -412,29 +419,6 @@ impl<'db> PlaceholderLike<DbInterner<'db>> for PlaceholderConst {
}
}
impl<'db> TypeVisitable<DbInterner<'db>> for ExprConst {
fn visit_with<V: rustc_type_ir::TypeVisitor<DbInterner<'db>>>(
&self,
visitor: &mut V,
) -> V::Result {
// Ensure we get back to this when we fill in the fields
let ExprConst = &self;
V::Result::output()
}
}
impl<'db> TypeFoldable<DbInterner<'db>> for ExprConst {
fn try_fold_with<F: rustc_type_ir::FallibleTypeFolder<DbInterner<'db>>>(
self,
folder: &mut F,
) -> Result<Self, F::Error> {
Ok(ExprConst)
}
fn fold_with<F: rustc_type_ir::TypeFolder<DbInterner<'db>>>(self, folder: &mut F) -> Self {
ExprConst
}
}
impl<'db> Relate<DbInterner<'db>> for ExprConst {
fn relate<R: rustc_type_ir::relate::TypeRelation<DbInterner<'db>>>(
relation: &mut R,

View file

@ -1,8 +1,8 @@
//! Definition of `SolverDefId`
use hir_def::{
AdtId, CallableDefId, ConstId, EnumId, EnumVariantId, FunctionId, GenericDefId, ImplId,
StaticId, StructId, TraitId, TypeAliasId, UnionId,
AdtId, CallableDefId, ConstId, EnumId, EnumVariantId, FunctionId, GeneralConstId, GenericDefId,
ImplId, StaticId, StructId, TraitId, TypeAliasId, UnionId,
};
use rustc_type_ir::inherent;
use stdx::impl_from;
@ -119,6 +119,16 @@ impl From<GenericDefId> for SolverDefId {
}
}
impl From<GeneralConstId> for SolverDefId {
#[inline]
fn from(value: GeneralConstId) -> Self {
match value {
GeneralConstId::ConstId(const_id) => SolverDefId::ConstId(const_id),
GeneralConstId::StaticId(static_id) => SolverDefId::StaticId(static_id),
}
}
}
impl TryFrom<SolverDefId> for GenericDefId {
type Error = SolverDefId;

View file

@ -2,6 +2,7 @@
use hir_def::{GenericDefId, GenericParamId};
use intern::{Interned, Symbol};
use macros::{TypeFoldable, TypeVisitable};
use rustc_type_ir::inherent::Const as _;
use rustc_type_ir::{
ClosureArgs, CollectAndApply, ConstVid, CoroutineArgs, CoroutineClosureArgs, FnSig, FnSigTys,
@ -23,7 +24,7 @@ use super::{
interned_vec_db,
};
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
#[derive(Copy, Clone, PartialEq, Eq, Hash, TypeVisitable, TypeFoldable)]
pub enum GenericArg<'db> {
Ty(Ty<'db>),
Lifetime(Region<'db>),
@ -55,6 +56,13 @@ impl<'db> GenericArg<'db> {
}
}
pub fn konst(self) -> Option<Const<'db>> {
match self.kind() {
GenericArgKind::Const(konst) => Some(konst),
_ => None,
}
}
pub fn region(self) -> Option<Region<'db>> {
match self.kind() {
GenericArgKind::Lifetime(r) => Some(r),
@ -72,7 +80,7 @@ impl<'db> From<Term<'db>> for GenericArg<'db> {
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
#[derive(Copy, Clone, PartialEq, Eq, Hash, TypeVisitable, TypeFoldable)]
pub enum Term<'db> {
Ty(Ty<'db>),
Const(Const<'db>),
@ -130,39 +138,6 @@ impl<'db> IntoKind for GenericArg<'db> {
}
}
impl<'db> TypeVisitable<DbInterner<'db>> for GenericArg<'db> {
fn visit_with<V: rustc_type_ir::TypeVisitor<DbInterner<'db>>>(
&self,
visitor: &mut V,
) -> V::Result {
match self {
GenericArg::Lifetime(lt) => lt.visit_with(visitor),
GenericArg::Ty(ty) => ty.visit_with(visitor),
GenericArg::Const(ct) => ct.visit_with(visitor),
}
}
}
impl<'db> TypeFoldable<DbInterner<'db>> for GenericArg<'db> {
fn try_fold_with<F: rustc_type_ir::FallibleTypeFolder<DbInterner<'db>>>(
self,
folder: &mut F,
) -> Result<Self, F::Error> {
match self.kind() {
GenericArgKind::Lifetime(lt) => lt.try_fold_with(folder).map(Into::into),
GenericArgKind::Type(ty) => ty.try_fold_with(folder).map(Into::into),
GenericArgKind::Const(ct) => ct.try_fold_with(folder).map(Into::into),
}
}
fn fold_with<F: rustc_type_ir::TypeFolder<DbInterner<'db>>>(self, folder: &mut F) -> Self {
match self.kind() {
GenericArgKind::Lifetime(lt) => lt.fold_with(folder).into(),
GenericArgKind::Type(ty) => ty.fold_with(folder).into(),
GenericArgKind::Const(ct) => ct.fold_with(folder).into(),
}
}
}
impl<'db> Relate<DbInterner<'db>> for GenericArg<'db> {
fn relate<R: rustc_type_ir::relate::TypeRelation<DbInterner<'db>>>(
relation: &mut R,
@ -553,36 +528,6 @@ impl<'db> From<Const<'db>> for Term<'db> {
}
}
impl<'db> TypeVisitable<DbInterner<'db>> for Term<'db> {
fn visit_with<V: rustc_type_ir::TypeVisitor<DbInterner<'db>>>(
&self,
visitor: &mut V,
) -> V::Result {
match self {
Term::Ty(ty) => ty.visit_with(visitor),
Term::Const(ct) => ct.visit_with(visitor),
}
}
}
impl<'db> TypeFoldable<DbInterner<'db>> for Term<'db> {
fn try_fold_with<F: rustc_type_ir::FallibleTypeFolder<DbInterner<'db>>>(
self,
folder: &mut F,
) -> Result<Self, F::Error> {
match self.kind() {
TermKind::Ty(ty) => ty.try_fold_with(folder).map(Into::into),
TermKind::Const(ct) => ct.try_fold_with(folder).map(Into::into),
}
}
fn fold_with<F: rustc_type_ir::TypeFolder<DbInterner<'db>>>(self, folder: &mut F) -> Self {
match self.kind() {
TermKind::Ty(ty) => ty.fold_with(folder).into(),
TermKind::Const(ct) => ct.fold_with(folder).into(),
}
}
}
impl<'db> Relate<DbInterner<'db>> for Term<'db> {
fn relate<R: rustc_type_ir::relate::TypeRelation<DbInterner<'db>>>(
relation: &mut R,

View file

@ -1,6 +1,7 @@
use std::ops::ControlFlow;
use hir_def::{ImplId, TraitId};
use macros::{TypeFoldable, TypeVisitable};
use rustc_type_ir::{
Interner,
solve::{BuiltinImplSource, CandidateSource, Certainty, inspect::ProbeKind},
@ -177,7 +178,7 @@ pub type SelectionResult<'db, T> = Result<Option<T>, SelectionError<'db>>;
/// ### The type parameter `N`
///
/// See explanation on `ImplSourceUserDefinedData`.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[derive(Debug, Clone, PartialEq, Eq, Hash, TypeVisitable, TypeFoldable)]
pub enum ImplSource<'db, N> {
/// ImplSource identifying a particular impl.
UserDefined(ImplSourceUserDefinedData<'db, N>),
@ -242,8 +243,10 @@ impl<'db, N> ImplSource<'db, N> {
/// is `Obligation`, as one might expect. During codegen, however, this
/// is `()`, because codegen only requires a shallow resolution of an
/// impl, and nested obligations are satisfied later.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[derive(Debug, Clone, PartialEq, Eq, Hash, TypeVisitable, TypeFoldable)]
pub struct ImplSourceUserDefinedData<'db, N> {
#[type_visitable(ignore)]
#[type_foldable(identity)]
pub impl_def_id: ImplId,
pub args: GenericArgs<'db>,
pub nested: Vec<N>,

View file

@ -8,6 +8,7 @@ use std::{
};
use hir_def::TraitId;
use macros::{TypeFoldable, TypeVisitable};
use rustc_type_ir::elaborate::Elaboratable;
use rustc_type_ir::{
PredicatePolarity, Upcast,
@ -65,8 +66,10 @@ impl ObligationCause {
/// either identifying an `impl` (e.g., `impl Eq for i32`) that
/// satisfies the obligation, or else finding a bound that is in
/// scope. The eventual result is usually a `Selection` (defined below).
#[derive(Clone, Debug)]
#[derive(Clone, Debug, TypeVisitable, TypeFoldable)]
pub struct Obligation<'db, T> {
#[type_foldable(identity)]
#[type_visitable(ignore)]
/// The reason we have to prove this thing.
pub cause: ObligationCause,
@ -117,39 +120,6 @@ impl<'db> Elaboratable<DbInterner<'db>> for PredicateObligation<'db> {
}
}
impl<'db, T: TypeVisitable<DbInterner<'db>>> TypeVisitable<DbInterner<'db>> for Obligation<'db, T> {
fn visit_with<V: rustc_type_ir::TypeVisitor<DbInterner<'db>>>(
&self,
visitor: &mut V,
) -> V::Result {
rustc_ast_ir::try_visit!(self.param_env.visit_with(visitor));
self.predicate.visit_with(visitor)
}
}
impl<'db, T: TypeFoldable<DbInterner<'db>>> TypeFoldable<DbInterner<'db>> for Obligation<'db, T> {
fn try_fold_with<F: rustc_type_ir::FallibleTypeFolder<DbInterner<'db>>>(
self,
folder: &mut F,
) -> Result<Self, F::Error> {
Ok(Obligation {
cause: self.cause.clone(),
param_env: self.param_env.try_fold_with(folder)?,
predicate: self.predicate.try_fold_with(folder)?,
recursion_depth: self.recursion_depth,
})
}
fn fold_with<F: rustc_type_ir::TypeFolder<DbInterner<'db>>>(self, folder: &mut F) -> Self {
Obligation {
cause: self.cause.clone(),
param_env: self.param_env.fold_with(folder),
predicate: self.predicate.fold_with(folder),
recursion_depth: self.recursion_depth,
}
}
}
impl<'db, T: Copy> Obligation<'db, T> {
pub fn as_goal(&self) -> Goal<'db, T> {
Goal { param_env: self.param_env, predicate: self.predicate }

View file

@ -1,77 +1,81 @@
//! Things related to the Interner in the next-trait-solver.
#![allow(unused)]
#![allow(unused)] // FIXME(next-solver): Remove this.
use std::{fmt, ops::ControlFlow};
pub use tls_db::{attach_db, attach_db_allow_change, with_attached_db};
use base_db::Crate;
use chalk_ir::{ProgramClauseImplication, SeparatorTraitRef, Variances};
use hir_def::lang_item::LangItem;
use hir_def::signatures::{FieldData, FnFlags, ImplFlags, StructFlags, TraitFlags};
use hir_def::{AdtId, BlockId, GenericDefId, TypeAliasId, VariantId};
use hir_def::{AttrDefId, Lookup};
use hir_def::{CallableDefId, EnumVariantId, ItemContainerId, StructId, UnionId};
use hir_def::{
AdtId, AttrDefId, BlockId, CallableDefId, EnumVariantId, GenericDefId, ItemContainerId, Lookup,
StructId, TypeAliasId, UnionId, VariantId,
lang_item::LangItem,
signatures::{FieldData, FnFlags, ImplFlags, StructFlags, TraitFlags},
};
use intern::sym::non_exhaustive;
use intern::{Interned, impl_internable, sym};
use la_arena::Idx;
use rustc_abi::{Align, ReprFlags, ReprOptions};
use rustc_ast_ir::visit::VisitorResult;
use rustc_hash::FxHashSet;
use rustc_index::bit_set::DenseBitSet;
use rustc_type_ir::elaborate::elaborate;
use rustc_type_ir::error::TypeError;
use rustc_type_ir::inherent::{
AdtDef as _, GenericArgs as _, GenericsOf, IntoKind, SliceLike as _, Span as _,
};
use rustc_type_ir::lang_items::{SolverAdtLangItem, SolverLangItem, SolverTraitLangItem};
use rustc_type_ir::solve::SizedTraitKind;
use rustc_index::{IndexVec, bit_set::DenseBitSet};
use rustc_type_ir::{
AliasTerm, AliasTermKind, AliasTy, AliasTyKind, EarlyBinder, FlagComputation, Flags,
ImplPolarity, InferTy, ProjectionPredicate, TraitPredicate, TraitRef, Upcast,
AliasTerm, AliasTermKind, AliasTy, AliasTyKind, BoundVar, CollectAndApply, DebruijnIndex,
EarlyBinder, FlagComputation, Flags, GenericArgKind, ImplPolarity, InferTy,
ProjectionPredicate, RegionKind, TermKind, TraitPredicate, TraitRef, TypeVisitableExt,
UniverseIndex, Upcast, Variance, WithCachedTypeInfo,
elaborate::{self, elaborate},
error::TypeError,
inherent::{
self, AdtDef as _, Const as _, GenericArgs as _, GenericsOf, IntoKind, ParamEnv as _,
Region as _, SliceLike as _, Span as _, Ty as _,
},
ir_print,
lang_items::{SolverAdtLangItem, SolverLangItem, SolverTraitLangItem},
relate,
solve::SizedTraitKind,
};
use salsa::plumbing::AsId;
use smallvec::{SmallVec, smallvec};
use std::fmt;
use std::ops::ControlFlow;
use syntax::ast::SelfParamKind;
use tracing::debug;
use triomphe::Arc;
use rustc_ast_ir::visit::VisitorResult;
use rustc_index::IndexVec;
use rustc_type_ir::TypeVisitableExt;
use rustc_type_ir::{
BoundVar, CollectAndApply, DebruijnIndex, GenericArgKind, RegionKind, TermKind, UniverseIndex,
Variance, WithCachedTypeInfo, elaborate,
inherent::{self, Const as _, Region as _, Ty as _},
ir_print, relate,
use crate::{
ConstScalar, FnAbi, Interner,
db::HirDatabase,
lower_nextsolver::{self, TyLoweringContext},
method_resolution::{ALL_FLOAT_FPS, ALL_INT_FPS, TyFingerprint},
next_solver::{
AdtIdWrapper, BoundConst, CallableIdWrapper, CanonicalVarKind, ClosureIdWrapper,
CoroutineIdWrapper, Ctor, FnSig, FxIndexMap, ImplIdWrapper, InternedWrapperNoDebug,
RegionAssumptions, SolverContext, SolverDefIds, TraitIdWrapper, TypeAliasIdWrapper,
TypingMode,
infer::{
DbInternerInferExt, InferCtxt,
traits::{Obligation, ObligationCause},
},
obligation_ctxt::ObligationCtxt,
util::{ContainsTypeErrors, explicit_item_bounds, for_trait_impls},
},
};
use crate::lower_nextsolver::{self, TyLoweringContext};
use crate::method_resolution::{ALL_FLOAT_FPS, ALL_INT_FPS, TyFingerprint};
use crate::next_solver::infer::InferCtxt;
use crate::next_solver::util::{ContainsTypeErrors, explicit_item_bounds, for_trait_impls};
use crate::next_solver::{
AdtIdWrapper, BoundConst, CallableIdWrapper, CanonicalVarKind, ClosureIdWrapper,
CoroutineIdWrapper, Ctor, FnSig, FxIndexMap, ImplIdWrapper, InternedWrapperNoDebug,
RegionAssumptions, SolverContext, SolverDefIds, TraitIdWrapper, TypeAliasIdWrapper,
};
use crate::{ConstScalar, FnAbi, Interner, db::HirDatabase};
use super::generics::generics;
use super::util::sizedness_constraint_for_ty;
use super::{
Binder, BoundExistentialPredicate, BoundExistentialPredicates, BoundTy, BoundTyKind, Clause,
Clauses, Const, ConstKind, ErrorGuaranteed, ExprConst, ExternalConstraints,
ClauseKind, Clauses, Const, ConstKind, ErrorGuaranteed, ExprConst, ExternalConstraints,
ExternalConstraintsData, GenericArg, GenericArgs, InternedClausesWrapper, ParamConst, ParamEnv,
ParamTy, PlaceholderConst, PlaceholderTy, PredefinedOpaques, PredefinedOpaquesData, Predicate,
PredicateKind, Term, Ty, TyKind, Tys, ValueConst,
PredicateKind, SolverDefId, Term, Ty, TyKind, Tys, Valtree, ValueConst,
abi::Safety,
fold::{BoundVarReplacer, BoundVarReplacerDelegate, FnMutDelegate},
generics::Generics,
generics::{Generics, generics},
mapping::ChalkToNextSolver,
region::{
BoundRegion, BoundRegionKind, EarlyParamRegion, LateParamRegion, PlaceholderRegion, Region,
},
util::sizedness_constraint_for_ty,
};
use super::{ClauseKind, SolverDefId, Valtree};
#[macro_export]
#[doc(hidden)]
@ -1102,7 +1106,15 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> {
fn alias_ty_kind(self, alias: rustc_type_ir::AliasTy<Self>) -> AliasTyKind {
match alias.def_id {
SolverDefId::InternedOpaqueTyId(_) => AliasTyKind::Opaque,
SolverDefId::TypeAliasId(_) => AliasTyKind::Projection,
SolverDefId::TypeAliasId(type_alias) => match type_alias.loc(self.db).container {
ItemContainerId::ImplId(impl_)
if self.db.impl_signature(impl_).target_trait.is_none() =>
{
AliasTyKind::Inherent
}
ItemContainerId::TraitId(_) | ItemContainerId::ImplId(_) => AliasTyKind::Projection,
_ => AliasTyKind::Free,
},
_ => unimplemented!("Unexpected alias: {:?}", alias.def_id),
}
}
@ -1113,7 +1125,19 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> {
) -> rustc_type_ir::AliasTermKind {
match alias.def_id {
SolverDefId::InternedOpaqueTyId(_) => AliasTermKind::OpaqueTy,
SolverDefId::TypeAliasId(_) => AliasTermKind::ProjectionTy,
SolverDefId::TypeAliasId(type_alias) => match type_alias.loc(self.db).container {
ItemContainerId::ImplId(impl_)
if self.db.impl_signature(impl_).target_trait.is_none() =>
{
AliasTermKind::InherentTy
}
ItemContainerId::TraitId(_) | ItemContainerId::ImplId(_) => {
AliasTermKind::ProjectionTy
}
_ => AliasTermKind::FreeTy,
},
// rustc creates an `AnonConst` for consts, and evaluates them with CTFE (normalizing projections
// via selection, similar to ours `find_matching_impl()`, and not with the trait solver), so mimic it.
SolverDefId::ConstId(_) => AliasTermKind::UnevaluatedConst,
_ => unimplemented!("Unexpected alias: {:?}", alias.def_id),
}
@ -1676,8 +1700,7 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> {
}
fn impl_is_default(self, impl_def_id: Self::ImplId) -> bool {
// FIXME
false
self.db.impl_signature(impl_def_id.0).is_default()
}
#[tracing::instrument(skip(self), ret)]
@ -1731,7 +1754,7 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> {
}
fn delay_bug(self, msg: impl ToString) -> Self::ErrorGuaranteed {
panic!("Bug encountered in next-trait-solver.")
panic!("Bug encountered in next-trait-solver: {}", msg.to_string())
}
fn is_general_coroutine(self, coroutine_def_id: Self::CoroutineId) -> bool {
@ -1929,7 +1952,12 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> {
false
}
fn impl_specializes(self, impl_def_id: Self::ImplId, victim_def_id: Self::ImplId) -> bool {
// FIXME(next-solver): Make this a query? Can this make cycles?
fn impl_specializes(
self,
specializing_impl_def_id: Self::ImplId,
parent_impl_def_id: Self::ImplId,
) -> bool {
false
}

View file

@ -539,7 +539,7 @@ impl<'db> ChalkToNextSolver<'db, Const<'db>> for chalk_ir::Const<Interner> {
ConstScalar::Bytes(bytes, memory) => {
rustc_type_ir::ConstKind::Value(ValueConst::new(
data.ty.to_nextsolver(interner),
ConstBytes(bytes.clone(), memory.clone()),
ConstBytes { memory: bytes.clone(), memory_map: memory.clone() },
))
}
ConstScalar::UnevaluatedConst(c, subst) => {
@ -1710,8 +1710,10 @@ pub fn convert_const_for_result<'db>(
let bytes = value_const.value.inner();
let value = chalk_ir::ConstValue::Concrete(chalk_ir::ConcreteConst {
// SAFETY: we will never actually use this without a database
interned: ConstScalar::Bytes(bytes.0.clone(), unsafe {
std::mem::transmute::<MemoryMap<'db>, MemoryMap<'static>>(bytes.1.clone())
interned: ConstScalar::Bytes(bytes.memory.clone(), unsafe {
std::mem::transmute::<MemoryMap<'db>, MemoryMap<'static>>(
bytes.memory_map.clone(),
)
}),
});
return chalk_ir::ConstData {

View file

@ -3,6 +3,7 @@
use std::cmp::Ordering;
use intern::Interned;
use macros::{TypeFoldable, TypeVisitable};
use rustc_ast_ir::try_visit;
use rustc_type_ir::{
self as ty, CollectAndApply, DebruijnIndex, EarlyBinder, FlagComputation, Flags,
@ -424,7 +425,7 @@ impl<'db> rustc_type_ir::TypeSuperVisitable<DbInterner<'db>> for Clauses<'db> {
pub struct Clause<'db>(pub(crate) Predicate<'db>);
// We could cram the reveal into the clauses like rustc does, probably
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, TypeVisitable, TypeFoldable)]
pub struct ParamEnv<'db> {
pub(crate) clauses: Clauses<'db>,
}
@ -435,28 +436,6 @@ impl<'db> ParamEnv<'db> {
}
}
impl<'db> TypeVisitable<DbInterner<'db>> for ParamEnv<'db> {
fn visit_with<V: rustc_type_ir::TypeVisitor<DbInterner<'db>>>(
&self,
visitor: &mut V,
) -> V::Result {
try_visit!(self.clauses.visit_with(visitor));
V::Result::output()
}
}
impl<'db> TypeFoldable<DbInterner<'db>> for ParamEnv<'db> {
fn try_fold_with<F: rustc_type_ir::FallibleTypeFolder<DbInterner<'db>>>(
self,
folder: &mut F,
) -> Result<Self, F::Error> {
Ok(ParamEnv { clauses: self.clauses.try_fold_with(folder)? })
}
fn fold_with<F: rustc_type_ir::TypeFolder<DbInterner<'db>>>(self, folder: &mut F) -> Self {
ParamEnv { clauses: self.clauses.fold_with(folder) }
}
}
impl<'db> rustc_type_ir::inherent::ParamEnv<DbInterner<'db>> for ParamEnv<'db> {
fn caller_bounds(self) -> impl rustc_type_ir::inherent::SliceLike<Item = Clause<'db>> {
self.clauses

View file

@ -149,13 +149,9 @@ impl<'db> SolverDelegate for SolverContext<'db> {
fn fetch_eligible_assoc_item(
&self,
goal_trait_ref: rustc_type_ir::TraitRef<Self::Interner>,
trait_assoc_def_id: <Self::Interner as rustc_type_ir::Interner>::DefId,
trait_assoc_def_id: SolverDefId,
impl_id: ImplIdWrapper,
) -> Result<Option<<Self::Interner as rustc_type_ir::Interner>::DefId>, ErrorGuaranteed> {
let trait_assoc_id = match trait_assoc_def_id {
SolverDefId::TypeAliasId(id) => id,
_ => panic!("Unexpected SolverDefId"),
};
) -> Result<Option<SolverDefId>, ErrorGuaranteed> {
let trait_ = self
.0
.interner
@ -167,18 +163,47 @@ impl<'db> SolverDelegate for SolverContext<'db> {
.def_id
.0;
let trait_data = trait_.trait_items(self.0.interner.db());
let id =
impl_id.0.impl_items(self.0.interner.db()).items.iter().find_map(|item| -> Option<_> {
match item {
(_, AssocItemId::TypeAliasId(type_alias)) => {
let name = &self.0.interner.db().type_alias_signature(*type_alias).name;
let found_trait_assoc_id = trait_data.associated_type_by_name(name)?;
(found_trait_assoc_id == trait_assoc_id).then_some(*type_alias)
}
_ => None,
}
});
Ok(id.map(SolverDefId::TypeAliasId))
let impl_items = impl_id.0.impl_items(self.0.interner.db());
let id = match trait_assoc_def_id {
SolverDefId::TypeAliasId(trait_assoc_id) => {
let trait_assoc_data = self.0.interner.db.type_alias_signature(trait_assoc_id);
impl_items
.items
.iter()
.find_map(|(impl_assoc_name, impl_assoc_id)| {
if let AssocItemId::TypeAliasId(impl_assoc_id) = *impl_assoc_id
&& *impl_assoc_name == trait_assoc_data.name
{
Some(impl_assoc_id)
} else {
None
}
})
.map(SolverDefId::TypeAliasId)
}
SolverDefId::ConstId(trait_assoc_id) => {
let trait_assoc_data = self.0.interner.db.const_signature(trait_assoc_id);
let trait_assoc_name = trait_assoc_data
.name
.as_ref()
.expect("unnamed consts should not get passed to the solver");
impl_items
.items
.iter()
.find_map(|(impl_assoc_name, impl_assoc_id)| {
if let AssocItemId::ConstId(impl_assoc_id) = *impl_assoc_id
&& impl_assoc_name == trait_assoc_name
{
Some(impl_assoc_id)
} else {
None
}
})
.map(SolverDefId::ConstId)
}
_ => panic!("Unexpected SolverDefId"),
};
Ok(id)
}
fn is_transmutable(
@ -200,9 +225,9 @@ impl<'db> SolverDelegate for SolverContext<'db> {
SolverDefId::StaticId(c) => GeneralConstId::StaticId(c),
_ => unreachable!(),
};
let subst = uv.args.to_chalk(self.interner);
let subst = uv.args;
let ec = self.cx().db.const_eval(c, subst, None).ok()?;
Some(ec.to_nextsolver(self.interner))
Some(ec)
}
fn compute_goal_fast_path(

View file

@ -8,11 +8,10 @@ use hir_def::{AdtId, GenericDefId, TypeOrConstParamId, TypeParamId};
use intern::{Interned, Symbol, sym};
use rustc_abi::{Float, Integer, Size};
use rustc_ast_ir::{Mutability, try_visit, visit::VisitorResult};
use rustc_type_ir::TyVid;
use rustc_type_ir::{
BoundVar, ClosureKind, CollectAndApply, FlagComputation, Flags, FloatTy, FloatVid, InferTy,
IntTy, IntVid, Interner, TypeFoldable, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable,
TypeVisitableExt, TypeVisitor, UintTy, WithCachedTypeInfo,
IntTy, IntVid, Interner, TyVid, TypeFoldable, TypeSuperFoldable, TypeSuperVisitable,
TypeVisitable, TypeVisitableExt, TypeVisitor, UintTy, WithCachedTypeInfo,
inherent::{
Abi, AdtDef as _, BoundVarLike, Const as _, GenericArgs as _, IntoKind, ParamLike,
PlaceholderLike, Safety as _, SliceLike, Ty as _,
@ -24,14 +23,13 @@ use rustc_type_ir::{
use salsa::plumbing::{AsId, FromId};
use smallvec::SmallVec;
use crate::next_solver::{AdtDef, Binder};
use crate::{
FnAbi,
db::HirDatabase,
interner::InternedWrapperNoDebug,
next_solver::{
CallableIdWrapper, ClosureIdWrapper, Const, CoroutineIdWrapper, FnSig, GenericArg,
PolyFnSig, TypeAliasIdWrapper,
AdtDef, Binder, CallableIdWrapper, ClosureIdWrapper, Const, CoroutineIdWrapper, FnSig,
GenericArg, PolyFnSig, Region, TypeAliasIdWrapper,
abi::Safety,
util::{CoroutineArgsExt, IntegerTypeExt},
},
@ -392,7 +390,7 @@ impl<'db> Ty<'db> {
/// Whether the type contains some non-lifetime, aka. type or const, error type.
pub fn references_non_lt_error(self) -> bool {
self.references_error() && self.visit_with(&mut ReferencesNonLifetimeError).is_break()
references_non_lt_error(&self)
}
pub fn callable_sig(self, interner: DbInterner<'db>) -> Option<Binder<'db, FnSig<'db>>> {
@ -409,6 +407,13 @@ impl<'db> Ty<'db> {
}
}
pub fn as_reference(self) -> Option<(Ty<'db>, Region<'db>, Mutability)> {
match self.kind() {
TyKind::Ref(lifetime, ty, mutability) => Some((ty, lifetime, mutability)),
_ => None,
}
}
pub fn as_reference_or_ptr(self) -> Option<(Ty<'db>, Rawness, Mutability)> {
match self.kind() {
TyKind::Ref(_, ty, mutability) => Some((ty, Rawness::Ref, mutability)),
@ -417,6 +422,18 @@ impl<'db> Ty<'db> {
}
}
pub fn strip_references(self) -> Ty<'db> {
let mut t = self;
while let TyKind::Ref(_lifetime, ty, _mutability) = t.kind() {
t = ty;
}
t
}
pub fn strip_reference(self) -> Ty<'db> {
self.as_reference().map_or(self, |(ty, _, _)| ty)
}
/// Replace infer vars with errors.
///
/// This needs to be called for every type that may contain infer vars and is yielded to outside inference,
@ -428,6 +445,10 @@ impl<'db> Ty<'db> {
}
}
pub fn references_non_lt_error<'db, T: TypeVisitableExt<DbInterner<'db>>>(t: &T) -> bool {
t.references_error() && t.visit_with(&mut ReferencesNonLifetimeError).is_break()
}
struct ReferencesNonLifetimeError;
impl<'db> TypeVisitor<DbInterner<'db>> for ReferencesNonLifetimeError {

View file

@ -20,11 +20,10 @@ use hir_expand::name::Name;
use intern::sym;
use rustc_abi::TargetDataLayout;
use rustc_hash::FxHashSet;
use rustc_type_ir::inherent::{GenericArgs, IntoKind, SliceLike};
use rustc_type_ir::inherent::{IntoKind, SliceLike};
use smallvec::{SmallVec, smallvec};
use span::Edition;
use crate::next_solver::mapping::NextSolverToChalk;
use crate::{
ChalkTraitId, Const, ConstScalar, Interner, Substitution, TargetFeatures, TraitRef,
TraitRefExt, Ty,
@ -34,7 +33,7 @@ use crate::{
mir::pad16,
next_solver::{
DbInterner,
mapping::{ChalkToNextSolver, convert_args_for_result},
mapping::{ChalkToNextSolver, NextSolverToChalk, convert_args_for_result},
},
to_chalk_trait_id,
};
@ -196,15 +195,6 @@ pub(super) fn associated_type_by_name_including_super_traits(
pub(crate) struct ClosureSubst<'a>(pub(crate) &'a Substitution);
impl<'a> ClosureSubst<'a> {
pub(crate) fn parent_subst(&self, db: &dyn HirDatabase) -> Substitution {
let interner = DbInterner::new_with(db, None, None);
let subst =
<Substitution as ChalkToNextSolver<crate::next_solver::GenericArgs<'_>>>::to_nextsolver(
self.0, interner,
);
subst.split_closure_args().parent_args.to_chalk(interner)
}
pub(crate) fn sig_ty(&self, db: &dyn HirDatabase) -> Ty {
let interner = DbInterner::new_with(db, None, None);
let subst =
@ -310,10 +300,12 @@ impl FallibleTypeFolder<Interner> for UnevaluatedConstEvaluatorFolder<'_> {
if let chalk_ir::ConstValue::Concrete(c) = &constant.data(Interner).value
&& let ConstScalar::UnevaluatedConst(id, subst) = &c.interned
{
if let Ok(eval) = self.db.const_eval(*id, subst.clone(), None) {
return Ok(eval);
let interner = DbInterner::conjure();
if let Ok(eval) = self.db.const_eval(*id, subst.to_nextsolver(interner), None) {
return Ok(eval.to_chalk(interner));
} else {
return Ok(unknown_const(constant.data(Interner).ty.clone()));
return Ok(unknown_const(constant.data(Interner).ty.to_nextsolver(interner))
.to_chalk(interner));
}
}
Ok(constant)

View file

@ -75,7 +75,7 @@ use hir_ty::{
GenericArgData, Interner, ParamKind, ProjectionTy, QuantifiedWhereClause, Scalar, Substitution,
TraitEnvironment, TraitRefExt, Ty, TyBuilder, TyDefId, TyExt, TyKind, TyLoweringDiagnostic,
ValueTyDefId, WhereClause, all_super_traits, autoderef, check_orphan_rules,
consteval::{ConstExt, try_const_usize, unknown_const_as_generic},
consteval_chalk::{ConstExt, try_const_usize, unknown_const_as_generic},
diagnostics::BodyValidationDiagnostic,
direct_super_traits, error_lifetime, known_const_to_ast,
layout::{Layout as TyLayout, RustcEnumVariantIdx, RustcFieldIdx, TagEncoding},
@ -2153,8 +2153,11 @@ impl DefWithBody {
mir::MirSpan::Unknown => continue,
};
acc.push(
MovedOutOfRef { ty: Type::new_for_crate(krate, moof.ty.clone()), span }
.into(),
MovedOutOfRef {
ty: Type::new_for_crate(krate, moof.ty.to_chalk(interner)),
span,
}
.into(),
)
}
let mol = &borrowck_result.mutability_of_locals;
@ -2649,9 +2652,10 @@ impl Function {
db: &dyn HirDatabase,
span_formatter: impl Fn(FileId, TextRange) -> String,
) -> Result<String, ConstEvalError<'_>> {
let interner = DbInterner::new_with(db, None, None);
let body = db.monomorphized_mir_body(
self.id.into(),
Substitution::empty(Interner),
GenericArgs::new_from_iter(interner, []),
db.trait_environment(self.id.into()),
)?;
let (result, output) = interpret_mir(db, body, false, None)?;
@ -2933,8 +2937,10 @@ impl Const {
/// Evaluate the constant.
pub fn eval(self, db: &dyn HirDatabase) -> Result<EvaluatedConst, ConstEvalError<'_>> {
db.const_eval(self.id.into(), Substitution::empty(Interner), None)
.map(|it| EvaluatedConst { const_: it, def: self.id.into() })
let interner = DbInterner::new_with(db, None, None);
let ty = db.value_ty(self.id.into()).unwrap().instantiate_identity().to_chalk(interner);
db.const_eval(self.id.into(), GenericArgs::new_from_iter(interner, []), None)
.map(|it| EvaluatedConst { const_: it.to_chalk(interner), def: self.id.into(), ty })
}
}
@ -2947,6 +2953,7 @@ impl HasVisibility for Const {
pub struct EvaluatedConst {
def: DefWithBodyId,
const_: hir_ty::Const,
ty: hir_ty::Ty,
}
impl EvaluatedConst {
@ -2955,6 +2962,7 @@ impl EvaluatedConst {
}
pub fn render_debug<'db>(&self, db: &'db dyn HirDatabase) -> Result<String, MirEvalError<'db>> {
let interner = DbInterner::new_with(db, None, None);
let data = self.const_.data(Interner);
if let TyKind::Scalar(s) = data.ty.kind(Interner)
&& matches!(s, Scalar::Int(_) | Scalar::Uint(_))
@ -2972,7 +2980,12 @@ impl EvaluatedConst {
return Ok(result);
}
}
mir::render_const_using_debug_impl(db, self.def, &self.const_)
mir::render_const_using_debug_impl(
db,
self.def,
self.const_.to_nextsolver(interner),
self.ty.to_nextsolver(interner),
)
}
}
@ -3011,8 +3024,10 @@ impl Static {
/// Evaluate the static initializer.
pub fn eval(self, db: &dyn HirDatabase) -> Result<EvaluatedConst, ConstEvalError<'_>> {
db.const_eval(self.id.into(), Substitution::empty(Interner), None)
.map(|it| EvaluatedConst { const_: it, def: self.id.into() })
let interner = DbInterner::new_with(db, None, None);
let ty = db.value_ty(self.id.into()).unwrap().instantiate_identity().to_chalk(interner);
db.const_eval(self.id.into(), GenericArgs::new_from_iter(interner, []), None)
.map(|it| EvaluatedConst { const_: it.to_chalk(interner), def: self.id.into(), ty })
}
}

View file

@ -35,7 +35,6 @@ use hir_expand::{
mod_path::{ModPath, PathKind, path},
name::{AsName, Name},
};
use hir_ty::next_solver::GenericArgs;
use hir_ty::{
Adjustment, AliasTy, InferenceResult, Interner, LifetimeElisionKind, ProjectionTy,
Substitution, ToChalk, TraitEnvironment, Ty, TyKind, TyLoweringContext,
@ -47,7 +46,8 @@ use hir_ty::{
lang_items::lang_items_for_bin_op,
method_resolution,
next_solver::{
DbInterner,
DbInterner, GenericArgs, TypingMode,
infer::DbInternerInferExt,
mapping::{ChalkToNextSolver, NextSolverToChalk},
},
};
@ -1439,12 +1439,9 @@ impl<'db> SourceAnalyzer<'db> {
None => return (const_id, subs),
};
let env = db.trait_environment_for_body(owner);
method_resolution::lookup_impl_const(
DbInterner::new_with(db, None, None),
env,
const_id,
subs,
)
let interner = DbInterner::new_with(db, Some(env.krate), env.block);
let infcx = interner.infer_ctxt().build(TypingMode::PostAnalysis);
method_resolution::lookup_impl_const(&infcx, env, const_id, subs)
}
fn lang_trait_fn(

View file

@ -6306,6 +6306,8 @@ const FOO$0: (&str, &str) = {
);
}
// FIXME(next-solver): this fails to normalize the const, probably due to the solver
// refusing to give the impl because of the error type.
#[test]
fn hover_const_eval_in_generic_trait() {
// Doesn't compile, but we shouldn't crash.
@ -6327,12 +6329,16 @@ fn test() {
*FOO*
```rust
ra_test_fixture::S
ra_test_fixture::Trait
```
```rust
const FOO: bool = true
const FOO: bool = false
```
---
`Self` = `S<{unknown}>`
"#]],
);
}

View file

@ -6,7 +6,7 @@
//! }
//! ```
use hir::{
ChalkTyInterner, DefWithBody,
DefWithBody,
db::{DefDatabase as _, HirDatabase as _},
mir::{MirSpan, TerminatorKind},
};
@ -46,7 +46,7 @@ pub(super) fn hints(
if !place.projection.is_empty() {
continue; // Ignore complex cases for now
}
if mir.locals[place.local].ty.adt_id(ChalkTyInterner).is_none() {
if mir.locals[place.local].ty.as_adt().is_none() {
continue; // Arguably only ADTs have significant drop impls
}
let Some(&binding_idx) = local_to_binding.get(place.local) else {

View file

@ -0,0 +1,19 @@
[package]
name = "macros"
version = "0.0.0"
repository.workspace = true
description = "Proc macros for rust-analyzer."
authors.workspace = true
edition.workspace = true
license.workspace = true
rust-version.workspace = true
[lib]
proc-macro = true
[dependencies]
proc-macro2 = "1"
quote = "1"
syn = "2.0.9"
synstructure = "0.13.0"

View file

@ -0,0 +1,164 @@
//! Proc macros for rust-analyzer.
use quote::{ToTokens, quote};
use syn::parse_quote;
use synstructure::decl_derive;
decl_derive!(
[TypeFoldable, attributes(type_foldable)] =>
/// Derives `TypeFoldable` for the annotated `struct` or `enum` (`union` is not supported).
///
/// The fold will produce a value of the same struct or enum variant as the input, with
/// each field respectively folded using the `TypeFoldable` implementation for its type.
/// However, if a field of a struct or an enum variant is annotated with
/// `#[type_foldable(identity)]` then that field will retain its incumbent value (and its
/// type is not required to implement `TypeFoldable`).
type_foldable_derive
);
decl_derive!(
[TypeVisitable, attributes(type_visitable)] =>
/// Derives `TypeVisitable` for the annotated `struct` or `enum` (`union` is not supported).
///
/// Each field of the struct or enum variant will be visited in definition order, using the
/// `TypeVisitable` implementation for its type. However, if a field of a struct or an enum
/// variant is annotated with `#[type_visitable(ignore)]` then that field will not be
/// visited (and its type is not required to implement `TypeVisitable`).
type_visitable_derive
);
fn type_visitable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream {
if let syn::Data::Union(_) = s.ast().data {
panic!("cannot derive on union")
}
// ignore fields with #[type_visitable(ignore)]
s.filter(|bi| {
let mut ignored = false;
bi.ast().attrs.iter().for_each(|attr| {
if !attr.path().is_ident("type_visitable") {
return;
}
let _ = attr.parse_nested_meta(|nested| {
if nested.path.is_ident("ignore") {
ignored = true;
}
Ok(())
});
});
!ignored
});
if !s.ast().generics.lifetimes().any(|lt| lt.lifetime.ident == "db") {
s.add_impl_generic(parse_quote! { 'db });
}
s.add_bounds(synstructure::AddBounds::Generics);
let body_visit = s.each(|bind| {
quote! {
match ::rustc_type_ir::VisitorResult::branch(
::rustc_type_ir::TypeVisitable::visit_with(#bind, __visitor)
) {
::core::ops::ControlFlow::Continue(()) => {},
::core::ops::ControlFlow::Break(r) => {
return ::rustc_type_ir::VisitorResult::from_residual(r);
},
}
}
});
s.bind_with(|_| synstructure::BindStyle::Move);
s.bound_impl(
quote!(::rustc_type_ir::TypeVisitable<::hir_ty::next_solver::DbInterner<'db>>),
quote! {
fn visit_with<__V: ::rustc_type_ir::TypeVisitor<::hir_ty::next_solver::DbInterner<'db>>>(
&self,
__visitor: &mut __V
) -> __V::Result {
match *self { #body_visit }
<__V::Result as ::rustc_type_ir::VisitorResult>::output()
}
},
)
}
fn type_foldable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream {
if let syn::Data::Union(_) = s.ast().data {
panic!("cannot derive on union")
}
if !s.ast().generics.lifetimes().any(|lt| lt.lifetime.ident == "db") {
s.add_impl_generic(parse_quote! { 'db });
}
s.add_bounds(synstructure::AddBounds::Generics);
s.bind_with(|_| synstructure::BindStyle::Move);
let try_body_fold = s.each_variant(|vi| {
let bindings = vi.bindings();
vi.construct(|_, index| {
let bind = &bindings[index];
// retain value of fields with #[type_foldable(identity)]
if has_ignore_attr(&bind.ast().attrs, "type_foldable", "identity") {
bind.to_token_stream()
} else {
quote! {
::rustc_type_ir::TypeFoldable::try_fold_with(#bind, __folder)?
}
}
})
});
let body_fold = s.each_variant(|vi| {
let bindings = vi.bindings();
vi.construct(|_, index| {
let bind = &bindings[index];
// retain value of fields with #[type_foldable(identity)]
if has_ignore_attr(&bind.ast().attrs, "type_foldable", "identity") {
bind.to_token_stream()
} else {
quote! {
::rustc_type_ir::TypeFoldable::fold_with(#bind, __folder)
}
}
})
});
s.bound_impl(
quote!(::rustc_type_ir::TypeFoldable<::hir_ty::next_solver::DbInterner<'db>>),
quote! {
fn try_fold_with<__F: ::rustc_type_ir::FallibleTypeFolder<::hir_ty::next_solver::DbInterner<'db>>>(
self,
__folder: &mut __F
) -> Result<Self, __F::Error> {
Ok(match self { #try_body_fold })
}
fn fold_with<__F: ::rustc_type_ir::TypeFolder<::hir_ty::next_solver::DbInterner<'db>>>(
self,
__folder: &mut __F
) -> Self {
match self { #body_fold }
}
},
)
}
fn has_ignore_attr(attrs: &[syn::Attribute], name: &'static str, meta: &'static str) -> bool {
let mut ignored = false;
attrs.iter().for_each(|attr| {
if !attr.path().is_ident(name) {
return;
}
let _ = attr.parse_nested_meta(|nested| {
if nested.path.is_ident(meta) {
ignored = true;
}
Ok(())
});
});
ignored
}