blob: 7d6734303c48bec73ff1f51f3b0696c778b86386 [file] [log] [blame]
//! 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)
}
}
}