diff --git a/crates/hir-def/src/data.rs b/crates/hir-def/src/data.rs index a887612707..37506b6cdc 100644 --- a/crates/hir-def/src/data.rs +++ b/crates/hir-def/src/data.rs @@ -223,6 +223,7 @@ pub struct TraitData { pub is_unsafe: bool, pub rustc_has_incoherent_inherent_impls: bool, pub skip_array_during_method_dispatch: bool, + pub skip_boxed_slice_during_method_dispatch: bool, pub fundamental: bool, pub visibility: RawVisibility, /// Whether the trait has `#[rust_skip_array_during_method_dispatch]`. `hir_ty` will ignore @@ -250,8 +251,17 @@ impl TraitData { let is_unsafe = tr_def.is_unsafe; let visibility = item_tree[tr_def.visibility].clone(); let attrs = item_tree.attrs(db, module_id.krate(), ModItem::from(tree_id.value).into()); - let skip_array_during_method_dispatch = + let mut skip_array_during_method_dispatch = attrs.by_key(&sym::rustc_skip_array_during_method_dispatch).exists(); + let mut skip_boxed_slice_during_method_dispatch = false; + for tt in attrs.by_key(&sym::rustc_skip_during_method_dispatch).tt_values() { + for tt in tt.token_trees.iter() { + if let crate::tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) = tt { + skip_array_during_method_dispatch |= ident.sym == sym::array; + skip_boxed_slice_during_method_dispatch |= ident.sym == sym::boxed_slice; + } + } + } let rustc_has_incoherent_inherent_impls = attrs.by_key(&sym::rustc_has_incoherent_inherent_impls).exists(); let fundamental = attrs.by_key(&sym::fundamental).exists(); @@ -269,6 +279,7 @@ impl TraitData { is_unsafe, visibility, skip_array_during_method_dispatch, + skip_boxed_slice_during_method_dispatch, rustc_has_incoherent_inherent_impls, fundamental, }), diff --git a/crates/hir-ty/src/method_resolution.rs b/crates/hir-ty/src/method_resolution.rs index d3ddc883a8..c07bd08be4 100644 --- a/crates/hir-ty/src/method_resolution.rs +++ b/crates/hir-ty/src/method_resolution.rs @@ -5,7 +5,7 @@ use std::ops::ControlFlow; use base_db::CrateId; -use chalk_ir::{cast::Cast, Mutability, TyKind, UniverseIndex, WhereClause}; +use chalk_ir::{cast::Cast, UniverseIndex, WithKind}; use hir_def::{ data::{adt::StructFlags, ImplData}, nameres::DefMap, @@ -16,7 +16,6 @@ use hir_expand::name::Name; use intern::sym; use rustc_hash::{FxHashMap, FxHashSet}; use smallvec::{smallvec, SmallVec}; -use span::Edition; use stdx::never; use triomphe::Arc; @@ -25,12 +24,14 @@ use crate::{ db::HirDatabase, error_lifetime, from_chalk_trait_id, from_foreign_def_id, infer::{unify::InferenceTable, Adjust, Adjustment, OverloadedDeref, PointerCast}, + lang_items::is_box, primitive::{FloatTy, IntTy, UintTy}, to_chalk_trait_id, utils::all_super_traits, - AdtId, Canonical, CanonicalVarKinds, DebruijnIndex, DynTyExt, ForeignDefId, Goal, Guidance, - InEnvironment, Interner, Scalar, Solution, Substitution, TraitEnvironment, TraitRef, - TraitRefExt, Ty, TyBuilder, TyExt, + AdtId, Canonical, CanonicalVarKinds, DebruijnIndex, DynTyExt, ForeignDefId, GenericArgData, + Goal, Guidance, InEnvironment, Interner, Mutability, Scalar, Solution, Substitution, + TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder, TyExt, TyKind, VariableKind, + WhereClause, }; /// This is used as a key for indexing impls. @@ -1146,17 +1147,30 @@ fn iterate_trait_method_candidates( 'traits: for &t in traits_in_scope { let data = db.trait_data(t); - // Traits annotated with `#[rustc_skip_array_during_method_dispatch]` are skipped during + // Traits annotated with `#[rustc_skip_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 - && matches!(self_ty.kind(Interner), chalk_ir::TyKind::Array(..)) + && matches!(self_ty.kind(Interner), TyKind::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 { + if !db.crate_graph()[krate].edition.at_least_2021() { + continue; + } + } + if data.skip_boxed_slice_during_method_dispatch + && matches!( + self_ty.kind(Interner), TyKind::Adt(AdtId(def), subst) + if is_box(table.db, *def) + && matches!(subst.at(Interner, 0).assert_ty_ref(Interner).kind(Interner), TyKind::Slice(..)) + ) + { + // 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.at_least_2024() { continue; } } @@ -1619,15 +1633,11 @@ fn generic_implements_goal( let kinds = binders.iter().cloned().chain(trait_ref.substitution.iter(Interner).skip(1).map(|it| { let vk = match it.data(Interner) { - chalk_ir::GenericArgData::Ty(_) => { - chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General) - } - chalk_ir::GenericArgData::Lifetime(_) => chalk_ir::VariableKind::Lifetime, - chalk_ir::GenericArgData::Const(c) => { - chalk_ir::VariableKind::Const(c.data(Interner).ty.clone()) - } + GenericArgData::Ty(_) => VariableKind::Ty(chalk_ir::TyVariableKind::General), + GenericArgData::Lifetime(_) => VariableKind::Lifetime, + GenericArgData::Const(c) => VariableKind::Const(c.data(Interner).ty.clone()), }; - chalk_ir::WithKind::new(vk, UniverseIndex::ROOT) + WithKind::new(vk, UniverseIndex::ROOT) })); let binders = CanonicalVarKinds::from_iter(Interner, kinds); diff --git a/crates/hir-ty/src/tests/method_resolution.rs b/crates/hir-ty/src/tests/method_resolution.rs index 63a83d403f..360a129283 100644 --- a/crates/hir-ty/src/tests/method_resolution.rs +++ b/crates/hir-ty/src/tests/method_resolution.rs @@ -1640,6 +1640,55 @@ impl<'a, T> IntoIterator for &'a [T] { ); } +#[test] +fn skip_during_method_dispatch() { + check_types( + r#" +//- /main2018.rs crate:main2018 deps:core edition:2018 +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_during_method_dispatch(array, boxed_slice)] +pub trait IntoIterator { + type Out; + fn into_iter(self) -> Self::Out; +} + +impl IntoIterator for [T; 1] { + type Out = T; + fn into_iter(self) -> Self::Out { loop {} } +} +impl<'a, T> IntoIterator for &'a [T] { + type Out = &'a T; + fn into_iter(self) -> Self::Out { loop {} } +} + "#, + ); +} + #[test] fn sized_blanket_impl() { check_infer( diff --git a/crates/intern/src/symbol/symbols.rs b/crates/intern/src/symbol/symbols.rs index 1454d825b7..d810fac36a 100644 --- a/crates/intern/src/symbol/symbols.rs +++ b/crates/intern/src/symbol/symbols.rs @@ -400,6 +400,7 @@ define_symbols! { rustc_reservation_impl, rustc_safe_intrinsic, rustc_skip_array_during_method_dispatch, + rustc_skip_during_method_dispatch, semitransparent, shl_assign, shl, @@ -455,4 +456,6 @@ define_symbols! { vectorcall, wasm, win64, + array, + boxed_slice, } diff --git a/crates/parser/src/edition.rs b/crates/parser/src/edition.rs index 831a482059..5d3d3cbd47 100644 --- a/crates/parser/src/edition.rs +++ b/crates/parser/src/edition.rs @@ -20,6 +20,10 @@ impl Edition { self >= Edition::Edition2024 } + pub fn at_least_2021(self) -> bool { + self >= Edition::Edition2021 + } + pub fn at_least_2018(self) -> bool { self >= Edition::Edition2018 }