Unnamed repository; edit this file 'description' to name the repository.
Feature a "type annotations needed" diagnostic
There are two kinds of this diagnostic:
1 Some constructs, like calling or indexing, require the type to be known *immediately*.
2. Otherwise, if we cannot resolve the variable at the end of inference, it's an error.
This PR implements only the first kind, which is also simpler to implement. We just need to push an error in `structurally_resolve_type()` (as opposed to `try_structurally_resolve_type()` that never errors) if the type cannot be resolved, and of course also make sure we only call this function where rustc does.
The second case needs to track the origin of infer vars, and it's for another time.
| -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) } |