Skip to content

Support rustc_skip_during_method_dispatch #17618

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion crates/hir-def/src/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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();
Expand All @@ -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,
}),
Expand Down
42 changes: 26 additions & 16 deletions crates/hir-ty/src/method_resolution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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;

Expand All @@ -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.
Expand Down Expand Up @@ -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;
}
}
Expand Down Expand Up @@ -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);

Expand Down
49 changes: 49 additions & 0 deletions crates/hir-ty/src/tests/method_resolution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<T> 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(
Expand Down
3 changes: 3 additions & 0 deletions crates/intern/src/symbol/symbols.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -455,4 +456,6 @@ define_symbols! {
vectorcall,
wasm,
win64,
array,
boxed_slice,
}
4 changes: 4 additions & 0 deletions crates/parser/src/edition.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down