diff --git a/crates/hir-def/src/hir/type_ref.rs b/crates/hir-def/src/hir/type_ref.rs index 1fd878ab9b..6b7f03ec00 100644 --- a/crates/hir-def/src/hir/type_ref.rs +++ b/crates/hir-def/src/hir/type_ref.rs @@ -106,6 +106,14 @@ pub struct FnType { pub abi: Option, } +impl FnType { + #[inline] + pub fn split_params_and_ret(&self) -> (&[(Option, TypeRefId)], TypeRefId) { + let (ret, params) = self.params.split_last().expect("should have at least return type"); + (params, ret.1) + } +} + #[derive(Clone, PartialEq, Eq, Hash, Debug)] pub struct ArrayType { pub ty: TypeRefId, diff --git a/crates/hir-ty/src/chalk_db.rs b/crates/hir-ty/src/chalk_db.rs index bf16e08c37..2aa9401eef 100644 --- a/crates/hir-ty/src/chalk_db.rs +++ b/crates/hir-ty/src/chalk_db.rs @@ -27,6 +27,7 @@ use crate::{ db::{HirDatabase, InternedCoroutine}, from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, generics::generics, + lower::LifetimeElisionKind, make_binders, make_single_type_binders, mapping::{ToChalk, TypeAliasAsValue, from_chalk}, method_resolution::{ALL_FLOAT_FPS, ALL_INT_FPS, TraitImpls, TyFingerprint}, @@ -632,9 +633,14 @@ pub(crate) fn associated_ty_data_query( let type_alias_data = db.type_alias_signature(type_alias); let generic_params = generics(db, type_alias.into()); let resolver = hir_def::resolver::HasResolver::resolver(type_alias, db); - let mut ctx = - crate::TyLoweringContext::new(db, &resolver, &type_alias_data.store, type_alias.into()) - .with_type_param_mode(crate::lower::ParamLoweringMode::Variable); + let mut ctx = crate::TyLoweringContext::new( + db, + &resolver, + &type_alias_data.store, + type_alias.into(), + LifetimeElisionKind::AnonymousReportError, + ) + .with_type_param_mode(crate::lower::ParamLoweringMode::Variable); let trait_subst = TyBuilder::subst_for_def(db, trait_, None) .fill_with_bound_vars(crate::DebruijnIndex::INNERMOST, 0) diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs index cad2e3ce99..38807b6f72 100644 --- a/crates/hir-ty/src/infer.rs +++ b/crates/hir-ty/src/infer.rs @@ -34,8 +34,8 @@ use chalk_ir::{ }; use either::Either; use hir_def::{ - AdtId, AssocItemId, DefWithBodyId, FieldId, FunctionId, GenericDefId, GenericParamId, ImplId, - ItemContainerId, Lookup, TraitId, TupleFieldId, TupleId, TypeAliasId, VariantId, + AdtId, AssocItemId, ConstId, DefWithBodyId, FieldId, FunctionId, GenericDefId, GenericParamId, + ImplId, ItemContainerId, Lookup, TraitId, TupleFieldId, TupleId, TypeAliasId, VariantId, builtin_type::{BuiltinInt, BuiltinType, BuiltinUint}, expr_store::{Body, ExpressionStore, HygieneId, path::Path}, hir::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, LabelId, PatId}, @@ -67,9 +67,9 @@ use crate::{ expr::ExprIsRead, unify::InferenceTable, }, - lower::{GenericArgsPosition, ImplTraitLoweringMode, diagnostics::TyLoweringDiagnostic}, + lower::{ImplTraitLoweringMode, LifetimeElisionKind, diagnostics::TyLoweringDiagnostic}, mir::MirSpan, - to_assoc_type_id, + static_lifetime, to_assoc_type_id, traits::FnTrait, utils::UnevaluatedConstEvaluatorFolder, }; @@ -96,7 +96,7 @@ pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc { ctx.collect_fn(f); } - DefWithBodyId::ConstId(c) => ctx.collect_const(&db.const_signature(c)), + DefWithBodyId::ConstId(c) => ctx.collect_const(c, &db.const_signature(c)), DefWithBodyId::StaticId(s) => ctx.collect_static(&db.static_signature(s)), DefWithBodyId::VariantId(v) => { ctx.return_ty = TyBuilder::builtin( @@ -899,9 +899,13 @@ impl<'a> InferenceContext<'a> { result } - fn collect_const(&mut self, data: &ConstSignature) { - let return_ty = - self.make_ty(data.type_ref, &data.store, InferenceTyDiagnosticSource::Signature); + fn collect_const(&mut self, id: ConstId, data: &ConstSignature) { + let return_ty = self.make_ty( + data.type_ref, + &data.store, + InferenceTyDiagnosticSource::Signature, + LifetimeElisionKind::for_const(id.loc(self.db).container), + ); // Constants might be defining usage sites of TAITs. self.make_tait_coercion_table(iter::once(&return_ty)); @@ -910,8 +914,12 @@ impl<'a> InferenceContext<'a> { } fn collect_static(&mut self, data: &StaticSignature) { - let return_ty = - self.make_ty(data.type_ref, &data.store, InferenceTyDiagnosticSource::Signature); + let return_ty = self.make_ty( + data.type_ref, + &data.store, + InferenceTyDiagnosticSource::Signature, + LifetimeElisionKind::Elided(static_lifetime()), + ); // Statics might be defining usage sites of TAITs. self.make_tait_coercion_table(iter::once(&return_ty)); @@ -921,12 +929,15 @@ impl<'a> InferenceContext<'a> { fn collect_fn(&mut self, func: FunctionId) { let data = self.db.function_signature(func); - let mut param_tys = - self.with_ty_lowering(&data.store, InferenceTyDiagnosticSource::Signature, |ctx| { + let mut param_tys = self.with_ty_lowering( + &data.store, + InferenceTyDiagnosticSource::Signature, + LifetimeElisionKind::for_fn_params(&data), + |ctx| { ctx.type_param_mode(ParamLoweringMode::Placeholder); - ctx.in_fn_signature = true; data.params.iter().map(|&type_ref| ctx.lower_ty(type_ref)).collect::>() - }); + }, + ); // 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 @@ -967,10 +978,10 @@ impl<'a> InferenceContext<'a> { let return_ty = self.with_ty_lowering( &data.store, InferenceTyDiagnosticSource::Signature, + LifetimeElisionKind::for_fn_ret(), |ctx| { ctx.type_param_mode(ParamLoweringMode::Placeholder) .impl_trait_mode(ImplTraitLoweringMode::Opaque); - ctx.in_fn_signature = true; ctx.lower_ty(return_ty) }, ); @@ -1304,6 +1315,7 @@ impl<'a> InferenceContext<'a> { &mut self, store: &ExpressionStore, types_source: InferenceTyDiagnosticSource, + lifetime_elision: LifetimeElisionKind, f: impl FnOnce(&mut TyLoweringContext<'_>) -> R, ) -> R { let mut ctx = TyLoweringContext::new( @@ -1313,12 +1325,18 @@ impl<'a> InferenceContext<'a> { &self.diagnostics, types_source, self.generic_def, + lifetime_elision, ); f(&mut ctx) } fn with_body_ty_lowering(&mut self, f: impl FnOnce(&mut TyLoweringContext<'_>) -> R) -> R { - self.with_ty_lowering(self.body, InferenceTyDiagnosticSource::Body, f) + self.with_ty_lowering( + self.body, + InferenceTyDiagnosticSource::Body, + LifetimeElisionKind::Infer, + f, + ) } fn make_ty( @@ -1326,29 +1344,46 @@ impl<'a> InferenceContext<'a> { type_ref: TypeRefId, store: &ExpressionStore, type_source: InferenceTyDiagnosticSource, + lifetime_elision: LifetimeElisionKind, ) -> Ty { - let ty = self.with_ty_lowering(store, type_source, |ctx| ctx.lower_ty(type_ref)); + let ty = self + .with_ty_lowering(store, type_source, lifetime_elision, |ctx| ctx.lower_ty(type_ref)); let ty = self.insert_type_vars(ty); self.normalize_associated_types_in(ty) } fn make_body_ty(&mut self, type_ref: TypeRefId) -> Ty { - self.make_ty(type_ref, self.body, InferenceTyDiagnosticSource::Body) + self.make_ty( + type_ref, + self.body, + InferenceTyDiagnosticSource::Body, + LifetimeElisionKind::Infer, + ) } fn make_body_const(&mut self, const_ref: ConstRef, ty: Ty) -> Const { - let const_ = self.with_ty_lowering(self.body, InferenceTyDiagnosticSource::Body, |ctx| { - ctx.type_param_mode = ParamLoweringMode::Placeholder; - ctx.lower_const(&const_ref, ty) - }); + let const_ = self.with_ty_lowering( + self.body, + InferenceTyDiagnosticSource::Body, + LifetimeElisionKind::Infer, + |ctx| { + ctx.type_param_mode = ParamLoweringMode::Placeholder; + ctx.lower_const(&const_ref, ty) + }, + ); self.insert_type_vars(const_) } fn make_path_as_body_const(&mut self, path: &Path, ty: Ty) -> Const { - let const_ = self.with_ty_lowering(self.body, InferenceTyDiagnosticSource::Body, |ctx| { - ctx.type_param_mode = ParamLoweringMode::Placeholder; - ctx.lower_path_as_const(path, ty) - }); + let const_ = self.with_ty_lowering( + self.body, + InferenceTyDiagnosticSource::Body, + LifetimeElisionKind::Infer, + |ctx| { + ctx.type_param_mode = ParamLoweringMode::Placeholder; + ctx.lower_path_as_const(path, ty) + }, + ); self.insert_type_vars(const_) } @@ -1357,9 +1392,12 @@ impl<'a> InferenceContext<'a> { } fn make_body_lifetime(&mut self, lifetime_ref: &LifetimeRef) -> Lifetime { - let lt = self.with_ty_lowering(self.body, InferenceTyDiagnosticSource::Body, |ctx| { - ctx.lower_lifetime(lifetime_ref) - }); + let lt = self.with_ty_lowering( + self.body, + InferenceTyDiagnosticSource::Body, + LifetimeElisionKind::Infer, + |ctx| ctx.lower_lifetime(lifetime_ref), + ); self.insert_type_vars(lt) } @@ -1529,8 +1567,9 @@ impl<'a> InferenceContext<'a> { &self.diagnostics, InferenceTyDiagnosticSource::Body, self.generic_def, + LifetimeElisionKind::Infer, ); - let mut path_ctx = ctx.at_path(path, node, GenericArgsPosition::Value); + let mut path_ctx = ctx.at_path(path, node); let (resolution, unresolved) = if value_ns { let Some(res) = path_ctx.resolve_path_in_value_ns(HygieneId::ROOT) else { return (self.err_ty(), None); @@ -1538,14 +1577,14 @@ impl<'a> InferenceContext<'a> { match res { ResolveValueResult::ValueNs(value, _) => match value { ValueNs::EnumVariantId(var) => { - let substs = path_ctx.substs_from_path(var.into(), true); + let substs = path_ctx.substs_from_path(var.into(), true, false); drop(ctx); let ty = self.db.ty(var.lookup(self.db).parent.into()); let ty = self.insert_type_vars(ty.substitute(Interner, &substs)); return (ty, Some(var.into())); } ValueNs::StructId(strukt) => { - let substs = path_ctx.substs_from_path(strukt.into(), true); + let substs = path_ctx.substs_from_path(strukt.into(), true, false); drop(ctx); let ty = self.db.ty(strukt.into()); let ty = self.insert_type_vars(ty.substitute(Interner, &substs)); @@ -1567,21 +1606,21 @@ impl<'a> InferenceContext<'a> { }; return match resolution { TypeNs::AdtId(AdtId::StructId(strukt)) => { - let substs = path_ctx.substs_from_path(strukt.into(), true); + let substs = path_ctx.substs_from_path(strukt.into(), true, false); drop(ctx); let ty = self.db.ty(strukt.into()); let ty = self.insert_type_vars(ty.substitute(Interner, &substs)); forbid_unresolved_segments((ty, Some(strukt.into())), unresolved) } TypeNs::AdtId(AdtId::UnionId(u)) => { - let substs = path_ctx.substs_from_path(u.into(), true); + let substs = path_ctx.substs_from_path(u.into(), true, false); drop(ctx); let ty = self.db.ty(u.into()); let ty = self.insert_type_vars(ty.substitute(Interner, &substs)); forbid_unresolved_segments((ty, Some(u.into())), unresolved) } TypeNs::EnumVariantId(var) => { - let substs = path_ctx.substs_from_path(var.into(), true); + let substs = path_ctx.substs_from_path(var.into(), true, false); drop(ctx); let ty = self.db.ty(var.lookup(self.db).parent.into()); let ty = self.insert_type_vars(ty.substitute(Interner, &substs)); @@ -1665,7 +1704,7 @@ impl<'a> InferenceContext<'a> { never!("resolver should always resolve lang item paths"); return (self.err_ty(), None); }; - let substs = path_ctx.substs_from_path_segment(it.into(), true, None); + let substs = path_ctx.substs_from_path_segment(it.into(), true, None, false); drop(ctx); let ty = self.db.ty(it.into()); let ty = self.insert_type_vars(ty.substitute(Interner, &substs)); diff --git a/crates/hir-ty/src/infer/closure.rs b/crates/hir-ty/src/infer/closure.rs index 59ec3adcc4..e7b776a356 100644 --- a/crates/hir-ty/src/infer/closure.rs +++ b/crates/hir-ty/src/infer/closure.rs @@ -440,6 +440,8 @@ impl InferenceContext<'_> { // collect explicitly written argument types for arg_type in arg_types.iter() { let arg_ty = match arg_type { + // FIXME: I think rustc actually lowers closure params with `LifetimeElisionKind::AnonymousCreateParameter` + // (but the return type with infer). Some(type_ref) => self.make_body_ty(*type_ref), None => self.table.new_type_var(), }; diff --git a/crates/hir-ty/src/infer/diagnostics.rs b/crates/hir-ty/src/infer/diagnostics.rs index 2c633a03c5..e3c4f5562d 100644 --- a/crates/hir-ty/src/infer/diagnostics.rs +++ b/crates/hir-ty/src/infer/diagnostics.rs @@ -12,7 +12,7 @@ use hir_def::expr_store::path::Path; use hir_def::{hir::ExprOrPatId, resolver::Resolver}; use la_arena::{Idx, RawIdx}; -use crate::lower::GenericArgsPosition; +use crate::lower::LifetimeElisionKind; use crate::{ InferenceDiagnostic, InferenceTyDiagnosticSource, TyLoweringContext, TyLoweringDiagnostic, db::HirDatabase, @@ -66,8 +66,13 @@ impl<'a> InferenceTyLoweringContext<'a> { diagnostics: &'a Diagnostics, source: InferenceTyDiagnosticSource, generic_def: GenericDefId, + lifetime_elision: LifetimeElisionKind, ) -> Self { - Self { ctx: TyLoweringContext::new(db, resolver, store, generic_def), diagnostics, source } + Self { + ctx: TyLoweringContext::new(db, resolver, store, generic_def, lifetime_elision), + diagnostics, + source, + } } #[inline] @@ -75,7 +80,6 @@ impl<'a> InferenceTyLoweringContext<'a> { &'b mut self, path: &'b Path, node: ExprOrPatId, - position: GenericArgsPosition, ) -> PathLoweringContext<'b, 'a> { let on_diagnostic = PathDiagnosticCallback { data: Either::Right(PathDiagnosticCallbackData { diagnostics: self.diagnostics, node }), @@ -85,14 +89,13 @@ impl<'a> InferenceTyLoweringContext<'a> { .push(InferenceDiagnostic::PathDiagnostic { node: data.node, diag }); }, }; - PathLoweringContext::new(&mut self.ctx, on_diagnostic, path, position) + PathLoweringContext::new(&mut self.ctx, on_diagnostic, path) } #[inline] pub(super) fn at_path_forget_diagnostics<'b>( &'b mut self, path: &'b Path, - position: GenericArgsPosition, ) -> PathLoweringContext<'b, 'a> { let on_diagnostic = PathDiagnosticCallback { data: Either::Right(PathDiagnosticCallbackData { @@ -101,7 +104,7 @@ impl<'a> InferenceTyLoweringContext<'a> { }), callback: |_data, _, _diag| {}, }; - PathLoweringContext::new(&mut self.ctx, on_diagnostic, path, position) + PathLoweringContext::new(&mut self.ctx, on_diagnostic, path) } #[inline] diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs index 2980549c23..643587e8af 100644 --- a/crates/hir-ty/src/infer/expr.rs +++ b/crates/hir-ty/src/infer/expr.rs @@ -37,7 +37,7 @@ use crate::{ }, lang_items::lang_items_for_bin_op, lower::{ - GenericArgsPosition, ParamLoweringMode, lower_to_chalk_mutability, + LifetimeElisionKind, ParamLoweringMode, lower_to_chalk_mutability, path::{GenericArgsLowerer, TypeLikeConst, substs_from_args_and_bindings}, }, mapping::{ToChalk, from_chalk}, @@ -2162,6 +2162,23 @@ impl InferenceContext<'_> { } } } + + fn report_elided_lifetimes_in_path( + &mut self, + _def: GenericDefId, + _expected_count: u32, + _hard_error: bool, + ) { + unreachable!("we set `LifetimeElisionKind::Infer`") + } + + fn report_elision_failure(&mut self, _def: GenericDefId, _expected_count: u32) { + unreachable!("we set `LifetimeElisionKind::Infer`") + } + + fn report_missing_lifetime(&mut self, _def: GenericDefId, _expected_count: u32) { + unreachable!("we set `LifetimeElisionKind::Infer`") + } } substs_from_args_and_bindings( @@ -2170,7 +2187,8 @@ impl InferenceContext<'_> { generic_args, def, true, - GenericArgsPosition::MethodCall, + LifetimeElisionKind::Infer, + false, None, &mut LowererCtx { ctx: self, expr }, ) diff --git a/crates/hir-ty/src/infer/path.rs b/crates/hir-ty/src/infer/path.rs index 0e1d23b694..bdaec615ac 100644 --- a/crates/hir-ty/src/infer/path.rs +++ b/crates/hir-ty/src/infer/path.rs @@ -16,7 +16,7 @@ use crate::{ consteval, error_lifetime, generics::generics, infer::diagnostics::InferenceTyLoweringContext as TyLoweringContext, - lower::GenericArgsPosition, + lower::LifetimeElisionKind, method_resolution::{self, VisibleFromModule}, to_chalk_trait_id, }; @@ -96,12 +96,12 @@ impl InferenceContext<'_> { }; let substs = self.with_body_ty_lowering(|ctx| { - let mut path_ctx = ctx.at_path(path, id, GenericArgsPosition::Value); + let mut path_ctx = ctx.at_path(path, id); let last_segment = path.segments().len().checked_sub(1); if let Some(last_segment) = last_segment { path_ctx.set_current_segment(last_segment) } - path_ctx.substs_from_path(value_def, true) + path_ctx.substs_from_path(value_def, true, false) }); let substs = substs.as_slice(Interner); @@ -162,11 +162,12 @@ impl InferenceContext<'_> { &self.diagnostics, InferenceTyDiagnosticSource::Body, self.generic_def, + LifetimeElisionKind::Infer, ); let mut path_ctx = if no_diagnostics { - ctx.at_path_forget_diagnostics(path, GenericArgsPosition::Value) + ctx.at_path_forget_diagnostics(path) } else { - ctx.at_path(path, id, GenericArgsPosition::Value) + ctx.at_path(path, id) }; let (value, self_subst) = if let Some(type_ref) = path.type_anchor() { let last = path.segments().last()?; diff --git a/crates/hir-ty/src/lib.rs b/crates/hir-ty/src/lib.rs index 2cb977b634..128569d55d 100644 --- a/crates/hir-ty/src/lib.rs +++ b/crates/hir-ty/src/lib.rs @@ -94,8 +94,8 @@ pub use infer::{ }; pub use interner::Interner; pub use lower::{ - ImplTraitLoweringMode, ParamLoweringMode, TyDefId, TyLoweringContext, ValueTyDefId, - associated_type_shorthand_candidates, diagnostics::*, + ImplTraitLoweringMode, LifetimeElisionKind, ParamLoweringMode, TyDefId, TyLoweringContext, + ValueTyDefId, associated_type_shorthand_candidates, diagnostics::*, }; pub use mapping::{ from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, from_placeholder_idx, @@ -529,13 +529,13 @@ pub type PolyFnSig = Binders; impl CallableSig { pub fn from_params_and_return( - params: impl ExactSizeIterator, + params: impl Iterator, ret: Ty, is_varargs: bool, safety: Safety, abi: FnAbi, ) -> CallableSig { - let mut params_and_return = Vec::with_capacity(params.len() + 1); + let mut params_and_return = Vec::with_capacity(params.size_hint().0 + 1); params_and_return.extend(params); params_and_return.push(ret); CallableSig { params_and_return: params_and_return.into(), is_varargs, safety, abi } diff --git a/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs index 073a584d8d..eecca32240 100644 --- a/crates/hir-ty/src/lower.rs +++ b/crates/hir-ty/src/lower.rs @@ -25,8 +25,8 @@ use chalk_ir::{ use either::Either; use hir_def::{ AdtId, AssocItemId, CallableDefId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId, - FunctionId, GenericDefId, GenericParamId, HasModule, ImplId, LocalFieldId, Lookup, StaticId, - StructId, TypeAliasId, TypeOrConstParamId, UnionId, VariantId, + FunctionId, GenericDefId, GenericParamId, HasModule, ImplId, ItemContainerId, LocalFieldId, + Lookup, StaticId, StructId, TypeAliasId, TypeOrConstParamId, UnionId, VariantId, builtin_type::BuiltinType, expr_store::{ExpressionStore, path::Path}, hir::generics::{ @@ -35,7 +35,7 @@ use hir_def::{ item_tree::FieldsShape, lang_item::LangItem, resolver::{HasResolver, LifetimeNs, Resolver, TypeNs}, - signatures::{TraitFlags, TypeAliasFlags}, + signatures::{FunctionSignature, TraitFlags, TypeAliasFlags}, type_ref::{ ConstRef, LifetimeRef, LiteralConstRef, PathId, TraitBoundModifier, TraitRef as HirTraitRef, TypeBound, TypeRef, TypeRefId, @@ -86,21 +86,70 @@ impl ImplTraitLoweringState { pub(crate) struct PathDiagnosticCallbackData(TypeRefId); -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub(crate) enum GenericArgsPosition { - Type, - /// E.g. functions. - Value, - MethodCall, - // FIXME: This is a temporary variant we need to work around the lack of lifetime elision. - // The reason for its existence is that in `check_generic_args_len()`, without this, we will - // not infer elide lifetimes. - // They indeed should not be inferred - they should be elided - but we won't elide them either, - // emitting an error instead. rustc elides them in late resolve, and the generics it passes - // to lowering already include them. We probably can't do that, but we will still need to - // account for them when we properly implement lifetime elision. - FnSignature, - OtherSignature, +#[derive(Debug, Clone)] +pub enum LifetimeElisionKind { + /// Create a new anonymous lifetime parameter and reference it. + /// + /// If `report_in_path`, report an error when encountering lifetime elision in a path: + /// ```compile_fail + /// struct Foo<'a> { x: &'a () } + /// async fn foo(x: Foo) {} + /// ``` + /// + /// Note: the error should not trigger when the elided lifetime is in a pattern or + /// expression-position path: + /// ``` + /// struct Foo<'a> { x: &'a () } + /// async fn foo(Foo { x: _ }: Foo<'_>) {} + /// ``` + AnonymousCreateParameter { report_in_path: bool }, + + /// Replace all anonymous lifetimes by provided lifetime. + Elided(Lifetime), + + /// Give a hard error when either `&` or `'_` is written. Used to + /// rule out things like `where T: Foo<'_>`. Does not imply an + /// error on default object bounds (e.g., `Box`). + AnonymousReportError, + + /// Resolves elided lifetimes to `'static` if there are no other lifetimes in scope, + /// otherwise give a warning that the previous behavior of introducing a new early-bound + /// lifetime is a bug and will be removed (if `only_lint` is enabled). + StaticIfNoLifetimeInScope { only_lint: bool }, + + /// Signal we cannot find which should be the anonymous lifetime. + ElisionFailure, + + /// Infer all elided lifetimes. + Infer, +} + +impl LifetimeElisionKind { + #[inline] + pub(crate) fn for_const(const_parent: ItemContainerId) -> LifetimeElisionKind { + match const_parent { + ItemContainerId::ExternBlockId(_) | ItemContainerId::ModuleId(_) => { + LifetimeElisionKind::Elided(static_lifetime()) + } + ItemContainerId::ImplId(_) => { + LifetimeElisionKind::StaticIfNoLifetimeInScope { only_lint: true } + } + ItemContainerId::TraitId(_) => { + LifetimeElisionKind::StaticIfNoLifetimeInScope { only_lint: false } + } + } + } + + #[inline] + pub(crate) fn for_fn_params(data: &FunctionSignature) -> LifetimeElisionKind { + LifetimeElisionKind::AnonymousCreateParameter { report_in_path: data.is_async() } + } + + #[inline] + pub(crate) fn for_fn_ret() -> LifetimeElisionKind { + // FIXME: We should use the elided lifetime here, or `ElisionFailure`. + LifetimeElisionKind::Elided(error_lifetime()) + } } #[derive(Debug)] @@ -120,7 +169,7 @@ pub struct TyLoweringContext<'a> { /// Tracks types with explicit `?Sized` bounds. pub(crate) unsized_types: FxHashSet, pub(crate) diagnostics: Vec, - pub(crate) in_fn_signature: bool, + lifetime_elision: LifetimeElisionKind, } impl<'a> TyLoweringContext<'a> { @@ -129,6 +178,7 @@ impl<'a> TyLoweringContext<'a> { resolver: &'a Resolver, store: &'a ExpressionStore, def: GenericDefId, + lifetime_elision: LifetimeElisionKind, ) -> Self { let impl_trait_mode = ImplTraitLoweringState::new(ImplTraitLoweringMode::Disallowed); let type_param_mode = ParamLoweringMode::Placeholder; @@ -144,7 +194,7 @@ impl<'a> TyLoweringContext<'a> { type_param_mode, unsized_types: FxHashSet::default(), diagnostics: Vec::new(), - in_fn_signature: false, + lifetime_elision, } } @@ -167,6 +217,17 @@ impl<'a> TyLoweringContext<'a> { self.with_debruijn(self.in_binders.shifted_in_from(debruijn), f) } + fn with_lifetime_elision( + &mut self, + lifetime_elision: LifetimeElisionKind, + f: impl FnOnce(&mut TyLoweringContext<'_>) -> T, + ) -> T { + let old_lifetime_elision = mem::replace(&mut self.lifetime_elision, lifetime_elision); + let result = f(self); + self.lifetime_elision = old_lifetime_elision; + result + } + pub fn with_impl_trait_mode(self, impl_trait_mode: ImplTraitLoweringMode) -> Self { Self { impl_trait_mode: ImplTraitLoweringState::new(impl_trait_mode), ..self } } @@ -318,10 +379,18 @@ impl<'a> TyLoweringContext<'a> { TypeRef::Placeholder => TyKind::Error.intern(Interner), TypeRef::Fn(fn_) => { let substs = self.with_shifted_in(DebruijnIndex::ONE, |ctx| { - Substitution::from_iter( - Interner, - fn_.params.iter().map(|&(_, tr)| ctx.lower_ty(tr)), - ) + let (params, ret) = fn_.split_params_and_ret(); + let mut subst = Vec::with_capacity(fn_.params.len()); + ctx.with_lifetime_elision( + LifetimeElisionKind::AnonymousCreateParameter { report_in_path: false }, + |ctx| { + subst.extend(params.iter().map(|&(_, tr)| ctx.lower_ty(tr))); + }, + ); + ctx.with_lifetime_elision(LifetimeElisionKind::for_fn_ret(), |ctx| { + subst.push(ctx.lower_ty(ret)); + }); + Substitution::from_iter(Interner, subst) }); TyKind::Function(FnPointer { num_binders: 0, // FIXME lower `for<'a> fn()` correctly @@ -431,11 +500,6 @@ impl<'a> TyLoweringContext<'a> { self, Self::on_path_diagnostic_callback(path_id.type_ref()), &self.store[path_id], - if self.in_fn_signature { - GenericArgsPosition::FnSignature - } else { - GenericArgsPosition::Type - }, ) } @@ -855,8 +919,14 @@ pub(crate) fn field_types_with_diagnostics_query( }; let generics = generics(db, def); let mut res = ArenaMap::default(); - let mut ctx = TyLoweringContext::new(db, &resolver, &var_data.store, def) - .with_type_param_mode(ParamLoweringMode::Variable); + let mut ctx = TyLoweringContext::new( + db, + &resolver, + &var_data.store, + def, + LifetimeElisionKind::AnonymousReportError, + ) + .with_type_param_mode(ParamLoweringMode::Variable); for (field_id, field_data) in var_data.fields().iter() { res.insert(field_id, make_binders(db, &generics, ctx.lower_ty(field_data.type_ref))); } @@ -879,8 +949,14 @@ pub(crate) fn generic_predicates_for_param_query( ) -> GenericPredicates { let generics = generics(db, def); let resolver = def.resolver(db); - let mut ctx = TyLoweringContext::new(db, &resolver, generics.store(), def) - .with_type_param_mode(ParamLoweringMode::Variable); + let mut ctx = TyLoweringContext::new( + db, + &resolver, + generics.store(), + def, + LifetimeElisionKind::AnonymousReportError, + ) + .with_type_param_mode(ParamLoweringMode::Variable); // we have to filter out all other predicates *first*, before attempting to lower them let predicate = |pred: &_, generics: &Generics, ctx: &mut TyLoweringContext<'_>| match pred { @@ -987,8 +1063,14 @@ pub(crate) fn trait_environment_query( ) -> Arc { let generics = generics(db, def); let resolver = def.resolver(db); - let mut ctx = TyLoweringContext::new(db, &resolver, generics.store(), def) - .with_type_param_mode(ParamLoweringMode::Placeholder); + let mut ctx = TyLoweringContext::new( + db, + &resolver, + generics.store(), + def, + LifetimeElisionKind::AnonymousReportError, + ) + .with_type_param_mode(ParamLoweringMode::Placeholder); let mut traits_in_scope = Vec::new(); let mut clauses = Vec::new(); for maybe_parent_generics in @@ -1086,8 +1168,14 @@ where { let generics = generics(db, def); let resolver = def.resolver(db); - let mut ctx = TyLoweringContext::new(db, &resolver, generics.store(), def) - .with_type_param_mode(ParamLoweringMode::Variable); + let mut ctx = TyLoweringContext::new( + db, + &resolver, + generics.store(), + def, + LifetimeElisionKind::AnonymousReportError, + ) + .with_type_param_mode(ParamLoweringMode::Variable); let mut predicates = Vec::new(); for maybe_parent_generics in @@ -1188,9 +1276,15 @@ pub(crate) fn generic_defaults_with_diagnostics_query( } let resolver = def.resolver(db); - let mut ctx = TyLoweringContext::new(db, &resolver, generic_params.store(), def) - .with_impl_trait_mode(ImplTraitLoweringMode::Disallowed) - .with_type_param_mode(ParamLoweringMode::Variable); + let mut ctx = TyLoweringContext::new( + db, + &resolver, + generic_params.store(), + def, + LifetimeElisionKind::AnonymousReportError, + ) + .with_impl_trait_mode(ImplTraitLoweringMode::Disallowed) + .with_type_param_mode(ParamLoweringMode::Variable); let mut idx = 0; let mut has_any_default = false; let mut defaults = generic_params @@ -1273,17 +1367,27 @@ pub(crate) fn generic_defaults_with_diagnostics_cycle_result( fn fn_sig_for_fn(db: &dyn HirDatabase, def: FunctionId) -> PolyFnSig { let data = db.function_signature(def); let resolver = def.resolver(db); - let mut ctx_params = TyLoweringContext::new(db, &resolver, &data.store, def.into()) - .with_type_param_mode(ParamLoweringMode::Variable); - ctx_params.in_fn_signature = true; + let mut ctx_params = TyLoweringContext::new( + db, + &resolver, + &data.store, + def.into(), + LifetimeElisionKind::for_fn_params(&data), + ) + .with_type_param_mode(ParamLoweringMode::Variable); let params = data.params.iter().map(|&tr| ctx_params.lower_ty(tr)); let ret = match data.ret_type { Some(ret_type) => { - let mut ctx_ret = TyLoweringContext::new(db, &resolver, &data.store, def.into()) - .with_impl_trait_mode(ImplTraitLoweringMode::Opaque) - .with_type_param_mode(ParamLoweringMode::Variable); - ctx_ret.in_fn_signature = true; + let mut ctx_ret = TyLoweringContext::new( + db, + &resolver, + &data.store, + def.into(), + LifetimeElisionKind::for_fn_ret(), + ) + .with_impl_trait_mode(ImplTraitLoweringMode::Opaque) + .with_type_param_mode(ParamLoweringMode::Variable); ctx_ret.lower_ty(ret_type) } None => TyKind::Tuple(0, Substitution::empty(Interner)).intern(Interner), @@ -1316,8 +1420,15 @@ fn type_for_const(db: &dyn HirDatabase, def: ConstId) -> Binders { let data = db.const_signature(def); let generics = generics(db, def.into()); let resolver = def.resolver(db); - let mut ctx = TyLoweringContext::new(db, &resolver, &data.store, def.into()) - .with_type_param_mode(ParamLoweringMode::Variable); + let parent = def.loc(db).container; + let mut ctx = TyLoweringContext::new( + db, + &resolver, + &data.store, + def.into(), + LifetimeElisionKind::for_const(parent), + ) + .with_type_param_mode(ParamLoweringMode::Variable); make_binders(db, &generics, ctx.lower_ty(data.type_ref)) } @@ -1326,18 +1437,20 @@ fn type_for_const(db: &dyn HirDatabase, def: ConstId) -> Binders { fn type_for_static(db: &dyn HirDatabase, def: StaticId) -> Binders { let data = db.static_signature(def); let resolver = def.resolver(db); - let mut ctx = TyLoweringContext::new(db, &resolver, &data.store, def.into()); + let mut ctx = TyLoweringContext::new( + db, + &resolver, + &data.store, + def.into(), + LifetimeElisionKind::Elided(static_lifetime()), + ); Binders::empty(Interner, ctx.lower_ty(data.type_ref)) } fn fn_sig_for_struct_constructor(db: &dyn HirDatabase, def: StructId) -> PolyFnSig { - let struct_data = db.variant_fields(def.into()); - let fields = struct_data.fields(); - let resolver = def.resolver(db); - let mut ctx = TyLoweringContext::new(db, &resolver, &struct_data.store, def.into()) - .with_type_param_mode(ParamLoweringMode::Variable); - let params = fields.iter().map(|(_, field)| ctx.lower_ty(field.type_ref)); + let field_tys = db.field_types(def.into()); + let params = field_tys.iter().map(|(_, ty)| ty.skip_binders().clone()); let (ret, binders) = type_for_adt(db, def.into()).into_value_and_skipped_binders(); Binders::new( binders, @@ -1364,13 +1477,9 @@ fn type_for_struct_constructor(db: &dyn HirDatabase, def: StructId) -> Option PolyFnSig { - let var_data = db.variant_fields(def.into()); - let fields = var_data.fields(); - let resolver = def.resolver(db); + let field_tys = db.field_types(def.into()); + let params = field_tys.iter().map(|(_, ty)| ty.skip_binders().clone()); let parent = def.lookup(db).parent; - let mut ctx = TyLoweringContext::new(db, &resolver, &var_data.store, parent.into()) - .with_type_param_mode(ParamLoweringMode::Variable); - let params = fields.iter().map(|(_, field)| ctx.lower_ty(field.type_ref)); let (ret, binders) = type_for_adt(db, parent.into()).into_value_and_skipped_binders(); Binders::new( binders, @@ -1429,9 +1538,15 @@ pub(crate) fn type_for_type_alias_with_diagnostics_query( } else { let resolver = t.resolver(db); let alias = db.type_alias_signature(t); - let mut ctx = TyLoweringContext::new(db, &resolver, &alias.store, t.into()) - .with_impl_trait_mode(ImplTraitLoweringMode::Opaque) - .with_type_param_mode(ParamLoweringMode::Variable); + let mut ctx = TyLoweringContext::new( + db, + &resolver, + &alias.store, + t.into(), + LifetimeElisionKind::AnonymousReportError, + ) + .with_impl_trait_mode(ImplTraitLoweringMode::Opaque) + .with_type_param_mode(ParamLoweringMode::Variable); let res = alias .ty .map(|type_ref| ctx.lower_ty(type_ref)) @@ -1517,8 +1632,14 @@ pub(crate) fn impl_self_ty_with_diagnostics_query( let impl_data = db.impl_signature(impl_id); let resolver = impl_id.resolver(db); let generics = generics(db, impl_id.into()); - let mut ctx = TyLoweringContext::new(db, &resolver, &impl_data.store, impl_id.into()) - .with_type_param_mode(ParamLoweringMode::Variable); + let mut ctx = TyLoweringContext::new( + db, + &resolver, + &impl_data.store, + impl_id.into(), + LifetimeElisionKind::AnonymousCreateParameter { report_in_path: true }, + ) + .with_type_param_mode(ParamLoweringMode::Variable); ( make_binders(db, &generics, ctx.lower_ty(impl_data.self_ty)), create_diagnostics(ctx.diagnostics), @@ -1537,7 +1658,13 @@ pub(crate) fn const_param_ty_with_diagnostics_query( let (parent_data, store) = db.generic_params_and_store(def.parent()); let data = &parent_data[def.local_id()]; let resolver = def.parent().resolver(db); - let mut ctx = TyLoweringContext::new(db, &resolver, &store, def.parent()); + let mut ctx = TyLoweringContext::new( + db, + &resolver, + &store, + def.parent(), + LifetimeElisionKind::AnonymousReportError, + ); let ty = match data { TypeOrConstParamData::TypeParamData(_) => { never!(); @@ -1566,8 +1693,14 @@ pub(crate) fn impl_trait_with_diagnostics_query( ) -> Option<(Binders, Diagnostics)> { let impl_data = db.impl_signature(impl_id); let resolver = impl_id.resolver(db); - let mut ctx = TyLoweringContext::new(db, &resolver, &impl_data.store, impl_id.into()) - .with_type_param_mode(ParamLoweringMode::Variable); + let mut ctx = TyLoweringContext::new( + db, + &resolver, + &impl_data.store, + impl_id.into(), + LifetimeElisionKind::AnonymousCreateParameter { report_in_path: true }, + ) + .with_type_param_mode(ParamLoweringMode::Variable); let (self_ty, binders) = db.impl_self_ty(impl_id).into_value_and_skipped_binders(); let target_trait = impl_data.target_trait.as_ref()?; let trait_ref = Binders::new(binders, ctx.lower_trait_ref(target_trait, self_ty)?); @@ -1581,9 +1714,10 @@ pub(crate) fn return_type_impl_traits( // FIXME unify with fn_sig_for_fn instead of doing lowering twice, maybe let data = db.function_signature(def); let resolver = def.resolver(db); - let mut ctx_ret = TyLoweringContext::new(db, &resolver, &data.store, def.into()) - .with_impl_trait_mode(ImplTraitLoweringMode::Opaque) - .with_type_param_mode(ParamLoweringMode::Variable); + let mut ctx_ret = + TyLoweringContext::new(db, &resolver, &data.store, def.into(), LifetimeElisionKind::Infer) + .with_impl_trait_mode(ImplTraitLoweringMode::Opaque) + .with_type_param_mode(ParamLoweringMode::Variable); if let Some(ret_type) = data.ret_type { let _ret = ctx_ret.lower_ty(ret_type); } @@ -1603,9 +1737,15 @@ pub(crate) fn type_alias_impl_traits( ) -> Option>> { let data = db.type_alias_signature(def); let resolver = def.resolver(db); - let mut ctx = TyLoweringContext::new(db, &resolver, &data.store, def.into()) - .with_impl_trait_mode(ImplTraitLoweringMode::Opaque) - .with_type_param_mode(ParamLoweringMode::Variable); + let mut ctx = TyLoweringContext::new( + db, + &resolver, + &data.store, + def.into(), + LifetimeElisionKind::AnonymousReportError, + ) + .with_impl_trait_mode(ImplTraitLoweringMode::Opaque) + .with_type_param_mode(ParamLoweringMode::Variable); if let Some(type_ref) = data.ty { let _ty = ctx.lower_ty(type_ref); } diff --git a/crates/hir-ty/src/lower/diagnostics.rs b/crates/hir-ty/src/lower/diagnostics.rs index a5a13d64e0..009f047109 100644 --- a/crates/hir-ty/src/lower/diagnostics.rs +++ b/crates/hir-ty/src/lower/diagnostics.rs @@ -63,6 +63,24 @@ pub enum PathLoweringDiagnostic { /// Whether the `GenericArgs` contains a `Self` arg. has_self_arg: bool, }, + ElidedLifetimesInPath { + generics_source: PathGenericsSource, + def: GenericDefId, + expected_count: u32, + hard_error: bool, + }, + /// An elided lifetimes was used (either implicitly, by not specifying lifetimes, or explicitly, by using `'_`), + /// but lifetime elision could not find a lifetime to replace it with. + ElisionFailure { + generics_source: PathGenericsSource, + def: GenericDefId, + expected_count: u32, + }, + MissingLifetime { + generics_source: PathGenericsSource, + def: GenericDefId, + expected_count: u32, + }, } #[derive(Debug, Clone, Copy, PartialEq, Eq)] diff --git a/crates/hir-ty/src/lower/path.rs b/crates/hir-ty/src/lower/path.rs index c89aad87f7..d3ca438d6e 100644 --- a/crates/hir-ty/src/lower/path.rs +++ b/crates/hir-ty/src/lower/path.rs @@ -27,8 +27,8 @@ use crate::{ db::HirDatabase, error_lifetime, generics::{Generics, generics}, - lower::{GenericArgsPosition, named_associated_type_shorthand_candidates}, - to_assoc_type_id, to_chalk_trait_id, to_placeholder_idx, + lower::{LifetimeElisionKind, named_associated_type_shorthand_candidates}, + static_lifetime, to_assoc_type_id, to_chalk_trait_id, to_placeholder_idx, utils::associated_type_by_name_including_super_traits, }; @@ -52,7 +52,6 @@ pub(crate) struct PathLoweringContext<'a, 'b> { current_segment_idx: usize, /// Contains the previous segment if `current_segment_idx == segments.len()` current_or_prev_segment: PathSegment<'a>, - position: GenericArgsPosition, } impl<'a, 'b> PathLoweringContext<'a, 'b> { @@ -61,7 +60,6 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { ctx: &'a mut TyLoweringContext<'b>, on_diagnostic: PathDiagnosticCallback<'a>, path: &'a Path, - position: GenericArgsPosition, ) -> Self { let segments = path.segments(); let first_segment = segments.first().unwrap_or(PathSegment::MISSING); @@ -72,7 +70,6 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { segments, current_segment_idx: 0, current_or_prev_segment: first_segment, - position, } } @@ -122,6 +119,19 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { .expect("invalid segment passed to PathLoweringContext::set_current_segment()"); } + #[inline] + fn with_lifetime_elision( + &mut self, + lifetime_elision: LifetimeElisionKind, + f: impl FnOnce(&mut PathLoweringContext<'_, '_>) -> T, + ) -> T { + let old_lifetime_elision = + std::mem::replace(&mut self.ctx.lifetime_elision, lifetime_elision); + let result = f(self); + self.ctx.lifetime_elision = old_lifetime_elision; + result + } + pub(crate) fn lower_ty_relative_path( &mut self, ty: Ty, @@ -141,22 +151,6 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { } } - fn prohibit_parenthesized_generic_args(&mut self) -> bool { - if let Some(generic_args) = self.current_or_prev_segment.args_and_bindings { - match generic_args.parenthesized { - GenericArgsParentheses::No => {} - GenericArgsParentheses::ReturnTypeNotation | GenericArgsParentheses::ParenSugar => { - let segment = self.current_segment_u32(); - self.on_diagnostic( - PathLoweringDiagnostic::ParenthesizedGenericArgsWithoutFnTrait { segment }, - ); - return true; - } - } - } - false - } - // When calling this, the current segment is the resolved segment (we don't advance it yet). pub(crate) fn lower_partly_resolved_path( &mut self, @@ -189,6 +183,7 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { associated_ty.into(), false, None, + true, ); let substitution = Substitution::from_iter( Interner, @@ -511,7 +506,7 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { // 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(associated_ty.into(), false, None); + let substs = self.substs_from_path_segment(associated_ty.into(), false, None, true); let substs = Substitution::from_iter( Interner, @@ -539,7 +534,7 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { TyDefId::AdtId(it) => it.into(), TyDefId::TypeAliasId(it) => it.into(), }; - let substs = self.substs_from_path_segment(generic_def, infer_args, None); + let substs = self.substs_from_path_segment(generic_def, infer_args, None, false); self.ctx.db.ty(typeable).substitute(Interner, &substs) } @@ -552,6 +547,7 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { // special-case enum variants resolved: ValueTyDefId, infer_args: bool, + lowering_assoc_type_generics: bool, ) -> Substitution { let prev_current_segment_idx = self.current_segment_idx; let prev_current_segment = self.current_or_prev_segment; @@ -588,7 +584,12 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { var.lookup(self.ctx.db).parent.into() } }; - let result = self.substs_from_path_segment(generic_def, infer_args, None); + let result = self.substs_from_path_segment( + generic_def, + infer_args, + None, + lowering_assoc_type_generics, + ); self.current_segment_idx = prev_current_segment_idx; self.current_or_prev_segment = prev_current_segment; result @@ -599,26 +600,41 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { def: GenericDefId, infer_args: bool, explicit_self_ty: Option, + lowering_assoc_type_generics: bool, ) -> Substitution { - let prohibit_parens = match def { - GenericDefId::TraitId(trait_) => { - // RTN is prohibited anyways if we got here. - let is_rtn = - self.current_or_prev_segment.args_and_bindings.is_some_and(|generics| { - generics.parenthesized == GenericArgsParentheses::ReturnTypeNotation - }); - let is_fn_trait = !self - .ctx - .db - .trait_signature(trait_) - .flags - .contains(TraitFlags::RUSTC_PAREN_SUGAR); - is_rtn || is_fn_trait + let mut lifetime_elision = self.ctx.lifetime_elision.clone(); + + if let Some(args) = self.current_or_prev_segment.args_and_bindings { + if args.parenthesized != GenericArgsParentheses::No { + let prohibit_parens = match def { + GenericDefId::TraitId(trait_) => { + // RTN is prohibited anyways if we got here. + let is_rtn = + args.parenthesized == GenericArgsParentheses::ReturnTypeNotation; + let is_fn_trait = self + .ctx + .db + .trait_signature(trait_) + .flags + .contains(TraitFlags::RUSTC_PAREN_SUGAR); + is_rtn || !is_fn_trait + } + _ => true, + }; + + if prohibit_parens { + let segment = self.current_segment_u32(); + self.on_diagnostic( + PathLoweringDiagnostic::ParenthesizedGenericArgsWithoutFnTrait { segment }, + ); + + return TyBuilder::unknown_subst(self.ctx.db, def); + } + + // `Fn()`-style generics are treated like functions for the purpose of lifetime elision. + lifetime_elision = + LifetimeElisionKind::AnonymousCreateParameter { report_in_path: false }; } - _ => true, - }; - if prohibit_parens && self.prohibit_parenthesized_generic_args() { - return TyBuilder::unknown_subst(self.ctx.db, def); } self.substs_from_args_and_bindings( @@ -627,6 +643,8 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { infer_args, explicit_self_ty, PathGenericsSource::Segment(self.current_segment_u32()), + lowering_assoc_type_generics, + lifetime_elision, ) } @@ -637,6 +655,8 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { infer_args: bool, explicit_self_ty: Option, generics_source: PathGenericsSource, + lowering_assoc_type_generics: bool, + lifetime_elision: LifetimeElisionKind, ) -> Substitution { struct LowererCtx<'a, 'b, 'c> { ctx: &'a mut PathLoweringContext<'b, 'c>, @@ -761,6 +781,36 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { GenericParamId::LifetimeParamId(_) => error_lifetime().cast(Interner), } } + + fn report_elided_lifetimes_in_path( + &mut self, + def: GenericDefId, + expected_count: u32, + hard_error: bool, + ) { + self.ctx.on_diagnostic(PathLoweringDiagnostic::ElidedLifetimesInPath { + generics_source: self.generics_source, + def, + expected_count, + hard_error, + }); + } + + fn report_elision_failure(&mut self, def: GenericDefId, expected_count: u32) { + self.ctx.on_diagnostic(PathLoweringDiagnostic::ElisionFailure { + generics_source: self.generics_source, + def, + expected_count, + }); + } + + fn report_missing_lifetime(&mut self, def: GenericDefId, expected_count: u32) { + self.ctx.on_diagnostic(PathLoweringDiagnostic::MissingLifetime { + generics_source: self.generics_source, + def, + expected_count, + }); + } } substs_from_args_and_bindings( @@ -769,7 +819,8 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { args_and_bindings, def, infer_args, - self.position, + lifetime_elision, + lowering_assoc_type_generics, explicit_self_ty, &mut LowererCtx { ctx: self, generics_source }, ) @@ -789,7 +840,7 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { resolved: TraitId, explicit_self_ty: Ty, ) -> Substitution { - self.substs_from_path_segment(resolved.into(), false, Some(explicit_self_ty)) + self.substs_from_path_segment(resolved.into(), false, Some(explicit_self_ty), false) } pub(super) fn assoc_type_bindings_from_type_bound<'c>( @@ -807,20 +858,25 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { 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_args_and_bindings( - binding.args.as_ref(), - associated_ty.into(), - false, // this is not relevant - Some(super_trait_ref.self_type_parameter(Interner)), - PathGenericsSource::AssocType { - segment: self.current_segment_u32(), - assoc_type: binding_idx as u32, - }, - ); + let substitution = + self.with_lifetime_elision(LifetimeElisionKind::AnonymousReportError, |this| { + // 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`). + this.substs_from_args_and_bindings( + binding.args.as_ref(), + associated_ty.into(), + false, // this is not relevant + Some(super_trait_ref.self_type_parameter(Interner)), + PathGenericsSource::AssocType { + segment: this.current_segment_u32(), + assoc_type: binding_idx as u32, + }, + false, + this.ctx.lifetime_elision.clone(), + ) + }); let substitution = Substitution::from_iter( Interner, super_trait_ref.substitution.iter(Interner).chain( @@ -836,25 +892,48 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { let mut predicates: SmallVec<[_; 1]> = SmallVec::with_capacity( binding.type_ref.as_ref().map_or(0, |_| 1) + binding.bounds.len(), ); + if let Some(type_ref) = binding.type_ref { - match (&self.ctx.store[type_ref], self.ctx.impl_trait_mode.mode) { - (TypeRef::ImplTrait(_), ImplTraitLoweringMode::Disallowed) => (), - (_, ImplTraitLoweringMode::Disallowed | ImplTraitLoweringMode::Opaque) => { - let ty = self.ctx.lower_ty(type_ref); - let alias_eq = - AliasEq { alias: AliasTy::Projection(projection_ty.clone()), ty }; - predicates - .push(crate::wrap_empty_binders(WhereClause::AliasEq(alias_eq))); + let lifetime_elision = + if args_and_bindings.parenthesized == GenericArgsParentheses::ParenSugar { + // `Fn()`-style generics are elided like functions. This is `Output` (we lower to it in hir-def). + LifetimeElisionKind::for_fn_ret() + } else { + self.ctx.lifetime_elision.clone() + }; + self.with_lifetime_elision(lifetime_elision, |this| { + match (&this.ctx.store[type_ref], this.ctx.impl_trait_mode.mode) { + (TypeRef::ImplTrait(_), ImplTraitLoweringMode::Disallowed) => (), + ( + _, + ImplTraitLoweringMode::Disallowed | ImplTraitLoweringMode::Opaque, + ) => { + let ty = this.ctx.lower_ty(type_ref); + let alias_eq = AliasEq { + alias: AliasTy::Projection(projection_ty.clone()), + ty, + }; + predicates.push(crate::wrap_empty_binders(WhereClause::AliasEq( + alias_eq, + ))); + } } + }); + } + + self.with_lifetime_elision(LifetimeElisionKind::AnonymousReportError, |this| { + for bound in binding.bounds.iter() { + predicates.extend( + this.ctx.lower_type_bound( + bound, + TyKind::Alias(AliasTy::Projection(projection_ty.clone())) + .intern(Interner), + false, + ), + ); } - } - for bound in binding.bounds.iter() { - predicates.extend(self.ctx.lower_type_bound( - bound, - TyKind::Alias(AliasTy::Projection(projection_ty.clone())).intern(Interner), - false, - )); - } + }); + predicates }) }) @@ -868,6 +947,17 @@ pub(crate) enum TypeLikeConst<'a> { } pub(crate) trait GenericArgsLowerer { + fn report_elided_lifetimes_in_path( + &mut self, + def: GenericDefId, + expected_count: u32, + hard_error: bool, + ); + + fn report_elision_failure(&mut self, def: GenericDefId, expected_count: u32); + + fn report_missing_lifetime(&mut self, def: GenericDefId, expected_count: u32); + fn report_len_mismatch( &mut self, def: GenericDefId, @@ -905,7 +995,8 @@ fn check_generic_args_len( def: GenericDefId, def_generics: &Generics, infer_args: bool, - position: GenericArgsPosition, + lifetime_elision: &LifetimeElisionKind, + lowering_assoc_type_generics: bool, ctx: &mut impl GenericArgsLowerer, ) -> bool { let mut had_error = false; @@ -921,19 +1012,37 @@ fn check_generic_args_len( } } - // FIXME: Function signature lifetime elision has to be considered here once we have it - let infer_lifetimes = - position != GenericArgsPosition::OtherSignature && provided_lifetimes_count == 0; - - let max_expected_lifetime_args = def_generics.len_lifetimes_self(); - let min_expected_lifetime_args = if infer_lifetimes { 0 } else { max_expected_lifetime_args }; - if provided_lifetimes_count < min_expected_lifetime_args - || max_expected_lifetime_args < provided_lifetimes_count - { + let lifetime_args_len = def_generics.len_lifetimes_self(); + if provided_lifetimes_count == 0 && lifetime_args_len > 0 && !lowering_assoc_type_generics { + // In generic associated types, we never allow inferring the lifetimes. + match lifetime_elision { + &LifetimeElisionKind::AnonymousCreateParameter { report_in_path } => { + ctx.report_elided_lifetimes_in_path(def, lifetime_args_len as u32, report_in_path); + had_error |= report_in_path; + } + LifetimeElisionKind::AnonymousReportError => { + ctx.report_missing_lifetime(def, lifetime_args_len as u32); + had_error = true + } + LifetimeElisionKind::ElisionFailure => { + ctx.report_elision_failure(def, lifetime_args_len as u32); + had_error = true; + } + LifetimeElisionKind::StaticIfNoLifetimeInScope { only_lint: _ } => { + // FIXME: Check there are other lifetimes in scope, and error/lint. + } + LifetimeElisionKind::Elided(_) => { + ctx.report_elided_lifetimes_in_path(def, lifetime_args_len as u32, false); + } + LifetimeElisionKind::Infer => { + // Allow eliding lifetimes. + } + } + } else if lifetime_args_len != provided_lifetimes_count { ctx.report_len_mismatch( def, provided_lifetimes_count as u32, - max_expected_lifetime_args as u32, + lifetime_args_len as u32, IncorrectGenericsLenKind::Lifetimes, ); had_error = true; @@ -974,7 +1083,8 @@ pub(crate) fn substs_from_args_and_bindings( args_and_bindings: Option<&GenericArgs>, def: GenericDefId, mut infer_args: bool, - position: GenericArgsPosition, + lifetime_elision: LifetimeElisionKind, + lowering_assoc_type_generics: bool, explicit_self_ty: Option, ctx: &mut impl GenericArgsLowerer, ) -> Substitution { @@ -991,8 +1101,15 @@ pub(crate) fn substs_from_args_and_bindings( args_slice.iter().any(|arg| !matches!(arg, GenericArg::Lifetime(_))); infer_args &= !has_non_lifetime_args; - let had_count_error = - check_generic_args_len(args_and_bindings, def, &def_generics, infer_args, position, ctx); + let had_count_error = check_generic_args_len( + args_and_bindings, + def, + &def_generics, + infer_args, + &lifetime_elision, + lowering_assoc_type_generics, + ctx, + ); let mut substs = Vec::with_capacity(def_generics.len()); @@ -1120,7 +1237,29 @@ pub(crate) fn substs_from_args_and_bindings( (None, Some(&(param_id, param))) => { // If there are fewer arguments than parameters, it means we're inferring the remaining arguments. - substs.push(ctx.inferred_kind(def, param_id, param, infer_args, &substs)); + let param = if let GenericParamId::LifetimeParamId(_) = param_id { + match &lifetime_elision { + LifetimeElisionKind::ElisionFailure + | LifetimeElisionKind::AnonymousCreateParameter { report_in_path: true } + | LifetimeElisionKind::AnonymousReportError => { + assert!(had_count_error); + ctx.inferred_kind(def, param_id, param, infer_args, &substs) + } + LifetimeElisionKind::StaticIfNoLifetimeInScope { only_lint: _ } => { + static_lifetime().cast(Interner) + } + LifetimeElisionKind::Elided(lifetime) => lifetime.clone().cast(Interner), + LifetimeElisionKind::AnonymousCreateParameter { report_in_path: false } + | LifetimeElisionKind::Infer => { + // FIXME: With `AnonymousCreateParameter`, we need to create a new lifetime parameter here + // (but this will probably be done in hir-def lowering instead). + ctx.inferred_kind(def, param_id, param, infer_args, &substs) + } + } + } else { + ctx.inferred_kind(def, param_id, param, infer_args, &substs) + }; + substs.push(param); params.next(); } diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs index d8bcaa0e74..b6e3002ed5 100644 --- a/crates/hir/src/diagnostics.rs +++ b/crates/hir/src/diagnostics.rs @@ -118,6 +118,8 @@ diagnostics![ BadRtn, IncorrectGenericsLen, IncorrectGenericsOrder, + MissingLifetime, + ElidedLifetimesInPath, ]; #[derive(Debug)] @@ -440,6 +442,23 @@ pub struct IncorrectGenericsLen { pub def: GenericDef, } +#[derive(Debug)] +pub struct MissingLifetime { + /// Points at the name if there are no generics. + pub generics_or_segment: InFile>>, + pub expected: u32, + pub def: GenericDef, +} + +#[derive(Debug)] +pub struct ElidedLifetimesInPath { + /// Points at the name if there are no generics. + pub generics_or_segment: InFile>>, + pub expected: u32, + pub def: GenericDef, + pub hard_error: bool, +} + #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum GenericArgKind { Lifetime, @@ -861,6 +880,31 @@ impl AnyDiagnostic { let expected_kind = GenericArgKind::from_id(param_id); IncorrectGenericsOrder { provided_arg, expected_kind }.into() } + PathLoweringDiagnostic::MissingLifetime { generics_source, expected_count, def } + | PathLoweringDiagnostic::ElisionFailure { generics_source, expected_count, def } => { + let generics_or_segment = + path_generics_source_to_ast(&path.value, generics_source)?; + let generics_or_segment = path.with_value(AstPtr::new(&generics_or_segment)); + MissingLifetime { generics_or_segment, expected: expected_count, def: def.into() } + .into() + } + PathLoweringDiagnostic::ElidedLifetimesInPath { + generics_source, + expected_count, + def, + hard_error, + } => { + let generics_or_segment = + path_generics_source_to_ast(&path.value, generics_source)?; + let generics_or_segment = path.with_value(AstPtr::new(&generics_or_segment)); + ElidedLifetimesInPath { + generics_or_segment, + expected: expected_count, + def: def.into(), + hard_error, + } + .into() + } }) } diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs index 108c8f0e18..00e67fbd74 100644 --- a/crates/hir/src/source_analyzer.rs +++ b/crates/hir/src/source_analyzer.rs @@ -34,8 +34,8 @@ use hir_expand::{ name::{AsName, Name}, }; use hir_ty::{ - Adjustment, AliasTy, InferenceResult, Interner, ProjectionTy, Substitution, TraitEnvironment, - Ty, TyExt, TyKind, TyLoweringContext, + Adjustment, AliasTy, InferenceResult, Interner, LifetimeElisionKind, ProjectionTy, + Substitution, TraitEnvironment, Ty, TyExt, TyKind, TyLoweringContext, diagnostics::{ InsideUnsafeBlock, record_literal_missing_fields, record_pattern_missing_fields, unsafe_operations, @@ -261,11 +261,15 @@ impl SourceAnalyzer { pub(crate) fn type_of_type(&self, db: &dyn HirDatabase, ty: &ast::Type) -> Option { let type_ref = self.type_id(ty)?; - let ty = hir_ty::TyLoweringContext::new( + let ty = TyLoweringContext::new( db, &self.resolver, self.store()?, self.resolver.generic_def()?, + // FIXME: Is this correct here? Anyway that should impact mostly diagnostics, which we don't emit here + // (this can impact the lifetimes generated, e.g. in `const` they won't be `'static`, but this seems like a + // small problem). + LifetimeElisionKind::Infer, ) .lower_ty(type_ref); Some(Type::new_with_resolver(db, &self.resolver, ty)) @@ -1553,7 +1557,8 @@ fn resolve_hir_path_( let (ty, unresolved) = match path.type_anchor() { Some(type_ref) => resolver.generic_def().and_then(|def| { let (_, res) = - TyLoweringContext::new(db, resolver, store?, def).lower_ty_ext(type_ref); + TyLoweringContext::new(db, resolver, store?, def, LifetimeElisionKind::Infer) + .lower_ty_ext(type_ref); res.map(|ty_ns| (ty_ns, path.segments().first())) }), None => { @@ -1681,7 +1686,8 @@ fn resolve_hir_path_qualifier( let (ty, unresolved) = match path.type_anchor() { Some(type_ref) => resolver.generic_def().and_then(|def| { let (_, res) = - TyLoweringContext::new(db, resolver, store, def).lower_ty_ext(type_ref); + TyLoweringContext::new(db, resolver, store, def, LifetimeElisionKind::Infer) + .lower_ty_ext(type_ref); res.map(|ty_ns| (ty_ns, path.segments().first())) }), None => { diff --git a/crates/ide-diagnostics/src/handlers/elided_lifetimes_in_path.rs b/crates/ide-diagnostics/src/handlers/elided_lifetimes_in_path.rs new file mode 100644 index 0000000000..438dd2fdcb --- /dev/null +++ b/crates/ide-diagnostics/src/handlers/elided_lifetimes_in_path.rs @@ -0,0 +1,112 @@ +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; + +// Diagnostic: elided-lifetimes-in-path +// +// This diagnostic is triggered when lifetimes are elided in paths. It is a lint only for some cases, +// and a hard error for others. +pub(crate) fn elided_lifetimes_in_path( + ctx: &DiagnosticsContext<'_>, + d: &hir::ElidedLifetimesInPath, +) -> Diagnostic { + if d.hard_error { + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0726"), + "implicit elided lifetime not allowed here", + d.generics_or_segment.map(Into::into), + ) + .experimental() + } else { + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcLint("elided_lifetimes_in_paths"), + "hidden lifetime parameters in types are deprecated", + d.generics_or_segment.map(Into::into), + ) + .experimental() + } +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn fn_() { + check_diagnostics( + r#" +#![warn(elided_lifetimes_in_paths)] + +struct Foo<'a>(&'a ()); + +fn foo(_: Foo) {} + // ^^^ warn: hidden lifetime parameters in types are deprecated + "#, + ); + check_diagnostics( + r#" +#![warn(elided_lifetimes_in_paths)] + +struct Foo<'a>(&'a ()); + +fn foo(_: Foo<'_>) -> Foo { loop {} } + // ^^^ warn: hidden lifetime parameters in types are deprecated + "#, + ); + } + + #[test] + fn async_fn() { + check_diagnostics( + r#" +struct Foo<'a>(&'a ()); + +async fn foo(_: Foo) {} + // ^^^ error: implicit elided lifetime not allowed here + "#, + ); + check_diagnostics( + r#" +#![warn(elided_lifetimes_in_paths)] + +struct Foo<'a>(&'a ()); + +fn foo(_: Foo<'_>) -> Foo { loop {} } + // ^^^ warn: hidden lifetime parameters in types are deprecated + "#, + ); + } + + #[test] + fn no_error_when_explicitly_elided() { + check_diagnostics( + r#" +#![warn(elided_lifetimes_in_paths)] + +struct Foo<'a>(&'a ()); +trait Trait<'a> {} + +fn foo(_: Foo<'_>) -> Foo<'_> { loop {} } +async fn bar(_: Foo<'_>) -> Foo<'_> { loop {} } +impl Foo<'_> {} +impl Trait<'_> for Foo<'_> {} + "#, + ); + } + + #[test] + fn impl_() { + check_diagnostics( + r#" +struct Foo<'a>(&'a ()); +trait Trait<'a> {} + +impl Foo {} + // ^^^ error: implicit elided lifetime not allowed here + +impl Trait for Foo<'_> {} + // ^^^^^ error: implicit elided lifetime not allowed here + "#, + ); + } +} diff --git a/crates/ide-diagnostics/src/handlers/missing_lifetime.rs b/crates/ide-diagnostics/src/handlers/missing_lifetime.rs new file mode 100644 index 0000000000..8cdbb6384f --- /dev/null +++ b/crates/ide-diagnostics/src/handlers/missing_lifetime.rs @@ -0,0 +1,92 @@ +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; + +// Diagnostic: missing-lifetime +// +// This diagnostic is triggered when a lifetime argument is missing. +pub(crate) fn missing_lifetime( + ctx: &DiagnosticsContext<'_>, + d: &hir::MissingLifetime, +) -> Diagnostic { + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcHardError("E0106"), + "missing lifetime specifier", + d.generics_or_segment.map(Into::into), + ) + .experimental() +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn in_fields() { + check_diagnostics( + r#" +struct Foo<'a>(&'a ()); +struct Bar(Foo); + // ^^^ error: missing lifetime specifier + "#, + ); + } + + #[test] + fn bounds() { + check_diagnostics( + r#" +struct Foo<'a, T>(&'a T); +trait Trait<'a> { + type Assoc; +} + +fn foo<'a, T: Trait>( + // ^^^^^ error: missing lifetime specifier + _: impl Trait<'a, Assoc: Trait>, + // ^^^^^ error: missing lifetime specifier +) +where + Foo: Trait<'a>, + // ^^^ error: missing lifetime specifier +{ +} + "#, + ); + } + + #[test] + fn generic_defaults() { + check_diagnostics( + r#" +struct Foo<'a>(&'a ()); + +struct Bar(T); + // ^^^ error: missing lifetime specifier + "#, + ); + } + + #[test] + fn type_alias_type() { + check_diagnostics( + r#" +struct Foo<'a>(&'a ()); + +type Bar = Foo; + // ^^^ error: missing lifetime specifier + "#, + ); + } + + #[test] + fn const_param_ty() { + check_diagnostics( + r#" +struct Foo<'a>(&'a ()); + +fn bar() {} + // ^^^ error: missing lifetime specifier + "#, + ); + } +} diff --git a/crates/ide-diagnostics/src/lib.rs b/crates/ide-diagnostics/src/lib.rs index ddaef57b53..11efedd8a5 100644 --- a/crates/ide-diagnostics/src/lib.rs +++ b/crates/ide-diagnostics/src/lib.rs @@ -27,6 +27,7 @@ mod handlers { pub(crate) mod await_outside_of_async; pub(crate) mod bad_rtn; pub(crate) mod break_outside_of_loop; + pub(crate) mod elided_lifetimes_in_path; pub(crate) mod expected_function; pub(crate) mod generic_args_prohibited; pub(crate) mod inactive_code; @@ -40,6 +41,7 @@ mod handlers { pub(crate) mod malformed_derive; pub(crate) mod mismatched_arg_count; pub(crate) mod missing_fields; + pub(crate) mod missing_lifetime; pub(crate) mod missing_match_arms; pub(crate) mod missing_unsafe; pub(crate) mod moved_out_of_ref; @@ -503,6 +505,8 @@ pub fn semantic_diagnostics( AnyDiagnostic::BadRtn(d) => handlers::bad_rtn::bad_rtn(&ctx, &d), AnyDiagnostic::IncorrectGenericsLen(d) => handlers::incorrect_generics_len::incorrect_generics_len(&ctx, &d), AnyDiagnostic::IncorrectGenericsOrder(d) => handlers::incorrect_generics_order::incorrect_generics_order(&ctx, &d), + AnyDiagnostic::MissingLifetime(d) => handlers::missing_lifetime::missing_lifetime(&ctx, &d), + AnyDiagnostic::ElidedLifetimesInPath(d) => handlers::elided_lifetimes_in_path::elided_lifetimes_in_path(&ctx, &d), }; res.push(d) }