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 | 768 |
1 files changed, 480 insertions, 288 deletions
diff --git a/crates/hir-ty/src/lower/path.rs b/crates/hir-ty/src/lower/path.rs index 09a256a86d..9ba0da6f49 100644 --- a/crates/hir-ty/src/lower/path.rs +++ b/crates/hir-ty/src/lower/path.rs @@ -1,42 +1,52 @@ //! A wrapper around [`TyLoweringContext`] specifically for lowering paths. -use chalk_ir::{BoundVar, cast::Cast, fold::Shift}; use either::Either; use hir_def::{ - GenericDefId, GenericParamId, TraitId, + GenericDefId, GenericParamId, Lookup, TraitId, TypeAliasId, expr_store::{ - ExpressionStore, - path::{GenericArg, GenericArgs, GenericArgsParentheses, Path, PathSegment, PathSegments}, + ExpressionStore, HygieneId, + path::{ + GenericArg as HirGenericArg, GenericArgs as HirGenericArgs, GenericArgsParentheses, + Path, PathSegment, PathSegments, + }, }, hir::generics::{ GenericParamDataRef, TypeOrConstParamData, TypeParamData, TypeParamProvenance, }, - resolver::TypeNs, + resolver::{ResolveValueResult, TypeNs, ValueNs}, signatures::TraitFlags, type_ref::{TypeRef, TypeRefId}, }; +use hir_expand::name::Name; +use rustc_type_ir::{ + AliasTerm, AliasTy, AliasTyKind, + inherent::{GenericArgs as _, Region as _, SliceLike, Ty as _}, +}; use smallvec::SmallVec; use stdx::never; use crate::{ - AliasEq, AliasTy, GenericArgsProhibitedReason, ImplTraitLoweringMode, IncorrectGenericsLenKind, - Interner, ParamLoweringMode, PathGenericsSource, PathLoweringDiagnostic, ProjectionTy, - QuantifiedWhereClause, Substitution, TraitRef, Ty, TyBuilder, TyDefId, TyKind, WhereClause, - consteval_chalk::{unknown_const, unknown_const_as_generic}, + GenericArgsProhibitedReason, IncorrectGenericsLenKind, PathGenericsSource, + PathLoweringDiagnostic, TyDefId, ValueTyDefId, + consteval::{unknown_const, unknown_const_as_generic}, db::HirDatabase, - error_lifetime, generics::{Generics, generics}, - lower::{LifetimeElisionKind, TyLoweringContext, named_associated_type_shorthand_candidates}, + lower::{ + LifetimeElisionKind, PathDiagnosticCallbackData, named_associated_type_shorthand_candidates, + }, next_solver::{ - DbInterner, - mapping::{ChalkToNextSolver, NextSolverToChalk}, + Binder, Clause, Const, DbInterner, ErrorGuaranteed, GenericArg, GenericArgs, Predicate, + ProjectionPredicate, Region, TraitRef, Ty, }, - to_assoc_type_id, to_chalk_trait_id, to_placeholder_idx, - utils::associated_type_by_name_including_super_traits, +}; + +use super::{ + ImplTraitLoweringMode, TyLoweringContext, associated_type_by_name_including_super_traits, + const_param_ty_query, ty_query, }; type CallbackData<'a, 'db> = Either< - super::PathDiagnosticCallbackData, + PathDiagnosticCallbackData, crate::infer::diagnostics::PathDiagnosticCallbackData<'a, 'db>, >; @@ -45,12 +55,12 @@ type CallbackData<'a, 'db> = Either< pub(crate) struct PathDiagnosticCallback<'a, 'db> { pub(crate) data: CallbackData<'a, 'db>, pub(crate) callback: - fn(&CallbackData<'_, 'db>, &mut TyLoweringContext<'_>, PathLoweringDiagnostic), + fn(&CallbackData<'_, 'db>, &mut TyLoweringContext<'db, '_>, PathLoweringDiagnostic), } -pub(crate) struct PathLoweringContext<'a, 'b> { - ctx: &'a mut TyLoweringContext<'b>, - on_diagnostic: PathDiagnosticCallback<'a, 'b>, +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, @@ -58,11 +68,11 @@ pub(crate) struct PathLoweringContext<'a, 'b> { current_or_prev_segment: PathSegment<'a>, } -impl<'a, 'b> PathLoweringContext<'a, 'b> { +impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { #[inline] pub(crate) fn new( - ctx: &'a mut TyLoweringContext<'b>, - on_diagnostic: PathDiagnosticCallback<'a, 'b>, + ctx: &'a mut TyLoweringContext<'db, 'b>, + on_diagnostic: PathDiagnosticCallback<'a, 'db>, path: &'a Path, ) -> Self { let segments = path.segments(); @@ -84,7 +94,7 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { } #[inline] - pub(crate) fn ty_ctx(&mut self) -> &mut TyLoweringContext<'b> { + pub(crate) fn ty_ctx(&mut self) -> &mut TyLoweringContext<'db, 'b> { self.ctx } @@ -110,10 +120,24 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { } #[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, - f: impl FnOnce(&mut PathLoweringContext<'_, '_>) -> T, + 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); @@ -124,12 +148,13 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { pub(crate) fn lower_ty_relative_path( &mut self, - ty: Ty, + ty: Ty<'db>, // 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 { + ) -> (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 @@ -137,7 +162,7 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { } _ => { // FIXME report error (ambiguous associated type) - (TyKind::Error.intern(Interner), None) + (Ty::new_error(self.ctx.interner, ErrorGuaranteed), None) } } } @@ -147,8 +172,11 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { &mut self, resolution: TypeNs, infer_args: bool, - ) -> (Ty, Option<TypeNs>) { + ) -> (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_) => { @@ -156,15 +184,17 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { 1 => { let trait_ref = self.lower_trait_ref_from_resolved_path( trait_, - TyKind::Error.intern(Interner), - infer_args, + Ty::new_error(self.ctx.interner, ErrorGuaranteed), + false, ); - + 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_.trait_items(self.ctx.db).associated_type_by_name(segment.name); + 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 @@ -173,27 +203,30 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { // this point (`trait_ref.substitution`). let substitution = self.substs_from_path_segment( associated_ty.into(), - infer_args, + false, None, true, ); - let substitution = Substitution::from_iter( - Interner, - trait_ref.substitution.iter(Interner).chain( - substitution - .iter(Interner) - .skip(trait_ref.substitution.len(Interner)), - ), + let args = GenericArgs::new_from_iter( + self.ctx.interner, + trait_ref + .args + .iter() + .chain(substitution.iter().skip(trait_ref.args.len())), ); - TyKind::Alias(AliasTy::Projection(ProjectionTy { - associated_ty_id: to_assoc_type_id(associated_ty), - substitution, - })) - .intern(Interner) + 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) - TyKind::Error.intern(Interner) + Ty::new_error(self.ctx.interner, ErrorGuaranteed) } } } @@ -201,73 +234,34 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { // Trait object type without dyn; this should be handled in upstream. See // `lower_path()`. stdx::never!("unexpected fully resolved trait path"); - TyKind::Error.intern(Interner) + Ty::new_error(self.ctx.interner, ErrorGuaranteed) } _ => { // FIXME report error (ambiguous associated type) - TyKind::Error.intern(Interner) + Ty::new_error(self.ctx.interner, ErrorGuaranteed) } }; return (ty, None); } - TypeNs::GenericParam(param_id) => match self.ctx.type_param_mode { - ParamLoweringMode::Placeholder => { - let generics = self.ctx.generics(); - let idx = generics.type_or_const_param_idx(param_id.into()).unwrap(); - TyKind::Placeholder(to_placeholder_idx( - self.ctx.db, - param_id.into(), - idx as u32, - )) - } - ParamLoweringMode::Variable => { - 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); - } - Some(idx) => idx, - }; - - TyKind::BoundVar(BoundVar::new(self.ctx.in_binders, idx)) - } - } - .intern(Interner), - TypeNs::SelfType(impl_id) => { + TypeNs::GenericParam(param_id) => { let generics = self.ctx.generics(); - - match self.ctx.type_param_mode { - ParamLoweringMode::Placeholder => { - // `def` can be either impl itself or item within, and we need impl itself - // now. - let generics = generics.parent_or_self(); - let interner = DbInterner::new_with(self.ctx.db, None, None); - let subst = generics.placeholder_subst(self.ctx.db); - let args: crate::next_solver::GenericArgs<'_> = - subst.to_nextsolver(interner); - self.ctx - .db - .impl_self_ty(impl_id) - .instantiate(interner, args) - .to_chalk(interner) + 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()); + self.ctx.type_param(param_id, idx as u32) } - ParamLoweringMode::Variable => TyBuilder::impl_self_ty(self.ctx.db, impl_id) - .fill_with_bound_vars(self.ctx.in_binders, 0) - .build(DbInterner::conjure()) - .to_chalk(DbInterner::conjure()), } } + TypeNs::SelfType(impl_id) => self.ctx.db.impl_self_ty(impl_id).skip_binder(), TypeNs::AdtSelfType(adt) => { - 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 => { - generics.bound_vars_subst(self.ctx.db, self.ctx.in_binders) - } - }; - let interner = DbInterner::conjure(); - let args: crate::next_solver::GenericArgs<'_> = substs.to_nextsolver(interner); - self.ctx.db.ty(adt.into()).instantiate(interner, args).to_chalk(interner) + let args = GenericArgs::identity_for_item(self.ctx.interner, adt.into()); + Ty::new_adt(self.ctx.interner, adt, args) } TypeNs::AdtId(it) => self.lower_path_inner(it.into(), infer_args), @@ -275,15 +269,19 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { TypeNs::TypeAliasId(it) => self.lower_path_inner(it.into(), infer_args), // FIXME: report error TypeNs::EnumVariantId(_) | TypeNs::ModuleId(_) => { - return (TyKind::Error.intern(Interner), None); + return (Ty::new_error(self.ctx.interner, ErrorGuaranteed), None); } }; + tracing::debug!(?ty); + self.skip_resolved_segment(); self.lower_ty_relative_path(ty, Some(resolution), infer_args) } - fn handle_type_ns_resolution(&mut self, resolution: &TypeNs) { + /// This returns whether to keep the resolution (`true`) of throw it (`false`). + #[must_use] + fn handle_type_ns_resolution(&mut self, resolution: &TypeNs) -> bool { let mut prohibit_generics_on_resolved = |reason| { if self.current_or_prev_segment.args_and_bindings.is_some() { let segment = self.current_segment_u32(); @@ -302,7 +300,13 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { prohibit_generics_on_resolved(GenericArgsProhibitedReason::TyParam) } TypeNs::AdtSelfType(_) => { - prohibit_generics_on_resolved(GenericArgsProhibitedReason::SelfTy) + prohibit_generics_on_resolved(GenericArgsProhibitedReason::SelfTy); + + if self.ctx.lowering_param_default.is_some() { + // Generic defaults are not allowed to refer to `Self`. + // FIXME: Emit an error. + return false; + } } TypeNs::BuiltinType(_) => { prohibit_generics_on_resolved(GenericArgsProhibitedReason::PrimitiveTy) @@ -315,6 +319,8 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { | TypeNs::TypeAliasId(_) | TypeNs::TraitId(_) => {} } + + true } pub(crate) fn resolve_path_in_type_ns_fully(&mut self) -> Option<TypeNs> { @@ -325,6 +331,7 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { 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)?; @@ -347,11 +354,6 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { 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 { @@ -371,87 +373,233 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { }); } - self.handle_type_ns_resolution(&resolution); + if !self.handle_type_ns_resolution(&resolution) { + return None; + } Some((resolution, remaining_index)) } - 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( + 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.ctx.def, - res, - Some(segment.name.clone()), - move |name, t, associated_ty| { - if name != segment.name { - return None; - } - let generics = self.ctx.generics(); + self.path, + hygiene_id, + )?; - let parent_subst = t.substitution.clone(); - let parent_subst = match self.ctx.type_param_mode { - ParamLoweringMode::Placeholder => { - // if we're lowering to placeholders, we have to put them in now. - let s = generics.placeholder_subst(self.ctx.db); - s.apply(parent_subst, Interner) - } - ParamLoweringMode::Variable => { - // We need to shift in the bound vars, since - // `named_associated_type_shorthand_candidates` does not do that. - parent_subst.shifted_in_from(Interner, self.ctx.in_binders) + 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, + }); } }; - // 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(), infer_args, None, true); - - let substs = Substitution::from_iter( - Interner, - parent_subst - .iter(Interner) - .chain(substs.iter(Interner).skip(parent_subst.len(Interner))), - ); + 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(_) => { + prohibit_generics_on_resolved(GenericArgsProhibitedReason::Const) + } + ValueNs::StaticId(_) => { + prohibit_generics_on_resolved(GenericArgsProhibitedReason::Static) + } + ValueNs::LocalBinding(_) => { + prohibit_generics_on_resolved(GenericArgsProhibitedReason::LocalVariable) + } + ValueNs::FunctionId(_) + | ValueNs::StructId(_) + | ValueNs::EnumVariantId(_) + | ValueNs::ConstId(_) => {} + } + } + ResolveValueResult::Partial(resolution, _, _) => { + if !self.handle_type_ns_resolution(resolution) { + return None; + } + } + }; + Some(res) + } - Some( - TyKind::Alias(AliasTy::Projection(ProjectionTy { - associated_ty_id: to_assoc_type_id(associated_ty), - substitution: substs, - })) - .intern(Interner), - ) - }, - ); + #[tracing::instrument(skip(self), ret)] + fn select_associated_type(&mut self, res: Option<TypeNs>, infer_args: bool) -> Ty<'db> { + let interner = self.ctx.interner; + let Some(res) = res else { + return Ty::new_error(self.ctx.interner, ErrorGuaranteed); + }; + let def = self.ctx.def; + let segment = self.current_or_prev_segment; + let assoc_name = segment.name; + let check_alias = |name: &Name, t: TraitRef<'db>, associated_ty: TypeAliasId| { + if name != assoc_name { + return None; + } - ty.unwrap_or_else(|| TyKind::Error.intern(Interner)) + // 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(), infer_args, None, true); + + let substs = 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 { + fn lower_path_inner(&mut self, typeable: TyDefId, infer_args: bool) -> Ty<'db> { let generic_def = match typeable { - TyDefId::BuiltinType(builtin) => return TyBuilder::builtin(builtin), + TyDefId::BuiltinType(builtinty) => { + return Ty::from_builtin_type(self.ctx.interner, builtinty); + } TyDefId::AdtId(it) => it.into(), TyDefId::TypeAliasId(it) => it.into(), }; - let substs = self.substs_from_path_segment(generic_def, infer_args, None, false); - let interner = DbInterner::conjure(); - let args: crate::next_solver::GenericArgs<'_> = substs.to_nextsolver(interner); - self.ctx.db.ty(typeable).instantiate(interner, args).to_chalk(interner) + 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, + ) -> 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 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>, + explicit_self_ty: Option<Ty<'db>>, lowering_assoc_type_generics: bool, - ) -> Substitution { + ) -> GenericArgs<'db> { let old_lifetime_elision = self.ctx.lifetime_elision.clone(); if let Some(args) = self.current_or_prev_segment.args_and_bindings @@ -478,7 +626,7 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { PathLoweringDiagnostic::ParenthesizedGenericArgsWithoutFnTrait { segment }, ); - return TyBuilder::unknown_subst(self.ctx.db, def); + return unknown_subst(self.ctx.interner, def); } // `Fn()`-style generics are treated like functions for the purpose of lifetime elision. @@ -501,20 +649,20 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { pub(super) fn substs_from_args_and_bindings( &mut self, - args_and_bindings: Option<&GenericArgs>, + args_and_bindings: Option<&HirGenericArgs>, def: GenericDefId, infer_args: bool, - explicit_self_ty: Option<Ty>, + explicit_self_ty: Option<Ty<'db>>, generics_source: PathGenericsSource, lowering_assoc_type_generics: bool, - lifetime_elision: LifetimeElisionKind, - ) -> Substitution { - struct LowererCtx<'a, 'b, 'c> { - ctx: &'a mut PathLoweringContext<'b, 'c>, + lifetime_elision: LifetimeElisionKind<'db>, + ) -> GenericArgs<'db> { + struct LowererCtx<'a, 'b, 'c, 'db> { + ctx: &'a mut PathLoweringContext<'b, 'c, 'db>, generics_source: PathGenericsSource, } - impl GenericArgsLowerer for LowererCtx<'_, '_, '_> { + impl<'db> GenericArgsLowerer<'db> for LowererCtx<'_, '_, '_, 'db> { fn report_len_mismatch( &mut self, def: GenericDefId, @@ -549,23 +697,24 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { &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) + arg: &HirGenericArg, + ) -> GenericArg<'db> { + match (param, *arg) { + ( + GenericParamDataRef::LifetimeParamData(_), + HirGenericArg::Lifetime(lifetime), + ) => self.ctx.ctx.lower_lifetime(lifetime).into(), + (GenericParamDataRef::TypeParamData(_), HirGenericArg::Type(type_ref)) => { + self.ctx.ctx.lower_ty(type_ref).into() } - (GenericParamDataRef::ConstParamData(_), GenericArg::Const(konst)) => { + (GenericParamDataRef::ConstParamData(_), HirGenericArg::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) + .lower_const(konst, const_param_ty_query(self.ctx.ctx.db, const_id)) + .into() } _ => unreachable!("unmatching param kinds were passed to `provided_kind()`"), } @@ -573,9 +722,9 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { fn provided_type_like_const( &mut self, - const_ty: Ty, + const_ty: Ty<'db>, arg: TypeLikeConst<'_>, - ) -> crate::Const { + ) -> Const<'db> { match arg { TypeLikeConst::Path(path) => self.ctx.ctx.lower_path_as_const(path, const_ty), TypeLikeConst::Infer => unknown_const(const_ty), @@ -588,18 +737,19 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { 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)) - }; + preceding_args: &[GenericArg<'db>], + ) -> GenericArg<'db> { + let default = + || { + self.ctx.ctx.db.generic_defaults(def).get(preceding_args.len()).map( + |default| default.instantiate(self.ctx.ctx.interner, preceding_args), + ) + }; match param { - GenericParamDataRef::LifetimeParamData(_) => error_lifetime().cast(Interner), + GenericParamDataRef::LifetimeParamData(_) => { + Region::new(self.ctx.ctx.interner, rustc_type_ir::ReError(ErrorGuaranteed)) + .into() + } GenericParamDataRef::TypeParamData(param) => { if !infer_args && param.default.is_some() @@ -607,7 +757,7 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { { return default; } - TyKind::Error.intern(Interner).cast(Interner) + Ty::new_error(self.ctx.ctx.interner, ErrorGuaranteed).into() } GenericParamDataRef::ConstParamData(param) => { if !infer_args @@ -619,19 +769,23 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { 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) + unknown_const_as_generic(const_param_ty_query(self.ctx.ctx.db, const_id)) } } } - fn parent_arg(&mut self, param_id: GenericParamId) -> crate::GenericArg { + fn parent_arg(&mut self, param_id: GenericParamId) -> GenericArg<'db> { match param_id { - GenericParamId::TypeParamId(_) => TyKind::Error.intern(Interner).cast(Interner), + GenericParamId::TypeParamId(_) => { + Ty::new_error(self.ctx.ctx.interner, ErrorGuaranteed).into() + } GenericParamId::ConstParamId(const_id) => { - unknown_const_as_generic(self.ctx.ctx.db.const_param_ty(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() } - GenericParamId::LifetimeParamId(_) => error_lifetime().cast(Interner), } } @@ -649,6 +803,14 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { }); } + 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, @@ -674,38 +836,39 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { pub(crate) fn lower_trait_ref_from_resolved_path( &mut self, resolved: TraitId, - explicit_self_ty: Ty, + explicit_self_ty: Ty<'db>, infer_args: bool, - ) -> TraitRef { - let substs = self.trait_ref_substs_from_path(resolved, explicit_self_ty, infer_args); - TraitRef { trait_id: to_chalk_trait_id(resolved), substitution: substs } + ) -> TraitRef<'db> { + let args = self.trait_ref_substs_from_path(resolved, explicit_self_ty, infer_args); + TraitRef::new_from_args(self.ctx.interner, resolved.into(), args) } fn trait_ref_substs_from_path( &mut self, resolved: TraitId, - explicit_self_ty: Ty, + explicit_self_ty: Ty<'db>, infer_args: bool, - ) -> Substitution { + ) -> GenericArgs<'db> { 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, - trait_ref: TraitRef, - ) -> Option<impl Iterator<Item = QuantifiedWhereClause> + use<'a, 'b, 'c>> { + 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.clone(), + trait_ref, &binding.name, ); let (super_trait_ref, associated_ty) = match found { None => return SmallVec::new(), Some(t) => t, }; - let substitution = + 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 @@ -715,7 +878,7 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { binding.args.as_ref(), associated_ty.into(), false, // this is not relevant - Some(super_trait_ref.self_type_parameter(Interner)), + Some(super_trait_ref.self_ty()), PathGenericsSource::AssocType { segment: this.current_segment_u32(), assoc_type: binding_idx as u32, @@ -724,27 +887,20 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { this.ctx.lifetime_elision.clone(), ) }); - let substitution = Substitution::from_iter( - Interner, - super_trait_ref.substitution.iter(Interner).chain( - substitution - .iter(Interner) - .skip(super_trait_ref.substitution.len(Interner)), - ), + let args = GenericArgs::new_from_iter( + interner, + super_trait_ref.args.iter().chain(args.iter().skip(super_trait_ref.args.len())), ); - let projection_ty = ProjectionTy { - associated_ty_id: to_assoc_type_id(associated_ty), - substitution, - }; + 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 { 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() + LifetimeElisionKind::for_fn_ret(self.ctx.interner) } else { self.ctx.lifetime_elision.clone() }; @@ -756,31 +912,33 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> { 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, - ))); + 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, + )); } - - 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 }) }) @@ -793,7 +951,7 @@ pub(crate) enum TypeLikeConst<'a> { Path(&'a Path), } -pub(crate) trait GenericArgsLowerer { +pub(crate) trait GenericArgsLowerer<'db> { fn report_elided_lifetimes_in_path( &mut self, def: GenericDefId, @@ -801,6 +959,8 @@ pub(crate) trait GenericArgsLowerer { 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( @@ -817,10 +977,11 @@ pub(crate) trait GenericArgsLowerer { &mut self, param_id: GenericParamId, param: GenericParamDataRef<'_>, - arg: &GenericArg, - ) -> crate::GenericArg; + arg: &HirGenericArg, + ) -> GenericArg<'db>; - fn provided_type_like_const(&mut self, const_ty: Ty, arg: TypeLikeConst<'_>) -> crate::Const; + fn provided_type_like_const(&mut self, const_ty: Ty<'db>, arg: TypeLikeConst<'_>) + -> Const<'db>; fn inferred_kind( &mut self, @@ -828,21 +989,21 @@ pub(crate) trait GenericArgsLowerer { param_id: GenericParamId, param: GenericParamDataRef<'_>, infer_args: bool, - preceding_args: &[crate::GenericArg], - ) -> crate::GenericArg; + preceding_args: &[GenericArg<'db>], + ) -> GenericArg<'db>; - fn parent_arg(&mut self, param_id: GenericParamId) -> crate::GenericArg; + fn parent_arg(&mut self, param_id: GenericParamId) -> GenericArg<'db>; } /// Returns true if there was an error. -fn check_generic_args_len( - args_and_bindings: Option<&GenericArgs>, +fn check_generic_args_len<'db>( + args_and_bindings: Option<&HirGenericArgs>, def: GenericDefId, def_generics: &Generics, infer_args: bool, - lifetime_elision: &LifetimeElisionKind, + lifetime_elision: &LifetimeElisionKind<'db>, lowering_assoc_type_generics: bool, - ctx: &mut impl GenericArgsLowerer, + ctx: &mut impl GenericArgsLowerer<'db>, ) -> bool { let mut had_error = false; @@ -851,8 +1012,10 @@ fn check_generic_args_len( 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, + HirGenericArg::Lifetime(_) => provided_lifetimes_count += 1, + HirGenericArg::Type(_) | HirGenericArg::Const(_) => { + provided_types_and_consts_count += 1 + } } } } @@ -873,6 +1036,13 @@ fn check_generic_args_len( 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); } @@ -919,17 +1089,21 @@ fn check_generic_args_len( had_error } -pub(crate) fn substs_from_args_and_bindings( - db: &dyn HirDatabase, +pub(crate) fn substs_from_args_and_bindings<'db>( + db: &'db dyn HirDatabase, store: &ExpressionStore, - args_and_bindings: Option<&GenericArgs>, + args_and_bindings: Option<&HirGenericArgs>, def: GenericDefId, mut infer_args: bool, - lifetime_elision: LifetimeElisionKind, + lifetime_elision: LifetimeElisionKind<'db>, lowering_assoc_type_generics: bool, - explicit_self_ty: Option<Ty>, - ctx: &mut impl GenericArgsLowerer, -) -> Substitution { + explicit_self_ty: Option<Ty<'db>>, + ctx: &mut impl GenericArgsLowerer<'db>, +) -> GenericArgs<'db> { + let interner = DbInterner::new_with(db, None, None); + + tracing::debug!(?args_and_bindings); + // Order is // - Parent parameters // - Optional Self parameter @@ -940,7 +1114,7 @@ pub(crate) fn substs_from_args_and_bindings( // 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(_))); + args_slice.iter().any(|arg| !matches!(arg, HirGenericArg::Lifetime(_))); infer_args &= !has_non_lifetime_args; let had_count_error = check_generic_args_len( @@ -981,7 +1155,7 @@ pub(crate) fn substs_from_args_and_bindings( 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(|| { + explicit_self_ty.map(|it| it.into()).unwrap_or_else(|| { ctx.inferred_kind(def, self_param_id, self_param, infer_args, &substs) }) }; @@ -996,7 +1170,7 @@ pub(crate) fn substs_from_args_and_bindings( // 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)) + (HirGenericArg::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 @@ -1004,15 +1178,15 @@ pub(crate) fn substs_from_args_and_bindings( 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(_)) => { + (HirGenericArg::Lifetime(_), GenericParamDataRef::LifetimeParamData(_)) + | (HirGenericArg::Type(_), GenericParamDataRef::TypeParamData(_)) + | (HirGenericArg::Const(_), GenericParamDataRef::ConstParamData(_)) => { substs.push(ctx.provided_kind(param_id, param, arg)); args.next(); params.next(); } ( - GenericArg::Type(_) | GenericArg::Const(_), + HirGenericArg::Type(_) | HirGenericArg::Const(_), GenericParamDataRef::LifetimeParamData(_), ) => { // We expected a lifetime argument, but got a type or const @@ -1021,13 +1195,13 @@ pub(crate) fn substs_from_args_and_bindings( params.next(); force_infer_lt = Some((arg_idx as u32, param_id)); } - (GenericArg::Type(type_ref), GenericParamDataRef::ConstParamData(_)) => { + (HirGenericArg::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)); + 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 { @@ -1066,7 +1240,7 @@ pub(crate) fn substs_from_args_and_bindings( // after a type or const). We want to throw an error in this case. if !had_count_error { assert!( - matches!(arg, GenericArg::Lifetime(_)), + matches!(arg, HirGenericArg::Lifetime(_)), "the only possible situation here is incorrect lifetime order" ); let (provided_arg_idx, param_id) = @@ -1081,12 +1255,16 @@ pub(crate) fn substs_from_args_and_bindings( // 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::AnonymousCreateParameter { report_in_path: true } + 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::Elided(lifetime) => lifetime.clone().cast(Interner), + 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 @@ -1105,7 +1283,7 @@ pub(crate) fn substs_from_args_and_bindings( } } - Substitution::from_iter(Interner, substs) + GenericArgs::new_from_iter(interner, substs) } fn type_looks_like_const( @@ -1124,3 +1302,17 @@ fn type_looks_like_const( _ => None, } } + +fn unknown_subst<'db>(interner: DbInterner<'db>, def: impl Into<GenericDefId>) -> GenericArgs<'db> { + let params = generics(interner.db(), def.into()); + 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(_) => Region::error(interner).into(), + }), + ) +} |