diff options
Diffstat (limited to 'src/tools/rust-analyzer/crates/hir-ty')
36 files changed, 1544 insertions, 447 deletions
diff --git a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml index a1d6835bf..ae837ac6d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml +++ b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml @@ -13,18 +13,20 @@ doctest = false cov-mark = "2.0.0-pre.1" itertools = "0.10.5" arrayvec = "0.7.2" +bitflags = "1.3.2" smallvec = "1.10.0" ena = "0.14.0" tracing = "0.1.35" rustc-hash = "1.1.0" scoped-tls = "1.0.0" -chalk-solve = { version = "0.86.0", default-features = false } -chalk-ir = "0.86.0" -chalk-recursive = { version = "0.86.0", default-features = false } -chalk-derive = "0.86.0" +chalk-solve = { version = "0.88.0", default-features = false } +chalk-ir = "0.88.0" +chalk-recursive = { version = "0.88.0", default-features = false } +chalk-derive = "0.88.0" la-arena = { version = "0.3.0", path = "../../lib/la-arena" } once_cell = "1.15.0" typed-arena = "2.0.1" +rustc_index = { version = "0.0.20221221", package = "hkalbasi-rustc-ap-rustc_index", default-features = false } stdx = { path = "../stdx", version = "0.0.0" } hir-def = { path = "../hir-def", version = "0.0.0" } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs b/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs index 78911d8dc..cbcf8f74c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs @@ -82,11 +82,11 @@ pub(crate) fn autoderef_step( } // FIXME: replace uses of this with Autoderef above -pub fn autoderef<'a>( - db: &'a dyn HirDatabase, +pub fn autoderef( + db: &dyn HirDatabase, env: Arc<TraitEnvironment>, ty: Canonical<Ty>, -) -> impl Iterator<Item = Canonical<Ty>> + 'a { +) -> impl Iterator<Item = Canonical<Ty>> + '_ { let mut table = InferenceTable::new(db, env); let ty = table.instantiate_canonical(ty); let mut autoderef = Autoderef::new(&mut table, ty); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs b/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs index 9ae752556..d5ef0c22d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/builder.rs @@ -142,7 +142,7 @@ impl<D> TyBuilder<D> { match (a.data(Interner), e) { (chalk_ir::GenericArgData::Ty(_), ParamKind::Type) | (chalk_ir::GenericArgData::Const(_), ParamKind::Const(_)) => (), - _ => panic!("Mismatched kinds: {:?}, {:?}, {:?}", a, self.vec, self.param_kinds), + _ => panic!("Mismatched kinds: {a:?}, {:?}, {:?}", self.vec, self.param_kinds), } } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs index 43c3451ca..1c2b8de7f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs @@ -568,6 +568,7 @@ fn well_known_trait_from_lang_attr(name: &str) -> Option<WellKnownTrait> { "sized" => WellKnownTrait::Sized, "unpin" => WellKnownTrait::Unpin, "unsize" => WellKnownTrait::Unsize, + "tuple_trait" => WellKnownTrait::Tuple, _ => return None, }) } @@ -585,6 +586,7 @@ fn lang_attr_from_well_known_trait(attr: WellKnownTrait) -> &'static str { WellKnownTrait::FnOnce => "fn_once", WellKnownTrait::Generator => "generator", WellKnownTrait::Sized => "sized", + WellKnownTrait::Tuple => "tuple_trait", WellKnownTrait::Unpin => "unpin", WellKnownTrait::Unsize => "unsize", } 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 2c0c6e0b8..8df70330f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs @@ -90,14 +90,14 @@ impl Display for ComputedExpr { ComputedExpr::Literal(l) => match l { Literal::Int(x, _) => { if *x >= 10 { - write!(f, "{} ({:#X})", x, x) + write!(f, "{x} ({x:#X})") } else { x.fmt(f) } } Literal::Uint(x, _) => { if *x >= 10 { - write!(f, "{} ({:#X})", x, x) + write!(f, "{x} ({x:#X})") } else { x.fmt(f) } @@ -131,7 +131,7 @@ fn scalar_max(scalar: &Scalar) -> i128 { IntTy::I16 => i16::MAX as i128, IntTy::I32 => i32::MAX as i128, IntTy::I64 => i64::MAX as i128, - IntTy::I128 => i128::MAX as i128, + IntTy::I128 => i128::MAX, }, Scalar::Uint(x) => match x { chalk_ir::UintTy::Usize => usize::MAX as i128, @@ -139,7 +139,7 @@ fn scalar_max(scalar: &Scalar) -> i128 { chalk_ir::UintTy::U16 => u16::MAX as i128, chalk_ir::UintTy::U32 => u32::MAX as i128, chalk_ir::UintTy::U64 => u64::MAX as i128, - chalk_ir::UintTy::U128 => i128::MAX as i128, // ignore too big u128 for now + chalk_ir::UintTy::U128 => i128::MAX, // ignore too big u128 for now }, Scalar::Float(_) => 0, } @@ -351,15 +351,17 @@ pub fn eval_const( .infer .assoc_resolutions_for_expr(expr_id) .ok_or(ConstEvalError::SemanticError("unresolved assoc item"))? + .0 { hir_def::AssocItemId::FunctionId(_) => { Err(ConstEvalError::NotSupported("assoc function")) } + // FIXME use actual impl for trait assoc const hir_def::AssocItemId::ConstId(c) => ctx.db.const_eval(c), hir_def::AssocItemId::TypeAliasId(_) => { Err(ConstEvalError::NotSupported("assoc type alias")) } - } + }; } }; match pr { @@ -402,7 +404,7 @@ pub(crate) fn path_to_const( args_lazy: impl FnOnce() -> Generics, debruijn: DebruijnIndex, ) -> Option<Const> { - match resolver.resolve_path_in_value_ns_fully(db.upcast(), &path) { + match resolver.resolve_path_in_value_ns_fully(db.upcast(), path) { Some(ValueNs::GenericParam(p)) => { let ty = db.const_param_ty(p); let args = args_lazy(); @@ -509,10 +511,10 @@ pub(crate) fn const_eval_query_variant( ) } -pub(crate) fn eval_to_const<'a>( +pub(crate) fn eval_to_const( expr: Idx<Expr>, mode: ParamLoweringMode, - ctx: &mut InferenceContext<'a>, + ctx: &mut InferenceContext<'_>, args: impl FnOnce() -> Generics, debruijn: DebruijnIndex, ) -> Const { 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 b76506f6e..3c930c077 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 @@ -14,7 +14,7 @@ fn check_number(ra_fixture: &str, answer: i128) { match r { ComputedExpr::Literal(Literal::Int(r, _)) => assert_eq!(r, answer), ComputedExpr::Literal(Literal::Uint(r, _)) => assert_eq!(r, answer as u128), - x => panic!("Expected number but found {:?}", x), + x => panic!("Expected number but found {x:?}"), } } @@ -25,7 +25,6 @@ fn eval_goal(ra_fixture: &str) -> Result<ComputedExpr, ConstEvalError> { let scope = &def_map[module_id.local_id].scope; let const_id = scope .declarations() - .into_iter() .find_map(|x| match x { hir_def::ModuleDefId::ConstId(x) => { if db.const_data(x).name.as_ref()?.to_string() == "GOAL" { @@ -126,7 +125,7 @@ fn enums() { assert_eq!(name, "E::A"); assert_eq!(val, 1); } - x => panic!("Expected enum but found {:?}", x), + x => panic!("Expected enum but found {x:?}"), } } 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 932fce835..54b244620 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs @@ -3,20 +3,23 @@ use std::sync::Arc; -use arrayvec::ArrayVec; use base_db::{impl_intern_key, salsa, CrateId, Upcast}; use hir_def::{ - db::DefDatabase, expr::ExprId, BlockId, ConstId, ConstParamId, DefWithBodyId, EnumVariantId, - FunctionId, GenericDefId, ImplId, LifetimeParamId, LocalFieldId, TypeOrConstParamId, VariantId, + db::DefDatabase, + expr::ExprId, + layout::{Layout, LayoutError, TargetDataLayout}, + AdtId, BlockId, ConstId, ConstParamId, DefWithBodyId, EnumVariantId, FunctionId, GenericDefId, + ImplId, LifetimeParamId, LocalFieldId, TypeOrConstParamId, VariantId, }; use la_arena::ArenaMap; +use smallvec::SmallVec; use crate::{ chalk_db, consteval::{ComputedExpr, ConstEvalError}, method_resolution::{InherentImpls, TraitImpls, TyFingerprint}, Binders, CallableDefId, FnDefId, GenericArg, ImplTraitId, InferenceResult, Interner, PolyFnSig, - QuantifiedWhereClause, ReturnTypeImplTraits, TraitRef, Ty, TyDefId, ValueTyDefId, + QuantifiedWhereClause, ReturnTypeImplTraits, Substitution, TraitRef, Ty, TyDefId, ValueTyDefId, }; use hir_expand::name::Name; @@ -57,6 +60,13 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> { #[salsa::invoke(crate::lower::field_types_query)] fn field_types(&self, var: VariantId) -> Arc<ArenaMap<LocalFieldId, Binders<Ty>>>; + #[salsa::invoke(crate::layout::layout_of_adt_query)] + #[salsa::cycle(crate::layout::layout_of_adt_recover)] + fn layout_of_adt(&self, def: AdtId, subst: Substitution) -> Result<Layout, LayoutError>; + + #[salsa::invoke(crate::layout::target_data_layout_query)] + fn target_data_layout(&self, krate: CrateId) -> Arc<TargetDataLayout>; + #[salsa::invoke(crate::lower::callable_item_sig)] fn callable_item_signature(&self, def: CallableDefId) -> PolyFnSig; @@ -92,10 +102,15 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> { fn inherent_impls_in_block(&self, block: BlockId) -> Option<Arc<InherentImpls>>; /// Collects all crates in the dependency graph that have impls for the - /// given fingerprint. This is only used for primitive types; for - /// user-defined types we just look at the crate where the type is defined. - #[salsa::invoke(crate::method_resolution::inherent_impl_crates_query)] - fn inherent_impl_crates(&self, krate: CrateId, fp: TyFingerprint) -> ArrayVec<CrateId, 2>; + /// given fingerprint. This is only used for primitive types and types + /// annotated with `rustc_has_incoherent_inherent_impls`; for other types + /// we just look at the crate where the type is defined. + #[salsa::invoke(crate::method_resolution::incoherent_inherent_impl_crates)] + fn incoherent_inherent_impl_crates( + &self, + krate: CrateId, + fp: TyFingerprint, + ) -> SmallVec<[CrateId; 2]>; #[salsa::invoke(TraitImpls::trait_impls_in_crate_query)] fn trait_impls_in_crate(&self, krate: CrateId) -> Arc<TraitImpls>; 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 d51ad72bd..8b0f051b4 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 @@ -12,16 +12,16 @@ pub(crate) mod usefulness; use chalk_ir::Mutability; use hir_def::{ - adt::VariantData, body::Body, expr::PatId, AdtId, EnumVariantId, HasModule, LocalFieldId, - VariantId, + adt::VariantData, body::Body, expr::PatId, AdtId, EnumVariantId, LocalFieldId, VariantId, }; -use hir_expand::name::{name, Name}; +use hir_expand::name::Name; use stdx::{always, never}; use crate::{ db::HirDatabase, display::{HirDisplay, HirDisplayError, HirFormatter}, infer::BindingMode, + lang_items::is_box, InferenceResult, Interner, Substitution, Ty, TyExt, TyKind, }; @@ -386,7 +386,7 @@ impl HirDisplay for Pat { } subpattern.hir_fmt(f) } - PatKind::LiteralBool { value } => write!(f, "{}", value), + PatKind::LiteralBool { value } => write!(f, "{value}"), PatKind::Or { pats } => f.write_joined(pats.iter(), " | "), } } @@ -405,13 +405,6 @@ where } } -fn is_box(adt: AdtId, db: &dyn HirDatabase) -> bool { - let owned_box = name![owned_box].to_smol_str(); - let krate = adt.module(db.upcast()).krate(); - let box_adt = db.lang_item(krate, owned_box).and_then(|it| it.as_struct()).map(AdtId::from); - Some(adt) == box_adt -} - pub(crate) trait PatternFoldable: Sized { fn fold_with<F: PatternFolder>(&self, folder: &mut F) -> Self { self.super_fold_with(folder) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/deconstruct_pat.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/deconstruct_pat.rs index 47d60fc41..d130827a7 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/deconstruct_pat.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/deconstruct_pat.rs @@ -372,7 +372,7 @@ impl Constructor { hir_def::AdtId::UnionId(id) => id.into(), } } - _ => panic!("bad constructor {:?} for adt {:?}", self, adt), + _ => panic!("bad constructor {self:?} for adt {adt:?}"), } } 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 a22a4b170..66e813eed 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -16,7 +16,7 @@ use hir_def::{ path::{Path, PathKind}, type_ref::{ConstScalar, TraitBoundModifier, TypeBound, TypeRef}, visibility::Visibility, - HasModule, ItemContainerId, Lookup, ModuleId, TraitId, + HasModule, ItemContainerId, Lookup, ModuleDefId, ModuleId, TraitId, }; use hir_expand::{hygiene::Hygiene, name::Name}; use itertools::Itertools; @@ -35,9 +35,27 @@ use crate::{ TraitRefExt, Ty, TyExt, TyKind, WhereClause, }; +pub trait HirWrite: fmt::Write { + fn start_location_link(&mut self, location: ModuleDefId); + fn end_location_link(&mut self); +} + +// String will ignore link metadata +impl HirWrite for String { + fn start_location_link(&mut self, _: ModuleDefId) {} + + fn end_location_link(&mut self) {} +} + +// `core::Formatter` will ignore metadata +impl HirWrite for fmt::Formatter<'_> { + fn start_location_link(&mut self, _: ModuleDefId) {} + fn end_location_link(&mut self) {} +} + pub struct HirFormatter<'a> { pub db: &'a dyn HirDatabase, - fmt: &'a mut dyn fmt::Write, + fmt: &'a mut dyn HirWrite, buf: String, curr_size: usize, pub(crate) max_size: Option<usize>, @@ -45,6 +63,16 @@ pub struct HirFormatter<'a> { display_target: DisplayTarget, } +impl HirFormatter<'_> { + fn start_location_link(&mut self, location: ModuleDefId) { + self.fmt.start_location_link(location); + } + + fn end_location_link(&mut self) { + self.fmt.end_location_link(); + } +} + pub trait HirDisplay { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError>; @@ -148,13 +176,13 @@ impl<'a> HirFormatter<'a> { let mut first = true; for e in iter { if !first { - write!(self, "{}", sep)?; + write!(self, "{sep}")?; } first = false; // Abbreviate multiple omitted types with a single ellipsis. if self.should_truncate() { - return write!(self, "{}", TYPE_HINT_TRUNCATION); + return write!(self, "{TYPE_HINT_TRUNCATION}"); } e.hir_fmt(self)?; @@ -245,12 +273,9 @@ pub struct HirDisplayWrapper<'a, T> { display_target: DisplayTarget, } -impl<'a, T> fmt::Display for HirDisplayWrapper<'a, T> -where - T: HirDisplay, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.t.hir_fmt(&mut HirFormatter { +impl<T: HirDisplay> HirDisplayWrapper<'_, T> { + pub fn write_to<F: HirWrite>(&self, f: &mut F) -> Result<(), HirDisplayError> { + self.t.hir_fmt(&mut HirFormatter { db: self.db, fmt: f, buf: String::with_capacity(20), @@ -258,7 +283,16 @@ where max_size: self.max_size, omit_verbose_types: self.omit_verbose_types, display_target: self.display_target, - }) { + }) + } +} + +impl<'a, T> fmt::Display for HirDisplayWrapper<'a, T> +where + T: HirDisplay, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.write_to(f) { Ok(()) => Ok(()), Err(HirDisplayError::FmtError) => Err(fmt::Error), Err(HirDisplayError::DisplaySourceCodeError(_)) => { @@ -286,7 +320,7 @@ impl<T: HirDisplay + Internable> HirDisplay for Interned<T> { impl HirDisplay for ProjectionTy { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { if f.should_truncate() { - return write!(f, "{}", TYPE_HINT_TRUNCATION); + return write!(f, "{TYPE_HINT_TRUNCATION}"); } let trait_ref = self.trait_ref(f.db); @@ -308,7 +342,7 @@ impl HirDisplay for ProjectionTy { impl HirDisplay for OpaqueTy { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { if f.should_truncate() { - return write!(f, "{}", TYPE_HINT_TRUNCATION); + return write!(f, "{TYPE_HINT_TRUNCATION}"); } self.substitution.at(Interner, 0).hir_fmt(f) @@ -351,7 +385,7 @@ impl HirDisplay for BoundVar { impl HirDisplay for Ty { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { if f.should_truncate() { - return write!(f, "{}", TYPE_HINT_TRUNCATION); + return write!(f, "{TYPE_HINT_TRUNCATION}"); } match self.kind(Interner) { @@ -530,6 +564,7 @@ impl HirDisplay for Ty { } } TyKind::Adt(AdtId(def_id), parameters) => { + f.start_location_link((*def_id).into()); match f.display_target { DisplayTarget::Diagnostics | DisplayTarget::Test => { let name = match *def_id { @@ -537,7 +572,7 @@ impl HirDisplay for Ty { hir_def::AdtId::UnionId(it) => f.db.union_data(it).name.clone(), hir_def::AdtId::EnumId(it) => f.db.enum_data(it).name.clone(), }; - write!(f, "{}", name)?; + write!(f, "{name}")?; } DisplayTarget::SourceCode { module_id } => { if let Some(path) = find_path::find_path( @@ -546,7 +581,7 @@ impl HirDisplay for Ty { module_id, false, ) { - write!(f, "{}", path)?; + write!(f, "{path}")?; } else { return Err(HirDisplayError::DisplaySourceCodeError( DisplaySourceCodeError::PathNotFound, @@ -554,6 +589,7 @@ impl HirDisplay for Ty { } } } + f.end_location_link(); if parameters.len(Interner) > 0 { let parameters_to_write = if f.display_target.is_source_code() @@ -701,7 +737,7 @@ impl HirDisplay for Ty { if sig.params().is_empty() { write!(f, "||")?; } else if f.should_truncate() { - write!(f, "|{}|", TYPE_HINT_TRUNCATION)?; + write!(f, "|{TYPE_HINT_TRUNCATION}|")?; } else { write!(f, "|")?; f.write_joined(sig.params(), ", ")?; @@ -892,7 +928,7 @@ pub fn write_bounds_like_dyn_trait_with_prefix( default_sized: SizedByDefault, f: &mut HirFormatter<'_>, ) -> Result<(), HirDisplayError> { - write!(f, "{}", prefix)?; + write!(f, "{prefix}")?; if !predicates.is_empty() || predicates.is_empty() && matches!(default_sized, SizedByDefault::Sized { .. }) { @@ -1020,7 +1056,7 @@ fn fmt_trait_ref( use_as: bool, ) -> Result<(), HirDisplayError> { if f.should_truncate() { - return write!(f, "{}", TYPE_HINT_TRUNCATION); + return write!(f, "{TYPE_HINT_TRUNCATION}"); } tr.self_type_parameter(Interner).hir_fmt(f)?; @@ -1047,7 +1083,7 @@ impl HirDisplay for TraitRef { impl HirDisplay for WhereClause { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { if f.should_truncate() { - return write!(f, "{}", TYPE_HINT_TRUNCATION); + return write!(f, "{TYPE_HINT_TRUNCATION}"); } match self { @@ -1098,7 +1134,6 @@ impl HirDisplay for LifetimeData { write!(f, "{}", param_data.name) } LifetimeData::Static => write!(f, "'static"), - LifetimeData::Empty(_) => Ok(()), LifetimeData::Erased => Ok(()), LifetimeData::Phantom(_, _) => Ok(()), } @@ -1162,7 +1197,7 @@ impl HirDisplay for TypeRef { hir_def::type_ref::Mutability::Shared => "*const ", hir_def::type_ref::Mutability::Mut => "*mut ", }; - write!(f, "{}", mutability)?; + write!(f, "{mutability}")?; inner.hir_fmt(f)?; } TypeRef::Reference(inner, lifetime, mutability) => { @@ -1174,13 +1209,13 @@ impl HirDisplay for TypeRef { if let Some(lifetime) = lifetime { write!(f, "{} ", lifetime.name)?; } - write!(f, "{}", mutability)?; + write!(f, "{mutability}")?; inner.hir_fmt(f)?; } TypeRef::Array(inner, len) => { write!(f, "[")?; inner.hir_fmt(f)?; - write!(f, "; {}]", len)?; + write!(f, "; {len}]")?; } TypeRef::Slice(inner) => { write!(f, "[")?; @@ -1197,7 +1232,7 @@ impl HirDisplay for TypeRef { for index in 0..function_parameters.len() { let (param_name, param_type) = &function_parameters[index]; if let Some(name) = param_name { - write!(f, "{}: ", name)?; + write!(f, "{name}: ")?; } param_type.hir_fmt(f)?; @@ -1373,7 +1408,7 @@ impl HirDisplay for hir_def::path::GenericArg { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> { match self { hir_def::path::GenericArg::Type(ty) => ty.hir_fmt(f), - hir_def::path::GenericArg::Const(c) => write!(f, "{}", c), + hir_def::path::GenericArg::Const(c) => write!(f, "{c}"), hir_def::path::GenericArg::Lifetime(lifetime) => write!(f, "{}", lifetime.name), } } 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 0b3c23f57..6b59f1c20 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -19,10 +19,11 @@ use std::sync::Arc; use chalk_ir::{cast::Cast, ConstValue, DebruijnIndex, Mutability, Safety, Scalar, TypeFlags}; use hir_def::{ body::Body, - builtin_type::BuiltinType, + builtin_type::{BuiltinInt, BuiltinType, BuiltinUint}, data::{ConstData, StaticData}, - expr::{BindingAnnotation, ExprId, PatId}, + expr::{BindingAnnotation, ExprId, ExprOrPatId, PatId}, lang_item::LangItemTarget, + layout::Integer, path::{path, Path}, resolver::{HasResolver, ResolveValueResult, Resolver, TypeNs, ValueNs}, type_ref::TypeRef, @@ -33,7 +34,7 @@ use hir_expand::name::{name, Name}; use itertools::Either; use la_arena::ArenaMap; use rustc_hash::FxHashMap; -use stdx::{always, impl_from}; +use stdx::always; use crate::{ db::HirDatabase, fold_tys, fold_tys_and_consts, infer::coerce::CoerceMany, @@ -70,8 +71,26 @@ pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<Infer DefWithBodyId::StaticId(s) => ctx.collect_static(&db.static_data(s)), DefWithBodyId::VariantId(v) => { ctx.return_ty = TyBuilder::builtin(match db.enum_data(v.parent).variant_body_type() { - Either::Left(builtin) => BuiltinType::Int(builtin), - Either::Right(builtin) => BuiltinType::Uint(builtin), + hir_def::layout::IntegerType::Pointer(signed) => match signed { + true => BuiltinType::Int(BuiltinInt::Isize), + false => BuiltinType::Uint(BuiltinUint::Usize), + }, + hir_def::layout::IntegerType::Fixed(size, signed) => match signed { + true => BuiltinType::Int(match size { + Integer::I8 => BuiltinInt::I8, + Integer::I16 => BuiltinInt::I16, + Integer::I32 => BuiltinInt::I32, + Integer::I64 => BuiltinInt::I64, + Integer::I128 => BuiltinInt::I128, + }), + false => BuiltinType::Uint(match size { + Integer::I8 => BuiltinUint::U8, + Integer::I16 => BuiltinUint::U16, + Integer::I32 => BuiltinUint::U32, + Integer::I64 => BuiltinUint::U64, + Integer::I128 => BuiltinUint::U128, + }), + }, }); } } @@ -101,13 +120,6 @@ pub(crate) fn normalize(db: &dyn HirDatabase, owner: DefWithBodyId, ty: Ty) -> T table.resolve_completely(ty_with_vars) } -#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] -enum ExprOrPatId { - ExprId(ExprId), - PatId(PatId), -} -impl_from!(ExprId, PatId for ExprOrPatId); - /// Binding modes inferred for patterns. /// <https://doc.rust-lang.org/reference/patterns.html#binding-modes> #[derive(Copy, Clone, Debug, Eq, PartialEq)] @@ -189,6 +201,8 @@ pub(crate) type InferResult<T> = Result<InferOk<T>, TypeError>; #[derive(Debug, PartialEq, Eq, Clone)] pub enum InferenceDiagnostic { NoSuchField { expr: ExprId }, + PrivateField { expr: ExprId, field: FieldId }, + PrivateAssocItem { id: ExprOrPatId, item: AssocItemId }, BreakOutsideOfLoop { expr: ExprId, is_break: bool }, MismatchedArgCount { call_expr: ExprId, expected: usize, found: usize }, } @@ -330,7 +344,7 @@ pub struct InferenceResult { /// For each struct literal or pattern, records the variant it resolves to. variant_resolutions: FxHashMap<ExprOrPatId, VariantId>, /// For each associated item record what it resolves to - assoc_resolutions: FxHashMap<ExprOrPatId, AssocItemId>, + assoc_resolutions: FxHashMap<ExprOrPatId, (AssocItemId, Substitution)>, pub diagnostics: Vec<InferenceDiagnostic>, pub type_of_expr: ArenaMap<ExprId, Ty>, /// For each pattern record the type it resolves to. @@ -360,11 +374,11 @@ impl InferenceResult { pub fn variant_resolution_for_pat(&self, id: PatId) -> Option<VariantId> { self.variant_resolutions.get(&id.into()).copied() } - pub fn assoc_resolutions_for_expr(&self, id: ExprId) -> Option<AssocItemId> { - self.assoc_resolutions.get(&id.into()).copied() + pub fn assoc_resolutions_for_expr(&self, id: ExprId) -> Option<(AssocItemId, Substitution)> { + self.assoc_resolutions.get(&id.into()).cloned() } - pub fn assoc_resolutions_for_pat(&self, id: PatId) -> Option<AssocItemId> { - self.assoc_resolutions.get(&id.into()).copied() + pub fn assoc_resolutions_for_pat(&self, id: PatId) -> Option<(AssocItemId, Substitution)> { + self.assoc_resolutions.get(&id.into()).cloned() } pub fn type_mismatch_for_expr(&self, expr: ExprId) -> Option<&TypeMismatch> { self.type_mismatches.get(&expr.into()) @@ -484,7 +498,7 @@ impl<'a> InferenceContext<'a> { result: InferenceResult::default(), table: unify::InferenceTable::new(db, trait_env.clone()), trait_env, - return_ty: TyKind::Error.intern(Interner), // set in collect_fn_signature + return_ty: TyKind::Error.intern(Interner), // set in collect_* calls resume_yield_tys: None, db, owner, @@ -498,6 +512,8 @@ impl<'a> InferenceContext<'a> { fn resolve_all(self) -> InferenceResult { let InferenceContext { mut table, mut result, .. } = self; + table.fallback_if_possible(); + // FIXME resolve obligations as well (use Guidance if necessary) table.resolve_obligations_as_possible(); @@ -516,6 +532,9 @@ impl<'a> InferenceContext<'a> { for (_, subst) in result.method_resolutions.values_mut() { *subst = table.resolve_completely(subst.clone()); } + for (_, subst) in result.assoc_resolutions.values_mut() { + *subst = table.resolve_completely(subst.clone()); + } for adjustment in result.expr_adjustments.values_mut().flatten() { adjustment.target = table.resolve_completely(adjustment.target.clone()); } @@ -537,8 +556,20 @@ impl<'a> InferenceContext<'a> { let data = self.db.function_data(func); let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver) .with_impl_trait_mode(ImplTraitLoweringMode::Param); - let param_tys = + let mut param_tys = data.params.iter().map(|(_, type_ref)| ctx.lower_ty(type_ref)).collect::<Vec<_>>(); + // Check if function contains a va_list, if it does then we append it to the parameter types + // that are collected from the function data + if data.is_varargs() { + let va_list_ty = match self.resolve_va_list() { + Some(va_list) => TyBuilder::adt(self.db, va_list) + .fill_with_defaults(self.db, || self.table.new_type_var()) + .build(), + None => self.err_ty(), + }; + + param_tys.push(va_list_ty) + } for (ty, pat) in param_tys.into_iter().zip(self.body.params.iter()) { let ty = self.insert_type_vars(ty); let ty = self.normalize_associated_types_in(ty); @@ -551,14 +582,17 @@ impl<'a> InferenceContext<'a> { } else { &*data.ret_type }; - let return_ty = self.make_ty_with_mode(return_ty, ImplTraitLoweringMode::Opaque); - self.return_ty = return_ty; - if let Some(rpits) = self.db.return_type_impl_traits(func) { + let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver) + .with_impl_trait_mode(ImplTraitLoweringMode::Opaque); + let return_ty = ctx.lower_ty(return_ty); + let return_ty = self.insert_type_vars(return_ty); + + let return_ty = if let Some(rpits) = self.db.return_type_impl_traits(func) { // RPIT opaque types use substitution of their parent function. let fn_placeholders = TyBuilder::placeholder_subst(self.db, func); - self.return_ty = fold_tys( - self.return_ty.clone(), + fold_tys( + return_ty, |ty, _| { let opaque_ty_id = match ty.kind(Interner) { TyKind::OpaqueType(opaque_ty_id, _) => *opaque_ty_id, @@ -579,14 +613,18 @@ impl<'a> InferenceContext<'a> { let (var_predicate, binders) = predicate .substitute(Interner, &var_subst) .into_value_and_skipped_binders(); - always!(binders.len(Interner) == 0); // quantified where clauses not yet handled + always!(binders.is_empty(Interner)); // quantified where clauses not yet handled self.push_obligation(var_predicate.cast(Interner)); } var }, DebruijnIndex::INNERMOST, - ); - } + ) + } else { + return_ty + }; + + self.return_ty = self.normalize_associated_types_in(return_ty); } fn infer_body(&mut self) { @@ -609,8 +647,8 @@ impl<'a> InferenceContext<'a> { self.result.variant_resolutions.insert(id, variant); } - fn write_assoc_resolution(&mut self, id: ExprOrPatId, item: AssocItemId) { - self.result.assoc_resolutions.insert(id, item); + fn write_assoc_resolution(&mut self, id: ExprOrPatId, item: AssocItemId, subs: Substitution) { + self.result.assoc_resolutions.insert(id, (item, subs)); } fn write_pat_ty(&mut self, pat: PatId, ty: Ty) { @@ -621,23 +659,14 @@ impl<'a> InferenceContext<'a> { self.result.diagnostics.push(diagnostic); } - fn make_ty_with_mode( - &mut self, - type_ref: &TypeRef, - impl_trait_mode: ImplTraitLoweringMode, - ) -> Ty { + fn make_ty(&mut self, type_ref: &TypeRef) -> Ty { // FIXME use right resolver for block - let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver) - .with_impl_trait_mode(impl_trait_mode); + let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver); let ty = ctx.lower_ty(type_ref); let ty = self.insert_type_vars(ty); self.normalize_associated_types_in(ty) } - fn make_ty(&mut self, type_ref: &TypeRef) -> Ty { - self.make_ty_with_mode(type_ref, ImplTraitLoweringMode::Disallowed) - } - fn err_ty(&self) -> Ty { self.result.standard_types.unknown.clone() } @@ -656,7 +685,7 @@ impl<'a> InferenceContext<'a> { } } - /// Replaces Ty::Unknown by a new type var, so we can maybe still infer it. + /// Replaces `Ty::Error` by a new type var, so we can maybe still infer it. fn insert_type_vars_shallow(&mut self, ty: Ty) -> Ty { match ty.kind(Interner) { TyKind::Error => self.table.new_type_var(), @@ -983,6 +1012,11 @@ impl<'a> InferenceContext<'a> { let trait_ = self.resolve_ops_index()?; self.db.trait_data(trait_).associated_type_by_name(&name![Output]) } + + fn resolve_va_list(&self) -> Option<AdtId> { + let struct_ = self.resolve_lang_item(name![va_list])?.as_struct()?; + Some(struct_.into()) + } } /// When inferring an expression, we propagate downward whatever type hint we 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 b1f4de826..8f9cdac37 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 @@ -1,13 +1,12 @@ //! Type inference for expressions. use std::{ - collections::hash_map::Entry, iter::{repeat, repeat_with}, mem, }; use chalk_ir::{ - cast::Cast, fold::Shift, DebruijnIndex, GenericArgData, Mutability, TyVariableKind, + cast::Cast, fold::Shift, DebruijnIndex, GenericArgData, Mutability, TyKind, TyVariableKind, }; use hir_def::{ expr::{ @@ -35,8 +34,8 @@ use crate::{ primitive::{self, UintTy}, static_lifetime, to_chalk_trait_id, utils::{generics, Generics}, - AdtId, Binders, CallableDefId, FnPointer, FnSig, FnSubst, Interner, Rawness, Scalar, - Substitution, TraitRef, Ty, TyBuilder, TyExt, TyKind, + Adjust, Adjustment, AdtId, AutoBorrow, Binders, CallableDefId, FnPointer, FnSig, FnSubst, + Interner, Rawness, Scalar, Substitution, TraitRef, Ty, TyBuilder, TyExt, }; use super::{ @@ -152,11 +151,20 @@ impl<'a> InferenceContext<'a> { .1 } Expr::TryBlock { body } => { - self.with_breakable_ctx(BreakableKind::Border, self.err_ty(), None, |this| { - let _inner = this.infer_expr(*body, expected); + // The type that is returned from the try block + let try_ty = self.table.new_type_var(); + if let Some(ty) = expected.only_has_type(&mut self.table) { + self.unify(&try_ty, &ty); + } + + // The ok-ish type that is expected from the last expression + let ok_ty = self.resolve_associated_type(try_ty.clone(), self.resolve_ops_try_ok()); + + self.with_breakable_ctx(BreakableKind::Block, ok_ty.clone(), None, |this| { + this.infer_expr(*body, &Expectation::has_type(ok_ty)); }); - // FIXME should be std::result::Result<{inner}, _> - self.err_ty() + + try_ty } Expr::Async { body } => { let ret_ty = self.table.new_type_var(); @@ -326,6 +334,7 @@ impl<'a> InferenceContext<'a> { let (param_tys, ret_ty) = match res { Some(res) => { let adjustments = auto_deref_adjust_steps(&derefs); + // FIXME: Handle call adjustments for Fn/FnMut self.write_expr_adj(*callee, adjustments); res } @@ -465,6 +474,12 @@ impl<'a> InferenceContext<'a> { TyKind::Error.intern(Interner) } } + Expr::Yeet { expr } => { + if let &Some(expr) = expr { + self.infer_expr_inner(expr, &Expectation::None); + } + TyKind::Never.intern(Interner) + } Expr::RecordLit { path, fields, spread, .. } => { let (ty, def_id) = self.resolve_variant(path.as_deref(), false); if let Some(variant) = def_id { @@ -506,6 +521,7 @@ impl<'a> InferenceContext<'a> { let receiver_ty = self.infer_expr_inner(*expr, &Expectation::none()); let mut autoderef = Autoderef::new(&mut self.table, receiver_ty); + let mut private_field = None; let ty = autoderef.by_ref().find_map(|(derefed_ty, _)| { let (field_id, parameters) = match derefed_ty.kind(Interner) { TyKind::Tuple(_, substs) => { @@ -532,13 +548,8 @@ impl<'a> InferenceContext<'a> { let is_visible = self.db.field_visibilities(field_id.parent)[field_id.local_id] .is_visible_from(self.db.upcast(), self.resolver.module()); if !is_visible { - // Write down the first field resolution even if it is not visible - // This aids IDE features for private fields like goto def and in - // case of autoderef finding an applicable field, this will be - // overwritten in a following cycle - if let Entry::Vacant(entry) = self.result.field_resolutions.entry(tgt_expr) - { - entry.insert(field_id); + if private_field.is_none() { + private_field = Some(field_id); } return None; } @@ -557,7 +568,17 @@ impl<'a> InferenceContext<'a> { let ty = self.normalize_associated_types_in(ty); ty } - _ => self.err_ty(), + _ => { + // Write down the first private field resolution if we found no field + // This aids IDE features for private fields like goto def + if let Some(field) = private_field { + self.result.field_resolutions.insert(tgt_expr, field); + self.result + .diagnostics + .push(InferenceDiagnostic::PrivateField { expr: tgt_expr, field }); + } + self.err_ty() + } }; ty } @@ -940,7 +961,7 @@ impl<'a> InferenceContext<'a> { Expr::RecordLit { path, fields, .. } => { let subs = fields.iter().map(|f| (f.name.clone(), f.expr)); - self.infer_record_pat_like(path.as_deref(), &rhs_ty, (), lhs.into(), subs) + self.infer_record_pat_like(path.as_deref(), &rhs_ty, (), lhs, subs) } Expr::Underscore => rhs_ty.clone(), _ => { @@ -1018,14 +1039,38 @@ impl<'a> InferenceContext<'a> { self.infer_expr_coerce(rhs, &Expectation::has_type(rhs_ty.clone())); let ret_ty = match method_ty.callable_sig(self.db) { - Some(sig) => sig.ret().clone(), + Some(sig) => { + let p_left = &sig.params()[0]; + if matches!(op, BinaryOp::CmpOp(..) | BinaryOp::Assignment { .. }) { + if let &TyKind::Ref(mtbl, _, _) = p_left.kind(Interner) { + self.write_expr_adj( + lhs, + vec![Adjustment { + kind: Adjust::Borrow(AutoBorrow::Ref(mtbl)), + target: p_left.clone(), + }], + ); + } + } + let p_right = &sig.params()[1]; + if matches!(op, BinaryOp::CmpOp(..)) { + if let &TyKind::Ref(mtbl, _, _) = p_right.kind(Interner) { + self.write_expr_adj( + rhs, + vec![Adjustment { + kind: Adjust::Borrow(AutoBorrow::Ref(mtbl)), + target: p_right.clone(), + }], + ); + } + } + sig.ret().clone() + } None => self.err_ty(), }; let ret_ty = self.normalize_associated_types_in(ret_ty); - // FIXME: record autoref adjustments - // use knowledge of built-in binary ops, which can sometimes help inference if let Some(builtin_rhs) = self.builtin_binary_op_rhs_expectation(op, lhs_ty.clone()) { self.unify(&builtin_rhs, &rhs_ty); @@ -1122,20 +1167,26 @@ impl<'a> InferenceContext<'a> { let traits_in_scope = self.resolver.traits_in_scope(self.db.upcast()); let resolved = method_resolution::lookup_method( - &canonicalized_receiver.value, self.db, + &canonicalized_receiver.value, self.trait_env.clone(), &traits_in_scope, VisibleFromModule::Filter(self.resolver.module()), method_name, ); let (receiver_ty, method_ty, substs) = match resolved { - Some((adjust, func)) => { + Some((adjust, func, visible)) => { 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, generic_args); self.write_expr_adj(receiver, adjustments); self.write_method_resolution(tgt_expr, func, substs.clone()); + if !visible { + self.push_diagnostic(InferenceDiagnostic::PrivateAssocItem { + id: tgt_expr.into(), + item: func.into(), + }) + } (ty, self.db.value_ty(func.into()), substs) } None => ( @@ -1309,7 +1360,7 @@ impl<'a> InferenceContext<'a> { ty, c, ParamLoweringMode::Placeholder, - || generics(this.db.upcast(), (&this.resolver).generic_def().unwrap()), + || generics(this.db.upcast(), this.resolver.generic_def().unwrap()), DebruijnIndex::INNERMOST, ) }, 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 53259d66d..f154dac8e 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 @@ -153,7 +153,7 @@ impl<'a> InferenceContext<'a> { ) -> Ty { let mut expected = self.resolve_ty_shallow(expected); - if is_non_ref_pat(&self.body, pat) { + if 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()); @@ -220,7 +220,7 @@ impl<'a> InferenceContext<'a> { ), Pat::Record { path: p, args: fields, ellipsis: _ } => { let subs = fields.iter().map(|f| (f.name.clone(), f.pat)); - self.infer_record_pat_like(p.as_deref(), &expected, default_bm, pat.into(), subs) + self.infer_record_pat_like(p.as_deref(), &expected, default_bm, pat, subs) } Pat::Path(path) => { // FIXME use correct resolver for the surrounding expression 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 ebe9d6fb5..8bd17c0f3 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 @@ -7,13 +7,15 @@ use hir_def::{ AdtId, AssocItemId, EnumVariantId, ItemContainerId, Lookup, }; use hir_expand::name::Name; +use stdx::never; use crate::{ builder::ParamKind, consteval, method_resolution::{self, VisibleFromModule}, utils::generics, - Interner, Substitution, TraitRefExt, Ty, TyBuilder, TyExt, TyKind, ValueTyDefId, + InferenceDiagnostic, Interner, Substitution, TraitRefExt, Ty, TyBuilder, TyExt, TyKind, + ValueTyDefId, }; use super::{ExprOrPatId, InferenceContext, TraitRef}; @@ -212,7 +214,7 @@ impl<'a> InferenceContext<'a> { AssocItemId::TypeAliasId(_) => unreachable!(), }; - self.write_assoc_resolution(id, item); + self.write_assoc_resolution(id, item, trait_ref.substitution.clone()); Some((def, Some(trait_ref.substitution))) } @@ -233,7 +235,8 @@ impl<'a> InferenceContext<'a> { let canonical_ty = self.canonicalize(ty.clone()); let traits_in_scope = self.resolver.traits_in_scope(self.db.upcast()); - method_resolution::iterate_method_candidates( + let mut not_visible = None; + let res = method_resolution::iterate_method_candidates( &canonical_ty.value, self.db, self.table.trait_env.clone(), @@ -241,7 +244,7 @@ impl<'a> InferenceContext<'a> { VisibleFromModule::Filter(self.resolver.module()), Some(name), method_resolution::LookupMode::Path, - move |_ty, item| { + |_ty, item, visible| { let (def, container) = match item { AssocItemId::FunctionId(f) => { (ValueNs::FunctionId(f), f.lookup(self.db.upcast()).container) @@ -259,7 +262,7 @@ impl<'a> InferenceContext<'a> { let impl_self_ty = self.db.impl_self_ty(impl_id).substitute(Interner, &impl_substs); self.unify(&impl_self_ty, &ty); - Some(impl_substs) + impl_substs } ItemContainerId::TraitId(trait_) => { // we're picking this method @@ -268,15 +271,32 @@ impl<'a> InferenceContext<'a> { .fill_with_inference_vars(&mut self.table) .build(); self.push_obligation(trait_ref.clone().cast(Interner)); - Some(trait_ref.substitution) + trait_ref.substitution + } + ItemContainerId::ModuleId(_) | ItemContainerId::ExternBlockId(_) => { + never!("assoc item contained in module/extern block"); + return None; } - ItemContainerId::ModuleId(_) | ItemContainerId::ExternBlockId(_) => None, }; - self.write_assoc_resolution(id, item); - Some((def, substs)) + if visible { + Some((def, item, Some(substs), true)) + } else { + if not_visible.is_none() { + not_visible = Some((def, item, Some(substs), false)); + } + None + } }, - ) + ); + let res = res.or(not_visible); + if let Some((_, item, Some(ref substs), visible)) = res { + self.write_assoc_resolution(id, item, substs.clone()); + if !visible { + self.push_diagnostic(InferenceDiagnostic::PrivateAssocItem { id, item }) + } + } + res.map(|(def, _, substs, _)| (def, substs)) } fn resolve_enum_variant_on_ty( 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 12f45f00f..e7ddd1591 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 @@ -1,6 +1,6 @@ //! Unification and canonicalization logic. -use std::{fmt, mem, sync::Arc}; +use std::{fmt, iter, mem, sync::Arc}; use chalk_ir::{ cast::Cast, fold::TypeFoldable, interner::HasInterner, zip::Zip, CanonicalVarKind, FloatTy, @@ -128,9 +128,13 @@ pub(crate) fn unify( )) } -#[derive(Copy, Clone, Debug)] -pub(crate) struct TypeVariableData { - diverging: bool, +bitflags::bitflags! { + #[derive(Default)] + pub(crate) struct TypeVariableFlags: u8 { + const DIVERGING = 1 << 0; + const INTEGER = 1 << 1; + const FLOAT = 1 << 2; + } } type ChalkInferenceTable = chalk_solve::infer::InferenceTable<Interner>; @@ -140,14 +144,14 @@ pub(crate) struct InferenceTable<'a> { pub(crate) db: &'a dyn HirDatabase, pub(crate) trait_env: Arc<TraitEnvironment>, var_unification_table: ChalkInferenceTable, - type_variable_table: Vec<TypeVariableData>, + type_variable_table: Vec<TypeVariableFlags>, pending_obligations: Vec<Canonicalized<InEnvironment<Goal>>>, } pub(crate) struct InferenceTableSnapshot { var_table_snapshot: chalk_solve::infer::InferenceSnapshot<Interner>, pending_obligations: Vec<Canonicalized<InEnvironment<Goal>>>, - type_variable_table_snapshot: Vec<TypeVariableData>, + type_variable_table_snapshot: Vec<TypeVariableFlags>, } impl<'a> InferenceTable<'a> { @@ -169,19 +173,19 @@ impl<'a> InferenceTable<'a> { /// result. pub(super) fn propagate_diverging_flag(&mut self) { for i in 0..self.type_variable_table.len() { - if !self.type_variable_table[i].diverging { + if !self.type_variable_table[i].contains(TypeVariableFlags::DIVERGING) { continue; } let v = InferenceVar::from(i as u32); let root = self.var_unification_table.inference_var_root(v); if let Some(data) = self.type_variable_table.get_mut(root.index() as usize) { - data.diverging = true; + *data |= TypeVariableFlags::DIVERGING; } } } pub(super) fn set_diverging(&mut self, iv: InferenceVar, diverging: bool) { - self.type_variable_table[iv.index() as usize].diverging = diverging; + self.type_variable_table[iv.index() as usize].set(TypeVariableFlags::DIVERGING, diverging); } fn fallback_value(&self, iv: InferenceVar, kind: TyVariableKind) -> Ty { @@ -189,7 +193,7 @@ impl<'a> InferenceTable<'a> { _ if self .type_variable_table .get(iv.index() as usize) - .map_or(false, |data| data.diverging) => + .map_or(false, |data| data.contains(TypeVariableFlags::DIVERGING)) => { TyKind::Never } @@ -247,10 +251,8 @@ impl<'a> InferenceTable<'a> { } fn extend_type_variable_table(&mut self, to_index: usize) { - self.type_variable_table.extend( - (0..1 + to_index - self.type_variable_table.len()) - .map(|_| TypeVariableData { diverging: false }), - ); + let count = to_index - self.type_variable_table.len() + 1; + self.type_variable_table.extend(iter::repeat(TypeVariableFlags::default()).take(count)); } fn new_var(&mut self, kind: TyVariableKind, diverging: bool) -> Ty { @@ -258,7 +260,15 @@ impl<'a> InferenceTable<'a> { // Chalk might have created some type variables for its own purposes that we don't know about... self.extend_type_variable_table(var.index() as usize); assert_eq!(var.index() as usize, self.type_variable_table.len() - 1); - self.type_variable_table[var.index() as usize].diverging = diverging; + let flags = self.type_variable_table.get_mut(var.index() as usize).unwrap(); + if diverging { + *flags |= TypeVariableFlags::DIVERGING; + } + if matches!(kind, TyVariableKind::Integer) { + *flags |= TypeVariableFlags::INTEGER; + } else if matches!(kind, TyVariableKind::Float) { + *flags |= TypeVariableFlags::FLOAT; + } var.to_ty_with_kind(Interner, kind) } @@ -340,6 +350,51 @@ impl<'a> InferenceTable<'a> { self.resolve_with_fallback(t, &|_, _, d, _| d) } + /// Apply a fallback to unresolved scalar types. Integer type variables and float type + /// variables are replaced with i32 and f64, respectively. + /// + /// This method is only intended to be called just before returning inference results (i.e. in + /// `InferenceContext::resolve_all()`). + /// + /// FIXME: This method currently doesn't apply fallback to unconstrained general type variables + /// whereas rustc replaces them with `()` or `!`. + pub(super) fn fallback_if_possible(&mut self) { + let int_fallback = TyKind::Scalar(Scalar::Int(IntTy::I32)).intern(Interner); + let float_fallback = TyKind::Scalar(Scalar::Float(FloatTy::F64)).intern(Interner); + + let scalar_vars: Vec<_> = self + .type_variable_table + .iter() + .enumerate() + .filter_map(|(index, flags)| { + let kind = if flags.contains(TypeVariableFlags::INTEGER) { + TyVariableKind::Integer + } else if flags.contains(TypeVariableFlags::FLOAT) { + TyVariableKind::Float + } else { + return None; + }; + + // FIXME: This is not really the nicest way to get `InferenceVar`s. Can we get them + // without directly constructing them from `index`? + let var = InferenceVar::from(index as u32).to_ty(Interner, kind); + Some(var) + }) + .collect(); + + for var in scalar_vars { + let maybe_resolved = self.resolve_ty_shallow(&var); + if let TyKind::InferenceVar(_, kind) = maybe_resolved.kind(Interner) { + let fallback = match kind { + TyVariableKind::Integer => &int_fallback, + TyVariableKind::Float => &float_fallback, + TyVariableKind::General => unreachable!(), + }; + self.unify(&var, fallback); + } + } + } + /// Unify two relatable values (e.g. `Ty`) and register new trait goals that arise from that. pub(crate) fn unify<T: ?Sized + Zip<Interner>>(&mut self, ty1: &T, ty2: &T) -> bool { let result = match self.try_unify(ty1, ty2) { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/interner.rs b/src/tools/rust-analyzer/crates/hir-ty/src/interner.rs index ca76e08fd..441503a30 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/interner.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/interner.rs @@ -143,7 +143,7 @@ impl chalk_ir::interner::Interner for Interner { fn debug_goal(goal: &Goal<Interner>, fmt: &mut fmt::Formatter<'_>) -> Option<fmt::Result> { let goal_data = goal.data(Interner); - Some(write!(fmt, "{:?}", goal_data)) + Some(write!(fmt, "{goal_data:?}")) } fn debug_goals( @@ -228,7 +228,7 @@ impl chalk_ir::interner::Interner for Interner { Interned::new(InternedWrapper(chalk_ir::TyData { kind, flags })) } - fn ty_data<'a>(self, ty: &'a Self::InternedType) -> &'a chalk_ir::TyData<Self> { + fn ty_data(self, ty: &Self::InternedType) -> &chalk_ir::TyData<Self> { &ty.0 } @@ -236,10 +236,7 @@ impl chalk_ir::interner::Interner for Interner { Interned::new(InternedWrapper(lifetime)) } - fn lifetime_data<'a>( - self, - lifetime: &'a Self::InternedLifetime, - ) -> &'a chalk_ir::LifetimeData<Self> { + fn lifetime_data(self, lifetime: &Self::InternedLifetime) -> &chalk_ir::LifetimeData<Self> { &lifetime.0 } @@ -247,7 +244,7 @@ impl chalk_ir::interner::Interner for Interner { Interned::new(InternedWrapper(constant)) } - fn const_data<'a>(self, constant: &'a Self::InternedConst) -> &'a chalk_ir::ConstData<Self> { + fn const_data(self, constant: &Self::InternedConst) -> &chalk_ir::ConstData<Self> { &constant.0 } @@ -267,10 +264,10 @@ impl chalk_ir::interner::Interner for Interner { parameter } - fn generic_arg_data<'a>( + fn generic_arg_data( self, - parameter: &'a Self::InternedGenericArg, - ) -> &'a chalk_ir::GenericArgData<Self> { + parameter: &Self::InternedGenericArg, + ) -> &chalk_ir::GenericArgData<Self> { parameter } @@ -285,11 +282,11 @@ impl chalk_ir::interner::Interner for Interner { data.into_iter().collect() } - fn goal_data<'a>(self, goal: &'a Self::InternedGoal) -> &'a GoalData<Self> { + fn goal_data(self, goal: &Self::InternedGoal) -> &GoalData<Self> { goal } - fn goals_data<'a>(self, goals: &'a Self::InternedGoals) -> &'a [Goal<Interner>] { + fn goals_data(self, goals: &Self::InternedGoals) -> &[Goal<Interner>] { goals } @@ -300,10 +297,7 @@ impl chalk_ir::interner::Interner for Interner { Ok(Interned::new(InternedWrapper(data.into_iter().collect::<Result<_, _>>()?))) } - fn substitution_data<'a>( - self, - substitution: &'a Self::InternedSubstitution, - ) -> &'a [GenericArg] { + fn substitution_data(self, substitution: &Self::InternedSubstitution) -> &[GenericArg] { &substitution.as_ref().0 } @@ -314,10 +308,10 @@ impl chalk_ir::interner::Interner for Interner { data } - fn program_clause_data<'a>( + fn program_clause_data( self, - clause: &'a Self::InternedProgramClause, - ) -> &'a chalk_ir::ProgramClauseData<Self> { + clause: &Self::InternedProgramClause, + ) -> &chalk_ir::ProgramClauseData<Self> { clause } @@ -328,10 +322,10 @@ impl chalk_ir::interner::Interner for Interner { Ok(Interned::new(InternedWrapper(data.into_iter().collect::<Result<_, _>>()?))) } - fn program_clauses_data<'a>( + fn program_clauses_data( self, - clauses: &'a Self::InternedProgramClauses, - ) -> &'a [chalk_ir::ProgramClause<Self>] { + clauses: &Self::InternedProgramClauses, + ) -> &[chalk_ir::ProgramClause<Self>] { clauses } @@ -342,10 +336,10 @@ impl chalk_ir::interner::Interner for Interner { Ok(Interned::new(InternedWrapper(data.into_iter().collect::<Result<_, _>>()?))) } - fn quantified_where_clauses_data<'a>( + fn quantified_where_clauses_data( self, - clauses: &'a Self::InternedQuantifiedWhereClauses, - ) -> &'a [chalk_ir::QuantifiedWhereClause<Self>] { + clauses: &Self::InternedQuantifiedWhereClauses, + ) -> &[chalk_ir::QuantifiedWhereClause<Self>] { clauses } @@ -356,10 +350,10 @@ impl chalk_ir::interner::Interner for Interner { Ok(Interned::new(InternedWrapper(data.into_iter().collect::<Result<_, _>>()?))) } - fn variable_kinds_data<'a>( + fn variable_kinds_data( self, - parameter_kinds: &'a Self::InternedVariableKinds, - ) -> &'a [chalk_ir::VariableKind<Self>] { + parameter_kinds: &Self::InternedVariableKinds, + ) -> &[chalk_ir::VariableKind<Self>] { ¶meter_kinds.as_ref().0 } @@ -370,10 +364,10 @@ impl chalk_ir::interner::Interner for Interner { Ok(Interned::new(InternedWrapper(data.into_iter().collect::<Result<_, _>>()?))) } - fn canonical_var_kinds_data<'a>( + fn canonical_var_kinds_data( self, - canonical_var_kinds: &'a Self::InternedCanonicalVarKinds, - ) -> &'a [chalk_ir::CanonicalVarKind<Self>] { + canonical_var_kinds: &Self::InternedCanonicalVarKinds, + ) -> &[chalk_ir::CanonicalVarKind<Self>] { canonical_var_kinds } @@ -384,10 +378,10 @@ impl chalk_ir::interner::Interner for Interner { data.into_iter().collect() } - fn constraints_data<'a>( + fn constraints_data( self, - constraints: &'a Self::InternedConstraints, - ) -> &'a [chalk_ir::InEnvironment<chalk_ir::Constraint<Self>>] { + constraints: &Self::InternedConstraints, + ) -> &[chalk_ir::InEnvironment<chalk_ir::Constraint<Self>>] { constraints } fn debug_closure_id( @@ -410,10 +404,7 @@ impl chalk_ir::interner::Interner for Interner { Ok(Interned::new(InternedWrapper(data.into_iter().collect::<Result<_, _>>()?))) } - fn variances_data<'a>( - self, - variances: &'a Self::InternedVariances, - ) -> &'a [chalk_ir::Variance] { + fn variances_data(self, variances: &Self::InternedVariances) -> &[chalk_ir::Variance] { variances } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lang_items.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lang_items.rs new file mode 100644 index 000000000..afc54e729 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lang_items.rs @@ -0,0 +1,20 @@ +//! Functions to detect special lang items + +use hir_def::{AdtId, HasModule}; +use hir_expand::name; + +use crate::db::HirDatabase; + +pub fn is_box(adt: AdtId, db: &dyn HirDatabase) -> bool { + let owned_box = name![owned_box].to_smol_str(); + let krate = adt.module(db.upcast()).krate(); + let box_adt = db.lang_item(krate, owned_box).and_then(|it| it.as_struct()).map(AdtId::from); + Some(adt) == box_adt +} + +pub fn is_unsafe_cell(adt: AdtId, db: &dyn HirDatabase) -> bool { + let owned_box = name![unsafe_cell].to_smol_str(); + let krate = adt.module(db.upcast()).krate(); + let box_adt = db.lang_item(krate, owned_box).and_then(|it| it.as_struct()).map(AdtId::from); + Some(adt) == box_adt +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs new file mode 100644 index 000000000..7a1cca314 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs @@ -0,0 +1,279 @@ +//! Compute the binary representation of a type + +use std::sync::Arc; + +use base_db::CrateId; +use chalk_ir::{AdtId, TyKind}; +use hir_def::{ + layout::{ + Abi, FieldsShape, Integer, Layout, LayoutCalculator, LayoutError, Primitive, ReprOptions, + RustcEnumVariantIdx, Scalar, Size, StructKind, TargetDataLayout, Variants, WrappingRange, + }, + LocalFieldId, +}; +use stdx::never; + +use crate::{db::HirDatabase, Interner, Substitution, Ty}; + +use self::adt::struct_variant_idx; +pub use self::{ + adt::{layout_of_adt_query, layout_of_adt_recover}, + target::target_data_layout_query, +}; + +macro_rules! user_error { + ($x: expr) => { + return Err(LayoutError::UserError(format!($x))) + }; +} + +mod adt; +mod target; + +struct LayoutCx<'a> { + db: &'a dyn HirDatabase, + krate: CrateId, +} + +impl LayoutCalculator for LayoutCx<'_> { + type TargetDataLayoutRef = Arc<TargetDataLayout>; + + fn delay_bug(&self, txt: &str) { + never!("{}", txt); + } + + fn current_data_layout(&self) -> Arc<TargetDataLayout> { + self.db.target_data_layout(self.krate) + } +} + +fn scalar_unit(dl: &TargetDataLayout, value: Primitive) -> Scalar { + Scalar::Initialized { value, valid_range: WrappingRange::full(value.size(dl)) } +} + +fn scalar(dl: &TargetDataLayout, value: Primitive) -> Layout { + Layout::scalar(dl, scalar_unit(dl, value)) +} + +pub fn layout_of_ty(db: &dyn HirDatabase, ty: &Ty, krate: CrateId) -> Result<Layout, LayoutError> { + let cx = LayoutCx { db, krate }; + let dl = &*cx.current_data_layout(); + Ok(match ty.kind(Interner) { + TyKind::Adt(AdtId(def), subst) => db.layout_of_adt(*def, subst.clone())?, + TyKind::Scalar(s) => match s { + chalk_ir::Scalar::Bool => Layout::scalar( + dl, + Scalar::Initialized { + value: Primitive::Int(Integer::I8, false), + valid_range: WrappingRange { start: 0, end: 1 }, + }, + ), + chalk_ir::Scalar::Char => Layout::scalar( + dl, + Scalar::Initialized { + value: Primitive::Int(Integer::I32, false), + valid_range: WrappingRange { start: 0, end: 0x10FFFF }, + }, + ), + chalk_ir::Scalar::Int(i) => scalar( + dl, + Primitive::Int( + match i { + chalk_ir::IntTy::Isize => dl.ptr_sized_integer(), + chalk_ir::IntTy::I8 => Integer::I8, + chalk_ir::IntTy::I16 => Integer::I16, + chalk_ir::IntTy::I32 => Integer::I32, + chalk_ir::IntTy::I64 => Integer::I64, + chalk_ir::IntTy::I128 => Integer::I128, + }, + true, + ), + ), + chalk_ir::Scalar::Uint(i) => scalar( + dl, + Primitive::Int( + match i { + chalk_ir::UintTy::Usize => dl.ptr_sized_integer(), + chalk_ir::UintTy::U8 => Integer::I8, + chalk_ir::UintTy::U16 => Integer::I16, + chalk_ir::UintTy::U32 => Integer::I32, + chalk_ir::UintTy::U64 => Integer::I64, + chalk_ir::UintTy::U128 => Integer::I128, + }, + false, + ), + ), + chalk_ir::Scalar::Float(f) => scalar( + dl, + match f { + chalk_ir::FloatTy::F32 => Primitive::F32, + chalk_ir::FloatTy::F64 => Primitive::F64, + }, + ), + }, + TyKind::Tuple(len, tys) => { + let kind = if *len == 0 { StructKind::AlwaysSized } else { StructKind::MaybeUnsized }; + + let fields = tys + .iter(Interner) + .map(|k| layout_of_ty(db, k.assert_ty_ref(Interner), krate)) + .collect::<Result<Vec<_>, _>>()?; + let fields = fields.iter().collect::<Vec<_>>(); + let fields = fields.iter().collect::<Vec<_>>(); + cx.univariant(dl, &fields, &ReprOptions::default(), kind).ok_or(LayoutError::Unknown)? + } + TyKind::Array(element, count) => { + let count = match count.data(Interner).value { + chalk_ir::ConstValue::Concrete(c) => match c.interned { + hir_def::type_ref::ConstScalar::Int(x) => x as u64, + hir_def::type_ref::ConstScalar::UInt(x) => x as u64, + hir_def::type_ref::ConstScalar::Unknown => { + user_error!("unknown const generic parameter") + } + _ => user_error!("mismatched type of const generic parameter"), + }, + _ => return Err(LayoutError::HasPlaceholder), + }; + let element = layout_of_ty(db, element, krate)?; + let size = element.size.checked_mul(count, dl).ok_or(LayoutError::SizeOverflow)?; + + let abi = if count != 0 && matches!(element.abi, Abi::Uninhabited) { + Abi::Uninhabited + } else { + Abi::Aggregate { sized: true } + }; + + let largest_niche = if count != 0 { element.largest_niche } else { None }; + + Layout { + variants: Variants::Single { index: struct_variant_idx() }, + fields: FieldsShape::Array { stride: element.size, count }, + abi, + largest_niche, + align: element.align, + size, + } + } + TyKind::Slice(element) => { + let element = layout_of_ty(db, element, krate)?; + Layout { + variants: Variants::Single { index: struct_variant_idx() }, + fields: FieldsShape::Array { stride: element.size, count: 0 }, + abi: Abi::Aggregate { sized: false }, + largest_niche: None, + align: element.align, + size: Size::ZERO, + } + } + // Potentially-wide pointers. + TyKind::Ref(_, _, pointee) | TyKind::Raw(_, pointee) => { + let mut data_ptr = scalar_unit(dl, Primitive::Pointer); + if matches!(ty.kind(Interner), TyKind::Ref(..)) { + data_ptr.valid_range_mut().start = 1; + } + + // let pointee = tcx.normalize_erasing_regions(param_env, pointee); + // if pointee.is_sized(tcx.at(DUMMY_SP), param_env) { + // return Ok(tcx.intern_layout(LayoutS::scalar(cx, data_ptr))); + // } + + let unsized_part = struct_tail_erasing_lifetimes(db, pointee.clone()); + let metadata = match unsized_part.kind(Interner) { + TyKind::Slice(_) | TyKind::Str => { + scalar_unit(dl, Primitive::Int(dl.ptr_sized_integer(), false)) + } + TyKind::Dyn(..) => { + let mut vtable = scalar_unit(dl, Primitive::Pointer); + vtable.valid_range_mut().start = 1; + vtable + } + _ => { + // pointee is sized + return Ok(Layout::scalar(dl, data_ptr)); + } + }; + + // Effectively a (ptr, meta) tuple. + 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::Dyn(_) | TyKind::Foreign(_) => { + let mut unit = layout_of_unit(&cx, dl)?; + match unit.abi { + Abi::Aggregate { ref mut sized } => *sized = false, + _ => user_error!("bug"), + } + unit + } + TyKind::Function(_) => { + let mut ptr = scalar_unit(dl, Primitive::Pointer); + ptr.valid_range_mut().start = 1; + Layout::scalar(dl, ptr) + } + TyKind::Closure(_, _) + | TyKind::OpaqueType(_, _) + | TyKind::Generator(_, _) + | TyKind::GeneratorWitness(_, _) => return Err(LayoutError::NotImplemented), + TyKind::AssociatedType(_, _) + | TyKind::Error + | TyKind::Alias(_) + | TyKind::Placeholder(_) + | TyKind::BoundVar(_) + | TyKind::InferenceVar(_, _) => return Err(LayoutError::HasPlaceholder), + }) +} + +fn layout_of_unit(cx: &LayoutCx<'_>, dl: &TargetDataLayout) -> Result<Layout, LayoutError> { + cx.univariant::<RustcEnumVariantIdx, &&Layout>( + dl, + &[], + &ReprOptions::default(), + StructKind::AlwaysSized, + ) + .ok_or(LayoutError::Unknown) +} + +fn struct_tail_erasing_lifetimes(db: &dyn HirDatabase, pointee: Ty) -> Ty { + match pointee.kind(Interner) { + TyKind::Adt(AdtId(adt), subst) => match adt { + &hir_def::AdtId::StructId(i) => { + let data = db.struct_data(i); + let mut it = data.variant_data.fields().iter().rev(); + match it.next() { + Some((f, _)) => field_ty(db, i.into(), f, subst), + None => pointee, + } + } + _ => pointee, + }, + _ => pointee, + } +} + +fn field_ty( + db: &dyn HirDatabase, + def: hir_def::VariantId, + fd: LocalFieldId, + subst: &Substitution, +) -> Ty { + db.field_types(def)[fd].clone().substitute(Interner, subst) +} + +#[cfg(test)] +mod tests; 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 new file mode 100644 index 000000000..23166a5a5 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs @@ -0,0 +1,134 @@ +//! Compute the binary representation of structs, unions and enums + +use std::ops::Bound; + +use hir_def::{ + adt::VariantData, + layout::{Integer, IntegerExt, Layout, LayoutCalculator, LayoutError, RustcEnumVariantIdx}, + AdtId, EnumVariantId, HasModule, LocalEnumVariantId, VariantId, +}; +use la_arena::RawIdx; +use smallvec::SmallVec; + +use crate::{db::HirDatabase, lang_items::is_unsafe_cell, layout::field_ty, Substitution}; + +use super::{layout_of_ty, LayoutCx}; + +pub(crate) fn struct_variant_idx() -> RustcEnumVariantIdx { + RustcEnumVariantIdx(LocalEnumVariantId::from_raw(RawIdx::from(0))) +} + +pub fn layout_of_adt_query( + db: &dyn HirDatabase, + def: AdtId, + subst: Substitution, +) -> Result<Layout, LayoutError> { + let cx = LayoutCx { db, krate: def.module(db.upcast()).krate() }; + let dl = cx.current_data_layout(); + let handle_variant = |def: VariantId, var: &VariantData| { + var.fields() + .iter() + .map(|(fd, _)| layout_of_ty(db, &field_ty(db, def, fd, &subst), cx.krate)) + .collect::<Result<Vec<_>, _>>() + }; + let (variants, is_enum, is_union, repr) = match def { + AdtId::StructId(s) => { + let data = db.struct_data(s); + let mut r = SmallVec::<[_; 1]>::new(); + r.push(handle_variant(s.into(), &data.variant_data)?); + (r, false, false, data.repr.unwrap_or_default()) + } + AdtId::UnionId(id) => { + let data = db.union_data(id); + let mut r = SmallVec::new(); + r.push(handle_variant(id.into(), &data.variant_data)?); + (r, false, true, data.repr.unwrap_or_default()) + } + AdtId::EnumId(e) => { + let data = db.enum_data(e); + let r = data + .variants + .iter() + .map(|(idx, v)| { + handle_variant( + EnumVariantId { parent: e, local_id: idx }.into(), + &v.variant_data, + ) + }) + .collect::<Result<SmallVec<_>, _>>()?; + (r, true, false, data.repr.unwrap_or_default()) + } + }; + let variants = + variants.iter().map(|x| x.iter().collect::<Vec<_>>()).collect::<SmallVec<[_; 1]>>(); + let variants = variants.iter().map(|x| x.iter().collect()).collect(); + if is_union { + cx.layout_of_union(&repr, &variants).ok_or(LayoutError::Unknown) + } else { + cx.layout_of_struct_or_enum( + &repr, + &variants, + is_enum, + is_unsafe_cell(def, db), + layout_scalar_valid_range(db, def), + |min, max| Integer::repr_discr(&dl, &repr, min, max).unwrap_or((Integer::I8, false)), + variants.iter_enumerated().filter_map(|(id, _)| { + let AdtId::EnumId(e) = def else { return None }; + let d = match db + .const_eval_variant(EnumVariantId { parent: e, local_id: id.0 }) + .ok()? + { + crate::consteval::ComputedExpr::Literal(l) => match l { + hir_def::expr::Literal::Int(i, _) => i, + hir_def::expr::Literal::Uint(i, _) => i as i128, + _ => return None, + }, + _ => return None, + }; + Some((id, d)) + }), + // FIXME: The current code for niche-filling relies on variant indices + // instead of actual discriminants, so enums with + // explicit discriminants (RFC #2363) would misbehave and we should disable + // niche optimization for them. + // The code that do it in rustc: + // repr.inhibit_enum_layout_opt() || def + // .variants() + // .iter_enumerated() + // .any(|(i, v)| v.discr != ty::VariantDiscr::Relative(i.as_u32())) + repr.inhibit_enum_layout_opt(), + !is_enum + && variants + .iter() + .next() + .and_then(|x| x.last().map(|x| x.is_unsized())) + .unwrap_or(true), + ) + .ok_or(LayoutError::SizeOverflow) + } +} + +fn layout_scalar_valid_range(db: &dyn HirDatabase, def: AdtId) -> (Bound<u128>, Bound<u128>) { + let attrs = db.attrs(def.into()); + let get = |name| { + let attr = attrs.by_key(name).tt_values(); + for tree in attr { + if let Some(x) = tree.token_trees.first() { + if let Ok(x) = x.to_string().parse() { + return Bound::Included(x); + } + } + } + Bound::Unbounded + }; + (get("rustc_layout_scalar_valid_range_start"), get("rustc_layout_scalar_valid_range_end")) +} + +pub fn layout_of_adt_recover( + _: &dyn HirDatabase, + _: &[String], + _: &AdtId, + _: &Substitution, +) -> Result<Layout, LayoutError> { + user_error!("infinite sized recursive type"); +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/target.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/target.rs new file mode 100644 index 000000000..37b831652 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/target.rs @@ -0,0 +1,36 @@ +//! Target dependent parameters needed for layouts + +use std::sync::Arc; + +use base_db::CrateId; +use hir_def::layout::{Endian, Size, TargetDataLayout}; + +use crate::db::HirDatabase; + +pub fn target_data_layout_query(db: &dyn HirDatabase, krate: CrateId) -> Arc<TargetDataLayout> { + let crate_graph = db.crate_graph(); + let target_layout = &crate_graph[krate].target_layout; + let cfg_options = &crate_graph[krate].cfg_options; + Arc::new( + target_layout + .as_ref() + .and_then(|it| TargetDataLayout::parse_from_llvm_datalayout_string(it).ok()) + .unwrap_or_else(|| { + let endian = match cfg_options.get_cfg_values("target_endian").next() { + Some(x) if x.as_str() == "big" => Endian::Big, + _ => Endian::Little, + }; + let pointer_size = Size::from_bytes( + match cfg_options.get_cfg_values("target_pointer_width").next() { + Some(x) => match x.as_str() { + "16" => 2, + "32" => 4, + _ => 8, + }, + _ => 8, + }, + ); + TargetDataLayout { endian, pointer_size, ..TargetDataLayout::default() } + }), + ) +} 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 new file mode 100644 index 000000000..53838cf41 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs @@ -0,0 +1,208 @@ +use base_db::fixture::WithFixture; +use chalk_ir::{AdtId, TyKind}; +use hir_def::{ + db::DefDatabase, + layout::{Layout, LayoutError}, +}; + +use crate::{test_db::TestDB, Interner, Substitution}; + +use super::layout_of_ty; + +fn eval_goal(ra_fixture: &str, minicore: &str) -> Result<Layout, LayoutError> { + // using unstable cargo features failed, fall back to using plain rustc + let mut cmd = std::process::Command::new("rustc"); + cmd.args(["-Z", "unstable-options", "--print", "target-spec-json"]).env("RUSTC_BOOTSTRAP", "1"); + let output = cmd.output().unwrap(); + assert!(output.status.success(), "{}", output.status); + let stdout = String::from_utf8(output.stdout).unwrap(); + let target_data_layout = + stdout.split_once(r#""data-layout": ""#).unwrap().1.split_once('"').unwrap().0.to_owned(); + + let ra_fixture = format!( + "{minicore}//- /main.rs crate:test target_data_layout:{target_data_layout}\n{ra_fixture}", + ); + + let (db, file_id) = TestDB::with_single_file(&ra_fixture); + let module_id = db.module_for_file(file_id); + let def_map = module_id.def_map(&db); + let scope = &def_map[module_id.local_id].scope; + let adt_id = scope + .declarations() + .find_map(|x| match x { + hir_def::ModuleDefId::AdtId(x) => { + let name = match x { + hir_def::AdtId::StructId(x) => db.struct_data(x).name.to_smol_str(), + hir_def::AdtId::UnionId(x) => db.union_data(x).name.to_smol_str(), + hir_def::AdtId::EnumId(x) => db.enum_data(x).name.to_smol_str(), + }; + (name == "Goal").then_some(x) + } + _ => None, + }) + .unwrap(); + let goal_ty = TyKind::Adt(AdtId(adt_id), Substitution::empty(Interner)).intern(Interner); + layout_of_ty(&db, &goal_ty, module_id.krate()) +} + +#[track_caller] +fn check_size_and_align(ra_fixture: &str, minicore: &str, size: u64, align: u64) { + let l = eval_goal(ra_fixture, minicore).unwrap(); + assert_eq!(l.size.bytes(), size); + assert_eq!(l.align.abi.bytes(), align); +} + +#[track_caller] +fn check_fail(ra_fixture: &str, e: LayoutError) { + let r = eval_goal(ra_fixture, ""); + assert_eq!(r, Err(e)); +} + +macro_rules! size_and_align { + (minicore: $($x:tt),*;$($t:tt)*) => { + { + #[allow(dead_code)] + $($t)* + check_size_and_align( + stringify!($($t)*), + &format!("//- minicore: {}\n", stringify!($($x),*)), + ::std::mem::size_of::<Goal>() as u64, + ::std::mem::align_of::<Goal>() as u64, + ); + } + }; + ($($t:tt)*) => { + { + #[allow(dead_code)] + $($t)* + check_size_and_align( + stringify!($($t)*), + "", + ::std::mem::size_of::<Goal>() as u64, + ::std::mem::align_of::<Goal>() as u64, + ); + } + }; +} + +#[test] +fn hello_world() { + size_and_align! { + struct Goal(i32); + } +} + +#[test] +fn field_order_optimization() { + size_and_align! { + struct Goal(u8, i32, u8); + } + size_and_align! { + #[repr(C)] + struct Goal(u8, i32, u8); + } +} + +#[test] +fn recursive() { + size_and_align! { + struct Goal { + left: &'static Goal, + right: &'static Goal, + } + } + size_and_align! { + struct BoxLike<T: ?Sized>(*mut T); + struct Goal(BoxLike<Goal>); + } + check_fail( + r#"struct Goal(Goal);"#, + LayoutError::UserError("infinite sized recursive type".to_string()), + ); + check_fail( + r#" + struct Foo<T>(Foo<T>); + struct Goal(Foo<i32>); + "#, + LayoutError::UserError("infinite sized recursive type".to_string()), + ); +} + +#[test] +fn generic() { + size_and_align! { + struct Pair<A, B>(A, B); + struct Goal(Pair<Pair<i32, u8>, i64>); + } + size_and_align! { + struct X<const N: usize> { + field1: [i32; N], + field2: [u8; N], + } + struct Goal(X<1000>); + } +} + +#[test] +fn enums() { + size_and_align! { + enum Goal { + Quit, + Move { x: i32, y: i32 }, + ChangeColor(i32, i32, i32), + } + } +} + +#[test] +fn primitives() { + size_and_align! { + struct Goal(i32, i128, isize, usize, f32, f64, bool, char); + } +} + +#[test] +fn tuple() { + size_and_align! { + struct Goal((), (i32, u64, bool)); + } +} + +#[test] +fn non_zero() { + size_and_align! { + minicore: non_zero, option; + use core::num::NonZeroU8; + struct Goal(Option<NonZeroU8>); + } +} + +#[test] +fn niche_optimization() { + size_and_align! { + minicore: option; + struct Goal(Option<&'static i32>); + } + size_and_align! { + minicore: option; + struct Goal(Option<Option<bool>>); + } +} + +#[test] +fn enums_with_discriminants() { + size_and_align! { + enum Goal { + A = 1000, + B = 2000, + C = 3000, + } + } + size_and_align! { + enum Goal { + A = 254, + B, + C, // implicitly becomes 256, so we need two bytes + } + } +} 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 39514fc44..cbe6873c7 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -27,6 +27,8 @@ pub mod display; pub mod method_resolution; pub mod primitive; pub mod traits; +pub mod layout; +pub mod lang_items; #[cfg(test)] mod tests; @@ -38,7 +40,7 @@ use std::sync::Arc; use chalk_ir::{ fold::{Shift, TypeFoldable}, interner::HasInterner, - NoSolution, UniverseIndex, + NoSolution, }; use hir_def::{expr::ExprId, type_ref::Rawness, TypeOrConstParamId}; use hir_expand::name; @@ -46,7 +48,9 @@ use itertools::Either; use traits::FnTrait; use utils::Generics; -use crate::{consteval::unknown_const, db::HirDatabase, utils::generics}; +use crate::{ + consteval::unknown_const, db::HirDatabase, infer::unify::InferenceTable, utils::generics, +}; pub use autoderef::autoderef; pub use builder::{ParamKind, TyBuilder}; @@ -511,7 +515,7 @@ where let mut error_replacer = ErrorReplacer { vars: 0 }; let value = match t.clone().try_fold_with(&mut error_replacer, DebruijnIndex::INNERMOST) { Ok(t) => t, - Err(_) => panic!("Encountered unbound or inference vars in {:?}", t), + Err(_) => panic!("Encountered unbound or inference vars in {t:?}"), }; let kinds = (0..error_replacer.vars).map(|_| { chalk_ir::CanonicalVarKind::new( @@ -531,54 +535,31 @@ pub fn callable_sig_from_fnonce( let fn_once_trait = FnTrait::FnOnce.get_id(db, krate)?; let output_assoc_type = db.trait_data(fn_once_trait).associated_type_by_name(&name![Output])?; + let mut table = InferenceTable::new(db, env.clone()); let b = TyBuilder::trait_ref(db, fn_once_trait); if b.remaining() != 2 { return None; } - let fn_once = b.push(self_ty.clone()).fill_with_bound_vars(DebruijnIndex::INNERMOST, 0).build(); - let kinds = fn_once - .substitution - .iter(Interner) - .skip(1) - .map(|x| { - let vk = match x.data(Interner) { - chalk_ir::GenericArgData::Ty(_) => { - chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General) - } - chalk_ir::GenericArgData::Lifetime(_) => chalk_ir::VariableKind::Lifetime, - chalk_ir::GenericArgData::Const(c) => { - chalk_ir::VariableKind::Const(c.data(Interner).ty.clone()) - } - }; - chalk_ir::WithKind::new(vk, UniverseIndex::ROOT) - }) - .collect::<Vec<_>>(); - - // FIXME: chalk refuses to solve `<Self as FnOnce<^0.0>>::Output == ^0.1`, so we first solve - // `<Self as FnOnce<^0.0>>` and then replace `^0.0` with the concrete argument tuple. - let trait_env = env.env.clone(); - let obligation = InEnvironment { goal: fn_once.cast(Interner), environment: trait_env }; - let canonical = - Canonical { binders: CanonicalVarKinds::from_iter(Interner, kinds), value: obligation }; - let subst = match db.trait_solve(krate, canonical) { - Some(Solution::Unique(vars)) => vars.value.subst, - _ => return None, - }; - let args = subst.at(Interner, 0).ty(Interner)?; - let params = match args.kind(Interner) { - chalk_ir::TyKind::Tuple(_, subst) => { - subst.iter(Interner).filter_map(|arg| arg.ty(Interner).cloned()).collect::<Vec<_>>() - } - _ => return None, - }; - let fn_once = - TyBuilder::trait_ref(db, fn_once_trait).push(self_ty.clone()).push(args.clone()).build(); - let projection = - TyBuilder::assoc_type_projection(db, output_assoc_type, Some(fn_once.substitution.clone())) - .build(); + // Register two obligations: + // - Self: FnOnce<?args_ty> + // - <Self as FnOnce<?args_ty>>::Output == ?ret_ty + let args_ty = table.new_type_var(); + let trait_ref = b.push(self_ty.clone()).push(args_ty.clone()).build(); + let projection = TyBuilder::assoc_type_projection( + db, + output_assoc_type, + Some(trait_ref.substitution.clone()), + ) + .build(); + table.register_obligation(trait_ref.cast(Interner)); + let ret_ty = table.normalize_projection_ty(projection); + + let ret_ty = table.resolve_completely(ret_ty); + let args_ty = table.resolve_completely(args_ty); - let ret_ty = db.normalize_projection(projection, env); + let params = + args_ty.as_tuple()?.iter(Interner).map(|it| it.assert_ty_ref(Interner)).cloned().collect(); - Some(CallableSig::from_params_and_return(params, ret_ty.clone(), false, Safety::Safe)) + Some(CallableSig::from_params_and_return(params, ret_ty, false, Safety::Safe)) } 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 baf9842d5..592410008 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -603,9 +603,8 @@ impl<'a> TyLoweringContext<'a> { } fn select_associated_type(&self, res: Option<TypeNs>, segment: PathSegment<'_>) -> Ty { - let (def, res) = match (self.resolver.generic_def(), res) { - (Some(def), Some(res)) => (def, res), - _ => return TyKind::Error.intern(Interner), + let Some((def, res)) = self.resolver.generic_def().zip(res) else { + return TyKind::Error.intern(Interner); }; let ty = named_associated_type_shorthand_candidates( self.db, @@ -617,6 +616,21 @@ impl<'a> TyLoweringContext<'a> { return None; } + let parent_subst = t.substitution.clone(); + let parent_subst = match self.type_param_mode { + ParamLoweringMode::Placeholder => { + // if we're lowering to placeholders, we have to put them in now. + let generics = generics(self.db.upcast(), def); + let s = generics.placeholder_subst(self.db); + s.apply(parent_subst, Interner) + } + ParamLoweringMode::Variable => { + // We need to shift in the bound vars, since + // `named_associated_type_shorthand_candidates` does not do that. + parent_subst.shifted_in_from(Interner, self.in_binders) + } + }; + // FIXME: `substs_from_path_segment()` pushes `TyKind::Error` for every parent // generic params. It's inefficient to splice the `Substitution`s, so we may want // that method to optionally take parent `Substitution` as we already know them at @@ -632,22 +646,9 @@ impl<'a> TyLoweringContext<'a> { let substs = Substitution::from_iter( Interner, - substs.iter(Interner).take(len_self).chain(t.substitution.iter(Interner)), + substs.iter(Interner).take(len_self).chain(parent_subst.iter(Interner)), ); - let substs = match self.type_param_mode { - ParamLoweringMode::Placeholder => { - // if we're lowering to placeholders, we have to put - // them in now - let generics = generics(self.db.upcast(), def); - let s = generics.placeholder_subst(self.db); - s.apply(substs, Interner) - } - ParamLoweringMode::Variable => substs, - }; - // We need to shift in the bound vars, since - // associated_type_shorthand_candidates does not do that - let substs = substs.shifted_in_from(Interner, self.in_binders); Some( TyKind::Alias(AliasTy::Projection(ProjectionTy { associated_ty_id: to_assoc_type_id(associated_ty), @@ -779,7 +780,7 @@ impl<'a> TyLoweringContext<'a> { |_, c, ty| { const_or_path_to_chalk( self.db, - &self.resolver, + self.resolver, ty, c, self.type_param_mode, @@ -1190,9 +1191,9 @@ pub fn associated_type_shorthand_candidates<R>( db: &dyn HirDatabase, def: GenericDefId, res: TypeNs, - cb: impl FnMut(&Name, &TraitRef, TypeAliasId) -> Option<R>, + mut cb: impl FnMut(&Name, TypeAliasId) -> Option<R>, ) -> Option<R> { - named_associated_type_shorthand_candidates(db, def, res, None, cb) + named_associated_type_shorthand_candidates(db, def, res, None, |name, _, id| cb(name, id)) } fn named_associated_type_shorthand_candidates<R>( @@ -1202,6 +1203,9 @@ fn named_associated_type_shorthand_candidates<R>( def: GenericDefId, res: TypeNs, assoc_name: Option<Name>, + // Do NOT let `cb` touch `TraitRef` outside of `TyLoweringContext`. Its substitution contains + // free `BoundVar`s that need to be shifted and only `TyLoweringContext` knows how to do that + // properly (see `TyLoweringContext::select_associated_type()`). mut cb: impl FnMut(&Name, &TraitRef, TypeAliasId) -> Option<R>, ) -> Option<R> { let mut search = |t| { @@ -1792,8 +1796,7 @@ pub(crate) fn impl_self_ty_query(db: &dyn HirDatabase, impl_id: ImplId) -> Binde let impl_data = db.impl_data(impl_id); let resolver = impl_id.resolver(db.upcast()); let _cx = stdx::panic_context::enter(format!( - "impl_self_ty_query({:?} -> {:?} -> {:?})", - impl_id, impl_loc, impl_data + "impl_self_ty_query({impl_id:?} -> {impl_loc:?} -> {impl_data:?})" )); let generics = generics(db.upcast(), impl_id.into()); let ctx = @@ -1830,8 +1833,7 @@ pub(crate) fn impl_trait_query(db: &dyn HirDatabase, impl_id: ImplId) -> Option< let impl_data = db.impl_data(impl_id); let resolver = impl_id.resolver(db.upcast()); let _cx = stdx::panic_context::enter(format!( - "impl_trait_query({:?} -> {:?} -> {:?})", - impl_id, impl_loc, impl_data + "impl_trait_query({impl_id:?} -> {impl_loc:?} -> {impl_data:?})" )); let ctx = TyLoweringContext::new(db, &resolver).with_type_param_mode(ParamLoweringMode::Variable); @@ -1850,7 +1852,7 @@ pub(crate) fn return_type_impl_traits( let ctx_ret = TyLoweringContext::new(db, &resolver) .with_impl_trait_mode(ImplTraitLoweringMode::Opaque) .with_type_param_mode(ParamLoweringMode::Variable); - let _ret = (&ctx_ret).lower_ty(&data.ret_type); + let _ret = ctx_ret.lower_ty(&data.ret_type); let generics = generics(db.upcast(), def.into()); let return_type_impl_traits = ReturnTypeImplTraits { impl_traits: ctx_ret.opaque_type_data.into_inner() }; @@ -1979,7 +1981,7 @@ fn fallback_bound_vars<T: TypeFoldable<Interner> + HasInterner<Interner = Intern if bound.index_if_innermost().map_or(true, is_allowed) { bound.shifted_in_from(binders).to_const(Interner, ty) } else { - unknown_const(ty.clone()) + unknown_const(ty) } }, ) 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 8bcfa2728..2328dceb8 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 @@ -2,18 +2,17 @@ //! For details about how this works in rustc, see the method lookup page in the //! [rustc guide](https://rust-lang.github.io/rustc-guide/method-lookup.html) //! and the corresponding code mostly in rustc_hir_analysis/check/method/probe.rs. -use std::{iter, ops::ControlFlow, sync::Arc}; +use std::{ops::ControlFlow, sync::Arc}; -use arrayvec::ArrayVec; use base_db::{CrateId, Edition}; use chalk_ir::{cast::Cast, Mutability, UniverseIndex}; use hir_def::{ data::ImplData, item_scope::ItemScope, nameres::DefMap, AssocItemId, BlockId, ConstId, - FunctionId, GenericDefId, HasModule, ImplId, ItemContainerId, Lookup, ModuleDefId, ModuleId, - TraitId, + FunctionId, HasModule, ImplId, ItemContainerId, Lookup, ModuleDefId, ModuleId, TraitId, }; use hir_expand::name::Name; use rustc_hash::{FxHashMap, FxHashSet}; +use smallvec::{smallvec, SmallVec}; use stdx::never; use crate::{ @@ -336,21 +335,18 @@ impl InherentImpls { } } -pub(crate) fn inherent_impl_crates_query( +pub(crate) fn incoherent_inherent_impl_crates( db: &dyn HirDatabase, krate: CrateId, fp: TyFingerprint, -) -> ArrayVec<CrateId, 2> { +) -> SmallVec<[CrateId; 2]> { let _p = profile::span("inherent_impl_crates_query"); - let mut res = ArrayVec::new(); + let mut res = SmallVec::new(); let crate_graph = db.crate_graph(); + // should pass crate for finger print and do reverse deps + for krate in crate_graph.transitive_deps(krate) { - if res.is_full() { - // we don't currently look for or store more than two crates here, - // so don't needlessly look at more crates than necessary. - break; - } let impls = db.inherent_impls_in_crate(krate); if impls.map.get(&fp).map_or(false, |v| !v.is_empty()) { res.push(krate); @@ -392,19 +388,40 @@ pub fn def_crates( db: &dyn HirDatabase, ty: &Ty, cur_crate: CrateId, -) -> Option<ArrayVec<CrateId, 2>> { - let mod_to_crate_ids = |module: ModuleId| Some(iter::once(module.krate()).collect()); - - let fp = TyFingerprint::for_inherent_impl(ty); - +) -> Option<SmallVec<[CrateId; 2]>> { match ty.kind(Interner) { - TyKind::Adt(AdtId(def_id), _) => mod_to_crate_ids(def_id.module(db.upcast())), - TyKind::Foreign(id) => { - mod_to_crate_ids(from_foreign_def_id(*id).lookup(db.upcast()).module(db.upcast())) + &TyKind::Adt(AdtId(def_id), _) => { + let rustc_has_incoherent_inherent_impls = match def_id { + hir_def::AdtId::StructId(id) => { + db.struct_data(id).rustc_has_incoherent_inherent_impls + } + hir_def::AdtId::UnionId(id) => { + db.union_data(id).rustc_has_incoherent_inherent_impls + } + hir_def::AdtId::EnumId(id) => db.enum_data(id).rustc_has_incoherent_inherent_impls, + }; + Some(if rustc_has_incoherent_inherent_impls { + db.incoherent_inherent_impl_crates(cur_crate, TyFingerprint::Adt(def_id)) + } else { + smallvec![def_id.module(db.upcast()).krate()] + }) + } + &TyKind::Foreign(id) => { + let alias = from_foreign_def_id(id); + Some(if db.type_alias_data(alias).rustc_has_incoherent_inherent_impls { + db.incoherent_inherent_impl_crates(cur_crate, TyFingerprint::ForeignType(id)) + } else { + smallvec![alias.module(db.upcast()).krate()] + }) + } + TyKind::Dyn(_) => { + let trait_id = ty.dyn_trait()?; + Some(if db.trait_data(trait_id).rustc_has_incoherent_inherent_impls { + db.incoherent_inherent_impl_crates(cur_crate, TyFingerprint::Dyn(trait_id)) + } else { + smallvec![trait_id.module(db.upcast()).krate()] + }) } - TyKind::Dyn(_) => ty - .dyn_trait() - .and_then(|trait_| mod_to_crate_ids(GenericDefId::TraitId(trait_).module(db.upcast()))), // for primitives, there may be impls in various places (core and alloc // mostly). We just check the whole crate graph for crates with impls // (cached behind a query). @@ -412,10 +429,11 @@ pub fn def_crates( | TyKind::Str | TyKind::Slice(_) | TyKind::Array(..) - | TyKind::Raw(..) => { - Some(db.inherent_impl_crates(cur_crate, fp.expect("fingerprint for primitive"))) - } - _ => return None, + | TyKind::Raw(..) => Some(db.incoherent_inherent_impl_crates( + cur_crate, + TyFingerprint::for_inherent_impl(ty).expect("fingerprint for primitive"), + )), + _ => None, } } @@ -470,14 +488,15 @@ pub fn lang_names_for_bin_op(op: syntax::ast::BinaryOp) -> Option<(Name, Name)> /// Look up the method with the given name. pub(crate) fn lookup_method( - ty: &Canonical<Ty>, db: &dyn HirDatabase, + ty: &Canonical<Ty>, env: Arc<TraitEnvironment>, traits_in_scope: &FxHashSet<TraitId>, visible_from_module: VisibleFromModule, name: &Name, -) -> Option<(ReceiverAdjustments, FunctionId)> { - iterate_method_candidates( +) -> Option<(ReceiverAdjustments, FunctionId, bool)> { + let mut not_visible = None; + let res = iterate_method_candidates( ty, db, env, @@ -485,11 +504,16 @@ pub(crate) fn lookup_method( visible_from_module, Some(name), LookupMode::MethodCall, - |adjustments, f| match f { - AssocItemId::FunctionId(f) => Some((adjustments, f)), + |adjustments, f, visible| match f { + AssocItemId::FunctionId(f) if visible => Some((adjustments, f, true)), + AssocItemId::FunctionId(f) if not_visible.is_none() => { + not_visible = Some((adjustments, f, false)); + None + } _ => None, }, - ) + ); + res.or(not_visible) } /// Whether we're looking up a dotted method call (like `v.len()`) or a path @@ -601,7 +625,7 @@ pub(crate) fn iterate_method_candidates<T>( visible_from_module: VisibleFromModule, name: Option<&Name>, mode: LookupMode, - mut callback: impl FnMut(ReceiverAdjustments, AssocItemId) -> Option<T>, + mut callback: impl FnMut(ReceiverAdjustments, AssocItemId, bool) -> Option<T>, ) -> Option<T> { let mut slot = None; iterate_method_candidates_dyn( @@ -612,9 +636,9 @@ pub(crate) fn iterate_method_candidates<T>( visible_from_module, name, mode, - &mut |adj, item| { + &mut |adj, item, visible| { assert!(slot.is_none()); - if let Some(it) = callback(adj, item) { + if let Some(it) = callback(adj, item, visible) { slot = Some(it); return ControlFlow::Break(()); } @@ -624,6 +648,30 @@ pub(crate) fn iterate_method_candidates<T>( slot } +pub fn lookup_impl_const( + db: &dyn HirDatabase, + env: Arc<TraitEnvironment>, + const_id: ConstId, + subs: Substitution, +) -> ConstId { + let trait_id = match const_id.lookup(db.upcast()).container { + ItemContainerId::TraitId(id) => id, + _ => return const_id, + }; + let substitution = Substitution::from_iter(Interner, subs.iter(Interner)); + let trait_ref = TraitRef { trait_id: to_chalk_trait_id(trait_id), substitution }; + + let const_data = db.const_data(const_id); + let name = match const_data.name.as_ref() { + Some(name) => name, + None => return const_id, + }; + + lookup_impl_assoc_item_for_trait_ref(trait_ref, db, env, name) + .and_then(|assoc| if let AssocItemId::ConstId(id) = assoc { Some(id) } else { None }) + .unwrap_or(const_id) +} + /// Looks up the impl method that actually runs for the trait method `func`. /// /// Returns `func` if it's not a method defined in a trait or the lookup failed. @@ -645,15 +693,17 @@ pub fn lookup_impl_method( }; let name = &db.function_data(func).name; - lookup_impl_method_for_trait_ref(trait_ref, db, env, name).unwrap_or(func) + lookup_impl_assoc_item_for_trait_ref(trait_ref, db, env, name) + .and_then(|assoc| if let AssocItemId::FunctionId(id) = assoc { Some(id) } else { None }) + .unwrap_or(func) } -fn lookup_impl_method_for_trait_ref( +fn lookup_impl_assoc_item_for_trait_ref( trait_ref: TraitRef, db: &dyn HirDatabase, env: Arc<TraitEnvironment>, name: &Name, -) -> Option<FunctionId> { +) -> Option<AssocItemId> { let self_ty = trait_ref.self_type_parameter(Interner); let self_ty_fp = TyFingerprint::for_trait_impl(&self_ty)?; let impls = db.trait_impls_in_deps(env.krate); @@ -663,7 +713,15 @@ fn lookup_impl_method_for_trait_ref( let impl_data = find_matching_impl(impls, table, trait_ref)?; impl_data.items.iter().find_map(|it| match it { - AssocItemId::FunctionId(f) => (db.function_data(*f).name == *name).then(|| *f), + AssocItemId::FunctionId(f) => { + (db.function_data(*f).name == *name).then_some(AssocItemId::FunctionId(*f)) + } + AssocItemId::ConstId(c) => db + .const_data(*c) + .name + .as_ref() + .map(|n| *n == *name) + .and_then(|result| if result { Some(AssocItemId::ConstId(*c)) } else { None }), _ => None, }) } @@ -719,7 +777,7 @@ pub fn iterate_path_candidates( name, LookupMode::Path, // the adjustments are not relevant for path lookup - &mut |_, id| callback(id), + &mut |_, id, _| callback(id), ) } @@ -731,7 +789,7 @@ pub fn iterate_method_candidates_dyn( visible_from_module: VisibleFromModule, name: Option<&Name>, mode: LookupMode, - callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId) -> ControlFlow<()>, + callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>, ) -> ControlFlow<()> { match mode { LookupMode::MethodCall => { @@ -795,7 +853,7 @@ fn iterate_method_candidates_with_autoref( traits_in_scope: &FxHashSet<TraitId>, visible_from_module: VisibleFromModule, name: Option<&Name>, - mut callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId) -> ControlFlow<()>, + mut callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>, ) -> ControlFlow<()> { if receiver_ty.value.is_general_var(Interner, &receiver_ty.binders) { // don't try to resolve methods on unknown types @@ -856,7 +914,7 @@ fn iterate_method_candidates_by_receiver( traits_in_scope: &FxHashSet<TraitId>, visible_from_module: VisibleFromModule, name: Option<&Name>, - mut callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId) -> ControlFlow<()>, + mut callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>, ) -> ControlFlow<()> { let mut table = InferenceTable::new(db, env); let receiver_ty = table.instantiate_canonical(receiver_ty.clone()); @@ -868,7 +926,7 @@ fn iterate_method_candidates_by_receiver( while let Some((self_ty, _)) = autoderef.next() { iterate_inherent_methods( &self_ty, - &mut autoderef.table, + autoderef.table, name, Some(&receiver_ty), Some(receiver_adjustments.clone()), @@ -883,7 +941,7 @@ fn iterate_method_candidates_by_receiver( while let Some((self_ty, _)) = autoderef.next() { iterate_trait_method_candidates( &self_ty, - &mut autoderef.table, + autoderef.table, traits_in_scope, name, Some(&receiver_ty), @@ -902,7 +960,7 @@ fn iterate_method_candidates_for_self_ty( traits_in_scope: &FxHashSet<TraitId>, visible_from_module: VisibleFromModule, name: Option<&Name>, - mut callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId) -> ControlFlow<()>, + mut callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>, ) -> ControlFlow<()> { let mut table = InferenceTable::new(db, env); let self_ty = table.instantiate_canonical(self_ty.clone()); @@ -933,7 +991,7 @@ fn iterate_trait_method_candidates( name: Option<&Name>, receiver_ty: Option<&Ty>, receiver_adjustments: Option<ReceiverAdjustments>, - callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId) -> ControlFlow<()>, + callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>, ) -> ControlFlow<()> { let db = table.db; let env = table.trait_env.clone(); @@ -964,9 +1022,11 @@ fn iterate_trait_method_candidates( for &(_, item) in data.items.iter() { // Don't pass a `visible_from_module` down to `is_valid_candidate`, // since only inherent methods should be included into visibility checking. - if !is_valid_candidate(table, name, receiver_ty, item, self_ty, None) { - continue; - } + let visible = match is_valid_candidate(table, name, receiver_ty, item, self_ty, None) { + IsValidCandidate::Yes => true, + IsValidCandidate::NotVisible => false, + IsValidCandidate::No => continue, + }; if !known_implemented { let goal = generic_implements_goal(db, env.clone(), t, &canonical_self_ty); if db.trait_solve(env.krate, goal.cast(Interner)).is_none() { @@ -974,7 +1034,7 @@ fn iterate_trait_method_candidates( } } known_implemented = true; - callback(receiver_adjustments.clone().unwrap_or_default(), item)?; + callback(receiver_adjustments.clone().unwrap_or_default(), item, visible)?; } } ControlFlow::Continue(()) @@ -987,7 +1047,7 @@ fn iterate_inherent_methods( receiver_ty: Option<&Ty>, receiver_adjustments: Option<ReceiverAdjustments>, visible_from_module: VisibleFromModule, - callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId) -> ControlFlow<()>, + callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>, ) -> ControlFlow<()> { let db = table.db; let env = table.trait_env.clone(); @@ -1076,7 +1136,7 @@ fn iterate_inherent_methods( name: Option<&Name>, receiver_ty: Option<&Ty>, receiver_adjustments: Option<ReceiverAdjustments>, - callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId) -> ControlFlow<()>, + callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>, traits: impl Iterator<Item = TraitId>, ) -> ControlFlow<()> { let db = table.db; @@ -1084,9 +1144,13 @@ fn iterate_inherent_methods( let data = db.trait_data(t); for &(_, item) in data.items.iter() { // We don't pass `visible_from_module` as all trait items should be visible. - if is_valid_candidate(table, name, receiver_ty, item, self_ty, None) { - callback(receiver_adjustments.clone().unwrap_or_default(), item)?; - } + let visible = + match is_valid_candidate(table, name, receiver_ty, item, self_ty, None) { + IsValidCandidate::Yes => true, + IsValidCandidate::NotVisible => false, + IsValidCandidate::No => continue, + }; + callback(receiver_adjustments.clone().unwrap_or_default(), item, visible)?; } } ControlFlow::Continue(()) @@ -1100,17 +1164,25 @@ fn iterate_inherent_methods( receiver_ty: Option<&Ty>, receiver_adjustments: Option<ReceiverAdjustments>, visible_from_module: Option<ModuleId>, - callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId) -> ControlFlow<()>, + callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId, bool) -> ControlFlow<()>, ) -> ControlFlow<()> { let db = table.db; let impls_for_self_ty = impls.for_self_ty(self_ty); for &impl_def in impls_for_self_ty { for &item in &db.impl_data(impl_def).items { - if !is_valid_candidate(table, name, receiver_ty, item, self_ty, visible_from_module) - { - continue; - } - callback(receiver_adjustments.clone().unwrap_or_default(), item)?; + let visible = match is_valid_candidate( + table, + name, + receiver_ty, + item, + self_ty, + visible_from_module, + ) { + IsValidCandidate::Yes => true, + IsValidCandidate::NotVisible => false, + IsValidCandidate::No => continue, + }; + callback(receiver_adjustments.clone().unwrap_or_default(), item, visible)?; } } ControlFlow::Continue(()) @@ -1139,7 +1211,7 @@ pub fn resolve_indexing_op( macro_rules! check_that { ($cond:expr) => { if !$cond { - return false; + return IsValidCandidate::No; } }; } @@ -1151,7 +1223,7 @@ fn is_valid_candidate( item: AssocItemId, self_ty: &Ty, visible_from_module: Option<ModuleId>, -) -> bool { +) -> IsValidCandidate { let db = table.db; match item { AssocItemId::FunctionId(m) => { @@ -1162,31 +1234,37 @@ fn is_valid_candidate( check_that!(receiver_ty.is_none()); check_that!(name.map_or(true, |n| data.name.as_ref() == Some(n))); - check_that!(visible_from_module.map_or(true, |from_module| { - let v = db.const_visibility(c).is_visible_from(db.upcast(), from_module); - if !v { + + if let Some(from_module) = visible_from_module { + if !db.const_visibility(c).is_visible_from(db.upcast(), from_module) { cov_mark::hit!(const_candidate_not_visible); + return IsValidCandidate::NotVisible; } - v - })); + } if let ItemContainerId::ImplId(impl_id) = c.lookup(db.upcast()).container { let self_ty_matches = table.run_in_snapshot(|table| { let expected_self_ty = TyBuilder::impl_self_ty(db, impl_id) .fill_with_inference_vars(table) .build(); - table.unify(&expected_self_ty, &self_ty) + table.unify(&expected_self_ty, self_ty) }); if !self_ty_matches { cov_mark::hit!(const_candidate_self_type_mismatch); - return false; + return IsValidCandidate::No; } } - true + IsValidCandidate::Yes } - _ => false, + _ => IsValidCandidate::No, } } +enum IsValidCandidate { + Yes, + No, + NotVisible, +} + fn is_valid_fn_candidate( table: &mut InferenceTable<'_>, fn_id: FunctionId, @@ -1194,19 +1272,17 @@ fn is_valid_fn_candidate( receiver_ty: Option<&Ty>, self_ty: &Ty, visible_from_module: Option<ModuleId>, -) -> bool { +) -> IsValidCandidate { let db = table.db; let data = db.function_data(fn_id); check_that!(name.map_or(true, |n| n == &data.name)); - check_that!(visible_from_module.map_or(true, |from_module| { - let v = db.function_visibility(fn_id).is_visible_from(db.upcast(), from_module); - if !v { + if let Some(from_module) = visible_from_module { + if !db.function_visibility(fn_id).is_visible_from(db.upcast(), from_module) { cov_mark::hit!(autoderef_candidate_not_visible); + return IsValidCandidate::NotVisible; } - v - })); - + } table.run_in_snapshot(|table| { let container = fn_id.lookup(db.upcast()).container; let (impl_subst, expect_self_ty) = match container { @@ -1245,7 +1321,7 @@ 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()); - predicates + let valid = predicates .iter() .map(|predicate| { let (p, b) = predicate @@ -1260,12 +1336,16 @@ fn is_valid_fn_candidate( // 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()) + .all(|p| table.try_obligation(p.cast(Interner)).is_some()); + match valid { + true => IsValidCandidate::Yes, + false => IsValidCandidate::No, + } } else { // For `ItemContainerId::TraitId`, we check if `self_ty` implements the trait in // `iterate_trait_method_candidates()`. // For others, this function shouldn't be called. - true + IsValidCandidate::Yes } }) } 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 ebbc54101..ba5d9c241 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs @@ -94,18 +94,19 @@ fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool, display_sour types.insert(file_range, expected.trim_start_matches("type: ").to_string()); } else if expected.starts_with("expected") { mismatches.insert(file_range, expected); - } else if expected.starts_with("adjustments: ") { + } else if expected.starts_with("adjustments:") { adjustments.insert( file_range, expected - .trim_start_matches("adjustments: ") + .trim_start_matches("adjustments:") + .trim() .split(',') .map(|it| it.trim().to_string()) .filter(|it| !it.is_empty()) .collect(), ); } else { - panic!("unexpected annotation: {}", expected); + panic!("unexpected annotation: {expected}"); } had_annotations = true; } @@ -176,17 +177,17 @@ fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool, display_sour assert_eq!(actual, expected); } if let Some(expected) = adjustments.remove(&range) { - if let Some(adjustments) = inference_result.expr_adjustments.get(&expr) { - assert_eq!( - expected, - adjustments - .iter() - .map(|Adjustment { kind, .. }| format!("{:?}", kind)) - .collect::<Vec<_>>() - ); - } else { - panic!("expected {:?} adjustments, found none", expected); - } + let adjustments = inference_result + .expr_adjustments + .get(&expr) + .map_or_else(Default::default, |it| &**it); + assert_eq!( + expected, + adjustments + .iter() + .map(|Adjustment { kind, .. }| format!("{kind:?}")) + .collect::<Vec<_>>() + ); } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs index 7e3aecc2a..3e110abaf 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs @@ -807,3 +807,37 @@ fn main() { "#, ); } + +#[test] +fn adjust_comparison_arguments() { + check_no_mismatches( + r" +//- minicore: eq +struct Struct; +impl core::cmp::PartialEq for Struct { + fn eq(&self, other: &Self) -> bool { true } +} +fn test() { + Struct == Struct; + // ^^^^^^ adjustments: Borrow(Ref(Not)) + // ^^^^^^ adjustments: Borrow(Ref(Not)) +}", + ); +} + +#[test] +fn adjust_assign_lhs() { + check_no_mismatches( + r" +//- minicore: add +struct Struct; +impl core::ops::AddAssign for Struct { + fn add_assign(&mut self, other: Self) {} +} +fn test() { + Struct += Struct; + // ^^^^^^ adjustments: Borrow(Ref(Mut)) + // ^^^^^^ adjustments: +}", + ); +} 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 3e08e83e8..073d6d9be 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 @@ -24,7 +24,7 @@ fn typing_whitespace_inside_a_function_should_not_invalidate_types() { db.infer(def); }); }); - assert!(format!("{:?}", events).contains("infer")) + assert!(format!("{events:?}").contains("infer")) } let new_text = " @@ -46,6 +46,6 @@ fn typing_whitespace_inside_a_function_should_not_invalidate_types() { db.infer(def); }); }); - assert!(!format!("{:?}", events).contains("infer"), "{:#?}", events) + assert!(!format!("{events:?}").contains("infer"), "{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 b3adafaaf..8b75ec842 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 @@ -849,7 +849,7 @@ fn main() { //^^^^^^^^^^^^^^^^^ RegisterBlock } "#; - let fixture = format!("{}\n//- /foo.rs\n{}", fixture, data); + let fixture = format!("{fixture}\n//- /foo.rs\n{data}"); { let _b = bench("include macro"); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs index 5d76d185f..6c7a53299 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs @@ -1867,3 +1867,53 @@ fn g<T: Trait>(a: T) { "#, ); } + +#[test] +fn incoherent_impls() { + check( + r#" +//- minicore: error, send +pub struct Box<T>(T); +use core::error::Error; + +#[rustc_allow_incoherent_impl] +impl dyn Error { + pub fn downcast<T: Error + 'static>(self: Box<Self>) -> Result<Box<T>, Box<dyn Error>> { + loop {} + } +} +#[rustc_allow_incoherent_impl] +impl dyn Error + Send { + /// Attempts to downcast the box to a concrete type. + pub fn downcast<T: Error + 'static>(self: Box<Self>) -> Result<Box<T>, Box<dyn Error + Send>> { + let err: Box<dyn Error> = self; + // ^^^^ expected Box<dyn Error>, got Box<dyn Error + Send> + // FIXME, type mismatch should not occur + <dyn Error>::downcast(err).map_err(|_| loop {}) + //^^^^^^^^^^^^^^^^^^^^^ type: fn downcast<{unknown}>(Box<dyn Error>) -> Result<Box<{unknown}>, Box<dyn Error>> + } +} +"#, + ); +} + +#[test] +fn fallback_private_methods() { + check( + r#" +mod module { + pub struct Struct; + + impl Struct { + fn func(&self) {} + } +} + +fn foo() { + let s = module::Struct; + s.func(); + //^^^^^^^^ type: () +} +"#, + ); +} 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 74de33117..9333e2693 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 @@ -1080,3 +1080,15 @@ fn my_fn(#[cfg(feature = "feature")] u8: u8, u32: u32) {} "#, ); } + +#[test] +fn var_args() { + check_types( + r#" +#[lang = "va_list"] +pub struct VaListImpl<'f>; +fn my_fn(foo: ...) {} + //^^^ VaListImpl +"#, + ); +} 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 4e4639745..de6ae7fff 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 @@ -1723,3 +1723,24 @@ fn bar() -> ControlFlow<(), ()> { "#, ); } + +#[test] +fn assoc_type_shorthand_with_gats_in_binders() { + // c.f. test `issue_4885()` + check_no_mismatches( + r#" +trait Gats { + type Assoc<T>; +} +trait Foo<T> {} + +struct Bar<'a, B: Gats, A> { + field: &'a dyn Foo<B::Assoc<A>>, +} + +fn foo(b: Bar) { + let _ = b.field; +} +"#, + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs index d7431443b..146145523 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs @@ -2064,17 +2064,17 @@ fn fn_pointer_return() { fn block_modifiers_smoke_test() { check_infer( r#" -//- minicore: future +//- minicore: future, try async fn main() { let x = unsafe { 92 }; let y = async { async { () }.await }; - let z = try { () }; + let z: core::ops::ControlFlow<(), _> = try { () }; let w = const { 92 }; let t = 'a: { 92 }; } "#, expect![[r#" - 16..162 '{ ...2 }; }': () + 16..193 '{ ...2 }; }': () 26..27 'x': i32 30..43 'unsafe { 92 }': i32 30..43 'unsafe { 92 }': i32 @@ -2086,17 +2086,17 @@ async fn main() { 65..77 'async { () }': impl Future<Output = ()> 65..83 'async ....await': () 73..75 '()': () - 95..96 'z': {unknown} - 99..109 'try { () }': () - 99..109 'try { () }': {unknown} - 105..107 '()': () - 119..120 'w': i32 - 123..135 'const { 92 }': i32 - 123..135 'const { 92 }': i32 - 131..133 '92': i32 - 145..146 't': i32 - 149..159 ''a: { 92 }': i32 - 155..157 '92': i32 + 95..96 'z': ControlFlow<(), ()> + 130..140 'try { () }': () + 130..140 'try { () }': ControlFlow<(), ()> + 136..138 '()': () + 150..151 'w': i32 + 154..166 'const { 92 }': i32 + 154..166 'const { 92 }': i32 + 162..164 '92': i32 + 176..177 't': i32 + 180..190 ''a: { 92 }': i32 + 186..188 '92': i32 "#]], ) } 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 3d7194b6f..d01fe0632 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 @@ -1389,6 +1389,22 @@ fn foo<const C: u8, T>() -> (impl FnOnce(&str, T), impl Trait<u8>) { } #[test] +fn return_pos_impl_trait_in_projection() { + // Note that the unused type param `X` is significant; see #13307. + check_no_mismatches( + r#" +//- minicore: sized +trait Future { type Output; } +impl Future for () { type Output = i32; } +type Foo<F> = (<F as Future>::Output, F); +fn foo<X>() -> Foo<impl Future<Output = ()>> { + (0, ()) +} +"#, + ) +} + +#[test] fn dyn_trait() { check_infer( r#" @@ -4084,3 +4100,68 @@ where "#, ); } + +#[test] +fn bin_op_with_scalar_fallback() { + // Extra impls are significant so that chalk doesn't give us definite guidances. + check_types( + r#" +//- minicore: add +use core::ops::Add; + +struct Vec2<T>(T, T); + +impl Add for Vec2<i32> { + type Output = Self; + fn add(self, rhs: Self) -> Self::Output { loop {} } +} +impl Add for Vec2<u32> { + type Output = Self; + fn add(self, rhs: Self) -> Self::Output { loop {} } +} +impl Add for Vec2<f32> { + type Output = Self; + fn add(self, rhs: Self) -> Self::Output { loop {} } +} +impl Add for Vec2<f64> { + type Output = Self; + fn add(self, rhs: Self) -> Self::Output { loop {} } +} + +fn test() { + let a = Vec2(1, 2); + let b = Vec2(3, 4); + let c = a + b; + //^ Vec2<i32> + let a = Vec2(1., 2.); + let b = Vec2(3., 4.); + let c = a + b; + //^ Vec2<f64> +} +"#, + ); +} + +#[test] +fn trait_method_with_scalar_fallback() { + check_types( + r#" +trait Trait { + type Output; + fn foo(&self) -> Self::Output; +} +impl<T> Trait for T { + type Output = T; + fn foo(&self) -> Self::Output { loop {} } +} +fn test() { + let a = 42; + let b = a.foo(); + //^ i32 + let a = 3.14; + let b = a.foo(); + //^ f64 +} +"#, + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tls.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tls.rs index 92711a24f..b7e6ee674 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tls.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tls.rs @@ -67,12 +67,12 @@ impl DebugContext<'_> { let trait_ref = projection_ty.trait_ref(self.0); let trait_params = trait_ref.substitution.as_slice(Interner); let self_ty = trait_ref.self_type_parameter(Interner); - write!(fmt, "<{:?} as {}", self_ty, trait_name)?; + write!(fmt, "<{self_ty:?} as {trait_name}")?; if trait_params.len() > 1 { write!( fmt, "<{}>", - trait_params[1..].iter().format_with(", ", |x, f| f(&format_args!("{:?}", x))), + trait_params[1..].iter().format_with(", ", |x, f| f(&format_args!("{x:?}"))), )?; } write!(fmt, ">::{}", type_alias_data.name)?; @@ -83,7 +83,7 @@ impl DebugContext<'_> { write!( fmt, "<{}>", - proj_params.iter().format_with(", ", |x, f| f(&format_args!("{:?}", x))), + proj_params.iter().format_with(", ", |x, f| f(&format_args!("{x:?}"))), )?; } @@ -105,9 +105,9 @@ impl DebugContext<'_> { } }; match def { - CallableDefId::FunctionId(_) => write!(fmt, "{{fn {}}}", name), + CallableDefId::FunctionId(_) => write!(fmt, "{{fn {name}}}"), CallableDefId::StructId(_) | CallableDefId::EnumVariantId(_) => { - write!(fmt, "{{ctor {}}}", name) + write!(fmt, "{{ctor {name}}}") } } } 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 c425f35ac..778a6b820 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/traits.rs @@ -18,7 +18,7 @@ use crate::{ }; /// This controls how much 'time' we give the Chalk solver before giving up. -const CHALK_SOLVER_FUEL: i32 = 100; +const CHALK_SOLVER_FUEL: i32 = 1000; #[derive(Debug, Copy, Clone)] pub(crate) struct ChalkContext<'a> { @@ -55,13 +55,10 @@ impl TraitEnvironment { } } - pub fn traits_in_scope_from_clauses<'a>( - &'a self, - ty: Ty, - ) -> impl Iterator<Item = TraitId> + 'a { + pub fn traits_in_scope_from_clauses(&self, ty: Ty) -> impl Iterator<Item = TraitId> + '_ { self.traits_from_clauses .iter() - .filter_map(move |(self_ty, trait_id)| (*self_ty == ty).then(|| *trait_id)) + .filter_map(move |(self_ty, trait_id)| (*self_ty == ty).then_some(*trait_id)) } } @@ -130,7 +127,7 @@ fn solve( let mut solve = || { let _ctx = if is_chalk_debug() || is_chalk_print() { - Some(panic_context::enter(format!("solving {:?}", goal))) + Some(panic_context::enter(format!("solving {goal:?}"))) } else { None }; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs b/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs index e54bcb421..9893566bd 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs @@ -17,7 +17,7 @@ use hir_def::{ ConstParamId, FunctionId, GenericDefId, ItemContainerId, Lookup, TraitId, TypeAliasId, TypeOrConstParamId, TypeParamId, }; -use hir_expand::name::{known, Name}; +use hir_expand::name::Name; use itertools::Either; use rustc_hash::FxHashSet; use smallvec::{smallvec, SmallVec}; @@ -184,9 +184,7 @@ pub(crate) struct Generics { } impl Generics { - pub(crate) fn iter_id<'a>( - &'a self, - ) -> impl Iterator<Item = Either<TypeParamId, ConstParamId>> + 'a { + pub(crate) fn iter_id(&self) -> impl Iterator<Item = Either<TypeParamId, ConstParamId>> + '_ { self.iter().map(|(id, data)| match data { TypeOrConstParamData::TypeParamData(_) => Either::Left(TypeParamId::from_unchecked(id)), TypeOrConstParamData::ConstParamData(_) => { @@ -216,9 +214,9 @@ impl Generics { } /// Iterator over types and const params of parent. - pub(crate) fn iter_parent<'a>( - &'a self, - ) -> impl DoubleEndedIterator<Item = (TypeOrConstParamId, &'a TypeOrConstParamData)> + 'a { + pub(crate) fn iter_parent( + &self, + ) -> impl DoubleEndedIterator<Item = (TypeOrConstParamId, &TypeOrConstParamData)> { self.parent_generics().into_iter().flat_map(|it| { let to_toc_id = move |(local_id, p)| (TypeOrConstParamId { parent: it.def, local_id }, p); @@ -335,54 +333,18 @@ pub fn is_fn_unsafe_to_call(db: &dyn HirDatabase, func: FunctionId) -> bool { // Function in an `extern` block are always unsafe to call, except when it has // `"rust-intrinsic"` ABI there are a few exceptions. let id = block.lookup(db.upcast()).id; - !matches!( - id.item_tree(db.upcast())[id.value].abi.as_deref(), - Some("rust-intrinsic") if !is_intrinsic_fn_unsafe(&data.name) - ) + + let is_intrinsic = + id.item_tree(db.upcast())[id.value].abi.as_deref() == Some("rust-intrinsic"); + + if is_intrinsic { + // Intrinsics are unsafe unless they have the rustc_safe_intrinsic attribute + !data.attrs.by_key("rustc_safe_intrinsic").exists() + } else { + // Extern items are always unsafe + true + } } _ => false, } } - -/// Returns `true` if the given intrinsic is unsafe to call, or false otherwise. -fn is_intrinsic_fn_unsafe(name: &Name) -> bool { - // Should be kept in sync with https://github.com/rust-lang/rust/blob/532d2b14c05f9bc20b2d27cbb5f4550d28343a36/compiler/rustc_typeck/src/check/intrinsic.rs#L72-L106 - ![ - known::abort, - known::add_with_overflow, - known::bitreverse, - known::black_box, - known::bswap, - known::caller_location, - known::ctlz, - known::ctpop, - known::cttz, - known::discriminant_value, - known::forget, - known::likely, - known::maxnumf32, - known::maxnumf64, - known::min_align_of, - known::minnumf32, - known::minnumf64, - known::mul_with_overflow, - known::needs_drop, - known::ptr_guaranteed_eq, - known::ptr_guaranteed_ne, - known::rotate_left, - known::rotate_right, - known::rustc_peek, - known::saturating_add, - known::saturating_sub, - known::size_of, - known::sub_with_overflow, - known::type_id, - known::type_name, - known::unlikely, - known::variant_count, - known::wrapping_add, - known::wrapping_mul, - known::wrapping_sub, - ] - .contains(name) -} |