| //! A wrapper around [`TyLoweringContext`] specifically for lowering paths. |
| |
| use std::ops::Deref; |
| |
| use either::Either; |
| use hir_def::{ |
| AssocItemId, GenericDefId, GenericParamId, Lookup, TraitId, TypeAliasId, |
| builtin_type::BuiltinType, |
| expr_store::{ |
| ExpressionStore, HygieneId, |
| path::{GenericArg, GenericArgs, GenericArgsParentheses, Path, PathSegment, PathSegments}, |
| }, |
| hir::generics::{ |
| GenericParamDataRef, TypeOrConstParamData, TypeParamData, TypeParamProvenance, |
| }, |
| resolver::{ResolveValueResult, TypeNs, ValueNs}, |
| signatures::TraitFlags, |
| type_ref::{TypeRef, TypeRefId}, |
| }; |
| use hir_expand::name::Name; |
| use intern::sym; |
| use rustc_hash::FxHashSet; |
| use rustc_type_ir::{ |
| AliasTerm, AliasTy, AliasTyKind, TypeVisitableExt, |
| inherent::{GenericArgs as _, IntoKind, Region as _, SliceLike, Ty as _}, |
| }; |
| use smallvec::{SmallVec, smallvec}; |
| use stdx::never; |
| |
| use crate::{ |
| GenericArgsProhibitedReason, IncorrectGenericsLenKind, PathGenericsSource, |
| PathLoweringDiagnostic, TyDefId, ValueTyDefId, |
| consteval_nextsolver::{unknown_const, unknown_const_as_generic}, |
| db::HirDatabase, |
| generics::{Generics, generics}, |
| lower::PathDiagnosticCallbackData, |
| lower_nextsolver::{ |
| LifetimeElisionKind, PredicateFilter, generic_predicates_filtered_by, |
| named_associated_type_shorthand_candidates, |
| }, |
| next_solver::{ |
| AdtDef, Binder, Clause, Const, DbInterner, ErrorGuaranteed, Predicate, ProjectionPredicate, |
| Region, SolverDefId, TraitRef, Ty, |
| mapping::{ChalkToNextSolver, convert_binder_to_early_binder}, |
| }, |
| primitive, |
| }; |
| |
| use super::{ |
| ImplTraitLoweringMode, TyLoweringContext, associated_type_by_name_including_super_traits, |
| const_param_ty_query, ty_query, |
| }; |
| |
| type CallbackData<'a> = |
| Either<PathDiagnosticCallbackData, crate::infer::diagnostics::PathDiagnosticCallbackData<'a>>; |
| |
| // We cannot use `&mut dyn FnMut()` because of lifetime issues, and we don't want to use `Box<dyn FnMut()>` |
| // because of the allocation, so we create a lifetime-less callback, tailored for our needs. |
| pub(crate) struct PathDiagnosticCallback<'a, 'db> { |
| pub(crate) data: CallbackData<'a>, |
| pub(crate) callback: |
| fn(&CallbackData<'_>, &mut TyLoweringContext<'db, '_>, PathLoweringDiagnostic), |
| } |
| |
| pub(crate) struct PathLoweringContext<'a, 'b, 'db> { |
| ctx: &'a mut TyLoweringContext<'db, 'b>, |
| on_diagnostic: PathDiagnosticCallback<'a, 'db>, |
| path: &'a Path, |
| segments: PathSegments<'a>, |
| current_segment_idx: usize, |
| /// Contains the previous segment if `current_segment_idx == segments.len()` |
| current_or_prev_segment: PathSegment<'a>, |
| } |
| |
| impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { |
| #[inline] |
| pub(crate) fn new( |
| ctx: &'a mut TyLoweringContext<'db, 'b>, |
| on_diagnostic: PathDiagnosticCallback<'a, 'db>, |
| path: &'a Path, |
| ) -> Self { |
| let segments = path.segments(); |
| let first_segment = segments.first().unwrap_or(PathSegment::MISSING); |
| Self { |
| ctx, |
| on_diagnostic, |
| path, |
| segments, |
| current_segment_idx: 0, |
| current_or_prev_segment: first_segment, |
| } |
| } |
| |
| #[inline] |
| #[cold] |
| fn on_diagnostic(&mut self, diag: PathLoweringDiagnostic) { |
| (self.on_diagnostic.callback)(&self.on_diagnostic.data, self.ctx, diag); |
| } |
| |
| #[inline] |
| pub(crate) fn ty_ctx(&mut self) -> &mut TyLoweringContext<'db, 'b> { |
| self.ctx |
| } |
| |
| #[inline] |
| fn current_segment_u32(&self) -> u32 { |
| self.current_segment_idx as u32 |
| } |
| |
| #[inline] |
| fn skip_resolved_segment(&mut self) { |
| if !matches!(self.path, Path::LangItem(..)) { |
| // In lang items, the resolved "segment" is not one of the segments. Perhaps we should've put it |
| // point at -1, but I don't feel this is clearer. |
| self.current_segment_idx += 1; |
| } |
| self.update_current_segment(); |
| } |
| |
| #[inline] |
| fn update_current_segment(&mut self) { |
| self.current_or_prev_segment = |
| self.segments.get(self.current_segment_idx).unwrap_or(self.current_or_prev_segment); |
| } |
| |
| #[inline] |
| pub(crate) fn ignore_last_segment(&mut self) { |
| self.segments = self.segments.strip_last(); |
| } |
| |
| #[inline] |
| pub(crate) fn set_current_segment(&mut self, segment: usize) { |
| self.current_segment_idx = segment; |
| self.current_or_prev_segment = self |
| .segments |
| .get(segment) |
| .expect("invalid segment passed to PathLoweringContext::set_current_segment()"); |
| } |
| |
| #[inline] |
| fn with_lifetime_elision<T>( |
| &mut self, |
| lifetime_elision: LifetimeElisionKind<'db>, |
| f: impl FnOnce(&mut PathLoweringContext<'_, '_, 'db>) -> 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<'db>, |
| // We need the original resolution to lower `Self::AssocTy` correctly |
| res: Option<TypeNs>, |
| ) -> (Ty<'db>, Option<TypeNs>) { |
| let remaining_segments = self.segments.len() - self.current_segment_idx; |
| match remaining_segments { |
| 0 => (ty, res), |
| 1 => { |
| // resolve unselected assoc types |
| (self.select_associated_type(res), None) |
| } |
| _ => { |
| // FIXME report error (ambiguous associated type) |
| (Ty::new_error(self.ctx.interner, ErrorGuaranteed), None) |
| } |
| } |
| } |
| |
| 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, |
| resolution: TypeNs, |
| infer_args: bool, |
| ) -> (Ty<'db>, Option<TypeNs>) { |
| let remaining_segments = self.segments.skip(self.current_segment_idx + 1); |
| tracing::debug!(?remaining_segments); |
| let rem_seg_len = remaining_segments.len(); |
| tracing::debug!(?rem_seg_len); |
| |
| let ty = match resolution { |
| TypeNs::TraitId(trait_) => { |
| let ty = match remaining_segments.len() { |
| 1 => { |
| let trait_ref = self.lower_trait_ref_from_resolved_path( |
| trait_, |
| Ty::new_error(self.ctx.interner, ErrorGuaranteed), |
| ); |
| tracing::debug!(?trait_ref); |
| self.skip_resolved_segment(); |
| let segment = self.current_or_prev_segment; |
| let trait_id = trait_ref.def_id.0; |
| let found = |
| trait_id.trait_items(self.ctx.db).associated_type_by_name(segment.name); |
| |
| tracing::debug!(?found); |
| match found { |
| Some(associated_ty) => { |
| // 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 (`trait_ref.substitution`). |
| let substitution = self.substs_from_path_segment( |
| associated_ty.into(), |
| false, |
| None, |
| true, |
| ); |
| let args = crate::next_solver::GenericArgs::new_from_iter( |
| self.ctx.interner, |
| trait_ref |
| .args |
| .iter() |
| .chain(substitution.iter().skip(trait_ref.args.len())), |
| ); |
| Ty::new_alias( |
| self.ctx.interner, |
| AliasTyKind::Projection, |
| AliasTy::new_from_args( |
| self.ctx.interner, |
| associated_ty.into(), |
| args, |
| ), |
| ) |
| } |
| None => { |
| // FIXME: report error (associated type not found) |
| Ty::new_error(self.ctx.interner, ErrorGuaranteed) |
| } |
| } |
| } |
| 0 => { |
| // Trait object type without dyn; this should be handled in upstream. See |
| // `lower_path()`. |
| stdx::never!("unexpected fully resolved trait path"); |
| Ty::new_error(self.ctx.interner, ErrorGuaranteed) |
| } |
| _ => { |
| // FIXME report error (ambiguous associated type) |
| Ty::new_error(self.ctx.interner, ErrorGuaranteed) |
| } |
| }; |
| return (ty, None); |
| } |
| TypeNs::GenericParam(param_id) => { |
| let generics = self.ctx.generics(); |
| let idx = generics.type_or_const_param_idx(param_id.into()); |
| match idx { |
| None => { |
| never!("no matching generics"); |
| Ty::new_error(self.ctx.interner, ErrorGuaranteed) |
| } |
| Some(idx) => { |
| let (pidx, param) = generics.iter().nth(idx).unwrap(); |
| assert_eq!(pidx, param_id.into()); |
| let p = match param { |
| GenericParamDataRef::TypeParamData(p) => p, |
| _ => unreachable!(), |
| }; |
| Ty::new_param( |
| self.ctx.interner, |
| param_id, |
| idx as u32, |
| p.name |
| .as_ref() |
| .map_or_else(|| sym::MISSING_NAME.clone(), |p| p.symbol().clone()), |
| ) |
| } |
| } |
| } |
| TypeNs::SelfType(impl_id) => self.ctx.db.impl_self_ty_ns(impl_id).skip_binder(), |
| TypeNs::AdtSelfType(adt) => { |
| let args = crate::next_solver::GenericArgs::identity_for_item( |
| self.ctx.interner, |
| adt.into(), |
| ); |
| Ty::new_adt(self.ctx.interner, AdtDef::new(adt, self.ctx.interner), args) |
| } |
| |
| TypeNs::AdtId(it) => self.lower_path_inner(it.into(), infer_args), |
| 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(_) | TypeNs::ModuleId(_) => { |
| return (Ty::new_error(self.ctx.interner, ErrorGuaranteed), None); |
| } |
| }; |
| |
| tracing::debug!(?ty); |
| |
| self.skip_resolved_segment(); |
| self.lower_ty_relative_path(ty, Some(resolution)) |
| } |
| |
| fn handle_type_ns_resolution(&mut self, resolution: &TypeNs) { |
| let mut prohibit_generics_on_resolved = |reason| { |
| if self.current_or_prev_segment.args_and_bindings.is_some() { |
| let segment = self.current_segment_u32(); |
| self.on_diagnostic(PathLoweringDiagnostic::GenericArgsProhibited { |
| segment, |
| reason, |
| }); |
| } |
| }; |
| |
| match resolution { |
| TypeNs::SelfType(_) => { |
| prohibit_generics_on_resolved(GenericArgsProhibitedReason::SelfTy) |
| } |
| TypeNs::GenericParam(_) => { |
| prohibit_generics_on_resolved(GenericArgsProhibitedReason::TyParam) |
| } |
| TypeNs::AdtSelfType(_) => { |
| prohibit_generics_on_resolved(GenericArgsProhibitedReason::SelfTy) |
| } |
| TypeNs::BuiltinType(_) => { |
| prohibit_generics_on_resolved(GenericArgsProhibitedReason::PrimitiveTy) |
| } |
| TypeNs::ModuleId(_) => { |
| prohibit_generics_on_resolved(GenericArgsProhibitedReason::Module) |
| } |
| TypeNs::AdtId(_) |
| | TypeNs::EnumVariantId(_) |
| | TypeNs::TypeAliasId(_) |
| | TypeNs::TraitId(_) => {} |
| } |
| } |
| |
| pub(crate) fn resolve_path_in_type_ns_fully(&mut self) -> Option<TypeNs> { |
| let (res, unresolved) = self.resolve_path_in_type_ns()?; |
| if unresolved.is_some() { |
| return None; |
| } |
| Some(res) |
| } |
| |
| #[tracing::instrument(skip(self), ret)] |
| 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, self.path)?; |
| |
| let segments = self.segments; |
| if segments.is_empty() || matches!(self.path, Path::LangItem(..)) { |
| // `segments.is_empty()` can occur with `self`. |
| return Some((resolution, remaining_index)); |
| } |
| |
| let (module_segments, resolved_segment_idx, enum_segment) = match remaining_index { |
| None if prefix_info.enum_variant => { |
| (segments.strip_last_two(), segments.len() - 1, Some(segments.len() - 2)) |
| } |
| None => (segments.strip_last(), segments.len() - 1, None), |
| Some(i) => (segments.take(i - 1), i - 1, None), |
| }; |
| |
| self.current_segment_idx = resolved_segment_idx; |
| self.current_or_prev_segment = |
| segments.get(resolved_segment_idx).expect("should have resolved segment"); |
| |
| if matches!(self.path, Path::BarePath(..)) { |
| // Bare paths cannot have generics, so skip them as an optimization. |
| return Some((resolution, remaining_index)); |
| } |
| |
| for (i, mod_segment) in module_segments.iter().enumerate() { |
| if mod_segment.args_and_bindings.is_some() { |
| self.on_diagnostic(PathLoweringDiagnostic::GenericArgsProhibited { |
| segment: i as u32, |
| reason: GenericArgsProhibitedReason::Module, |
| }); |
| } |
| } |
| |
| if let Some(enum_segment) = enum_segment |
| && segments.get(enum_segment).is_some_and(|it| it.args_and_bindings.is_some()) |
| && segments.get(enum_segment + 1).is_some_and(|it| it.args_and_bindings.is_some()) |
| { |
| self.on_diagnostic(PathLoweringDiagnostic::GenericArgsProhibited { |
| segment: (enum_segment + 1) as u32, |
| reason: GenericArgsProhibitedReason::EnumVariant, |
| }); |
| } |
| |
| self.handle_type_ns_resolution(&resolution); |
| |
| Some((resolution, remaining_index)) |
| } |
| |
| pub(crate) fn resolve_path_in_value_ns( |
| &mut self, |
| hygiene_id: HygieneId, |
| ) -> Option<ResolveValueResult> { |
| let (res, prefix_info) = self.ctx.resolver.resolve_path_in_value_ns_with_prefix_info( |
| self.ctx.db, |
| self.path, |
| hygiene_id, |
| )?; |
| |
| let segments = self.segments; |
| if segments.is_empty() || matches!(self.path, Path::LangItem(..)) { |
| // `segments.is_empty()` can occur with `self`. |
| return Some(res); |
| } |
| |
| let (mod_segments, enum_segment, resolved_segment_idx) = match res { |
| ResolveValueResult::Partial(_, unresolved_segment, _) => { |
| (segments.take(unresolved_segment - 1), None, unresolved_segment - 1) |
| } |
| ResolveValueResult::ValueNs(ValueNs::EnumVariantId(_), _) |
| if prefix_info.enum_variant => |
| { |
| (segments.strip_last_two(), segments.len().checked_sub(2), segments.len() - 1) |
| } |
| ResolveValueResult::ValueNs(..) => (segments.strip_last(), None, segments.len() - 1), |
| }; |
| |
| self.current_segment_idx = resolved_segment_idx; |
| self.current_or_prev_segment = |
| segments.get(resolved_segment_idx).expect("should have resolved segment"); |
| |
| for (i, mod_segment) in mod_segments.iter().enumerate() { |
| if mod_segment.args_and_bindings.is_some() { |
| self.on_diagnostic(PathLoweringDiagnostic::GenericArgsProhibited { |
| segment: i as u32, |
| reason: GenericArgsProhibitedReason::Module, |
| }); |
| } |
| } |
| |
| if let Some(enum_segment) = enum_segment |
| && segments.get(enum_segment).is_some_and(|it| it.args_and_bindings.is_some()) |
| && segments.get(enum_segment + 1).is_some_and(|it| it.args_and_bindings.is_some()) |
| { |
| self.on_diagnostic(PathLoweringDiagnostic::GenericArgsProhibited { |
| segment: (enum_segment + 1) as u32, |
| reason: GenericArgsProhibitedReason::EnumVariant, |
| }); |
| } |
| |
| match &res { |
| ResolveValueResult::ValueNs(resolution, _) => { |
| let resolved_segment_idx = self.current_segment_u32(); |
| let resolved_segment = self.current_or_prev_segment; |
| |
| let mut prohibit_generics_on_resolved = |reason| { |
| if resolved_segment.args_and_bindings.is_some() { |
| self.on_diagnostic(PathLoweringDiagnostic::GenericArgsProhibited { |
| segment: resolved_segment_idx, |
| reason, |
| }); |
| } |
| }; |
| |
| match resolution { |
| ValueNs::ImplSelf(_) => { |
| prohibit_generics_on_resolved(GenericArgsProhibitedReason::SelfTy) |
| } |
| // FIXME: rustc generates E0107 (incorrect number of generic arguments) and not |
| // E0109 (generic arguments provided for a type that doesn't accept them) for |
| // consts and statics, presumably as a defense against future in which consts |
| // 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(_) => { |
| prohibit_generics_on_resolved(GenericArgsProhibitedReason::Const) |
| } |
| ValueNs::StaticId(_) => { |
| prohibit_generics_on_resolved(GenericArgsProhibitedReason::Static) |
| } |
| ValueNs::FunctionId(_) | ValueNs::StructId(_) | ValueNs::EnumVariantId(_) => {} |
| ValueNs::LocalBinding(_) => {} |
| } |
| } |
| ResolveValueResult::Partial(resolution, _, _) => { |
| self.handle_type_ns_resolution(resolution); |
| } |
| }; |
| Some(res) |
| } |
| |
| #[tracing::instrument(skip(self), ret)] |
| fn select_associated_type(&mut self, res: Option<TypeNs>) -> Ty<'db> { |
| let interner = self.ctx.interner; |
| let Some(res) = res else { |
| return Ty::new_error(self.ctx.interner, ErrorGuaranteed); |
| }; |
| let db = self.ctx.db; |
| let def = self.ctx.def; |
| let segment = self.current_or_prev_segment; |
| let assoc_name = segment.name; |
| let mut check_alias = |name: &Name, t: TraitRef<'db>, associated_ty: TypeAliasId| { |
| if name != assoc_name { |
| return None; |
| } |
| |
| // 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 (`t.substitution`). |
| let substs = self.substs_from_path_segment(associated_ty.into(), false, None, true); |
| |
| let substs = crate::next_solver::GenericArgs::new_from_iter( |
| interner, |
| t.args.iter().chain(substs.iter().skip(t.args.len())), |
| ); |
| |
| Some(Ty::new_alias( |
| interner, |
| AliasTyKind::Projection, |
| AliasTy::new(interner, associated_ty.into(), substs), |
| )) |
| }; |
| named_associated_type_shorthand_candidates( |
| interner, |
| def, |
| res, |
| Some(assoc_name.clone()), |
| check_alias, |
| ) |
| .unwrap_or_else(|| Ty::new_error(interner, ErrorGuaranteed)) |
| } |
| |
| fn lower_path_inner(&mut self, typeable: TyDefId, infer_args: bool) -> Ty<'db> { |
| let generic_def = match typeable { |
| TyDefId::BuiltinType(builtinty) => return builtin(self.ctx.interner, builtinty), |
| TyDefId::AdtId(it) => it.into(), |
| TyDefId::TypeAliasId(it) => it.into(), |
| }; |
| let args = self.substs_from_path_segment(generic_def, infer_args, None, false); |
| let ty = ty_query(self.ctx.db, typeable); |
| ty.instantiate(self.ctx.interner, args) |
| } |
| |
| /// Collect generic arguments from a path into a `Substs`. See also |
| /// `create_substs_for_ast_path` and `def_to_ty` in rustc. |
| pub(crate) fn substs_from_path( |
| &mut self, |
| // Note that we don't call `db.value_type(resolved)` here, |
| // `ValueTyDefId` is just a convenient way to pass generics and |
| // special-case enum variants |
| resolved: ValueTyDefId, |
| infer_args: bool, |
| lowering_assoc_type_generics: bool, |
| ) -> crate::next_solver::GenericArgs<'db> { |
| let interner = self.ctx.interner; |
| let prev_current_segment_idx = self.current_segment_idx; |
| let prev_current_segment = self.current_or_prev_segment; |
| |
| let generic_def = match resolved { |
| ValueTyDefId::FunctionId(it) => it.into(), |
| ValueTyDefId::StructId(it) => it.into(), |
| ValueTyDefId::UnionId(it) => it.into(), |
| ValueTyDefId::ConstId(it) => it.into(), |
| ValueTyDefId::StaticId(_) => { |
| return crate::next_solver::GenericArgs::new_from_iter(interner, []); |
| } |
| ValueTyDefId::EnumVariantId(var) => { |
| // the generic args for an enum variant may be either specified |
| // on the segment referring to the enum, or on the segment |
| // referring to the variant. So `Option::<T>::None` and |
| // `Option::None::<T>` are both allowed (though the former is |
| // FIXME: This isn't strictly correct, enum variants may be used not through the enum |
| // (via `use Enum::Variant`). The resolver returns whether they were, but we don't have its result |
| // available here. The worst that can happen is that we will show some confusing diagnostics to the user, |
| // if generics exist on the module and they don't match with the variant. |
| // preferred). See also `def_ids_for_path_segments` in rustc. |
| // |
| // `wrapping_sub(1)` will return a number which `get` will return None for if current_segment_idx<2. |
| // This simplifies the code a bit. |
| let penultimate_idx = self.current_segment_idx.wrapping_sub(1); |
| let penultimate = self.segments.get(penultimate_idx); |
| if let Some(penultimate) = penultimate |
| && self.current_or_prev_segment.args_and_bindings.is_none() |
| && penultimate.args_and_bindings.is_some() |
| { |
| self.current_segment_idx = penultimate_idx; |
| self.current_or_prev_segment = penultimate; |
| } |
| var.lookup(self.ctx.db).parent.into() |
| } |
| }; |
| 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 |
| } |
| |
| pub(crate) fn substs_from_path_segment( |
| &mut self, |
| def: GenericDefId, |
| infer_args: bool, |
| explicit_self_ty: Option<Ty<'db>>, |
| lowering_assoc_type_generics: bool, |
| ) -> crate::next_solver::GenericArgs<'db> { |
| let mut lifetime_elision = self.ctx.lifetime_elision.clone(); |
| |
| if let Some(args) = self.current_or_prev_segment.args_and_bindings |
| && 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 unknown_subst(self.ctx.interner, def); |
| } |
| |
| // `Fn()`-style generics are treated like functions for the purpose of lifetime elision. |
| lifetime_elision = |
| LifetimeElisionKind::AnonymousCreateParameter { report_in_path: false }; |
| } |
| |
| self.substs_from_args_and_bindings( |
| self.current_or_prev_segment.args_and_bindings, |
| def, |
| infer_args, |
| explicit_self_ty, |
| PathGenericsSource::Segment(self.current_segment_u32()), |
| lowering_assoc_type_generics, |
| lifetime_elision, |
| ) |
| } |
| |
| pub(super) fn substs_from_args_and_bindings( |
| &mut self, |
| args_and_bindings: Option<&GenericArgs>, |
| def: GenericDefId, |
| infer_args: bool, |
| explicit_self_ty: Option<Ty<'db>>, |
| generics_source: PathGenericsSource, |
| lowering_assoc_type_generics: bool, |
| lifetime_elision: LifetimeElisionKind<'db>, |
| ) -> crate::next_solver::GenericArgs<'db> { |
| struct LowererCtx<'a, 'b, 'c, 'db> { |
| ctx: &'a mut PathLoweringContext<'b, 'c, 'db>, |
| generics_source: PathGenericsSource, |
| } |
| |
| impl<'db> GenericArgsLowerer<'db> for LowererCtx<'_, '_, '_, 'db> { |
| 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::next_solver::GenericArg<'db> { |
| match (param, arg) { |
| (GenericParamDataRef::LifetimeParamData(_), GenericArg::Lifetime(lifetime)) => { |
| self.ctx.ctx.lower_lifetime(*lifetime).into() |
| } |
| (GenericParamDataRef::TypeParamData(_), GenericArg::Type(type_ref)) => { |
| self.ctx.ctx.lower_ty(*type_ref).into() |
| } |
| (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, const_param_ty_query(self.ctx.ctx.db, const_id)) |
| .into() |
| } |
| _ => unreachable!("unmatching param kinds were passed to `provided_kind()`"), |
| } |
| } |
| |
| fn provided_type_like_const( |
| &mut self, |
| const_ty: Ty<'db>, |
| arg: TypeLikeConst<'_>, |
| ) -> crate::next_solver::Const<'db> { |
| 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::next_solver::GenericArg<'db>], |
| ) -> crate::next_solver::GenericArg<'db> { |
| let default = || { |
| self.ctx.ctx.db.generic_defaults(def).get(preceding_args.len()).map(|default| { |
| convert_binder_to_early_binder( |
| self.ctx.ctx.interner, |
| def, |
| default.to_nextsolver(self.ctx.ctx.interner), |
| ) |
| .instantiate(self.ctx.ctx.interner, preceding_args) |
| }) |
| }; |
| match param { |
| GenericParamDataRef::LifetimeParamData(_) => { |
| Region::new(self.ctx.ctx.interner, rustc_type_ir::ReError(ErrorGuaranteed)) |
| .into() |
| } |
| GenericParamDataRef::TypeParamData(param) => { |
| if !infer_args |
| && param.default.is_some() |
| && let Some(default) = default() |
| { |
| return default; |
| } |
| Ty::new_error(self.ctx.ctx.interner, ErrorGuaranteed).into() |
| } |
| GenericParamDataRef::ConstParamData(param) => { |
| if !infer_args |
| && param.default.is_some() |
| && 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(const_param_ty_query(self.ctx.ctx.db, const_id)) |
| } |
| } |
| } |
| |
| fn parent_arg( |
| &mut self, |
| param_id: GenericParamId, |
| ) -> crate::next_solver::GenericArg<'db> { |
| match param_id { |
| GenericParamId::TypeParamId(_) => { |
| Ty::new_error(self.ctx.ctx.interner, ErrorGuaranteed).into() |
| } |
| GenericParamId::ConstParamId(const_id) => { |
| unknown_const_as_generic(const_param_ty_query(self.ctx.ctx.db, const_id)) |
| } |
| GenericParamId::LifetimeParamId(_) => { |
| Region::new(self.ctx.ctx.interner, rustc_type_ir::ReError(ErrorGuaranteed)) |
| .into() |
| } |
| } |
| } |
| |
| 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( |
| 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<'db>, |
| ) -> TraitRef<'db> { |
| let args = self.trait_ref_substs_from_path(resolved, explicit_self_ty); |
| TraitRef::new_from_args(self.ctx.interner, resolved.into(), args) |
| } |
| |
| fn trait_ref_substs_from_path( |
| &mut self, |
| resolved: TraitId, |
| explicit_self_ty: Ty<'db>, |
| ) -> crate::next_solver::GenericArgs<'db> { |
| self.substs_from_path_segment(resolved.into(), false, Some(explicit_self_ty), false) |
| } |
| |
| pub(super) fn assoc_type_bindings_from_type_bound<'c>( |
| mut self, |
| trait_ref: TraitRef<'db>, |
| ) -> Option<impl Iterator<Item = Clause<'db>> + use<'a, 'b, 'c, 'db>> { |
| let interner = self.ctx.interner; |
| self.current_or_prev_segment.args_and_bindings.map(|args_and_bindings| { |
| 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, |
| &binding.name, |
| ); |
| let (super_trait_ref, associated_ty) = match found { |
| None => return SmallVec::new(), |
| Some(t) => t, |
| }; |
| let args = |
| 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_ty()), |
| PathGenericsSource::AssocType { |
| segment: this.current_segment_u32(), |
| assoc_type: binding_idx as u32, |
| }, |
| false, |
| this.ctx.lifetime_elision.clone(), |
| ) |
| }); |
| let args = crate::next_solver::GenericArgs::new_from_iter( |
| interner, |
| super_trait_ref.args.iter().chain(args.iter().skip(super_trait_ref.args.len())), |
| ); |
| let projection_term = |
| AliasTerm::new_from_args(interner, associated_ty.into(), args); |
| 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 pred = Clause(Predicate::new( |
| interner, |
| Binder::dummy(rustc_type_ir::PredicateKind::Clause( |
| rustc_type_ir::ClauseKind::Projection(ProjectionPredicate { |
| projection_term, |
| term: ty.into(), |
| }), |
| )), |
| )); |
| predicates.push(pred); |
| } |
| } |
| } |
| for bound in binding.bounds.iter() { |
| predicates.extend(self.ctx.lower_type_bound( |
| bound, |
| Ty::new_alias( |
| self.ctx.interner, |
| AliasTyKind::Projection, |
| AliasTy::new_from_args(self.ctx.interner, associated_ty.into(), args), |
| ), |
| false, |
| )); |
| } |
| predicates |
| }) |
| }) |
| } |
| } |
| |
| /// A const that were parsed like a type. |
| pub(crate) enum TypeLikeConst<'a> { |
| Infer, |
| Path(&'a Path), |
| } |
| |
| pub(crate) trait GenericArgsLowerer<'db> { |
| 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::next_solver::GenericArg<'db>; |
| |
| fn provided_type_like_const(&mut self, const_ty: Ty<'db>, arg: TypeLikeConst<'_>) |
| -> Const<'db>; |
| |
| fn inferred_kind( |
| &mut self, |
| def: GenericDefId, |
| param_id: GenericParamId, |
| param: GenericParamDataRef<'_>, |
| infer_args: bool, |
| preceding_args: &[crate::next_solver::GenericArg<'db>], |
| ) -> crate::next_solver::GenericArg<'db>; |
| |
| fn parent_arg(&mut self, param_id: GenericParamId) -> crate::next_solver::GenericArg<'db>; |
| } |
| |
| /// Returns true if there was an error. |
| fn check_generic_args_len<'db>( |
| args_and_bindings: Option<&GenericArgs>, |
| def: GenericDefId, |
| def_generics: &Generics, |
| infer_args: bool, |
| lifetime_elision: &LifetimeElisionKind<'db>, |
| lowering_assoc_type_generics: bool, |
| ctx: &mut impl GenericArgsLowerer<'db>, |
| ) -> 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>( |
| db: &'db dyn HirDatabase, |
| store: &ExpressionStore, |
| args_and_bindings: Option<&GenericArgs>, |
| def: GenericDefId, |
| mut infer_args: bool, |
| lifetime_elision: LifetimeElisionKind<'db>, |
| lowering_assoc_type_generics: bool, |
| explicit_self_ty: Option<Ty<'db>>, |
| ctx: &mut impl GenericArgsLowerer<'db>, |
| ) -> crate::next_solver::GenericArgs<'db> { |
| let interner = DbInterner::new_with(db, None, None); |
| |
| tracing::debug!(?args_and_bindings); |
| |
| // 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.into()).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 = const_param_ty_query(db, param_id); |
| substs.push(ctx.provided_type_like_const(const_ty, konst).into()); |
| 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: _ } => { |
| Region::new_static(interner).into() |
| } |
| LifetimeElisionKind::Elided(lifetime) => (*lifetime).into(), |
| 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, |
| } |
| } |
| |
| crate::next_solver::GenericArgs::new_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, |
| } |
| } |
| |
| fn unknown_subst<'db>( |
| interner: DbInterner<'db>, |
| def: impl Into<GenericDefId>, |
| ) -> crate::next_solver::GenericArgs<'db> { |
| let params = generics(interner.db(), def.into()); |
| crate::next_solver::GenericArgs::new_from_iter( |
| interner, |
| params.iter_id().map(|id| match id { |
| GenericParamId::TypeParamId(_) => Ty::new_error(interner, ErrorGuaranteed).into(), |
| GenericParamId::ConstParamId(id) => { |
| unknown_const_as_generic(const_param_ty_query(interner.db(), id)) |
| } |
| GenericParamId::LifetimeParamId(_) => { |
| crate::next_solver::Region::error(interner).into() |
| } |
| }), |
| ) |
| } |
| |
| pub(crate) fn builtin<'db>(interner: DbInterner<'db>, builtin: BuiltinType) -> Ty<'db> { |
| match builtin { |
| BuiltinType::Char => Ty::new(interner, rustc_type_ir::TyKind::Char), |
| BuiltinType::Bool => Ty::new_bool(interner), |
| BuiltinType::Str => Ty::new(interner, rustc_type_ir::TyKind::Str), |
| BuiltinType::Int(t) => { |
| let int_ty = match primitive::int_ty_from_builtin(t) { |
| chalk_ir::IntTy::Isize => rustc_type_ir::IntTy::Isize, |
| chalk_ir::IntTy::I8 => rustc_type_ir::IntTy::I8, |
| chalk_ir::IntTy::I16 => rustc_type_ir::IntTy::I16, |
| chalk_ir::IntTy::I32 => rustc_type_ir::IntTy::I32, |
| chalk_ir::IntTy::I64 => rustc_type_ir::IntTy::I64, |
| chalk_ir::IntTy::I128 => rustc_type_ir::IntTy::I128, |
| }; |
| Ty::new_int(interner, int_ty) |
| } |
| BuiltinType::Uint(t) => { |
| let uint_ty = match primitive::uint_ty_from_builtin(t) { |
| chalk_ir::UintTy::Usize => rustc_type_ir::UintTy::Usize, |
| chalk_ir::UintTy::U8 => rustc_type_ir::UintTy::U8, |
| chalk_ir::UintTy::U16 => rustc_type_ir::UintTy::U16, |
| chalk_ir::UintTy::U32 => rustc_type_ir::UintTy::U32, |
| chalk_ir::UintTy::U64 => rustc_type_ir::UintTy::U64, |
| chalk_ir::UintTy::U128 => rustc_type_ir::UintTy::U128, |
| }; |
| Ty::new_uint(interner, uint_ty) |
| } |
| BuiltinType::Float(t) => { |
| let float_ty = match primitive::float_ty_from_builtin(t) { |
| chalk_ir::FloatTy::F16 => rustc_type_ir::FloatTy::F16, |
| chalk_ir::FloatTy::F32 => rustc_type_ir::FloatTy::F32, |
| chalk_ir::FloatTy::F64 => rustc_type_ir::FloatTy::F64, |
| chalk_ir::FloatTy::F128 => rustc_type_ir::FloatTy::F128, |
| }; |
| Ty::new_float(interner, float_ty) |
| } |
| } |
| } |