Unnamed repository; edit this file 'description' to name the repository.
Feature a "type annotations needed diagnostic" when an infer var cannot be resolved at the end of inference
Chayim Refael Friedman 4 weeks ago
parent 7dab289 · commit a8b5abd
-rw-r--r--crates/hir-def/src/expr_store.rs1
-rw-r--r--crates/hir-def/src/expr_store/lower.rs10
-rw-r--r--crates/hir-def/src/expr_store/pretty.rs1
-rw-r--r--crates/hir-def/src/hir.rs3
-rw-r--r--crates/hir-ty/src/db.rs2
-rw-r--r--crates/hir-ty/src/diagnostics/unsafe_check.rs2
-rw-r--r--crates/hir-ty/src/infer.rs199
-rw-r--r--crates/hir-ty/src/infer/callee.rs48
-rw-r--r--crates/hir-ty/src/infer/closure.rs10
-rw-r--r--crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs4
-rw-r--r--crates/hir-ty/src/infer/coerce.rs16
-rw-r--r--crates/hir-ty/src/infer/expr.rs2
-rw-r--r--crates/hir-ty/src/infer/pat.rs7
-rw-r--r--crates/hir-ty/src/infer/path.rs10
-rw-r--r--crates/hir-ty/src/infer/unify.rs275
-rw-r--r--crates/hir-ty/src/lib.rs12
-rw-r--r--crates/hir-ty/src/lower.rs2
-rw-r--r--crates/hir-ty/src/lower/path.rs12
-rw-r--r--crates/hir-ty/src/method_resolution/confirm.rs8
-rw-r--r--crates/hir-ty/src/mir/lower.rs2
-rw-r--r--crates/hir-ty/src/mir/lower/pattern_matching.rs2
-rw-r--r--crates/hir-ty/src/next_solver/generic_arg.rs38
-rw-r--r--crates/hir-ty/src/next_solver/infer/mod.rs26
-rw-r--r--crates/hir-ty/src/next_solver/infer/region_constraints/mod.rs21
-rw-r--r--crates/hir-ty/src/next_solver/infer/relate/generalize.rs4
-rw-r--r--crates/hir-ty/src/next_solver/infer/type_variable.rs62
-rw-r--r--crates/hir-ty/src/next_solver/infer/unify_key.rs53
-rw-r--r--crates/hir-ty/src/next_solver/region.rs2
-rw-r--r--crates/hir-ty/src/next_solver/solver.rs39
-rw-r--r--crates/hir-ty/src/next_solver/ty.rs69
-rw-r--r--crates/hir/src/diagnostics.rs43
-rw-r--r--crates/hir/src/lib.rs2
-rw-r--r--crates/hir/src/semantics.rs8
-rw-r--r--crates/ide-diagnostics/src/handlers/await_outside_of_async.rs3
-rw-r--r--crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs2
-rw-r--r--crates/ide-diagnostics/src/handlers/incorrect_case.rs10
-rw-r--r--crates/ide-diagnostics/src/handlers/incorrect_generics_len.rs12
-rw-r--r--crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs2
-rw-r--r--crates/ide-diagnostics/src/handlers/missing_fields.rs7
-rw-r--r--crates/ide-diagnostics/src/handlers/type_mismatch.rs1
-rw-r--r--crates/ide-diagnostics/src/handlers/type_must_be_known.rs79
-rw-r--r--crates/ide-diagnostics/src/handlers/typed_hole.rs2
-rw-r--r--crates/ide-diagnostics/src/tests/overly_long_real_world_cases.rs9
-rw-r--r--crates/macros/src/lib.rs14
-rw-r--r--crates/test-utils/src/minicore.rs25
45 files changed, 852 insertions, 309 deletions
diff --git a/crates/hir-def/src/expr_store.rs b/crates/hir-def/src/expr_store.rs
index 497ed7d37f..51951896f2 100644
--- a/crates/hir-def/src/expr_store.rs
+++ b/crates/hir-def/src/expr_store.rs
@@ -561,6 +561,7 @@ impl ExpressionStore {
| Pat::ConstBlock(..)
| Pat::Wild
| Pat::Missing
+ | Pat::Rest
| Pat::Expr(_) => {}
&Pat::Bind { subpat, .. } => {
if let Some(subpat) = subpat {
diff --git a/crates/hir-def/src/expr_store/lower.rs b/crates/hir-def/src/expr_store/lower.rs
index 3440fbee6d..fd8b50d714 100644
--- a/crates/hir-def/src/expr_store/lower.rs
+++ b/crates/hir-def/src/expr_store/lower.rs
@@ -2622,15 +2622,7 @@ impl<'db> ExprCollector<'db> {
let expr_id = self.alloc_expr(expr, expr_ptr);
Pat::Lit(expr_id)
}
- ast::Pat::RestPat(_) => {
- // `RestPat` requires special handling and should not be mapped
- // to a Pat. Here we are using `Pat::Missing` as a fallback for
- // when `RestPat` is mapped to `Pat`, which can easily happen
- // when the source code being analyzed has a malformed pattern
- // which includes `..` in a place where it isn't valid.
-
- Pat::Missing
- }
+ ast::Pat::RestPat(_) => Pat::Rest,
ast::Pat::BoxPat(boxpat) => {
let inner = self.collect_pat_opt(boxpat.pat(), binding_list);
Pat::Box { inner }
diff --git a/crates/hir-def/src/expr_store/pretty.rs b/crates/hir-def/src/expr_store/pretty.rs
index fdd0654508..bb35009f36 100644
--- a/crates/hir-def/src/expr_store/pretty.rs
+++ b/crates/hir-def/src/expr_store/pretty.rs
@@ -895,6 +895,7 @@ impl Printer<'_> {
match pat {
Pat::Missing => w!(self, "�"),
+ Pat::Rest => w!(self, ".."),
Pat::Wild => w!(self, "_"),
Pat::Tuple { args, ellipsis } => {
w!(self, "(");
diff --git a/crates/hir-def/src/hir.rs b/crates/hir-def/src/hir.rs
index a1a346cabc..93fa7ff961 100644
--- a/crates/hir-def/src/hir.rs
+++ b/crates/hir-def/src/hir.rs
@@ -666,6 +666,8 @@ pub struct RecordFieldPat {
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum Pat {
Missing,
+ /// A rest pattern. Not valid outside special context.
+ Rest,
Wild,
Tuple {
args: Box<[PatId]>,
@@ -721,6 +723,7 @@ impl Pat {
| Pat::ConstBlock(..)
| Pat::Wild
| Pat::Missing
+ | Pat::Rest
| Pat::Expr(_) => {}
Pat::Bind { subpat, .. } => {
subpat.iter().copied().for_each(f);
diff --git a/crates/hir-ty/src/db.rs b/crates/hir-ty/src/db.rs
index 3bf2d9a6a6..99bad2682b 100644
--- a/crates/hir-ty/src/db.rs
+++ b/crates/hir-ty/src/db.rs
@@ -147,7 +147,7 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug {
#[salsa::invoke(crate::lower::const_param_ty_query)]
#[salsa::transparent]
- fn const_param_ty_ns<'db>(&'db self, def: ConstParamId) -> Ty<'db>;
+ fn const_param_ty<'db>(&'db self, def: ConstParamId) -> Ty<'db>;
#[salsa::invoke(crate::lower::impl_trait_with_diagnostics)]
#[salsa::transparent]
diff --git a/crates/hir-ty/src/diagnostics/unsafe_check.rs b/crates/hir-ty/src/diagnostics/unsafe_check.rs
index ee33f7d158..4893d72a5c 100644
--- a/crates/hir-ty/src/diagnostics/unsafe_check.rs
+++ b/crates/hir-ty/src/diagnostics/unsafe_check.rs
@@ -258,7 +258,7 @@ impl<'db> UnsafeVisitor<'db> {
self.on_unsafe_op(current.into(), UnsafetyReason::UnionField)
}
// `Or` only wraps other patterns, and `Missing`/`Wild` do not constitute a read.
- Pat::Missing | Pat::Wild | Pat::Or(_) => {}
+ Pat::Missing | Pat::Rest | Pat::Wild | Pat::Or(_) => {}
}
}
diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs
index 30b420b6d5..4aeb5ec71c 100644
--- a/crates/hir-ty/src/infer.rs
+++ b/crates/hir-ty/src/infer.rs
@@ -29,7 +29,7 @@ mod path;
mod place_op;
pub(crate) mod unify;
-use std::{cell::OnceCell, convert::identity, fmt, iter, ops::Deref};
+use std::{cell::OnceCell, convert::identity, fmt, ops::Deref};
use base_db::{Crate, FxIndexMap};
use either::Either;
@@ -50,6 +50,7 @@ use hir_def::{
use hir_expand::{mod_path::ModPath, name::Name};
use indexmap::IndexSet;
use la_arena::ArenaMap;
+use macros::{TypeFoldable, TypeVisitable};
use rustc_ast_ir::Mutability;
use rustc_hash::{FxHashMap, FxHashSet};
use rustc_type_ir::{
@@ -76,14 +77,15 @@ use crate::{
diagnostics::{Diagnostics, InferenceTyLoweringContext as TyLoweringContext},
expr::ExprIsRead,
pat::PatOrigin,
+ unify::resolve_completely::WriteBackCtxt,
},
lower::{
ImplTraitIdx, ImplTraitLoweringMode, LifetimeElisionKind, diagnostics::TyLoweringDiagnostic,
},
method_resolution::CandidateId,
next_solver::{
- AliasTy, Const, DbInterner, ErrorGuaranteed, GenericArgs, Region, StoredGenericArgs,
- StoredTy, StoredTys, Ty, TyKind, Tys,
+ AliasTy, Const, DbInterner, ErrorGuaranteed, GenericArgs, Region, StoredGenericArg,
+ StoredGenericArgs, StoredTy, StoredTys, Term, Ty, TyKind, Tys,
abi::Safety,
infer::{InferCtxt, ObligationInspector, traits::ObligationCause},
},
@@ -188,7 +190,7 @@ fn infer_signature_query(db: &dyn HirDatabase, def: GenericDefId) -> InferenceRe
// Array lengths are always `usize`.
RootExprOrigin::ArrayLength => Expectation::has_type(ctx.types.types.usize),
// Const parameter default: look up the param's declared type.
- RootExprOrigin::ConstParam(local_id) => Expectation::has_type(db.const_param_ty_ns(
+ RootExprOrigin::ConstParam(local_id) => Expectation::has_type(db.const_param_ty(
ConstParamId::from_unchecked(TypeOrConstParamId { parent: def, local_id }),
)),
// Path const generic args: determining the expected type requires
@@ -307,107 +309,152 @@ pub enum InferenceTyDiagnosticSource {
Signature,
}
-#[derive(Debug, PartialEq, Eq, Clone)]
+#[derive(Debug, PartialEq, Eq, Clone, TypeVisitable, TypeFoldable)]
pub enum InferenceDiagnostic {
NoSuchField {
+ #[type_visitable(ignore)]
field: ExprOrPatId,
+ #[type_visitable(ignore)]
private: Option<LocalFieldId>,
+ #[type_visitable(ignore)]
variant: VariantId,
},
PrivateField {
+ #[type_visitable(ignore)]
expr: ExprId,
+ #[type_visitable(ignore)]
field: FieldId,
},
PrivateAssocItem {
+ #[type_visitable(ignore)]
id: ExprOrPatId,
+ #[type_visitable(ignore)]
item: AssocItemId,
},
UnresolvedField {
+ #[type_visitable(ignore)]
expr: ExprId,
receiver: StoredTy,
+ #[type_visitable(ignore)]
name: Name,
+ #[type_visitable(ignore)]
method_with_same_name_exists: bool,
},
UnresolvedMethodCall {
+ #[type_visitable(ignore)]
expr: ExprId,
receiver: StoredTy,
+ #[type_visitable(ignore)]
name: Name,
/// Contains the type the field resolves to
field_with_same_name: Option<StoredTy>,
+ #[type_visitable(ignore)]
assoc_func_with_same_name: Option<FunctionId>,
},
UnresolvedAssocItem {
+ #[type_visitable(ignore)]
id: ExprOrPatId,
},
UnresolvedIdent {
+ #[type_visitable(ignore)]
id: ExprOrPatId,
},
// FIXME: This should be emitted in body lowering
BreakOutsideOfLoop {
+ #[type_visitable(ignore)]
expr: ExprId,
+ #[type_visitable(ignore)]
is_break: bool,
+ #[type_visitable(ignore)]
bad_value_break: bool,
},
MismatchedArgCount {
+ #[type_visitable(ignore)]
call_expr: ExprId,
+ #[type_visitable(ignore)]
expected: usize,
+ #[type_visitable(ignore)]
found: usize,
},
MismatchedTupleStructPatArgCount {
+ #[type_visitable(ignore)]
pat: PatId,
+ #[type_visitable(ignore)]
expected: usize,
+ #[type_visitable(ignore)]
found: usize,
},
ExpectedFunction {
+ #[type_visitable(ignore)]
call_expr: ExprId,
found: StoredTy,
},
TypedHole {
+ #[type_visitable(ignore)]
expr: ExprId,
expected: StoredTy,
},
CastToUnsized {
+ #[type_visitable(ignore)]
expr: ExprId,
cast_ty: StoredTy,
},
InvalidCast {
+ #[type_visitable(ignore)]
expr: ExprId,
+ #[type_visitable(ignore)]
error: CastError,
expr_ty: StoredTy,
cast_ty: StoredTy,
},
TyDiagnostic {
+ #[type_visitable(ignore)]
source: InferenceTyDiagnosticSource,
+ #[type_visitable(ignore)]
diag: TyLoweringDiagnostic,
},
PathDiagnostic {
+ #[type_visitable(ignore)]
node: ExprOrPatId,
+ #[type_visitable(ignore)]
diag: PathLoweringDiagnostic,
},
MethodCallIncorrectGenericsLen {
+ #[type_visitable(ignore)]
expr: ExprId,
+ #[type_visitable(ignore)]
provided_count: u32,
+ #[type_visitable(ignore)]
expected_count: u32,
+ #[type_visitable(ignore)]
kind: IncorrectGenericsLenKind,
+ #[type_visitable(ignore)]
def: GenericDefId,
},
MethodCallIncorrectGenericsOrder {
+ #[type_visitable(ignore)]
expr: ExprId,
+ #[type_visitable(ignore)]
param_id: GenericParamId,
+ #[type_visitable(ignore)]
arg_idx: u32,
/// Whether the `GenericArgs` contains a `Self` arg.
+ #[type_visitable(ignore)]
has_self_arg: bool,
},
InvalidLhsOfAssignment {
+ #[type_visitable(ignore)]
lhs: ExprId,
},
TypeMustBeKnown {
- at_point: ExprOrPatId,
+ #[type_visitable(ignore)]
+ at_point: Span,
+ top_term: Option<StoredGenericArg>,
},
}
/// A mismatch between an expected and an inferred type.
-#[derive(Clone, PartialEq, Eq, Debug, Hash)]
+#[derive(Clone, PartialEq, Eq, Debug, Hash, TypeVisitable, TypeFoldable)]
pub struct TypeMismatch {
pub expected: StoredTy,
pub actual: StoredTy,
@@ -1181,7 +1228,7 @@ pub(crate) struct InferenceContext<'body, 'db> {
deferred_call_resolutions: FxHashMap<ExprId, Vec<DeferredCallResolution<'db>>>,
diagnostics: Diagnostics,
- vars_emitted_type_must_be_known_for: FxHashSet<Ty<'db>>,
+ vars_emitted_type_must_be_known_for: FxHashSet<Term<'db>>,
}
#[derive(Clone, Debug)]
@@ -1331,14 +1378,15 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
// there is no problem in it being `pub(crate)`, remove this comment.
fn resolve_all(self) -> InferenceResult {
let InferenceContext {
- mut table,
+ table,
mut result,
tuple_field_accesses_rev,
diagnostics,
types,
+ vars_emitted_type_must_be_known_for,
..
} = self;
- let mut diagnostics = diagnostics.finish();
+ let 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 {
@@ -1359,30 +1407,28 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
pat_adjustments,
binding_modes: _,
expr_adjustments,
- tuple_field_access_types: _,
+ tuple_field_access_types,
coercion_casts: _,
- diagnostics: _,
+ diagnostics: result_diagnostics,
} = &mut result;
+ let mut resolver =
+ WriteBackCtxt::new(table, diagnostics, vars_emitted_type_must_be_known_for);
skipped_ref_pats.shrink_to_fit();
for ty in type_of_expr.values_mut() {
- *ty = table.resolve_completely(ty.as_ref()).store();
- *has_errors = *has_errors || ty.as_ref().references_non_lt_error();
+ resolver.resolve_completely(ty);
}
type_of_expr.shrink_to_fit();
for ty in type_of_pat.values_mut() {
- *ty = table.resolve_completely(ty.as_ref()).store();
- *has_errors = *has_errors || ty.as_ref().references_non_lt_error();
+ resolver.resolve_completely(ty);
}
type_of_pat.shrink_to_fit();
for ty in type_of_binding.values_mut() {
- *ty = table.resolve_completely(ty.as_ref()).store();
- *has_errors = *has_errors || ty.as_ref().references_non_lt_error();
+ resolver.resolve_completely(ty);
}
type_of_binding.shrink_to_fit();
for ty in type_of_type_placeholder.values_mut() {
- *ty = table.resolve_completely(ty.as_ref()).store();
- *has_errors = *has_errors || ty.as_ref().references_non_lt_error();
+ resolver.resolve_completely(ty);
}
type_of_type_placeholder.shrink_to_fit();
type_of_opaque.shrink_to_fit();
@@ -1390,61 +1436,25 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
if let Some(type_mismatches) = type_mismatches {
*has_errors = true;
for mismatch in type_mismatches.values_mut() {
- mismatch.expected = table.resolve_completely(mismatch.expected.as_ref()).store();
- mismatch.actual = table.resolve_completely(mismatch.actual.as_ref()).store();
+ resolver.resolve_type_mismatch(mismatch);
}
type_mismatches.shrink_to_fit();
}
- diagnostics.retain_mut(|diagnostic| {
- use InferenceDiagnostic::*;
- match diagnostic {
- ExpectedFunction { found: ty, .. }
- | UnresolvedField { receiver: ty, .. }
- | UnresolvedMethodCall { receiver: ty, .. } => {
- *ty = table.resolve_completely(ty.as_ref()).store();
- // FIXME: Remove this when we are on par with rustc in terms of inference
- if ty.as_ref().references_non_lt_error() {
- return false;
- }
-
- if let UnresolvedMethodCall { field_with_same_name, .. } = diagnostic
- && let Some(ty) = field_with_same_name
- {
- *ty = table.resolve_completely(ty.as_ref()).store();
- if ty.as_ref().references_non_lt_error() {
- *field_with_same_name = None;
- }
- }
- }
- TypedHole { expected: ty, .. } => {
- *ty = table.resolve_completely(ty.as_ref()).store();
- }
- _ => (),
- }
- true
- });
- diagnostics.shrink_to_fit();
for (_, subst) in method_resolutions.values_mut() {
- *subst = table.resolve_completely(subst.as_ref()).store();
- *has_errors =
- *has_errors || subst.as_ref().types().any(|ty| ty.references_non_lt_error());
+ resolver.resolve_completely(subst);
}
method_resolutions.shrink_to_fit();
for (_, subst) in assoc_resolutions.values_mut() {
- *subst = table.resolve_completely(subst.as_ref()).store();
- *has_errors =
- *has_errors || subst.as_ref().types().any(|ty| ty.references_non_lt_error());
+ resolver.resolve_completely(subst);
}
assoc_resolutions.shrink_to_fit();
for adjustment in expr_adjustments.values_mut().flatten() {
- adjustment.target = table.resolve_completely(adjustment.target.as_ref()).store();
- *has_errors = *has_errors || adjustment.target.as_ref().references_non_lt_error();
+ resolver.resolve_completely(&mut adjustment.target);
}
expr_adjustments.shrink_to_fit();
for adjustments in pat_adjustments.values_mut() {
for adjustment in &mut *adjustments {
- adjustment.source = table.resolve_completely(adjustment.source.as_ref()).store();
- *has_errors = *has_errors || adjustment.source.as_ref().references_non_lt_error();
+ resolver.resolve_completely(&mut adjustment.source);
}
adjustments.shrink_to_fit();
}
@@ -1458,7 +1468,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
};
for (place, _, sources) in fake_reads {
- *place = table.resolve_completely(std::mem::replace(place, dummy_place()));
+ resolver.resolve_completely_with_default(place, dummy_place());
place.projections.shrink_to_fit();
for source in &mut *sources {
source.shrink_to_fit();
@@ -1469,7 +1479,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
for min_capture in min_captures.values_mut() {
for captured in &mut *min_capture {
let CapturedPlace { place, info, mutability: _ } = captured;
- *place = table.resolve_completely(std::mem::replace(place, dummy_place()));
+ resolver.resolve_completely_with_default(place, dummy_place());
let CaptureInfo { sources, capture_kind: _ } = info;
for source in &mut *sources {
source.shrink_to_fit();
@@ -1481,17 +1491,18 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
min_captures.shrink_to_fit();
}
closures_data.shrink_to_fit();
- result.tuple_field_access_types = tuple_field_accesses_rev
+ *tuple_field_access_types = tuple_field_accesses_rev
.into_iter()
- .map(|subst| table.resolve_completely(subst).store())
- .inspect(|subst| {
- *has_errors =
- *has_errors || subst.as_ref().iter().any(|ty| ty.references_non_lt_error());
+ .map(|mut subst| {
+ resolver.resolve_completely(&mut subst);
+ subst.store()
})
.collect();
- result.tuple_field_access_types.shrink_to_fit();
+ tuple_field_access_types.shrink_to_fit();
- result.diagnostics = diagnostics;
+ let (diagnostics, resolver_has_errors) = resolver.resolve_diagnostics();
+ *result_diagnostics = diagnostics;
+ *has_errors |= resolver_has_errors;
result
}
@@ -1502,6 +1513,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
&data.store,
InferenceTyDiagnosticSource::Signature,
LifetimeElisionKind::for_const(self.interner(), id.loc(self.db).container),
+ Span::Dummy,
);
self.return_ty = return_ty;
@@ -1513,6 +1525,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
&data.store,
InferenceTyDiagnosticSource::Signature,
LifetimeElisionKind::Elided(self.types.regions.statik),
+ Span::Dummy,
);
self.return_ty = return_ty;
@@ -1545,16 +1558,16 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
param_tys.push(va_list_ty);
}
- let mut param_tys =
- param_tys.into_iter().chain(iter::repeat(self.table.next_ty_var(Span::Dummy)));
+ let mut param_tys = param_tys.into_iter();
if let Some(self_param) = self_param
&& let Some(ty) = param_tys.next()
{
- let ty = self.process_user_written_ty(ty);
+ let ty = self.process_user_written_ty(Span::Dummy, ty);
self.write_binding_ty(self_param, ty);
}
- for (ty, pat) in param_tys.zip(params) {
- let ty = self.process_user_written_ty(ty);
+ for pat in params {
+ let ty = param_tys.next().unwrap_or_else(|| self.table.next_ty_var(Span::Dummy));
+ let ty = self.process_user_written_ty(Span::Dummy, ty);
self.infer_top_pat(*pat, ty, PatOrigin::Param);
}
@@ -1569,7 +1582,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
ctx.lower_ty(return_ty)
},
);
- self.process_user_written_ty(return_ty)
+ self.process_user_written_ty(Span::Dummy, return_ty)
}
None => self.types.types.unit,
};
@@ -1606,7 +1619,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
let var = self.table.next_ty_var(Span::Dummy);
// Suppress future errors on this var. Add more things here when we add more diagnostics.
- self.vars_emitted_type_must_be_known_for.insert(var);
+ self.vars_emitted_type_must_be_known_for.insert(var.into());
var
} else {
@@ -1751,10 +1764,11 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
store: &ExpressionStore,
type_source: InferenceTyDiagnosticSource,
lifetime_elision: LifetimeElisionKind<'db>,
+ span: Span,
) -> Ty<'db> {
let ty = self
.with_ty_lowering(store, type_source, lifetime_elision, |ctx| ctx.lower_ty(type_ref));
- let ty = self.process_user_written_ty(ty);
+ let ty = self.process_user_written_ty(span, ty);
// Record the association from placeholders' TypeRefId to type variables.
// We only record them if their number matches. This assumes TypeRef::walk and TypeVisitable process the items in the same order.
@@ -1781,6 +1795,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
self.store,
InferenceTyDiagnosticSource::Body,
LifetimeElisionKind::Infer,
+ type_ref.into(),
)
}
@@ -1791,17 +1806,22 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
LifetimeElisionKind::Infer,
|ctx| ctx.lower_const(const_ref, ty),
);
- self.insert_type_vars(const_, Span::Dummy)
+ self.insert_type_vars(const_, const_ref.expr.into())
}
- pub(crate) fn make_path_as_body_const(&mut self, path: &Path, ty: Ty<'db>) -> Const<'db> {
+ pub(crate) fn make_path_as_body_const(
+ &mut self,
+ type_ref: TypeRefId,
+ path: &Path,
+ ty: Ty<'db>,
+ ) -> Const<'db> {
let const_ = self.with_ty_lowering(
self.store,
InferenceTyDiagnosticSource::Body,
LifetimeElisionKind::Infer,
|ctx| ctx.lower_path_as_const(path, ty),
);
- self.insert_type_vars(const_, Span::Dummy)
+ self.insert_type_vars(const_, type_ref.into())
}
fn err_ty(&self) -> Ty<'db> {
@@ -1887,8 +1907,8 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
}
/// Whenever you lower a user-written type, you should call this.
- fn process_user_written_ty(&mut self, ty: Ty<'db>) -> Ty<'db> {
- self.table.process_user_written_ty(ty)
+ fn process_user_written_ty(&mut self, span: Span, ty: Ty<'db>) -> Ty<'db> {
+ self.table.process_user_written_ty(span, ty)
}
/// The difference of this method from `process_user_written_ty()` is that this method doesn't register a well-formed obligation,
@@ -1979,8 +1999,11 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
node: ExprOrPatId,
ty: Ty<'db>,
) -> Ty<'db> {
- if self.vars_emitted_type_must_be_known_for.insert(ty) {
- self.push_diagnostic(InferenceDiagnostic::TypeMustBeKnown { at_point: node });
+ if self.vars_emitted_type_must_be_known_for.insert(ty.into()) {
+ self.push_diagnostic(InferenceDiagnostic::TypeMustBeKnown {
+ at_point: node.into(),
+ top_term: None,
+ });
}
self.types.types.error
}
@@ -2029,7 +2052,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
return (self.err_ty(), None);
}
let (mut ty, type_ns) = ctx.lower_ty_ext(type_anchor);
- ty = self.table.process_user_written_ty(ty);
+ ty = self.table.process_user_written_ty(type_anchor.into(), ty);
if let Some(TypeNs::SelfType(impl_)) = type_ns
&& let Some(trait_ref) = self.db.impl_trait(impl_)
@@ -2197,7 +2220,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
(ty, _) = path_ctx.lower_partly_resolved_path(resolution, true);
tried_resolving_once = true;
- ty = self.table.process_user_written_ty(ty);
+ ty = self.table.process_user_written_ty(node.into(), ty);
if ty.is_ty_error() {
return (self.err_ty(), None);
}
@@ -2228,7 +2251,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
}
let (mut ty, _) = path_ctx.lower_partly_resolved_path(resolution, true);
- ty = self.table.process_user_written_ty(ty);
+ ty = self.table.process_user_written_ty(node.into(), ty);
if let Some(segment) = remaining_segments.get(1)
&& let Some((AdtId::EnumId(id), _)) = ty.as_adt()
diff --git a/crates/hir-ty/src/infer/callee.rs b/crates/hir-ty/src/infer/callee.rs
index d8639a79bd..ffdde58c48 100644
--- a/crates/hir-ty/src/infer/callee.rs
+++ b/crates/hir-ty/src/infer/callee.rs
@@ -5,7 +5,9 @@ use std::iter;
use intern::sym;
use tracing::debug;
-use hir_def::{CallableDefId, hir::ExprId, signatures::FunctionSignature};
+use hir_def::{
+ CallableDefId, ConstParamId, TypeOrConstParamId, hir::ExprId, signatures::FunctionSignature,
+};
use rustc_type_ir::{
InferTy, Interner,
inherent::{GenericArgs as _, IntoKind, Ty as _},
@@ -20,7 +22,7 @@ use crate::{
},
method_resolution::{MethodCallee, TreatNotYetDefinedOpaques},
next_solver::{
- FnSig, Ty, TyKind,
+ ConstKind, FnSig, Ty, TyKind,
infer::{BoundRegionConversionTime, traits::ObligationCause},
},
};
@@ -349,12 +351,26 @@ impl<'db> InferenceContext<'_, 'db> {
fn check_legacy_const_generics(
&mut self,
callee: Option<CallableDefId>,
+ callee_ty: Ty<'db>,
args: &[ExprId],
) -> Box<[u32]> {
- let func = match callee {
- Some(CallableDefId::FunctionId(func)) => func,
+ let (func, fn_generic_args) = match (callee, callee_ty.kind()) {
+ (Some(CallableDefId::FunctionId(func)), TyKind::FnDef(_, fn_generic_args)) => {
+ (func, fn_generic_args)
+ }
_ => return Default::default(),
};
+ let generics = crate::generics::generics(self.db, func.into());
+ let const_params = generics
+ .iter_self_type_or_consts()
+ .filter(|(_, param_data)| param_data.const_param().is_some())
+ .map(|(idx, _)| {
+ ConstParamId::from_unchecked(TypeOrConstParamId {
+ parent: func.into(),
+ local_id: idx,
+ })
+ })
+ .collect::<Vec<_>>();
let data = FunctionSignature::of(self.db, func);
let Some(legacy_const_generics_indices) = data.legacy_const_generics_indices(self.db, func)
@@ -376,11 +392,29 @@ impl<'db> InferenceContext<'_, 'db> {
}
// check legacy const parameters
- for arg_idx in legacy_const_generics_indices.iter().copied() {
+ for (const_idx, arg_idx) in legacy_const_generics_indices.iter().copied().enumerate() {
if arg_idx >= args.len() as u32 {
continue;
}
- let expected = Expectation::none(); // FIXME use actual const ty, when that is lowered correctly
+
+ if let Some(const_arg) = fn_generic_args.get(const_idx).and_then(|it| it.konst())
+ && let ConstKind::Infer(_) = const_arg.kind()
+ {
+ // Instantiate the generic arg with an error type, to prevent errors from it.
+ // FIXME: Actually lower the expression as const.
+ _ = self
+ .table
+ .at(&ObligationCause::dummy())
+ .eq(self.types.consts.error, const_arg)
+ .map(|infer_ok| self.table.register_infer_ok(infer_ok));
+ }
+
+ let expected = if let Some(&const_param) = const_params.get(const_idx) {
+ Expectation::has_type(self.db.const_param_ty(const_param))
+ } else {
+ Expectation::None
+ };
+
self.infer_expr(args[arg_idx as usize], &expected, ExprIsRead::Yes);
// FIXME: evaluate and unify with the const
}
@@ -420,7 +454,7 @@ impl<'db> InferenceContext<'_, 'db> {
fn_sig,
);
- let indices_to_skip = self.check_legacy_const_generics(def_id, arg_exprs);
+ let indices_to_skip = self.check_legacy_const_generics(def_id, callee_ty, arg_exprs);
self.check_call_arguments(
call_expr,
fn_sig.inputs(),
diff --git a/crates/hir-ty/src/infer/closure.rs b/crates/hir-ty/src/infer/closure.rs
index 078c5c707d..93c98f2542 100644
--- a/crates/hir-ty/src/infer/closure.rs
+++ b/crates/hir-ty/src/infer/closure.rs
@@ -866,18 +866,12 @@ impl<'db> InferenceContext<'_, 'db> {
let interner = self.interner();
let supplied_return = match decl_output {
- Some(output) => {
- let output = self.make_body_ty(output);
- self.process_user_written_ty(output)
- }
+ Some(output) => self.make_body_ty(output),
None => self.table.next_ty_var(closure_expr.into()),
};
// First, convert the types that the user supplied (if any).
let supplied_arguments = decl_inputs.iter().map(|&input| match input {
- Some(input) => {
- let input = self.make_body_ty(input);
- self.process_user_written_ty(input)
- }
+ Some(input) => self.make_body_ty(input),
None => self.table.next_ty_var(closure_expr.into()),
});
diff --git a/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs b/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs
index f140b12491..0fd3cda31d 100644
--- a/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs
+++ b/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs
@@ -1026,7 +1026,8 @@ impl<'a, 'b, 'db, D: Delegate<'db>> ExprUseVisitor<'a, 'b, 'db, D> {
| Pat::Ref { .. }
| Pat::Tuple { .. }
| Pat::Wild
- | Pat::Missing => {
+ | Pat::Missing
+ | Pat::Rest => {
// If the PatKind is Or, Box, Ref, Guard, or Tuple, the relevant accesses
// are made later as these patterns contains subpatterns.
// If the PatKind is Missing, Wild or Err, any relevant accesses are made when processing
@@ -1671,6 +1672,7 @@ impl<'db, D: Delegate<'db>> ExprUseVisitor<'_, '_, 'db, D> {
| Pat::ConstBlock(..)
| Pat::Range { .. }
| Pat::Missing
+ | Pat::Rest
| Pat::Wild => {
// always ok
}
diff --git a/crates/hir-ty/src/infer/coerce.rs b/crates/hir-ty/src/infer/coerce.rs
index 60f7b4073a..55e02a6933 100644
--- a/crates/hir-ty/src/infer/coerce.rs
+++ b/crates/hir-ty/src/infer/coerce.rs
@@ -901,7 +901,7 @@ impl<'db> InferenceContext<'_, 'db> {
target = self.table.try_structurally_resolve_type(target);
debug!("coercion::try({:?}: {:?} -> {:?})", expr, source, target);
- let cause = ObligationCause::new();
+ let cause = ObligationCause::with_span(expr.into());
let coerce_never = self.expr_guaranteed_to_constitute_read_for_never(expr, expr_is_read);
let mut coerce = Coerce {
delegate: InferenceCoercionDelegate(self),
@@ -976,8 +976,12 @@ impl<'db> InferenceContext<'_, 'db> {
match self.table.commit_if_ok(|table| {
// We need to eagerly handle nested obligations due to lazy norm.
let mut ocx = ObligationCtxt::new(&table.infer_ctxt);
- let value =
- ocx.lub(&ObligationCause::new(), table.param_env, prev_ty, new_ty)?;
+ let value = ocx.lub(
+ &ObligationCause::with_span(new.into()),
+ table.param_env,
+ prev_ty,
+ new_ty,
+ )?;
if ocx.try_evaluate_obligations().is_empty() {
Ok(InferOk { value, obligations: ocx.into_pending_obligations() })
} else {
@@ -1025,7 +1029,7 @@ impl<'db> InferenceContext<'_, 'db> {
let sig = self
.table
.infer_ctxt
- .at(&ObligationCause::new(), self.table.param_env)
+ .at(&ObligationCause::with_span(new.into()), self.table.param_env)
.lub(a_sig, b_sig)
.map(|ok| self.table.register_infer_ok(ok))?;
@@ -1071,7 +1075,7 @@ impl<'db> InferenceContext<'_, 'db> {
// operate on values and not places, so a never coercion is valid.
let mut coerce = Coerce {
delegate: InferenceCoercionDelegate(self),
- cause: ObligationCause::new(),
+ cause: ObligationCause::with_span(new.into()),
allow_two_phase: AllowTwoPhase::No,
coerce_never: true,
use_lub: true,
@@ -1107,7 +1111,7 @@ impl<'db> InferenceContext<'_, 'db> {
.commit_if_ok(|table| {
table
.infer_ctxt
- .at(&ObligationCause::new(), table.param_env)
+ .at(&ObligationCause::with_span(new.into()), table.param_env)
.lub(prev_ty, new_ty)
})
.unwrap_err())
diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs
index d2d3849b2e..a6c8cda404 100644
--- a/crates/hir-ty/src/infer/expr.rs
+++ b/crates/hir-ty/src/infer/expr.rs
@@ -176,7 +176,7 @@ impl<'db> InferenceContext<'_, 'db> {
fn pat_guaranteed_to_constitute_read_for_never(&self, pat: PatId) -> bool {
match &self.store[pat] {
// Does not constitute a read.
- Pat::Wild => false,
+ Pat::Wild | Pat::Rest => false,
// This is unnecessarily restrictive when the pattern that doesn't
// constitute a read is unreachable.
diff --git a/crates/hir-ty/src/infer/pat.rs b/crates/hir-ty/src/infer/pat.rs
index 2c38fe74b1..ac209adef8 100644
--- a/crates/hir-ty/src/infer/pat.rs
+++ b/crates/hir-ty/src/infer/pat.rs
@@ -446,7 +446,8 @@ impl<'a, 'db> InferenceContext<'a, 'db> {
pat_info,
)
}
- Pat::Missing | Pat::Wild => expected,
+ Pat::Missing => self.types.types.error,
+ Pat::Wild | Pat::Rest => expected,
// We allow any type here; we ensure that the type is uninhabited during match checking.
// Pat::Never => expected,
Pat::Path(_) => {
@@ -658,8 +659,8 @@ impl<'a, 'db> InferenceContext<'a, 'db> {
Pat::Ref { .. }
// No need to do anything on a missing pattern.
| Pat::Missing
- // A `_` pattern works with any expected type, so there's no need to do anything.
- | Pat::Wild
+ // A `_`/`..` pattern works with any expected type, so there's no need to do anything.
+ | Pat::Wild | Pat::Rest
// Bindings also work with whatever the expected type is,
// and moreover if we peel references off, that will give us the wrong binding type.
// Also, we can have a subpattern `binding @ pat`.
diff --git a/crates/hir-ty/src/infer/path.rs b/crates/hir-ty/src/infer/path.rs
index 2c094f572f..c020c9812b 100644
--- a/crates/hir-ty/src/infer/path.rs
+++ b/crates/hir-ty/src/infer/path.rs
@@ -38,7 +38,7 @@ impl<'db> InferenceContext<'_, 'db> {
}
ValuePathResolution::NonGeneric(ty) => return Some((value, ty)),
};
- let args = self.insert_type_vars(substs, Span::Dummy);
+ let args = self.insert_type_vars(substs, id.into());
self.add_required_obligations_for_value_path(generic_def, args);
@@ -91,7 +91,7 @@ impl<'db> InferenceContext<'_, 'db> {
};
}
ValueNs::GenericParam(it) => {
- return Some(ValuePathResolution::NonGeneric(self.db.const_param_ty_ns(it)));
+ return Some(ValuePathResolution::NonGeneric(self.db.const_param_ty(it)));
}
};
@@ -157,12 +157,12 @@ impl<'db> InferenceContext<'_, 'db> {
let last = path.segments().last()?;
let (ty, orig_ns) = path_ctx.ty_ctx().lower_ty_ext(type_ref);
- let ty = self.table.process_user_written_ty(ty);
+ let ty = self.table.process_user_written_ty(type_ref.into(), ty);
path_ctx.ignore_last_segment();
let (ty, _) = path_ctx.lower_ty_relative_path(ty, orig_ns, true);
drop_ctx(ctx, no_diagnostics);
- let ty = self.table.process_user_written_ty(ty);
+ let ty = self.table.process_user_written_ty(id.into(), ty);
self.resolve_ty_assoc_item(ty, last.name, id).map(|(it, substs)| (it, Some(substs)))?
} else {
let hygiene = self.store.expr_or_pat_path_hygiene(id);
@@ -205,7 +205,7 @@ impl<'db> InferenceContext<'_, 'db> {
return None;
}
- let ty = self.process_user_written_ty(ty);
+ let ty = self.process_user_written_ty(id.into(), ty);
self.resolve_ty_assoc_item(ty, last_segment.name, id)
}
diff --git a/crates/hir-ty/src/infer/unify.rs b/crates/hir-ty/src/infer/unify.rs
index 31b6d50886..4342375621 100644
--- a/crates/hir-ty/src/infer/unify.rs
+++ b/crates/hir-ty/src/infer/unify.rs
@@ -280,19 +280,6 @@ impl<'db> InferenceTable<'db> {
self.infer_ctxt.var_for_def(id, span)
}
- pub(crate) fn resolve_completely<T>(&mut self, value: T) -> T
- where
- T: TypeFoldable<DbInterner<'db>>,
- {
- let value = self.infer_ctxt.resolve_vars_if_possible(value);
-
- let mut goals = vec![];
-
- // FIXME(next-solver): Handle `goals`.
-
- value.fold_with(&mut resolve_completely::Resolver::new(self, true, &mut goals))
- }
-
pub(crate) fn at<'a>(&'a self, cause: &'a ObligationCause) -> At<'a, 'db> {
self.infer_ctxt.at(cause, self.param_env)
}
@@ -404,19 +391,21 @@ impl<'db> InferenceTable<'db> {
where
I: IntoIterator<Item = PredicateObligation<'db>>,
{
- obligations.into_iter().for_each(|obligation| {
- self.register_predicate(obligation);
- });
+ self.fulfillment_cx.register_predicate_obligations(&self.infer_ctxt, obligations);
}
/// checking later, during regionck, that `arg` is well-formed.
pub(crate) fn register_wf_obligation(&mut self, term: Term<'db>, cause: ObligationCause) {
- self.register_predicate(Obligation::new(
- self.interner(),
- cause,
- self.param_env,
- ClauseKind::WellFormed(term),
- ));
+ let _ = (term, cause);
+ // FIXME: We don't currently register an obligation here because we don't implement
+ // wf checking anyway and this function is currently often passed dummy spans, which could
+ // prevent reporting "type annotation needed" errors.
+ // self.register_predicate(Obligation::new(
+ // self.interner(),
+ // cause,
+ // self.param_env,
+ // ClauseKind::WellFormed(term),
+ // ));
}
/// Registers obligations that all `args` are well-formed.
@@ -434,9 +423,9 @@ impl<'db> InferenceTable<'db> {
}
/// Whenever you lower a user-written type, you should call this.
- pub(crate) fn process_user_written_ty(&mut self, ty: Ty<'db>) -> Ty<'db> {
- self.process_remote_user_written_ty(ty)
- // FIXME: Register a well-formed obligation.
+ pub(crate) fn process_user_written_ty(&mut self, span: Span, ty: Ty<'db>) -> Ty<'db> {
+ let ty = self.insert_type_vars(ty, span);
+ self.try_structurally_resolve_type(ty)
}
/// The difference of this method from `process_user_written_ty()` is that this method doesn't register a well-formed obligation,
@@ -460,20 +449,234 @@ impl fmt::Debug for InferenceTable<'_> {
}
}
-mod resolve_completely {
- use rustc_type_ir::{DebruijnIndex, Flags, TypeFolder, TypeSuperFoldable};
+pub(super) mod resolve_completely {
+ use rustc_hash::FxHashSet;
+ use rustc_type_ir::{
+ DebruijnIndex, Flags, InferConst, InferTy, TypeFlags, TypeFoldable, TypeFolder,
+ TypeSuperFoldable, TypeVisitableExt, inherent::IntoKind,
+ };
+ use stdx::never;
+ use thin_vec::ThinVec;
use crate::{
- infer::unify::InferenceTable,
+ InferenceDiagnostic, Span,
+ infer::{TypeMismatch, unify::InferenceTable},
next_solver::{
- Const, DbInterner, Goal, Predicate, Region, Term, Ty,
+ Const, ConstKind, DbInterner, DefaultAny, GenericArg, Goal, Predicate, Region, Term,
+ TermKind, Ty, TyKind,
infer::{resolve::ReplaceInferWithError, traits::ObligationCause},
normalize::deeply_normalize_with_skipped_universes_and_ambiguous_coroutine_goals,
},
};
+ pub(crate) struct WriteBackCtxt<'db> {
+ table: InferenceTable<'db>,
+ diagnostics: ThinVec<InferenceDiagnostic>,
+ has_errors: bool,
+ spans_emitted_type_must_be_known_for: FxHashSet<Span>,
+ types: &'db DefaultAny<'db>,
+ }
+
+ impl<'db> WriteBackCtxt<'db> {
+ pub(crate) fn new(
+ table: InferenceTable<'db>,
+ diagnostics: ThinVec<InferenceDiagnostic>,
+ vars_emitted_type_must_be_known_for: FxHashSet<Term<'db>>,
+ ) -> Self {
+ let spans_emitted_type_must_be_known_for = vars_emitted_type_must_be_known_for
+ .into_iter()
+ .filter_map(|term| match term.kind() {
+ TermKind::Ty(ty) => match ty.kind() {
+ TyKind::Infer(InferTy::TyVar(vid)) => {
+ Some(table.infer_ctxt.type_var_span(vid))
+ }
+ _ => None,
+ },
+ TermKind::Const(ct) => match ct.kind() {
+ ConstKind::Infer(InferConst::Var(vid)) => {
+ table.infer_ctxt.const_var_span(vid)
+ }
+ _ => None,
+ },
+ })
+ .collect();
+
+ Self {
+ types: table.interner().default_types(),
+ table,
+ diagnostics,
+ has_errors: false,
+ spans_emitted_type_must_be_known_for,
+ }
+ }
+
+ pub(crate) fn resolve_type_mismatch(&mut self, value_ref: &mut TypeMismatch) {
+ // Ignore diagnostics from type mismatches, which are diagnostics themselves.
+ // FIXME: We should make type mismatches just regular diagnostics.
+ let prev_diagnostics_len = self.diagnostics.len();
+ self.resolve_completely(value_ref);
+ self.diagnostics.truncate(prev_diagnostics_len);
+ }
+
+ pub(crate) fn resolve_completely<T>(&mut self, value_ref: &mut T)
+ where
+ T: TypeFoldable<DbInterner<'db>>,
+ {
+ self.resolve_completely_with_default(value_ref, value_ref.clone());
+ }
+
+ pub(crate) fn resolve_completely_with_default<T>(&mut self, value_ref: &mut T, default: T)
+ where
+ T: TypeFoldable<DbInterner<'db>>,
+ {
+ let value = std::mem::replace(value_ref, default);
+
+ let value = self.table.resolve_vars_if_possible(value);
+
+ let mut goals = vec![];
+
+ // FIXME(next-solver): Handle `goals`.
+
+ *value_ref = value.fold_with(&mut Resolver::new(self, true, &mut goals));
+ }
+
+ pub(crate) fn resolve_diagnostics(mut self) -> (ThinVec<InferenceDiagnostic>, bool) {
+ let has_errors = self.has_errors;
+
+ // Ignore diagnostics made from resolving diagnostics.
+ let mut diagnostics = std::mem::take(&mut self.diagnostics);
+ diagnostics.retain_mut(|diagnostic| {
+ self.resolve_completely(diagnostic);
+
+ if let InferenceDiagnostic::ExpectedFunction { found: ty, .. }
+ | InferenceDiagnostic::UnresolvedField { receiver: ty, .. }
+ | InferenceDiagnostic::UnresolvedMethodCall { receiver: ty, .. } = diagnostic
+ && ty.as_ref().references_non_lt_error()
+ {
+ false
+ } else {
+ true
+ }
+ });
+ diagnostics.shrink_to_fit();
+
+ (diagnostics, has_errors)
+ }
+ }
+
+ struct DiagnoseInferVars<'a, 'db> {
+ ctx: &'a mut WriteBackCtxt<'db>,
+ top_term: Term<'db>,
+ }
+
+ impl<'db> DiagnoseInferVars<'_, 'db> {
+ const TYPE_FLAGS: TypeFlags = TypeFlags::HAS_INFER.union(TypeFlags::HAS_NON_REGION_ERROR);
+
+ fn err_on_span(&mut self, span: Span) {
+ if !self.ctx.spans_emitted_type_must_be_known_for.insert(span) {
+ // Suppress duplicate diagnostics.
+ return;
+ }
+
+ if span.is_dummy() {
+ return;
+ }
+
+ // We have to be careful not to insert infer vars here, as we won't resolve this new diagnostic.
+ let top_term = self.top_term.fold_with(&mut ReplaceInferWithError::new(self.cx()));
+ self.ctx.diagnostics.push(InferenceDiagnostic::TypeMustBeKnown {
+ at_point: span,
+ top_term: Some(GenericArg::from(top_term).store()),
+ });
+ }
+ }
+
+ impl<'db> TypeFolder<DbInterner<'db>> for DiagnoseInferVars<'_, 'db> {
+ fn cx(&self) -> DbInterner<'db> {
+ self.ctx.table.interner()
+ }
+
+ fn fold_ty(&mut self, t: Ty<'db>) -> Ty<'db> {
+ if !t.has_type_flags(Self::TYPE_FLAGS) {
+ return t;
+ }
+
+ match t.kind() {
+ TyKind::Error(_) => {
+ self.ctx.has_errors = true;
+ t
+ }
+ TyKind::Infer(infer_ty) => match infer_ty {
+ InferTy::TyVar(vid) => {
+ self.err_on_span(self.ctx.table.infer_ctxt.type_var_span(vid));
+ self.ctx.has_errors = true;
+ self.ctx.types.types.error
+ }
+ InferTy::IntVar(_) => {
+ never!("fallback should have resolved all int vars");
+ self.ctx.types.types.i32
+ }
+ InferTy::FloatVar(_) => {
+ never!("fallback should have resolved all float vars");
+ self.ctx.types.types.f64
+ }
+ InferTy::FreshTy(_) | InferTy::FreshIntTy(_) | InferTy::FreshFloatTy(_) => {
+ never!("should not have fresh infer vars outside of caching");
+ self.ctx.has_errors = true;
+ self.ctx.types.types.error
+ }
+ },
+ _ => t.super_fold_with(self),
+ }
+ }
+
+ fn fold_const(&mut self, c: Const<'db>) -> Const<'db> {
+ if !c.has_type_flags(Self::TYPE_FLAGS) {
+ return c;
+ }
+
+ match c.kind() {
+ ConstKind::Error(_) => {
+ self.ctx.has_errors = true;
+ c
+ }
+ ConstKind::Infer(infer_ct) => match infer_ct {
+ InferConst::Var(vid) => {
+ if let Some(span) = self.ctx.table.infer_ctxt.const_var_span(vid) {
+ self.err_on_span(span);
+ }
+ self.ctx.has_errors = true;
+ self.ctx.types.consts.error
+ }
+ InferConst::Fresh(_) => {
+ never!("should not have fresh infer vars outside of caching");
+ self.ctx.has_errors = true;
+ self.ctx.types.consts.error
+ }
+ },
+ _ => c.super_fold_with(self),
+ }
+ }
+
+ fn fold_predicate(&mut self, p: Predicate<'db>) -> Predicate<'db> {
+ if !p.has_type_flags(Self::TYPE_FLAGS) {
+ return p;
+ }
+ p.super_fold_with(self)
+ }
+
+ fn fold_region(&mut self, r: Region<'db>) -> Region<'db> {
+ if r.is_var() {
+ // For now, we don't error on regions.
+ self.ctx.types.regions.error
+ } else {
+ r
+ }
+ }
+ }
+
pub(super) struct Resolver<'a, 'db> {
- ctx: &'a mut InferenceTable<'db>,
+ ctx: &'a mut WriteBackCtxt<'db>,
/// Whether we should normalize, disabled when resolving predicates.
should_normalize: bool,
nested_goals: &'a mut Vec<Goal<'db, Predicate<'db>>>,
@@ -481,7 +684,7 @@ mod resolve_completely {
impl<'a, 'db> Resolver<'a, 'db> {
pub(super) fn new(
- ctx: &'a mut InferenceTable<'db>,
+ ctx: &'a mut WriteBackCtxt<'db>,
should_normalize: bool,
nested_goals: &'a mut Vec<Goal<'db, Predicate<'db>>>,
) -> Resolver<'a, 'db> {
@@ -498,7 +701,7 @@ mod resolve_completely {
{
let value = if self.should_normalize {
let cause = ObligationCause::new();
- let at = self.ctx.at(&cause);
+ let at = self.ctx.table.at(&cause);
let universes = vec![None; outer_exclusive_binder(value).as_usize()];
match deeply_normalize_with_skipped_universes_and_ambiguous_coroutine_goals(
at, value, universes,
@@ -516,17 +719,17 @@ mod resolve_completely {
value
};
- value.fold_with(&mut ReplaceInferWithError::new(self.ctx.interner()))
+ value.fold_with(&mut DiagnoseInferVars { ctx: self.ctx, top_term: value.into() })
}
}
- impl<'cx, 'db> TypeFolder<DbInterner<'db>> for Resolver<'cx, 'db> {
+ impl<'db> TypeFolder<DbInterner<'db>> for Resolver<'_, 'db> {
fn cx(&self) -> DbInterner<'db> {
- self.ctx.interner()
+ self.ctx.table.interner()
}
fn fold_region(&mut self, r: Region<'db>) -> Region<'db> {
- if r.is_var() { Region::error(self.ctx.interner()) } else { r }
+ if r.is_var() { self.ctx.types.regions.error } else { r }
}
fn fold_ty(&mut self, ty: Ty<'db>) -> Ty<'db> {
diff --git a/crates/hir-ty/src/lib.rs b/crates/hir-ty/src/lib.rs
index 3fb8632d6b..4433dd6425 100644
--- a/crates/hir-ty/src/lib.rs
+++ b/crates/hir-ty/src/lib.rs
@@ -689,6 +689,18 @@ impl From<ExprOrPatId> for Span {
}
}
+impl Span {
+ pub(crate) fn pick_best(a: Span, b: Span) -> Span {
+ // We prefer dummy spans to minimize the risk of false errors.
+ if b.is_dummy() { b } else { a }
+ }
+
+ #[inline]
+ pub fn is_dummy(&self) -> bool {
+ matches!(self, Self::Dummy)
+ }
+}
+
pub fn setup_tracing() -> Option<tracing::subscriber::DefaultGuard> {
use std::env;
use std::sync::LazyLock;
diff --git a/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs
index 335aff2c1d..7325cd0ef8 100644
--- a/crates/hir-ty/src/lower.rs
+++ b/crates/hir-ty/src/lower.rs
@@ -2379,7 +2379,7 @@ fn push_const_arg_has_type_predicates<'db>(
interner,
ParamConst { id: param_id, index: (param_index + const_params_offset) as u32 },
),
- db.const_param_ty_ns(param_id),
+ db.const_param_ty(param_id),
)
.upcast(interner),
));
diff --git a/crates/hir-ty/src/lower/path.rs b/crates/hir-ty/src/lower/path.rs
index a364894539..d6b9c375fc 100644
--- a/crates/hir-ty/src/lower/path.rs
+++ b/crates/hir-ty/src/lower/path.rs
@@ -736,6 +736,7 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> {
fn provided_type_like_const(
&mut self,
+ _type_ref: TypeRefId,
const_ty: Ty<'db>,
arg: TypeLikeConst<'_>,
) -> Const<'db> {
@@ -1002,8 +1003,12 @@ pub(crate) trait GenericArgsLowerer<'db> {
arg: &HirGenericArg,
) -> GenericArg<'db>;
- fn provided_type_like_const(&mut self, const_ty: Ty<'db>, arg: TypeLikeConst<'_>)
- -> Const<'db>;
+ fn provided_type_like_const(
+ &mut self,
+ type_ref: TypeRefId,
+ const_ty: Ty<'db>,
+ arg: TypeLikeConst<'_>,
+ ) -> Const<'db>;
fn inferred_kind(
&mut self,
@@ -1225,7 +1230,8 @@ pub(crate) fn substs_from_args_and_bindings<'db>(
panic!("unmatching param kinds");
};
let const_ty = const_param_ty_query(db, param_id);
- substs.push(ctx.provided_type_like_const(const_ty, konst).into());
+ substs
+ .push(ctx.provided_type_like_const(*type_ref, const_ty, konst).into());
args.next();
params.next();
} else {
diff --git a/crates/hir-ty/src/method_resolution/confirm.rs b/crates/hir-ty/src/method_resolution/confirm.rs
index 6536b599c8..a29e3db18d 100644
--- a/crates/hir-ty/src/method_resolution/confirm.rs
+++ b/crates/hir-ty/src/method_resolution/confirm.rs
@@ -5,6 +5,7 @@ use hir_def::{
FunctionId, GenericDefId, GenericParamId, ItemContainerId, TraitId,
expr_store::path::{GenericArg as HirGenericArg, GenericArgs as HirGenericArgs},
hir::{ExprId, generics::GenericParamDataRef},
+ type_ref::TypeRefId,
};
use rustc_type_ir::{
TypeFoldable,
@@ -400,7 +401,7 @@ impl<'a, 'b, 'db> ConfirmContext<'a, 'b, 'db> {
let GenericParamId::ConstParamId(const_id) = param_id else {
unreachable!("non-const param ID for const param");
};
- let const_ty = self.ctx.db.const_param_ty_ns(const_id);
+ let const_ty = self.ctx.db.const_param_ty(const_id);
self.ctx.make_body_const(*konst, const_ty).into()
}
_ => unreachable!("unmatching param kinds were passed to `provided_kind()`"),
@@ -409,11 +410,14 @@ impl<'a, 'b, 'db> ConfirmContext<'a, 'b, 'db> {
fn provided_type_like_const(
&mut self,
+ type_ref: TypeRefId,
const_ty: Ty<'db>,
arg: TypeLikeConst<'_>,
) -> Const<'db> {
match arg {
- TypeLikeConst::Path(path) => self.ctx.make_path_as_body_const(path, const_ty),
+ TypeLikeConst::Path(path) => {
+ self.ctx.make_path_as_body_const(type_ref, path, const_ty)
+ }
TypeLikeConst::Infer => self.ctx.table.next_const_var(Span::Dummy),
}
}
diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs
index b935a6ed32..49fb6f5305 100644
--- a/crates/hir-ty/src/mir/lower.rs
+++ b/crates/hir-ty/src/mir/lower.rs
@@ -581,7 +581,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> {
ParamConst { id: p, index },
)
.store(),
- ty: self.db.const_param_ty_ns(p).store(),
+ ty: self.db.const_param_ty(p).store(),
},
span: None,
}),
diff --git a/crates/hir-ty/src/mir/lower/pattern_matching.rs b/crates/hir-ty/src/mir/lower/pattern_matching.rs
index 68b1c8b3b6..03c608f4b2 100644
--- a/crates/hir-ty/src/mir/lower/pattern_matching.rs
+++ b/crates/hir-ty/src/mir/lower/pattern_matching.rs
@@ -132,7 +132,7 @@ impl<'db> MirLowerCtx<'_, 'db> {
.into(),
);
Ok(match &self.store[pattern] {
- Pat::Missing => return Err(MirLowerError::IncompletePattern),
+ Pat::Missing | Pat::Rest => return Err(MirLowerError::IncompletePattern),
Pat::Wild => (current, current_else),
Pat::Tuple { args, ellipsis } => {
let subst = match self.infer.pat_ty(pattern).kind() {
diff --git a/crates/hir-ty/src/next_solver/generic_arg.rs b/crates/hir-ty/src/next_solver/generic_arg.rs
index 72cf2f9f07..05955d060b 100644
--- a/crates/hir-ty/src/next_solver/generic_arg.rs
+++ b/crates/hir-ty/src/next_solver/generic_arg.rs
@@ -194,6 +194,25 @@ impl std::fmt::Debug for StoredGenericArg {
}
}
+impl<'db> TypeVisitable<DbInterner<'db>> for StoredGenericArg {
+ fn visit_with<V: TypeVisitor<DbInterner<'db>>>(&self, visitor: &mut V) -> V::Result {
+ self.as_ref().visit_with(visitor)
+ }
+}
+
+impl<'db> TypeFoldable<DbInterner<'db>> for StoredGenericArg {
+ fn try_fold_with<F: FallibleTypeFolder<DbInterner<'db>>>(
+ self,
+ folder: &mut F,
+ ) -> Result<Self, F::Error> {
+ Ok(self.as_ref().try_fold_with(folder)?.store())
+ }
+
+ fn fold_with<F: TypeFolder<DbInterner<'db>>>(self, folder: &mut F) -> Self {
+ self.as_ref().fold_with(folder).store()
+ }
+}
+
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub struct GenericArg<'db> {
ptr: GenericArgImpl<'db>,
@@ -457,6 +476,25 @@ impl_foldable_for_interned_slice!(GenericArgs);
impl<'db> rustc_type_ir::inherent::GenericArg<DbInterner<'db>> for GenericArg<'db> {}
+impl<'db> TypeVisitable<DbInterner<'db>> for StoredGenericArgs {
+ fn visit_with<V: TypeVisitor<DbInterner<'db>>>(&self, visitor: &mut V) -> V::Result {
+ self.as_ref().visit_with(visitor)
+ }
+}
+
+impl<'db> TypeFoldable<DbInterner<'db>> for StoredGenericArgs {
+ fn try_fold_with<F: FallibleTypeFolder<DbInterner<'db>>>(
+ self,
+ folder: &mut F,
+ ) -> Result<Self, F::Error> {
+ Ok(self.as_ref().try_fold_with(folder)?.store())
+ }
+
+ fn fold_with<F: TypeFolder<DbInterner<'db>>>(self, folder: &mut F) -> Self {
+ self.as_ref().fold_with(folder).store()
+ }
+}
+
impl<'db> GenericArgs<'db> {
/// Creates an `GenericArgs` for generic parameter definitions,
/// by calling closures to obtain each kind.
diff --git a/crates/hir-ty/src/next_solver/infer/mod.rs b/crates/hir-ty/src/next_solver/infer/mod.rs
index 33003f5375..f038c47a8b 100644
--- a/crates/hir-ty/src/next_solver/infer/mod.rs
+++ b/crates/hir-ty/src/next_solver/infer/mod.rs
@@ -946,8 +946,8 @@ impl<'db> InferCtxt<'db> {
use self::type_variable::TypeVariableValue;
match self.inner.borrow_mut().type_variables().probe(vid) {
- TypeVariableValue::Known { value } => Ok(value),
- TypeVariableValue::Unknown { universe } => Err(universe),
+ TypeVariableValue::Known { value, .. } => Ok(value),
+ TypeVariableValue::Unknown { universe, .. } => Err(universe),
}
}
@@ -1015,6 +1015,13 @@ impl<'db> InferCtxt<'db> {
}
}
+ pub fn shallow_resolve_term(&self, term: Term<'db>) -> Term<'db> {
+ match term.kind() {
+ TermKind::Ty(ty) => self.shallow_resolve(ty).into(),
+ TermKind::Const(ct) => self.shallow_resolve_const(ct).into(),
+ }
+ }
+
pub fn root_var(&self, var: TyVid) -> TyVid {
self.inner.borrow_mut().type_variables().root_var(var)
}
@@ -1096,6 +1103,21 @@ impl<'db> InferCtxt<'db> {
}
}
+ /// Returns the span of the type variable identified by `vid`.
+ ///
+ /// No attempt is made to resolve `vid` to its root variable.
+ pub fn type_var_span(&self, vid: TyVid) -> Span {
+ self.inner.borrow_mut().type_variables().var_span(vid)
+ }
+
+ /// Returns the span of the const variable identified by `vid`
+ pub fn const_var_span(&self, vid: ConstVid) -> Option<Span> {
+ match self.inner.borrow_mut().const_unification_table().probe_value(vid) {
+ ConstVariableValue::Known { .. } => None,
+ ConstVariableValue::Unknown { span, .. } => Some(span),
+ }
+ }
+
// Instantiates the bound variables in a given binder with fresh inference
// variables in the current universe.
//
diff --git a/crates/hir-ty/src/next_solver/infer/region_constraints/mod.rs b/crates/hir-ty/src/next_solver/infer/region_constraints/mod.rs
index dda1bb56ef..544d79daf0 100644
--- a/crates/hir-ty/src/next_solver/infer/region_constraints/mod.rs
+++ b/crates/hir-ty/src/next_solver/infer/region_constraints/mod.rs
@@ -354,8 +354,12 @@ impl<'db> RegionConstraintCollector<'db, '_> {
*any_unifications = false;
// Manually inlined `self.unification_table_mut()` as `self` is used in the closure.
ut::UnificationTable::with_log(&mut self.storage.unification_table, &mut self.undo_log)
- .reset_unifications(|key| RegionVariableValue::Unknown {
- universe: self.storage.var_infos[key.vid].universe,
+ .reset_unifications(|key| {
+ let var_info = &self.storage.var_infos[key.vid];
+ RegionVariableValue::Unknown {
+ universe: var_info.universe,
+ span: var_info.span,
+ }
});
}
@@ -379,7 +383,8 @@ impl<'db> RegionConstraintCollector<'db, '_> {
pub(super) fn new_region_var(&mut self, universe: UniverseIndex, span: Span) -> RegionVid {
let vid = self.storage.var_infos.push(RegionVariableInfo { universe, span });
- let u_vid = self.unification_table_mut().new_key(RegionVariableValue::Unknown { universe });
+ let u_vid =
+ self.unification_table_mut().new_key(RegionVariableValue::Unknown { universe, span });
assert_eq!(vid, u_vid.vid);
self.undo_log.push(AddVar(vid));
debug!("created new region variable {:?} in {:?}", vid, universe);
@@ -413,7 +418,7 @@ impl<'db> RegionConstraintCollector<'db, '_> {
debug!("make_eqregion: unifying {:?} with {:?}", vid, b);
if self
.unification_table_mut()
- .unify_var_value(vid, RegionVariableValue::Known { value: b })
+ .unify_var_value(vid, RegionVariableValue::Known { value: b, span: None })
.is_ok()
{
self.storage.any_unifications = true;
@@ -423,7 +428,7 @@ impl<'db> RegionConstraintCollector<'db, '_> {
debug!("make_eqregion: unifying {:?} with {:?}", a, vid);
if self
.unification_table_mut()
- .unify_var_value(vid, RegionVariableValue::Known { value: a })
+ .unify_var_value(vid, RegionVariableValue::Known { value: a, span: None })
.is_ok()
{
self.storage.any_unifications = true;
@@ -510,15 +515,15 @@ impl<'db> RegionConstraintCollector<'db, '_> {
let mut ut = self.unification_table_mut();
let root_vid = ut.find(vid).vid;
match ut.probe_value(root_vid) {
- RegionVariableValue::Known { value } => value,
+ RegionVariableValue::Known { value, .. } => value,
RegionVariableValue::Unknown { .. } => Region::new_var(cx, root_vid),
}
}
pub fn probe_value(&mut self, vid: RegionVid) -> Result<Region<'db>, UniverseIndex> {
match self.unification_table_mut().probe_value(vid) {
- RegionVariableValue::Known { value } => Ok(value),
- RegionVariableValue::Unknown { universe } => Err(universe),
+ RegionVariableValue::Known { value, .. } => Ok(value),
+ RegionVariableValue::Unknown { universe, .. } => Err(universe),
}
}
diff --git a/crates/hir-ty/src/next_solver/infer/relate/generalize.rs b/crates/hir-ty/src/next_solver/infer/relate/generalize.rs
index 152592683a..e55e43a4cd 100644
--- a/crates/hir-ty/src/next_solver/infer/relate/generalize.rs
+++ b/crates/hir-ty/src/next_solver/infer/relate/generalize.rs
@@ -459,11 +459,11 @@ impl<'db> TypeRelation<DbInterner<'db>> for Generalizer<'_, 'db> {
} else {
let probe = inner.type_variables().probe(vid);
match probe {
- TypeVariableValue::Known { value: u } => {
+ TypeVariableValue::Known { value: u, .. } => {
drop(inner);
self.relate(u, u)
}
- TypeVariableValue::Unknown { universe } => {
+ TypeVariableValue::Unknown { universe, .. } => {
match self.ambient_variance {
// Invariant: no need to make a fresh type variable
// if we can name the universe.
diff --git a/crates/hir-ty/src/next_solver/infer/type_variable.rs b/crates/hir-ty/src/next_solver/infer/type_variable.rs
index 070d2582d8..6b3936ba80 100644
--- a/crates/hir-ty/src/next_solver/infer/type_variable.rs
+++ b/crates/hir-ty/src/next_solver/infer/type_variable.rs
@@ -7,7 +7,6 @@ use std::ops::Range;
use ena::snapshot_vec as sv;
use ena::undo_log::Rollback;
use ena::unify as ut;
-use rustc_index::IndexVec;
use rustc_type_ir::TyVid;
use rustc_type_ir::UniverseIndex;
use rustc_type_ir::inherent::Ty as _;
@@ -61,8 +60,6 @@ impl<'tcx> Rollback<UndoLog<'tcx>> for TypeVariableStorage<'tcx> {
#[derive(Debug, Clone, Default)]
pub(crate) struct TypeVariableStorage<'db> {
- /// The origins of each type variable.
- values: IndexVec<TyVid, TypeVariableData>,
/// Two variables are unified in `eq_relations` when we have a
/// constraint `?X == ?Y`. This table also stores, for each key,
/// the known value.
@@ -94,12 +91,7 @@ pub(crate) struct TypeVariableTable<'a, 'db> {
undo_log: &'a mut InferCtxtUndoLogs<'db>,
}
-#[derive(Debug, Clone)]
-pub(crate) struct TypeVariableData {
- span: Span,
-}
-
-#[derive(Clone, Debug)]
+#[derive(Clone, Copy, Debug)]
pub(crate) enum TypeVariableValue<'db> {
Known { value: Ty<'db> },
Unknown { universe: UniverseIndex },
@@ -111,7 +103,7 @@ impl<'db> TypeVariableValue<'db> {
pub(crate) fn known(&self) -> Option<Ty<'db>> {
match self {
TypeVariableValue::Unknown { .. } => None,
- TypeVariableValue::Known { value } => Some(*value),
+ TypeVariableValue::Known { value, .. } => Some(*value),
}
}
@@ -137,19 +129,14 @@ impl<'db> TypeVariableStorage<'db> {
&self.eq_relations
}
- pub(super) fn finalize_rollback(&mut self) {
- debug_assert!(self.values.len() >= self.eq_relations.len());
- self.values.truncate(self.eq_relations.len());
- }
+ pub(super) fn finalize_rollback(&mut self) {}
}
impl<'db> TypeVariableTable<'_, 'db> {
- /// Returns the span that was given when `vid` was created.
- ///
- /// Note that this function does not return care whether
- /// `vid` has been unified with something else or not.
- pub(crate) fn var_span(&self, vid: TyVid) -> Span {
- self.storage.values[vid].span
+ pub(crate) fn var_span(&mut self, vid: TyVid) -> Span {
+ // We return the span from unification and not equation, since when equating we also unify,
+ // and we want to prevent duplicate diagnostics from vars that were unified.
+ self.sub_unification_table().probe_value(vid).span
}
/// Records that `a == b`, depending on `dir`.
@@ -190,20 +177,17 @@ impl<'db> TypeVariableTable<'_, 'db> {
pub(crate) fn new_var(&mut self, universe: UniverseIndex, span: Span) -> TyVid {
let eq_key = self.eq_relations().new_key(TypeVariableValue::Unknown { universe });
- let sub_key = self.sub_unification_table().new_key(());
+ let sub_key = self.sub_unification_table().new_key(TypeVariableSubValue { span });
debug_assert_eq!(eq_key.vid, sub_key.vid);
- let index = self.storage.values.push(TypeVariableData { span });
- debug_assert_eq!(eq_key.vid, index);
-
debug!("new_var(index={:?}, universe={:?}, span={:?})", eq_key.vid, universe, span);
- index
+ eq_key.vid
}
/// Returns the number of type variables created thus far.
pub(crate) fn num_vars(&self) -> usize {
- self.storage.values.len()
+ self.storage.eq_relations.len()
}
/// Returns the "root" variable of `vid` in the `eq_relations`
@@ -303,9 +287,6 @@ impl<'db> ut::UnifyKey for TyVidEqKey<'db> {
fn tag() -> &'static str {
"TyVidEqKey"
}
- fn order_roots(a: Self, _: &Self::Value, b: Self, _: &Self::Value) -> Option<(Self, Self)> {
- if a.vid.as_u32() < b.vid.as_u32() { Some((a, b)) } else { Some((b, a)) }
- }
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
@@ -320,8 +301,13 @@ impl From<TyVid> for TyVidSubKey {
}
}
+#[derive(Debug, Clone, Copy)]
+pub(crate) struct TypeVariableSubValue {
+ span: Span,
+}
+
impl ut::UnifyKey for TyVidSubKey {
- type Value = ();
+ type Value = TypeVariableSubValue;
#[inline]
fn index(&self) -> u32 {
self.vid.as_u32()
@@ -335,6 +321,14 @@ impl ut::UnifyKey for TyVidSubKey {
}
}
+impl ut::UnifyValue for TypeVariableSubValue {
+ type Error = ut::NoError;
+
+ fn unify_values(value1: &Self, value2: &Self) -> Result<Self, Self::Error> {
+ Ok(TypeVariableSubValue { span: Span::pick_best(value1.span, value2.span) })
+ }
+}
+
impl<'db> ut::UnifyValue for TypeVariableValue<'db> {
type Error = ut::NoError;
@@ -348,11 +342,9 @@ impl<'db> ut::UnifyValue for TypeVariableValue<'db> {
}
// If one side is known, prefer that one.
- (&TypeVariableValue::Known { .. }, &TypeVariableValue::Unknown { .. }) => {
- Ok(value1.clone())
- }
- (&TypeVariableValue::Unknown { .. }, &TypeVariableValue::Known { .. }) => {
- Ok(value2.clone())
+ (&TypeVariableValue::Known { value }, &TypeVariableValue::Unknown { .. })
+ | (&TypeVariableValue::Unknown { .. }, &TypeVariableValue::Known { value }) => {
+ Ok(TypeVariableValue::Known { value })
}
// If both sides are *unknown*, it hardly matters, does it?
diff --git a/crates/hir-ty/src/next_solver/infer/unify_key.rs b/crates/hir-ty/src/next_solver/infer/unify_key.rs
index 07eeda255b..061b8531d3 100644
--- a/crates/hir-ty/src/next_solver/infer/unify_key.rs
+++ b/crates/hir-ty/src/next_solver/infer/unify_key.rs
@@ -11,10 +11,10 @@ use crate::{
next_solver::{Const, Region},
};
-#[derive(Clone, Debug)]
+#[derive(Clone, Copy, Debug)]
pub(crate) enum RegionVariableValue<'db> {
- Known { value: Region<'db> },
- Unknown { universe: UniverseIndex },
+ Known { value: Region<'db>, span: Option<Span> },
+ Unknown { universe: UniverseIndex, span: Span },
}
#[derive(PartialEq, Copy, Clone, Debug)]
@@ -54,9 +54,15 @@ impl<'db> UnifyValue for RegionVariableValue<'db> {
Err(RegionUnificationError)
}
- (RegionVariableValue::Known { value }, RegionVariableValue::Unknown { universe })
- | (RegionVariableValue::Unknown { universe }, RegionVariableValue::Known { value }) => {
- let universe_of_value = match (*value).kind() {
+ (
+ &RegionVariableValue::Known { value, span: span_known },
+ &RegionVariableValue::Unknown { universe, span: span_unknown },
+ )
+ | (
+ &RegionVariableValue::Unknown { universe, span: span_unknown },
+ &RegionVariableValue::Known { value, span: span_known },
+ ) => {
+ let universe_of_value = match value.kind() {
RegionKind::ReStatic
| RegionKind::ReErased
| RegionKind::ReLateParam(..)
@@ -68,23 +74,28 @@ impl<'db> UnifyValue for RegionVariableValue<'db> {
}
};
+ let span = match span_known {
+ Some(span_known) => Span::pick_best(span_known, span_unknown),
+ None => span_unknown,
+ };
if universe.can_name(universe_of_value) {
- Ok(RegionVariableValue::Known { value: *value })
+ Ok(RegionVariableValue::Known { value, span: Some(span) })
} else {
Err(RegionUnificationError)
}
}
(
- RegionVariableValue::Unknown { universe: a },
- RegionVariableValue::Unknown { universe: b },
+ &RegionVariableValue::Unknown { universe: a, span: span1 },
+ &RegionVariableValue::Unknown { universe: b, span: span2 },
) => {
// If we unify two unconstrained regions then whatever
// value they wind up taking (which must be the same value) must
// be nameable by both universes. Therefore, the resulting
// universe is the minimum of the two universes, because that is
// the one which contains the fewest names in scope.
- Ok(RegionVariableValue::Unknown { universe: (*a).min(*b) })
+ let span = Span::pick_best(span1, span2);
+ Ok(RegionVariableValue::Unknown { universe: a.min(b), span })
}
}
}
@@ -92,7 +103,7 @@ impl<'db> UnifyValue for RegionVariableValue<'db> {
// Generic consts.
-#[derive(Clone, Debug)]
+#[derive(Clone, Copy, Debug)]
pub(crate) enum ConstVariableValue<'db> {
Known { value: Const<'db> },
Unknown { span: Span, universe: UniverseIndex },
@@ -134,9 +145,6 @@ impl<'db> UnifyKey for ConstVidKey<'db> {
fn tag() -> &'static str {
"ConstVidKey"
}
- fn order_roots(a: Self, _: &Self::Value, b: Self, _: &Self::Value) -> Option<(Self, Self)> {
- if a.vid.as_u32() < b.vid.as_u32() { Some((a, b)) } else { Some((b, a)) }
- }
}
impl<'db> UnifyValue for ConstVariableValue<'db> {
@@ -149,25 +157,22 @@ impl<'db> UnifyValue for ConstVariableValue<'db> {
}
// If one side is known, prefer that one.
- (ConstVariableValue::Known { .. }, ConstVariableValue::Unknown { .. }) => {
- Ok(value1.clone())
- }
- (ConstVariableValue::Unknown { .. }, ConstVariableValue::Known { .. }) => {
- Ok(value2.clone())
- }
+ (ConstVariableValue::Known { .. }, ConstVariableValue::Unknown { .. }) => Ok(*value1),
+ (ConstVariableValue::Unknown { .. }, ConstVariableValue::Known { .. }) => Ok(*value2),
// If both sides are *unknown*, it hardly matters, does it?
(
- ConstVariableValue::Unknown { span: origin, universe: universe1 },
- ConstVariableValue::Unknown { span: _, universe: universe2 },
+ &ConstVariableValue::Unknown { span: span1, universe: universe1 },
+ &ConstVariableValue::Unknown { span: span2, universe: universe2 },
) => {
// If we unify two unbound variables, ?T and ?U, then whatever
// value they wind up taking (which must be the same value) must
// be nameable by both universes. Therefore, the resulting
// universe is the minimum of the two universes, because that is
// the one which contains the fewest names in scope.
- let universe = cmp::min(*universe1, *universe2);
- Ok(ConstVariableValue::Unknown { span: *origin, universe })
+ let universe = cmp::min(universe1, universe2);
+ let span = Span::pick_best(span1, span2);
+ Ok(ConstVariableValue::Unknown { span, universe })
}
}
}
diff --git a/crates/hir-ty/src/next_solver/region.rs b/crates/hir-ty/src/next_solver/region.rs
index 3f0aebac2d..7acbcbf4aa 100644
--- a/crates/hir-ty/src/next_solver/region.rs
+++ b/crates/hir-ty/src/next_solver/region.rs
@@ -136,7 +136,7 @@ impl<'db> Region<'db> {
}
RegionKind::ReError(..) => {
flags |= TypeFlags::HAS_FREE_REGIONS;
- flags |= TypeFlags::HAS_ERROR;
+ flags |= TypeFlags::HAS_RE_ERROR;
}
}
diff --git a/crates/hir-ty/src/next_solver/solver.rs b/crates/hir-ty/src/next_solver/solver.rs
index 018ecd66e0..6abc87f088 100644
--- a/crates/hir-ty/src/next_solver/solver.rs
+++ b/crates/hir-ty/src/next_solver/solver.rs
@@ -6,7 +6,7 @@ use hir_def::{
};
use rustc_next_trait_solver::delegate::SolverDelegate;
use rustc_type_ir::{
- AliasTyKind, GenericArgKind, InferCtxtLike, Interner, PredicatePolarity, TypeFlags,
+ AliasTyKind, GenericArgKind, InferCtxtLike, InferTy, Interner, PredicatePolarity, TypeFlags,
TypeVisitableExt,
inherent::{IntoKind, Term as _, Ty as _},
lang_items::SolverTraitLangItem,
@@ -293,10 +293,10 @@ impl<'db> SolverDelegate for SolverContext<'db> {
}
if trait_pred.polarity() == PredicatePolarity::Positive {
- match self.0.cx().as_trait_lang_item(trait_pred.def_id()) {
+ match self.0.interner.as_trait_lang_item(trait_pred.def_id()) {
Some(SolverTraitLangItem::Sized) | Some(SolverTraitLangItem::MetaSized) => {
let predicate = self.resolve_vars_if_possible(goal.predicate);
- if sizedness_fast_path(self.cx(), predicate, goal.param_env) {
+ if sizedness_fast_path(self.interner, predicate, goal.param_env) {
return Some(Certainty::Yes);
}
}
@@ -322,17 +322,31 @@ impl<'db> SolverDelegate for SolverContext<'db> {
let pred = goal.predicate.kind();
match pred.no_bound_vars()? {
- PredicateKind::Clause(ClauseKind::RegionOutlives(_outlives)) => Some(Certainty::Yes),
- PredicateKind::Clause(ClauseKind::TypeOutlives(_outlives)) => Some(Certainty::Yes),
+ PredicateKind::DynCompatible(def_id)
+ if self.0.interner.trait_is_dyn_compatible(def_id) =>
+ {
+ Some(Certainty::Yes)
+ }
+ PredicateKind::Clause(ClauseKind::RegionOutlives(outlives)) => {
+ self.0.sub_regions(outlives.1, outlives.0);
+ Some(Certainty::Yes)
+ }
+ PredicateKind::Clause(ClauseKind::TypeOutlives(outlives)) => {
+ self.0.register_type_outlives_constraint(outlives.0, outlives.1);
+
+ Some(Certainty::Yes)
+ }
PredicateKind::Subtype(SubtypePredicate { a, b, .. })
| PredicateKind::Coerce(CoercePredicate { a, b }) => {
- if self.shallow_resolve(a).is_ty_var() && self.shallow_resolve(b).is_ty_var() {
- // FIXME: We also need to register a subtype relation between these vars
- // when those are added, and if they aren't in the same sub root then
- // we should mark this goal as `has_changed`.
- Some(Certainty::AMBIGUOUS)
- } else {
- None
+ match (self.shallow_resolve(a).kind(), self.shallow_resolve(b).kind()) {
+ (
+ TyKind::Infer(InferTy::TyVar(a_vid)),
+ TyKind::Infer(InferTy::TyVar(b_vid)),
+ ) => {
+ self.sub_unify_ty_vids_raw(a_vid, b_vid);
+ Some(Certainty::AMBIGUOUS)
+ }
+ _ => None,
}
}
PredicateKind::Clause(ClauseKind::ConstArgHasType(ct, _)) => {
@@ -343,6 +357,7 @@ impl<'db> SolverDelegate for SolverContext<'db> {
}
}
PredicateKind::Clause(ClauseKind::WellFormed(arg)) => {
+ let arg = self.shallow_resolve_term(arg);
if arg.is_trivially_wf(self.interner) {
Some(Certainty::Yes)
} else if arg.is_infer() {
diff --git a/crates/hir-ty/src/next_solver/ty.rs b/crates/hir-ty/src/next_solver/ty.rs
index 0fd02eb8ca..511259ecd8 100644
--- a/crates/hir-ty/src/next_solver/ty.rs
+++ b/crates/hir-ty/src/next_solver/ty.rs
@@ -17,7 +17,7 @@ use rustc_type_ir::{
IntVid, Interner, TyVid, TypeFoldable, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable,
TypeVisitableExt, TypeVisitor, UintTy, Upcast, WithCachedTypeInfo,
inherent::{
- AdtDef as _, BoundExistentialPredicates, Const as _, GenericArgs as _, IntoKind, ParamLike,
+ AdtDef as _, BoundExistentialPredicates, GenericArgs as _, IntoKind, ParamLike,
Safety as _, SliceLike, Ty as _,
},
relate::Relate,
@@ -31,8 +31,8 @@ use crate::{
lower::GenericPredicates,
next_solver::{
AdtDef, AliasTy, Binder, CallableIdWrapper, Clause, ClauseKind, ClosureIdWrapper, Const,
- CoroutineClosureIdWrapper, CoroutineIdWrapper, FnSig, GenericArgKind, PolyFnSig, Region,
- TraitRef, TypeAliasIdWrapper,
+ CoroutineClosureIdWrapper, CoroutineIdWrapper, FnSig, GenericArgKind, PolyFnSig, Predicate,
+ Region, TraitRef, TypeAliasIdWrapper,
abi::Safety,
impl_foldable_for_interned_slice, impl_stored_interned, interned_slice,
util::{CoroutineArgsExt, IntegerTypeExt},
@@ -817,25 +817,11 @@ impl<'db> Ty<'db> {
}
pub fn references_non_lt_error<'db, T: TypeVisitableExt<DbInterner<'db>>>(t: &T) -> bool {
- t.references_error() && t.visit_with(&mut ReferencesNonLifetimeError).is_break()
-}
-
-struct ReferencesNonLifetimeError;
-
-impl<'db> TypeVisitor<DbInterner<'db>> for ReferencesNonLifetimeError {
- type Result = ControlFlow<()>;
-
- fn visit_ty(&mut self, ty: Ty<'db>) -> Self::Result {
- if ty.is_ty_error() { ControlFlow::Break(()) } else { ty.super_visit_with(self) }
- }
-
- fn visit_const(&mut self, c: Const<'db>) -> Self::Result {
- if c.is_ct_error() { ControlFlow::Break(()) } else { c.super_visit_with(self) }
- }
+ t.has_non_region_error()
}
pub fn references_only_ty_error<'db, T: TypeVisitableExt<DbInterner<'db>>>(t: &T) -> bool {
- t.references_error() && t.visit_with(&mut ReferencesOnlyTyError).is_break()
+ references_non_lt_error(t) && t.visit_with(&mut ReferencesOnlyTyError).is_break()
}
struct ReferencesOnlyTyError;
@@ -844,7 +830,29 @@ impl<'db> TypeVisitor<DbInterner<'db>> for ReferencesOnlyTyError {
type Result = ControlFlow<()>;
fn visit_ty(&mut self, ty: Ty<'db>) -> Self::Result {
- if ty.is_ty_error() { ControlFlow::Break(()) } else { ty.super_visit_with(self) }
+ if !ty.references_non_lt_error() {
+ ControlFlow::Continue(())
+ } else if ty.is_ty_error() {
+ ControlFlow::Break(())
+ } else {
+ ty.super_visit_with(self)
+ }
+ }
+
+ fn visit_const(&mut self, c: Const<'db>) -> Self::Result {
+ if !references_non_lt_error(&c) {
+ ControlFlow::Continue(())
+ } else {
+ c.super_visit_with(self)
+ }
+ }
+
+ fn visit_predicate(&mut self, p: Predicate<'db>) -> Self::Result {
+ if !references_non_lt_error(&p) {
+ ControlFlow::Continue(())
+ } else {
+ p.super_visit_with(self)
+ }
}
}
@@ -880,6 +888,15 @@ impl<'db> TypeVisitable<DbInterner<'db>> for Ty<'db> {
}
}
+impl<'db> TypeVisitable<DbInterner<'db>> for StoredTy {
+ fn visit_with<V: rustc_type_ir::TypeVisitor<DbInterner<'db>>>(
+ &self,
+ visitor: &mut V,
+ ) -> V::Result {
+ self.as_ref().visit_with(visitor)
+ }
+}
+
impl<'db> TypeSuperVisitable<DbInterner<'db>> for Ty<'db> {
fn super_visit_with<V: rustc_type_ir::TypeVisitor<DbInterner<'db>>>(
&self,
@@ -946,6 +963,18 @@ impl<'db> TypeFoldable<DbInterner<'db>> for Ty<'db> {
}
}
+impl<'db> TypeFoldable<DbInterner<'db>> for StoredTy {
+ fn try_fold_with<F: rustc_type_ir::FallibleTypeFolder<DbInterner<'db>>>(
+ self,
+ folder: &mut F,
+ ) -> Result<Self, F::Error> {
+ Ok(self.as_ref().try_fold_with(folder)?.store())
+ }
+ fn fold_with<F: rustc_type_ir::TypeFolder<DbInterner<'db>>>(self, folder: &mut F) -> Self {
+ self.as_ref().fold_with(folder).store()
+ }
+}
+
impl<'db> TypeSuperFoldable<DbInterner<'db>> for Ty<'db> {
fn try_super_fold_with<F: rustc_type_ir::FallibleTypeFolder<DbInterner<'db>>>(
self,
diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs
index 8f9465cf1b..18f28541af 100644
--- a/crates/hir/src/diagnostics.rs
+++ b/crates/hir/src/diagnostics.rs
@@ -6,9 +6,9 @@
use cfg::{CfgExpr, CfgOptions};
use either::Either;
use hir_def::{
- DefWithBodyId, GenericParamId, SyntheticSyntax,
+ DefWithBodyId, GenericParamId, HasModule, SyntheticSyntax,
expr_store::{
- ExprOrPatPtr, ExprOrPatSource, ExpressionStoreSourceMap, hir_assoc_type_binding_to_ast,
+ ExprOrPatPtr, ExpressionStoreSourceMap, hir_assoc_type_binding_to_ast,
hir_generic_arg_to_ast, hir_segment_to_ast_segment,
},
hir::ExprOrPatId,
@@ -19,6 +19,7 @@ use hir_ty::{
PathLoweringDiagnostic, TyLoweringDiagnostic, TyLoweringDiagnosticKind,
db::HirDatabase,
diagnostics::{BodyValidationDiagnostic, UnsafetyReason},
+ display::{DisplayTarget, HirDisplay},
};
use syntax::{
AstNode, AstPtr, SyntaxError, SyntaxNodePtr, TextRange,
@@ -108,7 +109,7 @@ diagnostics![AnyDiagnostic<'db> ->
IncorrectGenericsOrder,
MissingLifetime,
ElidedLifetimesInPath,
- TypeMustBeKnown,
+ TypeMustBeKnown<'db>,
];
#[derive(Debug)]
@@ -446,8 +447,9 @@ pub struct ElidedLifetimesInPath {
}
#[derive(Debug)]
-pub struct TypeMustBeKnown {
- pub at_point: ExprOrPatSource,
+pub struct TypeMustBeKnown<'db> {
+ pub at_point: InFile<AstPtr<Either<ast::Type, Either<ast::Expr, ast::Pat>>>>,
+ pub top_term: Option<Either<Type<'db>, String>>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
@@ -631,6 +633,12 @@ impl<'db> AnyDiagnostic<'db> {
.inspect_err(|_| stdx::never!("inference diagnostic in desugared pattern"))
.ok()
};
+ let type_syntax = |pat| {
+ source_map
+ .type_syntax(pat)
+ .inspect_err(|_| stdx::never!("inference diagnostic in desugared type"))
+ .ok()
+ };
let expr_or_pat_syntax = |id| match id {
ExprOrPatId::ExprId(expr) => expr_syntax(expr),
ExprOrPatId::PatId(pat) => pat_syntax(pat),
@@ -806,8 +814,29 @@ impl<'db> AnyDiagnostic<'db> {
let lhs = expr_syntax(lhs)?;
InvalidLhsOfAssignment { lhs }.into()
}
- &InferenceDiagnostic::TypeMustBeKnown { at_point } => {
- TypeMustBeKnown { at_point: expr_or_pat_syntax(at_point)? }.into()
+ &InferenceDiagnostic::TypeMustBeKnown { at_point, ref top_term } => {
+ let at_point = match at_point {
+ hir_ty::Span::ExprId(idx) => expr_syntax(idx)?.map(|it| it.wrap_right()),
+ hir_ty::Span::PatId(idx) => pat_syntax(idx)?.map(|it| it.wrap_right()),
+ hir_ty::Span::TypeRefId(idx) => type_syntax(idx)?.map(|it| it.wrap_left()),
+ hir_ty::Span::Dummy => unreachable!(
+ "should never create TypeMustBeKnown diagnostic for dummy spans"
+ ),
+ };
+ let top_term = top_term.as_ref().map(|top_term| match top_term.as_ref().kind() {
+ rustc_type_ir::GenericArgKind::Type(ty) => Either::Left(Type {
+ ty,
+ env: crate::body_param_env_from_has_crate(db, def),
+ }),
+ // FIXME: Printing the const to string is definitely not the correct thing to do here.
+ rustc_type_ir::GenericArgKind::Const(konst) => Either::Right(
+ konst.display(db, DisplayTarget::from_crate(db, def.krate(db))).to_string(),
+ ),
+ rustc_type_ir::GenericArgKind::Lifetime(_) => {
+ unreachable!("we currently don't emit TypeMustBeKnown for lifetimes")
+ }
+ });
+ TypeMustBeKnown { at_point, top_term }.into()
}
})
}
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index f5ca22e10c..8cc82113b3 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -4795,7 +4795,7 @@ impl ConstParam {
}
pub fn ty(self, db: &dyn HirDatabase) -> Type<'_> {
- Type::new(db, self.id.parent(), db.const_param_ty_ns(self.id))
+ Type::new(db, self.id.parent(), db.const_param_ty(self.id))
}
pub fn default(
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index 17f5b51cc3..252f4ac7f2 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -47,8 +47,8 @@ use smallvec::{SmallVec, smallvec};
use span::{FileId, SyntaxContext};
use stdx::{TupleExt, always};
use syntax::{
- AstNode, AstToken, Direction, SmolStr, SmolStrBuilder, SyntaxElement, SyntaxKind, SyntaxNode,
- SyntaxNodePtr, SyntaxToken, T, TextRange, TextSize,
+ AstNode, AstPtr, AstToken, Direction, SmolStr, SmolStrBuilder, SyntaxElement, SyntaxKind,
+ SyntaxNode, SyntaxNodePtr, SyntaxToken, T, TextRange, TextSize,
algo::skip_trivia_token,
ast::{self, HasAttrs as _, HasGenericParams},
};
@@ -543,6 +543,10 @@ impl<'db> SemanticsImpl<'db> {
node
}
+ pub fn to_node<N: AstNode>(&self, ptr: InFile<AstPtr<N>>) -> N {
+ ptr.value.to_node(&self.parse_or_expand(ptr.file_id))
+ }
+
pub fn expand(&self, file_id: MacroCallId) -> ExpandResult<SyntaxNode> {
let res = self.db.parse_macro_expansion(file_id).map(|it| it.0.syntax_node());
self.cache(res.value.clone(), file_id.into());
diff --git a/crates/ide-diagnostics/src/handlers/await_outside_of_async.rs b/crates/ide-diagnostics/src/handlers/await_outside_of_async.rs
index 2a7b0098ed..7cadff84fb 100644
--- a/crates/ide-diagnostics/src/handlers/await_outside_of_async.rs
+++ b/crates/ide-diagnostics/src/handlers/await_outside_of_async.rs
@@ -52,6 +52,7 @@ async fn bar() {
fn await_inside_closure() {
check_diagnostics(
r#"
+//- minicore: future
async fn foo() {}
async fn bar() {
@@ -66,6 +67,7 @@ async fn bar() {
fn await_inside_async_block() {
check_diagnostics(
r#"
+//- minicore: future
async fn foo() {}
fn bar() {
@@ -79,6 +81,7 @@ fn bar() {
fn await_in_complex_context() {
check_diagnostics(
r#"
+//- minicore: future
async fn foo() {}
fn bar() {
diff --git a/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs b/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs
index cbcaab6c74..02f3cab565 100644
--- a/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs
+++ b/crates/ide-diagnostics/src/handlers/break_outside_of_loop.rs
@@ -147,7 +147,7 @@ fn test() {
r#"
//- minicore: option, try
fn test() {
- try {
+ let _: Option<_> = try {
|| {
let x = Some(2);
Some(x?)
diff --git a/crates/ide-diagnostics/src/handlers/incorrect_case.rs b/crates/ide-diagnostics/src/handlers/incorrect_case.rs
index 5410f8b58a..5a456504e1 100644
--- a/crates/ide-diagnostics/src/handlers/incorrect_case.rs
+++ b/crates/ide-diagnostics/src/handlers/incorrect_case.rs
@@ -1006,18 +1006,18 @@ fn func() {
#![allow(unused_variables)]
#[warn(nonstandard_style)]
fn foo() {
- let BAR;
+ let BAR: i32;
// ^^^ 💡 warn: Variable `BAR` should have snake_case name, e.g. `bar`
#[allow(non_snake_case)]
- let FOO;
+ let FOO: i32;
}
#[warn(nonstandard_style)]
fn foo() {
- let BAR;
+ let BAR: i32;
// ^^^ 💡 warn: Variable `BAR` should have snake_case name, e.g. `bar`
#[expect(non_snake_case)]
- let FOO;
+ let FOO: i32;
#[allow(non_snake_case)]
struct qux;
// ^^^ 💡 warn: Structure `qux` should have UpperCamelCase name, e.g. `Qux`
@@ -1060,7 +1060,7 @@ mod FINE_WITH_BAD_CASE;
struct QUX;
const foo: i32 = 0;
fn BAR() {
- let BAZ;
+ let BAZ: i32;
_ = BAZ;
}
"#,
diff --git a/crates/ide-diagnostics/src/handlers/incorrect_generics_len.rs b/crates/ide-diagnostics/src/handlers/incorrect_generics_len.rs
index 25220704e0..2a31a41fbc 100644
--- a/crates/ide-diagnostics/src/handlers/incorrect_generics_len.rs
+++ b/crates/ide-diagnostics/src/handlers/incorrect_generics_len.rs
@@ -43,6 +43,8 @@ struct Bar<T, U>(T, U);
fn foo() {
let _ = Bar::<()>;
// ^^^^^^ error: this struct takes 2 generic arguments but 1 generic argument was supplied
+ // ^^^^^^^^^ error: type annotations needed
+ // | full type: `fn Bar<(), {unknown}>((), {unknown}) -> Bar<(), {unknown}>`
}
"#,
@@ -51,6 +53,10 @@ fn foo() {
#[test]
fn enum_variant() {
+ // FIXME: We should not have a "type annotations needed" error here, but to do that
+ // we'll need to have access to the `InferenceContext` in `TyLoweringContext`, to
+ // generate the infer var with a dummy span (instead of inserting it after the fact
+ // with a non-dummy span).
check_diagnostics(
r#"
enum Enum<T, U> {
@@ -60,8 +66,12 @@ enum Enum<T, U> {
fn foo() {
let _ = Enum::<()>::Variant;
// ^^^^^^ error: this enum takes 2 generic arguments but 1 generic argument was supplied
+ // ^^^^^^^^^^^^^^^^^^^ error: type annotations needed
+ // | full type: `fn Variant<(), {unknown}>((), {unknown}) -> Enum<(), {unknown}>`
let _ = Enum::Variant::<()>;
// ^^^^^^ error: this enum takes 2 generic arguments but 1 generic argument was supplied
+ // ^^^^^^^^^^^^^^^^^^^ error: type annotations needed
+ // | full type: `fn Variant<(), {unknown}>((), {unknown}) -> Enum<(), {unknown}>`
}
"#,
@@ -127,6 +137,8 @@ struct Bar<T, const N: usize>(T);
fn bar() {
let _ = Bar::<()>;
// ^^^^^^ error: this struct takes 2 generic arguments but 1 generic argument was supplied
+ // ^^^^^^^^^ error: type annotations needed
+ // | full type: `fn Bar<(), _>(()) -> Bar<(), _>`
}
"#,
);
diff --git a/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs b/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs
index 4c0985c7ae..b900a8f5cc 100644
--- a/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs
+++ b/crates/ide-diagnostics/src/handlers/mismatched_arg_count.rs
@@ -453,6 +453,8 @@ fn g() {
b::<1, 3>(0, 2);
b(0, 1, 2);
+ // ^ error: type annotations needed
+ // | full type: `fn b<_, _>(u8, u8)`
//^ error: expected 4 arguments, found 3
}
"#,
diff --git a/crates/ide-diagnostics/src/handlers/missing_fields.rs b/crates/ide-diagnostics/src/handlers/missing_fields.rs
index 85368cc09f..117702923b 100644
--- a/crates/ide-diagnostics/src/handlers/missing_fields.rs
+++ b/crates/ide-diagnostics/src/handlers/missing_fields.rs
@@ -293,12 +293,15 @@ fn baz(s: S) -> i32 {
#[test]
fn missing_record_pat_field_box() {
check_diagnostics(
- r"
+ r#"
+#![feature(lang_items)]
+#[lang = "owned_box"]
+struct Box<T>(T);
struct S { s: Box<u32> }
fn x(a: S) {
let S { box s } = a;
}
-",
+"#,
)
}
diff --git a/crates/ide-diagnostics/src/handlers/type_mismatch.rs b/crates/ide-diagnostics/src/handlers/type_mismatch.rs
index 355617a2b1..a845e0b59a 100644
--- a/crates/ide-diagnostics/src/handlers/type_mismatch.rs
+++ b/crates/ide-diagnostics/src/handlers/type_mismatch.rs
@@ -1340,6 +1340,7 @@ pub fn foo<T: Foo>(_: T) -> (T::Out,) { loop { } }
fn main() {
let _x = foo(2);
+ // ^^ error: type annotations needed
}
"#,
);
diff --git a/crates/ide-diagnostics/src/handlers/type_must_be_known.rs b/crates/ide-diagnostics/src/handlers/type_must_be_known.rs
index 4b72497408..5363f4a5ce 100644
--- a/crates/ide-diagnostics/src/handlers/type_must_be_known.rs
+++ b/crates/ide-diagnostics/src/handlers/type_must_be_known.rs
@@ -1,17 +1,56 @@
+use either::Either;
+use hir::HirDisplay;
+use stdx::format_to;
+use syntax::{AstNode, SyntaxNodePtr, ast};
+
use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
// Diagnostic: type-must-be-known
//
// This diagnostic is triggered when rust-analyzer cannot infer some type.
-pub(crate) fn type_must_be_known(
- ctx: &DiagnosticsContext<'_>,
- d: &hir::TypeMustBeKnown,
+pub(crate) fn type_must_be_known<'db>(
+ ctx: &DiagnosticsContext<'db>,
+ d: &hir::TypeMustBeKnown<'db>,
) -> Diagnostic {
+ let mut at_point = d.at_point.map(|it| it.syntax_node_ptr());
+ let mut top_term = d.top_term.clone();
+
+ // Do some adjustments to the node: FIXME: We should probably do that at the emitting site.
+ let node = ctx.sema.to_node(d.at_point);
+ if let Either::Right(Either::Left(expr)) = &node
+ && let Some(Either::Left(top_ty)) = &d.top_term
+ && let Some(expr_ty) = ctx.sema.type_of_expr(expr)
+ && expr_ty.original == *top_ty
+ && !top_ty.is_unknown()
+ && let Some(parent) = expr.syntax().parent().and_then(ast::CallExpr::cast)
+ && let Some(callable) = top_ty.as_callable(ctx.db())
+ && let ret_ty = callable.return_type()
+ && ret_ty.contains_unknown()
+ {
+ top_term = Some(Either::Left(ret_ty));
+ at_point.value = SyntaxNodePtr::new(parent.syntax());
+ }
+
+ let message = match &top_term {
+ Some(top_term) if !matches!(top_term, Either::Left(ty) if ty.is_unknown()) => {
+ let mut message = "type annotations needed\nfull type: `".to_owned();
+ match top_term {
+ Either::Left(ty) => {
+ format_to!(message, "{}", ty.display(ctx.db(), ctx.display_target))
+ }
+ Either::Right(konst) => message.push_str(konst),
+ }
+ message.push_str("`\n");
+ message
+ }
+ Some(_) => "type annotations needed".to_owned(),
+ None => "type annotations needed; type must be known at this point".to_owned(),
+ };
Diagnostic::new_with_syntax_node_ptr(
ctx,
DiagnosticCode::RustcHardError("E0282"),
- "type annotations needed; type must be known at this point",
- d.at_point.map(|it| it.into()),
+ message,
+ at_point,
)
}
@@ -20,7 +59,7 @@ mod tests {
use crate::tests::check_diagnostics;
#[test]
- fn smoke_test() {
+ fn some_expressions_require_knowing_type() {
check_diagnostics(
r#"
fn foo() {
@@ -36,4 +75,32 @@ fn foo() {
"#,
);
}
+
+ #[test]
+ fn binding_without_type() {
+ check_diagnostics(
+ r#"
+fn any<T>() -> T { loop {} }
+fn foo() {
+ let _x = any();
+ // ^^^^^ error: type annotations needed
+}
+ "#,
+ );
+ }
+
+ #[test]
+ fn struct_with_generic() {
+ check_diagnostics(
+ r#"
+struct X<T>(T);
+fn any<T>() -> X<T> { loop {} }
+fn foo() {
+ let _x = any();
+ // ^^^^^ error: type annotations needed
+ // | full type: `X<{unknown}>`
+}
+ "#,
+ );
+ }
}
diff --git a/crates/ide-diagnostics/src/handlers/typed_hole.rs b/crates/ide-diagnostics/src/handlers/typed_hole.rs
index fd1674e2a4..ddd1dd402e 100644
--- a/crates/ide-diagnostics/src/handlers/typed_hole.rs
+++ b/crates/ide-diagnostics/src/handlers/typed_hole.rs
@@ -166,6 +166,8 @@ fn t<T>() -> T { loop {} }
r#"
fn main() {
let _x = [(); _];
+ // ^ error: type annotations needed
+ // | full type: `[(); _]`
// FIXME: This should trigger error
// let _y: [(); 10] = [(); _];
_ = 0;
diff --git a/crates/ide-diagnostics/src/tests/overly_long_real_world_cases.rs b/crates/ide-diagnostics/src/tests/overly_long_real_world_cases.rs
index 9883bcc84f..301613e920 100644
--- a/crates/ide-diagnostics/src/tests/overly_long_real_world_cases.rs
+++ b/crates/ide-diagnostics/src/tests/overly_long_real_world_cases.rs
@@ -2722,6 +2722,13 @@ fn foo() {
tracing::error!();
}
"#,
- &["E0432", "inactive-code", "unresolved-macro-call", "syntax-error", "macro-error"],
+ &[
+ "E0432",
+ "E0282",
+ "inactive-code",
+ "unresolved-macro-call",
+ "syntax-error",
+ "macro-error",
+ ],
);
}
diff --git a/crates/macros/src/lib.rs b/crates/macros/src/lib.rs
index de8c3f2e55..9088efeca4 100644
--- a/crates/macros/src/lib.rs
+++ b/crates/macros/src/lib.rs
@@ -5,7 +5,7 @@ use syn::parse_quote;
use synstructure::decl_derive;
decl_derive!(
- [TypeFoldable, attributes(type_foldable)] =>
+ [TypeFoldable, attributes(type_foldable, type_visitable)] =>
/// Derives `TypeFoldable` for the annotated `struct` or `enum` (`union` is not supported).
///
/// The fold will produce a value of the same struct or enum variant as the input, with
@@ -102,8 +102,10 @@ fn type_foldable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::Toke
vi.construct(|_, index| {
let bind = &bindings[index];
- // retain value of fields with #[type_foldable(identity)]
- if has_ignore_attr(&bind.ast().attrs, "type_foldable", "identity") {
+ // retain value of fields with #[type_foldable(identity)] or #[type_visitable(ignore)]
+ if has_ignore_attr(&bind.ast().attrs, "type_foldable", "identity")
+ || has_ignore_attr(&bind.ast().attrs, "type_visitable", "ignore")
+ {
bind.to_token_stream()
} else {
quote! {
@@ -118,8 +120,10 @@ fn type_foldable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::Toke
vi.construct(|_, index| {
let bind = &bindings[index];
- // retain value of fields with #[type_foldable(identity)]
- if has_ignore_attr(&bind.ast().attrs, "type_foldable", "identity") {
+ // retain value of fields with #[type_foldable(identity)] or #[type_visitable(ignore)]
+ if has_ignore_attr(&bind.ast().attrs, "type_foldable", "identity")
+ || has_ignore_attr(&bind.ast().attrs, "type_visitable", "ignore")
+ {
bind.to_token_stream()
} else {
quote! {
diff --git a/crates/test-utils/src/minicore.rs b/crates/test-utils/src/minicore.rs
index c447870e96..33a20951da 100644
--- a/crates/test-utils/src/minicore.rs
+++ b/crates/test-utils/src/minicore.rs
@@ -67,7 +67,7 @@
//! size_of: sized
//! sized:
//! slice:
-//! str:
+//! str: sized, result
//! sync: sized
//! transmute:
//! try: infallible
@@ -1726,6 +1726,22 @@ pub mod iter {
}
}
+ pub struct Map<I, F> {
+ iter: I,
+ f: F,
+ }
+ impl<B, I: Iterator, F> Iterator for Map<I, F>
+ where
+ F: FnMut(I::Item) -> B,
+ {
+ type Item = B;
+
+ #[inline]
+ fn next(&mut self) -> B {
+ loop {}
+ }
+ }
+
pub struct FilterMap<I, F> {
iter: I,
f: F,
@@ -1800,6 +1816,13 @@ pub mod iter {
{
loop {}
}
+ fn map<B, F>(self, _f: F) -> crate::iter::Map<Self, F>
+ where
+ Self: Sized,
+ F: FnMut(Self::Item) -> B,
+ {
+ loop {}
+ }
fn filter_map<B, F>(self, _f: F) -> crate::iter::FilterMap<Self, F>
where
Self: Sized,