Skip to content

Commit 51788be

Browse files
committed
rustc_skip_during_method_dispatch: decouple receiver & edition
1 parent 1a1cc05 commit 51788be

File tree

13 files changed

+186
-49
lines changed

13 files changed

+186
-49
lines changed

Cargo.lock

+1
Original file line numberDiff line numberDiff line change
@@ -3745,6 +3745,7 @@ dependencies = [
37453745
"rustc_trait_selection",
37463746
"rustc_type_ir",
37473747
"smallvec",
3748+
"thin-vec",
37483749
"tracing",
37493750
]
37503751

compiler/rustc_feature/src/builtin_attrs.rs

+5-4
Original file line numberDiff line numberDiff line change
@@ -969,11 +969,12 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
969969
"the `#[rustc_main]` attribute is used internally to specify test entry point function",
970970
),
971971
rustc_attr!(
972-
rustc_skip_during_method_dispatch, Normal, template!(List: "array, boxed_slice"), WarnFollowing,
973-
EncodeCrossCrate::No,
972+
rustc_skip_during_method_dispatch, Normal,
973+
template!(List: r#"receiver = "name", before_edition = "N""#),
974+
DuplicatesOk, EncodeCrossCrate::No,
974975
"the `#[rustc_skip_during_method_dispatch]` attribute is used to exclude a trait \
975-
from method dispatch when the receiver is of the following type, for compatibility in \
976-
editions < 2021 (array) or editions < 2024 (boxed_slice)."
976+
from method dispatch when the receiver is of the type `receiver`, \
977+
for compatibility in editions < `before_edition`."
977978
),
978979
rustc_attr!(
979980
rustc_must_implement_one_of, Normal, template!(List: "function1, function2, ..."),

compiler/rustc_hir_analysis/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -30,5 +30,6 @@ rustc_target = { path = "../rustc_target" }
3030
rustc_trait_selection = { path = "../rustc_trait_selection" }
3131
rustc_type_ir = { path = "../rustc_type_ir" }
3232
smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
33+
thin-vec = "0.2.12"
3334
tracing = "0.1"
3435
# tidy-alphabetical-end

compiler/rustc_hir_analysis/src/collect.rs

+72-15
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@ use std::cell::Cell;
1818
use std::iter;
1919
use std::ops::Bound;
2020

21-
use rustc_ast::Recovered;
21+
use rustc_ast::{
22+
self as ast, Attribute, MetaItem, MetaItemKind, MetaItemLit, NestedMetaItem, Recovered,
23+
};
2224
use rustc_data_structures::captures::Captures;
2325
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
2426
use rustc_data_structures::unord::UnordMap;
@@ -33,15 +35,18 @@ use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
3335
use rustc_infer::traits::ObligationCause;
3436
use rustc_middle::hir::nested_filter;
3537
use rustc_middle::query::Providers;
38+
use rustc_middle::ty::trait_def::GatedReceiver;
3639
use rustc_middle::ty::util::{Discr, IntTypeExt};
3740
use rustc_middle::ty::{self, AdtKind, Const, IsSuggestable, Ty, TyCtxt};
3841
use rustc_middle::{bug, span_bug};
42+
use rustc_span::edition::Edition;
3943
use rustc_span::symbol::{kw, sym, Ident, Symbol};
4044
use rustc_span::{Span, DUMMY_SP};
4145
use rustc_target::spec::abi;
4246
use rustc_trait_selection::error_reporting::traits::suggestions::NextTypeParamName;
4347
use rustc_trait_selection::infer::InferCtxtExt;
4448
use rustc_trait_selection::traits::ObligationCtxt;
49+
use thin_vec::ThinVec;
4550
use tracing::{debug, instrument};
4651

4752
use crate::check::intrinsic::intrinsic_operation_unsafety;
@@ -1205,6 +1210,60 @@ fn adt_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::AdtDef<'_> {
12051210
tcx.mk_adt_def(def_id.to_def_id(), kind, variants, repr, is_anonymous)
12061211
}
12071212

1213+
fn parse_rustc_skip_during_method_dispatch(
1214+
dcx: DiagCtxtHandle<'_>,
1215+
attr: &Attribute,
1216+
) -> Result<(GatedReceiver, Edition), ErrorGuaranteed> {
1217+
debug_assert!(attr.has_name(sym::rustc_skip_during_method_dispatch));
1218+
let mut receiver: Option<GatedReceiver> = None;
1219+
let mut before: Option<Edition> = None;
1220+
for arg in attr.meta_item_list().unwrap_or_default() {
1221+
let arg_span = arg.span();
1222+
if let NestedMetaItem::MetaItem(MetaItem {
1223+
path: ast::Path { segments, span: key_span, .. },
1224+
kind: MetaItemKind::NameValue(MetaItemLit { symbol: value, span: value_span, .. }),
1225+
..
1226+
}) = arg
1227+
&& let [ast::PathSegment { ident: key, .. }] = segments.as_slice()
1228+
{
1229+
match key.as_str() {
1230+
"receiver" => {
1231+
if receiver
1232+
.replace(value.as_str().parse().map_err(|()| {
1233+
dcx.span_err(value_span, "Expected `array` or `boxed_slice`")
1234+
})?)
1235+
.is_some()
1236+
{
1237+
Err(dcx.span_err(arg_span, "`receiver` should be specified only once"))?
1238+
}
1239+
}
1240+
1241+
"before" => {
1242+
if before
1243+
.replace(
1244+
value.as_str().parse().map_err(|()| {
1245+
dcx.span_err(value_span, "Could not parse edition")
1246+
})?,
1247+
)
1248+
.is_some()
1249+
{
1250+
Err(dcx.span_err(arg_span, "`before` should be specified only once"))?
1251+
}
1252+
}
1253+
_ => Err(dcx.span_err(key_span, "Expected either `receiver` or `before`"))?,
1254+
}
1255+
} else {
1256+
Err(dcx
1257+
.span_err(arg_span, "Expected either `receiver = \"...\"` or `before = \"...\"`"))?
1258+
};
1259+
}
1260+
1261+
Ok((
1262+
receiver.ok_or_else(|| dcx.span_err(attr.span, "Missing `receiver`"))?,
1263+
before.ok_or_else(|| dcx.span_err(attr.span, "Missing `before`"))?,
1264+
))
1265+
}
1266+
12081267
fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef {
12091268
let item = tcx.hir().expect_item(def_id);
12101269

@@ -1234,20 +1293,19 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef {
12341293
let rustc_coinductive = tcx.has_attr(def_id, sym::rustc_coinductive);
12351294
let is_fundamental = tcx.has_attr(def_id, sym::fundamental);
12361295

1237-
// FIXME: We could probably do way better attribute validation here.
1238-
let mut skip_array_during_method_dispatch = false;
1239-
let mut skip_boxed_slice_during_method_dispatch = false;
1296+
let mut skip_during_method_dispatch: ThinVec<(GatedReceiver, Edition)> = ThinVec::new();
12401297
for attr in tcx.get_attrs(def_id, sym::rustc_skip_during_method_dispatch) {
1241-
if let Some(lst) = attr.meta_item_list() {
1242-
for item in lst {
1243-
if let Some(ident) = item.ident() {
1244-
match ident.as_str() {
1245-
"array" => skip_array_during_method_dispatch = true,
1246-
"boxed_slice" => skip_boxed_slice_during_method_dispatch = true,
1247-
_ => (),
1248-
}
1249-
}
1298+
if let Ok(parsed) = parse_rustc_skip_during_method_dispatch(tcx.dcx(), attr) {
1299+
if skip_during_method_dispatch.iter().any(|prev| prev.0 == parsed.0) {
1300+
tcx.dcx().span_err(
1301+
attr.span,
1302+
format!(
1303+
"Duplicate `#[rustc_skip_during_method_dispatch(receiver = \"{}\")]`",
1304+
parsed.0
1305+
),
1306+
);
12501307
}
1308+
skip_during_method_dispatch.push(parsed);
12511309
}
12521310
}
12531311

@@ -1386,8 +1444,7 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef {
13861444
is_marker,
13871445
is_coinductive: rustc_coinductive || is_auto,
13881446
is_fundamental,
1389-
skip_array_during_method_dispatch,
1390-
skip_boxed_slice_during_method_dispatch,
1447+
skip_during_method_dispatch,
13911448
specialization_kind,
13921449
must_implement_one_of,
13931450
implement_via_object,

compiler/rustc_hir_typeck/src/method/probe.rs

+4-17
Original file line numberDiff line numberDiff line change
@@ -1473,24 +1473,11 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
14731473
));
14741474
}
14751475
TraitCandidate(poly_trait_ref) => {
1476-
// Some trait methods are excluded for arrays before 2021.
1477-
// (`array.into_iter()` wants a slice iterator for compatibility.)
1476+
// Handle `#[rustc_skip_during_method_dispatch]`
14781477
if let Some(method_name) = self.method_name {
1479-
if self_ty.is_array() && !method_name.span.at_least_rust_2021() {
1480-
let trait_def = self.tcx.trait_def(poly_trait_ref.def_id());
1481-
if trait_def.skip_array_during_method_dispatch {
1482-
return ProbeResult::NoMatch;
1483-
}
1484-
}
1485-
1486-
// Some trait methods are excluded for boxed slices before 2024.
1487-
// (`boxed_slice.into_iter()` wants a slice iterator for compatibility.)
1488-
if self_ty.is_box()
1489-
&& self_ty.boxed_ty().is_slice()
1490-
&& !method_name.span.at_least_rust_2024()
1491-
{
1492-
let trait_def = self.tcx.trait_def(poly_trait_ref.def_id());
1493-
if trait_def.skip_boxed_slice_during_method_dispatch {
1478+
let trait_def = self.tcx.trait_def(poly_trait_ref.def_id());
1479+
for &(receiver, edition) in &trait_def.skip_during_method_dispatch {
1480+
if method_name.span.edition() < edition && receiver.matches(self_ty) {
14941481
return ProbeResult::NoMatch;
14951482
}
14961483
}

compiler/rustc_middle/src/ty/trait_def.rs

+44-6
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,60 @@
11
use std::iter;
2+
use std::str::FromStr;
23

34
use rustc_data_structures::fx::FxIndexMap;
45
use rustc_errors::ErrorGuaranteed;
56
use rustc_hir as hir;
67
use rustc_hir::def::DefKind;
78
use rustc_hir::def_id::{DefId, LOCAL_CRATE};
89
use rustc_macros::{Decodable, Encodable, HashStable};
10+
use rustc_span::edition::Edition;
11+
use thin_vec::ThinVec;
912
use tracing::debug;
1013

1114
use crate::query::LocalCrate;
1215
use crate::traits::specialization_graph;
1316
use crate::ty::fast_reject::{self, SimplifiedType, TreatParams};
1417
use crate::ty::{Ident, Ty, TyCtxt};
1518

19+
/// TODO
20+
#[derive(Copy, Clone, PartialEq, Eq, HashStable, Encodable, Decodable)]
21+
pub enum GatedReceiver {
22+
Array,
23+
BoxedSlice,
24+
}
25+
26+
impl GatedReceiver {
27+
#[must_use]
28+
/// TODO
29+
pub fn matches(self, ty: Ty<'_>) -> bool {
30+
match self {
31+
GatedReceiver::Array => ty.is_array(),
32+
GatedReceiver::BoxedSlice => ty.is_box() && ty.boxed_ty().is_slice(),
33+
}
34+
}
35+
}
36+
37+
impl std::fmt::Display for GatedReceiver {
38+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
39+
f.write_str(match self {
40+
Self::Array => "array",
41+
Self::BoxedSlice => "boxed_slice",
42+
})
43+
}
44+
}
45+
46+
impl FromStr for GatedReceiver {
47+
type Err = ();
48+
49+
fn from_str(s: &str) -> Result<Self, Self::Err> {
50+
match s {
51+
"array" => Ok(Self::Array),
52+
"boxed_slice" => Ok(Self::BoxedSlice),
53+
_ => Err(()),
54+
}
55+
}
56+
}
57+
1658
/// A trait's definition with type information.
1759
#[derive(HashStable, Encodable, Decodable)]
1860
pub struct TraitDef {
@@ -50,15 +92,11 @@ pub struct TraitDef {
5092
/// added in the future.
5193
pub is_fundamental: bool,
5294

95+
// TODO: update comment
5396
/// If `true`, then this trait has the `#[rustc_skip_during_method_dispatch(array)]`
5497
/// attribute, indicating that editions before 2021 should not consider this trait
5598
/// during method dispatch if the receiver is an array.
56-
pub skip_array_during_method_dispatch: bool,
57-
58-
/// If `true`, then this trait has the `#[rustc_skip_during_method_dispatch(boxed_slice)]`
59-
/// attribute, indicating that editions before 2024 should not consider this trait
60-
/// during method dispatch if the receiver is a boxed slice.
61-
pub skip_boxed_slice_during_method_dispatch: bool,
99+
pub skip_during_method_dispatch: ThinVec<(GatedReceiver, Edition)>,
62100

63101
/// Used to determine whether the standard library is allowed to specialize
64102
/// on this trait.

compiler/rustc_smir/src/rustc_smir/convert/ty.rs

+31-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
//! Conversion of internal Rust compiler `ty` items to stable ones.
22
3+
use rustc_middle::ty::trait_def::GatedReceiver;
34
use rustc_middle::ty::Ty;
45
use rustc_middle::{mir, ty};
6+
use rustc_span::edition::Edition;
57
use stable_mir::ty::{
68
AdtKind, FloatTy, GenericArgs, GenericParamDef, IntTy, Region, RigidTy, TyKind, UintTy,
79
};
@@ -532,6 +534,30 @@ impl<'tcx> Stable<'tcx> for ty::trait_def::TraitSpecializationKind {
532534
}
533535
}
534536

537+
impl Stable<'_> for Edition {
538+
type T = stable_mir::edition::Edition;
539+
540+
fn stable(&self, _: &mut Tables<'_>) -> Self::T {
541+
match self {
542+
Self::Edition2015 => Self::T::Edition2015,
543+
Self::Edition2018 => Self::T::Edition2018,
544+
Self::Edition2021 => Self::T::Edition2021,
545+
Self::Edition2024 => Self::T::Edition2024,
546+
}
547+
}
548+
}
549+
550+
impl Stable<'_> for GatedReceiver {
551+
type T = stable_mir::ty::GatedReceiver;
552+
553+
fn stable(&self, _: &mut Tables<'_>) -> Self::T {
554+
match self {
555+
Self::Array => Self::T::Array,
556+
Self::BoxedSlice => Self::T::BoxedSlice,
557+
}
558+
}
559+
}
560+
535561
impl<'tcx> Stable<'tcx> for ty::TraitDef {
536562
type T = stable_mir::ty::TraitDecl;
537563
fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
@@ -545,8 +571,11 @@ impl<'tcx> Stable<'tcx> for ty::TraitDef {
545571
has_auto_impl: self.has_auto_impl,
546572
is_marker: self.is_marker,
547573
is_coinductive: self.is_coinductive,
548-
skip_array_during_method_dispatch: self.skip_array_during_method_dispatch,
549-
skip_boxed_slice_during_method_dispatch: self.skip_boxed_slice_during_method_dispatch,
574+
skip_during_method_dispatch: self
575+
.skip_during_method_dispatch
576+
.iter()
577+
.map(|x| x.stable(tables))
578+
.collect(),
550579
specialization_kind: self.specialization_kind.stable(tables),
551580
must_implement_one_of: self
552581
.must_implement_one_of

compiler/stable_mir/src/edition.rs

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
use serde::Serialize;
2+
3+
#[non_exhaustive]
4+
#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
5+
pub enum Edition {
6+
Edition2015,
7+
Edition2018,
8+
Edition2021,
9+
Edition2024,
10+
}

compiler/stable_mir/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ pub mod abi;
3232
#[macro_use]
3333
pub mod crate_def;
3434
pub mod compiler_interface;
35+
pub mod edition;
3536
#[macro_use]
3637
pub mod error;
3738
pub mod mir;

compiler/stable_mir/src/ty.rs

+9-2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use super::mir::{Body, Mutability, Safety};
77
use super::{with, DefId, Error, Symbol};
88
use crate::abi::{FnAbi, Layout};
99
use crate::crate_def::{CrateDef, CrateDefType};
10+
use crate::edition::Edition;
1011
use crate::mir::alloc::{read_target_int, read_target_uint, AllocId};
1112
use crate::mir::mono::StaticDef;
1213
use crate::target::MachineInfo;
@@ -1317,6 +1318,13 @@ pub enum TraitSpecializationKind {
13171318
AlwaysApplicable,
13181319
}
13191320

1321+
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize)]
1322+
#[non_exhaustive]
1323+
pub enum GatedReceiver {
1324+
Array,
1325+
BoxedSlice,
1326+
}
1327+
13201328
#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
13211329
pub struct TraitDecl {
13221330
pub def_id: TraitDef,
@@ -1325,8 +1333,7 @@ pub struct TraitDecl {
13251333
pub has_auto_impl: bool,
13261334
pub is_marker: bool,
13271335
pub is_coinductive: bool,
1328-
pub skip_array_during_method_dispatch: bool,
1329-
pub skip_boxed_slice_during_method_dispatch: bool,
1336+
pub skip_during_method_dispatch: Vec<(GatedReceiver, Edition)>,
13301337
pub specialization_kind: TraitSpecializationKind,
13311338
pub must_implement_one_of: Option<Vec<Ident>>,
13321339
pub implement_via_object: bool,

library/alloc/src/boxed.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -2304,7 +2304,7 @@ impl<'a, I, A: Allocator> !Iterator for &'a Box<[I], A> {}
23042304
#[stable(feature = "boxed_slice_into_iter", since = "1.80.0")]
23052305
impl<'a, I, A: Allocator> !Iterator for &'a mut Box<[I], A> {}
23062306

2307-
// Note: the `#[rustc_skip_during_method_dispatch(boxed_slice)]` on `trait IntoIterator`
2307+
// Note: the `rustc_skip_during_method_dispatch` attribute on `trait IntoIterator`
23082308
// hides this implementation from explicit `.into_iter()` calls on editions < 2024,
23092309
// so those calls will still resolve to the slice implementation, by reference.
23102310
#[stable(feature = "boxed_slice_into_iter", since = "1.80.0")]

library/core/src/array/iter.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ pub struct IntoIter<T, const N: usize> {
3535
alive: IndexRange,
3636
}
3737

38-
// Note: the `#[rustc_skip_during_method_dispatch(array)]` on `trait IntoIterator`
38+
// Note: the `rustc_skip_during_method_dispatch` attribute on `trait IntoIterator`
3939
// hides this implementation from explicit `.into_iter()` calls on editions < 2021,
4040
// so those calls will still resolve to the slice implementation, by reference.
4141
#[stable(feature = "array_into_iter_impl", since = "1.53.0")]

0 commit comments

Comments
 (0)