Properly handle lifetimes when checking generic arguments len

And also, prepare for correct lowering of lifetime. We still don't handle most lifetimes correctly, but a bit more of the foundation to lifetime elision is now implemented.
diff --git a/crates/hir-def/src/hir/type_ref.rs b/crates/hir-def/src/hir/type_ref.rs
index 1fd878a..6b7f03e 100644
--- a/crates/hir-def/src/hir/type_ref.rs
+++ b/crates/hir-def/src/hir/type_ref.rs
@@ -106,6 +106,14 @@
     pub abi: Option<Symbol>,
 }
 
+impl FnType {
+    #[inline]
+    pub fn split_params_and_ret(&self) -> (&[(Option<Name>, 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 bf16e08..2aa9401 100644
--- a/crates/hir-ty/src/chalk_db.rs
+++ b/crates/hir-ty/src/chalk_db.rs
@@ -27,6 +27,7 @@
     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 @@
     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 cad2e3c..38807b6 100644
--- a/crates/hir-ty/src/infer.rs
+++ b/crates/hir-ty/src/infer.rs
@@ -34,8 +34,8 @@
 };
 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 @@
         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 @@
         DefWithBodyId::FunctionId(f) => {
             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 @@
         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 @@
     }
 
     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 @@
 
     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::<Vec<_>>()
-            });
+            },
+        );
 
         // Check if function contains a va_list, if it does then we append it to the parameter types
         // that are collected from the function data
@@ -967,10 +978,10 @@
                 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 @@
         &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 @@
             &self.diagnostics,
             types_source,
             self.generic_def,
+            lifetime_elision,
         );
         f(&mut ctx)
     }
 
     fn with_body_ty_lowering<R>(&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 @@
         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 @@
     }
 
     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 @@
             &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 @@
             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 @@
         };
         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 @@
                     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 59ec3ad..e7b776a 100644
--- a/crates/hir-ty/src/infer/closure.rs
+++ b/crates/hir-ty/src/infer/closure.rs
@@ -440,6 +440,8 @@
         // 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 2c633a0..e3c4f55 100644
--- a/crates/hir-ty/src/infer/diagnostics.rs
+++ b/crates/hir-ty/src/infer/diagnostics.rs
@@ -12,7 +12,7 @@
 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 @@
         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 @@
         &'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 @@
                     .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 @@
             }),
             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 2980549..643587e 100644
--- a/crates/hir-ty/src/infer/expr.rs
+++ b/crates/hir-ty/src/infer/expr.rs
@@ -37,7 +37,7 @@
     },
     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 @@
                     }
                 }
             }
+
+            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 @@
             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 0e1d23b..bdaec61 100644
--- a/crates/hir-ty/src/infer/path.rs
+++ b/crates/hir-ty/src/infer/path.rs
@@ -16,7 +16,7 @@
     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 @@
         };
 
         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 @@
             &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 2cb977b..128569d 100644
--- a/crates/hir-ty/src/lib.rs
+++ b/crates/hir-ty/src/lib.rs
@@ -94,8 +94,8 @@
 };
 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 @@
 
 impl CallableSig {
     pub fn from_params_and_return(
-        params: impl ExactSizeIterator<Item = Ty>,
+        params: impl Iterator<Item = Ty>,
         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 073a584..eecca32 100644
--- a/crates/hir-ty/src/lower.rs
+++ b/crates/hir-ty/src/lower.rs
@@ -25,8 +25,8 @@
 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 @@
     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 @@
 
 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<dyn Foo>`).
+    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 @@
     /// Tracks types with explicit `?Sized` bounds.
     pub(crate) unsized_types: FxHashSet<Ty>,
     pub(crate) diagnostics: Vec<TyLoweringDiagnostic>,
-    pub(crate) in_fn_signature: bool,
+    lifetime_elision: LifetimeElisionKind,
 }
 
 impl<'a> TyLoweringContext<'a> {
@@ -129,6 +178,7 @@
         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 @@
             type_param_mode,
             unsized_types: FxHashSet::default(),
             diagnostics: Vec::new(),
-            in_fn_signature: false,
+            lifetime_elision,
         }
     }
 
@@ -167,6 +217,17 @@
         self.with_debruijn(self.in_binders.shifted_in_from(debruijn), f)
     }
 
+    fn with_lifetime_elision<T>(
+        &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 @@
             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 @@
             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 @@
     };
     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 @@
 ) -> 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 @@
 ) -> Arc<TraitEnvironment> {
     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 @@
 {
     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 @@
     }
     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 @@
 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 @@
     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_static(db: &dyn HirDatabase, def: StaticId) -> Binders<Ty> {
     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 fn_sig_for_enum_variant_constructor(db: &dyn HirDatabase, def: EnumVariantId) -> 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 @@
     } 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 @@
     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 @@
     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 @@
 ) -> Option<(Binders<TraitRef>, 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 @@
     // 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 @@
 ) -> Option<Arc<Binders<ImplTraits>>> {
     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 a5a13d6..009f047 100644
--- a/crates/hir-ty/src/lower/diagnostics.rs
+++ b/crates/hir-ty/src/lower/diagnostics.rs
@@ -63,6 +63,24 @@
         /// 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 c89aad8..d3ca438 100644
--- a/crates/hir-ty/src/lower/path.rs
+++ b/crates/hir-ty/src/lower/path.rs
@@ -27,8 +27,8 @@
     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 @@
     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 @@
         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 @@
             segments,
             current_segment_idx: 0,
             current_or_prev_segment: first_segment,
-            position,
         }
     }
 
@@ -122,6 +119,19 @@
             .expect("invalid segment passed to PathLoweringContext::set_current_segment()");
     }
 
+    #[inline]
+    fn with_lifetime_elision<T>(
+        &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 @@
         }
     }
 
-    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 @@
                                     associated_ty.into(),
                                     false,
                                     None,
+                                    true,
                                 );
                                 let substitution = Substitution::from_iter(
                                     Interner,
@@ -511,7 +506,7 @@
                 // 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 @@
             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 @@
         // 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 @@
                 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 @@
         def: GenericDefId,
         infer_args: bool,
         explicit_self_ty: Option<Ty>,
+        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 @@
             infer_args,
             explicit_self_ty,
             PathGenericsSource::Segment(self.current_segment_u32()),
+            lowering_assoc_type_generics,
+            lifetime_elision,
         )
     }
 
@@ -637,6 +655,8 @@
         infer_args: bool,
         explicit_self_ty: Option<Ty>,
         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 @@
                     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 @@
             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 @@
         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 @@
                     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 @@
                 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) 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 @@
     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 @@
         }
     }
 
-    // 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 @@
     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<Ty>,
     ctx: &mut impl GenericArgsLowerer,
 ) -> Substitution {
@@ -991,8 +1101,15 @@
         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 @@
 
             (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 d8bcaa0..b6e3002 100644
--- a/crates/hir/src/diagnostics.rs
+++ b/crates/hir/src/diagnostics.rs
@@ -118,6 +118,8 @@
     BadRtn,
     IncorrectGenericsLen,
     IncorrectGenericsOrder,
+    MissingLifetime,
+    ElidedLifetimesInPath,
 ];
 
 #[derive(Debug)]
@@ -440,6 +442,23 @@
     pub def: GenericDef,
 }
 
+#[derive(Debug)]
+pub struct MissingLifetime {
+    /// Points at the name if there are no generics.
+    pub generics_or_segment: InFile<AstPtr<Either<ast::GenericArgList, ast::NameRef>>>,
+    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<AstPtr<Either<ast::GenericArgList, ast::NameRef>>>,
+    pub expected: u32,
+    pub def: GenericDef,
+    pub hard_error: bool,
+}
+
 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
 pub enum GenericArgKind {
     Lifetime,
@@ -861,6 +880,31 @@
                 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 108c8f0..00e67fb 100644
--- a/crates/hir/src/source_analyzer.rs
+++ b/crates/hir/src/source_analyzer.rs
@@ -34,8 +34,8 @@
     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 @@
 
     pub(crate) fn type_of_type(&self, db: &dyn HirDatabase, ty: &ast::Type) -> Option<Type> {
         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 @@
         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 @@
         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 0000000..438dd2f
--- /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 0000000..8cdbb63
--- /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<T>: Trait<'a>,
+    // ^^^ error: missing lifetime specifier
+{
+}
+        "#,
+        );
+    }
+
+    #[test]
+    fn generic_defaults() {
+        check_diagnostics(
+            r#"
+struct Foo<'a>(&'a ());
+
+struct Bar<T = Foo>(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<const F: Foo>() {}
+             // ^^^ error: missing lifetime specifier
+        "#,
+        );
+    }
+}
diff --git a/crates/ide-diagnostics/src/lib.rs b/crates/ide-diagnostics/src/lib.rs
index ddaef57..11efedd 100644
--- a/crates/ide-diagnostics/src/lib.rs
+++ b/crates/ide-diagnostics/src/lib.rs
@@ -27,6 +27,7 @@
     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 @@
     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 @@
             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)
     }