Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir/src/lib.rs')
| -rw-r--r-- | crates/hir/src/lib.rs | 710 |
1 files changed, 559 insertions, 151 deletions
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 35424feec8..5926d86542 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -6,7 +6,7 @@ //! applied. So, the relation between syntax and HIR is many-to-one. //! //! HIR is the public API of the all of the compiler logic above syntax trees. -//! It is written in "OO" style. Each type is self contained (as in, it knows it's +//! It is written in "OO" style. Each type is self contained (as in, it knows its //! parents and full context). It should be "clean code". //! //! `hir_*` crates are the implementation of the compiler logic. @@ -33,24 +33,25 @@ pub mod symbols; mod display; -use std::{iter, ops::ControlFlow, sync::Arc}; +use std::{iter, ops::ControlFlow}; use arrayvec::ArrayVec; use base_db::{CrateDisplayName, CrateId, CrateOrigin, Edition, FileId, ProcMacroKind}; use either::Either; use hir_def::{ - adt::VariantData, body::{BodyDiagnostic, SyntheticSyntax}, - expr::{BindingAnnotation, BindingId, ExprOrPatId, LabelId, Pat}, + data::adt::VariantData, generics::{LifetimeParamData, TypeOrConstParamData, TypeParamProvenance}, + hir::{BindingAnnotation, BindingId, ExprOrPatId, LabelId, Pat}, item_tree::ItemTreeNode, - lang_item::{LangItem, LangItemTarget}, - layout::{Layout, LayoutError, ReprOptions}, + lang_item::LangItemTarget, + layout::{self, ReprOptions, TargetDataLayout}, + macro_id_to_def_id, nameres::{self, diagnostics::DefDiagnostic, ModuleOrigin}, per_ns::PerNs, resolver::{HasResolver, Resolver}, src::HasSource as _, - AdtId, AssocItemId, AssocItemLoc, AttrDefId, ConstId, ConstParamId, DefWithBodyId, EnumId, + AssocItemId, AssocItemLoc, AttrDefId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId, FunctionId, GenericDefId, HasModule, ImplId, ItemContainerId, LifetimeParamId, LocalEnumVariantId, LocalFieldId, Lookup, MacroExpander, MacroId, ModuleId, StaticId, StructId, TraitAliasId, TraitId, TypeAliasId, TypeOrConstParamId, TypeParamId, UnionId, @@ -61,14 +62,15 @@ use hir_ty::{ consteval::{try_const_usize, unknown_const_as_generic, ConstEvalError, ConstExt}, diagnostics::BodyValidationDiagnostic, display::HexifiedConst, - layout::layout_of_ty, + layout::{Layout as TyLayout, RustcEnumVariantIdx, TagEncoding}, method_resolution::{self, TyFingerprint}, mir::{self, interpret_mir}, primitive::UintTy, traits::FnTrait, AliasTy, CallableDefId, CallableSig, Canonical, CanonicalVarKinds, Cast, ClosureId, GenericArgData, Interner, ParamKind, QuantifiedWhereClause, Scalar, Substitution, - TraitEnvironment, TraitRefExt, Ty, TyBuilder, TyDefId, TyExt, TyKind, WhereClause, + TraitEnvironment, TraitRefExt, Ty, TyBuilder, TyDefId, TyExt, TyKind, ValueTyDefId, + WhereClause, }; use itertools::Itertools; use nameres::diagnostics::DefDiagnosticKind; @@ -79,6 +81,7 @@ use syntax::{ ast::{self, HasAttrs as _, HasDocComments, HasName}, AstNode, AstPtr, SmolStr, SyntaxNode, SyntaxNodePtr, TextRange, T, }; +use triomphe::Arc; use crate::db::{DefDatabase, HirDatabase}; @@ -86,11 +89,13 @@ pub use crate::{ attrs::{HasAttrs, Namespace}, diagnostics::{ AnyDiagnostic, BreakOutsideOfLoop, ExpectedFunction, InactiveCode, IncoherentImpl, - IncorrectCase, InvalidDeriveTarget, MacroError, MalformedDerive, MismatchedArgCount, - MissingFields, MissingMatchArms, MissingUnsafe, NeedMut, NoSuchField, PrivateAssocItem, - PrivateField, ReplaceFilterMapNextWithFindMap, TypeMismatch, UnimplementedBuiltinMacro, - UnresolvedExternCrate, UnresolvedField, UnresolvedImport, UnresolvedMacroCall, - UnresolvedMethodCall, UnresolvedModule, UnresolvedProcMacro, UnusedMut, + IncorrectCase, InvalidDeriveTarget, MacroDefError, MacroError, MacroExpansionParseError, + MalformedDerive, MismatchedArgCount, MissingFields, MissingMatchArms, MissingUnsafe, + MovedOutOfRef, NeedMut, NoSuchField, PrivateAssocItem, PrivateField, + ReplaceFilterMapNextWithFindMap, TypeMismatch, TypedHole, UndeclaredLabel, + UnimplementedBuiltinMacro, UnreachableLabel, UnresolvedExternCrate, UnresolvedField, + UnresolvedImport, UnresolvedMacroCall, UnresolvedMethodCall, UnresolvedModule, + UnresolvedProcMacro, UnusedMut, }, has_source::HasSource, semantics::{PathResolution, Semantics, SemanticsScope, TypeInfo, VisibleTraits}, @@ -108,20 +113,18 @@ pub use crate::{ pub use { cfg::{CfgAtom, CfgExpr, CfgOptions}, hir_def::{ - adt::StructKind, - attr::{Attrs, AttrsWithOwner, Documentation}, - builtin_attr::AttributeTemplate, + attr::{builtin::AttributeTemplate, Attrs, AttrsWithOwner, Documentation}, + data::adt::StructKind, find_path::PrefixKind, import_map, - nameres::ModuleSource, + lang_item::LangItem, + nameres::{DefMap, ModuleSource}, path::{ModPath, PathKind}, type_ref::{Mutability, TypeRef}, visibility::Visibility, - // FIXME: This is here since it is input of a method in `HirWrite` - // and things outside of hir need to implement that trait. We probably - // should move whole `hir_ty::display` to this crate so we will become - // able to use `ModuleDef` or `Definition` instead of `ModuleDefId`. - ModuleDefId, + // FIXME: This is here since some queries take it as input that are used + // outside of hir. + {AdtId, ModuleDefId}, }, hir_expand::{ attrs::Attr, @@ -129,7 +132,8 @@ pub use { ExpandResult, HirFileId, InFile, MacroFile, Origin, }, hir_ty::{ - display::{HirDisplay, HirDisplayError, HirWrite}, + display::{ClosureStyle, HirDisplay, HirDisplayError, HirWrite}, + layout::LayoutError, mir::MirEvalError, PointerCast, Safety, }, @@ -198,7 +202,7 @@ impl Crate { pub fn root_module(self, db: &dyn HirDatabase) -> Module { let def_map = db.crate_def_map(self.id); - Module { id: def_map.module_id(def_map.root()) } + Module { id: def_map.module_id(DefMap::ROOT) } } pub fn modules(self, db: &dyn HirDatabase) -> Vec<Module> { @@ -253,7 +257,8 @@ impl Crate { } pub fn potential_cfg(&self, db: &dyn HirDatabase) -> CfgOptions { - db.crate_graph()[self.id].potential_cfg_options.clone() + let data = &db.crate_graph()[self.id]; + data.potential_cfg_options.clone().unwrap_or_else(|| data.cfg_options.clone()) } } @@ -326,7 +331,7 @@ impl ModuleDef { segments.extend(m.name(db)) } segments.reverse(); - Some(segments.into_iter().join("::")) + Some(segments.iter().map(|it| it.display(db.upcast())).join("::")) } pub fn canonical_module_path( @@ -470,12 +475,11 @@ impl Module { /// in the module tree of any target in `Cargo.toml`. pub fn crate_root(self, db: &dyn HirDatabase) -> Module { let def_map = db.crate_def_map(self.id.krate()); - Module { id: def_map.module_id(def_map.root()) } + Module { id: def_map.module_id(DefMap::ROOT) } } - pub fn is_crate_root(self, db: &dyn HirDatabase) -> bool { - let def_map = db.crate_def_map(self.id.krate()); - def_map.root() == self.id.local_id + pub fn is_crate_root(self) -> bool { + DefMap::ROOT == self.id.local_id } /// Iterates over all child modules. @@ -552,7 +556,11 @@ impl Module { /// Fills `acc` with the module's diagnostics. pub fn diagnostics(self, db: &dyn HirDatabase, acc: &mut Vec<AnyDiagnostic>) { let _p = profile::span("Module::diagnostics").detail(|| { - format!("{:?}", self.name(db).map_or("<unknown>".into(), |name| name.to_string())) + format!( + "{:?}", + self.name(db) + .map_or("<unknown>".into(), |name| name.display(db.upcast()).to_string()) + ) }); let def_map = self.id.def_map(db.upcast()); for diag in def_map.diagnostics() { @@ -562,6 +570,7 @@ impl Module { } emit_def_diagnostic(db, acc, diag); } + for decl in self.declarations(db) { match decl { ModuleDef::Module(m) => { @@ -600,9 +609,11 @@ impl Module { } acc.extend(decl.diagnostics(db)) } + ModuleDef::Macro(m) => emit_macro_def_diagnostics(db, acc, m), _ => acc.extend(decl.diagnostics(db)), } } + self.legacy_macros(db).into_iter().for_each(|m| emit_macro_def_diagnostics(db, acc, m)); let inherent_impls = db.inherent_impls_in_crate(self.id.krate()); @@ -684,8 +695,31 @@ impl Module { } } +fn emit_macro_def_diagnostics(db: &dyn HirDatabase, acc: &mut Vec<AnyDiagnostic>, m: Macro) { + let id = macro_id_to_def_id(db.upcast(), m.id); + if let Err(e) = db.macro_def(id) { + let Some(ast) = id.ast_id().left() else { + never!("MacroDefError for proc-macro: {:?}", e); + return; + }; + emit_def_diagnostic_( + db, + acc, + &DefDiagnosticKind::MacroDefError { ast, message: e.to_string() }, + ); + } +} + fn emit_def_diagnostic(db: &dyn HirDatabase, acc: &mut Vec<AnyDiagnostic>, diag: &DefDiagnostic) { - match &diag.kind { + emit_def_diagnostic_(db, acc, &diag.kind) +} + +fn emit_def_diagnostic_( + db: &dyn HirDatabase, + acc: &mut Vec<AnyDiagnostic>, + diag: &DefDiagnosticKind, +) { + match diag { DefDiagnosticKind::UnresolvedModule { ast: declaration, candidates } => { let decl = declaration.to_node(db.upcast()); acc.push( @@ -725,7 +759,6 @@ fn emit_def_diagnostic(db: &dyn HirDatabase, acc: &mut Vec<AnyDiagnostic>, diag: .into(), ); } - DefDiagnosticKind::UnresolvedProcMacro { ast, krate } => { let (node, precise_location, macro_name, kind) = precise_macro_call_location(ast, db); acc.push( @@ -733,7 +766,6 @@ fn emit_def_diagnostic(db: &dyn HirDatabase, acc: &mut Vec<AnyDiagnostic>, diag: .into(), ); } - DefDiagnosticKind::UnresolvedMacroCall { ast, path } => { let (node, precise_location, _, _) = precise_macro_call_location(ast, db); acc.push( @@ -746,12 +778,16 @@ fn emit_def_diagnostic(db: &dyn HirDatabase, acc: &mut Vec<AnyDiagnostic>, diag: .into(), ); } - DefDiagnosticKind::MacroError { ast, message } => { let (node, precise_location, _, _) = precise_macro_call_location(ast, db); acc.push(MacroError { node, precise_location, message: message.clone() }.into()); } - + DefDiagnosticKind::MacroExpansionParseError { ast, errors } => { + let (node, precise_location, _, _) = precise_macro_call_location(ast, db); + acc.push( + MacroExpansionParseError { node, precise_location, errors: errors.clone() }.into(), + ); + } DefDiagnosticKind::UnimplementedBuiltinMacro { ast } => { let node = ast.to_node(db.upcast()); // Must have a name, otherwise we wouldn't emit it. @@ -793,6 +829,17 @@ fn emit_def_diagnostic(db: &dyn HirDatabase, acc: &mut Vec<AnyDiagnostic>, diag: None => stdx::never!("derive diagnostic on item without derive attribute"), } } + DefDiagnosticKind::MacroDefError { ast, message } => { + let node = ast.to_node(db.upcast()); + acc.push( + MacroDefError { + node: InFile::new(ast.file_id, AstPtr::new(&node)), + name: node.name().map(|it| it.syntax().text_range()), + message: message.clone(), + } + .into(), + ); + } } } @@ -800,7 +847,7 @@ fn precise_macro_call_location( ast: &MacroCallKind, db: &dyn HirDatabase, ) -> (InFile<SyntaxNodePtr>, Option<TextRange>, Option<String>, MacroKind) { - // FIXME: maaybe we actually want slightly different ranges for the different macro diagnostics + // FIXME: maybe we actually want slightly different ranges for the different macro diagnostics // - e.g. the full attribute for macro errors, but only the name for name resolution match ast { MacroCallKind::FnLike { ast_id, .. } => { @@ -915,7 +962,8 @@ impl Field { } pub fn layout(&self, db: &dyn HirDatabase) -> Result<Layout, LayoutError> { - layout_of_ty(db, &self.ty(db).ty, self.parent.module(db).krate().into()) + db.layout_of_ty(self.ty(db).ty.clone(), self.parent.module(db).krate().into()) + .map(|layout| Layout(layout, db.target_data_layout(self.krate(db).into()).unwrap())) } pub fn parent_def(&self, _db: &dyn HirDatabase) -> VariantDef { @@ -959,6 +1007,10 @@ impl Struct { Type::from_def(db, self.id) } + pub fn constructor_ty(self, db: &dyn HirDatabase) -> Type { + Type::from_value_def(db, self.id) + } + pub fn repr(self, db: &dyn HirDatabase) -> Option<ReprOptions> { db.struct_data(self.id).repr } @@ -996,6 +1048,10 @@ impl Union { Type::from_def(db, self.id) } + pub fn constructor_ty(self, db: &dyn HirDatabase) -> Type { + Type::from_value_def(db, self.id) + } + pub fn fields(self, db: &dyn HirDatabase) -> Vec<Field> { db.union_data(self.id) .variant_data @@ -1034,6 +1090,10 @@ impl Enum { db.enum_data(self.id).variants.iter().map(|(id, _)| Variant { parent: self, id }).collect() } + pub fn repr(self, db: &dyn HirDatabase) -> Option<ReprOptions> { + db.enum_data(self.id).repr + } + pub fn ty(self, db: &dyn HirDatabase) -> Type { Type::from_def(db, self.id) } @@ -1043,7 +1103,7 @@ impl Enum { Type::new_for_crate( self.id.lookup(db.upcast()).container.krate(), TyBuilder::builtin(match db.enum_data(self.id).variant_body_type() { - hir_def::layout::IntegerType::Pointer(sign) => match sign { + layout::IntegerType::Pointer(sign) => match sign { true => hir_def::builtin_type::BuiltinType::Int( hir_def::builtin_type::BuiltinInt::Isize, ), @@ -1051,29 +1111,34 @@ impl Enum { hir_def::builtin_type::BuiltinUint::Usize, ), }, - hir_def::layout::IntegerType::Fixed(i, sign) => match sign { + layout::IntegerType::Fixed(i, sign) => match sign { true => hir_def::builtin_type::BuiltinType::Int(match i { - hir_def::layout::Integer::I8 => hir_def::builtin_type::BuiltinInt::I8, - hir_def::layout::Integer::I16 => hir_def::builtin_type::BuiltinInt::I16, - hir_def::layout::Integer::I32 => hir_def::builtin_type::BuiltinInt::I32, - hir_def::layout::Integer::I64 => hir_def::builtin_type::BuiltinInt::I64, - hir_def::layout::Integer::I128 => hir_def::builtin_type::BuiltinInt::I128, + layout::Integer::I8 => hir_def::builtin_type::BuiltinInt::I8, + layout::Integer::I16 => hir_def::builtin_type::BuiltinInt::I16, + layout::Integer::I32 => hir_def::builtin_type::BuiltinInt::I32, + layout::Integer::I64 => hir_def::builtin_type::BuiltinInt::I64, + layout::Integer::I128 => hir_def::builtin_type::BuiltinInt::I128, }), false => hir_def::builtin_type::BuiltinType::Uint(match i { - hir_def::layout::Integer::I8 => hir_def::builtin_type::BuiltinUint::U8, - hir_def::layout::Integer::I16 => hir_def::builtin_type::BuiltinUint::U16, - hir_def::layout::Integer::I32 => hir_def::builtin_type::BuiltinUint::U32, - hir_def::layout::Integer::I64 => hir_def::builtin_type::BuiltinUint::U64, - hir_def::layout::Integer::I128 => hir_def::builtin_type::BuiltinUint::U128, + layout::Integer::I8 => hir_def::builtin_type::BuiltinUint::U8, + layout::Integer::I16 => hir_def::builtin_type::BuiltinUint::U16, + layout::Integer::I32 => hir_def::builtin_type::BuiltinUint::U32, + layout::Integer::I64 => hir_def::builtin_type::BuiltinUint::U64, + layout::Integer::I128 => hir_def::builtin_type::BuiltinUint::U128, }), }, }), ) } + /// Returns true if at least one variant of this enum is a non-unit variant. pub fn is_data_carrying(self, db: &dyn HirDatabase) -> bool { self.variants(db).iter().any(|v| !matches!(v.kind(db), StructKind::Unit)) } + + pub fn layout(self, db: &dyn HirDatabase) -> Result<Layout, LayoutError> { + Adt::from(self).layout(db) + } } impl HasVisibility for Enum { @@ -1103,6 +1168,10 @@ impl Variant { self.parent } + pub fn constructor_ty(self, db: &dyn HirDatabase) -> Type { + Type::from_value_def(db, EnumVariantId { parent: self.parent.id, local_id: self.id }) + } + pub fn name(self, db: &dyn HirDatabase) -> Name { db.enum_data(self.parent.id).variants[self.id].name.clone() } @@ -1130,6 +1199,18 @@ impl Variant { pub fn eval(self, db: &dyn HirDatabase) -> Result<i128, ConstEvalError> { db.const_eval_discriminant(self.into()) } + + pub fn layout(&self, db: &dyn HirDatabase) -> Result<Layout, LayoutError> { + let parent_enum = self.parent_enum(db); + let parent_layout = parent_enum.layout(db)?; + Ok(match &parent_layout.0.variants { + layout::Variants::Multiple { variants, .. } => Layout( + Arc::new(variants[RustcEnumVariantIdx(self.id)].clone()), + db.target_data_layout(parent_enum.krate(db).into()).unwrap(), + ), + _ => parent_layout, + }) + } } /// Variants inherit visibility from the parent enum. @@ -1161,7 +1242,9 @@ impl Adt { if db.generic_params(self.into()).iter().count() != 0 { return Err(LayoutError::HasPlaceholder); } - db.layout_of_adt(self.into(), Substitution::empty(Interner)) + let krate = self.krate(db).id; + db.layout_of_adt(self.into(), Substitution::empty(Interner), krate) + .map(|layout| Layout(layout, db.target_data_layout(krate).unwrap())) } /// Turns this ADT into a type. Any type parameters of the ADT will be @@ -1392,6 +1475,12 @@ impl DefWithBody { } .into(), ), + BodyDiagnostic::UnreachableLabel { node, name } => { + acc.push(UnreachableLabel { node: node.clone(), name: name.clone() }.into()) + } + BodyDiagnostic::UndeclaredLabel { node, name } => { + acc.push(UndeclaredLabel { node: node.clone(), name: name.clone() }.into()) + } } } @@ -1404,14 +1493,6 @@ impl DefWithBody { let field = source_map.field_syntax(expr); acc.push(NoSuchField { field }.into()) } - &hir_ty::InferenceDiagnostic::BreakOutsideOfLoop { - expr, - is_break, - bad_value_break, - } => { - let expr = expr_syntax(expr); - acc.push(BreakOutsideOfLoop { expr, is_break, bad_value_break }.into()) - } &hir_ty::InferenceDiagnostic::MismatchedArgCount { call_expr, expected, found } => { acc.push( MismatchedArgCount { call_expr: expr_syntax(call_expr), expected, found } @@ -1483,14 +1564,29 @@ impl DefWithBody { .into(), ) } + &hir_ty::InferenceDiagnostic::BreakOutsideOfLoop { + expr, + is_break, + bad_value_break, + } => { + let expr = expr_syntax(expr); + acc.push(BreakOutsideOfLoop { expr, is_break, bad_value_break }.into()) + } + hir_ty::InferenceDiagnostic::TypedHole { expr, expected } => { + let expr = expr_syntax(*expr); + acc.push( + TypedHole { + expr, + expected: Type::new(db, DefWithBodyId::from(self), expected.clone()), + } + .into(), + ) + } } } for (pat_or_expr, mismatch) in infer.type_mismatches() { let expr_or_pat = match pat_or_expr { ExprOrPatId::ExprId(expr) => source_map.expr_syntax(expr).map(Either::Left), - // FIXME: Re-enable these once we have less false positives - ExprOrPatId::PatId(_pat) => continue, - #[allow(unreachable_patterns)] ExprOrPatId::PatId(pat) => source_map.pat_syntax(pat).map(Either::Right), }; let expr_or_pat = match expr_or_pat { @@ -1515,7 +1611,7 @@ impl DefWithBody { match source_map.expr_syntax(expr) { Ok(expr) => acc.push(MissingUnsafe { expr }.into()), Err(SyntheticSyntax) => { - // FIXME: Here and eslwhere in this file, the `expr` was + // FIXME: Here and elsewhere in this file, the `expr` was // desugared, report or assert that this doesn't happen. } } @@ -1523,35 +1619,71 @@ impl DefWithBody { let hir_body = db.body(self.into()); - if let Ok(borrowck_result) = db.borrowck(self.into()) { - let mir_body = &borrowck_result.mir_body; - let mol = &borrowck_result.mutability_of_locals; - for (binding_id, _) in hir_body.bindings.iter() { - let need_mut = &mol[mir_body.binding_locals[binding_id]]; - let local = Local { parent: self.into(), binding_id }; - match (need_mut, local.is_mut(db)) { - (mir::MutabilityReason::Mut { .. }, true) - | (mir::MutabilityReason::Not, false) => (), - (mir::MutabilityReason::Mut { spans }, false) => { - for span in spans { - let span: InFile<SyntaxNodePtr> = match span { - mir::MirSpan::ExprId(e) => match source_map.expr_syntax(*e) { - Ok(s) => s.map(|x| x.into()), - Err(_) => continue, - }, - mir::MirSpan::PatId(p) => match source_map.pat_syntax(*p) { - Ok(s) => s.map(|x| match x { - Either::Left(e) => e.into(), - Either::Right(e) => e.into(), - }), - Err(_) => continue, - }, - mir::MirSpan::Unknown => continue, - }; - acc.push(NeedMut { local, span }.into()); + if let Ok(borrowck_results) = db.borrowck(self.into()) { + for borrowck_result in borrowck_results.iter() { + let mir_body = &borrowck_result.mir_body; + for moof in &borrowck_result.moved_out_of_ref { + let span: InFile<SyntaxNodePtr> = match moof.span { + mir::MirSpan::ExprId(e) => match source_map.expr_syntax(e) { + Ok(s) => s.map(|x| x.into()), + Err(_) => continue, + }, + mir::MirSpan::PatId(p) => match source_map.pat_syntax(p) { + Ok(s) => s.map(|x| match x { + Either::Left(e) => e.into(), + Either::Right(e) => e.into(), + }), + Err(_) => continue, + }, + mir::MirSpan::Unknown => continue, + }; + acc.push( + MovedOutOfRef { ty: Type::new_for_crate(krate, moof.ty.clone()), span } + .into(), + ) + } + let mol = &borrowck_result.mutability_of_locals; + for (binding_id, binding_data) in hir_body.bindings.iter() { + if binding_data.problems.is_some() { + // We should report specific diagnostics for these problems, not `need-mut` and `unused-mut`. + continue; + } + let Some(&local) = mir_body.binding_locals.get(binding_id) else { + continue; + }; + let need_mut = &mol[local]; + let local = Local { parent: self.into(), binding_id }; + match (need_mut, local.is_mut(db)) { + (mir::MutabilityReason::Mut { .. }, true) + | (mir::MutabilityReason::Not, false) => (), + (mir::MutabilityReason::Mut { spans }, false) => { + for span in spans { + let span: InFile<SyntaxNodePtr> = match span { + mir::MirSpan::ExprId(e) => match source_map.expr_syntax(*e) { + Ok(s) => s.map(|x| x.into()), + Err(_) => continue, + }, + mir::MirSpan::PatId(p) => match source_map.pat_syntax(*p) { + Ok(s) => s.map(|x| match x { + Either::Left(e) => e.into(), + Either::Right(e) => e.into(), + }), + Err(_) => continue, + }, + mir::MirSpan::Unknown => continue, + }; + acc.push(NeedMut { local, span }.into()); + } + } + (mir::MutabilityReason::Not, true) => { + if !infer.mutated_bindings_in_closure.contains(&binding_id) { + let should_ignore = matches!(body[binding_id].name.as_str(), Some(x) if x.starts_with("_")); + if !should_ignore { + acc.push(UnusedMut { local }.into()) + } + } } } - (mir::MutabilityReason::Not, true) => acc.push(UnusedMut { local }.into()), } } } @@ -1686,6 +1818,10 @@ impl Function { db.function_data(self.id).name.clone() } + pub fn ty(self, db: &dyn HirDatabase) -> Type { + Type::from_value_def(db, self.id) + } + /// Get this function's return type pub fn ret_type(self, db: &dyn HirDatabase) -> Type { let resolver = self.id.resolver(db.upcast()); @@ -1797,12 +1933,41 @@ impl Function { def_map.fn_as_proc_macro(self.id).map(|id| Macro { id: id.into() }) } - pub fn eval(self, db: &dyn HirDatabase) -> Result<(), MirEvalError> { - let body = db - .mir_body(self.id.into()) - .map_err(|e| MirEvalError::MirLowerError(self.id.into(), e))?; - interpret_mir(db, &body, false)?; - Ok(()) + pub fn eval( + self, + db: &dyn HirDatabase, + span_formatter: impl Fn(FileId, TextRange) -> String, + ) -> String { + let body = match db.monomorphized_mir_body( + self.id.into(), + Substitution::empty(Interner), + db.trait_environment(self.id.into()), + ) { + Ok(body) => body, + Err(e) => { + let mut r = String::new(); + _ = e.pretty_print(&mut r, db, &span_formatter); + return r; + } + }; + let (result, stdout, stderr) = interpret_mir(db, &body, 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 } } @@ -1837,7 +2002,7 @@ impl Param { } pub fn name(&self, db: &dyn HirDatabase) -> Option<Name> { - db.function_data(self.func.id).params[self.idx].0.clone() + Some(self.as_local(db)?.name(db)) } pub fn as_local(&self, db: &dyn HirDatabase) -> Option<Local> { @@ -1878,7 +2043,7 @@ impl SelfParam { func_data .params .first() - .map(|(_, param)| match &**param { + .map(|param| match &**param { TypeRef::Reference(.., mutability) => match mutability { hir_def::type_ref::Mutability::Shared => Access::Shared, hir_def::type_ref::Mutability::Mut => Access::Exclusive, @@ -1939,24 +2104,12 @@ impl Const { } pub fn ty(self, db: &dyn HirDatabase) -> Type { - let data = db.const_data(self.id); - let resolver = self.id.resolver(db.upcast()); - let ctx = hir_ty::TyLoweringContext::new(db, &resolver); - let ty = ctx.lower_ty(&data.type_ref); - Type::new_with_resolver_inner(db, &resolver, ty) + Type::from_value_def(db, self.id) } pub fn render_eval(self, db: &dyn HirDatabase) -> Result<String, ConstEvalError> { - let c = db.const_eval(self.id)?; + let c = db.const_eval(self.id.into(), Substitution::empty(Interner))?; let r = format!("{}", HexifiedConst(c).display(db)); - // We want to see things like `<utf8-error>` and `<layout-error>` as they are probably bug in our - // implementation, but there is no need to show things like `<enum-not-supported>` or `<ref-not-supported>` to - // the user. - if r.contains("not-supported>") { - return Err(ConstEvalError::MirEvalError(MirEvalError::NotSupported( - "rendering complex constants".to_string(), - ))); - } return Ok(r); } } @@ -1990,11 +2143,7 @@ impl Static { } pub fn ty(self, db: &dyn HirDatabase) -> Type { - let data = db.static_data(self.id); - let resolver = self.id.resolver(db.upcast()); - let ctx = hir_ty::TyLoweringContext::new(db, &resolver); - let ty = ctx.lower_ty(&data.type_ref); - Type::new_with_resolver_inner(db, &resolver, ty) + Type::from_value_def(db, self.id) } } @@ -2545,8 +2694,12 @@ impl LocalSource { self.source.file_id.original_file(db.upcast()) } - pub fn name(&self) -> Option<ast::Name> { - self.source.value.name() + pub fn file(&self) -> HirFileId { + self.source.file_id + } + + pub fn name(&self) -> Option<InFile<ast::Name>> { + self.source.as_ref().map(|it| it.name()).transpose() } pub fn syntax(&self) -> &SyntaxNode { @@ -2689,9 +2842,7 @@ impl BuiltinAttr { } fn builtin(name: &str) -> Option<Self> { - hir_def::builtin_attr::INERT_ATTRIBUTES - .iter() - .position(|tool| tool.name == name) + hir_def::attr::builtin::find_builtin_attr_idx(name) .map(|idx| BuiltinAttr { krate: None, idx: idx as u32 }) } @@ -2699,14 +2850,14 @@ impl BuiltinAttr { // FIXME: Return a `Name` here match self.krate { Some(krate) => db.crate_def_map(krate).registered_attrs()[self.idx as usize].clone(), - None => SmolStr::new(hir_def::builtin_attr::INERT_ATTRIBUTES[self.idx as usize].name), + None => SmolStr::new(hir_def::attr::builtin::INERT_ATTRIBUTES[self.idx as usize].name), } } pub fn template(&self, _: &dyn HirDatabase) -> Option<AttributeTemplate> { match self.krate { Some(_) => None, - None => Some(hir_def::builtin_attr::INERT_ATTRIBUTES[self.idx as usize].template), + None => Some(hir_def::attr::builtin::INERT_ATTRIBUTES[self.idx as usize].template), } } } @@ -2729,7 +2880,7 @@ impl ToolModule { } fn builtin(name: &str) -> Option<Self> { - hir_def::builtin_attr::TOOL_MODULES + hir_def::attr::builtin::TOOL_MODULES .iter() .position(|&tool| tool == name) .map(|idx| ToolModule { krate: None, idx: idx as u32 }) @@ -2739,7 +2890,7 @@ impl ToolModule { // FIXME: Return a `Name` here match self.krate { Some(krate) => db.crate_def_map(krate).registered_tools()[self.idx as usize].clone(), - None => SmolStr::new(hir_def::builtin_attr::TOOL_MODULES[self.idx as usize]), + None => SmolStr::new(hir_def::attr::builtin::TOOL_MODULES[self.idx as usize]), } } } @@ -3117,6 +3268,103 @@ impl TraitRef { } } +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct Closure { + id: ClosureId, + subst: Substitution, +} + +impl From<Closure> for ClosureId { + fn from(value: Closure) -> Self { + value.id + } +} + +impl Closure { + fn as_ty(self) -> Ty { + TyKind::Closure(self.id, self.subst).intern(Interner) + } + + pub fn display_with_id(&self, db: &dyn HirDatabase) -> String { + self.clone().as_ty().display(db).with_closure_style(ClosureStyle::ClosureWithId).to_string() + } + + pub fn display_with_impl(&self, db: &dyn HirDatabase) -> String { + self.clone().as_ty().display(db).with_closure_style(ClosureStyle::ImplFn).to_string() + } + + pub fn captured_items(&self, db: &dyn HirDatabase) -> Vec<ClosureCapture> { + let owner = db.lookup_intern_closure((self.id).into()).0; + let infer = &db.infer(owner); + let info = infer.closure_info(&self.id); + info.0 + .iter() + .cloned() + .map(|capture| ClosureCapture { owner, closure: self.id, capture }) + .collect() + } + + pub fn capture_types(&self, db: &dyn HirDatabase) -> Vec<Type> { + let owner = db.lookup_intern_closure((self.id).into()).0; + let infer = &db.infer(owner); + let (captures, _) = infer.closure_info(&self.id); + captures + .iter() + .cloned() + .map(|capture| Type { + env: db.trait_environment_for_body(owner), + ty: capture.ty(&self.subst), + }) + .collect() + } + + pub fn fn_trait(&self, db: &dyn HirDatabase) -> FnTrait { + let owner = db.lookup_intern_closure((self.id).into()).0; + let infer = &db.infer(owner); + let info = infer.closure_info(&self.id); + info.1 + } +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct ClosureCapture { + owner: DefWithBodyId, + closure: ClosureId, + capture: hir_ty::CapturedItem, +} + +impl ClosureCapture { + pub fn local(&self) -> Local { + Local { parent: self.owner, binding_id: self.capture.local() } + } + + pub fn kind(&self) -> CaptureKind { + match self.capture.kind() { + hir_ty::CaptureKind::ByRef( + hir_ty::mir::BorrowKind::Shallow | hir_ty::mir::BorrowKind::Shared, + ) => CaptureKind::SharedRef, + hir_ty::CaptureKind::ByRef(hir_ty::mir::BorrowKind::Unique) => { + CaptureKind::UniqueSharedRef + } + hir_ty::CaptureKind::ByRef(hir_ty::mir::BorrowKind::Mut { .. }) => { + CaptureKind::MutableRef + } + hir_ty::CaptureKind::ByValue => CaptureKind::Move, + } + } + + pub fn display_place(&self, db: &dyn HirDatabase) -> String { + self.capture.display_place(self.owner, db) + } +} + +pub enum CaptureKind { + SharedRef, + UniqueSharedRef, + MutableRef, + Move, +} + #[derive(Clone, PartialEq, Eq, Debug)] pub struct Type { env: Arc<TraitEnvironment>, @@ -3164,24 +3412,33 @@ impl Type { Type { env: environment, ty } } - fn from_def(db: &dyn HirDatabase, def: impl HasResolver + Into<TyDefId>) -> Type { - let ty_def = def.into(); - let parent_subst = match ty_def { - TyDefId::TypeAliasId(id) => match id.lookup(db.upcast()).container { - ItemContainerId::TraitId(id) => { - let subst = TyBuilder::subst_for_def(db, id, None).fill_with_unknown().build(); - Some(subst) - } - ItemContainerId::ImplId(id) => { - let subst = TyBuilder::subst_for_def(db, id, None).fill_with_unknown().build(); - Some(subst) - } - _ => None, + fn from_def(db: &dyn HirDatabase, def: impl Into<TyDefId> + HasResolver) -> Type { + let ty = db.ty(def.into()); + let substs = TyBuilder::unknown_subst( + db, + match def.into() { + TyDefId::AdtId(it) => GenericDefId::AdtId(it), + TyDefId::TypeAliasId(it) => GenericDefId::TypeAliasId(it), + TyDefId::BuiltinType(_) => return Type::new(db, def, ty.skip_binders().clone()), }, - _ => None, - }; - let ty = TyBuilder::def_ty(db, ty_def, parent_subst).fill_with_unknown().build(); - Type::new(db, def, ty) + ); + Type::new(db, def, ty.substitute(Interner, &substs)) + } + + fn from_value_def(db: &dyn HirDatabase, def: impl Into<ValueTyDefId> + HasResolver) -> Type { + let ty = db.value_ty(def.into()); + let substs = TyBuilder::unknown_subst( + db, + match def.into() { + ValueTyDefId::ConstId(it) => GenericDefId::ConstId(it), + ValueTyDefId::FunctionId(it) => GenericDefId::FunctionId(it), + ValueTyDefId::StructId(it) => GenericDefId::AdtId(AdtId::StructId(it)), + ValueTyDefId::UnionId(it) => GenericDefId::AdtId(AdtId::UnionId(it)), + ValueTyDefId::EnumVariantId(it) => GenericDefId::EnumVariantId(it), + ValueTyDefId::StaticId(_) => return Type::new(db, def, ty.skip_binders().clone()), + }, + ); + Type::new(db, def, ty.substitute(Interner, &substs)) } pub fn new_slice(ty: Type) -> Type { @@ -3331,7 +3588,7 @@ impl Type { binders: CanonicalVarKinds::empty(Interner), }; - db.trait_solve(self.env.krate, goal).is_some() + db.trait_solve(self.env.krate, self.env.block, goal).is_some() } pub fn normalize_trait_assoc_type( @@ -3378,7 +3635,12 @@ impl Type { } pub fn as_callable(&self, db: &dyn HirDatabase) -> Option<Callable> { + let mut the_ty = &self.ty; let callee = match self.ty.kind(Interner) { + TyKind::Ref(_, _, ty) if ty.as_closure().is_some() => { + the_ty = ty; + Callee::Closure(ty.as_closure().unwrap()) + } TyKind::Closure(id, _) => Callee::Closure(*id), TyKind::Function(_) => Callee::FnPtr, TyKind::FnDef(..) => Callee::Def(self.ty.callable_def(db)?), @@ -3393,7 +3655,7 @@ impl Type { } }; - let sig = self.ty.callable_sig(db)?; + let sig = the_ty.callable_sig(db)?; Some(Callable { ty: self.clone(), sig, callee, is_bound_method: false }) } @@ -3401,6 +3663,13 @@ impl Type { matches!(self.ty.kind(Interner), TyKind::Closure { .. }) } + pub fn as_closure(&self) -> Option<Closure> { + match self.ty.kind(Interner) { + TyKind::Closure(id, subst) => Some(Closure { id: *id, subst: subst.clone() }), + _ => None, + } + } + pub fn is_fn(&self) -> bool { matches!(self.ty.kind(Interner), TyKind::FnDef(..) | TyKind::Function { .. }) } @@ -3502,9 +3771,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 } @@ -3517,8 +3786,7 @@ impl Type { fn autoderef_<'a>(&'a self, db: &'a dyn HirDatabase) -> impl Iterator<Item = Ty> + 'a { // There should be no inference vars in types passed here let canonical = hir_ty::replace_errors_with_variables(&self.ty); - let environment = self.env.clone(); - autoderef(db, environment, canonical).map(|canonical| canonical.value) + autoderef(db, self.env.clone(), canonical).map(|canonical| canonical.value) } // This would be nicer if it just returned an iterator, but that runs into @@ -3636,7 +3904,7 @@ impl Type { self.as_adt() .and_then(|a| a.lifetime(db).and_then(|lt| Some((<.name).to_smol_str()))) .into_iter() - // add the type and const paramaters + // add the type and const parameters .chain(self.type_and_const_arguments(db)) } @@ -3955,6 +4223,11 @@ impl Type { .map(|id| TypeOrConstParam { id }.split(db).either_into()) .collect() } + + pub fn layout(&self, db: &dyn HirDatabase) -> Result<Layout, LayoutError> { + db.layout_of_ty(self.ty.clone(), self.env.krate) + .map(|layout| Layout(layout, db.target_data_layout(self.env.krate).unwrap())) + } } // FIXME: Document this @@ -4064,6 +4337,48 @@ fn closure_source(db: &dyn HirDatabase, closure: ClosureId) -> Option<ast::Closu } } +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct Layout(Arc<TyLayout>, Arc<TargetDataLayout>); + +impl Layout { + pub fn size(&self) -> u64 { + self.0.size.bytes() + } + + pub fn align(&self) -> u64 { + self.0.align.abi.bytes() + } + + pub fn niches(&self) -> Option<u128> { + Some(self.0.largest_niche?.available(&*self.1)) + } + + pub fn field_offset(&self, idx: usize) -> Option<u64> { + match self.0.fields { + layout::FieldsShape::Primitive => None, + layout::FieldsShape::Union(_) => Some(0), + layout::FieldsShape::Array { stride, count } => { + let i = u64::try_from(idx).ok()?; + (i < count).then_some((stride * i).bytes()) + } + layout::FieldsShape::Arbitrary { ref offsets, .. } => Some(offsets.get(idx)?.bytes()), + } + } + + pub fn enum_tag_size(&self) -> Option<usize> { + let tag_size = + if let layout::Variants::Multiple { tag, tag_encoding, .. } = &self.0.variants { + match tag_encoding { + TagEncoding::Direct => tag.size(&*self.1).bytes_usize(), + TagEncoding::Niche { .. } => 0, + } + } else { + return None; + }; + Some(tag_size) + } +} + #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum BindingMode { Move, @@ -4215,6 +4530,12 @@ impl HasCrate for Union { } } +impl HasCrate for Enum { + fn krate(&self, db: &dyn HirDatabase) -> Crate { + self.module(db).krate() + } +} + impl HasCrate for Field { fn krate(&self, db: &dyn HirDatabase) -> Crate { self.parent_def(db).module(db).krate() @@ -4286,3 +4607,90 @@ impl HasCrate for Module { Module::krate(*self) } } + +pub trait HasContainer { + fn container(&self, db: &dyn HirDatabase) -> ItemContainer; +} + +impl HasContainer for Module { + fn container(&self, db: &dyn HirDatabase) -> ItemContainer { + // FIXME: handle block expressions as modules (their parent is in a different DefMap) + let def_map = self.id.def_map(db.upcast()); + match def_map[self.id.local_id].parent { + Some(parent_id) => ItemContainer::Module(Module { id: def_map.module_id(parent_id) }), + None => ItemContainer::Crate(def_map.krate()), + } + } +} + +impl HasContainer for Function { + fn container(&self, db: &dyn HirDatabase) -> ItemContainer { + container_id_to_hir(self.id.lookup(db.upcast()).container) + } +} + +impl HasContainer for Struct { + fn container(&self, db: &dyn HirDatabase) -> ItemContainer { + ItemContainer::Module(Module { id: self.id.lookup(db.upcast()).container }) + } +} + +impl HasContainer for Union { + fn container(&self, db: &dyn HirDatabase) -> ItemContainer { + ItemContainer::Module(Module { id: self.id.lookup(db.upcast()).container }) + } +} + +impl HasContainer for Enum { + fn container(&self, db: &dyn HirDatabase) -> ItemContainer { + ItemContainer::Module(Module { id: self.id.lookup(db.upcast()).container }) + } +} + +impl HasContainer for TypeAlias { + fn container(&self, db: &dyn HirDatabase) -> ItemContainer { + container_id_to_hir(self.id.lookup(db.upcast()).container) + } +} + +impl HasContainer for Const { + fn container(&self, db: &dyn HirDatabase) -> ItemContainer { + container_id_to_hir(self.id.lookup(db.upcast()).container) + } +} + +impl HasContainer for Static { + fn container(&self, db: &dyn HirDatabase) -> ItemContainer { + container_id_to_hir(self.id.lookup(db.upcast()).container) + } +} + +impl HasContainer for Trait { + fn container(&self, db: &dyn HirDatabase) -> ItemContainer { + ItemContainer::Module(Module { id: self.id.lookup(db.upcast()).container }) + } +} + +impl HasContainer for TraitAlias { + fn container(&self, db: &dyn HirDatabase) -> ItemContainer { + ItemContainer::Module(Module { id: self.id.lookup(db.upcast()).container }) + } +} + +fn container_id_to_hir(c: ItemContainerId) -> ItemContainer { + match c { + ItemContainerId::ExternBlockId(_id) => ItemContainer::ExternBlock(), + ItemContainerId::ModuleId(id) => ItemContainer::Module(Module { id }), + ItemContainerId::ImplId(id) => ItemContainer::Impl(Impl { id }), + ItemContainerId::TraitId(id) => ItemContainer::Trait(Trait { id }), + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum ItemContainer { + Trait(Trait), + Impl(Impl), + Module(Module), + ExternBlock(), + Crate(CrateId), +} |