Skip to content

Commit

Permalink
Auto merge of rust-lang#113199 - b-naber:slice-pattern-type-inference…
Browse files Browse the repository at this point in the history
…, r=lcnr

Infer type in irrefutable slice patterns with fixed length as array

Fixes rust-lang#76342

In irrefutable slice patterns with a fixed length, we can infer the type as an array type. We now choose to prefer some implementations over others, e.g. in:

```
struct Zeroes;

const ARR: [usize; 2] = [0; 2];
const ARR2: [usize; 2] = [2; 2];

impl Into<&'static [usize; 2]> for Zeroes {
    fn into(self) -> &'static [usize; 2] {
        &ARR
    }
}

impl Into<&'static [usize]> for Zeroes {
    fn into(self) -> &'static [usize] {
        &ARR2
    }
}

fn main() {
    let &[a, b] = Zeroes.into();
}
```

We now prefer the impl candidate `impl Into<&'static [usize; 2]> for Zeroes`, it's not entirely clear to me that this is correct, but given that the slice impl would require a type annotation anyway, this doesn't seem unreasonable.

r? `@lcnr`
  • Loading branch information
bors committed Aug 3, 2023
2 parents c115ec1 + 5a9af37 commit fcf3006
Show file tree
Hide file tree
Showing 19 changed files with 489 additions and 105 deletions.
2 changes: 1 addition & 1 deletion compiler/rustc_hir_typeck/src/_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// #55810: Type check patterns first so we get types for all bindings.
let scrut_span = scrut.span.find_ancestor_inside(expr.span).unwrap_or(scrut.span);
for arm in arms {
self.check_pat_top(&arm.pat, scrutinee_ty, Some(scrut_span), Some(scrut));
self.check_pat_top(&arm.pat, scrutinee_ty, Some(scrut_span), Some(scrut), None);
}

// Now typecheck the blocks.
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_hir_typeck/src/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ pub(super) fn check_fn<'a, 'tcx>(
for (idx, (param_ty, param)) in inputs_fn.chain(maybe_va_list).zip(body.params).enumerate() {
// Check the pattern.
let ty_span = try { inputs_hir?.get(idx)?.span };
fcx.check_pat_top(&param.pat, param_ty, ty_span, None);
fcx.check_pat_top(&param.pat, param_ty, ty_span, None, None);

// Check that argument is Sized.
if !params_can_be_unsized {
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1463,11 +1463,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
};

// Type check the pattern. Override if necessary to avoid knock-on errors.
self.check_pat_top(&decl.pat, decl_ty, ty_span, origin_expr);
self.check_pat_top(&decl.pat, decl_ty, ty_span, origin_expr, Some(decl.origin));
let pat_ty = self.node_ty(decl.pat.hir_id);
self.overwrite_local_ty_if_err(decl.hir_id, decl.pat, pat_ty);

if let Some(blk) = decl.els {
if let Some(blk) = decl.origin.try_get_else() {
let previous_diverges = self.diverges.get();
let else_ty = self.check_block_with_expected(blk, NoExpectation);
let cause = self.cause(blk.span, ObligationCauseCode::LetElse);
Expand Down
26 changes: 23 additions & 3 deletions compiler/rustc_hir_typeck/src/gather_locals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,26 @@ use rustc_span::def_id::LocalDefId;
use rustc_span::Span;
use rustc_trait_selection::traits;

/// Provides context for checking patterns in declarations. More specifically this
/// allows us to infer array types if the pattern is irrefutable and allows us to infer
/// the size of the array. See issue #76342.
#[derive(Debug, Copy, Clone)]
pub(super) enum DeclOrigin<'a> {
// from an `if let` expression
LetExpr,
// from `let x = ..`
LocalDecl { els: Option<&'a hir::Block<'a>> },
}

impl<'a> DeclOrigin<'a> {
pub(super) fn try_get_else(&self) -> Option<&'a hir::Block<'a>> {
match self {
Self::LocalDecl { els } => *els,
Self::LetExpr => None,
}
}
}

/// A declaration is an abstraction of [hir::Local] and [hir::Let].
///
/// It must have a hir_id, as this is how we connect gather_locals to the check functions.
Expand All @@ -18,20 +38,20 @@ pub(super) struct Declaration<'a> {
pub ty: Option<&'a hir::Ty<'a>>,
pub span: Span,
pub init: Option<&'a hir::Expr<'a>>,
pub els: Option<&'a hir::Block<'a>>,
pub origin: DeclOrigin<'a>,
}

impl<'a> From<&'a hir::Local<'a>> for Declaration<'a> {
fn from(local: &'a hir::Local<'a>) -> Self {
let hir::Local { hir_id, pat, ty, span, init, els, source: _ } = *local;
Declaration { hir_id, pat, ty, span, init, els }
Declaration { hir_id, pat, ty, span, init, origin: DeclOrigin::LocalDecl { els } }
}
}

impl<'a> From<&'a hir::Let<'a>> for Declaration<'a> {
fn from(let_expr: &'a hir::Let<'a>) -> Self {
let hir::Let { hir_id, pat, ty, span, init } = *let_expr;
Declaration { hir_id, pat, ty, span, init: Some(init), els: None }
Declaration { hir_id, pat, ty, span, init: Some(init), origin: DeclOrigin::LetExpr }
}
}

Expand Down
Loading

0 comments on commit fcf3006

Please sign in to comment.