diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-19 09:26:03 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-19 09:26:03 +0000 |
commit | 9918693037dce8aa4bb6f08741b6812923486c18 (patch) | |
tree | 21d2b40bec7e6a7ea664acee056eb3d08e15a1cf /src/tools/rust-analyzer/crates/hir-ty | |
parent | Releasing progress-linux version 1.75.0+dfsg1-5~progress7.99u1. (diff) | |
download | rustc-9918693037dce8aa4bb6f08741b6812923486c18.tar.xz rustc-9918693037dce8aa4bb6f08741b6812923486c18.zip |
Merging upstream version 1.76.0+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/tools/rust-analyzer/crates/hir-ty')
39 files changed, 1083 insertions, 607 deletions
diff --git a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml index b95ae05cc..bbcb76a43 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml +++ b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml @@ -13,27 +13,27 @@ doctest = false [dependencies] cov-mark = "2.0.0-pre.1" -itertools = "0.10.5" +itertools.workspace = true arrayvec = "0.7.2" -bitflags = "2.1.0" +bitflags.workspace = true smallvec.workspace = true ena = "0.14.0" -either = "1.7.0" +either.workspace = true oorandom = "11.1.3" -tracing = "0.1.35" +tracing.workspace = true rustc-hash = "1.1.0" scoped-tls = "1.0.0" -chalk-solve = { version = "0.92.0", default-features = false } -chalk-ir = "0.92.0" -chalk-recursive = { version = "0.92.0", default-features = false } -chalk-derive = "0.92.0" +chalk-solve = { version = "0.95.0", default-features = false } +chalk-ir = "0.95.0" +chalk-recursive = { version = "0.95.0", default-features = false } +chalk-derive = "0.95.0" la-arena.workspace = true once_cell = "1.17.0" triomphe.workspace = true nohash-hasher.workspace = true typed-arena = "2.0.1" -rustc_index.workspace = true +rustc-dependencies.workspace = true # local deps stdx.workspace = true @@ -47,12 +47,13 @@ limit.workspace = true [dev-dependencies] expect-test = "1.4.0" -tracing = "0.1.35" -tracing-subscriber = { version = "0.3.16", default-features = false, features = [ - "registry", -] } -tracing-tree = "0.2.1" +tracing.workspace = true +tracing-subscriber.workspace = true +tracing-tree.workspace = true project-model = { path = "../project-model" } # local deps test-utils.workspace = true + +[features] +in-rust-tree = ["rustc-dependencies/in-rust-tree"] diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs index c0b243ea2..c9ab35685 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs @@ -28,6 +28,7 @@ pub trait TyExt { fn is_unknown(&self) -> bool; fn contains_unknown(&self) -> bool; fn is_ty_var(&self) -> bool; + fn is_union(&self) -> bool; fn as_adt(&self) -> Option<(hir_def::AdtId, &Substitution)>; fn as_builtin(&self) -> Option<BuiltinType>; @@ -96,6 +97,10 @@ impl TyExt for Ty { matches!(self.kind(Interner), TyKind::InferenceVar(_, _)) } + fn is_union(&self) -> bool { + matches!(self.adt_id(Interner), Some(AdtId(hir_def::AdtId::UnionId(_)))) + } + fn as_adt(&self) -> Option<(hir_def::AdtId, &Substitution)> { match self.kind(Interner) { TyKind::Adt(AdtId(adt), parameters) => Some((*adt, parameters)), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs index 0348680e5..9792d945e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs @@ -1,9 +1,10 @@ //! Constant evaluation details -use base_db::CrateId; +use base_db::{salsa::Cycle, CrateId}; use chalk_ir::{cast::Cast, BoundVar, DebruijnIndex}; use hir_def::{ - hir::Expr, + body::Body, + hir::{Expr, ExprId}, path::Path, resolver::{Resolver, ValueNs}, type_ref::LiteralConstRef, @@ -136,7 +137,7 @@ pub fn intern_const_ref( ty: Ty, krate: CrateId, ) -> Const { - let layout = db.layout_of_ty(ty.clone(), Arc::new(TraitEnvironment::empty(krate))); + let layout = db.layout_of_ty(ty.clone(), TraitEnvironment::empty(krate)); let bytes = match value { LiteralConstRef::Int(i) => { // FIXME: We should handle failure of layout better. @@ -184,7 +185,7 @@ pub fn try_const_usize(db: &dyn HirDatabase, c: &Const) -> Option<u128> { pub(crate) fn const_eval_recover( _: &dyn HirDatabase, - _: &[String], + _: &Cycle, _: &GeneralConstId, _: &Substitution, _: &Option<Arc<TraitEnvironment>>, @@ -194,7 +195,7 @@ pub(crate) fn const_eval_recover( pub(crate) fn const_eval_static_recover( _: &dyn HirDatabase, - _: &[String], + _: &Cycle, _: &StaticId, ) -> Result<Const, ConstEvalError> { Err(ConstEvalError::MirLowerError(MirLowerError::Loop)) @@ -202,7 +203,7 @@ pub(crate) fn const_eval_static_recover( pub(crate) fn const_eval_discriminant_recover( _: &dyn HirDatabase, - _: &[String], + _: &Cycle, _: &EnumVariantId, ) -> Result<i128, ConstEvalError> { Err(ConstEvalError::MirLowerError(MirLowerError::Loop)) @@ -280,7 +281,7 @@ pub(crate) fn const_eval_discriminant_variant( // get an `InferenceResult` instead of an `InferenceContext`. And we should remove `ctx.clone().resolve_all()` here // and make this function private. See the fixme comment on `InferenceContext::resolve_all`. pub(crate) fn eval_to_const( - expr: Idx<Expr>, + expr: ExprId, mode: ParamLoweringMode, ctx: &mut InferenceContext<'_>, args: impl FnOnce() -> Generics, @@ -288,13 +289,24 @@ pub(crate) fn eval_to_const( ) -> Const { let db = ctx.db; let infer = ctx.clone().resolve_all(); + fn has_closure(body: &Body, expr: ExprId) -> bool { + if matches!(body[expr], Expr::Closure { .. }) { + return true; + } + let mut r = false; + body[expr].walk_child_exprs(|idx| r |= has_closure(body, idx)); + r + } + if has_closure(&ctx.body, expr) { + // Type checking clousres need an isolated body (See the above FIXME). Bail out early to prevent panic. + return unknown_const(infer[expr].clone()); + } if let Expr::Path(p) = &ctx.body.exprs[expr] { let resolver = &ctx.resolver; if let Some(c) = path_to_const(db, resolver, p, mode, args, debruijn, infer[expr].clone()) { return c; } } - 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, Arc::new(mir_body), true, None).0 { return result; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs index 7ad3659a4..b395e7f4a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs @@ -1159,6 +1159,20 @@ fn pattern_matching_slice() { "#, 33213, ); + check_number( + r#" + //- minicore: slice, index, coerce_unsized, copy + const fn f(mut slice: &[u32]) -> usize { + slice = match slice { + [0, rest @ ..] | rest => rest, + }; + slice.len() + } + const GOAL: usize = f(&[]) + f(&[10]) + f(&[0, 100]) + + f(&[1000, 1000, 1000]) + f(&[0, 57, 34, 46, 10000, 10000]); + "#, + 10, + ); } #[test] diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs index 9c96b5ab8..410bcbf03 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs @@ -20,8 +20,8 @@ use crate::{ method_resolution::{InherentImpls, TraitImpls, TyFingerprint}, mir::{BorrowckResult, MirBody, MirLowerError}, Binders, CallableDefId, ClosureId, Const, FnDefId, GenericArg, ImplTraitId, InferenceResult, - Interner, PolyFnSig, QuantifiedWhereClause, ReturnTypeImplTraits, Substitution, TraitRef, Ty, - TyDefId, ValueTyDefId, + Interner, PolyFnSig, QuantifiedWhereClause, ReturnTypeImplTraits, Substitution, + TraitEnvironment, TraitRef, Ty, TyDefId, ValueTyDefId, }; use hir_expand::name::Name; @@ -47,7 +47,7 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> { &self, def: DefWithBodyId, subst: Substitution, - env: Arc<crate::TraitEnvironment>, + env: Arc<TraitEnvironment>, ) -> Result<Arc<MirBody>, MirLowerError>; #[salsa::invoke(crate::mir::monomorphized_mir_body_for_closure_query)] @@ -55,7 +55,7 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> { &self, def: ClosureId, subst: Substitution, - env: Arc<crate::TraitEnvironment>, + env: Arc<TraitEnvironment>, ) -> Result<Arc<MirBody>, MirLowerError>; #[salsa::invoke(crate::mir::borrowck_query)] @@ -81,7 +81,7 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> { &self, def: GeneralConstId, subst: Substitution, - trait_env: Option<Arc<crate::TraitEnvironment>>, + trait_env: Option<Arc<TraitEnvironment>>, ) -> Result<Const, ConstEvalError>; #[salsa::invoke(crate::consteval::const_eval_static_query)] @@ -104,16 +104,12 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> { &self, def: AdtId, subst: Substitution, - env: Arc<crate::TraitEnvironment>, + env: Arc<TraitEnvironment>, ) -> Result<Arc<Layout>, LayoutError>; #[salsa::invoke(crate::layout::layout_of_ty_query)] #[salsa::cycle(crate::layout::layout_of_ty_recover)] - fn layout_of_ty( - &self, - ty: Ty, - env: Arc<crate::TraitEnvironment>, - ) -> Result<Arc<Layout>, LayoutError>; + fn layout_of_ty(&self, ty: Ty, env: Arc<TraitEnvironment>) -> Result<Arc<Layout>, LayoutError>; #[salsa::invoke(crate::layout::target_data_layout_query)] fn target_data_layout(&self, krate: CrateId) -> Option<Arc<TargetDataLayout>>; @@ -121,7 +117,7 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> { #[salsa::invoke(crate::method_resolution::lookup_impl_method_query)] fn lookup_impl_method( &self, - env: Arc<crate::TraitEnvironment>, + env: Arc<TraitEnvironment>, func: FunctionId, fn_subst: Substitution, ) -> (FunctionId, Substitution); @@ -149,10 +145,10 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> { #[salsa::invoke(crate::lower::trait_environment_for_body_query)] #[salsa::transparent] - fn trait_environment_for_body(&self, def: DefWithBodyId) -> Arc<crate::TraitEnvironment>; + fn trait_environment_for_body(&self, def: DefWithBodyId) -> Arc<TraitEnvironment>; #[salsa::invoke(crate::lower::trait_environment_query)] - fn trait_environment(&self, def: GenericDefId) -> Arc<crate::TraitEnvironment>; + fn trait_environment(&self, def: GenericDefId) -> Arc<TraitEnvironment>; #[salsa::invoke(crate::lower::generic_defaults_query)] #[salsa::cycle(crate::lower::generic_defaults_recover)] @@ -249,7 +245,7 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> { fn normalize_projection( &self, projection: crate::ProjectionTy, - env: Arc<crate::TraitEnvironment>, + env: Arc<TraitEnvironment>, ) -> Ty; #[salsa::invoke(trait_solve_wait)] diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics.rs index ef43ed5c4..c1b361900 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics.rs @@ -11,9 +11,3 @@ pub use crate::diagnostics::{ }, unsafe_check::{missing_unsafe, unsafe_expressions, UnsafeExpr}, }; - -#[derive(Debug, PartialEq, Eq)] -pub struct IncoherentImpl { - pub file_id: hir_expand::HirFileId, - pub impl_: syntax::AstPtr<syntax::ast::Impl>, -} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs index 36d69edf9..51a044d8e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs @@ -9,6 +9,7 @@ //! - constants (e.g. `const FOO: u8 = 10;`) //! - static items (e.g. `static FOO: u8 = 10;`) //! - match arm bindings (e.g. `foo @ Some(_)`) +//! - modules (e.g. `mod foo { ... }` or `mod foo;`) mod case_conv; @@ -18,12 +19,12 @@ use hir_def::{ data::adt::VariantData, hir::{Pat, PatId}, src::HasSource, - AdtId, AttrDefId, ConstId, DefWithBodyId, EnumId, EnumVariantId, FunctionId, ItemContainerId, - Lookup, ModuleDefId, StaticId, StructId, + AdtId, AttrDefId, ConstId, EnumId, FunctionId, ItemContainerId, Lookup, ModuleDefId, ModuleId, + StaticId, StructId, }; use hir_expand::{ name::{AsName, Name}, - HirFileId, + HirFileId, MacroFileIdExt, }; use stdx::{always, never}; use syntax::{ @@ -83,6 +84,7 @@ pub enum IdentType { Structure, Variable, Variant, + Module, } impl fmt::Display for IdentType { @@ -97,6 +99,7 @@ impl fmt::Display for IdentType { IdentType::Structure => "Structure", IdentType::Variable => "Variable", IdentType::Variant => "Variant", + IdentType::Module => "Module", }; repr.fmt(f) @@ -132,6 +135,7 @@ impl<'a> DeclValidator<'a> { pub(super) fn validate_item(&mut self, item: ModuleDefId) { match item { + ModuleDefId::ModuleId(module_id) => self.validate_module(module_id), ModuleDefId::FunctionId(func) => self.validate_func(func), ModuleDefId::AdtId(adt) => self.validate_adt(adt), ModuleDefId::ConstId(const_id) => self.validate_const(const_id), @@ -192,7 +196,7 @@ impl<'a> DeclValidator<'a> { AttrDefId::GenericParamId(_) => None, } .map_or(false, |file_id| { - file_id.is_custom_derive(db.upcast()) || file_id.is_builtin_derive(db.upcast()) + matches!(file_id.macro_file(), Some(file_id) if file_id.is_custom_derive(db.upcast()) || file_id.is_builtin_derive(db.upcast())) }) }; @@ -230,6 +234,55 @@ impl<'a> DeclValidator<'a> { || parent() } + fn validate_module(&mut self, module_id: ModuleId) { + // Check whether non-snake case identifiers are allowed for this module. + if self.allowed(module_id.into(), allow::NON_SNAKE_CASE, false) { + return; + } + + // Check the module name. + let Some(module_name) = module_id.name(self.db.upcast()) else { return }; + let module_name_replacement = + module_name.as_str().and_then(to_lower_snake_case).map(|new_name| Replacement { + current_name: module_name, + suggested_text: new_name, + expected_case: CaseType::LowerSnakeCase, + }); + + if let Some(module_name_replacement) = module_name_replacement { + let module_data = &module_id.def_map(self.db.upcast())[module_id.local_id]; + let module_src = module_data.declaration_source(self.db.upcast()); + + if let Some(module_src) = module_src { + let ast_ptr = match module_src.value.name() { + Some(name) => name, + None => { + never!( + "Replacement ({:?}) was generated for a module without a name: {:?}", + module_name_replacement, + module_src + ); + return; + } + }; + + let diagnostic = IncorrectCase { + file: module_src.file_id, + ident_type: IdentType::Module, + ident: AstPtr::new(&ast_ptr), + expected_case: module_name_replacement.expected_case, + ident_text: module_name_replacement + .current_name + .display(self.db.upcast()) + .to_string(), + suggested_text: module_name_replacement.suggested_text, + }; + + self.sink.push(diagnostic); + } + } + } + fn validate_func(&mut self, func: FunctionId) { let data = self.db.function_data(func); if matches!(func.lookup(self.db.upcast()).container, ItemContainerId::ExternBlockId(_)) { @@ -237,8 +290,6 @@ impl<'a> DeclValidator<'a> { return; } - self.validate_body_inner_items(func.into()); - // Check whether non-snake case identifiers are allowed for this function. if self.allowed(func.into(), allow::NON_SNAKE_CASE, false) { return; @@ -336,48 +387,44 @@ impl<'a> DeclValidator<'a> { for (id, replacement) in pats_replacements { if let Ok(source_ptr) = source_map.pat_syntax(id) { - if let Some(expr) = source_ptr.value.as_ref().left() { + if let Some(ptr) = source_ptr.value.clone().cast::<ast::IdentPat>() { let root = source_ptr.file_syntax(self.db.upcast()); - if let ast::Pat::IdentPat(ident_pat) = expr.to_node(&root) { - let parent = match ident_pat.syntax().parent() { - Some(parent) => parent, - None => continue, - }; - let name_ast = match ident_pat.name() { - Some(name_ast) => name_ast, - None => continue, - }; + let ident_pat = ptr.to_node(&root); + let parent = match ident_pat.syntax().parent() { + Some(parent) => parent, + None => continue, + }; + let name_ast = match ident_pat.name() { + Some(name_ast) => name_ast, + None => continue, + }; + + let is_param = ast::Param::can_cast(parent.kind()); + + // We have to check that it's either `let var = ...` or `var @ Variant(_)` statement, + // because e.g. match arms are patterns as well. + // In other words, we check that it's a named variable binding. + let is_binding = ast::LetStmt::can_cast(parent.kind()) + || (ast::MatchArm::can_cast(parent.kind()) + && ident_pat.at_token().is_some()); + if !(is_param || is_binding) { + // This pattern is not an actual variable declaration, e.g. `Some(val) => {..}` match arm. + continue; + } - let is_param = ast::Param::can_cast(parent.kind()); - - // We have to check that it's either `let var = ...` or `var @ Variant(_)` statement, - // because e.g. match arms are patterns as well. - // In other words, we check that it's a named variable binding. - let is_binding = ast::LetStmt::can_cast(parent.kind()) - || (ast::MatchArm::can_cast(parent.kind()) - && ident_pat.at_token().is_some()); - if !(is_param || is_binding) { - // This pattern is not an actual variable declaration, e.g. `Some(val) => {..}` match arm. - continue; - } + let ident_type = + if is_param { IdentType::Parameter } else { IdentType::Variable }; - let ident_type = - if is_param { IdentType::Parameter } else { IdentType::Variable }; - - let diagnostic = IncorrectCase { - file: source_ptr.file_id, - ident_type, - ident: AstPtr::new(&name_ast), - expected_case: replacement.expected_case, - ident_text: replacement - .current_name - .display(self.db.upcast()) - .to_string(), - suggested_text: replacement.suggested_text, - }; + let diagnostic = IncorrectCase { + file: source_ptr.file_id, + ident_type, + ident: AstPtr::new(&name_ast), + expected_case: replacement.expected_case, + ident_text: replacement.current_name.display(self.db.upcast()).to_string(), + suggested_text: replacement.suggested_text, + }; - self.sink.push(diagnostic); - } + self.sink.push(diagnostic); } } } @@ -519,11 +566,6 @@ impl<'a> DeclValidator<'a> { fn validate_enum(&mut self, enum_id: EnumId) { let data = self.db.enum_data(enum_id); - for (local_id, _) in data.variants.iter() { - let variant_id = EnumVariantId { parent: enum_id, local_id }; - self.validate_body_inner_items(variant_id.into()); - } - // Check whether non-camel case names are allowed for this enum. if self.allowed(enum_id.into(), allow::NON_CAMEL_CASE_TYPES, false) { return; @@ -648,8 +690,6 @@ impl<'a> DeclValidator<'a> { fn validate_const(&mut self, const_id: ConstId) { let data = self.db.const_data(const_id); - self.validate_body_inner_items(const_id.into()); - if self.allowed(const_id.into(), allow::NON_UPPER_CASE_GLOBAL, false) { return; } @@ -698,8 +738,6 @@ impl<'a> DeclValidator<'a> { return; } - self.validate_body_inner_items(static_id.into()); - if self.allowed(static_id.into(), allow::NON_UPPER_CASE_GLOBAL, false) { return; } @@ -737,17 +775,4 @@ impl<'a> DeclValidator<'a> { self.sink.push(diagnostic); } - - // FIXME: We don't currently validate names within `DefWithBodyId::InTypeConstId`. - /// Recursively validates inner scope items, such as static variables and constants. - fn validate_body_inner_items(&mut self, body_id: DefWithBodyId) { - let body = self.db.body(body_id); - for (_, block_def_map) in body.blocks(self.db.upcast()) { - for (_, module) in block_def_map.modules() { - for def_id in module.scope.declarations() { - self.validate_item(def_id); - } - } - } - } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check/case_conv.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check/case_conv.rs index 2c1368962..cbe1af157 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check/case_conv.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check/case_conv.rs @@ -11,50 +11,7 @@ pub(crate) fn to_camel_case(ident: &str) -> Option<String> { return None; } - // Taken from rustc. - let ret = ident - .trim_matches('_') - .split('_') - .filter(|component| !component.is_empty()) - .map(|component| { - let mut camel_cased_component = String::with_capacity(component.len()); - - let mut new_word = true; - let mut prev_is_lower_case = true; - - for c in component.chars() { - // Preserve the case if an uppercase letter follows a lowercase letter, so that - // `camelCase` is converted to `CamelCase`. - if prev_is_lower_case && c.is_uppercase() { - new_word = true; - } - - if new_word { - camel_cased_component.extend(c.to_uppercase()); - } else { - camel_cased_component.extend(c.to_lowercase()); - } - - prev_is_lower_case = c.is_lowercase(); - new_word = false; - } - - camel_cased_component - }) - .fold((String::new(), None), |(acc, prev): (_, Option<String>), next| { - // separate two components with an underscore if their boundary cannot - // be distinguished using an uppercase/lowercase case distinction - let join = prev - .and_then(|prev| { - let f = next.chars().next()?; - let l = prev.chars().last()?; - Some(!char_has_case(l) && !char_has_case(f)) - }) - .unwrap_or(false); - (acc + if join { "_" } else { "" } + &next, Some(next)) - }) - .0; - Some(ret) + Some(stdx::to_camel_case(ident)) } /// Converts an identifier to a lower_snake_case form. @@ -97,7 +54,9 @@ fn is_camel_case(name: &str) -> bool { && !name.chars().any(|snd| { let ret = match fst { None => false, - Some(fst) => char_has_case(fst) && snd == '_' || char_has_case(snd) && fst == '_', + Some(fst) => { + stdx::char_has_case(fst) && snd == '_' || stdx::char_has_case(snd) && fst == '_' + } }; fst = Some(snd); @@ -135,11 +94,6 @@ fn is_snake_case<F: Fn(char) -> bool>(ident: &str, wrong_case: F) -> bool { }) } -// Taken from rustc. -fn char_has_case(c: char) -> bool { - c.is_lowercase() || c.is_uppercase() -} - #[cfg(test)] mod tests { use super::*; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs index f8cdeaa5e..2e04bbfee 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs @@ -147,7 +147,7 @@ impl<'a> PatCtxt<'a> { } hir_def::hir::Pat::Bind { id, subpat, .. } => { - let bm = self.infer.binding_modes[id]; + let bm = self.infer.binding_modes[pat]; ty = &self.infer[id]; let name = &self.body.bindings[id].name; match (bm, ty.kind(Interner)) { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs index f6d6b00d7..d81926f7c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -23,7 +23,7 @@ use hir_def::{ EnumVariantId, HasModule, ItemContainerId, LocalFieldId, Lookup, ModuleDefId, ModuleId, TraitId, }; -use hir_expand::{hygiene::Hygiene, name::Name}; +use hir_expand::name::Name; use intern::{Internable, Interned}; use itertools::Itertools; use la_arena::ArenaMap; @@ -448,9 +448,8 @@ fn render_const_scalar( ) -> Result<(), HirDisplayError> { // FIXME: We need to get krate from the final callers of the hir display // infrastructure and have it here as a field on `f`. - let trait_env = Arc::new(TraitEnvironment::empty( - *f.db.crate_graph().crates_in_topological_order().last().unwrap(), - )); + let trait_env = + TraitEnvironment::empty(*f.db.crate_graph().crates_in_topological_order().last().unwrap()); match ty.kind(Interner) { TyKind::Scalar(s) => match s { Scalar::Bool => write!(f, "{}", if b[0] == 0 { false } else { true }), @@ -945,6 +944,7 @@ impl HirDisplay for Ty { ItemInNs::Types((*def_id).into()), module_id, false, + true, ) { write!(f, "{}", path.display(f.db.upcast()))?; } else { @@ -1731,13 +1731,13 @@ impl HirDisplay for TypeRef { f.write_joined(bounds, " + ")?; } TypeRef::Macro(macro_call) => { - let macro_call = macro_call.to_node(f.db.upcast()); - let ctx = hir_def::lower::LowerCtx::with_hygiene( + let ctx = hir_def::lower::LowerCtx::with_span_map( f.db.upcast(), - &Hygiene::new_unhygienic(), + f.db.span_map(macro_call.file_id), ); + let macro_call = macro_call.to_node(f.db.upcast()); match macro_call.path() { - Some(path) => match Path::from_src(path, &ctx) { + Some(path) => match Path::from_src(&ctx, path) { Some(path) => path.hir_fmt(f)?, None => write!(f, "{{macro}}")?, }, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs index 78d3c667a..6f724e458 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -113,7 +113,7 @@ pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<Infer // FIXME(const-generic-body): We should not get the return type in this way. ctx.return_ty = c .lookup(db.upcast()) - .thing + .expected_ty .box_any() .downcast::<InTypeConstIdMetadata>() .unwrap() @@ -420,7 +420,19 @@ pub struct InferenceResult { standard_types: InternedStandardTypes, /// Stores the types which were implicitly dereferenced in pattern binding modes. pub pat_adjustments: FxHashMap<PatId, Vec<Ty>>, - pub binding_modes: ArenaMap<BindingId, BindingMode>, + /// Stores the binding mode (`ref` in `let ref x = 2`) of bindings. + /// + /// This one is tied to the `PatId` instead of `BindingId`, because in some rare cases, a binding in an + /// or pattern can have multiple binding modes. For example: + /// ``` + /// fn foo(mut slice: &[u32]) -> usize { + /// slice = match slice { + /// [0, rest @ ..] | rest => rest, + /// }; + /// } + /// ``` + /// the first `rest` has implicit `ref` binding mode, but the second `rest` binding mode is `move`. + pub binding_modes: ArenaMap<PatId, BindingMode>, pub expr_adjustments: FxHashMap<ExprId, Vec<Adjustment>>, pub(crate) closure_info: FxHashMap<ClosureId, (Vec<CapturedItem>, FnTrait)>, // FIXME: remove this field @@ -1140,20 +1152,15 @@ impl<'a> InferenceContext<'a> { (ty, variant) } TypeNs::TypeAliasId(it) => { - let container = it.lookup(self.db.upcast()).container; - let parent_subst = match container { - ItemContainerId::TraitId(id) => { - let subst = TyBuilder::subst_for_def(self.db, id, None) - .fill_with_inference_vars(&mut self.table) - .build(); - Some(subst) - } - // Type aliases do not exist in impls. - _ => None, + let resolved_seg = match unresolved { + None => path.segments().last().unwrap(), + Some(n) => path.segments().get(path.segments().len() - n - 1).unwrap(), }; - let ty = TyBuilder::def_ty(self.db, it.into(), parent_subst) - .fill_with_inference_vars(&mut self.table) - .build(); + let substs = + ctx.substs_from_path_segment(resolved_seg, Some(it.into()), true, None); + let ty = self.db.ty(it.into()); + let ty = self.insert_type_vars(ty.substitute(Interner, &substs)); + self.resolve_variant_on_alias(ty, unresolved, mod_path) } TypeNs::AdtSelfType(_) => { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs index 13d6b5643..af74df103 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs @@ -679,7 +679,7 @@ impl InferenceContext<'_> { | Pat::Range { .. } => { update_result(CaptureKind::ByRef(BorrowKind::Shared)); } - Pat::Bind { id, .. } => match self.result.binding_modes[*id] { + Pat::Bind { id, .. } => match self.result.binding_modes[p] { crate::BindingMode::Move => { if self.is_ty_copy(self.result.type_of_binding[*id].clone()) { update_result(CaptureKind::ByRef(BorrowKind::Shared)); @@ -735,6 +735,32 @@ impl InferenceContext<'_> { self.walk_expr(expr); } + fn restrict_precision_for_unsafe(&mut self) { + for capture in &mut self.current_captures { + let mut ty = self.table.resolve_completely(self.result[capture.place.local].clone()); + if ty.as_raw_ptr().is_some() || ty.is_union() { + capture.kind = CaptureKind::ByRef(BorrowKind::Shared); + capture.place.projections.truncate(0); + continue; + } + for (i, p) in capture.place.projections.iter().enumerate() { + ty = p.projected_ty( + ty, + self.db, + |_, _, _| { + unreachable!("Closure field only happens in MIR"); + }, + self.owner.module(self.db.upcast()).krate(), + ); + if ty.as_raw_ptr().is_some() || ty.is_union() { + capture.kind = CaptureKind::ByRef(BorrowKind::Shared); + capture.place.projections.truncate(i + 1); + break; + } + } + } + } + fn adjust_for_move_closure(&mut self) { for capture in &mut self.current_captures { if let Some(first_deref) = @@ -838,8 +864,8 @@ impl InferenceContext<'_> { | Pat::ConstBlock(_) | Pat::Path(_) | Pat::Lit(_) => self.consume_place(place, pat.into()), - Pat::Bind { id, subpat: _ } => { - let mode = self.result.binding_modes[*id]; + Pat::Bind { id: _, subpat: _ } => { + let mode = self.result.binding_modes[pat]; let capture_kind = match mode { BindingMode::Move => { self.consume_place(place, pat.into()); @@ -924,6 +950,7 @@ impl InferenceContext<'_> { self.result.mutated_bindings_in_closure.insert(item.place.local); } } + self.restrict_precision_for_unsafe(); // closure_kind should be done before adjust_for_move_closure let closure_kind = self.closure_kind(); match capture_by { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs index 0c3c725a7..a5e77a12d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs @@ -18,7 +18,6 @@ use hir_def::{ use hir_expand::name::{name, Name}; use stdx::always; use syntax::ast::RangeOp; -use triomphe::Arc; use crate::{ autoderef::{builtin_deref, deref_by_trait, Autoderef}, @@ -40,7 +39,8 @@ use crate::{ traits::FnTrait, utils::{generics, Generics}, Adjust, Adjustment, AdtId, AutoBorrow, Binders, CallableDefId, FnPointer, FnSig, FnSubst, - Interner, Rawness, Scalar, Substitution, TraitRef, Ty, TyBuilder, TyExt, TyKind, + Interner, Rawness, Scalar, Substitution, TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, + TyKind, }; use super::{ @@ -579,7 +579,7 @@ impl InferenceContext<'_> { } ty } - Expr::Field { expr, name } => self.infer_field_access(tgt_expr, *expr, name), + Expr::Field { expr, name } => self.infer_field_access(tgt_expr, *expr, name, expected), Expr::Await { expr } => { let inner_ty = self.infer_expr_inner(*expr, &Expectation::none()); self.resolve_associated_type(inner_ty, self.resolve_future_future_output()) @@ -1291,7 +1291,7 @@ impl InferenceContext<'_> { let g = self.resolver.update_to_inner_scope(self.db.upcast(), self.owner, expr); let prev_env = block_id.map(|block_id| { let prev_env = self.table.trait_env.clone(); - Arc::make_mut(&mut self.table.trait_env).block = Some(block_id); + TraitEnvironment::with_block(&mut self.table.trait_env, block_id); prev_env }); @@ -1456,7 +1456,13 @@ impl InferenceContext<'_> { }) } - fn infer_field_access(&mut self, tgt_expr: ExprId, receiver: ExprId, name: &Name) -> Ty { + fn infer_field_access( + &mut self, + tgt_expr: ExprId, + receiver: ExprId, + name: &Name, + expected: &Expectation, + ) -> Ty { let receiver_ty = self.infer_expr_inner(receiver, &Expectation::none()); if name.is_missing() { @@ -1482,28 +1488,42 @@ impl InferenceContext<'_> { ty } None => { - // no field found, - let method_with_same_name_exists = { - self.get_traits_in_scope(); - - let canonicalized_receiver = self.canonicalize(receiver_ty.clone()); - method_resolution::lookup_method( - self.db, - &canonicalized_receiver.value, - self.table.trait_env.clone(), - self.get_traits_in_scope().as_ref().left_or_else(|&it| it), - VisibleFromModule::Filter(self.resolver.module()), - name, - ) - .is_some() - }; + // no field found, lets attempt to resolve it like a function so that IDE things + // work out while people are typing + let canonicalized_receiver = self.canonicalize(receiver_ty.clone()); + let resolved = method_resolution::lookup_method( + self.db, + &canonicalized_receiver.value, + self.table.trait_env.clone(), + self.get_traits_in_scope().as_ref().left_or_else(|&it| it), + VisibleFromModule::Filter(self.resolver.module()), + name, + ); self.result.diagnostics.push(InferenceDiagnostic::UnresolvedField { expr: tgt_expr, - receiver: receiver_ty, + receiver: receiver_ty.clone(), name: name.clone(), - method_with_same_name_exists, + method_with_same_name_exists: resolved.is_some(), }); - self.err_ty() + match resolved { + Some((adjust, func, _)) => { + let (ty, adjustments) = adjust.apply(&mut self.table, receiver_ty); + let generics = generics(self.db.upcast(), func.into()); + let substs = self.substs_for_method_call(generics, None); + self.write_expr_adj(receiver, adjustments); + self.write_method_resolution(tgt_expr, func, substs.clone()); + + self.check_method_call( + tgt_expr, + &[], + self.db.value_ty(func.into()), + substs, + ty, + expected, + ) + } + None => self.err_ty(), + } } } } @@ -1517,7 +1537,7 @@ impl InferenceContext<'_> { generic_args: Option<&GenericArgs>, expected: &Expectation, ) -> Ty { - let receiver_ty = self.infer_expr(receiver, &Expectation::none()); + let receiver_ty = self.infer_expr_inner(receiver, &Expectation::none()); let canonicalized_receiver = self.canonicalize(receiver_ty.clone()); let resolved = method_resolution::lookup_method( @@ -1568,23 +1588,32 @@ impl InferenceContext<'_> { ) } }; + self.check_method_call(tgt_expr, args, method_ty, substs, receiver_ty, expected) + } + + fn check_method_call( + &mut self, + tgt_expr: ExprId, + args: &[ExprId], + method_ty: Binders<Ty>, + substs: Substitution, + receiver_ty: Ty, + expected: &Expectation, + ) -> Ty { let method_ty = method_ty.substitute(Interner, &substs); self.register_obligations_for_call(&method_ty); - let (formal_receiver_ty, param_tys, ret_ty, is_varargs) = + let ((formal_receiver_ty, param_tys), ret_ty, is_varargs) = match method_ty.callable_sig(self.db) { - Some(sig) => { + Some(sig) => ( if !sig.params().is_empty() { - ( - sig.params()[0].clone(), - sig.params()[1..].to_vec(), - sig.ret().clone(), - sig.is_varargs, - ) + (sig.params()[0].clone(), sig.params()[1..].to_vec()) } else { - (self.err_ty(), Vec::new(), sig.ret().clone(), sig.is_varargs) - } - } - None => (self.err_ty(), Vec::new(), self.err_ty(), true), + (self.err_ty(), Vec::new()) + }, + sig.ret().clone(), + sig.is_varargs, + ), + None => ((self.err_ty(), Vec::new()), self.err_ty(), true), }; self.unify(&formal_receiver_ty, &receiver_ty); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs index 4e28ec060..acdb54028 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs @@ -262,7 +262,7 @@ impl InferenceContext<'_> { fn infer_pat(&mut self, pat: PatId, expected: &Ty, mut default_bm: BindingMode) -> Ty { let mut expected = self.resolve_ty_shallow(expected); - if is_non_ref_pat(self.body, pat) { + if self.is_non_ref_pat(self.body, pat) { let mut pat_adjustments = Vec::new(); while let Some((inner, _lifetime, mutability)) = expected.as_reference() { pat_adjustments.push(expected.clone()); @@ -421,7 +421,7 @@ impl InferenceContext<'_> { } else { BindingMode::convert(mode) }; - self.result.binding_modes.insert(binding, mode); + self.result.binding_modes.insert(pat, mode); let inner_ty = match subpat { Some(subpat) => self.infer_pat(subpat, &expected, default_bm), @@ -496,24 +496,28 @@ impl InferenceContext<'_> { self.infer_expr(expr, &Expectation::has_type(expected.clone())) } -} -fn is_non_ref_pat(body: &hir_def::body::Body, pat: PatId) -> bool { - match &body[pat] { - Pat::Tuple { .. } - | Pat::TupleStruct { .. } - | Pat::Record { .. } - | Pat::Range { .. } - | Pat::Slice { .. } => true, - Pat::Or(pats) => pats.iter().all(|p| is_non_ref_pat(body, *p)), - // FIXME: ConstBlock/Path/Lit might actually evaluate to ref, but inference is unimplemented. - Pat::Path(..) => true, - Pat::ConstBlock(..) => true, - Pat::Lit(expr) => !matches!( - body[*expr], - Expr::Literal(Literal::String(..) | Literal::CString(..) | Literal::ByteString(..)) - ), - Pat::Wild | Pat::Bind { .. } | Pat::Ref { .. } | Pat::Box { .. } | Pat::Missing => false, + fn is_non_ref_pat(&mut self, body: &hir_def::body::Body, pat: PatId) -> bool { + match &body[pat] { + Pat::Tuple { .. } + | Pat::TupleStruct { .. } + | Pat::Record { .. } + | Pat::Range { .. } + | Pat::Slice { .. } => true, + Pat::Or(pats) => pats.iter().all(|p| self.is_non_ref_pat(body, *p)), + Pat::Path(p) => { + let v = self.resolve_value_path_inner(p, pat.into()); + v.is_some_and(|x| !matches!(x.0, hir_def::resolver::ValueNs::ConstId(_))) + } + Pat::ConstBlock(..) => false, + Pat::Lit(expr) => !matches!( + body[*expr], + Expr::Literal(Literal::String(..) | Literal::CString(..) | Literal::ByteString(..)) + ), + Pat::Wild | Pat::Bind { .. } | Pat::Ref { .. } | Pat::Box { .. } | Pat::Missing => { + false + } + } } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs index c6bbf2f61..49fb78f67 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs @@ -40,33 +40,7 @@ impl InferenceContext<'_> { } fn resolve_value_path(&mut self, path: &Path, id: ExprOrPatId) -> Option<ValuePathResolution> { - let (value, self_subst) = if let Some(type_ref) = path.type_anchor() { - let last = path.segments().last()?; - - // Don't use `self.make_ty()` here as we need `orig_ns`. - let ctx = - crate::lower::TyLoweringContext::new(self.db, &self.resolver, self.owner.into()); - let (ty, orig_ns) = ctx.lower_ty_ext(type_ref); - let ty = self.table.insert_type_vars(ty); - let ty = self.table.normalize_associated_types_in(ty); - - let remaining_segments_for_ty = path.segments().take(path.segments().len() - 1); - let (ty, _) = ctx.lower_ty_relative_path(ty, orig_ns, remaining_segments_for_ty); - let ty = self.table.insert_type_vars(ty); - let ty = self.table.normalize_associated_types_in(ty); - self.resolve_ty_assoc_item(ty, last.name, id).map(|(it, substs)| (it, Some(substs)))? - } else { - // FIXME: report error, unresolved first path segment - let value_or_partial = - self.resolver.resolve_path_in_value_ns(self.db.upcast(), path)?; - - match value_or_partial { - ResolveValueResult::ValueNs(it, _) => (it, None), - ResolveValueResult::Partial(def, remaining_index, _) => self - .resolve_assoc_item(def, path, remaining_index, id) - .map(|(it, substs)| (it, Some(substs)))?, - } - }; + let (value, self_subst) = self.resolve_value_path_inner(path, id)?; let value_def = match value { ValueNs::LocalBinding(pat) => match self.result.type_of_binding.get(pat) { @@ -144,6 +118,41 @@ impl InferenceContext<'_> { Some(ValuePathResolution::GenericDef(value_def, generic_def, substs)) } + pub(super) fn resolve_value_path_inner( + &mut self, + path: &Path, + id: ExprOrPatId, + ) -> Option<(ValueNs, Option<chalk_ir::Substitution<Interner>>)> { + let (value, self_subst) = if let Some(type_ref) = path.type_anchor() { + let last = path.segments().last()?; + + // Don't use `self.make_ty()` here as we need `orig_ns`. + let ctx = + crate::lower::TyLoweringContext::new(self.db, &self.resolver, self.owner.into()); + let (ty, orig_ns) = ctx.lower_ty_ext(type_ref); + let ty = self.table.insert_type_vars(ty); + let ty = self.table.normalize_associated_types_in(ty); + + let remaining_segments_for_ty = path.segments().take(path.segments().len() - 1); + let (ty, _) = ctx.lower_ty_relative_path(ty, orig_ns, remaining_segments_for_ty); + let ty = self.table.insert_type_vars(ty); + let ty = self.table.normalize_associated_types_in(ty); + self.resolve_ty_assoc_item(ty, last.name, id).map(|(it, substs)| (it, Some(substs)))? + } else { + // FIXME: report error, unresolved first path segment + let value_or_partial = + self.resolver.resolve_path_in_value_ns(self.db.upcast(), path)?; + + match value_or_partial { + ResolveValueResult::ValueNs(it, _) => (it, None), + ResolveValueResult::Partial(def, remaining_index, _) => self + .resolve_assoc_item(def, path, remaining_index, id) + .map(|(it, substs)| (it, Some(substs)))?, + } + }; + Some((value, self_subst)) + } + fn add_required_obligations_for_value_path(&mut self, def: GenericDefId, subst: &Substitution) { let predicates = self.db.generic_predicates(def); for predicate in predicates.iter() { @@ -390,6 +399,7 @@ impl InferenceContext<'_> { } } +#[derive(Debug)] enum ValuePathResolution { // It's awkward to wrap a single ID in two enums, but we need both and this saves fallible // conversion between them + `unwrap()`. diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs index 0a68a9f3b..ac39bdf5b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs @@ -43,7 +43,7 @@ where } impl<T: HasInterner<Interner = Interner>> Canonicalized<T> { - pub(super) fn apply_solution( + pub(crate) fn apply_solution( &self, ctx: &mut InferenceTable<'_>, solution: Canonical<Substitution>, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs index 1a6106c02..bfc4f1383 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs @@ -1,5 +1,8 @@ //! Compute the binary representation of a type +use std::fmt; + +use base_db::salsa::Cycle; use chalk_ir::{AdtId, FloatTy, IntTy, TyKind, UintTy}; use hir_def::{ layout::{ @@ -9,6 +12,10 @@ use hir_def::{ LocalEnumVariantId, LocalFieldId, StructId, }; use la_arena::{Idx, RawIdx}; +use rustc_dependencies::{ + abi::AddressSpace, + index::{IndexSlice, IndexVec}, +}; use stdx::never; use triomphe::Arc; @@ -22,19 +29,13 @@ pub use self::{ target::target_data_layout_query, }; -macro_rules! user_error { - ($it: expr) => { - return Err(LayoutError::UserError(format!($it).into())) - }; -} - mod adt; mod target; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct RustcEnumVariantIdx(pub LocalEnumVariantId); -impl rustc_index::vec::Idx for RustcEnumVariantIdx { +impl rustc_dependencies::index::Idx for RustcEnumVariantIdx { fn new(idx: usize) -> Self { RustcEnumVariantIdx(Idx::from_raw(RawIdx::from(idx as u32))) } @@ -44,19 +45,63 @@ impl rustc_index::vec::Idx for RustcEnumVariantIdx { } } -pub type Layout = LayoutS<RustcEnumVariantIdx>; +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct RustcFieldIdx(pub LocalFieldId); + +impl RustcFieldIdx { + pub fn new(idx: usize) -> Self { + RustcFieldIdx(Idx::from_raw(RawIdx::from(idx as u32))) + } +} + +impl rustc_dependencies::index::Idx for RustcFieldIdx { + fn new(idx: usize) -> Self { + RustcFieldIdx(Idx::from_raw(RawIdx::from(idx as u32))) + } + + fn index(self) -> usize { + u32::from(self.0.into_raw()) as usize + } +} + +pub type Layout = LayoutS<RustcFieldIdx, RustcEnumVariantIdx>; pub type TagEncoding = hir_def::layout::TagEncoding<RustcEnumVariantIdx>; -pub type Variants = hir_def::layout::Variants<RustcEnumVariantIdx>; +pub type Variants = hir_def::layout::Variants<RustcFieldIdx, RustcEnumVariantIdx>; #[derive(Debug, PartialEq, Eq, Clone)] pub enum LayoutError { - UserError(Box<str>), - SizeOverflow, - TargetLayoutNotAvailable, - HasPlaceholder, + HasErrorConst, HasErrorType, + HasPlaceholder, + InvalidSimdType, NotImplemented, + RecursiveTypeWithoutIndirection, + SizeOverflow, + TargetLayoutNotAvailable, Unknown, + UserReprTooSmall, +} + +impl std::error::Error for LayoutError {} +impl fmt::Display for LayoutError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + LayoutError::HasErrorConst => write!(f, "type contains an unevaluatable const"), + LayoutError::HasErrorType => write!(f, "type contains an error"), + LayoutError::HasPlaceholder => write!(f, "type contains placeholders"), + LayoutError::InvalidSimdType => write!(f, "invalid simd type definition"), + LayoutError::NotImplemented => write!(f, "not implemented"), + LayoutError::RecursiveTypeWithoutIndirection => { + write!(f, "recursive type without indirection") + } + LayoutError::SizeOverflow => write!(f, "size overflow"), + LayoutError::TargetLayoutNotAvailable => write!(f, "target layout not available"), + LayoutError::Unknown => write!(f, "unknown"), + LayoutError::UserReprTooSmall => { + write!(f, "the `#[repr]` hint is too small to hold the discriminants of the enum") + } + } + } } struct LayoutCx<'a> { @@ -66,7 +111,7 @@ struct LayoutCx<'a> { impl<'a> LayoutCalculator for LayoutCx<'a> { type TargetDataLayoutRef = &'a TargetDataLayout; - fn delay_bug(&self, txt: &str) { + fn delayed_bug(&self, txt: String) { never!("{}", txt); } @@ -95,9 +140,7 @@ fn layout_of_simd_ty( let f0_ty = match fields.iter().next() { Some(it) => it.1.clone().substitute(Interner, subst), - None => { - user_error!("simd type with zero fields"); - } + None => return Err(LayoutError::InvalidSimdType), }; // The element type and number of elements of the SIMD vector @@ -111,7 +154,7 @@ fn layout_of_simd_ty( // Extract the number of elements from the layout of the array field: let FieldsShape::Array { count, .. } = db.layout_of_ty(f0_ty.clone(), env.clone())?.fields else { - user_error!("Array with non array layout"); + return Err(LayoutError::Unknown); }; (e_ty.clone(), count, true) @@ -123,7 +166,7 @@ fn layout_of_simd_ty( // Compute the ABI of the element type: let e_ly = db.layout_of_ty(e_ty, env.clone())?; let Abi::Scalar(e_abi) = e_ly.abi else { - user_error!("simd type with inner non scalar type"); + return Err(LayoutError::Unknown); }; // Compute the size and alignment of the vector: @@ -145,6 +188,8 @@ fn layout_of_simd_ty( largest_niche: e_ly.largest_niche, size, align, + max_repr_align: None, + unadjusted_abi_align: align.abi, })) } @@ -230,13 +275,11 @@ pub fn layout_of_ty_query( .map(|k| db.layout_of_ty(k.assert_ty_ref(Interner).clone(), trait_env.clone())) .collect::<Result<Vec<_>, _>>()?; let fields = fields.iter().map(|it| &**it).collect::<Vec<_>>(); - let fields = fields.iter().collect::<Vec<_>>(); + let fields = fields.iter().collect::<IndexVec<_, _>>(); cx.univariant(dl, &fields, &ReprOptions::default(), kind).ok_or(LayoutError::Unknown)? } TyKind::Array(element, count) => { - let count = try_const_usize(db, &count).ok_or(LayoutError::UserError(Box::from( - "unevaluated or mistyped const generic parameter", - )))? as u64; + let count = try_const_usize(db, &count).ok_or(LayoutError::HasErrorConst)? as u64; let element = db.layout_of_ty(element.clone(), trait_env.clone())?; let size = element.size.checked_mul(count, dl).ok_or(LayoutError::SizeOverflow)?; @@ -255,6 +298,8 @@ pub fn layout_of_ty_query( largest_niche, align: element.align, size, + max_repr_align: None, + unadjusted_abi_align: element.align.abi, } } TyKind::Slice(element) => { @@ -266,11 +311,23 @@ pub fn layout_of_ty_query( largest_niche: None, align: element.align, size: Size::ZERO, + max_repr_align: None, + unadjusted_abi_align: element.align.abi, } } + TyKind::Str => Layout { + variants: Variants::Single { index: struct_variant_idx() }, + fields: FieldsShape::Array { stride: Size::from_bytes(1), count: 0 }, + abi: Abi::Aggregate { sized: false }, + largest_niche: None, + align: dl.i8_align, + size: Size::ZERO, + max_repr_align: None, + unadjusted_abi_align: dl.i8_align.abi, + }, // Potentially-wide pointers. TyKind::Ref(_, _, pointee) | TyKind::Raw(_, pointee) => { - let mut data_ptr = scalar_unit(dl, Primitive::Pointer); + let mut data_ptr = scalar_unit(dl, Primitive::Pointer(AddressSpace::DATA)); if matches!(ty.kind(Interner), TyKind::Ref(..)) { data_ptr.valid_range_mut().start = 1; } @@ -294,7 +351,7 @@ pub fn layout_of_ty_query( scalar_unit(dl, Primitive::Int(dl.ptr_sized_integer(), false)) } TyKind::Dyn(..) => { - let mut vtable = scalar_unit(dl, Primitive::Pointer); + let mut vtable = scalar_unit(dl, Primitive::Pointer(AddressSpace::DATA)); vtable.valid_range_mut().start = 1; vtable } @@ -308,32 +365,17 @@ pub fn layout_of_ty_query( cx.scalar_pair(data_ptr, metadata) } TyKind::FnDef(_, _) => layout_of_unit(&cx, dl)?, - TyKind::Str => Layout { - variants: Variants::Single { index: struct_variant_idx() }, - fields: FieldsShape::Array { stride: Size::from_bytes(1), count: 0 }, - abi: Abi::Aggregate { sized: false }, - largest_niche: None, - align: dl.i8_align, - size: Size::ZERO, - }, - TyKind::Never => Layout { - variants: Variants::Single { index: struct_variant_idx() }, - fields: FieldsShape::Primitive, - abi: Abi::Uninhabited, - largest_niche: None, - align: dl.i8_align, - size: Size::ZERO, - }, + TyKind::Never => cx.layout_of_never_type(), TyKind::Dyn(_) | TyKind::Foreign(_) => { let mut unit = layout_of_unit(&cx, dl)?; match unit.abi { Abi::Aggregate { ref mut sized } => *sized = false, - _ => user_error!("bug"), + _ => return Err(LayoutError::Unknown), } unit } TyKind::Function(_) => { - let mut ptr = scalar_unit(dl, Primitive::Pointer); + let mut ptr = scalar_unit(dl, Primitive::Pointer(dl.instruction_address_space)); ptr.valid_range_mut().start = 1; Layout::scalar(dl, ptr) } @@ -363,7 +405,7 @@ pub fn layout_of_ty_query( }) .collect::<Result<Vec<_>, _>>()?; let fields = fields.iter().map(|it| &**it).collect::<Vec<_>>(); - let fields = fields.iter().collect::<Vec<_>>(); + let fields = fields.iter().collect::<IndexVec<_, _>>(); cx.univariant(dl, &fields, &ReprOptions::default(), StructKind::AlwaysSized) .ok_or(LayoutError::Unknown)? } @@ -390,17 +432,17 @@ pub fn layout_of_ty_query( pub fn layout_of_ty_recover( _: &dyn HirDatabase, - _: &[String], + _: &Cycle, _: &Ty, _: &Arc<TraitEnvironment>, ) -> Result<Arc<Layout>, LayoutError> { - user_error!("infinite sized recursive type"); + Err(LayoutError::RecursiveTypeWithoutIndirection) } fn layout_of_unit(cx: &LayoutCx<'_>, dl: &TargetDataLayout) -> Result<Layout, LayoutError> { - cx.univariant::<RustcEnumVariantIdx, &&Layout>( + cx.univariant::<RustcFieldIdx, RustcEnumVariantIdx, &&Layout>( dl, - &[], + IndexSlice::empty(), &ReprOptions::default(), StructKind::AlwaysSized, ) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs index 85ef649b8..39788a950 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs @@ -2,12 +2,14 @@ use std::{cmp, ops::Bound}; +use base_db::salsa::Cycle; use hir_def::{ data::adt::VariantData, layout::{Integer, LayoutCalculator, ReprOptions, TargetDataLayout}, AdtId, EnumVariantId, LocalEnumVariantId, VariantId, }; use la_arena::RawIdx; +use rustc_dependencies::index::IndexVec; use smallvec::SmallVec; use triomphe::Arc; @@ -20,8 +22,8 @@ use crate::{ use super::LayoutCx; -pub(crate) fn struct_variant_idx() -> RustcEnumVariantIdx { - RustcEnumVariantIdx(LocalEnumVariantId::from_raw(RawIdx::from(0))) +pub(crate) const fn struct_variant_idx() -> RustcEnumVariantIdx { + RustcEnumVariantIdx(LocalEnumVariantId::from_raw(RawIdx::from_u32(0))) } pub fn layout_of_adt_query( @@ -74,7 +76,7 @@ pub fn layout_of_adt_query( .iter() .map(|it| it.iter().map(|it| &**it).collect::<Vec<_>>()) .collect::<SmallVec<[_; 1]>>(); - let variants = variants.iter().map(|it| it.iter().collect()).collect(); + let variants = variants.iter().map(|it| it.iter().collect()).collect::<IndexVec<_, _>>(); let result = if matches!(def, AdtId::UnionId(..)) { cx.layout_of_union(&repr, &variants).ok_or(LayoutError::Unknown)? } else { @@ -105,7 +107,7 @@ pub fn layout_of_adt_query( && variants .iter() .next() - .and_then(|it| it.last().map(|it| !it.is_unsized())) + .and_then(|it| it.iter().last().map(|it| !it.is_unsized())) .unwrap_or(true), ) .ok_or(LayoutError::SizeOverflow)? @@ -119,7 +121,15 @@ fn layout_scalar_valid_range(db: &dyn HirDatabase, def: AdtId) -> (Bound<u128>, let attr = attrs.by_key(name).tt_values(); for tree in attr { if let Some(it) = tree.token_trees.first() { - if let Ok(it) = it.to_string().parse() { + let text = it.to_string().replace('_', ""); + let (text, base) = match text.as_bytes() { + [b'0', b'x', ..] => (&text[2..], 16), + [b'0', b'o', ..] => (&text[2..], 8), + [b'0', b'b', ..] => (&text[2..], 2), + _ => (&*text, 10), + }; + + if let Ok(it) = u128::from_str_radix(text, base) { return Bound::Included(it); } } @@ -131,12 +141,12 @@ fn layout_scalar_valid_range(db: &dyn HirDatabase, def: AdtId) -> (Bound<u128>, pub fn layout_of_adt_recover( _: &dyn HirDatabase, - _: &[String], + _: &Cycle, _: &AdtId, _: &Substitution, _: &Arc<TraitEnvironment>, ) -> Result<Arc<Layout>, LayoutError> { - user_error!("infinite sized recursive type"); + Err(LayoutError::RecursiveTypeWithoutIndirection) } /// Finds the appropriate Integer type and signedness for the given @@ -160,11 +170,7 @@ fn repr_discr( let discr = Integer::from_attr(dl, ity); let fit = if ity.is_signed() { signed_fit } else { unsigned_fit }; if discr < fit { - return Err(LayoutError::UserError( - "Integer::repr_discr: `#[repr]` hint too small for \ - discriminant range of enum " - .into(), - )); + return Err(LayoutError::UserReprTooSmall); } return Ok((discr, ity.is_signed())); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs index ffdbb9de9..5e3a86c80 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs @@ -210,16 +210,13 @@ fn recursive() { struct BoxLike<T: ?Sized>(*mut T); struct Goal(BoxLike<Goal>); } - check_fail( - r#"struct Goal(Goal);"#, - LayoutError::UserError("infinite sized recursive type".into()), - ); + check_fail(r#"struct Goal(Goal);"#, LayoutError::RecursiveTypeWithoutIndirection); check_fail( r#" struct Foo<T>(Foo<T>); struct Goal(Foo<i32>); "#, - LayoutError::UserError("infinite sized recursive type".into()), + LayoutError::RecursiveTypeWithoutIndirection, ); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests/closure.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests/closure.rs index bbe855a14..939025461 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests/closure.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests/closure.rs @@ -186,9 +186,9 @@ fn capture_specific_fields() { fn match_pattern() { size_and_align_expr! { struct X(i64, i32, (u8, i128)); - let y: X = X(2, 5, (7, 3)); + let _y: X = X(2, 5, (7, 3)); move |x: i64| { - match y { + match _y { _ => x, } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs index 405bb001b..cf174feed 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -1,6 +1,7 @@ //! The type system. We currently use this to infer types for completion, hover //! information and various assists. -#![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)] +#![warn(rust_2018_idioms, unused_lifetimes)] +#![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] #[allow(unused)] macro_rules! eprintln { @@ -72,14 +73,15 @@ pub use infer::{ }; pub use interner::Interner; pub use lower::{ - associated_type_shorthand_candidates, CallableDefId, ImplTraitLoweringMode, TyDefId, - TyLoweringContext, ValueTyDefId, + associated_type_shorthand_candidates, CallableDefId, ImplTraitLoweringMode, ParamLoweringMode, + TyDefId, TyLoweringContext, ValueTyDefId, }; pub use mapping::{ from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, from_placeholder_idx, lt_from_placeholder_idx, to_assoc_type_id, to_chalk_trait_id, to_foreign_def_id, to_placeholder_idx, }; +pub use method_resolution::check_orphan_rules; pub use traits::TraitEnvironment; pub use utils::{all_super_traits, is_fn_unsafe_to_call}; @@ -120,7 +122,7 @@ pub type TyKind = chalk_ir::TyKind<Interner>; pub type TypeFlags = chalk_ir::TypeFlags; pub type DynTy = chalk_ir::DynTy<Interner>; pub type FnPointer = chalk_ir::FnPointer<Interner>; -// pub type FnSubst = chalk_ir::FnSubst<Interner>; +// pub type FnSubst = chalk_ir::FnSubst<Interner>; // a re-export so we don't lose the tuple constructor pub use chalk_ir::FnSubst; pub type ProjectionTy = chalk_ir::ProjectionTy<Interner>; pub type AliasTy = chalk_ir::AliasTy<Interner>; @@ -320,8 +322,7 @@ impl CallableSig { pub fn from_fn_ptr(fn_ptr: &FnPointer) -> CallableSig { CallableSig { // FIXME: what to do about lifetime params? -> return PolyFnSig - // FIXME: use `Arc::from_iter` when it becomes available - params_and_return: Arc::from( + params_and_return: Arc::from_iter( fn_ptr .substitution .clone() @@ -330,8 +331,7 @@ impl CallableSig { .0 .as_slice(Interner) .iter() - .map(|arg| arg.assert_ty_ref(Interner).clone()) - .collect::<Vec<_>>(), + .map(|arg| arg.assert_ty_ref(Interner).clone()), ), is_varargs: fn_ptr.sig.variadic, safety: fn_ptr.sig.safety, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs index 9a61f1535..97c4a741f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -10,7 +10,7 @@ use std::{ iter, }; -use base_db::CrateId; +use base_db::{salsa::Cycle, CrateId}; use chalk_ir::{ cast::Cast, fold::Shift, fold::TypeFoldable, interner::HasInterner, Mutability, Safety, }; @@ -113,7 +113,9 @@ pub struct TyLoweringContext<'a> { pub db: &'a dyn HirDatabase, resolver: &'a Resolver, in_binders: DebruijnIndex, - owner: TypeOwnerId, + // FIXME: Should not be an `Option` but `Resolver` currently does not return owners in all cases + // where expected + owner: Option<TypeOwnerId>, /// Note: Conceptually, it's thinkable that we could be in a location where /// some type params should be represented as placeholders, and others /// should be converted to variables. I think in practice, this isn't @@ -127,6 +129,14 @@ pub struct TyLoweringContext<'a> { impl<'a> TyLoweringContext<'a> { pub fn new(db: &'a dyn HirDatabase, resolver: &'a Resolver, owner: TypeOwnerId) -> Self { + Self::new_maybe_unowned(db, resolver, Some(owner)) + } + + pub fn new_maybe_unowned( + db: &'a dyn HirDatabase, + resolver: &'a Resolver, + owner: Option<TypeOwnerId>, + ) -> Self { let impl_trait_mode = ImplTraitLoweringState::Disallowed; let type_param_mode = ParamLoweringMode::Placeholder; let in_binders = DebruijnIndex::INNERMOST; @@ -213,10 +223,11 @@ impl<'a> TyLoweringContext<'a> { } pub fn lower_const(&self, const_ref: &ConstRef, const_type: Ty) -> Const { + let Some(owner) = self.owner else { return unknown_const(const_type) }; const_or_path_to_chalk( self.db, self.resolver, - self.owner, + owner, const_type, const_ref, self.type_param_mode, @@ -407,11 +418,7 @@ impl<'a> TyLoweringContext<'a> { drop(expander); let ty = self.lower_ty(&type_ref); - self.expander - .borrow_mut() - .as_mut() - .unwrap() - .exit(self.db.upcast(), mark); + self.expander.borrow_mut().as_mut().unwrap().exit(mark); Some(ty) } _ => { @@ -768,7 +775,7 @@ impl<'a> TyLoweringContext<'a> { } } - fn substs_from_path_segment( + pub(super) fn substs_from_path_segment( &self, segment: PathSegment<'_>, def: Option<GenericDefId>, @@ -1097,10 +1104,25 @@ impl<'a> TyLoweringContext<'a> { binding.type_ref.as_ref().map_or(0, |_| 1) + binding.bounds.len(), ); if let Some(type_ref) = &binding.type_ref { - let ty = self.lower_ty(type_ref); - let alias_eq = - AliasEq { alias: AliasTy::Projection(projection_ty.clone()), ty }; - predicates.push(crate::wrap_empty_binders(WhereClause::AliasEq(alias_eq))); + if let (TypeRef::ImplTrait(bounds), ImplTraitLoweringState::Disallowed) = + (type_ref, &self.impl_trait_mode) + { + for bound in bounds { + predicates.extend( + self.lower_type_bound( + bound, + TyKind::Alias(AliasTy::Projection(projection_ty.clone())) + .intern(Interner), + false, + ), + ); + } + } else { + let ty = self.lower_ty(type_ref); + let alias_eq = + AliasEq { alias: AliasTy::Projection(projection_ty.clone()), ty }; + predicates.push(crate::wrap_empty_binders(WhereClause::AliasEq(alias_eq))); + } } for bound in binding.bounds.iter() { predicates.extend(self.lower_type_bound( @@ -1383,51 +1405,50 @@ pub(crate) fn generic_predicates_for_param_query( let ctx = TyLoweringContext::new(db, &resolver, def.into()) .with_type_param_mode(ParamLoweringMode::Variable); let generics = generics(db.upcast(), def); - let mut predicates: Vec<_> = resolver - .where_predicates_in_scope() - // we have to filter out all other predicates *first*, before attempting to lower them - .filter(|pred| match pred { - WherePredicate::ForLifetime { target, bound, .. } - | WherePredicate::TypeBound { target, bound, .. } => { - match target { - WherePredicateTypeTarget::TypeRef(type_ref) => { - if ctx.lower_ty_only_param(type_ref) != Some(param_id) { - return false; - } - } - &WherePredicateTypeTarget::TypeOrConstParam(local_id) => { - let target_id = TypeOrConstParamId { parent: def, local_id }; - if target_id != param_id { - return false; - } - } - }; - match &**bound { - TypeBound::ForLifetime(_, path) | TypeBound::Path(path, _) => { - // Only lower the bound if the trait could possibly define the associated - // type we're looking for. + // we have to filter out all other predicates *first*, before attempting to lower them + let predicate = |pred: &&_| match pred { + WherePredicate::ForLifetime { target, bound, .. } + | WherePredicate::TypeBound { target, bound, .. } => { + let invalid_target = match target { + WherePredicateTypeTarget::TypeRef(type_ref) => { + ctx.lower_ty_only_param(type_ref) != Some(param_id) + } + &WherePredicateTypeTarget::TypeOrConstParam(local_id) => { + let target_id = TypeOrConstParamId { parent: def, local_id }; + target_id != param_id + } + }; + if invalid_target { + return false; + } + + match &**bound { + TypeBound::ForLifetime(_, path) | TypeBound::Path(path, _) => { + // Only lower the bound if the trait could possibly define the associated + // type we're looking for. - let assoc_name = match &assoc_name { - Some(it) => it, - None => return true, - }; - let tr = match resolver.resolve_path_in_type_ns_fully(db.upcast(), path) { - Some(TypeNs::TraitId(tr)) => tr, - _ => return false, - }; + let Some(assoc_name) = &assoc_name else { return true }; + let Some(TypeNs::TraitId(tr)) = + resolver.resolve_path_in_type_ns_fully(db.upcast(), path) + else { + return false; + }; - all_super_traits(db.upcast(), tr).iter().any(|tr| { - db.trait_data(*tr).items.iter().any(|(name, item)| { - matches!(item, AssocItemId::TypeAliasId(_)) && name == assoc_name - }) + all_super_traits(db.upcast(), tr).iter().any(|tr| { + db.trait_data(*tr).items.iter().any(|(name, item)| { + matches!(item, AssocItemId::TypeAliasId(_)) && name == assoc_name }) - } - TypeBound::Lifetime(_) | TypeBound::Error => false, + }) } + TypeBound::Lifetime(_) | TypeBound::Error => false, } - WherePredicate::Lifetime { .. } => false, - }) + } + WherePredicate::Lifetime { .. } => false, + }; + let mut predicates: Vec<_> = resolver + .where_predicates_in_scope() + .filter(predicate) .flat_map(|pred| { ctx.lower_where_predicate(pred, true).map(|p| make_binders(db, &generics, p)) }) @@ -1444,13 +1465,12 @@ pub(crate) fn generic_predicates_for_param_query( pub(crate) fn generic_predicates_for_param_recover( _db: &dyn HirDatabase, - _cycle: &[String], + _cycle: &Cycle, _def: &GenericDefId, _param_id: &TypeOrConstParamId, _assoc_name: &Option<Name>, ) -> Arc<[Binders<QuantifiedWhereClause>]> { - // FIXME: use `Arc::from_iter` when it becomes available - Arc::from(vec![]) + Arc::from_iter(None) } pub(crate) fn trait_environment_for_body_query( @@ -1459,7 +1479,7 @@ pub(crate) fn trait_environment_for_body_query( ) -> Arc<TraitEnvironment> { let Some(def) = def.as_generic_def_id() else { let krate = def.module(db.upcast()).krate(); - return Arc::new(TraitEnvironment::empty(krate)); + return TraitEnvironment::empty(krate); }; db.trait_environment(def) } @@ -1519,7 +1539,7 @@ pub(crate) fn trait_environment_query( let env = chalk_ir::Environment::new(Interner).add_clauses(Interner, clauses); - Arc::new(TraitEnvironment { krate, block: None, traits_from_clauses: traits_in_scope, env }) + TraitEnvironment::new(krate, None, traits_in_scope.into_boxed_slice(), env) } /// Resolve the where clause(s) of an item with generics. @@ -1588,69 +1608,54 @@ pub(crate) fn generic_defaults_query( let generic_params = generics(db.upcast(), def); let parent_start_idx = generic_params.len_self(); - let defaults = Arc::from( - generic_params - .iter() - .enumerate() - .map(|(idx, (id, p))| { - match p { - TypeOrConstParamData::TypeParamData(p) => { - let mut ty = p - .default - .as_ref() - .map_or(TyKind::Error.intern(Interner), |t| ctx.lower_ty(t)); - // Each default can only refer to previous parameters. - // Type variable default referring to parameter coming - // after it is forbidden (FIXME: report diagnostic) - ty = fallback_bound_vars(ty, idx, parent_start_idx); - crate::make_binders(db, &generic_params, ty.cast(Interner)) - } - TypeOrConstParamData::ConstParamData(p) => { - let mut val = p.default.as_ref().map_or_else( - || { - unknown_const_as_generic( - db.const_param_ty(ConstParamId::from_unchecked(id)), - ) - }, - |c| { - let c = ctx.lower_const(c, ctx.lower_ty(&p.ty)); - c.cast(Interner) - }, - ); - // Each default can only refer to previous parameters, see above. - val = fallback_bound_vars(val, idx, parent_start_idx); - make_binders(db, &generic_params, val) - } - } - }) - // FIXME: use `Arc::from_iter` when it becomes available - .collect::<Vec<_>>(), - ); + let defaults = Arc::from_iter(generic_params.iter().enumerate().map(|(idx, (id, p))| { + match p { + TypeOrConstParamData::TypeParamData(p) => { + let mut ty = + p.default.as_ref().map_or(TyKind::Error.intern(Interner), |t| ctx.lower_ty(t)); + // Each default can only refer to previous parameters. + // Type variable default referring to parameter coming + // after it is forbidden (FIXME: report diagnostic) + ty = fallback_bound_vars(ty, idx, parent_start_idx); + crate::make_binders(db, &generic_params, ty.cast(Interner)) + } + TypeOrConstParamData::ConstParamData(p) => { + let mut val = p.default.as_ref().map_or_else( + || { + unknown_const_as_generic( + db.const_param_ty(ConstParamId::from_unchecked(id)), + ) + }, + |c| { + let c = ctx.lower_const(c, ctx.lower_ty(&p.ty)); + c.cast(Interner) + }, + ); + // Each default can only refer to previous parameters, see above. + val = fallback_bound_vars(val, idx, parent_start_idx); + make_binders(db, &generic_params, val) + } + } + })); defaults } pub(crate) fn generic_defaults_recover( db: &dyn HirDatabase, - _cycle: &[String], + _cycle: &Cycle, def: &GenericDefId, ) -> Arc<[Binders<crate::GenericArg>]> { let generic_params = generics(db.upcast(), *def); // FIXME: this code is not covered in tests. // we still need one default per parameter - let defaults = Arc::from( - generic_params - .iter_id() - .map(|id| { - let val = match id { - Either::Left(_) => TyKind::Error.intern(Interner).cast(Interner), - Either::Right(id) => unknown_const_as_generic(db.const_param_ty(id)), - }; - crate::make_binders(db, &generic_params, val) - }) - // FIXME: use `Arc::from_iter` when it becomes available - .collect::<Vec<_>>(), - ); + let defaults = Arc::from_iter(generic_params.iter_id().map(|id| { + let val = match id { + Either::Left(_) => TyKind::Error.intern(Interner).cast(Interner), + Either::Right(id) => unknown_const_as_generic(db.const_param_ty(id)), + }; + crate::make_binders(db, &generic_params, val) + })); defaults } @@ -1774,10 +1779,11 @@ fn type_for_type_alias(db: &dyn HirDatabase, t: TypeAliasId) -> Binders<Ty> { let resolver = t.resolver(db.upcast()); let ctx = TyLoweringContext::new(db, &resolver, t.into()) .with_type_param_mode(ParamLoweringMode::Variable); - if db.type_alias_data(t).is_extern { + let type_alias_data = db.type_alias_data(t); + if type_alias_data.is_extern { Binders::empty(Interner, TyKind::Foreign(crate::to_foreign_def_id(t)).intern(Interner)) } else { - let type_ref = &db.type_alias_data(t).type_ref; + let type_ref = &type_alias_data.type_ref; let inner = ctx.lower_ty(type_ref.as_deref().unwrap_or(&TypeRef::Error)); make_binders(db, &generics, inner) } @@ -1866,7 +1872,7 @@ pub(crate) fn ty_query(db: &dyn HirDatabase, def: TyDefId) -> Binders<Ty> { } } -pub(crate) fn ty_recover(db: &dyn HirDatabase, _cycle: &[String], def: &TyDefId) -> Binders<Ty> { +pub(crate) fn ty_recover(db: &dyn HirDatabase, _cycle: &Cycle, def: &TyDefId) -> Binders<Ty> { let generics = match *def { TyDefId::BuiltinType(_) => return Binders::empty(Interner, TyKind::Error.intern(Interner)), TyDefId::AdtId(it) => generics(db.upcast(), it.into()), @@ -1916,7 +1922,7 @@ pub(crate) fn const_param_ty_query(db: &dyn HirDatabase, def: ConstParamId) -> T pub(crate) fn impl_self_ty_recover( db: &dyn HirDatabase, - _cycle: &[String], + _cycle: &Cycle, impl_id: &ImplId, ) -> Binders<Ty> { let generics = generics(db.upcast(), (*impl_id).into()); @@ -2048,7 +2054,7 @@ pub(crate) fn const_or_path_to_chalk( .intern_in_type_const(InTypeConstLoc { id: it, owner, - thing: Box::new(InTypeConstIdMetadata(expected_ty.clone())), + expected_ty: Box::new(InTypeConstIdMetadata(expected_ty.clone())), }) .into(); intern_const_scalar( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs index f3a5f69b2..041d61c1b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs @@ -27,8 +27,9 @@ use crate::{ primitive::{FloatTy, IntTy, UintTy}, static_lifetime, to_chalk_trait_id, utils::all_super_traits, - AdtId, Canonical, CanonicalVarKinds, DebruijnIndex, DynTyExt, ForeignDefId, InEnvironment, - Interner, Scalar, Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder, TyExt, + AdtId, Canonical, CanonicalVarKinds, DebruijnIndex, DynTyExt, ForeignDefId, Goal, Guidance, + InEnvironment, Interner, Scalar, Solution, Substitution, TraitEnvironment, TraitRef, + TraitRefExt, Ty, TyBuilder, TyExt, }; /// This is used as a key for indexing impls. @@ -167,12 +168,9 @@ impl TraitImpls { ) -> Arc<[Arc<Self>]> { let _p = profile::span("trait_impls_in_deps_query").detail(|| format!("{krate:?}")); let crate_graph = db.crate_graph(); - // FIXME: use `Arc::from_iter` when it becomes available - Arc::from( - crate_graph - .transitive_deps(krate) - .map(|krate| db.trait_impls_in_crate(krate)) - .collect::<Vec<_>>(), + + Arc::from_iter( + crate_graph.transitive_deps(krate).map(|krate| db.trait_impls_in_crate(krate)), ) } @@ -862,6 +860,62 @@ fn is_inherent_impl_coherent( } } +/// Checks whether the impl satisfies the orphan rules. +/// +/// Given `impl<P1..=Pn> Trait<T1..=Tn> for T0`, an `impl`` is valid only if at least one of the following is true: +/// - Trait is a local trait +/// - All of +/// - At least one of the types `T0..=Tn`` must be a local type. Let `Ti`` be the first such type. +/// - No uncovered type parameters `P1..=Pn` may appear in `T0..Ti`` (excluding `Ti`) +pub fn check_orphan_rules(db: &dyn HirDatabase, impl_: ImplId) -> bool { + let substs = TyBuilder::placeholder_subst(db, impl_); + let Some(impl_trait) = db.impl_trait(impl_) else { + // not a trait impl + return true; + }; + + let local_crate = impl_.lookup(db.upcast()).container.krate(); + let is_local = |tgt_crate| tgt_crate == local_crate; + + let trait_ref = impl_trait.substitute(Interner, &substs); + let trait_id = from_chalk_trait_id(trait_ref.trait_id); + if is_local(trait_id.module(db.upcast()).krate()) { + // trait to be implemented is local + return true; + } + + let unwrap_fundamental = |ty: Ty| match ty.kind(Interner) { + TyKind::Ref(_, _, referenced) => referenced.clone(), + &TyKind::Adt(AdtId(hir_def::AdtId::StructId(s)), ref subs) => { + let struct_data = db.struct_data(s); + if struct_data.flags.contains(StructFlags::IS_FUNDAMENTAL) { + let next = subs.type_parameters(Interner).next(); + match next { + Some(ty) => ty, + None => ty, + } + } else { + ty + } + } + _ => ty, + }; + // - At least one of the types `T0..=Tn`` must be a local type. Let `Ti`` be the first such type. + let is_not_orphan = trait_ref.substitution.type_parameters(Interner).any(|ty| { + match unwrap_fundamental(ty).kind(Interner) { + &TyKind::Adt(AdtId(id), _) => is_local(id.module(db.upcast()).krate()), + TyKind::Error => true, + TyKind::Dyn(it) => it.principal().map_or(false, |trait_ref| { + is_local(from_chalk_trait_id(trait_ref.trait_id).module(db.upcast()).krate()) + }), + _ => false, + } + }); + // FIXME: param coverage + // - No uncovered type parameters `P1..=Pn` may appear in `T0..Ti`` (excluding `Ti`) + is_not_orphan +} + pub fn iterate_path_candidates( ty: &Canonical<Ty>, db: &dyn HirDatabase, @@ -1422,26 +1476,52 @@ fn is_valid_fn_candidate( // We need to consider the bounds on the impl to distinguish functions of the same name // for a type. let predicates = db.generic_predicates(impl_id.into()); - let valid = predicates - .iter() - .map(|predicate| { - let (p, b) = predicate - .clone() - .substitute(Interner, &impl_subst) - // Skipping the inner binders is ok, as we don't handle quantified where - // clauses yet. - .into_value_and_skipped_binders(); - stdx::always!(b.len(Interner) == 0); - p - }) - // It's ok to get ambiguity here, as we may not have enough information to prove - // obligations. We'll check if the user is calling the selected method properly - // later anyway. - .all(|p| table.try_obligation(p.cast(Interner)).is_some()); - match valid { - true => IsValidCandidate::Yes, - false => IsValidCandidate::No, + let goals = predicates.iter().map(|p| { + let (p, b) = p + .clone() + .substitute(Interner, &impl_subst) + // Skipping the inner binders is ok, as we don't handle quantified where + // clauses yet. + .into_value_and_skipped_binders(); + stdx::always!(b.len(Interner) == 0); + + p.cast::<Goal>(Interner) + }); + + for goal in goals.clone() { + let in_env = InEnvironment::new(&table.trait_env.env, goal); + let canonicalized = table.canonicalize(in_env); + let solution = table.db.trait_solve( + table.trait_env.krate, + table.trait_env.block, + canonicalized.value.clone(), + ); + + match solution { + Some(Solution::Unique(canonical_subst)) => { + canonicalized.apply_solution( + table, + Canonical { + binders: canonical_subst.binders, + value: canonical_subst.value.subst, + }, + ); + } + Some(Solution::Ambig(Guidance::Definite(substs))) => { + canonicalized.apply_solution(table, substs); + } + Some(_) => (), + None => return IsValidCandidate::No, + } } + + for goal in goals { + if table.try_obligation(goal).is_none() { + return IsValidCandidate::No; + } + } + + IsValidCandidate::Yes } else { // For `ItemContainerId::TraitId`, we check if `self_ty` implements the trait in // `iterate_trait_method_candidates()`. diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs index e953058cc..f1795e71d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs @@ -40,7 +40,6 @@ pub use monomorphization::{ use rustc_hash::FxHashMap; use smallvec::{smallvec, SmallVec}; use stdx::{impl_from, never}; -use triomphe::Arc; use super::consteval::{intern_const_scalar, try_const_usize}; @@ -147,7 +146,7 @@ impl<V, T> ProjectionElem<V, T> { base = normalize( db, // FIXME: we should get this from caller - Arc::new(TraitEnvironment::empty(krate)), + TraitEnvironment::empty(krate), base, ); } @@ -243,16 +242,16 @@ impl Default for ProjectionStore { } impl ProjectionStore { - fn shrink_to_fit(&mut self) { + pub fn shrink_to_fit(&mut self) { self.id_to_proj.shrink_to_fit(); self.proj_to_id.shrink_to_fit(); } - fn intern_if_exist(&self, projection: &[PlaceElem]) -> Option<ProjectionId> { + pub fn intern_if_exist(&self, projection: &[PlaceElem]) -> Option<ProjectionId> { self.proj_to_id.get(projection).copied() } - fn intern(&mut self, projection: Box<[PlaceElem]>) -> ProjectionId { + pub fn intern(&mut self, projection: Box<[PlaceElem]>) -> ProjectionId { let new_id = ProjectionId(self.proj_to_id.len() as u32); match self.proj_to_id.entry(projection) { Entry::Occupied(id) => *id.get(), @@ -267,20 +266,24 @@ impl ProjectionStore { } impl ProjectionId { - const EMPTY: ProjectionId = ProjectionId(0); + pub const EMPTY: ProjectionId = ProjectionId(0); + + pub fn is_empty(self) -> bool { + self == ProjectionId::EMPTY + } - fn lookup(self, store: &ProjectionStore) -> &[PlaceElem] { + pub fn lookup(self, store: &ProjectionStore) -> &[PlaceElem] { store.id_to_proj.get(&self).unwrap() } - fn project(self, projection: PlaceElem, store: &mut ProjectionStore) -> ProjectionId { + pub fn project(self, projection: PlaceElem, store: &mut ProjectionStore) -> ProjectionId { let mut current = self.lookup(store).to_vec(); current.push(projection); store.intern(current.into()) } } -#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct Place { pub local: LocalId, pub projection: ProjectionId, @@ -1007,7 +1010,7 @@ pub enum Rvalue { #[derive(Debug, PartialEq, Eq, Clone)] pub enum StatementKind { Assign(Place, Rvalue), - //FakeRead(Box<(FakeReadCause, Place)>), + FakeRead(Place), //SetDiscriminant { // place: Box<Place>, // variant_index: VariantIdx, @@ -1069,6 +1072,10 @@ pub struct MirBody { } impl MirBody { + pub fn local_to_binding_map(&self) -> ArenaMap<LocalId, BindingId> { + self.binding_locals.iter().map(|(it, y)| (*y, it)).collect() + } + fn walk_places(&mut self, mut f: impl FnMut(&mut Place, &mut ProjectionStore)) { fn for_operand( op: &mut Operand, @@ -1109,7 +1116,9 @@ impl MirBody { } } } - StatementKind::Deinit(p) => f(p, &mut self.projection_store), + StatementKind::FakeRead(p) | StatementKind::Deinit(p) => { + f(p, &mut self.projection_store) + } StatementKind::StorageLive(_) | StatementKind::StorageDead(_) | StatementKind::Nop => (), @@ -1186,3 +1195,9 @@ pub enum MirSpan { } impl_from!(ExprId, PatId for MirSpan); + +impl From<&ExprId> for MirSpan { + fn from(value: &ExprId) -> Self { + (*value).into() + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs index 41fb12965..74c5efd6c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs @@ -24,6 +24,7 @@ use super::{ pub enum MutabilityReason { Mut { spans: Vec<MirSpan> }, Not, + Unused, } #[derive(Debug, Clone, PartialEq, Eq)] @@ -144,7 +145,8 @@ fn moved_out_of_ref(db: &dyn HirDatabase, body: &MirBody) -> Vec<MovedOutOfRef> } } }, - StatementKind::Deinit(_) + StatementKind::FakeRead(_) + | StatementKind::Deinit(_) | StatementKind::StorageLive(_) | StatementKind::StorageDead(_) | StatementKind::Nop => (), @@ -264,7 +266,10 @@ fn ever_initialized_map( is_ever_initialized = false; } } - StatementKind::Deinit(_) | StatementKind::Nop | StatementKind::StorageLive(_) => (), + StatementKind::Deinit(_) + | StatementKind::FakeRead(_) + | StatementKind::Nop + | StatementKind::StorageLive(_) => (), } } let Some(terminator) = &block.terminator else { @@ -331,16 +336,37 @@ fn ever_initialized_map( result } +fn push_mut_span(local: LocalId, span: MirSpan, result: &mut ArenaMap<LocalId, MutabilityReason>) { + match &mut result[local] { + MutabilityReason::Mut { spans } => spans.push(span), + it @ (MutabilityReason::Not | MutabilityReason::Unused) => { + *it = MutabilityReason::Mut { spans: vec![span] } + } + }; +} + +fn record_usage(local: LocalId, result: &mut ArenaMap<LocalId, MutabilityReason>) { + match &mut result[local] { + it @ MutabilityReason::Unused => { + *it = MutabilityReason::Not; + } + _ => (), + }; +} + +fn record_usage_for_operand(arg: &Operand, result: &mut ArenaMap<LocalId, MutabilityReason>) { + if let Operand::Copy(p) | Operand::Move(p) = arg { + record_usage(p.local, result); + } +} + fn mutability_of_locals( db: &dyn HirDatabase, body: &MirBody, ) -> ArenaMap<LocalId, MutabilityReason> { let mut result: ArenaMap<LocalId, MutabilityReason> = - body.locals.iter().map(|it| (it.0, MutabilityReason::Not)).collect(); - let mut push_mut_span = |local, span| match &mut result[local] { - MutabilityReason::Mut { spans } => spans.push(span), - it @ MutabilityReason::Not => *it = MutabilityReason::Mut { spans: vec![span] }, - }; + body.locals.iter().map(|it| (it.0, MutabilityReason::Unused)).collect(); + let ever_init_maps = ever_initialized_map(db, body); for (block_id, mut ever_init_map) in ever_init_maps.into_iter() { let block = &body.basic_blocks[block_id]; @@ -350,23 +376,51 @@ fn mutability_of_locals( match place_case(db, body, place) { ProjectionCase::Direct => { if ever_init_map.get(place.local).copied().unwrap_or_default() { - push_mut_span(place.local, statement.span); + push_mut_span(place.local, statement.span, &mut result); } else { ever_init_map.insert(place.local, true); } } ProjectionCase::DirectPart => { // Partial initialization is not supported, so it is definitely `mut` - push_mut_span(place.local, statement.span); + push_mut_span(place.local, statement.span, &mut result); + } + ProjectionCase::Indirect => { + record_usage(place.local, &mut result); } - ProjectionCase::Indirect => (), + } + match value { + Rvalue::CopyForDeref(p) + | Rvalue::Discriminant(p) + | Rvalue::Len(p) + | Rvalue::Ref(_, p) => { + record_usage(p.local, &mut result); + } + Rvalue::Use(o) + | Rvalue::Repeat(o, _) + | Rvalue::Cast(_, o, _) + | Rvalue::UnaryOp(_, o) => record_usage_for_operand(o, &mut result), + Rvalue::CheckedBinaryOp(_, o1, o2) => { + for o in [o1, o2] { + record_usage_for_operand(o, &mut result); + } + } + Rvalue::Aggregate(_, args) => { + for arg in args.iter() { + record_usage_for_operand(arg, &mut result); + } + } + Rvalue::ShallowInitBox(_, _) | Rvalue::ShallowInitBoxWithAlloc(_) => (), } if let Rvalue::Ref(BorrowKind::Mut { .. }, p) = value { if place_case(db, body, p) != ProjectionCase::Indirect { - push_mut_span(p.local, statement.span); + push_mut_span(p.local, statement.span, &mut result); } } } + StatementKind::FakeRead(p) => { + record_usage(p.local, &mut result); + } StatementKind::StorageDead(p) => { ever_init_map.insert(*p, false); } @@ -386,15 +440,21 @@ fn mutability_of_locals( | TerminatorKind::FalseEdge { .. } | TerminatorKind::FalseUnwind { .. } | TerminatorKind::GeneratorDrop - | TerminatorKind::SwitchInt { .. } | TerminatorKind::Drop { .. } | TerminatorKind::DropAndReplace { .. } | TerminatorKind::Assert { .. } | TerminatorKind::Yield { .. } => (), - TerminatorKind::Call { destination, .. } => { + TerminatorKind::SwitchInt { discr, targets: _ } => { + record_usage_for_operand(discr, &mut result); + } + TerminatorKind::Call { destination, args, func, .. } => { + record_usage_for_operand(func, &mut result); + for arg in args.iter() { + record_usage_for_operand(arg, &mut result); + } if destination.projection.lookup(&body.projection_store).len() == 0 { if ever_init_map.get(destination.local).copied().unwrap_or_default() { - push_mut_span(destination.local, MirSpan::Unknown); + push_mut_span(destination.local, MirSpan::Unknown, &mut result); } else { ever_init_map.insert(destination.local, true); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs index 4364e0d32..fbfb6ff8c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs @@ -21,7 +21,7 @@ use hir_def::{ AdtId, ConstId, DefWithBodyId, EnumVariantId, FunctionId, HasModule, ItemContainerId, Lookup, StaticId, VariantId, }; -use hir_expand::{mod_path::ModPath, InFile}; +use hir_expand::{mod_path::ModPath, HirFileIdExt, InFile}; use intern::Interned; use la_arena::ArenaMap; use rustc_hash::{FxHashMap, FxHashSet}; @@ -162,7 +162,7 @@ pub struct Evaluator<'a> { not_special_fn_cache: RefCell<FxHashSet<FunctionId>>, mir_or_dyn_index_cache: RefCell<FxHashMap<(FunctionId, Substitution), MirOrDynIndex>>, /// Constantly dropping and creating `Locals` is very costly. We store - /// old locals that we normaly want to drop here, to reuse their allocations + /// old locals that we normally want to drop here, to reuse their allocations /// later. unused_locals_store: RefCell<FxHashMap<DefWithBodyId, Vec<Locals>>>, cached_ptr_size: usize, @@ -375,10 +375,7 @@ impl MirEvalError { Err(_) => continue, }, MirSpan::PatId(p) => match source_map.pat_syntax(*p) { - Ok(s) => s.map(|it| match it { - Either::Left(e) => e.into(), - Either::Right(e) => e.into(), - }), + Ok(s) => s.map(|it| it.syntax_node_ptr()), Err(_) => continue, }, MirSpan::Unknown => continue, @@ -842,6 +839,7 @@ impl Evaluator<'_> { } StatementKind::Deinit(_) => not_supported!("de-init statement"), StatementKind::StorageLive(_) + | StatementKind::FakeRead(_) | StatementKind::StorageDead(_) | StatementKind::Nop => (), } @@ -2301,7 +2299,7 @@ impl Evaluator<'_> { match self.get_mir_or_dyn_index(def, generic_args.clone(), locals, span)? { MirOrDynIndex::Dyn(self_ty_idx) => { // In the layout of current possible receiver, which at the moment of writing this code is one of - // `&T`, `&mut T`, `Box<T>`, `Rc<T>`, `Arc<T>`, and `Pin<P>` where `P` is one of possible recievers, + // `&T`, `&mut T`, `Box<T>`, `Rc<T>`, `Arc<T>`, and `Pin<P>` where `P` is one of possible receivers, // the vtable is exactly in the `[ptr_size..2*ptr_size]` bytes. So we can use it without branching on // the type. let first_arg = arg_bytes.clone().next().unwrap(); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs index 803ef631f..2de99e416 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs @@ -1045,7 +1045,7 @@ impl Evaluator<'_> { } "transmute" => { let [arg] = args else { - return Err(MirEvalError::TypeError("trasmute arg is not provided")); + return Err(MirEvalError::TypeError("transmute arg is not provided")); }; destination.write_from_interval(self, arg.interval) } @@ -1065,7 +1065,7 @@ impl Evaluator<'_> { } "ctlz" | "ctlz_nonzero" => { let [arg] = args else { - return Err(MirEvalError::TypeError("cttz arg is not provided")); + return Err(MirEvalError::TypeError("ctlz arg is not provided")); }; let result = u128::from_le_bytes(pad16(arg.get(self)?, false)).leading_zeros() as usize; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs index dd2dba717..639fabc19 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs @@ -2,7 +2,7 @@ use std::{fmt::Write, iter, mem}; -use base_db::FileId; +use base_db::{salsa::Cycle, FileId}; use chalk_ir::{BoundVar, ConstData, DebruijnIndex, TyKind}; use hir_def::{ body::Body, @@ -105,9 +105,14 @@ pub enum MirLowerError { /// A token to ensuring that each drop scope is popped at most once, thanks to the compiler that checks moves. struct DropScopeToken; impl DropScopeToken { - fn pop_and_drop(self, ctx: &mut MirLowerCtx<'_>, current: BasicBlockId) -> BasicBlockId { + fn pop_and_drop( + self, + ctx: &mut MirLowerCtx<'_>, + current: BasicBlockId, + span: MirSpan, + ) -> BasicBlockId { std::mem::forget(self); - ctx.pop_drop_scope_internal(current) + ctx.pop_drop_scope_internal(current, span) } /// It is useful when we want a drop scope is syntaxically closed, but we don't want to execute any drop @@ -529,6 +534,7 @@ impl<'ctx> MirLowerCtx<'ctx> { else { return Ok(None); }; + self.push_fake_read(current, cond_place, expr_id.into()); let (then_target, else_target) = self.pattern_match(current, None, cond_place, *pat)?; self.write_bytes_to_place( @@ -581,7 +587,7 @@ impl<'ctx> MirLowerCtx<'ctx> { self.lower_loop(current, place, *label, expr_id.into(), |this, begin| { let scope = this.push_drop_scope(); if let Some((_, mut current)) = this.lower_expr_as_place(begin, *body, true)? { - current = scope.pop_and_drop(this, current); + current = scope.pop_and_drop(this, current, body.into()); this.set_goto(current, begin, expr_id.into()); } else { scope.pop_assume_dropped(this); @@ -668,6 +674,7 @@ impl<'ctx> MirLowerCtx<'ctx> { else { return Ok(None); }; + self.push_fake_read(current, cond_place, expr_id.into()); let mut end = None; for MatchArm { pat, guard, expr } in arms.iter() { let (then, mut otherwise) = @@ -718,7 +725,8 @@ impl<'ctx> MirLowerCtx<'ctx> { .ok_or(MirLowerError::ContinueWithoutLoop)?, }; let begin = loop_data.begin; - current = self.drop_until_scope(loop_data.drop_scope_index, current); + current = + self.drop_until_scope(loop_data.drop_scope_index, current, expr_id.into()); self.set_goto(current, begin, expr_id.into()); Ok(None) } @@ -757,7 +765,7 @@ impl<'ctx> MirLowerCtx<'ctx> { self.current_loop_blocks.as_ref().unwrap().drop_scope_index, ), }; - current = self.drop_until_scope(drop_scope, current); + current = self.drop_until_scope(drop_scope, current, expr_id.into()); self.set_goto(current, end, expr_id.into()); Ok(None) } @@ -771,7 +779,7 @@ impl<'ctx> MirLowerCtx<'ctx> { return Ok(None); } } - current = self.drop_until_scope(0, current); + current = self.drop_until_scope(0, current, expr_id.into()); self.set_terminator(current, TerminatorKind::Return, expr_id.into()); Ok(None) } @@ -1299,6 +1307,7 @@ impl<'ctx> MirLowerCtx<'ctx> { return Ok(None); }; if matches!(&self.body.exprs[lhs], Expr::Underscore) { + self.push_fake_read_for_operand(current, rhs_op, span); return Ok(Some(current)); } if matches!( @@ -1575,6 +1584,16 @@ impl<'ctx> MirLowerCtx<'ctx> { self.result.basic_blocks[block].statements.push(statement); } + fn push_fake_read(&mut self, block: BasicBlockId, p: Place, span: MirSpan) { + self.push_statement(block, StatementKind::FakeRead(p).with_span(span)); + } + + fn push_fake_read_for_operand(&mut self, block: BasicBlockId, operand: Operand, span: MirSpan) { + if let Operand::Move(p) | Operand::Copy(p) = operand { + self.push_fake_read(block, p, span); + } + } + fn push_assignment( &mut self, block: BasicBlockId, @@ -1733,6 +1752,7 @@ impl<'ctx> MirLowerCtx<'ctx> { return Ok(None); }; current = c; + self.push_fake_read(current, init_place, span); (current, else_block) = self.pattern_match(current, None, init_place, *pat)?; match (else_block, else_branch) { @@ -1760,14 +1780,15 @@ impl<'ctx> MirLowerCtx<'ctx> { } } } - hir_def::hir::Statement::Expr { expr, has_semi: _ } => { + &hir_def::hir::Statement::Expr { expr, has_semi: _ } => { let scope2 = self.push_drop_scope(); - let Some((_, c)) = self.lower_expr_as_place(current, *expr, true)? else { + let Some((p, c)) = self.lower_expr_as_place(current, expr, true)? else { scope2.pop_assume_dropped(self); scope.pop_assume_dropped(self); return Ok(None); }; - current = scope2.pop_and_drop(self, c); + self.push_fake_read(c, p, expr.into()); + current = scope2.pop_and_drop(self, c, expr.into()); } } } @@ -1778,7 +1799,7 @@ impl<'ctx> MirLowerCtx<'ctx> { }; current = c; } - current = scope.pop_and_drop(self, current); + current = scope.pop_and_drop(self, current, span); Ok(Some(current)) } @@ -1858,9 +1879,14 @@ impl<'ctx> MirLowerCtx<'ctx> { } } - fn drop_until_scope(&mut self, scope_index: usize, mut current: BasicBlockId) -> BasicBlockId { + fn drop_until_scope( + &mut self, + scope_index: usize, + mut current: BasicBlockId, + span: MirSpan, + ) -> BasicBlockId { for scope in self.drop_scopes[scope_index..].to_vec().iter().rev() { - self.emit_drop_and_storage_dead_for_scope(scope, &mut current); + self.emit_drop_and_storage_dead_for_scope(scope, &mut current, span); } current } @@ -1876,17 +1902,22 @@ impl<'ctx> MirLowerCtx<'ctx> { } /// Don't call directly - fn pop_drop_scope_internal(&mut self, mut current: BasicBlockId) -> BasicBlockId { + fn pop_drop_scope_internal( + &mut self, + mut current: BasicBlockId, + span: MirSpan, + ) -> BasicBlockId { let scope = self.drop_scopes.pop().unwrap(); - self.emit_drop_and_storage_dead_for_scope(&scope, &mut current); + self.emit_drop_and_storage_dead_for_scope(&scope, &mut current, span); current } fn pop_drop_scope_assert_finished( &mut self, mut current: BasicBlockId, + span: MirSpan, ) -> Result<BasicBlockId> { - current = self.pop_drop_scope_internal(current); + current = self.pop_drop_scope_internal(current, span); if !self.drop_scopes.is_empty() { implementation_error!("Mismatched count between drop scope push and pops"); } @@ -1897,6 +1928,7 @@ impl<'ctx> MirLowerCtx<'ctx> { &mut self, scope: &DropScope, current: &mut Idx<BasicBlock>, + span: MirSpan, ) { for &l in scope.locals.iter().rev() { if !self.result.locals[l].ty.clone().is_copy(self.db, self.owner) { @@ -1904,13 +1936,10 @@ impl<'ctx> MirLowerCtx<'ctx> { self.set_terminator( prev, TerminatorKind::Drop { place: l.into(), target: *current, unwind: None }, - MirSpan::Unknown, + span, ); } - self.push_statement( - *current, - StatementKind::StorageDead(l).with_span(MirSpan::Unknown), - ); + self.push_statement(*current, StatementKind::StorageDead(l).with_span(span)); } } } @@ -1987,7 +2016,7 @@ pub fn mir_body_for_closure_query( |_| true, )?; if let Some(current) = ctx.lower_expr_to_place(*root, return_slot().into(), current)? { - let current = ctx.pop_drop_scope_assert_finished(current)?; + let current = ctx.pop_drop_scope_assert_finished(current, root.into())?; ctx.set_terminator(current, TerminatorKind::Return, (*root).into()); } let mut upvar_map: FxHashMap<LocalId, Vec<(&CapturedItem, usize)>> = FxHashMap::default(); @@ -2081,7 +2110,7 @@ pub fn mir_body_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Result<Arc<Mi pub fn mir_body_recover( _db: &dyn HirDatabase, - _cycle: &[String], + _cycle: &Cycle, _def: &DefWithBodyId, ) -> Result<Arc<MirBody>> { Err(MirLowerError::Loop) @@ -2131,7 +2160,7 @@ pub fn lower_to_mir( ctx.lower_params_and_bindings([].into_iter(), binding_picker)? }; if let Some(current) = ctx.lower_expr_to_place(root_expr, return_slot().into(), current)? { - let current = ctx.pop_drop_scope_assert_finished(current)?; + let current = ctx.pop_drop_scope_assert_finished(current, root_expr.into())?; ctx.set_terminator(current, TerminatorKind::Return, root_expr.into()); } Ok(ctx.result) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs index 270f75ad9..1120bb1c1 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs @@ -284,6 +284,7 @@ impl MirLowerCtx<'_> { ); (current, current_else) = self.pattern_match_binding( id, + *slice, next_place, (*slice).into(), current, @@ -395,6 +396,7 @@ impl MirLowerCtx<'_> { if mode == MatchingMode::Bind { self.pattern_match_binding( *id, + pattern, cond_place, pattern.into(), current, @@ -431,13 +433,14 @@ impl MirLowerCtx<'_> { fn pattern_match_binding( &mut self, id: BindingId, + pat: PatId, cond_place: Place, span: MirSpan, current: BasicBlockId, current_else: Option<BasicBlockId>, ) -> Result<(BasicBlockId, Option<BasicBlockId>)> { let target_place = self.binding_local(id)?; - let mode = self.infer.binding_modes[id]; + let mode = self.infer.binding_modes[pat]; self.push_storage_live(id, current)?; self.push_assignment( current, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs index df16d0d82..8da03eef2 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/monomorphization.rs @@ -9,6 +9,7 @@ use std::mem; +use base_db::salsa::Cycle; use chalk_ir::{ fold::{FallibleTypeFolder, TypeFoldable, TypeSuperFoldable}, ConstData, DebruijnIndex, @@ -248,6 +249,7 @@ impl Filler<'_> { | Rvalue::CopyForDeref(_) => (), }, StatementKind::Deinit(_) + | StatementKind::FakeRead(_) | StatementKind::StorageLive(_) | StatementKind::StorageDead(_) | StatementKind::Nop => (), @@ -299,7 +301,7 @@ pub fn monomorphized_mir_body_query( pub fn monomorphized_mir_body_recover( _: &dyn HirDatabase, - _: &[String], + _: &Cycle, _: &DefWithBodyId, _: &Substitution, _: &Arc<crate::TraitEnvironment>, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs index 0108859ff..a91f90bc2 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs @@ -145,7 +145,7 @@ impl<'a> MirPrettyCtx<'a> { let indent = mem::take(&mut self.indent); let mut ctx = MirPrettyCtx { body: &body, - local_to_binding: body.binding_locals.iter().map(|(it, y)| (*y, it)).collect(), + local_to_binding: body.local_to_binding_map(), result, indent, ..*self @@ -167,7 +167,7 @@ impl<'a> MirPrettyCtx<'a> { } fn new(body: &'a MirBody, hir_body: &'a Body, db: &'a dyn HirDatabase) -> Self { - let local_to_binding = body.binding_locals.iter().map(|(it, y)| (*y, it)).collect(); + let local_to_binding = body.local_to_binding_map(); MirPrettyCtx { body, db, @@ -233,6 +233,11 @@ impl<'a> MirPrettyCtx<'a> { this.place(p); wln!(this, ");"); } + StatementKind::FakeRead(p) => { + w!(this, "FakeRead("); + this.place(p); + wln!(this, ");"); + } StatementKind::Nop => wln!(this, "Nop;"), } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs index 7d19e0a19..6f4aef22d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs @@ -30,6 +30,7 @@ pub(crate) struct TestDB { impl Default for TestDB { fn default() -> Self { let mut this = Self { storage: Default::default(), events: Default::default() }; + this.setup_syntax_context_root(); this.set_expand_proc_attr_macros_with_durability(true, Durability::HIGH); this } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs index d22d0d85c..1446e83fa 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs @@ -269,12 +269,7 @@ fn pat_node( Some(match body_source_map.pat_syntax(pat) { Ok(sp) => { let root = db.parse_or_expand(sp.file_id); - sp.map(|ptr| { - ptr.either( - |it| it.to_node(&root).syntax().clone(), - |it| it.to_node(&root).syntax().clone(), - ) - }) + sp.map(|ptr| ptr.to_node(&root).syntax().clone()) } Err(SyntheticSyntax) => return None, }) @@ -303,12 +298,7 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String { let syntax_ptr = match body_source_map.pat_syntax(pat) { Ok(sp) => { let root = db.parse_or_expand(sp.file_id); - sp.map(|ptr| { - ptr.either( - |it| it.to_node(&root).syntax().clone(), - |it| it.to_node(&root).syntax().clone(), - ) - }) + sp.map(|ptr| ptr.to_node(&root).syntax().clone()) } Err(SyntheticSyntax) => continue, }; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs index bb15ca8c4..28e84e480 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs @@ -9,11 +9,10 @@ use super::visit_module; fn typing_whitespace_inside_a_function_should_not_invalidate_types() { let (mut db, pos) = TestDB::with_position( " - //- /lib.rs - fn foo() -> i32 { - $01 + 1 - } - ", +//- /lib.rs +fn foo() -> i32 { + $01 + 1 +}", ); { let events = db.log_executed(|| { @@ -27,12 +26,11 @@ fn typing_whitespace_inside_a_function_should_not_invalidate_types() { } let new_text = " - fn foo() -> i32 { - 1 - + - 1 - } - "; +fn foo() -> i32 { + 1 + + + 1 +}"; db.set_file_text(pos.file_id, Arc::from(new_text)); @@ -47,3 +45,55 @@ fn typing_whitespace_inside_a_function_should_not_invalidate_types() { assert!(!format!("{events:?}").contains("infer"), "{events:#?}") } } + +#[test] +fn typing_inside_a_function_should_not_invalidate_types_in_another() { + let (mut db, pos) = TestDB::with_position( + " +//- /lib.rs +fn foo() -> f32 { + 1.0 + 2.0 +} +fn bar() -> i32 { + $01 + 1 +} +fn baz() -> i32 { + 1 + 1 +}", + ); + { + let events = db.log_executed(|| { + let module = db.module_for_file(pos.file_id); + let crate_def_map = module.def_map(&db); + visit_module(&db, &crate_def_map, module.local_id, &mut |def| { + db.infer(def); + }); + }); + assert!(format!("{events:?}").contains("infer")) + } + + let new_text = " +fn foo() -> f32 { + 1.0 + 2.0 +} +fn bar() -> i32 { + 53 +} +fn baz() -> i32 { + 1 + 1 +} +"; + + db.set_file_text(pos.file_id, Arc::from(new_text)); + + { + let events = db.log_executed(|| { + let module = db.module_for_file(pos.file_id); + let crate_def_map = module.def_map(&db); + visit_module(&db, &crate_def_map, module.local_id, &mut |def| { + db.infer(def); + }); + }); + assert!(format!("{events:?}").matches("infer").count() == 1, "{events:#?}") + } +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs index 1e6e946a1..d16e0eb01 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs @@ -684,8 +684,7 @@ fn infer_builtin_macros_line() { } "#, expect![[r#" - !0..1 '0': i32 - !0..6 '0asu32': u32 + !0..4 '0u32': u32 63..87 '{ ...!(); }': () 73..74 'x': u32 "#]], @@ -723,8 +722,7 @@ fn infer_builtin_macros_column() { } "#, expect![[r#" - !0..1 '0': i32 - !0..6 '0asu32': u32 + !0..4 '0u32': u32 65..91 '{ ...!(); }': () 75..76 'x': u32 "#]], diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs index 0f5a3e175..7234af2d6 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs @@ -1129,3 +1129,65 @@ fn foo() { "#, ); } + +#[test] +fn generic_alias() { + check_types( + r#" +type Wrap<T> = T; + +enum X { + A { cool: u32, stuff: u32 }, + B, +} + +fn main() { + let wrapped = Wrap::<X>::A { + cool: 100, + stuff: 100, + }; + + if let Wrap::<X>::A { cool, ..} = &wrapped {} + //^^^^ &u32 +} +"#, + ); +} + +#[test] +fn type_mismatch_pat_const_reference() { + check_no_mismatches( + r#" +const TEST_STR: &'static str = "abcd"; + +fn main() { + let s = "abcd"; + match s { + TEST_STR => (), + _ => (), + } +} + + "#, + ); + check( + r#" +struct Foo<T>(T); + +impl<T> Foo<T> { + const TEST_I32_REF: &'static i32 = &3; + const TEST_I32: i32 = 3; +} + +fn main() { + match &6 { + Foo::<i32>::TEST_I32_REF => (), + Foo::<i32>::TEST_I32 => (), + //^^^^^^^^^^^^^^^^^^^^ expected &i32, got i32 + _ => (), + } +} + + "#, + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs index 6ea059065..35079e709 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs @@ -2000,3 +2000,15 @@ fn test() { "#, ); } + +#[test] +fn rustc_test_issue_52437() { + check_types( + r#" + fn main() { + let x = [(); &(&'static: loop { |x| {}; }) as *const _ as usize] + //^ [(); _] + } + "#, + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs index d36b885ec..003ae60e8 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs @@ -2598,6 +2598,34 @@ fn test<T: Trait>() { } #[test] +fn associated_type_in_type_bound() { + check_types( + r#" +//- minicore: deref +fn fb(f: Foo<&u8>) { + f.foobar(); + //^^^^^^^^^^ u8 +} +trait Bar { + fn bar(&self) -> u8; +} +impl Bar for u8 { + fn bar(&self) -> u8 { *self } +} + +struct Foo<F> { + foo: F, +} +impl<F: core::ops::Deref<Target = impl Bar>> Foo<F> { + fn foobar(&self) -> u8 { + self.foo.deref().bar() + } +} +"#, + ) +} + +#[test] fn dyn_trait_through_chalk() { check_types( r#" @@ -4439,42 +4467,42 @@ fn test(v: S<i32>) { fn associated_type_in_argument() { check( r#" - trait A { - fn m(&self) -> i32; - } +trait A { + fn m(&self) -> i32; +} - fn x<T: B>(k: &<T as B>::Ty) { - k.m(); - } +fn x<T: B>(k: &<T as B>::Ty) { + k.m(); +} - struct X; - struct Y; +struct X; +struct Y; - impl A for X { - fn m(&self) -> i32 { - 8 - } +impl A for X { + fn m(&self) -> i32 { + 8 } +} - impl A for Y { - fn m(&self) -> i32 { - 32 - } +impl A for Y { + fn m(&self) -> i32 { + 32 } +} - trait B { - type Ty: A; - } +trait B { + type Ty: A; +} - impl B for u16 { - type Ty = X; - } +impl B for u16 { + type Ty = X; +} - fn ttt() { - let inp = Y; - x::<u16>(&inp); - //^^^^ expected &X, got &Y - } - "#, +fn ttt() { + let inp = Y; + x::<u16>(&inp); + //^^^^ expected &X, got &Y +} +"#, ); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs index 3c7cfbaed..b6bc76bc9 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs @@ -48,18 +48,32 @@ pub struct TraitEnvironment { pub krate: CrateId, pub block: Option<BlockId>, // FIXME make this a BTreeMap - pub(crate) traits_from_clauses: Vec<(Ty, TraitId)>, + traits_from_clauses: Box<[(Ty, TraitId)]>, pub env: chalk_ir::Environment<Interner>, } impl TraitEnvironment { - pub fn empty(krate: CrateId) -> Self { - TraitEnvironment { + pub fn empty(krate: CrateId) -> Arc<Self> { + Arc::new(TraitEnvironment { krate, block: None, - traits_from_clauses: Vec::new(), + traits_from_clauses: Box::default(), env: chalk_ir::Environment::new(Interner), - } + }) + } + + pub fn new( + krate: CrateId, + block: Option<BlockId>, + traits_from_clauses: Box<[(Ty, TraitId)]>, + env: chalk_ir::Environment<Interner>, + ) -> Arc<Self> { + Arc::new(TraitEnvironment { krate, block, traits_from_clauses, env }) + } + + // pub fn with_block(self: &mut Arc<Self>, block: BlockId) { + pub fn with_block(this: &mut Arc<Self>, block: BlockId) { + Arc::make_mut(this).block = Some(block); } pub fn traits_in_scope_from_clauses(&self, ty: Ty) -> impl Iterator<Item = TraitId> + '_ { |