mirror of
				https://github.com/rust-lang/rust-analyzer.git
				synced 2025-11-03 13:13:18 +00:00 
			
		
		
		
	Merge #9105
9105: internal: calculate pattern adjustments r=flodiebold a=iDawer This extends `InferenceResult` with `pub pat_adjustments: FxHashMap<PatId, Vec<Ty>>`. Fixes #9095 Co-authored-by: Dawer <7803845+iDawer@users.noreply.github.com>
This commit is contained in:
		
						commit
						50936397cc
					
				@ -357,17 +357,20 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
 | 
			
		||||
            infer: &infer,
 | 
			
		||||
            db,
 | 
			
		||||
            pattern_arena: &pattern_arena,
 | 
			
		||||
            eprint_panic_context: &|| {
 | 
			
		||||
            panic_context: &|| {
 | 
			
		||||
                use syntax::AstNode;
 | 
			
		||||
                if let Ok(scrutinee_sptr) = source_map.expr_syntax(match_expr) {
 | 
			
		||||
                    let root = scrutinee_sptr.file_syntax(db.upcast());
 | 
			
		||||
                    if let Some(match_ast) = scrutinee_sptr.value.to_node(&root).syntax().parent() {
 | 
			
		||||
                        eprintln!(
 | 
			
		||||
                            "Match checking is about to panic on this expression:\n{}",
 | 
			
		||||
                            match_ast.to_string(),
 | 
			
		||||
                        );
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                let match_expr_text = source_map
 | 
			
		||||
                    .expr_syntax(match_expr)
 | 
			
		||||
                    .ok()
 | 
			
		||||
                    .and_then(|scrutinee_sptr| {
 | 
			
		||||
                        let root = scrutinee_sptr.file_syntax(db.upcast());
 | 
			
		||||
                        scrutinee_sptr.value.to_node(&root).syntax().parent()
 | 
			
		||||
                    })
 | 
			
		||||
                    .map(|node| node.to_string());
 | 
			
		||||
                format!(
 | 
			
		||||
                    "expression:\n{}",
 | 
			
		||||
                    match_expr_text.as_deref().unwrap_or("<synthesized expr>")
 | 
			
		||||
                )
 | 
			
		||||
            },
 | 
			
		||||
        };
 | 
			
		||||
        let report = compute_match_usefulness(&cx, &m_arms);
 | 
			
		||||
 | 
			
		||||
@ -100,10 +100,19 @@ impl<'a> PatCtxt<'a> {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub(crate) fn lower_pattern(&mut self, pat: hir_def::expr::PatId) -> Pat {
 | 
			
		||||
        // FIXME: implement pattern adjustments (implicit pattern dereference; "RFC 2005-match-ergonomics")
 | 
			
		||||
        // XXX(iDawer): Collecting pattern adjustments feels imprecise to me.
 | 
			
		||||
        // When lowering of & and box patterns are implemented this should be tested
 | 
			
		||||
        // in a manner of `match_ergonomics_issue_9095` test.
 | 
			
		||||
        // Pattern adjustment is part of RFC 2005-match-ergonomics.
 | 
			
		||||
        // More info https://github.com/rust-lang/rust/issues/42640#issuecomment-313535089
 | 
			
		||||
        let unadjusted_pat = self.lower_pattern_unadjusted(pat);
 | 
			
		||||
        unadjusted_pat
 | 
			
		||||
        self.infer.pat_adjustments.get(&pat).map(|it| &**it).unwrap_or_default().iter().rev().fold(
 | 
			
		||||
            unadjusted_pat,
 | 
			
		||||
            |subpattern, ref_ty| Pat {
 | 
			
		||||
                ty: ref_ty.clone(),
 | 
			
		||||
                kind: Box::new(PatKind::Deref { subpattern }),
 | 
			
		||||
            },
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn lower_pattern_unadjusted(&mut self, pat: hir_def::expr::PatId) -> Pat {
 | 
			
		||||
@ -1236,6 +1245,21 @@ fn main(f: Foo) {
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn match_ergonomics_issue_9095() {
 | 
			
		||||
        check_diagnostics(
 | 
			
		||||
            r#"
 | 
			
		||||
enum Foo<T> { A(T) }
 | 
			
		||||
fn main() {
 | 
			
		||||
    match &Foo::A(true) {
 | 
			
		||||
        _ => {}
 | 
			
		||||
        Foo::A(_) => {}
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
"#,
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    mod false_negatives {
 | 
			
		||||
        //! The implementation of match checking here is a work in progress. As we roll this out, we
 | 
			
		||||
        //! prefer false negatives to false positives (ideally there would be no false positives). This
 | 
			
		||||
 | 
			
		||||
@ -295,7 +295,7 @@ pub(crate) struct MatchCheckCtx<'a> {
 | 
			
		||||
    pub(crate) db: &'a dyn HirDatabase,
 | 
			
		||||
    /// Lowered patterns from arms plus generated by the check.
 | 
			
		||||
    pub(crate) pattern_arena: &'a RefCell<PatternArena>,
 | 
			
		||||
    pub(crate) eprint_panic_context: &'a dyn Fn(),
 | 
			
		||||
    pub(crate) panic_context: &'a dyn Fn() -> String,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'a> MatchCheckCtx<'a> {
 | 
			
		||||
@ -331,8 +331,7 @@ impl<'a> MatchCheckCtx<'a> {
 | 
			
		||||
 | 
			
		||||
    #[track_caller]
 | 
			
		||||
    pub(super) fn bug(&self, info: &str) -> ! {
 | 
			
		||||
        (self.eprint_panic_context)();
 | 
			
		||||
        panic!("bug: {}", info);
 | 
			
		||||
        panic!("bug: {}\n{}", info, (self.panic_context)());
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -150,6 +150,8 @@ pub struct InferenceResult {
 | 
			
		||||
    type_mismatches: FxHashMap<ExprOrPatId, TypeMismatch>,
 | 
			
		||||
    /// Interned Unknown to return references to.
 | 
			
		||||
    standard_types: InternedStandardTypes,
 | 
			
		||||
    /// Stores the types which were implicitly dereferenced in pattern binding modes.
 | 
			
		||||
    pub pat_adjustments: FxHashMap<PatId, Vec<Ty>>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl InferenceResult {
 | 
			
		||||
 | 
			
		||||
@ -101,7 +101,9 @@ impl<'a> InferenceContext<'a> {
 | 
			
		||||
        let mut expected = self.resolve_ty_shallow(expected);
 | 
			
		||||
 | 
			
		||||
        if is_non_ref_pat(&body, pat) {
 | 
			
		||||
            let mut pat_adjustments = Vec::new();
 | 
			
		||||
            while let Some((inner, _lifetime, mutability)) = expected.as_reference() {
 | 
			
		||||
                pat_adjustments.push(expected.clone());
 | 
			
		||||
                expected = self.resolve_ty_shallow(inner);
 | 
			
		||||
                default_bm = match default_bm {
 | 
			
		||||
                    BindingMode::Move => BindingMode::Ref(mutability),
 | 
			
		||||
@ -109,6 +111,11 @@ impl<'a> InferenceContext<'a> {
 | 
			
		||||
                    BindingMode::Ref(Mutability::Mut) => BindingMode::Ref(mutability),
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if !pat_adjustments.is_empty() {
 | 
			
		||||
                pat_adjustments.shrink_to_fit();
 | 
			
		||||
                self.result.pat_adjustments.insert(pat, pat_adjustments);
 | 
			
		||||
            }
 | 
			
		||||
        } else if let Pat::Ref { .. } = &body[pat] {
 | 
			
		||||
            cov_mark::hit!(match_ergonomics_ref);
 | 
			
		||||
            // When you encounter a `&pat` pattern, reset to Move.
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user