Auto merge of #16131 - HKalbasi:rustc-tests-fixup, r=HKalbasi

Fix false positive type mismatch in const reference patterns
This commit is contained in:
bors 2023-12-15 15:42:00 +00:00
commit 21b06c1beb
4 changed files with 97 additions and 46 deletions

View File

@ -262,7 +262,7 @@ pub(super) fn infer_top_pat(&mut self, pat: PatId, expected: &Ty) {
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 @@ fn infer_lit_pat(&mut self, expr: ExprId, expected: &Ty) -> Ty {
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
}
}
}
}

View File

@ -40,33 +40,7 @@ pub(super) fn infer_path(&mut self, path: &Path, id: ExprOrPatId) -> Option<Ty>
}
fn resolve_value_path(&mut self, path: &Path, id: ExprOrPatId) -> Option<ValuePathResolution> {
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 @@ fn resolve_value_path(&mut self, path: &Path, id: ExprOrPatId) -> Option<ValuePa
Some(ValuePathResolution::GenericDef(value_def, generic_def, substs))
}
pub(super) fn resolve_value_path_inner(
&mut self,
path: &Path,
id: ExprOrPatId,
) -> Option<(ValueNs, Option<chalk_ir::Substitution<Interner>>)> {
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() {

View File

@ -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>(T);
impl<T> Foo<T> {
const TEST_I32_REF: &'static i32 = &3;
const TEST_I32: i32 = 3;
}
fn main() {
match &6 {
Foo::<i32>::TEST_I32_REF => (),
Foo::<i32>::TEST_I32 => (),
//^^^^^^^^^^^^^^^^^^^^ expected &i32, got i32
_ => (),
}
}
"#,
);
}

View File

@ -86,7 +86,7 @@ fn measure_build(&mut self, sh: &Shell) -> anyhow::Result<()> {
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) {