Unnamed repository; edit this file 'description' to name the repository.
| -rw-r--r-- | crates/hir-ty/src/infer.rs | 54 | ||||
| -rw-r--r-- | crates/hir-ty/src/infer/callee.rs | 22 | ||||
| -rw-r--r-- | crates/hir-ty/src/infer/closure/analysis.rs | 8 | ||||
| -rw-r--r-- | crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs | 26 | ||||
| -rw-r--r-- | crates/hir-ty/src/infer/expr.rs | 72 | ||||
| -rw-r--r-- | crates/hir-ty/src/infer/pat.rs | 26 | ||||
| -rw-r--r-- | crates/hir-ty/src/infer/place_op.rs | 2 | ||||
| -rw-r--r-- | crates/hir-ty/src/infer/unify.rs | 10 | ||||
| -rw-r--r-- | crates/hir-ty/src/tests/never_type.rs | 4 | ||||
| -rw-r--r-- | crates/hir/src/diagnostics.rs | 11 | ||||
| -rw-r--r-- | crates/ide-diagnostics/src/handlers/missing_unsafe.rs | 2 | ||||
| -rw-r--r-- | crates/ide-diagnostics/src/handlers/type_must_be_known.rs | 39 | ||||
| -rw-r--r-- | crates/ide-diagnostics/src/lib.rs | 2 |
13 files changed, 186 insertions, 92 deletions
diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs index 82c5834e64..8b0702c483 100644 --- a/crates/hir-ty/src/infer.rs +++ b/crates/hir-ty/src/infer.rs @@ -401,6 +401,9 @@ pub enum InferenceDiagnostic { InvalidLhsOfAssignment { lhs: ExprId, }, + TypeMustBeKnown { + at_point: ExprOrPatId, + }, } /// A mismatch between an expected and an inferred type. @@ -1178,6 +1181,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>>, } #[derive(Clone, Debug)] @@ -1267,6 +1271,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { deferred_cast_checks: Vec::new(), inside_assignment: false, diagnostics: Diagnostics::default(), + vars_emitted_type_must_be_known_for: FxHashSet::default(), deferred_call_resolutions: FxHashMap::default(), } } @@ -1581,6 +1586,33 @@ impl<'body, 'db> InferenceContext<'body, 'db> { &self.table.infer_ctxt } + /// If `ty` is an error, returns an infer var instead. Otherwise, returns it. + /// + /// "Refreshing" types like this is useful for getting better types, but it is also + /// very dangerous: we might create duplicate diagnostics, for example if we try + /// to resolve it and fail. rustc doesn't do that for this reason (and is in general + /// more strict with how it uses error types; an error type in inputs will almost + /// always cause it to infer an error type in output, while we infer some type as much + /// as we can). + /// + /// Unfortunately, we cannot allow ourselves to do that. Not only we more often work + /// with incomplete code, we also have assists, for example "Generate constant", that + /// will assume the inferred type is the expected type even if the expression itself + /// cannot be inferred. Therefore, we choose a middle ground: refresh the type, + /// but if we return a new var, mark it so that no diagnostics will be issued on it. + fn insert_type_vars_shallow(&mut self, ty: Ty<'db>) -> Ty<'db> { + if ty.is_ty_error() { + let var = self.table.next_ty_var(); + + // 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); + + var + } else { + ty + } + } + fn infer_body(&mut self, body_expr: ExprId) { match self.return_coercion { Some(_) => self.infer_return(body_expr), @@ -1781,11 +1813,6 @@ impl<'body, 'db> InferenceContext<'body, 'db> { self.insert_type_vars(lt) } - /// Replaces `Ty::Error` by a new type var, so we can maybe still infer it. - fn insert_type_vars_shallow(&mut self, ty: Ty<'db>) -> Ty<'db> { - self.table.insert_type_vars_shallow(ty) - } - fn insert_type_vars<T>(&mut self, ty: T) -> T where T: TypeFoldable<DbInterner<'db>>, @@ -1873,6 +1900,11 @@ impl<'body, 'db> InferenceContext<'body, 'db> { self.table.resolve_vars_if_possible(t) } + pub(crate) fn structurally_resolve_type(&mut self, node: ExprOrPatId, ty: Ty<'db>) -> Ty<'db> { + let result = self.table.try_structurally_resolve_type(ty); + if result.is_ty_var() { self.type_must_be_known_at_this_point(node, ty) } else { result } + } + fn demand_eqtype( &mut self, id: ExprOrPatId, @@ -1938,11 +1970,13 @@ impl<'body, 'db> InferenceContext<'body, 'db> { } pub(crate) fn type_must_be_known_at_this_point( - &self, - _id: ExprOrPatId, - _ty: Ty<'db>, + &mut self, + node: ExprOrPatId, + ty: Ty<'db>, ) -> Ty<'db> { - // FIXME: Emit an diagnostic. + if self.vars_emitted_type_must_be_known_for.insert(ty) { + self.push_diagnostic(InferenceDiagnostic::TypeMustBeKnown { at_point: node }); + } self.types.types.error } @@ -2477,7 +2511,7 @@ impl<'db> Expectation<'db> { fn adjust_for_branches(&self, table: &mut unify::InferenceTable<'db>) -> Expectation<'db> { match *self { Expectation::HasType(ety) => { - let ety = table.structurally_resolve_type(ety); + let ety = table.try_structurally_resolve_type(ety); if ety.is_ty_var() { Expectation::None } else { Expectation::HasType(ety) } } Expectation::RValueLikeUnsized(ety) => Expectation::RValueLikeUnsized(ety), diff --git a/crates/hir-ty/src/infer/callee.rs b/crates/hir-ty/src/infer/callee.rs index 3d478912a3..a470c92124 100644 --- a/crates/hir-ty/src/infer/callee.rs +++ b/crates/hir-ty/src/infer/callee.rs @@ -47,9 +47,15 @@ impl<'db> InferenceContext<'_, 'db> { let mut autoderef = GeneralAutoderef::new_from_inference_context(self, expr_ty); let mut result = None; + let mut error_reported = false; while result.is_none() && autoderef.next().is_some() { - result = - Self::try_overloaded_call_step(call_expr, callee_expr, arg_exprs, &mut autoderef); + result = Self::try_overloaded_call_step( + call_expr, + callee_expr, + arg_exprs, + &mut autoderef, + &mut error_reported, + ); } // FIXME: rustc does some ABI checks here, but the ABI mapping is in rustc_target and we don't have access to that crate. @@ -65,10 +71,12 @@ impl<'db> InferenceContext<'_, 'db> { self.infer_expr_no_expect(arg, ExprIsRead::Yes); } - self.push_diagnostic(InferenceDiagnostic::ExpectedFunction { - call_expr, - found: original_callee_ty.store(), - }); + if !error_reported { + self.push_diagnostic(InferenceDiagnostic::ExpectedFunction { + call_expr, + found: original_callee_ty.store(), + }); + } self.types.types.error } @@ -97,6 +105,7 @@ impl<'db> InferenceContext<'_, 'db> { callee_expr: ExprId, arg_exprs: &[ExprId], autoderef: &mut InferenceContextAutoderef<'_, '_, 'db>, + error_reported: &mut bool, ) -> Option<CallStep<'db>> { let final_ty = autoderef.final_ty(); let adjusted_ty = autoderef.ctx().table.try_structurally_resolve_type(final_ty); @@ -211,6 +220,7 @@ impl<'db> InferenceContext<'_, 'db> { autoderef .ctx() .type_must_be_known_at_this_point(callee_expr.into(), adjusted_ty); + *error_reported = true; return None; } diff --git a/crates/hir-ty/src/infer/closure/analysis.rs b/crates/hir-ty/src/infer/closure/analysis.rs index b0f5533b3b..31b6252475 100644 --- a/crates/hir-ty/src/infer/closure/analysis.rs +++ b/crates/hir-ty/src/infer/closure/analysis.rs @@ -741,7 +741,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { }; let Some(min_cap_list) = root_var_min_capture_list.get_mut(&var_hir_id) else { - let mutability = self.determine_capture_mutability(&place); + let mutability = self.determine_capture_mutability(closure_def_id, &place); let min_cap_list = vec![CapturedPlace { place, info: capture_info, mutability }]; root_var_min_capture_list.insert(var_hir_id, min_cap_list); continue; @@ -850,7 +850,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { // Only need to insert when we don't have an ancestor in the existing min capture list if !ancestor_found { - let mutability = self.determine_capture_mutability(&place); + let mutability = self.determine_capture_mutability(closure_def_id, &place); let captured_place = CapturedPlace { place, info: updated_capture_info, mutability }; min_cap_list.push(captured_place); @@ -1016,7 +1016,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { /// A captured place is mutable if /// 1. Projections don't include a Deref of an immut-borrow, **and** /// 2. PlaceBase is mut or projections include a Deref of a mut-borrow. - fn determine_capture_mutability(&mut self, place: &Place) -> Mutability { + fn determine_capture_mutability(&mut self, closure_expr: ExprId, place: &Place) -> Mutability { let var_hir_id = match place.base { PlaceBase::Upvar { var_id, .. } => var_id, _ => unreachable!(), @@ -1029,7 +1029,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { }; for pointer_ty in place.deref_tys() { - match self.table.structurally_resolve_type(pointer_ty).kind() { + match self.structurally_resolve_type(closure_expr.into(), pointer_ty).kind() { // We don't capture derefs of raw ptrs TyKind::RawPtr(_, _) => unreachable!(), 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 bddb01ff99..f140b12491 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 @@ -797,7 +797,7 @@ impl<'a, 'b, 'db, D: Delegate<'db>> ExprUseVisitor<'a, 'b, 'db, D> { // Select just those fields of the `with` // expression that will actually be used - match self.cx.table.structurally_resolve_type(with_place.place.ty()).kind() { + match self.cx.structurally_resolve_type(with_expr.into(), with_place.place.ty()).kind() { TyKind::Adt(adt, args) if adt.is_struct() => { let AdtId::StructId(adt) = adt.def_id().0 else { unreachable!() }; let adt_fields = VariantId::from(adt).fields(self.cx.db).fields(); @@ -982,7 +982,7 @@ impl<'a, 'b, 'db, D: Delegate<'db>> ExprUseVisitor<'a, 'b, 'db, D> { // FIXME: Does the MIR code skip this read when matching on a ZST? // If so, we can also skip it here. read_discriminant(this); - } else if this.is_multivariant_adt(place.place.ty()) { + } else if this.is_multivariant_adt(pat.into(), place.place.ty()) { // Otherwise, this is a struct/enum variant, and so it's // only a read if we need to read the discriminant. read_discriminant(this); @@ -1001,7 +1001,7 @@ impl<'a, 'b, 'db, D: Delegate<'db>> ExprUseVisitor<'a, 'b, 'db, D> { read_discriminant(this); } Pat::Record { .. } | Pat::TupleStruct { .. } => { - if this.is_multivariant_adt(place.place.ty()) { + if this.is_multivariant_adt(pat.into(), place.place.ty()) { read_discriminant(this); } } @@ -1225,7 +1225,11 @@ impl<'db, D: Delegate<'db>> ExprUseVisitor<'_, '_, 'db, D> { // a bind-by-ref means that the base_ty will be the type of the ident itself, // but what we want here is the type of the underlying value being borrowed. // So peel off one-level, turning the &T into T. - match self.cx.table.structurally_resolve_type(base_ty).builtin_deref(false) { + match self + .cx + .structurally_resolve_type(pat.into(), base_ty) + .builtin_deref(false) + { Some(ty) => Ok(ty), None => { debug!("By-ref binding of non-derefable type: {base_ty:?}"); @@ -1430,7 +1434,8 @@ impl<'db, D: Delegate<'db>> ExprUseVisitor<'_, '_, 'db, D> { let place_ty = self.expr_ty(expr)?; let base_ty = self.expr_ty_adjusted(base)?; - let TyKind::Ref(region, _, mutbl) = self.cx.table.structurally_resolve_type(base_ty).kind() + let TyKind::Ref(region, _, mutbl) = + self.cx.structurally_resolve_type(base.into(), base_ty).kind() else { return Err(ErrorGuaranteed); }; @@ -1447,7 +1452,7 @@ impl<'db, D: Delegate<'db>> ExprUseVisitor<'_, '_, 'db, D> { ) -> Result<PlaceWithOrigin> { let base_curr_ty = base_place.place.ty(); let Some(deref_ty) = - self.cx.table.structurally_resolve_type(base_curr_ty).builtin_deref(true) + self.cx.structurally_resolve_type(node, base_curr_ty).builtin_deref(true) else { debug!("explicit deref of non-derefable type: {:?}", base_curr_ty); return Err(ErrorGuaranteed); @@ -1474,7 +1479,7 @@ impl<'db, D: Delegate<'db>> ExprUseVisitor<'_, '_, 'db, D> { /// Here `pat_hir_id` is the ExprId of the pattern itself. fn total_fields_in_tuple(&mut self, pat_id: PatId) -> usize { let ty = self.cx.result.pat_ty(pat_id); - match self.cx.table.structurally_resolve_type(ty).kind() { + match self.cx.structurally_resolve_type(pat_id.into(), ty).kind() { TyKind::Tuple(args) => args.len(), _ => panic!("tuple pattern not applied to a tuple"), } @@ -1629,8 +1634,7 @@ impl<'db, D: Delegate<'db>> ExprUseVisitor<'_, '_, 'db, D> { Pat::Slice { prefix: ref before, slice, suffix: ref after } => { let Some(element_ty) = self .cx - .table - .structurally_resolve_type(place_with_id.place.ty()) + .structurally_resolve_type(pat.into(), place_with_id.place.ty()) .builtin_index() else { debug!("explicit index of non-indexable type {:?}", place_with_id); @@ -1689,8 +1693,8 @@ impl<'db, D: Delegate<'db>> ExprUseVisitor<'_, '_, 'db, D> { /// FIXME(never_patterns): update this comment once the aforementioned MIR builder /// code is changed to be insensitive to inhhabitedness. #[instrument(skip(self), level = "debug")] - fn is_multivariant_adt(&mut self, ty: Ty<'db>) -> bool { - if let TyKind::Adt(def, _) = self.cx.table.structurally_resolve_type(ty).kind() { + fn is_multivariant_adt(&mut self, node: ExprOrPatId, ty: Ty<'db>) -> bool { + if let TyKind::Adt(def, _) = self.cx.structurally_resolve_type(node, ty).kind() { // Note that if a non-exhaustive SingleVariant is defined in another crate, we need // to assume that more cases will be added to the variant in the future. This mean // that we should handle non-exhaustive SingleVariant the same way we would handle diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs index 79010fbe1b..e179ca4bd3 100644 --- a/crates/hir-ty/src/infer/expr.rs +++ b/crates/hir-ty/src/infer/expr.rs @@ -397,9 +397,7 @@ impl<'db> InferenceContext<'_, 'db> { .1 } &Expr::Loop { body, label } => { - // FIXME: should be: - // let ty = expected.coercion_target_type(&mut self.table); - let ty = self.table.next_ty_var(); + let ty = expected.coercion_target_type(&mut self.table); let (breaks, ()) = self.with_breakable_ctx(BreakableKind::Loop, Some(ty), label, |this| { this.infer_expr( @@ -732,7 +730,7 @@ impl<'db> InferenceContext<'_, 'db> { let base_t = self.infer_expr_no_expect(*base, ExprIsRead::Yes); let idx_t = self.infer_expr_no_expect(*index, ExprIsRead::Yes); - let base_t = self.table.structurally_resolve_type(base_t); + let base_t = self.structurally_resolve_type((*base).into(), base_t); match self.lookup_indexing(tgt_expr, *base, base_t, idx_t) { Some((trait_index_ty, trait_element_ty)) => { // two-phase not needed because index_ty is never mutable @@ -890,7 +888,7 @@ impl<'db> InferenceContext<'_, 'db> { // allows them to be inferred based on how they are used later in the // function. if is_input { - let ty = this.table.structurally_resolve_type(ty); + let ty = this.structurally_resolve_type(expr.into(), ty); match ty.kind() { TyKind::FnDef(def, parameters) => { let fnptr_ty = Ty::new_fn_ptr( @@ -955,7 +953,6 @@ impl<'db> InferenceContext<'_, 'db> { } } }; - // use a new type variable if we got unknown here let ty = self.insert_type_vars_shallow(ty); self.write_expr_ty(tgt_expr, ty); if self.shallow_resolve(ty).is_never() @@ -1304,7 +1301,7 @@ impl<'db> InferenceContext<'_, 'db> { }; let mut oprnd_t = self.infer_expr_inner(oprnd, expected_inner, ExprIsRead::Yes); - oprnd_t = self.table.structurally_resolve_type(oprnd_t); + oprnd_t = self.structurally_resolve_type(oprnd.into(), oprnd_t); match unop { UnaryOp::Deref => { if let Some(ty) = self.lookup_derefing(expr, oprnd, oprnd_t) { @@ -1681,7 +1678,7 @@ impl<'db> InferenceContext<'_, 'db> { ) -> Ty<'db> { // Field projections don't constitute reads. let receiver_ty = self.infer_expr_inner(receiver, &Expectation::none(), ExprIsRead::No); - let receiver_ty = self.table.structurally_resolve_type(receiver_ty); + let receiver_ty = self.structurally_resolve_type(receiver.into(), receiver_ty); if name.is_missing() { // Bail out early, don't even try to look up field. Also, we don't issue an unresolved @@ -1979,38 +1976,39 @@ impl<'db> InferenceContext<'_, 'db> { .unwrap_or_default(); // If the arguments should be wrapped in a tuple (ex: closures), unwrap them here - let (formal_input_tys, expected_input_tys) = - if tuple_arguments == TupleArgumentsFlag::TupleArguments { - let tuple_type = self.table.structurally_resolve_type(formal_input_tys[0]); - match tuple_type.kind() { - // We expected a tuple and got a tuple - TyKind::Tuple(arg_types) => { - // Argument length differs - if arg_types.len() != provided_args.len() { - // FIXME: Emit an error. - } - let expected_input_tys = match expected_input_tys { - Some(expected_input_tys) => match expected_input_tys.first() { - Some(ty) => match ty.kind() { - TyKind::Tuple(tys) => Some(tys.iter().collect()), - _ => None, - }, - None => None, - }, - None => None, - }; - (arg_types.iter().collect(), expected_input_tys) - } - _ => { - // Otherwise, there's a mismatch, so clear out what we're expecting, and set - // our input types to err_args so we don't blow up the error messages + let (formal_input_tys, expected_input_tys) = if tuple_arguments + == TupleArgumentsFlag::TupleArguments + { + let tuple_type = self.structurally_resolve_type(call_expr.into(), formal_input_tys[0]); + match tuple_type.kind() { + // We expected a tuple and got a tuple + TyKind::Tuple(arg_types) => { + // Argument length differs + if arg_types.len() != provided_args.len() { // FIXME: Emit an error. - (vec![self.types.types.error; provided_args.len()], None) } + let expected_input_tys = match expected_input_tys { + Some(expected_input_tys) => match expected_input_tys.first() { + Some(ty) => match ty.kind() { + TyKind::Tuple(tys) => Some(tys.iter().collect()), + _ => None, + }, + None => None, + }, + None => None, + }; + (arg_types.iter().collect(), expected_input_tys) } - } else { - (formal_input_tys.to_vec(), expected_input_tys) - }; + _ => { + // Otherwise, there's a mismatch, so clear out what we're expecting, and set + // our input types to err_args so we don't blow up the error messages + // FIXME: Emit an error. + (vec![self.types.types.error; provided_args.len()], None) + } + } + } else { + (formal_input_tys.to_vec(), expected_input_tys) + }; // If there are no external expectations at the call site, just use the types from the function defn let expected_input_tys = if let Some(expected_input_tys) = expected_input_tys { diff --git a/crates/hir-ty/src/infer/pat.rs b/crates/hir-ty/src/infer/pat.rs index bba69c758a..3cd7461cf3 100644 --- a/crates/hir-ty/src/infer/pat.rs +++ b/crates/hir-ty/src/infer/pat.rs @@ -311,6 +311,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { }; let adjust_mode = self.calc_adjust_mode(pat, opt_path_res); let ty = self.infer_pat_inner(pat_id, opt_path_res, adjust_mode, expected, pat_info); + let ty = self.insert_type_vars_shallow(ty); self.write_pat_ty(pat_id, ty); // If we implicitly inserted overloaded dereferences before matching check the pattern to @@ -457,7 +458,9 @@ impl<'a, 'db> InferenceContext<'a, 'db> { ty } Pat::Lit(expr) => self.infer_lit_pat(expr, expected), - Pat::Range { start: lhs, end: rhs, .. } => self.infer_range_pat(lhs, rhs, expected), + Pat::Range { start: lhs, end: rhs, .. } => { + self.infer_range_pat(pat, lhs, rhs, expected) + } Pat::Bind { id: var_id, subpat } => { self.infer_bind_pat(pat, var_id, subpat, expected, pat_info) } @@ -755,7 +758,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { // types `[u8]` or `[u8; N]`, in order to type, e.g., `deref!(b"..."): Vec<u8>`. let mut pat_ty = ty; if matches!(literal, Literal::ByteString(_)) { - let expected = self.table.structurally_resolve_type(expected); + let expected = self.structurally_resolve_type(expr.into(), expected); match expected.kind() { // Allow `b"...": &[u8]` TyKind::Ref(_, inner_ty, _) @@ -806,8 +809,9 @@ impl<'a, 'db> InferenceContext<'a, 'db> { fn infer_range_pat( &mut self, - lhs: Option<ExprId>, - rhs: Option<ExprId>, + pat: PatId, + lhs_expr: Option<ExprId>, + rhs_expr: Option<ExprId>, expected: Ty<'db>, ) -> Ty<'db> { let mut calc_side = |opt_expr: Option<ExprId>| match opt_expr { @@ -826,8 +830,8 @@ impl<'a, 'db> InferenceContext<'a, 'db> { Some((fail, ty, expr)) } }; - let mut lhs = calc_side(lhs); - let mut rhs = calc_side(rhs); + let mut lhs = calc_side(lhs_expr); + let mut rhs = calc_side(rhs_expr); if let (Some((true, ..)), _) | (_, Some((true, ..))) = (lhs, rhs) { // There exists a side that didn't meet our criteria that the end-point @@ -854,7 +858,10 @@ impl<'a, 'db> InferenceContext<'a, 'db> { // This check is needed if both sides are inference variables. // We require types to be resolved here so that we emit inference failure // rather than "_ is not a char or numeric". - let ty = self.table.structurally_resolve_type(expected); + let ty = self.structurally_resolve_type( + lhs_expr.or(rhs_expr).map(ExprOrPatId::ExprId).unwrap_or(pat.into()), + expected, + ); if !(ty.is_numeric() || ty.is_char() || ty.references_error()) { // FIXME: Emit an error. return self.types.types.error; @@ -1096,7 +1103,8 @@ https://doc.rust-lang.org/reference/types.html#trait-objects"; let mut expected_len = elements.len(); if ddpos.is_some() { // Require known type only when `..` is present. - if let TyKind::Tuple(tys) = self.table.structurally_resolve_type(expected).kind() { + if let TyKind::Tuple(tys) = self.structurally_resolve_type(pat.into(), expected).kind() + { expected_len = tys.len(); } } @@ -1574,7 +1582,7 @@ https://doc.rust-lang.org/reference/types.html#trait-objects"; let _ = self.demand_eqtype(pat.into(), expected, resolved_arr_ty); } - let expected = self.table.structurally_resolve_type(expected); + let expected = self.structurally_resolve_type(pat.into(), expected); debug!(?expected); let (element_ty, opt_slice_ty, inferred) = match expected.kind() { diff --git a/crates/hir-ty/src/infer/place_op.rs b/crates/hir-ty/src/infer/place_op.rs index 1298b38097..1671b67d34 100644 --- a/crates/hir-ty/src/infer/place_op.rs +++ b/crates/hir-ty/src/infer/place_op.rs @@ -108,7 +108,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> { index_ty: Ty<'db>, ) -> Option<(/*index type*/ Ty<'db>, /*element type*/ Ty<'db>)> { let ty = autoderef.final_ty(); - let adjusted_ty = autoderef.ctx().table.structurally_resolve_type(ty); + let adjusted_ty = autoderef.ctx().structurally_resolve_type(base_expr.into(), ty); debug!( "try_index_step(expr={:?}, base_expr={:?}, adjusted_ty={:?}, \ index_ty={:?})", diff --git a/crates/hir-ty/src/infer/unify.rs b/crates/hir-ty/src/infer/unify.rs index c28fea0ff1..be4d370c24 100644 --- a/crates/hir-ty/src/infer/unify.rs +++ b/crates/hir-ty/src/infer/unify.rs @@ -343,11 +343,6 @@ impl<'db> InferenceTable<'db> { } } - pub(crate) fn structurally_resolve_type(&mut self, ty: Ty<'db>) -> Ty<'db> { - self.try_structurally_resolve_type(ty) - // FIXME: Err if it still contain infer vars. - } - pub(crate) fn snapshot(&mut self) -> CombinedSnapshot { self.infer_ctxt.start_snapshot() } @@ -433,11 +428,6 @@ impl<'db> InferenceTable<'db> { self.infer_ctxt.insert_type_vars(ty) } - /// Replaces `Ty::Error` by a new type var, so we can maybe still infer it. - pub(super) fn insert_type_vars_shallow(&mut self, ty: Ty<'db>) -> Ty<'db> { - if ty.is_ty_error() { self.next_ty_var() } else { ty } - } - /// 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) diff --git a/crates/hir-ty/src/tests/never_type.rs b/crates/hir-ty/src/tests/never_type.rs index 993293bb56..91273cd177 100644 --- a/crates/hir-ty/src/tests/never_type.rs +++ b/crates/hir-ty/src/tests/never_type.rs @@ -354,10 +354,10 @@ fn diverging_expression_3_break() { 11..85 '{ ...} }; }': () 54..55 'x': u32 63..82 '{ loop...k; } }': u32 - 65..80 'loop { break; }': () + 65..80 'loop { break; }': u32 70..80 '{ break; }': () 72..77 'break': ! - 65..80: expected u32, got () + 72..77: expected u32, got () 97..343 '{ ...; }; }': () 140..141 'x': u32 149..175 '{ for ...; }; }': u32 diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs index 1009a8b31b..8f9465cf1b 100644 --- a/crates/hir/src/diagnostics.rs +++ b/crates/hir/src/diagnostics.rs @@ -8,7 +8,7 @@ use either::Either; use hir_def::{ DefWithBodyId, GenericParamId, SyntheticSyntax, expr_store::{ - ExprOrPatPtr, ExpressionStoreSourceMap, hir_assoc_type_binding_to_ast, + ExprOrPatPtr, ExprOrPatSource, ExpressionStoreSourceMap, hir_assoc_type_binding_to_ast, hir_generic_arg_to_ast, hir_segment_to_ast_segment, }, hir::ExprOrPatId, @@ -108,6 +108,7 @@ diagnostics![AnyDiagnostic<'db> -> IncorrectGenericsOrder, MissingLifetime, ElidedLifetimesInPath, + TypeMustBeKnown, ]; #[derive(Debug)] @@ -444,6 +445,11 @@ pub struct ElidedLifetimesInPath { pub hard_error: bool, } +#[derive(Debug)] +pub struct TypeMustBeKnown { + pub at_point: ExprOrPatSource, +} + #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum GenericArgKind { Lifetime, @@ -800,6 +806,9 @@ 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() + } }) } diff --git a/crates/ide-diagnostics/src/handlers/missing_unsafe.rs b/crates/ide-diagnostics/src/handlers/missing_unsafe.rs index 6a37702fc5..3351f5dc1c 100644 --- a/crates/ide-diagnostics/src/handlers/missing_unsafe.rs +++ b/crates/ide-diagnostics/src/handlers/missing_unsafe.rs @@ -485,7 +485,7 @@ fn main() { let b = &raw const x.a; - let tmp = Vec::from([1, 2, 3]); + let tmp = [1, 2, 3]; let c = &raw const tmp[x.a]; // ^^^ 💡 error: access to union field is unsafe and requires an unsafe function or block diff --git a/crates/ide-diagnostics/src/handlers/type_must_be_known.rs b/crates/ide-diagnostics/src/handlers/type_must_be_known.rs new file mode 100644 index 0000000000..4b72497408 --- /dev/null +++ b/crates/ide-diagnostics/src/handlers/type_must_be_known.rs @@ -0,0 +1,39 @@ +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, +) -> Diagnostic { + 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()), + ) +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn smoke_test() { + check_diagnostics( + r#" +fn foo() { + let var = loop {}; + // ^^^ 💡 warn: unused variable + var(); + // ^^^ error: type annotations needed; type must be known at this point + let var = loop {}; + // ^^^ 💡 warn: unused variable + var[0]; + // ^^^ error: type annotations needed; type must be known at this point +} + "#, + ); + } +} diff --git a/crates/ide-diagnostics/src/lib.rs b/crates/ide-diagnostics/src/lib.rs index 74f0653660..a083447335 100644 --- a/crates/ide-diagnostics/src/lib.rs +++ b/crates/ide-diagnostics/src/lib.rs @@ -66,6 +66,7 @@ mod handlers { pub(crate) mod trait_impl_orphan; pub(crate) mod trait_impl_redundant_assoc_item; pub(crate) mod type_mismatch; + pub(crate) mod type_must_be_known; pub(crate) mod typed_hole; pub(crate) mod undeclared_label; pub(crate) mod unimplemented_builtin_macro; @@ -481,6 +482,7 @@ pub fn semantic_diagnostics( AnyDiagnostic::ElidedLifetimesInPath(d) => handlers::elided_lifetimes_in_path::elided_lifetimes_in_path(&ctx, &d), AnyDiagnostic::GenericDefaultRefersToSelf(d) => handlers::generic_default_refers_to_self::generic_default_refers_to_self(&ctx, &d), AnyDiagnostic::InvalidLhsOfAssignment(d) => handlers::invalid_lhs_of_assignment::invalid_lhs_of_assignment(&ctx, &d), + AnyDiagnostic::TypeMustBeKnown(d) => handlers::type_must_be_known::type_must_be_known(&ctx, &d), }; res.push(d) } |