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.
This commit is contained in:
Chayim Refael Friedman 2025-04-24 08:35:20 +03:00
parent 3d00e247dd
commit adcf699ea3
16 changed files with 858 additions and 226 deletions

View File

@ -106,6 +106,14 @@ pub struct FnType {
pub abi: Option<Symbol>, 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)] #[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct ArrayType { pub struct ArrayType {
pub ty: TypeRefId, pub ty: TypeRefId,

View File

@ -27,6 +27,7 @@ use crate::{
db::{HirDatabase, InternedCoroutine}, db::{HirDatabase, InternedCoroutine},
from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id,
generics::generics, generics::generics,
lower::LifetimeElisionKind,
make_binders, make_single_type_binders, make_binders, make_single_type_binders,
mapping::{ToChalk, TypeAliasAsValue, from_chalk}, mapping::{ToChalk, TypeAliasAsValue, from_chalk},
method_resolution::{ALL_FLOAT_FPS, ALL_INT_FPS, TraitImpls, TyFingerprint}, 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 type_alias_data = db.type_alias_signature(type_alias);
let generic_params = generics(db, type_alias.into()); let generic_params = generics(db, type_alias.into());
let resolver = hir_def::resolver::HasResolver::resolver(type_alias, db); let resolver = hir_def::resolver::HasResolver::resolver(type_alias, db);
let mut ctx = let mut ctx = crate::TyLoweringContext::new(
crate::TyLoweringContext::new(db, &resolver, &type_alias_data.store, type_alias.into()) db,
.with_type_param_mode(crate::lower::ParamLoweringMode::Variable); &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) let trait_subst = TyBuilder::subst_for_def(db, trait_, None)
.fill_with_bound_vars(crate::DebruijnIndex::INNERMOST, 0) .fill_with_bound_vars(crate::DebruijnIndex::INNERMOST, 0)

View File

@ -34,8 +34,8 @@ use chalk_ir::{
}; };
use either::Either; use either::Either;
use hir_def::{ use hir_def::{
AdtId, AssocItemId, DefWithBodyId, FieldId, FunctionId, GenericDefId, GenericParamId, ImplId, AdtId, AssocItemId, ConstId, DefWithBodyId, FieldId, FunctionId, GenericDefId, GenericParamId,
ItemContainerId, Lookup, TraitId, TupleFieldId, TupleId, TypeAliasId, VariantId, ImplId, ItemContainerId, Lookup, TraitId, TupleFieldId, TupleId, TypeAliasId, VariantId,
builtin_type::{BuiltinInt, BuiltinType, BuiltinUint}, builtin_type::{BuiltinInt, BuiltinType, BuiltinUint},
expr_store::{Body, ExpressionStore, HygieneId, path::Path}, expr_store::{Body, ExpressionStore, HygieneId, path::Path},
hir::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, LabelId, PatId}, hir::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, LabelId, PatId},
@ -67,9 +67,9 @@ use crate::{
expr::ExprIsRead, expr::ExprIsRead,
unify::InferenceTable, unify::InferenceTable,
}, },
lower::{GenericArgsPosition, ImplTraitLoweringMode, diagnostics::TyLoweringDiagnostic}, lower::{ImplTraitLoweringMode, LifetimeElisionKind, diagnostics::TyLoweringDiagnostic},
mir::MirSpan, mir::MirSpan,
to_assoc_type_id, static_lifetime, to_assoc_type_id,
traits::FnTrait, traits::FnTrait,
utils::UnevaluatedConstEvaluatorFolder, utils::UnevaluatedConstEvaluatorFolder,
}; };
@ -96,7 +96,7 @@ pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<Infer
DefWithBodyId::FunctionId(f) => { DefWithBodyId::FunctionId(f) => {
ctx.collect_fn(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::StaticId(s) => ctx.collect_static(&db.static_signature(s)),
DefWithBodyId::VariantId(v) => { DefWithBodyId::VariantId(v) => {
ctx.return_ty = TyBuilder::builtin( ctx.return_ty = TyBuilder::builtin(
@ -899,9 +899,13 @@ impl<'a> InferenceContext<'a> {
result result
} }
fn collect_const(&mut self, data: &ConstSignature) { fn collect_const(&mut self, id: ConstId, data: &ConstSignature) {
let return_ty = let return_ty = self.make_ty(
self.make_ty(data.type_ref, &data.store, InferenceTyDiagnosticSource::Signature); data.type_ref,
&data.store,
InferenceTyDiagnosticSource::Signature,
LifetimeElisionKind::for_const(id.loc(self.db).container),
);
// Constants might be defining usage sites of TAITs. // Constants might be defining usage sites of TAITs.
self.make_tait_coercion_table(iter::once(&return_ty)); self.make_tait_coercion_table(iter::once(&return_ty));
@ -910,8 +914,12 @@ impl<'a> InferenceContext<'a> {
} }
fn collect_static(&mut self, data: &StaticSignature) { fn collect_static(&mut self, data: &StaticSignature) {
let return_ty = let return_ty = self.make_ty(
self.make_ty(data.type_ref, &data.store, InferenceTyDiagnosticSource::Signature); data.type_ref,
&data.store,
InferenceTyDiagnosticSource::Signature,
LifetimeElisionKind::Elided(static_lifetime()),
);
// Statics might be defining usage sites of TAITs. // Statics might be defining usage sites of TAITs.
self.make_tait_coercion_table(iter::once(&return_ty)); self.make_tait_coercion_table(iter::once(&return_ty));
@ -921,12 +929,15 @@ impl<'a> InferenceContext<'a> {
fn collect_fn(&mut self, func: FunctionId) { fn collect_fn(&mut self, func: FunctionId) {
let data = self.db.function_signature(func); let data = self.db.function_signature(func);
let mut param_tys = let mut param_tys = self.with_ty_lowering(
self.with_ty_lowering(&data.store, InferenceTyDiagnosticSource::Signature, |ctx| { &data.store,
InferenceTyDiagnosticSource::Signature,
LifetimeElisionKind::for_fn_params(&data),
|ctx| {
ctx.type_param_mode(ParamLoweringMode::Placeholder); ctx.type_param_mode(ParamLoweringMode::Placeholder);
ctx.in_fn_signature = true;
data.params.iter().map(|&type_ref| ctx.lower_ty(type_ref)).collect::<Vec<_>>() 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 // 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 // that are collected from the function data
@ -967,10 +978,10 @@ impl<'a> InferenceContext<'a> {
let return_ty = self.with_ty_lowering( let return_ty = self.with_ty_lowering(
&data.store, &data.store,
InferenceTyDiagnosticSource::Signature, InferenceTyDiagnosticSource::Signature,
LifetimeElisionKind::for_fn_ret(),
|ctx| { |ctx| {
ctx.type_param_mode(ParamLoweringMode::Placeholder) ctx.type_param_mode(ParamLoweringMode::Placeholder)
.impl_trait_mode(ImplTraitLoweringMode::Opaque); .impl_trait_mode(ImplTraitLoweringMode::Opaque);
ctx.in_fn_signature = true;
ctx.lower_ty(return_ty) ctx.lower_ty(return_ty)
}, },
); );
@ -1304,6 +1315,7 @@ impl<'a> InferenceContext<'a> {
&mut self, &mut self,
store: &ExpressionStore, store: &ExpressionStore,
types_source: InferenceTyDiagnosticSource, types_source: InferenceTyDiagnosticSource,
lifetime_elision: LifetimeElisionKind,
f: impl FnOnce(&mut TyLoweringContext<'_>) -> R, f: impl FnOnce(&mut TyLoweringContext<'_>) -> R,
) -> R { ) -> R {
let mut ctx = TyLoweringContext::new( let mut ctx = TyLoweringContext::new(
@ -1313,12 +1325,18 @@ impl<'a> InferenceContext<'a> {
&self.diagnostics, &self.diagnostics,
types_source, types_source,
self.generic_def, self.generic_def,
lifetime_elision,
); );
f(&mut ctx) f(&mut ctx)
} }
fn with_body_ty_lowering<R>(&mut self, f: impl FnOnce(&mut TyLoweringContext<'_>) -> R) -> R { 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( fn make_ty(
@ -1326,29 +1344,46 @@ impl<'a> InferenceContext<'a> {
type_ref: TypeRefId, type_ref: TypeRefId,
store: &ExpressionStore, store: &ExpressionStore,
type_source: InferenceTyDiagnosticSource, type_source: InferenceTyDiagnosticSource,
lifetime_elision: LifetimeElisionKind,
) -> Ty { ) -> 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); let ty = self.insert_type_vars(ty);
self.normalize_associated_types_in(ty) self.normalize_associated_types_in(ty)
} }
fn make_body_ty(&mut self, type_ref: TypeRefId) -> 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 { fn make_body_const(&mut self, const_ref: ConstRef, ty: Ty) -> Const {
let const_ = self.with_ty_lowering(self.body, InferenceTyDiagnosticSource::Body, |ctx| { let const_ = self.with_ty_lowering(
ctx.type_param_mode = ParamLoweringMode::Placeholder; self.body,
ctx.lower_const(&const_ref, ty) InferenceTyDiagnosticSource::Body,
}); LifetimeElisionKind::Infer,
|ctx| {
ctx.type_param_mode = ParamLoweringMode::Placeholder;
ctx.lower_const(&const_ref, ty)
},
);
self.insert_type_vars(const_) self.insert_type_vars(const_)
} }
fn make_path_as_body_const(&mut self, path: &Path, ty: Ty) -> 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| { let const_ = self.with_ty_lowering(
ctx.type_param_mode = ParamLoweringMode::Placeholder; self.body,
ctx.lower_path_as_const(path, ty) InferenceTyDiagnosticSource::Body,
}); LifetimeElisionKind::Infer,
|ctx| {
ctx.type_param_mode = ParamLoweringMode::Placeholder;
ctx.lower_path_as_const(path, ty)
},
);
self.insert_type_vars(const_) self.insert_type_vars(const_)
} }
@ -1357,9 +1392,12 @@ impl<'a> InferenceContext<'a> {
} }
fn make_body_lifetime(&mut self, lifetime_ref: &LifetimeRef) -> Lifetime { fn make_body_lifetime(&mut self, lifetime_ref: &LifetimeRef) -> Lifetime {
let lt = self.with_ty_lowering(self.body, InferenceTyDiagnosticSource::Body, |ctx| { let lt = self.with_ty_lowering(
ctx.lower_lifetime(lifetime_ref) self.body,
}); InferenceTyDiagnosticSource::Body,
LifetimeElisionKind::Infer,
|ctx| ctx.lower_lifetime(lifetime_ref),
);
self.insert_type_vars(lt) self.insert_type_vars(lt)
} }
@ -1529,8 +1567,9 @@ impl<'a> InferenceContext<'a> {
&self.diagnostics, &self.diagnostics,
InferenceTyDiagnosticSource::Body, InferenceTyDiagnosticSource::Body,
self.generic_def, 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 (resolution, unresolved) = if value_ns {
let Some(res) = path_ctx.resolve_path_in_value_ns(HygieneId::ROOT) else { let Some(res) = path_ctx.resolve_path_in_value_ns(HygieneId::ROOT) else {
return (self.err_ty(), None); return (self.err_ty(), None);
@ -1538,14 +1577,14 @@ impl<'a> InferenceContext<'a> {
match res { match res {
ResolveValueResult::ValueNs(value, _) => match value { ResolveValueResult::ValueNs(value, _) => match value {
ValueNs::EnumVariantId(var) => { 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); drop(ctx);
let ty = self.db.ty(var.lookup(self.db).parent.into()); let ty = self.db.ty(var.lookup(self.db).parent.into());
let ty = self.insert_type_vars(ty.substitute(Interner, &substs)); let ty = self.insert_type_vars(ty.substitute(Interner, &substs));
return (ty, Some(var.into())); return (ty, Some(var.into()));
} }
ValueNs::StructId(strukt) => { 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); drop(ctx);
let ty = self.db.ty(strukt.into()); let ty = self.db.ty(strukt.into());
let ty = self.insert_type_vars(ty.substitute(Interner, &substs)); let ty = self.insert_type_vars(ty.substitute(Interner, &substs));
@ -1567,21 +1606,21 @@ impl<'a> InferenceContext<'a> {
}; };
return match resolution { return match resolution {
TypeNs::AdtId(AdtId::StructId(strukt)) => { 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); drop(ctx);
let ty = self.db.ty(strukt.into()); let ty = self.db.ty(strukt.into());
let ty = self.insert_type_vars(ty.substitute(Interner, &substs)); let ty = self.insert_type_vars(ty.substitute(Interner, &substs));
forbid_unresolved_segments((ty, Some(strukt.into())), unresolved) forbid_unresolved_segments((ty, Some(strukt.into())), unresolved)
} }
TypeNs::AdtId(AdtId::UnionId(u)) => { 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); drop(ctx);
let ty = self.db.ty(u.into()); let ty = self.db.ty(u.into());
let ty = self.insert_type_vars(ty.substitute(Interner, &substs)); let ty = self.insert_type_vars(ty.substitute(Interner, &substs));
forbid_unresolved_segments((ty, Some(u.into())), unresolved) forbid_unresolved_segments((ty, Some(u.into())), unresolved)
} }
TypeNs::EnumVariantId(var) => { 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); drop(ctx);
let ty = self.db.ty(var.lookup(self.db).parent.into()); let ty = self.db.ty(var.lookup(self.db).parent.into());
let ty = self.insert_type_vars(ty.substitute(Interner, &substs)); 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"); never!("resolver should always resolve lang item paths");
return (self.err_ty(), None); 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); drop(ctx);
let ty = self.db.ty(it.into()); let ty = self.db.ty(it.into());
let ty = self.insert_type_vars(ty.substitute(Interner, &substs)); let ty = self.insert_type_vars(ty.substitute(Interner, &substs));

View File

@ -440,6 +440,8 @@ impl InferenceContext<'_> {
// collect explicitly written argument types // collect explicitly written argument types
for arg_type in arg_types.iter() { for arg_type in arg_types.iter() {
let arg_ty = match arg_type { 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), Some(type_ref) => self.make_body_ty(*type_ref),
None => self.table.new_type_var(), None => self.table.new_type_var(),
}; };

View File

@ -12,7 +12,7 @@ use hir_def::expr_store::path::Path;
use hir_def::{hir::ExprOrPatId, resolver::Resolver}; use hir_def::{hir::ExprOrPatId, resolver::Resolver};
use la_arena::{Idx, RawIdx}; use la_arena::{Idx, RawIdx};
use crate::lower::GenericArgsPosition; use crate::lower::LifetimeElisionKind;
use crate::{ use crate::{
InferenceDiagnostic, InferenceTyDiagnosticSource, TyLoweringContext, TyLoweringDiagnostic, InferenceDiagnostic, InferenceTyDiagnosticSource, TyLoweringContext, TyLoweringDiagnostic,
db::HirDatabase, db::HirDatabase,
@ -66,8 +66,13 @@ impl<'a> InferenceTyLoweringContext<'a> {
diagnostics: &'a Diagnostics, diagnostics: &'a Diagnostics,
source: InferenceTyDiagnosticSource, source: InferenceTyDiagnosticSource,
generic_def: GenericDefId, generic_def: GenericDefId,
lifetime_elision: LifetimeElisionKind,
) -> Self { ) -> 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] #[inline]
@ -75,7 +80,6 @@ impl<'a> InferenceTyLoweringContext<'a> {
&'b mut self, &'b mut self,
path: &'b Path, path: &'b Path,
node: ExprOrPatId, node: ExprOrPatId,
position: GenericArgsPosition,
) -> PathLoweringContext<'b, 'a> { ) -> PathLoweringContext<'b, 'a> {
let on_diagnostic = PathDiagnosticCallback { let on_diagnostic = PathDiagnosticCallback {
data: Either::Right(PathDiagnosticCallbackData { diagnostics: self.diagnostics, node }), data: Either::Right(PathDiagnosticCallbackData { diagnostics: self.diagnostics, node }),
@ -85,14 +89,13 @@ impl<'a> InferenceTyLoweringContext<'a> {
.push(InferenceDiagnostic::PathDiagnostic { node: data.node, diag }); .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] #[inline]
pub(super) fn at_path_forget_diagnostics<'b>( pub(super) fn at_path_forget_diagnostics<'b>(
&'b mut self, &'b mut self,
path: &'b Path, path: &'b Path,
position: GenericArgsPosition,
) -> PathLoweringContext<'b, 'a> { ) -> PathLoweringContext<'b, 'a> {
let on_diagnostic = PathDiagnosticCallback { let on_diagnostic = PathDiagnosticCallback {
data: Either::Right(PathDiagnosticCallbackData { data: Either::Right(PathDiagnosticCallbackData {
@ -101,7 +104,7 @@ impl<'a> InferenceTyLoweringContext<'a> {
}), }),
callback: |_data, _, _diag| {}, callback: |_data, _, _diag| {},
}; };
PathLoweringContext::new(&mut self.ctx, on_diagnostic, path, position) PathLoweringContext::new(&mut self.ctx, on_diagnostic, path)
} }
#[inline] #[inline]

View File

@ -37,7 +37,7 @@ use crate::{
}, },
lang_items::lang_items_for_bin_op, lang_items::lang_items_for_bin_op,
lower::{ lower::{
GenericArgsPosition, ParamLoweringMode, lower_to_chalk_mutability, LifetimeElisionKind, ParamLoweringMode, lower_to_chalk_mutability,
path::{GenericArgsLowerer, TypeLikeConst, substs_from_args_and_bindings}, path::{GenericArgsLowerer, TypeLikeConst, substs_from_args_and_bindings},
}, },
mapping::{ToChalk, from_chalk}, 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( substs_from_args_and_bindings(
@ -2170,7 +2187,8 @@ impl InferenceContext<'_> {
generic_args, generic_args,
def, def,
true, true,
GenericArgsPosition::MethodCall, LifetimeElisionKind::Infer,
false,
None, None,
&mut LowererCtx { ctx: self, expr }, &mut LowererCtx { ctx: self, expr },
) )

View File

@ -16,7 +16,7 @@ use crate::{
consteval, error_lifetime, consteval, error_lifetime,
generics::generics, generics::generics,
infer::diagnostics::InferenceTyLoweringContext as TyLoweringContext, infer::diagnostics::InferenceTyLoweringContext as TyLoweringContext,
lower::GenericArgsPosition, lower::LifetimeElisionKind,
method_resolution::{self, VisibleFromModule}, method_resolution::{self, VisibleFromModule},
to_chalk_trait_id, to_chalk_trait_id,
}; };
@ -96,12 +96,12 @@ impl InferenceContext<'_> {
}; };
let substs = self.with_body_ty_lowering(|ctx| { 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); let last_segment = path.segments().len().checked_sub(1);
if let Some(last_segment) = last_segment { if let Some(last_segment) = last_segment {
path_ctx.set_current_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); let substs = substs.as_slice(Interner);
@ -162,11 +162,12 @@ impl InferenceContext<'_> {
&self.diagnostics, &self.diagnostics,
InferenceTyDiagnosticSource::Body, InferenceTyDiagnosticSource::Body,
self.generic_def, self.generic_def,
LifetimeElisionKind::Infer,
); );
let mut path_ctx = if no_diagnostics { let mut path_ctx = if no_diagnostics {
ctx.at_path_forget_diagnostics(path, GenericArgsPosition::Value) ctx.at_path_forget_diagnostics(path)
} else { } 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 (value, self_subst) = if let Some(type_ref) = path.type_anchor() {
let last = path.segments().last()?; let last = path.segments().last()?;

View File

@ -94,8 +94,8 @@ pub use infer::{
}; };
pub use interner::Interner; pub use interner::Interner;
pub use lower::{ pub use lower::{
ImplTraitLoweringMode, ParamLoweringMode, TyDefId, TyLoweringContext, ValueTyDefId, ImplTraitLoweringMode, LifetimeElisionKind, ParamLoweringMode, TyDefId, TyLoweringContext,
associated_type_shorthand_candidates, diagnostics::*, ValueTyDefId, associated_type_shorthand_candidates, diagnostics::*,
}; };
pub use mapping::{ pub use mapping::{
from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, from_placeholder_idx, from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, from_placeholder_idx,
@ -529,13 +529,13 @@ pub type PolyFnSig = Binders<CallableSig>;
impl CallableSig { impl CallableSig {
pub fn from_params_and_return( pub fn from_params_and_return(
params: impl ExactSizeIterator<Item = Ty>, params: impl Iterator<Item = Ty>,
ret: Ty, ret: Ty,
is_varargs: bool, is_varargs: bool,
safety: Safety, safety: Safety,
abi: FnAbi, abi: FnAbi,
) -> CallableSig { ) -> 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.extend(params);
params_and_return.push(ret); params_and_return.push(ret);
CallableSig { params_and_return: params_and_return.into(), is_varargs, safety, abi } CallableSig { params_and_return: params_and_return.into(), is_varargs, safety, abi }

View File

@ -25,8 +25,8 @@ use chalk_ir::{
use either::Either; use either::Either;
use hir_def::{ use hir_def::{
AdtId, AssocItemId, CallableDefId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId, AdtId, AssocItemId, CallableDefId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId,
FunctionId, GenericDefId, GenericParamId, HasModule, ImplId, LocalFieldId, Lookup, StaticId, FunctionId, GenericDefId, GenericParamId, HasModule, ImplId, ItemContainerId, LocalFieldId,
StructId, TypeAliasId, TypeOrConstParamId, UnionId, VariantId, Lookup, StaticId, StructId, TypeAliasId, TypeOrConstParamId, UnionId, VariantId,
builtin_type::BuiltinType, builtin_type::BuiltinType,
expr_store::{ExpressionStore, path::Path}, expr_store::{ExpressionStore, path::Path},
hir::generics::{ hir::generics::{
@ -35,7 +35,7 @@ use hir_def::{
item_tree::FieldsShape, item_tree::FieldsShape,
lang_item::LangItem, lang_item::LangItem,
resolver::{HasResolver, LifetimeNs, Resolver, TypeNs}, resolver::{HasResolver, LifetimeNs, Resolver, TypeNs},
signatures::{TraitFlags, TypeAliasFlags}, signatures::{FunctionSignature, TraitFlags, TypeAliasFlags},
type_ref::{ type_ref::{
ConstRef, LifetimeRef, LiteralConstRef, PathId, TraitBoundModifier, ConstRef, LifetimeRef, LiteralConstRef, PathId, TraitBoundModifier,
TraitRef as HirTraitRef, TypeBound, TypeRef, TypeRefId, TraitRef as HirTraitRef, TypeBound, TypeRef, TypeRefId,
@ -86,21 +86,70 @@ impl ImplTraitLoweringState {
pub(crate) struct PathDiagnosticCallbackData(TypeRefId); pub(crate) struct PathDiagnosticCallbackData(TypeRefId);
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone)]
pub(crate) enum GenericArgsPosition { pub enum LifetimeElisionKind {
Type, /// Create a new anonymous lifetime parameter and reference it.
/// E.g. functions. ///
Value, /// If `report_in_path`, report an error when encountering lifetime elision in a path:
MethodCall, /// ```compile_fail
// FIXME: This is a temporary variant we need to work around the lack of lifetime elision. /// struct Foo<'a> { x: &'a () }
// The reason for its existence is that in `check_generic_args_len()`, without this, we will /// async fn foo(x: Foo) {}
// 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 /// Note: the error should not trigger when the elided lifetime is in a pattern or
// to lowering already include them. We probably can't do that, but we will still need to /// expression-position path:
// account for them when we properly implement lifetime elision. /// ```
FnSignature, /// struct Foo<'a> { x: &'a () }
OtherSignature, /// 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)] #[derive(Debug)]
@ -120,7 +169,7 @@ pub struct TyLoweringContext<'a> {
/// Tracks types with explicit `?Sized` bounds. /// Tracks types with explicit `?Sized` bounds.
pub(crate) unsized_types: FxHashSet<Ty>, pub(crate) unsized_types: FxHashSet<Ty>,
pub(crate) diagnostics: Vec<TyLoweringDiagnostic>, pub(crate) diagnostics: Vec<TyLoweringDiagnostic>,
pub(crate) in_fn_signature: bool, lifetime_elision: LifetimeElisionKind,
} }
impl<'a> TyLoweringContext<'a> { impl<'a> TyLoweringContext<'a> {
@ -129,6 +178,7 @@ impl<'a> TyLoweringContext<'a> {
resolver: &'a Resolver, resolver: &'a Resolver,
store: &'a ExpressionStore, store: &'a ExpressionStore,
def: GenericDefId, def: GenericDefId,
lifetime_elision: LifetimeElisionKind,
) -> Self { ) -> Self {
let impl_trait_mode = ImplTraitLoweringState::new(ImplTraitLoweringMode::Disallowed); let impl_trait_mode = ImplTraitLoweringState::new(ImplTraitLoweringMode::Disallowed);
let type_param_mode = ParamLoweringMode::Placeholder; let type_param_mode = ParamLoweringMode::Placeholder;
@ -144,7 +194,7 @@ impl<'a> TyLoweringContext<'a> {
type_param_mode, type_param_mode,
unsized_types: FxHashSet::default(), unsized_types: FxHashSet::default(),
diagnostics: Vec::new(), 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) 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 { pub fn with_impl_trait_mode(self, impl_trait_mode: ImplTraitLoweringMode) -> Self {
Self { impl_trait_mode: ImplTraitLoweringState::new(impl_trait_mode), ..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::Placeholder => TyKind::Error.intern(Interner),
TypeRef::Fn(fn_) => { TypeRef::Fn(fn_) => {
let substs = self.with_shifted_in(DebruijnIndex::ONE, |ctx| { let substs = self.with_shifted_in(DebruijnIndex::ONE, |ctx| {
Substitution::from_iter( let (params, ret) = fn_.split_params_and_ret();
Interner, let mut subst = Vec::with_capacity(fn_.params.len());
fn_.params.iter().map(|&(_, tr)| ctx.lower_ty(tr)), 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 { TyKind::Function(FnPointer {
num_binders: 0, // FIXME lower `for<'a> fn()` correctly num_binders: 0, // FIXME lower `for<'a> fn()` correctly
@ -431,11 +500,6 @@ impl<'a> TyLoweringContext<'a> {
self, self,
Self::on_path_diagnostic_callback(path_id.type_ref()), Self::on_path_diagnostic_callback(path_id.type_ref()),
&self.store[path_id], &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 generics = generics(db, def);
let mut res = ArenaMap::default(); let mut res = ArenaMap::default();
let mut ctx = TyLoweringContext::new(db, &resolver, &var_data.store, def) let mut ctx = TyLoweringContext::new(
.with_type_param_mode(ParamLoweringMode::Variable); db,
&resolver,
&var_data.store,
def,
LifetimeElisionKind::AnonymousReportError,
)
.with_type_param_mode(ParamLoweringMode::Variable);
for (field_id, field_data) in var_data.fields().iter() { 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))); 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 { ) -> GenericPredicates {
let generics = generics(db, def); let generics = generics(db, def);
let resolver = def.resolver(db); let resolver = def.resolver(db);
let mut ctx = TyLoweringContext::new(db, &resolver, generics.store(), def) let mut ctx = TyLoweringContext::new(
.with_type_param_mode(ParamLoweringMode::Variable); 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 // we have to filter out all other predicates *first*, before attempting to lower them
let predicate = |pred: &_, generics: &Generics, ctx: &mut TyLoweringContext<'_>| match pred { let predicate = |pred: &_, generics: &Generics, ctx: &mut TyLoweringContext<'_>| match pred {
@ -987,8 +1063,14 @@ pub(crate) fn trait_environment_query(
) -> Arc<TraitEnvironment> { ) -> Arc<TraitEnvironment> {
let generics = generics(db, def); let generics = generics(db, def);
let resolver = def.resolver(db); let resolver = def.resolver(db);
let mut ctx = TyLoweringContext::new(db, &resolver, generics.store(), def) let mut ctx = TyLoweringContext::new(
.with_type_param_mode(ParamLoweringMode::Placeholder); db,
&resolver,
generics.store(),
def,
LifetimeElisionKind::AnonymousReportError,
)
.with_type_param_mode(ParamLoweringMode::Placeholder);
let mut traits_in_scope = Vec::new(); let mut traits_in_scope = Vec::new();
let mut clauses = Vec::new(); let mut clauses = Vec::new();
for maybe_parent_generics in for maybe_parent_generics in
@ -1086,8 +1168,14 @@ where
{ {
let generics = generics(db, def); let generics = generics(db, def);
let resolver = def.resolver(db); let resolver = def.resolver(db);
let mut ctx = TyLoweringContext::new(db, &resolver, generics.store(), def) let mut ctx = TyLoweringContext::new(
.with_type_param_mode(ParamLoweringMode::Variable); db,
&resolver,
generics.store(),
def,
LifetimeElisionKind::AnonymousReportError,
)
.with_type_param_mode(ParamLoweringMode::Variable);
let mut predicates = Vec::new(); let mut predicates = Vec::new();
for maybe_parent_generics in for maybe_parent_generics in
@ -1188,9 +1276,15 @@ pub(crate) fn generic_defaults_with_diagnostics_query(
} }
let resolver = def.resolver(db); let resolver = def.resolver(db);
let mut ctx = TyLoweringContext::new(db, &resolver, generic_params.store(), def) let mut ctx = TyLoweringContext::new(
.with_impl_trait_mode(ImplTraitLoweringMode::Disallowed) db,
.with_type_param_mode(ParamLoweringMode::Variable); &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 idx = 0;
let mut has_any_default = false; let mut has_any_default = false;
let mut defaults = generic_params 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 { fn fn_sig_for_fn(db: &dyn HirDatabase, def: FunctionId) -> PolyFnSig {
let data = db.function_signature(def); let data = db.function_signature(def);
let resolver = def.resolver(db); let resolver = def.resolver(db);
let mut ctx_params = TyLoweringContext::new(db, &resolver, &data.store, def.into()) let mut ctx_params = TyLoweringContext::new(
.with_type_param_mode(ParamLoweringMode::Variable); db,
ctx_params.in_fn_signature = true; &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 params = data.params.iter().map(|&tr| ctx_params.lower_ty(tr));
let ret = match data.ret_type { let ret = match data.ret_type {
Some(ret_type) => { Some(ret_type) => {
let mut ctx_ret = TyLoweringContext::new(db, &resolver, &data.store, def.into()) let mut ctx_ret = TyLoweringContext::new(
.with_impl_trait_mode(ImplTraitLoweringMode::Opaque) db,
.with_type_param_mode(ParamLoweringMode::Variable); &resolver,
ctx_ret.in_fn_signature = true; &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) ctx_ret.lower_ty(ret_type)
} }
None => TyKind::Tuple(0, Substitution::empty(Interner)).intern(Interner), None => TyKind::Tuple(0, Substitution::empty(Interner)).intern(Interner),
@ -1316,8 +1420,15 @@ fn type_for_const(db: &dyn HirDatabase, def: ConstId) -> Binders<Ty> {
let data = db.const_signature(def); let data = db.const_signature(def);
let generics = generics(db, def.into()); let generics = generics(db, def.into());
let resolver = def.resolver(db); let resolver = def.resolver(db);
let mut ctx = TyLoweringContext::new(db, &resolver, &data.store, def.into()) let parent = def.loc(db).container;
.with_type_param_mode(ParamLoweringMode::Variable); 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)) make_binders(db, &generics, ctx.lower_ty(data.type_ref))
} }
@ -1326,18 +1437,20 @@ fn type_for_const(db: &dyn HirDatabase, def: ConstId) -> Binders<Ty> {
fn type_for_static(db: &dyn HirDatabase, def: StaticId) -> Binders<Ty> { fn type_for_static(db: &dyn HirDatabase, def: StaticId) -> Binders<Ty> {
let data = db.static_signature(def); let data = db.static_signature(def);
let resolver = def.resolver(db); 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)) Binders::empty(Interner, ctx.lower_ty(data.type_ref))
} }
fn fn_sig_for_struct_constructor(db: &dyn HirDatabase, def: StructId) -> PolyFnSig { fn fn_sig_for_struct_constructor(db: &dyn HirDatabase, def: StructId) -> PolyFnSig {
let struct_data = db.variant_fields(def.into()); let field_tys = db.field_types(def.into());
let fields = struct_data.fields(); let params = field_tys.iter().map(|(_, ty)| ty.skip_binders().clone());
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 (ret, binders) = type_for_adt(db, def.into()).into_value_and_skipped_binders(); let (ret, binders) = type_for_adt(db, def.into()).into_value_and_skipped_binders();
Binders::new( Binders::new(
binders, binders,
@ -1364,13 +1477,9 @@ fn type_for_struct_constructor(db: &dyn HirDatabase, def: StructId) -> Option<Bi
} }
fn fn_sig_for_enum_variant_constructor(db: &dyn HirDatabase, def: EnumVariantId) -> PolyFnSig { fn fn_sig_for_enum_variant_constructor(db: &dyn HirDatabase, def: EnumVariantId) -> PolyFnSig {
let var_data = db.variant_fields(def.into()); let field_tys = db.field_types(def.into());
let fields = var_data.fields(); let params = field_tys.iter().map(|(_, ty)| ty.skip_binders().clone());
let resolver = def.resolver(db);
let parent = def.lookup(db).parent; 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(); let (ret, binders) = type_for_adt(db, parent.into()).into_value_and_skipped_binders();
Binders::new( Binders::new(
binders, binders,
@ -1429,9 +1538,15 @@ pub(crate) fn type_for_type_alias_with_diagnostics_query(
} else { } else {
let resolver = t.resolver(db); let resolver = t.resolver(db);
let alias = db.type_alias_signature(t); let alias = db.type_alias_signature(t);
let mut ctx = TyLoweringContext::new(db, &resolver, &alias.store, t.into()) let mut ctx = TyLoweringContext::new(
.with_impl_trait_mode(ImplTraitLoweringMode::Opaque) db,
.with_type_param_mode(ParamLoweringMode::Variable); &resolver,
&alias.store,
t.into(),
LifetimeElisionKind::AnonymousReportError,
)
.with_impl_trait_mode(ImplTraitLoweringMode::Opaque)
.with_type_param_mode(ParamLoweringMode::Variable);
let res = alias let res = alias
.ty .ty
.map(|type_ref| ctx.lower_ty(type_ref)) .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 impl_data = db.impl_signature(impl_id);
let resolver = impl_id.resolver(db); let resolver = impl_id.resolver(db);
let generics = generics(db, impl_id.into()); let generics = generics(db, impl_id.into());
let mut ctx = TyLoweringContext::new(db, &resolver, &impl_data.store, impl_id.into()) let mut ctx = TyLoweringContext::new(
.with_type_param_mode(ParamLoweringMode::Variable); 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)), make_binders(db, &generics, ctx.lower_ty(impl_data.self_ty)),
create_diagnostics(ctx.diagnostics), 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 (parent_data, store) = db.generic_params_and_store(def.parent());
let data = &parent_data[def.local_id()]; let data = &parent_data[def.local_id()];
let resolver = def.parent().resolver(db); 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 { let ty = match data {
TypeOrConstParamData::TypeParamData(_) => { TypeOrConstParamData::TypeParamData(_) => {
never!(); never!();
@ -1566,8 +1693,14 @@ pub(crate) fn impl_trait_with_diagnostics_query(
) -> Option<(Binders<TraitRef>, Diagnostics)> { ) -> Option<(Binders<TraitRef>, Diagnostics)> {
let impl_data = db.impl_signature(impl_id); let impl_data = db.impl_signature(impl_id);
let resolver = impl_id.resolver(db); let resolver = impl_id.resolver(db);
let mut ctx = TyLoweringContext::new(db, &resolver, &impl_data.store, impl_id.into()) let mut ctx = TyLoweringContext::new(
.with_type_param_mode(ParamLoweringMode::Variable); 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 (self_ty, binders) = db.impl_self_ty(impl_id).into_value_and_skipped_binders();
let target_trait = impl_data.target_trait.as_ref()?; let target_trait = impl_data.target_trait.as_ref()?;
let trait_ref = Binders::new(binders, ctx.lower_trait_ref(target_trait, self_ty)?); 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 // FIXME unify with fn_sig_for_fn instead of doing lowering twice, maybe
let data = db.function_signature(def); let data = db.function_signature(def);
let resolver = def.resolver(db); let resolver = def.resolver(db);
let mut ctx_ret = TyLoweringContext::new(db, &resolver, &data.store, def.into()) let mut ctx_ret =
.with_impl_trait_mode(ImplTraitLoweringMode::Opaque) TyLoweringContext::new(db, &resolver, &data.store, def.into(), LifetimeElisionKind::Infer)
.with_type_param_mode(ParamLoweringMode::Variable); .with_impl_trait_mode(ImplTraitLoweringMode::Opaque)
.with_type_param_mode(ParamLoweringMode::Variable);
if let Some(ret_type) = data.ret_type { if let Some(ret_type) = data.ret_type {
let _ret = ctx_ret.lower_ty(ret_type); let _ret = ctx_ret.lower_ty(ret_type);
} }
@ -1603,9 +1737,15 @@ pub(crate) fn type_alias_impl_traits(
) -> Option<Arc<Binders<ImplTraits>>> { ) -> Option<Arc<Binders<ImplTraits>>> {
let data = db.type_alias_signature(def); let data = db.type_alias_signature(def);
let resolver = def.resolver(db); let resolver = def.resolver(db);
let mut ctx = TyLoweringContext::new(db, &resolver, &data.store, def.into()) let mut ctx = TyLoweringContext::new(
.with_impl_trait_mode(ImplTraitLoweringMode::Opaque) db,
.with_type_param_mode(ParamLoweringMode::Variable); &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 { if let Some(type_ref) = data.ty {
let _ty = ctx.lower_ty(type_ref); let _ty = ctx.lower_ty(type_ref);
} }

View File

@ -63,6 +63,24 @@ pub enum PathLoweringDiagnostic {
/// Whether the `GenericArgs` contains a `Self` arg. /// Whether the `GenericArgs` contains a `Self` arg.
has_self_arg: bool, 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)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]

View File

@ -27,8 +27,8 @@ use crate::{
db::HirDatabase, db::HirDatabase,
error_lifetime, error_lifetime,
generics::{Generics, generics}, generics::{Generics, generics},
lower::{GenericArgsPosition, named_associated_type_shorthand_candidates}, lower::{LifetimeElisionKind, named_associated_type_shorthand_candidates},
to_assoc_type_id, to_chalk_trait_id, to_placeholder_idx, static_lifetime, to_assoc_type_id, to_chalk_trait_id, to_placeholder_idx,
utils::associated_type_by_name_including_super_traits, utils::associated_type_by_name_including_super_traits,
}; };
@ -52,7 +52,6 @@ pub(crate) struct PathLoweringContext<'a, 'b> {
current_segment_idx: usize, current_segment_idx: usize,
/// Contains the previous segment if `current_segment_idx == segments.len()` /// Contains the previous segment if `current_segment_idx == segments.len()`
current_or_prev_segment: PathSegment<'a>, current_or_prev_segment: PathSegment<'a>,
position: GenericArgsPosition,
} }
impl<'a, 'b> PathLoweringContext<'a, 'b> { impl<'a, 'b> PathLoweringContext<'a, 'b> {
@ -61,7 +60,6 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> {
ctx: &'a mut TyLoweringContext<'b>, ctx: &'a mut TyLoweringContext<'b>,
on_diagnostic: PathDiagnosticCallback<'a>, on_diagnostic: PathDiagnosticCallback<'a>,
path: &'a Path, path: &'a Path,
position: GenericArgsPosition,
) -> Self { ) -> Self {
let segments = path.segments(); let segments = path.segments();
let first_segment = segments.first().unwrap_or(PathSegment::MISSING); let first_segment = segments.first().unwrap_or(PathSegment::MISSING);
@ -72,7 +70,6 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> {
segments, segments,
current_segment_idx: 0, current_segment_idx: 0,
current_or_prev_segment: first_segment, 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()"); .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( pub(crate) fn lower_ty_relative_path(
&mut self, &mut self,
ty: Ty, 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). // When calling this, the current segment is the resolved segment (we don't advance it yet).
pub(crate) fn lower_partly_resolved_path( pub(crate) fn lower_partly_resolved_path(
&mut self, &mut self,
@ -189,6 +183,7 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> {
associated_ty.into(), associated_ty.into(),
false, false,
None, None,
true,
); );
let substitution = Substitution::from_iter( let substitution = Substitution::from_iter(
Interner, 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 // 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 // that method to optionally take parent `Substitution` as we already know them at
// this point (`t.substitution`). // 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( let substs = Substitution::from_iter(
Interner, Interner,
@ -539,7 +534,7 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> {
TyDefId::AdtId(it) => it.into(), TyDefId::AdtId(it) => it.into(),
TyDefId::TypeAliasId(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) self.ctx.db.ty(typeable).substitute(Interner, &substs)
} }
@ -552,6 +547,7 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> {
// special-case enum variants // special-case enum variants
resolved: ValueTyDefId, resolved: ValueTyDefId,
infer_args: bool, infer_args: bool,
lowering_assoc_type_generics: bool,
) -> Substitution { ) -> Substitution {
let prev_current_segment_idx = self.current_segment_idx; let prev_current_segment_idx = self.current_segment_idx;
let prev_current_segment = self.current_or_prev_segment; 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() 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_segment_idx = prev_current_segment_idx;
self.current_or_prev_segment = prev_current_segment; self.current_or_prev_segment = prev_current_segment;
result result
@ -599,26 +600,41 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> {
def: GenericDefId, def: GenericDefId,
infer_args: bool, infer_args: bool,
explicit_self_ty: Option<Ty>, explicit_self_ty: Option<Ty>,
lowering_assoc_type_generics: bool,
) -> Substitution { ) -> Substitution {
let prohibit_parens = match def { let mut lifetime_elision = self.ctx.lifetime_elision.clone();
GenericDefId::TraitId(trait_) => {
// RTN is prohibited anyways if we got here. if let Some(args) = self.current_or_prev_segment.args_and_bindings {
let is_rtn = if args.parenthesized != GenericArgsParentheses::No {
self.current_or_prev_segment.args_and_bindings.is_some_and(|generics| { let prohibit_parens = match def {
generics.parenthesized == GenericArgsParentheses::ReturnTypeNotation GenericDefId::TraitId(trait_) => {
}); // RTN is prohibited anyways if we got here.
let is_fn_trait = !self let is_rtn =
.ctx args.parenthesized == GenericArgsParentheses::ReturnTypeNotation;
.db let is_fn_trait = self
.trait_signature(trait_) .ctx
.flags .db
.contains(TraitFlags::RUSTC_PAREN_SUGAR); .trait_signature(trait_)
is_rtn || is_fn_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( self.substs_from_args_and_bindings(
@ -627,6 +643,8 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> {
infer_args, infer_args,
explicit_self_ty, explicit_self_ty,
PathGenericsSource::Segment(self.current_segment_u32()), 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, infer_args: bool,
explicit_self_ty: Option<Ty>, explicit_self_ty: Option<Ty>,
generics_source: PathGenericsSource, generics_source: PathGenericsSource,
lowering_assoc_type_generics: bool,
lifetime_elision: LifetimeElisionKind,
) -> Substitution { ) -> Substitution {
struct LowererCtx<'a, 'b, 'c> { struct LowererCtx<'a, 'b, 'c> {
ctx: &'a mut PathLoweringContext<'b, 'c>, ctx: &'a mut PathLoweringContext<'b, 'c>,
@ -761,6 +781,36 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> {
GenericParamId::LifetimeParamId(_) => error_lifetime().cast(Interner), 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( substs_from_args_and_bindings(
@ -769,7 +819,8 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> {
args_and_bindings, args_and_bindings,
def, def,
infer_args, infer_args,
self.position, lifetime_elision,
lowering_assoc_type_generics,
explicit_self_ty, explicit_self_ty,
&mut LowererCtx { ctx: self, generics_source }, &mut LowererCtx { ctx: self, generics_source },
) )
@ -789,7 +840,7 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> {
resolved: TraitId, resolved: TraitId,
explicit_self_ty: Ty, explicit_self_ty: Ty,
) -> Substitution { ) -> 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>( pub(super) fn assoc_type_bindings_from_type_bound<'c>(
@ -807,20 +858,25 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> {
None => return SmallVec::new(), None => return SmallVec::new(),
Some(t) => t, Some(t) => t,
}; };
// FIXME: `substs_from_path_segment()` pushes `TyKind::Error` for every parent let substitution =
// generic params. It's inefficient to splice the `Substitution`s, so we may want self.with_lifetime_elision(LifetimeElisionKind::AnonymousReportError, |this| {
// that method to optionally take parent `Substitution` as we already know them at // FIXME: `substs_from_path_segment()` pushes `TyKind::Error` for every parent
// this point (`super_trait_ref.substitution`). // generic params. It's inefficient to splice the `Substitution`s, so we may want
let substitution = self.substs_from_args_and_bindings( // that method to optionally take parent `Substitution` as we already know them at
binding.args.as_ref(), // this point (`super_trait_ref.substitution`).
associated_ty.into(), this.substs_from_args_and_bindings(
false, // this is not relevant binding.args.as_ref(),
Some(super_trait_ref.self_type_parameter(Interner)), associated_ty.into(),
PathGenericsSource::AssocType { false, // this is not relevant
segment: self.current_segment_u32(), Some(super_trait_ref.self_type_parameter(Interner)),
assoc_type: binding_idx as u32, PathGenericsSource::AssocType {
}, segment: this.current_segment_u32(),
); assoc_type: binding_idx as u32,
},
false,
this.ctx.lifetime_elision.clone(),
)
});
let substitution = Substitution::from_iter( let substitution = Substitution::from_iter(
Interner, Interner,
super_trait_ref.substitution.iter(Interner).chain( 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( let mut predicates: SmallVec<[_; 1]> = SmallVec::with_capacity(
binding.type_ref.as_ref().map_or(0, |_| 1) + binding.bounds.len(), binding.type_ref.as_ref().map_or(0, |_| 1) + binding.bounds.len(),
); );
if let Some(type_ref) = binding.type_ref { if let Some(type_ref) = binding.type_ref {
match (&self.ctx.store[type_ref], self.ctx.impl_trait_mode.mode) { let lifetime_elision =
(TypeRef::ImplTrait(_), ImplTraitLoweringMode::Disallowed) => (), if args_and_bindings.parenthesized == GenericArgsParentheses::ParenSugar {
(_, ImplTraitLoweringMode::Disallowed | ImplTraitLoweringMode::Opaque) => { // `Fn()`-style generics are elided like functions. This is `Output` (we lower to it in hir-def).
let ty = self.ctx.lower_ty(type_ref); LifetimeElisionKind::for_fn_ret()
let alias_eq = } else {
AliasEq { alias: AliasTy::Projection(projection_ty.clone()), ty }; self.ctx.lifetime_elision.clone()
predicates };
.push(crate::wrap_empty_binders(WhereClause::AliasEq(alias_eq))); 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 predicates
}) })
}) })
@ -868,6 +947,17 @@ pub(crate) enum TypeLikeConst<'a> {
} }
pub(crate) trait GenericArgsLowerer { 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( fn report_len_mismatch(
&mut self, &mut self,
def: GenericDefId, def: GenericDefId,
@ -905,7 +995,8 @@ fn check_generic_args_len(
def: GenericDefId, def: GenericDefId,
def_generics: &Generics, def_generics: &Generics,
infer_args: bool, infer_args: bool,
position: GenericArgsPosition, lifetime_elision: &LifetimeElisionKind,
lowering_assoc_type_generics: bool,
ctx: &mut impl GenericArgsLowerer, ctx: &mut impl GenericArgsLowerer,
) -> bool { ) -> bool {
let mut had_error = false; 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 lifetime_args_len = def_generics.len_lifetimes_self();
let infer_lifetimes = if provided_lifetimes_count == 0 && lifetime_args_len > 0 && !lowering_assoc_type_generics {
position != GenericArgsPosition::OtherSignature && provided_lifetimes_count == 0; // In generic associated types, we never allow inferring the lifetimes.
match lifetime_elision {
let max_expected_lifetime_args = def_generics.len_lifetimes_self(); &LifetimeElisionKind::AnonymousCreateParameter { report_in_path } => {
let min_expected_lifetime_args = if infer_lifetimes { 0 } else { max_expected_lifetime_args }; ctx.report_elided_lifetimes_in_path(def, lifetime_args_len as u32, report_in_path);
if provided_lifetimes_count < min_expected_lifetime_args had_error |= report_in_path;
|| max_expected_lifetime_args < provided_lifetimes_count }
{ 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( ctx.report_len_mismatch(
def, def,
provided_lifetimes_count as u32, provided_lifetimes_count as u32,
max_expected_lifetime_args as u32, lifetime_args_len as u32,
IncorrectGenericsLenKind::Lifetimes, IncorrectGenericsLenKind::Lifetimes,
); );
had_error = true; had_error = true;
@ -974,7 +1083,8 @@ pub(crate) fn substs_from_args_and_bindings(
args_and_bindings: Option<&GenericArgs>, args_and_bindings: Option<&GenericArgs>,
def: GenericDefId, def: GenericDefId,
mut infer_args: bool, mut infer_args: bool,
position: GenericArgsPosition, lifetime_elision: LifetimeElisionKind,
lowering_assoc_type_generics: bool,
explicit_self_ty: Option<Ty>, explicit_self_ty: Option<Ty>,
ctx: &mut impl GenericArgsLowerer, ctx: &mut impl GenericArgsLowerer,
) -> Substitution { ) -> Substitution {
@ -991,8 +1101,15 @@ pub(crate) fn substs_from_args_and_bindings(
args_slice.iter().any(|arg| !matches!(arg, GenericArg::Lifetime(_))); args_slice.iter().any(|arg| !matches!(arg, GenericArg::Lifetime(_)));
infer_args &= !has_non_lifetime_args; infer_args &= !has_non_lifetime_args;
let had_count_error = let had_count_error = check_generic_args_len(
check_generic_args_len(args_and_bindings, def, &def_generics, infer_args, position, ctx); args_and_bindings,
def,
&def_generics,
infer_args,
&lifetime_elision,
lowering_assoc_type_generics,
ctx,
);
let mut substs = Vec::with_capacity(def_generics.len()); 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))) => { (None, Some(&(param_id, param))) => {
// If there are fewer arguments than parameters, it means we're inferring the remaining arguments. // 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(); params.next();
} }

View File

@ -118,6 +118,8 @@ diagnostics![
BadRtn, BadRtn,
IncorrectGenericsLen, IncorrectGenericsLen,
IncorrectGenericsOrder, IncorrectGenericsOrder,
MissingLifetime,
ElidedLifetimesInPath,
]; ];
#[derive(Debug)] #[derive(Debug)]
@ -440,6 +442,23 @@ pub struct IncorrectGenericsLen {
pub def: GenericDef, 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)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GenericArgKind { pub enum GenericArgKind {
Lifetime, Lifetime,
@ -861,6 +880,31 @@ impl AnyDiagnostic {
let expected_kind = GenericArgKind::from_id(param_id); let expected_kind = GenericArgKind::from_id(param_id);
IncorrectGenericsOrder { provided_arg, expected_kind }.into() 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()
}
}) })
} }

View File

@ -34,8 +34,8 @@ use hir_expand::{
name::{AsName, Name}, name::{AsName, Name},
}; };
use hir_ty::{ use hir_ty::{
Adjustment, AliasTy, InferenceResult, Interner, ProjectionTy, Substitution, TraitEnvironment, Adjustment, AliasTy, InferenceResult, Interner, LifetimeElisionKind, ProjectionTy,
Ty, TyExt, TyKind, TyLoweringContext, Substitution, TraitEnvironment, Ty, TyExt, TyKind, TyLoweringContext,
diagnostics::{ diagnostics::{
InsideUnsafeBlock, record_literal_missing_fields, record_pattern_missing_fields, InsideUnsafeBlock, record_literal_missing_fields, record_pattern_missing_fields,
unsafe_operations, unsafe_operations,
@ -261,11 +261,15 @@ impl SourceAnalyzer {
pub(crate) fn type_of_type(&self, db: &dyn HirDatabase, ty: &ast::Type) -> Option<Type> { pub(crate) fn type_of_type(&self, db: &dyn HirDatabase, ty: &ast::Type) -> Option<Type> {
let type_ref = self.type_id(ty)?; let type_ref = self.type_id(ty)?;
let ty = hir_ty::TyLoweringContext::new( let ty = TyLoweringContext::new(
db, db,
&self.resolver, &self.resolver,
self.store()?, self.store()?,
self.resolver.generic_def()?, 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); .lower_ty(type_ref);
Some(Type::new_with_resolver(db, &self.resolver, ty)) Some(Type::new_with_resolver(db, &self.resolver, ty))
@ -1553,7 +1557,8 @@ fn resolve_hir_path_(
let (ty, unresolved) = match path.type_anchor() { let (ty, unresolved) = match path.type_anchor() {
Some(type_ref) => resolver.generic_def().and_then(|def| { Some(type_ref) => resolver.generic_def().and_then(|def| {
let (_, res) = 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())) res.map(|ty_ns| (ty_ns, path.segments().first()))
}), }),
None => { None => {
@ -1681,7 +1686,8 @@ fn resolve_hir_path_qualifier(
let (ty, unresolved) = match path.type_anchor() { let (ty, unresolved) = match path.type_anchor() {
Some(type_ref) => resolver.generic_def().and_then(|def| { Some(type_ref) => resolver.generic_def().and_then(|def| {
let (_, res) = 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())) res.map(|ty_ns| (ty_ns, path.segments().first()))
}), }),
None => { None => {

View File

@ -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
"#,
);
}
}

View File

@ -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
"#,
);
}
}

View File

@ -27,6 +27,7 @@ mod handlers {
pub(crate) mod await_outside_of_async; pub(crate) mod await_outside_of_async;
pub(crate) mod bad_rtn; pub(crate) mod bad_rtn;
pub(crate) mod break_outside_of_loop; pub(crate) mod break_outside_of_loop;
pub(crate) mod elided_lifetimes_in_path;
pub(crate) mod expected_function; pub(crate) mod expected_function;
pub(crate) mod generic_args_prohibited; pub(crate) mod generic_args_prohibited;
pub(crate) mod inactive_code; pub(crate) mod inactive_code;
@ -40,6 +41,7 @@ mod handlers {
pub(crate) mod malformed_derive; pub(crate) mod malformed_derive;
pub(crate) mod mismatched_arg_count; pub(crate) mod mismatched_arg_count;
pub(crate) mod missing_fields; pub(crate) mod missing_fields;
pub(crate) mod missing_lifetime;
pub(crate) mod missing_match_arms; pub(crate) mod missing_match_arms;
pub(crate) mod missing_unsafe; pub(crate) mod missing_unsafe;
pub(crate) mod moved_out_of_ref; 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::BadRtn(d) => handlers::bad_rtn::bad_rtn(&ctx, &d),
AnyDiagnostic::IncorrectGenericsLen(d) => handlers::incorrect_generics_len::incorrect_generics_len(&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::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) res.push(d)
} }