Merge pull request #18743 from ChayimFriedman2/e0107

feat: Unify handling of path diagnostics in hir-ty
This commit is contained in:
Lukas Wirth 2024-12-24 17:13:07 +00:00 committed by GitHub
commit 493ac4d4df
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 881 additions and 275 deletions

View file

@ -85,6 +85,8 @@ use crate::{
FxIndexMap, LocalModuleId, Lookup, MacroExpander, MacroId, ModuleId, ProcMacroId, UseId,
};
pub use self::path_resolution::ResolvePathResultPrefixInfo;
const PREDEFINED_TOOLS: &[SmolStr] = &[
SmolStr::new_static("clippy"),
SmolStr::new_static("rustfmt"),
@ -615,13 +617,15 @@ impl DefMap {
(res.resolved_def, res.segment_index)
}
/// The first `Option<usize>` points at the `Enum` segment in case of `Enum::Variant`, the second
/// points at the unresolved segments.
pub(crate) fn resolve_path_locally(
&self,
db: &dyn DefDatabase,
original_module: LocalModuleId,
path: &ModPath,
shadow: BuiltinShadowMode,
) -> (PerNs, Option<usize>) {
) -> (PerNs, Option<usize>, ResolvePathResultPrefixInfo) {
let res = self.resolve_path_fp_with_macro_single(
db,
ResolveMode::Other,
@ -630,7 +634,7 @@ impl DefMap {
shadow,
None, // Currently this function isn't used for macro resolution.
);
(res.resolved_def, res.segment_index)
(res.resolved_def, res.segment_index, res.prefix_info)
}
/// Ascends the `DefMap` hierarchy and calls `f` with every `DefMap` and containing module.

View file

@ -38,7 +38,7 @@ use crate::{
attr_resolution::{attr_macro_as_call_id, derive_macro_as_call_id},
diagnostics::DefDiagnostic,
mod_resolution::ModDir,
path_resolution::ReachedFixedPoint,
path_resolution::{ReachedFixedPoint, ResolvePathResultPrefixInfo},
proc_macro::{parse_macro_name_and_helper_attrs, ProcMacroDef, ProcMacroKind},
sub_namespace_match, BuiltinShadowMode, DefMap, MacroSubNs, ModuleData, ModuleOrigin,
ResolveMode,
@ -797,7 +797,7 @@ impl DefCollector<'_> {
return PartialResolvedImport::Unresolved;
}
if res.from_differing_crate {
if let ResolvePathResultPrefixInfo::DifferingCrate = res.prefix_info {
return PartialResolvedImport::Resolved(
def.filter_visibility(|v| matches!(v, Visibility::Public)),
);

View file

@ -43,21 +43,34 @@ pub(super) struct ResolvePathResult {
pub(super) resolved_def: PerNs,
pub(super) segment_index: Option<usize>,
pub(super) reached_fixedpoint: ReachedFixedPoint,
pub(super) from_differing_crate: bool,
pub(super) prefix_info: ResolvePathResultPrefixInfo,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ResolvePathResultPrefixInfo {
None,
DifferingCrate,
/// Path of the form `Enum::Variant` (and not `Variant` alone).
Enum,
}
impl ResolvePathResult {
fn empty(reached_fixedpoint: ReachedFixedPoint) -> ResolvePathResult {
ResolvePathResult::new(PerNs::none(), reached_fixedpoint, None, false)
ResolvePathResult::new(
PerNs::none(),
reached_fixedpoint,
None,
ResolvePathResultPrefixInfo::None,
)
}
fn new(
resolved_def: PerNs,
reached_fixedpoint: ReachedFixedPoint,
segment_index: Option<usize>,
from_differing_crate: bool,
prefix_info: ResolvePathResultPrefixInfo,
) -> ResolvePathResult {
ResolvePathResult { resolved_def, segment_index, reached_fixedpoint, from_differing_crate }
ResolvePathResult { resolved_def, segment_index, reached_fixedpoint, prefix_info }
}
}
@ -157,7 +170,17 @@ impl DefMap {
if result.reached_fixedpoint == ReachedFixedPoint::No {
result.reached_fixedpoint = new.reached_fixedpoint;
}
result.from_differing_crate |= new.from_differing_crate;
result.prefix_info = match (result.prefix_info, new.prefix_info) {
(ResolvePathResultPrefixInfo::None, it) => it,
(ResolvePathResultPrefixInfo::DifferingCrate, _) => {
ResolvePathResultPrefixInfo::DifferingCrate
}
(
ResolvePathResultPrefixInfo::Enum,
ResolvePathResultPrefixInfo::DifferingCrate,
) => ResolvePathResultPrefixInfo::DifferingCrate,
(ResolvePathResultPrefixInfo::Enum, _) => ResolvePathResultPrefixInfo::Enum,
};
result.segment_index = match (result.segment_index, new.segment_index) {
(Some(idx), None) => Some(idx),
(Some(old), Some(new)) => Some(old.max(new)),
@ -403,14 +426,14 @@ impl DefMap {
fn resolve_remaining_segments<'a>(
&self,
segments: impl Iterator<Item = (usize, &'a Name)>,
mut segments: impl Iterator<Item = (usize, &'a Name)>,
mut curr_per_ns: PerNs,
path: &ModPath,
db: &dyn DefDatabase,
shadow: BuiltinShadowMode,
original_module: LocalModuleId,
) -> ResolvePathResult {
for (i, segment) in segments {
while let Some((i, segment)) = segments.next() {
let curr = match curr_per_ns.take_types_full() {
Some(r) => r,
None => {
@ -443,7 +466,7 @@ impl DefMap {
def,
ReachedFixedPoint::Yes,
s.map(|s| s + i),
true,
ResolvePathResultPrefixInfo::DifferingCrate,
);
}
@ -488,17 +511,28 @@ impl DefMap {
),
})
});
match res {
Some(res) => res,
None => {
return ResolvePathResult::new(
PerNs::types(e.into(), curr.vis, curr.import),
ReachedFixedPoint::Yes,
Some(i),
false,
)
// FIXME: Need to filter visibility here and below? Not sure.
return match res {
Some(res) => {
if segments.next().is_some() {
// Enum variants are in value namespace, segments left => no resolution.
ResolvePathResult::empty(ReachedFixedPoint::No)
} else {
ResolvePathResult::new(
res,
ReachedFixedPoint::Yes,
None,
ResolvePathResultPrefixInfo::Enum,
)
}
}
}
None => ResolvePathResult::new(
PerNs::types(e.into(), curr.vis, curr.import),
ReachedFixedPoint::Yes,
Some(i),
ResolvePathResultPrefixInfo::None,
),
};
}
s => {
// could be an inherent method call in UFCS form
@ -513,7 +547,7 @@ impl DefMap {
PerNs::types(s, curr.vis, curr.import),
ReachedFixedPoint::Yes,
Some(i),
false,
ResolvePathResultPrefixInfo::None,
);
}
};
@ -522,7 +556,12 @@ impl DefMap {
.filter_visibility(|vis| vis.is_visible_from_def_map(db, self, original_module));
}
ResolvePathResult::new(curr_per_ns, ReachedFixedPoint::Yes, None, false)
ResolvePathResult::new(
curr_per_ns,
ReachedFixedPoint::Yes,
None,
ResolvePathResultPrefixInfo::None,
)
}
fn resolve_name_in_module(

View file

@ -240,6 +240,7 @@ pub struct PathSegment<'a> {
pub args_and_bindings: Option<&'a GenericArgs>,
}
#[derive(Debug, Clone, Copy)]
pub struct PathSegments<'a> {
segments: &'a [Name],
generic_args: Option<&'a [Option<GenericArgs>]>,
@ -259,6 +260,7 @@ impl<'a> PathSegments<'a> {
pub fn last(&self) -> Option<PathSegment<'a>> {
self.get(self.len().checked_sub(1)?)
}
pub fn get(&self, idx: usize) -> Option<PathSegment<'a>> {
let res = PathSegment {
name: self.segments.get(idx)?,
@ -266,24 +268,37 @@ impl<'a> PathSegments<'a> {
};
Some(res)
}
pub fn skip(&self, len: usize) -> PathSegments<'a> {
PathSegments {
segments: self.segments.get(len..).unwrap_or(&[]),
generic_args: self.generic_args.and_then(|it| it.get(len..)),
}
}
pub fn take(&self, len: usize) -> PathSegments<'a> {
PathSegments {
segments: self.segments.get(..len).unwrap_or(self.segments),
generic_args: self.generic_args.map(|it| it.get(..len).unwrap_or(it)),
}
}
pub fn strip_last(&self) -> PathSegments<'a> {
PathSegments {
segments: self.segments.split_last().map_or(&[], |it| it.1),
generic_args: self.generic_args.map(|it| it.split_last().map_or(&[][..], |it| it.1)),
}
}
pub fn strip_last_two(&self) -> PathSegments<'a> {
PathSegments {
segments: self.segments.get(..self.segments.len().saturating_sub(2)).unwrap_or(&[]),
generic_args: self
.generic_args
.map(|it| it.get(..it.len().saturating_sub(2)).unwrap_or(&[])),
}
}
pub fn iter(&self) -> impl Iterator<Item = PathSegment<'a>> {
self.segments
.iter()

View file

@ -21,7 +21,7 @@ use crate::{
hir::{BindingId, ExprId, LabelId},
item_scope::{BuiltinShadowMode, ImportId, ImportOrExternCrate, BUILTIN_SCOPE},
lang_item::LangItemTarget,
nameres::{DefMap, MacroSubNs},
nameres::{DefMap, MacroSubNs, ResolvePathResultPrefixInfo},
path::{ModPath, Path, PathKind},
per_ns::PerNs,
type_ref::{LifetimeRef, TypesMap},
@ -263,25 +263,37 @@ impl Resolver {
&self,
db: &dyn DefDatabase,
path: &Path,
mut hygiene_id: HygieneId,
hygiene_id: HygieneId,
) -> Option<ResolveValueResult> {
self.resolve_path_in_value_ns_with_prefix_info(db, path, hygiene_id).map(|(it, _)| it)
}
pub fn resolve_path_in_value_ns_with_prefix_info(
&self,
db: &dyn DefDatabase,
path: &Path,
mut hygiene_id: HygieneId,
) -> Option<(ResolveValueResult, ResolvePathResultPrefixInfo)> {
let path = match path {
Path::BarePath(mod_path) => mod_path,
Path::Normal(it) => it.mod_path(),
Path::LangItem(l, None) => {
return Some(ResolveValueResult::ValueNs(
match *l {
LangItemTarget::Function(it) => ValueNs::FunctionId(it),
LangItemTarget::Static(it) => ValueNs::StaticId(it),
LangItemTarget::Struct(it) => ValueNs::StructId(it),
LangItemTarget::EnumVariant(it) => ValueNs::EnumVariantId(it),
LangItemTarget::Union(_)
| LangItemTarget::ImplDef(_)
| LangItemTarget::TypeAlias(_)
| LangItemTarget::Trait(_)
| LangItemTarget::EnumId(_) => return None,
},
None,
return Some((
ResolveValueResult::ValueNs(
match *l {
LangItemTarget::Function(it) => ValueNs::FunctionId(it),
LangItemTarget::Static(it) => ValueNs::StaticId(it),
LangItemTarget::Struct(it) => ValueNs::StructId(it),
LangItemTarget::EnumVariant(it) => ValueNs::EnumVariantId(it),
LangItemTarget::Union(_)
| LangItemTarget::ImplDef(_)
| LangItemTarget::TypeAlias(_)
| LangItemTarget::Trait(_)
| LangItemTarget::EnumId(_) => return None,
},
None,
),
ResolvePathResultPrefixInfo::None,
))
}
Path::LangItem(l, Some(_)) => {
@ -296,7 +308,10 @@ impl Resolver {
| LangItemTarget::ImplDef(_)
| LangItemTarget::Static(_) => return None,
};
return Some(ResolveValueResult::Partial(type_ns, 1, None));
return Some((
ResolveValueResult::Partial(type_ns, 1, None),
ResolvePathResultPrefixInfo::None,
));
}
};
let n_segments = path.segments().len();
@ -326,9 +341,12 @@ impl Resolver {
});
if let Some(e) = entry {
return Some(ResolveValueResult::ValueNs(
ValueNs::LocalBinding(e.binding()),
None,
return Some((
ResolveValueResult::ValueNs(
ValueNs::LocalBinding(e.binding()),
None,
),
ResolvePathResultPrefixInfo::None,
));
}
}
@ -350,14 +368,17 @@ impl Resolver {
Scope::GenericParams { params, def } => {
if let Some(id) = params.find_const_by_name(first_name, *def) {
let val = ValueNs::GenericParam(id);
return Some(ResolveValueResult::ValueNs(val, None));
return Some((
ResolveValueResult::ValueNs(val, None),
ResolvePathResultPrefixInfo::None,
));
}
}
&Scope::ImplDefScope(impl_) => {
if *first_name == sym::Self_.clone() {
return Some(ResolveValueResult::ValueNs(
ValueNs::ImplSelf(impl_),
None,
return Some((
ResolveValueResult::ValueNs(ValueNs::ImplSelf(impl_), None),
ResolvePathResultPrefixInfo::None,
));
}
}
@ -377,22 +398,27 @@ impl Resolver {
Scope::GenericParams { params, def } => {
if let Some(id) = params.find_type_by_name(first_name, *def) {
let ty = TypeNs::GenericParam(id);
return Some(ResolveValueResult::Partial(ty, 1, None));
return Some((
ResolveValueResult::Partial(ty, 1, None),
ResolvePathResultPrefixInfo::None,
));
}
}
&Scope::ImplDefScope(impl_) => {
if *first_name == sym::Self_.clone() {
return Some(ResolveValueResult::Partial(
TypeNs::SelfType(impl_),
1,
None,
return Some((
ResolveValueResult::Partial(TypeNs::SelfType(impl_), 1, None),
ResolvePathResultPrefixInfo::None,
));
}
}
Scope::AdtScope(adt) => {
if *first_name == sym::Self_.clone() {
let ty = TypeNs::AdtSelfType(*adt);
return Some(ResolveValueResult::Partial(ty, 1, None));
return Some((
ResolveValueResult::Partial(ty, 1, None),
ResolvePathResultPrefixInfo::None,
));
}
}
Scope::BlockScope(m) => {
@ -413,7 +439,10 @@ impl Resolver {
// `use core::u16;`.
if path.kind == PathKind::Plain && n_segments > 1 {
if let Some(builtin) = BuiltinType::by_name(first_name) {
return Some(ResolveValueResult::Partial(TypeNs::BuiltinType(builtin), 1, None));
return Some((
ResolveValueResult::Partial(TypeNs::BuiltinType(builtin), 1, None),
ResolvePathResultPrefixInfo::None,
));
}
}
@ -924,15 +953,15 @@ impl ModuleItemMap {
&self,
db: &dyn DefDatabase,
path: &ModPath,
) -> Option<ResolveValueResult> {
let (module_def, idx) =
) -> Option<(ResolveValueResult, ResolvePathResultPrefixInfo)> {
let (module_def, unresolved_idx, prefix_info) =
self.def_map.resolve_path_locally(db, self.module_id, path, BuiltinShadowMode::Other);
match idx {
match unresolved_idx {
None => {
let (value, import) = to_value_ns(module_def)?;
Some(ResolveValueResult::ValueNs(value, import))
Some((ResolveValueResult::ValueNs(value, import), prefix_info))
}
Some(idx) => {
Some(unresolved_idx) => {
let def = module_def.take_types_full()?;
let ty = match def.def {
ModuleDefId::AdtId(it) => TypeNs::AdtId(it),
@ -948,7 +977,7 @@ impl ModuleItemMap {
| ModuleDefId::MacroId(_)
| ModuleDefId::StaticId(_) => return None,
};
Some(ResolveValueResult::Partial(ty, idx, def.import))
Some((ResolveValueResult::Partial(ty, unresolved_idx, def.import), prefix_info))
}
}
}
@ -958,7 +987,7 @@ impl ModuleItemMap {
db: &dyn DefDatabase,
path: &ModPath,
) -> Option<(TypeNs, Option<usize>, Option<ImportOrExternCrate>)> {
let (module_def, idx) =
let (module_def, idx, _) =
self.def_map.resolve_path_locally(db, self.module_id, path, BuiltinShadowMode::Other);
let (res, import) = to_type_ns(module_def)?;
Some((res, idx, import))

View file

@ -16,6 +16,7 @@
pub(crate) mod cast;
pub(crate) mod closure;
mod coerce;
mod diagnostics;
mod expr;
mod mutability;
mod pat;
@ -57,15 +58,20 @@ use crate::{
db::HirDatabase,
fold_tys,
generics::Generics,
infer::{coerce::CoerceMany, expr::ExprIsRead, unify::InferenceTable},
lower::{ImplTraitLoweringMode, TyLoweringDiagnostic},
infer::{
coerce::CoerceMany,
diagnostics::{Diagnostics, InferenceTyLoweringContext as TyLoweringContext},
expr::ExprIsRead,
unify::InferenceTable,
},
lower::{diagnostics::TyLoweringDiagnostic, ImplTraitLoweringMode},
mir::MirSpan,
to_assoc_type_id,
traits::FnTrait,
utils::{InTypeConstIdMetadata, UnevaluatedConstEvaluatorFolder},
AliasEq, AliasTy, Binders, ClosureId, Const, DomainGoal, GenericArg, Goal, ImplTraitId,
ImplTraitIdx, InEnvironment, Interner, Lifetime, OpaqueTyId, ParamLoweringMode, ProjectionTy,
Substitution, TraitEnvironment, Ty, TyBuilder, TyExt,
ImplTraitIdx, InEnvironment, Interner, Lifetime, OpaqueTyId, ParamLoweringMode,
PathLoweringDiagnostic, ProjectionTy, Substitution, TraitEnvironment, Ty, TyBuilder, TyExt,
};
// This lint has a false positive here. See the link below for details.
@ -276,6 +282,10 @@ pub enum InferenceDiagnostic {
source: InferenceTyDiagnosticSource,
diag: TyLoweringDiagnostic,
},
PathDiagnostic {
node: ExprOrPatId,
diag: PathLoweringDiagnostic,
},
}
/// A mismatch between an expected and an inferred type.
@ -442,6 +452,7 @@ pub struct InferenceResult {
/// [`InferenceContext`] and store the tuples substitution there. This map is the reverse of
/// that which allows us to resolve a [`TupleFieldId`]s type.
pub tuple_field_access_types: FxHashMap<TupleId, Substitution>,
/// During inference this field is empty and [`InferenceContext::diagnostics`] is filled instead.
pub diagnostics: Vec<InferenceDiagnostic>,
pub type_of_expr: ArenaMap<ExprId, Ty>,
/// For each pattern record the type it resolves to.
@ -579,6 +590,8 @@ pub(crate) struct InferenceContext<'a> {
pub(crate) db: &'a dyn HirDatabase,
pub(crate) owner: DefWithBodyId,
pub(crate) body: &'a Body,
/// Generally you should not resolve things via this resolver. Instead create a TyLoweringContext
/// and resolve the path via its methods. This will ensure proper error reporting.
pub(crate) resolver: Resolver,
generics: OnceCell<Option<Generics>>,
table: unify::InferenceTable<'a>,
@ -620,6 +633,8 @@ pub(crate) struct InferenceContext<'a> {
/// comment on `InferenceContext::sort_closures`
closure_dependencies: FxHashMap<ClosureId, Vec<ClosureId>>,
deferred_closures: FxHashMap<ClosureId, Vec<(Ty, Ty, Vec<Ty>, ExprId)>>,
diagnostics: Diagnostics,
}
#[derive(Clone, Debug)]
@ -701,6 +716,7 @@ impl<'a> InferenceContext<'a> {
deferred_closures: FxHashMap::default(),
closure_dependencies: FxHashMap::default(),
inside_assignment: false,
diagnostics: Diagnostics::default(),
}
}
@ -724,8 +740,10 @@ impl<'a> InferenceContext<'a> {
mut result,
mut deferred_cast_checks,
tuple_field_accesses_rev,
diagnostics,
..
} = self;
let mut diagnostics = diagnostics.finish();
// Destructure every single field so whenever new fields are added to `InferenceResult` we
// don't forget to handle them here.
let InferenceResult {
@ -733,7 +751,6 @@ impl<'a> InferenceContext<'a> {
field_resolutions: _,
variant_resolutions: _,
assoc_resolutions,
diagnostics,
type_of_expr,
type_of_pat,
type_of_binding,
@ -752,6 +769,7 @@ impl<'a> InferenceContext<'a> {
mutated_bindings_in_closure: _,
tuple_field_access_types: _,
coercion_casts,
diagnostics: _,
} = &mut result;
table.fallback_if_possible();
@ -866,6 +884,9 @@ impl<'a> InferenceContext<'a> {
*has_errors || subst.type_parameters(Interner).any(|ty| ty.contains_unknown());
})
.collect();
result.diagnostics = diagnostics;
result
}
@ -1238,41 +1259,28 @@ impl<'a> InferenceContext<'a> {
self.result.type_of_binding.insert(id, ty);
}
fn push_diagnostic(&mut self, diagnostic: InferenceDiagnostic) {
self.result.diagnostics.push(diagnostic);
}
fn push_ty_diagnostics(
&mut self,
source: InferenceTyDiagnosticSource,
diagnostics: Vec<TyLoweringDiagnostic>,
) {
self.result.diagnostics.extend(
diagnostics.into_iter().map(|diag| InferenceDiagnostic::TyDiagnostic { source, diag }),
);
fn push_diagnostic(&self, diagnostic: InferenceDiagnostic) {
self.diagnostics.push(diagnostic);
}
fn with_ty_lowering<R>(
&mut self,
types_map: &TypesMap,
types_source: InferenceTyDiagnosticSource,
f: impl FnOnce(&mut crate::lower::TyLoweringContext<'_>) -> R,
f: impl FnOnce(&mut TyLoweringContext<'_>) -> R,
) -> R {
let mut ctx = crate::lower::TyLoweringContext::new(
let mut ctx = TyLoweringContext::new(
self.db,
&self.resolver,
types_map,
self.owner.into(),
&self.diagnostics,
types_source,
);
let result = f(&mut ctx);
self.push_ty_diagnostics(types_source, ctx.diagnostics);
result
f(&mut ctx)
}
fn with_body_ty_lowering<R>(
&mut self,
f: impl FnOnce(&mut crate::lower::TyLoweringContext<'_>) -> R,
) -> R {
fn with_body_ty_lowering<R>(&mut self, f: impl FnOnce(&mut TyLoweringContext<'_>) -> R) -> R {
self.with_ty_lowering(&self.body.types, InferenceTyDiagnosticSource::Body, f)
}
@ -1451,51 +1459,55 @@ impl<'a> InferenceContext<'a> {
}
}
fn resolve_variant(&mut self, path: Option<&Path>, value_ns: bool) -> (Ty, Option<VariantId>) {
fn resolve_variant(
&mut self,
node: ExprOrPatId,
path: Option<&Path>,
value_ns: bool,
) -> (Ty, Option<VariantId>) {
let path = match path {
Some(path) => path,
None => return (self.err_ty(), None),
};
let mut ctx = crate::lower::TyLoweringContext::new(
let mut ctx = TyLoweringContext::new(
self.db,
&self.resolver,
&self.body.types,
self.owner.into(),
&self.diagnostics,
InferenceTyDiagnosticSource::Body,
);
let (resolution, unresolved) = if value_ns {
match self.resolver.resolve_path_in_value_ns(self.db.upcast(), path, HygieneId::ROOT) {
Some(ResolveValueResult::ValueNs(value, _)) => match value {
let Some(res) = ctx.resolve_path_in_value_ns(path, node, 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);
self.push_ty_diagnostics(
InferenceTyDiagnosticSource::Body,
ctx.diagnostics,
);
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);
self.push_ty_diagnostics(
InferenceTyDiagnosticSource::Body,
ctx.diagnostics,
);
drop(ctx);
let ty = self.db.ty(strukt.into());
let ty = self.insert_type_vars(ty.substitute(Interner, &substs));
return (ty, Some(strukt.into()));
}
ValueNs::ImplSelf(impl_id) => (TypeNs::SelfType(impl_id), None),
_ => return (self.err_ty(), None),
_ => {
drop(ctx);
return (self.err_ty(), None);
}
},
Some(ResolveValueResult::Partial(typens, unresolved, _)) => {
(typens, Some(unresolved))
}
None => return (self.err_ty(), None),
ResolveValueResult::Partial(typens, unresolved, _) => (typens, Some(unresolved)),
}
} else {
match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path) {
Some((it, idx, _)) => (it, idx),
match ctx.resolve_path_in_type_ns(path, node) {
Some((it, idx)) => (it, idx),
None => return (self.err_ty(), None),
}
};
@ -1506,21 +1518,21 @@ impl<'a> InferenceContext<'a> {
return match resolution {
TypeNs::AdtId(AdtId::StructId(strukt)) => {
let substs = ctx.substs_from_path(path, strukt.into(), true);
self.push_ty_diagnostics(InferenceTyDiagnosticSource::Body, ctx.diagnostics);
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);
self.push_ty_diagnostics(InferenceTyDiagnosticSource::Body, ctx.diagnostics);
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);
self.push_ty_diagnostics(InferenceTyDiagnosticSource::Body, ctx.diagnostics);
drop(ctx);
let ty = self.db.ty(var.lookup(self.db.upcast()).parent.into());
let ty = self.insert_type_vars(ty.substitute(Interner, &substs));
forbid_unresolved_segments((ty, Some(var.into())), unresolved)
@ -1531,6 +1543,7 @@ impl<'a> InferenceContext<'a> {
let mut ty = self.db.impl_self_ty(impl_id).substitute(Interner, &substs);
let Some(mut remaining_idx) = unresolved else {
drop(ctx);
return self.resolve_variant_on_alias(ty, None, mod_path);
};
@ -1538,6 +1551,7 @@ impl<'a> InferenceContext<'a> {
// 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);
@ -1558,18 +1572,27 @@ 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,
&mut |_, _reason| {
// FIXME: Report an error.
},
);
tried_resolving_once = true;
ty = self.table.insert_type_vars(ty);
ty = self.table.normalize_associated_types_in(ty);
@ -1582,7 +1605,7 @@ impl<'a> InferenceContext<'a> {
remaining_idx += 1;
remaining_segments = remaining_segments.skip(1);
}
self.push_ty_diagnostics(InferenceTyDiagnosticSource::Body, ctx.diagnostics);
drop(ctx);
let variant = ty.as_adt().and_then(|(id, _)| match id {
AdtId::StructId(s) => Some(VariantId::StructId(s)),
@ -1601,7 +1624,7 @@ impl<'a> InferenceContext<'a> {
};
let substs =
ctx.substs_from_path_segment(resolved_seg, Some(it.into()), true, None);
self.push_ty_diagnostics(InferenceTyDiagnosticSource::Body, ctx.diagnostics);
drop(ctx);
let ty = self.db.ty(it.into());
let ty = self.insert_type_vars(ty.substitute(Interner, &substs));

View file

@ -0,0 +1,128 @@
//! This file contains the [`Diagnostics`] type used during inference,
//! and a wrapper around [`TyLoweringContext`] ([`InferenceTyLoweringContext`]) that replaces
//! it and takes care of diagnostics in inference.
use std::cell::RefCell;
use std::ops::{Deref, DerefMut};
use hir_def::body::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 crate::db::HirDatabase;
use crate::{
InferenceDiagnostic, InferenceTyDiagnosticSource, Ty, TyLoweringContext, TyLoweringDiagnostic,
};
// Unfortunately, this struct needs to use interior mutability (but we encapsulate it)
// because when lowering types and paths we hold a `TyLoweringContext` that holds a reference
// to our resolver and so we cannot have mutable reference, but we really want to have
// ability to dispatch diagnostics during this work otherwise the code becomes a complete mess.
#[derive(Debug, Default, Clone)]
pub(super) struct Diagnostics(RefCell<Vec<InferenceDiagnostic>>);
impl Diagnostics {
pub(super) fn push(&self, diagnostic: InferenceDiagnostic) {
self.0.borrow_mut().push(diagnostic);
}
fn push_ty_diagnostics(
&self,
source: InferenceTyDiagnosticSource,
diagnostics: Vec<TyLoweringDiagnostic>,
) {
self.0.borrow_mut().extend(
diagnostics.into_iter().map(|diag| InferenceDiagnostic::TyDiagnostic { source, diag }),
);
}
pub(super) fn finish(self) -> Vec<InferenceDiagnostic> {
self.0.into_inner()
}
}
pub(super) struct InferenceTyLoweringContext<'a> {
ctx: TyLoweringContext<'a>,
diagnostics: &'a Diagnostics,
source: InferenceTyDiagnosticSource,
}
impl<'a> InferenceTyLoweringContext<'a> {
pub(super) fn new(
db: &'a dyn HirDatabase,
resolver: &'a Resolver,
types_map: &'a TypesMap,
owner: TypeOwnerId,
diagnostics: &'a Diagnostics,
source: InferenceTyDiagnosticSource,
) -> Self {
Self { ctx: TyLoweringContext::new(db, resolver, types_map, owner), diagnostics, source }
}
pub(super) fn resolve_path_in_type_ns(
&mut self,
path: &Path,
node: ExprOrPatId,
) -> Option<(TypeNs, Option<usize>)> {
let diagnostics = self.diagnostics;
self.ctx.resolve_path_in_type_ns(path, &mut |_, diag| {
diagnostics.push(InferenceDiagnostic::PathDiagnostic { node, diag })
})
}
pub(super) fn resolve_path_in_value_ns(
&mut self,
path: &Path,
node: ExprOrPatId,
hygiene_id: HygieneId,
) -> Option<ResolveValueResult> {
let diagnostics = self.diagnostics;
self.ctx.resolve_path_in_value_ns(path, hygiene_id, &mut |_, diag| {
diagnostics.push(InferenceDiagnostic::PathDiagnostic { node, diag })
})
}
pub(super) fn lower_partly_resolved_path(
&mut self,
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 }),
)
}
}
impl<'a> Deref for InferenceTyLoweringContext<'a> {
type Target = TyLoweringContext<'a>;
fn deref(&self) -> &Self::Target {
&self.ctx
}
}
impl DerefMut for InferenceTyLoweringContext<'_> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.ctx
}
}
impl Drop for InferenceTyLoweringContext<'_> {
fn drop(&mut self) {
self.diagnostics
.push_ty_diagnostics(self.source, std::mem::take(&mut self.ctx.diagnostics));
}
}

View file

@ -531,7 +531,7 @@ impl InferenceContext<'_> {
(params, ret_ty)
}
None => {
self.result.diagnostics.push(InferenceDiagnostic::ExpectedFunction {
self.push_diagnostic(InferenceDiagnostic::ExpectedFunction {
call_expr: tgt_expr,
found: callee_ty.clone(),
});
@ -707,7 +707,7 @@ impl InferenceContext<'_> {
self.result.standard_types.never.clone()
}
Expr::RecordLit { path, fields, spread, .. } => {
let (ty, def_id) = self.resolve_variant(path.as_deref(), false);
let (ty, def_id) = self.resolve_variant(tgt_expr.into(), path.as_deref(), false);
if let Some(t) = expected.only_has_type(&mut self.table) {
self.unify(&ty, &t);
@ -1816,9 +1816,10 @@ impl InferenceContext<'_> {
if !is_public {
if let Either::Left(field) = field_id {
// FIXME: Merge this diagnostic into UnresolvedField?
self.result
.diagnostics
.push(InferenceDiagnostic::PrivateField { expr: tgt_expr, field });
self.push_diagnostic(InferenceDiagnostic::PrivateField {
expr: tgt_expr,
field,
});
}
}
ty
@ -1835,7 +1836,7 @@ impl InferenceContext<'_> {
VisibleFromModule::Filter(self.resolver.module()),
name,
);
self.result.diagnostics.push(InferenceDiagnostic::UnresolvedField {
self.push_diagnostic(InferenceDiagnostic::UnresolvedField {
expr: tgt_expr,
receiver: receiver_ty.clone(),
name: name.clone(),
@ -1927,7 +1928,7 @@ impl InferenceContext<'_> {
},
);
self.result.diagnostics.push(InferenceDiagnostic::UnresolvedMethodCall {
self.push_diagnostic(InferenceDiagnostic::UnresolvedMethodCall {
expr: tgt_expr,
receiver: receiver_ty.clone(),
name: method_name.clone(),

View file

@ -35,7 +35,7 @@ impl InferenceContext<'_> {
ellipsis: Option<u32>,
subs: &[PatId],
) -> Ty {
let (ty, def) = self.resolve_variant(path, true);
let (ty, def) = self.resolve_variant(id.into(), path, true);
let var_data = def.map(|it| it.variant_data(self.db.upcast()));
if let Some(variant) = def {
self.write_variant_resolution(id.into(), variant);
@ -115,7 +115,7 @@ impl InferenceContext<'_> {
id: PatId,
subs: impl ExactSizeIterator<Item = (Name, PatId)>,
) -> Ty {
let (ty, def) = self.resolve_variant(path, false);
let (ty, def) = self.resolve_variant(id.into(), path, false);
if let Some(variant) = def {
self.write_variant_resolution(id.into(), variant);
}

View file

@ -14,6 +14,7 @@ use crate::{
builder::ParamKind,
consteval, error_lifetime,
generics::generics,
infer::diagnostics::InferenceTyLoweringContext as TyLoweringContext,
method_resolution::{self, VisibleFromModule},
to_chalk_trait_id, InferenceDiagnostic, Interner, Substitution, TraitRef, TraitRefExt, Ty,
TyBuilder, TyExt, TyKind, ValueTyDefId,
@ -147,36 +148,38 @@ impl InferenceContext<'_> {
path: &Path,
id: ExprOrPatId,
) -> Option<(ValueNs, Option<chalk_ir::Substitution<Interner>>)> {
// Don't use `self.make_ty()` here as we need `orig_ns`.
let mut ctx = TyLoweringContext::new(
self.db,
&self.resolver,
&self.body.types,
self.owner.into(),
&self.diagnostics,
InferenceTyDiagnosticSource::Body,
);
let (value, self_subst) = if let Some(type_ref) = path.type_anchor() {
let last = path.segments().last()?;
// Don't use `self.make_ty()` here as we need `orig_ns`.
let mut ctx = crate::lower::TyLoweringContext::new(
self.db,
&self.resolver,
&self.body.types,
self.owner.into(),
);
let (ty, orig_ns) = 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);
self.push_ty_diagnostics(InferenceTyDiagnosticSource::Body, ctx.diagnostics);
drop(ctx);
let ty = self.table.insert_type_vars(ty);
let ty = self.table.normalize_associated_types_in(ty);
self.resolve_ty_assoc_item(ty, last.name, id).map(|(it, substs)| (it, Some(substs)))?
} else {
let hygiene = self.body.expr_or_pat_path_hygiene(id);
// FIXME: report error, unresolved first path segment
let value_or_partial =
self.resolver.resolve_path_in_value_ns(self.db.upcast(), path, hygiene)?;
let value_or_partial = ctx.resolve_path_in_value_ns(path, id, hygiene)?;
drop(ctx);
match value_or_partial {
ResolveValueResult::ValueNs(it, _) => (it, None),
ResolveValueResult::Partial(def, remaining_index, _) => self
.resolve_assoc_item(def, path, remaining_index, id)
.resolve_assoc_item(id, def, path, remaining_index, id)
.map(|(it, substs)| (it, Some(substs)))?,
}
};
@ -212,6 +215,7 @@ impl InferenceContext<'_> {
fn resolve_assoc_item(
&mut self,
node: ExprOrPatId,
def: TypeNs,
path: &Path,
remaining_index: usize,
@ -260,17 +264,23 @@ impl InferenceContext<'_> {
// as Iterator>::Item::default`)
let remaining_segments_for_ty =
remaining_segments.take(remaining_segments.len() - 1);
let (ty, _) = self.with_body_ty_lowering(|ctx| {
ctx.lower_partly_resolved_path(
def,
resolved_segment,
remaining_segments_for_ty,
true,
&mut |_, _reason| {
// FIXME: Report an error.
},
)
});
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;
}

View file

@ -88,10 +88,10 @@ pub use infer::{
PointerCast,
};
pub use interner::Interner;
pub use lower::diagnostics::*;
pub use lower::{
associated_type_shorthand_candidates, GenericArgsProhibitedReason, ImplTraitLoweringMode,
ParamLoweringMode, TyDefId, TyLoweringContext, TyLoweringDiagnostic, TyLoweringDiagnosticKind,
ValueTyDefId,
associated_type_shorthand_candidates, ImplTraitLoweringMode, ParamLoweringMode, TyDefId,
TyLoweringContext, ValueTyDefId,
};
pub use mapping::{
from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, from_placeholder_idx,

View file

@ -5,6 +5,8 @@
//! - Building the type for an item: This happens through the `ty` query.
//!
//! This usually involves resolving names, collecting generic arguments etc.
pub(crate) mod diagnostics;
use std::{
cell::OnceCell,
iter, mem,
@ -21,6 +23,7 @@ use chalk_ir::{
use either::Either;
use hir_def::{
body::HygieneId,
builtin_type::BuiltinType,
data::adt::StructKind,
expander::Expander,
@ -29,9 +32,9 @@ use hir_def::{
WherePredicateTypeTarget,
},
lang_item::LangItem,
nameres::MacroSubNs,
nameres::{MacroSubNs, ResolvePathResultPrefixInfo},
path::{GenericArg, GenericArgs, ModPath, Path, PathKind, PathSegment, PathSegments},
resolver::{HasResolver, LifetimeNs, Resolver, TypeNs},
resolver::{HasResolver, LifetimeNs, ResolveValueResult, Resolver, TypeNs, ValueNs},
type_ref::{
ConstRef, LifetimeRef, PathId, TraitBoundModifier, TraitRef as HirTraitRef, TypeBound,
TypeRef, TypeRefId, TypesMap, TypesSourceMap,
@ -59,6 +62,7 @@ use crate::{
db::HirDatabase,
error_lifetime,
generics::{generics, trait_self_param_idx, Generics},
lower::diagnostics::*,
make_binders,
mapping::{from_chalk_trait_id, lt_to_placeholder_idx, ToChalk},
static_lifetime, to_assoc_type_id, to_chalk_trait_id, to_placeholder_idx,
@ -102,31 +106,6 @@ impl ImplTraitLoweringState {
}
}
type TypeSource = Either<TypeRefId, hir_def::type_ref::TypeSource>;
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct TyLoweringDiagnostic {
pub source: TypeSource,
pub kind: TyLoweringDiagnosticKind,
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum TyLoweringDiagnosticKind {
GenericArgsProhibited { segment: u32, reason: GenericArgsProhibitedReason },
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GenericArgsProhibitedReason {
Module,
TyParam,
SelfTy,
PrimitiveTy,
/// 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)]
pub struct TyLoweringContext<'a> {
pub db: &'a dyn HirDatabase,
@ -536,8 +515,8 @@ impl<'a> TyLoweringContext<'a> {
/// This is only for `generic_predicates_for_param`, where we can't just
/// lower the self types of the predicates since that could lead to cycles.
/// So we just check here if the `type_ref` resolves to a generic param, and which.
fn lower_ty_only_param(&self, type_ref: TypeRefId) -> Option<TypeOrConstParamId> {
let type_ref = &self.types_map[type_ref];
fn lower_ty_only_param(&mut self, type_ref_id: TypeRefId) -> Option<TypeOrConstParamId> {
let type_ref = &self.types_map[type_ref_id];
let path = match type_ref {
TypeRef::Path(path) => path,
_ => return None,
@ -548,8 +527,10 @@ impl<'a> TyLoweringContext<'a> {
if path.segments().len() > 1 {
return None;
}
let resolution = match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path) {
Some((it, None, _)) => it,
let resolution = match self
.resolve_path_in_type_ns(path, &mut Self::on_path_diagnostic_callback(type_ref_id))
{
Some((it, None)) => it,
_ => return None,
};
match resolution {
@ -584,11 +565,9 @@ impl<'a> TyLoweringContext<'a> {
resolution: TypeNs,
resolved_segment: PathSegment<'_>,
remaining_segments: PathSegments<'_>,
_resolved_segment_idx: u32,
infer_args: bool,
on_prohibited_generics_for_resolved_segment: &mut dyn FnMut(
&mut Self,
GenericArgsProhibitedReason,
),
_on_diagnostic: &mut dyn FnMut(&mut Self, PathLoweringDiagnostic),
) -> (Ty, Option<TypeNs>) {
let ty = match resolution {
TypeNs::TraitId(trait_) => {
@ -655,44 +634,28 @@ impl<'a> TyLoweringContext<'a> {
// FIXME(trait_alias): Implement trait alias.
return (TyKind::Error.intern(Interner), None);
}
TypeNs::GenericParam(param_id) => {
if resolved_segment.args_and_bindings.is_some() {
on_prohibited_generics_for_resolved_segment(
self,
GenericArgsProhibitedReason::TyParam,
);
TypeNs::GenericParam(param_id) => match self.type_param_mode {
ParamLoweringMode::Placeholder => {
TyKind::Placeholder(to_placeholder_idx(self.db, param_id.into()))
}
ParamLoweringMode::Variable => {
let idx = match self
.generics()
.expect("generics in scope")
.type_or_const_param_idx(param_id.into())
{
None => {
never!("no matching generics");
return (TyKind::Error.intern(Interner), None);
}
Some(idx) => idx,
};
match self.type_param_mode {
ParamLoweringMode::Placeholder => {
TyKind::Placeholder(to_placeholder_idx(self.db, param_id.into()))
}
ParamLoweringMode::Variable => {
let idx = match self
.generics()
.expect("generics in scope")
.type_or_const_param_idx(param_id.into())
{
None => {
never!("no matching generics");
return (TyKind::Error.intern(Interner), None);
}
Some(idx) => idx,
};
TyKind::BoundVar(BoundVar::new(self.in_binders, idx))
}
TyKind::BoundVar(BoundVar::new(self.in_binders, idx))
}
.intern(Interner)
}
.intern(Interner),
TypeNs::SelfType(impl_id) => {
if resolved_segment.args_and_bindings.is_some() {
on_prohibited_generics_for_resolved_segment(
self,
GenericArgsProhibitedReason::SelfTy,
);
}
let generics = self.generics().expect("impl should have generic param scope");
match self.type_param_mode {
@ -718,13 +681,6 @@ impl<'a> TyLoweringContext<'a> {
}
}
TypeNs::AdtSelfType(adt) => {
if resolved_segment.args_and_bindings.is_some() {
on_prohibited_generics_for_resolved_segment(
self,
GenericArgsProhibitedReason::SelfTy,
);
}
let generics = generics(self.db.upcast(), adt.into());
let substs = match self.type_param_mode {
ParamLoweringMode::Placeholder => generics.placeholder_subst(self.db),
@ -737,12 +693,6 @@ impl<'a> TyLoweringContext<'a> {
TypeNs::AdtId(it) => self.lower_path_inner(resolved_segment, it.into(), infer_args),
TypeNs::BuiltinType(it) => {
if resolved_segment.args_and_bindings.is_some() {
on_prohibited_generics_for_resolved_segment(
self,
GenericArgsProhibitedReason::PrimitiveTy,
);
}
self.lower_path_inner(resolved_segment, it.into(), infer_args)
}
TypeNs::TypeAliasId(it) => {
@ -754,6 +704,220 @@ impl<'a> TyLoweringContext<'a> {
self.lower_ty_relative_path(ty, Some(resolution), remaining_segments)
}
fn handle_type_ns_resolution(
&mut self,
resolution: &TypeNs,
resolved_segment: PathSegment<'_>,
resolved_segment_idx: usize,
on_diagnostic: &mut dyn FnMut(&mut Self, PathLoweringDiagnostic),
) {
let mut prohibit_generics_on_resolved = |reason| {
if resolved_segment.args_and_bindings.is_some() {
on_diagnostic(
self,
PathLoweringDiagnostic::GenericArgsProhibited {
segment: resolved_segment_idx as u32,
reason,
},
);
}
};
match resolution {
TypeNs::SelfType(_) => {
prohibit_generics_on_resolved(GenericArgsProhibitedReason::SelfTy)
}
TypeNs::GenericParam(_) => {
prohibit_generics_on_resolved(GenericArgsProhibitedReason::TyParam)
}
TypeNs::AdtSelfType(_) => {
prohibit_generics_on_resolved(GenericArgsProhibitedReason::SelfTy)
}
TypeNs::BuiltinType(_) => {
prohibit_generics_on_resolved(GenericArgsProhibitedReason::PrimitiveTy)
}
TypeNs::AdtId(_)
| TypeNs::EnumVariantId(_)
| TypeNs::TypeAliasId(_)
| TypeNs::TraitId(_)
| TypeNs::TraitAliasId(_) => {}
}
}
pub(crate) fn resolve_path_in_type_ns_fully(
&mut self,
path: &Path,
on_diagnostic: &mut dyn FnMut(&mut Self, PathLoweringDiagnostic),
) -> Option<TypeNs> {
let (res, unresolved) = self.resolve_path_in_type_ns(path, on_diagnostic)?;
if unresolved.is_some() {
return None;
}
Some(res)
}
pub(crate) fn resolve_path_in_type_ns(
&mut self,
path: &Path,
on_diagnostic: &mut dyn FnMut(&mut Self, PathLoweringDiagnostic),
) -> Option<(TypeNs, Option<usize>)> {
let (resolution, remaining_index, _) =
self.resolver.resolve_path_in_type_ns(self.db.upcast(), path)?;
let segments = path.segments();
match path {
// `segments.is_empty()` can occur with `self`.
Path::Normal(..) if !segments.is_empty() => (),
_ => return Some((resolution, remaining_index)),
};
let (module_segments, resolved_segment_idx, resolved_segment) = match remaining_index {
None => (
segments.strip_last(),
segments.len() - 1,
segments.last().expect("resolved path has at least one element"),
),
Some(i) => (segments.take(i - 1), i - 1, segments.get(i - 1).unwrap()),
};
for (i, mod_segment) in module_segments.iter().enumerate() {
if mod_segment.args_and_bindings.is_some() {
on_diagnostic(
self,
PathLoweringDiagnostic::GenericArgsProhibited {
segment: i as u32,
reason: GenericArgsProhibitedReason::Module,
},
);
}
}
self.handle_type_ns_resolution(
&resolution,
resolved_segment,
resolved_segment_idx,
on_diagnostic,
);
Some((resolution, remaining_index))
}
pub(crate) fn resolve_path_in_value_ns(
&mut self,
path: &Path,
hygiene_id: HygieneId,
on_diagnostic: &mut dyn FnMut(&mut Self, PathLoweringDiagnostic),
) -> Option<ResolveValueResult> {
let (res, prefix_info) = self.resolver.resolve_path_in_value_ns_with_prefix_info(
self.db.upcast(),
path,
hygiene_id,
)?;
let segments = path.segments();
match path {
// `segments.is_empty()` can occur with `self`.
Path::Normal(..) if !segments.is_empty() => (),
_ => return Some(res),
};
let (mod_segments, enum_segment) = match res {
ResolveValueResult::Partial(_, unresolved_segment, _) => {
(segments.take(unresolved_segment - 1), None)
}
ResolveValueResult::ValueNs(ValueNs::EnumVariantId(_), _)
if prefix_info == ResolvePathResultPrefixInfo::Enum =>
{
(segments.strip_last_two(), segments.len().checked_sub(2))
}
ResolveValueResult::ValueNs(..) => (segments.strip_last(), None),
};
for (i, mod_segment) in mod_segments.iter().enumerate() {
if mod_segment.args_and_bindings.is_some() {
on_diagnostic(
self,
PathLoweringDiagnostic::GenericArgsProhibited {
segment: i as u32,
reason: GenericArgsProhibitedReason::Module,
},
);
}
}
if let Some(enum_segment) = enum_segment {
if segments.get(enum_segment).is_some_and(|it| it.args_and_bindings.is_some())
&& segments.get(enum_segment + 1).is_some_and(|it| it.args_and_bindings.is_some())
{
on_diagnostic(
self,
PathLoweringDiagnostic::GenericArgsProhibited {
segment: (enum_segment + 1) as u32,
reason: GenericArgsProhibitedReason::EnumVariant,
},
);
}
}
match &res {
ResolveValueResult::ValueNs(resolution, _) => {
let resolved_segment_idx =
segments.len().checked_sub(1).unwrap_or_else(|| panic!("{path:?}"));
let resolved_segment = segments.last().unwrap();
let mut prohibit_generics_on_resolved = |reason| {
if resolved_segment.args_and_bindings.is_some() {
on_diagnostic(
self,
PathLoweringDiagnostic::GenericArgsProhibited {
segment: resolved_segment_idx as u32,
reason,
},
);
}
};
match resolution {
ValueNs::ImplSelf(_) => {
prohibit_generics_on_resolved(GenericArgsProhibitedReason::SelfTy)
}
// FIXME: rustc generates E0107 (incorrect number of generic arguments) and not
// E0109 (generic arguments provided for a type that doesn't accept them) for
// consts and statics, presumably as a defense against future in which consts
// and statics can be generic, or just because it was easier for rustc implementors.
// That means we'll show the wrong error code. Because of us it's easier to do it
// this way :)
ValueNs::GenericParam(_) | ValueNs::ConstId(_) => {
prohibit_generics_on_resolved(GenericArgsProhibitedReason::Const)
}
ValueNs::StaticId(_) => {
prohibit_generics_on_resolved(GenericArgsProhibitedReason::Static)
}
ValueNs::FunctionId(_) | ValueNs::StructId(_) | ValueNs::EnumVariantId(_) => {}
ValueNs::LocalBinding(_) => {}
}
}
ResolveValueResult::Partial(resolution, unresolved_idx, _) => {
let resolved_segment_idx = unresolved_idx - 1;
let resolved_segment = segments.get(resolved_segment_idx).unwrap();
self.handle_type_ns_resolution(
resolution,
resolved_segment,
resolved_segment_idx,
on_diagnostic,
);
}
};
Some(res)
}
fn on_path_diagnostic_callback(
type_ref: TypeRefId,
) -> impl FnMut(&mut Self, PathLoweringDiagnostic) {
move |this, diag| {
this.push_diagnostic(type_ref, TyLoweringDiagnosticKind::PathDiagnostic(diag))
}
}
pub(crate) fn lower_path(&mut self, path: &Path, path_id: PathId) -> (Ty, Option<TypeNs>) {
// Resolve the path (in type namespace)
if let Some(type_ref) = path.type_anchor() {
@ -761,11 +925,13 @@ impl<'a> TyLoweringContext<'a> {
return self.lower_ty_relative_path(ty, res, path.segments());
}
let (resolution, remaining_index, _) =
match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path) {
Some(it) => it,
None => return (TyKind::Error.intern(Interner), None),
};
let (resolution, remaining_index) = match self.resolve_path_in_type_ns(
path,
&mut Self::on_path_diagnostic_callback(path_id.type_ref()),
) {
Some(it) => it,
None => return (TyKind::Error.intern(Interner), None),
};
if matches!(resolution, TypeNs::TraitId(_)) && remaining_index.is_none() {
// trait object type without dyn
@ -774,38 +940,22 @@ impl<'a> TyLoweringContext<'a> {
return (ty, None);
}
let (module_segments, resolved_segment_idx, resolved_segment, remaining_segments) =
match remaining_index {
None => (
path.segments().strip_last(),
path.segments().len() - 1,
path.segments().last().expect("resolved path has at least one element"),
PathSegments::EMPTY,
),
Some(i) => (
path.segments().take(i - 1),
i - 1,
path.segments().get(i - 1).unwrap(),
path.segments().skip(i),
),
};
self.prohibit_generics(path_id, 0, module_segments, GenericArgsProhibitedReason::Module);
let (resolved_segment_idx, resolved_segment, remaining_segments) = match remaining_index {
None => (
path.segments().len() - 1,
path.segments().last().expect("resolved path has at least one element"),
PathSegments::EMPTY,
),
Some(i) => (i - 1, path.segments().get(i - 1).unwrap(), path.segments().skip(i)),
};
self.lower_partly_resolved_path(
resolution,
resolved_segment,
remaining_segments,
resolved_segment_idx as u32,
false,
&mut |this, reason| {
this.push_diagnostic(
path_id.type_ref(),
TyLoweringDiagnosticKind::GenericArgsProhibited {
segment: resolved_segment_idx as u32,
reason,
},
)
},
&mut Self::on_path_diagnostic_callback(path_id.type_ref()),
)
}
@ -1107,7 +1257,9 @@ impl<'a> TyLoweringContext<'a> {
if segment.args_and_bindings.is_some() {
self.push_diagnostic(
path_id.type_ref(),
TyLoweringDiagnosticKind::GenericArgsProhibited { segment: idx, reason },
TyLoweringDiagnosticKind::PathDiagnostic(
PathLoweringDiagnostic::GenericArgsProhibited { segment: idx, reason },
),
);
}
});
@ -1119,7 +1271,10 @@ impl<'a> TyLoweringContext<'a> {
explicit_self_ty: Ty,
) -> Option<TraitRef> {
let path = &self.types_map[path_id];
let resolved = match self.resolver.resolve_path_in_type_ns_fully(self.db.upcast(), path)? {
let resolved = match self.resolve_path_in_type_ns_fully(
path,
&mut Self::on_path_diagnostic_callback(path_id.type_ref()),
)? {
// FIXME(trait_alias): We need to handle trait alias here.
TypeNs::TraitId(tr) => tr,
_ => return None,

View file

@ -0,0 +1,36 @@
//! This files contains the declaration of diagnostics kinds for ty and path lowering.
use either::Either;
use hir_def::type_ref::TypeRefId;
type TypeSource = Either<TypeRefId, hir_def::type_ref::TypeSource>;
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct TyLoweringDiagnostic {
pub source: TypeSource,
pub kind: TyLoweringDiagnosticKind,
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum TyLoweringDiagnosticKind {
PathDiagnostic(PathLoweringDiagnostic),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GenericArgsProhibitedReason {
Module,
TyParam,
SelfTy,
PrimitiveTy,
Const,
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 },
}

View file

@ -15,12 +15,12 @@ use hir_expand::{name::Name, HirFileId, InFile};
use hir_ty::{
db::HirDatabase,
diagnostics::{BodyValidationDiagnostic, UnsafetyReason},
CastError, InferenceDiagnostic, InferenceTyDiagnosticSource, TyLoweringDiagnostic,
TyLoweringDiagnosticKind,
CastError, InferenceDiagnostic, InferenceTyDiagnosticSource, PathLoweringDiagnostic,
TyLoweringDiagnostic, TyLoweringDiagnosticKind,
};
use syntax::{
ast::{self, HasGenericArgs},
AstPtr, SyntaxError, SyntaxNodePtr, TextRange,
match_ast, AstNode, AstPtr, SyntaxError, SyntaxNodePtr, TextRange,
};
use triomphe::Arc;
@ -674,6 +674,39 @@ impl AnyDiagnostic {
};
Self::ty_diagnostic(diag, source_map, db)?
}
InferenceDiagnostic::PathDiagnostic { node, diag } => {
let source = expr_or_pat_syntax(*node)?;
let syntax = source.value.to_node(&db.parse_or_expand(source.file_id));
let path = match_ast! {
match (syntax.syntax()) {
ast::RecordExpr(it) => it.path()?,
ast::RecordPat(it) => it.path()?,
ast::TupleStructPat(it) => it.path()?,
ast::PathExpr(it) => it.path()?,
ast::PathPat(it) => it.path()?,
_ => return None,
}
};
Self::path_diagnostic(diag, source.with_value(path))?
}
})
}
fn path_diagnostic(
diag: &PathLoweringDiagnostic,
path: InFile<ast::Path>,
) -> Option<AnyDiagnostic> {
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()
} else {
AstPtr::new(&segment.parenthesized_arg_list()?).wrap_right()
};
let args = path.with_value(args);
GenericArgsProhibited { args, reason }.into()
}
})
}
@ -693,17 +726,10 @@ impl AnyDiagnostic {
Either::Right(source) => source,
};
let syntax = || source.value.to_node(&db.parse_or_expand(source.file_id));
Some(match diag.kind {
TyLoweringDiagnosticKind::GenericArgsProhibited { segment, reason } => {
Some(match &diag.kind {
TyLoweringDiagnosticKind::PathDiagnostic(diag) => {
let ast::Type::PathType(syntax) = syntax() else { return None };
let segment = hir_segment_to_ast_segment(&syntax.path()?, segment)?;
let args = if let Some(generics) = segment.generic_arg_list() {
AstPtr::new(&generics).wrap_left()
} else {
AstPtr::new(&segment.parenthesized_arg_list()?).wrap_right()
};
let args = source.with_value(args);
GenericArgsProhibited { args, reason }.into()
Self::path_diagnostic(diag, source.with_value(syntax.path()?))?
}
})
}

View file

@ -34,6 +34,8 @@ fn describe_reason(reason: GenericArgsProhibitedReason) -> String {
return "you can specify generic arguments on either the enum or the variant, but not both"
.to_owned();
}
GenericArgsProhibitedReason::Const => "constants",
GenericArgsProhibitedReason::Static => "statics",
};
format!("generic arguments are not allowed on {kind}")
}
@ -435,6 +437,144 @@ type T = bool<i32>;
impl Trait for () {
type Assoc = i32<bool>;
// ^^^^^^ 💡 error: generic arguments are not allowed on builtin types
}
"#,
);
}
#[test]
fn in_record_expr() {
check_diagnostics(
r#"
mod foo {
pub struct Bar { pub field: i32 }
}
fn baz() {
let _ = foo::<()>::Bar { field: 0 };
// ^^^^^^ 💡 error: generic arguments are not allowed on modules
}
"#,
);
}
#[test]
fn in_record_pat() {
check_diagnostics(
r#"
mod foo {
pub struct Bar { field: i32 }
}
fn baz(v: foo::Bar) {
let foo::<()>::Bar { .. } = v;
// ^^^^^^ 💡 error: generic arguments are not allowed on modules
}
"#,
);
}
#[test]
fn in_tuple_struct_pat() {
check_diagnostics(
r#"
mod foo {
pub struct Bar(i32);
}
fn baz(v: foo::Bar) {
let foo::<()>::Bar(..) = v;
// ^^^^^^ 💡 error: generic arguments are not allowed on modules
}
"#,
);
}
#[test]
fn in_path_pat() {
check_diagnostics(
r#"
mod foo {
pub struct Bar;
}
fn baz(v: foo::Bar) {
let foo::<()>::Bar = v;
// ^^^^^^ 💡 error: generic arguments are not allowed on modules
}
"#,
);
}
#[test]
fn in_path_expr() {
check_diagnostics(
r#"
mod foo {
pub struct Bar;
}
fn baz() {
let _ = foo::<()>::Bar;
// ^^^^^^ 💡 error: generic arguments are not allowed on modules
}
"#,
);
}
#[test]
fn const_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
let _ = STATIC::<()>;
// ^^^^^^ 💡 error: generic arguments are not allowed on statics
}
"#,
);
}
#[test]
fn enum_variant() {
check_diagnostics(
r#"
enum Enum<A> {
Variant(A),
}
mod enum_ {
pub(super) use super::Enum::Variant as V;
}
fn baz() {
let v = Enum::<()>::Variant::<()>(());
// ^^^^^^ 💡 error: you can specify generic arguments on either the enum or the variant, but not both
let Enum::<()>::Variant::<()>(..) = v;
// ^^^^^^ 💡 error: you can specify generic arguments on either the enum or the variant, but not both
let _ = Enum::<()>::Variant(());
let _ = Enum::Variant::<()>(());
}
fn foo() {
use Enum::Variant;
let _ = Variant::<()>(());
let _ = enum_::V::<()>(());
let _ = enum_::<()>::V::<()>(());
// ^^^^^^ 💡 error: generic arguments are not allowed on modules
}
"#,
);
}
#[test]
fn dyn_trait() {
check_diagnostics(
r#"
mod foo {
pub trait Trait {}
}
fn bar() {
let _: &dyn foo::<()>::Trait;
// ^^^^^^ 💡 error: generic arguments are not allowed on modules
let _: &foo::<()>::Trait;
// ^^^^^^ 💡 error: generic arguments are not allowed on modules
}
"#,
);