mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-28 11:20:54 +00:00
Add two new diagnostics: one for mismatch in generic arguments count, and another for mismatch in their kind
Also known as E0747 and E0107. And by the way, rewrite how we lower generic arguments and deduplicate it between paths and method calls. The new version is taken almost straight from rustc. This commit also changes the binders of `generic_defaults()`, to only include the binders of the arguments up to (and not including) the current argument. This make it easier to handle it in the rewritten lowering of generic args. It's also how rustc does it.
This commit is contained in:
parent
2b4b483af1
commit
99ce53b1d7
@ -35,7 +35,9 @@ use crate::{
|
||||
};
|
||||
|
||||
pub use self::body::{Body, BodySourceMap};
|
||||
pub use self::lower::hir_segment_to_ast_segment;
|
||||
pub use self::lower::{
|
||||
hir_assoc_type_binding_to_ast, hir_generic_arg_to_ast, hir_segment_to_ast_segment,
|
||||
};
|
||||
|
||||
/// A wrapper around [`span::SyntaxContextId`] that is intended only for comparisons.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
|
@ -788,6 +788,7 @@ impl ExprCollector<'_> {
|
||||
node: ast::GenericArgList,
|
||||
impl_trait_lower_fn: &mut impl FnMut(ThinVec<TypeBound>) -> TypeRef,
|
||||
) -> Option<GenericArgs> {
|
||||
// This needs to be kept in sync with `hir_generic_arg_to_ast()`.
|
||||
let mut args = Vec::new();
|
||||
let mut bindings = Vec::new();
|
||||
for generic_arg in node.generic_args() {
|
||||
@ -797,6 +798,7 @@ impl ExprCollector<'_> {
|
||||
args.push(GenericArg::Type(type_ref));
|
||||
}
|
||||
ast::GenericArg::AssocTypeArg(assoc_type_arg) => {
|
||||
// This needs to be kept in sync with `hir_assoc_type_binding_to_ast()`.
|
||||
if assoc_type_arg.param_list().is_some() {
|
||||
// We currently ignore associated return type bounds.
|
||||
continue;
|
||||
@ -3228,3 +3230,33 @@ enum ArgumentType {
|
||||
Format(FormatTrait),
|
||||
Usize,
|
||||
}
|
||||
|
||||
/// This function find the AST fragment that corresponds to an `AssociatedTypeBinding` in the HIR.
|
||||
pub fn hir_assoc_type_binding_to_ast(
|
||||
segment_args: &ast::GenericArgList,
|
||||
binding_idx: u32,
|
||||
) -> Option<ast::AssocTypeArg> {
|
||||
segment_args
|
||||
.generic_args()
|
||||
.filter_map(|arg| match arg {
|
||||
ast::GenericArg::AssocTypeArg(it) => Some(it),
|
||||
_ => None,
|
||||
})
|
||||
.filter(|binding| binding.param_list().is_none() && binding.name_ref().is_some())
|
||||
.nth(binding_idx as usize)
|
||||
}
|
||||
|
||||
/// This function find the AST generic argument from the one in the HIR. Does not support the `Self` argument.
|
||||
pub fn hir_generic_arg_to_ast(
|
||||
args: &ast::GenericArgList,
|
||||
arg_idx: u32,
|
||||
has_self_arg: bool,
|
||||
) -> Option<ast::GenericArg> {
|
||||
args.generic_args()
|
||||
.filter(|arg| match arg {
|
||||
ast::GenericArg::AssocTypeArg(_) => false,
|
||||
ast::GenericArg::LifetimeArg(arg) => arg.lifetime().is_some(),
|
||||
ast::GenericArg::ConstArg(_) | ast::GenericArg::TypeArg(_) => true,
|
||||
})
|
||||
.nth(arg_idx as usize - has_self_arg as usize)
|
||||
}
|
||||
|
@ -152,10 +152,8 @@ pub(super) fn lower_path(
|
||||
args: iter::once(self_type)
|
||||
.chain(it.args.iter().cloned())
|
||||
.collect(),
|
||||
|
||||
has_self_type: true,
|
||||
bindings: it.bindings.clone(),
|
||||
parenthesized: it.parenthesized,
|
||||
..it
|
||||
},
|
||||
None => GenericArgs {
|
||||
args: Box::new([self_type]),
|
||||
|
@ -138,6 +138,7 @@ impl GenericParamData {
|
||||
|
||||
impl_from!(TypeParamData, ConstParamData, LifetimeParamData for GenericParamData);
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum GenericParamDataRef<'a> {
|
||||
TypeParamData(&'a TypeParamData),
|
||||
ConstParamData(&'a ConstParamData),
|
||||
|
@ -306,29 +306,28 @@ impl TyBuilder<hir_def::AdtId> {
|
||||
// Note that we're building ADT, so we never have parent generic parameters.
|
||||
let defaults = db.generic_defaults(self.data.into());
|
||||
|
||||
for default_ty in &defaults[self.vec.len()..] {
|
||||
// NOTE(skip_binders): we only check if the arg type is error type.
|
||||
if let Some(x) = default_ty.skip_binders().ty(Interner) {
|
||||
if x.is_unknown() {
|
||||
self.vec.push(fallback().cast(Interner));
|
||||
continue;
|
||||
if let Some(defaults) = defaults.get(self.vec.len()..) {
|
||||
for default_ty in defaults {
|
||||
// NOTE(skip_binders): we only check if the arg type is error type.
|
||||
if let Some(x) = default_ty.skip_binders().ty(Interner) {
|
||||
if x.is_unknown() {
|
||||
self.vec.push(fallback().cast(Interner));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// Each default can only depend on the previous parameters.
|
||||
self.vec.push(default_ty.clone().substitute(Interner, &*self.vec).cast(Interner));
|
||||
}
|
||||
// Each default can only depend on the previous parameters.
|
||||
let subst_so_far = Substitution::from_iter(
|
||||
Interner,
|
||||
self.vec
|
||||
.iter()
|
||||
.cloned()
|
||||
.chain(self.param_kinds[self.vec.len()..].iter().map(|it| match it {
|
||||
ParamKind::Type => TyKind::Error.intern(Interner).cast(Interner),
|
||||
ParamKind::Lifetime => error_lifetime().cast(Interner),
|
||||
ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()),
|
||||
}))
|
||||
.take(self.param_kinds.len()),
|
||||
);
|
||||
self.vec.push(default_ty.clone().substitute(Interner, &subst_so_far).cast(Interner));
|
||||
}
|
||||
|
||||
// The defaults may be missing if no param has default, so fill that.
|
||||
let filler = self.param_kinds[self.vec.len()..].iter().map(|x| match x {
|
||||
ParamKind::Type => fallback().cast(Interner),
|
||||
ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()),
|
||||
ParamKind::Lifetime => error_lifetime().cast(Interner),
|
||||
});
|
||||
self.vec.extend(filler.casted(Interner));
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -200,6 +200,9 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug {
|
||||
def: GenericDefId,
|
||||
) -> (GenericDefaults, Diagnostics);
|
||||
|
||||
/// This returns an empty list if no parameter has default.
|
||||
///
|
||||
/// The binders of the returned defaults are only up to (not including) this parameter.
|
||||
#[salsa::invoke(crate::lower::generic_defaults_query)]
|
||||
#[salsa::transparent]
|
||||
fn generic_defaults(&self, def: GenericDefId) -> GenericDefaults;
|
||||
|
@ -1640,7 +1640,7 @@ fn generic_args_sans_defaults<'ga>(
|
||||
Some(default_parameter) => {
|
||||
// !is_err(default_parameter.skip_binders())
|
||||
// &&
|
||||
arg != &default_parameter.clone().substitute(Interner, ¶meters)
|
||||
arg != &default_parameter.clone().substitute(Interner, ¶meters[..i])
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -21,7 +21,6 @@ use hir_def::{
|
||||
},
|
||||
};
|
||||
use itertools::chain;
|
||||
use stdx::TupleExt;
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{Interner, Substitution, db::HirDatabase, lt_to_placeholder_idx, to_placeholder_idx};
|
||||
@ -76,10 +75,13 @@ impl Generics {
|
||||
self.iter_parent().map(|(id, _)| id)
|
||||
}
|
||||
|
||||
pub(crate) fn iter_self_type_or_consts_id(
|
||||
pub(crate) fn iter_self_type_or_consts(
|
||||
&self,
|
||||
) -> impl DoubleEndedIterator<Item = GenericParamId> + '_ {
|
||||
self.params.iter_type_or_consts().map(from_toc_id(self)).map(TupleExt::head)
|
||||
) -> impl DoubleEndedIterator<Item = (LocalTypeOrConstParamId, &TypeOrConstParamData)> + '_
|
||||
{
|
||||
let mut toc = self.params.iter_type_or_consts();
|
||||
let trait_self_param = self.has_trait_self_param.then(|| toc.next()).flatten();
|
||||
chain!(trait_self_param, toc)
|
||||
}
|
||||
|
||||
/// Iterate over the parent params followed by self params.
|
||||
@ -107,7 +109,7 @@ impl Generics {
|
||||
}
|
||||
|
||||
/// Iterator over types and const params of parent.
|
||||
fn iter_parent(
|
||||
pub(crate) fn iter_parent(
|
||||
&self,
|
||||
) -> impl DoubleEndedIterator<Item = (GenericParamId, GenericParamDataRef<'_>)> + '_ {
|
||||
self.parent_generics().into_iter().flat_map(|it| {
|
||||
@ -129,6 +131,10 @@ impl Generics {
|
||||
self.params.len()
|
||||
}
|
||||
|
||||
pub(crate) fn len_lifetimes_self(&self) -> usize {
|
||||
self.params.len_lifetimes()
|
||||
}
|
||||
|
||||
/// (parent total, self param, type params, const params, impl trait list, lifetimes)
|
||||
pub(crate) fn provenance_split(&self) -> (usize, bool, usize, usize, usize, usize) {
|
||||
let mut self_param = false;
|
||||
@ -144,7 +150,7 @@ impl Generics {
|
||||
TypeOrConstParamData::ConstParamData(_) => const_params += 1,
|
||||
});
|
||||
|
||||
let lifetime_params = self.params.iter_lt().count();
|
||||
let lifetime_params = self.params.len_lifetimes();
|
||||
|
||||
let parent_len = self.parent_generics().map_or(0, Generics::len);
|
||||
(parent_len, self_param, type_params, const_params, impl_trait_params, lifetime_params)
|
||||
|
@ -34,8 +34,8 @@ use chalk_ir::{
|
||||
};
|
||||
use either::Either;
|
||||
use hir_def::{
|
||||
AdtId, AssocItemId, DefWithBodyId, FieldId, FunctionId, GenericDefId, ImplId, ItemContainerId,
|
||||
Lookup, TraitId, TupleFieldId, TupleId, TypeAliasId, VariantId,
|
||||
AdtId, AssocItemId, DefWithBodyId, FieldId, FunctionId, GenericDefId, GenericParamId, ImplId,
|
||||
ItemContainerId, Lookup, TraitId, TupleFieldId, TupleId, TypeAliasId, VariantId,
|
||||
builtin_type::{BuiltinInt, BuiltinType, BuiltinUint},
|
||||
expr_store::{Body, ExpressionStore, HygieneId, path::Path},
|
||||
hir::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, LabelId, PatId},
|
||||
@ -55,8 +55,9 @@ use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
AliasEq, AliasTy, Binders, ClosureId, Const, DomainGoal, GenericArg, Goal, ImplTraitId,
|
||||
ImplTraitIdx, InEnvironment, Interner, Lifetime, OpaqueTyId, ParamLoweringMode,
|
||||
PathLoweringDiagnostic, ProjectionTy, Substitution, TraitEnvironment, Ty, TyBuilder, TyExt,
|
||||
ImplTraitIdx, InEnvironment, IncorrectGenericsLenKind, Interner, Lifetime, OpaqueTyId,
|
||||
ParamLoweringMode, PathLoweringDiagnostic, ProjectionTy, Substitution, TraitEnvironment, Ty,
|
||||
TyBuilder, TyExt,
|
||||
db::HirDatabase,
|
||||
fold_tys,
|
||||
generics::Generics,
|
||||
@ -66,7 +67,7 @@ use crate::{
|
||||
expr::ExprIsRead,
|
||||
unify::InferenceTable,
|
||||
},
|
||||
lower::{ImplTraitLoweringMode, diagnostics::TyLoweringDiagnostic},
|
||||
lower::{GenericArgsPosition, ImplTraitLoweringMode, diagnostics::TyLoweringDiagnostic},
|
||||
mir::MirSpan,
|
||||
to_assoc_type_id,
|
||||
traits::FnTrait,
|
||||
@ -275,6 +276,20 @@ pub enum InferenceDiagnostic {
|
||||
node: ExprOrPatId,
|
||||
diag: PathLoweringDiagnostic,
|
||||
},
|
||||
MethodCallIncorrectGenericsLen {
|
||||
expr: ExprId,
|
||||
provided_count: u32,
|
||||
expected_count: u32,
|
||||
kind: IncorrectGenericsLenKind,
|
||||
def: GenericDefId,
|
||||
},
|
||||
MethodCallIncorrectGenericsOrder {
|
||||
expr: ExprId,
|
||||
param_id: GenericParamId,
|
||||
arg_idx: u32,
|
||||
/// Whether the `GenericArgs` contains a `Self` arg.
|
||||
has_self_arg: bool,
|
||||
},
|
||||
}
|
||||
|
||||
/// A mismatch between an expected and an inferred type.
|
||||
@ -909,6 +924,7 @@ impl<'a> InferenceContext<'a> {
|
||||
let mut param_tys =
|
||||
self.with_ty_lowering(&data.store, InferenceTyDiagnosticSource::Signature, |ctx| {
|
||||
ctx.type_param_mode(ParamLoweringMode::Placeholder);
|
||||
ctx.in_fn_signature = true;
|
||||
data.params.iter().map(|&type_ref| ctx.lower_ty(type_ref)).collect::<Vec<_>>()
|
||||
});
|
||||
|
||||
@ -953,8 +969,9 @@ impl<'a> InferenceContext<'a> {
|
||||
InferenceTyDiagnosticSource::Signature,
|
||||
|ctx| {
|
||||
ctx.type_param_mode(ParamLoweringMode::Placeholder)
|
||||
.impl_trait_mode(ImplTraitLoweringMode::Opaque)
|
||||
.lower_ty(return_ty)
|
||||
.impl_trait_mode(ImplTraitLoweringMode::Opaque);
|
||||
ctx.in_fn_signature = true;
|
||||
ctx.lower_ty(return_ty)
|
||||
},
|
||||
);
|
||||
let return_ty = self.insert_type_vars(return_ty);
|
||||
@ -1513,7 +1530,7 @@ impl<'a> InferenceContext<'a> {
|
||||
InferenceTyDiagnosticSource::Body,
|
||||
self.generic_def,
|
||||
);
|
||||
let mut path_ctx = ctx.at_path(path, node);
|
||||
let mut path_ctx = ctx.at_path(path, node, GenericArgsPosition::Value);
|
||||
let (resolution, unresolved) = if value_ns {
|
||||
let Some(res) = path_ctx.resolve_path_in_value_ns(HygieneId::ROOT) else {
|
||||
return (self.err_ty(), None);
|
||||
|
@ -12,6 +12,7 @@ use hir_def::expr_store::path::Path;
|
||||
use hir_def::{hir::ExprOrPatId, resolver::Resolver};
|
||||
use la_arena::{Idx, RawIdx};
|
||||
|
||||
use crate::lower::GenericArgsPosition;
|
||||
use crate::{
|
||||
InferenceDiagnostic, InferenceTyDiagnosticSource, TyLoweringContext, TyLoweringDiagnostic,
|
||||
db::HirDatabase,
|
||||
@ -74,6 +75,7 @@ impl<'a> InferenceTyLoweringContext<'a> {
|
||||
&'b mut self,
|
||||
path: &'b Path,
|
||||
node: ExprOrPatId,
|
||||
position: GenericArgsPosition,
|
||||
) -> PathLoweringContext<'b, 'a> {
|
||||
let on_diagnostic = PathDiagnosticCallback {
|
||||
data: Either::Right(PathDiagnosticCallbackData { diagnostics: self.diagnostics, node }),
|
||||
@ -83,13 +85,14 @@ impl<'a> InferenceTyLoweringContext<'a> {
|
||||
.push(InferenceDiagnostic::PathDiagnostic { node: data.node, diag });
|
||||
},
|
||||
};
|
||||
PathLoweringContext::new(&mut self.ctx, on_diagnostic, path)
|
||||
PathLoweringContext::new(&mut self.ctx, on_diagnostic, path, position)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(super) fn at_path_forget_diagnostics<'b>(
|
||||
&'b mut self,
|
||||
path: &'b Path,
|
||||
position: GenericArgsPosition,
|
||||
) -> PathLoweringContext<'b, 'a> {
|
||||
let on_diagnostic = PathDiagnosticCallback {
|
||||
data: Either::Right(PathDiagnosticCallbackData {
|
||||
@ -98,7 +101,7 @@ impl<'a> InferenceTyLoweringContext<'a> {
|
||||
}),
|
||||
callback: |_data, _, _diag| {},
|
||||
};
|
||||
PathLoweringContext::new(&mut self.ctx, on_diagnostic, path)
|
||||
PathLoweringContext::new(&mut self.ctx, on_diagnostic, path, position)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -12,7 +12,7 @@ use hir_def::{
|
||||
expr_store::path::{GenericArg, GenericArgs, Path},
|
||||
hir::{
|
||||
ArithOp, Array, AsmOperand, AsmOptions, BinaryOp, Expr, ExprId, ExprOrPatId, LabelId,
|
||||
Literal, Pat, PatId, Statement, UnaryOp,
|
||||
Literal, Pat, PatId, Statement, UnaryOp, generics::GenericParamDataRef,
|
||||
},
|
||||
lang_item::{LangItem, LangItemTarget},
|
||||
resolver::ValueNs,
|
||||
@ -24,11 +24,11 @@ use syntax::ast::RangeOp;
|
||||
|
||||
use crate::{
|
||||
Adjust, Adjustment, AdtId, AutoBorrow, Binders, CallableDefId, CallableSig, DeclContext,
|
||||
DeclOrigin, Interner, Rawness, Scalar, Substitution, TraitEnvironment, TraitRef, Ty, TyBuilder,
|
||||
TyExt, TyKind,
|
||||
DeclOrigin, IncorrectGenericsLenKind, Interner, Rawness, Scalar, Substitution,
|
||||
TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, TyKind,
|
||||
autoderef::{Autoderef, builtin_deref, deref_by_trait},
|
||||
consteval, error_lifetime,
|
||||
generics::{Generics, generics},
|
||||
consteval,
|
||||
generics::generics,
|
||||
infer::{
|
||||
BreakableKind,
|
||||
coerce::{CoerceMany, CoerceNever, CoercionCause},
|
||||
@ -36,7 +36,10 @@ use crate::{
|
||||
pat::contains_explicit_ref_binding,
|
||||
},
|
||||
lang_items::lang_items_for_bin_op,
|
||||
lower::{ParamLoweringMode, generic_arg_to_chalk, lower_to_chalk_mutability},
|
||||
lower::{
|
||||
GenericArgsPosition, ParamLoweringMode, lower_to_chalk_mutability,
|
||||
path::{GenericArgsLowerer, TypeLikeConst, substs_from_args_and_bindings},
|
||||
},
|
||||
mapping::{ToChalk, from_chalk},
|
||||
method_resolution::{self, VisibleFromModule},
|
||||
primitive::{self, UintTy},
|
||||
@ -1658,8 +1661,7 @@ impl InferenceContext<'_> {
|
||||
match resolved {
|
||||
Some((adjust, func, _)) => {
|
||||
let (ty, adjustments) = adjust.apply(&mut self.table, receiver_ty);
|
||||
let generics = generics(self.db, func.into());
|
||||
let substs = self.substs_for_method_call(generics, None);
|
||||
let substs = self.substs_for_method_call(tgt_expr, func.into(), None);
|
||||
self.write_expr_adj(receiver, adjustments);
|
||||
self.write_method_resolution(tgt_expr, func, substs.clone());
|
||||
|
||||
@ -1809,8 +1811,7 @@ impl InferenceContext<'_> {
|
||||
let (ty, adjustments) = adjust.apply(&mut self.table, receiver_ty);
|
||||
self.write_expr_adj(receiver, adjustments);
|
||||
|
||||
let generics = generics(self.db, func.into());
|
||||
let substs = self.substs_for_method_call(generics, generic_args);
|
||||
let substs = self.substs_for_method_call(tgt_expr, func.into(), generic_args);
|
||||
self.write_method_resolution(tgt_expr, func, substs.clone());
|
||||
self.check_method_call(
|
||||
tgt_expr,
|
||||
@ -1860,8 +1861,7 @@ impl InferenceContext<'_> {
|
||||
|
||||
let recovered = match assoc_func_with_same_name {
|
||||
Some(f) => {
|
||||
let generics = generics(self.db, f.into());
|
||||
let substs = self.substs_for_method_call(generics, generic_args);
|
||||
let substs = self.substs_for_method_call(tgt_expr, f.into(), generic_args);
|
||||
let f = self
|
||||
.db
|
||||
.value_ty(f.into())
|
||||
@ -2051,83 +2051,129 @@ impl InferenceContext<'_> {
|
||||
|
||||
fn substs_for_method_call(
|
||||
&mut self,
|
||||
def_generics: Generics,
|
||||
expr: ExprId,
|
||||
def: GenericDefId,
|
||||
generic_args: Option<&GenericArgs>,
|
||||
) -> Substitution {
|
||||
let (
|
||||
parent_params,
|
||||
has_self_param,
|
||||
type_params,
|
||||
const_params,
|
||||
impl_trait_params,
|
||||
lifetime_params,
|
||||
) = def_generics.provenance_split();
|
||||
assert!(!has_self_param); // method shouldn't have another Self param
|
||||
let total_len =
|
||||
parent_params + type_params + const_params + impl_trait_params + lifetime_params;
|
||||
|
||||
let param_to_var = |id| match id {
|
||||
GenericParamId::TypeParamId(_) => self.table.new_type_var().cast(Interner),
|
||||
GenericParamId::ConstParamId(id) => {
|
||||
self.table.new_const_var(self.db.const_param_ty(id)).cast(Interner)
|
||||
}
|
||||
GenericParamId::LifetimeParamId(_) => self.table.new_lifetime_var().cast(Interner),
|
||||
};
|
||||
|
||||
let mut substs: Vec<_> = def_generics.iter_parent_id().map(param_to_var).collect();
|
||||
|
||||
// handle provided arguments
|
||||
if let Some(generic_args) = generic_args {
|
||||
// if args are provided, it should be all of them, but we can't rely on that
|
||||
let self_params = type_params + const_params + lifetime_params;
|
||||
|
||||
let mut args = generic_args.args.iter().peekable();
|
||||
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 inferred.
|
||||
// Once we have implemented lifetime inference correctly,
|
||||
// this should be handled in a proper way.
|
||||
(
|
||||
GenericParamId::LifetimeParamId(_),
|
||||
None | Some(GenericArg::Type(_) | GenericArg::Const(_)),
|
||||
) => error_lifetime().cast(Interner),
|
||||
|
||||
// If we run out of `generic_args`, stop pushing substs
|
||||
(_, None) => break,
|
||||
|
||||
// Normal cases
|
||||
(_, Some(_)) => generic_arg_to_chalk(
|
||||
self.db,
|
||||
kind_id,
|
||||
args.next().unwrap(), // `peek()` is `Some(_)`, so guaranteed no panic
|
||||
self,
|
||||
&self.body.store,
|
||||
|this, type_ref| this.make_body_ty(type_ref),
|
||||
|this, c, ty| this.make_body_const(*c, ty),
|
||||
|this, path, ty| this.make_path_as_body_const(path, ty),
|
||||
|this, lt_ref| this.make_body_lifetime(lt_ref),
|
||||
),
|
||||
};
|
||||
|
||||
substs.push(arg);
|
||||
}
|
||||
};
|
||||
|
||||
let mut param_to_var = |id| match id {
|
||||
GenericParamId::TypeParamId(_) => self.table.new_type_var().cast(Interner),
|
||||
GenericParamId::ConstParamId(id) => {
|
||||
self.table.new_const_var(self.db.const_param_ty(id)).cast(Interner)
|
||||
}
|
||||
GenericParamId::LifetimeParamId(_) => self.table.new_lifetime_var().cast(Interner),
|
||||
};
|
||||
|
||||
// Handle everything else as unknown.
|
||||
for (id, _data) in def_generics.iter().skip(substs.len()) {
|
||||
substs.push(param_to_var(id));
|
||||
struct LowererCtx<'a, 'b> {
|
||||
ctx: &'a mut InferenceContext<'b>,
|
||||
expr: ExprId,
|
||||
}
|
||||
assert_eq!(substs.len(), total_len);
|
||||
Substitution::from_iter(Interner, substs)
|
||||
|
||||
impl GenericArgsLowerer for LowererCtx<'_, '_> {
|
||||
fn report_len_mismatch(
|
||||
&mut self,
|
||||
def: GenericDefId,
|
||||
provided_count: u32,
|
||||
expected_count: u32,
|
||||
kind: IncorrectGenericsLenKind,
|
||||
) {
|
||||
self.ctx.push_diagnostic(InferenceDiagnostic::MethodCallIncorrectGenericsLen {
|
||||
expr: self.expr,
|
||||
provided_count,
|
||||
expected_count,
|
||||
kind,
|
||||
def,
|
||||
});
|
||||
}
|
||||
|
||||
fn report_arg_mismatch(
|
||||
&mut self,
|
||||
param_id: GenericParamId,
|
||||
arg_idx: u32,
|
||||
has_self_arg: bool,
|
||||
) {
|
||||
self.ctx.push_diagnostic(InferenceDiagnostic::MethodCallIncorrectGenericsOrder {
|
||||
expr: self.expr,
|
||||
param_id,
|
||||
arg_idx,
|
||||
has_self_arg,
|
||||
});
|
||||
}
|
||||
|
||||
fn provided_kind(
|
||||
&mut self,
|
||||
param_id: GenericParamId,
|
||||
param: GenericParamDataRef<'_>,
|
||||
arg: &GenericArg,
|
||||
) -> crate::GenericArg {
|
||||
match (param, arg) {
|
||||
(GenericParamDataRef::LifetimeParamData(_), GenericArg::Lifetime(lifetime)) => {
|
||||
self.ctx.make_body_lifetime(lifetime).cast(Interner)
|
||||
}
|
||||
(GenericParamDataRef::TypeParamData(_), GenericArg::Type(type_ref)) => {
|
||||
self.ctx.make_body_ty(*type_ref).cast(Interner)
|
||||
}
|
||||
(GenericParamDataRef::ConstParamData(_), GenericArg::Const(konst)) => {
|
||||
let GenericParamId::ConstParamId(const_id) = param_id else {
|
||||
unreachable!("non-const param ID for const param");
|
||||
};
|
||||
let const_ty = self.ctx.db.const_param_ty(const_id);
|
||||
self.ctx.make_body_const(*konst, const_ty).cast(Interner)
|
||||
}
|
||||
_ => unreachable!("unmatching param kinds were passed to `provided_kind()`"),
|
||||
}
|
||||
}
|
||||
|
||||
fn provided_type_like_const(
|
||||
&mut self,
|
||||
const_ty: Ty,
|
||||
arg: TypeLikeConst<'_>,
|
||||
) -> crate::Const {
|
||||
match arg {
|
||||
TypeLikeConst::Path(path) => self.ctx.make_path_as_body_const(path, const_ty),
|
||||
TypeLikeConst::Infer => self.ctx.table.new_const_var(const_ty),
|
||||
}
|
||||
}
|
||||
|
||||
fn inferred_kind(
|
||||
&mut self,
|
||||
_def: GenericDefId,
|
||||
param_id: GenericParamId,
|
||||
_param: GenericParamDataRef<'_>,
|
||||
_infer_args: bool,
|
||||
_preceding_args: &[crate::GenericArg],
|
||||
) -> crate::GenericArg {
|
||||
// Always create an inference var, even when `infer_args == false`. This helps with diagnostics,
|
||||
// and I think it's also required in the presence of `impl Trait` (that must be inferred).
|
||||
match param_id {
|
||||
GenericParamId::TypeParamId(_) => self.ctx.table.new_type_var().cast(Interner),
|
||||
GenericParamId::ConstParamId(const_id) => self
|
||||
.ctx
|
||||
.table
|
||||
.new_const_var(self.ctx.db.const_param_ty(const_id))
|
||||
.cast(Interner),
|
||||
GenericParamId::LifetimeParamId(_) => {
|
||||
self.ctx.table.new_lifetime_var().cast(Interner)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parent_arg(&mut self, param_id: GenericParamId) -> crate::GenericArg {
|
||||
match param_id {
|
||||
GenericParamId::TypeParamId(_) => self.ctx.table.new_type_var().cast(Interner),
|
||||
GenericParamId::ConstParamId(const_id) => self
|
||||
.ctx
|
||||
.table
|
||||
.new_const_var(self.ctx.db.const_param_ty(const_id))
|
||||
.cast(Interner),
|
||||
GenericParamId::LifetimeParamId(_) => {
|
||||
self.ctx.table.new_lifetime_var().cast(Interner)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
substs_from_args_and_bindings(
|
||||
self.db,
|
||||
self.body,
|
||||
generic_args,
|
||||
def,
|
||||
true,
|
||||
GenericArgsPosition::MethodCall,
|
||||
None,
|
||||
&mut LowererCtx { ctx: self, expr },
|
||||
)
|
||||
}
|
||||
|
||||
fn register_obligations_for_call(&mut self, callable_ty: &Ty) {
|
||||
|
@ -16,6 +16,7 @@ use crate::{
|
||||
consteval, error_lifetime,
|
||||
generics::generics,
|
||||
infer::diagnostics::InferenceTyLoweringContext as TyLoweringContext,
|
||||
lower::GenericArgsPosition,
|
||||
method_resolution::{self, VisibleFromModule},
|
||||
to_chalk_trait_id,
|
||||
};
|
||||
@ -95,7 +96,7 @@ impl InferenceContext<'_> {
|
||||
};
|
||||
|
||||
let substs = self.with_body_ty_lowering(|ctx| {
|
||||
let mut path_ctx = ctx.at_path(path, id);
|
||||
let mut path_ctx = ctx.at_path(path, id, GenericArgsPosition::Value);
|
||||
let last_segment = path.segments().len().checked_sub(1);
|
||||
if let Some(last_segment) = last_segment {
|
||||
path_ctx.set_current_segment(last_segment)
|
||||
@ -163,9 +164,9 @@ impl InferenceContext<'_> {
|
||||
self.generic_def,
|
||||
);
|
||||
let mut path_ctx = if no_diagnostics {
|
||||
ctx.at_path_forget_diagnostics(path)
|
||||
ctx.at_path_forget_diagnostics(path, GenericArgsPosition::Value)
|
||||
} else {
|
||||
ctx.at_path(path, id)
|
||||
ctx.at_path(path, id, GenericArgsPosition::Value)
|
||||
};
|
||||
let (value, self_subst) = if let Some(type_ref) = path.type_anchor() {
|
||||
let last = path.segments().last()?;
|
||||
|
@ -347,20 +347,24 @@ pub(crate) fn make_binders<T: HasInterner<Interner = Interner>>(
|
||||
generics: &Generics,
|
||||
value: T,
|
||||
) -> Binders<T> {
|
||||
Binders::new(
|
||||
VariableKinds::from_iter(
|
||||
Interner,
|
||||
generics.iter_id().map(|x| match x {
|
||||
hir_def::GenericParamId::ConstParamId(id) => {
|
||||
chalk_ir::VariableKind::Const(db.const_param_ty(id))
|
||||
}
|
||||
hir_def::GenericParamId::TypeParamId(_) => {
|
||||
chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General)
|
||||
}
|
||||
hir_def::GenericParamId::LifetimeParamId(_) => chalk_ir::VariableKind::Lifetime,
|
||||
}),
|
||||
),
|
||||
value,
|
||||
Binders::new(variable_kinds_from_iter(db, generics.iter_id()), value)
|
||||
}
|
||||
|
||||
pub(crate) fn variable_kinds_from_iter(
|
||||
db: &dyn HirDatabase,
|
||||
iter: impl Iterator<Item = hir_def::GenericParamId>,
|
||||
) -> VariableKinds {
|
||||
VariableKinds::from_iter(
|
||||
Interner,
|
||||
iter.map(|x| match x {
|
||||
hir_def::GenericParamId::ConstParamId(id) => {
|
||||
chalk_ir::VariableKind::Const(db.const_param_ty(id))
|
||||
}
|
||||
hir_def::GenericParamId::TypeParamId(_) => {
|
||||
chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General)
|
||||
}
|
||||
hir_def::GenericParamId::LifetimeParamId(_) => chalk_ir::VariableKind::Lifetime,
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -28,10 +28,7 @@ use hir_def::{
|
||||
FunctionId, GenericDefId, GenericParamId, HasModule, ImplId, LocalFieldId, Lookup, StaticId,
|
||||
StructId, TypeAliasId, TypeOrConstParamId, UnionId, VariantId,
|
||||
builtin_type::BuiltinType,
|
||||
expr_store::{
|
||||
ExpressionStore,
|
||||
path::{GenericArg, Path},
|
||||
},
|
||||
expr_store::{ExpressionStore, path::Path},
|
||||
hir::generics::{
|
||||
GenericParamDataRef, TypeOrConstParamData, WherePredicate, WherePredicateTypeTarget,
|
||||
},
|
||||
@ -55,9 +52,9 @@ use triomphe::{Arc, ThinArc};
|
||||
use crate::{
|
||||
AliasTy, Binders, BoundVar, CallableSig, Const, 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, all_super_traits,
|
||||
LifetimeOutlives, PolyFnSig, ProgramClause, QuantifiedWhereClause, QuantifiedWhereClauses,
|
||||
Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder, TyKind, WhereClause,
|
||||
all_super_traits,
|
||||
consteval::{intern_const_ref, path_to_const, unknown_const, unknown_const_as_generic},
|
||||
db::HirDatabase,
|
||||
error_lifetime,
|
||||
@ -70,6 +67,7 @@ use crate::{
|
||||
mapping::{ToChalk, from_chalk_trait_id, lt_to_placeholder_idx},
|
||||
static_lifetime, to_chalk_trait_id, to_placeholder_idx,
|
||||
utils::all_super_trait_refs,
|
||||
variable_kinds_from_iter,
|
||||
};
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
@ -89,6 +87,22 @@ impl ImplTraitLoweringState {
|
||||
|
||||
pub(crate) struct PathDiagnosticCallbackData(TypeRefId);
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub(crate) enum GenericArgsPosition {
|
||||
Type,
|
||||
/// E.g. functions.
|
||||
Value,
|
||||
MethodCall,
|
||||
// FIXME: This is a temporary variant we need to work around the lack of lifetime elision.
|
||||
// The reason for its existence is that in `check_generic_args_len()`, without this, we will
|
||||
// not infer elide lifetimes.
|
||||
// They indeed should not be inferred - they should be elided - but we won't elide them either,
|
||||
// emitting an error instead. rustc elides them in late resolve, and the generics it passes
|
||||
// to lowering already include them. We probably can't do that, but we will still need to
|
||||
// account for them when we properly implement lifetime elision.
|
||||
FnSignature,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TyLoweringContext<'a> {
|
||||
pub db: &'a dyn HirDatabase,
|
||||
@ -106,6 +120,7 @@ pub struct TyLoweringContext<'a> {
|
||||
/// Tracks types with explicit `?Sized` bounds.
|
||||
pub(crate) unsized_types: FxHashSet<Ty>,
|
||||
pub(crate) diagnostics: Vec<TyLoweringDiagnostic>,
|
||||
pub(crate) in_fn_signature: bool,
|
||||
}
|
||||
|
||||
impl<'a> TyLoweringContext<'a> {
|
||||
@ -129,6 +144,7 @@ impl<'a> TyLoweringContext<'a> {
|
||||
type_param_mode,
|
||||
unsized_types: FxHashSet::default(),
|
||||
diagnostics: Vec::new(),
|
||||
in_fn_signature: false,
|
||||
}
|
||||
}
|
||||
|
||||
@ -415,6 +431,11 @@ impl<'a> TyLoweringContext<'a> {
|
||||
self,
|
||||
Self::on_path_diagnostic_callback(path_id.type_ref()),
|
||||
&self.store[path_id],
|
||||
if self.in_fn_signature {
|
||||
GenericArgsPosition::FnSignature
|
||||
} else {
|
||||
GenericArgsPosition::Type
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@ -1172,22 +1193,30 @@ pub(crate) fn generic_defaults_with_diagnostics_query(
|
||||
.with_impl_trait_mode(ImplTraitLoweringMode::Disallowed)
|
||||
.with_type_param_mode(ParamLoweringMode::Variable);
|
||||
let mut idx = 0;
|
||||
let mut has_any_default = false;
|
||||
let mut defaults = generic_params
|
||||
.iter_self()
|
||||
.map(|(id, p)| {
|
||||
let result = handle_generic_param(&mut ctx, idx, id, p, &generic_params);
|
||||
.iter_parents_with_store()
|
||||
.map(|((id, p), store)| {
|
||||
ctx.store = store;
|
||||
let (result, has_default) = handle_generic_param(&mut ctx, idx, id, p, &generic_params);
|
||||
has_any_default |= has_default;
|
||||
idx += 1;
|
||||
result
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let diagnostics = create_diagnostics(mem::take(&mut ctx.diagnostics));
|
||||
defaults.extend(generic_params.iter_parents_with_store().map(|((id, p), store)| {
|
||||
ctx.store = store;
|
||||
let result = handle_generic_param(&mut ctx, idx, id, p, &generic_params);
|
||||
ctx.diagnostics.clear(); // Don't include diagnostics from the parent.
|
||||
defaults.extend(generic_params.iter_self().map(|(id, p)| {
|
||||
let (result, has_default) = handle_generic_param(&mut ctx, idx, id, p, &generic_params);
|
||||
has_any_default |= has_default;
|
||||
idx += 1;
|
||||
result
|
||||
}));
|
||||
let defaults = GenericDefaults(Some(Arc::from_iter(defaults)));
|
||||
let diagnostics = create_diagnostics(mem::take(&mut ctx.diagnostics));
|
||||
let defaults = if has_any_default {
|
||||
GenericDefaults(Some(Arc::from_iter(defaults)))
|
||||
} else {
|
||||
GenericDefaults(None)
|
||||
};
|
||||
return (defaults, diagnostics);
|
||||
|
||||
fn handle_generic_param(
|
||||
@ -1196,16 +1225,20 @@ pub(crate) fn generic_defaults_with_diagnostics_query(
|
||||
id: GenericParamId,
|
||||
p: GenericParamDataRef<'_>,
|
||||
generic_params: &Generics,
|
||||
) -> Binders<crate::GenericArg> {
|
||||
) -> (Binders<crate::GenericArg>, bool) {
|
||||
let binders = variable_kinds_from_iter(ctx.db, generic_params.iter_id().take(idx));
|
||||
match p {
|
||||
GenericParamDataRef::TypeParamData(p) => {
|
||||
let ty = p.default.as_ref().map_or(TyKind::Error.intern(Interner), |ty| {
|
||||
// Each default can only refer to previous parameters.
|
||||
// Type variable default referring to parameter coming
|
||||
// after it is forbidden (FIXME: report diagnostic)
|
||||
fallback_bound_vars(ctx.lower_ty(*ty), idx, 0)
|
||||
});
|
||||
crate::make_binders(ctx.db, generic_params, ty.cast(Interner))
|
||||
let ty = p.default.as_ref().map_or_else(
|
||||
|| TyKind::Error.intern(Interner),
|
||||
|ty| {
|
||||
// Each default can only refer to previous parameters.
|
||||
// Type variable default referring to parameter coming
|
||||
// after it is forbidden (FIXME: report diagnostic)
|
||||
fallback_bound_vars(ctx.lower_ty(*ty), idx)
|
||||
},
|
||||
);
|
||||
(Binders::new(binders, ty.cast(Interner)), p.default.is_some())
|
||||
}
|
||||
GenericParamDataRef::ConstParamData(p) => {
|
||||
let GenericParamId::ConstParamId(id) = id else {
|
||||
@ -1221,36 +1254,22 @@ pub(crate) fn generic_defaults_with_diagnostics_query(
|
||||
},
|
||||
);
|
||||
// Each default can only refer to previous parameters, see above.
|
||||
val = fallback_bound_vars(val, idx, 0);
|
||||
make_binders(ctx.db, generic_params, val)
|
||||
val = fallback_bound_vars(val, idx);
|
||||
(Binders::new(binders, val), p.default.is_some())
|
||||
}
|
||||
GenericParamDataRef::LifetimeParamData(_) => {
|
||||
make_binders(ctx.db, generic_params, error_lifetime().cast(Interner))
|
||||
(Binders::new(binders, error_lifetime().cast(Interner)), false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn generic_defaults_with_diagnostics_recover(
|
||||
db: &dyn HirDatabase,
|
||||
_db: &dyn HirDatabase,
|
||||
_cycle: &Cycle,
|
||||
def: GenericDefId,
|
||||
_def: GenericDefId,
|
||||
) -> (GenericDefaults, Diagnostics) {
|
||||
let generic_params = generics(db, def);
|
||||
if generic_params.len() == 0 {
|
||||
return (GenericDefaults(None), None);
|
||||
}
|
||||
// FIXME: this code is not covered in tests.
|
||||
// we still need one default per parameter
|
||||
let defaults = GenericDefaults(Some(Arc::from_iter(generic_params.iter_id().map(|id| {
|
||||
let val = match id {
|
||||
GenericParamId::TypeParamId(_) => TyKind::Error.intern(Interner).cast(Interner),
|
||||
GenericParamId::ConstParamId(id) => unknown_const_as_generic(db.const_param_ty(id)),
|
||||
GenericParamId::LifetimeParamId(_) => error_lifetime().cast(Interner),
|
||||
};
|
||||
crate::make_binders(db, &generic_params, val)
|
||||
}))));
|
||||
(defaults, None)
|
||||
(GenericDefaults(None), None)
|
||||
}
|
||||
|
||||
fn fn_sig_for_fn(db: &dyn HirDatabase, def: FunctionId) -> PolyFnSig {
|
||||
@ -1258,6 +1277,7 @@ fn fn_sig_for_fn(db: &dyn HirDatabase, def: FunctionId) -> PolyFnSig {
|
||||
let resolver = def.resolver(db);
|
||||
let mut ctx_params = TyLoweringContext::new(db, &resolver, &data.store, def.into())
|
||||
.with_type_param_mode(ParamLoweringMode::Variable);
|
||||
ctx_params.in_fn_signature = true;
|
||||
let params = data.params.iter().map(|&tr| ctx_params.lower_ty(tr));
|
||||
|
||||
let ret = match data.ret_type {
|
||||
@ -1265,6 +1285,7 @@ fn fn_sig_for_fn(db: &dyn HirDatabase, def: FunctionId) -> PolyFnSig {
|
||||
let mut ctx_ret = TyLoweringContext::new(db, &resolver, &data.store, def.into())
|
||||
.with_impl_trait_mode(ImplTraitLoweringMode::Opaque)
|
||||
.with_type_param_mode(ParamLoweringMode::Variable);
|
||||
ctx_ret.in_fn_signature = true;
|
||||
ctx_ret.lower_ty(ret_type)
|
||||
}
|
||||
None => TyKind::Tuple(0, Substitution::empty(Interner)).intern(Interner),
|
||||
@ -1612,73 +1633,14 @@ pub(crate) fn lower_to_chalk_mutability(m: hir_def::type_ref::Mutability) -> Mut
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if the provided generic arg matches its expected kind, then lower them via
|
||||
/// provided closures. Use unknown if there was kind mismatch.
|
||||
///
|
||||
pub(crate) fn generic_arg_to_chalk<'a, T>(
|
||||
db: &dyn HirDatabase,
|
||||
kind_id: GenericParamId,
|
||||
arg: &'a GenericArg,
|
||||
this: &mut T,
|
||||
store: &ExpressionStore,
|
||||
for_type: impl FnOnce(&mut T, TypeRefId) -> Ty + 'a,
|
||||
for_const: impl FnOnce(&mut T, &ConstRef, Ty) -> Const + 'a,
|
||||
for_const_ty_path_fallback: impl FnOnce(&mut T, &Path, Ty) -> Const + 'a,
|
||||
for_lifetime: impl FnOnce(&mut T, &LifetimeRef) -> Lifetime + 'a,
|
||||
) -> crate::GenericArg {
|
||||
let kind = match kind_id {
|
||||
GenericParamId::TypeParamId(_) => ParamKind::Type,
|
||||
GenericParamId::ConstParamId(id) => {
|
||||
let ty = db.const_param_ty(id);
|
||||
ParamKind::Const(ty)
|
||||
}
|
||||
GenericParamId::LifetimeParamId(_) => ParamKind::Lifetime,
|
||||
};
|
||||
match (arg, kind) {
|
||||
(GenericArg::Type(type_ref), ParamKind::Type) => for_type(this, *type_ref).cast(Interner),
|
||||
(GenericArg::Const(c), ParamKind::Const(c_ty)) => for_const(this, c, c_ty).cast(Interner),
|
||||
(GenericArg::Lifetime(lifetime_ref), ParamKind::Lifetime) => {
|
||||
for_lifetime(this, lifetime_ref).cast(Interner)
|
||||
}
|
||||
(GenericArg::Const(_), ParamKind::Type) => TyKind::Error.intern(Interner).cast(Interner),
|
||||
(GenericArg::Lifetime(_), ParamKind::Type) => TyKind::Error.intern(Interner).cast(Interner),
|
||||
(GenericArg::Type(t), ParamKind::Const(c_ty)) => match &store[*t] {
|
||||
TypeRef::Path(p) => for_const_ty_path_fallback(this, p, c_ty).cast(Interner),
|
||||
_ => unknown_const_as_generic(c_ty),
|
||||
},
|
||||
(GenericArg::Lifetime(_), ParamKind::Const(c_ty)) => unknown_const_as_generic(c_ty),
|
||||
(GenericArg::Type(_), ParamKind::Lifetime) => error_lifetime().cast(Interner),
|
||||
(GenericArg::Const(_), ParamKind::Lifetime) => error_lifetime().cast(Interner),
|
||||
}
|
||||
}
|
||||
|
||||
/// Replaces any 'free' `BoundVar`s in `s` by `TyKind::Error` from the perspective of generic
|
||||
/// parameter whose index is `param_index`. A `BoundVar` is free when it is or (syntactically)
|
||||
/// appears after the generic parameter of `param_index`.
|
||||
/// parameter whose index is `param_index`. A `BoundVar` is free when it appears after the
|
||||
/// generic parameter of `param_index`.
|
||||
fn fallback_bound_vars<T: TypeFoldable<Interner> + HasInterner<Interner = Interner>>(
|
||||
s: T,
|
||||
param_index: usize,
|
||||
parent_start: usize,
|
||||
) -> T {
|
||||
// Keep in mind that parent generic parameters, if any, come *after* those of the item in
|
||||
// question. In the diagrams below, `c*` and `p*` represent generic parameters of the item and
|
||||
// its parent respectively.
|
||||
let is_allowed = |index| {
|
||||
if param_index < parent_start {
|
||||
// The parameter of `param_index` is one from the item in question. Any parent generic
|
||||
// parameters or the item's generic parameters that come before `param_index` is
|
||||
// allowed.
|
||||
// [c1, .., cj, .., ck, p1, .., pl] where cj is `param_index`
|
||||
// ^^^^^^ ^^^^^^^^^^ these are allowed
|
||||
!(param_index..parent_start).contains(&index)
|
||||
} else {
|
||||
// The parameter of `param_index` is one from the parent generics. Only parent generic
|
||||
// parameters that come before `param_index` are allowed.
|
||||
// [c1, .., ck, p1, .., pj, .., pl] where pj is `param_index`
|
||||
// ^^^^^^ these are allowed
|
||||
(parent_start..param_index).contains(&index)
|
||||
}
|
||||
};
|
||||
let is_allowed = |index| (0..param_index).contains(&index);
|
||||
|
||||
crate::fold_free_vars(
|
||||
s,
|
||||
|
@ -1,6 +1,7 @@
|
||||
//! This files contains the declaration of diagnostics kinds for ty and path lowering.
|
||||
|
||||
use hir_def::type_ref::TypeRefId;
|
||||
use hir_def::{GenericDefId, GenericParamId};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
pub struct TyLoweringDiagnostic {
|
||||
@ -21,13 +22,51 @@ pub enum GenericArgsProhibitedReason {
|
||||
PrimitiveTy,
|
||||
Const,
|
||||
Static,
|
||||
LocalVariable,
|
||||
/// When there is a generic enum, within the expression `Enum::Variant`,
|
||||
/// either `Enum` or `Variant` are allowed to have generic arguments, but not both.
|
||||
EnumVariant,
|
||||
}
|
||||
|
||||
/// A path can have many generic arguments: each segment may have one associated with the
|
||||
/// segment, and in addition, each associated type binding may have generic arguments. This
|
||||
/// enum abstracts over both.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum PathGenericsSource {
|
||||
/// Generic arguments directly on the segment.
|
||||
Segment(u32),
|
||||
/// Generic arguments on an associated type, e.g. `Foo<Assoc<A, B> = C>` or `Foo<Assoc<A, B>: Bound>`.
|
||||
AssocType { segment: u32, assoc_type: u32 },
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
pub enum PathLoweringDiagnostic {
|
||||
GenericArgsProhibited { segment: u32, reason: GenericArgsProhibitedReason },
|
||||
ParenthesizedGenericArgsWithoutFnTrait { segment: u32 },
|
||||
GenericArgsProhibited {
|
||||
segment: u32,
|
||||
reason: GenericArgsProhibitedReason,
|
||||
},
|
||||
ParenthesizedGenericArgsWithoutFnTrait {
|
||||
segment: u32,
|
||||
},
|
||||
/// The expected lifetimes & types and consts counts can be found by inspecting the `GenericDefId`.
|
||||
IncorrectGenericsLen {
|
||||
generics_source: PathGenericsSource,
|
||||
provided_count: u32,
|
||||
expected_count: u32,
|
||||
kind: IncorrectGenericsLenKind,
|
||||
def: GenericDefId,
|
||||
},
|
||||
IncorrectGenericsOrder {
|
||||
generics_source: PathGenericsSource,
|
||||
param_id: GenericParamId,
|
||||
arg_idx: u32,
|
||||
/// Whether the `GenericArgs` contains a `Self` arg.
|
||||
has_self_arg: bool,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum IncorrectGenericsLenKind {
|
||||
Lifetimes,
|
||||
TypesAndConsts,
|
||||
}
|
||||
|
@ -1,30 +1,33 @@
|
||||
//! A wrapper around [`TyLoweringContext`] specifically for lowering paths.
|
||||
|
||||
use std::iter;
|
||||
|
||||
use chalk_ir::{BoundVar, cast::Cast, fold::Shift};
|
||||
use either::Either;
|
||||
use hir_def::{
|
||||
GenericDefId, GenericParamId, ItemContainerId, Lookup, TraitId,
|
||||
GenericDefId, GenericParamId, Lookup, TraitId,
|
||||
expr_store::{
|
||||
HygieneId,
|
||||
ExpressionStore, HygieneId,
|
||||
path::{GenericArg, GenericArgs, GenericArgsParentheses, Path, PathSegment, PathSegments},
|
||||
},
|
||||
hir::generics::{
|
||||
GenericParamDataRef, TypeOrConstParamData, TypeParamData, TypeParamProvenance,
|
||||
},
|
||||
resolver::{ResolveValueResult, TypeNs, ValueNs},
|
||||
signatures::TraitFlags,
|
||||
type_ref::TypeRef,
|
||||
type_ref::{TypeRef, TypeRefId},
|
||||
};
|
||||
use smallvec::SmallVec;
|
||||
use stdx::never;
|
||||
|
||||
use crate::{
|
||||
AliasEq, AliasTy, GenericArgsProhibitedReason, ImplTraitLoweringMode, Interner,
|
||||
ParamLoweringMode, PathLoweringDiagnostic, ProjectionTy, QuantifiedWhereClause, Substitution,
|
||||
TraitRef, Ty, TyBuilder, TyDefId, TyKind, TyLoweringContext, ValueTyDefId, WhereClause,
|
||||
consteval::unknown_const_as_generic,
|
||||
AliasEq, AliasTy, GenericArgsProhibitedReason, ImplTraitLoweringMode, IncorrectGenericsLenKind,
|
||||
Interner, ParamLoweringMode, PathGenericsSource, PathLoweringDiagnostic, ProjectionTy,
|
||||
QuantifiedWhereClause, Substitution, TraitRef, Ty, TyBuilder, TyDefId, TyKind,
|
||||
TyLoweringContext, ValueTyDefId, WhereClause,
|
||||
consteval::{unknown_const, unknown_const_as_generic},
|
||||
db::HirDatabase,
|
||||
error_lifetime,
|
||||
generics::generics,
|
||||
lower::{generic_arg_to_chalk, named_associated_type_shorthand_candidates},
|
||||
generics::{Generics, generics},
|
||||
lower::{GenericArgsPosition, named_associated_type_shorthand_candidates},
|
||||
to_assoc_type_id, to_chalk_trait_id, to_placeholder_idx,
|
||||
utils::associated_type_by_name_including_super_traits,
|
||||
};
|
||||
@ -49,6 +52,7 @@ pub(crate) struct PathLoweringContext<'a, 'b> {
|
||||
current_segment_idx: usize,
|
||||
/// Contains the previous segment if `current_segment_idx == segments.len()`
|
||||
current_or_prev_segment: PathSegment<'a>,
|
||||
position: GenericArgsPosition,
|
||||
}
|
||||
|
||||
impl<'a, 'b> PathLoweringContext<'a, 'b> {
|
||||
@ -57,6 +61,7 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> {
|
||||
ctx: &'a mut TyLoweringContext<'b>,
|
||||
on_diagnostic: PathDiagnosticCallback<'a>,
|
||||
path: &'a Path,
|
||||
position: GenericArgsPosition,
|
||||
) -> Self {
|
||||
let segments = path.segments();
|
||||
let first_segment = segments.first().unwrap_or(PathSegment::MISSING);
|
||||
@ -67,6 +72,7 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> {
|
||||
segments,
|
||||
current_segment_idx: 0,
|
||||
current_or_prev_segment: first_segment,
|
||||
position,
|
||||
}
|
||||
}
|
||||
|
||||
@ -449,14 +455,19 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> {
|
||||
// and statics can be generic, or just because it was easier for rustc implementors.
|
||||
// That means we'll show the wrong error code. Because of us it's easier to do it
|
||||
// this way :)
|
||||
ValueNs::GenericParam(_) | ValueNs::ConstId(_) => {
|
||||
ValueNs::GenericParam(_) => {
|
||||
prohibit_generics_on_resolved(GenericArgsProhibitedReason::Const)
|
||||
}
|
||||
ValueNs::StaticId(_) => {
|
||||
prohibit_generics_on_resolved(GenericArgsProhibitedReason::Static)
|
||||
}
|
||||
ValueNs::FunctionId(_) | ValueNs::StructId(_) | ValueNs::EnumVariantId(_) => {}
|
||||
ValueNs::LocalBinding(_) => {}
|
||||
ValueNs::LocalBinding(_) => {
|
||||
prohibit_generics_on_resolved(GenericArgsProhibitedReason::LocalVariable)
|
||||
}
|
||||
ValueNs::FunctionId(_)
|
||||
| ValueNs::StructId(_)
|
||||
| ValueNs::EnumVariantId(_)
|
||||
| ValueNs::ConstId(_) => {}
|
||||
}
|
||||
}
|
||||
ResolveValueResult::Partial(resolution, _, _) => {
|
||||
@ -615,6 +626,7 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> {
|
||||
def,
|
||||
infer_args,
|
||||
explicit_self_ty,
|
||||
PathGenericsSource::Segment(self.current_segment_u32()),
|
||||
)
|
||||
}
|
||||
|
||||
@ -624,144 +636,143 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> {
|
||||
def: GenericDefId,
|
||||
infer_args: bool,
|
||||
explicit_self_ty: Option<Ty>,
|
||||
generics_source: PathGenericsSource,
|
||||
) -> Substitution {
|
||||
// Order is
|
||||
// - Parent parameters
|
||||
// - Optional Self parameter
|
||||
// - Lifetime parameters
|
||||
// - Type or Const parameters
|
||||
let def_generics = generics(self.ctx.db, 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;
|
||||
struct LowererCtx<'a, 'b, 'c> {
|
||||
ctx: &'a mut PathLoweringContext<'b, 'c>,
|
||||
generics_source: PathGenericsSource,
|
||||
}
|
||||
|
||||
let ty_error = || TyKind::Error.intern(Interner).cast(Interner);
|
||||
let param_to_err = |id| match id {
|
||||
GenericParamId::ConstParamId(x) => {
|
||||
unknown_const_as_generic(self.ctx.db.const_param_ty(x))
|
||||
impl GenericArgsLowerer for LowererCtx<'_, '_, '_> {
|
||||
fn report_len_mismatch(
|
||||
&mut self,
|
||||
def: GenericDefId,
|
||||
provided_count: u32,
|
||||
expected_count: u32,
|
||||
kind: IncorrectGenericsLenKind,
|
||||
) {
|
||||
self.ctx.on_diagnostic(PathLoweringDiagnostic::IncorrectGenericsLen {
|
||||
generics_source: self.generics_source,
|
||||
provided_count,
|
||||
expected_count,
|
||||
kind,
|
||||
def,
|
||||
});
|
||||
}
|
||||
GenericParamId::TypeParamId(_) => ty_error(),
|
||||
GenericParamId::LifetimeParamId(_) => error_lifetime().cast(Interner),
|
||||
};
|
||||
|
||||
let mut substs: Vec<_> = def_generics.iter_parent_id().map(param_to_err).collect();
|
||||
fn report_arg_mismatch(
|
||||
&mut self,
|
||||
param_id: GenericParamId,
|
||||
arg_idx: u32,
|
||||
has_self_arg: bool,
|
||||
) {
|
||||
self.ctx.on_diagnostic(PathLoweringDiagnostic::IncorrectGenericsOrder {
|
||||
generics_source: self.generics_source,
|
||||
param_id,
|
||||
arg_idx,
|
||||
has_self_arg,
|
||||
});
|
||||
}
|
||||
|
||||
tracing::debug!(?substs, ?parent_params);
|
||||
|
||||
// we need to iterate the lifetime and type/const params separately as our order of them
|
||||
// differs from the supplied syntax
|
||||
|
||||
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);
|
||||
fn provided_kind(
|
||||
&mut self,
|
||||
param_id: GenericParamId,
|
||||
param: GenericParamDataRef<'_>,
|
||||
arg: &GenericArg,
|
||||
) -> crate::GenericArg {
|
||||
match (param, arg) {
|
||||
(GenericParamDataRef::LifetimeParamData(_), GenericArg::Lifetime(lifetime)) => {
|
||||
self.ctx.ctx.lower_lifetime(lifetime).cast(Interner)
|
||||
}
|
||||
(GenericParamDataRef::TypeParamData(_), GenericArg::Type(type_ref)) => {
|
||||
self.ctx.ctx.lower_ty(*type_ref).cast(Interner)
|
||||
}
|
||||
(GenericParamDataRef::ConstParamData(_), GenericArg::Const(konst)) => {
|
||||
let GenericParamId::ConstParamId(const_id) = param_id else {
|
||||
unreachable!("non-const param ID for const param");
|
||||
};
|
||||
self.ctx
|
||||
.ctx
|
||||
.lower_const(konst, self.ctx.ctx.db.const_param_ty(const_id))
|
||||
.cast(Interner)
|
||||
}
|
||||
_ => unreachable!("unmatching param kinds were passed to `provided_kind()`"),
|
||||
}
|
||||
}
|
||||
};
|
||||
let mut had_explicit_args = false;
|
||||
|
||||
if let Some(&GenericArgs { ref args, has_self_type, .. }) = args_and_bindings {
|
||||
// Fill in the self param first
|
||||
if has_self_type && self_param {
|
||||
had_explicit_args = true;
|
||||
if let Some(id) = def_toc_iter.next() {
|
||||
assert!(matches!(id, GenericParamId::TypeParamId(_)));
|
||||
had_explicit_args = true;
|
||||
if let GenericArg::Type(ty) = &args[0] {
|
||||
substs.push(self.ctx.lower_ty(*ty).cast(Interner));
|
||||
fn provided_type_like_const(
|
||||
&mut self,
|
||||
const_ty: Ty,
|
||||
arg: TypeLikeConst<'_>,
|
||||
) -> crate::Const {
|
||||
match arg {
|
||||
TypeLikeConst::Path(path) => self.ctx.ctx.lower_path_as_const(path, const_ty),
|
||||
TypeLikeConst::Infer => unknown_const(const_ty),
|
||||
}
|
||||
}
|
||||
|
||||
fn inferred_kind(
|
||||
&mut self,
|
||||
def: GenericDefId,
|
||||
param_id: GenericParamId,
|
||||
param: GenericParamDataRef<'_>,
|
||||
infer_args: bool,
|
||||
preceding_args: &[crate::GenericArg],
|
||||
) -> crate::GenericArg {
|
||||
let default = || {
|
||||
self.ctx
|
||||
.ctx
|
||||
.db
|
||||
.generic_defaults(def)
|
||||
.get(preceding_args.len())
|
||||
.map(|default| default.clone().substitute(Interner, preceding_args))
|
||||
};
|
||||
match param {
|
||||
GenericParamDataRef::LifetimeParamData(_) => error_lifetime().cast(Interner),
|
||||
GenericParamDataRef::TypeParamData(param) => {
|
||||
if !infer_args && param.default.is_some() {
|
||||
if let Some(default) = default() {
|
||||
return default;
|
||||
}
|
||||
}
|
||||
TyKind::Error.intern(Interner).cast(Interner)
|
||||
}
|
||||
GenericParamDataRef::ConstParamData(param) => {
|
||||
if !infer_args && param.default.is_some() {
|
||||
if let Some(default) = default() {
|
||||
return default;
|
||||
}
|
||||
}
|
||||
let GenericParamId::ConstParamId(const_id) = param_id else {
|
||||
unreachable!("non-const param ID for const param");
|
||||
};
|
||||
unknown_const_as_generic(self.ctx.ctx.db.const_param_ty(const_id))
|
||||
.cast(Interner)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
fill_self_param()
|
||||
};
|
||||
|
||||
// Then fill in the supplied lifetime args, or error lifetimes if there are too few
|
||||
// (default lifetimes aren't a thing)
|
||||
for arg in args
|
||||
.iter()
|
||||
.filter_map(|arg| match arg {
|
||||
GenericArg::Lifetime(arg) => Some(self.ctx.lower_lifetime(arg)),
|
||||
_ => None,
|
||||
})
|
||||
.chain(iter::repeat(error_lifetime()))
|
||||
.take(lifetime_params)
|
||||
{
|
||||
substs.push(arg.cast(Interner));
|
||||
}
|
||||
|
||||
let skip = if has_self_type { 1 } else { 0 };
|
||||
// Fill in supplied type and const args
|
||||
// Note if non-lifetime args are provided, it should be all of them, but we can't rely on that
|
||||
for (arg, id) in args
|
||||
.iter()
|
||||
.filter(|arg| !matches!(arg, GenericArg::Lifetime(_)))
|
||||
.skip(skip)
|
||||
.take(type_params + const_params)
|
||||
.zip(def_toc_iter)
|
||||
{
|
||||
had_explicit_args = true;
|
||||
let arg = generic_arg_to_chalk(
|
||||
self.ctx.db,
|
||||
id,
|
||||
arg,
|
||||
self.ctx,
|
||||
self.ctx.store,
|
||||
|ctx, type_ref| ctx.lower_ty(type_ref),
|
||||
|ctx, const_ref, ty| ctx.lower_const(const_ref, ty),
|
||||
|ctx, path, ty| ctx.lower_path_as_const(path, ty),
|
||||
|ctx, lifetime_ref| ctx.lower_lifetime(lifetime_ref),
|
||||
);
|
||||
substs.push(arg);
|
||||
fn parent_arg(&mut self, param_id: GenericParamId) -> crate::GenericArg {
|
||||
match param_id {
|
||||
GenericParamId::TypeParamId(_) => TyKind::Error.intern(Interner).cast(Interner),
|
||||
GenericParamId::ConstParamId(const_id) => {
|
||||
unknown_const_as_generic(self.ctx.ctx.db.const_param_ty(const_id))
|
||||
}
|
||||
GenericParamId::LifetimeParamId(_) => error_lifetime().cast(Interner),
|
||||
}
|
||||
}
|
||||
} else {
|
||||
fill_self_param();
|
||||
}
|
||||
|
||||
// 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).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 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 defaults[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));
|
||||
}
|
||||
} else {
|
||||
// Fill in remaining def params and parent params
|
||||
substs.extend(def_generics.iter_id().skip(substs.len()).map(param_to_err));
|
||||
}
|
||||
|
||||
assert_eq!(substs.len(), total_len, "expected {} substs, got {}", total_len, substs.len());
|
||||
Substitution::from_iter(Interner, substs)
|
||||
substs_from_args_and_bindings(
|
||||
self.ctx.db,
|
||||
self.ctx.store,
|
||||
args_and_bindings,
|
||||
def,
|
||||
infer_args,
|
||||
self.position,
|
||||
explicit_self_ty,
|
||||
&mut LowererCtx { ctx: self, generics_source },
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn lower_trait_ref_from_resolved_path(
|
||||
@ -786,7 +797,7 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> {
|
||||
trait_ref: TraitRef,
|
||||
) -> Option<impl Iterator<Item = QuantifiedWhereClause> + use<'a, 'b, 'c>> {
|
||||
self.current_or_prev_segment.args_and_bindings.map(|args_and_bindings| {
|
||||
args_and_bindings.bindings.iter().flat_map(move |binding| {
|
||||
args_and_bindings.bindings.iter().enumerate().flat_map(move |(binding_idx, binding)| {
|
||||
let found = associated_type_by_name_including_super_traits(
|
||||
self.ctx.db,
|
||||
trait_ref.clone(),
|
||||
@ -805,6 +816,10 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> {
|
||||
associated_ty.into(),
|
||||
false, // this is not relevant
|
||||
Some(super_trait_ref.self_type_parameter(Interner)),
|
||||
PathGenericsSource::AssocType {
|
||||
segment: self.current_segment_u32(),
|
||||
assoc_type: binding_idx as u32,
|
||||
},
|
||||
);
|
||||
let substitution = Substitution::from_iter(
|
||||
Interner,
|
||||
@ -845,3 +860,288 @@ impl<'a, 'b> PathLoweringContext<'a, 'b> {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// A const that were parsed like a type.
|
||||
pub(crate) enum TypeLikeConst<'a> {
|
||||
Infer,
|
||||
Path(&'a Path),
|
||||
}
|
||||
|
||||
pub(crate) trait GenericArgsLowerer {
|
||||
fn report_len_mismatch(
|
||||
&mut self,
|
||||
def: GenericDefId,
|
||||
provided_count: u32,
|
||||
expected_count: u32,
|
||||
kind: IncorrectGenericsLenKind,
|
||||
);
|
||||
|
||||
fn report_arg_mismatch(&mut self, param_id: GenericParamId, arg_idx: u32, has_self_arg: bool);
|
||||
|
||||
fn provided_kind(
|
||||
&mut self,
|
||||
param_id: GenericParamId,
|
||||
param: GenericParamDataRef<'_>,
|
||||
arg: &GenericArg,
|
||||
) -> crate::GenericArg;
|
||||
|
||||
fn provided_type_like_const(&mut self, const_ty: Ty, arg: TypeLikeConst<'_>) -> crate::Const;
|
||||
|
||||
fn inferred_kind(
|
||||
&mut self,
|
||||
def: GenericDefId,
|
||||
param_id: GenericParamId,
|
||||
param: GenericParamDataRef<'_>,
|
||||
infer_args: bool,
|
||||
preceding_args: &[crate::GenericArg],
|
||||
) -> crate::GenericArg;
|
||||
|
||||
fn parent_arg(&mut self, param_id: GenericParamId) -> crate::GenericArg;
|
||||
}
|
||||
|
||||
/// Returns true if there was an error.
|
||||
fn check_generic_args_len(
|
||||
args_and_bindings: Option<&GenericArgs>,
|
||||
def: GenericDefId,
|
||||
def_generics: &Generics,
|
||||
infer_args: bool,
|
||||
position: GenericArgsPosition,
|
||||
ctx: &mut impl GenericArgsLowerer,
|
||||
) -> bool {
|
||||
let mut had_error = false;
|
||||
|
||||
let (mut provided_lifetimes_count, mut provided_types_and_consts_count) = (0usize, 0usize);
|
||||
if let Some(args_and_bindings) = args_and_bindings {
|
||||
let args_no_self = &args_and_bindings.args[usize::from(args_and_bindings.has_self_type)..];
|
||||
for arg in args_no_self {
|
||||
match arg {
|
||||
GenericArg::Lifetime(_) => provided_lifetimes_count += 1,
|
||||
GenericArg::Type(_) | GenericArg::Const(_) => provided_types_and_consts_count += 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let infer_lifetimes =
|
||||
(position != GenericArgsPosition::Type || infer_args) && provided_lifetimes_count == 0;
|
||||
|
||||
let min_expected_lifetime_args =
|
||||
if infer_lifetimes { 0 } else { def_generics.len_lifetimes_self() };
|
||||
let max_expected_lifetime_args = def_generics.len_lifetimes_self();
|
||||
if !(min_expected_lifetime_args..=max_expected_lifetime_args)
|
||||
.contains(&provided_lifetimes_count)
|
||||
{
|
||||
ctx.report_len_mismatch(
|
||||
def,
|
||||
provided_lifetimes_count as u32,
|
||||
def_generics.len_lifetimes_self() as u32,
|
||||
IncorrectGenericsLenKind::Lifetimes,
|
||||
);
|
||||
had_error = true;
|
||||
}
|
||||
|
||||
let defaults_count =
|
||||
def_generics.iter_self_type_or_consts().filter(|(_, param)| param.has_default()).count();
|
||||
let named_type_and_const_params_count = def_generics
|
||||
.iter_self_type_or_consts()
|
||||
.filter(|(_, param)| match param {
|
||||
TypeOrConstParamData::TypeParamData(param) => {
|
||||
param.provenance == TypeParamProvenance::TypeParamList
|
||||
}
|
||||
TypeOrConstParamData::ConstParamData(_) => true,
|
||||
})
|
||||
.count();
|
||||
let expected_min =
|
||||
if infer_args { 0 } else { named_type_and_const_params_count - defaults_count };
|
||||
let expected_max = named_type_and_const_params_count;
|
||||
if !(expected_min..=expected_max).contains(&provided_types_and_consts_count) {
|
||||
ctx.report_len_mismatch(
|
||||
def,
|
||||
provided_types_and_consts_count as u32,
|
||||
named_type_and_const_params_count as u32,
|
||||
IncorrectGenericsLenKind::TypesAndConsts,
|
||||
);
|
||||
had_error = true;
|
||||
}
|
||||
|
||||
had_error
|
||||
}
|
||||
|
||||
pub(crate) fn substs_from_args_and_bindings(
|
||||
db: &dyn HirDatabase,
|
||||
store: &ExpressionStore,
|
||||
args_and_bindings: Option<&GenericArgs>,
|
||||
def: GenericDefId,
|
||||
mut infer_args: bool,
|
||||
position: GenericArgsPosition,
|
||||
explicit_self_ty: Option<Ty>,
|
||||
ctx: &mut impl GenericArgsLowerer,
|
||||
) -> Substitution {
|
||||
// Order is
|
||||
// - Parent parameters
|
||||
// - Optional Self parameter
|
||||
// - Lifetime parameters
|
||||
// - Type or Const parameters
|
||||
let def_generics = generics(db, def);
|
||||
let args_slice = args_and_bindings.map(|it| &*it.args).unwrap_or_default();
|
||||
|
||||
// We do not allow inference if there are specified args, i.e. we do not allow partial inference.
|
||||
let has_non_lifetime_args =
|
||||
args_slice.iter().any(|arg| !matches!(arg, GenericArg::Lifetime(_)));
|
||||
infer_args &= !has_non_lifetime_args;
|
||||
|
||||
let had_count_error =
|
||||
check_generic_args_len(args_and_bindings, def, &def_generics, infer_args, position, ctx);
|
||||
|
||||
let mut substs = Vec::with_capacity(def_generics.len());
|
||||
|
||||
substs.extend(def_generics.iter_parent_id().map(|id| ctx.parent_arg(id)));
|
||||
|
||||
let mut args = args_slice.iter().enumerate().peekable();
|
||||
let mut params = def_generics.iter_self().peekable();
|
||||
|
||||
// If we encounter a type or const when we expect a lifetime, we infer the lifetimes.
|
||||
// If we later encounter a lifetime, we know that the arguments were provided in the
|
||||
// wrong order. `force_infer_lt` records the type or const that forced lifetimes to be
|
||||
// inferred, so we can use it for diagnostics later.
|
||||
let mut force_infer_lt = None;
|
||||
|
||||
let has_self_arg = args_and_bindings.is_some_and(|it| it.has_self_type);
|
||||
// First, handle `Self` parameter. Consume it from the args if provided, otherwise from `explicit_self_ty`,
|
||||
// and lastly infer it.
|
||||
if let Some(&(
|
||||
self_param_id,
|
||||
self_param @ GenericParamDataRef::TypeParamData(TypeParamData {
|
||||
provenance: TypeParamProvenance::TraitSelf,
|
||||
..
|
||||
}),
|
||||
)) = params.peek()
|
||||
{
|
||||
let self_ty = if has_self_arg {
|
||||
let (_, self_ty) = args.next().expect("has_self_type=true, should have Self type");
|
||||
ctx.provided_kind(self_param_id, self_param, self_ty)
|
||||
} else {
|
||||
explicit_self_ty.map(|it| it.cast(Interner)).unwrap_or_else(|| {
|
||||
ctx.inferred_kind(def, self_param_id, self_param, infer_args, &substs)
|
||||
})
|
||||
};
|
||||
params.next();
|
||||
substs.push(self_ty);
|
||||
}
|
||||
|
||||
loop {
|
||||
// We're going to iterate through the generic arguments that the user
|
||||
// provided, matching them with the generic parameters we expect.
|
||||
// Mismatches can occur as a result of elided lifetimes, or for malformed
|
||||
// input. We try to handle both sensibly.
|
||||
match (args.peek(), params.peek()) {
|
||||
(Some(&(arg_idx, arg)), Some(&(param_id, param))) => match (arg, param) {
|
||||
(GenericArg::Type(_), GenericParamDataRef::TypeParamData(type_param))
|
||||
if type_param.provenance == TypeParamProvenance::ArgumentImplTrait =>
|
||||
{
|
||||
// Do not allow specifying `impl Trait` explicitly. We already err at that, but if we won't handle it here
|
||||
// we will handle it as if it was specified, instead of inferring it.
|
||||
substs.push(ctx.inferred_kind(def, param_id, param, infer_args, &substs));
|
||||
params.next();
|
||||
}
|
||||
(GenericArg::Lifetime(_), GenericParamDataRef::LifetimeParamData(_))
|
||||
| (GenericArg::Type(_), GenericParamDataRef::TypeParamData(_))
|
||||
| (GenericArg::Const(_), GenericParamDataRef::ConstParamData(_)) => {
|
||||
substs.push(ctx.provided_kind(param_id, param, arg));
|
||||
args.next();
|
||||
params.next();
|
||||
}
|
||||
(
|
||||
GenericArg::Type(_) | GenericArg::Const(_),
|
||||
GenericParamDataRef::LifetimeParamData(_),
|
||||
) => {
|
||||
// We expected a lifetime argument, but got a type or const
|
||||
// argument. That means we're inferring the lifetime.
|
||||
substs.push(ctx.inferred_kind(def, param_id, param, infer_args, &substs));
|
||||
params.next();
|
||||
force_infer_lt = Some((arg_idx as u32, param_id));
|
||||
}
|
||||
(GenericArg::Type(type_ref), GenericParamDataRef::ConstParamData(_)) => {
|
||||
if let Some(konst) = type_looks_like_const(store, *type_ref) {
|
||||
let GenericParamId::ConstParamId(param_id) = param_id else {
|
||||
panic!("unmatching param kinds");
|
||||
};
|
||||
let const_ty = db.const_param_ty(param_id);
|
||||
substs.push(ctx.provided_type_like_const(const_ty, konst).cast(Interner));
|
||||
args.next();
|
||||
params.next();
|
||||
} else {
|
||||
// See the `_ => { ... }` branch.
|
||||
if !had_count_error {
|
||||
ctx.report_arg_mismatch(param_id, arg_idx as u32, has_self_arg);
|
||||
}
|
||||
while args.next().is_some() {}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
// We expected one kind of parameter, but the user provided
|
||||
// another. This is an error. However, if we already know that
|
||||
// the arguments don't match up with the parameters, we won't issue
|
||||
// an additional error, as the user already knows what's wrong.
|
||||
if !had_count_error {
|
||||
ctx.report_arg_mismatch(param_id, arg_idx as u32, has_self_arg);
|
||||
}
|
||||
|
||||
// We've reported the error, but we want to make sure that this
|
||||
// problem doesn't bubble down and create additional, irrelevant
|
||||
// errors. In this case, we're simply going to ignore the argument
|
||||
// and any following arguments. The rest of the parameters will be
|
||||
// inferred.
|
||||
while args.next().is_some() {}
|
||||
}
|
||||
},
|
||||
|
||||
(Some(&(_, arg)), None) => {
|
||||
// We should never be able to reach this point with well-formed input.
|
||||
// There are two situations in which we can encounter this issue.
|
||||
//
|
||||
// 1. The number of arguments is incorrect. In this case, an error
|
||||
// will already have been emitted, and we can ignore it.
|
||||
// 2. We've inferred some lifetimes, which have been provided later (i.e.
|
||||
// after a type or const). We want to throw an error in this case.
|
||||
if !had_count_error {
|
||||
assert!(
|
||||
matches!(arg, GenericArg::Lifetime(_)),
|
||||
"the only possible situation here is incorrect lifetime order"
|
||||
);
|
||||
let (provided_arg_idx, param_id) =
|
||||
force_infer_lt.expect("lifetimes ought to have been inferred");
|
||||
ctx.report_arg_mismatch(param_id, provided_arg_idx, has_self_arg);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
(None, Some(&(param_id, param))) => {
|
||||
// If there are fewer arguments than parameters, it means we're inferring the remaining arguments.
|
||||
substs.push(ctx.inferred_kind(def, param_id, param, infer_args, &substs));
|
||||
params.next();
|
||||
}
|
||||
|
||||
(None, None) => break,
|
||||
}
|
||||
}
|
||||
|
||||
Substitution::from_iter(Interner, substs)
|
||||
}
|
||||
|
||||
fn type_looks_like_const(
|
||||
store: &ExpressionStore,
|
||||
type_ref: TypeRefId,
|
||||
) -> Option<TypeLikeConst<'_>> {
|
||||
// A path/`_` const will be parsed as a type, instead of a const, because when parsing/lowering
|
||||
// in hir-def we don't yet know the expected argument kind. rustc does this a bit differently,
|
||||
// when lowering to HIR it resolves the path, and if it doesn't resolve to the type namespace
|
||||
// it is lowered as a const. Our behavior could deviate from rustc when the value is resolvable
|
||||
// in both the type and value namespaces, but I believe we only allow more code.
|
||||
let type_ref = &store[type_ref];
|
||||
match type_ref {
|
||||
TypeRef::Path(path) => Some(TypeLikeConst::Path(path)),
|
||||
TypeRef::Placeholder => Some(TypeLikeConst::Infer),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
@ -6,14 +6,17 @@
|
||||
use cfg::{CfgExpr, CfgOptions};
|
||||
use either::Either;
|
||||
use hir_def::{
|
||||
DefWithBodyId, SyntheticSyntax,
|
||||
expr_store::{ExprOrPatPtr, ExpressionStoreSourceMap, hir_segment_to_ast_segment},
|
||||
DefWithBodyId, GenericParamId, SyntheticSyntax,
|
||||
expr_store::{
|
||||
ExprOrPatPtr, ExpressionStoreSourceMap, hir_assoc_type_binding_to_ast,
|
||||
hir_generic_arg_to_ast, hir_segment_to_ast_segment,
|
||||
},
|
||||
hir::ExprOrPatId,
|
||||
};
|
||||
use hir_expand::{HirFileId, InFile, mod_path::ModPath, name::Name};
|
||||
use hir_ty::{
|
||||
CastError, InferenceDiagnostic, InferenceTyDiagnosticSource, PathLoweringDiagnostic,
|
||||
TyLoweringDiagnostic, TyLoweringDiagnosticKind,
|
||||
CastError, InferenceDiagnostic, InferenceTyDiagnosticSource, PathGenericsSource,
|
||||
PathLoweringDiagnostic, TyLoweringDiagnostic, TyLoweringDiagnosticKind,
|
||||
db::HirDatabase,
|
||||
diagnostics::{BodyValidationDiagnostic, UnsafetyReason},
|
||||
};
|
||||
@ -24,11 +27,11 @@ use syntax::{
|
||||
};
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{AssocItem, Field, Function, Local, Trait, Type};
|
||||
use crate::{AssocItem, Field, Function, GenericDef, Local, Trait, Type};
|
||||
|
||||
pub use hir_def::VariantId;
|
||||
pub use hir_ty::{
|
||||
GenericArgsProhibitedReason,
|
||||
GenericArgsProhibitedReason, IncorrectGenericsLenKind,
|
||||
diagnostics::{CaseType, IncorrectCase},
|
||||
};
|
||||
|
||||
@ -113,6 +116,8 @@ diagnostics![
|
||||
GenericArgsProhibited,
|
||||
ParenthesizedGenericArgsWithoutFnTrait,
|
||||
BadRtn,
|
||||
IncorrectGenericsLen,
|
||||
IncorrectGenericsOrder,
|
||||
];
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -425,6 +430,39 @@ pub struct BadRtn {
|
||||
pub rtn: InFile<AstPtr<ast::ReturnTypeSyntax>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct IncorrectGenericsLen {
|
||||
/// Points at the name if there are no generics.
|
||||
pub generics_or_segment: InFile<AstPtr<Either<ast::GenericArgList, ast::NameRef>>>,
|
||||
pub kind: IncorrectGenericsLenKind,
|
||||
pub provided: u32,
|
||||
pub expected: u32,
|
||||
pub def: GenericDef,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum GenericArgKind {
|
||||
Lifetime,
|
||||
Type,
|
||||
Const,
|
||||
}
|
||||
|
||||
impl GenericArgKind {
|
||||
fn from_id(id: GenericParamId) -> Self {
|
||||
match id {
|
||||
GenericParamId::TypeParamId(_) => GenericArgKind::Type,
|
||||
GenericParamId::ConstParamId(_) => GenericArgKind::Const,
|
||||
GenericParamId::LifetimeParamId(_) => GenericArgKind::Lifetime,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct IncorrectGenericsOrder {
|
||||
pub provided_arg: InFile<AstPtr<ast::GenericArg>>,
|
||||
pub expected_kind: GenericArgKind,
|
||||
}
|
||||
|
||||
impl AnyDiagnostic {
|
||||
pub(crate) fn body_validation_diagnostic(
|
||||
db: &dyn HirDatabase,
|
||||
@ -714,6 +752,47 @@ impl AnyDiagnostic {
|
||||
};
|
||||
Self::path_diagnostic(diag, source.with_value(path))?
|
||||
}
|
||||
&InferenceDiagnostic::MethodCallIncorrectGenericsLen {
|
||||
expr,
|
||||
provided_count,
|
||||
expected_count,
|
||||
kind,
|
||||
def,
|
||||
} => {
|
||||
let syntax = expr_syntax(expr)?;
|
||||
let file_id = syntax.file_id;
|
||||
let syntax =
|
||||
syntax.with_value(syntax.value.cast::<ast::MethodCallExpr>()?).to_node(db);
|
||||
let generics_or_name = syntax
|
||||
.generic_arg_list()
|
||||
.map(Either::Left)
|
||||
.or_else(|| syntax.name_ref().map(Either::Right))?;
|
||||
let generics_or_name = InFile::new(file_id, AstPtr::new(&generics_or_name));
|
||||
IncorrectGenericsLen {
|
||||
generics_or_segment: generics_or_name,
|
||||
kind,
|
||||
provided: provided_count,
|
||||
expected: expected_count,
|
||||
def: def.into(),
|
||||
}
|
||||
.into()
|
||||
}
|
||||
&InferenceDiagnostic::MethodCallIncorrectGenericsOrder {
|
||||
expr,
|
||||
param_id,
|
||||
arg_idx,
|
||||
has_self_arg,
|
||||
} => {
|
||||
let syntax = expr_syntax(expr)?;
|
||||
let file_id = syntax.file_id;
|
||||
let syntax =
|
||||
syntax.with_value(syntax.value.cast::<ast::MethodCallExpr>()?).to_node(db);
|
||||
let generic_args = syntax.generic_arg_list()?;
|
||||
let provided_arg = hir_generic_arg_to_ast(&generic_args, arg_idx, has_self_arg)?;
|
||||
let provided_arg = InFile::new(file_id, AstPtr::new(&provided_arg));
|
||||
let expected_kind = GenericArgKind::from_id(param_id);
|
||||
IncorrectGenericsOrder { provided_arg, expected_kind }.into()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@ -750,6 +829,38 @@ impl AnyDiagnostic {
|
||||
let args = path.with_value(args);
|
||||
ParenthesizedGenericArgsWithoutFnTrait { args }.into()
|
||||
}
|
||||
PathLoweringDiagnostic::IncorrectGenericsLen {
|
||||
generics_source,
|
||||
provided_count,
|
||||
expected_count,
|
||||
kind,
|
||||
def,
|
||||
} => {
|
||||
let generics_or_segment =
|
||||
path_generics_source_to_ast(&path.value, generics_source)?;
|
||||
let generics_or_segment = path.with_value(AstPtr::new(&generics_or_segment));
|
||||
IncorrectGenericsLen {
|
||||
generics_or_segment,
|
||||
kind,
|
||||
provided: provided_count,
|
||||
expected: expected_count,
|
||||
def: def.into(),
|
||||
}
|
||||
.into()
|
||||
}
|
||||
PathLoweringDiagnostic::IncorrectGenericsOrder {
|
||||
generics_source,
|
||||
param_id,
|
||||
arg_idx,
|
||||
has_self_arg,
|
||||
} => {
|
||||
let generic_args =
|
||||
path_generics_source_to_ast(&path.value, generics_source)?.left()?;
|
||||
let provided_arg = hir_generic_arg_to_ast(&generic_args, arg_idx, has_self_arg)?;
|
||||
let provided_arg = path.with_value(AstPtr::new(&provided_arg));
|
||||
let expected_kind = GenericArgKind::from_id(param_id);
|
||||
IncorrectGenericsOrder { provided_arg, expected_kind }.into()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@ -771,3 +882,27 @@ impl AnyDiagnostic {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn path_generics_source_to_ast(
|
||||
path: &ast::Path,
|
||||
generics_source: PathGenericsSource,
|
||||
) -> Option<Either<ast::GenericArgList, ast::NameRef>> {
|
||||
Some(match generics_source {
|
||||
PathGenericsSource::Segment(segment) => {
|
||||
let segment = hir_segment_to_ast_segment(path, segment)?;
|
||||
segment
|
||||
.generic_arg_list()
|
||||
.map(Either::Left)
|
||||
.or_else(|| segment.name_ref().map(Either::Right))?
|
||||
}
|
||||
PathGenericsSource::AssocType { segment, assoc_type } => {
|
||||
let segment = hir_segment_to_ast_segment(path, segment)?;
|
||||
let segment_args = segment.generic_arg_list()?;
|
||||
let assoc = hir_assoc_type_binding_to_ast(&segment_args, assoc_type)?;
|
||||
assoc
|
||||
.generic_arg_list()
|
||||
.map(Either::Left)
|
||||
.or_else(|| assoc.name_ref().map(Either::Right))?
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -1709,10 +1709,11 @@ impl_from!(Struct, Union, Enum for Adt);
|
||||
impl Adt {
|
||||
pub fn has_non_default_type_params(self, db: &dyn HirDatabase) -> bool {
|
||||
let subst = db.generic_defaults(self.into());
|
||||
subst.iter().any(|ty| match ty.skip_binders().data(Interner) {
|
||||
GenericArgData::Ty(it) => it.is_unknown(),
|
||||
_ => false,
|
||||
})
|
||||
(subst.is_empty() && db.generic_params(self.into()).len_type_or_consts() != 0)
|
||||
|| subst.iter().any(|ty| match ty.skip_binders().data(Interner) {
|
||||
GenericArgData::Ty(it) => it.is_unknown(),
|
||||
_ => false,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn layout(self, db: &dyn HirDatabase) -> Result<Layout, LayoutError> {
|
||||
@ -3000,10 +3001,11 @@ pub struct TypeAlias {
|
||||
impl TypeAlias {
|
||||
pub fn has_non_default_type_params(self, db: &dyn HirDatabase) -> bool {
|
||||
let subst = db.generic_defaults(self.id.into());
|
||||
subst.iter().any(|ty| match ty.skip_binders().data(Interner) {
|
||||
GenericArgData::Ty(it) => it.is_unknown(),
|
||||
_ => false,
|
||||
})
|
||||
(subst.is_empty() && db.generic_params(self.id.into()).len_type_or_consts() != 0)
|
||||
|| subst.iter().any(|ty| match ty.skip_binders().data(Interner) {
|
||||
GenericArgData::Ty(it) => it.is_unknown(),
|
||||
_ => false,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn module(self, db: &dyn HirDatabase) -> Module {
|
||||
@ -3732,6 +3734,23 @@ impl GenericDef {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a string describing the kind of this type.
|
||||
#[inline]
|
||||
pub fn description(self) -> &'static str {
|
||||
match self {
|
||||
GenericDef::Function(_) => "function",
|
||||
GenericDef::Adt(Adt::Struct(_)) => "struct",
|
||||
GenericDef::Adt(Adt::Enum(_)) => "enum",
|
||||
GenericDef::Adt(Adt::Union(_)) => "union",
|
||||
GenericDef::Trait(_) => "trait",
|
||||
GenericDef::TraitAlias(_) => "trait alias",
|
||||
GenericDef::TypeAlias(_) => "type alias",
|
||||
GenericDef::Impl(_) => "impl",
|
||||
GenericDef::Const(_) => "constant",
|
||||
GenericDef::Static(_) => "static",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We cannot call this `Substitution` unfortunately...
|
||||
@ -4276,7 +4295,8 @@ fn generic_arg_from_param(db: &dyn HirDatabase, id: TypeOrConstParamId) -> Optio
|
||||
let local_idx = hir_ty::param_idx(db, id)?;
|
||||
let defaults = db.generic_defaults(id.parent);
|
||||
let ty = defaults.get(local_idx)?.clone();
|
||||
let subst = TyBuilder::placeholder_subst(db, id.parent);
|
||||
let full_subst = TyBuilder::placeholder_subst(db, id.parent);
|
||||
let subst = &full_subst.as_slice(Interner)[..local_idx];
|
||||
Some(ty.substitute(Interner, &subst))
|
||||
}
|
||||
|
||||
|
@ -36,6 +36,7 @@ fn describe_reason(reason: GenericArgsProhibitedReason) -> String {
|
||||
}
|
||||
GenericArgsProhibitedReason::Const => "constants",
|
||||
GenericArgsProhibitedReason::Static => "statics",
|
||||
GenericArgsProhibitedReason::LocalVariable => "local variables",
|
||||
};
|
||||
format!("generic arguments are not allowed on {kind}")
|
||||
}
|
||||
@ -320,7 +321,7 @@ trait E<A: foo::<()>::Trait>
|
||||
// ^^^^^ 💡 error: generic arguments are not allowed on builtin types
|
||||
}
|
||||
|
||||
impl<A: foo::<()>::Trait> E for ()
|
||||
impl<A: foo::<()>::Trait> E<()> for ()
|
||||
// ^^^^^^ 💡 error: generic arguments are not allowed on modules
|
||||
where bool<i32>: foo::Trait
|
||||
// ^^^^^ 💡 error: generic arguments are not allowed on builtin types
|
||||
@ -518,14 +519,14 @@ fn baz() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn const_and_static() {
|
||||
fn const_param_and_static() {
|
||||
check_diagnostics(
|
||||
r#"
|
||||
const CONST: i32 = 0;
|
||||
static STATIC: i32 = 0;
|
||||
fn baz() {
|
||||
let _ = CONST::<()>;
|
||||
// ^^^^^^ 💡 error: generic arguments are not allowed on constants
|
||||
fn baz<const CONST_PARAM: usize>() {
|
||||
let _ = CONST_PARAM::<()>;
|
||||
// ^^^^^^ 💡 error: generic arguments are not allowed on constants
|
||||
let _ = STATIC::<()>;
|
||||
// ^^^^^^ 💡 error: generic arguments are not allowed on statics
|
||||
}
|
||||
@ -533,6 +534,19 @@ fn baz() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn local_variable() {
|
||||
check_diagnostics(
|
||||
r#"
|
||||
fn baz() {
|
||||
let x = 1;
|
||||
let _ = x::<()>;
|
||||
// ^^^^^^ 💡 error: generic arguments are not allowed on local variables
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn enum_variant() {
|
||||
check_diagnostics(
|
||||
|
172
crates/ide-diagnostics/src/handlers/incorrect_generics_len.rs
Normal file
172
crates/ide-diagnostics/src/handlers/incorrect_generics_len.rs
Normal file
@ -0,0 +1,172 @@
|
||||
use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
|
||||
use hir::IncorrectGenericsLenKind;
|
||||
|
||||
// Diagnostic: incorrect-generics-len
|
||||
//
|
||||
// This diagnostic is triggered if the number of generic arguments does not match their declaration.
|
||||
pub(crate) fn incorrect_generics_len(
|
||||
ctx: &DiagnosticsContext<'_>,
|
||||
d: &hir::IncorrectGenericsLen,
|
||||
) -> Diagnostic {
|
||||
let owner_description = d.def.description();
|
||||
let expected = d.expected;
|
||||
let provided = d.provided;
|
||||
let kind_description = match d.kind {
|
||||
IncorrectGenericsLenKind::Lifetimes => "lifetime",
|
||||
IncorrectGenericsLenKind::TypesAndConsts => "generic",
|
||||
};
|
||||
let message = format!(
|
||||
"this {owner_description} takes {expected} {kind_description} argument{} \
|
||||
but {provided} {kind_description} argument{} {} supplied",
|
||||
if expected == 1 { "" } else { "s" },
|
||||
if provided == 1 { "" } else { "s" },
|
||||
if provided == 1 { "was" } else { "were" },
|
||||
);
|
||||
Diagnostic::new_with_syntax_node_ptr(
|
||||
ctx,
|
||||
DiagnosticCode::RustcHardError("E0107"),
|
||||
message,
|
||||
d.generics_or_segment.map(Into::into),
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::tests::check_diagnostics;
|
||||
|
||||
#[test]
|
||||
fn partially_specified_generics() {
|
||||
check_diagnostics(
|
||||
r#"
|
||||
struct Bar<T, U>(T, U);
|
||||
|
||||
fn foo() {
|
||||
let _ = Bar::<()>;
|
||||
// ^^^^^^ error: this struct takes 2 generic arguments but 1 generic argument was supplied
|
||||
}
|
||||
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn enum_variant() {
|
||||
check_diagnostics(
|
||||
r#"
|
||||
enum Enum<T, U> {
|
||||
Variant(T, U),
|
||||
}
|
||||
|
||||
fn foo() {
|
||||
let _ = Enum::<()>::Variant;
|
||||
// ^^^^^^ error: this enum takes 2 generic arguments but 1 generic argument was supplied
|
||||
let _ = Enum::Variant::<()>;
|
||||
// ^^^^^^ error: this enum takes 2 generic arguments but 1 generic argument was supplied
|
||||
}
|
||||
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lifetimes() {
|
||||
check_diagnostics(
|
||||
r#"
|
||||
struct Foo<'a, 'b>(&'a &'b ());
|
||||
struct Bar<'a>(&'a ());
|
||||
|
||||
fn foo() -> Foo {
|
||||
let _ = Foo;
|
||||
let _ = Foo::<>;
|
||||
let _ = Foo::<'static>;
|
||||
// ^^^^^^^^^^^ error: this struct takes 2 lifetime arguments but 1 lifetime argument was supplied
|
||||
loop {}
|
||||
}
|
||||
|
||||
fn bar(_v: Bar) -> Foo { loop {} }
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_error_for_elided_lifetimes() {
|
||||
check_diagnostics(
|
||||
r#"
|
||||
struct Foo<'a>(&'a ());
|
||||
|
||||
fn foo(_v: &()) -> Foo { loop {} }
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn errs_for_elided_lifetimes_if_lifetimes_are_explicitly_provided() {
|
||||
check_diagnostics(
|
||||
r#"
|
||||
struct Foo<'a, 'b>(&'a &'b ());
|
||||
|
||||
fn foo(_v: Foo<'_>
|
||||
// ^^^^ error: this struct takes 2 lifetime arguments but 1 lifetime argument was supplied
|
||||
) -> Foo<'static> { loop {} }
|
||||
// ^^^^^^^^^ error: this struct takes 2 lifetime arguments but 1 lifetime argument was supplied
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn types_and_consts() {
|
||||
check_diagnostics(
|
||||
r#"
|
||||
struct Foo<'a, T>(&'a T);
|
||||
fn foo(_v: Foo) {}
|
||||
// ^^^ error: this struct takes 1 generic argument but 0 generic arguments were supplied
|
||||
|
||||
struct Bar<T, const N: usize>(T);
|
||||
fn bar() {
|
||||
let _ = Bar::<()>;
|
||||
// ^^^^^^ error: this struct takes 2 generic arguments but 1 generic argument was supplied
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn respects_defaults() {
|
||||
check_diagnostics(
|
||||
r#"
|
||||
struct Foo<T = (), const N: usize = 0>(T);
|
||||
fn foo(_v: Foo) {}
|
||||
|
||||
struct Bar<T, const N: usize = 0>(T);
|
||||
fn bar(_v: Bar<()>) {}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn constant() {
|
||||
check_diagnostics(
|
||||
r#"
|
||||
const CONST: i32 = 0;
|
||||
fn baz() {
|
||||
let _ = CONST::<()>;
|
||||
// ^^^^^^ error: this constant takes 0 generic arguments but 1 generic argument was supplied
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn assoc_type() {
|
||||
check_diagnostics(
|
||||
r#"
|
||||
trait Trait {
|
||||
type Assoc;
|
||||
}
|
||||
|
||||
fn foo<T: Trait<Assoc<i32> = bool>>() {}
|
||||
// ^^^^^ error: this type alias takes 0 generic arguments but 1 generic argument was supplied
|
||||
"#,
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,80 @@
|
||||
use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
|
||||
use hir::GenericArgKind;
|
||||
use syntax::SyntaxKind;
|
||||
|
||||
// Diagnostic: incorrect-generics-order
|
||||
//
|
||||
// This diagnostic is triggered the order of provided generic arguments does not match their declaration.
|
||||
pub(crate) fn incorrect_generics_order(
|
||||
ctx: &DiagnosticsContext<'_>,
|
||||
d: &hir::IncorrectGenericsOrder,
|
||||
) -> Diagnostic {
|
||||
let provided_description = match d.provided_arg.value.kind() {
|
||||
SyntaxKind::CONST_ARG => "constant",
|
||||
SyntaxKind::LIFETIME_ARG => "lifetime",
|
||||
SyntaxKind::TYPE_ARG => "type",
|
||||
_ => panic!("non-generic-arg passed to `incorrect_generics_order()`"),
|
||||
};
|
||||
let expected_description = match d.expected_kind {
|
||||
GenericArgKind::Lifetime => "lifetime",
|
||||
GenericArgKind::Type => "type",
|
||||
GenericArgKind::Const => "constant",
|
||||
};
|
||||
let message =
|
||||
format!("{provided_description} provided when a {expected_description} was expected");
|
||||
Diagnostic::new_with_syntax_node_ptr(
|
||||
ctx,
|
||||
DiagnosticCode::RustcHardError("E0747"),
|
||||
message,
|
||||
d.provided_arg.map(Into::into),
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::tests::check_diagnostics;
|
||||
|
||||
#[test]
|
||||
fn lifetime_out_of_order() {
|
||||
check_diagnostics(
|
||||
r#"
|
||||
struct Foo<'a, T>(&'a T);
|
||||
|
||||
fn bar(_v: Foo<(), 'static>) {}
|
||||
// ^^ error: type provided when a lifetime was expected
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn types_and_consts() {
|
||||
check_diagnostics(
|
||||
r#"
|
||||
struct Foo<T>(T);
|
||||
fn foo1(_v: Foo<1>) {}
|
||||
// ^ error: constant provided when a type was expected
|
||||
fn foo2(_v: Foo<{ (1, 2) }>) {}
|
||||
// ^^^^^^^^^^ error: constant provided when a type was expected
|
||||
|
||||
struct Bar<const N: usize>;
|
||||
fn bar(_v: Bar<()>) {}
|
||||
// ^^ error: type provided when a constant was expected
|
||||
|
||||
struct Baz<T, const N: usize>(T);
|
||||
fn baz(_v: Baz<1, ()>) {}
|
||||
// ^ error: constant provided when a type was expected
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_error_when_num_incorrect() {
|
||||
check_diagnostics(
|
||||
r#"
|
||||
struct Baz<T, U>(T, U);
|
||||
fn baz(_v: Baz<1>) {}
|
||||
// ^^^ error: this struct takes 2 generic arguments but 1 generic argument was supplied
|
||||
"#,
|
||||
);
|
||||
}
|
||||
}
|
@ -32,6 +32,8 @@ mod handlers {
|
||||
pub(crate) mod inactive_code;
|
||||
pub(crate) mod incoherent_impl;
|
||||
pub(crate) mod incorrect_case;
|
||||
pub(crate) mod incorrect_generics_len;
|
||||
pub(crate) mod incorrect_generics_order;
|
||||
pub(crate) mod invalid_cast;
|
||||
pub(crate) mod invalid_derive_target;
|
||||
pub(crate) mod macro_error;
|
||||
@ -499,6 +501,8 @@ pub fn semantic_diagnostics(
|
||||
handlers::parenthesized_generic_args_without_fn_trait::parenthesized_generic_args_without_fn_trait(&ctx, &d)
|
||||
}
|
||||
AnyDiagnostic::BadRtn(d) => handlers::bad_rtn::bad_rtn(&ctx, &d),
|
||||
AnyDiagnostic::IncorrectGenericsLen(d) => handlers::incorrect_generics_len::incorrect_generics_len(&ctx, &d),
|
||||
AnyDiagnostic::IncorrectGenericsOrder(d) => handlers::incorrect_generics_order::incorrect_generics_order(&ctx, &d),
|
||||
};
|
||||
res.push(d)
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user