Unnamed repository; edit this file 'description' to name the repository.
-rw-r--r--crates/hir-def/src/data.rs4
-rw-r--r--crates/hir-def/src/path.rs10
-rw-r--r--crates/hir-def/src/resolver.rs3
-rw-r--r--crates/hir-expand/src/name.rs4
-rw-r--r--crates/hir-ty/src/infer.rs53
-rw-r--r--crates/hir-ty/src/infer/diagnostics.rs76
-rw-r--r--crates/hir-ty/src/infer/expr.rs4
-rw-r--r--crates/hir-ty/src/infer/pat.rs14
-rw-r--r--crates/hir-ty/src/infer/path.rs150
-rw-r--r--crates/hir-ty/src/lower.rs983
-rw-r--r--crates/hir-ty/src/lower/diagnostics.rs2
-rw-r--r--crates/hir-ty/src/lower/path.rs911
-rw-r--r--crates/hir/src/diagnostics.rs16
-rw-r--r--crates/ide-diagnostics/src/handlers/parenthesized_generic_args_without_fn_trait.rs59
-rw-r--r--crates/ide-diagnostics/src/lib.rs8
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_general.html15
-rw-r--r--crates/ide/src/syntax_highlighting/tests.rs15
-rw-r--r--crates/intern/src/symbol/symbols.rs33
-rw-r--r--crates/test-utils/src/minicore.rs6
19 files changed, 1222 insertions, 1144 deletions
diff --git a/crates/hir-def/src/data.rs b/crates/hir-def/src/data.rs
index c5bbd4edba..bec6627877 100644
--- a/crates/hir-def/src/data.rs
+++ b/crates/hir-def/src/data.rs
@@ -250,6 +250,7 @@ bitflags::bitflags! {
const RUSTC_HAS_INCOHERENT_INHERENT_IMPLS = 1 << 3;
const SKIP_ARRAY_DURING_METHOD_DISPATCH = 1 << 4;
const SKIP_BOXED_SLICE_DURING_METHOD_DISPATCH = 1 << 5;
+ const RUSTC_PAREN_SUGAR = 1 << 6;
}
}
@@ -294,6 +295,9 @@ impl TraitData {
if attrs.by_key(&sym::rustc_has_incoherent_inherent_impls).exists() {
flags |= TraitFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS;
}
+ if attrs.by_key(&sym::rustc_paren_sugar).exists() {
+ flags |= TraitFlags::RUSTC_PAREN_SUGAR;
+ }
let mut skip_array_during_method_dispatch =
attrs.by_key(&sym::rustc_skip_array_during_method_dispatch).exists();
diff --git a/crates/hir-def/src/path.rs b/crates/hir-def/src/path.rs
index e59c37104d..e6c2504d07 100644
--- a/crates/hir-def/src/path.rs
+++ b/crates/hir-def/src/path.rs
@@ -173,10 +173,7 @@ impl Path {
segments: path.mod_path().segments(),
generic_args: Some(path.generic_args()),
},
- Path::LangItem(_, seg) => PathSegments {
- segments: seg.as_ref().map_or(&[], |seg| std::slice::from_ref(seg)),
- generic_args: None,
- },
+ Path::LangItem(_, seg) => PathSegments { segments: seg.as_slice(), generic_args: None },
}
}
@@ -240,6 +237,11 @@ pub struct PathSegment<'a> {
pub args_and_bindings: Option<&'a GenericArgs>,
}
+impl PathSegment<'_> {
+ pub const MISSING: PathSegment<'static> =
+ PathSegment { name: &Name::missing(), args_and_bindings: None };
+}
+
#[derive(Debug, Clone, Copy)]
pub struct PathSegments<'a> {
segments: &'a [Name],
diff --git a/crates/hir-def/src/resolver.rs b/crates/hir-def/src/resolver.rs
index 7e13ae2f7a..9dfb6e3cc4 100644
--- a/crates/hir-def/src/resolver.rs
+++ b/crates/hir-def/src/resolver.rs
@@ -327,8 +327,9 @@ impl Resolver {
| LangItemTarget::ImplDef(_)
| LangItemTarget::Static(_) => return None,
};
+ // Remaining segments start from 0 because lang paths have no segments other than the remaining.
return Some((
- ResolveValueResult::Partial(type_ns, 1, None),
+ ResolveValueResult::Partial(type_ns, 0, None),
ResolvePathResultPrefixInfo::default(),
));
}
diff --git a/crates/hir-expand/src/name.rs b/crates/hir-expand/src/name.rs
index 21e5fb5ef9..0758bd4515 100644
--- a/crates/hir-expand/src/name.rs
+++ b/crates/hir-expand/src/name.rs
@@ -142,8 +142,8 @@ impl Name {
/// Ideally, we want a `gensym` semantics for missing names -- each missing
/// name is equal only to itself. It's not clear how to implement this in
/// salsa though, so we punt on that bit for a moment.
- pub fn missing() -> Name {
- Name { symbol: sym::MISSING_NAME.clone(), ctx: () }
+ pub const fn missing() -> Name {
+ Name { symbol: sym::consts::MISSING_NAME, ctx: () }
}
/// Returns true if this is a fake name for things missing in the source code. See
diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs
index a8cd971b05..0cb7002f44 100644
--- a/crates/hir-ty/src/infer.rs
+++ b/crates/hir-ty/src/infer.rs
@@ -16,7 +16,7 @@
pub(crate) mod cast;
pub(crate) mod closure;
mod coerce;
-mod diagnostics;
+pub(crate) mod diagnostics;
mod expr;
mod mutability;
mod pat;
@@ -1502,21 +1502,22 @@ impl<'a> InferenceContext<'a> {
&self.diagnostics,
InferenceTyDiagnosticSource::Body,
);
+ let mut path_ctx = ctx.at_path(path, node);
let (resolution, unresolved) = if value_ns {
- let Some(res) = ctx.resolve_path_in_value_ns(path, node, HygieneId::ROOT) else {
+ let Some(res) = path_ctx.resolve_path_in_value_ns(HygieneId::ROOT) else {
return (self.err_ty(), None);
};
match res {
ResolveValueResult::ValueNs(value, _) => match value {
ValueNs::EnumVariantId(var) => {
- let substs = ctx.substs_from_path(path, var.into(), true);
+ let substs = path_ctx.substs_from_path(var.into(), true);
drop(ctx);
let ty = self.db.ty(var.lookup(self.db.upcast()).parent.into());
let ty = self.insert_type_vars(ty.substitute(Interner, &substs));
return (ty, Some(var.into()));
}
ValueNs::StructId(strukt) => {
- let substs = ctx.substs_from_path(path, strukt.into(), true);
+ let substs = path_ctx.substs_from_path(strukt.into(), true);
drop(ctx);
let ty = self.db.ty(strukt.into());
let ty = self.insert_type_vars(ty.substitute(Interner, &substs));
@@ -1531,7 +1532,7 @@ impl<'a> InferenceContext<'a> {
ResolveValueResult::Partial(typens, unresolved, _) => (typens, Some(unresolved)),
}
} else {
- match ctx.resolve_path_in_type_ns(path, node) {
+ match path_ctx.resolve_path_in_type_ns() {
Some((it, idx)) => (it, idx),
None => return (self.err_ty(), None),
}
@@ -1542,21 +1543,21 @@ impl<'a> InferenceContext<'a> {
};
return match resolution {
TypeNs::AdtId(AdtId::StructId(strukt)) => {
- let substs = ctx.substs_from_path(path, strukt.into(), true);
+ let substs = path_ctx.substs_from_path(strukt.into(), true);
drop(ctx);
let ty = self.db.ty(strukt.into());
let ty = self.insert_type_vars(ty.substitute(Interner, &substs));
forbid_unresolved_segments((ty, Some(strukt.into())), unresolved)
}
TypeNs::AdtId(AdtId::UnionId(u)) => {
- let substs = ctx.substs_from_path(path, u.into(), true);
+ let substs = path_ctx.substs_from_path(u.into(), true);
drop(ctx);
let ty = self.db.ty(u.into());
let ty = self.insert_type_vars(ty.substitute(Interner, &substs));
forbid_unresolved_segments((ty, Some(u.into())), unresolved)
}
TypeNs::EnumVariantId(var) => {
- let substs = ctx.substs_from_path(path, var.into(), true);
+ let substs = path_ctx.substs_from_path(var.into(), true);
drop(ctx);
let ty = self.db.ty(var.lookup(self.db.upcast()).parent.into());
let ty = self.insert_type_vars(ty.substitute(Interner, &substs));
@@ -1567,31 +1568,32 @@ impl<'a> InferenceContext<'a> {
let substs = generics.placeholder_subst(self.db);
let mut ty = self.db.impl_self_ty(impl_id).substitute(Interner, &substs);
- let Some(mut remaining_idx) = unresolved else {
+ let Some(remaining_idx) = unresolved else {
drop(ctx);
return self.resolve_variant_on_alias(ty, None, mod_path);
};
let mut remaining_segments = path.segments().skip(remaining_idx);
+ if remaining_segments.len() >= 2 {
+ path_ctx.ignore_last_segment();
+ }
+
// We need to try resolving unresolved segments one by one because each may resolve
// to a projection, which `TyLoweringContext` cannot handle on its own.
let mut tried_resolving_once = false;
- while !remaining_segments.is_empty() {
- let resolved_segment = path.segments().get(remaining_idx - 1).unwrap();
- let current_segment = remaining_segments.take(1);
-
+ while let Some(current_segment) = remaining_segments.first() {
// If we can resolve to an enum variant, it takes priority over associated type
// of the same name.
if let Some((AdtId::EnumId(id), _)) = ty.as_adt() {
let enum_data = self.db.enum_data(id);
- let name = current_segment.first().unwrap().name;
- if let Some(variant) = enum_data.variant(name) {
+ if let Some(variant) = enum_data.variant(current_segment.name) {
return if remaining_segments.len() == 1 {
(ty, Some(variant.into()))
} else {
// We still have unresolved paths, but enum variants never have
// associated types!
+ // FIXME: Report an error.
(self.err_ty(), None)
};
}
@@ -1600,23 +1602,13 @@ impl<'a> InferenceContext<'a> {
if tried_resolving_once {
// FIXME: with `inherent_associated_types` this is allowed, but our `lower_partly_resolved_path()`
// will need to be updated to err at the correct segment.
- //
- // We need to stop here because otherwise the segment index passed to `lower_partly_resolved_path()`
- // will be incorrect, and that can mess up error reporting.
break;
}
// `lower_partly_resolved_path()` returns `None` as type namespace unless
// `remaining_segments` is empty, which is never the case here. We don't know
// which namespace the new `ty` is in until normalized anyway.
- (ty, _) = ctx.lower_partly_resolved_path(
- node,
- resolution,
- resolved_segment,
- current_segment,
- (remaining_idx - 1) as u32,
- false,
- );
+ (ty, _) = path_ctx.lower_partly_resolved_path(resolution, false);
tried_resolving_once = true;
ty = self.table.insert_type_vars(ty);
@@ -1626,8 +1618,6 @@ impl<'a> InferenceContext<'a> {
return (self.err_ty(), None);
}
- // FIXME(inherent_associated_types): update `resolution` based on `ty` here.
- remaining_idx += 1;
remaining_segments = remaining_segments.skip(1);
}
drop(ctx);
@@ -1643,12 +1633,7 @@ impl<'a> InferenceContext<'a> {
(ty, variant)
}
TypeNs::TypeAliasId(it) => {
- let resolved_seg = match unresolved {
- None => path.segments().last().unwrap(),
- Some(n) => path.segments().get(path.segments().len() - n - 1).unwrap(),
- };
- let substs =
- ctx.substs_from_path_segment(resolved_seg, Some(it.into()), true, None);
+ let substs = path_ctx.substs_from_path_segment(it.into(), true, None);
drop(ctx);
let ty = self.db.ty(it.into());
let ty = self.insert_type_vars(ty.substitute(Interner, &substs));
diff --git a/crates/hir-ty/src/infer/diagnostics.rs b/crates/hir-ty/src/infer/diagnostics.rs
index b85378531a..8c0446953a 100644
--- a/crates/hir-ty/src/infer/diagnostics.rs
+++ b/crates/hir-ty/src/infer/diagnostics.rs
@@ -5,16 +5,13 @@
use std::cell::RefCell;
use std::ops::{Deref, DerefMut};
-use hir_def::expr_store::HygieneId;
-use hir_def::hir::ExprOrPatId;
-use hir_def::path::{Path, PathSegment, PathSegments};
-use hir_def::resolver::{ResolveValueResult, Resolver, TypeNs};
-use hir_def::type_ref::TypesMap;
-use hir_def::TypeOwnerId;
+use either::Either;
+use hir_def::{hir::ExprOrPatId, path::Path, resolver::Resolver, type_ref::TypesMap, TypeOwnerId};
-use crate::db::HirDatabase;
use crate::{
- InferenceDiagnostic, InferenceTyDiagnosticSource, Ty, TyLoweringContext, TyLoweringDiagnostic,
+ db::HirDatabase,
+ lower::path::{PathDiagnosticCallback, PathLoweringContext},
+ InferenceDiagnostic, InferenceTyDiagnosticSource, TyLoweringContext, TyLoweringDiagnostic,
};
// Unfortunately, this struct needs to use interior mutability (but we encapsulate it)
@@ -44,6 +41,11 @@ impl Diagnostics {
}
}
+pub(crate) struct PathDiagnosticCallbackData<'a> {
+ node: ExprOrPatId,
+ diagnostics: &'a Diagnostics,
+}
+
pub(super) struct InferenceTyLoweringContext<'a> {
ctx: TyLoweringContext<'a>,
diagnostics: &'a Diagnostics,
@@ -51,6 +53,7 @@ pub(super) struct InferenceTyLoweringContext<'a> {
}
impl<'a> InferenceTyLoweringContext<'a> {
+ #[inline]
pub(super) fn new(
db: &'a dyn HirDatabase,
resolver: &'a Resolver,
@@ -62,65 +65,42 @@ impl<'a> InferenceTyLoweringContext<'a> {
Self { ctx: TyLoweringContext::new(db, resolver, types_map, owner), diagnostics, source }
}
- pub(super) fn resolve_path_in_type_ns(
- &mut self,
- path: &Path,
- node: ExprOrPatId,
- ) -> Option<(TypeNs, Option<usize>)> {
- let diagnostics = self.diagnostics;
- self.ctx.resolve_path_in_type_ns(path, &mut |_, diag| {
- diagnostics.push(InferenceDiagnostic::PathDiagnostic { node, diag })
- })
- }
-
- pub(super) fn resolve_path_in_value_ns(
- &mut self,
- path: &Path,
- node: ExprOrPatId,
- hygiene_id: HygieneId,
- ) -> Option<ResolveValueResult> {
- let diagnostics = self.diagnostics;
- self.ctx.resolve_path_in_value_ns(path, hygiene_id, &mut |_, diag| {
- diagnostics.push(InferenceDiagnostic::PathDiagnostic { node, diag })
- })
- }
-
- pub(super) fn lower_partly_resolved_path(
- &mut self,
+ #[inline]
+ pub(super) fn at_path<'b>(
+ &'b mut self,
+ path: &'b Path,
node: ExprOrPatId,
- resolution: TypeNs,
- resolved_segment: PathSegment<'_>,
- remaining_segments: PathSegments<'_>,
- resolved_segment_idx: u32,
- infer_args: bool,
- ) -> (Ty, Option<TypeNs>) {
- let diagnostics = self.diagnostics;
- self.ctx.lower_partly_resolved_path(
- resolution,
- resolved_segment,
- remaining_segments,
- resolved_segment_idx,
- infer_args,
- &mut |_, diag| diagnostics.push(InferenceDiagnostic::PathDiagnostic { node, diag }),
- )
+ ) -> PathLoweringContext<'b, 'a> {
+ let on_diagnostic = PathDiagnosticCallback {
+ data: Either::Right(PathDiagnosticCallbackData { diagnostics: self.diagnostics, node }),
+ callback: |data, _, diag| {
+ let data = data.as_ref().right().unwrap();
+ data.diagnostics
+ .push(InferenceDiagnostic::PathDiagnostic { node: data.node, diag });
+ },
+ };
+ PathLoweringContext::new(&mut self.ctx, on_diagnostic, path)
}
}
impl<'a> Deref for InferenceTyLoweringContext<'a> {
type Target = TyLoweringContext<'a>;
+ #[inline]
fn deref(&self) -> &Self::Target {
&self.ctx
}
}
impl DerefMut for InferenceTyLoweringContext<'_> {
+ #[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.ctx
}
}
impl Drop for InferenceTyLoweringContext<'_> {
+ #[inline]
fn drop(&mut self) {
self.diagnostics
.push_ty_diagnostics(self.source, std::mem::take(&mut self.ctx.diagnostics));
diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs
index bff8814347..80e3ca1fa2 100644
--- a/crates/hir-ty/src/infer/expr.rs
+++ b/crates/hir-ty/src/infer/expr.rs
@@ -2201,8 +2201,8 @@ impl InferenceContext<'_> {
for kind_id in def_generics.iter_self_id().take(self_params) {
let arg = args.peek();
let arg = match (kind_id, arg) {
- // Lifetimes can be elided.
- // Once we have implemented lifetime elision correctly,
+ // Lifetimes can be inferred.
+ // Once we have implemented lifetime inference correctly,
// this should be handled in a proper way.
(
GenericParamId::LifetimeParamId(_),
diff --git a/crates/hir-ty/src/infer/pat.rs b/crates/hir-ty/src/infer/pat.rs
index 5ff22bea34..d0fe00eceb 100644
--- a/crates/hir-ty/src/infer/pat.rs
+++ b/crates/hir-ty/src/infer/pat.rs
@@ -564,9 +564,17 @@ impl InferenceContext<'_> {
| Pat::Range { .. }
| Pat::Slice { .. } => true,
Pat::Or(pats) => pats.iter().all(|p| self.is_non_ref_pat(body, *p)),
- Pat::Path(p) => {
- let v = self.resolve_value_path_inner(p, pat.into());
- v.is_some_and(|x| !matches!(x.0, hir_def::resolver::ValueNs::ConstId(_)))
+ Pat::Path(path) => {
+ // A const is a reference pattern, but other value ns things aren't (see #16131). We don't need more than
+ // the hir-def resolver for this, because if there are segments left, this can only be an (associated) const.
+ //
+ // Do not use `TyLoweringContext`'s resolution, we want to ignore errors here (they'll be reported elsewhere).
+ let resolution = self.resolver.resolve_path_in_value_ns_fully(
+ self.db.upcast(),
+ path,
+ body.pat_path_hygiene(pat),
+ );
+ resolution.is_some_and(|it| !matches!(it, hir_def::resolver::ValueNs::ConstId(_)))
}
Pat::ConstBlock(..) => false,
Pat::Lit(expr) => !matches!(
diff --git a/crates/hir-ty/src/infer/path.rs b/crates/hir-ty/src/infer/path.rs
index 36ec60a7a2..3794912ee9 100644
--- a/crates/hir-ty/src/infer/path.rs
+++ b/crates/hir-ty/src/infer/path.rs
@@ -7,7 +7,6 @@ use hir_def::{
AdtId, AssocItemId, GenericDefId, ItemContainerId, Lookup,
};
use hir_expand::name::Name;
-use intern::sym;
use stdx::never;
use crate::{
@@ -94,7 +93,14 @@ impl InferenceContext<'_> {
return Some(ValuePathResolution::NonGeneric(ty));
};
- let substs = self.with_body_ty_lowering(|ctx| ctx.substs_from_path(path, value_def, true));
+ let substs = self.with_body_ty_lowering(|ctx| {
+ let mut path_ctx = ctx.at_path(path, id);
+ let last_segment = path.segments().len().checked_sub(1);
+ if let Some(last_segment) = last_segment {
+ path_ctx.set_current_segment(last_segment)
+ }
+ path_ctx.substs_from_path(value_def, true)
+ });
let substs = substs.as_slice(Interner);
if let ValueNs::EnumVariantId(_) = value {
@@ -156,15 +162,16 @@ impl InferenceContext<'_> {
&self.diagnostics,
InferenceTyDiagnosticSource::Body,
);
+ let mut path_ctx = ctx.at_path(path, id);
let (value, self_subst) = if let Some(type_ref) = path.type_anchor() {
let last = path.segments().last()?;
- let (ty, orig_ns) = ctx.lower_ty_ext(type_ref);
+ let (ty, orig_ns) = path_ctx.ty_ctx().lower_ty_ext(type_ref);
let ty = self.table.insert_type_vars(ty);
let ty = self.table.normalize_associated_types_in(ty);
- let remaining_segments_for_ty = path.segments().take(path.segments().len() - 1);
- let (ty, _) = ctx.lower_ty_relative_path(ty, orig_ns, remaining_segments_for_ty);
+ path_ctx.ignore_last_segment();
+ let (ty, _) = path_ctx.lower_ty_relative_path(ty, orig_ns);
drop(ctx);
let ty = self.table.insert_type_vars(ty);
let ty = self.table.normalize_associated_types_in(ty);
@@ -172,14 +179,52 @@ impl InferenceContext<'_> {
} else {
let hygiene = self.body.expr_or_pat_path_hygiene(id);
// FIXME: report error, unresolved first path segment
- let value_or_partial = ctx.resolve_path_in_value_ns(path, id, hygiene)?;
- drop(ctx);
+ let value_or_partial = path_ctx.resolve_path_in_value_ns(hygiene)?;
match value_or_partial {
- ResolveValueResult::ValueNs(it, _) => (it, None),
- ResolveValueResult::Partial(def, remaining_index, _) => self
- .resolve_assoc_item(id, def, path, remaining_index, id)
- .map(|(it, substs)| (it, Some(substs)))?,
+ ResolveValueResult::ValueNs(it, _) => {
+ drop(ctx);
+ (it, None)
+ }
+ ResolveValueResult::Partial(def, remaining_index, _) => {
+ // there may be more intermediate segments between the resolved one and
+ // the end. Only the last segment needs to be resolved to a value; from
+ // the segments before that, we need to get either a type or a trait ref.
+
+ let remaining_segments = path.segments().skip(remaining_index);
+ let is_before_last = remaining_segments.len() == 1;
+ let last_segment = remaining_segments
+ .last()
+ .expect("there should be at least one segment here");
+
+ let (resolution, substs) = match (def, is_before_last) {
+ (TypeNs::TraitId(trait_), true) => {
+ let self_ty = self.table.new_type_var();
+ let trait_ref =
+ path_ctx.lower_trait_ref_from_resolved_path(trait_, self_ty);
+ drop(ctx);
+ self.resolve_trait_assoc_item(trait_ref, last_segment, id)
+ }
+ (def, _) => {
+ // Either we already have a type (e.g. `Vec::new`), or we have a
+ // trait but it's not the last segment, so the next segment
+ // should resolve to an associated type of that trait (e.g. `<T
+ // as Iterator>::Item::default`)
+ path_ctx.ignore_last_segment();
+ let (ty, _) = path_ctx.lower_partly_resolved_path(def, true);
+ drop(ctx);
+ if ty.is_unknown() {
+ return None;
+ }
+
+ let ty = self.insert_type_vars(ty);
+ let ty = self.normalize_associated_types_in(ty);
+
+ self.resolve_ty_assoc_item(ty, last_segment.name, id)
+ }
+ }?;
+ (resolution, Some(substs))
+ }
}
};
Some((value, self_subst))
@@ -212,89 +257,6 @@ impl InferenceContext<'_> {
}
}
- fn resolve_assoc_item(
- &mut self,
- node: ExprOrPatId,
- def: TypeNs,
- path: &Path,
- remaining_index: usize,
- id: ExprOrPatId,
- ) -> Option<(ValueNs, Substitution)> {
- // there may be more intermediate segments between the resolved one and
- // the end. Only the last segment needs to be resolved to a value; from
- // the segments before that, we need to get either a type or a trait ref.
-
- let _d;
- let (resolved_segment, remaining_segments) = match path {
- Path::Normal { .. } | Path::BarePath(_) => {
- assert!(remaining_index < path.segments().len());
- (
- path.segments().get(remaining_index - 1).unwrap(),
- path.segments().skip(remaining_index),
- )
- }
- Path::LangItem(..) => (
- PathSegment {
- name: {
- _d = Name::new_symbol_root(sym::Unknown.clone());
- &_d
- },
- args_and_bindings: None,
- },
- path.segments(),
- ),
- };
- let is_before_last = remaining_segments.len() == 1;
-
- match (def, is_before_last) {
- (TypeNs::TraitId(trait_), true) => {
- let segment =
- remaining_segments.last().expect("there should be at least one segment here");
- let self_ty = self.table.new_type_var();
- let trait_ref = self.with_body_ty_lowering(|ctx| {
- ctx.lower_trait_ref_from_resolved_path(trait_, resolved_segment, self_ty)
- });
- self.resolve_trait_assoc_item(trait_ref, segment, id)
- }
- (def, _) => {
- // Either we already have a type (e.g. `Vec::new`), or we have a
- // trait but it's not the last segment, so the next segment
- // should resolve to an associated type of that trait (e.g. `<T
- // as Iterator>::Item::default`)
- let remaining_segments_for_ty =
- remaining_segments.take(remaining_segments.len() - 1);
- let mut ctx = TyLoweringContext::new(
- self.db,
- &self.resolver,
- &self.body.types,
- self.owner.into(),
- &self.diagnostics,
- InferenceTyDiagnosticSource::Body,
- );
- let (ty, _) = ctx.lower_partly_resolved_path(
- node,
- def,
- resolved_segment,
- remaining_segments_for_ty,
- (remaining_index - 1) as u32,
- true,
- );
- drop(ctx);
- if ty.is_unknown() {
- return None;
- }
-
- let ty = self.insert_type_vars(ty);
- let ty = self.normalize_associated_types_in(ty);
-
- let segment =
- remaining_segments.last().expect("there should be at least one segment here");
-
- self.resolve_ty_assoc_item(ty, segment.name, id)
- }
- }
- }
-
fn resolve_trait_assoc_item(
&mut self,
trait_ref: TraitRef,
diff --git a/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs
index db13e1fd35..af73b5ed9a 100644
--- a/crates/hir-ty/src/lower.rs
+++ b/crates/hir-ty/src/lower.rs
@@ -6,6 +6,7 @@
//!
//! This usually involves resolving names, collecting generic arguments etc.
pub(crate) mod diagnostics;
+pub(crate) mod path;
use std::{
cell::OnceCell,
@@ -26,29 +27,26 @@ use hir_def::{
builtin_type::BuiltinType,
data::{adt::StructKind, TraitFlags},
expander::Expander,
- expr_store::HygieneId,
generics::{
GenericParamDataRef, TypeOrConstParamData, TypeParamProvenance, WherePredicate,
WherePredicateTypeTarget,
},
lang_item::LangItem,
nameres::MacroSubNs,
- path::{GenericArg, GenericArgs, ModPath, Path, PathKind, PathSegment, PathSegments},
- resolver::{HasResolver, LifetimeNs, ResolveValueResult, Resolver, TypeNs, ValueNs},
+ path::{GenericArg, ModPath, Path, PathKind},
+ resolver::{HasResolver, LifetimeNs, Resolver, TypeNs},
type_ref::{
ConstRef, LifetimeRef, PathId, TraitBoundModifier, TraitRef as HirTraitRef, TypeBound,
TypeRef, TypeRefId, TypesMap, TypesSourceMap,
},
AdtId, AssocItemId, CallableDefId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId,
- FunctionId, GenericDefId, GenericParamId, HasModule, ImplId, InTypeConstLoc, ItemContainerId,
- LocalFieldId, Lookup, StaticId, StructId, TraitId, TypeAliasId, TypeOrConstParamId,
- TypeOwnerId, UnionId, VariantId,
+ FunctionId, GenericDefId, GenericParamId, HasModule, ImplId, InTypeConstLoc, LocalFieldId,
+ Lookup, StaticId, StructId, TypeAliasId, TypeOrConstParamId, TypeOwnerId, UnionId, VariantId,
};
use hir_expand::{name::Name, ExpandResult};
use la_arena::{Arena, ArenaMap};
use rustc_hash::FxHashSet;
use rustc_pattern_analysis::Captures;
-use smallvec::SmallVec;
use stdx::{impl_from, never};
use syntax::ast;
use triomphe::{Arc, ThinArc};
@@ -62,18 +60,19 @@ use crate::{
db::HirDatabase,
error_lifetime,
generics::{generics, trait_self_param_idx, Generics},
- lower::diagnostics::*,
+ lower::{
+ diagnostics::*,
+ path::{PathDiagnosticCallback, PathLoweringContext},
+ },
make_binders,
mapping::{from_chalk_trait_id, lt_to_placeholder_idx, ToChalk},
- static_lifetime, to_assoc_type_id, to_chalk_trait_id, to_placeholder_idx,
- utils::{
- all_super_trait_refs, associated_type_by_name_including_super_traits, InTypeConstIdMetadata,
- },
- AliasEq, AliasTy, Binders, BoundVar, CallableSig, Const, ConstScalar, DebruijnIndex, DynTy,
- FnAbi, FnPointer, FnSig, FnSubst, ImplTrait, ImplTraitId, ImplTraits, Interner, Lifetime,
- LifetimeData, LifetimeOutlives, ParamKind, PolyFnSig, ProgramClause, ProjectionTy,
- QuantifiedWhereClause, QuantifiedWhereClauses, Substitution, TraitEnvironment, TraitRef,
- TraitRefExt, Ty, TyBuilder, TyKind, WhereClause,
+ static_lifetime, to_chalk_trait_id, to_placeholder_idx,
+ utils::{all_super_trait_refs, InTypeConstIdMetadata},
+ AliasTy, Binders, BoundVar, CallableSig, Const, ConstScalar, DebruijnIndex, DynTy, FnAbi,
+ FnPointer, FnSig, FnSubst, ImplTrait, ImplTraitId, ImplTraits, Interner, Lifetime,
+ LifetimeData, LifetimeOutlives, ParamKind, PolyFnSig, ProgramClause, QuantifiedWhereClause,
+ QuantifiedWhereClauses, Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder,
+ TyKind, WhereClause,
};
#[derive(Debug, Default)]
@@ -106,6 +105,8 @@ impl ImplTraitLoweringState {
}
}
+pub(crate) struct PathDiagnosticCallbackData(TypeRefId);
+
#[derive(Debug)]
pub struct TyLoweringContext<'a> {
pub db: &'a dyn HirDatabase,
@@ -527,9 +528,8 @@ impl<'a> TyLoweringContext<'a> {
if path.segments().len() > 1 {
return None;
}
- let resolution = match self
- .resolve_path_in_type_ns(path, &mut Self::on_path_diagnostic_callback(type_ref_id))
- {
+ let mut ctx = self.at_path(PathId::from_type_ref_unchecked(type_ref_id));
+ let resolution = match ctx.resolve_path_in_type_ns() {
Some((it, None)) => it,
_ => return None,
};
@@ -539,409 +539,36 @@ impl<'a> TyLoweringContext<'a> {
}
}
- pub(crate) fn lower_ty_relative_path(
- &mut self,
- ty: Ty,
- // We need the original resolution to lower `Self::AssocTy` correctly
- res: Option<TypeNs>,
- remaining_segments: PathSegments<'_>,
- ) -> (Ty, Option<TypeNs>) {
- match remaining_segments.len() {
- 0 => (ty, res),
- 1 => {
- // resolve unselected assoc types
- let segment = remaining_segments.first().unwrap();
- (self.select_associated_type(res, segment), None)
- }
- _ => {
- // FIXME report error (ambiguous associated type)
- (TyKind::Error.intern(Interner), None)
- }
- }
- }
-
- pub(crate) fn lower_partly_resolved_path(
- &mut self,
- resolution: TypeNs,
- resolved_segment: PathSegment<'_>,
- remaining_segments: PathSegments<'_>,
- _resolved_segment_idx: u32,
- infer_args: bool,
- _on_diagnostic: &mut dyn FnMut(&mut Self, PathLoweringDiagnostic),
- ) -> (Ty, Option<TypeNs>) {
- 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_,
- resolved_segment,
- TyKind::Error.intern(Interner),
- );
- let segment = remaining_segments.first().unwrap();
- let found = self
- .db
- .trait_data(trait_ref.hir_trait_id())
- .associated_type_by_name(segment.name);
-
- 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(
- segment,
- Some(associated_ty.into()),
- false,
- None,
- );
- let len_self =
- generics(self.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)),
- );
- TyKind::Alias(AliasTy::Projection(ProjectionTy {
- associated_ty_id: to_assoc_type_id(associated_ty),
- substitution,
- }))
- .intern(Interner)
- }
- None => {
- // FIXME: report error (associated type not found)
- TyKind::Error.intern(Interner)
- }
- }
- }
- 0 => {
- // 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)
- }
- _ => {
- // FIXME report error (ambiguous associated type)
- TyKind::Error.intern(Interner)
- }
- };
- return (ty, None);
- }
- TypeNs::TraitAliasId(_) => {
- // FIXME(trait_alias): Implement trait alias.
- return (TyKind::Error.intern(Interner), None);
- }
- TypeNs::GenericParam(param_id) => match self.type_param_mode {
- ParamLoweringMode::Placeholder => {
- TyKind::Placeholder(to_placeholder_idx(self.db, param_id.into()))
- }
- ParamLoweringMode::Variable => {
- let idx = match self
- .generics()
- .expect("generics in scope")
- .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.in_binders, idx))
- }
- }
- .intern(Interner),
- TypeNs::SelfType(impl_id) => {
- let generics = self.generics().expect("impl should have generic param scope");
-
- match self.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 subst = generics.placeholder_subst(self.db);
- self.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.db, impl_id)
- .fill_with_bound_vars(self.in_binders, starting_from)
- .build()
- }
- }
- }
- TypeNs::AdtSelfType(adt) => {
- let generics = generics(self.db.upcast(), adt.into());
- let substs = match self.type_param_mode {
- ParamLoweringMode::Placeholder => generics.placeholder_subst(self.db),
- ParamLoweringMode::Variable => {
- generics.bound_vars_subst(self.db, self.in_binders)
- }
- };
- self.db.ty(adt.into()).substitute(Interner, &substs)
- }
-
- TypeNs::AdtId(it) => self.lower_path_inner(resolved_segment, it.into(), infer_args),
- TypeNs::BuiltinType(it) => {
- self.lower_path_inner(resolved_segment, it.into(), infer_args)
- }
- TypeNs::TypeAliasId(it) => {
- self.lower_path_inner(resolved_segment, it.into(), infer_args)
- }
- // FIXME: report error
- TypeNs::EnumVariantId(_) => return (TyKind::Error.intern(Interner), None),
- };
- self.lower_ty_relative_path(ty, Some(resolution), remaining_segments)
- }
-
- fn handle_type_ns_resolution(
- &mut self,
- resolution: &TypeNs,
- resolved_segment: PathSegment<'_>,
- resolved_segment_idx: usize,
- on_diagnostic: &mut dyn FnMut(&mut Self, PathLoweringDiagnostic),
- ) {
- let mut prohibit_generics_on_resolved = |reason| {
- if resolved_segment.args_and_bindings.is_some() {
- on_diagnostic(
- self,
- PathLoweringDiagnostic::GenericArgsProhibited {
- segment: resolved_segment_idx as u32,
- 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::AdtId(_)
- | TypeNs::EnumVariantId(_)
- | TypeNs::TypeAliasId(_)
- | TypeNs::TraitId(_)
- | TypeNs::TraitAliasId(_) => {}
- }
- }
-
- pub(crate) fn resolve_path_in_type_ns_fully(
- &mut self,
- path: &Path,
- on_diagnostic: &mut dyn FnMut(&mut Self, PathLoweringDiagnostic),
- ) -> Option<TypeNs> {
- let (res, unresolved) = self.resolve_path_in_type_ns(path, on_diagnostic)?;
- if unresolved.is_some() {
- return None;
- }
- Some(res)
- }
-
- pub(crate) fn resolve_path_in_type_ns(
- &mut self,
- path: &Path,
- on_diagnostic: &mut dyn FnMut(&mut Self, PathLoweringDiagnostic),
- ) -> Option<(TypeNs, Option<usize>)> {
- let (resolution, remaining_index, _, prefix_info) =
- self.resolver.resolve_path_in_type_ns_with_prefix_info(self.db.upcast(), path)?;
- let segments = path.segments();
-
- match path {
- // `segments.is_empty()` can occur with `self`.
- Path::Normal(..) if !segments.is_empty() => (),
- _ => 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),
- };
-
- for (i, mod_segment) in module_segments.iter().enumerate() {
- if mod_segment.args_and_bindings.is_some() {
- on_diagnostic(
- self,
- PathLoweringDiagnostic::GenericArgsProhibited {
- segment: i as u32,
- reason: GenericArgsProhibitedReason::Module,
- },
- );
- }
- }
-
- if let Some(enum_segment) = enum_segment {
- if 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())
- {
- on_diagnostic(
- self,
- PathLoweringDiagnostic::GenericArgsProhibited {
- segment: (enum_segment + 1) as u32,
- reason: GenericArgsProhibitedReason::EnumVariant,
- },
- );
- }
- }
-
- self.handle_type_ns_resolution(
- &resolution,
- segments.get(resolved_segment_idx).expect("should have resolved segment"),
- resolved_segment_idx,
- on_diagnostic,
- );
-
- Some((resolution, remaining_index))
- }
-
- pub(crate) fn resolve_path_in_value_ns(
- &mut self,
- path: &Path,
- hygiene_id: HygieneId,
- on_diagnostic: &mut dyn FnMut(&mut Self, PathLoweringDiagnostic),
- ) -> Option<ResolveValueResult> {
- let (res, prefix_info) = self.resolver.resolve_path_in_value_ns_with_prefix_info(
- self.db.upcast(),
- path,
- hygiene_id,
- )?;
-
- let segments = path.segments();
- match path {
- // `segments.is_empty()` can occur with `self`.
- Path::Normal(..) if !segments.is_empty() => (),
- _ => return Some(res),
- };
-
- let (mod_segments, enum_segment) = match res {
- ResolveValueResult::Partial(_, unresolved_segment, _) => {
- (segments.take(unresolved_segment - 1), None)
- }
- ResolveValueResult::ValueNs(ValueNs::EnumVariantId(_), _)
- if prefix_info.enum_variant =>
- {
- (segments.strip_last_two(), segments.len().checked_sub(2))
- }
- ResolveValueResult::ValueNs(..) => (segments.strip_last(), None),
- };
- for (i, mod_segment) in mod_segments.iter().enumerate() {
- if mod_segment.args_and_bindings.is_some() {
- on_diagnostic(
- self,
- PathLoweringDiagnostic::GenericArgsProhibited {
- segment: i as u32,
- reason: GenericArgsProhibitedReason::Module,
- },
- );
- }
- }
-
- if let Some(enum_segment) = enum_segment {
- if 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())
- {
- on_diagnostic(
- self,
- PathLoweringDiagnostic::GenericArgsProhibited {
- segment: (enum_segment + 1) as u32,
- reason: GenericArgsProhibitedReason::EnumVariant,
- },
- );
- }
+ #[inline]
+ fn on_path_diagnostic_callback(type_ref: TypeRefId) -> PathDiagnosticCallback<'static> {
+ PathDiagnosticCallback {
+ data: Either::Left(PathDiagnosticCallbackData(type_ref)),
+ callback: |data, this, diag| {
+ let type_ref = data.as_ref().left().unwrap().0;
+ this.push_diagnostic(type_ref, TyLoweringDiagnosticKind::PathDiagnostic(diag))
+ },
}
-
- match &res {
- ResolveValueResult::ValueNs(resolution, _) => {
- let resolved_segment_idx =
- segments.len().checked_sub(1).unwrap_or_else(|| panic!("{path:?}"));
- let resolved_segment = segments.last().unwrap();
-
- let mut prohibit_generics_on_resolved = |reason| {
- if resolved_segment.args_and_bindings.is_some() {
- on_diagnostic(
- self,
- PathLoweringDiagnostic::GenericArgsProhibited {
- segment: resolved_segment_idx as u32,
- 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, unresolved_idx, _) => {
- let resolved_segment_idx = unresolved_idx - 1;
- let resolved_segment = segments.get(resolved_segment_idx).unwrap();
- self.handle_type_ns_resolution(
- resolution,
- resolved_segment,
- resolved_segment_idx,
- on_diagnostic,
- );
- }
- };
- Some(res)
}
- fn on_path_diagnostic_callback(
- type_ref: TypeRefId,
- ) -> impl FnMut(&mut Self, PathLoweringDiagnostic) {
- move |this, diag| {
- this.push_diagnostic(type_ref, TyLoweringDiagnosticKind::PathDiagnostic(diag))
- }
+ #[inline]
+ fn at_path(&mut self, path_id: PathId) -> PathLoweringContext<'_, 'a> {
+ PathLoweringContext::new(
+ self,
+ Self::on_path_diagnostic_callback(path_id.type_ref()),
+ &self.types_map[path_id],
+ )
}
pub(crate) fn lower_path(&mut self, path: &Path, path_id: PathId) -> (Ty, Option<TypeNs>) {
// Resolve the path (in type namespace)
if let Some(type_ref) = path.type_anchor() {
let (ty, res) = self.lower_ty_ext(type_ref);
- return self.lower_ty_relative_path(ty, res, path.segments());
+ let mut ctx = self.at_path(path_id);
+ return ctx.lower_ty_relative_path(ty, res);
}
- let (resolution, remaining_index) = match self.resolve_path_in_type_ns(
- path,
- &mut Self::on_path_diagnostic_callback(path_id.type_ref()),
- ) {
+ let mut ctx = self.at_path(path_id);
+ let (resolution, remaining_index) = match ctx.resolve_path_in_type_ns() {
Some(it) => it,
None => return (TyKind::Error.intern(Interner), None),
};
@@ -953,354 +580,21 @@ impl<'a> TyLoweringContext<'a> {
return (ty, None);
}
- let (resolved_segment_idx, resolved_segment, remaining_segments) = match remaining_index {
- None => (
- path.segments().len() - 1,
- path.segments().last().expect("resolved path has at least one element"),
- PathSegments::EMPTY,
- ),
- Some(i) => (i - 1, path.segments().get(i - 1).unwrap(), path.segments().skip(i)),
- };
-
- self.lower_partly_resolved_path(
- resolution,
- resolved_segment,
- remaining_segments,
- resolved_segment_idx as u32,
- false,
- &mut Self::on_path_diagnostic_callback(path_id.type_ref()),
- )
- }
-
- fn select_associated_type(&mut self, res: Option<TypeNs>, segment: PathSegment<'_>) -> Ty {
- let Some((generics, res)) = self.generics().zip(res) else {
- return TyKind::Error.intern(Interner);
- };
- let ty = named_associated_type_shorthand_candidates(
- self.db,
- generics.def(),
- res,
- Some(segment.name.clone()),
- move |name, t, associated_ty| {
- let generics = self.generics().unwrap();
-
- if name != segment.name {
- return None;
- }
-
- let parent_subst = t.substitution.clone();
- let parent_subst = match self.type_param_mode {
- ParamLoweringMode::Placeholder => {
- // if we're lowering to placeholders, we have to put them in now.
- let s = generics.placeholder_subst(self.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.in_binders)
- }
- };
-
- // 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(segment, Some(associated_ty.into()), false, None);
-
- let len_self =
- crate::generics::generics(self.db.upcast(), associated_ty.into()).len_self();
-
- let substs = Substitution::from_iter(
- Interner,
- substs.iter(Interner).take(len_self).chain(parent_subst.iter(Interner)),
- );
-
- Some(
- TyKind::Alias(AliasTy::Projection(ProjectionTy {
- associated_ty_id: to_assoc_type_id(associated_ty),
- substitution: substs,
- }))
- .intern(Interner),
- )
- },
- );
-
- ty.unwrap_or_else(|| TyKind::Error.intern(Interner))
- }
-
- fn lower_path_inner(
- &mut self,
- segment: PathSegment<'_>,
- typeable: TyDefId,
- infer_args: bool,
- ) -> Ty {
- let generic_def = match typeable {
- TyDefId::BuiltinType(_) => None,
- TyDefId::AdtId(it) => Some(it.into()),
- TyDefId::TypeAliasId(it) => Some(it.into()),
- };
- let substs = self.substs_from_path_segment(segment, generic_def, infer_args, None);
- self.db.ty(typeable).substitute(Interner, &substs)
- }
-
- /// Collect generic arguments from a path into a `Substs`. See also
- /// `create_substs_for_ast_path` and `def_to_ty` in rustc.
- pub(super) fn substs_from_path(
- &mut self,
- path: &Path,
- // 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,
- ) -> Substitution {
- let last = path.segments().last();
- let (segment, generic_def) = match resolved {
- ValueTyDefId::FunctionId(it) => (last, Some(it.into())),
- ValueTyDefId::StructId(it) => (last, Some(it.into())),
- ValueTyDefId::UnionId(it) => (last, Some(it.into())),
- ValueTyDefId::ConstId(it) => (last, Some(it.into())),
- ValueTyDefId::StaticId(_) => (last, None),
- 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
- // preferred). See also `def_ids_for_path_segments` in rustc.
- let len = path.segments().len();
- let penultimate = len.checked_sub(2).and_then(|idx| path.segments().get(idx));
- let segment = match penultimate {
- Some(segment) if segment.args_and_bindings.is_some() => Some(segment),
- _ => last,
- };
- (segment, Some(var.lookup(self.db.upcast()).parent.into()))
- }
- };
- if let Some(segment) = segment {
- self.substs_from_path_segment(segment, generic_def, infer_args, None)
- } else if let Some(generic_def) = generic_def {
- // lang item
- self.substs_from_args_and_bindings(None, Some(generic_def), infer_args, None)
- } else {
- Substitution::empty(Interner)
- }
- }
-
- pub(super) fn substs_from_path_segment(
- &mut self,
- segment: PathSegment<'_>,
- def: Option<GenericDefId>,
- infer_args: bool,
- explicit_self_ty: Option<Ty>,
- ) -> Substitution {
- self.substs_from_args_and_bindings(
- segment.args_and_bindings,
- def,
- infer_args,
- explicit_self_ty,
- )
- }
-
- fn substs_from_args_and_bindings(
- &mut self,
- args_and_bindings: Option<&GenericArgs>,
- def: Option<GenericDefId>,
- infer_args: bool,
- explicit_self_ty: Option<Ty>,
- ) -> Substitution {
- let Some(def) = def else { return Substitution::empty(Interner) };
-
- // Order is
- // - Optional Self parameter
- // - Lifetime parameters
- // - Type or Const parameters
- // - Parent parameters
- let def_generics = generics(self.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);
- }
- }
- };
- 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.lower_ty(*ty).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.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.db,
- id,
- arg,
- self,
- self.types_map,
- |this, type_ref| this.lower_ty(type_ref),
- |this, const_ref, ty| this.lower_const(const_ref, ty),
- |this, lifetime_ref| this.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.db.const_param_ty(x)),
- 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.db.upcast()).container, ItemContainerId::TraitId(_))
- }
- _ => false,
- };
- let fill_defaults = (!infer_args || had_explicit_args) && !is_assoc_ty();
- if fill_defaults {
- let defaults = &*self.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));
- }
- // 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)
- }
-
- pub(crate) fn lower_trait_ref_from_resolved_path(
- &mut self,
- resolved: TraitId,
- segment: PathSegment<'_>,
- explicit_self_ty: Ty,
- ) -> TraitRef {
- let substs = self.trait_ref_substs_from_path(segment, resolved, explicit_self_ty);
- TraitRef { trait_id: to_chalk_trait_id(resolved), substitution: substs }
- }
-
- fn prohibit_generics(
- &mut self,
- path_id: PathId,
- idx: u32,
- segments: PathSegments<'_>,
- reason: GenericArgsProhibitedReason,
- ) {
- segments.iter().zip(idx..).for_each(|(segment, idx)| {
- if segment.args_and_bindings.is_some() {
- self.push_diagnostic(
- path_id.type_ref(),
- TyLoweringDiagnosticKind::PathDiagnostic(
- PathLoweringDiagnostic::GenericArgsProhibited { segment: idx, reason },
- ),
- );
- }
- });
+ ctx.lower_partly_resolved_path(resolution, false)
}
fn lower_trait_ref_from_path(
&mut self,
path_id: PathId,
explicit_self_ty: Ty,
- ) -> Option<TraitRef> {
- let path = &self.types_map[path_id];
- let resolved = match self.resolve_path_in_type_ns_fully(
- path,
- &mut Self::on_path_diagnostic_callback(path_id.type_ref()),
- )? {
+ ) -> Option<(TraitRef, PathLoweringContext<'_, 'a>)> {
+ let mut ctx = self.at_path(path_id);
+ let resolved = match ctx.resolve_path_in_type_ns_fully()? {
// FIXME(trait_alias): We need to handle trait alias here.
TypeNs::TraitId(tr) => tr,
_ => return None,
};
- // Do this after we verify it's indeed a trait to not confuse the user if they're not modules.
- self.prohibit_generics(
- path_id,
- 0,
- path.segments().strip_last(),
- GenericArgsProhibitedReason::Module,
- );
- let segment = path.segments().last().expect("path should have at least one segment");
- Some(self.lower_trait_ref_from_resolved_path(resolved, segment, explicit_self_ty))
+ Some((ctx.lower_trait_ref_from_resolved_path(resolved, explicit_self_ty), ctx))
}
fn lower_trait_ref(
@@ -1308,16 +602,7 @@ impl<'a> TyLoweringContext<'a> {
trait_ref: &HirTraitRef,
explicit_self_ty: Ty,
) -> Option<TraitRef> {
- self.lower_trait_ref_from_path(trait_ref.path, explicit_self_ty)
- }
-
- fn trait_ref_substs_from_path(
- &mut self,
- segment: PathSegment<'_>,
- resolved: TraitId,
- explicit_self_ty: Ty,
- ) -> Substitution {
- self.substs_from_path_segment(segment, Some(resolved.into()), false, Some(explicit_self_ty))
+ self.lower_trait_ref_from_path(trait_ref.path, explicit_self_ty).map(|it| it.0)
}
pub(crate) fn lower_where_predicate<'b>(
@@ -1365,11 +650,18 @@ impl<'a> TyLoweringContext<'a> {
self_ty: Ty,
ignore_bindings: bool,
) -> impl Iterator<Item = QuantifiedWhereClause> + use<'b, 'a> {
- let mut trait_ref = None;
- let clause = match bound {
- &TypeBound::Path(path, TraitBoundModifier::None) => {
- trait_ref = self.lower_trait_ref_from_path(path, self_ty);
- trait_ref.clone().map(WhereClause::Implemented).map(crate::wrap_empty_binders)
+ let mut assoc_bounds = None;
+ let mut clause = None;
+ match bound {
+ &TypeBound::Path(path, TraitBoundModifier::None) | &TypeBound::ForLifetime(_, path) => {
+ // FIXME Don't silently drop the hrtb lifetimes here
+ if let Some((trait_ref, ctx)) = self.lower_trait_ref_from_path(path, self_ty) {
+ if !ignore_bindings {
+ assoc_bounds =
+ ctx.assoc_type_bindings_from_type_bound(bound, trait_ref.clone());
+ }
+ clause = Some(crate::wrap_empty_binders(WhereClause::Implemented(trait_ref)));
+ }
}
&TypeBound::Path(path, TraitBoundModifier::Maybe) => {
let sized_trait = self
@@ -1381,170 +673,21 @@ impl<'a> TyLoweringContext<'a> {
// If we got another trait here ignore the bound completely.
let trait_id = self
.lower_trait_ref_from_path(path, self_ty.clone())
- .map(|trait_ref| trait_ref.hir_trait_id());
+ .map(|(trait_ref, _)| trait_ref.hir_trait_id());
if trait_id == sized_trait {
self.unsized_types.insert(self_ty);
}
- None
- }
- &TypeBound::ForLifetime(_, path) => {
- // FIXME Don't silently drop the hrtb lifetimes here
- trait_ref = self.lower_trait_ref_from_path(path, self_ty);
- trait_ref.clone().map(WhereClause::Implemented).map(crate::wrap_empty_binders)
}
TypeBound::Lifetime(l) => {
let lifetime = self.lower_lifetime(l);
- Some(crate::wrap_empty_binders(WhereClause::TypeOutlives(TypeOutlives {
+ clause = Some(crate::wrap_empty_binders(WhereClause::TypeOutlives(TypeOutlives {
ty: self_ty,
lifetime,
- })))
+ })));
}
- TypeBound::Use(_) | TypeBound::Error => None,
- };
- clause.into_iter().chain(
- trait_ref
- .filter(move |_| !ignore_bindings)
- .map(move |tr| self.assoc_type_bindings_from_type_bound(bound, tr))
- .into_iter()
- .flatten(),
- )
- }
-
- fn assoc_type_bindings_from_type_bound<'b>(
- &'b mut self,
- bound: &'b TypeBound,
- trait_ref: TraitRef,
- ) -> impl Iterator<Item = QuantifiedWhereClause> + use<'b, 'a> {
- let last_segment = match bound {
- &TypeBound::Path(path, TraitBoundModifier::None) | &TypeBound::ForLifetime(_, path) => {
- self.types_map[path].segments().last()
- }
- TypeBound::Path(_, TraitBoundModifier::Maybe)
- | TypeBound::Use(_)
- | TypeBound::Error
- | TypeBound::Lifetime(_) => None,
- };
- last_segment
- .into_iter()
- .filter_map(|segment| segment.args_and_bindings)
- .flat_map(|args_and_bindings| args_and_bindings.bindings.iter())
- .flat_map(move |binding| {
- let found = associated_type_by_name_including_super_traits(
- self.db,
- trait_ref.clone(),
- &binding.name,
- );
- let (super_trait_ref, associated_ty) = match found {
- 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_path_segment(
- // FIXME: This is hack. We shouldn't really build `PathSegment` directly.
- PathSegment { name: &binding.name, args_and_bindings: binding.args.as_ref() },
- Some(associated_ty.into()),
- false, // this is not relevant
- Some(super_trait_ref.self_type_parameter(Interner)),
- );
- let self_params = generics(self.db.upcast(), associated_ty.into()).len_self();
- let substitution = Substitution::from_iter(
- Interner,
- substitution
- .iter(Interner)
- .take(self_params)
- .chain(super_trait_ref.substitution.iter(Interner)),
- );
- let projection_ty = ProjectionTy {
- associated_ty_id: to_assoc_type_id(associated_ty),
- substitution,
- };
- 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.types_map[type_ref], self.impl_trait_mode.mode) {
- (TypeRef::ImplTrait(_), ImplTraitLoweringMode::Disallowed) => (),
- (_, ImplTraitLoweringMode::Disallowed | ImplTraitLoweringMode::Opaque) => {
- let ty = self.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
- .resolver
- .where_predicates_in_scope()
- .find_map(|(p, _)| match p {
- WherePredicate::TypeBound {
- target: WherePredicateTypeTarget::TypeOrConstParam(idx),
- bound: b,
- } if b == bound => Some(idx),
- _ => None,
- });
- let ty = if let Some(target_param_idx) = target_param_idx {
- let mut counter = 0;
- let generics = self.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.db,
- self.resolver,
- self.types_map,
- self.types_source_map,
- self.owner,
- )
- .with_type_param_mode(self.type_param_mode);
- match self.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.diagnostics.extend(ext.diagnostics);
- ty
- } else {
- self.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.lower_type_bound(
- bound,
- TyKind::Alias(AliasTy::Projection(projection_ty.clone())).intern(Interner),
- false,
- ));
- }
- predicates
- })
+ TypeBound::Use(_) | TypeBound::Error => {}
+ }
+ clause.into_iter().chain(assoc_bounds.into_iter().flatten())
}
fn lower_dyn_trait(&mut self, bounds: &[TypeBound]) -> Ty {
diff --git a/crates/hir-ty/src/lower/diagnostics.rs b/crates/hir-ty/src/lower/diagnostics.rs
index 7fe196cdbb..5c77bcd073 100644
--- a/crates/hir-ty/src/lower/diagnostics.rs
+++ b/crates/hir-ty/src/lower/diagnostics.rs
@@ -26,11 +26,11 @@ pub enum GenericArgsProhibitedReason {
Static,
/// When there is a generic enum, within the expression `Enum::Variant`,
/// either `Enum` or `Variant` are allowed to have generic arguments, but not both.
- // FIXME: This is not used now but it should be.
EnumVariant,
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum PathLoweringDiagnostic {
GenericArgsProhibited { segment: u32, reason: GenericArgsProhibitedReason },
+ ParenthesizedGenericArgsWithoutFnTrait { segment: u32 },
}
diff --git a/crates/hir-ty/src/lower/path.rs b/crates/hir-ty/src/lower/path.rs
new file mode 100644
index 0000000000..22c5bb9923
--- /dev/null
+++ b/crates/hir-ty/src/lower/path.rs
@@ -0,0 +1,911 @@
+//! A wrapper around [`TyLoweringContext`] specifically for lowering paths.
+
+use std::iter;
+
+use chalk_ir::{cast::Cast, fold::Shift, BoundVar};
+use either::Either;
+use hir_def::{
+ data::TraitFlags,
+ expr_store::HygieneId,
+ generics::{TypeParamProvenance, WherePredicate, WherePredicateTypeTarget},
+ path::{GenericArg, GenericArgs, Path, PathSegment, PathSegments},
+ resolver::{ResolveValueResult, TypeNs, ValueNs},
+ type_ref::{TypeBound, TypeRef},
+ GenericDefId, GenericParamId, ItemContainerId, Lookup, TraitId,
+};
+use smallvec::SmallVec;
+use stdx::never;
+
+use crate::{
+ consteval::unknown_const_as_generic,
+ 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,
+ 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<
+ super::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> {
+ pub(crate) data: CallbackData<'a>,
+ pub(crate) callback: fn(&CallbackData<'_>, &mut TyLoweringContext<'_>, PathLoweringDiagnostic),
+}
+
+pub(crate) struct PathLoweringContext<'a, 'b> {
+ ctx: &'a mut TyLoweringContext<'b>,
+ on_diagnostic: PathDiagnosticCallback<'a>,
+ 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> PathLoweringContext<'a, 'b> {
+ #[inline]
+ pub(crate) fn new(
+ ctx: &'a mut TyLoweringContext<'b>,
+ on_diagnostic: PathDiagnosticCallback<'a>,
+ 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<'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()");
+ }
+
+ pub(crate) fn lower_ty_relative_path(
+ &mut self,
+ ty: Ty,
+ // We need the original resolution to lower `Self::AssocTy` correctly
+ res: Option<TypeNs>,
+ ) -> (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)
+ }
+ _ => {
+ // FIXME report error (ambiguous associated type)
+ (TyKind::Error.intern(Interner), None)
+ }
+ }
+ }
+
+ 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,
+ resolution: TypeNs,
+ infer_args: bool,
+ ) -> (Ty, Option<TypeNs>) {
+ let remaining_segments = self.segments.skip(self.current_segment_idx + 1);
+
+ 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_,
+ TyKind::Error.intern(Interner),
+ );
+
+ 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);
+
+ 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,
+ );
+ 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)),
+ );
+ TyKind::Alias(AliasTy::Projection(ProjectionTy {
+ associated_ty_id: to_assoc_type_id(associated_ty),
+ substitution,
+ }))
+ .intern(Interner)
+ }
+ None => {
+ // FIXME: report error (associated type not found)
+ TyKind::Error.intern(Interner)
+ }
+ }
+ }
+ 0 => {
+ // 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)
+ }
+ _ => {
+ // FIXME report error (ambiguous associated type)
+ TyKind::Error.intern(Interner)
+ }
+ };
+ return (ty, None);
+ }
+ TypeNs::TraitAliasId(_) => {
+ // FIXME(trait_alias): Implement trait alias.
+ return (TyKind::Error.intern(Interner), None);
+ }
+ TypeNs::GenericParam(param_id) => match self.ctx.type_param_mode {
+ ParamLoweringMode::Placeholder => {
+ 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())
+ {
+ 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) => {
+ let generics = self.ctx.generics().expect("impl should have generic param scope");
+
+ 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 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()
+ }
+ }
+ }
+ TypeNs::AdtSelfType(adt) => {
+ let generics = generics(self.ctx.db.upcast(), 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)
+ }
+ };
+ self.ctx.db.ty(adt.into()).substitute(Interner, &substs)
+ }
+
+ 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(_) => return (TyKind::Error.intern(Interner), None),
+ };
+
+ 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::AdtId(_)
+ | TypeNs::EnumVariantId(_)
+ | TypeNs::TypeAliasId(_)
+ | TypeNs::TraitId(_)
+ | TypeNs::TraitAliasId(_) => {}
+ }
+ }
+
+ 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)
+ }
+
+ 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 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 {
+ if 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.upcast(),
+ 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 {
+ if 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)
+ }
+
+ fn select_associated_type(&mut self, res: Option<TypeNs>) -> Ty {
+ let Some((generics, res)) = self.ctx.generics().zip(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(),
+ res,
+ Some(segment.name.clone()),
+ move |name, t, associated_ty| {
+ let generics = self.ctx.generics().unwrap();
+
+ if name != segment.name {
+ return None;
+ }
+
+ 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)
+ }
+ };
+
+ // 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);
+
+ let len_self =
+ crate::generics::generics(self.ctx.db.upcast(), associated_ty.into())
+ .len_self();
+
+ let substs = Substitution::from_iter(
+ Interner,
+ substs.iter(Interner).take(len_self).chain(parent_subst.iter(Interner)),
+ );
+
+ Some(
+ TyKind::Alias(AliasTy::Projection(ProjectionTy {
+ associated_ty_id: to_assoc_type_id(associated_ty),
+ substitution: substs,
+ }))
+ .intern(Interner),
+ )
+ },
+ );
+
+ ty.unwrap_or_else(|| TyKind::Error.intern(Interner))
+ }
+
+ fn lower_path_inner(&mut self, typeable: TyDefId, infer_args: bool) -> Ty {
+ let generic_def = match typeable {
+ TyDefId::BuiltinType(builtin) => return TyBuilder::builtin(builtin),
+ TyDefId::AdtId(it) => it.into(),
+ TyDefId::TypeAliasId(it) => it.into(),
+ };
+ let substs = self.substs_from_path_segment(generic_def, infer_args, None);
+ self.ctx.db.ty(typeable).substitute(Interner, &substs)
+ }
+
+ /// 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,
+ ) -> Substitution {
+ 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 Substitution::empty(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 {
+ if 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.upcast()).parent.into()
+ }
+ };
+ let result = self.substs_from_path_segment(generic_def, infer_args, None);
+ 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>,
+ ) -> 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)
+ }
+ _ => true,
+ };
+ if prohibit_parens && self.prohibit_parenthesized_generic_args() {
+ return TyBuilder::unknown_subst(self.ctx.db, def);
+ }
+
+ self.substs_from_args_and_bindings(
+ self.current_or_prev_segment.args_and_bindings,
+ def,
+ infer_args,
+ explicit_self_ty,
+ )
+ }
+
+ 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>,
+ ) -> 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);
+ }
+ }
+ };
+ 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));
+ }
+ }
+ } 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))
+ }
+ 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));
+ }
+ // 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)
+ }
+
+ pub(crate) fn lower_trait_ref_from_resolved_path(
+ &mut self,
+ resolved: TraitId,
+ explicit_self_ty: Ty,
+ ) -> TraitRef {
+ let substs = self.trait_ref_substs_from_path(resolved, explicit_self_ty);
+ TraitRef { trait_id: to_chalk_trait_id(resolved), substitution: substs }
+ }
+
+ fn trait_ref_substs_from_path(
+ &mut self,
+ resolved: TraitId,
+ explicit_self_ty: Ty,
+ ) -> Substitution {
+ self.substs_from_path_segment(resolved.into(), false, Some(explicit_self_ty))
+ }
+
+ 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| {
+ let found = associated_type_by_name_including_super_traits(
+ self.ctx.db,
+ trait_ref.clone(),
+ &binding.name,
+ );
+ let (super_trait_ref, associated_ty) = match found {
+ 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 = Substitution::from_iter(
+ Interner,
+ substitution
+ .iter(Interner)
+ .take(self_params)
+ .chain(super_trait_ref.substitution.iter(Interner)),
+ );
+ let projection_ty = ProjectionTy {
+ associated_ty_id: to_assoc_type_id(associated_ty),
+ substitution,
+ };
+ 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, _)| {
+ match p {
+ WherePredicate::TypeBound {
+ target: WherePredicateTypeTarget::TypeOrConstParam(idx),
+ bound: b,
+ } if b == bound => 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)));
+ }
+ }
+ }
+ for bound in binding.bounds.iter() {
+ predicates.extend(self.ctx.lower_type_bound(
+ bound,
+ TyKind::Alias(AliasTy::Projection(projection_ty.clone())).intern(Interner),
+ false,
+ ));
+ }
+ predicates
+ })
+ })
+ }
+}
diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs
index 307673f52c..1ed0daa375 100644
--- a/crates/hir/src/diagnostics.rs
+++ b/crates/hir/src/diagnostics.rs
@@ -112,6 +112,7 @@ diagnostics![
UnusedMut,
UnusedVariable,
GenericArgsProhibited,
+ ParenthesizedGenericArgsWithoutFnTrait,
];
#[derive(Debug)]
@@ -414,6 +415,11 @@ pub struct GenericArgsProhibited {
pub reason: GenericArgsProhibitedReason,
}
+#[derive(Debug)]
+pub struct ParenthesizedGenericArgsWithoutFnTrait {
+ pub args: InFile<AstPtr<ast::ParenthesizedArgList>>,
+}
+
impl AnyDiagnostic {
pub(crate) fn body_validation_diagnostic(
db: &dyn HirDatabase,
@@ -703,8 +709,8 @@ impl AnyDiagnostic {
diag: &PathLoweringDiagnostic,
path: InFile<ast::Path>,
) -> Option<AnyDiagnostic> {
- Some(match diag {
- &PathLoweringDiagnostic::GenericArgsProhibited { segment, reason } => {
+ Some(match *diag {
+ PathLoweringDiagnostic::GenericArgsProhibited { segment, reason } => {
let segment = hir_segment_to_ast_segment(&path.value, segment)?;
let args = if let Some(generics) = segment.generic_arg_list() {
AstPtr::new(&generics).wrap_left()
@@ -714,6 +720,12 @@ impl AnyDiagnostic {
let args = path.with_value(args);
GenericArgsProhibited { args, reason }.into()
}
+ PathLoweringDiagnostic::ParenthesizedGenericArgsWithoutFnTrait { segment } => {
+ let segment = hir_segment_to_ast_segment(&path.value, segment)?;
+ let args = AstPtr::new(&segment.parenthesized_arg_list()?);
+ let args = path.with_value(args);
+ ParenthesizedGenericArgsWithoutFnTrait { args }.into()
+ }
})
}
diff --git a/crates/ide-diagnostics/src/handlers/parenthesized_generic_args_without_fn_trait.rs b/crates/ide-diagnostics/src/handlers/parenthesized_generic_args_without_fn_trait.rs
new file mode 100644
index 0000000000..ccf5172341
--- /dev/null
+++ b/crates/ide-diagnostics/src/handlers/parenthesized_generic_args_without_fn_trait.rs
@@ -0,0 +1,59 @@
+use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
+
+// Diagnostic: parenthesized-generic-args-without-fn-trait
+//
+// This diagnostic is shown when a `Fn`-trait-style generic parameters (`Trait(A, B) -> C`)
+// was used on non-`Fn` trait/type.
+pub(crate) fn parenthesized_generic_args_without_fn_trait(
+ ctx: &DiagnosticsContext<'_>,
+ d: &hir::ParenthesizedGenericArgsWithoutFnTrait,
+) -> Diagnostic {
+ Diagnostic::new_with_syntax_node_ptr(
+ ctx,
+ DiagnosticCode::RustcHardError("E0214"),
+ "parenthesized type parameters may only be used with a `Fn` trait",
+ d.args.map(Into::into),
+ )
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::tests::check_diagnostics;
+
+ #[test]
+ fn fn_traits_work() {
+ check_diagnostics(
+ r#"
+//- minicore: async_fn, fn
+fn foo<
+ A: Fn(),
+ B: FnMut() -> i32,
+ C: FnOnce(&str, bool),
+ D: AsyncFn::(u32) -> u32,
+ E: AsyncFnMut(),
+ F: AsyncFnOnce() -> bool,
+>() {}
+ "#,
+ );
+ }
+
+ #[test]
+ fn non_fn_trait() {
+ check_diagnostics(
+ r#"
+struct Struct<T>(T);
+enum Enum<T> { EnumVariant(T) }
+type TypeAlias<T> = bool;
+
+type Foo = TypeAlias() -> bool;
+ // ^^ error: parenthesized type parameters may only be used with a `Fn` trait
+
+fn foo(_a: Struct(i32)) {
+ // ^^^^^ error: parenthesized type parameters may only be used with a `Fn` trait
+ let _ = <Enum::(u32)>::EnumVariant(0);
+ // ^^^^^^^ error: parenthesized type parameters may only be used with a `Fn` trait
+}
+ "#,
+ );
+ }
+}
diff --git a/crates/ide-diagnostics/src/lib.rs b/crates/ide-diagnostics/src/lib.rs
index 50c91a6960..3ea41aa7e8 100644
--- a/crates/ide-diagnostics/src/lib.rs
+++ b/crates/ide-diagnostics/src/lib.rs
@@ -43,6 +43,7 @@ mod handlers {
pub(crate) mod mutability_errors;
pub(crate) mod no_such_field;
pub(crate) mod non_exhaustive_let;
+ pub(crate) mod parenthesized_generic_args_without_fn_trait;
pub(crate) mod private_assoc_item;
pub(crate) mod private_field;
pub(crate) mod remove_trailing_return;
@@ -466,7 +467,12 @@ pub fn semantic_diagnostics(
Some(it) => it,
None => continue,
},
- AnyDiagnostic::GenericArgsProhibited(d) => handlers::generic_args_prohibited::generic_args_prohibited(&ctx, &d)
+ AnyDiagnostic::GenericArgsProhibited(d) => {
+ handlers::generic_args_prohibited::generic_args_prohibited(&ctx, &d)
+ }
+ AnyDiagnostic::ParenthesizedGenericArgsWithoutFnTrait(d) => {
+ handlers::parenthesized_generic_args_without_fn_trait::parenthesized_generic_args_without_fn_trait(&ctx, &d)
+ }
};
res.push(d)
}
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_general.html b/crates/ide/src/syntax_highlighting/test_data/highlight_general.html
index 9be7c92fc7..9477d0d1b8 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_general.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_general.html
@@ -48,17 +48,6 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<pre><code><span class="keyword">use</span> <span class="module">inner</span><span class="operator">::</span><span class="brace">{</span><span class="self_keyword">self</span> <span class="keyword">as</span> <span class="module declaration">inner_mod</span><span class="brace">}</span><span class="semicolon">;</span>
<span class="keyword">mod</span> <span class="module declaration">inner</span> <span class="brace">{</span><span class="brace">}</span>
-<span class="keyword">pub</span> <span class="keyword">mod</span> <span class="module declaration public">ops</span> <span class="brace">{</span>
- <span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute">lang</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"fn_once"</span><span class="attribute_bracket attribute">]</span>
- <span class="keyword">pub</span> <span class="keyword">trait</span> <span class="trait declaration public">FnOnce</span><span class="angle">&lt;</span><span class="type_param declaration">Args</span><span class="angle">&gt;</span> <span class="brace">{</span><span class="brace">}</span>
-
- <span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute">lang</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"fn_mut"</span><span class="attribute_bracket attribute">]</span>
- <span class="keyword">pub</span> <span class="keyword">trait</span> <span class="trait declaration public">FnMut</span><span class="angle">&lt;</span><span class="type_param declaration">Args</span><span class="angle">&gt;</span><span class="colon">:</span> <span class="trait public">FnOnce</span><span class="angle">&lt;</span><span class="type_param">Args</span><span class="angle">&gt;</span> <span class="brace">{</span><span class="brace">}</span>
-
- <span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute">lang</span> <span class="operator attribute">=</span> <span class="string_literal attribute">"fn"</span><span class="attribute_bracket attribute">]</span>
- <span class="keyword">pub</span> <span class="keyword">trait</span> <span class="trait declaration public">Fn</span><span class="angle">&lt;</span><span class="type_param declaration">Args</span><span class="angle">&gt;</span><span class="colon">:</span> <span class="trait public">FnMut</span><span class="angle">&lt;</span><span class="type_param">Args</span><span class="angle">&gt;</span> <span class="brace">{</span><span class="brace">}</span>
-<span class="brace">}</span>
-
<span class="keyword">struct</span> <span class="struct declaration">Foo</span> <span class="brace">{</span>
<span class="field declaration">x</span><span class="colon">:</span> <span class="builtin_type">u32</span><span class="comma">,</span>
<span class="brace">}</span>
@@ -125,8 +114,8 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<span class="const_param const">FOO</span>
<span class="brace">}</span>
-<span class="keyword">use</span> <span class="module public">ops</span><span class="operator">::</span><span class="trait public">Fn</span><span class="semicolon">;</span>
-<span class="keyword">fn</span> <span class="function declaration">baz</span><span class="angle">&lt;</span><span class="type_param declaration">F</span><span class="colon">:</span> <span class="trait public">Fn</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="parenthesis">(</span><span class="parenthesis">)</span><span class="angle">&gt;</span><span class="parenthesis">(</span><span class="value_param callable declaration">f</span><span class="colon">:</span> <span class="type_param">F</span><span class="parenthesis">)</span> <span class="brace">{</span>
+<span class="keyword">use</span> <span class="module crate_root default_library library">core</span><span class="operator">::</span><span class="module default_library library">ops</span><span class="operator">::</span><span class="trait default_library library">Fn</span><span class="semicolon">;</span>
+<span class="keyword">fn</span> <span class="function declaration">baz</span><span class="angle">&lt;</span><span class="type_param declaration">F</span><span class="colon">:</span> <span class="trait default_library library">Fn</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">-&gt;</span> <span class="parenthesis">(</span><span class="parenthesis">)</span><span class="angle">&gt;</span><span class="parenthesis">(</span><span class="value_param callable declaration">f</span><span class="colon">:</span> <span class="type_param">F</span><span class="parenthesis">)</span> <span class="brace">{</span>
<span class="value_param callable">f</span><span class="parenthesis">(</span><span class="parenthesis">)</span>
<span class="brace">}</span>
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs
index c6e83d0d5e..3775265f23 100644
--- a/crates/ide/src/syntax_highlighting/tests.rs
+++ b/crates/ide/src/syntax_highlighting/tests.rs
@@ -136,22 +136,11 @@ use self::foo as bar;
fn test_highlighting() {
check_highlighting(
r#"
-//- minicore: derive, copy
+//- minicore: derive, copy, fn
//- /main.rs crate:main deps:foo
use inner::{self as inner_mod};
mod inner {}
-pub mod ops {
- #[lang = "fn_once"]
- pub trait FnOnce<Args> {}
-
- #[lang = "fn_mut"]
- pub trait FnMut<Args>: FnOnce<Args> {}
-
- #[lang = "fn"]
- pub trait Fn<Args>: FnMut<Args> {}
-}
-
struct Foo {
x: u32,
}
@@ -218,7 +207,7 @@ fn const_param<const FOO: usize>() -> usize {
FOO
}
-use ops::Fn;
+use core::ops::Fn;
fn baz<F: Fn() -> ()>(f: F) {
f()
}
diff --git a/crates/intern/src/symbol/symbols.rs b/crates/intern/src/symbol/symbols.rs
index 66553a2661..be0de6c936 100644
--- a/crates/intern/src/symbol/symbols.rs
+++ b/crates/intern/src/symbol/symbols.rs
@@ -13,15 +13,35 @@ use crate::{
macro_rules! define_symbols {
(@WITH_NAME: $($alias:ident = $value:literal,)* @PLAIN: $($name:ident,)*) => {
- // Ideally we would be emitting `const` here, but then we no longer have stable addresses
- // which is what we are relying on for equality! In the future if consts can refer to
- // statics we should swap these for `const`s and have the string literal being pointed
- // to be statics to refer to such that their address is stable.
+ // We define symbols as both `const`s and `static`s because some const code requires const symbols,
+ // but code from before the transition relies on the lifetime of the predefined symbols and making them
+ // `const`s make it error (because now they're temporaries). In the future we probably should only
+ // use consts.
+
+ /// Predefined symbols as `const`s (instead of the default `static`s).
+ pub mod consts {
+ use super::{Symbol, TaggedArcPtr};
+
+ // The strings should be in `static`s so that symbol equality holds.
+ $(
+ pub const $name: Symbol = {
+ static SYMBOL_STR: &str = stringify!($name);
+ Symbol { repr: TaggedArcPtr::non_arc(&SYMBOL_STR) }
+ };
+ )*
+ $(
+ pub const $alias: Symbol = {
+ static SYMBOL_STR: &str = $value;
+ Symbol { repr: TaggedArcPtr::non_arc(&SYMBOL_STR) }
+ };
+ )*
+ }
+
$(
- pub static $name: Symbol = Symbol { repr: TaggedArcPtr::non_arc(&stringify!($name)) };
+ pub static $name: Symbol = consts::$name;
)*
$(
- pub static $alias: Symbol = Symbol { repr: TaggedArcPtr::non_arc(&$value) };
+ pub static $alias: Symbol = consts::$alias;
)*
@@ -428,6 +448,7 @@ define_symbols! {
rustc_layout_scalar_valid_range_start,
rustc_legacy_const_generics,
rustc_macro_transparency,
+ rustc_paren_sugar,
rustc_reallocator,
rustc_reservation_impl,
rustc_safe_intrinsic,
diff --git a/crates/test-utils/src/minicore.rs b/crates/test-utils/src/minicore.rs
index 4ed68d18e8..202afebde7 100644
--- a/crates/test-utils/src/minicore.rs
+++ b/crates/test-utils/src/minicore.rs
@@ -647,18 +647,21 @@ pub mod ops {
#[lang = "fn"]
#[fundamental]
+ #[rustc_paren_sugar]
pub trait Fn<Args: Tuple>: FnMut<Args> {
extern "rust-call" fn call(&self, args: Args) -> Self::Output;
}
#[lang = "fn_mut"]
#[fundamental]
+ #[rustc_paren_sugar]
pub trait FnMut<Args: Tuple>: FnOnce<Args> {
extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output;
}
#[lang = "fn_once"]
#[fundamental]
+ #[rustc_paren_sugar]
pub trait FnOnce<Args: Tuple> {
#[lang = "fn_once_output"]
type Output;
@@ -736,12 +739,14 @@ pub mod ops {
#[lang = "async_fn"]
#[fundamental]
+ #[rustc_paren_sugar]
pub trait AsyncFn<Args: Tuple>: AsyncFnMut<Args> {
extern "rust-call" fn async_call(&self, args: Args) -> Self::CallRefFuture<'_>;
}
#[lang = "async_fn_mut"]
#[fundamental]
+ #[rustc_paren_sugar]
pub trait AsyncFnMut<Args: Tuple>: AsyncFnOnce<Args> {
#[lang = "call_ref_future"]
type CallRefFuture<'a>: Future<Output = Self::Output>
@@ -752,6 +757,7 @@ pub mod ops {
#[lang = "async_fn_once"]
#[fundamental]
+ #[rustc_paren_sugar]
pub trait AsyncFnOnce<Args: Tuple> {
#[lang = "async_fn_once_output"]
type Output;