From 955064b6aaa5c24e980328f9d9fbe731cc29636c Mon Sep 17 00:00:00 2001
From: Jonas Schievink <jonasschievink@gmail.com>
Date: Tue, 1 Jun 2021 21:33:14 +0200
Subject: [PATCH] Implement `#[rustc_skip_array_during_method_dispatch]`

---
 crates/hir_def/src/data.rs                   | 14 +++++-
 crates/hir_ty/src/method_resolution.rs       | 16 ++++++-
 crates/hir_ty/src/tests/method_resolution.rs | 49 ++++++++++++++++++++
 3 files changed, 77 insertions(+), 2 deletions(-)

diff --git a/crates/hir_def/src/data.rs b/crates/hir_def/src/data.rs
index d2bb381be02..2f06a6e29ac 100644
--- a/crates/hir_def/src/data.rs
+++ b/crates/hir_def/src/data.rs
@@ -143,6 +143,7 @@ pub struct TraitData {
     pub is_auto: bool,
     pub is_unsafe: bool,
     pub visibility: RawVisibility,
+    pub skip_array_during_method_dispatch: bool,
 }
 
 impl TraitData {
@@ -157,6 +158,10 @@ impl TraitData {
         let container = AssocContainerId::TraitId(tr);
         let visibility = item_tree[tr_def.visibility].clone();
         let mut expander = Expander::new(db, tr_loc.id.file_id(), module_id);
+        let skip_array_during_method_dispatch = item_tree
+            .attrs(db, tr_loc.container.krate(), ModItem::from(tr_loc.id.value).into())
+            .by_key("rustc_skip_array_during_method_dispatch")
+            .exists();
 
         let items = collect_items(
             db,
@@ -168,7 +173,14 @@ impl TraitData {
             100,
         );
 
-        Arc::new(TraitData { name, items, is_auto, is_unsafe, visibility })
+        Arc::new(TraitData {
+            name,
+            items,
+            is_auto,
+            is_unsafe,
+            visibility,
+            skip_array_during_method_dispatch,
+        })
     }
 
     pub fn associated_types(&self) -> impl Iterator<Item = TypeAliasId> + '_ {
diff --git a/crates/hir_ty/src/method_resolution.rs b/crates/hir_ty/src/method_resolution.rs
index af6b6cda709..a23527f7df2 100644
--- a/crates/hir_ty/src/method_resolution.rs
+++ b/crates/hir_ty/src/method_resolution.rs
@@ -5,7 +5,7 @@
 use std::{iter, sync::Arc};
 
 use arrayvec::ArrayVec;
-use base_db::CrateId;
+use base_db::{CrateId, Edition};
 use chalk_ir::{cast::Cast, Mutability, UniverseIndex};
 use hir_def::{
     lang_item::LangItemTarget, nameres::DefMap, AssocContainerId, AssocItemId, FunctionId,
@@ -639,6 +639,7 @@ fn iterate_trait_method_candidates(
     receiver_ty: Option<&Canonical<Ty>>,
     callback: &mut dyn FnMut(&Ty, AssocItemId) -> bool,
 ) -> bool {
+    let receiver_is_array = matches!(self_ty.value.kind(&Interner), chalk_ir::TyKind::Array(..));
     // if ty is `dyn Trait`, the trait doesn't need to be in scope
     let inherent_trait =
         self_ty.value.dyn_trait().into_iter().flat_map(|t| all_super_traits(db.upcast(), t));
@@ -655,6 +656,19 @@ fn iterate_trait_method_candidates(
     'traits: for t in traits {
         let data = db.trait_data(t);
 
+        // Traits annotated with `#[rustc_skip_array_during_method_dispatch]` are skipped during
+        // method resolution, if the receiver is an array, and we're compiling for editions before
+        // 2021.
+        // This is to make `[a].into_iter()` not break code with the new `IntoIterator` impl for
+        // arrays.
+        if data.skip_array_during_method_dispatch && receiver_is_array {
+            // FIXME: this should really be using the edition of the method name's span, in case it
+            // comes from a macro
+            if db.crate_graph()[krate].edition < Edition::Edition2021 {
+                continue;
+            }
+        }
+
         // we'll be lazy about checking whether the type implements the
         // trait, but if we find out it doesn't, we'll skip the rest of the
         // iteration
diff --git a/crates/hir_ty/src/tests/method_resolution.rs b/crates/hir_ty/src/tests/method_resolution.rs
index 058eb91299d..f26b2c8a79c 100644
--- a/crates/hir_ty/src/tests/method_resolution.rs
+++ b/crates/hir_ty/src/tests/method_resolution.rs
@@ -1349,3 +1349,52 @@ fn f() {
     "#,
     );
 }
+
+#[test]
+fn skip_array_during_method_dispatch() {
+    check_types(
+        r#"
+//- /main2018.rs crate:main2018 deps:core
+use core::IntoIterator;
+
+fn f() {
+    let v = [4].into_iter();
+    v;
+  //^ &i32
+
+    let a = [0, 1].into_iter();
+    a;
+  //^ &i32
+}
+
+//- /main2021.rs crate:main2021 deps:core edition:2021
+use core::IntoIterator;
+
+fn f() {
+    let v = [4].into_iter();
+    v;
+  //^ i32
+
+    let a = [0, 1].into_iter();
+    a;
+  //^ &i32
+}
+
+//- /core.rs crate:core
+#[rustc_skip_array_during_method_dispatch]
+pub trait IntoIterator {
+    type Out;
+    fn into_iter(self) -> Self::Out;
+}
+
+impl<T> IntoIterator for [T; 1] {
+    type Out = T;
+    fn into_iter(self) -> Self::Out {}
+}
+impl<'a, T> IntoIterator for &'a [T] {
+    type Out = &'a T;
+    fn into_iter(self) -> Self::Out {}
+}
+    "#,
+    );
+}