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.rs1016
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,
+ }
+}