diff --git a/crates/hir-def/src/body.rs b/crates/hir-def/src/body.rs index c2a27cd82ba..d4b4cbe6c7c 100644 --- a/crates/hir-def/src/body.rs +++ b/crates/hir-def/src/body.rs @@ -165,7 +165,7 @@ impl Body { }; let expander = Expander::new(db, file_id, module); let (mut body, source_map) = - Body::new(db, expander, params, body, module.krate, is_async_fn); + Body::new(db, def, expander, params, body, module.krate, is_async_fn); body.shrink_to_fit(); (Arc::new(body), Arc::new(source_map)) @@ -189,13 +189,14 @@ impl Body { fn new( db: &dyn DefDatabase, + owner: DefWithBodyId, expander: Expander, params: Option<(ast::ParamList, impl Iterator)>, body: Option, krate: CrateId, is_async_fn: bool, ) -> (Body, BodySourceMap) { - lower::lower(db, expander, params, body, krate, is_async_fn) + lower::lower(db, owner, expander, params, body, krate, is_async_fn) } fn shrink_to_fit(&mut self) { diff --git a/crates/hir-def/src/body/lower.rs b/crates/hir-def/src/body/lower.rs index d92698a938a..e9cb51d5bf8 100644 --- a/crates/hir-def/src/body/lower.rs +++ b/crates/hir-def/src/body/lower.rs @@ -40,11 +40,12 @@ use crate::{ nameres::{DefMap, MacroSubNs}, path::{GenericArgs, Path}, type_ref::{Mutability, Rawness, TypeRef}, - AdtId, BlockId, BlockLoc, ModuleDefId, UnresolvedMacro, + AdtId, BlockId, BlockLoc, DefWithBodyId, ModuleDefId, UnresolvedMacro, }; pub(super) fn lower( db: &dyn DefDatabase, + owner: DefWithBodyId, expander: Expander, params: Option<(ast::ParamList, impl Iterator)>, body: Option, @@ -53,6 +54,7 @@ pub(super) fn lower( ) -> (Body, BodySourceMap) { ExprCollector { db, + owner, krate, def_map: expander.module.def_map(db), source_map: BodySourceMap::default(), @@ -80,6 +82,7 @@ pub(super) fn lower( struct ExprCollector<'a> { db: &'a dyn DefDatabase, expander: Expander, + owner: DefWithBodyId, def_map: Arc, ast_id_map: Arc, krate: CrateId, @@ -269,16 +272,13 @@ impl ExprCollector<'_> { } Some(ast::BlockModifier::Const(_)) => { self.with_label_rib(RibKind::Constant, |this| { - this.collect_as_a_binding_owner_bad( - |this| { - this.collect_block_(e, |id, statements, tail| Expr::Const { - id, - statements, - tail, - }) - }, - syntax_ptr, - ) + let (result_expr_id, prev_binding_owner) = + this.initialize_binding_owner(syntax_ptr); + let inner_expr = this.collect_block(e); + let x = this.db.intern_anonymous_const((this.owner, inner_expr)); + this.body.exprs[result_expr_id] = Expr::Const(x); + this.current_binding_owner = prev_binding_owner; + result_expr_id }) } None => self.collect_block(e), diff --git a/crates/hir-def/src/body/pretty.rs b/crates/hir-def/src/body/pretty.rs index c3bd99b9487..8fe304aa14d 100644 --- a/crates/hir-def/src/body/pretty.rs +++ b/crates/hir-def/src/body/pretty.rs @@ -436,8 +436,8 @@ impl<'a> Printer<'a> { Expr::Async { id: _, statements, tail } => { self.print_block(Some("async "), statements, tail); } - Expr::Const { id: _, statements, tail } => { - self.print_block(Some("const "), statements, tail); + Expr::Const(id) => { + w!(self, "const {{ /* {id:?} */ }}"); } } } diff --git a/crates/hir-def/src/body/scope.rs b/crates/hir-def/src/body/scope.rs index 0340bd18c2a..b81130c0c24 100644 --- a/crates/hir-def/src/body/scope.rs +++ b/crates/hir-def/src/body/scope.rs @@ -218,9 +218,10 @@ fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut ExprScopes, scope scopes.set_scope(expr, scope); compute_block_scopes(statements, *tail, body, scopes, &mut scope); } - Expr::Unsafe { id, statements, tail } - | Expr::Async { id, statements, tail } - | Expr::Const { id, statements, tail } => { + Expr::Const(_) => { + // FIXME: This is broken. + } + Expr::Unsafe { id, statements, tail } | Expr::Async { id, statements, tail } => { let mut scope = scopes.new_block_scope(*scope, *id, None); // Overwrite the old scope for the block expr, so that every block scope can be found // via the block itself (important for blocks that only contain items, no expressions). diff --git a/crates/hir-def/src/db.rs b/crates/hir-def/src/db.rs index 98db5edccc6..c3721a94f67 100644 --- a/crates/hir-def/src/db.rs +++ b/crates/hir-def/src/db.rs @@ -16,16 +16,17 @@ use crate::{ TraitAliasData, TraitData, TypeAliasData, }, generics::GenericParams, + hir::ExprId, import_map::ImportMap, item_tree::{AttrOwner, ItemTree}, lang_item::{LangItem, LangItemTarget, LangItems}, nameres::{diagnostics::DefDiagnostic, DefMap}, visibility::{self, Visibility}, - AttrDefId, BlockId, BlockLoc, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc, ExternBlockId, - ExternBlockLoc, FunctionId, FunctionLoc, GenericDefId, ImplId, ImplLoc, LocalEnumVariantId, - LocalFieldId, Macro2Id, Macro2Loc, MacroRulesId, MacroRulesLoc, ProcMacroId, ProcMacroLoc, - StaticId, StaticLoc, StructId, StructLoc, TraitAliasId, TraitAliasLoc, TraitId, TraitLoc, - TypeAliasId, TypeAliasLoc, UnionId, UnionLoc, VariantId, + AnonymousConstId, AttrDefId, BlockId, BlockLoc, ConstId, ConstLoc, DefWithBodyId, EnumId, + EnumLoc, ExternBlockId, ExternBlockLoc, FunctionId, FunctionLoc, GenericDefId, ImplId, ImplLoc, + LocalEnumVariantId, LocalFieldId, Macro2Id, Macro2Loc, MacroRulesId, MacroRulesLoc, + ProcMacroId, ProcMacroLoc, StaticId, StaticLoc, StructId, StructLoc, TraitAliasId, + TraitAliasLoc, TraitId, TraitLoc, TypeAliasId, TypeAliasLoc, UnionId, UnionLoc, VariantId, }; #[salsa::query_group(InternDatabaseStorage)] @@ -60,6 +61,8 @@ pub trait InternDatabase: SourceDatabase { fn intern_proc_macro(&self, loc: ProcMacroLoc) -> ProcMacroId; #[salsa::interned] fn intern_macro_rules(&self, loc: MacroRulesLoc) -> MacroRulesId; + #[salsa::interned] + fn intern_anonymous_const(&self, id: (DefWithBodyId, ExprId)) -> AnonymousConstId; } #[salsa::query_group(DefDatabaseStorage)] diff --git a/crates/hir-def/src/hir.rs b/crates/hir-def/src/hir.rs index 1f4577629cc..f531109cd88 100644 --- a/crates/hir-def/src/hir.rs +++ b/crates/hir-def/src/hir.rs @@ -26,7 +26,7 @@ use crate::{ builtin_type::{BuiltinFloat, BuiltinInt, BuiltinUint}, path::{GenericArgs, Path}, type_ref::{Mutability, Rawness, TypeRef}, - BlockId, + AnonymousConstId, BlockId, }; pub use syntax::ast::{ArithOp, BinaryOp, CmpOp, LogicOp, Ordering, RangeOp, UnaryOp}; @@ -169,11 +169,7 @@ pub enum Expr { statements: Box<[Statement]>, tail: Option, }, - Const { - id: Option, - statements: Box<[Statement]>, - tail: Option, - }, + Const(AnonymousConstId), Unsafe { id: Option, statements: Box<[Statement]>, @@ -355,10 +351,10 @@ impl Expr { Expr::Let { expr, .. } => { f(*expr); } + Expr::Const(_) => (), Expr::Block { statements, tail, .. } | Expr::Unsafe { statements, tail, .. } - | Expr::Async { statements, tail, .. } - | Expr::Const { statements, tail, .. } => { + | Expr::Async { statements, tail, .. } => { for stmt in statements.iter() { match stmt { Statement::Let { initializer, else_branch, .. } => { diff --git a/crates/hir-def/src/lib.rs b/crates/hir-def/src/lib.rs index 66388552322..9cd3dfd6f7c 100644 --- a/crates/hir-def/src/lib.rs +++ b/crates/hir-def/src/lib.rs @@ -59,7 +59,11 @@ mod pretty; use std::hash::{Hash, Hasher}; -use base_db::{impl_intern_key, salsa, CrateId, ProcMacroKind}; +use base_db::{ + impl_intern_key, + salsa::{self, InternId}, + CrateId, ProcMacroKind, +}; use hir_expand::{ ast_id_map::FileAstId, attrs::{Attr, AttrId, AttrInput}, @@ -472,6 +476,46 @@ impl_from!( for ModuleDefId ); +// FIXME: make this a DefWithBodyId +#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] +pub struct AnonymousConstId(InternId); +impl_intern_key!(AnonymousConstId); + +/// A constant, which might appears as a const item, an annonymous const block in expressions +/// or patterns, or as a constant in types with const generics. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum GeneralConstId { + ConstId(ConstId), + AnonymousConstId(AnonymousConstId), +} + +impl_from!(ConstId, AnonymousConstId for GeneralConstId); + +impl GeneralConstId { + pub fn generic_def(self, db: &dyn db::DefDatabase) -> Option { + match self { + GeneralConstId::ConstId(x) => Some(x.into()), + GeneralConstId::AnonymousConstId(x) => { + let (parent, _) = db.lookup_intern_anonymous_const(x); + parent.as_generic_def_id() + } + } + } + + pub fn name(self, db: &dyn db::DefDatabase) -> String { + match self { + GeneralConstId::ConstId(const_id) => db + .const_data(const_id) + .name + .as_ref() + .and_then(|x| x.as_str()) + .unwrap_or("_") + .to_owned(), + GeneralConstId::AnonymousConstId(id) => format!("{{anonymous const {id:?}}}"), + } + } +} + /// The defs which have a body. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum DefWithBodyId { diff --git a/crates/hir-def/src/pretty.rs b/crates/hir-def/src/pretty.rs index 2d45c8c8da1..026750b62fa 100644 --- a/crates/hir-def/src/pretty.rs +++ b/crates/hir-def/src/pretty.rs @@ -12,6 +12,9 @@ use crate::{ }; pub(crate) fn print_path(path: &Path, buf: &mut dyn Write) -> fmt::Result { + if let Path::LangItem(x) = path { + return write!(buf, "$lang_item::{x:?}"); + } match path.type_anchor() { Some(anchor) => { write!(buf, "<")?; diff --git a/crates/hir-expand/src/name.rs b/crates/hir-expand/src/name.rs index 4e688c431ae..d6bad1a48c1 100644 --- a/crates/hir-expand/src/name.rs +++ b/crates/hir-expand/src/name.rs @@ -376,6 +376,7 @@ pub mod known { deref, div_assign, div, + drop, fn_mut, fn_once, future_trait, diff --git a/crates/hir-ty/src/chalk_ext.rs b/crates/hir-ty/src/chalk_ext.rs index 33c598fe364..3fb3de5ba8d 100644 --- a/crates/hir-ty/src/chalk_ext.rs +++ b/crates/hir-ty/src/chalk_ext.rs @@ -34,6 +34,7 @@ pub trait TyExt { fn as_closure(&self) -> Option; fn as_fn_def(&self, db: &dyn HirDatabase) -> Option; fn as_reference(&self) -> Option<(&Ty, Lifetime, Mutability)>; + fn as_raw_ptr(&self) -> Option<(&Ty, Mutability)>; fn as_reference_or_ptr(&self) -> Option<(&Ty, Rawness, Mutability)>; fn as_generic_def(&self, db: &dyn HirDatabase) -> Option; @@ -146,6 +147,7 @@ impl TyExt for Ty { Some(CallableDefId::StructId(_) | CallableDefId::EnumVariantId(_)) | None => None, } } + fn as_reference(&self) -> Option<(&Ty, Lifetime, Mutability)> { match self.kind(Interner) { TyKind::Ref(mutability, lifetime, ty) => Some((ty, lifetime.clone(), *mutability)), @@ -153,6 +155,13 @@ impl TyExt for Ty { } } + fn as_raw_ptr(&self) -> Option<(&Ty, Mutability)> { + match self.kind(Interner) { + TyKind::Raw(mutability, ty) => Some((ty, *mutability)), + _ => None, + } + } + fn as_reference_or_ptr(&self) -> Option<(&Ty, Rawness, Mutability)> { match self.kind(Interner) { TyKind::Ref(mutability, _, ty) => Some((ty, Rawness::Ref, *mutability)), diff --git a/crates/hir-ty/src/consteval.rs b/crates/hir-ty/src/consteval.rs index c3726905b67..0df2b39bbdc 100644 --- a/crates/hir-ty/src/consteval.rs +++ b/crates/hir-ty/src/consteval.rs @@ -7,10 +7,11 @@ use hir_def::{ path::Path, resolver::{Resolver, ValueNs}, type_ref::ConstRef, - DefWithBodyId, EnumVariantId, + EnumVariantId, GeneralConstId, StaticId, }; use la_arena::{Idx, RawIdx}; use stdx::never; +use triomphe::Arc; use crate::{ db::HirDatabase, infer::InferenceContext, layout::layout_of_ty, lower::ParamLoweringMode, @@ -158,13 +159,17 @@ pub fn usize_const(db: &dyn HirDatabase, value: Option, krate: CrateId) -> ) } -pub fn try_const_usize(c: &Const) -> Option { +pub fn try_const_usize(db: &dyn HirDatabase, c: &Const) -> Option { 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(x, _) => Some(u128::from_le_bytes(pad16(&x, false))), + ConstScalar::UnevaluatedConst(c, subst) => { + let ec = db.const_eval(*c, subst.clone()).ok()?; + try_const_usize(db, &ec) + } _ => None, }, } @@ -173,12 +178,20 @@ pub fn try_const_usize(c: &Const) -> Option { pub(crate) fn const_eval_recover( _: &dyn HirDatabase, _: &[String], - _: &DefWithBodyId, + _: &GeneralConstId, _: &Substitution, ) -> Result { Err(ConstEvalError::MirLowerError(MirLowerError::Loop)) } +pub(crate) fn const_eval_static_recover( + _: &dyn HirDatabase, + _: &[String], + _: &StaticId, +) -> Result { + Err(ConstEvalError::MirLowerError(MirLowerError::Loop)) +} + pub(crate) fn const_eval_discriminant_recover( _: &dyn HirDatabase, _: &[String], @@ -189,11 +202,28 @@ pub(crate) fn const_eval_discriminant_recover( pub(crate) fn const_eval_query( db: &dyn HirDatabase, - def: DefWithBodyId, + def: GeneralConstId, subst: Substitution, ) -> Result { - let body = db.mir_body(def)?; - let c = interpret_mir(db, &body, subst, false)?; + let body = match def { + GeneralConstId::ConstId(c) => db.mir_body(c.into())?, + GeneralConstId::AnonymousConstId(c) => { + let (def, root) = db.lookup_intern_anonymous_const(c); + let body = db.body(def); + let infer = db.infer(def); + Arc::new(lower_to_mir(db, def, &body, &infer, root)?) + } + }; + let c = interpret_mir(db, &body, subst, false).0?; + Ok(c) +} + +pub(crate) fn const_eval_static_query( + db: &dyn HirDatabase, + def: StaticId, +) -> Result { + let body = db.mir_body(def.into())?; + let c = interpret_mir(db, &body, Substitution::empty(Interner), false).0?; Ok(c) } @@ -216,8 +246,8 @@ pub(crate) fn const_eval_discriminant_variant( return Ok(value); } let mir_body = db.mir_body(def)?; - let c = interpret_mir(db, &mir_body, Substitution::empty(Interner), false)?; - let c = try_const_usize(&c).unwrap() as i128; + let c = interpret_mir(db, &mir_body, Substitution::empty(Interner), false).0?; + let c = try_const_usize(db, &c).unwrap() as i128; Ok(c) } @@ -241,7 +271,7 @@ pub(crate) fn eval_to_const( } let infer = ctx.clone().resolve_all(); if let Ok(mir_body) = lower_to_mir(ctx.db, ctx.owner, &ctx.body, &infer, expr) { - if let Ok(result) = interpret_mir(db, &mir_body, Substitution::empty(Interner), true) { + if let Ok(result) = interpret_mir(db, &mir_body, Substitution::empty(Interner), true).0 { return result; } } diff --git a/crates/hir-ty/src/consteval/tests.rs b/crates/hir-ty/src/consteval/tests.rs index 12b15065ddf..b4e276ea340 100644 --- a/crates/hir-ty/src/consteval/tests.rs +++ b/crates/hir-ty/src/consteval/tests.rs @@ -158,6 +158,22 @@ fn casts() { ); } +#[test] +fn raw_pointer_equality() { + check_number( + r#" + //- minicore: copy, eq + const GOAL: bool = { + let a = 2; + let p1 = a as *const i32; + let p2 = a as *const i32; + p1 == p2 + }; + "#, + 1, + ); +} + #[test] fn locals() { check_number( @@ -975,6 +991,22 @@ fn pattern_matching_literal() { ); } +#[test] +fn pattern_matching_slice() { + check_number( + r#" + //- minicore: slice, index, coerce_unsized, copy + const fn f(x: &[usize]) -> usize { + match x { + [a, b @ .., c, d] => *a + b.len() + *c + *d, + } + } + const GOAL: usize = f(&[10, 20, 3, 15, 1000, 60, 16]); + "#, + 10 + 4 + 60 + 16, + ); +} + #[test] fn pattern_matching_ergonomics() { check_number( @@ -1808,6 +1840,28 @@ fn array_and_index() { const GOAL: usize = [1, 2, 3, 4, 5].len();"#, 5, ); + check_number( + r#" + //- minicore: coerce_unsized, index, slice + const GOAL: [u16; 5] = [1, 2, 3, 4, 5];"#, + 1 + (2 << 16) + (3 << 32) + (4 << 48) + (5 << 64), + ); + check_number( + r#" + //- minicore: coerce_unsized, index, slice + const GOAL: [u16; 5] = [12; 5];"#, + 12 + (12 << 16) + (12 << 32) + (12 << 48) + (12 << 64), + ); + check_number( + r#" + //- minicore: coerce_unsized, index, slice + const LEN: usize = 4; + const GOAL: u16 = { + let x = [7; LEN]; + x[2] + }"#, + 7, + ); } #[test] @@ -1903,7 +1957,7 @@ fn enums() { "#, ); let r = eval_goal(&db, file_id).unwrap(); - assert_eq!(try_const_usize(&r), Some(1)); + assert_eq!(try_const_usize(&db, &r), Some(1)); } #[test] @@ -1931,6 +1985,29 @@ fn const_transfer_memory() { ); } +#[test] +fn anonymous_const_block() { + check_number( + r#" + extern "rust-intrinsic" { + pub fn size_of() -> usize; + } + + const fn f() -> usize { + let r = const { size_of::() }; + r + } + + const GOAL: usize = { + let x = const { 2 + const { 3 } }; + let y = f::(); + x + y + }; + "#, + 9, + ); +} + #[test] fn const_impl_assoc() { check_number( @@ -1939,9 +2016,9 @@ fn const_impl_assoc() { impl U5 { const VAL: usize = 5; } - const GOAL: usize = U5::VAL; + const GOAL: usize = U5::VAL + ::VAL; "#, - 5, + 10, ); } @@ -1972,6 +2049,35 @@ fn const_generic_subst_assoc_const_impl() { ); } +#[test] +fn associated_types() { + check_number( + r#" + trait Tr { + type Item; + fn get_item(&self) -> Self::Item; + } + + struct X(i32); + struct Y(i32); + + impl Tr for X { + type Item = Y; + fn get_item(&self) -> Self::Item { + Y(self.0 + 2) + } + } + + fn my_get_item(x: T) -> ::Item { + x.get_item() + } + + const GOAL: i32 = my_get_item(X(3)).0; + "#, + 5, + ); +} + #[test] fn const_trait_assoc() { check_number( diff --git a/crates/hir-ty/src/consteval/tests/intrinsics.rs b/crates/hir-ty/src/consteval/tests/intrinsics.rs index 40e68823acd..0dd120e5b9e 100644 --- a/crates/hir-ty/src/consteval/tests/intrinsics.rs +++ b/crates/hir-ty/src/consteval/tests/intrinsics.rs @@ -289,3 +289,31 @@ fn copy() { 19, ); } + +#[test] +fn ctpop() { + check_number( + r#" + extern "rust-intrinsic" { + pub fn ctpop(x: T) -> T; + } + + const GOAL: i64 = ctpop(-29); + "#, + 61, + ); +} + +#[test] +fn cttz() { + check_number( + r#" + extern "rust-intrinsic" { + pub fn cttz(x: T) -> T; + } + + const GOAL: i64 = cttz(-24); + "#, + 3, + ); +} diff --git a/crates/hir-ty/src/db.rs b/crates/hir-ty/src/db.rs index b37d90c7589..dcdeea02aee 100644 --- a/crates/hir-ty/src/db.rs +++ b/crates/hir-ty/src/db.rs @@ -6,8 +6,8 @@ use std::sync; use base_db::{impl_intern_key, salsa, CrateId, Upcast}; use hir_def::{ db::DefDatabase, hir::ExprId, layout::TargetDataLayout, AdtId, BlockId, ConstParamId, - DefWithBodyId, EnumVariantId, FunctionId, GenericDefId, ImplId, LifetimeParamId, LocalFieldId, - TypeOrConstParamId, VariantId, + DefWithBodyId, EnumVariantId, FunctionId, GeneralConstId, GenericDefId, ImplId, + LifetimeParamId, LocalFieldId, StaticId, TypeOrConstParamId, VariantId, }; use la_arena::ArenaMap; use smallvec::SmallVec; @@ -60,7 +60,12 @@ pub trait HirDatabase: DefDatabase + Upcast { #[salsa::invoke(crate::consteval::const_eval_query)] #[salsa::cycle(crate::consteval::const_eval_recover)] - fn const_eval(&self, def: DefWithBodyId, subst: Substitution) -> Result; + fn const_eval(&self, def: GeneralConstId, subst: Substitution) + -> Result; + + #[salsa::invoke(crate::consteval::const_eval_static_query)] + #[salsa::cycle(crate::consteval::const_eval_static_recover)] + fn const_eval_static(&self, def: StaticId) -> Result; #[salsa::invoke(crate::consteval::const_eval_discriminant_variant)] #[salsa::cycle(crate::consteval::const_eval_discriminant_recover)] diff --git a/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs index 3cfe78141d4..038c2eafd2e 100644 --- a/crates/hir-ty/src/display.rs +++ b/crates/hir-ty/src/display.rs @@ -420,13 +420,8 @@ impl HirDisplay for Const { ConstValue::Concrete(c) => match &c.interned { ConstScalar::Bytes(b, m) => render_const_scalar(f, &b, m, &data.ty), ConstScalar::UnevaluatedConst(c, parameters) => { - let const_data = f.db.const_data(*c); - write!( - f, - "{}", - const_data.name.as_ref().and_then(|x| x.as_str()).unwrap_or("_") - )?; - hir_fmt_generics(f, parameters, Some((*c).into()))?; + write!(f, "{}", c.name(f.db.upcast()))?; + hir_fmt_generics(f, parameters, c.generic_def(f.db.upcast()))?; Ok(()) } ConstScalar::Unknown => f.write_char('_'), diff --git a/crates/hir-ty/src/infer/closure.rs b/crates/hir-ty/src/infer/closure.rs index 6d03c76eb6a..cd8bd10255d 100644 --- a/crates/hir-ty/src/infer/closure.rs +++ b/crates/hir-ty/src/infer/closure.rs @@ -123,9 +123,14 @@ impl HirPlace { fn ty(&self, ctx: &mut InferenceContext<'_>) -> Ty { let mut ty = ctx.table.resolve_completely(ctx.result[self.local].clone()); for p in &self.projections { - ty = p.projected_ty(ty, ctx.db, |_, _, _| { - unreachable!("Closure field only happens in MIR"); - }); + ty = p.projected_ty( + ty, + ctx.db, + |_, _, _| { + unreachable!("Closure field only happens in MIR"); + }, + ctx.owner.module(ctx.db.upcast()).krate(), + ); } ty.clone() } @@ -447,7 +452,6 @@ impl InferenceContext<'_> { } } Expr::Async { statements, tail, .. } - | Expr::Const { statements, tail, .. } | Expr::Unsafe { statements, tail, .. } | Expr::Block { statements, tail, .. } => { for s in statements.iter() { @@ -605,6 +609,7 @@ impl InferenceContext<'_> { | Expr::Continue { .. } | Expr::Path(_) | Expr::Literal(_) + | Expr::Const(_) | Expr::Underscore => (), } } diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs index 96218e4fb09..361f78329e8 100644 --- a/crates/hir-ty/src/infer/expr.rs +++ b/crates/hir-ty/src/infer/expr.rs @@ -6,7 +6,7 @@ use std::{ }; use chalk_ir::{ - cast::Cast, fold::Shift, DebruijnIndex, GenericArgData, Mutability, TyKind, TyVariableKind, + cast::Cast, fold::Shift, DebruijnIndex, GenericArgData, Mutability, TyVariableKind, }; use hir_def::{ generics::TypeOrConstParamData, @@ -39,7 +39,7 @@ use crate::{ traits::FnTrait, utils::{generics, Generics}, Adjust, Adjustment, AdtId, AutoBorrow, Binders, CallableDefId, FnPointer, FnSig, FnSubst, - Interner, Rawness, Scalar, Substitution, TraitRef, Ty, TyBuilder, TyExt, + Interner, Rawness, Scalar, Substitution, TraitRef, Ty, TyBuilder, TyExt, TyKind, }; use super::{ @@ -164,9 +164,10 @@ impl<'a> InferenceContext<'a> { Expr::Unsafe { id, statements, tail } => { self.infer_block(tgt_expr, *id, statements, *tail, None, expected) } - Expr::Const { id, statements, tail } => { + Expr::Const(id) => { self.with_breakable_ctx(BreakableKind::Border, None, None, |this| { - this.infer_block(tgt_expr, *id, statements, *tail, None, expected) + let (_, expr) = this.db.lookup_intern_anonymous_const(*id); + this.infer_expr(expr, expected) }) .1 } diff --git a/crates/hir-ty/src/infer/mutability.rs b/crates/hir-ty/src/infer/mutability.rs index f344b0610c9..7794bd5c39f 100644 --- a/crates/hir-ty/src/infer/mutability.rs +++ b/crates/hir-ty/src/infer/mutability.rs @@ -42,10 +42,13 @@ impl<'a> InferenceContext<'a> { self.infer_mut_expr(else_branch, Mutability::Not); } } + Expr::Const(id) => { + let (_, expr) = self.db.lookup_intern_anonymous_const(*id); + self.infer_mut_expr(expr, Mutability::Not); + } Expr::Let { pat, expr } => self.infer_mut_expr(*expr, self.pat_bound_mutability(*pat)), Expr::Block { id: _, statements, tail, label: _ } | Expr::Async { id: _, statements, tail } - | Expr::Const { id: _, statements, tail } | Expr::Unsafe { id: _, statements, tail } => { for st in statements.iter() { match st { diff --git a/crates/hir-ty/src/infer/pat.rs b/crates/hir-ty/src/infer/pat.rs index 57480395110..80a0d309012 100644 --- a/crates/hir-ty/src/infer/pat.rs +++ b/crates/hir-ty/src/infer/pat.rs @@ -379,7 +379,7 @@ impl<'a> InferenceContext<'a> { if let &Some(slice_pat_id) = slice { let rest_pat_ty = match expected.kind(Interner) { TyKind::Array(_, length) => { - let len = try_const_usize(length); + let len = try_const_usize(self.db, length); let len = len.and_then(|len| len.checked_sub((prefix.len() + suffix.len()) as u128)); TyKind::Array(elem_ty.clone(), usize_const(self.db, len, self.resolver.krate())) diff --git a/crates/hir-ty/src/inhabitedness.rs b/crates/hir-ty/src/inhabitedness.rs index 25b226bb3b5..e5038543b68 100644 --- a/crates/hir-ty/src/inhabitedness.rs +++ b/crates/hir-ty/src/inhabitedness.rs @@ -82,7 +82,7 @@ impl TypeVisitor for UninhabitedFrom<'_> { 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(len) { + 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), }, diff --git a/crates/hir-ty/src/layout.rs b/crates/hir-ty/src/layout.rs index f74a2253a42..5c143cfcf94 100644 --- a/crates/hir-ty/src/layout.rs +++ b/crates/hir-ty/src/layout.rs @@ -148,7 +148,7 @@ pub fn layout_of_ty(db: &dyn HirDatabase, ty: &Ty, krate: CrateId) -> Result { - let count = try_const_usize(&count).ok_or(LayoutError::UserError( + let count = try_const_usize(db, &count).ok_or(LayoutError::UserError( "unevaluated or mistyped const generic parameter".to_string(), ))? as u64; let element = layout_of_ty(db, element, krate)?; diff --git a/crates/hir-ty/src/layout/tests.rs b/crates/hir-ty/src/layout/tests.rs index e1038c0affe..ea5186e9a9c 100644 --- a/crates/hir-ty/src/layout/tests.rs +++ b/crates/hir-ty/src/layout/tests.rs @@ -230,7 +230,8 @@ fn associated_types() { } struct Foo(::Ty); - struct Goal(Foo); + struct Bar(A::Ty); + struct Goal(Foo, Bar, ::Ty); } } diff --git a/crates/hir-ty/src/lib.rs b/crates/hir-ty/src/lib.rs index 28a2bf2838d..55803960e1a 100644 --- a/crates/hir-ty/src/lib.rs +++ b/crates/hir-ty/src/lib.rs @@ -44,7 +44,7 @@ use chalk_ir::{ NoSolution, TyData, }; use either::Either; -use hir_def::{hir::ExprId, type_ref::Rawness, ConstId, TypeOrConstParamId}; +use hir_def::{hir::ExprId, type_ref::Rawness, GeneralConstId, TypeOrConstParamId}; use hir_expand::name; use la_arena::{Arena, Idx}; use mir::{MirEvalError, VTableMap}; @@ -180,7 +180,7 @@ pub enum ConstScalar { Bytes(Vec, MemoryMap), // FIXME: this is a hack to get around chalk not being able to represent unevaluatable // constants - UnevaluatedConst(ConstId, Substitution), + UnevaluatedConst(GeneralConstId, Substitution), /// Case of an unknown value that rustc might know but we don't // FIXME: this is a hack to get around chalk not being able to represent unevaluatable // constants diff --git a/crates/hir-ty/src/method_resolution.rs b/crates/hir-ty/src/method_resolution.rs index 8d57211db07..bf1e875858a 100644 --- a/crates/hir-ty/src/method_resolution.rs +++ b/crates/hir-ty/src/method_resolution.rs @@ -699,7 +699,7 @@ pub fn lookup_impl_method( }; let name = &db.function_data(func).name; - lookup_impl_assoc_item_for_trait_ref(trait_ref, db, env, name) + let Some((impl_fn, impl_subst)) = lookup_impl_assoc_item_for_trait_ref(trait_ref, db, env, name) .and_then(|assoc| { if let (AssocItemId::FunctionId(id), subst) = assoc { Some((id, subst)) @@ -707,7 +707,16 @@ pub fn lookup_impl_method( None } }) - .unwrap_or((func, fn_subst)) + else { + return (func, fn_subst); + }; + ( + impl_fn, + Substitution::from_iter( + Interner, + fn_subst.iter(Interner).take(fn_params).chain(impl_subst.iter(Interner)), + ), + ) } fn lookup_impl_assoc_item_for_trait_ref( diff --git a/crates/hir-ty/src/mir.rs b/crates/hir-ty/src/mir.rs index 3ac208666a7..11c35dfb8a8 100644 --- a/crates/hir-ty/src/mir.rs +++ b/crates/hir-ty/src/mir.rs @@ -3,10 +3,11 @@ use std::{fmt::Display, iter}; use crate::{ - db::HirDatabase, display::HirDisplay, infer::PointerCast, lang_items::is_box, mapping::ToChalk, - CallableDefId, ClosureId, Const, ConstScalar, InferenceResult, Interner, MemoryMap, - Substitution, Ty, TyKind, + consteval::usize_const, db::HirDatabase, display::HirDisplay, infer::PointerCast, + lang_items::is_box, mapping::ToChalk, CallableDefId, ClosureId, Const, ConstScalar, + InferenceResult, Interner, MemoryMap, Substitution, Ty, TyKind, }; +use base_db::CrateId; use chalk_ir::Mutability; use hir_def::{ hir::{BindingId, Expr, ExprId, Ordering, PatId}, @@ -114,8 +115,8 @@ pub enum ProjectionElem { // FIXME: get rid of this, and use FieldId for tuples and closures TupleOrClosureField(usize), Index(V), - ConstantIndex { offset: u64, min_length: u64, from_end: bool }, - Subslice { from: u64, to: u64, from_end: bool }, + ConstantIndex { offset: u64, from_end: bool }, + Subslice { from: u64, to: u64 }, //Downcast(Option, VariantIdx), OpaqueCast(T), } @@ -126,6 +127,7 @@ impl ProjectionElem { base: Ty, db: &dyn HirDatabase, closure_field: impl FnOnce(ClosureId, &Substitution, usize) -> Ty, + krate: CrateId, ) -> Ty { match self { ProjectionElem::Deref => match &base.data(Interner).kind { @@ -163,16 +165,34 @@ impl ProjectionElem { return TyKind::Error.intern(Interner); } }, - ProjectionElem::Index(_) => match &base.data(Interner).kind { - TyKind::Array(inner, _) | TyKind::Slice(inner) => inner.clone(), + ProjectionElem::ConstantIndex { .. } | ProjectionElem::Index(_) => { + match &base.data(Interner).kind { + TyKind::Array(inner, _) | TyKind::Slice(inner) => inner.clone(), + _ => { + never!("Overloaded index is not a projection"); + return TyKind::Error.intern(Interner); + } + } + } + &ProjectionElem::Subslice { from, to } => match &base.data(Interner).kind { + TyKind::Array(inner, c) => { + let next_c = usize_const( + db, + match try_const_usize(db, c) { + None => None, + Some(x) => x.checked_sub(u128::from(from + to)), + }, + krate, + ); + TyKind::Array(inner.clone(), next_c).intern(Interner) + } + TyKind::Slice(_) => base.clone(), _ => { - never!("Overloaded index is not a projection"); + never!("Subslice projection should only happen on slice and array"); return TyKind::Error.intern(Interner); } }, - ProjectionElem::ConstantIndex { .. } - | ProjectionElem::Subslice { .. } - | ProjectionElem::OpaqueCast(_) => { + ProjectionElem::OpaqueCast(_) => { never!("We don't emit these yet"); return TyKind::Error.intern(Interner); } @@ -182,12 +202,24 @@ impl ProjectionElem { type PlaceElem = ProjectionElem; -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Place { pub local: LocalId, pub projection: Vec, } +impl Place { + fn is_parent(&self, child: &Place) -> bool { + self.local == child.local && child.projection.starts_with(&self.projection) + } + + fn iterate_over_parents(&self) -> impl Iterator + '_ { + (0..self.projection.len()) + .map(|x| &self.projection[0..x]) + .map(|x| Place { local: self.local, projection: x.to_vec() }) + } +} + impl From for Place { fn from(local: LocalId) -> Self { Self { local, projection: vec![] } @@ -941,7 +973,6 @@ pub struct MirBody { pub locals: Arena, pub start_block: BasicBlockId, pub owner: DefWithBodyId, - pub arg_count: usize, pub binding_locals: ArenaMap, pub param_locals: Vec, /// This field stores the closures directly owned by this body. It is used @@ -1029,10 +1060,6 @@ impl MirBody { } } -fn const_as_usize(c: &Const) -> usize { - try_const_usize(c).unwrap() as usize -} - #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum MirSpan { ExprId(ExprId), diff --git a/crates/hir-ty/src/mir/borrowck.rs b/crates/hir-ty/src/mir/borrowck.rs index b2425d94c61..a6af4e75d45 100644 --- a/crates/hir-ty/src/mir/borrowck.rs +++ b/crates/hir-ty/src/mir/borrowck.rs @@ -155,8 +155,10 @@ fn ever_initialized_map(body: &MirBody) -> ArenaMap { + Some(target).into_iter().chain(unwind.into_iter()).copied().collect() + } + TerminatorKind::DropAndReplace { .. } | TerminatorKind::Assert { .. } | TerminatorKind::Yield { .. } | TerminatorKind::GeneratorDrop diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs index 8ce16df8295..b16b77379de 100644 --- a/crates/hir-ty/src/mir/eval.rs +++ b/crates/hir-ty/src/mir/eval.rs @@ -5,25 +5,26 @@ use std::{borrow::Cow, collections::HashMap, fmt::Write, iter, ops::Range}; use base_db::{CrateId, FileId}; use chalk_ir::{ fold::{FallibleTypeFolder, TypeFoldable, TypeSuperFoldable}, - DebruijnIndex, Mutability, + DebruijnIndex, Mutability, ProjectionTy, }; use either::Either; use hir_def::{ builtin_type::BuiltinType, + data::adt::{StructFlags, VariantData}, lang_item::{lang_attr, LangItem}, layout::{TagEncoding, Variants}, - AdtId, DefWithBodyId, EnumVariantId, FunctionId, HasModule, ItemContainerId, Lookup, StaticId, - TypeOrConstParamId, VariantId, + AdtId, DefWithBodyId, EnumVariantId, FunctionId, GeneralConstId, HasModule, ItemContainerId, + Lookup, StaticId, TypeOrConstParamId, VariantId, }; use hir_expand::{name::Name, InFile}; use intern::Interned; use la_arena::ArenaMap; -use rustc_hash::FxHashMap; +use rustc_hash::{FxHashMap, FxHashSet}; use syntax::{SyntaxNodePtr, TextRange}; use triomphe::Arc; use crate::{ - consteval::{intern_const_scalar, ConstEvalError}, + consteval::{intern_const_scalar, try_const_usize, ConstEvalError}, db::HirDatabase, display::{ClosureStyle, HirDisplay}, from_placeholder_idx, @@ -31,7 +32,7 @@ use crate::{ layout::{layout_of_ty, Layout, LayoutError, RustcEnumVariantIdx}, mapping::from_chalk, method_resolution::{is_dyn_method, lookup_impl_const, lookup_impl_method}, - static_lifetime, + name, static_lifetime, traits::FnTrait, utils::{generics, ClosureSubst, Generics}, CallableDefId, ClosureId, Const, ConstScalar, FnDefId, GenericArgData, Interner, MemoryMap, @@ -39,11 +40,13 @@ use crate::{ }; use super::{ - const_as_usize, return_slot, AggregateKind, BinOp, CastKind, LocalId, MirBody, MirLowerError, - MirSpan, Operand, Place, ProjectionElem, Rvalue, StatementKind, TerminatorKind, UnOp, + return_slot, AggregateKind, BinOp, CastKind, LocalId, MirBody, MirLowerError, MirSpan, Operand, + Place, ProjectionElem, Rvalue, StatementKind, TerminatorKind, UnOp, }; mod shim; +#[cfg(test)] +mod tests; macro_rules! from_bytes { ($ty:tt, $value:expr) => { @@ -87,6 +90,33 @@ impl VTableMap { } } +#[derive(Debug, Default, Clone, PartialEq, Eq)] +struct TlsData { + keys: Vec, +} + +impl TlsData { + fn create_key(&mut self) -> usize { + self.keys.push(0); + self.keys.len() - 1 + } + + fn get_key(&mut self, key: usize) -> Result { + let r = self.keys.get(key).ok_or_else(|| { + MirEvalError::UndefinedBehavior(format!("Getting invalid tls key {key}")) + })?; + Ok(*r) + } + + fn set_key(&mut self, key: usize, value: u128) -> Result<()> { + let r = self.keys.get_mut(key).ok_or_else(|| { + MirEvalError::UndefinedBehavior(format!("Setting invalid tls key {key}")) + })?; + *r = value; + Ok(()) + } +} + pub struct Evaluator<'a> { db: &'a dyn HirDatabase, trait_env: Arc, @@ -99,6 +129,9 @@ pub struct Evaluator<'a> { /// store the type as an interned id in place of function and vtable pointers, and we recover back the type at the /// time of use. vtable_map: VTableMap, + thread_local_storage: TlsData, + stdout: Vec, + stderr: Vec, crate_id: CrateId, // FIXME: This is a workaround, see the comment on `interpret_mir` assert_placeholder_ty_is_unused: bool, @@ -259,6 +292,7 @@ pub enum MirEvalError { TargetDataLayoutNotAvailable, InvalidVTableId(usize), CoerceUnsizedError(Ty), + LangItemNotFound(LangItem), } impl MirEvalError { @@ -350,6 +384,7 @@ impl MirEvalError { | MirEvalError::StackOverflow | MirEvalError::TargetDataLayoutNotAvailable | MirEvalError::CoerceUnsizedError(_) + | MirEvalError::LangItemNotFound(_) | MirEvalError::InvalidVTableId(_) => writeln!(f, "{:?}", err)?, } Ok(()) @@ -362,6 +397,7 @@ impl std::fmt::Debug for MirEvalError { Self::ConstEvalError(arg0, arg1) => { f.debug_tuple("ConstEvalError").field(arg0).field(arg1).finish() } + Self::LangItemNotFound(arg0) => f.debug_tuple("LangItemNotFound").field(arg0).finish(), Self::LayoutError(arg0, arg1) => { f.debug_tuple("LayoutError").field(arg0).field(arg1).finish() } @@ -405,11 +441,32 @@ impl std::fmt::Debug for MirEvalError { type Result = std::result::Result; +#[derive(Debug, Default)] +struct DropFlags { + need_drop: FxHashSet, +} + +impl DropFlags { + fn add_place(&mut self, p: Place) { + if p.iterate_over_parents().any(|x| self.need_drop.contains(&x)) { + return; + } + self.need_drop.retain(|x| !p.is_parent(x)); + self.need_drop.insert(p); + } + + fn remove_place(&mut self, p: &Place) -> bool { + // FIXME: replace parents with parts + self.need_drop.remove(p) + } +} + #[derive(Debug)] struct Locals<'a> { - ptr: &'a ArenaMap, + ptr: &'a ArenaMap, body: &'a MirBody, subst: &'a Substitution, + drop_flags: DropFlags, } pub fn interpret_mir( @@ -422,18 +479,30 @@ pub fn interpret_mir( // a zero size, hoping that they are all outside of our current body. Even without a fix for #7434, we can // (and probably should) do better here, for example by excluding bindings outside of the target expression. assert_placeholder_ty_is_unused: bool, -) -> Result { +) -> (Result, String, String) { let ty = body.locals[return_slot()].ty.clone(); let mut evaluator = Evaluator::new(db, body, assert_placeholder_ty_is_unused); - let ty = evaluator.ty_filler(&ty, &subst, body.owner)?; - let bytes = evaluator.interpret_mir(&body, None.into_iter(), subst.clone())?; - let mut memory_map = evaluator.create_memory_map( - &bytes, - &ty, - &Locals { ptr: &ArenaMap::new(), body: &body, subst: &subst }, - )?; - memory_map.vtable = evaluator.vtable_map; - return Ok(intern_const_scalar(ConstScalar::Bytes(bytes, memory_map), ty)); + let x: Result = (|| { + let ty = evaluator.ty_filler(&ty, &subst, body.owner)?; + let bytes = evaluator.interpret_mir(&body, None.into_iter(), subst.clone())?; + let mut memory_map = evaluator.create_memory_map( + &bytes, + &ty, + &Locals { + ptr: &ArenaMap::new(), + body: &body, + subst: &subst, + drop_flags: DropFlags::default(), + }, + )?; + memory_map.vtable = evaluator.vtable_map.clone(); + return Ok(intern_const_scalar(ConstScalar::Bytes(bytes, memory_map), ty)); + })(); + ( + x, + String::from_utf8_lossy(&evaluator.stdout).into_owned(), + String::from_utf8_lossy(&evaluator.stderr).into_owned(), + ) } impl Evaluator<'_> { @@ -448,13 +517,16 @@ impl Evaluator<'_> { stack: vec![0], heap: vec![0], vtable_map: VTableMap::default(), + thread_local_storage: TlsData::default(), static_locations: HashMap::default(), db, trait_env, crate_id, + stdout: vec![], + stderr: vec![], assert_placeholder_ty_is_unused, stack_depth_limit: 100, - execution_limit: 100_000, + execution_limit: 1000_000, } } @@ -485,29 +557,37 @@ impl Evaluator<'_> { &'a self, p: &Place, locals: &'a Locals<'a>, - ) -> Result<(Address, Ty, Option)> { - let mut addr = locals.ptr[p.local]; + ) -> Result<(Address, Ty, Option)> { + let mut addr = locals.ptr[p.local].addr; let mut ty: Ty = self.ty_filler(&locals.body.locals[p.local].ty, locals.subst, locals.body.owner)?; - let mut metadata = None; // locals are always sized + let mut metadata: Option = None; // locals are always sized for proj in &p.projection { let prev_ty = ty.clone(); - ty = proj.projected_ty(ty, self.db, |c, subst, f| { - let (def, _) = self.db.lookup_intern_closure(c.into()); - let infer = self.db.infer(def); - let (captures, _) = infer.closure_info(&c); - let parent_subst = ClosureSubst(subst).parent_subst(); - captures - .get(f) - .expect("broken closure field") - .ty - .clone() - .substitute(Interner, parent_subst) - }); + ty = proj.projected_ty( + ty, + self.db, + |c, subst, f| { + let (def, _) = self.db.lookup_intern_closure(c.into()); + let infer = self.db.infer(def); + let (captures, _) = infer.closure_info(&c); + let parent_subst = ClosureSubst(subst).parent_subst(); + captures + .get(f) + .expect("broken closure field") + .ty + .clone() + .substitute(Interner, parent_subst) + }, + self.crate_id, + ); match proj { ProjectionElem::Deref => { metadata = if self.size_align_of(&ty, locals)?.is_none() { - Some(Interval { addr: addr.offset(self.ptr_size()), size: self.ptr_size() }) + Some( + Interval { addr: addr.offset(self.ptr_size()), size: self.ptr_size() } + .into(), + ) } else { None }; @@ -515,13 +595,57 @@ impl Evaluator<'_> { addr = Address::from_usize(x); } ProjectionElem::Index(op) => { - let offset = - from_bytes!(usize, self.read_memory(locals.ptr[*op], self.ptr_size())?); + let offset = from_bytes!( + usize, + self.read_memory(locals.ptr[*op].addr, self.ptr_size())? + ); metadata = None; // Result of index is always sized let ty_size = self.size_of_sized(&ty, locals, "array inner type should be sized")?; addr = addr.offset(ty_size * offset); } + &ProjectionElem::ConstantIndex { from_end, offset } => { + let offset = if from_end { + let len = match prev_ty.kind(Interner) { + TyKind::Array(_, c) => match try_const_usize(self.db, c) { + Some(x) => x as u64, + None => { + not_supported!("indexing array with unknown const from end") + } + }, + TyKind::Slice(_) => match metadata { + Some(x) => from_bytes!(u64, x.get(self)?), + None => not_supported!("slice place without metadata"), + }, + _ => not_supported!("bad type for const index"), + }; + (len - offset - 1) as usize + } else { + offset as usize + }; + metadata = None; // Result of index is always sized + let ty_size = + self.size_of_sized(&ty, locals, "array inner type should be sized")?; + addr = addr.offset(ty_size * offset); + } + &ProjectionElem::Subslice { from, to } => { + let inner_ty = match &ty.data(Interner).kind { + TyKind::Array(inner, _) | TyKind::Slice(inner) => inner.clone(), + _ => TyKind::Error.intern(Interner), + }; + metadata = match metadata { + Some(x) => { + let prev_len = from_bytes!(u64, x.get(self)?); + Some(IntervalOrOwned::Owned( + (prev_len - from - to).to_le_bytes().to_vec(), + )) + } + None => None, + }; + let ty_size = + self.size_of_sized(&inner_ty, locals, "array inner type should be sized")?; + addr = addr.offset(ty_size * (from as usize)); + } &ProjectionElem::TupleOrClosureField(f) => { let layout = self.layout(&prev_ty)?; let offset = layout.fields.offset(f).bytes_usize(); @@ -553,10 +677,6 @@ impl Evaluator<'_> { // FIXME: support structs with unsized fields metadata = None; } - ProjectionElem::ConstantIndex { .. } => { - not_supported!("constant index") - } - ProjectionElem::Subslice { .. } => not_supported!("subslice"), ProjectionElem::OpaqueCast(_) => not_supported!("opaque cast"), } } @@ -594,7 +714,11 @@ impl Evaluator<'_> { }) } - fn operand_ty_and_eval(&mut self, o: &Operand, locals: &Locals<'_>) -> Result { + fn operand_ty_and_eval( + &mut self, + o: &Operand, + locals: &mut Locals<'_>, + ) -> Result { Ok(IntervalAndTy { interval: self.eval_operand(o, locals)?, ty: self.operand_ty(o, locals)?, @@ -613,7 +737,12 @@ impl Evaluator<'_> { return Err(MirEvalError::StackOverflow); } let mut current_block_idx = body.start_block; - let mut locals = Locals { ptr: &ArenaMap::new(), body: &body, subst: &subst }; + let mut locals = Locals { + ptr: &ArenaMap::new(), + body: &body, + subst: &subst, + drop_flags: DropFlags::default(), + }; let (locals_ptr, stack_size) = { let mut stack_ptr = self.stack.len(); let addr = body @@ -624,7 +753,7 @@ impl Evaluator<'_> { self.size_of_sized(&x.ty, &locals, "no unsized local in extending stack")?; let my_ptr = stack_ptr; stack_ptr += size; - Ok((id, Stack(my_ptr))) + Ok((id, Interval { addr: Stack(my_ptr), size })) }) .collect::>>()?; let stack_size = stack_ptr - self.stack.len(); @@ -632,9 +761,10 @@ impl Evaluator<'_> { }; locals.ptr = &locals_ptr; self.stack.extend(iter::repeat(0).take(stack_size)); - let mut remain_args = body.arg_count; - for ((_, addr), value) in locals_ptr.iter().skip(1).zip(args) { - self.write_memory(*addr, &value)?; + let mut remain_args = body.param_locals.len(); + for ((l, interval), value) in locals_ptr.iter().skip(1).zip(args) { + locals.drop_flags.add_place(l.into()); + interval.write_from_bytes(self, &value)?; if remain_args == 0 { return Err(MirEvalError::TypeError("more arguments provided")); } @@ -654,8 +784,9 @@ impl Evaluator<'_> { match &statement.kind { StatementKind::Assign(l, r) => { let addr = self.place_addr(l, &locals)?; - let result = self.eval_rvalue(r, &locals)?.to_vec(&self)?; + let result = self.eval_rvalue(r, &mut locals)?.to_vec(&self)?; self.write_memory(addr, &result)?; + locals.drop_flags.add_place(l.clone()); } StatementKind::Deinit(_) => not_supported!("de-init statement"), StatementKind::StorageLive(_) @@ -678,18 +809,18 @@ impl Evaluator<'_> { cleanup: _, from_hir_call: _, } => { - let destination = self.place_interval(destination, &locals)?; + let destination_interval = self.place_interval(destination, &locals)?; let fn_ty = self.operand_ty(func, &locals)?; let args = args .iter() - .map(|x| self.operand_ty_and_eval(x, &locals)) + .map(|x| self.operand_ty_and_eval(x, &mut locals)) .collect::>>()?; match &fn_ty.data(Interner).kind { TyKind::Function(_) => { - let bytes = self.eval_operand(func, &locals)?; + let bytes = self.eval_operand(func, &mut locals)?; self.exec_fn_pointer( bytes, - destination, + destination_interval, &args, &locals, terminator.span, @@ -699,7 +830,7 @@ impl Evaluator<'_> { self.exec_fn_def( *def, generic_args, - destination, + destination_interval, &args, &locals, terminator.span, @@ -707,38 +838,33 @@ impl Evaluator<'_> { } x => not_supported!("unknown function type {x:?}"), } + locals.drop_flags.add_place(destination.clone()); current_block_idx = target.expect("broken mir, function without target"); } TerminatorKind::SwitchInt { discr, targets } => { let val = u128::from_le_bytes(pad16( - self.eval_operand(discr, &locals)?.get(&self)?, + self.eval_operand(discr, &mut locals)?.get(&self)?, false, )); current_block_idx = targets.target_for_value(val); } TerminatorKind::Return => { - let ty = body.locals[return_slot()].ty.clone(); self.stack_depth_limit += 1; - return Ok(self - .read_memory( - locals.ptr[return_slot()], - self.size_of_sized(&ty, &locals, "return type")?, - )? - .to_owned()); + return Ok(locals.ptr[return_slot()].get(self)?.to_vec()); } TerminatorKind::Unreachable => { return Err(MirEvalError::UndefinedBehavior("unreachable executed".to_owned())); } + TerminatorKind::Drop { place, target, unwind: _ } => { + self.drop_place(place, &mut locals, terminator.span)?; + current_block_idx = *target; + } _ => not_supported!("unknown terminator"), } } } - fn eval_rvalue<'a>( - &'a mut self, - r: &'a Rvalue, - locals: &'a Locals<'a>, - ) -> Result { + fn eval_rvalue(&mut self, r: &Rvalue, locals: &mut Locals<'_>) -> Result { use IntervalOrOwned::*; Ok(match r { Rvalue::Use(x) => Borrowed(self.eval_operand(x, locals)?), @@ -976,7 +1102,15 @@ impl Evaluator<'_> { } } } - Rvalue::Repeat(_, _) => not_supported!("evaluating repeat rvalue"), + Rvalue::Repeat(x, len) => { + let len = match try_const_usize(self.db, len) { + Some(x) => x as usize, + None => not_supported!("non evaluatable array len in repeat Rvalue"), + }; + let val = self.eval_operand(x, locals)?.get(self)?; + let size = len * val.len(); + Owned(val.iter().copied().cycle().take(size).collect()) + } Rvalue::ShallowInitBox(_, _) => not_supported!("shallow init box"), Rvalue::ShallowInitBoxWithAlloc(ty) => { let Some((size, align)) = self.size_align_of(ty, locals)? else { @@ -1135,7 +1269,12 @@ impl Evaluator<'_> { match self.coerce_unsized_look_through_fields(current_ty, for_ptr)? { ty => match &ty.data(Interner).kind { TyKind::Array(_, size) => { - let len = const_as_usize(size); + let len = match try_const_usize(self.db, size) { + None => not_supported!( + "unevaluatble len of array in coerce unsized" + ), + Some(x) => x as usize, + }; let mut r = Vec::with_capacity(16); let addr = addr.get(self)?; r.extend(addr.iter().copied()); @@ -1248,9 +1387,12 @@ impl Evaluator<'_> { Ok(result) } - fn eval_operand(&mut self, x: &Operand, locals: &Locals<'_>) -> Result { + fn eval_operand(&mut self, x: &Operand, locals: &mut Locals<'_>) -> Result { Ok(match x { - Operand::Copy(p) | Operand::Move(p) => self.eval_place(p, locals)?, + Operand::Copy(p) | Operand::Move(p) => { + locals.drop_flags.remove_place(p); + self.eval_place(p, locals)? + } Operand::Static(st) => { let addr = self.eval_static(*st, locals)?; Interval::new(addr, self.ptr_size()) @@ -1311,19 +1453,21 @@ impl Evaluator<'_> { Interval::new(addr, size) } ConstScalar::UnevaluatedConst(const_id, subst) => { - let subst = self.subst_filler(subst, locals); - let (const_id, subst) = lookup_impl_const( - self.db, - self.db.trait_environment_for_body(locals.body.owner), - *const_id, - subst, - ); + let mut const_id = *const_id; + let mut subst = self.subst_filler(subst, locals); + if let GeneralConstId::ConstId(c) = const_id { + let (c, s) = lookup_impl_const( + self.db, + self.db.trait_environment_for_body(locals.body.owner), + c, + subst, + ); + const_id = GeneralConstId::ConstId(c); + subst = s; + } let c = self.db.const_eval(const_id.into(), subst).map_err(|e| { - let const_data = self.db.const_data(const_id); - MirEvalError::ConstEvalError( - const_data.name.as_ref().and_then(|x| x.as_str()).unwrap_or("_").to_owned(), - Box::new(e), - ) + let name = const_id.name(self.db.upcast()); + MirEvalError::ConstEvalError(name, Box::new(e)) })?; if let chalk_ir::ConstValue::Concrete(c) = &c.data(Interner).value { if let ConstScalar::Bytes(_, _) = &c.interned { @@ -1345,6 +1489,9 @@ impl Evaluator<'_> { } fn read_memory(&self, addr: Address, size: usize) -> Result<&[u8]> { + if size == 0 { + return Ok(&[]); + } let (mem, pos) = match addr { Stack(x) => (&self.stack, x), Heap(x) => (&self.heap, x), @@ -1359,6 +1506,9 @@ impl Evaluator<'_> { } fn write_memory(&mut self, addr: Address, r: &[u8]) -> Result<()> { + if r.is_empty() { + return Ok(()); + } let (mem, pos) = match addr { Stack(x) => (&mut self.stack, x), Heap(x) => (&mut self.heap, x), @@ -1449,14 +1599,24 @@ impl Evaluator<'_> { outer_binder: DebruijnIndex, ) -> std::result::Result { 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)) + } 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, - subst, + subst: &subst, generics: Some(generics(self.db.upcast(), func.into())), }; filler.try_fold_ty(infer.type_of_rpit[idx].clone(), outer_binder) @@ -1791,6 +1951,18 @@ impl Evaluator<'_> { } let (imp, generic_args) = lookup_impl_method(self.db, self.trait_env.clone(), def, generic_args); + self.exec_looked_up_function(generic_args, locals, imp, arg_bytes, span, destination) + } + + fn exec_looked_up_function( + &mut self, + generic_args: Substitution, + locals: &Locals<'_>, + imp: FunctionId, + arg_bytes: Vec>, + span: MirSpan, + destination: Interval, + ) -> Result<()> { let generic_args = self.subst_filler(&generic_args, &locals); let def = imp.into(); let mir_body = self.db.mir_body(def).map_err(|e| MirEvalError::MirLowerError(imp, e))?; @@ -1853,13 +2025,12 @@ impl Evaluator<'_> { }; let static_data = self.db.static_data(st); let result = if !static_data.is_extern { - let konst = - self.db.const_eval(st.into(), Substitution::empty(Interner)).map_err(|e| { - MirEvalError::ConstEvalError( - static_data.name.as_str().unwrap_or("_").to_owned(), - Box::new(e), - ) - })?; + let konst = self.db.const_eval_static(st).map_err(|e| { + MirEvalError::ConstEvalError( + static_data.name.as_str().unwrap_or("_").to_owned(), + Box::new(e), + ) + })?; let data = &konst.data(Interner); if let chalk_ir::ConstValue::Concrete(c) = &data.value { self.allocate_const_in_heap(&c, &data.ty, locals, &konst)? @@ -1891,6 +2062,115 @@ impl Evaluator<'_> { } } } + + fn drop_place(&mut self, place: &Place, locals: &mut Locals<'_>, span: MirSpan) -> Result<()> { + let (addr, ty, metadata) = self.place_addr_and_ty_and_metadata(place, locals)?; + if !locals.drop_flags.remove_place(place) { + return Ok(()); + } + let metadata = match metadata { + Some(x) => x.get(self)?.to_vec(), + None => vec![], + }; + self.run_drop_glue_deep(ty, locals, addr, &metadata, span) + } + + fn run_drop_glue_deep( + &mut self, + ty: Ty, + locals: &Locals<'_>, + addr: Address, + _metadata: &[u8], + span: MirSpan, + ) -> Result<()> { + let Some(drop_fn) = (|| { + let drop_trait = self.db.lang_item(self.crate_id, LangItem::Drop)?.as_trait()?; + self.db.trait_data(drop_trait).method_by_name(&name![drop]) + })() else { + // in some tests we don't have drop trait in minicore, and + // we can ignore drop in them. + return Ok(()); + }; + let (impl_drop_candidate, subst) = lookup_impl_method( + self.db, + self.trait_env.clone(), + drop_fn, + Substitution::from1(Interner, ty.clone()), + ); + if impl_drop_candidate != drop_fn { + self.exec_looked_up_function( + subst, + locals, + impl_drop_candidate, + vec![addr.to_bytes()], + span, + Interval { addr: Address::Invalid(0), size: 0 }, + )?; + } + match ty.kind(Interner) { + TyKind::Adt(id, subst) => { + match id.0 { + AdtId::StructId(s) => { + let data = self.db.struct_data(s); + if data.flags.contains(StructFlags::IS_MANUALLY_DROP) { + return Ok(()); + } + let layout = self.layout_adt(id.0, subst.clone())?; + match data.variant_data.as_ref() { + VariantData::Record(fields) | VariantData::Tuple(fields) => { + let field_types = self.db.field_types(s.into()); + for (field, _) in fields.iter() { + let offset = layout + .fields + .offset(u32::from(field.into_raw()) as usize) + .bytes_usize(); + let addr = addr.offset(offset); + let ty = field_types[field].clone().substitute(Interner, subst); + self.run_drop_glue_deep(ty, locals, addr, &[], span)?; + } + } + VariantData::Unit => (), + } + } + AdtId::UnionId(_) => (), // union fields don't need drop + AdtId::EnumId(_) => (), + } + } + TyKind::AssociatedType(_, _) + | TyKind::Scalar(_) + | TyKind::Tuple(_, _) + | TyKind::Array(_, _) + | TyKind::Slice(_) + | TyKind::Raw(_, _) + | TyKind::Ref(_, _, _) + | TyKind::OpaqueType(_, _) + | TyKind::FnDef(_, _) + | TyKind::Str + | TyKind::Never + | TyKind::Closure(_, _) + | TyKind::Generator(_, _) + | TyKind::GeneratorWitness(_, _) + | TyKind::Foreign(_) + | TyKind::Error + | TyKind::Placeholder(_) + | TyKind::Dyn(_) + | TyKind::Alias(_) + | TyKind::Function(_) + | TyKind::BoundVar(_) + | TyKind::InferenceVar(_, _) => (), + }; + Ok(()) + } + + fn write_to_stdout(&mut self, interval: Interval) -> Result<()> { + self.stdout.extend(interval.get(self)?.to_vec()); + Ok(()) + } + + fn write_to_stderr(&mut self, interval: Interval) -> Result<()> { + self.stderr.extend(interval.get(self)?.to_vec()); + Ok(()) + } } pub fn pad16(x: &[u8], is_signed: bool) -> [u8; 16] { diff --git a/crates/hir-ty/src/mir/eval/shim.rs b/crates/hir-ty/src/mir/eval/shim.rs index 78938af15b8..ac295b58375 100644 --- a/crates/hir-ty/src/mir/eval/shim.rs +++ b/crates/hir-ty/src/mir/eval/shim.rs @@ -51,6 +51,24 @@ impl Evaluator<'_> { )?; return Ok(true); } + let is_extern_c = match def.lookup(self.db.upcast()).container { + hir_def::ItemContainerId::ExternBlockId(block) => { + let id = block.lookup(self.db.upcast()).id; + id.item_tree(self.db.upcast())[id.value].abi.as_deref() == Some("C") + } + _ => false, + }; + if is_extern_c { + self.exec_extern_c( + function_data.name.as_text().unwrap_or_default().as_str(), + args, + generic_args, + destination, + &locals, + span, + )?; + return Ok(true); + } let alloc_fn = function_data .attrs .iter() @@ -72,7 +90,7 @@ impl Evaluator<'_> { if let Some(x) = self.detect_lang_function(def) { let arg_bytes = args.iter().map(|x| Ok(x.get(&self)?.to_owned())).collect::>>()?; - let result = self.exec_lang_item(x, &arg_bytes)?; + let result = self.exec_lang_item(x, generic_args, &arg_bytes, locals, span)?; destination.write_from_bytes(self, &result)?; return Ok(true); } @@ -118,13 +136,20 @@ impl Evaluator<'_> { use LangItem::*; let candidate = lang_attr(self.db.upcast(), def)?; // We want to execute these functions with special logic - if [PanicFmt, BeginPanic, SliceLen].contains(&candidate) { + if [PanicFmt, BeginPanic, SliceLen, DropInPlace].contains(&candidate) { return Some(candidate); } None } - fn exec_lang_item(&self, x: LangItem, args: &[Vec]) -> Result> { + fn exec_lang_item( + &mut self, + x: LangItem, + generic_args: &Substitution, + args: &[Vec], + locals: &Locals<'_>, + span: MirSpan, + ) -> Result> { use LangItem::*; let mut args = args.iter(); match x { @@ -139,10 +164,114 @@ impl Evaluator<'_> { let ptr_size = arg.len() / 2; Ok(arg[ptr_size..].into()) } + DropInPlace => { + let ty = + generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)).ok_or( + MirEvalError::TypeError( + "generic argument of drop_in_place is not provided", + ), + )?; + let arg = args + .next() + .ok_or(MirEvalError::TypeError("argument of drop_in_place is not provided"))?; + self.run_drop_glue_deep( + ty.clone(), + locals, + Address::from_bytes(&arg[0..self.ptr_size()])?, + &arg[self.ptr_size()..], + span, + )?; + Ok(vec![]) + } x => not_supported!("Executing lang item {x:?}"), } } + fn exec_extern_c( + &mut self, + as_str: &str, + args: &[IntervalAndTy], + _generic_args: &Substitution, + destination: Interval, + locals: &Locals<'_>, + _span: MirSpan, + ) -> Result<()> { + match as_str { + "write" => { + let [fd, ptr, len] = args else { + return Err(MirEvalError::TypeError("libc::write args are not provided")); + }; + let fd = u128::from_le_bytes(pad16(fd.get(self)?, false)); + let interval = Interval { + addr: Address::from_bytes(ptr.get(self)?)?, + size: from_bytes!(usize, len.get(self)?), + }; + match fd { + 1 => { + self.write_to_stdout(interval)?; + } + 2 => { + self.write_to_stderr(interval)?; + } + _ => not_supported!("write to arbitrary file descriptor"), + } + Ok(()) + } + "pthread_key_create" => { + let key = self.thread_local_storage.create_key(); + let Some(arg0) = args.get(0) else { + return Err(MirEvalError::TypeError("pthread_key_create arg0 is not provided")); + }; + let arg0_addr = Address::from_bytes(arg0.get(self)?)?; + let key_ty = if let Some((ty, ..)) = arg0.ty.as_reference_or_ptr() { + ty + } else { + return Err(MirEvalError::TypeError( + "pthread_key_create arg0 is not a pointer", + )); + }; + let arg0_interval = Interval::new( + arg0_addr, + self.size_of_sized(key_ty, locals, "pthread_key_create key arg")?, + ); + arg0_interval.write_from_bytes(self, &key.to_le_bytes()[0..arg0_interval.size])?; + // return 0 as success + destination.write_from_bytes(self, &0u64.to_le_bytes()[0..destination.size])?; + Ok(()) + } + "pthread_getspecific" => { + let Some(arg0) = args.get(0) else { + return Err(MirEvalError::TypeError("pthread_getspecific arg0 is not provided")); + }; + let key = from_bytes!(usize, &pad16(arg0.get(self)?, false)[0..8]); + let value = self.thread_local_storage.get_key(key)?; + destination.write_from_bytes(self, &value.to_le_bytes()[0..destination.size])?; + Ok(()) + } + "pthread_setspecific" => { + let Some(arg0) = args.get(0) else { + return Err(MirEvalError::TypeError("pthread_setspecific arg0 is not provided")); + }; + let key = from_bytes!(usize, &pad16(arg0.get(self)?, false)[0..8]); + let Some(arg1) = args.get(1) else { + return Err(MirEvalError::TypeError("pthread_setspecific arg1 is not provided")); + }; + let value = from_bytes!(u128, pad16(arg1.get(self)?, false)); + self.thread_local_storage.set_key(key, value)?; + // return 0 as success + destination.write_from_bytes(self, &0u64.to_le_bytes()[0..destination.size])?; + Ok(()) + } + "pthread_key_delete" => { + // we ignore this currently + // return 0 as success + destination.write_from_bytes(self, &0u64.to_le_bytes()[0..destination.size])?; + Ok(()) + } + _ => not_supported!("unknown external function {as_str}"), + } + } + fn exec_intrinsic( &mut self, as_str: &str, @@ -288,7 +417,7 @@ impl Evaluator<'_> { let ans = lhs.get(self)? == rhs.get(self)?; destination.write_from_bytes(self, &[u8::from(ans)]) } - "wrapping_add" => { + "wrapping_add" | "unchecked_add" => { let [lhs, rhs] = args else { return Err(MirEvalError::TypeError("wrapping_add args are not provided")); }; @@ -297,6 +426,39 @@ impl Evaluator<'_> { let ans = lhs.wrapping_add(rhs); destination.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size]) } + "wrapping_sub" | "unchecked_sub" | "ptr_offset_from_unsigned" | "ptr_offset_from" => { + let [lhs, rhs] = args else { + return Err(MirEvalError::TypeError("wrapping_sub args are not provided")); + }; + let lhs = u128::from_le_bytes(pad16(lhs.get(self)?, false)); + let rhs = u128::from_le_bytes(pad16(rhs.get(self)?, false)); + let ans = lhs.wrapping_sub(rhs); + destination.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size]) + } + "unchecked_rem" => { + // FIXME: signed + let [lhs, rhs] = args else { + return Err(MirEvalError::TypeError("unchecked_rem args are not provided")); + }; + let lhs = u128::from_le_bytes(pad16(lhs.get(self)?, false)); + let rhs = u128::from_le_bytes(pad16(rhs.get(self)?, false)); + let ans = lhs.checked_rem(rhs).ok_or_else(|| { + MirEvalError::UndefinedBehavior("unchecked_rem with bad inputs".to_owned()) + })?; + destination.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size]) + } + "unchecked_div" | "exact_div" => { + // FIXME: signed + let [lhs, rhs] = args else { + return Err(MirEvalError::TypeError("unchecked_div args are not provided")); + }; + let lhs = u128::from_le_bytes(pad16(lhs.get(self)?, false)); + let rhs = u128::from_le_bytes(pad16(rhs.get(self)?, false)); + let ans = lhs.checked_div(rhs).ok_or_else(|| { + MirEvalError::UndefinedBehavior("unchecked_rem with bad inputs".to_owned()) + })?; + destination.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size]) + } "add_with_overflow" => { let [lhs, rhs] = args else { return Err(MirEvalError::TypeError("const_eval_select args are not provided")); @@ -373,6 +535,22 @@ impl Evaluator<'_> { }; destination.write_from_interval(self, arg.interval) } + "ctpop" => { + let [arg] = args else { + return Err(MirEvalError::TypeError("likely arg is not provided")); + }; + let result = u128::from_le_bytes(pad16(arg.get(self)?, false)).count_ones(); + destination + .write_from_bytes(self, &(result as u128).to_le_bytes()[0..destination.size]) + } + "cttz" | "cttz_nonzero" => { + let [arg] = args else { + return Err(MirEvalError::TypeError("likely arg is not provided")); + }; + let result = u128::from_le_bytes(pad16(arg.get(self)?, false)).trailing_zeros(); + destination + .write_from_bytes(self, &(result as u128).to_le_bytes()[0..destination.size]) + } "const_eval_select" => { let [tuple, const_fn, _] = args else { return Err(MirEvalError::TypeError("const_eval_select args are not provided")); diff --git a/crates/hir-ty/src/mir/eval/tests.rs b/crates/hir-ty/src/mir/eval/tests.rs new file mode 100644 index 00000000000..0685159f1c1 --- /dev/null +++ b/crates/hir-ty/src/mir/eval/tests.rs @@ -0,0 +1,299 @@ +use base_db::{fixture::WithFixture, FileId}; +use hir_def::db::DefDatabase; + +use crate::{db::HirDatabase, test_db::TestDB, Interner, Substitution}; + +use super::{interpret_mir, MirEvalError}; + +fn eval_main(db: &TestDB, file_id: FileId) -> Result<(String, String), MirEvalError> { + let module_id = db.module_for_file(file_id); + let def_map = module_id.def_map(db); + let scope = &def_map[module_id.local_id].scope; + let func_id = scope + .declarations() + .find_map(|x| match x { + hir_def::ModuleDefId::FunctionId(x) => { + if db.function_data(x).name.to_string() == "main" { + Some(x) + } else { + None + } + } + _ => None, + }) + .unwrap(); + let body = + db.mir_body(func_id.into()).map_err(|e| MirEvalError::MirLowerError(func_id.into(), e))?; + let (result, stdout, stderr) = interpret_mir(db, &body, Substitution::empty(Interner), false); + result?; + Ok((stdout, stderr)) +} + +fn check_pass(ra_fixture: &str) { + check_pass_and_stdio(ra_fixture, "", ""); +} + +fn check_pass_and_stdio(ra_fixture: &str, expected_stdout: &str, expected_stderr: &str) { + let (db, file_id) = TestDB::with_single_file(ra_fixture); + let x = eval_main(&db, file_id); + match x { + Err(e) => { + let mut err = String::new(); + let span_formatter = |file, range| format!("{:?} {:?}", file, range); + e.pretty_print(&mut err, &db, span_formatter).unwrap(); + panic!("Error in interpreting: {err}"); + } + Ok((stdout, stderr)) => { + assert_eq!(stdout, expected_stdout); + assert_eq!(stderr, expected_stderr); + } + } +} + +#[test] +fn function_with_extern_c_abi() { + check_pass( + r#" +extern "C" fn foo(a: i32, b: i32) -> i32 { + a + b +} + +fn main() { + let x = foo(2, 3); +} + "#, + ); +} + +#[test] +fn drop_basic() { + check_pass( + r#" +//- minicore: drop, add + +struct X<'a>(&'a mut i32); +impl<'a> Drop for X<'a> { + fn drop(&mut self) { + *self.0 += 1; + } +} + +struct NestedX<'a> { f1: X<'a>, f2: X<'a> } + +fn should_not_reach() { + _ // FIXME: replace this function with panic when that works +} + +fn my_drop2(x: X<'_>) { + return; +} + +fn my_drop(x: X<'_>) { + drop(x); +} + +fn main() { + let mut s = 10; + let mut x = X(&mut s); + my_drop(x); + x = X(&mut s); + my_drop2(x); + X(&mut s); // dropped immediately + let x = X(&mut s); + NestedX { f1: x, f2: X(&mut s) }; + if s != 15 { + should_not_reach(); + } +} + "#, + ); +} + +#[test] +fn drop_in_place() { + check_pass( + r#" +//- minicore: drop, add, coerce_unsized +use core::ptr::drop_in_place; + +struct X<'a>(&'a mut i32); +impl<'a> Drop for X<'a> { + fn drop(&mut self) { + *self.0 += 1; + } +} + +fn should_not_reach() { + _ // FIXME: replace this function with panic when that works +} + +fn main() { + let mut s = 2; + let x = X(&mut s); + drop_in_place(&mut x); + drop(x); + if s != 4 { + should_not_reach(); + } + let p: &mut [X] = &mut [X(&mut 2)]; + drop_in_place(p); +} + "#, + ); +} + +#[test] +fn manually_drop() { + check_pass( + r#" +//- minicore: manually_drop +use core::mem::ManuallyDrop; + +struct X; +impl Drop for X { + fn drop(&mut self) { + should_not_reach(); + } +} + +fn should_not_reach() { + _ // FIXME: replace this function with panic when that works +} + +fn main() { + let x = ManuallyDrop::new(X); +} + "#, + ); +} + +#[test] +fn generic_impl_for_trait_with_generic_method() { + check_pass( + r#" +//- minicore: drop +struct S(T); + +trait Tr { + fn f(&self, x: F); +} + +impl Tr for S { + fn f(&self, x: F) { + } +} + +fn main() { + let s = S(1u8); + s.f(5i64); +} + "#, + ); +} + +#[test] +fn index_of_slice_should_preserve_len() { + check_pass( + r#" +//- minicore: index, slice, coerce_unsized + +struct X; + +impl core::ops::Index for [i32] { + type Output = i32; + + fn index(&self, _: X) -> &i32 { + if self.len() != 3 { + should_not_reach(); + } + &self[0] + } +} + +fn should_not_reach() { + _ // FIXME: replace this function with panic when that works +} + +fn main() { + let x: &[i32] = &[1, 2, 3]; + &x[X]; +} + "#, + ); +} + +#[test] +fn unix_write_stdout() { + check_pass_and_stdio( + r#" +//- minicore: slice, index, coerce_unsized + +type pthread_key_t = u32; +type c_void = u8; +type c_int = i32; + +extern "C" { + pub fn write(fd: i32, buf: *const u8, count: usize) -> usize; +} + +fn main() { + let stdout = b"stdout"; + let stderr = b"stderr"; + write(1, &stdout[0], 6); + write(2, &stderr[0], 6); +} + "#, + "stdout", + "stderr", + ); +} + +#[test] +fn closure_layout_in_rpit() { + check_pass( + r#" +//- minicore: fn + +fn f(x: F) { + fn g(x: impl Fn()) -> impl FnOnce() { + move || { + x(); + } + } + g(x)(); +} + +fn main() { + f(|| {}); +} + "#, + ); +} + +#[test] +fn posix_tls() { + check_pass( + r#" +//- minicore: option + +type pthread_key_t = u32; +type c_void = u8; +type c_int = i32; + +extern "C" { + pub fn pthread_key_create( + key: *mut pthread_key_t, + dtor: Option, + ) -> c_int; + pub fn pthread_key_delete(key: pthread_key_t) -> c_int; + pub fn pthread_getspecific(key: pthread_key_t) -> *mut c_void; + pub fn pthread_setspecific(key: pthread_key_t, value: *const c_void) -> c_int; +} + +fn main() { + let mut key = 2; + pthread_key_create(&mut key, None); +} + "#, + ); +} diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs index 66b1d840bdc..a37e5e81687 100644 --- a/crates/hir-ty/src/mir/lower.rs +++ b/crates/hir-ty/src/mir/lower.rs @@ -14,7 +14,8 @@ use hir_def::{ lang_item::{LangItem, LangItemTarget}, path::Path, resolver::{resolver_for_expr, ResolveValueResult, ValueNs}, - AdtId, DefWithBodyId, EnumVariantId, HasModule, ItemContainerId, LocalFieldId, TraitId, + AdtId, DefWithBodyId, EnumVariantId, GeneralConstId, HasModule, ItemContainerId, LocalFieldId, + TraitId, }; use hir_expand::name::Name; use la_arena::ArenaMap; @@ -30,7 +31,6 @@ use crate::{ inhabitedness::is_ty_uninhabited_from, layout::{layout_of_ty, LayoutError}, mapping::ToChalk, - method_resolution::lookup_impl_const, static_lifetime, utils::{generics, ClosureSubst}, Adjust, Adjustment, AutoBorrow, CallableDefId, TyBuilder, TyExt, @@ -51,17 +51,22 @@ struct LoopBlocks { place: Place, } +#[derive(Debug, Clone, Default)] +struct DropScope { + /// locals, in order of definition (so we should run drop glues in reverse order) + locals: Vec, +} + struct MirLowerCtx<'a> { result: MirBody, owner: DefWithBodyId, current_loop_blocks: Option, - // FIXME: we should resolve labels in HIR lowering and always work with label id here, not - // with raw names. labeled_loop_blocks: FxHashMap, discr_temp: Option, db: &'a dyn HirDatabase, body: &'a Body, infer: &'a InferenceResult, + drop_scopes: Vec, } #[derive(Debug, Clone, PartialEq, Eq)] @@ -181,7 +186,6 @@ impl<'ctx> MirLowerCtx<'ctx> { binding_locals, param_locals: vec![], owner, - arg_count: body.params.len(), closures: vec![], }; let ctx = MirLowerCtx { @@ -193,15 +197,18 @@ impl<'ctx> MirLowerCtx<'ctx> { current_loop_blocks: None, labeled_loop_blocks: Default::default(), discr_temp: None, + drop_scopes: vec![DropScope::default()], }; ctx } - fn temp(&mut self, ty: Ty) -> Result { + fn temp(&mut self, ty: Ty, current: BasicBlockId, span: MirSpan) -> Result { if matches!(ty.kind(Interner), TyKind::Slice(_) | TyKind::Dyn(_)) { implementation_error!("unsized temporaries"); } - Ok(self.result.locals.alloc(Local { ty })) + let l = self.result.locals.alloc(Local { ty }); + self.push_storage_live_for_local(l, current, span)?; + Ok(l) } fn lower_expr_to_some_operand( @@ -234,7 +241,8 @@ impl<'ctx> MirLowerCtx<'ctx> { match adjustments.split_last() { Some((last, rest)) => match &last.kind { Adjust::NeverToAny => { - let temp = self.temp(TyKind::Never.intern(Interner))?; + let temp = + self.temp(TyKind::Never.intern(Interner), current, MirSpan::Unknown)?; self.lower_expr_to_place_with_adjust(expr_id, temp.into(), current, rest) } Adjust::Deref(_) => { @@ -303,45 +311,39 @@ impl<'ctx> MirLowerCtx<'ctx> { Err(MirLowerError::IncompleteExpr) }, Expr::Path(p) => { - let unresolved_name = || MirLowerError::unresolved_path(self.db, p); - let resolver = resolver_for_expr(self.db.upcast(), self.owner, expr_id); - let pr = resolver - .resolve_path_in_value_ns(self.db.upcast(), p) - .ok_or_else(unresolved_name)?; - let pr = match pr { - ResolveValueResult::ValueNs(v) => v, - ResolveValueResult::Partial(..) => { - if let Some((assoc, subst)) = self - .infer - .assoc_resolutions_for_expr(expr_id) - { - match assoc { - hir_def::AssocItemId::ConstId(c) => { - self.lower_const(c, current, place, subst, expr_id.into(), self.expr_ty_without_adjust(expr_id))?; - return Ok(Some(current)) - }, - hir_def::AssocItemId::FunctionId(_) => { - // FnDefs are zero sized, no action is needed. - return Ok(Some(current)) - } - hir_def::AssocItemId::TypeAliasId(_) => { - // FIXME: If it is unreachable, use proper error instead of `not_supported`. - not_supported!("associated functions and types") - }, - } - } else if let Some(variant) = self - .infer - .variant_resolution_for_expr(expr_id) - { - match variant { - VariantId::EnumVariantId(e) => ValueNs::EnumVariantId(e), - VariantId::StructId(s) => ValueNs::StructId(s), - VariantId::UnionId(_) => implementation_error!("Union variant as path"), - } - } else { - return Err(unresolved_name()); + let pr = if let Some((assoc, subst)) = self + .infer + .assoc_resolutions_for_expr(expr_id) + { + match assoc { + hir_def::AssocItemId::ConstId(c) => { + self.lower_const(c.into(), current, place, subst, expr_id.into(), self.expr_ty_without_adjust(expr_id))?; + return Ok(Some(current)) + }, + hir_def::AssocItemId::FunctionId(_) => { + // FnDefs are zero sized, no action is needed. + return Ok(Some(current)) } + hir_def::AssocItemId::TypeAliasId(_) => { + // FIXME: If it is unreachable, use proper error instead of `not_supported`. + not_supported!("associated functions and types") + }, } + } else if let Some(variant) = self + .infer + .variant_resolution_for_expr(expr_id) + { + match variant { + VariantId::EnumVariantId(e) => ValueNs::EnumVariantId(e), + VariantId::StructId(s) => ValueNs::StructId(s), + VariantId::UnionId(_) => implementation_error!("Union variant as path"), + } + } else { + let unresolved_name = || MirLowerError::unresolved_path(self.db, p); + let resolver = resolver_for_expr(self.db.upcast(), self.owner, expr_id); + resolver + .resolve_path_in_value_ns_fully(self.db.upcast(), p) + .ok_or_else(unresolved_name)? }; match pr { ValueNs::LocalBinding(_) | ValueNs::StaticId(_) => { @@ -357,7 +359,7 @@ impl<'ctx> MirLowerCtx<'ctx> { Ok(Some(current)) } ValueNs::ConstId(const_id) => { - self.lower_const(const_id, current, place, Substitution::empty(Interner), expr_id.into(), self.expr_ty_without_adjust(expr_id))?; + self.lower_const(const_id.into(), current, place, Substitution::empty(Interner), expr_id.into(), self.expr_ty_without_adjust(expr_id))?; Ok(Some(current)) } ValueNs::EnumVariantId(variant_id) => { @@ -470,9 +472,10 @@ impl<'ctx> MirLowerCtx<'ctx> { Expr::Block { id: _, statements, tail, label } => { if let Some(label) = label { self.lower_loop(current, place.clone(), Some(*label), expr_id.into(), |this, begin| { - if let Some(block) = this.lower_block_to_place(statements, begin, *tail, place, expr_id.into())? { + if let Some(current) = this.lower_block_to_place(statements, begin, *tail, place, expr_id.into())? { let end = this.current_loop_end()?; - this.set_goto(block, end, expr_id.into()); + let current = this.pop_drop_scope(current); + this.set_goto(current, end, expr_id.into()); } Ok(()) }) @@ -481,8 +484,9 @@ impl<'ctx> MirLowerCtx<'ctx> { } } Expr::Loop { body, label } => self.lower_loop(current, place, *label, expr_id.into(), |this, begin| { - if let Some((_, block)) = this.lower_expr_as_place(begin, *body, true)? { - this.set_goto(block, begin, expr_id.into()); + if let Some((_, current)) = this.lower_expr_as_place(begin, *body, true)? { + let current = this.pop_drop_scope(current); + this.set_goto(current, begin, expr_id.into()); } Ok(()) }), @@ -502,6 +506,7 @@ impl<'ctx> MirLowerCtx<'ctx> { expr_id.into(), ); if let Some((_, block)) = this.lower_expr_as_place(after_cond, *body, true)? { + let block = this.pop_drop_scope(block); this.set_goto(block, begin, expr_id.into()); } Ok(()) @@ -531,9 +536,9 @@ impl<'ctx> MirLowerCtx<'ctx> { let ref_mut_iterator_ty = TyKind::Ref(Mutability::Mut, static_lifetime(), iterator_ty.clone()).intern(Interner); let item_ty = &self.infer.type_of_pat[pat]; let option_item_ty = TyKind::Adt(chalk_ir::AdtId(option.into()), Substitution::from1(Interner, item_ty.clone())).intern(Interner); - let iterator_place: Place = self.temp(iterator_ty.clone())?.into(); - let option_item_place: Place = self.temp(option_item_ty.clone())?.into(); - let ref_mut_iterator_place: Place = self.temp(ref_mut_iterator_ty)?.into(); + let iterator_place: Place = self.temp(iterator_ty.clone(), current, expr_id.into())?.into(); + let option_item_place: Place = self.temp(option_item_ty.clone(), current, expr_id.into())?.into(); + let ref_mut_iterator_place: Place = self.temp(ref_mut_iterator_ty, current, expr_id.into())?.into(); let Some(current) = self.lower_call_and_args(into_iter_fn_op, Some(iterable).into_iter(), iterator_place.clone(), current, false, expr_id.into())? else { return Ok(None); @@ -556,6 +561,7 @@ impl<'ctx> MirLowerCtx<'ctx> { AdtPatternShape::Tuple { args: &[pat], ellipsis: None }, )?; if let Some((_, block)) = this.lower_expr_as_place(current, body, true)? { + let block = this.pop_drop_scope(block); this.set_goto(block, begin, expr_id.into()); } Ok(()) @@ -686,6 +692,7 @@ impl<'ctx> MirLowerCtx<'ctx> { return Ok(None); } } + current = self.drop_until_scope(0, current); self.set_terminator(current, TerminatorKind::Return, expr_id.into()); Ok(None) } @@ -770,7 +777,11 @@ impl<'ctx> MirLowerCtx<'ctx> { Expr::Await { .. } => not_supported!("await"), Expr::Yeet { .. } => not_supported!("yeet"), Expr::Async { .. } => not_supported!("async block"), - Expr::Const { .. } => not_supported!("anonymous const block"), + &Expr::Const(id) => { + let subst = self.placeholder_subst(); + self.lower_const(id.into(), current, place, subst, expr_id.into(), self.expr_ty_without_adjust(expr_id))?; + Ok(Some(current)) + }, Expr::Cast { expr, type_ref: _ } => { let Some((x, current)) = self.lower_expr_to_some_operand(*expr, current)? else { return Ok(None); @@ -830,11 +841,16 @@ impl<'ctx> MirLowerCtx<'ctx> { }, Expr::BinaryOp { lhs, rhs, op } => { let op = op.ok_or(MirLowerError::IncompleteExpr)?; - let is_builtin = { + let is_builtin = 'b: { // Without adjust here is a hack. We assume that we know every possible adjustment // for binary operator, and use without adjust to simplify our conditions. let lhs_ty = self.expr_ty_without_adjust(*lhs); let rhs_ty = self.expr_ty_without_adjust(*rhs); + if matches!(op ,BinaryOp::CmpOp(syntax::ast::CmpOp::Eq { .. })) { + if lhs_ty.as_raw_ptr().is_some() && rhs_ty.as_raw_ptr().is_some() { + break 'b true; + } + } let builtin_inequal_impls = matches!( op, BinaryOp::ArithOp(ArithOp::Shl | ArithOp::Shr) | BinaryOp::Assignment { op: Some(ArithOp::Shl | ArithOp::Shr) } @@ -973,8 +989,8 @@ impl<'ctx> MirLowerCtx<'ctx> { ProjectionElem::Deref => ProjectionElem::Deref, ProjectionElem::Field(x) => ProjectionElem::Field(x), ProjectionElem::TupleOrClosureField(x) => ProjectionElem::TupleOrClosureField(x), - ProjectionElem::ConstantIndex { offset, min_length, from_end } => ProjectionElem::ConstantIndex { offset, min_length, from_end }, - ProjectionElem::Subslice { from, to, from_end } => ProjectionElem::Subslice { from, to, from_end }, + ProjectionElem::ConstantIndex { offset, from_end } => ProjectionElem::ConstantIndex { offset, from_end }, + ProjectionElem::Subslice { from, to } => ProjectionElem::Subslice { from, to }, ProjectionElem::OpaqueCast(x) => ProjectionElem::OpaqueCast(x), ProjectionElem::Index(x) => match x { }, } @@ -982,12 +998,9 @@ impl<'ctx> MirLowerCtx<'ctx> { }; match &capture.kind { CaptureKind::ByRef(bk) => { - let placeholder_subst = match self.owner.as_generic_def_id() { - Some(x) => TyBuilder::placeholder_subst(self.db, x), - None => Substitution::empty(Interner), - }; + let placeholder_subst = self.placeholder_subst(); let tmp_ty = capture.ty.clone().substitute(Interner, &placeholder_subst); - let tmp: Place = self.temp(tmp_ty)?.into(); + let tmp: Place = self.temp(tmp_ty, current, capture.span)?.into(); self.push_assignment( current, tmp.clone(), @@ -1085,6 +1098,14 @@ impl<'ctx> MirLowerCtx<'ctx> { } } + fn placeholder_subst(&mut self) -> Substitution { + let placeholder_subst = match self.owner.as_generic_def_id() { + Some(x) => TyBuilder::placeholder_subst(self.db, x), + None => Substitution::empty(Interner), + }; + placeholder_subst + } + fn push_field_projection(&self, place: &mut Place, expr_id: ExprId) -> Result<()> { if let Expr::Field { expr, name } = &self.body[expr_id] { if let TyKind::Tuple(..) = self.expr_ty_after_adjustments(*expr).kind(Interner) { @@ -1146,7 +1167,7 @@ impl<'ctx> MirLowerCtx<'ctx> { fn lower_const( &mut self, - const_id: hir_def::ConstId, + const_id: GeneralConstId, prev_block: BasicBlockId, place: Place, subst: Substitution, @@ -1157,20 +1178,7 @@ impl<'ctx> MirLowerCtx<'ctx> { // We can't evaluate constant with substitution now, as generics are not monomorphized in lowering. intern_const_scalar(ConstScalar::UnevaluatedConst(const_id, subst), ty) } else { - let (const_id, subst) = lookup_impl_const( - self.db, - self.db.trait_environment_for_body(self.owner), - const_id, - subst, - ); - let name = self - .db - .const_data(const_id) - .name - .as_ref() - .and_then(|x| x.as_str()) - .unwrap_or("_") - .to_owned(); + let name = const_id.name(self.db.upcast()); self.db .const_eval(const_id.into(), subst) .map_err(|e| MirLowerError::ConstEvalError(name, Box::new(e)))? @@ -1313,12 +1321,14 @@ impl<'ctx> MirLowerCtx<'ctx> { self.push_statement(block, StatementKind::Assign(place, rvalue).with_span(span)); } - fn discr_temp_place(&mut self) -> Place { + fn discr_temp_place(&mut self, current: BasicBlockId) -> Place { match &self.discr_temp { Some(x) => x.clone(), None => { - let tmp: Place = - self.temp(TyBuilder::discr_ty()).expect("discr_ty is never unsized").into(); + let tmp: Place = self + .temp(TyBuilder::discr_ty(), current, MirSpan::Unknown) + .expect("discr_ty is never unsized") + .into(); self.discr_temp = Some(tmp.clone()); tmp } @@ -1349,6 +1359,7 @@ impl<'ctx> MirLowerCtx<'ctx> { None }; self.set_goto(prev_block, begin, span); + self.push_drop_scope(); f(self, begin)?; let my = mem::replace(&mut self.current_loop_blocks, prev).ok_or( MirLowerError::ImplementationError("current_loop_blocks is corrupt".to_string()), @@ -1409,27 +1420,9 @@ impl<'ctx> MirLowerCtx<'ctx> { is_ty_uninhabited_from(&self.infer[expr_id], self.owner.module(self.db.upcast()), self.db) } - /// This function push `StorageLive` statement for the binding, and applies changes to add `StorageDead` in - /// the appropriated places. + /// This function push `StorageLive` statement for the binding, and applies changes to add `StorageDead` and + /// `Drop` in the appropriated places. fn push_storage_live(&mut self, b: BindingId, current: BasicBlockId) -> Result<()> { - // Current implementation is wrong. It adds no `StorageDead` at the end of scope, and before each break - // and continue. It just add a `StorageDead` before the `StorageLive`, which is not wrong, but unneeded in - // the proper implementation. Due this limitation, implementing a borrow checker on top of this mir will falsely - // allow this: - // - // ``` - // let x; - // loop { - // let y = 2; - // x = &y; - // if some_condition { - // break; // we need to add a StorageDead(y) above this to kill the x borrow - // } - // } - // use(x) - // ``` - // But I think this approach work for mutability analysis, as user can't write code which mutates a binding - // after StorageDead, except loops, which are handled by this hack. let span = self.body.bindings[b] .definitions .first() @@ -1437,6 +1430,18 @@ impl<'ctx> MirLowerCtx<'ctx> { .map(MirSpan::PatId) .unwrap_or(MirSpan::Unknown); let l = self.binding_local(b)?; + self.push_storage_live_for_local(l, current, span) + } + + fn push_storage_live_for_local( + &mut self, + l: LocalId, + current: BasicBlockId, + span: MirSpan, + ) -> Result<()> { + self.drop_scopes.last_mut().unwrap().locals.push(l); + // FIXME: this storage dead is not neccessary, but since drop scope handling is broken, we need + // it to avoid falso positives in mutability errors self.push_statement(current, StatementKind::StorageDead(l).with_span(span)); self.push_statement(current, StatementKind::StorageLive(l).with_span(span)); Ok(()) @@ -1500,10 +1505,11 @@ impl<'ctx> MirLowerCtx<'ctx> { } } hir_def::hir::Statement::Expr { expr, has_semi: _ } => { + self.push_drop_scope(); let Some((_, c)) = self.lower_expr_as_place(current, *expr, true)? else { return Ok(None); }; - current = c; + current = self.pop_drop_scope(c); } } } @@ -1521,6 +1527,7 @@ impl<'ctx> MirLowerCtx<'ctx> { let base_param_count = self.result.param_locals.len(); self.result.param_locals.extend(params.clone().map(|(x, ty)| { let local_id = self.result.locals.alloc(Local { ty }); + self.drop_scopes.last_mut().unwrap().locals.push(local_id); if let Pat::Bind { id, subpat: None } = self.body[x] { if matches!( self.body.bindings[id].mode, @@ -1590,6 +1597,44 @@ impl<'ctx> MirLowerCtx<'ctx> { } } } + + fn drop_until_scope(&mut self, scope_index: usize, mut current: BasicBlockId) -> BasicBlockId { + for scope in self.drop_scopes[scope_index..].to_vec().iter().rev() { + self.emit_drop_and_storage_dead_for_scope(scope, &mut current); + } + current + } + + fn push_drop_scope(&mut self) { + self.drop_scopes.push(DropScope::default()); + } + + fn pop_drop_scope(&mut self, mut current: BasicBlockId) -> BasicBlockId { + let scope = self.drop_scopes.pop().unwrap(); + self.emit_drop_and_storage_dead_for_scope(&scope, &mut current); + current + } + + fn emit_drop_and_storage_dead_for_scope( + &mut self, + scope: &DropScope, + current: &mut Idx, + ) { + for &l in scope.locals.iter().rev() { + if !self.result.locals[l].ty.clone().is_copy(self.db, self.owner) { + let prev = std::mem::replace(current, self.new_basic_block()); + self.set_terminator( + prev, + TerminatorKind::Drop { place: l.into(), target: *current, unwind: None }, + MirSpan::Unknown, + ); + } + self.push_statement( + *current, + StatementKind::StorageDead(l).with_span(MirSpan::Unknown), + ); + } + } } fn cast_kind(source_ty: &Ty, target_ty: &Ty) -> Result { @@ -1628,10 +1673,10 @@ pub fn mir_body_for_closure_query( }; let (captures, _) = infer.closure_info(&closure); let mut ctx = MirLowerCtx::new(db, owner, &body, &infer); - ctx.result.arg_count = args.len() + 1; // 0 is return local ctx.result.locals.alloc(Local { ty: infer[*root].clone() }); - ctx.result.locals.alloc(Local { ty: infer[expr].clone() }); + let closure_local = ctx.result.locals.alloc(Local { ty: infer[expr].clone() }); + ctx.result.param_locals.push(closure_local); let Some(sig) = ClosureSubst(substs).sig_ty().callable_sig(db) else { implementation_error!("closure has not callable sig"); }; @@ -1639,8 +1684,9 @@ pub fn mir_body_for_closure_query( args.iter().zip(sig.params().iter()).map(|(x, y)| (*x, y.clone())), |_| true, )?; - if let Some(b) = ctx.lower_expr_to_place(*root, return_slot().into(), current)? { - ctx.set_terminator(b, TerminatorKind::Return, (*root).into()); + if let Some(current) = ctx.lower_expr_to_place(*root, return_slot().into(), current)? { + let current = ctx.pop_drop_scope(current); + ctx.set_terminator(current, TerminatorKind::Return, (*root).into()); } let mut upvar_map: FxHashMap> = FxHashMap::default(); for (i, capture) in captures.iter().enumerate() { @@ -1761,8 +1807,9 @@ pub fn lower_to_mir( } ctx.lower_params_and_bindings([].into_iter(), binding_picker)? }; - if let Some(b) = ctx.lower_expr_to_place(root_expr, return_slot().into(), current)? { - ctx.set_terminator(b, TerminatorKind::Return, root_expr.into()); + if let Some(current) = ctx.lower_expr_to_place(root_expr, return_slot().into(), current)? { + let current = ctx.pop_drop_scope(current); + ctx.set_terminator(current, TerminatorKind::Return, root_expr.into()); } Ok(ctx.result) } diff --git a/crates/hir-ty/src/mir/lower/as_place.rs b/crates/hir-ty/src/mir/lower/as_place.rs index 8d8870da0d4..9515c18bb64 100644 --- a/crates/hir-ty/src/mir/lower/as_place.rs +++ b/crates/hir-ty/src/mir/lower/as_place.rs @@ -17,7 +17,7 @@ impl MirLowerCtx<'_> { prev_block: BasicBlockId, ) -> Result> { let ty = self.expr_ty_without_adjust(expr_id); - let place = self.temp(ty)?; + let place = self.temp(ty, prev_block, expr_id.into())?; let Some(current) = self.lower_expr_to_place_without_adjust(expr_id, place.into(), prev_block)? else { return Ok(None); }; @@ -34,7 +34,7 @@ impl MirLowerCtx<'_> { .last() .map(|x| x.target.clone()) .unwrap_or_else(|| self.expr_ty_without_adjust(expr_id)); - let place = self.temp(ty)?; + let place = self.temp(ty, prev_block, expr_id.into())?; let Some(current) = self.lower_expr_to_place_with_adjust(expr_id, place.into(), prev_block, adjustments)? else { return Ok(None); }; @@ -128,12 +128,8 @@ impl MirLowerCtx<'_> { match &self.body.exprs[expr_id] { Expr::Path(p) => { let resolver = resolver_for_expr(self.db.upcast(), self.owner, expr_id); - let Some(pr) = resolver.resolve_path_in_value_ns(self.db.upcast(), p) else { - return Err(MirLowerError::unresolved_path(self.db, p)); - }; - let pr = match pr { - ResolveValueResult::ValueNs(v) => v, - ResolveValueResult::Partial(..) => return try_rvalue(self), + let Some(pr) = resolver.resolve_path_in_value_ns_fully(self.db.upcast(), p) else { + return try_rvalue(self); }; match pr { ValueNs::LocalBinding(pat_id) => { @@ -143,7 +139,7 @@ impl MirLowerCtx<'_> { let ty = self.expr_ty_without_adjust(expr_id); let ref_ty = TyKind::Ref(Mutability::Not, static_lifetime(), ty).intern(Interner); - let mut temp: Place = self.temp(ref_ty)?.into(); + let mut temp: Place = self.temp(ref_ty, current, expr_id.into())?.into(); self.push_assignment( current, temp.clone(), @@ -252,7 +248,8 @@ impl MirLowerCtx<'_> { else { return Ok(None); }; - let l_index = self.temp(self.expr_ty_after_adjustments(*index))?; + let l_index = + self.temp(self.expr_ty_after_adjustments(*index), current, expr_id.into())?; let Some(current) = self.lower_expr_to_place(*index, l_index.into(), current)? else { return Ok(None); }; @@ -273,16 +270,12 @@ impl MirLowerCtx<'_> { span: MirSpan, index_fn: (FunctionId, Substitution), ) -> Result> { - let (mutability, borrow_kind) = match base_ty.as_reference() { - Some((_, _, mutability)) => { - (mutability, BorrowKind::Mut { allow_two_phase_borrow: false }) - } - None => (Mutability::Not, BorrowKind::Shared), + let mutability = match base_ty.as_reference() { + Some((_, _, mutability)) => mutability, + None => Mutability::Not, }; let result_ref = TyKind::Ref(mutability, static_lifetime(), result_ty).intern(Interner); - let ref_place: Place = self.temp(base_ty)?.into(); - self.push_assignment(current, ref_place.clone(), Rvalue::Ref(borrow_kind, place), span); - let mut result: Place = self.temp(result_ref)?.into(); + let mut result: Place = self.temp(result_ref, current, span)?.into(); let index_fn_op = Operand::const_zst( TyKind::FnDef( self.db.intern_callable_def(CallableDefId::FunctionId(index_fn.0)).into(), @@ -290,7 +283,7 @@ impl MirLowerCtx<'_> { ) .intern(Interner), ); - let Some(current) = self.lower_call(index_fn_op, vec![Operand::Copy(ref_place), index_operand], result.clone(), current, false, span)? else { + let Some(current) = self.lower_call(index_fn_op, vec![Operand::Copy(place), index_operand], result.clone(), current, false, span)? else { return Ok(None); }; result.projection.push(ProjectionElem::Deref); @@ -318,7 +311,7 @@ impl MirLowerCtx<'_> { }; let ty_ref = TyKind::Ref(chalk_mut, static_lifetime(), source_ty.clone()).intern(Interner); let target_ty_ref = TyKind::Ref(chalk_mut, static_lifetime(), target_ty).intern(Interner); - let ref_place: Place = self.temp(ty_ref)?.into(); + let ref_place: Place = self.temp(ty_ref, current, span)?.into(); self.push_assignment(current, ref_place.clone(), Rvalue::Ref(borrow_kind, place), span); let deref_trait = self .resolve_lang_item(trait_lang_item)? @@ -336,7 +329,7 @@ impl MirLowerCtx<'_> { ) .intern(Interner), ); - let mut result: Place = self.temp(target_ty_ref)?.into(); + let mut result: Place = self.temp(target_ty_ref, current, span)?.into(); let Some(current) = self.lower_call(deref_fn_op, vec![Operand::Copy(ref_place)], result.clone(), current, false, span)? else { return Ok(None); }; diff --git a/crates/hir-ty/src/mir/lower/pattern_matching.rs b/crates/hir-ty/src/mir/lower/pattern_matching.rs index 97aa3b0f215..45c245e281b 100644 --- a/crates/hir-ty/src/mir/lower/pattern_matching.rs +++ b/crates/hir-ty/src/mir/lower/pattern_matching.rs @@ -107,7 +107,57 @@ impl MirLowerCtx<'_> { )? } Pat::Range { .. } => not_supported!("range pattern"), - Pat::Slice { .. } => not_supported!("slice pattern"), + Pat::Slice { prefix, slice, suffix } => { + pattern_matching_dereference(&mut cond_ty, &mut binding_mode, &mut cond_place); + for (i, &pat) in prefix.iter().enumerate() { + let mut next_place = cond_place.clone(); + next_place + .projection + .push(ProjectionElem::ConstantIndex { offset: i as u64, from_end: false }); + let cond_ty = self.infer[pat].clone(); + (current, current_else) = self.pattern_match( + current, + current_else, + next_place, + cond_ty, + pat, + binding_mode, + )?; + } + if let Some(slice) = slice { + if let Pat::Bind { id, subpat: _ } = self.body[*slice] { + let mut next_place = cond_place.clone(); + next_place.projection.push(ProjectionElem::Subslice { + from: prefix.len() as u64, + to: suffix.len() as u64, + }); + (current, current_else) = self.pattern_match_binding( + id, + &mut binding_mode, + next_place, + (*slice).into(), + current, + current_else, + )?; + } + } + for (i, &pat) in suffix.iter().enumerate() { + let mut next_place = cond_place.clone(); + next_place + .projection + .push(ProjectionElem::ConstantIndex { offset: i as u64, from_end: true }); + let cond_ty = self.infer[pat].clone(); + (current, current_else) = self.pattern_match( + current, + current_else, + next_place, + cond_ty, + pat, + binding_mode, + )?; + } + (current, current_else) + } Pat::Path(p) => match self.infer.variant_resolution_for_pat(pattern) { Some(variant) => self.pattern_matching_variant( cond_ty, @@ -128,17 +178,19 @@ impl MirLowerCtx<'_> { match pr { ResolveValueResult::ValueNs(v) => match v { ValueNs::ConstId(c) => { - let tmp: Place = self.temp(cond_ty.clone())?.into(); + let tmp: Place = + self.temp(cond_ty.clone(), current, pattern.into())?.into(); let span = pattern.into(); self.lower_const( - c, + c.into(), current, tmp.clone(), Substitution::empty(Interner), span, cond_ty.clone(), )?; - let tmp2: Place = self.temp(TyBuilder::bool())?.into(); + let tmp2: Place = + self.temp(TyBuilder::bool(), current, pattern.into())?.into(); self.push_assignment( current, tmp2.clone(), @@ -180,8 +232,6 @@ impl MirLowerCtx<'_> { _ => not_supported!("expression path literal"), }, Pat::Bind { id, subpat } => { - let target_place = self.binding_local(*id)?; - let mode = self.body.bindings[*id].mode; if let Some(subpat) = subpat { (current, current_else) = self.pattern_match( current, @@ -192,26 +242,14 @@ impl MirLowerCtx<'_> { binding_mode, )? } - if matches!(mode, BindingAnnotation::Ref | BindingAnnotation::RefMut) { - binding_mode = mode; - } - self.push_storage_live(*id, current)?; - self.push_assignment( - current, - target_place.into(), - match binding_mode { - BindingAnnotation::Unannotated | BindingAnnotation::Mutable => { - Operand::Copy(cond_place).into() - } - BindingAnnotation::Ref => Rvalue::Ref(BorrowKind::Shared, cond_place), - BindingAnnotation::RefMut => Rvalue::Ref( - BorrowKind::Mut { allow_two_phase_borrow: false }, - cond_place, - ), - }, + self.pattern_match_binding( + *id, + &mut binding_mode, + cond_place, pattern.into(), - ); - (current, current_else) + current, + current_else, + )? } Pat::TupleStruct { path: _, args, ellipsis } => { let Some(variant) = self.infer.variant_resolution_for_pat(pattern) else { @@ -249,6 +287,38 @@ impl MirLowerCtx<'_> { }) } + fn pattern_match_binding( + &mut self, + id: BindingId, + binding_mode: &mut BindingAnnotation, + cond_place: Place, + span: MirSpan, + current: BasicBlockId, + current_else: Option, + ) -> Result<(BasicBlockId, Option)> { + let target_place = self.binding_local(id)?; + let mode = self.body.bindings[id].mode; + if matches!(mode, BindingAnnotation::Ref | BindingAnnotation::RefMut) { + *binding_mode = mode; + } + self.push_storage_live(id, current)?; + self.push_assignment( + current, + target_place.into(), + match *binding_mode { + BindingAnnotation::Unannotated | BindingAnnotation::Mutable => { + Operand::Copy(cond_place).into() + } + BindingAnnotation::Ref => Rvalue::Ref(BorrowKind::Shared, cond_place), + BindingAnnotation::RefMut => { + Rvalue::Ref(BorrowKind::Mut { allow_two_phase_borrow: false }, cond_place) + } + }, + span, + ); + Ok((current, current_else)) + } + fn pattern_match_const( &mut self, current_else: Option, @@ -259,7 +329,7 @@ impl MirLowerCtx<'_> { ) -> Result<(BasicBlockId, Option)> { 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())?.into(); + let discr: Place = self.temp(TyBuilder::bool(), current, pattern.into())?.into(); self.push_assignment( current, discr.clone(), @@ -297,7 +367,7 @@ impl MirLowerCtx<'_> { Ok(match variant { VariantId::EnumVariantId(v) => { let e = self.const_eval_discriminant(v)? as u128; - let tmp = self.discr_temp_place(); + let tmp = self.discr_temp_place(current); self.push_assignment( current, tmp.clone(), diff --git a/crates/hir-ty/src/tests/simple.rs b/crates/hir-ty/src/tests/simple.rs index e249cddc2fc..f92589ea77c 100644 --- a/crates/hir-ty/src/tests/simple.rs +++ b/crates/hir-ty/src/tests/simple.rs @@ -1812,6 +1812,20 @@ fn main() { //^ [(); 7] }"#, ); + check_types( + r#" +trait Foo { + fn x(self); +} + +impl Foo for u8 { + fn x(self) { + let t = [0; 4 + 2]; + //^ [i32; 6] + } +} + "#, + ); } #[test] @@ -2137,6 +2151,7 @@ async fn main() { 136..138 '()': () 150..151 'w': i32 154..166 'const { 92 }': i32 + 154..166 'const { 92 }': i32 162..164 '92': i32 176..177 't': i32 180..190 ''a: { 92 }': i32 diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 1fac95ae5e3..b82e0f36011 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -1857,18 +1857,34 @@ impl Function { self, db: &dyn HirDatabase, span_formatter: impl Fn(FileId, TextRange) -> String, - ) -> Result<(), String> { - let converter = |e: MirEvalError| { - let mut r = String::new(); - _ = e.pretty_print(&mut r, db, &span_formatter); - r + ) -> String { + let body = match db.mir_body(self.id.into()) { + Ok(body) => body, + Err(e) => { + let mut r = String::new(); + _ = e.pretty_print(&mut r, db, &span_formatter); + return r; + } }; - let body = db - .mir_body(self.id.into()) - .map_err(|e| MirEvalError::MirLowerError(self.id.into(), e)) - .map_err(converter)?; - interpret_mir(db, &body, Substitution::empty(Interner), false).map_err(converter)?; - Ok(()) + let (result, stdout, stderr) = + interpret_mir(db, &body, Substitution::empty(Interner), false); + let mut text = match result { + Ok(_) => "pass".to_string(), + Err(e) => { + let mut r = String::new(); + _ = e.pretty_print(&mut r, db, &span_formatter); + r + } + }; + if !stdout.is_empty() { + text += "\n--------- stdout ---------\n"; + text += &stdout; + } + if !stderr.is_empty() { + text += "\n--------- stderr ---------\n"; + text += &stderr; + } + text } } @@ -3665,9 +3681,9 @@ impl Type { } } - pub fn as_array(&self, _db: &dyn HirDatabase) -> Option<(Type, usize)> { + pub fn as_array(&self, db: &dyn HirDatabase) -> Option<(Type, usize)> { if let TyKind::Array(ty, len) = &self.ty.kind(Interner) { - try_const_usize(len).map(|x| (self.derived(ty.clone()), x as usize)) + try_const_usize(db, len).map(|x| (self.derived(ty.clone()), x as usize)) } else { None } diff --git a/crates/ide/src/interpret_function.rs b/crates/ide/src/interpret_function.rs index d111f98955a..cbcbb4b09db 100644 --- a/crates/ide/src/interpret_function.rs +++ b/crates/ide/src/interpret_function.rs @@ -42,8 +42,5 @@ fn find_and_interpret(db: &RootDatabase, position: FilePosition) -> Option"); format!("file://{path}#{}:{}", line_col.line + 1, line_col.col) }; - match def.eval(db, span_formatter) { - Ok(_) => Some("pass".to_string()), - Err(e) => Some(e), - } + Some(def.eval(db, span_formatter)) } diff --git a/crates/test-utils/src/minicore.rs b/crates/test-utils/src/minicore.rs index c693235f344..07782114cf6 100644 --- a/crates/test-utils/src/minicore.rs +++ b/crates/test-utils/src/minicore.rs @@ -324,6 +324,13 @@ pub mod mem { } } } + +pub mod ptr { + #[lang = "drop_in_place"] + pub unsafe fn drop_in_place(to_drop: *mut T) { + unsafe { drop_in_place(to_drop) } + } +} // endregion:drop pub mod ops { @@ -1075,10 +1082,8 @@ pub mod iter { // region:panic mod panic { - pub macro panic_2021 { - ($($t:tt)+) => ( - /* Nothing yet */ - ), + pub macro panic_2021($($t:tt)+) { + /* Nothing yet */ } } // endregion:panic @@ -1158,8 +1163,8 @@ pub mod prelude { ops::Drop, // :drop ops::{Fn, FnMut, FnOnce}, // :fn option::Option::{self, None, Some}, // :option - result::Result::{self, Err, Ok}, // :result panic, // :panic + result::Result::{self, Err, Ok}, // :result }; }