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, FxIndexMap, LocalModuleId, Lookup, MacroExpander, MacroId, ModuleId, ProcMacroId, UseId,
}; };
pub use self::path_resolution::ResolvePathResultPrefixInfo;
const PREDEFINED_TOOLS: &[SmolStr] = &[ const PREDEFINED_TOOLS: &[SmolStr] = &[
SmolStr::new_static("clippy"), SmolStr::new_static("clippy"),
SmolStr::new_static("rustfmt"), SmolStr::new_static("rustfmt"),
@ -615,13 +617,15 @@ impl DefMap {
(res.resolved_def, res.segment_index) (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( pub(crate) fn resolve_path_locally(
&self, &self,
db: &dyn DefDatabase, db: &dyn DefDatabase,
original_module: LocalModuleId, original_module: LocalModuleId,
path: &ModPath, path: &ModPath,
shadow: BuiltinShadowMode, shadow: BuiltinShadowMode,
) -> (PerNs, Option<usize>) { ) -> (PerNs, Option<usize>, ResolvePathResultPrefixInfo) {
let res = self.resolve_path_fp_with_macro_single( let res = self.resolve_path_fp_with_macro_single(
db, db,
ResolveMode::Other, ResolveMode::Other,
@ -630,7 +634,7 @@ impl DefMap {
shadow, shadow,
None, // Currently this function isn't used for macro resolution. 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. /// 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}, attr_resolution::{attr_macro_as_call_id, derive_macro_as_call_id},
diagnostics::DefDiagnostic, diagnostics::DefDiagnostic,
mod_resolution::ModDir, mod_resolution::ModDir,
path_resolution::ReachedFixedPoint, path_resolution::{ReachedFixedPoint, ResolvePathResultPrefixInfo},
proc_macro::{parse_macro_name_and_helper_attrs, ProcMacroDef, ProcMacroKind}, proc_macro::{parse_macro_name_and_helper_attrs, ProcMacroDef, ProcMacroKind},
sub_namespace_match, BuiltinShadowMode, DefMap, MacroSubNs, ModuleData, ModuleOrigin, sub_namespace_match, BuiltinShadowMode, DefMap, MacroSubNs, ModuleData, ModuleOrigin,
ResolveMode, ResolveMode,
@ -797,7 +797,7 @@ impl DefCollector<'_> {
return PartialResolvedImport::Unresolved; return PartialResolvedImport::Unresolved;
} }
if res.from_differing_crate { if let ResolvePathResultPrefixInfo::DifferingCrate = res.prefix_info {
return PartialResolvedImport::Resolved( return PartialResolvedImport::Resolved(
def.filter_visibility(|v| matches!(v, Visibility::Public)), 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) resolved_def: PerNs,
pub(super) segment_index: Option<usize>, pub(super) segment_index: Option<usize>,
pub(super) reached_fixedpoint: ReachedFixedPoint, 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 { impl ResolvePathResult {
fn empty(reached_fixedpoint: ReachedFixedPoint) -> 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( fn new(
resolved_def: PerNs, resolved_def: PerNs,
reached_fixedpoint: ReachedFixedPoint, reached_fixedpoint: ReachedFixedPoint,
segment_index: Option<usize>, segment_index: Option<usize>,
from_differing_crate: bool, prefix_info: ResolvePathResultPrefixInfo,
) -> ResolvePathResult { ) -> 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 { if result.reached_fixedpoint == ReachedFixedPoint::No {
result.reached_fixedpoint = new.reached_fixedpoint; 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) { result.segment_index = match (result.segment_index, new.segment_index) {
(Some(idx), None) => Some(idx), (Some(idx), None) => Some(idx),
(Some(old), Some(new)) => Some(old.max(new)), (Some(old), Some(new)) => Some(old.max(new)),
@ -403,14 +426,14 @@ impl DefMap {
fn resolve_remaining_segments<'a>( fn resolve_remaining_segments<'a>(
&self, &self,
segments: impl Iterator<Item = (usize, &'a Name)>, mut segments: impl Iterator<Item = (usize, &'a Name)>,
mut curr_per_ns: PerNs, mut curr_per_ns: PerNs,
path: &ModPath, path: &ModPath,
db: &dyn DefDatabase, db: &dyn DefDatabase,
shadow: BuiltinShadowMode, shadow: BuiltinShadowMode,
original_module: LocalModuleId, original_module: LocalModuleId,
) -> ResolvePathResult { ) -> ResolvePathResult {
for (i, segment) in segments { while let Some((i, segment)) = segments.next() {
let curr = match curr_per_ns.take_types_full() { let curr = match curr_per_ns.take_types_full() {
Some(r) => r, Some(r) => r,
None => { None => {
@ -443,7 +466,7 @@ impl DefMap {
def, def,
ReachedFixedPoint::Yes, ReachedFixedPoint::Yes,
s.map(|s| s + i), s.map(|s| s + i),
true, ResolvePathResultPrefixInfo::DifferingCrate,
); );
} }
@ -488,17 +511,28 @@ impl DefMap {
), ),
}) })
}); });
match res { // FIXME: Need to filter visibility here and below? Not sure.
Some(res) => res, return match res {
None => { Some(res) => {
return ResolvePathResult::new( if segments.next().is_some() {
PerNs::types(e.into(), curr.vis, curr.import), // Enum variants are in value namespace, segments left => no resolution.
ReachedFixedPoint::Yes, ResolvePathResult::empty(ReachedFixedPoint::No)
Some(i), } else {
false, 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 => { s => {
// could be an inherent method call in UFCS form // could be an inherent method call in UFCS form
@ -513,7 +547,7 @@ impl DefMap {
PerNs::types(s, curr.vis, curr.import), PerNs::types(s, curr.vis, curr.import),
ReachedFixedPoint::Yes, ReachedFixedPoint::Yes,
Some(i), Some(i),
false, ResolvePathResultPrefixInfo::None,
); );
} }
}; };
@ -522,7 +556,12 @@ impl DefMap {
.filter_visibility(|vis| vis.is_visible_from_def_map(db, self, original_module)); .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( fn resolve_name_in_module(

View file

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

View file

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

View file

@ -16,6 +16,7 @@
pub(crate) mod cast; pub(crate) mod cast;
pub(crate) mod closure; pub(crate) mod closure;
mod coerce; mod coerce;
mod diagnostics;
mod expr; mod expr;
mod mutability; mod mutability;
mod pat; mod pat;
@ -57,15 +58,20 @@ use crate::{
db::HirDatabase, db::HirDatabase,
fold_tys, fold_tys,
generics::Generics, generics::Generics,
infer::{coerce::CoerceMany, expr::ExprIsRead, unify::InferenceTable}, infer::{
lower::{ImplTraitLoweringMode, TyLoweringDiagnostic}, coerce::CoerceMany,
diagnostics::{Diagnostics, InferenceTyLoweringContext as TyLoweringContext},
expr::ExprIsRead,
unify::InferenceTable,
},
lower::{diagnostics::TyLoweringDiagnostic, ImplTraitLoweringMode},
mir::MirSpan, mir::MirSpan,
to_assoc_type_id, to_assoc_type_id,
traits::FnTrait, traits::FnTrait,
utils::{InTypeConstIdMetadata, UnevaluatedConstEvaluatorFolder}, utils::{InTypeConstIdMetadata, UnevaluatedConstEvaluatorFolder},
AliasEq, AliasTy, Binders, ClosureId, Const, DomainGoal, GenericArg, Goal, ImplTraitId, AliasEq, AliasTy, Binders, ClosureId, Const, DomainGoal, GenericArg, Goal, ImplTraitId,
ImplTraitIdx, InEnvironment, Interner, Lifetime, OpaqueTyId, ParamLoweringMode, ProjectionTy, ImplTraitIdx, InEnvironment, Interner, Lifetime, OpaqueTyId, ParamLoweringMode,
Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, PathLoweringDiagnostic, ProjectionTy, Substitution, TraitEnvironment, Ty, TyBuilder, TyExt,
}; };
// This lint has a false positive here. See the link below for details. // This lint has a false positive here. See the link below for details.
@ -276,6 +282,10 @@ pub enum InferenceDiagnostic {
source: InferenceTyDiagnosticSource, source: InferenceTyDiagnosticSource,
diag: TyLoweringDiagnostic, diag: TyLoweringDiagnostic,
}, },
PathDiagnostic {
node: ExprOrPatId,
diag: PathLoweringDiagnostic,
},
} }
/// A mismatch between an expected and an inferred type. /// 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 /// [`InferenceContext`] and store the tuples substitution there. This map is the reverse of
/// that which allows us to resolve a [`TupleFieldId`]s type. /// that which allows us to resolve a [`TupleFieldId`]s type.
pub tuple_field_access_types: FxHashMap<TupleId, Substitution>, 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 diagnostics: Vec<InferenceDiagnostic>,
pub type_of_expr: ArenaMap<ExprId, Ty>, pub type_of_expr: ArenaMap<ExprId, Ty>,
/// For each pattern record the type it resolves to. /// 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) db: &'a dyn HirDatabase,
pub(crate) owner: DefWithBodyId, pub(crate) owner: DefWithBodyId,
pub(crate) body: &'a Body, 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, pub(crate) resolver: Resolver,
generics: OnceCell<Option<Generics>>, generics: OnceCell<Option<Generics>>,
table: unify::InferenceTable<'a>, table: unify::InferenceTable<'a>,
@ -620,6 +633,8 @@ pub(crate) struct InferenceContext<'a> {
/// comment on `InferenceContext::sort_closures` /// comment on `InferenceContext::sort_closures`
closure_dependencies: FxHashMap<ClosureId, Vec<ClosureId>>, closure_dependencies: FxHashMap<ClosureId, Vec<ClosureId>>,
deferred_closures: FxHashMap<ClosureId, Vec<(Ty, Ty, Vec<Ty>, ExprId)>>, deferred_closures: FxHashMap<ClosureId, Vec<(Ty, Ty, Vec<Ty>, ExprId)>>,
diagnostics: Diagnostics,
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -701,6 +716,7 @@ impl<'a> InferenceContext<'a> {
deferred_closures: FxHashMap::default(), deferred_closures: FxHashMap::default(),
closure_dependencies: FxHashMap::default(), closure_dependencies: FxHashMap::default(),
inside_assignment: false, inside_assignment: false,
diagnostics: Diagnostics::default(),
} }
} }
@ -724,8 +740,10 @@ impl<'a> InferenceContext<'a> {
mut result, mut result,
mut deferred_cast_checks, mut deferred_cast_checks,
tuple_field_accesses_rev, tuple_field_accesses_rev,
diagnostics,
.. ..
} = self; } = self;
let mut diagnostics = diagnostics.finish();
// Destructure every single field so whenever new fields are added to `InferenceResult` we // Destructure every single field so whenever new fields are added to `InferenceResult` we
// don't forget to handle them here. // don't forget to handle them here.
let InferenceResult { let InferenceResult {
@ -733,7 +751,6 @@ impl<'a> InferenceContext<'a> {
field_resolutions: _, field_resolutions: _,
variant_resolutions: _, variant_resolutions: _,
assoc_resolutions, assoc_resolutions,
diagnostics,
type_of_expr, type_of_expr,
type_of_pat, type_of_pat,
type_of_binding, type_of_binding,
@ -752,6 +769,7 @@ impl<'a> InferenceContext<'a> {
mutated_bindings_in_closure: _, mutated_bindings_in_closure: _,
tuple_field_access_types: _, tuple_field_access_types: _,
coercion_casts, coercion_casts,
diagnostics: _,
} = &mut result; } = &mut result;
table.fallback_if_possible(); table.fallback_if_possible();
@ -866,6 +884,9 @@ impl<'a> InferenceContext<'a> {
*has_errors || subst.type_parameters(Interner).any(|ty| ty.contains_unknown()); *has_errors || subst.type_parameters(Interner).any(|ty| ty.contains_unknown());
}) })
.collect(); .collect();
result.diagnostics = diagnostics;
result result
} }
@ -1238,41 +1259,28 @@ impl<'a> InferenceContext<'a> {
self.result.type_of_binding.insert(id, ty); self.result.type_of_binding.insert(id, ty);
} }
fn push_diagnostic(&mut self, diagnostic: InferenceDiagnostic) { fn push_diagnostic(&self, diagnostic: InferenceDiagnostic) {
self.result.diagnostics.push(diagnostic); self.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 with_ty_lowering<R>( fn with_ty_lowering<R>(
&mut self, &mut self,
types_map: &TypesMap, types_map: &TypesMap,
types_source: InferenceTyDiagnosticSource, types_source: InferenceTyDiagnosticSource,
f: impl FnOnce(&mut crate::lower::TyLoweringContext<'_>) -> R, f: impl FnOnce(&mut TyLoweringContext<'_>) -> R,
) -> R { ) -> R {
let mut ctx = crate::lower::TyLoweringContext::new( let mut ctx = TyLoweringContext::new(
self.db, self.db,
&self.resolver, &self.resolver,
types_map, types_map,
self.owner.into(), self.owner.into(),
&self.diagnostics,
types_source,
); );
let result = f(&mut ctx); f(&mut ctx)
self.push_ty_diagnostics(types_source, ctx.diagnostics);
result
} }
fn with_body_ty_lowering<R>( fn with_body_ty_lowering<R>(&mut self, f: impl FnOnce(&mut TyLoweringContext<'_>) -> R) -> R {
&mut self,
f: impl FnOnce(&mut crate::lower::TyLoweringContext<'_>) -> R,
) -> R {
self.with_ty_lowering(&self.body.types, InferenceTyDiagnosticSource::Body, f) 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 { let path = match path {
Some(path) => path, Some(path) => path,
None => return (self.err_ty(), None), None => return (self.err_ty(), None),
}; };
let mut ctx = crate::lower::TyLoweringContext::new( let mut ctx = TyLoweringContext::new(
self.db, self.db,
&self.resolver, &self.resolver,
&self.body.types, &self.body.types,
self.owner.into(), self.owner.into(),
&self.diagnostics,
InferenceTyDiagnosticSource::Body,
); );
let (resolution, unresolved) = if value_ns { let (resolution, unresolved) = if value_ns {
match self.resolver.resolve_path_in_value_ns(self.db.upcast(), path, HygieneId::ROOT) { let Some(res) = ctx.resolve_path_in_value_ns(path, node, HygieneId::ROOT) else {
Some(ResolveValueResult::ValueNs(value, _)) => match value { return (self.err_ty(), None);
};
match res {
ResolveValueResult::ValueNs(value, _) => match value {
ValueNs::EnumVariantId(var) => { ValueNs::EnumVariantId(var) => {
let substs = ctx.substs_from_path(path, var.into(), true); let substs = ctx.substs_from_path(path, var.into(), true);
self.push_ty_diagnostics( drop(ctx);
InferenceTyDiagnosticSource::Body,
ctx.diagnostics,
);
let ty = self.db.ty(var.lookup(self.db.upcast()).parent.into()); let ty = self.db.ty(var.lookup(self.db.upcast()).parent.into());
let ty = self.insert_type_vars(ty.substitute(Interner, &substs)); let ty = self.insert_type_vars(ty.substitute(Interner, &substs));
return (ty, Some(var.into())); return (ty, Some(var.into()));
} }
ValueNs::StructId(strukt) => { ValueNs::StructId(strukt) => {
let substs = ctx.substs_from_path(path, strukt.into(), true); let substs = ctx.substs_from_path(path, strukt.into(), true);
self.push_ty_diagnostics( drop(ctx);
InferenceTyDiagnosticSource::Body,
ctx.diagnostics,
);
let ty = self.db.ty(strukt.into()); let ty = self.db.ty(strukt.into());
let ty = self.insert_type_vars(ty.substitute(Interner, &substs)); let ty = self.insert_type_vars(ty.substitute(Interner, &substs));
return (ty, Some(strukt.into())); return (ty, Some(strukt.into()));
} }
ValueNs::ImplSelf(impl_id) => (TypeNs::SelfType(impl_id), None), 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, _)) => { ResolveValueResult::Partial(typens, unresolved, _) => (typens, Some(unresolved)),
(typens, Some(unresolved))
}
None => return (self.err_ty(), None),
} }
} else { } else {
match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path) { match ctx.resolve_path_in_type_ns(path, node) {
Some((it, idx, _)) => (it, idx), Some((it, idx)) => (it, idx),
None => return (self.err_ty(), None), None => return (self.err_ty(), None),
} }
}; };
@ -1506,21 +1518,21 @@ impl<'a> InferenceContext<'a> {
return match resolution { return match resolution {
TypeNs::AdtId(AdtId::StructId(strukt)) => { TypeNs::AdtId(AdtId::StructId(strukt)) => {
let substs = ctx.substs_from_path(path, strukt.into(), true); 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.db.ty(strukt.into());
let ty = self.insert_type_vars(ty.substitute(Interner, &substs)); let ty = self.insert_type_vars(ty.substitute(Interner, &substs));
forbid_unresolved_segments((ty, Some(strukt.into())), unresolved) forbid_unresolved_segments((ty, Some(strukt.into())), unresolved)
} }
TypeNs::AdtId(AdtId::UnionId(u)) => { TypeNs::AdtId(AdtId::UnionId(u)) => {
let substs = ctx.substs_from_path(path, u.into(), true); 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.db.ty(u.into());
let ty = self.insert_type_vars(ty.substitute(Interner, &substs)); let ty = self.insert_type_vars(ty.substitute(Interner, &substs));
forbid_unresolved_segments((ty, Some(u.into())), unresolved) forbid_unresolved_segments((ty, Some(u.into())), unresolved)
} }
TypeNs::EnumVariantId(var) => { TypeNs::EnumVariantId(var) => {
let substs = ctx.substs_from_path(path, var.into(), true); 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.db.ty(var.lookup(self.db.upcast()).parent.into());
let ty = self.insert_type_vars(ty.substitute(Interner, &substs)); let ty = self.insert_type_vars(ty.substitute(Interner, &substs));
forbid_unresolved_segments((ty, Some(var.into())), unresolved) 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 mut ty = self.db.impl_self_ty(impl_id).substitute(Interner, &substs);
let Some(mut remaining_idx) = unresolved else { let Some(mut remaining_idx) = unresolved else {
drop(ctx);
return self.resolve_variant_on_alias(ty, None, mod_path); 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 // We need to try resolving unresolved segments one by one because each may resolve
// to a projection, which `TyLoweringContext` cannot handle on its own. // to a projection, which `TyLoweringContext` cannot handle on its own.
let mut tried_resolving_once = false;
while !remaining_segments.is_empty() { while !remaining_segments.is_empty() {
let resolved_segment = path.segments().get(remaining_idx - 1).unwrap(); let resolved_segment = path.segments().get(remaining_idx - 1).unwrap();
let current_segment = remaining_segments.take(1); 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 // `lower_partly_resolved_path()` returns `None` as type namespace unless
// `remaining_segments` is empty, which is never the case here. We don't know // `remaining_segments` is empty, which is never the case here. We don't know
// which namespace the new `ty` is in until normalized anyway. // which namespace the new `ty` is in until normalized anyway.
(ty, _) = ctx.lower_partly_resolved_path( (ty, _) = ctx.lower_partly_resolved_path(
node,
resolution, resolution,
resolved_segment, resolved_segment,
current_segment, current_segment,
(remaining_idx - 1) as u32,
false, false,
&mut |_, _reason| {
// FIXME: Report an error.
},
); );
tried_resolving_once = true;
ty = self.table.insert_type_vars(ty); ty = self.table.insert_type_vars(ty);
ty = self.table.normalize_associated_types_in(ty); ty = self.table.normalize_associated_types_in(ty);
@ -1582,7 +1605,7 @@ impl<'a> InferenceContext<'a> {
remaining_idx += 1; remaining_idx += 1;
remaining_segments = remaining_segments.skip(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 { let variant = ty.as_adt().and_then(|(id, _)| match id {
AdtId::StructId(s) => Some(VariantId::StructId(s)), AdtId::StructId(s) => Some(VariantId::StructId(s)),
@ -1601,7 +1624,7 @@ impl<'a> InferenceContext<'a> {
}; };
let substs = let substs =
ctx.substs_from_path_segment(resolved_seg, Some(it.into()), true, None); 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.db.ty(it.into());
let ty = self.insert_type_vars(ty.substitute(Interner, &substs)); 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) (params, ret_ty)
} }
None => { None => {
self.result.diagnostics.push(InferenceDiagnostic::ExpectedFunction { self.push_diagnostic(InferenceDiagnostic::ExpectedFunction {
call_expr: tgt_expr, call_expr: tgt_expr,
found: callee_ty.clone(), found: callee_ty.clone(),
}); });
@ -707,7 +707,7 @@ impl InferenceContext<'_> {
self.result.standard_types.never.clone() self.result.standard_types.never.clone()
} }
Expr::RecordLit { path, fields, spread, .. } => { 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) { if let Some(t) = expected.only_has_type(&mut self.table) {
self.unify(&ty, &t); self.unify(&ty, &t);
@ -1816,9 +1816,10 @@ impl InferenceContext<'_> {
if !is_public { if !is_public {
if let Either::Left(field) = field_id { if let Either::Left(field) = field_id {
// FIXME: Merge this diagnostic into UnresolvedField? // FIXME: Merge this diagnostic into UnresolvedField?
self.result self.push_diagnostic(InferenceDiagnostic::PrivateField {
.diagnostics expr: tgt_expr,
.push(InferenceDiagnostic::PrivateField { expr: tgt_expr, field }); field,
});
} }
} }
ty ty
@ -1835,7 +1836,7 @@ impl InferenceContext<'_> {
VisibleFromModule::Filter(self.resolver.module()), VisibleFromModule::Filter(self.resolver.module()),
name, name,
); );
self.result.diagnostics.push(InferenceDiagnostic::UnresolvedField { self.push_diagnostic(InferenceDiagnostic::UnresolvedField {
expr: tgt_expr, expr: tgt_expr,
receiver: receiver_ty.clone(), receiver: receiver_ty.clone(),
name: name.clone(), name: name.clone(),
@ -1927,7 +1928,7 @@ impl InferenceContext<'_> {
}, },
); );
self.result.diagnostics.push(InferenceDiagnostic::UnresolvedMethodCall { self.push_diagnostic(InferenceDiagnostic::UnresolvedMethodCall {
expr: tgt_expr, expr: tgt_expr,
receiver: receiver_ty.clone(), receiver: receiver_ty.clone(),
name: method_name.clone(), name: method_name.clone(),

View file

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

View file

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

View file

@ -88,10 +88,10 @@ pub use infer::{
PointerCast, PointerCast,
}; };
pub use interner::Interner; pub use interner::Interner;
pub use lower::diagnostics::*;
pub use lower::{ pub use lower::{
associated_type_shorthand_candidates, GenericArgsProhibitedReason, ImplTraitLoweringMode, associated_type_shorthand_candidates, ImplTraitLoweringMode, ParamLoweringMode, TyDefId,
ParamLoweringMode, TyDefId, TyLoweringContext, TyLoweringDiagnostic, TyLoweringDiagnosticKind, TyLoweringContext, ValueTyDefId,
ValueTyDefId,
}; };
pub use mapping::{ pub use mapping::{
from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, from_placeholder_idx, 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. //! - Building the type for an item: This happens through the `ty` query.
//! //!
//! This usually involves resolving names, collecting generic arguments etc. //! This usually involves resolving names, collecting generic arguments etc.
pub(crate) mod diagnostics;
use std::{ use std::{
cell::OnceCell, cell::OnceCell,
iter, mem, iter, mem,
@ -21,6 +23,7 @@ use chalk_ir::{
use either::Either; use either::Either;
use hir_def::{ use hir_def::{
body::HygieneId,
builtin_type::BuiltinType, builtin_type::BuiltinType,
data::adt::StructKind, data::adt::StructKind,
expander::Expander, expander::Expander,
@ -29,9 +32,9 @@ use hir_def::{
WherePredicateTypeTarget, WherePredicateTypeTarget,
}, },
lang_item::LangItem, lang_item::LangItem,
nameres::MacroSubNs, nameres::{MacroSubNs, ResolvePathResultPrefixInfo},
path::{GenericArg, GenericArgs, ModPath, Path, PathKind, PathSegment, PathSegments}, path::{GenericArg, GenericArgs, ModPath, Path, PathKind, PathSegment, PathSegments},
resolver::{HasResolver, LifetimeNs, Resolver, TypeNs}, resolver::{HasResolver, LifetimeNs, ResolveValueResult, Resolver, TypeNs, ValueNs},
type_ref::{ type_ref::{
ConstRef, LifetimeRef, PathId, TraitBoundModifier, TraitRef as HirTraitRef, TypeBound, ConstRef, LifetimeRef, PathId, TraitBoundModifier, TraitRef as HirTraitRef, TypeBound,
TypeRef, TypeRefId, TypesMap, TypesSourceMap, TypeRef, TypeRefId, TypesMap, TypesSourceMap,
@ -59,6 +62,7 @@ use crate::{
db::HirDatabase, db::HirDatabase,
error_lifetime, error_lifetime,
generics::{generics, trait_self_param_idx, Generics}, generics::{generics, trait_self_param_idx, Generics},
lower::diagnostics::*,
make_binders, make_binders,
mapping::{from_chalk_trait_id, lt_to_placeholder_idx, ToChalk}, mapping::{from_chalk_trait_id, lt_to_placeholder_idx, ToChalk},
static_lifetime, to_assoc_type_id, to_chalk_trait_id, to_placeholder_idx, 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)] #[derive(Debug)]
pub struct TyLoweringContext<'a> { pub struct TyLoweringContext<'a> {
pub db: &'a dyn HirDatabase, 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 /// 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. /// 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. /// 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> { fn lower_ty_only_param(&mut self, type_ref_id: TypeRefId) -> Option<TypeOrConstParamId> {
let type_ref = &self.types_map[type_ref]; let type_ref = &self.types_map[type_ref_id];
let path = match type_ref { let path = match type_ref {
TypeRef::Path(path) => path, TypeRef::Path(path) => path,
_ => return None, _ => return None,
@ -548,8 +527,10 @@ impl<'a> TyLoweringContext<'a> {
if path.segments().len() > 1 { if path.segments().len() > 1 {
return None; return None;
} }
let resolution = match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path) { let resolution = match self
Some((it, None, _)) => it, .resolve_path_in_type_ns(path, &mut Self::on_path_diagnostic_callback(type_ref_id))
{
Some((it, None)) => it,
_ => return None, _ => return None,
}; };
match resolution { match resolution {
@ -584,11 +565,9 @@ impl<'a> TyLoweringContext<'a> {
resolution: TypeNs, resolution: TypeNs,
resolved_segment: PathSegment<'_>, resolved_segment: PathSegment<'_>,
remaining_segments: PathSegments<'_>, remaining_segments: PathSegments<'_>,
_resolved_segment_idx: u32,
infer_args: bool, infer_args: bool,
on_prohibited_generics_for_resolved_segment: &mut dyn FnMut( _on_diagnostic: &mut dyn FnMut(&mut Self, PathLoweringDiagnostic),
&mut Self,
GenericArgsProhibitedReason,
),
) -> (Ty, Option<TypeNs>) { ) -> (Ty, Option<TypeNs>) {
let ty = match resolution { let ty = match resolution {
TypeNs::TraitId(trait_) => { TypeNs::TraitId(trait_) => {
@ -655,44 +634,28 @@ impl<'a> TyLoweringContext<'a> {
// FIXME(trait_alias): Implement trait alias. // FIXME(trait_alias): Implement trait alias.
return (TyKind::Error.intern(Interner), None); return (TyKind::Error.intern(Interner), None);
} }
TypeNs::GenericParam(param_id) => { TypeNs::GenericParam(param_id) => match self.type_param_mode {
if resolved_segment.args_and_bindings.is_some() { ParamLoweringMode::Placeholder => {
on_prohibited_generics_for_resolved_segment( TyKind::Placeholder(to_placeholder_idx(self.db, param_id.into()))
self,
GenericArgsProhibitedReason::TyParam,
);
} }
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 { TyKind::BoundVar(BoundVar::new(self.in_binders, idx))
ParamLoweringMode::Placeholder => {
TyKind::Placeholder(to_placeholder_idx(self.db, param_id.into()))
}
ParamLoweringMode::Variable => {
let idx = match self
.generics()
.expect("generics in scope")
.type_or_const_param_idx(param_id.into())
{
None => {
never!("no matching generics");
return (TyKind::Error.intern(Interner), None);
}
Some(idx) => idx,
};
TyKind::BoundVar(BoundVar::new(self.in_binders, idx))
}
} }
.intern(Interner)
} }
.intern(Interner),
TypeNs::SelfType(impl_id) => { 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"); let generics = self.generics().expect("impl should have generic param scope");
match self.type_param_mode { match self.type_param_mode {
@ -718,13 +681,6 @@ impl<'a> TyLoweringContext<'a> {
} }
} }
TypeNs::AdtSelfType(adt) => { 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 generics = generics(self.db.upcast(), adt.into());
let substs = match self.type_param_mode { let substs = match self.type_param_mode {
ParamLoweringMode::Placeholder => generics.placeholder_subst(self.db), 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::AdtId(it) => self.lower_path_inner(resolved_segment, it.into(), infer_args),
TypeNs::BuiltinType(it) => { 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) self.lower_path_inner(resolved_segment, it.into(), infer_args)
} }
TypeNs::TypeAliasId(it) => { TypeNs::TypeAliasId(it) => {
@ -754,6 +704,220 @@ impl<'a> TyLoweringContext<'a> {
self.lower_ty_relative_path(ty, Some(resolution), remaining_segments) 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>) { pub(crate) fn lower_path(&mut self, path: &Path, path_id: PathId) -> (Ty, Option<TypeNs>) {
// Resolve the path (in type namespace) // Resolve the path (in type namespace)
if let Some(type_ref) = path.type_anchor() { 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()); return self.lower_ty_relative_path(ty, res, path.segments());
} }
let (resolution, remaining_index, _) = let (resolution, remaining_index) = match self.resolve_path_in_type_ns(
match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path) { path,
Some(it) => it, &mut Self::on_path_diagnostic_callback(path_id.type_ref()),
None => return (TyKind::Error.intern(Interner), None), ) {
}; Some(it) => it,
None => return (TyKind::Error.intern(Interner), None),
};
if matches!(resolution, TypeNs::TraitId(_)) && remaining_index.is_none() { if matches!(resolution, TypeNs::TraitId(_)) && remaining_index.is_none() {
// trait object type without dyn // trait object type without dyn
@ -774,38 +940,22 @@ impl<'a> TyLoweringContext<'a> {
return (ty, None); return (ty, None);
} }
let (module_segments, resolved_segment_idx, resolved_segment, remaining_segments) = let (resolved_segment_idx, resolved_segment, remaining_segments) = match remaining_index {
match remaining_index { None => (
None => ( path.segments().len() - 1,
path.segments().strip_last(), path.segments().last().expect("resolved path has at least one element"),
path.segments().len() - 1, PathSegments::EMPTY,
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)),
), };
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);
self.lower_partly_resolved_path( self.lower_partly_resolved_path(
resolution, resolution,
resolved_segment, resolved_segment,
remaining_segments, remaining_segments,
resolved_segment_idx as u32,
false, false,
&mut |this, reason| { &mut Self::on_path_diagnostic_callback(path_id.type_ref()),
this.push_diagnostic(
path_id.type_ref(),
TyLoweringDiagnosticKind::GenericArgsProhibited {
segment: resolved_segment_idx as u32,
reason,
},
)
},
) )
} }
@ -1107,7 +1257,9 @@ impl<'a> TyLoweringContext<'a> {
if segment.args_and_bindings.is_some() { if segment.args_and_bindings.is_some() {
self.push_diagnostic( self.push_diagnostic(
path_id.type_ref(), 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, explicit_self_ty: Ty,
) -> Option<TraitRef> { ) -> Option<TraitRef> {
let path = &self.types_map[path_id]; 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. // FIXME(trait_alias): We need to handle trait alias here.
TypeNs::TraitId(tr) => tr, TypeNs::TraitId(tr) => tr,
_ => return None, _ => 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::{ use hir_ty::{
db::HirDatabase, db::HirDatabase,
diagnostics::{BodyValidationDiagnostic, UnsafetyReason}, diagnostics::{BodyValidationDiagnostic, UnsafetyReason},
CastError, InferenceDiagnostic, InferenceTyDiagnosticSource, TyLoweringDiagnostic, CastError, InferenceDiagnostic, InferenceTyDiagnosticSource, PathLoweringDiagnostic,
TyLoweringDiagnosticKind, TyLoweringDiagnostic, TyLoweringDiagnosticKind,
}; };
use syntax::{ use syntax::{
ast::{self, HasGenericArgs}, ast::{self, HasGenericArgs},
AstPtr, SyntaxError, SyntaxNodePtr, TextRange, match_ast, AstNode, AstPtr, SyntaxError, SyntaxNodePtr, TextRange,
}; };
use triomphe::Arc; use triomphe::Arc;
@ -674,6 +674,39 @@ impl AnyDiagnostic {
}; };
Self::ty_diagnostic(diag, source_map, db)? 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, Either::Right(source) => source,
}; };
let syntax = || source.value.to_node(&db.parse_or_expand(source.file_id)); let syntax = || source.value.to_node(&db.parse_or_expand(source.file_id));
Some(match diag.kind { Some(match &diag.kind {
TyLoweringDiagnosticKind::GenericArgsProhibited { segment, reason } => { TyLoweringDiagnosticKind::PathDiagnostic(diag) => {
let ast::Type::PathType(syntax) = syntax() else { return None }; let ast::Type::PathType(syntax) = syntax() else { return None };
let segment = hir_segment_to_ast_segment(&syntax.path()?, segment)?; Self::path_diagnostic(diag, source.with_value(syntax.path()?))?
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()
} }
}) })
} }

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" return "you can specify generic arguments on either the enum or the variant, but not both"
.to_owned(); .to_owned();
} }
GenericArgsProhibitedReason::Const => "constants",
GenericArgsProhibitedReason::Static => "statics",
}; };
format!("generic arguments are not allowed on {kind}") format!("generic arguments are not allowed on {kind}")
} }
@ -435,6 +437,144 @@ type T = bool<i32>;
impl Trait for () { impl Trait for () {
type Assoc = i32<bool>; type Assoc = i32<bool>;
// ^^^^^^ 💡 error: generic arguments are not allowed on builtin types // ^^^^^^ 💡 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
} }
"#, "#,
); );