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
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, |