From 4547b622d8d29df964fa2914213088b148c498fc Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 17 Apr 2024 14:18:32 +0200 Subject: Merging upstream version 1.67.1+dfsg1. Signed-off-by: Daniel Baumann --- src/tools/rust-analyzer/crates/hir-ty/Cargo.toml | 2 +- .../rust-analyzer/crates/hir-ty/src/chalk_ext.rs | 17 ++- .../rust-analyzer/crates/hir-ty/src/display.rs | 43 ++++-- src/tools/rust-analyzer/crates/hir-ty/src/infer.rs | 2 +- .../rust-analyzer/crates/hir-ty/src/infer/expr.rs | 1 + .../rust-analyzer/crates/hir-ty/src/infer/path.rs | 2 +- .../rust-analyzer/crates/hir-ty/src/infer/unify.rs | 12 +- src/tools/rust-analyzer/crates/hir-ty/src/lib.rs | 108 ++++++++++++-- src/tools/rust-analyzer/crates/hir-ty/src/lower.rs | 143 ++++++++++++++----- .../crates/hir-ty/src/method_resolution.rs | 86 +++++++---- .../crates/hir-ty/src/tests/coercion.rs | 40 +++++- .../crates/hir-ty/src/tests/display_source_code.rs | 31 ++++ .../crates/hir-ty/src/tests/method_resolution.rs | 40 +++--- .../crates/hir-ty/src/tests/regression.rs | 16 +++ .../crates/hir-ty/src/tests/simple.rs | 18 +-- .../crates/hir-ty/src/tests/traits.rs | 157 ++++++++++++++++++--- src/tools/rust-analyzer/crates/hir-ty/src/tls.rs | 28 +++- 17 files changed, 580 insertions(+), 166 deletions(-) (limited to 'src/tools/rust-analyzer/crates/hir-ty') diff --git a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml index ed13275ba..a1d6835bf 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml +++ b/src/tools/rust-analyzer/crates/hir-ty/Cargo.toml @@ -4,7 +4,7 @@ version = "0.0.0" description = "TBD" license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.57" +rust-version = "1.65" [lib] doctest = false diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs index e2099d7e5..996b42f5b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs @@ -11,9 +11,9 @@ use syntax::SmolStr; use crate::{ db::HirDatabase, from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, - from_placeholder_idx, to_chalk_trait_id, AdtId, AliasEq, AliasTy, Binders, CallableDefId, - CallableSig, FnPointer, ImplTraitId, Interner, Lifetime, ProjectionTy, QuantifiedWhereClause, - Substitution, TraitRef, Ty, TyBuilder, TyKind, WhereClause, + from_placeholder_idx, to_chalk_trait_id, utils::generics, AdtId, AliasEq, AliasTy, Binders, + CallableDefId, CallableSig, FnPointer, ImplTraitId, Interner, Lifetime, ProjectionTy, + QuantifiedWhereClause, Substitution, TraitRef, Ty, TyBuilder, TyKind, WhereClause, }; pub trait TyExt { @@ -338,10 +338,13 @@ pub trait ProjectionTyExt { impl ProjectionTyExt for ProjectionTy { fn trait_ref(&self, db: &dyn HirDatabase) -> TraitRef { - TraitRef { - trait_id: to_chalk_trait_id(self.trait_(db)), - substitution: self.substitution.clone(), - } + // FIXME: something like `Split` trait from chalk-solve might be nice. + let generics = generics(db.upcast(), from_assoc_type_id(self.associated_ty_id).into()); + let substitution = Substitution::from_iter( + Interner, + self.substitution.iter(Interner).skip(generics.len_self()), + ); + TraitRef { trait_id: to_chalk_trait_id(self.trait_(db)), substitution } } fn trait_(&self, db: &dyn HirDatabase) -> TraitId { 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 0221f922f..a22a4b170 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -289,16 +289,18 @@ impl HirDisplay for ProjectionTy { return write!(f, "{}", TYPE_HINT_TRUNCATION); } - let trait_ = f.db.trait_data(self.trait_(f.db)); + let trait_ref = self.trait_ref(f.db); write!(f, "<")?; - self.self_type_parameter(f.db).hir_fmt(f)?; - write!(f, " as {}", trait_.name)?; - if self.substitution.len(Interner) > 1 { + fmt_trait_ref(&trait_ref, f, true)?; + write!(f, ">::{}", f.db.type_alias_data(from_assoc_type_id(self.associated_ty_id)).name)?; + let proj_params_count = + self.substitution.len(Interner) - trait_ref.substitution.len(Interner); + let proj_params = &self.substitution.as_slice(Interner)[..proj_params_count]; + if !proj_params.is_empty() { write!(f, "<")?; - f.write_joined(&self.substitution.as_slice(Interner)[1..], ", ")?; + f.write_joined(proj_params, ", ")?; write!(f, ">")?; } - write!(f, ">::{}", f.db.type_alias_data(from_assoc_type_id(self.associated_ty_id)).name)?; Ok(()) } } @@ -641,9 +643,12 @@ impl HirDisplay for Ty { // Use placeholder associated types when the target is test (https://rust-lang.github.io/chalk/book/clauses/type_equality.html#placeholder-associated-types) if f.display_target.is_test() { write!(f, "{}::{}", trait_.name, type_alias_data.name)?; + // Note that the generic args for the associated type come before those for the + // trait (including the self type). + // FIXME: reconsider the generic args order upon formatting? if parameters.len(Interner) > 0 { write!(f, "<")?; - f.write_joined(&*parameters.as_slice(Interner), ", ")?; + f.write_joined(parameters.as_slice(Interner), ", ")?; write!(f, ">")?; } } else { @@ -972,9 +977,20 @@ fn write_bounds_like_dyn_trait( angle_open = true; } if let AliasTy::Projection(proj) = alias { - let type_alias = - f.db.type_alias_data(from_assoc_type_id(proj.associated_ty_id)); - write!(f, "{} = ", type_alias.name)?; + let assoc_ty_id = from_assoc_type_id(proj.associated_ty_id); + let type_alias = f.db.type_alias_data(assoc_ty_id); + write!(f, "{}", type_alias.name)?; + + let proj_arg_count = generics(f.db.upcast(), assoc_ty_id.into()).len_self(); + if proj_arg_count > 0 { + write!(f, "<")?; + f.write_joined( + &proj.substitution.as_slice(Interner)[..proj_arg_count], + ", ", + )?; + write!(f, ">")?; + } + write!(f, " = ")?; } ty.hir_fmt(f)?; } @@ -1171,8 +1187,11 @@ impl HirDisplay for TypeRef { inner.hir_fmt(f)?; write!(f, "]")?; } - TypeRef::Fn(parameters, is_varargs) => { + &TypeRef::Fn(ref parameters, is_varargs, is_unsafe) => { // FIXME: Function pointer qualifiers. + if is_unsafe { + write!(f, "unsafe ")?; + } write!(f, "fn(")?; if let Some(((_, return_type), function_parameters)) = parameters.split_last() { for index in 0..function_parameters.len() { @@ -1187,7 +1206,7 @@ impl HirDisplay for TypeRef { write!(f, ", ")?; } } - if *is_varargs { + if is_varargs { write!(f, "{}...", if parameters.len() == 1 { "" } else { ", " })?; } write!(f, ")")?; 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 0efff651c..0b3c23f57 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -1020,7 +1020,7 @@ impl Expectation { /// The primary use case is where the expected type is a fat pointer, /// like `&[isize]`. For example, consider the following statement: /// - /// let x: &[isize] = &[1, 2, 3]; + /// let x: &[isize] = &[1, 2, 3]; /// /// In this case, the expected type for the `&[1, 2, 3]` expression is /// `&[isize]`. If however we were to say that `[1, 2, 3]` has the 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 f56108b26..b1f4de826 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 @@ -85,6 +85,7 @@ impl<'a> InferenceContext<'a> { let ty = match &self.body[tgt_expr] { Expr::Missing => self.err_ty(), &Expr::If { condition, then_branch, else_branch } => { + let expected = &expected.adjust_for_branches(&mut self.table); self.infer_expr( condition, &Expectation::has_type(TyKind::Scalar(Scalar::Bool).intern(Interner)), 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 7a4754cdc..ebe9d6fb5 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 @@ -157,7 +157,7 @@ impl<'a> InferenceContext<'a> { remaining_segments_for_ty, true, ); - if let TyKind::Error = ty.kind(Interner) { + if ty.is_unknown() { return None; } 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 b00e3216b..12f45f00f 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 @@ -340,8 +340,8 @@ impl<'a> InferenceTable<'a> { self.resolve_with_fallback(t, &|_, _, d, _| d) } - /// Unify two types and register new trait goals that arise from that. - pub(crate) fn unify(&mut self, ty1: &Ty, ty2: &Ty) -> bool { + /// Unify two relatable values (e.g. `Ty`) and register new trait goals that arise from that. + pub(crate) fn unify>(&mut self, ty1: &T, ty2: &T) -> bool { let result = match self.try_unify(ty1, ty2) { Ok(r) => r, Err(_) => return false, @@ -350,9 +350,13 @@ impl<'a> InferenceTable<'a> { true } - /// Unify two types and return new trait goals arising from it, so the + /// Unify two relatable values (e.g. `Ty`) and return new trait goals arising from it, so the /// caller needs to deal with them. - pub(crate) fn try_unify>(&mut self, t1: &T, t2: &T) -> InferResult<()> { + pub(crate) fn try_unify>( + &mut self, + t1: &T, + t2: &T, + ) -> InferResult<()> { match self.var_unification_table.relate( Interner, &self.db, 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 c4b700cbc..39514fc44 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -38,10 +38,12 @@ use std::sync::Arc; use chalk_ir::{ fold::{Shift, TypeFoldable}, interner::HasInterner, - NoSolution, + NoSolution, UniverseIndex, }; use hir_def::{expr::ExprId, type_ref::Rawness, TypeOrConstParamId}; +use hir_expand::name; use itertools::Either; +use traits::FnTrait; use utils::Generics; use crate::{consteval::unknown_const, db::HirDatabase, utils::generics}; @@ -51,7 +53,7 @@ pub use builder::{ParamKind, TyBuilder}; pub use chalk_ext::*; pub use infer::{ could_coerce, could_unify, Adjust, Adjustment, AutoBorrow, BindingMode, InferenceDiagnostic, - InferenceResult, + InferenceResult, OverloadedDeref, PointerCast, }; pub use interner::Interner; pub use lower::{ @@ -81,7 +83,20 @@ pub type PlaceholderIndex = chalk_ir::PlaceholderIndex; pub type VariableKind = chalk_ir::VariableKind; pub type VariableKinds = chalk_ir::VariableKinds; pub type CanonicalVarKinds = chalk_ir::CanonicalVarKinds; +/// Represents generic parameters and an item bound by them. When the item has parent, the binders +/// also contain the generic parameters for its parent. See chalk's documentation for details. +/// +/// One thing to keep in mind when working with `Binders` (and `Substitution`s, which represent +/// generic arguments) in rust-analyzer is that the ordering within *is* significant - the generic +/// parameters/arguments for an item MUST come before those for its parent. This is to facilitate +/// the integration with chalk-solve, which mildly puts constraints as such. See #13335 for its +/// motivation in detail. pub type Binders = chalk_ir::Binders; +/// Interned list of generic arguments for an item. When an item has parent, the `Substitution` for +/// it contains generic arguments for both its parent and itself. See chalk's documentation for +/// details. +/// +/// See `Binders` for the constraint on the ordering. pub type Substitution = chalk_ir::Substitution; pub type GenericArg = chalk_ir::GenericArg; pub type GenericArgData = chalk_ir::GenericArgData; @@ -124,14 +139,6 @@ pub type ConstrainedSubst = chalk_ir::ConstrainedSubst; pub type Guidance = chalk_solve::Guidance; pub type WhereClause = chalk_ir::WhereClause; -// FIXME: get rid of this -pub fn subst_prefix(s: &Substitution, n: usize) -> Substitution { - Substitution::from_iter( - Interner, - s.as_slice(Interner)[..std::cmp::min(s.len(Interner), n)].iter().cloned(), - ) -} - /// Return an index of a parameter in the generic type parameter list by it's id. pub fn param_idx(db: &dyn HirDatabase, id: TypeOrConstParamId) -> Option { generics(db.upcast(), id.parent).param_idx(id) @@ -203,6 +210,7 @@ pub(crate) fn make_binders>( pub struct CallableSig { params_and_return: Arc<[Ty]>, is_varargs: bool, + safety: Safety, } has_interner!(CallableSig); @@ -211,9 +219,14 @@ has_interner!(CallableSig); pub type PolyFnSig = Binders; impl CallableSig { - pub fn from_params_and_return(mut params: Vec, ret: Ty, is_varargs: bool) -> CallableSig { + pub fn from_params_and_return( + mut params: Vec, + ret: Ty, + is_varargs: bool, + safety: Safety, + ) -> CallableSig { params.push(ret); - CallableSig { params_and_return: params.into(), is_varargs } + CallableSig { params_and_return: params.into(), is_varargs, safety } } pub fn from_fn_ptr(fn_ptr: &FnPointer) -> CallableSig { @@ -230,13 +243,14 @@ impl CallableSig { .map(|arg| arg.assert_ty_ref(Interner).clone()) .collect(), is_varargs: fn_ptr.sig.variadic, + safety: fn_ptr.sig.safety, } } pub fn to_fn_ptr(&self) -> FnPointer { FnPointer { num_binders: 0, - sig: FnSig { abi: (), safety: Safety::Safe, variadic: self.is_varargs }, + sig: FnSig { abi: (), safety: self.safety, variadic: self.is_varargs }, substitution: FnSubst(Substitution::from_iter( Interner, self.params_and_return.iter().cloned(), @@ -261,7 +275,11 @@ impl TypeFoldable for CallableSig { ) -> Result { let vec = self.params_and_return.to_vec(); let folded = vec.try_fold_with(folder, outer_binder)?; - Ok(CallableSig { params_and_return: folded.into(), is_varargs: self.is_varargs }) + Ok(CallableSig { + params_and_return: folded.into(), + is_varargs: self.is_varargs, + safety: self.safety, + }) } } @@ -382,7 +400,6 @@ pub(crate) fn fold_tys_and_consts + TypeFold pub fn replace_errors_with_variables(t: &T) -> Canonical where T: HasInterner + TypeFoldable + Clone, - T: HasInterner, { use chalk_ir::{ fold::{FallibleTypeFolder, TypeSuperFoldable}, @@ -504,3 +521,64 @@ where }); Canonical { value, binders: chalk_ir::CanonicalVarKinds::from_iter(Interner, kinds) } } + +pub fn callable_sig_from_fnonce( + self_ty: &Ty, + env: Arc, + db: &dyn HirDatabase, +) -> Option { + let krate = env.krate; + 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 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::>(); + + // FIXME: chalk refuses to solve `>::Output == ^0.1`, so we first solve + // `>` 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::>() + } + _ => 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(); + + let ret_ty = db.normalize_projection(projection, env); + + Some(CallableSig::from_params_and_return(params, ret_ty.clone(), 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 223d705b1..baf9842d5 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -227,13 +227,17 @@ impl<'a> TyLoweringContext<'a> { .intern(Interner) } TypeRef::Placeholder => TyKind::Error.intern(Interner), - TypeRef::Fn(params, is_varargs) => { + &TypeRef::Fn(ref params, variadic, is_unsafe) => { let substs = self.with_shifted_in(DebruijnIndex::ONE, |ctx| { Substitution::from_iter(Interner, params.iter().map(|(_, tr)| ctx.lower_ty(tr))) }); TyKind::Function(FnPointer { num_binders: 0, // FIXME lower `for<'a> fn()` correctly - sig: FnSig { abi: (), safety: Safety::Safe, variadic: *is_varargs }, + sig: FnSig { + abi: (), + safety: if is_unsafe { Safety::Unsafe } else { Safety::Safe }, + variadic, + }, substitution: FnSubst(substs), }) .intern(Interner) @@ -447,12 +451,31 @@ impl<'a> TyLoweringContext<'a> { .db .trait_data(trait_ref.hir_trait_id()) .associated_type_by_name(segment.name); + match found { Some(associated_ty) => { - // FIXME handle type parameters on the segment + // 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 + // this point (`trait_ref.substitution`). + let substitution = self.substs_from_path_segment( + segment, + Some(associated_ty.into()), + false, + None, + ); + let len_self = + generics(self.db.upcast(), associated_ty.into()).len_self(); + let substitution = Substitution::from_iter( + Interner, + substitution + .iter(Interner) + .take(len_self) + .chain(trait_ref.substitution.iter(Interner)), + ); TyKind::Alias(AliasTy::Projection(ProjectionTy { associated_ty_id: to_assoc_type_id(associated_ty), - substitution: trait_ref.substitution, + substitution, })) .intern(Interner) } @@ -590,36 +613,48 @@ impl<'a> TyLoweringContext<'a> { res, Some(segment.name.clone()), move |name, t, associated_ty| { - if name == segment.name { - 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(), - self.resolver - .generic_def() - .expect("there should be generics if there's a generic param"), - ); - let s = generics.placeholder_subst(self.db); - s.apply(t.substitution.clone(), Interner) - } - ParamLoweringMode::Variable => t.substitution.clone(), - }; - // 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); - // FIXME handle type parameters on the segment - Some( - TyKind::Alias(AliasTy::Projection(ProjectionTy { - associated_ty_id: to_assoc_type_id(associated_ty), - substitution: substs, - })) - .intern(Interner), - ) - } else { - None + if name != segment.name { + return None; } + + // 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 + // this point (`t.substitution`). + let substs = self.substs_from_path_segment( + segment.clone(), + Some(associated_ty.into()), + false, + None, + ); + + let len_self = generics(self.db.upcast(), associated_ty.into()).len_self(); + + let substs = Substitution::from_iter( + Interner, + substs.iter(Interner).take(len_self).chain(t.substitution.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), + substitution: substs, + })) + .intern(Interner), + ) }, ); @@ -777,7 +812,15 @@ impl<'a> TyLoweringContext<'a> { // handle defaults. In expression or pattern path segments without // explicitly specified type arguments, missing type arguments are inferred // (i.e. defaults aren't used). - if !infer_args || had_explicit_args { + // Generic parameters for associated types are not supposed to have defaults, so we just + // ignore them. + let is_assoc_ty = if let GenericDefId::TypeAliasId(id) = def { + let container = id.lookup(self.db.upcast()).container; + matches!(container, ItemContainerId::TraitId(_)) + } else { + false + }; + if !is_assoc_ty && (!infer_args || had_explicit_args) { let defaults = self.db.generic_defaults(def); assert_eq!(total_len, defaults.len()); let parent_from = item_len - substs.len(); @@ -966,9 +1009,28 @@ impl<'a> TyLoweringContext<'a> { None => return SmallVec::new(), Some(t) => t, }; + // 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 + // this point (`super_trait_ref.substitution`). + let substitution = self.substs_from_path_segment( + // FIXME: This is hack. We shouldn't really build `PathSegment` directly. + PathSegment { name: &binding.name, args_and_bindings: binding.args.as_deref() }, + Some(associated_ty.into()), + false, // this is not relevant + Some(super_trait_ref.self_type_parameter(Interner)), + ); + let self_params = generics(self.db.upcast(), associated_ty.into()).len_self(); + let substitution = Substitution::from_iter( + Interner, + substitution + .iter(Interner) + .take(self_params) + .chain(super_trait_ref.substitution.iter(Interner)), + ); let projection_ty = ProjectionTy { associated_ty_id: to_assoc_type_id(associated_ty), - substitution: super_trait_ref.substitution, + substitution, }; let mut preds: SmallVec<[_; 1]> = SmallVec::with_capacity( binding.type_ref.as_ref().map_or(0, |_| 1) + binding.bounds.len(), @@ -1515,7 +1577,12 @@ fn fn_sig_for_fn(db: &dyn HirDatabase, def: FunctionId) -> PolyFnSig { .with_type_param_mode(ParamLoweringMode::Variable); let ret = ctx_ret.lower_ty(&data.ret_type); let generics = generics(db.upcast(), def.into()); - let sig = CallableSig::from_params_and_return(params, ret, data.is_varargs()); + let sig = CallableSig::from_params_and_return( + params, + ret, + data.is_varargs(), + if data.has_unsafe_kw() { Safety::Unsafe } else { Safety::Safe }, + ); make_binders(db, &generics, sig) } @@ -1559,7 +1626,7 @@ fn fn_sig_for_struct_constructor(db: &dyn HirDatabase, def: StructId) -> PolyFnS TyLoweringContext::new(db, &resolver).with_type_param_mode(ParamLoweringMode::Variable); let params = fields.iter().map(|(_, field)| ctx.lower_ty(&field.type_ref)).collect::>(); let (ret, binders) = type_for_adt(db, def.into()).into_value_and_skipped_binders(); - Binders::new(binders, CallableSig::from_params_and_return(params, ret, false)) + Binders::new(binders, CallableSig::from_params_and_return(params, ret, false, Safety::Safe)) } /// Build the type of a tuple struct constructor. @@ -1586,7 +1653,7 @@ fn fn_sig_for_enum_variant_constructor(db: &dyn HirDatabase, def: EnumVariantId) TyLoweringContext::new(db, &resolver).with_type_param_mode(ParamLoweringMode::Variable); let params = fields.iter().map(|(_, field)| ctx.lower_ty(&field.type_ref)).collect::>(); let (ret, binders) = type_for_adt(db, def.parent.into()).into_value_and_skipped_binders(); - Binders::new(binders, CallableSig::from_params_and_return(params, ret, false)) + Binders::new(binders, CallableSig::from_params_and_return(params, ret, false, Safety::Safe)) } /// Build the type of a tuple enum variant constructor. 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 3a1a3f4fd..8bcfa2728 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 @@ -22,10 +22,10 @@ use crate::{ from_foreign_def_id, infer::{unify::InferenceTable, Adjust, Adjustment, AutoBorrow, OverloadedDeref, PointerCast}, primitive::{FloatTy, IntTy, UintTy}, - static_lifetime, + static_lifetime, to_chalk_trait_id, utils::all_super_traits, AdtId, Canonical, CanonicalVarKinds, DebruijnIndex, ForeignDefId, InEnvironment, Interner, - Scalar, TraitEnvironment, TraitRefExt, Ty, TyBuilder, TyExt, TyKind, + Scalar, Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder, TyExt, TyKind, }; /// This is used as a key for indexing impls. @@ -541,7 +541,7 @@ pub struct ReceiverAdjustments { impl ReceiverAdjustments { pub(crate) fn apply(&self, table: &mut InferenceTable<'_>, ty: Ty) -> (Ty, Vec) { - let mut ty = ty; + let mut ty = table.resolve_ty_shallow(&ty); let mut adjust = Vec::new(); for _ in 0..self.autoderefs { match autoderef::autoderef_step(table, ty.clone()) { @@ -624,52 +624,76 @@ pub(crate) fn iterate_method_candidates( slot } +/// 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. pub fn lookup_impl_method( - self_ty: &Ty, db: &dyn HirDatabase, env: Arc, - trait_: TraitId, + func: FunctionId, + fn_subst: Substitution, +) -> FunctionId { + let trait_id = match func.lookup(db.upcast()).container { + ItemContainerId::TraitId(id) => id, + _ => return func, + }; + let trait_params = db.generic_params(trait_id.into()).type_or_consts.len(); + let fn_params = fn_subst.len(Interner) - trait_params; + let trait_ref = TraitRef { + trait_id: to_chalk_trait_id(trait_id), + substitution: Substitution::from_iter(Interner, fn_subst.iter(Interner).skip(fn_params)), + }; + + let name = &db.function_data(func).name; + lookup_impl_method_for_trait_ref(trait_ref, db, env, name).unwrap_or(func) +} + +fn lookup_impl_method_for_trait_ref( + trait_ref: TraitRef, + db: &dyn HirDatabase, + env: Arc, name: &Name, ) -> Option { - let self_ty_fp = TyFingerprint::for_trait_impl(self_ty)?; - let trait_impls = db.trait_impls_in_deps(env.krate); - let impls = trait_impls.for_trait_and_self_ty(trait_, self_ty_fp); - let mut table = InferenceTable::new(db, env.clone()); - find_matching_impl(impls, &mut table, &self_ty).and_then(|data| { - data.items.iter().find_map(|it| match it { - AssocItemId::FunctionId(f) => (db.function_data(*f).name == *name).then(|| *f), - _ => None, - }) + 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); + let impls = impls.for_trait_and_self_ty(trait_ref.hir_trait_id(), self_ty_fp); + + let table = InferenceTable::new(db, env); + + 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), + _ => None, }) } fn find_matching_impl( mut impls: impl Iterator, - table: &mut InferenceTable<'_>, - self_ty: &Ty, + mut table: InferenceTable<'_>, + actual_trait_ref: TraitRef, ) -> Option> { let db = table.db; loop { let impl_ = impls.next()?; let r = table.run_in_snapshot(|table| { let impl_data = db.impl_data(impl_); - let substs = + let impl_substs = TyBuilder::subst_for_def(db, impl_, None).fill_with_inference_vars(table).build(); - let impl_ty = db.impl_self_ty(impl_).substitute(Interner, &substs); - - table - .unify(self_ty, &impl_ty) - .then(|| { - let wh_goals = - crate::chalk_db::convert_where_clauses(db, impl_.into(), &substs) - .into_iter() - .map(|b| b.cast(Interner)); + let trait_ref = db + .impl_trait(impl_) + .expect("non-trait method in find_matching_impl") + .substitute(Interner, &impl_substs); - let goal = crate::Goal::all(Interner, wh_goals); + if !table.unify(&trait_ref, &actual_trait_ref) { + return None; + } - table.try_obligation(goal).map(|_| impl_data) - }) - .flatten() + let wcs = crate::chalk_db::convert_where_clauses(db, impl_.into(), &impl_substs) + .into_iter() + .map(|b| b.cast(Interner)); + let goal = crate::Goal::all(Interner, wcs); + table.try_obligation(goal).map(|_| impl_data) }); if r.is_some() { break r; @@ -1214,7 +1238,7 @@ fn is_valid_fn_candidate( let expected_receiver = sig.map(|s| s.params()[0].clone()).substitute(Interner, &fn_subst); - check_that!(table.unify(&receiver_ty, &expected_receiver)); + check_that!(table.unify(receiver_ty, &expected_receiver)); } if let ItemContainerId::ImplId(impl_id) = container { 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 d301595bc..7e3aecc2a 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 @@ -122,6 +122,23 @@ fn test() { ) } +#[test] +fn if_else_adjust_for_branches_discard_type_var() { + check_no_mismatches( + r#" +fn test() { + let f = || { + if true { + &"" + } else { + "" + } + }; +} +"#, + ); +} + #[test] fn match_first_coerce() { check_no_mismatches( @@ -182,6 +199,22 @@ fn test() { ); } +#[test] +fn match_adjust_for_branches_discard_type_var() { + check_no_mismatches( + r#" +fn test() { + let f = || { + match 0i32 { + 0i32 => &"", + _ => "", + } + }; +} +"#, + ); +} + #[test] fn return_coerce_unknown() { check_types( @@ -357,7 +390,7 @@ fn test() { let f: fn(u32) -> isize = foo; // ^^^ adjustments: Pointer(ReifyFnPointer) let f: unsafe fn(u32) -> isize = foo; - // ^^^ adjustments: Pointer(ReifyFnPointer) + // ^^^ adjustments: Pointer(ReifyFnPointer), Pointer(UnsafeFnPointer) }", ); } @@ -388,7 +421,10 @@ fn coerce_closure_to_fn_ptr() { check_no_mismatches( r" fn test() { - let f: fn(u32) -> isize = |x| { 1 }; + let f: fn(u32) -> u32 = |x| x; + // ^^^^^ adjustments: Pointer(ClosureFnPointer(Safe)) + let f: unsafe fn(u32) -> u32 = |x| x; + // ^^^^^ adjustments: Pointer(ClosureFnPointer(Unsafe)) }", ); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/display_source_code.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/display_source_code.rs index 8a8ff08cf..425432479 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/display_source_code.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/display_source_code.rs @@ -196,3 +196,34 @@ fn test( "#, ); } + +#[test] +fn projection_type_correct_arguments_order() { + check_types_source_code( + r#" +trait Foo { + type Assoc; +} +fn f>(a: T::Assoc) { + a; + //^ >::Assoc +} +"#, + ); +} + +#[test] +fn generic_associated_type_binding_in_impl_trait() { + check_types_source_code( + r#" +//- minicore: sized +trait Foo { + type Assoc; +} +fn f(a: impl Foo = i32>) { + a; + //^ impl Foo = i32> +} + "#, + ); +} 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 ac8edb841..5d76d185f 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 @@ -164,16 +164,16 @@ fn infer_associated_method_with_modules() { check_infer( r#" mod a { - struct A; + pub struct A; impl A { pub fn thing() -> A { A {} }} } mod b { - struct B; + pub struct B; impl B { pub fn thing() -> u32 { 99 }} - mod c { - struct C; + pub mod c { + pub struct C; impl C { pub fn thing() -> C { C {} }} } } @@ -186,22 +186,22 @@ fn infer_associated_method_with_modules() { } "#, expect![[r#" - 55..63 '{ A {} }': A - 57..61 'A {}': A - 125..131 '{ 99 }': u32 - 127..129 '99': u32 - 201..209 '{ C {} }': C - 203..207 'C {}': C - 240..324 '{ ...g(); }': () - 250..251 'x': A - 254..265 'a::A::thing': fn thing() -> A - 254..267 'a::A::thing()': A - 277..278 'y': u32 - 281..292 'b::B::thing': fn thing() -> u32 - 281..294 'b::B::thing()': u32 - 304..305 'z': C - 308..319 'c::C::thing': fn thing() -> C - 308..321 'c::C::thing()': C + 59..67 '{ A {} }': A + 61..65 'A {}': A + 133..139 '{ 99 }': u32 + 135..137 '99': u32 + 217..225 '{ C {} }': C + 219..223 'C {}': C + 256..340 '{ ...g(); }': () + 266..267 'x': A + 270..281 'a::A::thing': fn thing() -> A + 270..283 'a::A::thing()': A + 293..294 'y': u32 + 297..308 'b::B::thing': fn thing() -> u32 + 297..310 'b::B::thing()': u32 + 320..321 'z': C + 324..335 'c::C::thing': fn thing() -> C + 324..337 'c::C::thing()': C "#]], ); } 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 a155adcec..4e4639745 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 @@ -1707,3 +1707,19 @@ impl Trait for [T; N] { "#, ); } + +#[test] +fn unsize_array_with_inference_variable() { + check_types( + r#" +//- minicore: try, slice +use core::ops::ControlFlow; +fn foo() -> ControlFlow<(), [usize; 1]> { loop {} } +fn bar() -> ControlFlow<(), ()> { + let a = foo()?.len(); + //^ usize + ControlFlow::Continue(()) +} +"#, + ); +} 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 080e2ac1b..d7431443b 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 @@ -214,7 +214,7 @@ fn infer_paths() { fn a() -> u32 { 1 } mod b { - fn c() -> u32 { 1 } + pub fn c() -> u32 { 1 } } fn test() { @@ -225,13 +225,13 @@ fn test() { expect![[r#" 14..19 '{ 1 }': u32 16..17 '1': u32 - 47..52 '{ 1 }': u32 - 49..50 '1': u32 - 66..90 '{ ...c(); }': () - 72..73 'a': fn a() -> u32 - 72..75 'a()': u32 - 81..85 'b::c': fn c() -> u32 - 81..87 'b::c()': u32 + 51..56 '{ 1 }': u32 + 53..54 '1': u32 + 70..94 '{ ...c(); }': () + 76..77 'a': fn a() -> u32 + 76..79 'a()': u32 + 85..89 'b::c': fn c() -> u32 + 85..91 'b::c()': u32 "#]], ); } @@ -1856,7 +1856,7 @@ fn not_shadowing_module_by_primitive() { check_types( r#" //- /str.rs -fn foo() -> u32 {0} +pub fn foo() -> u32 {0} //- /main.rs mod str; 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 555b6972f..3d7194b6f 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 @@ -1706,7 +1706,7 @@ fn where_clause_trait_in_scope_for_method_resolution() { check_types( r#" mod foo { - trait Trait { + pub trait Trait { fn foo(&self) -> u32 { 0 } } } @@ -1723,7 +1723,7 @@ fn super_trait_method_resolution() { check_infer( r#" mod foo { - trait SuperTrait { + pub trait SuperTrait { fn foo(&self) -> u32 {} } } @@ -1735,15 +1735,15 @@ fn test(x: T, y: U) { y.foo(); }"#, expect![[r#" - 49..53 'self': &Self - 62..64 '{}': u32 - 181..182 'x': T - 187..188 'y': U - 193..222 '{ ...o(); }': () - 199..200 'x': T - 199..206 'x.foo()': u32 - 212..213 'y': U - 212..219 'y.foo()': u32 + 53..57 'self': &Self + 66..68 '{}': u32 + 185..186 'x': T + 191..192 'y': U + 197..226 '{ ...o(); }': () + 203..204 'x': T + 203..210 'x.foo()': u32 + 216..217 'y': U + 216..223 'y.foo()': u32 "#]], ); } @@ -1754,7 +1754,7 @@ fn super_trait_impl_trait_method_resolution() { r#" //- minicore: sized mod foo { - trait SuperTrait { + pub trait SuperTrait { fn foo(&self) -> u32 {} } } @@ -1764,12 +1764,12 @@ fn test(x: &impl Trait1) { x.foo(); }"#, expect![[r#" - 49..53 'self': &Self - 62..64 '{}': u32 - 115..116 'x': &impl Trait1 - 132..148 '{ ...o(); }': () - 138..139 'x': &impl Trait1 - 138..145 'x.foo()': u32 + 53..57 'self': &Self + 66..68 '{}': u32 + 119..120 'x': &impl Trait1 + 136..152 '{ ...o(); }': () + 142..143 'x': &impl Trait1 + 142..149 'x.foo()': u32 "#]], ); } @@ -3963,3 +3963,124 @@ fn g(t: &(dyn T + Send)) { "#, ); } + +#[test] +fn gats_in_path() { + check_types( + r#" +//- minicore: deref +use core::ops::Deref; +trait PointerFamily { + type Pointer: Deref; +} + +fn f(p: P::Pointer) { + let a = *p; + //^ i32 +} +fn g(p:

::Pointer) { + let a = *p; + //^ i32 +} + "#, + ); +} + +#[test] +fn gats_with_impl_trait() { + // FIXME: the last function (`fn i()`) is not valid Rust as of this writing because you cannot + // specify the same associated type multiple times even if their arguments are different (c.f. + // `fn h()`, which is valid). Reconsider how to treat these invalid types. + check_types( + r#" +//- minicore: deref +use core::ops::Deref; + +trait Trait { + type Assoc: Deref; + fn get(&self) -> Self::Assoc; +} + +fn f(v: impl Trait) { + let a = v.get::().deref(); + //^ &i32 + let a = v.get::().deref(); + //^ &T +} +fn g<'a, T: 'a>(v: impl Trait = &'a T>) { + let a = v.get::(); + //^ &T + let a = v.get::<()>(); + //^ Trait::Assoc<(), impl Trait = &T>> +} +fn h<'a>(v: impl Trait = &'a i32> + Trait = &'a i64>) { + let a = v.get::(); + //^ &i32 + let a = v.get::(); + //^ &i64 +} +fn i<'a>(v: impl Trait = &'a i32, Assoc = &'a i64>) { + let a = v.get::(); + //^ &i32 + let a = v.get::(); + //^ &i64 +} + "#, + ); +} + +#[test] +fn gats_with_dyn() { + // This test is here to keep track of how we infer things despite traits with GATs being not + // object-safe currently. + // FIXME: reconsider how to treat these invalid types. + check_infer_with_mismatches( + r#" +//- minicore: deref +use core::ops::Deref; + +trait Trait { + type Assoc: Deref; + fn get(&self) -> Self::Assoc; +} + +fn f<'a>(v: &dyn Trait = &'a i32>) { + v.get::().deref(); +} + "#, + expect![[r#" + 90..94 'self': &Self + 127..128 'v': &(dyn Trait = &i32>) + 164..195 '{ ...f(); }': () + 170..171 'v': &(dyn Trait = &i32>) + 170..184 'v.get::()': &i32 + 170..192 'v.get:...eref()': &i32 + "#]], + ); +} + +#[test] +fn gats_in_associated_type_binding() { + check_types( + r#" +trait Trait { + type Assoc; + fn get(&self) -> Self::Assoc; +} + +fn f(t: T) +where + T: Trait = u32>, + T: Trait = usize>, +{ + let a = t.get::(); + //^ u32 + let a = t.get::(); + //^ usize + let a = t.get::<()>(); + //^ Trait::Assoc<(), T> +} + + "#, + ); +} 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 547850b02..92711a24f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tls.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tls.rs @@ -5,7 +5,7 @@ use itertools::Itertools; use crate::{ chalk_db, db::HirDatabase, from_assoc_type_id, from_chalk_trait_id, mapping::from_chalk, - CallableDefId, Interner, + CallableDefId, Interner, ProjectionTyExt, }; use hir_def::{AdtId, ItemContainerId, Lookup, TypeAliasId}; @@ -63,17 +63,31 @@ impl DebugContext<'_> { ItemContainerId::TraitId(t) => t, _ => panic!("associated type not in trait"), }; - let trait_data = self.0.trait_data(trait_); - let params = projection_ty.substitution.as_slice(Interner); - write!(fmt, "<{:?} as {}", ¶ms[0], trait_data.name,)?; - if params.len() > 1 { + let trait_name = &self.0.trait_data(trait_).name; + 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)?; + if trait_params.len() > 1 { + write!( + fmt, + "<{}>", + trait_params[1..].iter().format_with(", ", |x, f| f(&format_args!("{:?}", x))), + )?; + } + write!(fmt, ">::{}", type_alias_data.name)?; + + let proj_params_count = projection_ty.substitution.len(Interner) - trait_params.len(); + let proj_params = &projection_ty.substitution.as_slice(Interner)[..proj_params_count]; + if !proj_params.is_empty() { write!( fmt, "<{}>", - ¶ms[1..].iter().format_with(", ", |x, f| f(&format_args!("{:?}", x))), + proj_params.iter().format_with(", ", |x, f| f(&format_args!("{:?}", x))), )?; } - write!(fmt, ">::{}", type_alias_data.name) + + Ok(()) } pub(crate) fn debug_fn_def_id( -- cgit v1.2.3