From 4f722165b66612e8103299972f644d94da93b78b Mon Sep 17 00:00:00 2001 From: hkalbasi Date: Fri, 15 Dec 2023 19:09:07 +0330 Subject: [PATCH 1/2] Fix false positive type mismatch in const reference patterns --- crates/hir-ty/src/infer/pat.rs | 40 +++++++++--------- crates/hir-ty/src/infer/path.rs | 63 ++++++++++++++++------------- crates/hir-ty/src/tests/patterns.rs | 38 +++++++++++++++++ 3 files changed, 96 insertions(+), 45 deletions(-) diff --git a/crates/hir-ty/src/infer/pat.rs b/crates/hir-ty/src/infer/pat.rs index 7ff12e5b7f851..acdb540289d71 100644 --- a/crates/hir-ty/src/infer/pat.rs +++ b/crates/hir-ty/src/infer/pat.rs @@ -262,7 +262,7 @@ impl InferenceContext<'_> { fn infer_pat(&mut self, pat: PatId, expected: &Ty, mut default_bm: BindingMode) -> Ty { let mut expected = self.resolve_ty_shallow(expected); - if is_non_ref_pat(self.body, pat) { + if self.is_non_ref_pat(self.body, pat) { let mut pat_adjustments = Vec::new(); while let Some((inner, _lifetime, mutability)) = expected.as_reference() { pat_adjustments.push(expected.clone()); @@ -496,24 +496,28 @@ impl InferenceContext<'_> { self.infer_expr(expr, &Expectation::has_type(expected.clone())) } -} -fn is_non_ref_pat(body: &hir_def::body::Body, pat: PatId) -> bool { - match &body[pat] { - Pat::Tuple { .. } - | Pat::TupleStruct { .. } - | Pat::Record { .. } - | Pat::Range { .. } - | Pat::Slice { .. } => true, - Pat::Or(pats) => pats.iter().all(|p| is_non_ref_pat(body, *p)), - // FIXME: ConstBlock/Path/Lit might actually evaluate to ref, but inference is unimplemented. - Pat::Path(..) => true, - Pat::ConstBlock(..) => true, - Pat::Lit(expr) => !matches!( - body[*expr], - Expr::Literal(Literal::String(..) | Literal::CString(..) | Literal::ByteString(..)) - ), - Pat::Wild | Pat::Bind { .. } | Pat::Ref { .. } | Pat::Box { .. } | Pat::Missing => false, + fn is_non_ref_pat(&mut self, body: &hir_def::body::Body, pat: PatId) -> bool { + match &body[pat] { + Pat::Tuple { .. } + | Pat::TupleStruct { .. } + | Pat::Record { .. } + | Pat::Range { .. } + | Pat::Slice { .. } => true, + Pat::Or(pats) => pats.iter().all(|p| self.is_non_ref_pat(body, *p)), + Pat::Path(p) => { + let v = self.resolve_value_path_inner(p, pat.into()); + v.is_some_and(|x| !matches!(x.0, hir_def::resolver::ValueNs::ConstId(_))) + } + Pat::ConstBlock(..) => false, + Pat::Lit(expr) => !matches!( + body[*expr], + Expr::Literal(Literal::String(..) | Literal::CString(..) | Literal::ByteString(..)) + ), + Pat::Wild | Pat::Bind { .. } | Pat::Ref { .. } | Pat::Box { .. } | Pat::Missing => { + false + } + } } } diff --git a/crates/hir-ty/src/infer/path.rs b/crates/hir-ty/src/infer/path.rs index fcfe1a3b5cf45..49fb78f67a656 100644 --- a/crates/hir-ty/src/infer/path.rs +++ b/crates/hir-ty/src/infer/path.rs @@ -40,33 +40,7 @@ impl InferenceContext<'_> { } fn resolve_value_path(&mut self, path: &Path, id: ExprOrPatId) -> Option { - let (value, self_subst) = if let Some(type_ref) = path.type_anchor() { - let last = path.segments().last()?; - - // Don't use `self.make_ty()` here as we need `orig_ns`. - let ctx = - crate::lower::TyLoweringContext::new(self.db, &self.resolver, self.owner.into()); - let (ty, orig_ns) = ctx.lower_ty_ext(type_ref); - let ty = self.table.insert_type_vars(ty); - let ty = self.table.normalize_associated_types_in(ty); - - let remaining_segments_for_ty = path.segments().take(path.segments().len() - 1); - let (ty, _) = ctx.lower_ty_relative_path(ty, orig_ns, remaining_segments_for_ty); - let ty = self.table.insert_type_vars(ty); - let ty = self.table.normalize_associated_types_in(ty); - self.resolve_ty_assoc_item(ty, last.name, id).map(|(it, substs)| (it, Some(substs)))? - } else { - // FIXME: report error, unresolved first path segment - let value_or_partial = - self.resolver.resolve_path_in_value_ns(self.db.upcast(), path)?; - - match value_or_partial { - ResolveValueResult::ValueNs(it, _) => (it, None), - ResolveValueResult::Partial(def, remaining_index, _) => self - .resolve_assoc_item(def, path, remaining_index, id) - .map(|(it, substs)| (it, Some(substs)))?, - } - }; + let (value, self_subst) = self.resolve_value_path_inner(path, id)?; let value_def = match value { ValueNs::LocalBinding(pat) => match self.result.type_of_binding.get(pat) { @@ -144,6 +118,41 @@ impl InferenceContext<'_> { Some(ValuePathResolution::GenericDef(value_def, generic_def, substs)) } + pub(super) fn resolve_value_path_inner( + &mut self, + path: &Path, + id: ExprOrPatId, + ) -> Option<(ValueNs, Option>)> { + let (value, self_subst) = if let Some(type_ref) = path.type_anchor() { + let last = path.segments().last()?; + + // Don't use `self.make_ty()` here as we need `orig_ns`. + let ctx = + crate::lower::TyLoweringContext::new(self.db, &self.resolver, self.owner.into()); + let (ty, orig_ns) = ctx.lower_ty_ext(type_ref); + let ty = self.table.insert_type_vars(ty); + let ty = self.table.normalize_associated_types_in(ty); + + let remaining_segments_for_ty = path.segments().take(path.segments().len() - 1); + let (ty, _) = ctx.lower_ty_relative_path(ty, orig_ns, remaining_segments_for_ty); + let ty = self.table.insert_type_vars(ty); + let ty = self.table.normalize_associated_types_in(ty); + self.resolve_ty_assoc_item(ty, last.name, id).map(|(it, substs)| (it, Some(substs)))? + } else { + // FIXME: report error, unresolved first path segment + let value_or_partial = + self.resolver.resolve_path_in_value_ns(self.db.upcast(), path)?; + + match value_or_partial { + ResolveValueResult::ValueNs(it, _) => (it, None), + ResolveValueResult::Partial(def, remaining_index, _) => self + .resolve_assoc_item(def, path, remaining_index, id) + .map(|(it, substs)| (it, Some(substs)))?, + } + }; + Some((value, self_subst)) + } + fn add_required_obligations_for_value_path(&mut self, def: GenericDefId, subst: &Substitution) { let predicates = self.db.generic_predicates(def); for predicate in predicates.iter() { diff --git a/crates/hir-ty/src/tests/patterns.rs b/crates/hir-ty/src/tests/patterns.rs index 5d7bab09c26e6..7234af2d68348 100644 --- a/crates/hir-ty/src/tests/patterns.rs +++ b/crates/hir-ty/src/tests/patterns.rs @@ -1153,3 +1153,41 @@ fn main() { "#, ); } + +#[test] +fn type_mismatch_pat_const_reference() { + check_no_mismatches( + r#" +const TEST_STR: &'static str = "abcd"; + +fn main() { + let s = "abcd"; + match s { + TEST_STR => (), + _ => (), + } +} + + "#, + ); + check( + r#" +struct Foo(T); + +impl Foo { + const TEST_I32_REF: &'static i32 = &3; + const TEST_I32: i32 = 3; +} + +fn main() { + match &6 { + Foo::::TEST_I32_REF => (), + Foo::::TEST_I32 => (), + //^^^^^^^^^^^^^^^^^^^^ expected &i32, got i32 + _ => (), + } +} + + "#, + ); +} From fa5a327786c4a4552602f2df4eb374d0b172dab2 Mon Sep 17 00:00:00 2001 From: hkalbasi Date: Fri, 15 Dec 2023 19:09:42 +0330 Subject: [PATCH 2/2] Use depth 1 in git clone --- xtask/src/metrics.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xtask/src/metrics.rs b/xtask/src/metrics.rs index 3d28ecdb0eb2b..845928432c4fd 100644 --- a/xtask/src/metrics.rs +++ b/xtask/src/metrics.rs @@ -86,7 +86,7 @@ impl Metrics { fn measure_rustc_tests(&mut self, sh: &Shell) -> anyhow::Result<()> { eprintln!("\nMeasuring rustc tests"); - cmd!(sh, "git clone https://github.com/rust-lang/rust").run()?; + cmd!(sh, "git clone --depth=1 https://github.com/rust-lang/rust").run()?; let output = cmd!(sh, "./target/release/rust-analyzer rustc-tests ./rust").read()?; for (metric, value, unit) in parse_metrics(&output) {