mirror of
https://github.com/rust-lang/rust.git
synced 2025-04-28 02:57:37 +00:00
Merge pull request #19127 from ChayimFriedman2/different-generic-args
feat: Refactor path lowering and serve a new path diagnostic
This commit is contained in:
commit
123f17c57b
@ -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();
|
||||
|
@ -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],
|
||||
|
@ -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(),
|
||||
));
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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));
|
||||
|
@ -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,
|
||||
#[inline]
|
||||
pub(super) fn at_path<'b>(
|
||||
&'b mut self,
|
||||
path: &'b 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,
|
||||
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));
|
||||
|
@ -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(_),
|
||||
|
@ -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!(
|
||||
|
@ -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,
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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 },
|
||||
}
|
||||
|
911
src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs
Normal file
911
src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs
Normal file
@ -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
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
@ -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()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
|
@ -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"><</span><span class="type_param declaration">Args</span><span class="angle">></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"><</span><span class="type_param declaration">Args</span><span class="angle">></span><span class="colon">:</span> <span class="trait public">FnOnce</span><span class="angle"><</span><span class="type_param">Args</span><span class="angle">></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"><</span><span class="type_param declaration">Args</span><span class="angle">></span><span class="colon">:</span> <span class="trait public">FnMut</span><span class="angle"><</span><span class="type_param">Args</span><span class="angle">></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"><</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">-></span> <span class="parenthesis">(</span><span class="parenthesis">)</span><span class="angle">></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"><</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">-></span> <span class="parenthesis">(</span><span class="parenthesis">)</span><span class="angle">></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>
|
||||
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user