Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-ty/src/lower/path.rs')
| -rw-r--r-- | crates/hir-ty/src/lower/path.rs | 1016 |
1 files changed, 696 insertions, 320 deletions
diff --git a/crates/hir-ty/src/lower/path.rs b/crates/hir-ty/src/lower/path.rs index a165932ddc..726eaf8b0a 100644 --- a/crates/hir-ty/src/lower/path.rs +++ b/crates/hir-ty/src/lower/path.rs @@ -1,33 +1,35 @@ //! A wrapper around [`TyLoweringContext`] specifically for lowering paths. -use std::iter; - -use chalk_ir::{cast::Cast, fold::Shift, BoundVar}; +use chalk_ir::{BoundVar, cast::Cast, fold::Shift}; use either::Either; use hir_def::{ - data::TraitFlags, - expr_store::HygieneId, - generics::{TypeParamProvenance, WherePredicate, WherePredicateTypeTarget}, - path::{GenericArg, GenericArgs, Path, PathSegment, PathSegments}, + GenericDefId, GenericParamId, Lookup, TraitId, + expr_store::{ + ExpressionStore, HygieneId, + path::{GenericArg, GenericArgs, GenericArgsParentheses, Path, PathSegment, PathSegments}, + }, + hir::generics::{ + GenericParamDataRef, TypeOrConstParamData, TypeParamData, TypeParamProvenance, + }, resolver::{ResolveValueResult, TypeNs, ValueNs}, - type_ref::{TypeBound, TypeRef, TypesMap}, - GenericDefId, GenericParamId, ItemContainerId, Lookup, TraitId, + signatures::TraitFlags, + type_ref::{TypeRef, TypeRefId}, }; use smallvec::SmallVec; use stdx::never; use crate::{ - consteval::unknown_const_as_generic, + AliasEq, AliasTy, GenericArgsProhibitedReason, ImplTraitLoweringMode, IncorrectGenericsLenKind, + Interner, ParamLoweringMode, PathGenericsSource, PathLoweringDiagnostic, ProjectionTy, + QuantifiedWhereClause, Substitution, TraitRef, Ty, TyBuilder, TyDefId, TyKind, + TyLoweringContext, ValueTyDefId, WhereClause, + consteval::{unknown_const, unknown_const_as_generic}, + db::HirDatabase, error_lifetime, - generics::generics, - lower::{ - generic_arg_to_chalk, named_associated_type_shorthand_candidates, ImplTraitLoweringState, - }, - to_assoc_type_id, to_chalk_trait_id, to_placeholder_idx, + generics::{Generics, generics}, + 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, - AliasEq, AliasTy, GenericArgsProhibitedReason, ImplTraitLoweringMode, Interner, - ParamLoweringMode, PathLoweringDiagnostic, ProjectionTy, QuantifiedWhereClause, Substitution, - TraitRef, Ty, TyBuilder, TyDefId, TyKind, TyLoweringContext, ValueTyDefId, WhereClause, }; type CallbackData<'a> = Either< @@ -117,17 +119,31 @@ 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, // We need the original resolution to lower `Self::AssocTy` correctly res: Option<TypeNs>, + infer_args: bool, ) -> (Ty, Option<TypeNs>) { match self.segments.len() - self.current_segment_idx { 0 => (ty, res), 1 => { // resolve unselected assoc types - (self.select_associated_type(res), None) + (self.select_associated_type(res, infer_args), None) } _ => { // FIXME report error (ambiguous associated type) @@ -136,19 +152,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 { - if generic_args.desugared_from_fn { - 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, @@ -164,12 +167,13 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { let trait_ref = self.lower_trait_ref_from_resolved_path( trait_, TyKind::Error.intern(Interner), + infer_args, ); self.skip_resolved_segment(); let segment = self.current_or_prev_segment; let found = - self.ctx.db.trait_data(trait_).associated_type_by_name(segment.name); + self.ctx.db.trait_items(trait_).associated_type_by_name(segment.name); match found { Some(associated_ty) => { @@ -179,17 +183,17 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { // this point (`trait_ref.substitution`). let substitution = self.substs_from_path_segment( associated_ty.into(), - false, + infer_args, None, + true, ); - let len_self = - generics(self.ctx.db.upcast(), associated_ty.into()).len_self(); let substitution = Substitution::from_iter( Interner, - substitution - .iter(Interner) - .take(len_self) - .chain(trait_ref.substitution.iter(Interner)), + trait_ref.substitution.iter(Interner).chain( + substitution + .iter(Interner) + .skip(trait_ref.substitution.len(Interner)), + ), ); TyKind::Alias(AliasTy::Projection(ProjectionTy { associated_ty_id: to_assoc_type_id(associated_ty), @@ -225,12 +229,7 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { TyKind::Placeholder(to_placeholder_idx(self.ctx.db, param_id.into())) } ParamLoweringMode::Variable => { - let idx = match self - .ctx - .generics() - .expect("generics in scope") - .type_or_const_param_idx(param_id.into()) - { + let idx = match self.ctx.generics().type_or_const_param_idx(param_id.into()) { None => { never!("no matching generics"); return (TyKind::Error.intern(Interner), None); @@ -243,7 +242,7 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { } .intern(Interner), TypeNs::SelfType(impl_id) => { - let generics = self.ctx.generics().expect("impl should have generic param scope"); + let generics = self.ctx.generics(); match self.ctx.type_param_mode { ParamLoweringMode::Placeholder => { @@ -253,22 +252,13 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { let subst = generics.placeholder_subst(self.ctx.db); self.ctx.db.impl_self_ty(impl_id).substitute(Interner, &subst) } - ParamLoweringMode::Variable => { - let starting_from = match generics.def() { - GenericDefId::ImplId(_) => 0, - // `def` is an item within impl. We need to substitute `BoundVar`s but - // remember that they are for parent (i.e. impl) generic params so they - // come after our own params. - _ => generics.len_self(), - }; - TyBuilder::impl_self_ty(self.ctx.db, impl_id) - .fill_with_bound_vars(self.ctx.in_binders, starting_from) - .build() - } + ParamLoweringMode::Variable => TyBuilder::impl_self_ty(self.ctx.db, impl_id) + .fill_with_bound_vars(self.ctx.in_binders, 0) + .build(), } } TypeNs::AdtSelfType(adt) => { - let generics = generics(self.ctx.db.upcast(), adt.into()); + let generics = generics(self.ctx.db, adt.into()); let substs = match self.ctx.type_param_mode { ParamLoweringMode::Placeholder => generics.placeholder_subst(self.ctx.db), ParamLoweringMode::Variable => { @@ -282,11 +272,13 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { TypeNs::BuiltinType(it) => self.lower_path_inner(it.into(), infer_args), TypeNs::TypeAliasId(it) => self.lower_path_inner(it.into(), infer_args), // FIXME: report error - TypeNs::EnumVariantId(_) => return (TyKind::Error.intern(Interner), None), + TypeNs::EnumVariantId(_) | TypeNs::ModuleId(_) => { + return (TyKind::Error.intern(Interner), None); + } }; self.skip_resolved_segment(); - self.lower_ty_relative_path(ty, Some(resolution)) + self.lower_ty_relative_path(ty, Some(resolution), infer_args) } fn handle_type_ns_resolution(&mut self, resolution: &TypeNs) { @@ -313,6 +305,9 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { TypeNs::BuiltinType(_) => { prohibit_generics_on_resolved(GenericArgsProhibitedReason::PrimitiveTy) } + TypeNs::ModuleId(_) => { + prohibit_generics_on_resolved(GenericArgsProhibitedReason::Module) + } TypeNs::AdtId(_) | TypeNs::EnumVariantId(_) | TypeNs::TypeAliasId(_) @@ -330,10 +325,8 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { } pub(crate) fn resolve_path_in_type_ns(&mut self) -> Option<(TypeNs, Option<usize>)> { - let (resolution, remaining_index, _, prefix_info) = self - .ctx - .resolver - .resolve_path_in_type_ns_with_prefix_info(self.ctx.db.upcast(), self.path)?; + let (resolution, remaining_index, _, prefix_info) = + self.ctx.resolver.resolve_path_in_type_ns_with_prefix_info(self.ctx.db, self.path)?; let segments = self.segments; if segments.is_empty() || matches!(self.path, Path::LangItem(..)) { @@ -388,7 +381,7 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { hygiene_id: HygieneId, ) -> Option<ResolveValueResult> { let (res, prefix_info) = self.ctx.resolver.resolve_path_in_value_ns_with_prefix_info( - self.ctx.db.upcast(), + self.ctx.db, self.path, hygiene_id, )?; @@ -459,14 +452,19 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { // and statics can be generic, or just because it was easier for rustc implementors. // That means we'll show the wrong error code. Because of us it's easier to do it // this way :) - ValueNs::GenericParam(_) | ValueNs::ConstId(_) => { + ValueNs::GenericParam(_) => { prohibit_generics_on_resolved(GenericArgsProhibitedReason::Const) } ValueNs::StaticId(_) => { prohibit_generics_on_resolved(GenericArgsProhibitedReason::Static) } - ValueNs::FunctionId(_) | ValueNs::StructId(_) | ValueNs::EnumVariantId(_) => {} - ValueNs::LocalBinding(_) => {} + ValueNs::LocalBinding(_) => { + prohibit_generics_on_resolved(GenericArgsProhibitedReason::LocalVariable) + } + ValueNs::FunctionId(_) + | ValueNs::StructId(_) + | ValueNs::EnumVariantId(_) + | ValueNs::ConstId(_) => {} } } ResolveValueResult::Partial(resolution, _, _) => { @@ -476,22 +474,21 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { Some(res) } - fn select_associated_type(&mut self, res: Option<TypeNs>) -> Ty { - let Some((generics, res)) = self.ctx.generics().zip(res) else { + fn select_associated_type(&mut self, res: Option<TypeNs>, infer_args: bool) -> Ty { + let Some(res) = res else { return TyKind::Error.intern(Interner); }; let segment = self.current_or_prev_segment; let ty = named_associated_type_shorthand_candidates( self.ctx.db, - generics.def(), + self.ctx.def, res, Some(segment.name.clone()), move |name, t, associated_ty| { - let generics = self.ctx.generics().unwrap(); - if name != segment.name { return None; } + let generics = self.ctx.generics(); let parent_subst = t.substitution.clone(); let parent_subst = match self.ctx.type_param_mode { @@ -511,15 +508,14 @@ 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 len_self = - crate::generics::generics(self.ctx.db.upcast(), associated_ty.into()) - .len_self(); + let substs = + self.substs_from_path_segment(associated_ty.into(), infer_args, None, true); let substs = Substitution::from_iter( Interner, - substs.iter(Interner).take(len_self).chain(parent_subst.iter(Interner)), + parent_subst + .iter(Interner) + .chain(substs.iter(Interner).skip(parent_subst.len(Interner))), ); Some( @@ -541,7 +537,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) } @@ -554,6 +550,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; @@ -587,10 +584,15 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { self.current_or_prev_segment = penultimate; } } - var.lookup(self.ctx.db.upcast()).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_or_prev_segment = prev_current_segment; result @@ -601,16 +603,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_) => { - let trait_data = self.ctx.db.trait_data(trait_); - !trait_data.flags.contains(TraitFlags::RUSTC_PAREN_SUGAR) + 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( @@ -618,6 +645,9 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { def, infer_args, explicit_self_ty, + PathGenericsSource::Segment(self.current_segment_u32()), + lowering_assoc_type_generics, + lifetime_elision, ) } @@ -627,152 +657,185 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { def: GenericDefId, infer_args: bool, explicit_self_ty: Option<Ty>, + generics_source: PathGenericsSource, + lowering_assoc_type_generics: bool, + lifetime_elision: LifetimeElisionKind, ) -> Substitution { - // Order is - // - Optional Self parameter - // - Lifetime parameters - // - Type or Const parameters - // - Parent parameters - let def_generics = generics(self.ctx.db.upcast(), def); - let ( - parent_params, - self_param, - type_params, - const_params, - impl_trait_params, - lifetime_params, - ) = def_generics.provenance_split(); - let item_len = - self_param as usize + type_params + const_params + impl_trait_params + lifetime_params; - let total_len = parent_params + item_len; - - let mut substs = Vec::new(); - - // we need to iterate the lifetime and type/const params separately as our order of them - // differs from the supplied syntax - - let ty_error = || TyKind::Error.intern(Interner).cast(Interner); - let mut def_toc_iter = def_generics.iter_self_type_or_consts_id(); - let fill_self_param = || { - if self_param { - let self_ty = explicit_self_ty.map(|x| x.cast(Interner)).unwrap_or_else(ty_error); - - if let Some(id) = def_toc_iter.next() { - assert!(matches!(id, GenericParamId::TypeParamId(_))); - substs.push(self_ty); + struct LowererCtx<'a, 'b, 'c> { + ctx: &'a mut PathLoweringContext<'b, 'c>, + generics_source: PathGenericsSource, + } + + impl GenericArgsLowerer for LowererCtx<'_, '_, '_> { + fn report_len_mismatch( + &mut self, + def: GenericDefId, + provided_count: u32, + expected_count: u32, + kind: IncorrectGenericsLenKind, + ) { + self.ctx.on_diagnostic(PathLoweringDiagnostic::IncorrectGenericsLen { + generics_source: self.generics_source, + provided_count, + expected_count, + kind, + def, + }); + } + + fn report_arg_mismatch( + &mut self, + param_id: GenericParamId, + arg_idx: u32, + has_self_arg: bool, + ) { + self.ctx.on_diagnostic(PathLoweringDiagnostic::IncorrectGenericsOrder { + generics_source: self.generics_source, + param_id, + arg_idx, + has_self_arg, + }); + } + + fn provided_kind( + &mut self, + param_id: GenericParamId, + param: GenericParamDataRef<'_>, + arg: &GenericArg, + ) -> crate::GenericArg { + match (param, arg) { + (GenericParamDataRef::LifetimeParamData(_), GenericArg::Lifetime(lifetime)) => { + self.ctx.ctx.lower_lifetime(*lifetime).cast(Interner) + } + (GenericParamDataRef::TypeParamData(_), GenericArg::Type(type_ref)) => { + self.ctx.ctx.lower_ty(*type_ref).cast(Interner) + } + (GenericParamDataRef::ConstParamData(_), GenericArg::Const(konst)) => { + let GenericParamId::ConstParamId(const_id) = param_id else { + unreachable!("non-const param ID for const param"); + }; + self.ctx + .ctx + .lower_const(konst, self.ctx.ctx.db.const_param_ty(const_id)) + .cast(Interner) + } + _ => unreachable!("unmatching param kinds were passed to `provided_kind()`"), } } - }; - let mut had_explicit_args = false; - - if let Some(&GenericArgs { ref args, has_self_type, .. }) = args_and_bindings { - // Fill in the self param first - if has_self_type && self_param { - had_explicit_args = true; - if let Some(id) = def_toc_iter.next() { - assert!(matches!(id, GenericParamId::TypeParamId(_))); - had_explicit_args = true; - if let GenericArg::Type(ty) = &args[0] { - substs.push(self.ctx.lower_ty(*ty).cast(Interner)); + + fn provided_type_like_const( + &mut self, + const_ty: Ty, + arg: TypeLikeConst<'_>, + ) -> crate::Const { + match arg { + TypeLikeConst::Path(path) => self.ctx.ctx.lower_path_as_const(path, const_ty), + TypeLikeConst::Infer => unknown_const(const_ty), + } + } + + fn inferred_kind( + &mut self, + def: GenericDefId, + param_id: GenericParamId, + param: GenericParamDataRef<'_>, + infer_args: bool, + preceding_args: &[crate::GenericArg], + ) -> crate::GenericArg { + let default = || { + self.ctx + .ctx + .db + .generic_defaults(def) + .get(preceding_args.len()) + .map(|default| default.clone().substitute(Interner, preceding_args)) + }; + match param { + GenericParamDataRef::LifetimeParamData(_) => error_lifetime().cast(Interner), + GenericParamDataRef::TypeParamData(param) => { + if !infer_args && param.default.is_some() { + if let Some(default) = default() { + return default; + } + } + TyKind::Error.intern(Interner).cast(Interner) + } + GenericParamDataRef::ConstParamData(param) => { + if !infer_args && param.default.is_some() { + if let Some(default) = default() { + return default; + } + } + let GenericParamId::ConstParamId(const_id) = param_id else { + unreachable!("non-const param ID for const param"); + }; + unknown_const_as_generic(self.ctx.ctx.db.const_param_ty(const_id)) + .cast(Interner) } } - } else { - fill_self_param() - }; - - // Then fill in the supplied lifetime args, or error lifetimes if there are too few - // (default lifetimes aren't a thing) - for arg in args - .iter() - .filter_map(|arg| match arg { - GenericArg::Lifetime(arg) => Some(self.ctx.lower_lifetime(arg)), - _ => None, - }) - .chain(iter::repeat(error_lifetime())) - .take(lifetime_params) - { - substs.push(arg.cast(Interner)); - } - - let skip = if has_self_type { 1 } else { 0 }; - // Fill in supplied type and const args - // Note if non-lifetime args are provided, it should be all of them, but we can't rely on that - for (arg, id) in args - .iter() - .filter(|arg| !matches!(arg, GenericArg::Lifetime(_))) - .skip(skip) - .take(type_params + const_params) - .zip(def_toc_iter) - { - had_explicit_args = true; - let arg = generic_arg_to_chalk( - self.ctx.db, - id, - arg, - self.ctx, - self.ctx.types_map, - |ctx, type_ref| ctx.lower_ty(type_ref), - |ctx, const_ref, ty| ctx.lower_const(const_ref, ty), - |ctx, lifetime_ref| ctx.lower_lifetime(lifetime_ref), - ); - substs.push(arg); } - } else { - fill_self_param(); - } - let param_to_err = |id| match id { - GenericParamId::ConstParamId(x) => { - unknown_const_as_generic(self.ctx.db.const_param_ty(x)) + fn parent_arg(&mut self, param_id: GenericParamId) -> crate::GenericArg { + match param_id { + GenericParamId::TypeParamId(_) => TyKind::Error.intern(Interner).cast(Interner), + GenericParamId::ConstParamId(const_id) => { + unknown_const_as_generic(self.ctx.ctx.db.const_param_ty(const_id)) + } + GenericParamId::LifetimeParamId(_) => error_lifetime().cast(Interner), + } } - GenericParamId::TypeParamId(_) => ty_error(), - GenericParamId::LifetimeParamId(_) => error_lifetime().cast(Interner), - }; - // handle defaults. In expression or pattern path segments without - // explicitly specified type arguments, missing type arguments are inferred - // (i.e. defaults aren't used). - // Generic parameters for associated types are not supposed to have defaults, so we just - // ignore them. - let is_assoc_ty = || match def { - GenericDefId::TypeAliasId(id) => { - matches!(id.lookup(self.ctx.db.upcast()).container, ItemContainerId::TraitId(_)) - } - _ => false, - }; - let fill_defaults = (!infer_args || had_explicit_args) && !is_assoc_ty(); - if fill_defaults { - let defaults = &*self.ctx.db.generic_defaults(def); - let (item, _parent) = defaults.split_at(item_len); - let parent_from = item_len - substs.len(); - - let mut rem = - def_generics.iter_id().skip(substs.len()).map(param_to_err).collect::<Vec<_>>(); - // Fill in defaults for type/const params - for (idx, default_ty) in item[substs.len()..].iter().enumerate() { - // each default can depend on the previous parameters - let substs_so_far = Substitution::from_iter( - Interner, - substs.iter().cloned().chain(rem[idx..].iter().cloned()), - ); - substs.push(default_ty.clone().substitute(Interner, &substs_so_far)); + + 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, + }); } - // Fill in remaining parent params - substs.extend(rem.drain(parent_from..)); - } else { - // Fill in remaining def params and parent params - substs.extend(def_generics.iter_id().skip(substs.len()).map(param_to_err)); } - assert_eq!(substs.len(), total_len, "expected {} substs, got {}", total_len, substs.len()); - Substitution::from_iter(Interner, substs) + substs_from_args_and_bindings( + self.ctx.db, + self.ctx.store, + args_and_bindings, + def, + infer_args, + lifetime_elision, + lowering_assoc_type_generics, + explicit_self_ty, + &mut LowererCtx { ctx: self, generics_source }, + ) } pub(crate) fn lower_trait_ref_from_resolved_path( &mut self, resolved: TraitId, explicit_self_ty: Ty, + infer_args: bool, ) -> TraitRef { - let substs = self.trait_ref_substs_from_path(resolved, explicit_self_ty); + let substs = self.trait_ref_substs_from_path(resolved, explicit_self_ty, infer_args); TraitRef { trait_id: to_chalk_trait_id(resolved), substitution: substs } } @@ -780,17 +843,17 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { &mut self, resolved: TraitId, explicit_self_ty: Ty, + infer_args: bool, ) -> Substitution { - self.substs_from_path_segment(resolved.into(), false, Some(explicit_self_ty)) + self.substs_from_path_segment(resolved.into(), infer_args, Some(explicit_self_ty), false) } pub(super) fn assoc_type_bindings_from_type_bound<'c>( mut self, - bound: &'c TypeBound, trait_ref: TraitRef, ) -> Option<impl Iterator<Item = QuantifiedWhereClause> + use<'a, 'b, 'c>> { self.current_or_prev_segment.args_and_bindings.map(|args_and_bindings| { - args_and_bindings.bindings.iter().flat_map(move |binding| { + args_and_bindings.bindings.iter().enumerate().flat_map(move |(binding_idx, binding)| { let found = associated_type_by_name_including_super_traits( self.ctx.db, trait_ref.clone(), @@ -800,23 +863,32 @@ 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)), - ); - let self_params = generics(self.ctx.db.upcast(), associated_ty.into()).len_self(); + 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, - substitution - .iter(Interner) - .take(self_params) - .chain(super_trait_ref.substitution.iter(Interner)), + super_trait_ref.substitution.iter(Interner).chain( + substitution + .iter(Interner) + .skip(super_trait_ref.substitution.len(Interner)), + ), ); let projection_ty = ProjectionTy { associated_ty_id: to_assoc_type_id(associated_ty), @@ -825,93 +897,397 @@ 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.types_map[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))); - } - (_, ImplTraitLoweringMode::Param | ImplTraitLoweringMode::Variable) => { - // Find the generic index for the target of our `bound` - let target_param_idx = - self.ctx.resolver.where_predicates_in_scope().find_map( - |(p, (_, types_map))| match p { - WherePredicate::TypeBound { - target: WherePredicateTypeTarget::TypeOrConstParam(idx), - bound: b, - } if std::ptr::eq::<TypesMap>( - self.ctx.types_map, - types_map, - ) && bound == b => - { - Some(idx) - } - _ => None, - }, - ); - let ty = if let Some(target_param_idx) = target_param_idx { - let mut counter = 0; - let generics = self.ctx.generics().expect("generics in scope"); - for (idx, data) in generics.iter_self_type_or_consts() { - // Count the number of `impl Trait` things that appear before - // the target of our `bound`. - // Our counter within `impl_trait_mode` should be that number - // to properly lower each types within `type_ref` - if data.type_param().is_some_and(|p| { - p.provenance == TypeParamProvenance::ArgumentImplTrait - }) { - counter += 1; - } - if idx == *target_param_idx { - break; - } - } - let mut ext = TyLoweringContext::new_maybe_unowned( - self.ctx.db, - self.ctx.resolver, - self.ctx.types_map, - self.ctx.types_source_map, - self.ctx.owner, - ) - .with_type_param_mode(self.ctx.type_param_mode); - match self.ctx.impl_trait_mode.mode { - ImplTraitLoweringMode::Param => { - ext.impl_trait_mode = - ImplTraitLoweringState::param(counter); - } - ImplTraitLoweringMode::Variable => { - ext.impl_trait_mode = - ImplTraitLoweringState::variable(counter); - } - _ => unreachable!(), - } - let ty = ext.lower_ty(type_ref); - self.ctx.diagnostics.extend(ext.diagnostics); - ty - } else { - 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, + ))); + } } - } - } - for bound in binding.bounds.iter() { - predicates.extend(self.ctx.lower_type_bound( - bound, - TyKind::Alias(AliasTy::Projection(projection_ty.clone())).intern(Interner), - false, - )); + }); } + + 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, + ), + ); + } + }); + predicates }) }) } } + +/// A const that were parsed like a type. +pub(crate) enum TypeLikeConst<'a> { + Infer, + Path(&'a Path), +} + +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, + provided_count: u32, + expected_count: u32, + kind: IncorrectGenericsLenKind, + ); + + fn report_arg_mismatch(&mut self, param_id: GenericParamId, arg_idx: u32, has_self_arg: bool); + + fn provided_kind( + &mut self, + param_id: GenericParamId, + param: GenericParamDataRef<'_>, + arg: &GenericArg, + ) -> crate::GenericArg; + + fn provided_type_like_const(&mut self, const_ty: Ty, arg: TypeLikeConst<'_>) -> crate::Const; + + fn inferred_kind( + &mut self, + def: GenericDefId, + param_id: GenericParamId, + param: GenericParamDataRef<'_>, + infer_args: bool, + preceding_args: &[crate::GenericArg], + ) -> crate::GenericArg; + + fn parent_arg(&mut self, param_id: GenericParamId) -> crate::GenericArg; +} + +/// Returns true if there was an error. +fn check_generic_args_len( + args_and_bindings: Option<&GenericArgs>, + def: GenericDefId, + def_generics: &Generics, + infer_args: bool, + lifetime_elision: &LifetimeElisionKind, + lowering_assoc_type_generics: bool, + ctx: &mut impl GenericArgsLowerer, +) -> bool { + let mut had_error = false; + + let (mut provided_lifetimes_count, mut provided_types_and_consts_count) = (0usize, 0usize); + if let Some(args_and_bindings) = args_and_bindings { + let args_no_self = &args_and_bindings.args[usize::from(args_and_bindings.has_self_type)..]; + for arg in args_no_self { + match arg { + GenericArg::Lifetime(_) => provided_lifetimes_count += 1, + GenericArg::Type(_) | GenericArg::Const(_) => provided_types_and_consts_count += 1, + } + } + } + + 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, + lifetime_args_len as u32, + IncorrectGenericsLenKind::Lifetimes, + ); + had_error = true; + } + + let defaults_count = + def_generics.iter_self_type_or_consts().filter(|(_, param)| param.has_default()).count(); + let named_type_and_const_params_count = def_generics + .iter_self_type_or_consts() + .filter(|(_, param)| match param { + TypeOrConstParamData::TypeParamData(param) => { + param.provenance == TypeParamProvenance::TypeParamList + } + TypeOrConstParamData::ConstParamData(_) => true, + }) + .count(); + let expected_max = named_type_and_const_params_count; + let expected_min = + if infer_args { 0 } else { named_type_and_const_params_count - defaults_count }; + if provided_types_and_consts_count < expected_min + || expected_max < provided_types_and_consts_count + { + ctx.report_len_mismatch( + def, + provided_types_and_consts_count as u32, + named_type_and_const_params_count as u32, + IncorrectGenericsLenKind::TypesAndConsts, + ); + had_error = true; + } + + had_error +} + +pub(crate) fn substs_from_args_and_bindings( + db: &dyn HirDatabase, + store: &ExpressionStore, + args_and_bindings: Option<&GenericArgs>, + def: GenericDefId, + mut infer_args: bool, + lifetime_elision: LifetimeElisionKind, + lowering_assoc_type_generics: bool, + explicit_self_ty: Option<Ty>, + ctx: &mut impl GenericArgsLowerer, +) -> Substitution { + // Order is + // - Parent parameters + // - Optional Self parameter + // - Lifetime parameters + // - Type or Const parameters + let def_generics = generics(db, def); + let args_slice = args_and_bindings.map(|it| &*it.args).unwrap_or_default(); + + // We do not allow inference if there are specified args, i.e. we do not allow partial inference. + let has_non_lifetime_args = + 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, + &lifetime_elision, + lowering_assoc_type_generics, + ctx, + ); + + let mut substs = Vec::with_capacity(def_generics.len()); + + substs.extend(def_generics.iter_parent_id().map(|id| ctx.parent_arg(id))); + + let mut args = args_slice.iter().enumerate().peekable(); + let mut params = def_generics.iter_self().peekable(); + + // If we encounter a type or const when we expect a lifetime, we infer the lifetimes. + // If we later encounter a lifetime, we know that the arguments were provided in the + // wrong order. `force_infer_lt` records the type or const that forced lifetimes to be + // inferred, so we can use it for diagnostics later. + let mut force_infer_lt = None; + + let has_self_arg = args_and_bindings.is_some_and(|it| it.has_self_type); + // First, handle `Self` parameter. Consume it from the args if provided, otherwise from `explicit_self_ty`, + // and lastly infer it. + if let Some(&( + self_param_id, + self_param @ GenericParamDataRef::TypeParamData(TypeParamData { + provenance: TypeParamProvenance::TraitSelf, + .. + }), + )) = params.peek() + { + let self_ty = if has_self_arg { + let (_, self_ty) = args.next().expect("has_self_type=true, should have Self type"); + ctx.provided_kind(self_param_id, self_param, self_ty) + } else { + explicit_self_ty.map(|it| it.cast(Interner)).unwrap_or_else(|| { + ctx.inferred_kind(def, self_param_id, self_param, infer_args, &substs) + }) + }; + params.next(); + substs.push(self_ty); + } + + loop { + // We're going to iterate through the generic arguments that the user + // provided, matching them with the generic parameters we expect. + // Mismatches can occur as a result of elided lifetimes, or for malformed + // input. We try to handle both sensibly. + match (args.peek(), params.peek()) { + (Some(&(arg_idx, arg)), Some(&(param_id, param))) => match (arg, param) { + (GenericArg::Type(_), GenericParamDataRef::TypeParamData(type_param)) + if type_param.provenance == TypeParamProvenance::ArgumentImplTrait => + { + // Do not allow specifying `impl Trait` explicitly. We already err at that, but if we won't handle it here + // we will handle it as if it was specified, instead of inferring it. + substs.push(ctx.inferred_kind(def, param_id, param, infer_args, &substs)); + params.next(); + } + (GenericArg::Lifetime(_), GenericParamDataRef::LifetimeParamData(_)) + | (GenericArg::Type(_), GenericParamDataRef::TypeParamData(_)) + | (GenericArg::Const(_), GenericParamDataRef::ConstParamData(_)) => { + substs.push(ctx.provided_kind(param_id, param, arg)); + args.next(); + params.next(); + } + ( + GenericArg::Type(_) | GenericArg::Const(_), + GenericParamDataRef::LifetimeParamData(_), + ) => { + // We expected a lifetime argument, but got a type or const + // argument. That means we're inferring the lifetime. + substs.push(ctx.inferred_kind(def, param_id, param, infer_args, &substs)); + params.next(); + force_infer_lt = Some((arg_idx as u32, param_id)); + } + (GenericArg::Type(type_ref), GenericParamDataRef::ConstParamData(_)) => { + if let Some(konst) = type_looks_like_const(store, *type_ref) { + let GenericParamId::ConstParamId(param_id) = param_id else { + panic!("unmatching param kinds"); + }; + let const_ty = db.const_param_ty(param_id); + substs.push(ctx.provided_type_like_const(const_ty, konst).cast(Interner)); + args.next(); + params.next(); + } else { + // See the `_ => { ... }` branch. + if !had_count_error { + ctx.report_arg_mismatch(param_id, arg_idx as u32, has_self_arg); + } + while args.next().is_some() {} + } + } + _ => { + // We expected one kind of parameter, but the user provided + // another. This is an error. However, if we already know that + // the arguments don't match up with the parameters, we won't issue + // an additional error, as the user already knows what's wrong. + if !had_count_error { + ctx.report_arg_mismatch(param_id, arg_idx as u32, has_self_arg); + } + + // We've reported the error, but we want to make sure that this + // problem doesn't bubble down and create additional, irrelevant + // errors. In this case, we're simply going to ignore the argument + // and any following arguments. The rest of the parameters will be + // inferred. + while args.next().is_some() {} + } + }, + + (Some(&(_, arg)), None) => { + // We should never be able to reach this point with well-formed input. + // There are two situations in which we can encounter this issue. + // + // 1. The number of arguments is incorrect. In this case, an error + // will already have been emitted, and we can ignore it. + // 2. We've inferred some lifetimes, which have been provided later (i.e. + // after a type or const). We want to throw an error in this case. + if !had_count_error { + assert!( + matches!(arg, GenericArg::Lifetime(_)), + "the only possible situation here is incorrect lifetime order" + ); + let (provided_arg_idx, param_id) = + force_infer_lt.expect("lifetimes ought to have been inferred"); + ctx.report_arg_mismatch(param_id, provided_arg_idx, has_self_arg); + } + + break; + } + + (None, Some(&(param_id, param))) => { + // If there are fewer arguments than parameters, it means we're inferring the remaining arguments. + 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(); + } + + (None, None) => break, + } + } + + Substitution::from_iter(Interner, substs) +} + +fn type_looks_like_const( + store: &ExpressionStore, + type_ref: TypeRefId, +) -> Option<TypeLikeConst<'_>> { + // A path/`_` const will be parsed as a type, instead of a const, because when parsing/lowering + // in hir-def we don't yet know the expected argument kind. rustc does this a bit differently, + // when lowering to HIR it resolves the path, and if it doesn't resolve to the type namespace + // it is lowered as a const. Our behavior could deviate from rustc when the value is resolvable + // in both the type and value namespaces, but I believe we only allow more code. + let type_ref = &store[type_ref]; + match type_ref { + TypeRef::Path(path) => Some(TypeLikeConst::Path(path)), + TypeRef::Placeholder => Some(TypeLikeConst::Infer), + _ => None, + } +} |