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>,
}
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,

View File

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

View File

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

View File

@ -440,6 +440,8 @@ impl InferenceContext<'_> {
// collect explicitly written argument types
for arg_type in arg_types.iter() {
let arg_ty = match arg_type {
// FIXME: I think rustc actually lowers closure params with `LifetimeElisionKind::AnonymousCreateParameter`
// (but the return type with infer).
Some(type_ref) => self.make_body_ty(*type_ref),
None => self.table.new_type_var(),
};

View File

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

View File

@ -37,7 +37,7 @@ use crate::{
},
lang_items::lang_items_for_bin_op,
lower::{
GenericArgsPosition, ParamLoweringMode, lower_to_chalk_mutability,
LifetimeElisionKind, ParamLoweringMode, lower_to_chalk_mutability,
path::{GenericArgsLowerer, TypeLikeConst, substs_from_args_and_bindings},
},
mapping::{ToChalk, from_chalk},
@ -2162,6 +2162,23 @@ impl InferenceContext<'_> {
}
}
}
fn report_elided_lifetimes_in_path(
&mut self,
_def: GenericDefId,
_expected_count: u32,
_hard_error: bool,
) {
unreachable!("we set `LifetimeElisionKind::Infer`")
}
fn report_elision_failure(&mut self, _def: GenericDefId, _expected_count: u32) {
unreachable!("we set `LifetimeElisionKind::Infer`")
}
fn report_missing_lifetime(&mut self, _def: GenericDefId, _expected_count: u32) {
unreachable!("we set `LifetimeElisionKind::Infer`")
}
}
substs_from_args_and_bindings(
@ -2170,7 +2187,8 @@ impl InferenceContext<'_> {
generic_args,
def,
true,
GenericArgsPosition::MethodCall,
LifetimeElisionKind::Infer,
false,
None,
&mut LowererCtx { ctx: self, expr },
)

View File

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

View File

@ -94,8 +94,8 @@ pub use infer::{
};
pub use interner::Interner;
pub use lower::{
ImplTraitLoweringMode, ParamLoweringMode, TyDefId, TyLoweringContext, ValueTyDefId,
associated_type_shorthand_candidates, diagnostics::*,
ImplTraitLoweringMode, LifetimeElisionKind, ParamLoweringMode, TyDefId, TyLoweringContext,
ValueTyDefId, associated_type_shorthand_candidates, diagnostics::*,
};
pub use mapping::{
from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, from_placeholder_idx,
@ -529,13 +529,13 @@ pub type PolyFnSig = Binders<CallableSig>;
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 }

View File

@ -25,8 +25,8 @@ use chalk_ir::{
use either::Either;
use hir_def::{
AdtId, AssocItemId, CallableDefId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId,
FunctionId, GenericDefId, GenericParamId, HasModule, ImplId, LocalFieldId, Lookup, StaticId,
StructId, TypeAliasId, TypeOrConstParamId, UnionId, VariantId,
FunctionId, GenericDefId, GenericParamId, HasModule, ImplId, ItemContainerId, LocalFieldId,
Lookup, StaticId, StructId, TypeAliasId, TypeOrConstParamId, UnionId, VariantId,
builtin_type::BuiltinType,
expr_store::{ExpressionStore, path::Path},
hir::generics::{
@ -35,7 +35,7 @@ use hir_def::{
item_tree::FieldsShape,
lang_item::LangItem,
resolver::{HasResolver, LifetimeNs, Resolver, TypeNs},
signatures::{TraitFlags, TypeAliasFlags},
signatures::{FunctionSignature, TraitFlags, TypeAliasFlags},
type_ref::{
ConstRef, LifetimeRef, LiteralConstRef, PathId, TraitBoundModifier,
TraitRef as HirTraitRef, TypeBound, TypeRef, TypeRefId,
@ -86,21 +86,70 @@ impl ImplTraitLoweringState {
pub(crate) struct PathDiagnosticCallbackData(TypeRefId);
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum GenericArgsPosition {
Type,
/// E.g. functions.
Value,
MethodCall,
// FIXME: This is a temporary variant we need to work around the lack of lifetime elision.
// The reason for its existence is that in `check_generic_args_len()`, without this, we will
// not infer elide lifetimes.
// They indeed should not be inferred - they should be elided - but we won't elide them either,
// emitting an error instead. rustc elides them in late resolve, and the generics it passes
// to lowering already include them. We probably can't do that, but we will still need to
// account for them when we properly implement lifetime elision.
FnSignature,
OtherSignature,
#[derive(Debug, Clone)]
pub enum LifetimeElisionKind {
/// Create a new anonymous lifetime parameter and reference it.
///
/// If `report_in_path`, report an error when encountering lifetime elision in a path:
/// ```compile_fail
/// struct Foo<'a> { x: &'a () }
/// async fn foo(x: Foo) {}
/// ```
///
/// Note: the error should not trigger when the elided lifetime is in a pattern or
/// expression-position path:
/// ```
/// struct Foo<'a> { x: &'a () }
/// async fn foo(Foo { x: _ }: Foo<'_>) {}
/// ```
AnonymousCreateParameter { report_in_path: bool },
/// Replace all anonymous lifetimes by provided lifetime.
Elided(Lifetime),
/// Give a hard error when either `&` or `'_` is written. Used to
/// rule out things like `where T: Foo<'_>`. Does not imply an
/// error on default object bounds (e.g., `Box<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 @@ pub struct TyLoweringContext<'a> {
/// 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 @@ impl<'a> TyLoweringContext<'a> {
resolver: &'a Resolver,
store: &'a ExpressionStore,
def: GenericDefId,
lifetime_elision: LifetimeElisionKind,
) -> Self {
let impl_trait_mode = ImplTraitLoweringState::new(ImplTraitLoweringMode::Disallowed);
let type_param_mode = ParamLoweringMode::Placeholder;
@ -144,7 +194,7 @@ impl<'a> TyLoweringContext<'a> {
type_param_mode,
unsized_types: FxHashSet::default(),
diagnostics: Vec::new(),
in_fn_signature: false,
lifetime_elision,
}
}
@ -167,6 +217,17 @@ impl<'a> TyLoweringContext<'a> {
self.with_debruijn(self.in_binders.shifted_in_from(debruijn), f)
}
fn with_lifetime_elision<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 @@ impl<'a> TyLoweringContext<'a> {
TypeRef::Placeholder => TyKind::Error.intern(Interner),
TypeRef::Fn(fn_) => {
let substs = self.with_shifted_in(DebruijnIndex::ONE, |ctx| {
Substitution::from_iter(
Interner,
fn_.params.iter().map(|&(_, tr)| ctx.lower_ty(tr)),
)
let (params, ret) = fn_.split_params_and_ret();
let mut subst = Vec::with_capacity(fn_.params.len());
ctx.with_lifetime_elision(
LifetimeElisionKind::AnonymousCreateParameter { report_in_path: false },
|ctx| {
subst.extend(params.iter().map(|&(_, tr)| ctx.lower_ty(tr)));
},
);
ctx.with_lifetime_elision(LifetimeElisionKind::for_fn_ret(), |ctx| {
subst.push(ctx.lower_ty(ret));
});
Substitution::from_iter(Interner, subst)
});
TyKind::Function(FnPointer {
num_binders: 0, // FIXME lower `for<'a> fn()` correctly
@ -431,11 +500,6 @@ impl<'a> TyLoweringContext<'a> {
self,
Self::on_path_diagnostic_callback(path_id.type_ref()),
&self.store[path_id],
if self.in_fn_signature {
GenericArgsPosition::FnSignature
} else {
GenericArgsPosition::Type
},
)
}
@ -855,8 +919,14 @@ pub(crate) fn field_types_with_diagnostics_query(
};
let generics = generics(db, def);
let mut res = ArenaMap::default();
let mut ctx = TyLoweringContext::new(db, &resolver, &var_data.store, def)
.with_type_param_mode(ParamLoweringMode::Variable);
let mut ctx = TyLoweringContext::new(
db,
&resolver,
&var_data.store,
def,
LifetimeElisionKind::AnonymousReportError,
)
.with_type_param_mode(ParamLoweringMode::Variable);
for (field_id, field_data) in var_data.fields().iter() {
res.insert(field_id, make_binders(db, &generics, ctx.lower_ty(field_data.type_ref)));
}
@ -879,8 +949,14 @@ pub(crate) fn generic_predicates_for_param_query(
) -> GenericPredicates {
let generics = generics(db, def);
let resolver = def.resolver(db);
let mut ctx = TyLoweringContext::new(db, &resolver, generics.store(), def)
.with_type_param_mode(ParamLoweringMode::Variable);
let mut ctx = TyLoweringContext::new(
db,
&resolver,
generics.store(),
def,
LifetimeElisionKind::AnonymousReportError,
)
.with_type_param_mode(ParamLoweringMode::Variable);
// we have to filter out all other predicates *first*, before attempting to lower them
let predicate = |pred: &_, generics: &Generics, ctx: &mut TyLoweringContext<'_>| match pred {
@ -987,8 +1063,14 @@ pub(crate) fn trait_environment_query(
) -> Arc<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 @@ where
{
let generics = generics(db, def);
let resolver = def.resolver(db);
let mut ctx = TyLoweringContext::new(db, &resolver, generics.store(), def)
.with_type_param_mode(ParamLoweringMode::Variable);
let mut ctx = TyLoweringContext::new(
db,
&resolver,
generics.store(),
def,
LifetimeElisionKind::AnonymousReportError,
)
.with_type_param_mode(ParamLoweringMode::Variable);
let mut predicates = Vec::new();
for maybe_parent_generics in
@ -1188,9 +1276,15 @@ pub(crate) fn generic_defaults_with_diagnostics_query(
}
let resolver = def.resolver(db);
let mut ctx = TyLoweringContext::new(db, &resolver, generic_params.store(), def)
.with_impl_trait_mode(ImplTraitLoweringMode::Disallowed)
.with_type_param_mode(ParamLoweringMode::Variable);
let mut ctx = TyLoweringContext::new(
db,
&resolver,
generic_params.store(),
def,
LifetimeElisionKind::AnonymousReportError,
)
.with_impl_trait_mode(ImplTraitLoweringMode::Disallowed)
.with_type_param_mode(ParamLoweringMode::Variable);
let mut idx = 0;
let mut has_any_default = false;
let mut defaults = generic_params
@ -1273,17 +1367,27 @@ pub(crate) fn generic_defaults_with_diagnostics_cycle_result(
fn fn_sig_for_fn(db: &dyn HirDatabase, def: FunctionId) -> PolyFnSig {
let data = db.function_signature(def);
let resolver = def.resolver(db);
let mut ctx_params = TyLoweringContext::new(db, &resolver, &data.store, def.into())
.with_type_param_mode(ParamLoweringMode::Variable);
ctx_params.in_fn_signature = true;
let mut ctx_params = TyLoweringContext::new(
db,
&resolver,
&data.store,
def.into(),
LifetimeElisionKind::for_fn_params(&data),
)
.with_type_param_mode(ParamLoweringMode::Variable);
let params = data.params.iter().map(|&tr| ctx_params.lower_ty(tr));
let ret = match data.ret_type {
Some(ret_type) => {
let mut ctx_ret = TyLoweringContext::new(db, &resolver, &data.store, def.into())
.with_impl_trait_mode(ImplTraitLoweringMode::Opaque)
.with_type_param_mode(ParamLoweringMode::Variable);
ctx_ret.in_fn_signature = true;
let mut ctx_ret = TyLoweringContext::new(
db,
&resolver,
&data.store,
def.into(),
LifetimeElisionKind::for_fn_ret(),
)
.with_impl_trait_mode(ImplTraitLoweringMode::Opaque)
.with_type_param_mode(ParamLoweringMode::Variable);
ctx_ret.lower_ty(ret_type)
}
None => TyKind::Tuple(0, Substitution::empty(Interner)).intern(Interner),
@ -1316,8 +1420,15 @@ fn type_for_const(db: &dyn HirDatabase, def: ConstId) -> Binders<Ty> {
let data = db.const_signature(def);
let generics = generics(db, def.into());
let resolver = def.resolver(db);
let mut ctx = TyLoweringContext::new(db, &resolver, &data.store, def.into())
.with_type_param_mode(ParamLoweringMode::Variable);
let parent = def.loc(db).container;
let mut ctx = TyLoweringContext::new(
db,
&resolver,
&data.store,
def.into(),
LifetimeElisionKind::for_const(parent),
)
.with_type_param_mode(ParamLoweringMode::Variable);
make_binders(db, &generics, ctx.lower_ty(data.type_ref))
}
@ -1326,18 +1437,20 @@ fn type_for_const(db: &dyn HirDatabase, def: ConstId) -> Binders<Ty> {
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 type_for_struct_constructor(db: &dyn HirDatabase, def: StructId) -> Option<Bi
}
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 @@ pub(crate) fn type_for_type_alias_with_diagnostics_query(
} else {
let resolver = t.resolver(db);
let alias = db.type_alias_signature(t);
let mut ctx = TyLoweringContext::new(db, &resolver, &alias.store, t.into())
.with_impl_trait_mode(ImplTraitLoweringMode::Opaque)
.with_type_param_mode(ParamLoweringMode::Variable);
let mut ctx = TyLoweringContext::new(
db,
&resolver,
&alias.store,
t.into(),
LifetimeElisionKind::AnonymousReportError,
)
.with_impl_trait_mode(ImplTraitLoweringMode::Opaque)
.with_type_param_mode(ParamLoweringMode::Variable);
let res = alias
.ty
.map(|type_ref| ctx.lower_ty(type_ref))
@ -1517,8 +1632,14 @@ pub(crate) fn impl_self_ty_with_diagnostics_query(
let impl_data = db.impl_signature(impl_id);
let resolver = impl_id.resolver(db);
let generics = generics(db, impl_id.into());
let mut ctx = TyLoweringContext::new(db, &resolver, &impl_data.store, impl_id.into())
.with_type_param_mode(ParamLoweringMode::Variable);
let mut ctx = TyLoweringContext::new(
db,
&resolver,
&impl_data.store,
impl_id.into(),
LifetimeElisionKind::AnonymousCreateParameter { report_in_path: true },
)
.with_type_param_mode(ParamLoweringMode::Variable);
(
make_binders(db, &generics, ctx.lower_ty(impl_data.self_ty)),
create_diagnostics(ctx.diagnostics),
@ -1537,7 +1658,13 @@ pub(crate) fn const_param_ty_with_diagnostics_query(
let (parent_data, store) = db.generic_params_and_store(def.parent());
let data = &parent_data[def.local_id()];
let resolver = def.parent().resolver(db);
let mut ctx = TyLoweringContext::new(db, &resolver, &store, def.parent());
let mut ctx = TyLoweringContext::new(
db,
&resolver,
&store,
def.parent(),
LifetimeElisionKind::AnonymousReportError,
);
let ty = match data {
TypeOrConstParamData::TypeParamData(_) => {
never!();
@ -1566,8 +1693,14 @@ pub(crate) fn impl_trait_with_diagnostics_query(
) -> Option<(Binders<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 @@ pub(crate) fn return_type_impl_traits(
// FIXME unify with fn_sig_for_fn instead of doing lowering twice, maybe
let data = db.function_signature(def);
let resolver = def.resolver(db);
let mut ctx_ret = TyLoweringContext::new(db, &resolver, &data.store, def.into())
.with_impl_trait_mode(ImplTraitLoweringMode::Opaque)
.with_type_param_mode(ParamLoweringMode::Variable);
let mut ctx_ret =
TyLoweringContext::new(db, &resolver, &data.store, def.into(), LifetimeElisionKind::Infer)
.with_impl_trait_mode(ImplTraitLoweringMode::Opaque)
.with_type_param_mode(ParamLoweringMode::Variable);
if let Some(ret_type) = data.ret_type {
let _ret = ctx_ret.lower_ty(ret_type);
}
@ -1603,9 +1737,15 @@ pub(crate) fn type_alias_impl_traits(
) -> Option<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);
}

View File

@ -63,6 +63,24 @@ pub enum PathLoweringDiagnostic {
/// Whether the `GenericArgs` contains a `Self` arg.
has_self_arg: bool,
},
ElidedLifetimesInPath {
generics_source: PathGenericsSource,
def: GenericDefId,
expected_count: u32,
hard_error: bool,
},
/// An elided lifetimes was used (either implicitly, by not specifying lifetimes, or explicitly, by using `'_`),
/// but lifetime elision could not find a lifetime to replace it with.
ElisionFailure {
generics_source: PathGenericsSource,
def: GenericDefId,
expected_count: u32,
},
MissingLifetime {
generics_source: PathGenericsSource,
def: GenericDefId,
expected_count: u32,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]

View File

@ -27,8 +27,8 @@ use crate::{
db::HirDatabase,
error_lifetime,
generics::{Generics, generics},
lower::{GenericArgsPosition, named_associated_type_shorthand_candidates},
to_assoc_type_id, to_chalk_trait_id, to_placeholder_idx,
lower::{LifetimeElisionKind, named_associated_type_shorthand_candidates},
static_lifetime, to_assoc_type_id, to_chalk_trait_id, to_placeholder_idx,
utils::associated_type_by_name_including_super_traits,
};
@ -52,7 +52,6 @@ pub(crate) struct PathLoweringContext<'a, 'b> {
current_segment_idx: usize,
/// Contains the previous segment if `current_segment_idx == segments.len()`
current_or_prev_segment: PathSegment<'a>,
position: GenericArgsPosition,
}
impl<'a, 'b> PathLoweringContext<'a, 'b> {
@ -61,7 +60,6 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> {
ctx: &'a mut TyLoweringContext<'b>,
on_diagnostic: PathDiagnosticCallback<'a>,
path: &'a Path,
position: GenericArgsPosition,
) -> Self {
let segments = path.segments();
let first_segment = segments.first().unwrap_or(PathSegment::MISSING);
@ -72,7 +70,6 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> {
segments,
current_segment_idx: 0,
current_or_prev_segment: first_segment,
position,
}
}
@ -122,6 +119,19 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> {
.expect("invalid segment passed to PathLoweringContext::set_current_segment()");
}
#[inline]
fn with_lifetime_elision<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 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> {
}
}
fn prohibit_parenthesized_generic_args(&mut self) -> bool {
if let Some(generic_args) = self.current_or_prev_segment.args_and_bindings {
match generic_args.parenthesized {
GenericArgsParentheses::No => {}
GenericArgsParentheses::ReturnTypeNotation | GenericArgsParentheses::ParenSugar => {
let segment = self.current_segment_u32();
self.on_diagnostic(
PathLoweringDiagnostic::ParenthesizedGenericArgsWithoutFnTrait { segment },
);
return true;
}
}
}
false
}
// When calling this, the current segment is the resolved segment (we don't advance it yet).
pub(crate) fn lower_partly_resolved_path(
&mut self,
@ -189,6 +183,7 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> {
associated_ty.into(),
false,
None,
true,
);
let substitution = Substitution::from_iter(
Interner,
@ -511,7 +506,7 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> {
// generic params. It's inefficient to splice the `Substitution`s, so we may want
// that method to optionally take parent `Substitution` as we already know them at
// this point (`t.substitution`).
let substs = self.substs_from_path_segment(associated_ty.into(), false, None);
let substs = self.substs_from_path_segment(associated_ty.into(), false, None, true);
let substs = Substitution::from_iter(
Interner,
@ -539,7 +534,7 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> {
TyDefId::AdtId(it) => it.into(),
TyDefId::TypeAliasId(it) => it.into(),
};
let substs = self.substs_from_path_segment(generic_def, infer_args, None);
let substs = self.substs_from_path_segment(generic_def, infer_args, None, false);
self.ctx.db.ty(typeable).substitute(Interner, &substs)
}
@ -552,6 +547,7 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> {
// special-case enum variants
resolved: ValueTyDefId,
infer_args: bool,
lowering_assoc_type_generics: bool,
) -> Substitution {
let prev_current_segment_idx = self.current_segment_idx;
let prev_current_segment = self.current_or_prev_segment;
@ -588,7 +584,12 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> {
var.lookup(self.ctx.db).parent.into()
}
};
let result = self.substs_from_path_segment(generic_def, infer_args, None);
let result = self.substs_from_path_segment(
generic_def,
infer_args,
None,
lowering_assoc_type_generics,
);
self.current_segment_idx = prev_current_segment_idx;
self.current_or_prev_segment = prev_current_segment;
result
@ -599,26 +600,41 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> {
def: GenericDefId,
infer_args: bool,
explicit_self_ty: Option<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 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> {
infer_args,
explicit_self_ty,
PathGenericsSource::Segment(self.current_segment_u32()),
lowering_assoc_type_generics,
lifetime_elision,
)
}
@ -637,6 +655,8 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> {
infer_args: bool,
explicit_self_ty: Option<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 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> {
GenericParamId::LifetimeParamId(_) => error_lifetime().cast(Interner),
}
}
fn report_elided_lifetimes_in_path(
&mut self,
def: GenericDefId,
expected_count: u32,
hard_error: bool,
) {
self.ctx.on_diagnostic(PathLoweringDiagnostic::ElidedLifetimesInPath {
generics_source: self.generics_source,
def,
expected_count,
hard_error,
});
}
fn report_elision_failure(&mut self, def: GenericDefId, expected_count: u32) {
self.ctx.on_diagnostic(PathLoweringDiagnostic::ElisionFailure {
generics_source: self.generics_source,
def,
expected_count,
});
}
fn report_missing_lifetime(&mut self, def: GenericDefId, expected_count: u32) {
self.ctx.on_diagnostic(PathLoweringDiagnostic::MissingLifetime {
generics_source: self.generics_source,
def,
expected_count,
});
}
}
substs_from_args_and_bindings(
@ -769,7 +819,8 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> {
args_and_bindings,
def,
infer_args,
self.position,
lifetime_elision,
lowering_assoc_type_generics,
explicit_self_ty,
&mut LowererCtx { ctx: self, generics_source },
)
@ -789,7 +840,7 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> {
resolved: TraitId,
explicit_self_ty: Ty,
) -> Substitution {
self.substs_from_path_segment(resolved.into(), false, Some(explicit_self_ty))
self.substs_from_path_segment(resolved.into(), false, Some(explicit_self_ty), false)
}
pub(super) fn assoc_type_bindings_from_type_bound<'c>(
@ -807,20 +858,25 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> {
None => return SmallVec::new(),
Some(t) => t,
};
// FIXME: `substs_from_path_segment()` pushes `TyKind::Error` for every parent
// generic params. It's inefficient to splice the `Substitution`s, so we may want
// that method to optionally take parent `Substitution` as we already know them at
// this point (`super_trait_ref.substitution`).
let substitution = self.substs_from_args_and_bindings(
binding.args.as_ref(),
associated_ty.into(),
false, // this is not relevant
Some(super_trait_ref.self_type_parameter(Interner)),
PathGenericsSource::AssocType {
segment: self.current_segment_u32(),
assoc_type: binding_idx as u32,
},
);
let substitution =
self.with_lifetime_elision(LifetimeElisionKind::AnonymousReportError, |this| {
// FIXME: `substs_from_path_segment()` pushes `TyKind::Error` for every parent
// generic params. It's inefficient to splice the `Substitution`s, so we may want
// that method to optionally take parent `Substitution` as we already know them at
// this point (`super_trait_ref.substitution`).
this.substs_from_args_and_bindings(
binding.args.as_ref(),
associated_ty.into(),
false, // this is not relevant
Some(super_trait_ref.self_type_parameter(Interner)),
PathGenericsSource::AssocType {
segment: this.current_segment_u32(),
assoc_type: binding_idx as u32,
},
false,
this.ctx.lifetime_elision.clone(),
)
});
let substitution = Substitution::from_iter(
Interner,
super_trait_ref.substitution.iter(Interner).chain(
@ -836,25 +892,48 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> {
let mut predicates: SmallVec<[_; 1]> = SmallVec::with_capacity(
binding.type_ref.as_ref().map_or(0, |_| 1) + binding.bounds.len(),
);
if let Some(type_ref) = binding.type_ref {
match (&self.ctx.store[type_ref], self.ctx.impl_trait_mode.mode) {
(TypeRef::ImplTrait(_), ImplTraitLoweringMode::Disallowed) => (),
(_, ImplTraitLoweringMode::Disallowed | ImplTraitLoweringMode::Opaque) => {
let ty = self.ctx.lower_ty(type_ref);
let alias_eq =
AliasEq { alias: AliasTy::Projection(projection_ty.clone()), ty };
predicates
.push(crate::wrap_empty_binders(WhereClause::AliasEq(alias_eq)));
let lifetime_elision =
if args_and_bindings.parenthesized == GenericArgsParentheses::ParenSugar {
// `Fn()`-style generics are elided like functions. This is `Output` (we lower to it in hir-def).
LifetimeElisionKind::for_fn_ret()
} else {
self.ctx.lifetime_elision.clone()
};
self.with_lifetime_elision(lifetime_elision, |this| {
match (&this.ctx.store[type_ref], this.ctx.impl_trait_mode.mode) {
(TypeRef::ImplTrait(_), ImplTraitLoweringMode::Disallowed) => (),
(
_,
ImplTraitLoweringMode::Disallowed | ImplTraitLoweringMode::Opaque,
) => {
let ty = this.ctx.lower_ty(type_ref);
let alias_eq = AliasEq {
alias: AliasTy::Projection(projection_ty.clone()),
ty,
};
predicates.push(crate::wrap_empty_binders(WhereClause::AliasEq(
alias_eq,
)));
}
}
});
}
self.with_lifetime_elision(LifetimeElisionKind::AnonymousReportError, |this| {
for bound in binding.bounds.iter() {
predicates.extend(
this.ctx.lower_type_bound(
bound,
TyKind::Alias(AliasTy::Projection(projection_ty.clone()))
.intern(Interner),
false,
),
);
}
}
for bound in binding.bounds.iter() {
predicates.extend(self.ctx.lower_type_bound(
bound,
TyKind::Alias(AliasTy::Projection(projection_ty.clone())).intern(Interner),
false,
));
}
});
predicates
})
})
@ -868,6 +947,17 @@ pub(crate) enum TypeLikeConst<'a> {
}
pub(crate) trait GenericArgsLowerer {
fn report_elided_lifetimes_in_path(
&mut self,
def: GenericDefId,
expected_count: u32,
hard_error: bool,
);
fn report_elision_failure(&mut self, def: GenericDefId, expected_count: u32);
fn report_missing_lifetime(&mut self, def: GenericDefId, expected_count: u32);
fn report_len_mismatch(
&mut self,
def: GenericDefId,
@ -905,7 +995,8 @@ fn check_generic_args_len(
def: GenericDefId,
def_generics: &Generics,
infer_args: bool,
position: GenericArgsPosition,
lifetime_elision: &LifetimeElisionKind,
lowering_assoc_type_generics: bool,
ctx: &mut impl GenericArgsLowerer,
) -> bool {
let mut had_error = false;
@ -921,19 +1012,37 @@ fn check_generic_args_len(
}
}
// FIXME: Function signature lifetime elision has to be considered here once we have it
let infer_lifetimes =
position != GenericArgsPosition::OtherSignature && provided_lifetimes_count == 0;
let max_expected_lifetime_args = def_generics.len_lifetimes_self();
let min_expected_lifetime_args = if infer_lifetimes { 0 } else { max_expected_lifetime_args };
if provided_lifetimes_count < min_expected_lifetime_args
|| max_expected_lifetime_args < provided_lifetimes_count
{
let lifetime_args_len = def_generics.len_lifetimes_self();
if provided_lifetimes_count == 0 && lifetime_args_len > 0 && !lowering_assoc_type_generics {
// In generic associated types, we never allow inferring the lifetimes.
match lifetime_elision {
&LifetimeElisionKind::AnonymousCreateParameter { report_in_path } => {
ctx.report_elided_lifetimes_in_path(def, lifetime_args_len as u32, report_in_path);
had_error |= report_in_path;
}
LifetimeElisionKind::AnonymousReportError => {
ctx.report_missing_lifetime(def, lifetime_args_len as u32);
had_error = true
}
LifetimeElisionKind::ElisionFailure => {
ctx.report_elision_failure(def, lifetime_args_len as u32);
had_error = true;
}
LifetimeElisionKind::StaticIfNoLifetimeInScope { only_lint: _ } => {
// FIXME: Check there are other lifetimes in scope, and error/lint.
}
LifetimeElisionKind::Elided(_) => {
ctx.report_elided_lifetimes_in_path(def, lifetime_args_len as u32, false);
}
LifetimeElisionKind::Infer => {
// Allow eliding lifetimes.
}
}
} else if lifetime_args_len != provided_lifetimes_count {
ctx.report_len_mismatch(
def,
provided_lifetimes_count as u32,
max_expected_lifetime_args as u32,
lifetime_args_len as u32,
IncorrectGenericsLenKind::Lifetimes,
);
had_error = true;
@ -974,7 +1083,8 @@ pub(crate) fn substs_from_args_and_bindings(
args_and_bindings: Option<&GenericArgs>,
def: GenericDefId,
mut infer_args: bool,
position: GenericArgsPosition,
lifetime_elision: LifetimeElisionKind,
lowering_assoc_type_generics: bool,
explicit_self_ty: Option<Ty>,
ctx: &mut impl GenericArgsLowerer,
) -> Substitution {
@ -991,8 +1101,15 @@ pub(crate) fn substs_from_args_and_bindings(
args_slice.iter().any(|arg| !matches!(arg, GenericArg::Lifetime(_)));
infer_args &= !has_non_lifetime_args;
let had_count_error =
check_generic_args_len(args_and_bindings, def, &def_generics, infer_args, position, ctx);
let had_count_error = check_generic_args_len(
args_and_bindings,
def,
&def_generics,
infer_args,
&lifetime_elision,
lowering_assoc_type_generics,
ctx,
);
let mut substs = Vec::with_capacity(def_generics.len());
@ -1120,7 +1237,29 @@ pub(crate) fn substs_from_args_and_bindings(
(None, Some(&(param_id, param))) => {
// If there are fewer arguments than parameters, it means we're inferring the remaining arguments.
substs.push(ctx.inferred_kind(def, param_id, param, infer_args, &substs));
let param = if let GenericParamId::LifetimeParamId(_) = param_id {
match &lifetime_elision {
LifetimeElisionKind::ElisionFailure
| LifetimeElisionKind::AnonymousCreateParameter { report_in_path: true }
| LifetimeElisionKind::AnonymousReportError => {
assert!(had_count_error);
ctx.inferred_kind(def, param_id, param, infer_args, &substs)
}
LifetimeElisionKind::StaticIfNoLifetimeInScope { only_lint: _ } => {
static_lifetime().cast(Interner)
}
LifetimeElisionKind::Elided(lifetime) => lifetime.clone().cast(Interner),
LifetimeElisionKind::AnonymousCreateParameter { report_in_path: false }
| LifetimeElisionKind::Infer => {
// FIXME: With `AnonymousCreateParameter`, we need to create a new lifetime parameter here
// (but this will probably be done in hir-def lowering instead).
ctx.inferred_kind(def, param_id, param, infer_args, &substs)
}
}
} else {
ctx.inferred_kind(def, param_id, param, infer_args, &substs)
};
substs.push(param);
params.next();
}

View File

@ -118,6 +118,8 @@ diagnostics![
BadRtn,
IncorrectGenericsLen,
IncorrectGenericsOrder,
MissingLifetime,
ElidedLifetimesInPath,
];
#[derive(Debug)]
@ -440,6 +442,23 @@ pub struct IncorrectGenericsLen {
pub def: GenericDef,
}
#[derive(Debug)]
pub struct MissingLifetime {
/// Points at the name if there are no generics.
pub generics_or_segment: InFile<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 @@ impl AnyDiagnostic {
let expected_kind = GenericArgKind::from_id(param_id);
IncorrectGenericsOrder { provided_arg, expected_kind }.into()
}
PathLoweringDiagnostic::MissingLifetime { generics_source, expected_count, def }
| PathLoweringDiagnostic::ElisionFailure { generics_source, expected_count, def } => {
let generics_or_segment =
path_generics_source_to_ast(&path.value, generics_source)?;
let generics_or_segment = path.with_value(AstPtr::new(&generics_or_segment));
MissingLifetime { generics_or_segment, expected: expected_count, def: def.into() }
.into()
}
PathLoweringDiagnostic::ElidedLifetimesInPath {
generics_source,
expected_count,
def,
hard_error,
} => {
let generics_or_segment =
path_generics_source_to_ast(&path.value, generics_source)?;
let generics_or_segment = path.with_value(AstPtr::new(&generics_or_segment));
ElidedLifetimesInPath {
generics_or_segment,
expected: expected_count,
def: def.into(),
hard_error,
}
.into()
}
})
}

View File

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

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